# ==========  RUNTIME SUPPORT CODE  ========== 
#
# This file contains basic support functions that use privileged instructions.
# It is needed by
#    "Basic Programs" (Stand-alone, single-threaded, run by the emulator)
#    Kernel Code running under the emulator
#
# 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  -  21 Sept 2023        - Reorganized
#

	.sourcefile	"BasicRuntime.s", "Privileged Instruction Support - Harry Porter"

	.export		EmulatorDebuggingRequested
	.export		HaltEmulator

	.export		GetCsrCycle
	.export		GetCsrInstr
	.export		GetCsrTimer
	.export		SetCsrTimer
	.export		CyclesFor4000Instructions
	.export		BoostCsrTimer
	.export		SetInterruptsTo
	.export		Sleep1
	.export		executeRestartInstruction

	.export		cpuid
	.export		intr_on
	.export		intr_off
	.export		intr_get
	.export		get_csr_prevpc
	.export		get_csr_stat2
	.export		get_csr_ptr
	.export		get_csr_cause
	.export		get_csr_bad
	.export		get_csr_addr
	.export		get_csr_pgtable
	.export		put_csr_prevpc
	.export		put_csr_ptr
	.export		put_csr_trapvec
	.export		put_csr_pgtable
	.export		incr_csr_timer
	.export		set_csr_timer
	.export		get_csr_cycle
	.export		put_sp
	.export		do_tlbclear
	.export		do_tlbflush
	.export		checkaddr_loadd
	.export		checkaddr_loadw
	.export		checkaddr_loadh
	.export		checkaddr_loadb

	.import		_kplEntry				# From compiled "main" function
	.import		CantContinue				# .

	.import		_P_BasicSystem_KPLSystemInitialize
	.export		_KPLSysInit

###	.import		_P_HostInterface_Control_PrintChar
###	.import		_P_HostInterface_Control_ReadChar
###	.import		_P_KPLSupport_print
###	.import		_P_KPLSupport_readString
###	.import		_P_HostInterface_UART_ReadChar
###	.import		_P_HostInterface_UART_PrintChar


	.begin		executable



#####################  _KPLSysInit  ####################
#
#           _KPLSysInit - UPCALL TO "KPLSystemInitialize"
# 
# This function is passed nothing and returns nothing.  It is called during
# startup to initialize the heap, as necessary.
#
# This function is called from code generated by the compiler.
# Here we are equating this to the name of a function written in KPL so
# the KPL function will be invoked to do the work.
#
# Standard KPL calling conventions: Yes
# Passed: nothing
# Returns: nothing
#
_KPLSysInit:	.equ	_P_BasicSystem_KPLSystemInitialize




# 
# =====================  HaltEmulator  =====================
# 
# external HaltEmulator (exitStatus: int)
#
# This function causes an immediate termination of the emulator with "exitStatus"
# being returned as the Unix/Linux exit code. No message is printed.
#
# Stack usage:     not used
# Arguments:       r1 = Exit code (0=ok/no error)
# Returned value:  < does not return >
# Interrupts:      Don't care
# Max Stack Usage: 24 bytes (CantContinue calls _putString)
# 
HaltEmulator:
	.function	"HaltEmulator",line=0,framesize=0
	sleep2				# The emulator will retrieve exitStatus from reg r1.
	call		CantContinue
	.endfunction


#####################  EmulatorDebuggingRequested  ####################
# 
# external EmulatorDebuggingRequested (codeAddress: int)
#
# This function is called by KPL code whenever we want to suspend execution
# and throw the user into the emulator's debugger. This is done by looking like
# a normal function, but it executes the DEBUG instruction, which will
# suspend emulation. By executing "go", the user can resume program execution.
#
# This function is passed a single argument (r1=codeAddress). The emulator will
# fetch this value and display that to the user.
# 
# Arguments:       r1=codeAddr
# Returned value:  none
# Interrupts:      Not modified
# Max Stack Usage: 16 bytes
# Standard calling conventions: Yes
#
#############################################################

