diff --git a/include/PR/os_exception.h b/include/PR/os_exception.h index 7140c96636..5af2bd6391 100644 --- a/include/PR/os_exception.h +++ b/include/PR/os_exception.h @@ -1,12 +1,6 @@ #ifndef PR_OS_EXCEPTION_H #define PR_OS_EXCEPTION_H -#include "ultratypes.h" - - -typedef u32 OSIntMask; -typedef u32 OSHWIntr; - /* Flags for debugging purpose */ #define OS_FLAG_CPU_BREAK 1 /* Break exception has occurred */ @@ -34,6 +28,15 @@ typedef u32 OSHWIntr; #define RCP_IMASK 0x003F0000 #define RCP_IMASKSHIFT 16 +/* OSHWIntr values */ +#define OS_INTR_CART 1 + +#ifdef _LANGUAGE_C + +#include "ultratypes.h" + +typedef u32 OSIntMask; +typedef u32 OSHWIntr; OSIntMask osGetIntMask(void); OSIntMask osSetIntMask(OSIntMask im); @@ -45,5 +48,6 @@ void __osGetHWIntrRoutine(OSHWIntr interrupt, s32 (**handler)(void), void** stac void __osSetGlobalIntMask(OSHWIntr mask); void __osResetGlobalIntMask(OSHWIntr mask); +#endif #endif diff --git a/include/PR/os_internal.h b/include/PR/os_internal.h index 10ae5515eb..f8368d8eff 100644 --- a/include/PR/os_internal.h +++ b/include/PR/os_internal.h @@ -1,14 +1,24 @@ #ifndef PR_OS_INTERNAL_H #define PR_OS_INTERNAL_H +#ifdef _LANGUAGE_C + #include "ultratypes.h" -#include "os_message.h" -#include "os_pi.h" -#include "os_internal_rsp.h" typedef struct __osHwInt { /* 0x0 */ s32 (*handler)(void); /* 0x4 */ void* stackEnd; } __osHwInt; // size = 0x8 +#else + +/* __osHwInt struct member offsets */ +#define HWINT_CALLBACK 0x00 +#define HWINT_SP 0x04 + +/* __osHwInt struct size */ +#define HWINT_SIZE 0x8 + +#endif + #endif diff --git a/include/PR/os_message.h b/include/PR/os_message.h index bb577f57b5..0a21272c17 100644 --- a/include/PR/os_message.h +++ b/include/PR/os_message.h @@ -1,20 +1,6 @@ #ifndef PR_OS_MESSAGE_H #define PR_OS_MESSAGE_H -#include "os_thread.h" - -typedef void* OSMesg; -typedef u32 OSEvent; - -typedef struct OSMesgQueue { - /* 0x00 */ OSThread* mtQueue; - /* 0x04 */ OSThread* fullQueue; - /* 0x08 */ s32 validCount; - /* 0x0C */ s32 first; - /* 0x10 */ s32 msgCount; - /* 0x14 */ OSMesg* msg; -} OSMesgQueue; // size = 0x18 - #define OS_NUM_EVENTS 15 #define OS_EVENT_SW1 0 /* CPU SW1 interrupt */ @@ -45,6 +31,22 @@ typedef struct OSMesgQueue { #define OS_MESG_NOBLOCK 0 #define OS_MESG_BLOCK 1 +#ifdef _LANGUAGE_C + +#include "os_thread.h" + +typedef void* OSMesg; +typedef u32 OSEvent; + +typedef struct OSMesgQueue { + /* 0x00 */ OSThread* mtQueue; + /* 0x04 */ OSThread* fullQueue; + /* 0x08 */ s32 validCount; + /* 0x0C */ s32 first; + /* 0x10 */ s32 msgCount; + /* 0x14 */ OSMesg* msg; +} OSMesgQueue; // size = 0x18 + #define MQ_GET_COUNT(mq) ((mq)->validCount) #define MQ_IS_EMPTY(mq) (MQ_GET_COUNT(mq) == 0) @@ -57,5 +59,17 @@ s32 osRecvMesg(OSMesgQueue* mq, OSMesg* msg, s32 flags); void osSetEventMesg(OSEvent e, OSMesgQueue* mq, OSMesg m); +#else + +/* OSMesgQueue struct member offsets */ + +#define MQ_MTQUEUE 0x00 +#define MQ_FULLQUEUE 0x04 +#define MQ_VALIDCOUNT 0x08 +#define MQ_FIRST 0x0C +#define MQ_MSGCOUNT 0x10 +#define MQ_MSG 0x14 + +#endif #endif diff --git a/include/PR/os_thread.h b/include/PR/os_thread.h index 4823377d09..95f01fb2b1 100644 --- a/include/PR/os_thread.h +++ b/include/PR/os_thread.h @@ -1,11 +1,35 @@ #ifndef PR_OS_THREAD_H #define PR_OS_THREAD_H -#include "ultratypes.h" +#define OS_STATE_STOPPED (1 << 0) +#define OS_STATE_RUNNABLE (1 << 1) +#define OS_STATE_RUNNING (1 << 2) +#define OS_STATE_WAITING (1 << 3) + +#define OS_PRIORITY_IDLE 0 +#define OS_PRIORITY_MAIN 10 +#define OS_PRIORITY_GRAPH 11 +#define OS_PRIORITY_AUDIOMGR 12 +#define OS_PRIORITY_PADMGR 14 +#define OS_PRIORITY_SCHED 15 +#define OS_PRIORITY_DMAMGR 16 +#define OS_PRIORITY_IRQMGR 17 +#define OS_PRIORITY_PIMGR 150 +#define OS_PRIORITY_FAULTCLIENT 126 +#define OS_PRIORITY_FAULT 127 +#define OS_PRIORITY_APPMAX 127 +#define OS_PRIORITY_RMONSPIN 200 +#define OS_PRIORITY_RMON 250 +#define OS_PRIORITY_VIMGR 254 +#define OS_PRIORITY_MAX 255 #define OS_FLAG_CPU_BREAK 1 #define OS_FLAG_FAULT 2 +#ifdef _LANGUAGE_C + +#include "ultratypes.h" + typedef s32 OSPri; typedef s32 OSId; @@ -47,29 +71,6 @@ typedef struct OSThread { /* 0x20 */ __OSThreadContext context; } OSThread; // size = 0x1B0 -#define OS_STATE_STOPPED (1 << 0) -#define OS_STATE_RUNNABLE (1 << 1) -#define OS_STATE_RUNNING (1 << 2) -#define OS_STATE_WAITING (1 << 3) - - -#define OS_PRIORITY_IDLE 0 -#define OS_PRIORITY_MAIN 10 -#define OS_PRIORITY_GRAPH 11 -#define OS_PRIORITY_AUDIOMGR 12 -#define OS_PRIORITY_PADMGR 14 -#define OS_PRIORITY_SCHED 15 -#define OS_PRIORITY_DMAMGR 16 -#define OS_PRIORITY_IRQMGR 17 -#define OS_PRIORITY_PIMGR 150 -#define OS_PRIORITY_FAULTCLIENT 126 -#define OS_PRIORITY_FAULT 127 -#define OS_PRIORITY_APPMAX 127 -#define OS_PRIORITY_RMONSPIN 200 -#define OS_PRIORITY_RMON 250 -#define OS_PRIORITY_VIMGR 254 -#define OS_PRIORITY_MAX 255 - void osCreateThread(OSThread* thread, OSId id, void* entry, void* arg, void* sp, OSPri p); void osDestroyThread(OSThread* t); void osYieldThread(void); @@ -82,4 +83,74 @@ OSPri osGetThreadPri(OSThread* t); // internal OSThread* __osGetActiveQueue(void); +#else + +/* OSThread struct member offsets */ + +#define THREAD_NEXT 0x00 +#define THREAD_PRI 0x04 +#define THREAD_QUEUE 0x08 +#define THREAD_TLNEXT 0x0C +#define THREAD_STATE 0x10 +#define THREAD_FLAGS 0x12 +#define THREAD_ID 0x14 +#define THREAD_FP 0x18 +#define THREAD_PROFILE 0x1C +#define THREAD_CONTEXT 0x20 +#define THREAD_AT (THREAD_CONTEXT + 0x000) +#define THREAD_V0 (THREAD_CONTEXT + 0x008) +#define THREAD_V1 (THREAD_CONTEXT + 0x010) +#define THREAD_A0 (THREAD_CONTEXT + 0x018) +#define THREAD_A1 (THREAD_CONTEXT + 0x020) +#define THREAD_A2 (THREAD_CONTEXT + 0x028) +#define THREAD_A3 (THREAD_CONTEXT + 0x030) +#define THREAD_T0 (THREAD_CONTEXT + 0x038) +#define THREAD_T1 (THREAD_CONTEXT + 0x040) +#define THREAD_T2 (THREAD_CONTEXT + 0x048) +#define THREAD_T3 (THREAD_CONTEXT + 0x050) +#define THREAD_T4 (THREAD_CONTEXT + 0x058) +#define THREAD_T5 (THREAD_CONTEXT + 0x060) +#define THREAD_T6 (THREAD_CONTEXT + 0x068) +#define THREAD_T7 (THREAD_CONTEXT + 0x070) +#define THREAD_S0 (THREAD_CONTEXT + 0x078) +#define THREAD_S1 (THREAD_CONTEXT + 0x080) +#define THREAD_S2 (THREAD_CONTEXT + 0x088) +#define THREAD_S3 (THREAD_CONTEXT + 0x090) +#define THREAD_S4 (THREAD_CONTEXT + 0x098) +#define THREAD_S5 (THREAD_CONTEXT + 0x0A0) +#define THREAD_S6 (THREAD_CONTEXT + 0x0A8) +#define THREAD_S7 (THREAD_CONTEXT + 0x0B0) +#define THREAD_T8 (THREAD_CONTEXT + 0x0B8) +#define THREAD_T9 (THREAD_CONTEXT + 0x0C0) +#define THREAD_GP (THREAD_CONTEXT + 0x0C8) +#define THREAD_SP (THREAD_CONTEXT + 0x0D0) +#define THREAD_S8 (THREAD_CONTEXT + 0x0D8) +#define THREAD_RA (THREAD_CONTEXT + 0x0E0) +#define THREAD_LO (THREAD_CONTEXT + 0x0E8) +#define THREAD_HI (THREAD_CONTEXT + 0x0F0) +#define THREAD_SR (THREAD_CONTEXT + 0x0F8) +#define THREAD_PC (THREAD_CONTEXT + 0x0FC) +#define THREAD_CAUSE (THREAD_CONTEXT + 0x100) +#define THREAD_BADVADDR (THREAD_CONTEXT + 0x104) +#define THREAD_RCP (THREAD_CONTEXT + 0x108) +#define THREAD_FPCSR (THREAD_CONTEXT + 0x10C) +#define THREAD_FP0 (THREAD_CONTEXT + 0x110) +#define THREAD_FP2 (THREAD_CONTEXT + 0x118) +#define THREAD_FP4 (THREAD_CONTEXT + 0x120) +#define THREAD_FP6 (THREAD_CONTEXT + 0x128) +#define THREAD_FP8 (THREAD_CONTEXT + 0x130) +#define THREAD_FP10 (THREAD_CONTEXT + 0x138) +#define THREAD_FP12 (THREAD_CONTEXT + 0x140) +#define THREAD_FP14 (THREAD_CONTEXT + 0x148) +#define THREAD_FP16 (THREAD_CONTEXT + 0x150) +#define THREAD_FP18 (THREAD_CONTEXT + 0x158) +#define THREAD_FP20 (THREAD_CONTEXT + 0x160) +#define THREAD_FP22 (THREAD_CONTEXT + 0x168) +#define THREAD_FP24 (THREAD_CONTEXT + 0x170) +#define THREAD_FP26 (THREAD_CONTEXT + 0x178) +#define THREAD_FP28 (THREAD_CONTEXT + 0x180) +#define THREAD_FP30 (THREAD_CONTEXT + 0x188) + +#endif + #endif diff --git a/include/PR/rcp.h b/include/PR/rcp.h index 1f2b57de5e..6b3671ea8b 100644 --- a/include/PR/rcp.h +++ b/include/PR/rcp.h @@ -1,10 +1,90 @@ #ifndef PR_RCP_H #define PR_RCP_H +#include "R4300.h" +#include "ultratypes.h" + +/** + * RCP memory map overview: + * + * 0x0000_0000 .. 0x03EF_FFFF RDRAM memory + * 0x03F0_0000 .. 0x03FF_FFFF RDRAM registers + * + * 0x0400_0000 .. 0x0400_2000 SP memory + * 0x0404_0000 .. 0x040F_FFFF SP registers + * 0x0410_0000 .. 0x041F_FFFF DP command registers + * 0x0420_0000 .. 0x042F_FFFF DP span registers + * 0x0430_0000 .. 0x043F_FFFF MI registers + * 0x0440_0000 .. 0x044F_FFFF VI registers + * 0x0450_0000 .. 0x045F_FFFF AI registers + * 0x0460_0000 .. 0x046F_FFFF PI registers + * 0x0470_0000 .. 0x047F_FFFF RI registers + * 0x0480_0000 .. 0x048F_FFFF SI registers + * 0x0490_0000 .. 0x04FF_FFFF unused + * + * 0x0500_0000 .. 0x05FF_FFFF cartridge domain 2 + * 0x0600_0000 .. 0x07FF_FFFF cartridge domain 1 + * 0x0800_0000 .. 0x0FFF_FFFF cartridge domain 2 + * 0x1000_0000 .. 0x1FBF_FFFF cartridge domain 1 + * + * 0x1FC0_0000 .. 0x1FC0_07BF PIF Boot Rom (1984 bytes) + * 0x1FC0_07C0 .. 0x1FC0_07FF PIF (JoyChannel) RAM (64 bytes) + * 0x1FC0_0800 .. 0x1FCF_FFFF Reserved + * 0x1FD0_0000 .. 0x7FFF_FFFF cartridge domain 1 + * 0x8000_0000 .. 0xFFFF_FFFF external SysAD device + */ + + +/** + * RDRAM memory + */ + +#define RDRAM_0_START 0x00000000 +#define RDRAM_0_END 0x001FFFFF +#define RDRAM_1_START 0x00200000 +#define RDRAM_1_END 0x003FFFFF + +#define RDRAM_START RDRAM_0_START +#define RDRAM_END RDRAM_1_END + + +/** + * RDRAM registers + */ +#define RDRAM_BASE_REG 0x03F00000 + +#define RDRAM_CONFIG_REG (RDRAM_BASE_REG + 0x00) +#define RDRAM_DEVICE_TYPE_REG (RDRAM_BASE_REG + 0x00) +#define RDRAM_DEVICE_ID_REG (RDRAM_BASE_REG + 0x04) +#define RDRAM_DELAY_REG (RDRAM_BASE_REG + 0x08) +#define RDRAM_MODE_REG (RDRAM_BASE_REG + 0x0C) +#define RDRAM_REF_INTERVAL_REG (RDRAM_BASE_REG + 0x10) +#define RDRAM_REF_ROW_REG (RDRAM_BASE_REG + 0x14) +#define RDRAM_RAS_INTERVAL_REG (RDRAM_BASE_REG + 0x18) +#define RDRAM_MIN_INTERVAL_REG (RDRAM_BASE_REG + 0x1C) +#define RDRAM_ADDR_SELECT_REG (RDRAM_BASE_REG + 0x20) +#define RDRAM_DEVICE_MANUF_REG (RDRAM_BASE_REG + 0x24) + +#define RDRAM_0_DEVICE_ID 0 +#define RDRAM_1_DEVICE_ID 1 + +#define RDRAM_RESET_MODE 0 +#define RDRAM_ACTIVE_MODE 1 +#define RDRAM_STANDBY_MODE 2 + +#define RDRAM_LENGTH (2 * 512 * 2048) +#define RDRAM_0_BASE_ADDRESS (RDRAM_0_DEVICE_ID * RDRAM_LENGTH) +#define RDRAM_1_BASE_ADDRESS (RDRAM_1_DEVICE_ID * RDRAM_LENGTH) + +#define RDRAM_0_CONFIG 0x00000 +#define RDRAM_1_CONFIG 0x00400 +#define RDRAM_GLOBAL_CONFIG 0x80000 + + /** * PIF Physical memory map (total size = 2 KB) * - * Size Description Mode + * Size Description Mode * 1FC007FF +-------+-----------------+-----+ * | 64 B | JoyChannel RAM | R/W | * 1FC007C0 +-------+-----------------+-----+ @@ -16,84 +96,13 @@ #define PIF_RAM_START 0x1FC007C0 #define PIF_RAM_END 0x1FC007FF -/* - * Patterns to interpret VI_CONTROL_REG - */ -#define VI_CTRL_TYPE_16 0x00002 /* [1:0] pixel size: 16 bit */ -#define VI_CTRL_TYPE_32 0x00003 /* [1:0] pixel size: 32 bit */ -#define VI_CTRL_GAMMA_DITHER_ON 0x00004 /* 2: default = on */ -#define VI_CTRL_GAMMA_ON 0x00008 /* 3: default = on */ -#define VI_CTRL_DIVOT_ON 0x00010 /* 4: default = on */ -#define VI_CTRL_SERRATE_ON 0x00040 /* 6: on if interlaced */ -#define VI_CTRL_ANTIALIAS_MASK 0x00300 /* [9:8] anti-alias mode */ -#define VI_CTRL_ANTIALIAS_MODE_1 0x00100 /* Bit [9:8] anti-alias mode */ -#define VI_CTRL_ANTIALIAS_MODE_2 0x00200 /* Bit [9:8] anti-alias mode */ -#define VI_CTRL_ANTIALIAS_MODE_3 0x00300 /* Bit [9:8] anti-alias mode */ -#define VI_CTRL_PIXEL_ADV_MASK 0x01000 /* [15:12] pixel advance mode? */ -#define VI_CTRL_PIXEL_ADV_1 0x01000 /* Bit [15:12] pixel advance mode? */ -#define VI_CTRL_PIXEL_ADV_2 0x02000 /* Bit [15:12] pixel advance mode? */ -#define VI_CTRL_PIXEL_ADV_3 0x03000 /* Bit [15:12] pixel advance mode? */ -#define VI_CTRL_DITHER_FILTER_ON 0x10000 /* 16: dither-filter mode */ - - -#define VI_NTSC_CLOCK 48681812 /* Hz = 48.681812 MHz */ -#define VI_PAL_CLOCK 49656530 /* Hz = 49.656530 MHz */ -#define VI_MPAL_CLOCK 48628316 /* Hz = 48.628316 MHz */ /** - * Audio Interface (AI) Registers + * Controller channel + * Each game controller channel has 4 error bits that are defined in bit 6-7 of + * the Rx and Tx data size area bytes. Programmers need to clear these bits + * when setting the Tx/Rx size area values for a channel */ -#define AI_BASE_REG 0x04500000 - -/* AI DRAM address (W): [23:0] starting RDRAM address (8B-aligned) */ -#define AI_DRAM_ADDR_REG (AI_BASE_REG + 0x00) /* R0: DRAM address */ - -/* AI length (R/W): [14:0] transfer length (v1.0) - Bottom 3 bits are ignored */ -/* [17:0] transfer length (v2.0) - Bottom 3 bits are ignored */ -#define AI_LEN_REG (AI_BASE_REG + 0x04) /* R1: Length */ - -/* AI control (W): [0] DMA enable - if LSB == 1, DMA is enabled */ -#define AI_CONTROL_REG (AI_BASE_REG + 0x08) /* R2: DMA Control */ - -/* Value for control register */ -#define AI_CONTROL_DMA_ON 1 /* LSB = 1: DMA enable*/ -#define AI_CONTROL_DMA_OFF 0 /* LSB = 1: DMA enable*/ - -/* - * AI status (R): [31]/[0] ai_full (addr & len buffer full), [30] ai_busy - * Note that a 1->0 transition in ai_full will set interrupt - * (W): clear audio interrupt - */ -#define AI_STATUS_REG (AI_BASE_REG + 0x0C) /* R3: Status */ - -/* Value for status register */ -#define AI_STATUS_FIFO_FULL (1 << 31) -#define AI_STATUS_DMA_BUSY (1 << 30) - -/* - * AI DAC sample period register (W): [13:0] dac rate - * - vid_clock/(dperiod + 1) is the DAC sample rate - * - (dperiod + 1) >= 66 * (aclockhp + 1) must be true - */ -#define AI_DACRATE_REG (AI_BASE_REG + 0x10) /* R4: DAC rate 14-lsb*/ - -/* DAC rate = video clock / audio frequency - * - DAC rate >= (66 * Bit rate) must be true - */ -#define AI_MAX_DAC_RATE 16384 /* 14-bit+1 */ -#define AI_MIN_DAC_RATE 132 - -/* - * AI bit rate (W): [3:0] bit rate (abus clock half period register - aclockhp) - * - vid_clock/(2 * (aclockhp + 1)) is the DAC clock rate - * - The abus clock stops if aclockhp is zero - */ -#define AI_BITRATE_REG (AI_BASE_REG + 0x14) /* R5: Bit rate 4-lsb */ - -/* Bit rate <= (DAC rate / 66) */ -#define AI_MAX_BIT_RATE 16 /* 4-bit+1 */ -#define AI_MIN_BIT_RATE 2 - #define CHNL_ERR_NORESP 0x80 /* Bit 7 (Rx): No response error */ #define CHNL_ERR_OVERRUN 0x40 /* Bit 6 (Rx): Overrun error */ #define CHNL_ERR_FRAME 0x80 /* Bit 7 (Tx): Frame error */ @@ -101,11 +110,18 @@ #define CHNL_ERR_MASK 0xC0 /* Bit 6-7: channel errors */ + +/** + * External device info + */ #define DEVICE_TYPE_CART 0 /* ROM cartridge */ #define DEVICE_TYPE_BULK 1 /* ROM bulk */ #define DEVICE_TYPE_64DD 2 /* 64 Disk Drive */ #define DEVICE_TYPE_SRAM 3 /* SRAM */ +/* 4-6 are reserved */ #define DEVICE_TYPE_INIT 7 /* initial value */ +/* 8-14 are reserved */ + /** * Signal Processor (SP) Memory @@ -115,43 +131,74 @@ #define SP_IMEM_START 0x04001000 #define SP_IMEM_END 0x04001FFF -#define SP_MEM_ADDR_REG 0x04040000 -#define SP_DRAM_ADDR_REG 0x04040004 -#define SP_RD_LEN_REG 0x04040008 -#define SP_WR_LEN_REG 0x0404000C -#define SP_STATUS_REG 0x04040010 -#define SP_DMA_FULL_REG 0x04040014 -#define SP_DMA_BUSY_REG 0x04040018 -#define SP_PC_REG 0x04080000 /** + * Signal Processor (SP) CP0 Registers + */ + +#define SP_BASE_REG 0x04040000 + +/* SP memory address (R/W): [12] 0=DMEM,1=IMEM, [11:0] DMEM/IMEM address */ +#define SP_MEM_ADDR_REG (SP_BASE_REG + 0x00) + +/* SP DRAM DMA address (R/W): [23:0] RDRAM address */ +#define SP_DRAM_ADDR_REG (SP_BASE_REG + 0x04) + +/* SP read DMA length (R/W): [31:20] skip, [19:12] count, [11:0] length; RDRAM -> I/DMEM */ +#define SP_RD_LEN_REG (SP_BASE_REG + 0x08) + +/* SP write DMA length (R/W): [31:20] skip, [19:12] count, [11:0] length; I/DMEM -> RDRAM */ +#define SP_WR_LEN_REG (SP_BASE_REG + 0x0C) + +/* SP status (R/W): [14:0] valid bits; see below for write/read mode */ +#define SP_STATUS_REG (SP_BASE_REG + 0x10) + +/* SP DMA full (R): [0] dma full */ +#define SP_DMA_FULL_REG (SP_BASE_REG + 0x14) + +/* SP DMA busy (R): [0] dma busy */ +#define SP_DMA_BUSY_REG (SP_BASE_REG + 0x18) + +/* SP semaphore (R/W): Read: [0] acquire semaphore; Write: [] release semaphore */ +#define SP_SEMAPHORE_REG (SP_BASE_REG + 0x1C) + +/* SP PC (R/W): [11:0] program counter */ +#define SP_PC_REG 0x04080000 + +/* + * SP_MEM_ADDR_REG: bit 12 + */ +#define SP_DMA_DMEM (0 << 12) +#define SP_DMA_IMEM (1 << 12) + +/* * SP_STATUS_REG: write bits */ -#define SP_CLR_HALT (1 << 0) -#define SP_SET_HALT (1 << 1) -#define SP_CLR_BROKE (1 << 2) -#define SP_CLR_INTR (1 << 3) -#define SP_SET_INTR (1 << 4) -#define SP_CLR_SSTEP (1 << 5) -#define SP_SET_SSTEP (1 << 6) -#define SP_CLR_INTR_BREAK (1 << 7) -#define SP_SET_INTR_BREAK (1 << 8) -#define SP_CLR_SIG0 (1 << 9) -#define SP_SET_SIG0 (1 << 10) -#define SP_CLR_SIG1 (1 << 11) -#define SP_SET_SIG1 (1 << 12) -#define SP_CLR_SIG2 (1 << 13) -#define SP_SET_SIG2 (1 << 14) -#define SP_CLR_SIG3 (1 << 15) -#define SP_SET_SIG3 (1 << 16) -#define SP_CLR_SIG4 (1 << 17) -#define SP_SET_SIG4 (1 << 18) -#define SP_CLR_SIG5 (1 << 19) -#define SP_SET_SIG5 (1 << 20) -#define SP_CLR_SIG6 (1 << 21) -#define SP_SET_SIG6 (1 << 22) -#define SP_CLR_SIG7 (1 << 23) -#define SP_SET_SIG7 (1 << 24) +#define SP_CLR_HALT (1 << 0) /* clear halt */ +#define SP_SET_HALT (1 << 1) /* set halt */ +#define SP_CLR_BROKE (1 << 2) /* clear broke */ +#define SP_CLR_INTR (1 << 3) /* clear interrupt */ +#define SP_SET_INTR (1 << 4) /* set interrupt */ +#define SP_CLR_SSTEP (1 << 5) /* clear sstep */ +#define SP_SET_SSTEP (1 << 6) /* set sstep */ +#define SP_CLR_INTR_BREAK (1 << 7) /* clear interrupt on break */ +#define SP_SET_INTR_BREAK (1 << 8) /* set interrupt on break */ +#define SP_CLR_SIG0 (1 << 9) /* clear signal 0 */ +#define SP_SET_SIG0 (1 << 10) /* set signal 0 */ +#define SP_CLR_SIG1 (1 << 11) /* clear signal 1 */ +#define SP_SET_SIG1 (1 << 12) /* set signal 1 */ +#define SP_CLR_SIG2 (1 << 13) /* clear signal 2 */ +#define SP_SET_SIG2 (1 << 14) /* set signal 2 */ +#define SP_CLR_SIG3 (1 << 15) /* clear signal 3 */ +#define SP_SET_SIG3 (1 << 16) /* set signal 3 */ +#define SP_CLR_SIG4 (1 << 17) /* clear signal 4 */ +#define SP_SET_SIG4 (1 << 18) /* set signal 4 */ +#define SP_CLR_SIG5 (1 << 19) /* clear signal 5 */ +#define SP_SET_SIG5 (1 << 20) /* set signal 5 */ +#define SP_CLR_SIG6 (1 << 21) /* clear signal 6 */ +#define SP_SET_SIG6 (1 << 22) /* set signal 6 */ +#define SP_CLR_SIG7 (1 << 23) /* clear signal 7 */ +#define SP_SET_SIG7 (1 << 24) /* set signal 7 */ /* * SP_STATUS_REG: read bits @@ -172,7 +219,7 @@ #define SP_STATUS_SIG6 (1 << 13) #define SP_STATUS_SIG7 (1 << 14) -/** +/* * SP_STATUS_REG: use of SIG bits */ #define SP_CLR_YIELD SP_CLR_SIG0 @@ -191,49 +238,463 @@ #define SP_SET_CPUSIGNAL SP_SET_SIG4 #define SP_STATUS_CPUSIGNAL SP_STATUS_SIG4 -#define VI_STATUS_REG 0x04400000 -#define VI_CONTROL_REG 0x04400000 -#define VI_ORIGIN_REG 0x04400004 -#define VI_DRAM_ADDR_REG 0x04400004 -#define VI_WIDTH_REG 0x04400008 -#define VI_H_WIDTH_REG 0x04400008 -#define VI_INTR_REG 0x0440000C -#define VI_V_INTER_REG 0x0440000C -#define VI_CURRENT_REG 0x04400010 -#define VI_V_CURRENT_LINE_REG 0x04400010 -#define VI_BURST_REG 0x04400014 -#define VI_TIMING_REG 0x04400014 -#define VI_V_SYNC_REG 0x04400018 // VI vertical sync -#define VI_H_SYNC_REG 0x0440001C // VI horizontal sync -#define VI_LEAP_REG 0x04400020 // VI horizontal sync leap -#define VI_H_SYNC_LEAP_REG 0x04400020 -#define VI_H_START_REG 0x04400024 // VI horizontal video -#define VI_H_VIDEO_REG 0x04400024 -#define VI_V_START_REG 0x04400028 // VI vertical video -#define VI_V_VIDEO_REG 0x04400028 -#define VI_V_BURST_REG 0x0440002C // VI vertical burst -#define VI_X_SCALE_REG 0x04400030 // VI x-scale -#define VI_Y_SCALE_REG 0x04400034 // VI y-scale +/* SP IMEM BIST REG (R/W): [6:0] BIST status bits; see below for detail */ +#define SP_IBIST_REG 0x04080004 -#define PI_DRAM_ADDR_REG 0x04600000 // PI DRAM address -#define PI_CART_ADDR_REG 0x04600004 // PI pbus (cartridge) address -#define PI_RD_LEN_REG 0x04600008 // PI read length -#define PI_WR_LEN_REG 0x0460000C // PI write length -#define PI_STATUS_REG 0x04600010 // PI status -#define PI_BSD_DOM1_LAT_REG 0x04600014 // PI dom1 latency -#define PI_DOMAIN1_REG 0x04600014 -#define PI_BSD_DOM1_PWD_REG 0x04600018 // PI dom1 pulse width -#define PI_BSD_DOM1_PGS_REG 0x0460001C // PI dom1 page size -#define PI_BSD_DOM1_RLS_REG 0x04600020 // PI dom1 release -#define PI_BSD_DOM2_LAT_REG 0x04600024 // PI dom2 latency -#define PI_DOMAIN2_REG 0x04600024 -#define PI_BSD_DOM2_PWD_REG 0x04600028 // PI dom2 pulse width -#define PI_BSD_DOM2_PGS_REG 0x0460002C // PI dom2 page size -#define PI_BSD_DOM2_RLS_REG 0x04600030 // PI dom2 release +/* + * SP_IBIST_REG: write bits + */ +#define SP_IBIST_CHECK (1 << 0) /* BIST check */ +#define SP_IBIST_GO (1 << 1) /* BIST go */ +#define SP_IBIST_CLEAR (1 << 2) /* BIST clear */ -#define PI_STATUS_DMA_BUSY (1 << 0) -#define PI_STATUS_IO_BUSY (1 << 1) -#define PI_STATUS_ERROR (1 << 2) +/* + * SP_BIST_REG: read bits + * First 2 bits are same as in write mode + */ +#define SP_IBIST_DONE (1 << 2) +#define SP_IBIST_FAILED 0x78 /* bits [6:3], BIST fail */ + + +/** + * Display Processor Command (DPC) Registers + */ +#define DPC_BASE_REG 0x04100000 + +/* DP CMD DMA start (R/W): [23:0] DMEM/RDRAM start address */ +#define DPC_START_REG (DPC_BASE_REG + 0x00) + +/* DP CMD DMA end (R/W): [23:0] DMEM/RDRAM end address */ +#define DPC_END_REG (DPC_BASE_REG + 0x04) + +/* DP CMD DMA end (R): [23:0] DMEM/RDRAM current address */ +#define DPC_CURRENT_REG (DPC_BASE_REG + 0x08) + +/* DP CMD status (R/W): [9:0] valid bits - see below for definitions */ +#define DPC_STATUS_REG (DPC_BASE_REG + 0x0C) + +/* DP clock counter (R): [23:0] clock counter */ +#define DPC_CLOCK_REG (DPC_BASE_REG + 0x10) + +/* DP buffer busy counter (R): [23:0] clock counter */ +#define DPC_BUFBUSY_REG (DPC_BASE_REG + 0x14) + +/* DP pipe busy counter (R): [23:0] clock counter */ +#define DPC_PIPEBUSY_REG (DPC_BASE_REG + 0x18) + +/* DP TMEM load counter (R): [23:0] clock counter */ +#define DPC_TMEM_REG (DPC_BASE_REG + 0x1C) + +/* + * DPC_STATUS_REG: write bits + */ +#define DPC_CLR_XBUS_DMEM_DMA (1 << 0) +#define DPC_SET_XBUS_DMEM_DMA (1 << 1) +#define DPC_CLR_FREEZE (1 << 2) +#define DPC_SET_FREEZE (1 << 3) +#define DPC_CLR_FLUSH (1 << 4) +#define DPC_SET_FLUSH (1 << 5) +#define DPC_CLR_TMEM_CTR (1 << 6) +#define DPC_CLR_PIPE_CTR (1 << 7) +#define DPC_CLR_CMD_CTR (1 << 8) +#define DPC_CLR_CLOCK_CTR (1 << 9) + +/* + * DPC_STATUS_REG: read bits + */ +#define DPC_STATUS_XBUS_DMEM_DMA (1 << 0) +#define DPC_STATUS_FREEZE (1 << 1) +#define DPC_STATUS_FLUSH (1 << 2) +#define DPC_STATUS_START_GCLK (1 << 3) +#define DPC_STATUS_TMEM_BUSY (1 << 4) +#define DPC_STATUS_PIPE_BUSY (1 << 5) +#define DPC_STATUS_CMD_BUSY (1 << 6) +#define DPC_STATUS_CBUF_READY (1 << 7) +#define DPC_STATUS_DMA_BUSY (1 << 8) +#define DPC_STATUS_END_VALID (1 << 9) +#define DPC_STATUS_START_VALID (1 << 10) + + +/** + * Display Processor Span (DPS) Registers + */ +#define DPS_BASE_REG 0x04200000 + +/* DP tmem built-in self-test (R/W): [10:0] BIST status bits */ +#define DPS_TBIST_REG (DPS_BASE_REG + 0x00) + +/* DP span test mode (R/W): [0] Span buffer test access enable */ +#define DPS_TEST_MODE_REG (DPS_BASE_REG + 0x04) + +/* DP span buffer test address (R/W): [6:0] bits */ +#define DPS_BUFTEST_ADDR_REG (DPS_BASE_REG + 0x08) + +/* DP span buffer test data (R/W): [31:0] span buffer data */ +#define DPS_BUFTEST_DATA_REG (DPS_BASE_REG + 0x0C) + +/* + * DPS_TMEM_BIST_REG: write bits + */ +#define DPS_TBIST_CHECK (1 << 0) +#define DPS_TBIST_GO (1 << 1) +#define DPS_TBIST_CLEAR (1 << 2) + +/* + * DPS_TMEM_BIST_REG: read bits + * First 2 bits are same as in write mode + */ +#define DPS_TBIST_DONE (1 << 2) +#define DPS_TBIST_FAILED 0x7F8 /* bits [10:3], BIST fail */ + + +/** + * MIPS Interface (MI) Registers + */ +#define MI_BASE_REG 0x04300000 + +/* MI init mode (W): [11] clear DP interrupt, [9/10] clear/set ebus test mode */ +/* [8] set init mode, [7] clear init mode, [6:0] init length */ +/* (R): [8] ebus test mode, [7] init mode, [6:0] init length */ +#define MI_INIT_MODE_REG (MI_BASE_REG + 0x00) +#define MI_MODE_REG MI_INIT_MODE_REG + +/* + * MI_MODE_REG: write bits + */ +#define MI_CLR_INIT (1 << 7) /* clear init mode */ +#define MI_SET_INIT (1 << 8) /* set init mode */ +#define MI_CLR_EBUS (1 << 9) /* clear ebus test */ +#define MI_SET_EBUS (1 << 10) /* set ebus test mode */ +#define MI_CLR_DP_INTR (1 << 11) /* clear dp interrupt */ +#define MI_CLR_RDRAM (1 << 12) /* clear RDRAM reg */ +#define MI_SET_RDRAM (1 << 13) /* set RDRAM reg mode */ + +/* + * MI_MODE_REG: read bits + */ +#define MI_MODE_INIT (1 << 7) /* init mode */ +#define MI_MODE_EBUS (1 << 8) /* ebus test mode */ +#define MI_MODE_RDRAM (1 << 9) /* RDRAM reg mode */ + +/* MI version (R): [31:24] rsp, [23:16] rdp, [15:8] rac, [7:0] io */ +#define MI_VERSION_REG (MI_BASE_REG + 0x04) +#define MI_NOOP_REG MI_VERSION_REG + +/* MI interrupt (R): [5:0] valid bits - see below for bit patterns */ +#define MI_INTR_REG (MI_BASE_REG + 0x08) + +/* MI interrupt mask (R): [5:0] valid bits - see below for bit patterns */ +/* (W): [11:0] valid bits - see below for bit patterns */ +#define MI_INTR_MASK_REG (MI_BASE_REG + 0x0C) + +/* + * MI_INTR_REG: read bits + */ +#define MI_INTR_SP (1 << 0) /* SP intr */ +#define MI_INTR_SI (1 << 1) /* SI intr */ +#define MI_INTR_AI (1 << 2) /* AI intr */ +#define MI_INTR_VI (1 << 3) /* VI intr */ +#define MI_INTR_PI (1 << 4) /* PI intr */ +#define MI_INTR_DP (1 << 5) /* DP intr */ + +/* + * MI_INTR_MASK_REG: write bits + */ +#define MI_INTR_MASK_CLR_SP (1 << 0) /* clear SP mask */ +#define MI_INTR_MASK_SET_SP (1 << 1) /* set SP mask */ +#define MI_INTR_MASK_CLR_SI (1 << 2) /* clear SI mask */ +#define MI_INTR_MASK_SET_SI (1 << 3) /* set SI mask */ +#define MI_INTR_MASK_CLR_AI (1 << 4) /* clear AI mask */ +#define MI_INTR_MASK_SET_AI (1 << 5) /* set AI mask */ +#define MI_INTR_MASK_CLR_VI (1 << 6) /* clear VI mask */ +#define MI_INTR_MASK_SET_VI (1 << 7) /* set VI mask */ +#define MI_INTR_MASK_CLR_PI (1 << 8) /* clear PI mask */ +#define MI_INTR_MASK_SET_PI (1 << 9) /* set PI mask */ +#define MI_INTR_MASK_CLR_DP (1 << 10) /* clear DP mask */ +#define MI_INTR_MASK_SET_DP (1 << 11) /* set DP mask */ + +/* + * MI_INTR_MASK_REG: read bits + */ +#define MI_INTR_MASK_SP (1 << 0) /* SP intr mask */ +#define MI_INTR_MASK_SI (1 << 1) /* SI intr mask */ +#define MI_INTR_MASK_AI (1 << 2) /* AI intr mask */ +#define MI_INTR_MASK_VI (1 << 3) /* VI intr mask */ +#define MI_INTR_MASK_PI (1 << 4) /* PI intr mask */ +#define MI_INTR_MASK_DP (1 << 5) /* DP intr mask */ + + +/** + * Video Interface (VI) Registers + */ +#define VI_BASE_REG 0x04400000 + +/* + * VI status/control (R/W): [15-0] valid bits: + * [1:0] = type[1:0] (pixel size) + * 0: blank (no data, no sync) + * 1: reserved + * 2: 5/5/5/3 ("16" bit) + * 3: 8/8/8/8 (32 bit) + * [2] = gamma_dither_enable (normally on, unless "special effect") + * [3] = gamma_enable (normally on, unless MPEG/JPEG) + * [4] = divot_enable (normally on if antialiased, unless decal lines) + * [5] = vbus_clock_enable - always off + * [6] = serrate (always on if interlaced, off if not) + * [7] = test_mode - diagnostics only + * [9:8] = anti-alias (aa) mode[1:0] + * 0: aa & resamp (always fetch extra lines) + * 1: aa & resamp (fetch extra lines if needed) + * 2: resamp only (treat as all fully covered) + * 3: neither (replicate pixels, no interpolate) + * [11] = kill_we - diagnostics only + * [15:12] = pixel_advance + * [16] = dither_filter_enable + */ +#define VI_CONTROL_REG (VI_BASE_REG + 0x00) +#define VI_STATUS_REG VI_CONTROL_REG + +/* VI origin (R/W): [23:0] frame buffer origin in bytes */ +#define VI_ORIGIN_REG (VI_BASE_REG + 0x04) +#define VI_DRAM_ADDR_REG VI_ORIGIN_REG + +/* VI width (R/W): [11:0] frame buffer line width in pixels */ +#define VI_WIDTH_REG (VI_BASE_REG + 0x08) +#define VI_H_WIDTH_REG VI_WIDTH_REG + +/* VI vertical intr (R/W): [9:0] interrupt when current half-line = V_INTR */ +#define VI_INTR_REG (VI_BASE_REG + 0x0C) +#define VI_V_INTR_REG VI_INTR_REG + +/* VI current vertical line (R/W): [9:0] current half line, sampled once per */ +/* line (the lsb of V_CURRENT is constant within a field, and in interlaced */ +/* modes gives the field number - which is constant for non-interlaced modes) */ +/* - Any write to this register will clear interrupt line */ +#define VI_CURRENT_REG (VI_BASE_REG + 0x10) +#define VI_V_CURRENT_LINE_REG VI_CURRENT_REG + +/* VI video timing (R/W): [29:20] start of color burst in pixels from h-sync */ +/* [19:16] vertical sync width in half lines, */ +/* [15: 8] color burst width in pixels, */ +/* [ 7: 0] horizontal sync width in pixels, */ +#define VI_BURST_REG (VI_BASE_REG + 0x14) +#define VI_TIMING_REG VI_BURST_REG + +/* VI vertical sync (R/W): [9:0] number of half-lines per field */ +#define VI_V_SYNC_REG (VI_BASE_REG + 0x18) + +/* VI horizontal sync (R/W): [20:16] a 5-bit leap pattern used for PAL only (h_sync_period) */ +/* [11: 0] total duration of a line in 1/4 pixel */ +#define VI_H_SYNC_REG (VI_BASE_REG + 0x1C) + +/* VI horizontal sync leap (R/W): [27:16] identical to h_sync_period */ +/* [11: 0] identical to h_sync_period */ +#define VI_LEAP_REG (VI_BASE_REG + 0x20) +#define VI_H_SYNC_LEAP_REG VI_LEAP_REG + +/* VI horizontal video (R/W): [25:16] start of active video in screen pixels */ +/* [ 9: 0] end of active video in screen pixels */ +#define VI_H_START_REG (VI_BASE_REG + 0x24) +#define VI_H_VIDEO_REG VI_H_START_REG + +/* VI vertical video (R/W): [25:16] start of active video in screen half-lines */ +/* [ 9: 0] end of active video in screen half-lines */ +#define VI_V_START_REG (VI_BASE_REG + 0x28) +#define VI_V_VIDEO_REG VI_V_START_REG + +/* VI vertical burst (R/W): [25:16] start of color burst enable in half-lines */ +/* [ 9: 0] end of color burst enable in half-lines */ +#define VI_V_BURST_REG (VI_BASE_REG + 0x2C) + +/* VI x-scale (R/W): [27:16] horizontal subpixel offset (2.10 format) */ +/* [11: 0] 1/horizontal scale up factor (2.10 format) */ +#define VI_X_SCALE_REG (VI_BASE_REG + 0x30) + +/* VI y-scale (R/W): [27:16] vertical subpixel offset (2.10 format) */ +/* [11: 0] 1/vertical scale up factor (2.10 format) */ +#define VI_Y_SCALE_REG (VI_BASE_REG + 0x34) + +/* + * VI_CONTROL_REG: read bits + */ +#define VI_CTRL_TYPE_16 0x00002 /* [1:0] pixel size: 16 bit */ +#define VI_CTRL_TYPE_32 0x00003 /* [1:0] pixel size: 32 bit */ +#define VI_CTRL_GAMMA_DITHER_ON 0x00004 /* 2: default = on */ +#define VI_CTRL_GAMMA_ON 0x00008 /* 3: default = on */ +#define VI_CTRL_DIVOT_ON 0x00010 /* 4: default = on */ +#define VI_CTRL_SERRATE_ON 0x00040 /* 6: on if interlaced */ +#define VI_CTRL_ANTIALIAS_MASK 0x00300 /* [9:8] anti-alias mode */ +#define VI_CTRL_ANTIALIAS_MODE_0 0x00000 /* Bit [9:8] anti-alias mode: AA enabled, resampling enabled, always fetch extra lines */ +#define VI_CTRL_ANTIALIAS_MODE_1 0x00100 /* Bit [9:8] anti-alias mode: AA enabled, resampling enabled, fetch extra lines as-needed */ +#define VI_CTRL_ANTIALIAS_MODE_2 0x00200 /* Bit [9:8] anti-alias mode: AA disabled, resampling enabled, operate as if everything is covered */ +#define VI_CTRL_ANTIALIAS_MODE_3 0x00300 /* Bit [9:8] anti-alias mode: AA disabled, resampling disabled, replicate pixels */ +#define VI_CTRL_PIXEL_ADV_MASK 0x0F000 /* [15:12] pixel advance mode */ +#define VI_CTRL_PIXEL_ADV(n) (((n) << 12) & VI_CTRL_PIXEL_ADV_MASK) /* Bit [15:12] pixel advance mode: Always 3 on N64 */ +#define VI_CTRL_DITHER_FILTER_ON 0x10000 /* 16: dither-filter mode */ + +/* + * Possible video clocks (NTSC or PAL) + */ +#define VI_NTSC_CLOCK 48681812 /* Hz = 48.681812 MHz */ +#define VI_PAL_CLOCK 49656530 /* Hz = 49.656530 MHz */ +#define VI_MPAL_CLOCK 48628316 /* Hz = 48.628316 MHz */ + + +/** + * Audio Interface (AI) Registers + * + * The address and length registers are double buffered; that is, they + * can be written twice before becoming full. + * The address must be written before the length. + */ +#define AI_BASE_REG 0x04500000 + +/* AI DRAM address (W): [23:0] starting RDRAM address (8B-aligned) */ +#define AI_DRAM_ADDR_REG (AI_BASE_REG + 0x00) + +/* AI length (R/W): [14:0] transfer length (v1.0) - Bottom 3 bits are ignored */ +/* [17:0] transfer length (v2.0) - Bottom 3 bits are ignored */ +#define AI_LEN_REG (AI_BASE_REG + 0x04) + +/* AI control (W): [0] DMA enable - if LSB == 1, DMA is enabled */ +#define AI_CONTROL_REG (AI_BASE_REG + 0x08) + +/* + * AI_CONTROL_REG: write bits + */ +#define AI_CONTROL_DMA_ON 1 /* LSB = 1: DMA enable */ +#define AI_CONTROL_DMA_OFF 0 /* LSB = 1: DMA enable */ + +/* AI status (R): [31]/[0] ai_full (addr & len buffer full), [30] ai_busy */ +/* Note that a 1->0 transition in ai_full will set interrupt */ +/* (W): clear audio interrupt */ +#define AI_STATUS_REG (AI_BASE_REG + 0x0C) + +/* + * AI_STATUS_REG: read bits + */ +#define AI_STATUS_FIFO_FULL (1 << 31) +#define AI_STATUS_DMA_BUSY (1 << 30) + +/* AI DAC sample period register (W): [13:0] dac rate */ +/* - vid_clock/(dperiod + 1) is the DAC sample rate */ +/* - (dperiod + 1) >= 66 * (aclockhp + 1) must be true */ +#define AI_DACRATE_REG (AI_BASE_REG + 0x10) + +/* DAC rate = video clock / audio frequency */ +/* - DAC rate >= (66 * Bit rate) must be true */ +#define AI_MAX_DAC_RATE 16384 /* 14-bit+1 */ +#define AI_MIN_DAC_RATE 132 + +/* AI bit rate (W): [3:0] bit rate (abus clock half period register - aclockhp) */ +/* - vid_clock/(2 * (aclockhp + 1)) is the DAC clock rate */ +/* - The abus clock stops if aclockhp is zero */ +#define AI_BITRATE_REG (AI_BASE_REG + 0x14) + +/* Bit rate <= (DAC rate / 66) */ +#define AI_MAX_BIT_RATE 16 /* 4-bit+1 */ +#define AI_MIN_BIT_RATE 2 + +/* + * Maximum and minimum values for audio frequency based on video clocks + * max frequency = (video clock / min dac rate) + * min frequency = (video clock / max dac rate) + */ +#define AI_NTSC_MAX_FREQ 368000 /* 368 KHz */ +#define AI_NTSC_MIN_FREQ 3000 /* 3 KHz ~ 2971 Hz */ + +#define AI_PAL_MAX_FREQ 376000 /* 376 KHz */ +#define AI_PAL_MIN_FREQ 3050 /* 3 KHz ~ 3031 Hz */ + +#define AI_MPAL_MAX_FREQ 368000 /* 368 KHz */ +#define AI_MPAL_MIN_FREQ 3000 /* 3 KHz ~ 2968 Hz */ + + +/** + * Peripheral Interface (PI) Registers + */ +#define PI_BASE_REG 0x04600000 + +/* PI DRAM address (R/W): [23:0] starting RDRAM address */ +#define PI_DRAM_ADDR_REG (PI_BASE_REG + 0x00) + +/* PI pbus (cartridge) address (R/W): [31:0] starting AD16 address */ +#define PI_CART_ADDR_REG (PI_BASE_REG + 0x04) + +/* PI read length (R/W): [23:0] read data length */ +#define PI_RD_LEN_REG (PI_BASE_REG + 0x08) + +/* PI write length (R/W): [23:0] write data length */ +#define PI_WR_LEN_REG (PI_BASE_REG + 0x0C) + +/* PI status (R): [3] interrupt flag, [2] error, [1] IO busy, [0] DMA busy */ +/* (W): [1] clear intr, [0] reset controller (and abort current op) */ +#define PI_STATUS_REG (PI_BASE_REG + 0x10) + +/* PI dom1 latency (R/W): [7:0] domain 1 device latency */ +#define PI_BSD_DOM1_LAT_REG (PI_BASE_REG + 0x14) + +/* PI dom1 pulse width (R/W): [7:0] domain 1 device R/W strobe pulse width */ +#define PI_BSD_DOM1_PWD_REG (PI_BASE_REG + 0x18) + +/* PI dom1 page size (R/W): [3:0] domain 1 device page size */ +#define PI_BSD_DOM1_PGS_REG (PI_BASE_REG + 0x1C) + +/* PI dom1 release (R/W): [1:0] domain 1 device R/W release duration */ +#define PI_BSD_DOM1_RLS_REG (PI_BASE_REG + 0x20) + +/* PI dom2 latency (R/W): [7:0] domain 2 device latency */ +#define PI_BSD_DOM2_LAT_REG (PI_BASE_REG + 0x24) + +/* PI dom2 pulse width (R/W): [7:0] domain 2 device R/W strobe pulse width */ +#define PI_BSD_DOM2_PWD_REG (PI_BASE_REG + 0x28) + +/* PI dom2 page size (R/W): [3:0] domain 2 device page size */ +#define PI_BSD_DOM2_PGS_REG (PI_BASE_REG + 0x2C) + +/* PI dom2 release (R/W): [1:0] domain 2 device R/W release duration */ +#define PI_BSD_DOM2_RLS_REG (PI_BASE_REG + 0x30) + +#define PI_DOMAIN1_REG PI_BSD_DOM1_LAT_REG +#define PI_DOMAIN2_REG PI_BSD_DOM2_LAT_REG + +#define PI_DOM_LAT_OFS 0x00 +#define PI_DOM_PWD_OFS 0x04 +#define PI_DOM_PGS_OFS 0x08 +#define PI_DOM_RLS_OFS 0x0C + +/* + * PI_STATUS_REG: read bits + * Bit 0: DMA busy - set when DMA is in progress + * Bit 1: IO busy - set when IO is in progress + * Bit 2: Error - set when CPU issues IO request while DMA is busy + */ +#define PI_STATUS_DMA_BUSY (1 << 0) +#define PI_STATUS_IO_BUSY (1 << 1) +#define PI_STATUS_ERROR (1 << 2) + +/* + * PI status register has 2 bits active when written to: + * Bit 0: When set, reset PIC + * Bit 1: When set, clear interrupt flag + * The values of the two bits can be ORed together to both reset PIC and + * clear interrupt at the same time. + * + * Note: + * - The PIC does generate an interrupt at the end of each DMA. CPU + * needs to clear the interrupt flag explicitly (from an interrupt + * handler) by writing into the STATUS register with bit 1 set. + * + * - When a DMA completes, the interrupt flag is set. CPU can issue + * another request even while the interrupt flag is set (as long as + * PIC is idle). However, it is the CPU's responsibility for + * maintaining accurate correspondence between DMA completions and + * interrupts. + * + * - When PIC is reset, if PIC happens to be busy, an interrupt will + * be generated as PIC returns to idle. Otherwise, no interrupt will + * be generated and PIC remains idle. + */ /* * PI_STATUS_REG: write bits @@ -252,22 +713,99 @@ #define PI_DOM2_ADDR1 0x05000000 /* to 0x05FFFFFF */ #define PI_DOM2_ADDR2 0x08000000 /* to 0x0FFFFFFF */ + +/** + * RDRAM Interface (RI) Registers + */ +#define RI_BASE_REG 0x04700000 + +/* RI mode (R/W): [3] stop R active, [2] stop T active, [1:0] operating mode */ +#define RI_MODE_REG (RI_BASE_REG + 0x00) + +/* RI config (R/W): [6] current control enable, [5:0] current control input */ +#define RI_CONFIG_REG (RI_BASE_REG + 0x04) + +/* RI current load (W): [] any write updates current control register */ +#define RI_CURRENT_LOAD_REG (RI_BASE_REG + 0x08) + +/* RI select (R/W): [3:2] receive select, [1:0] transmit select */ +#define RI_SELECT_REG (RI_BASE_REG + 0x0C) + +/* RI refresh (R/W): [16] refresh bank, [17] refresh enable, [18] refresh optimize */ +/* [7:0] clean refresh delay, [15:8] dirty refresh dela */ +#define RI_REFRESH_REG (RI_BASE_REG + 0x10) +#define RI_COUNT_REG RI_REFRESH_REG + +/* RI latency (R/W): [3:0] DMA latency/overlap */ +#define RI_LATENCY_REG (RI_BASE_REG + 0x14) + +/* RI error (R): [1] ack error, [0] nack error */ +#define RI_RERROR_REG (RI_BASE_REG + 0x18) + +/* RI error (W): [] any write clears all error bits */ +#define RI_WERROR_REG (RI_BASE_REG + 0x1C) + + /** * Serial Interface (SI) Registers */ #define SI_BASE_REG 0x04800000 +/* SI DRAM address (R/W): [23:0] starting RDRAM address */ #define SI_DRAM_ADDR_REG (SI_BASE_REG + 0x00) + +/* SI address read 64B (W): [] write begins a 64B DMA write PIF RAM -> RDRAM */ #define SI_PIF_ADDR_RD64B_REG (SI_BASE_REG + 0x04) + +/* Address SI_BASE_REG + (0x08, 0x0C, 0x14) are reserved */ + +/* SI address write 64B (W): [] write begins a 64B DMA read RDRAM -> PIF RAM */ #define SI_PIF_ADDR_WR64B_REG (SI_BASE_REG + 0x10) + +/* SI status (R/W): [] any write clears interrupt */ #define SI_STATUS_REG (SI_BASE_REG + 0x18) -#define SI_STATUS_DMA_BUSY (1 << 0) -#define SI_STATUS_IO_READ_BUSY (1 << 1) -#define SI_STATUS_DMA_ERROR (1 << 3) -#define SI_STATUS_INTERRUPT (1 << 12) +/* + * SI_STATUS_REG: read bits + */ +#define SI_STATUS_DMA_BUSY (1 << 0) /* DMA in progress */ +#define SI_STATUS_RD_BUSY (1 << 1) /* IO access in progress */ +#define SI_STATUS_DMA_ERROR (1 << 3) /* Overlapping DMA requests */ +#define SI_STATUS_INTERRUPT (1 << 12) /* Interrupt is set */ + + +/** + * Development Board GIO Control Registers + */ + +#define GIO_BASE_REG 0x18000000 + +/* Game to Host Interrupt */ +#define GIO_GIO_INTR_REG (GIO_BASE_REG+0x000) + +/* Game to Host SYNC */ +#define GIO_GIO_SYNC_REG (GIO_BASE_REG+0x400) + +/* Host to Game Interrupt */ +#define GIO_CART_INTR_REG (GIO_BASE_REG+0x800) + + +/** + * Common macros + */ +#if defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) #define IO_READ(addr) (*(vu32*)PHYS_TO_K1(addr)) #define IO_WRITE(addr,data) (*(vu32*)PHYS_TO_K1(addr)=(u32)(data)) +#define RCP_STAT_PRINT \ + rmonPrintf("current=%x start=%x end=%x dpstat=%x spstat=%x\n", \ + IO_READ(DPC_CURRENT_REG), \ + IO_READ(DPC_START_REG), \ + IO_READ(DPC_END_REG), \ + IO_READ(DPC_STATUS_REG), \ + IO_READ(SP_STATUS_REG)) + +#endif + #endif diff --git a/include/PR/ultratypes.h b/include/PR/ultratypes.h index 0e48d50dce..76f5e74ea4 100644 --- a/include/PR/ultratypes.h +++ b/include/PR/ultratypes.h @@ -1,6 +1,8 @@ #ifndef PR_ULTRATYPES_H #define PR_ULTRATYPES_H +#ifdef _LANGUAGE_C + typedef signed char s8; typedef unsigned char u8; typedef signed short int s16; @@ -39,3 +41,5 @@ typedef unsigned int size_t; typedef void* TexturePtr; #endif + +#endif diff --git a/include/PR/version.h b/include/PR/version.h new file mode 100644 index 0000000000..5aa8c3c9ce --- /dev/null +++ b/include/PR/version.h @@ -0,0 +1,14 @@ +#ifndef PR_VERSION_H +#define PR_VERSION_H + +#define LIBULTRA_VERSION_D 'D' +#define LIBULTRA_VERSION_E 'E' +#define LIBULTRA_VERSION_F 'F' +#define LIBULTRA_VERSION_G 'G' +#define LIBULTRA_VERSION_H 'H' +#define LIBULTRA_VERSION_I 'I' +#define LIBULTRA_VERSION_J 'J' +#define LIBULTRA_VERSION_K 'K' +#define LIBULTRA_VERSION_L 'L' + +#endif diff --git a/spec/spec b/spec/spec index 0982b58746..95ca2aec9a 100644 --- a/spec/spec +++ b/spec/spec @@ -56,9 +56,8 @@ beginseg include "$(BUILD_DIR)/src/libultra/io/viextendvstart.o" include "$(BUILD_DIR)/src/libultra/os/stopthread.o" include "$(BUILD_DIR)/src/libultra/os/recvmesg.o" - include "$(BUILD_DIR)/asm/boot/setintmask.text.o" - include "$(BUILD_DIR)/data/boot/setintmask.rodata.o" - include "$(BUILD_DIR)/asm/boot/getintmask.text.o" + include "$(BUILD_DIR)/src/libultra/os/setintmask.o" + include "$(BUILD_DIR)/src/libultra/os/getintmask.o" include "$(BUILD_DIR)/src/libultra/voice/voicesetword.o" include "$(BUILD_DIR)/src/libultra/vimodes/vimodentschpf1.o" include "$(BUILD_DIR)/src/libultra/vimodes/vimodepallan1.o" @@ -67,9 +66,7 @@ beginseg include "$(BUILD_DIR)/src/libultra/gu/sins.o" include "$(BUILD_DIR)/src/libultra/io/sptask.o" include "$(BUILD_DIR)/src/libultra/libc/ll.o" - include "$(BUILD_DIR)/asm/boot/exceptasm.text.o" - include "$(BUILD_DIR)/data/boot/exceptasm.data.o" - include "$(BUILD_DIR)/data/boot/exceptasm.rodata.o" + include "$(BUILD_DIR)/src/libultra/os/exceptasm.o" include "$(BUILD_DIR)/src/libultra/os/thread.o" include "$(BUILD_DIR)/src/libultra/os/destroythread.o" include "$(BUILD_DIR)/src/libultra/voice/voicecheckresult.o" diff --git a/src/libultra/os/exceptasm.s b/src/libultra/os/exceptasm.s new file mode 100644 index 0000000000..d6f4e1a03d --- /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 0000000000..f5e7cf7704 --- /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 0000000000..68a96bc8b3 --- /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)