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