//  TT8 specific includes
#include        <TT8.h>                 /* Tattletale Model 8 Definitions */
#include        <tat332.h>              /* 68332 Tattletale (7,8) Hardware Definitions */
#include        <sim332.h>              /* 68332 System Integration Module Definitions */
#include        <qsm332.h>              /* 68332 Queued Serial Module Definitions */
#include        <tpu332.h>              /* 68332 Time Processing Unit Definitions */
#include        <dio332.h>              /* 68332 Digital I/O Port Pin Definitions */
#include        <tt8pic.h>              /* Model 8 PIC Parallel Slave Port Definitions */
#include        <tt8lib.h>              /* definitions and prototypes for Model 8 library */

//  general C includes
#include        <stdio.h>
#include        <stdlib.h>

//  includes for the data logger
#include		"assert.h"
#include		"intexcpt.h"
#include		"text_log.h"


//  structure used to track the state of the interrupt/exception handler
struct int_excpt_control  {
	short int_excpt_count[256];		//  counters for the number of times that a particular
									//  interrupt or exception has occured since last reset
	short state;					//  allows us to track what we are doing currently.  If
									//  the exception catching routine causes another exception
									//  then we should do a complete reset.
} int_excpt_control;
#define		NORMAL		0
#define		PROCESSING	1

const char* exception_names[] = {
	"Reset: Initial Stack Pointer",
	"Reset: Initial Program Counter",
	"Bus Error",
	"Address Error",
	"Illegal Instruction",
	"Zero Division",
	"CHK, CHK2 Instructions",
	"TRAPcc, TRAPV Instructions",
	"Privlege Violation",
	"Trace",
	"Line 1010 Emulator",
	"Line 1111 Emulator",
	"Hardware Breakpoint",
	"Coproc protocol violation",
	"Format Err and Uninited Int 1",
	"Format Err and Uninited Int 2"
};	
const int n_exception_names = sizeof (exception_names)/sizeof (char*);




void unexpected_int_init (void)  {	
	static ExcCFrame unexpected;
	vfptr orig_handler, fpointer;
	const short n_counters = sizeof(int_excpt_control.int_excpt_count)/sizeof(short);
	short n;
	
	//  a count of -1 indicates that we are not setup to catch this particular
	//  error
	for (n = 0;  n < n_counters;  n++)
		int_excpt_control.int_excpt_count[n] = -1;
	int_excpt_control.state = NORMAL;
		
	orig_handler = InstallHandler (excpt_handler, 2, &unexpected);
	for (n = 3;  n < 256;  n++)  {
		//  don't interfer with the IRQ3 or 5 handler, which are already
		//  used by the TT8.  IRQ 1 is tied to RxD, and can interrupt, but
		//  is not used by the normal TT8 software.
		//  03/05/98 WJR: Skip installation of unexpected handler 
		//  03/05/98 WJR: for TPU interrupts
		if ((n == 27) || (n == 29) || (n == 64))
			continue;
	
		fpointer = InstallHandler (excpt_handler, n, &unexpected);
		int_excpt_control.int_excpt_count[n] = 0;
		
		#ifdef DEBUG
		//  verify that this vector wasn't already in use by someone else
		if (fpointer != orig_handler)  {
			printf ("vector %d not using normal handler.\n", n);
			printf ("orig vector is 0x%08lX; %d is 0x%08lX\n",
										orig_handler, n, fpointer);
		}
		#endif
	}
	return;
}			


	

