# ==========  GlobalTrapHandler  ========== 
#
#  This file contains "GlobalTrapHandler" which deals traps (interrupts and exceptions).
#  It prints out error information and halts. It also provides emulation for DIV/REM.
#
# Harry Porter  -  21 Sept 2023        - Reorganized
#

	.sourcefile	"GlobalTrapHandler.s", "Runtime Support - Harry Porter"

	.export		GlobalTrapHandler
	.export		CPUStateSaveArea
	.export		TimerInt_While_Disabled

	.import		EmulatorDebuggingRequested
	.import		CantContinue
	.import		_putString

	.import		_P_KPLSupport_printDecimal
	.import		_P_KPLSupport_printHex
	.import		_P_KPLSupport_TrapHandler_ArithmeticException
	.import		_P_KPLSupport_TrapHandler_UnalignedLoadStore
	.import		_P_KPLSupport_TrapHandler_NullAddress
	.import		_P_KPLSupport_TrapHandler_BadArrayIndex
	.import		_P_KPLSupport_TrapHandler_StackOverflow
	.import		_P_KPLSupport_TrapHandler_PageTableException
	.import		_P_KPLSupport_TrapHandler_EmulatedInstruction
	.import		_P_KPLSupport_TrapHandler_TimerInterrupt
	.import		_P_KPLSupport_TrapHandler_DMAComplete
	.import		_P_KPLSupport_TrapHandler_PLICInterrupt

	.export		TimerInterval				# Used by _entry and GlobalTrapHandler
TimerInterval:		.equ	0x7fff_ffff_ffff_ffff		# use 10000 for frequent interrupts

DIV_OPCODE: .equ	0x0007					# Opcodes involved in emulation
REM_OPCODE: .equ	0x0008


#
# The linker always tries to place each segment directly after other segments
# with similar (writable/executable) properties. We place this data section in
# low memory, which encourages the linker to place other data segments in
# low memory, where their variables can be accessed easily with small offsets
# from register gp.
#
	.begin		executable, gp=0x0_0001_0000


#
# The CPUStateSaveArea is the place where registers are saved at the time of a trap.
#
CPUStateSaveArea:
	.skip		8*15		# Room for r1...r15, with the following fields:
save_r1:	.equ		0
save_r2:	.equ		8
save_r3:	.equ		16
save_r4:	.equ		24
save_r5:	.equ		32
save_r6:	.equ		40
save_r7:	.equ		48
save_t:		.equ		56
save_s0:	.equ		64
save_s1:	.equ		72
save_s2:	.equ		80
save_tp:	.equ		88
save_gp:	.equ		96
save_lr:	.equ		104
save_sp:	.equ		112


#
# =====================  JUMP TABLE FOR ALL TRAPS  =====================
#
# Each entry in the jump table is 8 bytes and contains a jump to a handler
# function. This table is placed at a fixed, pre-determined address. Note that
# the address of each JUMP entry is exactly the same as the trap code, making 
# the jump fast and easy.
#
# This program handles the following traps by invoking a KPL function.
#   Exceptions:
#     Arithmetic
#     Unaligned LOAD/STORE
#     Null Address
#     Bad Array Index
#     Page Table
#     Kernel
#   Interrupts:
#     Timer
#     DMA-Complete
#
# No other traps should occur. If they do, we print an error message and halt.
#
# =====================  TRAP CODES  =====================
#
#      0   0x0000   Syscall    0
#    ...     ...      ...
#   8184   0x1FF8   Syscall 1023
#
#   8192   0x2000   Arithmetic Exception
#   8200   0x2008   Unaligned LOAD/STORE
#   8208   0x2010   Null Address Exception
#   8216   0x2018   Illegal Instruction, privilege violation
#   8224   0x2020   Page Illegal Address Exception
#   8232   0x2028   Page Table Exception
#   8240   0x2030   Page Invalid Exception
#   8248   0x2038   Page Write Exception
#   8256   0x2040   Page Fetch Exception
#   8264   0x2048   Page Copy-On-Write Exception
#   8272   0x2050   Page Dirty Exception
#   8280   0x2058   Debug Exception
#   8288   0x2060   Breakpoint Exception
#   8296   0x2068   Singlestep Exception
#   8304   0x2070   Kernel Exception
#   8312   0x2078   Emulated Instruction Exception
#   8320   0x2080   Hardware Fault Exception
#   8328   0x2088   Bad Array Index Exception
#   8336   0x2090   Stack Overflow Exception
#
#   8344   0x2098   Timer Interrupt
#   8352   0x20A0   DMA-Complete Interrupt
#
#   8360   0x20A8   Neighbor Send Complete - west
#   8368   0x20B0   Neighbor Send Complete - east
#   8376   0x20B8   Neighbor Send Complete - north
#   8384   0x20C0   Neighbor Send Complete - south
#   8392   0x20C8   Neighbor Send Complete - up
#   8400   0x20D0   Neighbor Send Complete - down
#   8408   0x20D8   Neighbor Incoming - west
#   8416   0x20F0   Neighbor Incoming - east
#   8424   0x20F8   Neighbor Incoming - north
#   8432   0x20F0   Neighbor Incoming - south
#   8440   0x20F8   Neighbor Incoming - up
#   8448   0x2100   Neighbor Incoming - down
#
TIMER_INTERRUPT_CAUSE_CODE:	.equ		0x2098

	.begin	kernel, startaddr = 0x000002000
