# ==========  RUNTIME SUPPORT CODE FOR KPL  ==========  
#
# This version supports KPL programs running in either kernel or user space.
#
# Harry Porter  -  30 May 2003         - From old Blitz-32 code
# Harry Porter  -  24 April 2019       - Initial version
# Harry Porter  -   1 May 2020         - Mods re: Host file I/O
# Harry Porter  -  21 Sept 2022        - Changed to support ISA version 2
# Harry Porter  -   7 August 2023      - Modified to support xv6
# Harry Porter  -  21 September 2023   - Removed privileged/platform-specific stuff
#

	.sourcefile	"KPLRuntime.s", "KPL Runtime Support - Harry Porter"


#
# These functions are implemented in this file and may be used by
# the KPL programmer.  They follow the standard KPL calling conventions.
#
	.export		endianSwapH
	.export		endianSwapW
	.export		endianSwapD
	.export		rotateRight32
	.export		rotateRight64
	.export		resetFloatingStatus
	.export		floatingUnderflow
	.export		floatingInvalid
	.export		floatingOverflow
	.export		floatingDivideByZero
	.export		floatingInexact
	.export		setFloatingUnderflow
	.export		setFloatingInvalid
	.export		setFloatingOverflow
	.export		setFloatingDivideByZero
	.export		setFloatingInexact
	.export		floatingSqrt
	.export		floatingAbs
	.export		floatingMin
	.export		floatingMax
	.export		initializeThreadPtr
	.export		LeafError
	.export		GetSP
	.export		PutSP
	.export		strEqual
	.export		maximizeString
	.export		memorySet8
	.export		memoryCopy8
	.export		memoryCopy
#
# These functions are implemented in this file and are used by 
# the code generated by the KPL compiler. These functions DO NOT FOLLOW
# the standard calling conventions.
#
	.export		_putString
	.export		_PerformThrow
	.export		_IsKindOf
	.export		CantContinue
#
# These functions will immediately jump to whichever function has been set
# during initialization, as indicated in the ThreadData object.
#
	.export		FatalError
	.export		exit
	.export		InvokeDebugger
	.export		MemoryAlloc
	.export		MemoryFree
	.export		Basic_PrintChar
	.export		Basic_ReadChar
#
# The following exported symbol is used in path-testing in the compiler.
# Since it is not actually used, the definition is meaningless.
#
	.export		KPL_Compiler_Path_Testing_Symbol
KPL_Compiler_Path_Testing_Symbol: 	.equ		0x0	
#
# This symbol that is created by the KPL compiler and used in this file:
#
	.import 	_Error_P_KPLSupport_UncaughtThrowError
#
# These functions are implemented in KPL and are called from here:
#
	.import		_P_KPLSupport_KPLMissingCatch
	.import		_P_KPLSupport_KPLFatalThrowError
	.import		_P_KPLSupport_print



#####################  KPL Runtime Error Handling  ####################
#
# The KPL compiler inserts a number of checks and tests to make sure everything is
# okay. If anything is ever discovered to be wrong, there will be a CALL to a
# function whose name begins "_runtimeError...".
#
# The KPL functions will print a message, throw an error, and/or call exit().
#
# The address of the error can be retrieved from the stack, since the call will
# follow the standard calling conventions. However, the error handler must never
# return to the compiled code encountering the error.

#
# The following functions are called directly from the compiled code.
# These function names are just equated to similarly named KPL functions.
#
	.export		_runtimeErrorWrongObject1
	.export		_runtimeErrorWrongObject2
	.export		_runtimeErrorWrongObject3
	.export		_runtimeErrorBadClassDescriptor
	.export		_runtimeErrorUninitializedObject
	.export		_runtimeErrorUninitializedArray
	.export		_runtimeErrorSetArraySize
	.export		_runtimeErrorCurrentArraySizeIsWrong
	.export		_runtimeErrorArrayTooLarge
	.export		_runtimeErrorArrayCountNotPositive

	.import		_P_KPLSupport_RuntimeErrorWrongObject1
	.import		_P_KPLSupport_RuntimeErrorWrongObject2
	.import		_P_KPLSupport_RuntimeErrorWrongObject3
	.import		_P_KPLSupport_RuntimeErrorBadClassDescriptor
	.import		_P_KPLSupport_RuntimeErrorUninitializedObject
	.import		_P_KPLSupport_RuntimeErrorUninitializedArray
	.import		_P_KPLSupport_RuntimeErrorSetArraySize
	.import		_P_KPLSupport_RuntimeErrorCurrentArraySizeIsWrong
	.import		_P_KPLSupport_RuntimeErrorArrayTooLarge
	.import		_P_KPLSupport_RuntimeErrorArrayCountNotPositive


	.begin		executable

#
# ####################  KPL Runtime Error Handlers  ####################
#
# The following functions are called whenever KPL code detects an error. The compiled
# KPL code may leave some information in a couple of the regs, as detailed below.
#
# Each of these functions will call the corresponding KPL function which will deal
# with the error, print out an error message, and then invoke the debugger. These
# all fatal errors; a return is not expected.
#
# 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, each function will save the return address in
# the frame, making it look to the debugger as if it was called from the
# failing instruction.
#
_runtimeErrorWrongObject1:
	.function	"_runtimeErrorWrongObject1",line=0,framesize=32
	# r1 = ptr to source DPT
	# r2 = ptr to expected DPT
	# r3 = code address
	mov		r3,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-32		# Allocate frame (4 doublewords)
	call		_P_KPLSupport_RuntimeErrorWrongObject1
	call		CantContinue
	.endfunction

_runtimeErrorWrongObject2:
	.function	"_runtimeErrorWrongObject2",line=0,framesize=32
	# r1 = ptr to target DPT
	# r2 = ptr to expected DPT
	# r3 = code address
	mov		r3,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-32		# Allocate frame (4 doublewords)
	call		_P_KPLSupport_RuntimeErrorWrongObject2
	call		CantContinue
	.endfunction