void excpt_handler (void)  {
	//  Important: all variables should be static so they don't go on
	//  the stack.  If they go on the stack they screw up our ability
	//  to figure out where the Stack Frame starts.
	static ExcStackFrame* Frame;
	static char* cp;
	static short offset, vector_num, frame_format;
	static int n, m;
	
	//  must grab the stack pointer immediately, before the rest of
	//  our code starts changing the stack pointer.
	cp = (char*)GetStackPtr ();
	
	//  if we were processing an unexpected interrupt or exception when this
	//  one occurred, then do an immediate complete software reset
	if (int_excpt_control.state != NORMAL)  {
		//  This will never return.  The stack, all of the current data, and
		//  the current context are destroyed by this.  It does not, however,
		//  issue a hardware reset
		Reset ();
	}
	int_excpt_control.state = PROCESSING;
		
	
	//  The startup code for the C version of this routine pushed some
	//  additional data on the stack.  There are 8 long words for d0-d7,
	//  7 for a0-a6, and one for the return address from the jsr that
	//  got us to this C subroutine from the InstallHandler routine
	Frame = (ExcStackFrame*)(cp + 16*4);

 	//  Printing the message is screen is only useful if we are running
 	//  on dry land in test mode, but it doesn't hurt anything, and it
 	//  is important even if we aren't trying to debug.  The normal sequence
 	//  of using log_printf is useless for certain exceptions which cause
 	//  an immediate reset and which therefore do not allow the disk routines
 	//  to be called.
 	//  SJM  10/8/99
 	log_printf ("IMPORTANT ERROR: UNEXPECTED INTERRUPT/EXCEPTION\n");
 	#ifndef DEBUG
 	printf ("IMPORTANT ERROR: UNEXPECTED INTERRUPT/EXCEPTION\n");
 	#endif
	log_printf ("Int/Exception type: ");
	#ifndef DEBUG
	printf ("Int/Exception type: ");
	#endif
	offset = (Frame->vectofs & 0x0FFF);
	vector_num = offset / 4;
	if (vector_num < n_exception_names)  {
		log_printf ("%s\n", exception_names[vector_num]);
		#ifndef DEBUG
		printf ("%s\n", exception_names[vector_num]);
		#endif
	}  else  {
		if (vector_num <= 23)  {
			log_printf ("unassigned, reserved\n");
			#ifndef DEBUG
			printf ("unassigned, reserved\n");
			#endif
		}  else  {
			if (vector_num == 24)  {
				log_printf ("Spurious Interrupt\n");
				#ifndef DEBUG
				printf ("Spurious Interrupt\n");
				#endif
			}  else  {
				if (vector_num <= 31)  {
					log_printf ("level %d interrupt autovector.\n", vector_num-24);
					#ifndef DEBUG
					printf ("level %d interrupt autovector.\n", vector_num-24);
					#endif
				}  else  {
					log_printf ("vector_num %d\n", vector_num);
					#ifndef DEBUG
					printf ("vector_num %d\n", vector_num);
					#endif
				}
			}
		}
	}
	
	frame_format = ((Frame->vectofs & 0xF000) >> 12);
	log_printf ("  Status Register = 0x%04X\n", Frame->eSR);
	switch (frame_format)  {
		case 0:
			log_printf ("  Program Counter = 0x%08lX\n", Frame->ePC);
//			log_printf ("  Vector/Offset = 0x%04X\n", Frame->vectofs);
			log_printf ("  Frame Format = 0x%04X\n", frame_format);
			log_printf ("  Vector Offset = 0x%04X\n", (Frame->vectofs&0x0FFF));
			break;
			
		case 2:
			log_printf ("  Next Instruction PC = 0x%08lX\n", Frame->ePC);
//			log_printf ("  Vector/Offset = 0x%04X\n", Frame->vectofs);
			log_printf ("  Frame Format = 0x%04X\n", frame_format);
			log_printf ("  Vector Offset = 0x%04X\n", (Frame->vectofs&0x0FFF));
			log_printf ("  Faulted instruction PC = 0x%08lX\n",
										*((long*)&Frame->states[0]));	
			for (n = 0;  n < 8;  n++)
				log_printf ("  State[%d] = 0x%04X\n", n, Frame->states[n]);
			break;
			
		case 0xC:
			log_printf ("  The bus-error stack frame has multiple formats.\n");
			log_printf ("  First PC = 0x%08lX\n", Frame->ePC);
//			log_printf ("  Vector/Offset = 0x%04X\n", Frame->vectofs);
			log_printf ("  Frame Format = 0x%04X\n", frame_format);
			log_printf ("  Vector Offset = 0x%04X\n", (Frame->vectofs&0x0FFF));
			log_printf ("  Faulted address = 0x%08lX\n",
										*((long*)&Frame->states[0]));	
			for (n = 2;  n < 8;  n++)
				log_printf ("  State[%d] = 0x%04X\n", n, Frame->states[n]);
			break;
				
		default:
			log_printf ("  Program Counter = 0x%08lX\n", Frame->ePC);
//			log_printf ("  Vector/Offset = 0x%04X\n", Frame->vectofs);
			log_printf ("  Frame Format = 0x%04X\n", frame_format);
			log_printf ("  Vector Offset = 0x%04X\n", (Frame->vectofs&0x0FFF));
			for (n = 0;  n < 8;  n++)
				log_printf ("  State[%d] = 0x%04X\n", n, Frame->states[n]);
			break;
	}			

	/*
	//  backup printout of data
	for (n = 0;  n < 9;  n++)  {
		printf ("%02X ", n);
		for (m = 0;  m < 16;  m++)  {
			if ((m%4) == 0)
				printf (" ");
			printf ("%02X ", (short)(cp[n*16 + m]&0xFF));
		}	
		printf ("\n");	
	}
	*/


	//  Now implement code to decide what we should do about the unexpected interrupt.
	//  The general rule is that if it has occured many times, perform a complete reset,
	//  and if not, just ignore it.
	int_excpt_control.int_excpt_count[vector_num]++;
	switch (vector_num)  {
		//  Since these can't happen, we should immediately do a complete
		//  reset if they do
		case 0:		//	Reset: Initial statck pointer (cannot happen)
		case 1:		//	Reset: Initial program counter (cannot happen)
		//  Both of these should already be caught by one of the TT8's
		//  standard interrupt handlers.  If we get them, then there
		//  is a serious problem, and we should do a complete reset
		case 27:	//  Level 3 interrupt autovector
		case 29:	//  Level 5 interrupt autovector
		//  These next two lines refer to illegal instructions, which
		//  are usually used for software emulation of an unimplemented
		//  instruction.  If they occur in our program, then they probably
		//  indicate an unfortunate overwrite of our code.  The only hope
		//  is that it is a seldom exercised sequence, and that an immediate
		//  reset will limit the damage and allow us to continue logging.
		case 10:	//	Line 1010 Emulator
		case 11:	//	Line 1111 Emulator

		//  we never recover from bus errors, so just reset
		case 2:		//	Bus error
		//  illegal instruction can't happen unless major screw up, so rest
		case 4:		//  Illegal instruction
		//  zero division will give bad result, so reset
		case 5:		//	Zero division
			Reset ();
			//  can never get here

		default:
		case 3:		//  Address error
		case 6:		//  CHK, CHK2 instructions
		case 7:		//  TRAPcc, TRAPV Instructions
		case 8:		//	Privledge violation
		case 9:		//	Trace
		case 12:	//	Hardware Breakpoint
		case 13:	//	Coproc protocol violation
		case 14:	//	Format Err and Uninited Int 1
		case 15:	//	Format Err and Uninited Int 2
		case 25:	//  Level 1 interrupt autovector
			//  Allow a couple of these errors, even though we don't expect them,
			//  (Cosmic Rays?) then do a complete reset
			if (int_excpt_control.int_excpt_count[vector_num] > 10)
				Reset ();
			break;	
	}
	int_excpt_control.state = NORMAL;
}	