EmulatorDebuggingRequested:
	.function			"EmulatorDebuggingRequested",line=0,framesize=16
	.local				0,"codeAddress",line=0,type="I"
        enterfun	sp,sp,-16	# Prologue
        store.d		0(sp),r1	# Local variable: "codeAddress" (for debugger)
Within_EmulatorDebuggingRequested:	# This particular label will be recognized by the emulator
	debug				# Suspend execution; emulator gets codeAddress from r1
	exitfun		sp,sp,16	# Restore the stack & return
	.endfunction



#############################################################
#
# Codes related to the CONTROL instructions
#
#############################################################

DIGITAL_READ:  .equ   0     # controlu   XXX,r0,DIGITAL_READ
DIGITAL_WRITE: .equ   1     # controlu   r0,XXX,DIGITAL_WRITE
HALT:          .equ   2     # controlu   r0,r0,HALT
SERIAL_STAT:   .equ   3     # controlu   XXX,r0,SERIAL_STAT
SERIAL_RECV:   .equ   4     # controlu   XXX,r0,SERIAL_RECV
SERIAL_SEND:   .equ   5     # controlu   r0,XXX,SERIAL_SEND
ENABLE_KERNEL: .equ   6     # controlu   XXX,r0,ENABLE_KERNEL  # grab csr_status; then set mode
SET_STATUS:    .equ   7     # controlu   r0,XXX,SET_STATUS     # set csr_status from reg.
TLB_DEBUG:     .equ   8     # controlu   regD,Reg1,TLB_DEBUG   # copy TLB[r1] to RegD


#############################################################
#
# Control_PrintString (str: ptr to array of byte)
#
# Passed:
#    r1 = pointer to an array of bytes.
# Standard Calling Conventions; uses stack
# Regs Changed:
#   r1 = ptr to chars
#   r2 = num chars left to print
#   r3 = status, the char
#
#############################################################

	.function	"Control_PrintString", line=0, framesize=8
XXX_Control_PrintString:
        addi		sp,sp,-8		# Function prologue
        store.d		0(sp),lr		#
	beqz		r1, CPS_return		# Return if ptr to String is null
	addi		r1,r1,8			# Advance pointer to first character
	loadw		r2,-4(r1)		# Move CURRENT length into r2
        beqz		r2,CPS_return		# IF count != 0
Ploop:          	                        #   REPEAT
printLoop:					#     LOOP
	controlu	r3,r0,SERIAL_STAT	#       Get serial status
	andi		r3,r3,0x02		#       Look at bit [1] only
	beqz		r3,printLoop		#     UNTIL OUTPUT READY = 1
	load.b		r3,0(r1)		#     r3 = the next byte from the string
	controlu	r0,r3,SERIAL_SEND	#     Send a byte from r3
	addi		r1,r1,1			#     Increment pointer
	addi		r2,r2,-1		#     Decrement counter
	bnez		r2,Ploop	        #   UNTIL count == 0
CPS_return:					# ENDIF
        load.d  	lr,0(sp)		# Return Sequence           
        addi    	sp,sp,8			# .
        ret					# .
	.endfunction

#############################################################
#
# Control_PrintChar (c: byte)
#
# Passed:
#    r1 = character -128...+127
# Uses:
#    none; uses stack
#
#############################################################

	.function	"Control_PrintChar", line=0, framesize=16
XXX_Control_PrintChar:
        addi		sp,sp,-16		# Function prologue
        store.d		0(sp),r1		#
        store.d		8(sp),r2		#
PrintCharLoop:
	controlu	r2,r0,SERIAL_STAT	#   Get serial status
	andi		r2,r2,0x02		#   Look at bit [1] only
	beqz		r2, PrintCharLoop	# UNTIL OUTPUT READY = 1
	controlu	r0,r1,SERIAL_SEND	# Print r1
        load.d		r1,0(sp)		# Return Sequence           
        load.d		r2,8(sp)		# .           
        addi		sp,sp,16		# .
        ret					# .
	.endfunction

#############################################################
#
# Control_ReadChar () returns byte
#
# Returns:
#    r1 = character -128...+127
#
#############################################################

	.function	"Control_PrintChar", line=0, framesize=0
