diff options
| author | 2024-04-25 21:24:10 +0100 | |
|---|---|---|
| committer | 2024-04-25 22:32:27 +0000 | |
| commit | 2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch) | |
| tree | 80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/DEV/XMAEM/INDEEMU.ASM | |
| parent | Merge pull request #430 from jpbaltazar/typoptbr (diff) | |
| download | ms-dos-main.tar.gz ms-dos-main.tar.xz ms-dos-main.zip | |
Diffstat (limited to 'v4.0/src/DEV/XMAEM/INDEEMU.ASM')
| -rw-r--r-- | v4.0/src/DEV/XMAEM/INDEEMU.ASM | 653 |
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 @@ | |||
| 1 | PAGE 60,132 | ||
| 2 | TITLE INDEEMU - 386 XMA EMULATOR - Sensitive Instruction Emulator | ||
| 3 | |||
| 4 | COMMENT # | ||
| 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 | |||
| 116 | PROG SEGMENT PARA PUBLIC 'PROG' | ||
| 117 | |||
| 118 | ASSUME CS:PROG | ||
| 119 | ASSUME DS:NOTHING | ||
| 120 | ASSUME ES:NOTHING | ||
| 121 | ASSUME SS:NOTHING | ||
| 122 | |||
| 123 | INDEEMU LABEL NEAR | ||
| 124 | |||
| 125 | ; The following entries are in other modules | ||
| 126 | |||
| 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 | ||
| 142 | |||
| 143 | PAGE | ||
| 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 | |||
| 154 | TABLE: | ||
| 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 | |||
| 163 | TABLE_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 | |||
| 203 | VALUE3 DB 3 | ||
| 204 | |||
| 205 | PUBLIC EMULATE | ||
| 206 | PUBLIC POPIO | ||
| 207 | PUBLIC INTCOM | ||
| 208 | |||
| 209 | EMULATE PROC NEAR | ||
| 210 | |||
| 211 | CLD | ||
| 212 | |||
| 213 | PAGE | ||
| 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 | |||
| 254 | PAGE | ||
| 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 | |||
| 276 | UNEXPECTED: | ||
| 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 | |||
| 292 | TRYIOPL3: | ||
| 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 | |||
| 307 | WHOOPS: | ||
| 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 | |||
| 317 | PAGE | ||
| 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 | |||
| 326 | LOCK_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 | |||
| 332 | PAGE | ||
| 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 | |||
| 339 | STI_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 | |||
| 345 | PAGE | ||
| 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 | |||
| 352 | CLI_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 | |||
| 358 | PAGE | ||
| 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 | |||
| 366 | INT3: | ||
| 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 | |||
| 374 | PAGE | ||
| 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 | |||
| 381 | INTO_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 | |||
| 389 | PAGE | ||
| 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 | |||
| 397 | IRET_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 | |||
| 452 | INT_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 | |||
| 474 | INTCONT: | ||
| 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 | |||
| 482 | INTCOM: | ||
| 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 | |||
| 546 | PUSHF_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 | |||
| 578 | POPF_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 | |||
| 616 | POPIO: | ||
| 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 | ||
| 631 | POPCONT: | ||
| 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 | |||
| 648 | EMULATE ENDP | ||
| 649 | |||
| 650 | PROG ENDS | ||
| 651 | |||
| 652 | END | ||
| 653 | |||