_runtimeErrorWrongObject3:
	.function	"_runtimeErrorWrongObject3",line=0,framesize=32
	# r1 = ptr to target object
	# r2 = ptr to source object
	# r3 = code address
	mov		r3,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-32		# Allocate frame (4 doublewords)
	call		_P_KPLSupport_RuntimeErrorWrongObject3
	call		CantContinue
	.endfunction

_runtimeErrorBadClassDescriptor:
	.function	"_runtimeErrorBadClassDescriptor",line=0,framesize=40
	# r1 = ptr to target object
	# r2 = ptr to source object
	# r3 = size of source object
	# r4 = code address
	mov		r4,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-40		# Allocate frame (5 doublewords)
	call		_P_KPLSupport_RuntimeErrorBadClassDescriptor
	call		CantContinue
	.endfunction

_runtimeErrorUninitializedObject:
	.function	"_runtimeErrorUninitializedObject",line=0,framesize=24
	# r1 = ptr to object
	# r2 = code address
	mov		r2,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-24		# Allocate frame (3 doublewords)
	call		_P_KPLSupport_RuntimeErrorUninitializedObject
	call		CantContinue
	.endfunction

_runtimeErrorUninitializedArray:
	.function	"_runtimeErrorUninitializedArray",line=0,framesize=24
	# r1 = ptr to array
	# r2 = code address
	mov		r2,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-24		# Allocate frame (3 doublewords)
	call		_P_KPLSupport_RuntimeErrorUninitializedArray
	call		CantContinue
	.endfunction

_runtimeErrorSetArraySize:
	.function	"_runtimeErrorSetArraySize",line=0,framesize=32
   	# r1 = ptr to array
        # r2 = newSize
	# r3 = code address
	mov		r3,lr
	store.d		-8(sp),r3		# Save return address for debugging info
	addi		sp,sp,-32		# Allocate frame (4 doublewords)
	call		_P_KPLSupport_RuntimeErrorSetArraySize
	call		CantContinue
	.endfunction

_runtimeErrorCurrentArraySizeIsWrong:
	.function	"_runtimeErrorCurrentArraySizeIsWrong",line=0,framesize=48
	# r1 = ptr to target object
	# r2 = ptr to source object
	# r3 = current size of source object
	# r4 = max size of target object
	# r5 = code address
	mov		r5,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-48		# Allocate frame (6 doublewords)
	call		_P_KPLSupport_RuntimeErrorCurrentArraySizeIsWrong
	call		CantContinue
	.endfunction

_runtimeErrorArrayTooLarge:
	.function	"_runtimeErrorArrayTooLarge",line=0,framesize=24
	# r1 = bad size
	# r2 = code address
	mov		r2,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-24		# Allocate frame (3 doublewords)
	call		_P_KPLSupport_RuntimeErrorArrayTooLarge
	call		CantContinue
	.endfunction

_runtimeErrorArrayCountNotPositive:
	.function	"_runtimeErrorArrayCountNotPositive",line=0,framesize=24
	# r1 = count
	# r2 = code address
	mov		r2,lr
	store.d		-8(sp),lr		# Save return address for debugging info
	addi		sp,sp,-24		# Allocate frame (3 doublewords)
	call		_P_KPLSupport_RuntimeErrorArrayCountNotPositive
	call		CantContinue
	.endfunction



#####################  CantContinue  ####################
#
# Come here if the KPL tries to continue after an error.
#
# Max Stack Usage: 24 bytes (calls _putString)
#
#############################################################
CantContinue:
	.function	"CantContinue",line=0,framesize=8
	enterfun	sp,sp,-8	# Prolog
CantContinue_2:
	movi		r1,noGoMessage	# Print "You may not continue"
	call		_putString	# .
	breakpoint			# Pop up to BLITZ Emulator command mode
	call		CantContinue_2	# ... and repeat, if "go" is entered
	.endfunction
noGoMessage:
	.string	"\n==================== The KPL program has terminated; you may not continue. ====================\n\0"
	.align	8



#####################  _putString  ####################
# 
# This function is passed a pointer to a string of characters terminated by '\0'.
# It prints all of them except the final '\0' by calling Basic_PrintChar.
# The string is not a KPL array, so there are no leading words (max size, current size).
# This function will print '\n' and '\r\n', in case we are in raw mode.
#
# Passed:
#   r1: ptr to "C" string with NUL termination
# Standard Calling Conventions:
#   No - KPL compiler expects register r2 to be preserved!
#   Doesn't use or require an arg-save-area in caller's frame.
#   Otherwise, follows conventions
# Not atomic or thread-safe
#
# Since we call a KPL function, we must allocate stack space for the
# arg save area. Here is the stack frame for this function:
#   sp -->  0: arg_save_area (one arg to Basic_PrintChar)
#           8: r2 save area
#          16: "p_save"
#          24: ret addr
#
#############################################################

_putString:
	.function	"_putString",line=0,framesize=32
	enterfun	sp,sp,-32		# Allocate frame
	store.d		0(sp),r1		# Save r1
	store.d		8(sp),r2		# Save r2
	store.d		16(sp),r1		# p_save = r1
putStLoop:					# LOOP
	load.d		r1,16(sp)		#   r1 = p_save
	addi		r2,r1,1			#   p_save = r1 + 1
	store.d		16(sp),r2		#   .
	load.b		r1,0(r1)		#   r1 = *r1
	beqz		r1,putStExit		#   IF r1 == 0 BREAK
	bnei		r1,'\n',putElse		#   IF r1 == \n
	call		Basic_PrintChar		#     Basic_PrintChar (\n)
	movi		r1,'\r'			#     Basic_PrintChar (\r)
putElse:					#   ELSE
	call		Basic_PrintChar		#     Basic_PrintChar (r1)
						#   ENDIF
	jump		putStLoop		# ENDLOOP
