// CRITERR.C -- Use _seterrormode() to control critical error
//		(INT 24h) processing.

#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>

//
// Prot mode segment regs, needed by critical error handler in CRITHAND.ASM
//
USHORT CSReg;
USHORT DSReg;
USHORT ESReg;
USHORT FSReg;
USHORT GSReg;

void CauseCritErr(void);
int __cdecl ProcessCritErr(FARPTR pIntStackFrame);
void CritErrHand();	// in CRITHAND.ASM

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

//
// Get our segment registers, for use by critical error handler
//
	_asm {
		mov CSReg,cs
		mov DSReg,ds
		mov ESReg,es
		mov ax,fs
		mov FSReg,ax
		mov ax,gs
		mov GSReg,ax
	}

//
// 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_rmiv_get(0x24, &RealCritErrVec);
	_dx_pmiv_get(0x24, &ProtCritErrVec);
	FP_SET(OurCritErrHand, CritErrHand, CSReg);
	_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();

//
// Set critical error to always fail, and cause a critical error
//
	_seterrormode(_CRIT_ERROR_FAIL);
	printf("\n** This call should just be failed immediately **\n");
	CauseCritErr();

//
// Set critical error to be handled, and cause a critical error
//
	_seterrormode(_CRIT_ERROR_PROMPT);
	printf("\n** Our handler should get get control again **\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
//
// 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.
//
int __cdecl 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;
	UCHAR	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));
	_dos_str_out("Critical error occurred on INT 21h function $");
	_itoa((int) (DosCEFrame.dos_ax >> 8), buf, 16);
	strcat(buf, "h\r\n$");
	_dos_str_out(buf);

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

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