int 23h
instruction. MS-DOS provides a default break handler routine that terminates the program. However, a well-written program generally replaces the default break handler with one of its own so it can capture ctrl-C or ctrl-break key sequences and shut the program down in an orderly fashion.iret
. If you point the interrupt 23h vector at an iret
instruction, MS-DOS will simply ignore any ctrl-C or ctrl-break keys you press. This is very useful for turning off the break handling during critical sections of code that you do not want the user to interrupt.
I/O Device Specific Error Message Abort, Retry, Ignore, Fail?
If the user presses an "A", this code immediately returns to DOS' COMMAND.COM program; it doesn't even close any open files. If the user presses an "R" to retry, MS-DOS will retry the I/O operation, though this usually results in another call to the critical error handler. The "I" option tells MS-DOS to ignore the error and return to the calling program as though nothing had happened. An "F" response instructs MS-DOS to return an error code to the calling program and let it handle the problem.
Of the above options, having the user press "A" is the most dangerous. This causes an immediate return to DOS and your code does not get the chance to clean up anything. For example, if you've patched some interrupt vectors, your program will not get the opportunity to restore them if the user selects the abort option. This may crash the system when MS-DOS loads the next program over the top of your interrupt service routine(s) in memory.
To intercept DOS critical errors, you will need to patch the interrupt 24h vector to point at your own interrupt service routine. Upon entry into your interrupt 24h service routine, the stack will contain the following data:
MS-DOS passes important information in several of the registers to your critical error handler. By inspecting these values you can determine the cause of the critical error and the device on which it occurred. The high order bit of the ah
register determines if the error occurred on a block structured device (typically a disk or tape) or a character device. The other bits in ah have the following meaning:
Bit(s) | Description |
---|---|
0 | 0=Read operation. 1=Write operation. |
1-2 | Indicates affected disk area. 00- MS-DOS area. 01- File allocation table (FAT). 10- Root directory. 11- Files area. |
3 | 0- Fail response not allowed. 1- Fail response is okay. |
4 | 0- Retry response not allowed. 1- Retry response is okay. |
5 | 0- Ignore response is not allowed. 1- Ignore response is okay. |
6 | Undefined |
7 | 0- Character device error. 1- Block structured device error. |
al
register contains the drive number where the error occurred (0=A, 1=B, 2=C, etc.). The value in the al
register is undefined for character devices.di
register contains additional information about the block device error (the upper byte of di
is undefined, you will need to mask out those bits before attempting to test this data).Error Code | Description |
---|---|
0 | Write protection error. |
1 | Unknown drive. |
2 | Drive not ready. |
3 | Invalid command. |
4 | Data error (CRC error). |
5 | Length of request structure is incorrect. |
6 | Seek error on device. |
7 | Disk is not formatted for MS-DOS. |
8 | Sector not found. |
9 | Printer out of paper. |
0Ah | Write error. |
0Bh | Read error. |
0Ch | General failure. |
0Fh | Disk was changed at inappropriate time. |
al
. The handler must return to DOS with an iret
instruction and al
must contain one of the following codes:Code | Meaning |
---|---|
0 | Ignore device error. |
1 | Retry I/O operation again. |
2 | Terminate process (abort). |
3 | Fail current system call. |
; Sample INT 24h critical error handler. ; ; This code demonstrates a sample critical error handler. ; It patches into INT 24h and displays an appropriate error ; message and asks the user if they want to retry, abort, ignore, ; or fail (just like DOS). .xlist include stdlib.a includelib stdlib.lib .list dseg segment para public 'data' Value word 0 ErrCode word 0 dseg ends cseg segment para public 'code' assume cs:cseg, ds:dseg ; A replacement critical error handler. Note that this routine ; is even worse than DOS', but it demonstrates how to write ; such a routine. Note that we cannot call any Standard Library ; I/O routines in the critical error handler because they do not ; use DOS calls 1-0Ch, which are the only allowable DOS calls at ; this point. CritErrMsg byte cr,lf byte "DOS Critical Error!",cr,lf byte "A)bort, R)etry, I)gnore, F)ail? $" MyInt24 proc far push dx push ds push ax push cs pop ds Int24Lp: lea dx, CritErrMsg mov ah, 9 ;DOS print string call. int 21h mov ah, 1 ;DOS read character call. int 21h and al, 5Fh ;Convert l.c. -> u.c. cmp al, 'I' ;Ignore? jne NotIgnore pop ax mov al, 0 jmp Quit24 NotIgnore: cmp al, 'r' ;Retry? jne NotRetry pop ax mov al, 1 jmp Quit24 NotRetry: cmp al, 'A' ;Abort? jne NotAbort pop ax mov al, 2 jmp Quit24 NotAbort: cmp al, 'F' jne BadChar pop ax mov al, 3 Quit24: pop ds pop dx iret BadChar: mov ah, 2 mov dl, 7 ;Bell character jmp Int24Lp MyInt24 endp Main proc mov ax, dseg mov ds, ax mov es, ax meminit mov ax, 0 mov es, ax mov word ptr es:[24h*4], offset MyInt24 mov es:[24h*4 + 2], cs mov ah, 5 mov dl, 'a' int 21h rcl Value, 1 and Value, 1 mov ErrCode, ax printf byte cr,lf,lf byte "Print char returned with error status %d and " byte "error code %d\n",0 dword Value, ErrCode Quit: ExitPgm ;DOS macro to quit program. Main endp cseg ends ; Allocate a reasonable amount of space for the stack (8k). ; Note: if you use the pattern matching package you should set up a ; somewhat larger stack. sseg segment para stack 'stack' stk db 1024 dup ("stack ") sseg ends ; zzzzzzseg must be the last segment that gets loaded into memory! ; This is where the heap begins. zzzzzzseg segment para public 'zzzzzz' LastBytes db 16 dup (?) zzzzzzseg ends end Main