/********************************************************************

	runtime_err.c
	Z-World, 2000

	This program demonstrates a custom runtime-error handler.

	The function defineErrorHandler() allows the user to install
	their own custom function to handle runtime errors.
	Information about the runtime error is pushed on the stack
	and can be accessed by the function.  When the custom error
	handler is running, the following information will be on
	the stack:

				SP+0	return adress for error handler call
				SP+2	runtime error code
				SP+4	0x0000 (can be used for additional information)
				SP+6	XPC when exception() was called (upper byte)
				SP+8	address where exception() was called	

	Test some different runtime errors by uncommenting different
	parts of the code.

	Notice that this particular user-defined error handler will
	pass execution back to the main program if a domain or
	range error occurs.  Most runtime errors called from Dynamic C
	libraries will exit if execution continues.  The domain and
	range errors in the math library, however, will continue on
	with the appropriate value (INF, zero, etc.) if the user-defined
	error handler passes control back to the executing program.
	In addition, the unexpected interrupt error handler will
	return from the interrupt and continue if execution is passed
	back to it.

 ********************************************************************/

root
void myRuntimeErrHandler(void *);

main()
{
	int	i, *ip;
	float	f;

	defineErrorHandler(myRuntimeErrHandler);

				
	// the default error handler will halt on these; but the
	// custom handler will pass execution back to the program
	f = acos(2);
	f = fmod(1, 0);

	// this runtime error will halt execution (make sure that
	// pointer checking is enabled in compiler options)
	ip = (int *)0x0000;
	*ip = 4;
	
	return(0);
}

/********************************************************************

	This runtime error handler will print out relevant information
	about the error that occurs then pass execution back to the
	function that called it (although in most cases that function
	will then exit to Dynamic C with a runtime error).

	Note that this function should always be in root memory!

 ********************************************************************/

root		// always compile this function to root!
void myRuntimeErrHandler()
{
	static int	error, xpc, addr;

	///////////////////////////////////////////
	// get all the relevant parameters off the stack
#asm
	ld		hl, (sp+@SP+2)
	ld		(error), hl		; get the runtime error code
	ld		hl, (sp+@SP+6)
	ld		(xpc), hl		; get the XPC where exception() was called
	ld		hl, (sp+@SP+8)
	ld		(addr), hl		; get the address exception() was called
#endasm

	///////////////////////////////////////////
	// customized code can be added here

	xpc = (xpc >> 8) & 0x00FF;		// we want the upper byte only
	printf("Runtime error %d occurred at %02x:%04x\n",
				error, xpc, addr);

	// Now let's pass execution back if it is a domain or range error
	// but trap all the other runtime errors.

	if ((error == ERR_DOMAIN) || (error == ERR_RANGE)) {
		printf("  - Execution being passed back to program...\n");
		return;
	}
					
	printf("  - This error will halt the program.\n");

	///////////////////////////////////////////
	// halt all errors that we don't want to pass back to execution,
	//		i.e. everything except domain and range errors
#asm
	; Dynamic C expects the XPC in the upper byte of HL,
	; the error code in the lower byte, and the address of
	; the error in HL', i.e. HL = [xpc][error], HL' = [address]

	ld		hl, (error)				; load L
	ld		a, (xpc)					
	ld		h, a						; load H
	exx
	ld		hl, (sp+@SP+14)		; load HL'
	exx
	call	_xexit					; halt execution (Dynamic C will notice)
#endasm
}