XXX_Control_ReadChar:				# LOOP
	controlu	r2,r0,SERIAL_STAT	#   Get serial status
	andi		r2,r2,0x01		#   Look at bit [0] only
	beqz		r2, XXX_Control_ReadChar	# UNTIL INPUT_AVAIL = 1
	controlu	r1,r0,SERIAL_RECV	# Fetch r1
        ret					# Return
	.endfunction



#############################################################
#
# Fixed addresses related to the Simple Serial Device
#
#############################################################

Serial_String_Char:	.equ	0x400104000
Serial_String_Hex:	.equ	0x400104008
Serial_String_Decimal:	.equ	0x400104010
Serial_String_Addr:	.equ	0x400104018
Serial_String_In_Len:	.equ	0x400104020


#############################################################
#
# Serial_ReadString (buffer: ptr to array of byte)
#
# The MAX size of the array is consulted and, upon return the
# CURRENT size of the array is reset to reflect the number of
# bytes now in the array.
#
# NOTE: The result will never contain \0. However, since the simple
# serial device will terminate the input with \0, the buffer must be
# large enough to accommodate it. The new CURRENT value will always
# be set to something less than MAX. The byte beyond the last array
# element will contain a \0 byte. Thus, it is always safe to call
# _putString on the string, as long as you add 8 to get past the MAX
# and CURRENT size words.
#
# The input is normally terminated with NEWLINE, so the last byte
# in the array will be \n. The input may contain UTF-8 bytes.
#
# Passed: r1 = ptr to input buffer (an array)
#
# Regs Changed:    r1-r5
# Max Stack Usage: 0 bytes
#
### NEED TO REVIEW
#
#############################################################

	.function	"Serial_ReadString", line=0, framesize=0
Serial_ReadString:
        loadw		r2,0(r1)		# Get MAX # of elements
	bgtz		r2,ReadStrCont		# If MAX <= 0
	storew		4(r1),r0		#    set CURRENT size to 0
	ret					#    return
ReadStrCont:					# endif
	addi		r1,r1,8			# Set r1 to point to the a[0]
	stored		Serial_String_Addr,r1	# Read input
	stored		Serial_String_In_Len,r2	#
	movi		r4,0			# r4 = count of chars = 0
	mov		r3,r1			# r3 = pointer walking through the array
ReadStrLoop:					# loop through the chars
	loadb		r5,0(r3)		#    if this char is 0
	beqz		r5,ReadStrDone		#      break
	addi		r3,r3,1			#    increment pointer
	addi		r4,r4,1			#    increment count
	blt		r4,r2,ReadStrLoop	# until count = MAX size
ReadStrDone:					# All Done:
	storew		-4(r1),r4		#   Set CURRENT size to r2
	ret
	.endfunction


#############################################################
#
# Serial_PrintString (s: ptr to array of byte)
#
# The PrintXXXXX functions print data directly and immediately to the output.
# They are intended for use in debugging the kernel; as such, they use the
# Simple Serial I/O device, which is nothing more than a "back-door" to the
# virtual machine. They may be called from any code, including from within an
# interrupt handler.
#
# They will print UTF-8 strings just fine.
#
# Standard KPL calling conventions: Yes
#
# Passed: r1 = ptr to string object
#
# Regs Changed:         r1, r2
# Max Stack Usage:      0 bytes
#
#############################################################

	.function	"Serial_PrintString", line=0, framesize=0
Serial_PrintString:
							# Ptr to String (array of byte) is in r1
	beqz		r1,SS_P_ret			# Return if ptr is null
	addi		r1,r1,8				# Advance pointer to first character
	loadw		r2,-4(r1)			# Move CURRENT length into r2
	stored		Serial_String_Addr,r1		# Invoke Simple Serial Device
	stored		Serial_String_Out_Len,r2	#
SS_P_ret:
	ret						# Return
	.endfunction

#############################################################
#
# Serial_PrintChar (value: int)
#
# Prints a character. Will print multi-byte UTF-8 characters correctly
# if called repeatedly for each byte in the sequence.
#
# Only the lower 8 bits are used.
#
# Passed:          r1 = value
# Regs Changed:    r1
# Max Stack Usage: 0 bytes
#
#############################################################

	.function	"Serial_PrintChar", line=0, framesize=0
