#include <regdef.h>
#include <xcpt_asm.h>

		.section .exc_vect
_xcpt_vector_tbl:
		.fill	16, 4, 0		
			
		.text

LEAF(_xcpt_handler)
		.set noreorder
		.set noat
		/* Note: exceptions do not save and restore registers k0 and k1.
		* on entry, k1 = exception class.
		*/
		
		/* allocate exception stack frame (on 8-byte boundary) */
		subu k1, sp, XCP_SIZE
		srl k1, 3 	/* shift right/left -> alligned on boundary */
		sll k1, 3

		/* save enough registers to get by */
		sw AT, XCP_AT(k1)
		sw v0, XCP_V0(k1)
		sw v1, XCP_V1(k1)
		sw a0, XCP_A0(k1)
		sw a1, XCP_A1(k1)
		sw a2, XCP_A2(k1)
		sw a3, XCP_A3(k1)
		sw sp, XCP_SP(k1)
		sw ra, XCP_RA(k1)
		
		/* get coprocessor 0 exception state */
		mfc0 a0, CP0_CR
		mfc0 a1, CP0_SR
		mfc0 a2, CP0_BADDR
		mfc0 a3, CP0_EPC

		/* we can safely use AT now */
		.set at

		/* switch to using sp to point at exception frame */
		move sp, k1
		
		/* nothing sensible to store for k0/k1, store zero */
		sw zero, XCP_K0(sp)
		sw zero, XCP_K1(sp)		

		/* we are now interruptible: dump all remaining state
		* into the exception stack frame.
		*/
		/* coprocessor exception state */
		sw a0, XCP_CR(sp)
		sw a1, XCP_SR(sp)
		sw a2, XCP_BADDR(sp)
		sw a3, XCP_EPC(sp)

		/* mdhi and mdlo */
		mfhi v0
		mflo v1
		sw v0, XCP_MDHI(sp)
		sw v1, XCP_MDLO(sp)

		/* Save all the other general registers.
		* We save zero, s0-s7 and s8 as well, as instruction emulators (e.g. FP
		* operations) and debuggers rely on all registers stored together in
		* well-defined structure.
		*/
		sw zero, XCP_ZERO(sp)
		sw t0, XCP_T0(sp)
		sw t1, XCP_T1(sp)
		sw t2, XCP_T2(sp)
		sw t3, XCP_T3(sp)
		sw t4, XCP_T4(sp)
		sw t5, XCP_T5(sp)
		sw t6, XCP_T6(sp)
		sw t7, XCP_T7(sp)
		sw s0, XCP_S0(sp)
		sw s1, XCP_S1(sp)
		sw s2, XCP_S2(sp)
		sw s3, XCP_S3(sp)
		sw s4, XCP_S4(sp)
		sw s5, XCP_S5(sp)
		sw s6, XCP_S6(sp)
		sw s7, XCP_S7(sp)
		sw t8, XCP_T8(sp)
		sw t9, XCP_T9(sp)
		sw gp, XCP_GP(sp)
		sw s8, XCP_S8(sp)

		/* I don't know what the following does. [rb] */
		/* load our _gp pointer */
#		la gp, _gp

		/* and call the C exception handler */
		move a0, sp	# arg1 = &xcp
		subu sp, 16	# (arg save area)
		j _xcpt_call
		move ra, zero	# fake return address
		
		/* This strange call to _xcpt_call with zero return address is to
		* help exception-aware debuggers to trace back over the exception event.
		* We are basically interposing a bogus stackframe (with a zero return
		* address) between the C exception handler and the actual machine
		* exception.
		*/
$xcptrest:
		.set noat
		add AT, sp, 16
		
		/* at points to exception frame */
$xcptrestother:
		/* restore all state */
		/* restore most general registers */
		lw t0, XCP_T0(AT)
		lw t1, XCP_T1(AT)
		lw t2, XCP_T2(AT)
		lw t3, XCP_T3(AT)
		lw t4, XCP_T4(AT)
		lw t5, XCP_T5(AT)
		lw t6, XCP_T6(AT)
		lw t7, XCP_T7(AT)
		lw s0, XCP_S0(AT)
		lw s1, XCP_S1(AT)
		lw s2, XCP_S2(AT)
		lw s3, XCP_S3(AT)
		lw s4, XCP_S4(AT)
		lw s5, XCP_S5(AT)
		lw s6, XCP_S6(AT)
		lw s7, XCP_S7(AT)
		lw t8, XCP_T8(AT)
		lw t9, XCP_T9(AT)
		lw gp, XCP_GP(AT)
		lw s8, XCP_S8(AT)

		/* mdhi and mdlo */
		lw v0, XCP_MDHI(AT)
		lw v1, XCP_MDLO(AT)
		mthi v0
		mtlo v1

		/* remaining general registers */
		lw a0, XCP_A0(AT)
		lw a1, XCP_A1(AT)
		lw a2, XCP_A2(AT)
		lw a3, XCP_A3(AT)
		lw ra, XCP_RA(AT)

		/* restore the exception-time status register */
		.set noreorder
		lw v0, XCP_SR(AT)
		nop			
		mtc0 v0, CP0_SR
		lw v1, XCP_V1(AT)
		lw v0, XCP_V0(AT)
		lw sp, XCP_SP(AT)

		/* we are not uninterruptible and can use k1 safely */
		lw k1, XCP_EPC(AT)
		lw AT, XCP_AT(AT)
		mtc0 k1, CP0_EPC
		j	k1
		rfe

		.set reorder
		.set at
END(_xcpt_handler)
		
		
LEAF(_xcpt_call)
		/* on entry: a0 == &xcp */
		subu sp, 24
		sw ra, 16(sp)
		
		/* punt out to _xcpt_deliver */
		jal _xcpt_deliver
		nop
		lw ra, 16(sp)
		addu sp, 24
		beqz ra, $xcptrest
		nop
		j ra
		nop
END(_xcpt_call)	