TrapJumpTable:

        jump    ArithmeticExceptionHandler
        .align  8
        jump    UnalignedLoadStoreHandler
        .align  8
        jump    NullAddressHandler
        .align  8
        jump    IllegalInstructionHandler
        .align  8
        jump    PagePrivilegeHandler
        .align  8
        jump    PageTableHandler
        .align  8
        jump    PageIllegalAddressHandler
        .align  8
        jump    PageWriteHandler
        .align  8
        jump    PageFetchHandler
        .align  8
        jump    PageCopyOnWriteHandler
        .align  8
        jump    PageFirstDirtyHandler
        .align  8
        jump    DebugHandler
        .align  8
        jump    BreakpointHandler
        .align  8
        jump    SinglestepHandler
        .align  8
        jump    EmulatedInstructionHandler
        .align  8
        jump    HardwareFaultHandler
        .align  8
        jump    BadArrayIndexHandler
        .align  8
        jump    StackOverflowHandler
        .align  8
        jump    TimerHandler
        .align  8
        jump    DMACompleteHandler
        .align  8
        jump    PLICHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
        jump    NeighborCommunicationHandler
        .align  8
JumpTableBeyond:




#############################################################
#
# TimerInt_While_Disabled ()
#
# This function will simulate a timer interrupt. It can be used to prematurely
# end the time-slice of this thread.
#
# Interrupts must be DISABLED on entry. It does this:
#     csr_stat2  <-- csr_status
#     csr_prevpc <-- return addr
#     csr_bad    <-- 0
#     csr_addr   <-- 0
#     csr_cause  <-- TIMER_INTERRUPT_CAUSE_CODE
#
# If and when this thread is rescheduled, interrupts WILL BE DISABLED.
#
# Standard KPL calling conventions: Yes
# Regs Changed:         r1
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
# Kernel Mode required.
#
#############################################################

	.function	"TimerInt_While_Disabled", line=0, framesize=0
TimerInt_While_Disabled:
	csrread		r1,csr_status		# If InterruptsEnabled (i.e., bit[1] == 1)
	andi		r1,r1,0x0002		# .
	beqz		r1,forcedYield_zero	# .
	movi		r1,forcedYieldError	#   print error and halt
	call		_putString		#   .
	call		CantContinue		#   .
forcedYield_zero:
	csrwrite	csr_bad,r0
	csrwrite	csr_addr,r0
	movi		r1,TIMER_INTERRUPT_CAUSE_CODE
	csrwrite	csr_cause,r1
	csrread		r1,csr_status
	csrwrite	csr_stat2,r1
	csrwrite	csr_prevpc,lr
	jump		GlobalTrapHandler
	.endfunction
forcedYieldError:
	.string		"\n***** TimerInt_While_Disabled called with interrupts ENABLED *****\n\0"
	.align		4
#
# =====================  Global Trap Handler  =====================
#
# All regs are valid on entry and belong the interrupted thread.
#
# We assume the following have just been set by the hardware:
#     csr_stat2, csr_prevpc, csr_cause, csr_bad, csr_addr
# We also assume: 
#     csr_ptr points to the CPUStateSaveArea
#     Interrupts are disabled on entry.
# All registers will be immediately saved in the CPUStateSaveArea.
# (Regs r1...r7 may contain useful info for the exception handlers
# and will be passed to the exception handler in some cases.)
#

GlobalTrapHandler:
	.function	"GlobalTrapHandler",line=0,framesize=0