Serial_PrintChar:
        andi		r1,r1,0x00ff
	stored		Serial_String_Char,r1
	ret
	.endfunction
Serial_String_Out_Len:	.equ	0x400104028

### #############################################################
### #
### # Serial_PrintHex (value: int)
### #
### # Print an integer in hex (e.g. "00001234AAAABBBB").
### #
### # Passed:          r1 = value
### # Regs Changed:    none
### # Max Stack Usage: 0 bytes
### #
### #############################################################
### 
### 	.function	"Serial_PrintHex", line=0, framesize=0
### Serial_PrintHex:
### 	stored		Serial_String_Hex,r1
### 	ret
### 	.endfunction
### 
### #############################################################
### #
### # Serial_PrintDecimal (value: int)
### #
### # Print an integer in decimal (e.g. "-123"),
### #
### # Passed:          r1 = value
### # Regs Changed:    none
### # Max Stack Usage: 0 bytes
### #
### #############################################################
### 
### 	.function	"Serial_PrintDecimal", line=0, framesize=0
### Serial_PrintDecimal:
### 	stored		Serial_String_Decimal,r1
### 	ret
### 	.endfunction
### 
### #############################################################
### #
### # Serial_PrintNL ()
### #
### # Prints '\n'.
### #
### # Passed:          < nothing >
### # Regs Changed:    r1
### # Max Stack Usage: 0 bytes
### #
### #############################################################
### 
### 	.function	"Serial_PrintNL", line=0, framesize=0
### Serial_PrintNL:
### 	movi		r1,0x0a
### 	stored		Serial_String_Char,r1
### 	ret
### 	.endfunction

#############################################################
#
# GetCsrCycle () returns int
#
# This function returns the value of csr_cycle.
#
# Standard KPL calling conventions: Yes
# Regs Changed:         r1
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
#
# Kernel Mode required.
#
#############################################################

	.function	"GetCsrCycle", line=0, framesize=0
GetCsrCycle:
	csrread		r1,csr_cycle
	ret
	.endfunction

#############################################################
#
# GetCsrInstr () returns int
#
# This function returns the value of csr_instr.
#
# Standard KPL calling conventions: Yes
# Regs Changed:         r1
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
#
# Kernel Mode required.
#
#############################################################

	.function	"GetCsrInstr", line=0, framesize=0
GetCsrInstr:
	csrread		r1,csr_instr
	ret
	.endfunction

#############################################################
#
# GetCsrTimer () returns int
#
# This function returns the value of csr_timer.
#
# Standard KPL calling conventions: Yes
# Regs Changed:         r1
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
#
# Kernel Mode required.
#
#############################################################

	.function	"GetCsrTimer", line=0, framesize=0
GetCsrTimer:
	csrread		r1,csr_timer
	ret
	.endfunction

#############################################################
#
# SetCsrTimer (int)
#
# This function stores the arg into csr_timer.
#
# Standard KPL calling conventions: Yes
# Regs Changed:         r1
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
#
# Kernel Mode required.
#
#############################################################

	.function	"SetCsrTimer", line=0, framesize=0
SetCsrTimer:
	csrwrite	csr_timer,r1
	ret
	.endfunction

#############################################################
#
# external CyclesFor4000Instructions () returns int
# 
# This function executes 4000 instructions and returns the number of cycles that it took.
#
# Standard KPL calling conventions: Yes
# Max Stack Usage:      0 bytes
#
# Kernel Mode required. Interrupts assumed to be DISABLED.
#
#############################################################

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

	movi		r1,999
	movi		r3,1234
	movi		r4,5678

	csrread		r7,csr_cycle

# Execute 999*4 = 3,996 instructions, incl CSRREAD
CycleCountLoop:
	ori		r2,r3,0		# same as MOV
	mul		r2,r3,r4
	addi		r1,r1,-1
	blt		r0,r1,CycleCountLoop

# Execute 4 more instructions (including the CSRREAD) for a total of exactly 4,000 instructions.
	ori		r2,r3,0
	mul		r2,r3,r4
	addi		r1,r1,-1
	csrread		r1,csr_cycle

	sub		r1,r1,r7
	ret
	.endfunction

