// CRITERR.C -- Install a critical error (INT 24h) handler.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <conio.h>
#include <ctype.h>
#include <dos.h>
#include <fcntl.h>
#include <errno.h>
#include <pldos32.h>
#include <pharlap.h>

#ifdef __WATCOMC__
#define _getch getch
#define _itoa itoa
#define _intdos intdos
#define _REGS REGS
#define _O_RDONLY O_RDONLY
// Names of regs in _REGS union are nonstandard
#define dx              edx 
#endif

void CauseCritErr(void);
int __STKCALL ProcessCritErr(FARPTR pIntStackFrame);
void ReadKey(UCHAR *pch);
void WriteStr(char *pStr);
// functions in CRITHAND.ASM
void CritErrHand();	
void GetSegRegs();
extern ULONG pProcessCritErr;	// offset of ProcessCritErr()

int main()
{
	CONFIG_INF Config;
	REALPTR	RealCritErrVec;
	FARPTR ProtCritErrVec;
	FARPTR OurCritErrHand;

//
// Get valid segment registers, for use by critical error handler, and
// get addr of C routine handler will call
//
	GetSegRegs();
	pProcessCritErr = (ULONG) ProcessCritErr;

//
// Tell the user to open the A: floppy door, so this program will be
// able to cause critical errors.
//
	printf("Please open the A: floppy disk door, press any key to \
continue\n");
	_getch();

//
// Cause a critical error by attempting to read from the open A: floppy
// disk drive.  The DOS Abort/Retry/Fail query should appear.
//
	printf("\n** DOS should receive this critical error **\n");
	CauseCritErr();

//
// Save previous critical error (INT 24h) handler, and install our
// handler to always get control in protected mode.
//
	_dx_config_inf(&Config, (UCHAR *) &Config);
	_dx_rmiv_get(0x24, &RealCritErrVec);
	_dx_pmiv_get(0x24, &ProtCritErrVec);
	FP_SET(OurCritErrHand, CritErrHand, Config.c_cs_sel);
	_dx_apmiv_set(0x24, OurCritErrHand);

//
// Cause a critical error again.  Our handler should get control.
//
	printf("\n** Our critical error handler should get this one **\n");
	CauseCritErr();

//
// Restore original critical error vectors
//
	_dx_rpmiv_set(0x24, ProtCritErrVec, RealCritErrVec);

	return 0;
}

void CauseCritErr(void)
{
	int	handle;
	UINT	errv;

	printf("Causing critical error by opening file on A: drive\n");
	errv = _dos_open("A:\foo", _O_RDONLY, &handle);
	if (errv == 0)
		printf("Open succeeded!!!\n");
	else
	{
		printf("Open failed, DOS error code = %d\n", errv);
		switch (errno)
		{
		case EACCES:
			printf("Access denied\n");
			break;
		case EINVAL:
			printf("Invalid access mode\n");
			break;
		case EMFILE:
			printf("Too many open file handles\n");
			break;
		case ENOENT:
			printf("Path or file not found\n");
			break;
		default:
			printf("Unknown errno: %d\n", errno);
			break;
		}
	}
	return;
}

//
// ProcessCritErr - This routine is called by the assembly language
//		critical error handler routine in CRITHAND.ASM
// 		Force stack-based calling conventions, and turn off any 
//		stack overflow checking.
//
// At critical error time, you can only make DOS calls 01h through 0Ch,
// so we can't use any C library calls that would use any other DOS calls
// (such as printf(), which uses Write File to write to standard output).
//
// Returns action code (0-3) to return to DOS in AL.
//
#ifndef __MSC32__
#pragma off(check_stack);
#endif
int __STKCALL ProcessCritErr(FARPTR pIntStackFrame)
{
	INT_STACK_FRAME IntFrame;	// protected mode interrupt stack frame
	struct {
		USHORT dos_ax;
		USHORT dos_bx;
		USHORT dos_cx;
		USHORT dos_dx;
		USHORT dos_si;
		USHORT dos_di;
		USHORT dos_bp;
		USHORT dos_ds;
		USHORT dos_es;
		USHORT dos_ip;
		USHORT dos_cs;
		USHORT dos_flags;
	} DosCEFrame;			// real mode DOS INT 24h stack frame
	REALPTR	pRealCEFrame;		// Real mode ptr to critical err frame
	UCHAR	ch;
	char	buf[25];

//
// Get the interrupt stack frame, and use that to get the address of the
// real mode stack frame when DOS issued the INT 24h
//
	ReadFarMem(&IntFrame, pIntStackFrame, sizeof(IntFrame));
	if (IntFrame.int_inum != 0x24 || !(IntFrame.int_dxfl & IFL_RMODE))
	{
		printf("Not INT 24h, or didn't originate in real mode!!\n");
		return 3;
	}
	RP_SET(pRealCEFrame, IntFrame.int_esp, IntFrame.int_ss);
	ReadRealMem(&DosCEFrame, pRealCEFrame, sizeof(DosCEFrame));
	WriteStr("Critical error occurred on INT 21h function $");
	_itoa((int) (DosCEFrame.dos_ax >> 8), buf, 16);
	strcat(buf, "h\r\n$");
	WriteStr(buf);

//
// Ask the user what action to take
//
	do {
		WriteStr("Enter action code (0=ignore, 1=retry, \
2=terminate, 3=fail): $");
		ReadKey(&ch);
		WriteStr("\r\n$");
	} while (ch < '0' || ch > '3');

	return (int) (ch - '0');
}

//
// Wait for a key to be typed with INT 21h function 01h
//
void ReadKey(UCHAR *pch)
{
	union _REGS r; 

        memset(&r, 0, sizeof(r));
        r.h.ah = 0x01;
        _intdos(&r, &r); 
	*pch = (UCHAR) r.h.al;
	return;
}

//
// Write a character string to the console with INT 21h function 09h
//
void WriteStr(char *pStr)
{
	union _REGS r; 

        memset(&r, 0, sizeof(r));
        r.h.ah = 0x09;
	r.x.dx = (UINT) pStr;
        _intdos(&r, &r); 
	return;
}
