In Chapter 5, we discussed how an application program can take over the Ctrl-C handler vector (Int 23H) and replace the MS-DOS default handler, to avoid losing control of the computer when the user enters a Ctrl-C or Ctrl-Break at the keyboard. Similarly, MS-DOS provides a critical-error-handler vector (Int 24H) that defines the routine to be called when unrecoverable hardware faults occur. The default MS-DOS critical-error handler is the routine that displays a message describing the error type and the cue
Abort, Retry, Ignore?
This message appears after such actions as the following:
Attempting to open a file on a disk drive that doesn't contain a floppy disk or whose door isn't closed
Trying to read a disk sector that contains a CRC error
Trying to print when the printer is off line
The unpleasant thing about MS-DOS's default critical-error handler is, of course, that if the user enters an A for Abort, the application that is currently executing is terminated abruptly and never has a chance to clean up and make a graceful exit. Intermediate files may be left on the disk, files that have been extended using FCBs are not properly closed so that the directory is updated, interrupt vectors may be left pointing into the transient program area, and so forth.
To write a truly bombproof MS-DOS application, you must take over the critical-error-handler vector and point it to your own routine, so that your program intercepts all catastrophic hardware errors and handles them appropriately. You can use MS-DOS Int 21H Function 25H to alter the Int 24H vector in a well-behaved manner. When your application exits, MS-DOS will automatically restore the previous contents of the Int 24H vector from information saved in the program segment prefix.
MS-DOS calls the critical-error handler for two general classes of errors—— disk-related and non-disk-related——and passes different information to the handler in the registers for each of these classes.
For disk-related errors, MS-DOS sets the registers as shown on the following page. (Bits 3—5 of the AH register are relevant only in MS-DOS versions 3.1 and later.)
Register Bit(s) Significance
AH 7 0, to signify disk error
6 Reserved
5 0 = ignore response not allowed
1 = ignore response allowed
4 0 = retry response not allowed
1 = retry response allowed
3 0 = fail response not allowed
1 = fail response allowed
1—2 Area where disk error occurred
00 = MS-DOS area
01 = file allocation table
10 = root directory
11 = files area
0 0 = read operation
1 = write operation
AL 0—7 Drive code (0 = A, 1 = B, and so
forth)
DI 0—7 Driver error code
8—15 Not used
BP:SI Segment:offset of device-driver
header
For non-disk-related errors, the interrupt was generated either as the result of a character-device error or because a corrupted memory image of the file allocation table was detected. In this case, MS-DOS sets the registers as follows:
Register Bit(s) Significance
AH 7 1, to signify a non-disk error
DI 0—7 Driver error code
8—15 Not used
BP:SI Segment:offset of device-driver
header
To determine whether the critical error was caused by a character device, use the address in the BP:SI registers to examine the device attribute word at offset 0004H in the presumed device-driver header. If bit 15 is set, then the error was indeed caused by a character device, and the program can inspect the name field of the driver's header to determine the device.
At entry to a critical-error handler, MS-DOS has already disabled interrupts and set up the stack as shown in Figure 8-8. A critical-error handler cannot use any MS-DOS services except Int 21H Functions 01H through 0CH (Traditional Character I/O), Int 21H Function 30H (Get MS-DOS Version), and Int 21H Function 59H (Get Extended Error Information). These functions use a special stack so that the context of the original function (which generated the critical error) will not be lost.
Figure 8-8. The stack at entry to a critical-error handler.
Please refer to the printed book for this figure.
The critical-error handler should return to MS-DOS by executing an IRET, passing one of the following action codes in the AL register:
Code Meaning
0 Ignore the error (MS-DOS acts as though the original
function call had succeeded).
1 Retry the operation.
2 Terminate the process that encountered the error.
3 Fail the function (an error code is returned to the
requesting process). Versions 3.1 and later only.
The critical-error handler should preserve all other registers and must not modify the device-driver header pointed to by BP:SI. A skeleton example of a critical-error handler is shown in Figure 8-9.
; prompt message used by
; critical-error handler
prompt db cr,lf,'Critical Error Occurred: '
db 'Abort, Retry, Ignore, Fail? $'
keys db 'aArRiIfF' ; possible user response keys
keys_len equ $-keys ; (both cases of each allowed)
codes db 2,2,1,1,0,0,3,3 ; codes returned to MS-DOS kernel
; for corresponding response keys
;
; This code is executed during program's initialization
; to install the new critical-error handler.
;
.
.
.
push ds ; save our data segment
mov dx,seg int24 ; DS:DX = handler address
mov ds,dx
mov dx,offset int24
mov ax,2524h ; function 25h = set vector
int 21h ; transfer to MS-DOS
pop ds ; restore data segment
.
.
.
;
; This is the replacement critical-error handler. It
; prompts the user for Abort, Retry, Ignore, or Fail, and
; returns the appropriate code to the MS-DOS kernel.
;
int24 proc far ; entered from MS-DOS kernel
push bx ; save registers
push cx
push dx
push si
push di
push bp
push ds
push es
int24a: mov ax,seg prompt ; display prompt for user
mov ds,ax ; using function 9 (print string
mov es,ax ; terminated by $ character)
mov dx,offset prompt
mov ah,9
int 21h
mov ah,1 ; get user's response
int 21h ; function 1 = read one character
mov di,offset keys ; look up code for response key
mov cx,keys_len
cld
repne scasb
jnz int24a ; prompt again if bad response
; set AL = action code for MS-DOS
; according to key that was entered:
; 0 = ignore, 1 = retry, 2 = abort,
; 3 = fail
mov al,[di+keys_len-1]
pop es ; restore registers
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
iret ; exit critical-error handler
int24 endp
Figure 8-9. A skeleton example of a replacement critical-error handler.