# Save general purpose registers in the CPUStateSaveArea:
#   Swap csr_ptr with register r7.
#   Save r9-r11 in the CPUStateSaveArea.
	csrswap		r7,csr_ptr,r7
	store.d		save_s0(r7),s0		# r9
	store.d		save_s1(r7),s1		# r10
	store.d		save_s2(r7),s2		# r11

# Determine if this is a SYSCALL Exception.
	csrread		s0,csr_cause		# s0 = cause code
	movi		s1,0x2000		# If not a sys call, goto "notSyscall"
	bge		s0,s1,notSyscall	#   (i.e., if cause >= 0x2000...)

####################  If this is a SYSCALL...  ####################

#   Syscalls are not implemented; Print an error message and exit.
### WARNING: arg_save_area for printDecimal & printHex is not allocated.
	movi		r1,SyscallMess1		# Print a message and terminate
	call		_putString		#   Print the first part
	csrread		r1,csr_cause		#   Print branch target
	srli		r1,r1,3			#   Shift to divide by 8 to get 0,1,3,...1023
	call		_P_KPLSupport_printDecimal	#
	movi    	r1, SyscallMess2	#   Print the second part
	call		_putString		#
	csrread 	r1,csr_prevpc		#   Print the addr of the syscall
	addi		r1,r1,-4		#
	call		_P_KPLSupport_printHex	#
	movi    	r1, SyscallMess3	#   Print third part
	call		CantContinue		#   Terminate execution

####################  If this is NOT A SYSCALL...  ####################

notSyscall:

# Save the rest of the registers...
#   Save r1-r6, r8, r12-r15 in the CPUStateSaveArea.
	store.d		save_r1(r7),r1
	store.d		save_r2(r7),r2
	store.d		save_r3(r7),r3
	store.d		save_r4(r7),r4
	store.d		save_r5(r7),r5
	store.d		save_r6(r7),r6
	store.d		save_t (r7),t		# r8
	store.d		save_tp(r7),tp		# r12
	store.d		save_gp(r7),gp		# r13
	store.d		save_lr(r7),lr		# r14
	store.d		save_sp(r7),sp		# r15
#   Read csr_ptr (i.e., previous value of r7) into a reg, and then store it in the CPUStateSaveArea.
	csrread		s2,csr_ptr		# r7
	stored		save_r7(r7),s2

###	movi		r1,HandlerMess1		# Print a message
###	addi		sp,sp,-24		#   Push 3*8 bytes to protect stack top
###	call		_putString		#   _putString uses stack, but modifies no regs
###	loadd		r1,save_r1(r7)		#   Restore r1
###	addi		sp,sp,+24		#   Pop 3*8 bytes

# Prepare to execute kernel-mode functions written in high-level KPL:
#   tp, gp, lr, and sp are unchanged, so no need to modify them.
#   Regs r1...r7 are unchanged and may contain additional info about the exception.
#   Copy r7 (the ptr to the CPUStateSaveArea) back into csr_ptr.
	csrwrite	csr_ptr,r7
	mov		r7,s2

# Dispatch to the individual trap handler.
	movi		s1,JumpTableBeyond		#   IF this value is out-of-range...
	blt		s0,s1,causeIsOK			#   .
	movi		r1,HandlerMess2			#     Print a nasty message
	call		_putString			#     .
	addpc		r1,0				#     r1 = address of this very instruction
	jump		EmulatorDebuggingRequested	#     Terminate execution (r1=codeAddr)
causeIsOK:						#   ENDIF
	jr		s0				#   The cause code is the address of the handler

# Upon entry to the KPL function to handle the interrupt/exception...
#   Registers r1...r7,tp,gp,lr,sp contain whatever values they had at the moment of interrupt.
#   Registers t,s0,s1,s2 have been trashed.
#   CSRs csr_stat2, csr_prevpc, csr_ptr, csr_pgtable, csr_trapvec
#      will remain unchanged throughout the handler function.


####################  Jump to here after handling an interrupt.  #####################

ReturnFromInterrupt:

# Make r7 point to the CPUStateSaveArea.
	csrread		r7,csr_ptr
# Fetch the saved r7 from the CPUStateSaveArea and move it into csr_ptr.
	load.d		r1,save_r7(r7)
	csrwrite	csr_ptr,r1
