/**
 * @file exc.c
 * @brief Exception handling functions
 * 
 */

#include "exc.h"

#include "thread.h"
#include "sys.h"
#include "int.h"
#include "syscall.h"
#include "offset.h"
#include "debug.h"
#include "io.h"

/** Exceptions comments */
 char *exec[] = {"Int", "Mod", "TLBL", "TLBS", "AdEL", "AdES", "IBE", "DBE", "Sys", "Bp", "RI", "CpU", 
	           "Ov", "Tr", "VCEI", "FPE", "WATCH", "VCED"};

/**
 * @brief Exception handler
 * @param stc - pointer to stack
 * 
 * Exception handler is called from sys.S. Parse cp0_cause register and
 * call function that reserve that exception
 * 
 */
void
exception (int *stc)
{
					#ifdef EXCEPTION_DEBUG
					printk("Exception raised..., current thread id = %d, ", current_thread->id);
					#endif

	/* store stack */
	current_thread->kernel_stack_context =  stc;

					#ifdef EXCEPTION_DEBUG
					printk("exception type %s\n", exec[cp0_cause_exccode (read_cp0_cause ())]);
					#endif 

	/* parsing cp0_cause */
	switch (cp0_cause_exccode (read_cp0_cause ())) {

	/*
	 * Interrupt exception is called when at least one of
	 * the unmasked interrupt signals is asserted. All interrupt
	 * signals are stored in the IP field of the Cause register.
	 * Software interrupts no 0 and 1 can be cleared by setting
	 * corresponding bit to 0.n the Cause register. Other hardware
	 * interrupts are deasserted by handling the condition causing
	 * the interrupt request.
	 */
	case EXC_INT:
		interrupt ();
		break;

	/*
	 * System Call exception is generated by the SYSCALL
	 * instruction. This exception is used to call
	 * operating system functions (API). A function code
	 * can be also stored in the SYSCALL opcode. Note
	 * that the return address should be increased by 4 to return
	 * to the instruction after the syscall. When the syscall is
	 * called in a branch delay, a more complicated algorithm
	 * have to be applicated (also this can be explicitly
	 * prohibited).
	 */
	case EXC_SYS:
		syscall ();
		break;

	/*
	 * Address Error exceptions are caused by an attempt to
	 * address an unaligned data or instruction. The invalid
	 * virtual address is stored in the BadVAddr register. This
	 * exception is probably a program error and is fatal
	 * in the most cases.
	 */
	case EXC_ADEL:		/* load data or fetch instruction */
	case EXC_ADES:		/* store data */
		panic("Attempt to address an unaligned data or instruction. Machine halted.\n");
		break;

	/*
	 * Debugging exceptions are debug feature of the MIPS
	 * instruction set.
	 */
	 
	 /*
	 * The Breakpoint is caused by the BREAK instruction.
	 * The BREAK instruction has two additional parameters
	 * which can be extracted from the opcode.
	 * 
	 * If there isn't an instuction in the brach delay slot then ignore,
	 * else panic.
	 */
	case EXC_BP:		/* breakpoint */
		if (cp0_cause_bd(read_cp0_cause())) {	/* read bd flag from cause register */
			/* bd flag is set -> there is an instruction in branch delay slot */
			panic("BREAK exception with an instruction in branch delay slot." 
				  "Machine hlated. \n");			
		} else {
			/* bd flag is NOT set -> there is NOT an instruction in branch delay slot */
			/* IGNORE BP exeption:
			 * To resume execution, the EPC register must be altered so that the BREAK
			 * instruction does not re-execute; this is accomplished by adding a value of
			 * 4 to the EPC register (EPC register + 4) before returning.
			 * (R4000_Users_Manual_2Ed)
			 */
			stc[OFFSET_EPC/4] = stc[OFFSET_EPC/4] + 4;
			
		}
		break;
	 
	 /*
	 * The Trap occurs if the test condition of the trap
	 * instruction is true. Often used by compilers for
	 * run-time range checking.
	 */
	case EXC_TR:		/* trap */
		break;			/*unhandled*/
	 
	 /*
	  *The Overflow is generated by signed arithmetic instructions
	  * when an arithmetic overflow occurs.
	  */
	case EXC_OV:		/* overflow */
		panic ("Arithmetic overflow occured" "Machine halted.\n");
		break;

	/*
	 * Coprocessor Unusable exception is caused by attempt to
	 * execute a coprocessor instruction without appropriate
	 * permissions. The coprocessor number is stored into the
	 * Cause register, field ce. It is up to the operating
	 * system whether the instruction is tested and simulated or
	 * whether the current process is punished. This exception
	 * is fatal in most cases.
	 */
	case EXC_CPU:
		panic ("Attempt to execute a coprocessor instruction without appropriate permissions"
		       "Machine halted.\n");
		break;

	/*
	 * Reserved Instruction exception is caused by an attempt to
	 * execute an opcode which was not recognized as a legal
	 * instruction. This exception indicates a program error
	 * and is usually fatal.
	 */
	case EXC_RI:
		panic ("Attempt to execute an opcode which was not recognized as a legal instruction."
		       "Machine halted.\n");
		break;
	
	/*
	 * A Watch exception occurs when a load or store instruction references the
	 * physical address specified in the WatchLo/WatchHi System Control
	 * Coprocessor (CP0) registers. The WatchLo register specifies whether a
	 * load or store initiated this exception.
	 */
	case EXC_WATCH:
		break;	/* ignore */

	/*
	 * A Bus Error exception is raised by board-level circuitry for events such as
	 * bus time-out, backplane bus parity errors, and invalid physical memory
	 * addresses or access types. This exception is not maskable.
	 */

	case EXC_IBE:
	case EXC_DBE:
		panic("Invalid physical memory addresses or access type."
			  "Machine halted.\n");
		break;
	
	default: 
					printk("Exception default\n");
					___halt(); //!!!!!to delete after debugging
		break;	/* ignore non-specified exceptions */
		
	}
	/* Return from the exception */
}
