diff options
Diffstat (limited to 'v4.0/src/DEV/XMAEM/INDEEXC.ASM')
| -rw-r--r-- | v4.0/src/DEV/XMAEM/INDEEXC.ASM | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/v4.0/src/DEV/XMAEM/INDEEXC.ASM b/v4.0/src/DEV/XMAEM/INDEEXC.ASM new file mode 100644 index 0000000..a8faaa5 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEEXC.ASM | |||
| @@ -0,0 +1,799 @@ | |||
| 1 | PAGE 60,132 | ||
| 2 | TITLE INDEEXC - 386 XMA EMULATOR - System Exception Handler | ||
| 3 | |||
| 4 | COMMENT # | ||
| 5 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
| 6 | * * | ||
| 7 | * MODULE NAME : INDEEXC * | ||
| 8 | * * | ||
| 9 | * * | ||
| 10 | * 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * | ||
| 11 | * * | ||
| 12 | * DESCRIPTIVE NAME: 80386 XMA Emulator System Exception Handler * | ||
| 13 | * * | ||
| 14 | * STATUS (LEVEL) : VERSION (0) LEVEL (1.0) * | ||
| 15 | * * | ||
| 16 | * FUNCTION : This module gets control whenever an interrupt 00 - 07, * | ||
| 17 | * 09 - 0E or 15 occurs. This is because this module's * | ||
| 18 | * entry point was placed in the IDT entries for these * | ||
| 19 | * interrupts. It determines what course of action to take * | ||
| 20 | * on the interrupt/exception. * | ||
| 21 | * * | ||
| 22 | * First thing it does is is to check to see who caused the * | ||
| 23 | * exception. If the exception came from the virtual 8086 * | ||
| 24 | * (V86) task then it will try to emulate the interrupt if * | ||
| 25 | * necessary. If the exception came from the emulator it- * | ||
| 26 | * self then we may have problems. If it was a general * | ||
| 27 | * protection exception (INT 0D) then it just ignores it and * | ||
| 28 | * passes control back to the V86 task. If it was a page * | ||
| 29 | * fault (INT 14) then it assumes that whatever is running in* | ||
| 30 | * the V86 task came up with a bad address so it terminates * | ||
| 31 | * the application since it is obviously bad. If it is * | ||
| 32 | * neither of these two errors then something has gone bad * | ||
| 33 | * in the emulator. When this happens it signals the error * | ||
| 34 | * handler to prompt the user to take a dump or reIPL the * | ||
| 35 | * system. * | ||
| 36 | * * | ||
| 37 | * The old error routine used to display a panel with the * | ||
| 38 | * contents of the registers and the stack. The new error * | ||
| 39 | * routine just forces the V86 task to run the NMI code. * | ||
| 40 | * The old routine was left in place for debugging purposes. * | ||
| 41 | * * | ||
| 42 | * MODULE TYPE : ASM * | ||
| 43 | * * | ||
| 44 | * REGISTER USAGE : 80386 Standard * | ||
| 45 | * * | ||
| 46 | * RESTRICTIONS : None * | ||
| 47 | * * | ||
| 48 | * DEPENDENCIES : None * | ||
| 49 | * * | ||
| 50 | * ENTRY POINT : VEXCPT13 * | ||
| 51 | * * | ||
| 52 | * LINKAGE : This entry point is placed in the IDT for each interrupt * | ||
| 53 | * we want to handle. Whenever one of those interrupts is * | ||
| 54 | * execupted, control comes here. * | ||
| 55 | * * | ||
| 56 | * INPUT PARMS : None * | ||
| 57 | * * | ||
| 58 | * RETURN PARMS : None * | ||
| 59 | * * | ||
| 60 | * OTHER EFFECTS : None * | ||
| 61 | * * | ||
| 62 | * EXIT NORMAL : IRET to the virtual 8086 task * | ||
| 63 | * * | ||
| 64 | * EXIT ERROR : Force the V86 task to execute an NMI * | ||
| 65 | * * | ||
| 66 | * EXTERNAL * | ||
| 67 | * REFERENCES : EMULATE - Entry point for INDEEMU * | ||
| 68 | * INT15 - Entry point for INDEI15 * | ||
| 69 | * * | ||
| 70 | * SUB-ROUTINES : HEXD - Display the double word in EAX * | ||
| 71 | * HEXW - Display the word in AX * | ||
| 72 | * HEXB - Display the byte in AL * | ||
| 73 | * * | ||
| 74 | * MACROS : DATAOV - Create a prefix for the following instruction * | ||
| 75 | * so that it accesses data 32 bits wide * | ||
| 76 | * ADDROV - Create a prefix for the following instruction * | ||
| 77 | * so that it uses addresses that are 32 bits wide * | ||
| 78 | * * | ||
| 79 | * CONTROL BLOCKS : INDEDAT.INC * | ||
| 80 | * * | ||
| 81 | * CHANGE ACTIVITY : * | ||
| 82 | * * | ||
| 83 | * $MOD(INDEEXC) COMP(LOAD) PROD(3270PC) : * | ||
| 84 | * * | ||
| 85 | * $D0=D0004700 410 870523 D : NEW FOR RELEASE 1.1 * | ||
| 86 | * $P1=P0000312 410 870804 D : CLEAN UP WARNING MESSAGES * | ||
| 87 | * * | ||
| 88 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
| 89 | # | ||
| 90 | |||
| 91 | .286P ; Enable recognition of 286 privileged instructs. | ||
| 92 | |||
| 93 | .XLIST ; Turn off the listing | ||
| 94 | INCLUDE INDEDAT.INC ; Include system data | ||
| 95 | |||
| 96 | IF1 ; Only include macros on the first pass of | ||
| 97 | INCLUDE INDEOVP.MAC ; of the assembler | ||
| 98 | ENDIF | ||
| 99 | .LIST ; Turn on the listing | ||
| 100 | |||
| 101 | SEX_ATTR EQU 04B00H | ||
| 102 | STACK_ATTR EQU 00700H | ||
| 103 | BLANK EQU 00020H | ||
| 104 | BP_START EQU 0 | ||
| 105 | |||
| 106 | PUBLIC INDEEXC | ||
| 107 | |||
| 108 | PROG SEGMENT PARA PUBLIC 'PROG' | ||
| 109 | |||
| 110 | ASSUME CS:PROG | ||
| 111 | ASSUME DS:NOTHING | ||
| 112 | ASSUME ES:NOTHING | ||
| 113 | ASSUME SS:NOTHING | ||
| 114 | |||
| 115 | INDEEXC LABEL NEAR | ||
| 116 | |||
| 117 | ; External entry points | ||
| 118 | |||
| 119 | EXTRN EMULATE:NEAR ; Entry point to INDEEMU | ||
| 120 | EXTRN INT15:NEAR ; Entry point to INDEI15 | ||
| 121 | |||
| 122 | ; External variables | ||
| 123 | |||
| 124 | EXTRN CRT_SELECTOR:WORD ; Selector for the display buffer (INDEI15) | ||
| 125 | EXTRN XMATID:BYTE ; Current bank ID (INDEXMA) | ||
| 126 | |||
| 127 | PAGE | ||
| 128 | |||
| 129 | VEXCPT13 LABEL NEAR | ||
| 130 | |||
| 131 | PUBLIC SEX | ||
| 132 | PUBLIC POPREGS ; P1C | ||
| 133 | PUBLIC HEXD | ||
| 134 | PUBLIC HEXW | ||
| 135 | PUBLIC HEXB | ||
| 136 | PUBLIC VEXCPT13 | ||
| 137 | PUBLIC DISPLAY | ||
| 138 | |||
| 139 | SEX PROC NEAR | ||
| 140 | |||
| 141 | CLD ; All moves go forward | ||
| 142 | |||
| 143 | ; Save the registers on the stack. These are the registers of the task that | ||
| 144 | ; got interrupted. | ||
| 145 | |||
| 146 | SAVE_REGS: | ||
| 147 | PUSH DS ; Save DS | ||
| 148 | |||
| 149 | DATAOV ; Save all the registers (32 bits wide). They are | ||
| 150 | PUSHA ; pushed in the order: AX, CX, DX, BX, original | ||
| 151 | ; SP (before the PUSHA), BP, SI, DI. | ||
| 152 | |||
| 153 | PUSH ES ; Save ES | ||
| 154 | MOV BP,SP ; Point BP to the start of the register save area | ||
| 155 | |||
| 156 | PAGE | ||
| 157 | ; First let's check to see who caused the exception, the V86 task or us. This | ||
| 158 | ; is done by checking the flags of the routine that was interrupted. The VM | ||
| 159 | ; flag is set for every routine that is running in V86 mode. There are really | ||
| 160 | ; only two entities in the system, the emulator and the V86 task. The V86 task | ||
| 161 | ; has the VM bit set when it is running, the emulator does not. So we can read | ||
| 162 | ; this bit to determine who was interrupted. | ||
| 163 | |||
| 164 | MOV AX,SS:WORD PTR [BP+BP_FL2] ; Get hi-order word of the flags | ||
| 165 | TEST AL,02H ; Check the VM bit | ||
| 166 | JZ DISPLAY ; Uh oh! It's us. | ||
| 167 | JMP LONGWAY ; It's the V86 task | ||
| 168 | |||
| 169 | PAGE | ||
| 170 | ; The following entry point, DISPLAY, is know to other modules. They jump here | ||
| 171 | ; when they encounter a severe error and want to call the error handler. | ||
| 172 | |||
| 173 | DISPLAY: | ||
| 174 | ; JMP DODISP ; Just display the registers. | ||
| 175 | ; Comment out for final product. | ||
| 176 | |||
| 177 | ; Check if it was a general protection exception. If so, then we'll just pass | ||
| 178 | ; control back to the V86 task and let it worry about it. | ||
| 179 | |||
| 180 | MOV BP,SP ; Point BP to the saved registers | ||
| 181 | CMP SS:WORD PTR [BP+BP_EX],0DH ; Was it a general protection | ||
| 182 | ; exception | ||
| 183 | JNE DISPCONT ; If not, the continue | ||
| 184 | JMP POPREGS ; Else just return to the V86 @P1C | ||
| 185 | ; task | ||
| 186 | |||
| 187 | ; Check if it was a page fault. Page faults only occur when the page that is | ||
| 188 | ; addressed is marked not present. When the emulator sets up memory it marks | ||
| 189 | ; all pages as present. And this is true because the emulator does no page | ||
| 190 | ; swapping. It messes with the page tables but it doesn't remove pages from | ||
| 191 | ; memory. Therefore, if the page is not present then whatever is running in | ||
| 192 | ; the V86 task came up with some wierd non-existant address. This guy obviously | ||
| 193 | ; has gone west or doesn't know what he's doing. So we forcr the application | ||
| 194 | ; to be terminated. | ||
| 195 | |||
| 196 | DISPCONT: | ||
| 197 | CMP SS:WORD PTR [BP+BP_EX],0EH ; Was it a page fault? | ||
| 198 | JNE CHKVM ; Nope. Continue checking. | ||
| 199 | JMP PAGE_FAULT ; Yup. Assume application had bad | ||
| 200 | ; addresses. Therefore, termin- | ||
| 201 | ; ate the application. | ||
| 202 | |||
| 203 | ; Lastly we'll check who had the error, them or us. We need to check again | ||
| 204 | ; because anybody can jump to the DISPLAY label above. If the V86 task had | ||
| 205 | ; the exception then we will just terminate whatever was running in the V86 | ||
| 206 | ; task. If the emulator had the exception, then obviously WE don't know | ||
| 207 | ; what WE'RE doing. Oops! In this case we will force an NMI and bring down | ||
| 208 | ; the whole system. If the emulator isn't healthy then nothing else should | ||
| 209 | ; run either. It may sould selfish, but if the emulator is damaged then the | ||
| 210 | ; rest of the system will die soon anyway. | ||
| 211 | |||
| 212 | CHKVM: | ||
| 213 | MOV AX,SS:WORD PTR [BP+BP_FL2] ; Check the VM bit in the flags | ||
| 214 | TEST AL,02H ; to see who was running | ||
| 215 | JZ ERROR ; It was us. Better Force an NMI. | ||
| 216 | JMP TERM_APP ; It was them. Terminate whatever | ||
| 217 | ; is running. | ||
| 218 | |||
| 219 | PAGE | ||
| 220 | ERROR: | ||
| 221 | |||
| 222 | ;----------------------------------------------------------------------------D1A | ||
| 223 | ; We're in big trouble now. Something has gone west and there's no way to D1A | ||
| 224 | ; get back home. At this point we give up and send up a flare. We signal D1A | ||
| 225 | ; the error handler by forcing the V86 task to execute the NMI interrupt. D1A | ||
| 226 | ; We put a marker of 0DEADH at the fixed location 0:4F2. This is in the D1A | ||
| 227 | ; BIOS communication area. The error handler will look here for our marker D1A | ||
| 228 | ; to determine if the NMI came from the emulator. If it finds it, it will D1A | ||
| 229 | ; put up a severe error with our return code and ask the user to take a dump D1A | ||
| 230 | ; or reIPL. The code following this new code is old code to display a debug D1A | ||
| 231 | ; panel with the contents of the registers and stack. It is left here for D1A | ||
| 232 | ; debugging but will not be executed when the new code is in place. D1A | ||
| 233 | ;----------------------------------------------------------------------------D1A | ||
| 234 | ; D1A | ||
| 235 | MOV AX,HUGE_PTR ; Load ES with a selector that @D1A | ||
| 236 | MOV ES,AX ; accesses all of memory as data @D1A | ||
| 237 | ; D1A | ||
| 238 | MOV DI,4F2H ; Put our 0DEADH marker at the @D1A | ||
| 239 | MOV WORD PTR ES:[DI],0DEADH ; fixed location 0:4F2. D1A | ||
| 240 | MOV WORD PTR SS:[BP+BP_EX],2; Put a 2, the NMI interrupt number,@D1A | ||
| 241 | ; in the exception field. The D1A | ||
| 242 | ; code after LONGWAY4 will use D1A | ||
| 243 | ; this number to get the interrupt D1A | ||
| 244 | ; vector. D1A | ||
| 245 | JMP LONGWAY4 ; Go do the NMI @D1A | ||
| 246 | |||
| 247 | PAGE | ||
| 248 | ; The following code will not be executed. It is left as a debugging tool. | ||
| 249 | |||
| 250 | DODISP: | ||
| 251 | ; Blank the display screen | ||
| 252 | MOV DI,CRT_SELECTOR ; Load ES with the selector for the | ||
| 253 | MOV ES,DI ; display buffer | ||
| 254 | XOR DI,DI ; DI points to the start of the buffer | ||
| 255 | MOV CX,80*15 ; Only clear 15 lines | ||
| 256 | MOV AX,STACK_ATTR+BLANK ; AH = white on black attribute | ||
| 257 | ; AL = ASCII for a blank | ||
| 258 | REP STOSW ; Write 15 rows of while blanks on black | ||
| 259 | |||
| 260 | ; Highlite the display area | ||
| 261 | |||
| 262 | XOR DI,DI ; DI points to the start of the buffer | ||
| 263 | MOV CX,80*6 ; Highlite 6 lines | ||
| 264 | MOV AX,SEX_ATTR+BLANK ; AH = white on red attribute | ||
| 265 | ; AL = ASCII for a blank | ||
| 266 | REP STOSW ; Highlight the 6 lines | ||
| 267 | |||
| 268 | ; Display the registers one at at time | ||
| 269 | |||
| 270 | MOV CX,21 ; 18 regs + excpt id + task id + error code | ||
| 271 | MOV SI,SYS_PATCH_DS ; Load DS with the selector for our data | ||
| 272 | MOV DS,SI ; area | ||
| 273 | MOV SI,OFFSET REG_TABLE ; DS:SI now points to the reg table | ||
| 274 | SUB AX,AX ; Clear AH | ||
| 275 | MOV AL,XMATID ; Load AL with the current XMA bank ID | ||
| 276 | MOV WORD PTR SS:[BP+BP_PSP2],AX ; The bank ID gets displayed as | ||
| 277 | ; the task ID | ||
| 278 | |||
| 279 | ; Display one register | ||
| 280 | |||
| 281 | DO_REG: | ||
| 282 | |||
| 283 | ; Calculate the offset into the display buffer | ||
| 284 | |||
| 285 | LODSB ; Get the row coordinate | ||
| 286 | MOV AH,160 ; Multiply by 160 byte to a row | ||
| 287 | MUL AH ; (80 bytes of character, attribute) | ||
| 288 | ADD AL,DS:BYTE PTR [SI] ; Add on the number of columns | ||
| 289 | ADC AH,0 ; Don't forget the carry | ||
| 290 | ADD AL,DS:BYTE PTR [SI] ; Add the columns again (remember - | ||
| 291 | ADC AH,0 ; character, attribute) | ||
| 292 | INC SI ; Point to next entry in the reg table | ||
| 293 | MOV DI,AX ; Load DI with the offset into the | ||
| 294 | ; display buffer | ||
| 295 | |||
| 296 | DO_ID: | ||
| 297 | |||
| 298 | ; Put the register name on the screen. | ||
| 299 | |||
| 300 | MOV BX,SEX_ATTR ; Load the attribute byte into AH | ||
| 301 | MOV AH,BH | ||
| 302 | LODSB ; Get the character to display | ||
| 303 | CMP AL,0 ; Are we at the end of the string yet? | ||
| 304 | JZ DID_ID ; Yup. Then go display the register | ||
| 305 | ; value. | ||
| 306 | STOSW ; Else put the next character of the | ||
| 307 | ; register name on the screen | ||
| 308 | JMP DO_ID ; Go get the next character | ||
| 309 | |||
| 310 | DID_ID: | ||
| 311 | |||
| 312 | ; Put the register value on the screen. | ||
| 313 | |||
| 314 | MOV BP,SP ; BP points the start of the register | ||
| 315 | ; save area | ||
| 316 | LODSW ; Get the offset of this register's | ||
| 317 | ; save area | ||
| 318 | ADD BP,AX ; Add to BP. BP ponts to the register | ||
| 319 | ; value. | ||
| 320 | LODSW ; Get the length of this register's | ||
| 321 | MOV DX,AX ; save area | ||
| 322 | CMP DX,2 ; If the length is not two words | ||
| 323 | JNE MORE ; Then go to the one word code | ||
| 324 | |||
| 325 | DATAOV ; Grab all 32 bits of the register | ||
| 326 | MOV AX,SS:WORD PTR [BP] | ||
| 327 | ADD BP,4 ; Point BP past the register value | ||
| 328 | CALL HEXD ; Display the 32 bit value | ||
| 329 | JMP LOOPREG ; Jump over the one word code | ||
| 330 | |||
| 331 | MORE: | ||
| 332 | MOV AX,SS:WORD PTR [BP] ; Get the word value into AX | ||
| 333 | ADD BP,2 ; Step BP past the register value | ||
| 334 | CALL HEXW ; Display the 16 bit value | ||
| 335 | |||
| 336 | LOOPREG: | ||
| 337 | LOOP DO_REG ; Go do another register | ||
| 338 | |||
| 339 | PAGE | ||
| 340 | ; | ||
| 341 | ; Let's go put up the stack for everyone to see !!! | ||
| 342 | ; | ||
| 343 | MOV BP,SP ; Reset BP to point to the beginning | ||
| 344 | ; of the register save area | ||
| 345 | |||
| 346 | ; If the V86 task faulted, display its stack. Else display our stack. | ||
| 347 | |||
| 348 | MOV AX,SS:WORD PTR [BP+BP_FL2] ; Alright, whose fault was it? | ||
| 349 | TEST AL,02H ; Check the VM flag | ||
| 350 | JZ NOTVM86 ; Gulp! It's us. | ||
| 351 | |||
| 352 | ; It was the V86 task that faulted. Set DS:SI to point to their stack. | ||
| 353 | |||
| 354 | MOV AX,HUGE_PTR ; Load DS with a slector that accesses | ||
| 355 | MOV DS,AX ; all of memory as data | ||
| 356 | DATAOV | ||
| 357 | SUB SI,SI ; Clear all 32 bits of ESI | ||
| 358 | MOV SI,SS:[BP+BP_SP] ; Load SI wiht the V86 task's SP | ||
| 359 | DATAOV | ||
| 360 | SUB AX,AX ; Clear all 32 bits of EAX | ||
| 361 | MOV AX,SS:[BP+BP_SS] ; Get the V86 task's SS | ||
| 362 | DATAOV ; Shift it left 4 bits to convert it | ||
| 363 | SHL AX,4 ; to an offset | ||
| 364 | DATAOV ; Add it on to SP. Now SI contains | ||
| 365 | ADD SI,AX ; the offest of the stack from 0 | ||
| 366 | |||
| 367 | MOV BP,0FFFFH ; I don't know what this code does but | ||
| 368 | DATAOV ; I left it anyway. The following | ||
| 369 | ; comment is the only clue. | ||
| 370 | SHL BP,16 ; Make stack seg limit very large | ||
| 371 | JMP COMSTACK | ||
| 372 | |||
| 373 | ; It was us that faulted. Set DS:SI to point to our stack. | ||
| 374 | |||
| 375 | NOTVM86: | ||
| 376 | MOV AX,SS ; Load DS with our own SS | ||
| 377 | MOV DS,AX | ||
| 378 | DATAOV | ||
| 379 | SUB SI,SI ; Clear all 32 bits of ESI | ||
| 380 | MOV SI,SP ; Now DS:SI points to our stack | ||
| 381 | |||
| 382 | ; DS:SI points to the beginning of a stack. Now display it. | ||
| 383 | |||
| 384 | COMSTACK: | ||
| 385 | MOV DI,1120 ; Load DI with the offset into the | ||
| 386 | ; display buffer of where we want | ||
| 387 | ; to display the stack | ||
| 388 | MOV CX,70H ; Display 70H words of the stack | ||
| 389 | MOV BX,STACK_ATTR ; Load BH with the attribute byte | ||
| 390 | |||
| 391 | DISP_STACK: | ||
| 392 | |||
| 393 | ADDROV ; Get a word off of the stack | ||
| 394 | LODSW | ||
| 395 | ADDROV ; Intel bug # A0-119 | ||
| 396 | NOP ; Intel bug # A0-119 | ||
| 397 | CALL HEXW ; Display the word | ||
| 398 | MOV AX,STACK_ATTR+BLANK ; Put a blank after each word | ||
| 399 | STOSW ; Put that on the screen | ||
| 400 | LOOP DISP_STACK ; Do the rest of the stack | ||
| 401 | |||
| 402 | ; Wait for the operator to press the system request key to go to | ||
| 403 | ; the monitor, or the escape key to perform the reset operation | ||
| 404 | |||
| 405 | WAIT_HERE: | ||
| 406 | |||
| 407 | IN AL,064H ; Poll the keystroke port | ||
| 408 | TEST AL,000000001B ; Did the user hit a key? | ||
| 409 | JZ WAIT_HERE ; Nope. Go check again. | ||
| 410 | |||
| 411 | IN AL,060H ; Get the keystroke | ||
| 412 | CMP AL,054H ; System request key? | ||
| 413 | JNE CHK_ESC ; No. Check for Esc key. | ||
| 414 | |||
| 415 | CMP SS:WORD PTR [BP+BP_EX],0EH ; It was system request key. Now | ||
| 416 | ; check for a page fault. | ||
| 417 | JE PAGE_FAULT ; If so, go remove the extra return code | ||
| 418 | ; from the stack and terminate the | ||
| 419 | ; application running in the V86 task. | ||
| 420 | JMP POPREGS ; Else just return to the V86 task @P1C | ||
| 421 | |||
| 422 | CHK_ESC: | ||
| 423 | CMP AL,001H ; Was the Esc key hit? | ||
| 424 | JNE CHKPRT ; Nope. Go check for Print Screen and | ||
| 425 | ; Ctrl-Alt-Del. | ||
| 426 | MOV BP,SP ; Point BP to the register save area | ||
| 427 | CMP SS:WORD PTR [BP+BP_EX],0EH ; Check for a page fault | ||
| 428 | JE PAGE_FAULT ; If so, go remove the extra return | ||
| 429 | ; code from the stack and terminate | ||
| 430 | ; the application running in the | ||
| 431 | ; V86 task. | ||
| 432 | MOV AX,SS:WORD PTR [BP+BP_FL2] ; Else, Esc key hit and no page fault | ||
| 433 | TEST AL,02H ; Check who faulted, them or us | ||
| 434 | JZ DO_RESET ; If it's us, then reIPL. | ||
| 435 | |||
| 436 | TERM_APP: | ||
| 437 | MOV SS:WORD PTR [BP+BP_EX],21H ; If it's them, termintate whatever | ||
| 438 | MOV SS:WORD PTR [BP+BP_AX],4CFFH ; is running by forcing a DOS | ||
| 439 | ; termintate. Return code is FF. | ||
| 440 | JMP DO_MONITOR ; Go pass the interrupt to the V86 | ||
| 441 | ; task | ||
| 442 | |||
| 443 | PAGE_FAULT: | ||
| 444 | ; | ||
| 445 | ; On a page fault the 80386 processor puts an extra error code on our stack. | ||
| 446 | ; (How handy!) We now need to remove the extra error code so that when we pop | ||
| 447 | ; the registers off our stack at the end we end up with our stack possitioned | ||
| 448 | ; correctly for the IRET. To do this, we move everything on the stack that is | ||
| 449 | ; below the extra error code up four bytes. The error code takes up four bytes. | ||
| 450 | ; | ||
| 451 | |||
| 452 | STD ; Shift into reverse, 'cause stacks | ||
| 453 | ; grow down | ||
| 454 | MOV CX,(BP_EC-BP_START)/2 ; Load CX with the number of words | ||
| 455 | MOV DI,(BP_EC+2-BP_START) ; Point DI to the last word of the | ||
| 456 | ADD DI,BP ; extra error code | ||
| 457 | MOV SI,(BP_EC-2-BP_START) ; Point SI to the last word of the | ||
| 458 | ADD SI,BP ; exception code | ||
| 459 | MOV AX,SS ; Set up the selectors | ||
| 460 | MOV ES,AX | ||
| 461 | MOV DS,AX | ||
| 462 | STACK_LOOP: | ||
| 463 | LODSW ; Get a word off the stack | ||
| 464 | STOSW ; And move it up four bytes | ||
| 465 | LOOP STACK_LOOP ; Do that trick again | ||
| 466 | |||
| 467 | CLD ; Shift back into forward | ||
| 468 | ADD BP,4 ; Scoot BP up four bytes to point to | ||
| 469 | ; pur new register save area | ||
| 470 | MOV SP,BP ; Adjust SP, too | ||
| 471 | JMP TERM_APP ; Go kill whatever is running in the V86 | ||
| 472 | ; task | ||
| 473 | CHKPRT: | ||
| 474 | CMP AL,053H ; Was the Del (as in Ctrl-Alt-Del) key | ||
| 475 | ; pressed? | ||
| 476 | JE DO_RESET ; If so, then reIPL | ||
| 477 | |||
| 478 | CMP AL,037H ; Was the print screen key pressed? | ||
| 479 | JNE WAIT_HERE ; Nope. Must be an invalid key. Go get | ||
| 480 | ; another keystroke. | ||
| 481 | |||
| 482 | MOV BP,SP ; It was a print screen. Reset BP to | ||
| 483 | ; point to our register save area. | ||
| 484 | MOV AX,SS:WORD PTR [BP+BP_FL2] ; If is was us that had the problem | ||
| 485 | TEST AL,02H ; then we don't allow print screen | ||
| 486 | ; because the system is not healthy | ||
| 487 | JZ WAIT_HERE ; Go get another key | ||
| 488 | |||
| 489 | MOV SS:WORD PTR [BP+BP_EX],05H ; If it was them then we can do a | ||
| 490 | JMP DO_MONITOR ; print screen. Force the V86 | ||
| 491 | ; task to do an INT 5 (Prt Sc). | ||
| 492 | |||
| 493 | ; | ||
| 494 | ; Reset the system, i.e. reIPL. Put a 1234 in the BIOS reset flag at 472H. | ||
| 495 | ; This will keep BIOS from running through the whole POST upon reIPL. | ||
| 496 | ; | ||
| 497 | |||
| 498 | DO_RESET: | ||
| 499 | MOV AX,HUGE_PTR ; Load ES with a selector that accesses all | ||
| 500 | MOV ES,AX ; of memory as data | ||
| 501 | DATAOV | ||
| 502 | SUB DI,DI ; Clear EDI (32 bit DI) | ||
| 503 | MOV DI,472H ; Load the offset of the BIOS reset flag | ||
| 504 | MOV AX,1234H | ||
| 505 | ADDROV | ||
| 506 | STOSW ; Put 1234 in the BIOS reset flag | ||
| 507 | |||
| 508 | ADDROV ; Intel bug # A0-119 | ||
| 509 | NOP ; Intel bug # A0-119 | ||
| 510 | |||
| 511 | MOV AL,0FEH ; Now OUT a FE to port 64H. This will cause | ||
| 512 | OUT 064H,AL ; the machine to reIPL. | ||
| 513 | |||
| 514 | HALT: HLT ; Just in case we don't reIPL, this halt @P1C | ||
| 515 | JMP HALT ; loop will keep the processor from doing @P1C | ||
| 516 | ; anything else | ||
| 517 | |||
| 518 | DO_MONITOR: | ||
| 519 | |||
| 520 | ; If the exception camefrom the V86 task then pass the interrupt to the | ||
| 521 | ; real mode interrupt vector. | ||
| 522 | |||
| 523 | MOV BP,SP ; Reset BP to point to our register save area | ||
| 524 | ; on the stack | ||
| 525 | MOV AX,SS:WORD PTR [BP+BP_FL2] ; Check if it was the V86 task that | ||
| 526 | TEST AL,02H ; faulted | ||
| 527 | JNZ LONGWAY ; If so, pass the interrupt on | ||
| 528 | JMP POPREGS ; Otherwise just return @P1C | ||
| 529 | |||
| 530 | PAGE | ||
| 531 | |||
| 532 | ; We come here if the check up front said it was the V86 task that faulted. | ||
| 533 | |||
| 534 | LONGWAY: | ||
| 535 | MOV SS:WORD PTR [BP+BP_SP2],0 ;Purify high-order words of SP, SS | ||
| 536 | MOV SS:WORD PTR [BP+BP_SS2],0 ; and IP | ||
| 537 | MOV SS:WORD PTR [BP+BP_IP2],0 | ||
| 538 | |||
| 539 | ; Test for interrupt versus exception. | ||
| 540 | |||
| 541 | CMP SS:WORD PTR [BP+BP_EX],13 ; Check if it was a general | ||
| 542 | ; protection exception | ||
| 543 | JNE LONGWAY2 ; If not, continue checking | ||
| 544 | JMP EMULATE ; If so, then go to INDEEMU to | ||
| 545 | ; emulate the instruction | ||
| 546 | LONGWAY2: | ||
| 547 | CMP SS:WORD PTR [BP+BP_EX],6 ; Was it an invalid op-code | ||
| 548 | ; exception? | ||
| 549 | JB LONGWAY4 ; If lower, then pass the | ||
| 550 | ; interrupt back to the V86 task | ||
| 551 | CMP SS:WORD PTR [BP+BP_EX],7 ; Was it a coprocessor not avail- | ||
| 552 | ; able exception? | ||
| 553 | JA LONGWAY3 ; If greater then do more checking | ||
| 554 | JMP EMULATE ; Emulation needed for interrupts | ||
| 555 | ; 6 and 7 | ||
| 556 | LONGWAY3: | ||
| 557 | CMP SS:WORD PTR [BP+BP_EX],15H ; Check if it was INT 15 | ||
| 558 | JNE LONGWAY4 ; Nope, pass interrupt back to | ||
| 559 | ; the V86 task | ||
| 560 | JMP INT15 ; Emulation needed for INT 15 | ||
| 561 | |||
| 562 | LONGWAY4: | ||
| 563 | |||
| 564 | ; Pass the interrupt back to the V86 task. | ||
| 565 | |||
| 566 | MOV AX,HUGE_PTR ; Load ES with a selector that accesses | ||
| 567 | MOV ES,AX ; all of memory as data | ||
| 568 | DATAOV | ||
| 569 | SUB DI,DI ; Clear all 32 bits of EDI | ||
| 570 | MOV DI,SS:[BP+BP_SP] ; Load DI with the V86 task's SP | ||
| 571 | SUB DI,6 ; Decrement "SP" to make room for the | ||
| 572 | ; push of IP, CS and the flags. | ||
| 573 | ; Note that this assumes there are at | ||
| 574 | ; least 6 bytes keft on the stack. | ||
| 575 | MOV SS:WORD PTR [BP+BP_SP],DI ; Put the new SP into the V86 register | ||
| 576 | ; save area | ||
| 577 | DATAOV | ||
| 578 | SUB AX,AX ; Clear all 32 bits of EAX | ||
| 579 | MOV AX,SS:[BP+BP_SS] ; Load AX with the V86 task's SS | ||
| 580 | DATAOV ; Shift it left four bits to convert | ||
| 581 | SHL AX,4 ; it to an offset | ||
| 582 | DATAOV ; Add it on to SP. Now DI contains | ||
| 583 | ADD DI,AX ; the offest of the stack from 0 | ||
| 584 | |||
| 585 | ; Now put the V86 task's IP, CS and flags on the stack. They are put on in | ||
| 586 | ; reverse order because the stack grows down, but we are going up as we put | ||
| 587 | ; the stuff on the stack. | ||
| 588 | |||
| 589 | MOV AX,SS:[BP+BP_IP] ; Get the V86 task's IP | ||
| 590 | ADDROV | ||
| 591 | STOSW ; Put it on his stack | ||
| 592 | ADDROV ; Intel bug # A0-119 | ||
| 593 | NOP ; Intel bug # A0-119 | ||
| 594 | |||
| 595 | MOV AX,SS:[BP+BP_CS] ; Get the V86 task's CS | ||
| 596 | ADDROV | ||
| 597 | STOSW ; Put it on his stack | ||
| 598 | ADDROV ; Intel bug # A0-119 | ||
| 599 | NOP ; Intel bug # A0-119 | ||
| 600 | |||
| 601 | MOV AX,SS:[BP+BP_FL] ; Get the V86 task's flags | ||
| 602 | ADDROV | ||
| 603 | STOSW ; Put them on his stack | ||
| 604 | ADDROV ; Intel bug # A0-119 | ||
| 605 | NOP ; Intel bug # A0-119 | ||
| 606 | AND AX,3CFFH ; Clean up the flags for our IRET | ||
| 607 | MOV WORD PTR SS:[BP+BP_FL],AX | ||
| 608 | |||
| 609 | MOV SI,SS:[BP+BP_EX] ; Get the interrupt vector | ||
| 610 | SHL SI,2 ; Multiply by four because interrupt | ||
| 611 | ; vectorsare four bytes long | ||
| 612 | MOV AX,HUGE_PTR ; Load DS with a selector that accesses | ||
| 613 | MOV DS,AX ; all of memory as data | ||
| 614 | LODSW ; Get the IP for the interrupt | ||
| 615 | MOV WORD PTR SS:[BP+BP_IP],AX ; Store it in the V86 task's IP | ||
| 616 | LODSW ; Get the CS for the interrupt | ||
| 617 | MOV WORD PTR SS:[BP+BP_CS],AX ; Store it in the V86 task's CS | ||
| 618 | |||
| 619 | PAGE | ||
| 620 | POPREGS: ; @P1C | ||
| 621 | |||
| 622 | ; Pop the saved registers off of our stack and IRET to the V86 task. | ||
| 623 | |||
| 624 | POP ES ; Restore ES | ||
| 625 | DATAOV ; Restore all the registers | ||
| 626 | POPA ; (32 bit registers) | ||
| 627 | POP DS ; Restore DS | ||
| 628 | ADD SP,(BP_IP-BP_EX) ; Step SP past the error code placed | ||
| 629 | ; on our stack by the 80386 | ||
| 630 | DATAOV | ||
| 631 | IRET ; IRET to the V86 task | ||
| 632 | |||
| 633 | SEX ENDP | ||
| 634 | |||
| 635 | SUBTTL HEXD - Convert DWORD in EAX to ASCII string at ES:DI | ||
| 636 | PAGE | ||
| 637 | ; | ||
| 638 | ; INPUT: EAX = hex double word to display | ||
| 639 | ; BH = attribute byte | ||
| 640 | ; ES:DI = location in the display buffer where the characters are | ||
| 641 | ; to be placed | ||
| 642 | ; | ||
| 643 | ; OUTPUT: DI is incremented past last character displayed | ||
| 644 | ; Characters are placed on the screen | ||
| 645 | ; | ||
| 646 | |||
| 647 | HEXD PROC NEAR | ||
| 648 | DATAOV | ||
| 649 | PUSH AX ; Save EAX on the stack | ||
| 650 | DATAOV | ||
| 651 | SHR AX,24 ; Shift the high order byte into AL | ||
| 652 | CALL HEXB ; Convert the byte in AL to ASCII at ES:DI | ||
| 653 | DATAOV | ||
| 654 | POP AX ; Restore the original EAX | ||
| 655 | PUSH AX ; Save the low word of EAX (i.e. AX) | ||
| 656 | DATAOV | ||
| 657 | SHR AX,16 ; Shift the second highest byte into AL | ||
| 658 | CALL HEXB ; Convert the byte in AL to ASCII at ES:DI | ||
| 659 | POP AX ; Restore the low word of EAX (i.e. AX) | ||
| 660 | PUSH AX ; And save it again | ||
| 661 | XCHG AH,AL ; Move the thrid highest byte into AL | ||
| 662 | CALL HEXB ; Convert the byte in AL to an ASCII string | ||
| 663 | POP AX ; Restore AX | ||
| 664 | CALL HEXB ; And conver the last byte to ASCII at ES:DI | ||
| 665 | RET | ||
| 666 | |||
| 667 | HEXD ENDP | ||
| 668 | |||
| 669 | SUBTTL HEXW - Convert WORD in AX to ASCII string at ES:DI | ||
| 670 | PAGE | ||
| 671 | ; | ||
| 672 | ; INPUT: AX = hex word to display | ||
| 673 | ; BH = attribute byte | ||
| 674 | ; ES:DI = location in the display buffer where the characters are | ||
| 675 | ; to be placed | ||
| 676 | ; | ||
| 677 | ; OUTPUT: DI is incremented past last character | ||
| 678 | ; Characters are placed on the screen | ||
| 679 | ; | ||
| 680 | |||
| 681 | HEXW PROC NEAR | ||
| 682 | |||
| 683 | PUSH AX ; Save the value in AX on the stack | ||
| 684 | XCHG AH,AL ; Move the high byte into AL | ||
| 685 | CALL HEXB ; Convert the byte in AL to a string at ES:DI | ||
| 686 | POP AX ; Restore AX | ||
| 687 | CALL HEXB ; Convert the low byte to ASCII at ES:DI | ||
| 688 | RET | ||
| 689 | |||
| 690 | HEXW ENDP | ||
| 691 | |||
| 692 | SUBTTL HEXD - Convert BYTE in AL to ASCII string at ES:DI | ||
| 693 | PAGE | ||
| 694 | ; | ||
| 695 | ; INPUT: AL = hex byte to display | ||
| 696 | ; BH = attribute byte | ||
| 697 | ; ES:DI = location in the display buffer where the characters are | ||
| 698 | ; to be placed | ||
| 699 | ; | ||
| 700 | ; OUTPUT: DI is incremented past last character | ||
| 701 | ; Characters are placed on the screen | ||
| 702 | ; | ||
| 703 | |||
| 704 | HEXB PROC NEAR | ||
| 705 | |||
| 706 | PUSH AX ; Save the value in AX | ||
| 707 | AND AL,0F0H ; Clear the low nibble of AL | ||
| 708 | SHR AL,1 ; Shift the high nibble into the low nibble | ||
| 709 | SHR AL,1 | ||
| 710 | SHR AL,1 | ||
| 711 | SHR AL,1 | ||
| 712 | ADD AL,030H ; Add '0' to convert to ASCII | ||
| 713 | CMP AL,03AH ; Was this hex digit greater than 9? | ||
| 714 | JC OK1 ; Nope. It's OK, so go display it. | ||
| 715 | ADD AL,7 ; Yup. Then convert to 'A' to 'F'. | ||
| 716 | OK1: MOV AH,BH ; Move the attribute into AH | ||
| 717 | STOSW ; Put the character & attribute into the display | ||
| 718 | ; buffer at ES:DI | ||
| 719 | POP AX ; Restore AX | ||
| 720 | AND AL,00FH ; Clear the high nibble of AL | ||
| 721 | ADD AL,030H ; Convert the low nibble to ASCII as before | ||
| 722 | CMP AL,03AH ; Hex digit greater than 9? | ||
| 723 | JC OK2 ; Nope. It's OK, so go display it. | ||
| 724 | ADD AL,7 ; Yup. Then convert to 'A' to 'F'. | ||
| 725 | OK2: MOV AH,BH | ||
| 726 | STOSW | ||
| 727 | RET | ||
| 728 | |||
| 729 | HEXB ENDP | ||
| 730 | |||
| 731 | PAGE | ||
| 732 | |||
| 733 | REG MACRO NAME,ROW,COL,L | ||
| 734 | DB &ROW ; Display of register &NAME starts in | ||
| 735 | DB &COL ; row &ROW and column &COL | ||
| 736 | DB '&NAME:' ; Name to display for register &NAME | ||
| 737 | DB 0 ; End of string marker | ||
| 738 | DW BP_&NAME ; Offset of value of register &NAME | ||
| 739 | ; that we saved on our stack | ||
| 740 | DW &L ; Number of words in the register | ||
| 741 | ENDM | ||
| 742 | |||
| 743 | SUBTTL Register table | ||
| 744 | PAGE | ||
| 745 | REG_TABLE LABEL NEAR | ||
| 746 | ; | ||
| 747 | ; Declare data used for displaying the registers on the screen. For each | ||
| 748 | ; register there is a structure that contains the row and column of where the | ||
| 749 | ; display of the register starts, the text or register name ended with a 0, the | ||
| 750 | ; offset into the stack where the value in the register was saved, and the | ||
| 751 | ; number of words in the register. | ||
| 752 | ; | ||
| 753 | |||
| 754 | ; First, lets fake a register to put the exception message on the screen. | ||
| 755 | |||
| 756 | DB 1 ; Row 1 | ||
| 757 | DB 10 ; Column 10 | ||
| 758 | DB 'System Exception - ' ; Text | ||
| 759 | DB 0 ; End of text | ||
| 760 | DW BP_EX ; Offset to hex value on the stack | ||
| 761 | DW 1 ; Number of words of data | ||
| 762 | |||
| 763 | ; Now, fake one to put the task id (bank ID) on the screen. | ||
| 764 | |||
| 765 | DB 1 ; Row 1 | ||
| 766 | DB 50 ; Column 50 | ||
| 767 | DB 'Task ID - ' ; Text | ||
| 768 | DB 0 ; End of text | ||
| 769 | DW BP_PSP2 ; Offset to hex value on the stack | ||
| 770 | DW 1 ; Number of words of data | ||
| 771 | |||
| 772 | ; Now, lets do the registers | ||
| 773 | |||
| 774 | REG CS,3,1,1 | ||
| 775 | REG IP,3,9,2 | ||
| 776 | REG SS,3,21,1 | ||
| 777 | REG SP,3,29,2 | ||
| 778 | REG DS,3,41,1 | ||
| 779 | REG SI,3,49,2 | ||
| 780 | REG ES,3,61,1 | ||
| 781 | REG DI,3,69,2 | ||
| 782 | |||
| 783 | REG AX,4,1,2 | ||
| 784 | REG BX,4,13,2 | ||
| 785 | REG CX,4,25,2 | ||
| 786 | REG DX,4,37,2 | ||
| 787 | REG BP,4,49,2 | ||
| 788 | REG EC,4,61,2 | ||
| 789 | |||
| 790 | REG FL,5,1,2 | ||
| 791 | REG VMDS,5,18,1 | ||
| 792 | REG VMES,5,33,1 | ||
| 793 | REG VMFS,5,48,1 | ||
| 794 | REG VMGS,5,63,1 | ||
| 795 | |||
| 796 | PROG ENDS | ||
| 797 | |||
| 798 | END | ||
| 799 | |||