putStExit:					# .
	loadd		r1,0(sp)		# Restore regs and return
	loadd		r2,8(sp)		# Restore regs and return
	exitfun		sp,sp,32		# .
	.endfunction


### _putString:
### 	.function	"_putString",line=0,framesize=0
### 	stored	-8(sp),r1		# save registers
### 	stored	-16(sp),r2		# .
### 	stored	-24(sp),r3		# .
### 	movi	r3,0x0001		# Suspend interrupts
### 	csrswap	r3,csr_status,r3	# .
### putStLoop:				# loop
### 	loadb	r2,0(r1)		#   r2 := next char
### 	beqz	r2,putStExit		#   if (r2 == '\0') then break
### 	stored	Serial_String_Char,r2   #   Print char
### 	addi	r1,r1,1			#   incr ptr
### 	jump	putStLoop		# end
### putStExit:				# .
### 	csrswap	r3,csr_status,r3	# Restore csr_status
### 	loadd	r1,-8(sp)		# restore regs
### 	loadd	r2,-16(sp)		# .
### 	loadd	r3,-24(sp)		# .
### 	ret				# return
### 	.endfunction


#####################  strEqual  ####################
# 
# external strEqual (p1, p2: String)
#
# This function returns TRUE if the strings have the same size and contain the same bytes.
#
# Here is the algorithm:
#
#       -- Compare the array sizes...
#       byteCount = *asPtrTo (p1 + 4, word)
#       if byteCount != *asPtrTo (p2 + 4, word)
#         return false
#       endIf
#       -- Advance past the array header...
#       p1 = p1 + 8
#       p2 = p2 + 8
#       -- In a loop, compare doublewords...
#       while byteCount > 7
#         if * asPtrTo (p1, int) != * asPtrTo (p2, int)
#           return false
#         endIf
#         p1 = p1 + 8
#         p2 = p2 + 8
#         byteCount = byteCount - 8
#       endWhile
#       -- In a loop, compare remaining bytes...
#       while byteCount > 0
#         if * asPtrTo (p1, byte) != * asPtrTo (p2, byte)
#           return false
#         endIf
#         p1 = p1 + 1
#         p2 = p2 + 1
#         byteCount = byteCount - 1
#       endWhile
#       return true
#
# Register usage:
#   r1 = p1
#   r2 = p2
#   r3 = byteCount
#   r4 = work reg
#   r5 = work reg
# Max Stack Usage: 8
#
#############################################################

strEqual:
	.function		"strEqual",line=0,framesize=8
	store.d	-8(sp),lr		# Prologue
        addi	sp,sp,-8		# .

#-- Compare the array sizes...
	loadw	r3,4(r1)		# byteCount = current arraySize (p1)
	loadw	r4,4(r2)		# t = current arraySize (p2)
	bne	r3,r4,stEq_retF		# If not equal, return false

#-- Advance past the array header...
	addi	r1,r1,8			# p1 = p1 + 8
	addi	r2,r2,8			# p2 = p2 + 8

#-- In a loop, compare doublewords...
	blei	r3,7,stEq_loopD_x	# WHILE byteCount > 7
stEq_loopD:				# .
	loadd	r4,0(r1)		#   If *p1 != *p2 (doubleword)
	loadd	r5,0(r2)		#   .
	bne	r4,r5,stEq_retF		#     return false
	addi	r1,r1,8			#   p1 = p1 + 8
	addi	r2,r2,8			#   p2 = p2 + 8
	addi	r3,r3,-8		#   byteCount = byteCount - 8
	bgti	r3,7,stEq_loopD		# ENDWHILE
stEq_loopD_x:				# .

#-- In a loop, compare remaining bytes...
	blez	r3,stEq_loopB_x		# WHILE byteCount > 0
stEq_loopB:				# .
	loadb	r4,0(r1)		#   If *p1 != *p2 (byte)
	loadb	r5,0(r2)		#   .
	bne	r4,r5,stEq_retF		#     return false
	addi	r1,r1,1			#   p1 = p1 + 1
	addi	r2,r2,1			#   p2 = p2 + 1
	addi	r3,r3,-1		#   byteCount = byteCount - 1
	bgtz	r3,stEq_loopB		# ENDWHILE
stEq_loopB_x:				# .

#-- Come here to return TRUE...
stEq_retT:
	addi	sp,sp,8			# Restore the stack
	load.d	lr,-8(sp)		#
	movi	r1,1			# RETURN true
	ret				#

#-- Come here to return FALSE...
stEq_retF:
	addi	sp,sp,8			# Restore the stack
	load.d	lr,-8(sp)		#
	movi	r1,0			# RETURN false
	ret				#
	.endfunction



#############################################################
#
# Here is the layout of the ThreadData object.
#
#############################################################

OFFSET_OF_catchStack:		.equ	8
OFFSET_OF_memAllocFun:		.equ	16
OFFSET_OF_memFreeFun:		.equ	24
OFFSET_OF_fatalErrorFun:	.equ	32
OFFSET_OF_exitFun:		.equ	40
OFFSET_OF_debuggerFun:		.equ	48
OFFSET_OF_basicPrintCharFun:	.equ	56
OFFSET_OF_basicReadCharFun:	.equ	64

#############################################################
#
# external MemoryAlloc (byteCount: int) returns ptr to byte
# external MemoryFree (p: ptr to byte)
# external FatalError (errorMessage: String)
# external exit (exitCode: String)
# external InvokeDebugger (codeAddress: int)
# external Basic_PrintChar (ch: int)
# external Basic_ReadChar  () returns byte
# 
# This function looks in the ThreadData object, which is pointed to by "tp".
# A particular field within this object will contain a pointer to the actual
# function to be called.
#
#############################################################

MemoryAlloc:		load.d	t,OFFSET_OF_memAllocFun(tp)
			jr	t

