Category : C Source Code
Archive   : TC_CRIT.ZIP
Filename : CRITTER.C

Output of file : CRITTER.C contained in archive : TC_CRIT.ZIP

#if !defined(__SMALL__) && !defined(__MEDIUM__)
#error This program will only work with SMALL and MEDIUM memory models.

#pragma inline


With all these include files, you should be able to compile this
program with all warnings on (-w) and not get a single error
or warning.

int handler(int errval, int ax, int bp, int si);

main() - Main function

This function will simply try to do a findfirst(), also known as a DIR list,
for the pathname entered on the command line. This should cause enough
error to jump DOS into the critical error handler routine.

Compile this program with Turbo C with the following command line:

tcc (-ms | -mm ) critter cxltcs.lib



main(int argc, char *argv[])
struct ffblk ff;

/* Initialize the CXL Video Library Functions */

/* Initialize our own crittical error handler! */

/* If nothing was entered on the command line, try to
print something to AUX (which is COM1) and hope
they don't have modem turned on! */
if ( argc<2 )
/* For some reason with TC, if you keep pressing FAIL, or
IGNORE, the fprintf() function will end up returning
with a pass code. Must be in the TC library. */
if ( fprintf(stdaux, "\n\n")==EOF )
printf("Error accessing AUX\n\n" );
printf("Access AUX without error\n\n" );
if ( findfirst(argv[1], &ff, FA_RDONLY|FA_DIREC|FA_ARCH) )
printf("Error accessing this pathname: %s\n\n", argv[1] );
printf("Access without error: %s\n\n", argv[1] );


#ifdef ZERO

The following is some information I have found on the DOS Critical
Error Interrupt and DOS Error Codes. I think the more you understand
the DOS Critical Error process, the better off you are to make
modifications to this program.

You may decide to use the DOS Get Extended Error function to get the
most information about an error that you possibly can. This is one
of the few DOS functions that you are allowed to call from a DOS
Critical Error Handler. From there you can take action based on the
Locus, Class, and Action codes returned. You could handle the
error entirely on your own. For example, if the DOS action code is

"Immediate Exit", you might want to do just that! It's up to you
on how far you want to take this handler, but I think this is a
good base that you can start with. Enjoy...

DOS Fn 59H: Get Extended Error Info

| Expects | AH | 59H DOS 3.0+
\---------* BX | 0000H (version number: 0000H for DOS 3.0, 3.1, and 3.2)
| Returns | AX | extended error code (0 if no error has occurred)
\---------* BH | error class
| BL | suggested action
| CH | locus (where the error occurred)

Description: Use this function to figure out what to do after a DOS function
failed because of an error DOS 3.0+ only. You can use this:
o inside an INT 24H Critical Error handler
o after any INT 21H DOS function which signals an error with CF
o after FCB-style functions which return AL=0ffH means an error

See DOS Error Codes for a full listing of possible error codes,
classes, suggested actions, and loci that may be returned by
this function.

Versions: This function is not available before DOS 3.00. With DOS 2.x,
when CF returns indicating an error, use your own program logic
to decide what action to take. With DOS 3.0+, when a function
returns CF=1, you are advised to ignore the error code returned
in AX, call this function, and take the action suggested in BL.

DOS Error Codes

DOS 2.0+ Error Codes (returned in AX when Carry Flag is set)

Error # Error #
Hex Dec Meaning Hex Dec Meaning
*** *** ****************************** *** *** ****************************
1 1 Invalid function number 0aH 10 Invalid environment
2 2 File not found 0bH 11 Invalid format
3 3 Path not found 0cH 12 Invalid access code
4 4 Too many open files 0dH 13 Invalid data
5 5 Access denied 0eH 14 (not used)
6 6 Invalid handle 0fH 15 Invalid drive specified
7 7 Memory ctrl blocks destroyed 10H 16 Can't remove current dir
8 8 Insufficient memory 11H 17 Not same device
9 9 Invalid memory block address 12H 18 No more matching files

