Critical-Error Handlers

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.