MemoryFree:		load.d	t,OFFSET_OF_memFreeFun(tp)
			jr	t

FatalError:		load.d	t,OFFSET_OF_fatalErrorFun(tp)
			jr	t

exit:			load.d	t,OFFSET_OF_exitFun(tp)
			jr	t

InvokeDebugger:		load.d	t,OFFSET_OF_debuggerFun(tp)
			jr	t

Basic_PrintChar:	load.d	t,OFFSET_OF_basicPrintCharFun(tp)
			jr	t

Basic_ReadChar:		load.d	t,OFFSET_OF_basicReadCharFun(tp)
			jr	t




#####################  _IsKindOf  ####################
#
#           _IsKindOf - UPCALL TO "KPLIsKindOf"
# 
# This function is passed a pointer to an object in r1 and a pointer to
# a type descriptor in r2.  It determines whether that object "is a kind of"
# that type.  It returns either TRUE (1) or FALSE (0) in r1.
#
# This function is called from code generated by the compiler.  It will perform
# an "upcall" to a function written in KPL.
#
# Standard KPL calling conventions: Yes
# Passed: r1 = pointer to object
#         r2 = pointer to type descriptor
# Returns: bool
#
#############################################################

#  _IsKindOf:	.equ	_P_KPLSupport_KPLIsKindOf
_IsKindOf:
        .function       "_IsKindOf",line=0,framesize=8

#
# Initially, this function was written in KPL; that version was tested.
# Subsequently, the function was re-coded in assembly, as follows. Here is
# the original KPL code, which described what this assembly code does...
#
#   function KPLIsKindOf (objectPtr: ptr to Object, ptrToMatch: ptr to void) returns bool
#
#     -- There will be an up-call from the runtime system to this function, which
#     -- will evaluate the "isKindOf" relation.  This routine determines whether
#     -- the object pointed to satisfies the "isKindOf" relation, i.e., whether it
#     -- is an instance of the given type, or of one of its super-classes or super-
#     -- interfaces.
#     --
#     -- If the object ptr is null or if the object is uninitialized we'll get
#     -- a Null Address Exception.
#
# r1 = objectPtr:  ptr to Object
# r2 = ptrToMatch: ptr to void
# r3 = dispTable:  ptr to KPL_DISPATCH_TABLE
# r4 = myClass:    ptr to KPL_CLASS_DESCRIPTOR
# r5 = next:       ptr to ptr to void
#
#      -- Look at the object and get a pointer to the Dispatch Table...
#      dispTable = * asPtrTo (objectPtr, ptr to KPL_DISPATCH_TABLE)
#
#      -- Get a ptr to the Class Descriptor...
#      myClass = dispTable.classDescriptor
#
#      -- Make sure the magic number is what we expect it to be...
#      if myClass.magic != 0x434c415353646573   -- 'CLASSdes'
#        FatalError ("WITHIN KPLIsKindOf: Bad Magic Number in class descriptor")
#      endIf
#
#      -- Run through all supers.  (Each class is a super of itself.)
#      next = & myClass.firstSuperPtr
#      while true
#        if *next == null
#          return false
#        elseIf *next == ptrToMatch
#          return true
#        endIf
#        next = next + 8
#      endWhile

	store.d -8(sp),lr			# save return address
	addi	sp,sp,-8			# allocate frame
	load.d	r3,0(r1)			# dispTable = * asPtrTo (objectPtr, ptr to KPL_DISPATCH_TABLE)
	load.d	r4,0(r3)			# myClass = dispTable.classDescriptor
	load.d	r6,0(r4)			# temp = myClass.magic
	beqi	r6,0x434c415353646573,IsKind_OK	# IF temp != 'CLASSdes'
	movi	r1,BadNumberString		#    CALL FatalError ("Bad Magic Number")
	call	FatalError			#    .
IsKind_OK:					# ENDIF
	addi	r5,r4,32			# next = & myClass.firstSuperPtr
IsKind_Loop:					# LOOP
	load.d	r6,0(r5)			#   r6 = *next
	beqz	r6,IsKind_return_false		#   IF r6 == NULL return false
	beq	r6,r2,IsKind_return_true	#   IF r6 == ptrToMatch return true
	addi	r5,r5,8				#   next = next + 8
	jump	IsKind_Loop			# ENDLOOP

IsKind_return_false:
	movi	r1,0
	addi	sp,sp,8
	load.d	lr,-8(sp)
	ret

IsKind_return_true:
	movi	r1,1
	addi	sp,sp,8
	load.d	lr,-8(sp)
	ret
	.endfunction

	.align	8
BadNumberString:
	.word		BadNumberString_x-BadNumberString_1
	.word		BadNumberString_x-BadNumberString_1
BadNumberString_1:
	.string		"WITHIN KPLIsKindOf: Bad Magic Number in class descriptor"
BadNumberString_x:
	.byte		0	# (So previous label is different from the next label)
	.align		4