# Restore all saved registers from the CPUStateSaveArea except r7.
	load.d		r1,save_r1(r7)
	load.d		r2,save_r2(r7)
	load.d		r3,save_r3(r7)
	load.d		r4,save_r4(r7)
	load.d		r5,save_r5(r7)
	load.d		r6,save_r6(r7)
	load.d		t, save_t (r7)		# r8
	load.d		s0,save_s0(r7)		# r9
	load.d		s1,save_s1(r7)		# r10
	load.d		s2,save_s2(r7)		# r11
	load.d		tp,save_tp(r7)		# r12
	load.d		gp,save_gp(r7)		# r13
	load.d		lr,save_lr(r7)		# r14
	load.d		sp,save_sp(r7)		# r15
# Swap csr_ptr with r7.
	csrswap		r7,csr_ptr,r7
# Resume execution
	sysret
	.endfunction

HandlerMess1:
	.string	"\n**********  Trap Handler Invoked  **********\n\0"
HandlerMess2:
	.string	"***** CAUSE CODE (csr_cause) IS INVALID *****\n\0"
SyscallMess1:
	.string	"***** A syscall has occurred. Syscall code = \0"
SyscallMess2:
	.string	" (dec)   Address of SYSCALL instruction = \0"
SyscallMess3:
	.string	" *****\n\0"
	.align	8


# =====================  Trap Hander Functions  =====================
#
# The following functions are jumped to when an interrupt or exception occurs.
# Each function will call the corresponding KPL functions to deal with
# the situation. (In the case of errors, the KPL code will to print out
# a message and invoke the debugger.)
#
# The called KPL function expects the standard calling conventions to
# be observed. This is important because the debugger will also expect the
# stack to look like KPL functions had been called. Therefore, each of
# these functions will allocate a frame on the stack and set up the arguments
# to the KPL function. This frame is necessary because the called KPL
# function will expect the frame to have stack space in which it can store
# the argument values. Also, these functions will save the return address in the
# frame, making it look to the debugger as if they were called from the
# failing instruction.
#
# NOTE: On entry registers r1...r7 will have the values they had at the moment
#       of the exception. However, they are modified here to the args expected
#       by the KPL function.
#
# In this environment (i.e., a single thread executing in kernel mode with no page
# table) many of the traps simply cannot occur. These handlers do not invoke KPL
# functions; instead they print a message and terminate immediately.
#
ArithmeticExceptionHandler:
	.function	"ArithmeticExceptionHandler",line=0,framesize=24
	csrread		r1,csr_prevpc		# arg1 = r1=addr of offending instruction 
	csrread		r2,csr_bad		# arg2 = r2=offending instruction
	store.d		-8(sp),r1		# Save return address for debugging info
	addi		sp,sp,-24		# Allocate frame (3 doublewords)
        csrset		csr_status,0x0002	# Enable interrupts for trapping future errors 
	call		_P_KPLSupport_TrapHandler_ArithmeticException
	call		CantContinue
	.endfunction

UnalignedLoadStoreHandler:
	.function	"UnalignedLoadStoreHandler",line=0,framesize=32
	csrread		r1,csr_prevpc		# arg1 = r1=code addr
	csrread		r2,csr_bad		# arg2 = r2=offending instruction
	csrread		r3,csr_addr		# arg3 = r3=unaligned address
	store.d		-8(sp),r1		# Save return address for debugging info
	addi		sp,sp,-32		# Allocate frame (4 doublewords)
        csrset		csr_status,0x0002	# Enable interrupts for trapping future errors
	call		_P_KPLSupport_TrapHandler_UnalignedLoadStore
	call		CantContinue
	.endfunction

NullAddressHandler:
	.function	"NullAddressHandler",line=0,framesize=16
	csrread		r1,csr_prevpc		# arg1 = r1=code addr
	store.d		-8(sp),r1		# Save return address for debugging info
	addi		sp,sp,-16		# Allocate frame (2 doublewords)
        csrset		csr_status,0x0002	# Enable interrupts for trapping future errors
	call		_P_KPLSupport_TrapHandler_NullAddress
	call		CantContinue
	.endfunction

BadArrayIndexHandler:
	.function	"BadArrayIndexHandler",line=0,framesize=32
						# NOTE: KPL always uses r1 and r2 this way:
						#    arg1 = r1=bad index
						#    arg2 = r2=ptr to array
	csrread		r3,csr_prevpc		# arg3 = r3=code addr
	store.d		-8(sp),r3		# Save return address for debugging info
	addi		sp,sp,-32		# Allocate frame (4 doublewords)
        csrset		csr_status,0x0002	# Enable interrupts for trapping future errors
	call		_P_KPLSupport_TrapHandler_BadArrayIndex
	call		CantContinue
	.endfunction