*** *** **************** DOS 3.x Fn 59H additions **************************
0 0 No error occurred (don't expect 0 unless no error has EVER occurred)
13H 19 Attempted write on write-protected disk - 0 \
14H 20 Unknown unit ID 1 |
15H 21 Disk drive not ready 2 |
16H 22 Unknown command 3 |
17H 23 Disk data error (CRC error) 4 *=>These correspond to
18H 24 Bad request structure length 5 | errors 0-0cH passed in
19H 25 Disk seek error 6 | the DI register to an
1aH 26 Unknown disk media type 7 | INT 24H critical error
1bH 27 Disk sector not found 8 | handler and they match
1cH 28 Printer out of paper 9 | the values in AL after
1dH 29 Write fault error 0aH | errors in INT 25H/26H
1eH 30 Read fault error 0bH |
1fH 31 General failure ---------------------- 0cH /
20H 32 File sharing violation
21H 33 File locking violation
22H 34 Invalid disk change
23H 35 FCB unavailable (too many FCBs)
24H 36 Sharing buffer overflow
25H-31H (reserved)
32H 50 Network request not supported
33H 51 Remote computer not listening
34H 52 Duplicate name on network
35H 53 Network name not found
36H 54 Network busy
37H 55 Network device no longer exists
38H 56 Net BIOS command limit exceeded
39H 57 Network adapter hardware error
3aH 58 Incorrect response from network
3bH 59 Unexpected network error
3cH 60 Incompatible remote adapter
3dH 61 Print queue full
3eH 62 Not enough space for print file
3fH 63 Print file was deleted
40H 64 Network name was deleted
41H 65 Access denied
42H 66 Incorrect network device type
43H 67 Network name not found
44H 68 Network name limit exceeded
45H 69 Net BIOS session limit exceeded
46H 70 Temporarily paused
47H 71 Network request not accepted
48H 72 Print or disk redirection is paused
49H-4fH (reserved)
50H 80 File already exists
51H 81 (reserved)
52H 82 Cannot make directory entry
53H 83 "fail" error from INT 24H Critical Error handler
54H 84 Too many redirections
55H 85 Duplicate redirection
56H 86 Invalid password
57H 87 Invalid parameter
58H 88 Network data fault

| Error Class | These codes are meant to provide additional information to
\-------------/ help you determine how to handle the error. Fn 59H returns
these informational codes in BH.

Class #
Hex Dec Meaning (returned in BH by Fn 59H)
*** *** *********************************************************************
1 1 Out of resource: no more FCBs, memory, channels, file handles, etc.
2 2 Temporary situation: this will go away in a while (eg, locked file)
3 3 Authorization problem: You need higher permission to do this
4 4 Internal error: DOS is at fault
5 5 Hardware failure
6 6 System failure: DOS is at fault
7 7 Application error: passed bad info to DOS, inconsistent request, etc.
8 8 Not found: requested file/item can't be found
9 9 Bad format: file/item invalid format (EXE file bad, bad disk, etc.)
0aH 10 Locked: file/item locked
0bH 11 Media error: wrong disk, disk CRC error, etc.
0cH 12 Already exists: collision with existing file/item
0dH 13 Unknown error class: classification doesn't exist or is inappropriate

| Suggested Action | These codes indicate appropriate action to take when an
\------------------/ error occurs. The idea is to limit your coding by NOT
looking for specific error numbers in your application. Instead, maintain
upward compatibility by taking the suggested actions listed below. Fn 59H
returns these codes in BL.

Hex Dec Meaning (returned in BL by Fn 59H)
*** *** *********************************************************************
1 1 Retry: Retry the operation a few times. If the error continues to
occur, ask the user if he wants to continue or quit.

2 2 Delay Retry: Delay a while and retry the operation a few times. If
the error continues to occur, ask the user if she wants to continue
or quit.

3 3 User Input: If data presented to DOS was entered by a user, ask the
user to re-enter input (eg, bad drive ID or pathname).

4 4 Abort: Abort the application, but it's OK to perform cleanup
operations such as closing files, updating indexes, releasing memory
blocks, etc.

5 5 Immediate Exit: Abort immediately without attempting cleanup. The
system is in an unhealthy state and immediate exit is best.

6 6 Ignore: the error doesn't make any difference

7 7 Retry after user action: the user needs to perform some action, such
as inserting a diskette. Prompt the user and then retry.

| Error Locus | These codes are meant to help you figure out where the error
\-------------/ occurred. Fn 59H returns these informational codes in CH.
Locus #
Hex Dec Meaning (returned in CH by Fn 59H)
*** *** *********************************************************************
1 1 Unknown: No specific area to pin-point error source
2 2 Block device: error occurred on disk or tape drive
3 3 (reserved)
4 4 Character device
5 5 Memory

| DOS Versions | Error handling is upwardly-compatible for all DOS versions.
\--------------/ These general rules apply:

o DOS 1.x: indicates some errors by placing 0ffH is AL upon return.

o DOS 2.x: New 2.x calls indicate errors by setting the Carry Flag (CF=CY=1)
and putting an error code into AX.

o DOS 3.x: 2.x and 3.x calls still return error codes in AX when CF=CY, but
there is no guarantee that future calls will do so. You are
advised to use DOS Fn 59H to obtain error information.

INT 24H: Critical Error Handler

This vector (0000:0090) is set to the address that will gain control when a
DOS device driver encounters a critical error. The address at this vector is
copied into the PSP by DOS Fns 26H (Build PSP) and 4bH (EXEC). Upon exit to
the parent process, the parent's values are restored to the INT 24H vector
from its PSP.

The normal DOS INT 24H handler is the code that displays the message:

Abort, Retry, Ignore? _

which you see when a diskette drive door is open or the printer is off line.

Most sophisticated programs supply their critical error handler to avoid
having DOS screw up a carefully-maintained screen display.
| Entry | Upon entry to the INT 24H handler, the registers are as follows:
BP:SI => a Device Header (see below) which will help locate the failing device
DI = bits 0-7 contain an error code (bits 8-15 are undefined)
AL = if AH bit 7=0, AL is the drive number (0=A, 1=B, etc.)
AH = error information as follows:
| |0| | | | | | Note: when bit 7=1, bits 0-6 are not defined
***-*******-*-*** bit
| | | | \*/ \=> 0: operation type: 0=read, 1=write
| | | | \====> 1-2: affected disk area:
| | | | 00=system files, 01= File Allocation Table
| | | | 10=directory, 11=data area
| | | \=======> 3: 1=FAIL exit is allowed ----\
| | \=========> 4: 1=RETRY exit is allowed *=> DOS 3.x only
| \===========> 5: 1=IGNORE exit is allowed --/
\===============> 7: device type: 0=disk, 1=other

You can handle the error by prompting the user to take action (eg, close the
drive door or turn on the printer). DOS functions above 0cH should not be
used (except Fn 59H is OK--don't use Fn 30H to obtain the DOS version).

You can obtain additional information from DOS Fn 59H and/or the device
driver header block pointed to by BP:SI.
| Exit | After handling or trying to correct the error, you can set AL with an
\------/ action code and get back to DOS indicating one of these actions:
AL=0: ignore the error
AL=1: retry the operation
AL=2: abort. Terminate via the INT 23H address (as on Ctrl-Break)
AL=3: return to application indicating a failed DOS function

DOS 3.x Examine AH on entry to see which actions are not allowed.

The AL=3 option seems ideal. For instance, if an "Open File" operation
caused the error because of no disk in the drive, you could let your
application take care of it with its normal error handling. However, it has

o The AL=3 option is NOT available with DOS version prior to DOS 3.0
o DOS always returns error code 53H which is not too informative

| The Stack | Upon entry into the INT 24H error handler, the stack is in a
\-----------/ known state as described here:

IP Return address to get
CS back to DOS via IRET
AX,BX,CX,DX,SI,DI,BP,DS,ES Application program registers before INT 21H
IP Return address to get directly
CS to the application
Flags Application's Flags register

This information can be used to determine which DOS function failed (in AH as
saved on the stack) and any parameters in other registers. But its primary
value is that it lets your INT 24H handler restore the registers and exit
directly to the application program.

You will typically want to pretend to be DOS and set AX with a DOS error code
and set CF=CY to indicate an error (most good software uses this technique).
If you exit to the application this way, the manual says that DOS will be in
an "unstable" state until a Fn higher than 0cH is used. Just be sure that
the application does a Fn 30H or some such as it handles the error.

The INT 24H vector is NOT taken for disk errors occurring during INT 25H/26H

Device Header Layout

The binary image file of a device driver begins with this structure. During
installation, the NextDevice field is updated to fit in with device chain.

Offset Size Contents
****** **** ******************************************************************
/-------*-------\ NextDevice: FAR address of next device in chain
+0 4 |offset segment| (offset=0ffffH if this the last/only driver)
+4 2 |DevAttr| bit fields that make up the Device Attribute
*---*---* (See below)
+6 2 |Stratgy| offset address of device STRATEGY routine
+8 2 |Intrupt| offset address of device INTERRUPT routine
+0aH 8 |'L' 'P' 'T' '1' 20H 20H 20H 20H| blank-padded Device_Name (or
\---*---*---*---*---*---*---*---/ block device sub-unit number)

Device Attribute

The DevAttr field of the Device Header indicates properties of a device.
1 1 1 1 1 1
| | | |0| |0 0 0 0| |0 0| | | | |
*******-***-*-*-*-***-*-********* bit# mask value
| | | | | | | | \=> 0: 1=standard input device (a & 0001H)
| | | | | | | \===> 1: 1=standard output device (a & 0002H)
| | | | | | \=====> 2: 1=standard NUL device (a & 0004H)
| | | | | \=======> 3: 1=clock device (a & 0008H)
| | | | \=============> 6: 1=supports logical devices (a & 0040H)
| | | \=======================> 11: 1=supports open/close/RM (a & 0800H)
| | \===========================> 13: 1=non-IBM block device (a & 2000H)
| \=============================> 14: 1=supports IOCTL (a & 4000H)
\===============================> 15: 1=character device; 0=block (a & 8000H)

o set the standard I/O bits (0 and 1) when you replace the CON driver

o the NUL device cannot be reassigned

o The logical devices bit (DOS 3.2+) indicates support of device requests 17H
and 18H (Get/Set Logical Device) used in managing the "phantom floppy"
concept of the DRIVER.SYS device driver.

o the open/close/RM bit (DOS 3.0+) indicates support for Removable Media-
related device requests (0dH to 0fH) so a block device driver can optimize
with local buffering or a character device can send initialization sequences

o the non-IBM device bit affects the action of the 'Build BPB' request (02H)

o the IOCTL bit indicates support for device utilities which use DOS Fn 44H

o the character device bit affects Input and Output requests and determines
the meaning of the 'device name' field of the Device Header If this bit
is 0, the device is a block device (usually a disk drive).

o the clock device bit indicates a replacement for the CLOCK$ device. CLOCK$
is a character device which handles device requests to input and output
exactly 6 bytes. An input request (command code 4) should return 6 bytes
indicating the current time/date and an output request (command code 8)
should accept 6 bytes to set the clock/calendar. The format for CLOCK$ I/O
Offset Size Contents
****** **** ***********************************************
+0 2 | date | days since January 1, 1980
+2 1 |min| current minute (0-59)
+3 1 |hrs| current hour (0-24)
+4 1 |sec| current second (0-59)
+5 1 |hun| current hundredth of a second (0-99)



This function replaces DOS's crude critical interrupt handler with a nice
looking function to popup a window and handle the DOS critical error
with class.


/* These are defined by DOS. */
#define IGNORE 0
#define RETRY 1
#define ABORT 2
#define FAIL 3 /* DOS 3.x only! */

int handler(int errval, int ax, int bp, int si)
int code;
unsigned ah = (ax>>8)&0x00ff; /* The AH register */

static char allowed[5], /* Allowed action characters */
ch, /* Holds the keyboard character */
msg[80], /* Temp buffer for strings */
device[9], /* Holds device name (ie. LPT1) */
far *dev_name; /* Points to the device name */

/* Canned DOS messages. */
static char *err_msg[] ={"Attempted write on write protected disk",
"Unknown unit ID",
"Disk drive not ready",
"Unknown command",
"Disk data error (CRC error)",
"Bad request structure length",
"Disk seek error",
"Unknown disk media type",
"Disk sector not found",
"Printer out of paper",
"Write fault error",
"Read fault error",
"General failure",
"Unknown error code"};

/* if out of the range of our messages, just say don't know */
if ( errval>0x0c )

/* Open the window and tell them it is a DOS error */

/* Define the shadow if you like shadows... */
/* wshadow(DGREY|_BLACK); */

AH gives a lot of information on entry to this routine. Among
some of this information bit 7 will be zero (0) if it
is a disk error, and a one (1) if it is another error (like
a printer error).
if ( (ah & 0x80) )
/* Point to the device header, then the device name which
is 8 characters offset into the header! */
dev_name = (char far *) MK_FP(bp,si);
dev_name += 0x0a;

The device name is 8 characters long and padded with spaces.
This is hardly an ASCIIZ string that C will handle gracefullt!
So we get one character at a time from the device name and
stuff it into our own character string buffer and terminate
it with a zero.

(Lets use int code for a counter...)
for (code=0; code<8 && dev_name[code]!=' '; code++ )
sprintf(msg, "%s error on device %s", (ah&0x01) ? "Write" : "Read" ,
sprintf(msg, "%s error on drive %c", (ah&0x01) ? "Write" : "Read" ,
(char)('A'+(ax&0x00ff)) );

wcenters(1, WHITE|_RED, msg);
sprintf(msg, "\"%s\"", err_msg[errval]);
wcenters(2, WHITE|_RED, msg);

We have to act according to the DOS version we are running. DOS
versions 3.x+ will allow extra stuff when a DOS error occurs. We
try to make use of as much information as we can.
if ( _osmajor>2 )
What we do here is build a string based on what actions
are allowed in the error handler. We test bits in the AH
register to see which actions are allowed.

Bit Action
3 1=Fail allowed
4 1=Retry allowed
5 1=Ignore allowed


/* Abort is always allowed! */
strcpy(msg, "Abort, ");

if ( ah&0x10 ) /* Is Retry allowed? */
strcat(msg, "Retry");

if ( ah&0x20 ) /* Is Ignore allowed? */
strcat(msg, ", Ignore");

if ( ah&0x8 ) /* Is Fail allowed? */
strcat(msg, ", Fail");
strcat(msg, "?");

/* If not DOS 3.x+, then simply allow the good old 3 actions! */
strcpy(msg, "Abort, Retry, Ignore?");
strcpy(allowed, "ARI");

/* Display the message in the center of the window. */

Then get a character and only allow the ones which match
actions we are going to allow.
asm mov ax,0
asm int 16h
ch = _AL & 0xdf ;
} while ( strchr(allowed,ch )==NULL );

/* Now lets set the return code to the correct code. */
switch (ch)
case 'A':
code = ABORT;
case 'R':
code = RETRY;
case 'I':
code = IGNORE;
case 'F':
code = FAIL;


/* end of handler() */

  3 Responses to “Category : C Source Code
Archive   : TC_CRIT.ZIP
Filename : CRITTER.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: