2 TITLE INDEEXC
- 386 XMA EMULATOR
- System Exception Handler
5 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7 * MODULE NAME
: INDEEXC
*
10 * 5669-196 (C
) COPYRIGHT
1988 Microsoft Corp
. *
12 * DESCRIPTIVE NAME
: 80386 XMA Emulator System Exception Handler
*
14 * STATUS
(LEVEL
) : VERSION
(0) LEVEL
(1.0) *
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
. *
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
*
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
. *
44 * REGISTER USAGE
: 80386 Standard
*
46 * RESTRICTIONS
: None
*
48 * DEPENDENCIES
: None
*
50 * ENTRY POINT
: VEXCPT13
*
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
. *
56 * INPUT PARMS
: None
*
58 * RETURN PARMS
: None
*
60 * OTHER EFFECTS
: None
*
62 * EXIT NORMAL
: IRET to the
virtual 8086 task
*
64 * EXIT ERROR
: Force the V86 task to execute an NMI
*
67 * REFERENCES
: EMULATE
- Entry point for INDEEMU
*
68 * INT15
- Entry point for INDEI15
*
70 * SUB-ROUTINES
: HEXD
- Display the double
word in EAX *
71 * HEXW
- Display the
word in AX *
72 * HEXB
- Display the
byte in AL *
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
*
79 * CONTROL BLOCKS
: INDEDAT
.INC *
83 * $
MOD(INDEEXC
) COMP
(LOAD) PROD
(3270PC
) : *
85 * $D0=D0004700
410 870523 D
: NEW FOR RELEASE
1.1 *
86 * $P1
=P0000312
410 870804 D
: CLEAN UP WARNING MESSAGES
*
88 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
91 .286P
; Enable recognition of 286 privileged instructs.
93 .XLIST
; Turn off the listing
94 INCLUDE INDEDAT
.INC ; Include system data
96 IF1
; Only include macros on the first pass of
97 INCLUDE INDEOVP
.MAC
; of the assembler
99 .LIST
; Turn on the listing
102 STACK_ATTR EQU
00700H
108 PROG
SEGMENT PARA
PUBLIC 'PROG'
117 ; External entry points
119 EXTRN EMULATE
:NEAR ; Entry point to INDEEMU
120 EXTRN INT15
:NEAR ; Entry point to INDEI15
124 EXTRN CRT_SELECTOR
:WORD ; Selector for the display buffer (INDEI15)
125 EXTRN XMATID
:BYTE ; Current bank ID (INDEXMA)
141 CLD ; All moves go forward
143 ; Save the registers on the stack. These are the registers of the task that
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.
154 MOV BP,SP ; Point BP to the start of the register save area
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.
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
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.
174 ; JMP DODISP ; Just display the registers.
175 ; Comment out for final product.
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.
180 MOV BP,SP ; Point BP to the saved registers
181 CMP SS:WORD PTR [BP+BP_EX
],0DH ; Was it a general protection
183 JNE DISPCONT
; If not, the continue
184 JMP POPREGS
; Else just return to the V86 @P1C
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
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.
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.
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
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
235 MOV AX,HUGE_PTR
; Load ES with a selector that @D1A
236 MOV ES,AX ; accesses all of memory as data @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
245 JMP LONGWAY4
; Go do the NMI @D1A
248 ; The following code will not be executed. It is left as a debugging tool.
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
260 ; Highlite the display area
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
268 ; Display the registers one at at time
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
273 MOV SI,OFFSET REG_TABLE
; DS:SI now points to the reg table
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
279 ; Display one register
283 ; Calculate the offset into the display buffer
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
298 ; Put the register name on the screen.
300 MOV BX,SEX_ATTR
; Load the attribute byte into AH
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
306 STOSW ; Else put the next character of the
307 ; register name on the screen
308 JMP DO_ID
; Go get the next character
312 ; Put the register value on the screen.
314 MOV BP,SP ; BP points the start of the register
316 LODSW ; Get the offset of this register's
318 ADD BP,AX ; Add to BP. BP ponts to the register
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
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
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
337 LOOP DO_REG
; Go do another register
341 ; Let's go put up the stack for everyone to see !!!
343 MOV BP,SP ; Reset BP to point to the beginning
344 ; of the register save area
346 ; If the V86 task faulted, display its stack. Else display our stack.
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.
352 ; It was the V86 task that faulted. Set DS:SI to point to their stack.
354 MOV AX,HUGE_PTR
; Load DS with a slector that accesses
355 MOV DS,AX ; all of memory as data
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
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
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
373 ; It was us that faulted. Set DS:SI to point to our stack.
376 MOV AX,SS ; Load DS with our own SS
379 SUB SI,SI ; Clear all 32 bits of ESI
380 MOV SI,SP ; Now DS:SI points to our stack
382 ; DS:SI points to the beginning of a stack. Now display it.
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
393 ADDROV
; Get a word off of the stack
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
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
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.
411 IN AL,060H ; Get the keystroke
412 CMP AL,054H ; System request key?
413 JNE CHK_ESC
; No. Check for Esc key.
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
423 CMP AL,001H ; Was the Esc key hit?
424 JNE CHKPRT
; Nope. Go check for Print Screen and
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
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.
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
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.
452 STD ; Shift into reverse, 'cause stacks
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
463 LODSW ; Get a word off the stack
464 STOSW ; And move it up four bytes
465 LOOP STACK_LOOP
; Do that trick again
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
474 CMP AL,053H ; Was the Del (as in Ctrl-Alt-Del) key
476 JE DO_RESET
; If so, then reIPL
478 CMP AL,037H ; Was the print screen key pressed?
479 JNE WAIT_HERE
; Nope. Must be an invalid key. Go get
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
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).
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.
499 MOV AX,HUGE_PTR
; Load ES with a selector that accesses all
500 MOV ES,AX ; of memory as data
502 SUB DI,DI ; Clear EDI (32 bit DI)
503 MOV DI,472H
; Load the offset of the BIOS reset flag
506 STOSW ; Put 1234 in the BIOS reset flag
508 ADDROV
; Intel bug # A0-119
509 NOP ; Intel bug # A0-119
511 MOV AL,0FEH ; Now OUT a FE to port 64H. This will cause
512 OUT 064H,AL ; the machine to reIPL.
514 HALT: HLT ; Just in case we don't reIPL, this halt @P1C
515 JMP HALT
; loop will keep the processor from doing @P1C
520 ; If the exception camefrom the V86 task then pass the interrupt to the
521 ; real mode interrupt vector.
523 MOV BP,SP ; Reset BP to point to our register save area
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
532 ; We come here if the check up front said it was the V86 task that faulted.
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
539 ; Test for interrupt versus exception.
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
547 CMP SS:WORD PTR [BP+BP_EX
],6 ; Was it an invalid op-code
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-
553 JA LONGWAY3
; If greater then do more checking
554 JMP EMULATE
; Emulation needed for interrupts
557 CMP SS:WORD PTR [BP+BP_EX
],15H
; Check if it was INT 15
558 JNE LONGWAY4
; Nope, pass interrupt back to
560 JMP INT15
; Emulation needed for INT 15
564 ; Pass the interrupt back to the V86 task.
566 MOV AX,HUGE_PTR
; Load ES with a selector that accesses
567 MOV ES,AX ; all of memory as data
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
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
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.
589 MOV AX,SS:[BP+BP_IP
] ; Get the V86 task's IP
591 STOSW ; Put it on his stack
592 ADDROV
; Intel bug # A0-119
593 NOP ; Intel bug # A0-119
595 MOV AX,SS:[BP+BP_CS
] ; Get the V86 task's CS
597 STOSW ; Put it on his stack
598 ADDROV
; Intel bug # A0-119
599 NOP ; Intel bug # A0-119
601 MOV AX,SS:[BP+BP_FL
] ; Get the V86 task's flags
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
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
622 ; Pop the saved registers off of our stack and IRET to the V86 task.
625 DATAOV
; Restore all the registers
626 POPA ; (32 bit registers)
628 ADD SP,(BP_IP
-BP_EX
) ; Step SP past the error code placed
629 ; on our stack by the 80386
631 IRET ; IRET to the V86 task
635 SUBTTL HEXD
- Convert
DWORD in EAX to ASCII string
at ES:DI
638 ; INPUT: EAX = hex double word to display
639 ; BH = attribute byte
640 ; ES:DI = location in the display buffer where the characters are
643 ; OUTPUT: DI is incremented past last character displayed
644 ; Characters are placed on the screen
649 PUSH AX ; Save EAX on the stack
651 SHR AX,24 ; Shift the high order byte into AL
652 CALL HEXB
; Convert the byte in AL to ASCII at ES:DI
654 POP AX ; Restore the original EAX
655 PUSH AX ; Save the low word of EAX (i.e. AX)
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
664 CALL HEXB
; And conver the last byte to ASCII at ES:DI
669 SUBTTL HEXW
- Convert
WORD in AX to ASCII string
at ES:DI
672 ; INPUT: AX = hex word to display
673 ; BH = attribute byte
674 ; ES:DI = location in the display buffer where the characters are
677 ; OUTPUT: DI is incremented past last character
678 ; Characters are placed on the screen
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
687 CALL HEXB
; Convert the low byte to ASCII at ES:DI
692 SUBTTL HEXD
- Convert
BYTE in AL to ASCII string
at ES:DI
695 ; INPUT: AL = hex byte to display
696 ; BH = attribute byte
697 ; ES:DI = location in the display buffer where the characters are
700 ; OUTPUT: DI is incremented past last character
701 ; Characters are placed on the screen
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
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
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'.
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
743 SUBTTL Register table
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.
754 ; First, lets fake a register to put the exception message on the screen.
758 DB 'System Exception - ' ; Text
760 DW BP_EX
; Offset to hex value on the stack
761 DW 1 ; Number of words of data
763 ; Now, fake one to put the task id (bank ID) on the screen.
767 DB 'Task ID - ' ; Text
769 DW BP_PSP2
; Offset to hex value on the stack
770 DW 1 ; Number of words of data
772 ; Now, lets do the registers