StackOverflowHandler:
	.function	"StackOverflowHandler",line=0,framesize=16
	csrread		r1,csr_prevpc		# arg1 = r1=code addr
	store.d		-8(sp),r3		# Save return address for debugging info
	addi		sp,sp,-16		# Allocate frame (2 doublewords)
        csrset		csr_status,0x0002	# Enable interrupts for trapping future errors
	call		_P_KPLSupport_TrapHandler_StackOverflow
	call		CantContinue
	.endfunction

IllegalInstructionHandler:
	.function	"IllegalInstructionHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess4		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

PagePrivilegeHandler:
	.function	"PagePrivilegeHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess19		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

PageTableHandler:
	.function	"PageTableHandler",line=0,framesize=24
	csrread		r1,csr_prevpc		# arg1 = r1=addr of offending instruction 
	csrread		r2,csr_addr		# arg2 = r2=bad address
	store.d		-8(sp),r1		# Save return address for debugging info
	addi		sp,sp,-24		# Allocate frame (3 doublewords)
        csrset		csr_status,0x0002	# Enable interrupts for trapping future errors
	call		_P_KPLSupport_TrapHandler_PageTableException
	call		CantContinue
	.endfunction

PageIllegalAddressHandler:
	.function	"PageIllegalAddressHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess21		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

PageWriteHandler:
	.function	"PageWriteHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess22		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

PageFetchHandler:
	.function	"PageFetchHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess23		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

PageCopyOnWriteHandler:
	.function	"PageCopyOnWriteHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess24		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

PageFirstDirtyHandler:
	.function	"PageFirstDirtyHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess25		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

DebugHandler:
	.function	"DebugHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess10		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

BreakpointHandler:
	.function	"BreakpointHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess11		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

SinglestepHandler:
	.function	"SinglestepHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess12		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

EmulatedInstructionHandler:
	.function	"EmulatedInstructionHandler",line=0,framesize=32
	csrread		r4,csr_bad			# If this is DIV or REM, goto Emulate_DivRem.
	srli		r5,r4,16			# .
	movi		s0,0				# .  If DIV, clear s0 and jump
	beqi		r5,DIV_OPCODE, Emulate_DivRem	# .
	movi		s0,1				# .  If REM, set s0 and jump
	beqi		r5,REM_OPCODE, Emulate_DivRem	# .


						# Here is the stack frame:
						#    sp-->  0: arg save area
						#           8: arg save area
						#          16: arg save area
						#          24: ret addr
						# Here are the args
	csrread		r1,csr_prevpc		#     r1 = code addr
	csrread		r2,csr_bad		#     r2 = offending instruction
	movi		r3,CPUStateSaveArea	#     r3 = ptr to reg save area
	addi		sp,sp,-32		# Allocate frame (4 doublewords)
	store.d		24(sp),r1		# Save return address for debugging info
#	csrset		csr_status,0x0002	# Interrupt handlers run with INTERRUPTS DISABLED
	call		_P_KPLSupport_TrapHandler_EmulatedInstruction
	jump		ReturnFromInterrupt
	.endfunction

HardwareFaultHandler:
	.function	"HardwareFaultHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess15		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction

TimerHandler:
	.function	"TimerHandler",line=0,framesize=16
	addi		sp,sp,-24		# The interrupted pgm may use up to
						# .  3*8 bytes beyond the stack top.
#	movi		r1,TimerOccurred	# Print a message
#	call		_putString		# .
	movi		r1,TimerInterval        # Recharge the timer
	csrwrite	csr_timer,r1		# .
	csrread		r1,csr_prevpc		# arg1 = r1=code addr
	store.d		-8(sp),r1		# Save return address for debugging info
	addi		sp,sp,-16		# Allocate frame (2 doublewords)
#	csrset		csr_status,0x0002	# Interrupt handlers run with INTERRUPTS DISABLED
	call		_P_KPLSupport_TrapHandler_TimerInterrupt
	jump		ReturnFromInterrupt
	.endfunction

DMACompleteHandler:
	.function	"DMACompleteHandler",line=0,framesize=16
	addi		sp,sp,-24		# The interrupted pgm may use up to
						# .  3*8 bytes beyond the stack top.
#	movi		r1,DMAOccurred		# Print a message
#	call		_putString		# .
	csrread		r1,csr_prevpc		# arg1 = r1=code addr
	store.d		-8(sp),r1		# Save return address for debugging info
	addi		sp,sp,-16		# Allocate frame (2 doublewords)
