summaryrefslogtreecommitdiff
path: root/lib/ultralib/src/os/exceptasm.s
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ultralib/src/os/exceptasm.s')
-rw-r--r--lib/ultralib/src/os/exceptasm.s450
1 files changed, 362 insertions, 88 deletions
diff --git a/lib/ultralib/src/os/exceptasm.s b/lib/ultralib/src/os/exceptasm.s
index b7699a8..919482e 100644
--- a/lib/ultralib/src/os/exceptasm.s
+++ b/lib/ultralib/src/os/exceptasm.s
@@ -1,3 +1,7 @@
+#ifdef MODERN_CC
+.set gp=64
+#endif
+
#include "PR/R4300.h"
#include "sys/asm.h"
#include "sys/regdef.h"
@@ -7,6 +11,7 @@
#include "exceptasm.h"
#include "threadasm.h"
#include "PR/os_version.h"
+
#define KMC_CODE_ENTRY 0xBFF00014
#define KMC_WPORT 0xBFF08000
#define KMC_STAT 0xBFF08004
@@ -101,10 +106,14 @@ __os_Kdebug_Pkt:
__osPreviousThread:
.word 0
#endif
-#endif
+#endif
.text
+/**
+ * The exception preamble is copied to the exception vectors at
+ * UT_VEC, XUT_VEC, ECC_VEC, E_VEC, to direct execution to __osException
+ */
LEAF(__osExceptionPreamble)
la k0, __osException
jr k0
@@ -174,26 +183,27 @@ pt_prof:
END(__ptException)
#endif
-LEAF(__osException)
+LEAF(__osException)
+ /* Load scratch space for thread saving */
la k0, __osThreadSave
- /* save AT */
+ /* Save AT */
.set noat
sd $1, THREAD_GP1(k0);
.set at
- /* save sr */
-STAY2(mfc0 k1, C0_SR)
+ /* Save SR */
+ MFC0( k1, C0_SR)
sw k1, THREAD_SR(k0)
- /* clear interrupts */
- and k1, k1, -4
-STAY2(mtc0 k1, C0_SR)
- /* save other regs */
+ /* Disable interrupts */
+ and k1, k1, ~(SR_IE | SR_EXL)
+ MTC0( k1, C0_SR)
+ /* Save some temp registers for use in the following */
sd $8, THREAD_GP8(k0)
sd $9, THREAD_GP9(k0)
sd $10, THREAD_GP10(k0)
- /* say fp has not been used */
+ /* Mark FPU as unused */
sw zero, THREAD_FP(k0)
- /* this instruction is useless, leftover because of bad placement of an ifdef for the debug version */
-STAY2(mfc0 t0, C0_CAUSE)
+ /* This instruction is useless, leftover because of bad placement of an ifdef for the debug version */
+ MFC0( t0, C0_CAUSE)
#ifndef _FINALROM
lw t2, __kmc_pt_mode
bnez t2, skip_kmc_mode
@@ -207,7 +217,7 @@ STAY2(mfc0 t0, C0_CAUSE)
la t1, RDB_WRITE_INTR_REG
sw zero, (t1)
IP7check:
-STAY2(mfc0 t0, C0_CAUSE)
+ MFC0( t0, C0_CAUSE)
andi t0, t0, CAUSE_IP7
bne zero, t0, IP7check
la t2, RDB_BASE_REG
@@ -364,7 +374,7 @@ notIP7:
5:
sw t1, __osRdb_IP6_CurSend
checkIP6:
-STAY2(mfc0 t0, C0_CAUSE)
+ MFC0( t0, C0_CAUSE)
andi t0, t0, CAUSE_IP6
bne zero, t0, checkIP6
la t0, RDB_BASE_REG
@@ -377,7 +387,7 @@ rdbout:
ld $1, THREAD_GP1(k0)
.set at
lw k1, THREAD_SR(k0)
-STAY2(mtc0 k1, C0_SR)
+ MTC0( k1, C0_SR)
.set noreorder
nop
nop
@@ -390,8 +400,9 @@ skip_kmc_mode:
#endif
savecontext:
+ /* Save the context of the previously running thread to be restored when it resumes */
move t0, k0
- lw k0, __osRunningThread
+ lw k0, __osRunningThread
#if !defined(_FINALROM) && BUILD_VERSION >= VERSION_K
sw k0, __osPreviousThread
#endif
@@ -440,8 +451,13 @@ savecontext:
lw k1, THREAD_SR(k0)
andi t1, k1, SR_IMASK
beqz t1, savercp
-
-/*if any interrupts are enabled*/
+
+ /*
+ * If any CPU interrupts are enabled in the SR of the previous thread, bitwise-OR in the
+ * disabled CPU interrupts from the global interrupt mask.
+ * This is an attempt at reverting the effect of masking the SR of the thread with the
+ * global interrupt mask. This is however broken, see comments for osSetIntMask.
+ */
la t0, __OSGlobalIntMask
lw t0, 0(t0)
xor t2, t0, ~0 /* not except not using not */
@@ -456,9 +472,14 @@ savecontext:
or k1, k1, t1
savercp:
+ /* Save the currently masked RCP interrupts. */
lw t1, PHYS_TO_K1(MI_INTR_MASK_REG)
beqz t1, endrcp
+ /*
+ * Similar to the above comment, but for RCP interrupt enable bits rather than CPU.
+ * This suffers from the same problem as above.
+ */
la t0, __OSGlobalIntMask
lw t0, 0(t0)
@@ -470,11 +491,12 @@ savercp:
or t1, t1, t0
endrcp:
sw t1, THREAD_RCP(k0)
-STAY2(mfc0 t0, C0_EPC)
+ MFC0( t0, C0_EPC)
sw t0, THREAD_PC(k0)
lw t0, THREAD_FP(k0)
beqz t0, 1f
-STAY2(cfc1 t0, fcr31)
+ /* Save FP Registers if FPU was used by the thread */
+ CFC1( t0, fcr31)
NOP
sw t0, THREAD_FPCSR(k0)
sdc1 $f0, THREAD_FP0(k0)
@@ -494,7 +516,11 @@ STAY2(cfc1 t0, fcr31)
sdc1 $f28, THREAD_FP28(k0)
sdc1 $f30, THREAD_FP30(k0)
1:
-STAY2(mfc0 t0, C0_CAUSE)
+ /*
+ * Determine the cause of the exception or interrupt and
+ * enter appropriate handling routine
+ */
+ MFC0( t0, C0_CAUSE)
sw t0, THREAD_CAUSE(k0)
.set noreorder
@@ -518,21 +544,28 @@ no_rdb_mesg:
#endif
andi t1, t0, CAUSE_EXCMASK
+ /* Test for break exception */
li t2, EXC_BREAK
beq t1, t2, handle_break
-
+
+ /* Test for CpU (coprocessor unusable) exception */
li t2, EXC_CPU
beq t1, t2, handle_CpU
-
+
+ /* Test for interrupt, if it is not an interrupt, panic */
li t2, EXC_INT
bne t1, t2, panic
handle_interrupt:
and s0, k1, t0
next_interrupt:
+ /*
+ * Handle external interrupt causes, using a jump table
+ * to enter into the appropriate handler
+ */
andi t1, s0, SR_IMASK
srl t2, t1, 0xc
bnez t2, 1f
-
+
srl t2, t1, SR_IMASKSHIFT
addi t2, t2, 16
1:
@@ -542,272 +575,425 @@ next_interrupt:
lw t2, __osIntTable(t2)
jr t2
+/**
+ * IP6 Interrupt
+ * Only signalled by development hardware
+ */
IP6_Hdlr:
+ /* Mask out interrupt and continue */
and s0, s0, ~CAUSE_IP6
b next_interrupt
-
+
+/**
+ * IP7 Interrupt
+ * Only signalled by development hardware
+ */
IP7_Hdlr:
+ /* Mask out interrupt and continue */
and s0, s0, ~CAUSE_IP7
b next_interrupt
+/**
+ * IP8/Counter Interrupt
+ * Once the cop0 count register reaches the value of the
+ * cop0 compare register, this interrupt is triggered
+ */
counter:
-STAY2(mfc0 t1, C0_COMPARE)
-STAY2(mtc0 t1, C0_COMPARE)
+ MFC0( t1, C0_COMPARE)
+ MTC0( t1, C0_COMPARE)
li a0, MESG(OS_EVENT_COUNTER)
+ /* Post counter message */
jal send_mesg
+ /* Mask out interrupt and continue */
and s0, s0, ~CAUSE_IP8
b next_interrupt
+/**
+ * IP4/Cartridge Interrupt
+ * Signalled by the N64 Disk Drive
+ */
cart:
#if BUILD_VERSION >= VERSION_J
+ /* Mask out interrupt */
and s0, s0, ~CAUSE_IP4
+ /* Load cart callback set by __osSetHWIntrRoutine */
la t1, __osHwIntTable
add t1, HWINTR_SIZE
lw t2, HWINTR_CALLBACK(t1)
+ /* If the callback is NULL, handling is done */
beqz t2, 1f
+ /* Set up a stack and run the callback */
lw sp, HWINTR_SP(t1)
jalr t2
-
+
beqz v0, 1f
+ /* Redispatch immediately if the callback returned nonzero */
b redispatch
-
+
1:
+ /* Post a cart event message */
li a0, MESG(OS_EVENT_CART)
jal send_mesg
+ /* Continue */
b next_interrupt
#else
li a0, MESG(OS_EVENT_CART)
+ /* Mask out interrupt */
and s0, s0, ~CAUSE_IP4
- la sp, leoDiskStack
+ la sp, leoDiskStack
addiu sp, 0x1000 - 0x10 # Stack size minus initial frame
+ /* Load cart callback set by __osSetHWIntrRoutine */
li t2, HWINTR_SIZE
lw t2, __osHwIntTable(t2)
+ /* If the callback is NULL, handling is done */
beqz t2, 1f
-
+
+ /* Set up a stack and run the callback */
jalr t2
li a0, MESG(OS_EVENT_CART)
-
+
beqz v0, 1f
+ /* Redispatch immediately if the callback returned nonzero */
b redispatch
-
+
1:
+ /* Post a cart event message */
jal send_mesg
+ /* Continue */
b next_interrupt
#endif
+/**
+ * IP3/RCP Interrupt
+ * Signalled by the RCP for various reasons, described below
+ */
rcp:
+ /*
+ * Load the MI interrupts and mask with the RCP bits in the global interrupt mask
+ *
+ * @bug this clobbers the t0 register which is expected to hold the value of the
+ * C0_CAUSE register in the sw1 and sw2 handlers. If the sw1 or sw2 handler runs
+ * after this, the interrupt will not be cleared properly.
+ */
lw s1, PHYS_TO_K1(MI_INTR_REG)
la t0, __OSGlobalIntMask
lw t0, (t0)
srl t0, t0, 0x10
and s1, s1, t0
+
+/**
+ * Signal Processor (SP) Interrupt
+ */
+ /* Test for sp interrupt */
andi t1, s1, MI_INTR_SP
beqz t1, vi
- andi s1, s1, 0x3e
+ /* Test for yielded or done signals in particular */
+
+ /* Mask out SP interrupt */
+ andi s1, s1, (MI_INTR_SI | MI_INTR_AI | MI_INTR_VI | MI_INTR_PI | MI_INTR_DP)
lw ta0, PHYS_TO_K1(SP_STATUS_REG)
li t1, (SP_CLR_INTR | SP_CLR_SIG3)
+ /* Clear interrupt and signal 3 */
sw t1, PHYS_TO_K1(SP_STATUS_REG)
- andi ta0, ta0, 0x300
+ andi ta0, ta0, (SP_STATUS_YIELDED | SP_STATUS_TASKDONE)
beqz ta0, sp_other_break
+ /* Post an SP event message */
li a0, MESG(OS_EVENT_SP)
jal send_mesg
beqz s1, NoMoreRcpInts
+ /* Step over sp_other_break handler */
b vi
sp_other_break:
+ /*
+ * An sp signal that is not due to yielding or task completion, such as
+ * an sp breakpoint. Post a different event message
+ */
li a0, MESG(OS_EVENT_SP_BREAK)
jal send_mesg
beqz s1, NoMoreRcpInts
-
+
+/**
+ * Video Interface (VI) Interrupt
+ */
vi:
- andi t1, s1, 0x8
+ /* Test for vi interrupt */
+ andi t1, s1, MI_INTR_VI
beqz t1, ai
-
- andi s1, s1, 0x37
-
+
+ /* Mask out vi interrupt */
+ andi s1, s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_AI | MI_INTR_PI | MI_INTR_DP)
+
+ /* Clear interrupt */
sw zero, PHYS_TO_K1(VI_CURRENT_REG)
+
+ /* Post vi event message */
li a0, MESG(OS_EVENT_VI)
jal send_mesg
beqz s1, NoMoreRcpInts
-
+
+/**
+ * Audio Interface (AI) Interrupt
+ */
ai:
- andi t1, s1, 0x4
+ /* Test for ai interrupt */
+ andi t1, s1, MI_INTR_AI
beqz t1, si
- andi s1, s1, 0x3b
-
+ /* Mask out ai interrupt */
+ andi s1, s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_VI | MI_INTR_PI | MI_INTR_DP)
+
li t1, 1
+ /* Clear interrupt */
sw t1, PHYS_TO_K1(AI_STATUS_REG)
+ /* Post ai event message */
li a0, MESG(OS_EVENT_AI)
jal send_mesg
beqz s1, NoMoreRcpInts
-
+
+/**
+ * Serial Interface (SI) Interrupt
+ */
si:
- andi t1, s1, 0x2
+ /* Test for si interrupt */
+ andi t1, s1, MI_INTR_SI
beqz t1, pi
-
- andi s1, s1, 0x3d
- /* any write clears interrupts */
- sw zero, PHYS_TO_K1(SI_STATUS_REG)
+
+ /* Mask out si interrupt */
+ andi s1, s1, (MI_INTR_SP | MI_INTR_AI | MI_INTR_VI | MI_INTR_PI | MI_INTR_DP)
+ /* Clear interrupt */
+ sw zero, PHYS_TO_K1(SI_STATUS_REG)
+
+ /* Post si event message */
li a0, MESG(OS_EVENT_SI)
jal send_mesg
beqz s1, NoMoreRcpInts
-
+
+/**
+ * Parallel Interface (PI) Interrupt
+ */
pi:
- andi t1, s1, 0x10
+ /* Test for pi interrupt */
+ andi t1, s1, MI_INTR_PI
beqz t1, dp
- andi s1, s1, 0x2f
-
+ /* Mask out pi interrupt */
+ andi s1, s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_AI | MI_INTR_VI | MI_INTR_DP)
+
+ /* Clear interrupt */
li t1, PI_STATUS_CLR_INTR
sw t1, PHYS_TO_K1(PI_STATUS_REG)
#if BUILD_VERSION >= VERSION_J
+ /* Load pi callback */
la t1, __osPiIntTable
lw t2, (t1)
+ /* Skip callback if NULL */
beqz t2, 1f
+ /* Set up a stack and run the callback */
lw sp, 4(t1)
move a0, v0
jalr t2
+ /* If the callback returns non-zero, do not post a pi event message */
bnez v0, 2f
1:
#endif
+ /* Post pi event message */
li a0, MESG(OS_EVENT_PI)
jal send_mesg
#if BUILD_VERSION >= VERSION_J
2:
#endif
beqz s1, NoMoreRcpInts
-
+
+/**
+ * Display Processor (DP) Interrupt
+ */
dp:
- andi t1, s1, 0x20
+ /* Test for dp interrupt */
+ andi t1, s1, MI_INTR_DP
beqz t1, NoMoreRcpInts
- andi s1, s1, 0x1f
+ /* Mask out dp interrupt */
+ andi s1, s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_AI | MI_INTR_VI | MI_INTR_PI)
+ /* Clear dp interrupt */
li t1, MI_CLR_DP_INTR
sw t1, PHYS_TO_K1(MI_INIT_MODE_REG)
-
+
+ /* Post dp event message */
li a0, MESG(OS_EVENT_DP)
jal send_mesg
NoMoreRcpInts:
+ /* Mask out interrupt and continue */
and s0, s0, ~CAUSE_IP3
b next_interrupt
+/**
+ * IP5/PreNMI Interrupt
+ * Reset button has been pressed
+ */
prenmi:
+ /* Disable IP5/PreNMI interrupt for the previously running thread */
lw k1, THREAD_SR(k0)
and k1, k1, ~CAUSE_IP5
sw k1, THREAD_SR(k0)
+ /* Test __osShutdown for first PreNMI event */
la t1, __osShutdown
lw t2, 0(t1)
beqz t2, firstnmi
-
+
+ /* Mask out interrupt and redispatch immediately */
and s0, s0, ~CAUSE_IP5
b redispatch
firstnmi:
+ /* Set __osShutdown */
li t2, 1
- sw t2, 0(t1) /* __osShutdown */
+ sw t2, (t1)
+ /* Post a PreNMI event message */
li a0, MESG(OS_EVENT_PRENMI)
jal send_mesg
+ /* Mask out and disable IP5/PreNMI interrupt for the highest priority thread */
and s0, s0, ~CAUSE_IP5
lw t2, __osRunQueue
lw k1, THREAD_SR(t2)
and k1, k1, ~CAUSE_IP5
sw k1, THREAD_SR(t2)
+ /* Redispatch immediately */
b redispatch
sw2:
+ /* Mask out interrupt */
and t0, t0, ~CAUSE_SW2
-STAY2(mtc0 t0, C0_CAUSE)
+ MTC0( t0, C0_CAUSE)
+ /* Post sw2 event message */
li a0, MESG(OS_EVENT_SW2)
jal send_mesg
+ /* Mask out interrupt and continue */
and s0, s0, ~CAUSE_SW2
b next_interrupt
sw1:
+ /* Mask out interrupt */
and t0, t0, ~CAUSE_SW1
-STAY2(mtc0 t0, C0_CAUSE)
+ MTC0( t0, C0_CAUSE)
+ /* Post sw1 event message */
li a0, MESG(OS_EVENT_SW1)
jal send_mesg
+ /* Mask out interrupt and continue */
and s0, s0, ~CAUSE_SW1
b next_interrupt
handle_break:
+ /* Set last thread as having hit a break exception */
li t1, OS_FLAG_CPU_BREAK
sh t1, THREAD_FLAGS(k0)
+ /* Post a cpu break event message */
li a0, MESG(OS_EVENT_CPU_BREAK)
jal send_mesg
+
+ /* Redispatch */
b redispatch
-
+
redispatch:
+ /* Get priority of previously running thread */
lw t1, THREAD_PRI(k0)
lw t2, __osRunQueue
+ /* Get highest priority from waiting threads */
lw t3, THREAD_PRI(t2)
-
+
bge t1, t3, enqueueRunning
-
+
+ /*
+ * The previously running thread is no longer the highest priority,
+ * enqueue it to the run queue to wait its turn again
+ */
move a1, k0
la a0, __osRunQueue
-
+
jal __osEnqueueThread
-
+
j __osDispatchThread
-
+
+/**
+ * Resume the previously running thread by placing it at the top of
+ * the run queue and dispatching it
+ */
enqueueRunning:
la t1, __osRunQueue
lw t2, MQ_MTQUEUE(t1)
sw t2, THREAD_NEXT(k0)
sw k0, MQ_MTQUEUE(t1)
j __osDispatchThread
+
+/**
+ * Unhandled exceptions & interrupts end up here,
+ * trap to software by posting a fault message
+ */
panic:
+ /* Mark the thread as having faulted */
sw k0, __osFaultedThread
li t1, OS_STATE_STOPPED
sh t1, THREAD_STATE(k0)
li t1, OS_FLAG_FAULT
sh t1, THREAD_FLAGS(k0)
-STAY2(mfc0 t2, C0_BADVADDR)
+ /* Save C0_BADVADDR */
+ MFC0( t2, C0_BADVADDR)
sw t2, THREAD_BADVADDR(k0)
+ /* Post the fault message */
li a0, MESG(OS_EVENT_FAULT)
jal send_mesg
+
+ /* Dispatch next thread */
j __osDispatchThread
END(__osException)
-
+
+/**
+ * Handles posting event messages to the listening message queue, if there is one
+ */
LEAF(send_mesg)
+ /* Save return address */
move s2, ra
+
+ /* Load pointer to listening message queue */
la t2, __osEventStateTab
addu t2, t2, a0
lw t1, OS_EVENTSTATE_MESSAGE_QUEUE(t2)
+
+ /* If there is no listening message queue, done */
beqz t1, send_done
-
+
+ /* Test if the message queue is full, if so do not post the message */
lw t3, MQ_VALIDCOUNT(t1)
lw ta0, MQ_MSGCOUNT(t1)
-
+
bge t3, ta0, send_done
+ /* Add validcount to first and modulo with msgcount */
lw ta1, MQ_FIRST(t1)
addu ta1, ta1, t3
@@ -815,19 +1001,27 @@ LEAF(send_mesg)
lw ta0, MQ_MSG(t1)
mul ta1, ta1, 4
addu ta0, ta0, ta1
+
+ /* Fetch the message to post */
lw ta1, OS_EVENTSTATE_MESSAGE(t2)
- sw ta1, 0(ta0) /* msg[ta1] = */
+ /* Post the message to the message queue */
+ sw ta1, 0(ta0)
+ /* Increment the validCount */
addiu t2, t3, 1
sw t2, MQ_VALIDCOUNT(t1)
+ /*
+ * If there was a thread blocked on this message queue,
+ * wake it up
+ */
lw t2, MQ_MTQUEUE(t1)
lw t3, THREAD_NEXT(t2)
beqz t3, send_done
-
+
move a0, t1
jal __osPopThread
move t2, v0
-
+
move a1, t2
la a0, __osRunQueue
jal __osEnqueueThread
@@ -835,13 +1029,17 @@ send_done:
jr s2
END(send_mesg)
-LEAF(handle_CpU) /* coprocessor error */
+/**
+ * Handle coprocessor unusable exception
+ */
+LEAF(handle_CpU)
and t1, t0, CAUSE_CEMASK
srl t1, t1, CAUSE_CESHIFT
- li t2, 1 /* cp1 error */
+ /* if not coprocessor 1, panic */
+ li t2, 1
bne t1, t2, panic
- /* enable cp1 (fpu) for this thread */
+ /* Mark cop1 as usable for previous thread */
li t1, 1
sw t1, THREAD_FP(k0)
lw k1, THREAD_SR(k0)
@@ -850,14 +1048,25 @@ LEAF(handle_CpU) /* coprocessor error */
b enqueueRunning
END(handle_CpU)
+/**
+ * void __osEnqueueAndYield(OSThread** threadQueue);
+ *
+ * Voluntary thread yielding.
+ * Enqueues the currently running thread to the top of the
+ * thread queue `threadQueue` and yields to the highest priority
+ * unblocked runnable thread.
+ */
LEAF(__osEnqueueAndYield)
lw a1, __osRunningThread
#if !defined(_FINALROM) && BUILD_VERSION >= VERSION_K
sw a1, __osPreviousThread
#endif
-STAY2(mfc0 t0, C0_SR)
+ /* Save SR */
+ MFC0( t0, C0_SR)
ori t0, t0, SR_EXL
sw t0, THREAD_SR(a1)
+
+ /* Save callee-saved registers */
sd s0, THREAD_GP16(a1)
sd s1, THREAD_GP17(a1)
sd s2, THREAD_GP18(a1)
@@ -871,6 +1080,8 @@ STAY2(mfc0 t0, C0_SR)
sd s8, THREAD_GP30(a1)
sd ra, THREAD_GP31(a1)
sw ra, THREAD_PC(a1)
+
+ /* Save FPU callee-saved registers if the current thread has used the FPU */
lw k1, THREAD_FP(a1)
beqz k1, 1f
cfc1 k1, fcr31
@@ -886,6 +1097,10 @@ STAY2(mfc0 t0, C0_SR)
andi t1, k1, SR_IMASK
beqz t1, 2f
+ /*
+ * This code does the same thing as the block just above the `savercp` label.
+ * See the comment there for more about this.
+ */
la t0, __OSGlobalIntMask
lw t0, 0(t0)
xor t0, t0, ~0
@@ -898,36 +1113,57 @@ STAY2(mfc0 t0, C0_SR)
lw k1, PHYS_TO_K1(MI_INTR_MASK_REG)
beqz k1, 3f
+ /*
+ * This code does the same thing as the block just below the `savercp` label.
+ * See the comment there for more about this.
+ */
la k0, __OSGlobalIntMask
lw k0, 0(k0)
- srl k0, k0, 0x10
+ srl k0, k0, RCP_IMASKSHIFT
xor k0, k0, ~0
- andi k0, k0, 0x3f
+ andi k0, k0, (RCP_IMASK >> RCP_IMASKSHIFT)
lw t0, THREAD_RCP(a1)
and k0, k0, t0
or k1, k1, k0
3:
+ /*
+ * If the specified thread queue is null, skip
+ * straight to dispatching
+ */
sw k1, THREAD_RCP(a1)
beqz a0, noEnqueue
jal __osEnqueueThread
noEnqueue:
j __osDispatchThread
END(__osEnqueueAndYield)
-
-/*__osEnqueueThread(OSThread **, OSThread *)*/
+
+/**
+ * void __osEnqueueThread(OSThread** threadQueue, OSThread* thread);
+ *
+ * Enqueues `thread` to the thread queue `threadQueue`, inserted by priority
+ */
LEAF(__osEnqueueThread)
move t9, a0
lw t8, 0(a0)
lw ta3, THREAD_PRI(a1)
lw ta2, THREAD_PRI(t8)
+ /*
+ * If the current highest priority thread is a lower priority than
+ * the new thread, skip searching the queue
+ */
blt ta2, ta3, 2f
1:
+ /*
+ * Search the queue for the position to insert the thread to maintain
+ * ordering by priority
+ */
move t9, t8
lw t8, THREAD_NEXT(t8)
lw ta2, THREAD_PRI(t8)
bge ta2, ta3, 1b
2:
+ /* Insert the thread into the queue */
lw t8, THREAD_NEXT(t9)
sw t8, THREAD_NEXT(a1)
sw a1, THREAD_NEXT(t9)
@@ -935,23 +1171,42 @@ LEAF(__osEnqueueThread)
jr ra
END(__osEnqueueThread)
+/**
+ * OSThread* __osPopThread(OSThread** threadQueue);
+ *
+ * Pops the highest priority thread from the top of the
+ * thread queue `threadQueue` and returns it
+ */
LEAF(__osPopThread)
lw v0, 0(a0) /* a0 is OSThread** */
lw t9, THREAD_NEXT(v0)
sw t9, 0(a0)
jr ra
END(__osPopThread)
+
#if BUILD_VERSION >= VERSION_K
LEAF(__osNop)
jr ra
END(__osNop)
#endif
+
+/**
+ * void __osDispatchThread(void);
+ *
+ * Dispatches the next thread to run after restoring the context
+ */
LEAF(__osDispatchThread)
+ /* Obtain highest priority thread from the active run queue */
la a0, __osRunQueue
jal __osPopThread
+ /* Set thread as running */
sw v0, __osRunningThread
li t0, OS_STATE_RUNNING
sh t0, THREAD_STATE(v0)
+ /*
+ * Restore SR, masking out any interrupts that are not also
+ * enabled in the global interrupt mask
+ */
move k0, v0
#if !defined(_FINALROM) && BUILD_VERSION >= VERSION_K
@@ -964,7 +1219,7 @@ LEAF(__osDispatchThread)
jalr t0
#endif
-/* There's another 1: label somewhere around here in version J and below */
+/* There is another 1: label somewhere around here in version J and below */
#if BUILD_VERSION < VERSION_K
1:
#endif
@@ -978,7 +1233,8 @@ __osDispatchThreadSave:
and t1, t1, t0
and k1, k1, ~SR_IMASK
or k1, k1, t1
-STAY2(mtc0 k1, C0_SR)
+ MTC0( k1, C0_SR)
+/* Restore GPRs */
.set noat
ld $1, THREAD_GP1(k0)
.set at
@@ -1014,13 +1270,16 @@ STAY2(mtc0 k1, C0_SR)
mtlo k1
ld k1, THREAD_HI(k0)
mthi k1
+ /* Move thread pc to EPC so that eret will return execution to where the thread left off */
lw k1, THREAD_PC(k0)
-STAY2(mtc0 k1, C0_EPC)
+ MTC0( k1, C0_EPC)
+
+ /* Check if the FPU was used by this thread and if so also restore the FPU registers */
lw k1, THREAD_FP(k0)
beqz k1, 1f
lw k1, THREAD_FPCSR(k0)
-STAY2(ctc1 k1, fcr31)
+ CTC1( k1, fcr31)
ldc1 $f0, THREAD_FP0(k0)
ldc1 $f2, THREAD_FP2(k0)
ldc1 $f4, THREAD_FP4(k0)
@@ -1037,8 +1296,12 @@ STAY2(ctc1 k1, fcr31)
ldc1 $f26, THREAD_FP26(k0)
ldc1 $f28, THREAD_FP28(k0)
ldc1 $f30, THREAD_FP30(k0)
-
+
1:
+ /*
+ * Restore RCP interrupt mask, masking out any RCP interrupts that
+ * are not also enabled in the global interrupt mask
+ */
.set noreorder
lw k1, THREAD_RCP(k0)
la k0, __OSGlobalIntMask
@@ -1052,18 +1315,29 @@ STAY2(ctc1 k1, fcr31)
la k0, PHYS_TO_K1(MI_INTR_MASK_REG)
sw k1, 0(k0)
+ /* Empty pipeline */
nop
nop
nop
nop
+
+ /* Resume thread execution */
eret
.set reorder
END(__osDispatchThread)
+/**
+ * void __osCleanupThread(void);
+ *
+ * When a thread entrypoint function returns, it returns to this function.
+ * This function is responsible for cleaning up the thread, signalling for the
+ * current thread to be destroyed.
+ */
LEAF(__osCleanupThread)
move a0, zero
#if !defined(BBPLAYER) && !defined(__sgi)
nop
#endif
+ /* Despite being a jal, this function does not return as the thread will have been destroyed */
jal osDestroyThread
END(__osCleanupThread)