#####################  _PerformThrow  ####################
#
# This function is passed r1 = an ErrorID (just a ptr to an error name string).
# It looks down the Catch Stack until it finds a CATCH_RECORD with a matching
# ErrorID.  Then it jumps to the corresponding catch code, using the ptr stored
# in the CATCH_RECORD.  It also restores the SP to its saved value, which makes
# it possible to re-enter the suspended function.
#
# When an error is thrown, but not caught, this code will perform an upcall to
# the function "KPLMissingCatch" in package "System".  That function
# should print some info about the situation and then throw "UncaughtError".
# Thus, we should never be returning from the call.  (The user code may or may
# not catch "UncaughtError", but if this too is uncaught, this code will call
# "KPLFatalThrowError".)
#
# When this code is invoked, we have already moved the arguments into registers
# r2...r7 and the others are stored within the argument-passing-area in the top frame.
#
# We'll need some regs to work with here, so we push r6 and r7 onto the stack, just above
# the current frame, without modifying sp.
#
# Upon entry, register r14 (lr) contains the PC from the KPL code. If we get a runtime
# error, we jump to the handler (instead of calling it), which will preserve this address.
# However, an FatalThrowError is pretty nasty and we should not rely on calling KPL code to
# avoid infinite looping in the event that the user code can't handle errors properly.
#
# This function ends by jumping to the catch code.
#
#
#     << r1 = errorID    THIS IS DONE IN THE COMPILED CODE >>
#        save r6, r7 on the stack
#        r7 = tp (ptr to record on top of CATCH STACK)
#    loop:
#          if r7 == NULL
#            if r1 == "uncaughtThrow" then call KPLFatalThrowError
#            call KPLMissingCatch (shift args; for errors arg1 is in r2, etc.)
#            goto _PerformThrow_CatastrophicFailure
#          end
#          if r7->errorID == r1 then break
#          r7 = r7->next
#        goto loop
#        restore SP from r7->oldSP
#        jump to r7->catchCode
#        (but first restore r6 and r7: NOTE that "lr" is trashed in doing this.)
#
#    _PerformThrow_CatastrophicFailure:
#        Print error and terminate execution
#
#############################################################

_PerformThrow:
        .function       "_PerformThrow",line=0,framesize=24
# We need a frame big enough for our return address and the 2 args to the functions
# we might call (KPLFatalThrowError, KPLMissingCatch).
	store.d	-8(sp),r7			# Save r7
	store.d	-16(sp),r6			# Save r6
	load.d	r7,8(tp)			# r7 = CatchStack top ptr
_PerformThrow_loop:				# LOOP:
	bnez	r7,_PerformThrow_else		#   if r7 == NULL
						#     if r1 == "UncaughtThrowError"
	bnei	r1,_Error_P_KPLSupport_UncaughtThrowError,_PerformThrow_NotFatal
	mov	r1,r2				#       r1 = arg1 of UncaughtThrowError
	mov	r2,r3				#       r2 = arg2 of UncaughtThrowError
	store.d	-8(sp),lr			#       Setup a frame (for debugger's use)
	addi	sp,sp,-24			#       .
	call	_P_KPLSupport_KPLFatalThrowError   #    call KPLFatalThrowError
	jump	_PerformThrow_CatastrophicFailure  #    (If we return, goto runtime error)
_PerformThrow_NotFatal:				#     endIf
	addi	r2,lr,-4			#     r2 = addr of throw, not the following instr
	store.d	-8(sp),lr			#     Setup a frame (for debugger's use)
	addi	sp,sp,-24			#     .
	call	_P_KPLSupport_KPLMissingCatch	#     call KPLMissingCatch (r1 = errorID)
	jump	_PerformThrow_CatastrophicFailure  #     (If we return, goto runtime error)
_PerformThrow_else:				#   end
	load.d	r6,8(r7)			#   r6 = errorID of this record
	beq	r1,r6,_PerformThrow_found	#   if it matches r1, then goto FOUND
	load.d	r7,0(r7)			#   r7 = r7->next
	jump     _PerformThrow_loop		# END LOOP
_PerformThrow_found:				# FOUND:
	mov	r1,sp				#   r1 = old SP (so catch code can retrieve big args)
	mov	t,r7				#   t points to the catch record
	load.d	r6,-16(sp)			#   restore r6
	load.d	r7,-8(sp)			#   restore r7
						#   lr points to instr after throw
	load.d	sp,24(t)			#   sp = sp from stack record
	load.d	t,16(t)				#   t = catch code addr
	jr	t				#   jump to catch code

_PerformThrow_CatastrophicFailure:
	movi	r1,Throw_Err_Mess		# Print an error message
	call	_putString			# .
	movi	r1,-1				# Terminate execution
	call	exit				# .
	.endfunction

Throw_Err_Mess:
	.string		"\n********** FATAL ERROR IN RUNTIME SUPPORT: Attempt to return "
	.string		"from KPLFatalThrowError or KPLMissingCatch! **********\n\0"
	.align		8
#
# On entry to the catch code:
#        r1 = the old stack pointer (needed in order to retrieve big arguments)
#        sp = the new sp
#        r2 ... r7 = the first arguments



##### #############################################################
##### #
##### # print (str: ptr to array of byte)
##### #
##### # Prints by calling Basic_PrintChar
##### #
##### ##### NOTE: This function is implemented both in KPL and assembler.
##### #####       The performance difference seems negligible.
##### ##### NOTE: The KPL version is slightly different and better; it
##### #####       takes into account the echoing of \n with \r\n. Thus,
##### #####       the KPL version is preferred.
##### #
##### # Passed:
##### #    r1 = pointer to an array of bytes.
##### # Standard Calling Conventions; uses stack
##### #    0(sp) = p (ptr to chars)
##### #    8(sp) = count (num chars left to print)
##### #
##### #############################################################
##### 
##### 	.function "print", line=0, framesize=24
##### ##### print:
##### 	enterfun	sp,sp,-24		# Allocate frame (24 bytes)
##### 
##### 	beqz		r1,BaPr_return		# Return if ptr to String is null
##### 	addi		r1,r1,8			# p = r1+8 (advance to first byte)
##### 	stored		0(sp),r1		# .
##### 	loadw		r2,-4(r1)		# Get CURRENT length and save
##### 	stored		8(sp),r2		# .
#####         beqz		r2,BaPr_return		# IF count != 0 abort
##### BaPr_loop:          	                        # LOOP
##### 	loadd		r1,0(sp)		#   r1 = *(p++)
##### 	addi		r3,r1,1			#   .
##### 	stored		0(sp),r3		#   .
##### 	loadb		r1,0(r1)		#   .
##### 	call		Basic_PrintChar		#   Call Basic_PrintChar (r1)
##### 	loadd		r2,8(sp)		#   Decrement count
##### 	addi		r2,r2,-1		#   .
##### 	stored		8(sp),r2		#   .
##### 	bnez		r2,BaPr_loop	        # UNTIL count == 0
##### BaPr_return:					# ENDIF
#####         exitfun		sp,sp,24		# Return
##### 	.endfunction