#	csrset		csr_status,0x0002	# Interrupt handlers run with INTERRUPTS DISABLED
	call		_P_KPLSupport_TrapHandler_DMAComplete
	jump		ReturnFromInterrupt
	.endfunction

PLICHandler:
	.function	"PLICHandler",line=0,framesize=16
	addi		sp,sp,-24		# The interrupted pgm may use up to
						# .  3*8 bytes beyond the stack top.
	csrread		r1,csr_prevpc		# arg1 = r1=code addr
	store.d		-8(sp),r1		# Save return address for debugging info
	addi		sp,sp,-16		# Allocate frame (2 doublewords)
#	csrset		csr_status,0x0002	# Interrupt handlers run with INTERRUPTS DISABLED
	call		_P_KPLSupport_TrapHandler_PLICInterrupt
	jump		ReturnFromInterrupt
	.endfunction

NeighborCommunicationHandler:
	.function	"NeighborCommunicationHandler",line=0,framesize=8
	csrread		lr,csr_prevpc		# Save "return address" for debugging info
	enterfun	sp,sp,-8		# Allocate frame (1 doubleword)
	movi		r1,ExceptMess18		# Print an error message
	call		PrintCSRPrevPCAndHalt	# .  and terminate execution
	.endfunction



# 
# =====================  PrintCSRPrevPCAndHalt  =====================
#
# Come here to print a message and terminate. There is no return.
# On entry, r1 points to a message to print.
#    sp --> 0: arg save area (1 arg for PrintHex)
#           8: ret addr
#
PrintCSRPrevPCAndHalt:
	.function	"PrintCSRPrevPCAndHalt",line=0,framesize=16
	enterfun	sp,sp,-16			# Prolog
	call		_putString			# Print the first message
	movi    	r1,PrevPCMess1			# Print "Prev PC = "
	call		_putString			#
	csrread 	r1,csr_prevpc			# Print csr_prevpc
	call		_P_KPLSupport_printHex		#
	movi    	r1,PrevPCMess2			# Print "\n"
	call		_putString			#
	csrread 	r1,csr_prevpc			# Call EmulatorDebuggingRequested (csr_prevpc)
	call		EmulatorDebuggingRequested	# .
	call		CantContinue			# Terminate execution
	.endfunction


PrevPCMess1:
	.string	"The address of the offending instruction is: \0"
PrevPCMess2:
	.string	" (or the previous instruction)\n\0"
TimerOccurred:
	.string	"\n***** A TIMER INTERRUPT has occurred! *****\n\0"
DMAOccurred:
	.string	"\n***** A DMA COMPLETE INTERRUPT has occurred! *****\n\0"
KernelOccurred:
	.string	"\n***** A KERNEL EXCEPTION has occurred! *****\n\0"

ExceptMess4:
	.string	"\n***** An ILLEGAL INSTRUCTION / PRIVILEGE VIOLATION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess10:
	.string	"\n***** A DEBUG EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess11:
	.string	"\n***** A BREAKPOINT EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess12:
	.string	"\n***** A SINGLESTEP EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess14:
	.string	"\n***** An EMULATED INSTRUCTION EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess15:
	.string	"\n***** A HARDWARE FAULT EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess18:
	.string	"\n***** A NEIGHBOR COMMUNICATION INTERRUPT has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess19:
	.string	"\n***** A PAGE PRIVILEGE EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess21:
	.string	"\n***** A PAGE ILLEGAL ADDRESS EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess22:
	.string	"\n***** A PAGE WRITE EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess23:
	.string	"\n***** A PAGE FETCH EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess24:
	.string	"\n***** A PAGE COPY-ON-WRITE EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"
ExceptMess25:
	.string	"\n***** A PAGE FIRST DIRTY EXCEPTION has occurred!  Type 'stack' to see stack. *****\n\0"

	.align	8



