2 TITLE INDEEMU
- 386 XMA EMULATOR
- Sensitive Instruction Emulator
5 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7 * MODULE NAME
: INDEEMU
*
10 * 5669-196 (C
) COPYRIGHT
1988 Microsoft Corporation
*
12 * DESCRIPTIVE NAME
: 386 XMA emulator
- emulate sensitive instructions
*
14 * STATUS
(LEVEL
) : VERSION
(0) LEVEL
(1.0) *
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
. *
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
. *
38 * REGISTER USAGE
: 80386 Standard
*
40 * RESTRICTIONS
: None
*
42 * DEPENDENCIES
: None
*
44 * ENTRY POINT
: EMULATE
*
46 * LINKAGE
: Jumped to by INDEEXC
*
48 * INPUT PARMS
: None
*
50 * RETURN PARMS
: None
*
52 * OTHER EFFECTS
: None
*
54 * EXIT NORMAL
: IRET to the V86 task
*
56 * EXIT ERROR
: Jump to error routine
in INDEEXC
*
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
*
83 * SUB-ROUTINES
: None
*
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
*
91 * CONTROL BLOCKS
: INDEDAT
.INC *
95 * $
MOD(INDEEMU
) COMP
(LOAD) PROD
(3270PC
) : *
97 * $D0=D0004700
410 870523 D
: NEW FOR RELEASE
1.1 *
98 * $P1
=P0000312
410 870804 D
: CLEAN UP WARNING MESSAGES
*
100 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
103 .286P
; Enable recognition of 286 privileged instructs.
105 .XLIST
; Turn off the listing
108 IF1
; Only include macros on the first pass
112 .LIST
; Turn on the listing
116 PROG
SEGMENT PARA
PUBLIC 'PROG'
125 ; The following entries are in other modules
127 EXTRN XMAIN
:NEAR ; Byte IN from port # in DX
128 IN_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
133 OUT_INST EQU XMAOUT
; @P1C
134 EXTRN OUTW
:NEAR ; Word OUT to port # in DX
135 EXTRN XMAOUTIMMED
:NEAR ; Byte OUT to immediate port #
136 OUTIMMED 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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
156 REPT 256 ; Initialize the table so that all instructions
157 JMP UNEXPECTED
; jump to UNEXPECTED
161 ; Now set up the entries for the instructions we want to emulate
164 ORG TABLE
+(0FBH*3) ; 0FBH is the op-code for STI.
166 ORG TABLE
+(0FAH*3) ; 0FAH is the op-code for CLI.
168 ORG TABLE
+(0F0H*3) ; 0F0H is the op-code for LOCK.
170 ORG TABLE
+(0EFH*3) ; 0EFH is the op-code for OUT for a word.
172 ORG TABLE
+(0EEH*3) ; 0EEH is the op-code for OUT for a byte.
174 ORG TABLE
+(0EDH*3) ; 0EDH is the op-code for IN for a word.
176 ORG TABLE
+(0ECH*3) ; 0ECH is the op-code for IN for a byte.
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.
188 ORG TABLE
+(0CEH*3) ; 0CEH is the op-code for INTO.
190 ORG TABLE
+(0CDH*3) ; 0CDH is the op-code for INT.
192 ORG TABLE
+(0CCH*3) ; 0CCH is the op-code for INT3.
194 ORG TABLE
+(09DH*3) ; 09DH is the op-code for POPF.
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
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. ;;
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
224 MOV AX,HUGE_PTR
; Load DS with a selector that will
225 MOV DS,AX ; access all of memory as data
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
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
237 ADD SI,AX ; Add on IP. Now SI contains the offset
238 ; from 0 of the instruction that
241 LODSB ; Get the op-code into AL
243 ADDROV
; Intel bug # A0-119
244 NOP ; Intel bug # A0-119
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
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.
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. ;;
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 ;;
274 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
282 MOV AX,8000H
; Set the paging enabled bit
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
287 CMOV
CR0,EAX ; Reset CR0
288 JMP POPREGS
; Return to the V86 task @P1C
290 ; Try setting the IOPL to 3
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
297 JE WHOOPS
; If we're already at IOPL 3 the some-
298 ; things fishy. Time to signal an
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.
309 ; Convert jump address back to opcode in al
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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
340 OR WORD PTR SS:[BP+BP_FL
],3200H
; Set the enable interrupts bit
342 ADD WORD PTR SS:[BP+BP_IP
],1 ; Step IP past STI instruction
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
398 DATAOV
; Get the user's ESP (32 bit SP)
399 MOV AX,SS:[BP+BP_SP
] ; and save it in ESI.
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
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.
417 LODSW ; Get the user's EIP into EAX
419 ADDROV
; Intel bug # A0-119
420 NOP ; Intel bug # A0-119
422 MOV WORD PTR SS:[BP+BP_IP
],AX ; Put IP into the register values
425 LODSW ; Get the user's CS into EAX
427 ADDROV
; Intel bug # A0-119
428 NOP ; Intel bug # A0-119
430 MOV WORD PTR SS:[BP+BP_CS
],AX ; Put CS into the register values
433 LODSW ; Get the user's flags (32 bits)
435 ADDROV
; Intel bug # A0-119
436 NOP ; Intel bug # A0-119
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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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.
459 LODSB ; Get the interrupt number
461 ADDROV
; Intel bug # A0-119
462 NOP ; Intel bug # A0-119
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
468 ; Step IP past the INT instruction.
470 ADD WORD PTR SS:[BP+BP_IP
],2 ; STEP IP PAST INT INSTRUCTION
472 ; Check for INT 15. This is handled by INDEI15.
475 CMP AL,15H
; Is it interrupt 15?
476 JNE INTCOM
; If not, continue
477 JMP INT15
; Else go to INDEI15
479 ; Now use the interrupt number to get the appropriate interrupt vector from
483 MOV AX,HUGE_PTR
; Load ES with the selector that
484 MOV ES,AX ; accesses all of memory as data
486 MOV DI,SS:[BP+BP_SP
] ; Load EDI with the user's ESP
487 ; Now ES:EDI points to the user's
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
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
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.
508 STOSW ; Put IP on the V86 task's stack
509 ADDROV
; Intel bug # A0-119
510 NOP ; Intel bug # A0-119
514 STOSW ; Put CS on the V86 task's stack
515 ADDROV
; Intel bug # A0-119
516 NOP ; Intel bug # A0-119
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
521 STOSW ; Put the flags on the v86 task's
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
529 ; Use the interrupt number to get the CS and IP of the interrupt routine
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
539 JMP POPREGS
; Go return to the V86 task @P1C
541 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
542 ;; Emulate the PUSHF instruction. Get the V86 task's flags and put them on ;;
544 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
547 MOV AX,HUGE_PTR
; Load ES with the selector that
548 MOV ES,AX ; accesses all of memory as data
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
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
569 ADD WORD PTR SS:[BP+BP_IP
],1 ; Step IP past PUSHF instruction
571 JMP POPIO
; Go return to the V86 task
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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
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
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
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
617 CMP SS:WORD PTR [BP+BP_EX
],1 ; First check if the reason we got
618 ; control was because of a
620 JE POPCONT
; If so, then we don't have to
621 ; give control to the single
622 ; step routine 'cause we already
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
633 ; Restore the registers. On entry, in INDEEXC, the registers were pushed as:
634 ; DS, all registers, ES.
638 POPA ; Restore all the registers (32 bits wide)
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
645 DATAOV
; IP, CS, and flags are saved 32 bits wide
646 IRET ; Give control back to the V86 task