#####################  maximizeString  ####################
#
# maximizeString (p: ptr to array of ...)
#
# Set CURRENT size to MAX size; throw error if array is uninitialized.
#
# Passed:          r1 = ptr to an array
# Regs Changed:    t
# Max Stack Usage: 0 bytes
#
#############################################################

	.function "maximizeString", line=0, framesize=0
maximizeString:
	load.w	t,0(r1)		# get MAX # of elements
	# In the following, reg t may be modified, but only if the jump is taken... 
        beqz	t,_runtimeErrorUninitializedArray
	store.w	4(r1),t		# store into CURRENT # field
	ret
	.endfunction

#####################  endianSwapH  ####################
#
# external endianSwapH (arg: int) returns halfword
#
# Swap bytes in the lower halfword and sign-extend.
#
# Passed:          r1 = arg: int
# Returns:         r1 = halfword
# Regs Changed:    r1
# Max Stack Usage: 0 bytes
#
#############################################################

	.function "endianSwapH", line=0, framesize=0
endianSwapH:
	endianh	r1,r1
        sexth	r1,r1
	ret
	.endfunction

#####################  endianSwapW  ####################
#
# external endianSwapW (arg: int) returns word
#
# Swap bytes in the lower word and sign-extend.
#
# Passed:          r1 = arg: int
# Returns:         r1 = word
# Regs Changed:    r1
# Max Stack Usage: 0 bytes
#
#############################################################

	.function "endianSwapW", line=0, framesize=0
endianSwapW:
	endianw	r1,r1
        sextw	r1,r1
	ret
	.endfunction

#####################  endianSwapD  ####################
#
# external endianSwapD (arg: int) returns int
#
# Swap bytes all.
#
# Passed:          r1 = arg: int
# Returns:         r1 = int
# Regs Changed:    r1
# Max Stack Usage: 0 bytes
#
#############################################################

	.function "endianSwapD", line=0, framesize=0
endianSwapD:
	endiand	r1,r1
	ret
	.endfunction

#####################  rotateRight32  ####################
#
# external rotateRight32 (arg, shift: int) returns int
#
# Rotate the 32 least significant bits right but "shift" bits. If "shift" is
# negative, rotate left. Only the least 6 bits of shift are used, and interpreted
# as a signed number in -32 ... +31. The high-order 32 bits of the result are
# set to zero.
#
# Passed:          r1 = arg: int
# Passed:          r2 = shift: int
# Returns:         r1 = int
# Regs Changed:    r1, r3
# Max Stack Usage: 0 bytes
#
#############################################################

	.function "rotateRight32", line=0, framesize=0
rotateRight32:
	slli	r1,r1,32
	srli	r3,r1,32
	or	r1,r1,r3
	rotr	r1,r1,r2
	srli	r1,r1,32
	ret
	.endfunction

#####################  rotateRight64  ####################
#
# external rotateRight64 (arg, shift: int) returns int
#
# Execute the ROTR instruction.
#
# Passed:          r1 = arg: int
# Passed:          r2 = shift: int
# Returns:         r1 = int
# Regs Changed:    r1, r3
# Max Stack Usage: 0 bytes
#
#############################################################

	.function "rotateRight64", line=0, framesize=0
rotateRight64:
	rotr	r1,r1,r2
	ret
	.endfunction

#####################  resetFloatingStatus  ####################
#
# external resetFloatingStatus ()
#
# This function resets the FLOAT_STATUS bits (i.e., NV, OF, UF, DZ, NX) within csr_status. 
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "resetFloatingStatus",line=0,framesize=0
resetFloatingStatus:
	getstat		t
	andi		t,t,0xffffffffffffff07		# clear bits [7:3]
	putstat		t
	ret
	.endfunction

#####################  floatingInexact  ####################
#
# external floatingInexact () returns bool
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

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

	getstat		r1
	srli		r1,r1,3		# isolate bit 3
	andi		r1,r1,1
	ret
	.endfunction

#####################  floatingDivideByZero  ####################
#
# external floatingDivideByZero () returns bool
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

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

	getstat 	r1
	srli		r1,r1,4		# isolate bit 4
	andi		r1,r1,1
	ret
	.endfunction

#####################  floatingUnderflow  ####################
#
# external floatingUnderflow () returns bool
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

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

	getstat	 r1
	srli		r1,r1,5		# isolate bit 5
	andi		r1,r1,1
	ret
	.endfunction

#####################  floatingOverflow  ####################
#
# external floatingOverflow () returns bool
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

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

	getstat		r1
	srli		r1,r1,6		# isolate bit 6
	andi		r1,r1,1
	ret
	.endfunction

#####################  floatingInvalid  ####################
#
# external floatingInvalid () returns bool
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
        .function       "floatingInvalid",line=0,framesize=0
floatingInvalid:

	getstat		r1
	srli		r1,r1,7		        # isolate bit 7
	andi		r1,r1,1
	ret
	.endfunction

#####################  setFloatingInexact  ####################
#
# external setFloatingInexact ()
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "setFloatingInexact",line=0,framesize=0
setFloatingInexact:
	getstat		r1
	ori		r1,r1,0x0008            # set bit 3
	putstat		r1
	ret
	.endfunction

#####################  setFloatingDivideByZero  ####################
#
# external setFloatingDivideByZero ()
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "setFloatingDivideByZero",line=0,framesize=0
setFloatingDivideByZero:
	getstat		r1
	ori		r1,r1,0x0010            # set bit 4
	putstat		r1
	ret
	.endfunction