# 
# =====================  Emulate DIV & REM  =====================
#
# When a DIV or REM instruction causes an Emulated Instruction exception, all
# registers will be saved in the CPUStateSaveArea and we will jump here.
# This function will perform the operation and move the result into CPUStateSaveArea.
#
# This function is passed a single argument (r1=codeAddress), which it will place
# at the top of the stack. The emulator will fetch this value from the stack top
# and display that to the user.
# 
# Stack usage:     Stack is not used.
# On Entry:
#   s0               0=DIV, 1=REM
#   args             In registers, which are stored in the CPUStateSaveArea
# On Exit:
#   Returned value is moved into CPUStateSaveArea, at appropriate offset.
#   Jumps to ReturnFromInterrupt
# On Arithmetic Exception:
#   ...???...
# Interrupts:        Runs with interrupts disabled.
#
	.function	"Emulate_DivRem",line=0,framesize=0
	csrread		r4,csr_bad			# If this is DIV or REM, goto Emulate_DivRem.
Emulate_DivRem:
	srli		r5,r4,1				# Use Reg1 to get offset into CPUStateSaveArea
	andi		r5,r5,0x0078			# .   e.g., r1=0, r2=8, ...
	loadd		r1,CPUStateSaveArea-8 (r5)	# r1 = CPUStateSaveArea [r5] = "upper"
	srli		r5,r4,5				# Use Reg2 to get offset into CPUStateSaveArea
	andi		r5,r5,0x0078			# .
	loadd		r2,CPUStateSaveArea-8 (r5)	# r2 = CPUStateSaveArea [r5] = "divisor"
#	srli		r5,r4,9				# Use Reg3 to get offset into CPUStateSaveArea
#	andi		r5,r5,0x0078			# .
#	loadd		r3,CPUStateSaveArea-8 (r5)	# r3 = CPUStateSaveArea [r5]

	beqz		r2,ArithmeticExceptionHandler		# If divisor is 0, then cause an arithmetic exception.
	bnei		r1,0x8000_0000_0000_0000,Div_Rem_OK	# If MIN_64/-1, then cause an arithmetic exception.
	beqi		r2,-1,ArithmeticExceptionHandler	# .
Div_Rem_OK:							# .

# Look at the signs of upper and divisor, and set s1 according to this table.
#    s1     upper    divisor     DIV     REM
#    00       +         +         +       +
#    01       +         -         -       +
#    10       -         +         -       -
#    11       -         -         +       -
# We'll use this code later to adjust the result. Also negate any negative values

	movi		s1,0				# s1 = 0
	bgez		r2,divisor_notneg		# IF divisor < 0
	bitnot		r2,r2				#   Negate divisor (ignoring overflow)
	movi		t,1				#   .
	add3		r2,r2,t,r0			#   .
	movi		s1,1				#   s1 = 1
divisor_notneg:						# ENDIF

	bgez		r1,upper_notneg			# IF upper < 0
	bitnot		r1,r1				#   Negate upper (ignoring overflow)
	movi		t,1				#   .
	add3		r1,r1,t,r0			#   .
	addi		s1,s1,2				#   Add binary 10 to s1
upper_notneg:						# ENDIF

	call		NonRestoringDivide64		# Call the function to do the work
	
	csrread		r5,csr_bad			# Determine address of CPUStateSaveArea [RegD]
	slli		r5,r5,3				# .
	andi		r5,r5,0x0078			# .

	bnez		s0,got_rem			# IF this was a DIV instruction...
	beqz		s1,div_ok			#   IF s1 = 1 or 2  (i.e., not 0 or 3)
	beqi		s1,3,div_ok			#   .
	bitnot		r1,r1				#     Negate r1 (ignoring overflow)
	movi		t,1				#   .
	add3		r1,r1,t,r0			#   .
div_ok:							#   ENDIF
	stored		CPUStateSaveArea-8 (r5),r1	#   CPUStateSaveArea [RegD] = r1

	jump		div_rem_done			# ELSE
got_rem:						# .
	beqz		s1,rem_ok			#   IF s1 = 2 or 3  (i.e., not 0 or 1)
	beqi		s1,1,rem_ok			#   .
	bitnot		r4,r4				#     Negate r4 (ignoring overflow)
	movi		t,1				#   .
	add3		r4,r4,t,r0			#   .
rem_ok:							#   ENDIF
	stored		CPUStateSaveArea-8 (r5),r4	#   CPUStateSaveArea [RegD] = r4

div_rem_done:						# ENDIF

	jump		ReturnFromInterrupt		# Return from trap
	.endfunction


