diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/libultra/io/si.c | 2 | ||||
| -rw-r--r-- | src/libultra/io/sirawdma.c | 2 | ||||
| -rw-r--r-- | src/libultra/os/exceptasm.s | 879 | ||||
| -rw-r--r-- | src/libultra/os/getintmask.s | 53 | ||||
| -rw-r--r-- | src/libultra/os/setintmask.s | 154 |
5 files changed, 1088 insertions, 2 deletions
diff --git a/src/libultra/io/si.c b/src/libultra/io/si.c index 4f9dff154..688c5e700 100644 --- a/src/libultra/io/si.c +++ b/src/libultra/io/si.c @@ -4,7 +4,7 @@ s32 __osSiDeviceBusy() { register u32 status = IO_READ(SI_STATUS_REG); - if (status & (SI_STATUS_DMA_BUSY | SI_STATUS_IO_READ_BUSY)) { + if (status & (SI_STATUS_DMA_BUSY | SI_STATUS_RD_BUSY)) { return true; } else { return false; diff --git a/src/libultra/io/sirawdma.c b/src/libultra/io/sirawdma.c index 4c5dc8cc9..eb6611c1c 100644 --- a/src/libultra/io/sirawdma.c +++ b/src/libultra/io/sirawdma.c @@ -2,7 +2,7 @@ #include "alignment.h" s32 __osSiRawStartDma(s32 direction, void* dramAddr) { - if (IO_READ(SI_STATUS_REG) & (SI_STATUS_DMA_BUSY | SI_STATUS_IO_READ_BUSY)) { + if (IO_READ(SI_STATUS_REG) & (SI_STATUS_DMA_BUSY | SI_STATUS_RD_BUSY)) { return -1; } diff --git a/src/libultra/os/exceptasm.s b/src/libultra/os/exceptasm.s new file mode 100644 index 000000000..d6f4e1a03 --- /dev/null +++ b/src/libultra/os/exceptasm.s @@ -0,0 +1,879 @@ +#ifdef __GNUC__ +.set gp=64 +#endif +#include "PR/asm.h" +#include "PR/regdef.h" +#include "PR/R4300.h" +#include "PR/rcp.h" +#include "PR/os_message.h" +#include "PR/os_thread.h" +#include "PR/os_exception.h" +#include "PR/os_internal.h" +#include "PR/version.h" + +#define MESG(x) ((x) << 3) + +.data +.align 2 + +DATA(__osHwIntTable) + .word 0, 0 + .word 0, 0 /* cart */ + .word 0, 0 + .word 0, 0 + .word 0, 0 +ENDDATA(__osHwIntTable) + +DATA(__osPiIntTable) + .word 0, 0 +ENDDATA(__osPiIntTable) + +.rdata +.align 2 + +__osIntOffTable: + .byte 0x00 /* redispatch */ + .byte 0x14 /* prenmi */ + .byte 0x18 /* IP6_Hdlr */ + .byte 0x18 /* IP6_Hdlr */ + .byte 0x1C /* IP7_Hdlr */ + .byte 0x1C /* IP7_Hdlr */ + .byte 0x1C /* IP7_Hdlr */ + .byte 0x1C /* IP7_Hdlr */ + .byte 0x20 /* counter */ + .byte 0x20 /* counter */ + .byte 0x20 /* counter */ + .byte 0x20 /* counter */ + .byte 0x20 /* counter */ + .byte 0x20 /* counter */ + .byte 0x20 /* counter */ + .byte 0x20 /* counter */ + .byte 0x00 /* redispatch */ + .byte 0x04 /* sw1 */ + .byte 0x08 /* sw2 */ + .byte 0x08 /* sw2 */ + .byte 0x0C /* rcp */ + .byte 0x0C /* rcp */ + .byte 0x0C /* rcp */ + .byte 0x0C /* rcp */ + .byte 0x10 /* cart */ + .byte 0x10 /* cart */ + .byte 0x10 /* cart */ + .byte 0x10 /* cart */ + .byte 0x10 /* cart */ + .byte 0x10 /* cart */ + .byte 0x10 /* cart */ + .byte 0x10 /* cart */ + +__osIntTable: + .word redispatch + .word sw1 + .word sw2 + .word rcp + .word cart + .word prenmi + .word IP6_Hdlr + .word IP7_Hdlr + .word counter + +.text + +/** + * The 16-byte 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 +END(__osExceptionPreamble) + +LEAF(__osException) +.set noat + /* Load scratch space for thread saving */ + la k0, __osThreadSave + /* Save at */ + sd AT, THREAD_AT(k0) +.set at + /* Save sr */ + MFC0( k1, C0_SR) + sw k1, THREAD_SR(k0) + /* Disable interrupts */ + and k1, k1, ~(SR_IE | SR_EXL) + MTC0( k1, C0_SR) + /* Save some temp registers for use in the following */ + sd t0, THREAD_T0(k0) + sd t1, THREAD_T1(k0) + sd t2, THREAD_T2(k0) + /* Mark FPU as unused */ + sw zero, THREAD_FP(k0) + /* Left over from misplaced ifdef, immediately overwritten on next instruction */ + MFC0( t0, C0_CAUSE) +savecontext: +.set noreorder + /* Save the previously running thread's context to be restored when it resumes */ + move t0, k0 + lw k0, __osRunningThread + ld t1, THREAD_AT(t0) + sd t1, THREAD_AT(k0) + ld t1, THREAD_SR(t0) + sd t1, THREAD_SR(k0) + ld t1, THREAD_T0(t0) + sd t1, THREAD_T0(k0) + ld t1, THREAD_T1(t0) + sd t1, THREAD_T1(k0) + ld t1, THREAD_T2(t0) + sd t1, THREAD_T2(k0) +.set reorder + sd $2, THREAD_V0(k0) + sd $3, THREAD_V1(k0) + sd $4, THREAD_A0(k0) + sd $5, THREAD_A1(k0) + sd $6, THREAD_A2(k0) + sd $7, THREAD_A3(k0) + sd $11, THREAD_T3(k0) + sd $12, THREAD_T4(k0) + sd $13, THREAD_T5(k0) + sd $14, THREAD_T6(k0) + sd $15, THREAD_T7(k0) + sd $16, THREAD_S0(k0) + sd $17, THREAD_S1(k0) + sd $18, THREAD_S2(k0) + sd $19, THREAD_S3(k0) + sd $20, THREAD_S4(k0) + sd $21, THREAD_S5(k0) + sd $22, THREAD_S6(k0) + sd $23, THREAD_S7(k0) + sd $24, THREAD_T8(k0) + sd $25, THREAD_T9(k0) + sd $28, THREAD_GP(k0) + sd $29, THREAD_SP(k0) + sd $30, THREAD_S8(k0) + sd $31, THREAD_RA(k0) + mflo t0 + sd t0, THREAD_LO(k0) + mfhi t0 + sd t0, THREAD_HI(k0) + lw k1, THREAD_SR(k0) + andi t1, k1, SR_IMASK + beqz t1, savercp + /* If any CPU interrupts are enabled in the previous thread's SR, bitwise-OR in the */ + /* disabled CPU interrupts from the global interrupt mask. */ + /* This is an attempt at reverting the effect of masking the thread's SR with the */ + /* global interrupt mask. This is however broken, see comments for osSetIntMask. */ + la t0, __OSGlobalIntMask + lw t0, (t0) + xor t2, t0, 0xFFFFFFFF + andi t2, t2, SR_IMASK + or t4, t1, t2 + and t3, k1, ~SR_IMASK + or t3, t3, t4 + sw t3, THREAD_SR(k0) + andi t0, t0, SR_IMASK + and t1, t1, t0 + and k1, k1, ~SR_IMASK + 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, (t0) + srl t0, t0, RCP_IMASKSHIFT + xor t0, t0, 0xFFFFFFFF + andi t0, t0, (RCP_IMASK >> RCP_IMASKSHIFT) + lw t4, THREAD_RCP(k0) + and t0, t0, t4 + or t1, t1, t0 +endrcp: + sw t1, THREAD_RCP(k0) + MFC0( t0, C0_EPC) + sw t0, THREAD_PC(k0) + lw t0, THREAD_FP(k0) + beqz t0, handle_interrupt + /* Save FP Registers if FPU was used by the thread */ +.set noreorder + cfc1 t0, C1_FPCSR + nop + sw t0, THREAD_FPCSR(k0) +.set reorder + sdc1 $f0, THREAD_FP0(k0) + sdc1 $f2, THREAD_FP2(k0) + sdc1 $f4, THREAD_FP4(k0) + sdc1 $f6, THREAD_FP6(k0) + sdc1 $f8, THREAD_FP8(k0) + sdc1 $f10, THREAD_FP10(k0) + sdc1 $f12, THREAD_FP12(k0) + sdc1 $f14, THREAD_FP14(k0) + sdc1 $f16, THREAD_FP16(k0) + sdc1 $f18, THREAD_FP18(k0) + sdc1 $f20, THREAD_FP20(k0) + sdc1 $f22, THREAD_FP22(k0) + sdc1 $f24, THREAD_FP24(k0) + sdc1 $f26, THREAD_FP26(k0) + sdc1 $f28, THREAD_FP28(k0) + sdc1 $f30, THREAD_FP30(k0) + +handle_interrupt: + /* Determine the cause of the exception or interrupt and */ + /* enter appropriate handling routine */ + MFC0( t0, C0_CAUSE) + sw t0, THREAD_CAUSE(k0) +label: + li t1, OS_STATE_RUNNABLE + sh t1, THREAD_STATE(k0) + 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's not an interrupt, panic */ + li t2, EXC_INT + bne t1, t2, panic + + and s0, k1, t0 + +next_interrupt: + /* Handle external interrupt causes, using a jump table */ + /* to enter into the appropriate handler */ + andi t1, s0, CAUSE_IPMASK + srl t2, t1, CAUSE_IPSHIFT + 4 + bnez t2, 1f + + srl t2, t1, CAUSE_IPSHIFT + addi t2, t2, 0x10 +1: + lbu t2, __osIntOffTable(t2) + 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: + MFC0( t1, C0_COMPARE) + MTC0( t1, C0_COMPARE) + /* Post counter message */ + li a0, MESG(OS_EVENT_COUNTER) + 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: + /* Mask out interrupt */ + and s0, s0, ~CAUSE_IP4 + /* Load cart callback set by __osSetHWIntrRoutine */ + la t1, __osHwIntTable + addi t1, t1, (OS_INTR_CART * HWINT_SIZE) + lw t2, HWINT_CALLBACK(t1) + /* If the callback is NULL, handling is done */ + beqz t2, 1f + /* Set up a stack and run the callback */ + lw sp, HWINT_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 + +/** + * 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, RCP_IMASKSHIFT + and s1, s1, t0 + +/** + * Signal Processor (SP) Interrupt + */ + /* Test for sp interrupt */ + andi t1, s1, MI_INTR_SP + beqz t1, vi + /* Mask out SP interrupt */ + andi s1, s1, (MI_INTR_SI | MI_INTR_AI | MI_INTR_VI | MI_INTR_PI | MI_INTR_DP) + lw t4, PHYS_TO_K1(SP_STATUS_REG) + /* Clear interrupt and signal 3 */ + li t1, (SP_CLR_INTR | SP_CLR_SIG3) + sw t1, PHYS_TO_K1(SP_STATUS_REG) + /* Test for yielded or done signals in particular */ + andi t4, t4, (SP_STATUS_YIELDED | SP_STATUS_TASKDONE) + beqz t4, 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: + /* Test for vi interrupt */ + andi t1, s1, MI_INTR_VI + beqz t1, ai + /* 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: + /* Test for ai interrupt */ + andi t1, s1, MI_INTR_AI + beqz t1, si + + /* Mask out ai interrupt */ + andi s1, s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_VI | MI_INTR_PI | MI_INTR_DP) + /* Clear interrupt */ + li t1, 1 + 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: + /* Test for si interrupt */ + andi t1, s1, MI_INTR_SI + beqz t1, pi + + /* 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: + /* Test for pi interrupt */ + andi t1, s1, MI_INTR_PI + beqz t1, dp + + /* Mask out pi interrupt */ + andi s1, s1, (MI_INTR_SP | MI_INTR_SI | MI_INTR_AI | MI_INTR_VI | MI_INTR_DP) + /* Clear the interrupt */ + li t1, PI_STATUS_CLR_INTR + sw t1, PHYS_TO_K1(PI_STATUS_REG) + /* Load pi callback */ + la t1, __osPiIntTable + lw t2, HWINT_CALLBACK(t1) + /* Skip callback if NULL */ + beqz t2, no_pi_callback + /* Set up a stack and run the callback */ + lw sp, HWINT_SP(t1) + move a0, v0 + jalr t2 + /* If the callback returns non-zero, don't post a pi event message */ + bnez v0, skip_pi_mesg +no_pi_callback: + /* Post pi event message */ + li a0, MESG(OS_EVENT_PI) + jal send_mesg +skip_pi_mesg: + beqz s1, NoMoreRcpInts + +/** + * Display Processor (DP) Interrupt + */ +dp: + /* Test for dp interrupt */ + andi t1, s1, MI_INTR_DP + beqz t1, NoMoreRcpInts + + /* 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, ~SR_IBIT5 + sw k1, THREAD_SR(k0) + /* Test __osShutdown for first PreNMI event */ + la t1, __osShutdown + lw t2, (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, (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, ~SR_IBIT5 + lw t2, __osRunQueue + lw k1, THREAD_SR(t2) + and k1, k1, ~SR_IBIT5 + sw k1, THREAD_SR(t2) + /* Redispatch immediately */ + b redispatch + +sw2: + /* Mask out interrupt */ + and t0, t0, ~CAUSE_SW2 + 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 + 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, (t1) + sw t2, THREAD_NEXT(k0) + sw k0, (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) + /* 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 + +/** + * Handles posting event messages to the listening message queue, if there is one + */ +send_mesg: + /* Save return address */ + move s2, ra + /* Load pointer to listening message queue */ + la t2, __osEventStateTab + addu t2, t2, a0 + lw t1, (t2) + /* If there is no listening message queue, done */ + beqz t1, send_done + + /* Test if the message queue is full, if so don't post the message */ + lw t3, MQ_VALIDCOUNT(t1) + lw t4, MQ_MSGCOUNT(t1) + bge t3, t4, send_done + + /* Add validcount to first and modulo with msgcount */ + lw t5, MQ_FIRST(t1) + addu t5, t5, t3 + rem t5, t5, t4 + lw t4, MQ_MSG(t1) + mul t5, t5, 4 + addu t4, t4, t5 + /* Post the message to the message queue */ + lw t5, 4(t2) + sw t5, (t4) + /* 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, (t2) + beqz t3, send_done + move a0, t1 + jal __osPopThread + move t2, v0 + move a1, t2 + la a0, __osRunQueue + jal __osEnqueueThread +send_done: + jr s2 + +/** + * Handle coprocessor unusable exception + */ +handle_CpU: + and t1, t0, CAUSE_CEMASK + srl t1, t1, CAUSE_CESHIFT + li t2, 1 /* if not coprocessor 1, panic */ + bne t1, t2, panic + /* Mark cop1 as usable for previous thread */ + li t1, 1 + sw t1, THREAD_FP(k0) + lw k1, THREAD_SR(k0) + or k1, k1, SR_CU1 + sw k1, THREAD_SR(k0) + b enqueueRunning +END(__osException) + +/** + * 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 + /* Save SR */ + MFC0( t0, C0_SR) + ori t0, t0, SR_EXL + sw t0, THREAD_SR(a1) + /* Save callee-saved registers */ + sd s0, THREAD_S0(a1) + sd s1, THREAD_S1(a1) + sd s2, THREAD_S2(a1) + sd s3, THREAD_S3(a1) + sd s4, THREAD_S4(a1) + sd s5, THREAD_S5(a1) + sd s6, THREAD_S6(a1) + sd s7, THREAD_S7(a1) + sd gp, THREAD_GP(a1) + sd sp, THREAD_SP(a1) + sd fp, THREAD_S8(a1) + sd ra, THREAD_RA(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, C1_FPCSR + sw k1, THREAD_FPCSR(a1) + sdc1 $f20, THREAD_FP20(a1) + sdc1 $f22, THREAD_FP22(a1) + sdc1 $f24, THREAD_FP24(a1) + sdc1 $f26, THREAD_FP26(a1) + sdc1 $f28, THREAD_FP28(a1) + sdc1 $f30, THREAD_FP30(a1) +1: + lw k1, THREAD_SR(a1) + 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, (t0) + xor t0, t0, 0xFFFFFFFF + andi t0, t0, SR_IMASK + or t1, t1, t0 + and k1, k1, ~SR_IMASK + or k1, k1, t1 + sw k1, THREAD_SR(a1) +2: + 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, (k0) + srl k0, k0, RCP_IMASKSHIFT + xor k0, k0, 0xFFFFFFFF + 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, no_enqueue + jal __osEnqueueThread +no_enqueue: + j __osDispatchThread +END(__osEnqueueAndYield) + +/** + * void __osEnqueueThread(OSThread** threadQueue, OSThread* thread); + * + * Enqueues `thread` to the thread queue `threadQueue`, inserted by priority + */ +LEAF(__osEnqueueThread) + move t9, a0 + lw t8, (a0) + lw t7, THREAD_PRI(a1) + lw t6, THREAD_PRI(t8) + /* If the current highest priority thread is a lower priority than */ + /* the new thread, skip searching the queue */ + blt t6, t7, 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 t6, THREAD_PRI(t8) + bge t6, t7, 1b +2: + /* Insert the thread into the queue */ + lw t8, (t9) + sw t8, THREAD_NEXT(a1) + sw a1, (t9) + sw a0, THREAD_QUEUE(a1) + 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, (a0) + lw t9, THREAD_NEXT(v0) + sw t9, (a0) + jr ra +END(__osPopThread) + +LEAF(__osNop) + jr ra +END(__osNop) + +/** + * 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 + lw k1, THREAD_SR(k0) + la t0, __OSGlobalIntMask + lw t0, (t0) + andi t0, t0, SR_IMASK + andi t1, k1, SR_IMASK + and t1, t1, t0 + and k1, k1, ~SR_IMASK + or k1, k1, t1 + MTC0( k1, C0_SR) + /* Restore GPRs */ +.set noat + ld AT, THREAD_AT(k0) + ld v0, THREAD_V0(k0) + ld v1, THREAD_V1(k0) + ld a0, THREAD_A0(k0) + ld a1, THREAD_A1(k0) + ld a2, THREAD_A2(k0) + ld a3, THREAD_A3(k0) + ld t0, THREAD_T0(k0) + ld t1, THREAD_T1(k0) + ld t2, THREAD_T2(k0) + ld t3, THREAD_T3(k0) + ld t4, THREAD_T4(k0) + ld t5, THREAD_T5(k0) + ld t6, THREAD_T6(k0) + ld t7, THREAD_T7(k0) + ld s0, THREAD_S0(k0) + ld s1, THREAD_S1(k0) + ld s2, THREAD_S2(k0) + ld s3, THREAD_S3(k0) + ld s4, THREAD_S4(k0) + ld s5, THREAD_S5(k0) + ld s6, THREAD_S6(k0) + ld s7, THREAD_S7(k0) + ld t8, THREAD_T8(k0) + ld t9, THREAD_T9(k0) + ld gp, THREAD_GP(k0) + ld sp, THREAD_SP(k0) + ld fp, THREAD_S8(k0) + ld ra, THREAD_RA(k0) + ld k1, THREAD_LO(k0) + 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) + 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 + +.set noreorder + lw k1, THREAD_FPCSR(k0) + ctc1 k1, C1_FPCSR +.set reorder + ldc1 $f0, THREAD_FP0(k0) + ldc1 $f2, THREAD_FP2(k0) + ldc1 $f4, THREAD_FP4(k0) + ldc1 $f6, THREAD_FP6(k0) + ldc1 $f8, THREAD_FP8(k0) + ldc1 $f10, THREAD_FP10(k0) + ldc1 $f12, THREAD_FP12(k0) + ldc1 $f14, THREAD_FP14(k0) + ldc1 $f16, THREAD_FP16(k0) + ldc1 $f18, THREAD_FP18(k0) + ldc1 $f20, THREAD_FP20(k0) + ldc1 $f22, THREAD_FP22(k0) + ldc1 $f24, THREAD_FP24(k0) + 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 + lw k0, (k0) + srl k0, k0, RCP_IMASKSHIFT + and k1, k1, k0 + sll k1, k1, 1 + la k0, __osRcpImTable + addu k1, k1, k0 + lhu k1, (k1) + la k0, PHYS_TO_K1(MI_INTR_MASK_REG) + sw k1, (k0) + /* Empty pipeline */ + nop + nop + nop + nop + /* Resume thread execution */ + eret +.set reorder +.set at +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 + jal osDestroyThread + /* Despite being a jal, this function does not return as the thread will have been destroyed */ +END(__osCleanupThread) diff --git a/src/libultra/os/getintmask.s b/src/libultra/os/getintmask.s new file mode 100644 index 000000000..f5e7cf770 --- /dev/null +++ b/src/libultra/os/getintmask.s @@ -0,0 +1,53 @@ +#include "PR/asm.h" +#include "PR/regdef.h" +#include "PR/R4300.h" +#include "PR/rcp.h" +#include "PR/os_exception.h" + +.text + +/** + * OSIntMask osGetIntMask(void); + * + * Gets the interrupt enable mask for the current thread. + * Interrupts that are not enabled in the global interrupt mask __OSGlobalIntMask + * are not returned here. The global interrupt mask is OS-internal and is not + * expected to change during runtime. + * + * @bug Some usage of the global interrupt mask is broken both in the + * get/set interrupt mask routines and in the exception handler routines. + * See the comment for osSetIntMask for more details. + */ +LEAF(osGetIntMask) +.set noreorder + /* Extract interrupt enable bits from current SR */ + mfc0 v0, C0_SR + andi v0, v0, (SR_IMASK | SR_IE) + /* Get value of __OSGlobalIntMask */ + la t0, __OSGlobalIntMask + lw t1, (t0) + /* Bitwise-OR in the disabled CPU bits of __OSGlobalIntMask */ + xor t0, t1, ~0 + andi t0, t0, SR_IMASK + or v0, v0, t0 + /* Fetch MI_INTR_MASK_REG */ + lw t1, PHYS_TO_K1(MI_INTR_MASK_REG) + /* If there are RCP interrupts masked */ + beqz t1, 1f + /* Get value of __OSGlobalIntMask */ + la t0, __OSGlobalIntMask /* Note: macro expansion in delay slot */ + lw t0, (t0) + /* Bitwise-OR in the disabled RCP bits of __OSGlobalIntMask */ + srl t0, t0, RCP_IMASKSHIFT + xor t0, t0, ~0 + andi t0, t0, (RCP_IMASK >> RCP_IMASKSHIFT) + or t1, t1, t0 +1: + /* Shift the RCP bits to not conflict with the CPU bits */ + sll t2, t1, RCP_IMASKSHIFT + /* OR the CPU and RCP bits together */ + or v0, v0, t2 + jr ra + nop +.set reorder +END(osGetIntMask) diff --git a/src/libultra/os/setintmask.s b/src/libultra/os/setintmask.s new file mode 100644 index 000000000..68a96bc8b --- /dev/null +++ b/src/libultra/os/setintmask.s @@ -0,0 +1,154 @@ +#include "PR/asm.h" +#include "PR/regdef.h" +#include "PR/R4300.h" +#include "PR/rcp.h" +#include "PR/os_exception.h" + +.rdata +.align 2 + +/** + * LUT to convert between an interrupt mask value and a value for MI_INTR_MASK_REG. + * The interrupt mask value is a single bit 0 = disabled, 1 = enabled, while writes to MI_INTR_MASK_REG has two distinct non-zero + * values for set and clear, hence the need for a conversion step. + */ +DATA(__osRcpImTable) + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_CLR_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_CLR_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_CLR_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_CLR_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_CLR_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_CLR_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP + .half MI_INTR_MASK_SET_SP | MI_INTR_MASK_SET_SI | MI_INTR_MASK_SET_AI | MI_INTR_MASK_SET_VI | MI_INTR_MASK_SET_PI | MI_INTR_MASK_SET_DP +ENDDATA(__osRcpImTable) + +.text + +/** + * OSIntMask osSetIntMask(OSIntMask); + * + * Sets the interrupt enable mask for the current thread. External interrupts + * originating either in the CPU or the RCP may be "masked out" so that they + * are not handled. This is sometimes important for critical code sections + * that must not be interrupted. + * Interrupts that are not enabled in the global interrupt mask __OSGlobalIntMask + * cannot be set here. The global interrupt mask is OS-internal and is not + * expected to change during runtime. + * The returned value is the previous interrupt enable mask so that it can be + * restored later. + * + * @bug Some usage of the global interrupt mask is broken both in the + * get/set interrupt mask routines and in the exception handler routines. + * While a thread is running, the C0_SR interrupt enable bits contain the + * interrupt enable bits for the current thread masked by the global + * interrupt mask. There is an attempt to recover only the original interrupt + * enable bits belonging to the thread itself using the operation + * (SR | ~__OSGlobalIntMask). + * However, this does not work as intended and can cause interrupts to end + * up enabled when not intended to be. The same issue is present for the + * RCP interrupt enable bits in MI_INTR_MASK_REG. + * This does not cause issues in practice as __OSGlobalIntMask is almost always + * OS_IM_ALL, so the operation is usually simply (SR | 0). + */ +LEAF(osSetIntMask) + /* Extract interrupt enable bits from current SR */ + MFC0( t4, C0_SR) +.set noreorder + and v0, t4, (SR_IMASK | SR_IE) + /* Get value of __OSGlobalIntMask */ + la t0, __OSGlobalIntMask + lw t3, (t0) + /* Bitwise-OR in the disabled CPU bits of __OSGlobalIntMask */ + xor t0, t3, 0xFFFFFFFF + and t0, t0, SR_IMASK + or v0, v0, t0 + /* Fetch MI_INTR_MASK_REG */ + lw t2, PHYS_TO_K1(MI_INTR_MASK_REG) + /* If there are RCP interrupts masked */ + beqz t2, 1f + srl t1, t3, RCP_IMASKSHIFT + /* Bitwise-OR in the disabled RCP bits of __OSGlobalIntMask */ + xor t1, t1, 0xFFFFFFFF + and t1, t1, (RCP_IMASK >> RCP_IMASKSHIFT) + or t2, t2, t1 +1: + /* Shift the RCP bits to not conflict with the CPU bits */ + sll t2, t2, RCP_IMASKSHIFT + /* OR the CPU and RCP bits together */ + or v0, v0, t2 + /* Extract RCP interrupt enable bits from requested mask and mask with __OSGlobalIntMask */ + and t0, a0, RCP_IMASK + and t0, t0, t3 + /* Convert to a value for MI_INTR_MASK_REG and set it */ + srl t0, t0, (RCP_IMASKSHIFT-1) + lhu t2, __osRcpImTable(t0) + sw t2, PHYS_TO_K1(MI_INTR_MASK_REG) + /* Extract CPU interrupt enable bits from requested mask and mask with __OSGlobalIntMask */ + and t0, a0, OS_IM_CPU + and t1, t3, SR_IMASK + and t0, t0, t1 + and t4, t4, ~SR_IMASK + /* Bitwise OR in the remaining bits of SR and set new SR */ + or t4, t4, t0 + MTC0( t4, C0_SR) + NOP + NOP + jr ra +END(osSetIntMask) |