#####################  setFloatingUnderflow  ####################
#
# external setFloatingUnderflow ()
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "setFloatingUnderflow",line=0,framesize=0
setFloatingUnderflow:
	getstat		r1
	ori		r1,r1,0x0020            # set bit 5
	putstat		r1
	ret
	.endfunction

##################### setFloatingOverflow  ####################
#
# external setFloatingOverflow ()
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "setFloatingOverflow",line=0,framesize=0
setFloatingOverflow:
	getstat		r1
	ori		r1,r1,0x0040            # set bit 6
	putstat		r1
	ret
	.endfunction

#####################  setFloatingInvalid  ####################
#
# external setFloatingInvalid ()
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "setFloatingInvalid",line=0,framesize=0
setFloatingInvalid:
	getstat		r1
	ori		r1,r1,0x0080            # set bit 7
	putstat		r1
	ret
	.endfunction

#####################  floatingSqrt  ####################
#
# external floatingSqrt (d: double) returns double
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "floatingSqrt",line=0,framesize=0
floatingSqrt:
	fsqrt		r1,r1
	ret
	.endfunction

#####################  floatingAbs  ####################
#
# external floatingAbs (d: double) returns double
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "floatingAbs",line=0,framesize=0
floatingAbs:
	fabs		r1,r1
	ret
	.endfunction

#####################  floatingMin  ####################
#
# external floatingMin (d1, d2: double) returns double
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "floatingMin",line=0,framesize=0
floatingMin:
	fmin		r1,r1,r2
	ret
	.endfunction

#####################  floatingMax  ####################
#
# external floatingMax d1, d2: double) returns double
#
# This function follows the standard calling conventions
#
# Max Stack Usage: 0 bytes
#
#############################################################

        .function       "floatingMax",line=0,framesize=0
floatingMax:
	fmax		r1,r1,r2
	ret
	.endfunction

#####################  initializeThreadPtr  ####################
#
# initializeThreadPtr (p: ptr to ThreadData)
#
# This function stores a value into register "tp".
#
# Standard KPL calling conventions: Yes
# Regs Changed:         tp
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
#
#############################################################

	.function "initializeThreadPtr", line=0, framesize=0
initializeThreadPtr:
	mov	tp,r1
	ret
	.endfunction

#####################  GetSP  ####################
#
# GetSP () returns int
#
# This function returns the value of the SP register.
#
# Standard KPL calling conventions: Yes
# Regs Changed:         r1
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
#
#############################################################

	.function	"GetSP", line=0, framesize=0
GetSP:
	mov		r1,sp
	ret
	.endfunction

#####################  PutSP  ####################
#
# PutSP () returns int
#
# This function updates the SP register.
#
# Standard KPL calling conventions: Yes
#
#############################################################

	.function	"PutSP", line=0, framesize=0
PutSP:
	mov		sp,r1
	ret
	.endfunction

#####################  LeafError  ####################
#
# external LeafError ()
# 
# This is a leaf function that will cause an Arithmetic Exception. Used to test error reporting.
#
#############################################################

LeafError:
	.function	"LeafError",line=0,framesize=0
	movi		r1,0x7fffffffffffffff
	addi		r1,r1,1
	ret
	.endfunction

#####################  memorySet8  ####################
# 
# external memorySet8 (destPtr, initVal, byteCount)
#
# This function stores "initVal" in memory. The pointer must be evenly divisible by 8.
# If byteCount <= 0, then this is a nop. If byteCount is not a multiple of 8, it will
# be rounded up to the next multiple of 8. This function is implemented in assembly
# only for efficiency.
#
# Register usage:
#   r1 = destPtr
#   r2 = initVal
#   r3 = byteCount
#   r4 = beyond pointer
# Stack:
#   KPL calling conventions are followed.
#
#############################################################

memorySet8:
	.function		"memorySet8",line=0,framesize=8
	store.d	-8(sp),lr		# Prologue
        addi	sp,sp,-8		# .
	ble	r3,r0,MZ8_ret		# If byteCount is <= 0 then return
	add	r4,r1,r3		# r4 = beyondPtr
MZ8_loop:				# REPEAT
	stored	0(r1),r2		#   *r1 = initVal
	addi	r1,r1,8			#   r1 = r1 + 8
	blt	r1,r4,MZ8_loop		# UNTIL r1 >= beyondPtr
MZ8_ret:				# Restore the stack
	addi	sp,sp,8			# .
	load.d	lr,-8(sp)		# .
	ret				# RETURN
	.endfunction

#####################  memoryCopy8  ####################
# 
# external memoryCopy8 (destPtr, srcPtr, byteCount)
#
# Both pointers must be evenly divisible by 8. If byteCount <= 0, then this is a nop.
# If byteCount is not a multiple of 8, it will be rounded up to the next multiple of 8.
#
# This function is implemented in assembly only for efficiency. 
#
# This function is similar to "memoryCopy", except with "memoryCopy" there are no
# alignment requirements. There is not much to be gained since "memoryCopy" only
# adds 8 instructions per call. For copying a 16 KiByte page this is 10,246 instructions
# versus 10,254 instructions, which is a negligible overhead.
#
# Instructions executed:
#    4     function prologue and epilog
#  + 2
#  + 5*k   where k = n/8 = number of doublewords
#
# Register usage:
#   r1 = destPtr
#   r2 = srcPtr
#   r3 = byteCount
#   r4 = beyond pointer
#   r5 = temp
# Stack:
#   KPL calling conventions are followed.
#
#############################################################

memoryCopy8:
	.function		"memoryCopy8",line=0,framesize=8
	store.d	-8(sp),lr		# Prologue
        addi	sp,sp,-8		# . (for debugger)
	ble	r3,r0,MC8_ret		# If byteCount is <= 0 then return
	add	r4,r1,r3		# beyondPtr = destPtr + byteCount