#############################  NonRestoringDivide64 #############################
#  
# 64-bit Unsigned Division -- THOROUGHLY TESTED
# 
# This function is passed two values within 0...0x7fff_ffff_ffff_ffff. It computes
# DIV and REM and stores them.
# 
# If the divisor is 0, then DIV will be set to -1.
# Otherwise, both DIV and REM will be within 0...0x7fff_ffff_ffff_ffff.
# 
# This function uses the "non-restoring algorithm", which is described in "Computer
# Organization and Design", Patterson & Hennessy.
# 
# The inputs are assumed to be valid, i.e., within 0 ... 0x7fff_ffff_ffff_ffff; no check is made.
# This uses UnsignedAdd (instead of normal +) since overflow will happen but must be ignored.
#
# On entry:
#   r1 = Dividend (upper)
#   r2 = Divisor
# On exit:
#   r1 = DIV result
#   r4 = REM result
# Stack Usage:
#   None (this is a leaf function)
#
### HERE IS THE ALGORITHM (from divide.c)...
###
###    function NonRestoringDivide64_asm (upper, divisor: int, div_ptr, rem_ptr: ptr to int)
###      var
###        remainder_hi, remainder_lo, i, neg_divisor: int
###      neg_divisor = -divisor
###      remainder_hi = 0
###      remainder_lo = upper
###      remainder_hi = unsignedAdd (remainder_hi, neg_divisor)
###      for i = 1 to 65
###        if remainder_hi < 0
###          -- shift remainder
###          remainder_hi = remainder_hi << 1
###          if remainder_lo < 0
###            remainder_hi = remainder_hi | 0x0000_0000_0000_0001
###          endIf
###          remainder_lo = remainder_lo << 1
###          -- Add
###          remainder_hi = unsignedAdd (remainder_hi, divisor)
###        else
###          -- shift remainder
###          remainder_hi = remainder_hi << 1
###          if remainder_lo < 0
###            remainder_hi = remainder_hi | 0x0000_0000_0000_0001
###          endIf
###          remainder_lo = remainder_lo << 1
###          -- Set LS bit of remainder
###          remainder_lo = remainder_lo | 0x0000_0000_0000_0001
###          -- Add
###          remainder_hi = unsignedAdd (remainder_hi, neg_divisor)
###        endIf
###      endFor
###      remainder_hi = unsignedAdd (remainder_hi, divisor)
###      * div_ptr = remainder_lo
###      * rem_ptr = remainder_hi >> 1
###    endFunction
#
# r1 = upper, remainder_lo
# r2 = divisor
# r3 = div_result
# r4 = rem_result
# r5 = remainder_hi
# r6 = neg_divisor
# r7 = counter

NonRestoringDivide64:
		.function	"NonRestoringDivide64",line=0,framesize=0
		bitnot		r6,r2			# neg_divisor = -divisor (ignoring overflow)
		movi		t,1			# .
		add3		r6,r6,t,r0		# .
		movi		r5,0			# remainder_hi = 0
							# remainder_lo = upper
		movi		r7,65			# FOR i = 65 ... 1
loop:							# .
		bgez		r5,not_neg		#   IF remainder_hi < 0
							#     Shift remainder left 1 bit
		slli		r5,r5,1			#     .  remainder_hi = remainder_hi << 1
		bgez		r1,label_1		#     .  if remainder_lo < 0
		ori		r5,r5,0x0001		#     .    remainder_hi = remainder_hi | 0x0000_0000_0000_0001
label_1:						#     .  endIf
		slli		r1,r1,1			#     .  remainder_lo = remainder_lo << 1
		add3		r5,r5,r2,r0		#     remainder_hi = unsignedAdd (remainder_hi, divisor)
		jump		endif			#   ELSE
not_neg:						#   .
							#     Shift remainder left 1 bit
		slli		r5,r5,1			#     .  remainder_hi = remainder_hi << 1
		bgez		r1,label_2		#     .  if remainder_lo < 0
		ori		r5,r5,0x0001		#     .    remainder_hi = remainder_hi | 0x0000_0000_0000_0001
label_2:						#     .  endIf
		slli		r1,r1,1			#     .  remainder_lo = remainder_lo << 1
		ori		r1,r1,0x0001		#     remainder_lo = remainder_lo | 0x0000_0000_0000_0001
		add3		r5,r5,r6,r0		#     remainder_hi = unsignedAdd (remainder_hi, neg_divisor)
endif:							#   ENDIF
		addi		r7,r7,-1		# ENDFOR
		bnez		r7,loop			# .
		add3		r5,r5,r2,r0		# remainder_hi = unsignedAdd (remainder_hi, divisor)
#		mov		r3,r1			# *div_result = remainder_lo
		srli		r4,r5,1			# *rem_result = remainder_hi >> 1
		ret					# Return
		.endfunction