#############################################################
#
# external BoostCsrTimer (boostAmt: int)
# 
# This function will add "boostAmt" to csr_timer.
#
# Standard KPL calling conventions: Yes
# Max Stack Usage:      0 bytes
# Kernel Mode required.
#
#############################################################
BoostCsrTimer:
	.function	"BoostCsrTimer",line=0,framesize=0
	csrread		r2,csr_timer
	add		r2,r1,r2
	csrwrite	csr_timer,r2
	ret
	.endfunction

#############################################################
#
# SetInterruptsTo (bool) returns bool
#
# This function is passed TRUE=1=ENABLED or FALSE=0=DISABLED.
# It sets the "InterruptsEnabled" bit in csr_status to this value
# and returns the previous value.
#
# If there happens to be a timer interrupt and this thread is delayed,
# it will still function correctly when this thread is rescheduled.
# (Of course this assumes that the entire state of this thread, including
# csr_status, is preserved and restored correctly.)
#
# If this function is being used to enable interrupts, then sometimes
# there will be an immediate interrupt and there may be a delay before
# this thread is finally rescheduled and this function returns.
#
# Standard KPL calling conventions: Yes
# Regs Changed:         r1, r2
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
#
#############################################################

	.function	"SetInterruptsTo", line=0, framesize=0
SetInterruptsTo:
	csrread		r2,csr_status		# Get the previous value
	beqz		r1,SetIntClear		# If argument is 1
	csrset  	csr_status,0x02		#   set the bit
	jump		SetIntEnd		#
SetIntClear:					# else
	csrclr  	csr_status,0x02		#   clear the bit
SetIntEnd:					# endif
	andi		r2,r2,0x02		# Turn the previous "interrupts_enabled" bit
	srli		r1,r2,1			# into a 0 or 1
	ret					# return it.
	.endfunction

#############################################################
#
# Sleep1 ()
#
# This function will execute the "sleep1" machine instruction.
# The "sleep1" instruction will enable interrupts and halt CPU 
# execution until the next interrupt arrives.
#
# The sleep state ends when an interrupt occurs. During the sleep
# state, the Program Counter (PC) points to the instruction following
# the SLEEP instruction so, after the interrupt trap handler returns,
# instruction execution will resume and the return will be executed.
#
# Standard KPL calling conventions: Yes
# Regs Changed:         none
# Atomic / Thread safe: Yes
# Max Stack Usage:      0 bytes
# The "INTERRUPTS_ENABLED" bit will be set.
#
#############################################################

	.function	"Sleep1", line=0, framesize=0
Sleep1:
	sleep1
	ret
	.endfunction

#############################################################
#
# external executeRestartInstruction ()
#
# Execute the RESTART instruction.
#
# Passed:          nothing
# Returns:         <return will not happen>
# Max Stack Usage: 0 bytes
#
#############################################################

	.function	"executeRestartInstruction", line=0, framesize=0
executeRestartInstruction:
	restart
	.endfunction


#############################################################
#
# Functions to read/update CSR registers
#
# Standard Calling Conventions
#
#############################################################


# Return the core number
cpuid:
	.function	"cpuid", line=0, framesize=0
	csrread		r1,csr_core
	andi		r1,r1,0x7fff
	ret
	.endfunction


# Enable timer & device interrupts
intr_on:
	.function	"intr_on", line=0, framesize=0
	csrset		csr_status,0x02		#   set bit "Interrupts Enabled"
  	ret
	.endfunction


# Disable timer & device interrupts
intr_off:
	.function	"intr_off", line=0, framesize=0
	csrclr		csr_status,0x02		#   clear bit "Interrupts Enabled"
	ret
	.endfunction


# Are timer & device interrupts enabled? Return TRUE if enabled.
intr_get:
	.function	"intr_get", line=0, framesize=0
	csrread		r1,csr_status		# Get bit "Interrupts Enabled"
	andi		r1,r1,0x02		# Isolate and return it
	srli		r1,r1,1
	ret
	.endfunction