MC8_loop:				# REPEAT
	loadd	r5,0(r2)		#   t = * srcPtr
	stored	0(r1),r5		#   * destPtr = t
	addi	r1,r1,8			#   destPtr = destPtr + 8
	addi	r2,r2,8			#   srcPtr = srcPtr + 8
	blt	r1,r4,MC8_loop		# UNTIL destPtr >= beyondPtr
MC8_ret:
	addi	sp,sp,8			# Restore the stack
#	load.d	lr,-8(sp)		#   (lr not modified)
	ret				# RETURN
	.endfunction

#####################  memoryCopy  ####################
# 
# external memoryCopy (p1, p2: ptr to byte, byteCount: int)
#
# This functions copies "byteCount" bytes from the address pointed to by "p2"
# to the address pointed to by "p1". There is no check for overlap.
#
# This algorithm tries to be fast. Let n = byteCount.
#
#    n           instructions (not counting prologue/return overhead of 4 instructions)
#   ===          ============
#    0              2
#    1              5
#  ...n...          3*n + 2
#    7             23
# When the pointers are both aligned...
#    8             15
#  ...8*j+k...      5*j + 3*k + 10
#   15             35
#   16             20
#   ...
#   20             32
#
# NOTE: For doubleword aligned copies, this function requires:
#    4     function prologue and epilog
#  + 10
#  + 5*k   where k = n/8 = number of doublewords
#
# Register usage:
#   r1 = p1
#   r2 = p2
#   r3 = byteCount
#   r4 = work reg
#   r5 = bc, modified byteCount
#   r6 = beyondPtr
# Max Stack Usage: 8
#
#############################################################

memoryCopy:
	.function		"memoryCopy",line=0,framesize=8
	store.d	-8(sp),lr		# Prologue
        addi	sp,sp,-8		# .

#-- If the pointers are doubleword aligned and the byteCount > 7...
	blei	r3,7,MC1_CHECK_7	# If byteCount <= 7
                                        #   goto CHECK_7
	andi	r4,r1,0x0007		# If p1 is not doubleword aligned
	bnez	r4,MC1_UNALIGNED	#   goto UNALIGNED
	andi	r4,r2,0x0007		# If p2 is not doubleword aligned
	bnez	r4,MC1_UNALIGNED	#   goto UNALIGNED

#-- In a loop, copy doublewords...

	andi	r5,r3,!0x0007		#   bc = byteCount rounded down to a multiple of 8
	add	r6,r1,r5		#   beyondPtr = p1 + bc
	sub	r3,r3,r5		#   byteCount = byteCount - bc
MC1_loopD:				#   DO
	loadd	r4,0(r2)		#     If *p1 = *p2 (doubleword)
	stored	0(r1),r4		#     .
	addi	r1,r1,8			#     p1 = p1 + 8
	addi	r2,r2,8			#     p2 = p2 + 8
	blt	r1,r6,MC1_loopD		#   UNTIL p1 >= beyondPtr

#-- The pointers are not aligned, so we must copy each byte individually...
MC1_UNALIGNED:				# UNALIGNED:

#-- In a loop, copy all bytes, except the last 7...
	blei	r3,7,MC1_CHECK_7	# IF byteCount > 7
	addi	r5,r3,-7		#   bc = byteCount - 7
	add	r6,r1,r5		#   beyondPtr = p1 + bc
	sub	r3,r3,r5		#   byteCount = byteCount - bc
MC1_loopB:				#   DO
	loadb	r4,0(r2)		#     If *p1 = *p2 (byte)
	storeb	0(r1),r4		#     .
	addi	r1,r1,1			#     p1 = p1 + 1
	addi	r2,r2,1			#     p2 = p2 + 1
	blt	r1,r6,MC1_loopB		#   UNTIL p1 >= beyondPtr
#					# ENDIF

#-- At this point, we have byteCount<=7. Move all remaining bytes...
MC1_CHECK_7:				# CHECK_7:
	beqi	r3,0,MC1_RETURN		#   if byteCount == 0 goto RETURN
	beqi	r3,1,MC1_DO_1		#   if byteCount == 1 goto DO_1
	beqi	r3,2,MC1_DO_2		#   if byteCount == 2 goto DO_2
	beqi	r3,3,MC1_DO_3		#   if byteCount == 3 goto DO_3
	beqi	r3,4,MC1_DO_4		#   if byteCount == 4 goto DO_4
	beqi	r3,5,MC1_DO_5		#   if byteCount == 5 goto DO_5
	beqi	r3,6,MC1_DO_6		#   if byteCount == 6 goto DO_6

MC1_DO_7:				# DO_7:
	loadb	r4,6(r2)		#   * (p1+6) = * (p2+6)
	storeb	6(r1),r4		#   .
MC1_DO_6:				# DO_6:
	loadb	r4,5(r2)		#   * (p1+5) = * (p2+5)
	storeb	5(r1),r4		#   .
MC1_DO_5:				# DO_5:
	loadb	r4,4(r2)		#   * (p1+4) = * (p2+4)
	storeb	4(r1),r4		#   .
MC1_DO_4:				# DO_4:
	loadb	r4,3(r2)		#   * (p1+3) = * (p2+3)
	storeb	3(r1),r4		#   .
MC1_DO_3:				# DO_3:
	loadb	r4,2(r2)		#   * (p1+2) = * (p2+2)
	storeb	2(r1),r4		#   .
MC1_DO_2:				# DO_2:
	loadb	r4,1(r2)		#   * (p1+1) = * (p2+1)
	storeb	1(r1),r4		#   .
MC1_DO_1:				# DO_1:
	loadb	r4,0(r2)		#   * (p1+0) = * (p2+0)
	storeb	0(r1),r4		#   .
	
#-- Come here to return...
MC1_RETURN:
	addi	sp,sp,8			# Restore the stack
#	load.d	lr,-8(sp)		#   (lr was not modified)
	ret				#
	.endfunction


