mirror of https://github.com/zeldaret/mm.git
				
				
				
			Handwritten asm: exceptasm, get/setintmask
This commit is contained in:
		
							parent
							
								
									ce93b71b95
								
							
						
					
					
						commit
						79fb045379
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										848
									
								
								include/PR/rcp.h
								
								
								
								
							
							
						
						
									
										848
									
								
								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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  | @ -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" | ||||
|  |  | |||
|  | @ -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) | ||||
|  | @ -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) | ||||
|  | @ -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) | ||||
		Loading…
	
		Reference in New Issue
	
	 Tharo
						Tharo