# Return csr_prevpc
get_csr_prevpc:
	.function	"get_csr_prevpc", line=0, framesize=0
	csrread		r1,csr_prevpc
	ret
	.endfunction


# Return csr_stat2
get_csr_stat2:
	.function	"get_csr_stat2", line=0, framesize=0
	csrread		r1,csr_stat2
	ret
	.endfunction


# Return csr_ptr
get_csr_ptr:
	.function	"get_csr_ptr", line=0, framesize=0
	csrread		r1,csr_ptr
	ret
	.endfunction


# Return csr_cause
get_csr_cause:
	.function	"get_csr_cause", line=0, framesize=0
	csrread		r1,csr_cause
	ret
	.endfunction


# Return csr_bad
get_csr_bad:
	.function	"get_csr_bad", line=0, framesize=0
	csrread		r1,csr_bad
	ret
	.endfunction


# Return csr_addr
get_csr_addr:
	.function	"get_csr_addr", line=0, framesize=0
	csrread		r1,csr_addr
	ret
	.endfunction


# Return csr_pgtable
get_csr_pgtable:
	.function	"get_csr_pgtable", line=0, framesize=0
	csrread		r1,csr_pgtable
	ret
	.endfunction


# Update csr_prevpc
put_csr_prevpc:
	.function	"put_csr_prevpc", line=0, framesize=0
	csrwrite	csr_prevpc,r1
	ret
	.endfunction


# Update csr_stat2
put_csr_stat2:
	.function	"put_csr_stat2", line=0, framesize=0
	.export		put_csr_stat2
	csrwrite	csr_stat2,r1
	ret
	.endfunction


# Update csr_ptr
put_csr_ptr:
	.function	"put_csr_ptr", line=0, framesize=0
	csrwrite	csr_ptr,r1
	ret
	.endfunction


# Update csr_trapvec
put_csr_trapvec:
	.function	"put_csr_trapvec", line=0, framesize=0
	csrwrite	csr_trapvec,r1
	ret
	.endfunction


# Update csr_pgtable
put_csr_pgtable:
	.function	"put_csr_pgtable", line=0, framesize=0
	csrwrite	csr_pgtable,r1
	tlbclear
        fence
	ret
	.endfunction


# Add r1 to csr_timer
incr_csr_timer:
	.function	"incr_csr_timer", line=0, framesize=0
	csrread		r2,csr_timer
	add		r1,r1,r2
	csrwrite	csr_timer,r1
	ret
	.endfunction


# Update csr_timer
set_csr_timer:
	.function	"set_csr_timer", line=0, framesize=0
	csrwrite	csr_timer,r1
	ret
	.endfunction


# Return csr_cycle
get_csr_cycle:
	.function	"get_csr_cycle", line=0, framesize=0
	csrread		r1,csr_cycle
	ret
	.endfunction


# Update put_sp
put_sp:
	.function	"put_sp", line=0, framesize=0
	mov		sp,r1
	ret
	.endfunction


# Update do_tlbclear
do_tlbclear:
	.function	"do_tlbclear", line=0, framesize=0
	tlbclear
        fence
	ret
	.endfunction


# Execute "do_tlbflush"
do_tlbflush:
	.function	"do_tlbflush", line=0, framesize=0
	tlbflush	r1
        fence
	ret
	.endfunction


# Execute "checkaddr_loadd"
checkaddr_loadd:
	.function	"checkaddr_loadd", line=0, framesize=0
	checkaddr	r1,r1,3
        fence
	ret
	.endfunction


# Execute "checkaddr_loadw"
checkaddr_loadw:
	.function	"checkaddr_loadw", line=0, framesize=0
	checkaddr	r1,r1,3
        fence
	ret
	.endfunction


# Execute "checkaddr_loadh"
checkaddr_loadh:
	.function	"checkaddr_loadh", line=0, framesize=0
	checkaddr	r1,r1,3
        fence
	ret
	.endfunction


# Execute "checkaddr_loadb"
checkaddr_loadb:
	.function	"checkaddr_loadb", line=0, framesize=0
	checkaddr	r1,r1,3
        fence
	ret
	.endfunction

