summaryrefslogtreecommitdiff
path: root/v4.0/src/DEV/XMAEM/INDEEMU.ASM
diff options
context:
space:
mode:
Diffstat (limited to 'v4.0/src/DEV/XMAEM/INDEEMU.ASM')
-rw-r--r--v4.0/src/DEV/XMAEM/INDEEMU.ASM653
1 files changed, 653 insertions, 0 deletions
diff --git a/v4.0/src/DEV/XMAEM/INDEEMU.ASM b/v4.0/src/DEV/XMAEM/INDEEMU.ASM
new file mode 100644
index 0000000..9534ac2
--- /dev/null
+++ b/v4.0/src/DEV/XMAEM/INDEEMU.ASM
@@ -0,0 +1,653 @@
1PAGE 60,132
2TITLE INDEEMU - 386 XMA EMULATOR - Sensitive Instruction Emulator
3
4COMMENT #
5* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6* *
7* MODULE NAME : INDEEMU *
8* *
9* *
10* 5669-196 (C) COPYRIGHT 1988 Microsoft Corporation *
11* *
12* DESCRIPTIVE NAME: 386 XMA emulator - emulate sensitive instructions *
13* *
14* STATUS (LEVEL) : VERSION (0) LEVEL (1.0) *
15* *
16* FUNCTION : When the I/O privilege level (IOPL) is less than 3 then *
17* the processor flags an exception and gives control to *
18* the emulator whenever the virtual 8086 (V86) task tries *
19* to execute a sensitive instruction. The set of sensitive *
20* instructions includes: STI, CLI, INT3, INTO, IRET, INT, *
21* PUSHF, POPF, LOCK, IN and OUT. This moudle will emulate *
22* these intructions for the V86 task. It will also set the *
23* IOPL to 3. This will keep the processor from raising *
24* further exceptions for these instructions. This in turn *
25* improves performance because the emulator will not be *
26* given control each time one of these instructions is *
27* executed by the V86 task. *
28* *
29* This module also has a small piece of code to handle *
30* exception 7, coprocessor not available. This exception *
31* is raised when the EM (EMulation), MP (Monitor Processor),*
32* and TS (Task Switch) bits in CR0 are on. When this *
33* happens it turns off these bits and retries the instruc- *
34* tion that faulted. *
35* *
36* MODULE TYPE : ASM *
37* *
38* REGISTER USAGE : 80386 Standard *
39* *
40* RESTRICTIONS : None *
41* *
42* DEPENDENCIES : None *
43* *
44* ENTRY POINT : EMULATE *
45* *
46* LINKAGE : Jumped to by INDEEXC *
47* *
48* INPUT PARMS : None *
49* *
50* RETURN PARMS : None *
51* *
52* OTHER EFFECTS : None *
53* *
54* EXIT NORMAL : IRET to the V86 task *
55* *
56* EXIT ERROR : Jump to error routine in INDEEXC *
57* *
58* EXTERNAL *
59* REFERENCES : POPREGS - Entry point in INDEEXC to pop the registers P1C*
60* off the stack and IRET to the V86 task. *
61* DISPLAY - Entry point in INDEEXC for the error routine *
62* that does the NMI to the error handler. *
63* INT15 - Entry point to INDEI15, the INT 15 handler. *
64* XMAIN - Entry point in INDEXMA to handle IN for a byte *
65* at the port address in DX *
66* INW - Entry point in INDEXMA to handle IN for a word *
67* at the port address in DX *
68* INIMMED - Entry point in INDEXMA to handle IN for a byte *
69* at the immediate port address given *
70* INWIMMED- Entry point in INDEXMA to handle IN for a word *
71* at the immediate port address given *
72* XMAOUT - Entry point in INDEXMA to handle OUT for a byte *
73* at the port address in DX *
74* OUTW - Entry point in INDEXMA to handle OUT for a word *
75* at the port address in DX *
76* XMAOUTIMMED - Entry point in INDEXMA to handle OUT for a *
77* byte at the immediate port address given *
78* XMAOUTWIMMED - Entry point in INDEXMA to handle OUT for a *
79* word at the immediate port address given *
80* MANPORT - Entry point in INDEDMA to issue an out to the *
81* port that will reIPL the system *
82* *
83* SUB-ROUTINES : None *
84* *
85* MACROS : DATAOV - Create a prefix for the following instruction *
86* so that it accesses data 32 bits wide *
87* ADDROV - Create a prefix for the following instruction *
88* so that it uses addresses that are 32 bits wide *
89* CMOV - Move to or from a control register *
90* *
91* CONTROL BLOCKS : INDEDAT.INC *
92* *
93* CHANGE ACTIVITY : *
94* *
95* $MOD(INDEEMU) COMP(LOAD) PROD(3270PC) : *
96* *
97* $D0=D0004700 410 870523 D : NEW FOR RELEASE 1.1 *
98* $P1=P0000312 410 870804 D : CLEAN UP WARNING MESSAGES *
99* *
100* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
101#
102
103 .286P ; Enable recognition of 286 privileged instructs.
104
105 .XLIST ; Turn off the listing
106 INCLUDE INDEDAT.INC
107
108 IF1 ; Only include macros on the first pass
109 INCLUDE INDEOVP.MAC
110 INCLUDE INDEINS.MAC
111 ENDIF
112 .LIST ; Turn on the listing
113
114 PUBLIC INDEEMU
115
116PROG SEGMENT PARA PUBLIC 'PROG'
117
118 ASSUME CS:PROG
119 ASSUME DS:NOTHING
120 ASSUME ES:NOTHING
121 ASSUME SS:NOTHING
122
123INDEEMU LABEL NEAR
124
125 ; The following entries are in other modules
126
127 EXTRN XMAIN:NEAR ; Byte IN from port # in DX
128IN_INST EQU XMAIN ; @P1C
129 EXTRN INW:NEAR ; Word IN from port # in DX
130 EXTRN INIMMED:NEAR ; Byte IN from immediate port #
131 EXTRN INWIMMED:NEAR ; Word IN from immediate port #
132 EXTRN XMAOUT:NEAR ; Byte OUT to port # in DX
133OUT_INST EQU XMAOUT ; @P1C
134 EXTRN OUTW:NEAR ; Word OUT to port # in DX
135 EXTRN XMAOUTIMMED:NEAR ; Byte OUT to immediate port #
136OUTIMMED EQU XMAOUTIMMED ;
137 EXTRN OUTWIMMED:NEAR ; Word OUT to immediate port #
138 EXTRN DISPLAY:NEAR ; Signal the error handler
139 EXTRN MANPORT:NEAR ; ReIPL the system
140 EXTRN INT15:NEAR ; Handle INT 15
141 EXTRN POPREGS:NEAR ; Pop the registers and IRET to V86 task @P1C
142
143PAGE
144;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
145;; The following is a jump table for each of the instructions. There are 256 ;;
146;; entries, each three bytes long, one for each possible op-code. The op- ;;
147;; code is used as an index into the table. Each entry is a jump instruction ;;
148;; instruction telling where to jump for each particular op-code. The table ;;
149;; is initialized such that all in- structions jump to the routin for ;;
150;; unexpected op-codes. Then the entries for the instructions we want to ;;
151;; emulate are set to jump to the appropriate routine. ;;
152;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
153
154TABLE:
155 .XLIST
156 REPT 256 ; Initialize the table so that all instructions
157 JMP UNEXPECTED ; jump to UNEXPECTED
158 ENDM
159 .LIST
160
161; Now set up the entries for the instructions we want to emulate
162
163TABLE_END:
164 ORG TABLE+(0FBH*3) ; 0FBH is the op-code for STI.
165 JMP STI_INST ; @P1C
166 ORG TABLE+(0FAH*3) ; 0FAH is the op-code for CLI.
167 JMP CLI_INST ; @P1C
168 ORG TABLE+(0F0H*3) ; 0F0H is the op-code for LOCK.
169 JMP LOCK_INST ; @P1C
170 ORG TABLE+(0EFH*3) ; 0EFH is the op-code for OUT for a word.
171 JMP OUTW ;
172 ORG TABLE+(0EEH*3) ; 0EEH is the op-code for OUT for a byte.
173 JMP OUT_INST ; @P1C
174 ORG TABLE+(0EDH*3) ; 0EDH is the op-code for IN for a word.
175 JMP INW
176 ORG TABLE+(0ECH*3) ; 0ECH is the op-code for IN for a byte.
177 JMP IN_INST ; @P1C
178 ORG TABLE+(0E7H*3) ; 0E7H is the op-code for OUT for a word to
179 JMP OUTWIMMED ; an immediate port value.
180 ORG TABLE+(0E6H*3) ; 0E6H is the op-code for OUT for a byte to
181 JMP OUTIMMED ; an immediate port value.
182 ORG TABLE+(0E5H*3) ; 0E5H is the op-code for IN for a word to
183 JMP INWIMMED ; an immediate port value.
184 ORG TABLE+(0E4H*3) ; 0E4H is the op-code for IN for a byte to
185 JMP INIMMED ; an immediate port value.
186 ORG TABLE+(0CFH*3) ; 0CFH is the op-code for IRET.
187 JMP IRET_INST ; @P1C
188 ORG TABLE+(0CEH*3) ; 0CEH is the op-code for INTO.
189 JMP INTO_INST ; @P1C
190 ORG TABLE+(0CDH*3) ; 0CDH is the op-code for INT.
191 JMP INT_INST ; @P1C
192 ORG TABLE+(0CCH*3) ; 0CCH is the op-code for INT3.
193 JMP INT3 ;
194 ORG TABLE+(09DH*3) ; 09DH is the op-code for POPF.
195 JMP POPF_INST ; @P1C
196 ORG TABLE+(09CH*3) ; 09CH is the op-code for PUSHF.
197 JMP PUSHF_INST ; @P1C
198 ORG TABLE+(00FH*3) ; 00FH is the op-code for POP CS.
199 JMP MANPORT ; Expedient until 0F opcode properly emulated
200
201 ORG TABLE_END
202
203VALUE3 DB 3
204
205 PUBLIC EMULATE
206 PUBLIC POPIO
207 PUBLIC INTCOM
208
209EMULATE PROC NEAR
210
211 CLD
212
213PAGE
214;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
215;; Get the op-code that faulted from real memory. Use it as an index into ;;
216;; the jump table to go to the appropriate routine. ;;
217;; ;;
218;; Note: The DATAOV macro creates a prefix that makes the instruction that ;;
219;; immediately follows access all data as 32 bits wide. Similarly, ;;
220;; the ADDROV macro creates a prefix that makes the instruction that ;;
221;; immediately follows use addresses that are 32 bits wide. ;;
222;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
223
224 MOV AX,HUGE_PTR ; Load DS with a selector that will
225 MOV DS,AX ; access all of memory as data
226
227 MOV SS:WORD PTR [BP+BP_IP2],0 ; Clear the high words of the V86
228 MOV SS:WORD PTR [BP+BP_CS2],0 ; task's CS and IP
229
230 DATAOV
231 MOV SI,SS:[BP+BP_IP] ; Get the V86 IP into our SI. The high
232 DATAOV ; order word is zeroes.
233 MOV AX,SS:[BP+BP_CS] ; Get the V86 CS into AX. Again, the
234 DATAOV ; high order word is zeroes.
235 SHL AX,4 ; Multiply CS by 16 to convert it to an
236 DATAOV ; offset.
237 ADD SI,AX ; Add on IP. Now SI contains the offset
238 ; from 0 of the instruction that
239 ; faulted.
240 ADDROV
241 LODSB ; Get the op-code into AL
242
243 ADDROV ; Intel bug # A0-119
244 NOP ; Intel bug # A0-119
245
246 MUL VALUE3 ; Multiply the op-code by 3 to get an
247 LEA BX,TABLE ; index into the jump table
248 ADD AX,BX ; Add on the offset of the base of the
249 ; table
250 JMP AX ; Jump to the entry in the table. This
251 ; entry will then jump us to the
252 ; routine that handles this op-code.
253
254PAGE
255;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
256;; Woops! We got an op-code that we did not expect to fault. First let's ;;
257;; check if this instruction faulted because the coprocessor was not avail- ;;
258;; able. This will be signalled by an exception code of 07. If this is the ;;
259;; case then reset the the following bits in CR0: EM (EMulation) says that ;;
260;; coprocessor functions are emulated by software when set to 0; MP (monitor ;;
261;; Processor), when set to 1 raises an exception 7 when TS (Task Switched) ;;
262;; is set to 1 and a WAIT instruction is executed. TS is set every time ;;
263;; there is a task switch. ;;
264;; ;;
265;; If it was not an execption 7 then we'll check the I/O privilege level ;;
266;; (IOPL). An IOPL other less than 3 will cause all I/O and some sensitive ;;
267;; instructions to fault. We really don't want to be bothered by all these ;;
268;; faulting instructions so we'll set the IOPL to 3 which will allow anyone ;;
269;; to do I/O and the sensitive instructions. This will improve performance ;;
270;; since the V86 task will be interrupted less often. But first we'll check ;;
271;; to see if the IOPL is already 3. If so then we got trouble. Most likely ;;
272;; it's an invalid op-code. In this case we'll signal the error handler in ;;
273;; INDEEXC. ;;
274;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
275
276UNEXPECTED:
277 CMP SS:WORD PTR [BP+BP_EX],0007H
278 ; Check if it's an 07 exception -- co-
279 ; processor not available
280 JNE TRYIOPL3 ; If no then try setting the IOPL to 3
281
282 MOV AX,8000H ; Set the paging enabled bit
283 DATAOV
284 SHL AX,16 ; It's the one all the way on the left
285 MOV AX,0001H ; Set protect mode on. Leave all other
286 ; bits off.
287 CMOV CR0,EAX ; Reset CR0
288 JMP POPREGS ; Return to the V86 task @P1C
289
290; Try setting the IOPL to 3
291
292TRYIOPL3:
293 MOV BX,AX ; Save the faulty op-code in BX
294 MOV AX,SS:WORD PTR [BP+BP_FL] ; Get the V86 flags and check if
295 AND AX,3000H ; IOPL is already set to 3
296 CMP AX,3000H
297 JE WHOOPS ; If we're already at IOPL 3 the some-
298 ; things fishy. Time to signal an
299 ; error.
300 OR SS:WORD PTR [BP+BP_FL],3000H
301 ; Otherwise set IOPL to 3 and return to
302 JMP POPREGS ; the V86 task and let it try to @P1C
303 ; execute the instruction again.
304
305; We got trouble.
306
307WHOOPS:
308
309; Convert jump address back to opcode in al
310
311 MOV AX,BX ; Put the jump table index back into AX
312 LEA BX,TABLE ; Subtract the offset of the base of the
313 SUB AX,BX ; jump table
314 DIV VALUE3 ; Divide AX by 3 to get the opcode back.
315 JMP DISPLAY ; Go to the error routine in INDEEXC
316
317PAGE
318;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
319;; Emulate the LOCK instruction. This is an instruction we really don't want ;;
320;; to emulate so we will set the IOPL to 3 so that further LOCKs won't bother ;;
321;; us. If the exception code is for "invalid op-code" then we will just jump ;;
322;; to the routine above to set the IOPL to 3. Otherwise we will just step IP ;;
323;; past the LOCK instruction thus treating it as a NOP. ;;
324;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
325
326LOCK_INST: ; @P1C
327 CMP SS:WORD PTR [BP+BP_EX],0006H ; Check if it's an invalid op code
328 JNE TRYIOPL3 ; Try setting the IOPL to 3
329 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the instruction
330 JMP POPIO ; thus treating it as a NOP
331
332PAGE
333;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
334;; Emulate the STI, enable interupts, instruction. This is pretty simple to ;;
335;; do. Just get the V86 task's flags and flip on the enable interrupts bit. ;;
336;; And while we're at it we'll set the IOPL to 3. ;;
337;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
338
339STI_INST: ; @P1C
340 OR WORD PTR SS:[BP+BP_FL],3200H ; Set the enable interrupts bit
341 ; and set IOPL to 3
342 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past STI instruction
343 JMP POPIO
344
345PAGE
346;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
347;; Emulate the CLI, disable interrupts, instruction. Just as in STI above, ;;
348;; all that is needed is to turn of the enable interrups bit. And again, set ;;
349;; the IOPL to 3 so that we won't get control again. ;;
350;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
351
352CLI_INST: ; @P1C
353 AND WORD PTR SS:[BP+BP_FL],3DFFH ; Set interrupts disabled
354 OR WORD PTR SS:[BP+BP_FL],3000H ; Insure IOPL = 3 for speed
355 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past instruction
356 JMP POPIO
357
358PAGE
359;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
360;; Emulate the INT3 instruction. To do this we put a 3 in the exception code ;;
361;; and jump to the portion of the code that emulates the INT instruction. ;;
362;; That code uses the exception code to get the interrupt vector from real ;;
363;; memory and gives control to the V86 task at the interrupt address. ;;
364;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
365
366INT3:
367 MOV WORD PTR SS:[BP+BP_EX],3 ; Put a 3 in the exception field
368 ; This will cause the INTCOM
369 ; section to go to interrupt 3
370 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past INT3 inscruction
371 JMP INTCOM ; Go get the vector from real
372 ; memory
373
374PAGE
375;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
376;; Emulate the INTO instruction. This is done just like the INT3 above. It ;;
377;; puts a 4 in the exception code and jumps to the code in the INT emulator ;;
378;; that will get the real address of the interrupt. ;;
379;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
380
381INTO_INST: ; @P1C
382 MOV WORD PTR SS:[BP+BP_EX],4 ; Put a 4 in the exception field
383 ; This will cause the INTCOM
384 ; section to go to interrupt 4
385 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past INTO inscruction
386 JMP INTCOM ; Go get the vector from real
387 ; memory
388
389PAGE
390;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
391;; Emulate the IRET instruction. Get CS, IP and the flags off of the V86 ;;
392;; task's stack and place them in the register values on our stack. When we ;;
393;; return control to the V86 task these values will be taken off of our stack ;;
394;; and placed in the V86 task's registers. ;;
395;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
396
397IRET_INST: ; @P1C
398 DATAOV ; Get the user's ESP (32 bit SP)
399 MOV AX,SS:[BP+BP_SP] ; and save it in ESI.
400 DATAOV
401 MOV SI,AX
402 ADD AX,6 ; Add 6 to the user's SP. This
403 MOV SS:WORD PTR [BP+BP_SP],AX ; skips over the IP, CS and
404 ; flags on the user's stack.
405 ; This puts SP where it would be
406 ; after the IRET. It assumes
407 ; there are at least six bytes
408 ; on the stack.
409 DATAOV
410 MOV AX,SS:[BP+BP_SS] ; Get the user's SS and multiply
411 DATAOV ; by 16. This converts the
412 SHL AX,4 ; segment value to an offset.
413 DATAOV ; Add this on to the ESP value in
414 ADD SI,AX ; ESI and now ESI is the offset
415 ; from 0 of the user's stack.
416 ADDROV
417 LODSW ; Get the user's EIP into EAX
418
419 ADDROV ; Intel bug # A0-119
420 NOP ; Intel bug # A0-119
421
422 MOV WORD PTR SS:[BP+BP_IP],AX ; Put IP into the register values
423 ; on our stack
424 ADDROV
425 LODSW ; Get the user's CS into EAX
426
427 ADDROV ; Intel bug # A0-119
428 NOP ; Intel bug # A0-119
429
430 MOV WORD PTR SS:[BP+BP_CS],AX ; Put CS into the register values
431 ; on our stack
432 ADDROV
433 LODSW ; Get the user's flags (32 bits)
434
435 ADDROV ; Intel bug # A0-119
436 NOP ; Intel bug # A0-119
437
438 AND AX,3FFFH ; Clean up the flags
439 OR AX,3000H ; Set IOPL to 3
440 MOV WORD PTR SS:[BP+BP_FL],AX ; Put the flags into the register
441 ; values on our stack
442 JMP POPREGS ; Go return to the V86 task @P1C
443
444;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
445;; Emulate the INT instruction. Step the V86 task's CS and IP past the INT ;;
446;; instruction. Push the flags, CS and IP in the task's stack. Get the ;;
447;; interrupt number and use it to find the appropriate interrupt vector in ;;
448;; low memory. Set the task's CS and IP to the interrupt vector and return ;;
449;; control to the task. ;;
450;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
451
452INT_INST: ; @P1C
453
454; Get the interrupt number from the instruction. It is the second byte of the
455; instruction. DS:SI was used to get the op-code. Now DS:SI points to the next
456; byte of the instruction. All we have to do is get it.
457
458 ADDROV
459 LODSB ; Get the interrupt number
460
461 ADDROV ; Intel bug # A0-119
462 NOP ; Intel bug # A0-119
463
464 MOV AH,0 ; Clear the high byte
465 MOV WORD PTR SS:[BP+BP_EX],AX ; Save the interrupt number in
466 ; the exception code field
467
468; Step IP past the INT instruction.
469
470 ADD WORD PTR SS:[BP+BP_IP],2 ; STEP IP PAST INT INSTRUCTION
471
472; Check for INT 15. This is handled by INDEI15.
473
474INTCONT:
475 CMP AL,15H ; Is it interrupt 15?
476 JNE INTCOM ; If not, continue
477 JMP INT15 ; Else go to INDEI15
478
479; Now use the interrupt number to get the appropriate interrupt vector from
480; low core.
481
482INTCOM:
483 MOV AX,HUGE_PTR ; Load ES with the selector that
484 MOV ES,AX ; accesses all of memory as data
485 DATAOV
486 MOV DI,SS:[BP+BP_SP] ; Load EDI with the user's ESP
487 ; Now ES:EDI points to the user's
488 ; stack
489 SUB DI,6 ; Decrement "SP" to make space for
490 ; the flags, CS snd IP
491 MOV SS:WORD PTR [BP+BP_SP],DI ; Set the user's new SP
492
493 DATAOV
494 MOV AX,SS:[BP+BP_SS] ; Get the user's SS and shift it
495 DATAOV ; left four bits to convert it
496 SHL AX,4 ; to an offset
497 DATAOV ; Add it to EDI so that EDI now
498 ADD DI,AX ; contains the physical offset
499 ; of the user's stack
500
501; Now put the flags, CS and IP on the V86 task's stack. They are put on in the
502; order IP, CS, flags. This is backwards from the INT push order of flags, CS
503; and then IP. This is because we are moving forward through memory (CLD)
504; whereas the stack grows backwards through memory as things apushed on to it.
505
506 MOV AX,SS:[BP+BP_IP]
507 ADDROV
508 STOSW ; Put IP on the V86 task's stack
509 ADDROV ; Intel bug # A0-119
510 NOP ; Intel bug # A0-119
511
512 MOV AX,SS:[BP+BP_CS]
513 ADDROV
514 STOSW ; Put CS on the V86 task's stack
515 ADDROV ; Intel bug # A0-119
516 NOP ; Intel bug # A0-119
517
518 MOV AX,SS:[BP+BP_FL] ; Get the v86 task's flags
519 OR AX,3000H ; Set IPOL to 3 while we're here
520 ADDROV
521 STOSW ; Put the flags on the v86 task's
522 ; stack
523
524 ADDROV ; INTEL BUG # A0-119
525 NOP ; INTEL BUG # A0-119
526 AND AX,3CFFH ; Clean up flags for our IRET
527 MOV WORD PTR SS:[BP+BP_FL],AX
528
529; Use the interrupt number to get the CS and IP of the interrupt routine
530
531 MOV SI,SS:[BP+BP_EX] ; Get the interrupt number
532 SHL SI,2 ; Multiply by 4 since interrupt
533 ; vectors are 4 bytes long
534 LODSW ; Get the IP for the vector
535 MOV WORD PTR SS:[BP+BP_IP],AX ; Put it in the V86 task's IP
536 LODSW ; Get the CS for the vector
537 MOV WORD PTR SS:[BP+BP_CS],AX ; Put it in the V86 task's CS
538
539 JMP POPREGS ; Go return to the V86 task @P1C
540
541;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
542;; Emulate the PUSHF instruction. Get the V86 task's flags and put them on ;;
543;; its stack. ;;
544;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
545
546PUSHF_INST: ; @P1C
547 MOV AX,HUGE_PTR ; Load ES with the selector that
548 MOV ES,AX ; accesses all of memory as data
549
550 DATAOV
551 MOV DI,SS:[BP+BP_SP] ; Load EDI with the V86 task's SP
552 SUB DI,2 ; Decrement "SP" by one word to
553 ; make room for the flags
554 MOV SS:WORD PTR [BP+BP_SP],DI ; Store the new V86 task's SP
555 DATAOV
556 MOV AX,SS:[BP+BP_SS] ; Get the user's SS and shift it
557 DATAOV ; left four bits to convert it
558 SHL AX,4 ; to an offset
559 DATAOV ; Add it to EDI so that EDI now
560 ADD DI,AX ; contains the physical offset
561 ; of the user's stack
562 MOV AX,SS:[BP+BP_FL] ; Get the v86 task's flags
563 OR AX,3000H ; Set IPOL to 3 so that we won't
564 ADDROV ; be bothered anymore
565 STOSW ; Put the flags on the stack
566 ADDROV ; Intel bug # A0-119
567 NOP ; Intel bug # A0-119
568
569 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past PUSHF instruction
570
571 JMP POPIO ; Go return to the V86 task
572
573;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
574;; Emulate the POPF instruction. Get the next word off of the V86 task's ;;
575;; stack, set IOPL to 3 and put it in the V86 task's flags. ;;
576;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
577
578POPF_INST: ; @P1C
579 MOV AX,HUGE_PTR ; Big segment selector
580 MOV DS,AX ; Stack seg
581 DATAOV ; Create 32-bit operand prefix for next instruction
582 MOV AX,SS:[BP+BP_SP] ; stack ptr
583 DATAOV ; Create 32-bit operand prefix for next instruction
584 MOV SI,AX ; SI = stack ptr
585 ADD AX,2
586 MOV SS:WORD PTR [BP+BP_SP],AX ; NEW STACK POINTER
587 DATAOV ; Create 32-bit operand prefix for next instruction
588 MOV AX,SS:[BP+BP_SS] ; Convert ss to 20 bit address
589 DATAOV ; Create 32-bit operand prefix for next instruction
590 SHL AX,4
591 DATAOV ; Create 32-bit operand prefix for next instruction
592 ADD SI,AX ; Now have 32-bit offset from 0
593 ADDROV ; Use 32-bit offset
594 LODSW ; GET REAL MODE FLAGS
595 ADDROV ; INTEL BUG # A0-119
596 NOP ; INTEL BUG # A0-119
597 AND AX,0FFFH ; CLEAN UP FLAGS FOR OUR IRET
598; A POPF at level 3 will not change IOPL - WE WANT TO KEEP IT AT IOPL = 3
599 OR AX,3000H ; SET IOPL = 3
600 MOV WORD PTR SS:[BP+BP_FL],AX
601 ADD WORD PTR SS:[BP+BP_IP],1 ; STEP IP PAST INSTRUCTION
602 JMP POPIO ; CHECK FOR SINGLE STEP
603
604;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
605;; The following entry point, POPIO, is the exit routine for situations when ;;
606;; a single step condition would be lost by a normal IRET to the V86 task. ;;
607;; You see, in real mode the single step interrupt gets control whenever the ;;
608;; single step flag is on. However, we just got control and emulated the ;;
609;; instruction. If we just return to the V86 task at CS:IP then the step ;;
610;; between the instruction we just emulated and the next instruction will be ;;
611;; missed by the single step routine. Therefore we check the V86 task's flags;;
612;; to see if the single step flag is on. If so, then we give control to the ;;
613;; singel step interrupt. Otherwise we just IRET to the V86 task's CS:IP. ;;
614;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
615
616POPIO:
617 CMP SS:WORD PTR [BP+BP_EX],1 ; First check if the reason we got
618 ; control was because of a
619 ; single step
620 JE POPCONT ; If so, then we don't have to
621 ; give control to the single
622 ; step routine 'cause we already
623 ; did it.
624 TEST WORD PTR SS:[BP+BP_FL],0100H ; Was the single step flag on?
625 JZ POPCONT ; If not then just IRET
626 MOV SS:WORD PTR [BP+BP_EX],1 ; Otherwise put a 1 (single step
627 JMP INTCOM ; interrupt number) in the
628 ; exception code and go to
629 ; INTCOM to give control to the
630 ; interrupt
631POPCONT:
632
633; Restore the registers. On entry, in INDEEXC, the registers were pushed as:
634; DS, all registers, ES.
635
636 POP ES ; Restore ES
637 DATAOV
638 POPA ; Restore all the registers (32 bits wide)
639 POP DS ; Restore DS
640 ADD SP,(BP_IP-BP_EX); Move SP past the exception ID an error code
641 ; that were put on our stack when the 386
642 ; gave us control for the exception.
643 ; SS:SP now points to the V86's IP, CS, flags
644 ; for the IRET
645 DATAOV ; IP, CS, and flags are saved 32 bits wide
646 IRET ; Give control back to the V86 task
647
648EMULATE ENDP
649
650PROG ENDS
651
652 END
653