summaryrefslogtreecommitdiff
path: root/src/Dolphin/os
diff options
context:
space:
mode:
authormrb0nk500 <b0nk@b0nk.xyz>2023-02-01 18:45:02 -0400
committermrb0nk500 <b0nk@b0nk.xyz>2023-02-01 18:50:25 -0400
commit9fa0a7f1da1b70bee995f53c6c96c43189018772 (patch)
tree114548896790eaff23cdca84a025281de86bbb51 /src/Dolphin/os
parent2ba3289286bbfcf9fcc13fd135d976058d8b6c2e (diff)
global: Import Dolphin SDK
This version comes from the Metroid Prime decompilation project. https://github.com/PrimeDecomp/prime
Diffstat (limited to 'src/Dolphin/os')
-rw-r--r--src/Dolphin/os/OS.c609
-rw-r--r--src/Dolphin/os/OSAlarm.c183
-rw-r--r--src/Dolphin/os/OSAlloc.c195
-rw-r--r--src/Dolphin/os/OSArena.c39
-rw-r--r--src/Dolphin/os/OSAudioSystem.c115
-rw-r--r--src/Dolphin/os/OSCache.c426
-rw-r--r--src/Dolphin/os/OSContext.c550
-rw-r--r--src/Dolphin/os/OSError.c360
-rw-r--r--src/Dolphin/os/OSFont.c757
-rw-r--r--src/Dolphin/os/OSInterrupt.c433
-rw-r--r--src/Dolphin/os/OSLink.c556
-rw-r--r--src/Dolphin/os/OSMemory.c247
-rw-r--r--src/Dolphin/os/OSMessage.c86
-rw-r--r--src/Dolphin/os/OSMutex.c223
-rw-r--r--src/Dolphin/os/OSReboot.c166
-rw-r--r--src/Dolphin/os/OSReset.c189
-rw-r--r--src/Dolphin/os/OSResetSW.c281
-rw-r--r--src/Dolphin/os/OSRtc.c544
-rw-r--r--src/Dolphin/os/OSSync.c29
-rw-r--r--src/Dolphin/os/OSThread.c852
-rw-r--r--src/Dolphin/os/OSTime.c137
-rw-r--r--src/Dolphin/os/__ppc_eabi_init.cpp70
-rw-r--r--src/Dolphin/os/__start.c165
23 files changed, 7079 insertions, 133 deletions
diff --git a/src/Dolphin/os/OS.c b/src/Dolphin/os/OS.c
new file mode 100644
index 0000000..f902d5b
--- /dev/null
+++ b/src/Dolphin/os/OS.c
@@ -0,0 +1,609 @@
+#include "dolphin/os.h"
+#include "dolphin/DVDPriv.h"
+#include "dolphin/db.h"
+#include "dolphin/os/OSBootInfo.h"
+
+extern OSTime __OSGetSystemTime();
+extern char _db_stack_end[];
+
+#define OS_BI2_DEBUG_ADDRESS 0x800000F4
+#define DEBUGFLAG_ADDR 0x800030E8
+#define OS_DEBUG_ADDRESS_2 0x800030E9
+#define OS_CURRENTCONTEXT_PADDR 0x00C0
+
+extern char* __OSResetSWInterruptHandler[];
+
+vu16 __OSDeviceCode : (OS_BASE_CACHED | 0x30E6);
+static DVDDriveInfo DriveInfo ATTRIBUTE_ALIGN(32);
+static DVDCommandBlock DriveBlock;
+
+static OSBootInfo* BootInfo;
+static u32* BI2DebugFlag;
+static u32* BI2DebugFlagHolder;
+__declspec(weak) BOOL __OSIsGcam = FALSE;
+static f64 ZeroF;
+static f32 ZeroPS[2];
+static BOOL AreWeInitialized = FALSE;
+static __OSExceptionHandler* OSExceptionTable;
+OSTime __OSStartTime;
+BOOL __OSInIPL;
+
+extern u8 __ArenaHi[];
+extern u8 __ArenaLo[];
+extern u32 __DVDLongFileNameFlag;
+extern u32 __PADSpec;
+
+#define OS_EXCEPTIONTABLE_ADDR 0x3000
+#define OS_DBJUMPPOINT_ADDR 0x60
+// memory locations for important stuff
+#define OS_CACHED_REGION_PREFIX 0x8000
+#define OS_BI2_DEBUG_ADDRESS 0x800000F4
+#define OS_BI2_DEBUGFLAG_OFFSET 0xC
+#define PAD3_BUTTON_ADDR 0x800030E4
+#define OS_DVD_DEVICECODE 0x800030E6
+#define DEBUGFLAG_ADDR 0x800030E8
+#define OS_DEBUG_ADDRESS_2 0x800030E9
+#define DB_EXCEPTIONRET_OFFSET 0xC
+#define DB_EXCEPTIONDEST_OFFSET 0x8
+#define MSR_RI_BIT 0x1E
+
+void OSDefaultExceptionHandler(__OSException exception, OSContext* context);
+extern BOOL __DBIsExceptionMarked(__OSException);
+static void OSExceptionInit(void);
+
+/* clang-format off */
+asm void __OSFPRInit(void)
+{
+ nofralloc
+
+ mfmsr r3
+ ori r3, r3, 0x2000
+ mtmsr r3
+
+ mfspr r3, 0x398
+ rlwinm. r3, r3, 3, 31, 31
+ beq SkipPairedSingles
+
+ lis r3, ZeroPS@ha
+ addi r3, r3, ZeroPS@l
+ psq_l fp0, 0(r3), 0, 0
+ ps_mr fp1, fp0
+ ps_mr fp2, fp0
+ ps_mr fp3, fp0
+ ps_mr fp4, fp0
+ ps_mr fp5, fp0
+ ps_mr fp6, fp0
+ ps_mr fp7, fp0
+ ps_mr fp8, fp0
+ ps_mr fp9, fp0
+ ps_mr fp10, fp0
+ ps_mr fp11, fp0
+ ps_mr fp12, fp0
+ ps_mr fp13, fp0
+ ps_mr fp14, fp0
+ ps_mr fp15, fp0
+ ps_mr fp16, fp0
+ ps_mr fp17, fp0
+ ps_mr fp18, fp0
+ ps_mr fp19, fp0
+ ps_mr fp20, fp0
+ ps_mr fp21, fp0
+ ps_mr fp22, fp0
+ ps_mr fp23, fp0
+ ps_mr fp24, fp0
+ ps_mr fp25, fp0
+ ps_mr fp26, fp0
+ ps_mr fp27, fp0
+ ps_mr fp28, fp0
+ ps_mr fp29, fp0
+ ps_mr fp30, fp0
+ ps_mr fp31, fp0
+
+SkipPairedSingles:
+ lfd fp0, ZeroF
+ fmr fp1, fp0
+ fmr fp2, fp0
+ fmr fp3, fp0
+ fmr fp4, fp0
+ fmr fp5, fp0
+ fmr fp6, fp0
+ fmr fp7, fp0
+ fmr fp8, fp0
+ fmr fp9, fp0
+ fmr fp10, fp0
+ fmr fp11, fp0
+ fmr fp12, fp0
+ fmr fp13, fp0
+ fmr fp14, fp0
+ fmr fp15, fp0
+ fmr fp16, fp0
+ fmr fp17, fp0
+ fmr fp18, fp0
+ fmr fp19, fp0
+ fmr fp20, fp0
+ fmr fp21, fp0
+ fmr fp22, fp0
+ fmr fp23, fp0
+ fmr fp24, fp0
+ fmr fp25, fp0
+ fmr fp26, fp0
+ fmr fp27, fp0
+ fmr fp28, fp0
+ fmr fp29, fp0
+ fmr fp30, fp0
+ fmr fp31, fp0
+
+ mtfsf 0xFF, fp0
+
+ blr
+}
+/* clang-format on */
+
+u32 OSGetConsoleType() {
+ if (BootInfo == NULL || BootInfo->consoleType == 0) {
+ return OS_CONSOLE_ARTHUR;
+ }
+ return BootInfo->consoleType;
+}
+
+void* __OSSavedRegionStart;
+void* __OSSavedRegionEnd;
+
+extern u32 BOOT_REGION_START : 0x812FDFF0; //(*(u32 *)0x812fdff0)
+extern u32 BOOT_REGION_END : 0x812FDFEC; //(*(u32 *)0x812fdfec)
+
+void ClearArena(void) {
+ if ((u32)(OSGetResetCode() + 0x80000000) != 0U) {
+ __OSSavedRegionStart = 0U;
+ __OSSavedRegionEnd = 0U;
+ memset(OSGetArenaLo(), 0U, (u32)OSGetArenaHi() - (u32)OSGetArenaLo());
+ return;
+ }
+ __OSSavedRegionStart = (void*)BOOT_REGION_START;
+ __OSSavedRegionEnd = (void*)BOOT_REGION_END;
+ if (BOOT_REGION_START == 0U) {
+ memset(OSGetArenaLo(), 0U, (u32)OSGetArenaHi() - (u32)OSGetArenaLo());
+ return;
+ }
+
+ if ((u32)OSGetArenaLo() < (u32)__OSSavedRegionStart) {
+ if ((u32)OSGetArenaHi() <= (u32)__OSSavedRegionStart) {
+ memset((u32)OSGetArenaLo(), 0U, (u32)OSGetArenaHi() - (u32)OSGetArenaLo());
+ return;
+ }
+ memset(OSGetArenaLo(), 0U, (u32)__OSSavedRegionStart - (u32)OSGetArenaLo());
+ if ((u32)OSGetArenaHi() > (u32)__OSSavedRegionEnd) {
+ memset((u32)__OSSavedRegionEnd, 0, (u32)OSGetArenaHi() - (u32)__OSSavedRegionEnd);
+ }
+ }
+}
+
+static void InquiryCallback(s32 result, DVDCommandBlock* block) {
+ switch (block->state) {
+ case 0:
+ __OSDeviceCode = (u16)(0x8000 | DriveInfo.deviceCode);
+ break;
+ default:
+ __OSDeviceCode = 1;
+ break;
+ }
+}
+
+void OSInit(void) {
+ /*
+ Initializes the Dolphin operating system.
+ - most of the main operations get farmed out to other functions
+ - loading debug info and setting up heap bounds largely happen here
+ - a lot of OS reporting also gets controlled here
+ */
+ // pretty sure this is the min(/max) amount of pointers etc for the stack to match
+ BI2Debug* DebugInfo;
+ void* debugArenaLo;
+ u32 inputConsoleType;
+ u32 tdev;
+
+ // check if we've already done all this or not
+ if ((BOOL)AreWeInitialized == FALSE) { // fantastic name
+ AreWeInitialized = TRUE; // flag to make sure we don't have to do this again
+
+ // SYSTEM //
+ __OSStartTime = __OSGetSystemTime();
+ OSDisableInterrupts();
+
+ // set some PPC things
+ PPCDisableSpeculation();
+ PPCSetFpNonIEEEMode();
+
+ // DEBUG //
+ // load some DVD stuff
+ BI2DebugFlag = 0; // debug flag from the DVD BI2 header
+ BootInfo = (OSBootInfo*)OS_BASE_CACHED; // set pointer to BootInfo
+
+ __DVDLongFileNameFlag = (u32)0; // flag to tell us whether we make it through the debug loading
+
+ // time to grab a bunch of debug info from the DVD
+ // the address for where the BI2 debug info is, is stored at OS_BI2_DEBUG_ADDRESS
+ DebugInfo = (BI2Debug*)*((u32*)OS_BI2_DEBUG_ADDRESS);
+
+ // if the debug info address exists, grab some debug info
+ if (DebugInfo != NULL) {
+ BI2DebugFlag = &DebugInfo->debugFlag; // debug flag from DVD BI2
+ __PADSpec = (u32)DebugInfo->padSpec; // some other info from DVD BI2
+ *((u8*)DEBUGFLAG_ADDR) = (u8)*BI2DebugFlag; // store flag in mem
+ *((u8*)OS_DEBUG_ADDRESS_2) = (u8)__PADSpec; // store other info in mem
+ } else if (BootInfo->arenaHi) { // if the top of the heap is already set
+ BI2DebugFlagHolder = (u32*)*((u8*)DEBUGFLAG_ADDR); // grab whatever's stored at 0x800030E8
+ BI2DebugFlag = (u32*)&BI2DebugFlagHolder; // flag is then address of flag holder
+ __PADSpec = (u32) * ((u8*)OS_DEBUG_ADDRESS_2); // pad spec is whatever's at 0x800030E9
+ }
+
+ __DVDLongFileNameFlag = 1; // we made it through debug!
+
+ // HEAP //
+ // set up bottom of heap (ArenaLo)
+ // grab address from BootInfo if it exists, otherwise use default __ArenaLo
+ OSSetArenaLo((BootInfo->arenaLo == NULL) ? __ArenaLo : BootInfo->arenaLo);
+
+ // if the input arenaLo is null, and debug flag location exists (and flag is < 2),
+ // set arenaLo to just past the end of the db stack
+ if ((BootInfo->arenaLo == NULL) && (BI2DebugFlag != 0) && (*BI2DebugFlag < 2)) {
+ debugArenaLo = (char*)(((u32)_db_stack_end + 0x1f) & ~0x1f);
+ OSSetArenaLo(debugArenaLo);
+ }
+
+ // set up top of heap (ArenaHi)
+ // grab address from BootInfo if it exists, otherwise use default __ArenaHi
+ OSSetArenaHi((BootInfo->arenaHi == NULL) ? __ArenaHi : BootInfo->arenaHi);
+
+ // OS INIT AND REPORT //
+ // initialise a whole bunch of OS stuff
+ OSExceptionInit();
+ __OSInitSystemCall();
+ OSInitAlarm();
+ __OSModuleInit();
+ __OSInterruptInit();
+ __OSSetInterruptHandler(__OS_INTERRUPT_PI_RSW, (void*)__OSResetSWInterruptHandler);
+ __OSContextInit();
+ __OSCacheInit();
+ EXIInit();
+ SIInit();
+ __OSInitSram();
+ __OSThreadInit();
+ __OSInitAudioSystem();
+ PPCMthid2(PPCMfhid2() & 0xBFFFFFFF);
+ if ((BOOL)__OSInIPL == FALSE) {
+ __OSInitMemoryProtection();
+ }
+
+ // begin OS reporting
+ OSReport("\nDolphin OS $Revision: 54 $.\n");
+ OSReport("Kernel built : %s %s\n", "Jun 5 2002", "02:09:12");
+ OSReport("Console Type : ");
+
+ // this is a function in the same file, but it doesn't seem to match
+ // inputConsoleType = OSGetConsoleType();
+
+ // inputConsoleType = (BootInfo == NULL || (inputConsoleType = BootInfo->consoleType) == 0) ?
+ // 0x10000002 : BootInfo->consoleType;
+ if (BootInfo == NULL || (inputConsoleType = BootInfo->consoleType) == 0) {
+ inputConsoleType = OS_CONSOLE_ARTHUR; // default console type
+ } else {
+ inputConsoleType = BootInfo->consoleType;
+ }
+
+ // work out what console type this corresponds to and report it
+ // consoleTypeSwitchHi = inputConsoleType & 0xF0000000;
+ switch (inputConsoleType & 0xffff0000) { // check "first" byte
+ case OS_CONSOLE_RETAIL:
+ OSReport("Retail %d\n", inputConsoleType);
+ break;
+ default:
+ switch (inputConsoleType & 0x0000ffff) { // if "first" byte is 2, check "the rest"
+ case OS_CONSOLE_EMULATOR:
+ OSReport("Mac Emulator\n");
+ break;
+ case OS_CONSOLE_PC_EMULATOR:
+ OSReport("PC Emulator\n");
+ break;
+ case OS_CONSOLE_ARTHUR:
+ OSReport("EPPC Arthur\n");
+ break;
+ case OS_CONSOLE_MINNOW:
+ OSReport("EPPC Minnow\n");
+ break;
+ default:
+ tdev = ((u32)inputConsoleType & 0x0000ffff);
+ OSReport("Development HW%d (%08x)\n", tdev - 3, inputConsoleType);
+ break;
+ }
+ break;
+ }
+
+ // report memory size
+ OSReport("Memory %d MB\n", (u32)BootInfo->memorySize >> 0x14U);
+ // report heap bounds
+ OSReport("Arena : 0x%x - 0x%x\n", OSGetArenaLo(), OSGetArenaHi());
+
+ // if location of debug flag exists, and flag is >= 2, enable MetroTRKInterrupts
+ if (BI2DebugFlag && ((*BI2DebugFlag) >= 2)) {
+ EnableMetroTRKInterrupts();
+ }
+
+ // free up memory and re-enable things
+ ClearArena();
+ OSEnableInterrupts();
+
+ // check if we can load OS from IPL; if not, grab it from DVD (?)
+ if ((BOOL)__OSInIPL == FALSE) {
+ DVDInit();
+ if ((BOOL)__OSIsGcam) {
+ __OSDeviceCode = 0x9000;
+ return;
+ }
+ DCInvalidateRange(&DriveInfo, sizeof(DriveInfo));
+ DVDInquiryAsync((char*)&DriveBlock, &DriveInfo, InquiryCallback);
+ }
+ }
+}
+static u32 __OSExceptionLocations[] = {
+ 0x00000100, 0x00000200, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000800,
+ 0x00000900, 0x00000C00, 0x00000D00, 0x00000F00, 0x00001300, 0x00001400, 0x00001700,
+};
+
+// dummy entry points to the OS Exception vector
+void __OSEVStart(void);
+void __OSEVEnd(void);
+void __OSEVSetNumber(void);
+void __OSExceptionVector(void);
+
+void __DBVECTOR(void);
+void __OSDBINTSTART(void);
+void __OSDBINTEND(void);
+void __OSDBJUMPSTART(void);
+void __OSDBJUMPEND(void);
+
+#define NOP 0x60000000
+
+__OSExceptionHandler __OSSetExceptionHandler(__OSException exception, __OSExceptionHandler handler);
+
+/*
+ * --INFO--
+ * Address: 800EB654
+ * Size: 000280
+ */
+static void OSExceptionInit(void) {
+ __OSException exception;
+ void* destAddr;
+
+ // These two vars help us change the exception number embedded
+ // in the exception handler code.
+ u32* opCodeAddr;
+ u32 oldOpCode;
+
+ // Address range of the actual code to be copied.
+ u8* handlerStart;
+ u32 handlerSize;
+
+ // Install the first level exception vector.
+ opCodeAddr = (u32*)__OSEVSetNumber;
+ oldOpCode = *opCodeAddr;
+ handlerStart = (u8*)__OSEVStart;
+ handlerSize = (u32)((u8*)__OSEVEnd - (u8*)__OSEVStart);
+
+ // Install the DB integrator, only if we are the first OSInit to be run
+ destAddr = (void*)OSPhysicalToCached(OS_DBJUMPPOINT_ADDR);
+ if (*(u32*)destAddr == 0) // Lomem should be zero cleared only once by BS2
+ {
+ DBPrintf("Installing OSDBIntegrator\n");
+ memcpy(destAddr, (void*)__OSDBINTSTART, (u32)__OSDBINTEND - (u32)__OSDBINTSTART);
+ DCFlushRangeNoSync(destAddr, (u32)__OSDBINTEND - (u32)__OSDBINTSTART);
+ __sync();
+ ICInvalidateRange(destAddr, (u32)__OSDBINTEND - (u32)__OSDBINTSTART);
+ }
+
+ // Copy the right vector into the table
+ for (exception = 0; exception < __OS_EXCEPTION_MAX; exception++) {
+ if (BI2DebugFlag && (*BI2DebugFlag >= 2) && __DBIsExceptionMarked(exception)) {
+ // this DBPrintf is suspicious.
+ DBPrintf(">>> OSINIT: exception %d commandeered by TRK\n", exception);
+ continue;
+ }
+
+ // Modify the copy of code in text before transferring
+ // to the exception table.
+ *opCodeAddr = oldOpCode | exception;
+
+ // Modify opcodes at __DBVECTOR if necessary
+ if (__DBIsExceptionMarked(exception)) {
+ DBPrintf(">>> OSINIT: exception %d vectored to debugger\n", exception);
+ memcpy((void*)__DBVECTOR, (void*)__OSDBJUMPSTART, (u32)__OSDBJUMPEND - (u32)__OSDBJUMPSTART);
+ } else {
+ // make sure the opcodes are still nop
+ u32* ops = (u32*)__DBVECTOR;
+ int cb;
+
+ for (cb = 0; cb < (u32)__OSDBJUMPEND - (u32)__OSDBJUMPSTART; cb += sizeof(u32)) {
+ *ops++ = NOP;
+ }
+ }
+
+ // Install the modified handler.
+ destAddr = (void*)OSPhysicalToCached(__OSExceptionLocations[(u32)exception]);
+ memcpy(destAddr, handlerStart, handlerSize);
+ DCFlushRangeNoSync(destAddr, handlerSize);
+ __sync();
+ ICInvalidateRange(destAddr, handlerSize);
+ }
+
+ // initialize pointer to exception table
+ OSExceptionTable = OSPhysicalToCached(OS_EXCEPTIONTABLE_ADDR);
+
+ // install default exception handlers
+ for (exception = 0; exception < __OS_EXCEPTION_MAX; exception++) {
+ __OSSetExceptionHandler(exception, OSDefaultExceptionHandler);
+ }
+
+ // restore the old opcode, so that we can re-start an application without
+ // downloading the text segments
+ *opCodeAddr = oldOpCode;
+
+ DBPrintf("Exceptions initialized...\n");
+}
+
+static asm void __OSDBIntegrator(void) {
+ /* clang-format off */
+ nofralloc
+entry __OSDBINTSTART
+ li r5, OS_DBINTERFACE_ADDR
+ mflr r3
+ stw r3, DB_EXCEPTIONRET_OFFSET(r5)
+ lwz r3, DB_EXCEPTIONDEST_OFFSET(r5)
+ oris r3, r3, OS_CACHED_REGION_PREFIX
+ mtlr r3
+ li r3, 0x30 // MSR_IR | MSR_DR // turn on memory addressing
+ mtmsr r3
+ blr
+entry __OSDBINTEND
+ /* clang-format on */
+}
+
+static asm void __OSDBJump(void){
+ /* clang-format off */
+
+ nofralloc
+entry __OSDBJUMPSTART
+ bla OS_DBJUMPPOINT_ADDR
+entry __OSDBJUMPEND
+ /* clang-format on */
+
+}
+
+__OSExceptionHandler
+ __OSSetExceptionHandler(__OSException exception, __OSExceptionHandler handler) {
+ __OSExceptionHandler oldHandler;
+ oldHandler = OSExceptionTable[exception];
+ OSExceptionTable[exception] = handler;
+ return oldHandler;
+}
+
+__OSExceptionHandler __OSGetExceptionHandler(__OSException exception) {
+ return OSExceptionTable[exception];
+}
+
+static asm void OSExceptionVector(void) {
+ /* clang-format off */
+ nofralloc
+
+entry __OSEVStart
+ // Save r4 into SPRG0
+ mtsprg 0, r4
+
+ // Load current context physical address into r4
+ lwz r4, OS_CURRENTCONTEXT_PADDR
+
+ // Save r3 - r5 into the current context
+ stw r3, OS_CONTEXT_R3(r4)
+ mfsprg r3, 0
+ stw r3, OS_CONTEXT_R4(r4)
+ stw r5, OS_CONTEXT_R5(r4)
+
+ lhz r3, OS_CONTEXT_STATE(r4)
+ ori r3, r3, OS_CONTEXT_STATE_EXC
+ sth r3, OS_CONTEXT_STATE(r4)
+
+ // Save misc registers
+ mfcr r3
+ stw r3, OS_CONTEXT_CR(r4)
+ mflr r3
+ stw r3, OS_CONTEXT_LR(r4)
+ mfctr r3
+ stw r3, OS_CONTEXT_CTR(r4)
+ mfxer r3
+ stw r3, OS_CONTEXT_XER(r4)
+ mfsrr0 r3
+ stw r3, OS_CONTEXT_SRR0(r4)
+ mfsrr1 r3
+ stw r3, OS_CONTEXT_SRR1(r4)
+ mr r5, r3
+
+entry __DBVECTOR
+ nop
+
+ // Set SRR1[IR|DR] to turn on address
+ // translation at the next RFI
+ mfmsr r3
+ ori r3, r3, 0x30
+ mtsrr1 r3
+
+ // This lets us change the exception number based on the
+ // exception we're installing.
+entry __OSEVSetNumber
+ addi r3, 0, 0x0000
+
+ // Load current context virtual address into r4
+ lwz r4, 0xD4
+
+ // Check non-recoverable interrupt
+ rlwinm. r5, r5, 0, MSR_RI_BIT, MSR_RI_BIT
+ bne recoverable
+ addis r5, 0, OSDefaultExceptionHandler@ha
+ addi r5, r5, OSDefaultExceptionHandler@l
+ mtsrr0 r5
+ rfi
+ // NOT REACHED HERE
+
+recoverable:
+ // Locate exception handler.
+ rlwinm r5, r3, 2, 22, 29 // r5 contains exception*4
+ lwz r5, OS_EXCEPTIONTABLE_ADDR(r5)
+ mtsrr0 r5
+
+ // Final state
+ // r3 - exception number
+ // r4 - pointer to context
+ // r5 - garbage
+ // srr0 - exception handler
+ // srr1 - address translation enalbed, not yet recoverable
+
+ rfi
+ // NOT REACHED HERE
+ // The handler will restore state
+
+entry __OSEVEnd
+ nop
+ /* clang-format on */
+}
+
+void __OSUnhandledException(__OSException exception, OSContext* context, u32 dsisr, u32 dar);
+asm void OSDefaultExceptionHandler(register __OSException exception, register OSContext* context) {
+ /* clang-format off */
+ nofralloc
+ OS_EXCEPTION_SAVE_GPRS(context)
+ mfdsisr r5
+ mfdar r6
+
+ stwu r1,-8(r1)
+ b __OSUnhandledException
+ /* clang-foramt on */
+}
+
+void __OSPSInit(void)
+{
+ PPCMthid2(PPCMfhid2() | 0xA0000000);
+ ICFlashInvalidate();
+ __sync();
+ // clang-format off
+ asm
+ {
+ li r3, 0
+ mtspr GQR0, r3
+ }
+ // clang-format on
+}
+
+vu32 __DIRegs[16] : 0xCC006000;
+#define DI_CONFIG_IDX 0x9
+#define DI_CONFIG_CONFIG_MASK 0xFF
+u32 __OSGetDIConfig(void) { return (__DIRegs[DI_CONFIG_IDX] & DI_CONFIG_CONFIG_MASK); }
+
+void OSRegisterVersion(const char* id) { OSReport("%s\n", id); }
diff --git a/src/Dolphin/os/OSAlarm.c b/src/Dolphin/os/OSAlarm.c
new file mode 100644
index 0000000..d362ceb
--- /dev/null
+++ b/src/Dolphin/os/OSAlarm.c
@@ -0,0 +1,183 @@
+#include "dolphin/PPCArch.h"
+#include "dolphin/os/OSPriv.h"
+
+static struct OSAlarmQueue {
+ OSAlarm* head;
+ OSAlarm* tail;
+} AlarmQueue;
+
+static void DecrementerExceptionHandler(__OSException exception, OSContext* context);
+static BOOL OnReset(BOOL final);
+
+void OSInitAlarm(void) {
+ if (__OSGetExceptionHandler(8) != DecrementerExceptionHandler) {
+ AlarmQueue.head = AlarmQueue.tail = NULL;
+ __OSSetExceptionHandler(8, DecrementerExceptionHandler);
+ }
+}
+
+void OSCreateAlarm(OSAlarm* alarm) {
+ alarm->handler = 0;
+ alarm->tag = 0;
+}
+
+static void SetTimer(OSAlarm* alarm) {
+ OSTime delta;
+
+ delta = alarm->fire - __OSGetSystemTime();
+ if (delta < 0) {
+ PPCMtdec(0);
+ } else if (delta < 0x80000000) {
+ PPCMtdec((u32)delta);
+ } else {
+ PPCMtdec(0x7fffffff);
+ }
+}
+
+static void InsertAlarm(OSAlarm* alarm, OSTime fire, OSAlarmHandler handler) {
+ OSAlarm* next;
+ OSAlarm* prev;
+
+ if (0 < alarm->period) {
+ OSTime time = __OSGetSystemTime();
+
+ fire = alarm->start;
+ if (alarm->start < time) {
+ fire += alarm->period * ((time - alarm->start) / alarm->period + 1);
+ }
+ }
+
+ alarm->handler = handler;
+ alarm->fire = fire;
+
+ for (next = AlarmQueue.head; next; next = next->next) {
+ if (next->fire <= fire) {
+ continue;
+ }
+
+ alarm->prev = next->prev;
+ next->prev = alarm;
+ alarm->next = next;
+ prev = alarm->prev;
+ if (prev) {
+ prev->next = alarm;
+ } else {
+ AlarmQueue.head = alarm;
+ SetTimer(alarm);
+ }
+ return;
+ }
+ alarm->next = 0;
+ prev = AlarmQueue.tail;
+ AlarmQueue.tail = alarm;
+ alarm->prev = prev;
+ if (prev) {
+ prev->next = alarm;
+ } else {
+ AlarmQueue.head = AlarmQueue.tail = alarm;
+ SetTimer(alarm);
+ }
+}
+
+void OSSetAlarm(OSAlarm* alarm, OSTime tick, OSAlarmHandler handler) {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+ alarm->period = 0;
+ InsertAlarm(alarm, __OSGetSystemTime() + tick, handler);
+ OSRestoreInterrupts(enabled);
+}
+
+void OSSetPeriodicAlarm(OSAlarm* alarm, OSTime start, OSTime period, OSAlarmHandler handler) {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+ alarm->period = period;
+ alarm->start = __OSTimeToSystemTime(start);
+ InsertAlarm(alarm, 0, handler);
+ OSRestoreInterrupts(enabled);
+}
+
+void OSCancelAlarm(OSAlarm* alarm) {
+ OSAlarm* next;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ if (alarm->handler == 0) {
+ OSRestoreInterrupts(enabled);
+ return;
+ }
+
+ next = alarm->next;
+ if (next == 0) {
+ AlarmQueue.tail = alarm->prev;
+ } else {
+ next->prev = alarm->prev;
+ }
+ if (alarm->prev) {
+ alarm->prev->next = next;
+ } else {
+ AlarmQueue.head = next;
+ if (next) {
+ SetTimer(next);
+ }
+ }
+ alarm->handler = 0;
+
+ OSRestoreInterrupts(enabled);
+}
+
+static void DecrementerExceptionCallback(register __OSException exception,
+ register OSContext* context) {
+ OSAlarm* alarm;
+ OSAlarm* next;
+ OSAlarmHandler handler;
+ OSTime time;
+ OSContext exceptionContext;
+ time = __OSGetSystemTime();
+ alarm = AlarmQueue.head;
+ if (alarm == 0) {
+ OSLoadContext(context);
+ }
+
+ if (time < alarm->fire) {
+ SetTimer(alarm);
+ OSLoadContext(context);
+ }
+
+ next = alarm->next;
+ AlarmQueue.head = next;
+ if (next == 0) {
+ AlarmQueue.tail = 0;
+ } else {
+ next->prev = 0;
+ }
+
+ handler = alarm->handler;
+ alarm->handler = 0;
+ if (0 < alarm->period) {
+ InsertAlarm(alarm, 0, handler);
+ }
+
+ if (AlarmQueue.head) {
+ SetTimer(AlarmQueue.head);
+ }
+
+ OSDisableScheduler();
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ handler(alarm, context);
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ OSEnableScheduler();
+ __OSReschedule();
+ OSLoadContext(context);
+}
+
+static asm void DecrementerExceptionHandler(register __OSException exception,
+ register OSContext* context) {
+
+ nofralloc
+ OS_EXCEPTION_SAVE_GPRS(context)
+ stwu r1, -8(r1)
+ b DecrementerExceptionCallback
+}
diff --git a/src/Dolphin/os/OSAlloc.c b/src/Dolphin/os/OSAlloc.c
new file mode 100644
index 0000000..2850380
--- /dev/null
+++ b/src/Dolphin/os/OSAlloc.c
@@ -0,0 +1,195 @@
+#include "dolphin/os.h"
+
+typedef struct OSHeapDescriptor {
+ s32 size; // at 0x0
+ struct OSHeapCell* freeList; // at 0x4
+ struct OSHeapCell* usedList; // at 0x8
+} OSHeapDescriptor;
+
+typedef struct OSHeapCell {
+ struct OSHeapCell* prev; // at 0x0
+ struct OSHeapCell* next; // at 0x4
+ s32 size; // at 0x8
+ struct OSHeapDescriptor* hd; // at 0xC
+ s32 usedSize; // at 0x10
+ char UNK_0x14[0x20 - 0x14];
+} OSHeapCell;
+
+volatile s32 __OSCurrHeap = -1;
+
+static void* ArenaEnd = NULL;
+static void* ArenaStart = NULL;
+static s32 NumHeaps = 0;
+static OSHeapDescriptor* HeapArray = NULL;
+
+static OSHeapCell* DLAddFront(OSHeapCell* list, OSHeapCell* child) {
+ child->next = list;
+ child->prev = NULL;
+
+ if (list != NULL) {
+ list->prev = child;
+ }
+
+ return child;
+}
+
+static OSHeapCell* DLExtract(OSHeapCell* list, OSHeapCell* child) {
+ if (child->next != NULL) {
+ child->next->prev = child->prev;
+ }
+
+ if (child->prev == NULL) {
+ return child->next;
+ }
+
+ child->prev->next = child->next;
+ return list;
+}
+
+static OSHeapCell* DLInsert(OSHeapCell* list, OSHeapCell* child) {
+ OSHeapCell* prev = NULL;
+ OSHeapCell* next = list;
+
+ for (; next != NULL; prev = next, next = next->next) {
+ if (child <= next) {
+ break;
+ }
+ }
+
+ child->next = next;
+ child->prev = prev;
+
+ if (next != NULL) {
+ next->prev = child;
+
+ if ((u8*)child + child->size == (u8*)next) {
+ child->size += next->size;
+ next = next->next;
+ child->next = next;
+ if (next != NULL) {
+ next->prev = child;
+ }
+ }
+ }
+
+ if (prev != NULL) {
+ prev->next = child;
+
+ if ((u8*)prev + prev->size == (u8*)child) {
+ prev->size += child->size;
+ prev->next = next;
+ if (next != NULL) {
+ next->prev = prev;
+ }
+ }
+
+ return list;
+ } else {
+ return child;
+ }
+}
+
+void* OSAllocFromHeap(s32 handle, s32 size) {
+ OSHeapDescriptor* hd = &HeapArray[handle];
+ OSHeapCell* cell;
+ u32 avail;
+
+ hd = &HeapArray[handle];
+ size = OSRoundUp32B(size + sizeof(OSHeapCell));
+
+ for (cell = hd->freeList; cell != NULL; cell = cell->next) {
+ if (size <= cell->size) {
+ break;
+ }
+ }
+
+ if (cell == NULL) {
+ return NULL;
+ }
+
+ avail = cell->size - size;
+ if (avail < 64) {
+ hd->freeList = DLExtract(hd->freeList, cell);
+ } else {
+ OSHeapCell* adj;
+ cell->size = size;
+
+ adj = (OSHeapCell*)((u8*)cell + size);
+ adj->size = avail;
+ adj->prev = cell->prev;
+ adj->next = cell->next;
+
+ if (adj->next != NULL) {
+ adj->next->prev = adj;
+ }
+
+ if (adj->prev != NULL) {
+ adj->prev->next = adj;
+ } else {
+ hd->freeList = adj;
+ }
+ }
+
+ hd->usedList = DLAddFront(hd->usedList, cell);
+ return (u8*)cell + sizeof(OSHeapCell);
+}
+
+void OSFreeToHeap(s32 handle, void* p) {
+ OSHeapDescriptor* hd = &HeapArray[handle];
+ OSHeapCell* cell = (OSHeapCell*)((u8*)p - sizeof(OSHeapCell));
+ hd->usedList = DLExtract(hd->usedList, cell);
+ hd->freeList = DLInsert(hd->freeList, cell);
+}
+
+s32 OSSetCurrentHeap(s32 handle) {
+ s32 old = __OSCurrHeap;
+ __OSCurrHeap = handle;
+ return old;
+}
+
+void* OSInitAlloc(void* start, void* end, s32 numHeaps) {
+ u32 headerSize;
+ int i;
+
+ headerSize = numHeaps * sizeof(OSHeapDescriptor);
+ HeapArray = (OSHeapDescriptor*)start;
+ NumHeaps = numHeaps;
+
+ for (i = 0; i < NumHeaps; i++) {
+ OSHeapDescriptor* hd = &HeapArray[i];
+ hd->size = -1;
+ hd->usedList = NULL;
+ hd->freeList = NULL;
+ }
+
+ __OSCurrHeap = -1;
+ ArenaStart = OSRoundUp32BPtr((u8*)HeapArray + headerSize);
+ ArenaEnd = OSRoundDown32BPtr(end);
+
+ return ArenaStart;
+}
+
+s32 OSCreateHeap(void* start, void* end) {
+ s32 handle;
+
+ start = OSRoundUp32BPtr(start);
+ end = OSRoundDown32BPtr(end);
+
+ for (handle = 0; handle < NumHeaps; handle++) {
+ OSHeapCell* cell = (OSHeapCell*)start;
+ OSHeapDescriptor* hd = &HeapArray[handle];
+ if (hd->size < 0) {
+ hd->size = (u32)end - (u32)start;
+
+ cell->prev = NULL;
+ cell->next = NULL;
+ cell->size = hd->size;
+
+ hd->freeList = cell;
+ hd->usedList = NULL;
+ return handle;
+ }
+ }
+
+ return -1;
+}
diff --git a/src/Dolphin/os/OSArena.c b/src/Dolphin/os/OSArena.c
new file mode 100644
index 0000000..da864ab
--- /dev/null
+++ b/src/Dolphin/os/OSArena.c
@@ -0,0 +1,39 @@
+#include <dolphin/os/OSArena.h>
+
+#define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1))
+#define TRUNC(n, a) (((u32)(n)) & ~((a)-1))
+
+void* __OSArenaHi;
+void* __OSArenaLo = (void*)-1;
+
+void* OSGetArenaHi(void) { return __OSArenaHi; }
+
+void* OSGetArenaLo(void) { return __OSArenaLo; }
+
+void OSSetArenaHi(void* addr) { __OSArenaHi = addr; }
+
+void OSSetArenaLo(void* addr) { __OSArenaLo = addr; }
+
+void* OSAllocFromArenaLo(u32 size, u32 align) {
+ void* ptr;
+ u8* arenaLo;
+
+ ptr = OSGetArenaLo();
+ arenaLo = ptr = (void*)ROUND(ptr, align);
+ arenaLo += size;
+ arenaLo = (u8*)ROUND(arenaLo, align);
+ OSSetArenaLo(arenaLo);
+ return ptr;
+}
+
+void* OSAllocFromArenaHi(u32 size, u32 align) {
+ void* ptr;
+ u8* arenaHi;
+
+ arenaHi = OSGetArenaHi();
+ arenaHi = (u8*)TRUNC(arenaHi, align);
+ arenaHi -= size;
+ arenaHi = ptr = (void*)TRUNC(arenaHi, align);
+ OSSetArenaHi(arenaHi);
+ return ptr;
+}
diff --git a/src/Dolphin/os/OSAudioSystem.c b/src/Dolphin/os/OSAudioSystem.c
new file mode 100644
index 0000000..1eddcbf
--- /dev/null
+++ b/src/Dolphin/os/OSAudioSystem.c
@@ -0,0 +1,115 @@
+#include "types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static u8 DSPInitCode[128] = {
+ // clang-format off
+ 0x02, 0x9F, 0x00, 0x10, 0x02, 0x9F, 0x00, 0x33, 0x02, 0x9F, 0x00, 0x34, 0x02, 0x9F, 0x00, 0x35,
+ 0x02, 0x9F, 0x00, 0x36, 0x02, 0x9F, 0x00, 0x37, 0x02, 0x9F, 0x00, 0x38, 0x02, 0x9F, 0x00, 0x39,
+ 0x12, 0x06, 0x12, 0x03, 0x12, 0x04, 0x12, 0x05, 0x00, 0x80, 0x80, 0x00, 0x00, 0x88, 0xFF, 0xFF,
+ 0x00, 0x84, 0x10, 0x00, 0x00, 0x64, 0x00, 0x1D, 0x02, 0x18, 0x00, 0x00, 0x81, 0x00, 0x1C, 0x1E,
+ 0x00, 0x44, 0x1B, 0x1E, 0x00, 0x84, 0x08, 0x00, 0x00, 0x64, 0x00, 0x27, 0x19, 0x1E, 0x00, 0x00,
+ 0x00, 0xDE, 0xFF, 0xFC, 0x02, 0xA0, 0x80, 0x00, 0x02, 0x9C, 0x00, 0x28, 0x16, 0xFC, 0x00, 0x54,
+ 0x16, 0xFD, 0x43, 0x48, 0x00, 0x21, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF, 0x02, 0xFF,
+ 0x02, 0xFF, 0x02, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // clang-format on
+};
+
+volatile u16 __DSPRegs[] : 0xCC005000;
+#define __DSPWorkBuffer (void*)0x81000000
+
+void __OSInitAudioSystem(void) {
+ u32 r28;
+ u16 r3;
+
+ u32 padding;
+
+ memcpy((void*)((u8*)OSGetArenaHi() - 128), __DSPWorkBuffer, 128);
+ memcpy(__DSPWorkBuffer, (void*)DSPInitCode, 128);
+
+ DCFlushRange(__DSPWorkBuffer, 128);
+
+ __DSPRegs[9] = 0x43;
+ __DSPRegs[5] = 0x8AC;
+ __DSPRegs[5] |= 1;
+ while (__DSPRegs[5] & 1)
+ ;
+ __DSPRegs[0] = 0;
+ while (((__DSPRegs[2] << 16) | __DSPRegs[3]) & 0x80000000)
+ ;
+ *(u32*)&__DSPRegs[16] = 0x1000000;
+ *(u32*)&__DSPRegs[18] = 0;
+ *(u32*)&__DSPRegs[20] = 0x20;
+
+ r3 = __DSPRegs[5];
+ while (!(r3 & 0x20))
+ r3 = __DSPRegs[5];
+ __DSPRegs[5] = r3;
+
+ r28 = OSGetTick();
+ while ((s32)(OSGetTick() - r28) < 0x892)
+ ;
+
+ *(u32*)&__DSPRegs[16] = 0x1000000;
+ *(u32*)&__DSPRegs[18] = 0;
+ *(u32*)&__DSPRegs[20] = 0x20;
+
+ r3 = __DSPRegs[5];
+ while (!(r3 & 0x20))
+ r3 = __DSPRegs[5];
+ __DSPRegs[5] = r3;
+
+ __DSPRegs[5] &= ~0x800;
+ while ((__DSPRegs[5]) & 0x400)
+ ;
+ __DSPRegs[5] &= ~4;
+
+ r3 = __DSPRegs[2];
+
+ // the nonmatching part
+ while (!(r3 & 0x8000))
+ r3 = __DSPRegs[2];
+
+ (void)__DSPRegs[3];
+ r3 != 42069;
+ __DSPRegs[5] |= 4;
+ __DSPRegs[5] = 0x8AC;
+ __DSPRegs[5] |= 1;
+ while (__DSPRegs[5] & 1)
+ ;
+ memcpy(__DSPWorkBuffer, (void*)((u8*)OSGetArenaHi() - 128), 128);
+}
+
+void __OSStopAudioSystem(void) {
+ u32 r28;
+
+#define waitUntil(load, mask) \
+ r28 = (load); \
+ while (r28 & (mask)) { \
+ r28 = (load); \
+ }
+
+ __DSPRegs[5] = 0x804;
+ r28 = __DSPRegs[27];
+ __DSPRegs[27] = r28 & ~0x8000;
+ waitUntil(__DSPRegs[5], 0x400);
+ waitUntil(__DSPRegs[5], 0x200);
+ __DSPRegs[5] = 0x8ac;
+ __DSPRegs[0] = 0;
+
+ while (((__DSPRegs[2] << 16) | __DSPRegs[3]) & 0x80000000)
+ ;
+ r28 = OSGetTick();
+ while ((s32)(OSGetTick() - r28) < 0x2c)
+ ;
+ __DSPRegs[5] |= 1;
+ waitUntil(__DSPRegs[5], 0x001);
+
+#undef waitUntil
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/Dolphin/os/OSCache.c b/src/Dolphin/os/OSCache.c
new file mode 100644
index 0000000..f0d7895
--- /dev/null
+++ b/src/Dolphin/os/OSCache.c
@@ -0,0 +1,426 @@
+#include "dolphin/PPCArch.h"
+#include "dolphin/os.h"
+
+// Can't use this due to weird condition register issues
+//#include "asm_types.h"
+#define HID2 920
+
+#include "dolphin/db.h"
+
+/* clang-format off */
+asm void DCEnable() {
+ nofralloc
+ sync
+ mfspr r3, HID0
+ ori r3, r3, 0x4000
+ mtspr HID0, r3
+ blr
+}
+
+asm void DCInvalidateRange(register void* addr, register u32 nBytes) {
+ nofralloc
+ cmplwi nBytes, 0
+ blelr
+ clrlwi r5, addr, 27
+ add nBytes, nBytes, r5
+ addi nBytes, nBytes, 31
+ srwi nBytes, nBytes, 5
+ mtctr nBytes
+
+@1
+ dcbi r0, addr
+ addi addr, addr, 32
+ bdnz @1
+ blr
+}
+
+
+asm void DCFlushRange(register void* addr, register u32 nBytes) {
+ nofralloc
+ cmplwi nBytes, 0
+ blelr
+ clrlwi r5, addr, 27
+ add nBytes, nBytes, r5
+ addi nBytes, nBytes, 31
+ srwi nBytes, nBytes, 5
+ mtctr nBytes
+
+@1
+ dcbf r0, addr
+ addi addr, addr, 32
+ bdnz @1
+ sc
+ blr
+}
+
+asm void DCStoreRange(register void* addr, register u32 nBytes) {
+ nofralloc
+ cmplwi nBytes, 0
+ blelr
+ clrlwi r5, addr, 27
+ add nBytes, nBytes, r5
+ addi nBytes, nBytes, 31
+ srwi nBytes, nBytes, 5
+ mtctr nBytes
+
+@1
+ dcbst r0, addr
+ addi addr, addr, 32
+ bdnz @1
+ sc
+
+ blr
+}
+
+asm void DCFlushRangeNoSync(register void* addr, register u32 nBytes) {
+ nofralloc
+ cmplwi nBytes, 0
+ blelr
+ clrlwi r5, addr, 27
+ add nBytes, nBytes, r5
+ addi nBytes, nBytes, 31
+ srwi nBytes, nBytes, 5
+ mtctr nBytes
+
+@1
+ dcbf r0, addr
+ addi addr, addr, 32
+ bdnz @1
+ blr
+}
+
+
+asm void DCStoreRangeNoSync(register void* addr, register u32 nBytes) {
+ nofralloc
+ cmplwi nBytes, 0
+ blelr
+ clrlwi r5, addr, 27
+ add nBytes, nBytes, r5
+ addi nBytes, nBytes, 31
+ srwi nBytes, nBytes, 5
+ mtctr nBytes
+
+@1
+ dcbst r0, addr
+ addi addr, addr, 32
+ bdnz @1
+
+ blr
+}
+
+asm void DCZeroRange(register void* addr, register u32 nBytes) {
+ nofralloc
+ cmplwi nBytes, 0
+ blelr
+ clrlwi r5, addr, 27
+ add nBytes, nBytes, r5
+ addi nBytes, nBytes, 31
+ srwi nBytes, nBytes, 5
+ mtctr nBytes
+
+@1
+ dcbz r0, addr
+ addi addr, addr, 32
+ bdnz @1
+
+ blr
+}
+
+
+asm void ICInvalidateRange(register void* addr, register u32 nBytes) {
+ nofralloc
+ cmplwi nBytes, 0
+ blelr
+ clrlwi r5, addr, 27
+ add nBytes, nBytes, r5
+ addi nBytes, nBytes, 31
+ srwi nBytes, nBytes, 5
+ mtctr nBytes
+
+@1
+ icbi r0, addr
+ addi addr, addr, 32
+ bdnz @1
+ sync
+ isync
+
+ blr
+}
+
+
+asm void ICFlashInvalidate() {
+ nofralloc
+ mfspr r3, HID0
+ ori r3, r3, 0x800
+ mtspr HID0, r3
+ blr
+}
+
+asm void ICEnable() {
+ nofralloc
+ isync
+ mfspr r3, HID0
+ ori r3, r3, 0x8000
+ mtspr HID0, r3
+ blr
+}
+
+#define LC_LINES 512
+#define CACHE_LINES 1024
+
+asm void __LCEnable() {
+ nofralloc
+ mfmsr r5
+ ori r5, r5, 0x1000
+ mtmsr r5
+
+ lis r3, OS_CACHED_REGION_PREFIX
+ li r4, CACHE_LINES
+ mtctr r4
+_touchloop:
+ dcbt 0,r3
+ dcbst 0,r3
+ addi r3,r3,32
+ bdnz _touchloop
+ mfspr r4, HID2
+ oris r4, r4, 0x100F
+ mtspr HID2, r4
+
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ lis r3, LC_BASE_PREFIX
+ ori r3, r3, 0x0002
+ mtspr DBAT3L, r3
+ ori r3, r3, 0x01fe
+ mtspr DBAT3U, r3
+ isync
+ lis r3, LC_BASE_PREFIX
+ li r6, LC_LINES
+ mtctr r6
+ li r6, 0
+
+_lockloop:
+ dcbz_l r6, r3
+ addi r3, r3, 32
+ bdnz+ _lockloop
+
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+ blr
+}
+
+void LCEnable() {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ __LCEnable();
+ OSRestoreInterrupts(enabled);
+}
+
+
+asm void LCDisable() {
+ nofralloc
+ lis r3, LC_BASE_PREFIX
+ li r4, LC_LINES
+ mtctr r4
+@1
+ dcbi r0, r3
+ addi r3, r3, 32
+ bdnz @1
+ mfspr r4, HID2
+ rlwinm r4, r4, 0, 4, 2
+ mtspr HID2, r4
+ blr
+}
+
+
+asm void LCLoadBlocks(register void* destTag, register void* srcAddr, register u32 numBlocks) {
+ nofralloc
+ rlwinm r6, numBlocks, 30, 27, 31
+ rlwinm srcAddr, srcAddr, 0, 4, 31
+ or r6, r6, srcAddr
+ mtspr DMA_U, r6
+ rlwinm r6, numBlocks, 2, 28, 29
+ or r6, r6, destTag
+ ori r6, r6, 0x12
+ mtspr DMA_L, r6
+ blr
+}
+
+asm void LCStoreBlocks(register void* destAddr, register void* srcTag, register u32 numBlocks) {
+ nofralloc
+ rlwinm r6, numBlocks, 30, 27, 31
+ rlwinm destAddr, destAddr, 0, 4, 31
+ or r6, r6, destAddr
+ mtspr DMA_U, r6
+ rlwinm r6, numBlocks, 2, 28, 29
+ or r6, r6, srcTag
+ ori r6, r6, 0x2
+ mtspr DMA_L, r6
+ blr
+}
+
+/* clang-format on */
+
+u32 LCLoadData(register void* destAddr, register void* srcAddr, register u32 nBytes) {
+ u32 numBlocks = (nBytes + 31) / 32;
+ u32 numTransactions = (numBlocks + 128 - 1) / 128;
+
+ while (numBlocks > 0) {
+ if (numBlocks < 128) {
+ LCLoadBlocks(destAddr, srcAddr, numBlocks);
+ numBlocks = 0;
+ } else {
+ LCLoadBlocks(destAddr, srcAddr, 0);
+ numBlocks -= 128;
+ destAddr = (void*)((u32)destAddr + 4096);
+ srcAddr = (void*)((u32)srcAddr + 4096);
+ }
+ }
+
+ return numTransactions;
+}
+u32 LCStoreData(void* destAddr, void* srcAddr, u32 nBytes) {
+ u32 numBlocks = (nBytes + 31) / 32;
+ u32 numTransactions = (numBlocks + 128 - 1) / 128;
+
+ while (numBlocks > 0) {
+ if (numBlocks < 128) {
+ LCStoreBlocks(destAddr, srcAddr, numBlocks);
+ numBlocks = 0;
+ } else {
+ LCStoreBlocks(destAddr, srcAddr, 0);
+ numBlocks -= 128;
+ destAddr = (void*)((u32)destAddr + 4096);
+ srcAddr = (void*)((u32)srcAddr + 4096);
+ }
+ }
+
+ return numTransactions;
+}
+
+/* clang-format off */
+asm u32 LCQueueLength() {
+ nofralloc
+ mfspr r4, HID2
+ rlwinm r3, r4, 8, 28, 31
+ blr
+}
+
+asm void LCQueueWait(register u32 len) {
+ nofralloc
+ addi len, len, 1
+@1
+ mfspr r4, HID2
+ rlwinm r4, r4, 8, 28, 31
+ cmpw cr2, r4, r3
+ bge cr2, @1
+ blr
+}
+
+/* clang-format on */
+static void L2Disable(void) {
+ __sync();
+ PPCMtl2cr(PPCMfl2cr() & ~0x80000000);
+ __sync();
+}
+
+void L2GlobalInvalidate(void) {
+ L2Disable();
+ PPCMtl2cr(PPCMfl2cr() | 0x00200000);
+ while (PPCMfl2cr() & 0x00000001u)
+ ;
+ PPCMtl2cr(PPCMfl2cr() & ~0x00200000);
+ while (PPCMfl2cr() & 0x00000001u) {
+ DBPrintf(">>> L2 INVALIDATE : SHOULD NEVER HAPPEN\n");
+ }
+}
+
+static void L2Init(void) {
+ u32 oldMSR;
+ oldMSR = PPCMfmsr();
+ __sync();
+ PPCMtmsr(MSR_IR | MSR_DR);
+ __sync();
+ L2Disable();
+ L2GlobalInvalidate();
+ PPCMtmsr(oldMSR);
+}
+
+void L2Enable(void) { PPCMtl2cr((PPCMfl2cr() | L2CR_L2E) & ~L2CR_L2I); }
+
+void DMAErrorHandler(OSError error, OSContext* context, ...) {
+ u32 hid2 = PPCMfhid2();
+
+ OSReport("Machine check received\n");
+ OSReport("HID2 = 0x%x SRR1 = 0x%x\n", hid2, context->srr1);
+ if (!(hid2 & (HID2_DCHERR | HID2_DNCERR | HID2_DCMERR | HID2_DQOERR)) ||
+ !(context->srr1 & SRR1_DMA_BIT)) {
+ OSReport("Machine check was not DMA/locked cache related\n");
+ OSDumpContext(context);
+ PPCHalt();
+ }
+
+ OSReport("DMAErrorHandler(): An error occurred while processing DMA.\n");
+ OSReport("The following errors have been detected and cleared :\n");
+
+ if (hid2 & HID2_DCHERR) {
+ OSReport("\t- Requested a locked cache tag that was already in the cache\n");
+ }
+
+ if (hid2 & HID2_DNCERR) {
+ OSReport("\t- DMA attempted to access normal cache\n");
+ }
+
+ if (hid2 & HID2_DCMERR) {
+ OSReport("\t- DMA missed in data cache\n");
+ }
+
+ if (hid2 & HID2_DQOERR) {
+ OSReport("\t- DMA queue overflowed\n");
+ }
+
+ // write hid2 back to clear the error bits
+ PPCMthid2(hid2);
+}
+
+void __OSCacheInit() {
+ if (!(PPCMfhid0() & HID0_ICE)) {
+ ICEnable();
+ DBPrintf("L1 i-caches initialized\n");
+ }
+ if (!(PPCMfhid0() & HID0_DCE)) {
+ DCEnable();
+ DBPrintf("L1 d-caches initialized\n");
+ }
+
+ if (!(PPCMfl2cr() & L2CR_L2E)) {
+ L2Init();
+ L2Enable();
+ DBPrintf("L2 cache initialized\n");
+ }
+
+ OSSetErrorHandler(OS_ERROR_MACHINE_CHECK, DMAErrorHandler);
+ DBPrintf("Locked cache machine check handler installed\n");
+}
diff --git a/src/Dolphin/os/OSContext.c b/src/Dolphin/os/OSContext.c
new file mode 100644
index 0000000..6acefe4
--- /dev/null
+++ b/src/Dolphin/os/OSContext.c
@@ -0,0 +1,550 @@
+#include <dolphin/os.h>
+#include <dolphin/db.h>
+
+#define HID2 920
+
+volatile OSContext* __OSCurrentContext : (OS_BASE_CACHED | 0x00D4);
+volatile OSContext* __OSFPUContext : (OS_BASE_CACHED | 0x00D8);
+
+static asm void __OSLoadFPUContext(register u32, register OSContext* fpuContext) {
+ // clang-format off
+ nofralloc
+ lhz r5, fpuContext->state;
+ clrlwi. r5, r5, 31
+ beq _return
+
+ lfd fp0, OS_CONTEXT_FPSCR(fpuContext)
+ mtfsf 0xFF, fp0
+ mfspr r5, HID2
+ rlwinm. r5, r5, 3, 31, 31
+ beq _regular_FPRs
+
+ psq_l fp0, OS_CONTEXT_PSF0(fpuContext), 0, 0
+ psq_l fp1, OS_CONTEXT_PSF1(fpuContext), 0, 0
+ psq_l fp2, OS_CONTEXT_PSF2(fpuContext), 0, 0
+ psq_l fp3, OS_CONTEXT_PSF3(fpuContext), 0, 0
+ psq_l fp4, OS_CONTEXT_PSF4(fpuContext), 0, 0
+ psq_l fp5, OS_CONTEXT_PSF5(fpuContext), 0, 0
+ psq_l fp6, OS_CONTEXT_PSF6(fpuContext), 0, 0
+ psq_l fp7, OS_CONTEXT_PSF7(fpuContext), 0, 0
+ psq_l fp8, OS_CONTEXT_PSF8(fpuContext), 0, 0
+ psq_l fp9, OS_CONTEXT_PSF9(fpuContext), 0, 0
+ psq_l fp10, OS_CONTEXT_PSF10(fpuContext), 0, 0
+ psq_l fp11, OS_CONTEXT_PSF11(fpuContext), 0, 0
+ psq_l fp12, OS_CONTEXT_PSF12(fpuContext), 0, 0
+ psq_l fp13, OS_CONTEXT_PSF13(fpuContext), 0, 0
+ psq_l fp14, OS_CONTEXT_PSF14(fpuContext), 0, 0
+ psq_l fp15, OS_CONTEXT_PSF15(fpuContext), 0, 0
+ psq_l fp16, OS_CONTEXT_PSF16(fpuContext), 0, 0
+ psq_l fp17, OS_CONTEXT_PSF17(fpuContext), 0, 0
+ psq_l fp18, OS_CONTEXT_PSF18(fpuContext), 0, 0
+ psq_l fp19, OS_CONTEXT_PSF19(fpuContext), 0, 0
+ psq_l fp20, OS_CONTEXT_PSF20(fpuContext), 0, 0
+ psq_l fp21, OS_CONTEXT_PSF21(fpuContext), 0, 0
+ psq_l fp22, OS_CONTEXT_PSF22(fpuContext), 0, 0
+ psq_l fp23, OS_CONTEXT_PSF23(fpuContext), 0, 0
+ psq_l fp24, OS_CONTEXT_PSF24(fpuContext), 0, 0
+ psq_l fp25, OS_CONTEXT_PSF25(fpuContext), 0, 0
+ psq_l fp26, OS_CONTEXT_PSF26(fpuContext), 0, 0
+ psq_l fp27, OS_CONTEXT_PSF27(fpuContext), 0, 0
+ psq_l fp28, OS_CONTEXT_PSF28(fpuContext), 0, 0
+ psq_l fp29, OS_CONTEXT_PSF29(fpuContext), 0, 0
+ psq_l fp30, OS_CONTEXT_PSF30(fpuContext), 0, 0
+ psq_l fp31, OS_CONTEXT_PSF31(fpuContext), 0, 0
+
+_regular_FPRs:
+ lfd fp0, fpuContext->fpr[0]
+ lfd fp1, fpuContext->fpr[1]
+ lfd fp2, fpuContext->fpr[2]
+ lfd fp3, fpuContext->fpr[3]
+ lfd fp4, fpuContext->fpr[4]
+ lfd fp5, fpuContext->fpr[5]
+ lfd fp6, fpuContext->fpr[6]
+ lfd fp7, fpuContext->fpr[7]
+ lfd fp8, fpuContext->fpr[8]
+ lfd fp9, fpuContext->fpr[9]
+ lfd fp10, fpuContext->fpr[10]
+ lfd fp11, fpuContext->fpr[11]
+ lfd fp12, fpuContext->fpr[12]
+ lfd fp13, fpuContext->fpr[13]
+ lfd fp14, fpuContext->fpr[14]
+ lfd fp15, fpuContext->fpr[15]
+ lfd fp16, fpuContext->fpr[16]
+ lfd fp17, fpuContext->fpr[17]
+ lfd fp18, fpuContext->fpr[18]
+ lfd fp19, fpuContext->fpr[19]
+ lfd fp20, fpuContext->fpr[20]
+ lfd fp21, fpuContext->fpr[21]
+ lfd fp22, fpuContext->fpr[22]
+ lfd fp23, fpuContext->fpr[23]
+ lfd fp24, fpuContext->fpr[24]
+ lfd fp25, fpuContext->fpr[25]
+ lfd fp26, fpuContext->fpr[26]
+ lfd fp27, fpuContext->fpr[27]
+ lfd fp28, fpuContext->fpr[28]
+ lfd fp29, fpuContext->fpr[29]
+ lfd fp30, fpuContext->fpr[30]
+ lfd fp31, fpuContext->fpr[31]
+_return:
+ blr
+ // clang-format on
+}
+
+static asm void __OSSaveFPUContext(register u32, register u32, register OSContext* fpuContext) {
+ // clang-format off
+ nofralloc
+
+ lhz r3, fpuContext->state
+ ori r3, r3, 1
+ sth r3, fpuContext->state
+
+ stfd fp0, fpuContext->fpr[0]
+ stfd fp1, fpuContext->fpr[1]
+ stfd fp2, fpuContext->fpr[2]
+ stfd fp3, fpuContext->fpr[3]
+ stfd fp4, fpuContext->fpr[4]
+ stfd fp5, fpuContext->fpr[5]
+ stfd fp6, fpuContext->fpr[6]
+ stfd fp7, fpuContext->fpr[7]
+ stfd fp8, fpuContext->fpr[8]
+ stfd fp9, fpuContext->fpr[9]
+ stfd fp10, fpuContext->fpr[10]
+ stfd fp11, fpuContext->fpr[11]
+ stfd fp12, fpuContext->fpr[12]
+ stfd fp13, fpuContext->fpr[13]
+ stfd fp14, fpuContext->fpr[14]
+ stfd fp15, fpuContext->fpr[15]
+ stfd fp16, fpuContext->fpr[16]
+ stfd fp17, fpuContext->fpr[17]
+ stfd fp18, fpuContext->fpr[18]
+ stfd fp19, fpuContext->fpr[19]
+ stfd fp20, fpuContext->fpr[20]
+ stfd fp21, fpuContext->fpr[21]
+ stfd fp22, fpuContext->fpr[22]
+ stfd fp23, fpuContext->fpr[23]
+ stfd fp24, fpuContext->fpr[24]
+ stfd fp25, fpuContext->fpr[25]
+ stfd fp26, fpuContext->fpr[26]
+ stfd fp27, fpuContext->fpr[27]
+ stfd fp28, fpuContext->fpr[28]
+ stfd fp29, fpuContext->fpr[29]
+ stfd fp30, fpuContext->fpr[30]
+ stfd fp31, fpuContext->fpr[31]
+
+ mffs fp0
+ stfd fp0, OS_CONTEXT_FPSCR(fpuContext)
+
+ lfd fp0, fpuContext->fpr[0]
+
+ mfspr r3, HID2
+ rlwinm. r3, r3, 3, 31, 31
+ bc 12, 2, _return
+
+ psq_st fp0, OS_CONTEXT_PSF0(fpuContext), 0, 0
+ psq_st fp1, OS_CONTEXT_PSF1(fpuContext), 0, 0
+ psq_st fp2, OS_CONTEXT_PSF2(fpuContext), 0, 0
+ psq_st fp3, OS_CONTEXT_PSF3(fpuContext), 0, 0
+ psq_st fp4, OS_CONTEXT_PSF4(fpuContext), 0, 0
+ psq_st fp5, OS_CONTEXT_PSF5(fpuContext), 0, 0
+ psq_st fp6, OS_CONTEXT_PSF6(fpuContext), 0, 0
+ psq_st fp7, OS_CONTEXT_PSF7(fpuContext), 0, 0
+ psq_st fp8, OS_CONTEXT_PSF8(fpuContext), 0, 0
+ psq_st fp9, OS_CONTEXT_PSF9(fpuContext), 0, 0
+ psq_st fp10, OS_CONTEXT_PSF10(fpuContext), 0, 0
+ psq_st fp11, OS_CONTEXT_PSF11(fpuContext), 0, 0
+ psq_st fp12, OS_CONTEXT_PSF12(fpuContext), 0, 0
+ psq_st fp13, OS_CONTEXT_PSF13(fpuContext), 0, 0
+ psq_st fp14, OS_CONTEXT_PSF14(fpuContext), 0, 0
+ psq_st fp15, OS_CONTEXT_PSF15(fpuContext), 0, 0
+ psq_st fp16, OS_CONTEXT_PSF16(fpuContext), 0, 0
+ psq_st fp17, OS_CONTEXT_PSF17(fpuContext), 0, 0
+ psq_st fp18, OS_CONTEXT_PSF18(fpuContext), 0, 0
+ psq_st fp19, OS_CONTEXT_PSF19(fpuContext), 0, 0
+ psq_st fp20, OS_CONTEXT_PSF20(fpuContext), 0, 0
+ psq_st fp21, OS_CONTEXT_PSF21(fpuContext), 0, 0
+ psq_st fp22, OS_CONTEXT_PSF22(fpuContext), 0, 0
+ psq_st fp23, OS_CONTEXT_PSF23(fpuContext), 0, 0
+ psq_st fp24, OS_CONTEXT_PSF24(fpuContext), 0, 0
+ psq_st fp25, OS_CONTEXT_PSF25(fpuContext), 0, 0
+ psq_st fp26, OS_CONTEXT_PSF26(fpuContext), 0, 0
+ psq_st fp27, OS_CONTEXT_PSF27(fpuContext), 0, 0
+ psq_st fp28, OS_CONTEXT_PSF28(fpuContext), 0, 0
+ psq_st fp29, OS_CONTEXT_PSF29(fpuContext), 0, 0
+ psq_st fp30, OS_CONTEXT_PSF30(fpuContext), 0, 0
+ psq_st fp31, OS_CONTEXT_PSF31(fpuContext), 0, 0
+
+_return:
+ blr
+ // clang-format on
+}
+
+asm void OSLoadFPUContext(register OSContext* fpuContext) {
+ // clang-format off
+ nofralloc
+ addi r4, fpuContext, 0
+ b __OSLoadFPUContext
+ // clang-format on
+}
+
+asm void OSSaveFPUContext(register OSContext* fpuContext) {
+ // clang-format off
+ nofralloc
+ addi r5, fpuContext, 0
+ b __OSSaveFPUContext
+ // clang-format on
+}
+
+asm void OSSetCurrentContext(register OSContext* context){
+ // clang-format off
+ nofralloc
+
+ addis r4, r0, OS_CACHED_REGION_PREFIX
+
+ stw context, 0x00D4(r4)
+
+ clrlwi r5, context, 2
+ stw r5, 0x00C0(r4)
+
+ lwz r5, 0x00D8(r4)
+ cmpw r5, context
+ bne _disableFPU
+
+ lwz r6, context->srr1
+ ori r6, r6, 0x2000
+ stw r6, context->srr1
+ mfmsr r6
+ ori r6, r6, 2
+ mtmsr r6
+ blr
+
+_disableFPU:
+ lwz r6, context->srr1
+ rlwinm r6, r6, 0, 19, 17
+ stw r6, context->srr1
+ mfmsr r6
+ rlwinm r6, r6, 0, 19, 17
+ ori r6, r6, 2
+ mtmsr r6
+ isync
+ blr
+ // clang-format on
+}
+
+OSContext* OSGetCurrentContext(void) {
+ return (OSContext*)__OSCurrentContext;
+}
+
+asm u32 OSSaveContext(register OSContext* context) {
+ // clang-format off
+ nofralloc
+ stmw r13, context->gpr[13]
+ mfspr r0, GQR1
+ stw r0, context->gqr[1]
+ mfspr r0, GQR2
+ stw r0, context->gqr[2]
+ mfspr r0, GQR3
+ stw r0, context->gqr[3]
+ mfspr r0, GQR4
+ stw r0, context->gqr[4]
+ mfspr r0, GQR5
+ stw r0, context->gqr[5]
+ mfspr r0, GQR6
+ stw r0, context->gqr[6]
+ mfspr r0, GQR7
+ stw r0, context->gqr[7]
+ mfcr r0
+ stw r0, context->cr
+ mflr r0
+ stw r0, context->lr
+ stw r0, context->srr0
+ mfmsr r0
+ stw r0, context->srr1
+ mfctr r0
+ stw r0, context->ctr
+ mfxer r0
+ stw r0, context->xer
+ stw r1, context->gpr[1]
+ stw r2, context->gpr[2]
+ li r0, 0x1
+ stw r0, context->gpr[3]
+ li r3, 0
+ blr
+ // clang-format on
+}
+
+extern void __RAS_OSDisableInterrupts_begin();
+extern void __RAS_OSDisableInterrupts_end();
+
+asm void OSLoadContext(register OSContext* context) {
+ // clang-format off
+ nofralloc
+
+ lis r4,__RAS_OSDisableInterrupts_begin@ha
+ lwz r6,context->srr0
+ addi r5,r4,__RAS_OSDisableInterrupts_begin@l
+ cmplw r6,r5
+ ble _notInRAS
+ lis r4,__RAS_OSDisableInterrupts_end@ha
+ addi r0,r4,__RAS_OSDisableInterrupts_end@l
+ cmplw r6,r0
+ bge _notInRAS
+ stw r5,context->srr0
+
+_notInRAS:
+
+ lwz r0, context->gpr[0]
+ lwz r1, context->gpr[1]
+ lwz r2, context->gpr[2]
+
+ lhz r4, context->state
+ rlwinm. r5, r4, 0, 30, 30
+ beq notexc
+ rlwinm r4, r4, 0, 31, 29
+ sth r4, context->state
+ lmw r5, context->gpr[5]
+ b misc
+notexc:
+ lmw r13, context->gpr[13]
+misc:
+
+ lwz r4, context->gqr[1]
+ mtspr GQR1, r4
+ lwz r4, context->gqr[2]
+ mtspr GQR2, r4
+ lwz r4, context->gqr[3]
+ mtspr GQR3, r4
+ lwz r4, context->gqr[4]
+ mtspr GQR4, r4
+ lwz r4, context->gqr[5]
+ mtspr GQR5, r4
+ lwz r4, context->gqr[6]
+ mtspr GQR6, r4
+ lwz r4, context->gqr[7]
+ mtspr GQR7, r4
+
+ lwz r4, context->cr
+ mtcr r4
+ lwz r4, context->lr
+ mtlr r4
+ lwz r4, context->ctr
+ mtctr r4
+ lwz r4, context->xer
+ mtxer r4
+
+ mfmsr r4
+ rlwinm r4, r4, 0, 17, 15
+ rlwinm r4, r4, 0, 31, 29
+ mtmsr r4
+
+ lwz r4, context->srr0
+ mtsrr0 r4
+ lwz r4, context->srr1
+ mtsrr1 r4
+
+ lwz r4, context->gpr[4]
+ lwz r3, context->gpr[3]
+
+ rfi
+ // clang-format on
+}
+
+asm u32 OSGetStackPointer() {
+ // clang-format off
+ nofralloc
+ mr r3, r1
+ blr
+ // clang-format on
+}
+
+asm u32 OSSwitchStack(register u32 newsp) {
+ // clang-format off
+ nofralloc
+ mr r5, r1
+ mr r1, newsp
+ mr r3, r5
+ blr
+ // clang-format on
+}
+
+asm int OSSwitchFiber(register u32 pc, register u32 newsp) {
+ // clang-format off
+ nofralloc
+ mflr r0
+ mr r5, r1
+ stwu r5, -8(newsp)
+ mr r1, newsp
+ stw r0, 4(r5)
+ mtlr pc
+ blrl
+ lwz r5, 0(r1)
+ lwz r0, 4(r5)
+ mtlr r0
+ mr r1, r5
+ blr
+ // clang-format on
+}
+
+void OSClearContext(register OSContext* context) {
+ context->mode = 0;
+ context->state = 0;
+ if (context == __OSFPUContext)
+ __OSFPUContext = NULL;
+}
+
+asm void OSInitContext(register OSContext* context, register u32 pc, register u32 newsp) {
+ // clang-format off
+ nofralloc
+
+ stw pc, OS_CONTEXT_SRR0(context)
+ stw newsp, OS_CONTEXT_R1(context)
+ li r11, 0
+ ori r11, r11, 0x00008000 | 0x00000020 | 0x00000010 | 0x00000002 | 0x00001000
+ stw r11, OS_CONTEXT_SRR1(context)
+ li r0, 0x0
+ stw r0, OS_CONTEXT_CR(context)
+ stw r0, OS_CONTEXT_XER(context)
+
+
+ stw r2, OS_CONTEXT_R2(context)
+ stw r13, OS_CONTEXT_R13(context)
+
+ stw r0, OS_CONTEXT_R3(context)
+ stw r0, OS_CONTEXT_R4(context)
+ stw r0, OS_CONTEXT_R5(context)
+ stw r0, OS_CONTEXT_R6(context)
+ stw r0, OS_CONTEXT_R7(context)
+ stw r0, OS_CONTEXT_R8(context)
+ stw r0, OS_CONTEXT_R9(context)
+ stw r0, OS_CONTEXT_R10(context)
+ stw r0, OS_CONTEXT_R11(context)
+ stw r0, OS_CONTEXT_R12(context)
+
+ stw r0, OS_CONTEXT_R14(context)
+ stw r0, OS_CONTEXT_R15(context)
+ stw r0, OS_CONTEXT_R16(context)
+ stw r0, OS_CONTEXT_R17(context)
+ stw r0, OS_CONTEXT_R18(context)
+ stw r0, OS_CONTEXT_R19(context)
+ stw r0, OS_CONTEXT_R20(context)
+ stw r0, OS_CONTEXT_R21(context)
+ stw r0, OS_CONTEXT_R22(context)
+ stw r0, OS_CONTEXT_R23(context)
+ stw r0, OS_CONTEXT_R24(context)
+ stw r0, OS_CONTEXT_R25(context)
+ stw r0, OS_CONTEXT_R26(context)
+ stw r0, OS_CONTEXT_R27(context)
+ stw r0, OS_CONTEXT_R28(context)
+ stw r0, OS_CONTEXT_R29(context)
+ stw r0, OS_CONTEXT_R30(context)
+ stw r0, OS_CONTEXT_R31(context)
+
+ stw r0, OS_CONTEXT_GQR0(context)
+ stw r0, OS_CONTEXT_GQR1(context)
+ stw r0, OS_CONTEXT_GQR2(context)
+ stw r0, OS_CONTEXT_GQR3(context)
+ stw r0, OS_CONTEXT_GQR4(context)
+ stw r0, OS_CONTEXT_GQR5(context)
+ stw r0, OS_CONTEXT_GQR6(context)
+ stw r0, OS_CONTEXT_GQR7(context)
+
+ b OSClearContext
+ // clang-format on
+}
+
+void OSDumpContext(OSContext* context) {
+ u32 i;
+ u32* p;
+
+ OSReport("------------------------- Context 0x%08x -------------------------\n", context);
+
+ for (i = 0; i < 16; ++i) {
+ OSReport("r%-2d = 0x%08x (%14d) r%-2d = 0x%08x (%14d)\n", i, context->gpr[i],
+ context->gpr[i], i + 16, context->gpr[i + 16], context->gpr[i + 16]);
+ }
+
+ OSReport("LR = 0x%08x CR = 0x%08x\n", context->lr, context->cr);
+ OSReport("SRR0 = 0x%08x SRR1 = 0x%08x\n", context->srr0, context->srr1);
+
+ OSReport("\nGQRs----------\n");
+ for (i = 0; i < 4; ++i) {
+ OSReport("gqr%d = 0x%08x \t gqr%d = 0x%08x\n", i, context->gqr[i], i + 4, context->gqr[i + 4]);
+ }
+
+ if (context->state & OS_CONTEXT_STATE_FPSAVED) {
+ OSContext* currentContext;
+ OSContext fpuContext;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ currentContext = OSGetCurrentContext();
+ OSClearContext(&fpuContext);
+ OSSetCurrentContext(&fpuContext);
+
+ OSReport("\n\nFPRs----------\n");
+ for (i = 0; i < 32; i += 2) {
+ OSReport("fr%d \t= %d \t fr%d \t= %d\n", i, (u32)context->fpr[i], i + 1,
+ (u32)context->fpr[i + 1]);
+ }
+ OSReport("\n\nPSFs----------\n");
+ for (i = 0; i < 32; i += 2) {
+ OSReport("ps%d \t= 0x%x \t ps%d \t= 0x%x\n", i, (u32)context->psf[i], i + 1,
+ (u32)context->psf[i + 1]);
+ }
+
+ OSClearContext(&fpuContext);
+ OSSetCurrentContext(currentContext);
+ OSRestoreInterrupts(enabled);
+ }
+
+ OSReport("\nAddress: Back Chain LR Save\n");
+ for (i = 0, p = (u32*)context->gpr[1]; p && (u32)p != 0xffffffff && i++ < 16; p = (u32*)*p) {
+ OSReport("0x%08x: 0x%08x 0x%08x\n", p, p[0], p[1]);
+ }
+}
+
+static asm void OSSwitchFPUContext(register __OSException exception, register OSContext* context) {
+ // clang-format off
+ nofralloc
+ mfmsr r5
+ ori r5, r5, 0x2000
+ mtmsr r5
+ isync
+ lwz r5, OS_CONTEXT_SRR1(context)
+ ori r5, r5, 0x2000
+ mtsrr1 r5
+ addis r3, r0, OS_CACHED_REGION_PREFIX
+ lwz r5, 0x00D8(r3)
+ stw context, 0x00D8(r3)
+ cmpw r5, r4
+ beq _restoreAndExit
+ cmpwi r5, 0x0
+ beq _loadNewFPUContext
+ bl __OSSaveFPUContext
+_loadNewFPUContext:
+ bl __OSLoadFPUContext
+_restoreAndExit:
+ lwz r3, OS_CONTEXT_CR(context)
+ mtcr r3
+ lwz r3, OS_CONTEXT_LR(context)
+ mtlr r3
+ lwz r3, OS_CONTEXT_SRR0(context)
+ mtsrr0 r3
+ lwz r3, OS_CONTEXT_CTR(context)
+ mtctr r3
+ lwz r3, OS_CONTEXT_XER(context)
+ mtxer r3
+ lhz r3, context->state
+ rlwinm r3, r3, 0, 31, 29
+ sth r3, context->state
+ lwz r5, OS_CONTEXT_R5(context)
+ lwz r3, OS_CONTEXT_R3(context)
+ lwz r4, OS_CONTEXT_R4(context)
+ rfi
+ // clang-format on
+}
+
+void __OSContextInit(void) {
+ __OSSetExceptionHandler(__OS_EXCEPTION_FLOATING_POINT, OSSwitchFPUContext);
+ __OSFPUContext = NULL;
+ DBPrintf("FPU-unavailable handler installed\n");
+}
diff --git a/src/Dolphin/os/OSError.c b/src/Dolphin/os/OSError.c
new file mode 100644
index 0000000..3859a62
--- /dev/null
+++ b/src/Dolphin/os/OSError.c
@@ -0,0 +1,360 @@
+#include <dolphin/PPCArch.h>
+#include <dolphin/dsp_regs.h>
+#include <dolphin/dvd_regs.h>
+#include <dolphin/os.h>
+#include <stdio.h>
+
+OSThread* __OSCurrentThread : (OS_BASE_CACHED | 0x00E4);
+OSThreadQueue __OSActiveThreadQueue : (OS_BASE_CACHED | 0x00DC);
+volatile OSContext* __OSFPUContext : (OS_BASE_CACHED | 0x00D8);
+
+OSErrorHandler __OSErrorTable[OS_ERROR_MAX];
+#define FPSCR_ENABLE (FPSCR_VE | FPSCR_OE | FPSCR_UE | FPSCR_ZE | FPSCR_XE)
+u32 __OSFpscrEnableBits = FPSCR_ENABLE;
+
+__declspec(weak) void OSReport(const char* msg, ...) {
+ va_list args;
+ va_start(args, msg);
+ vprintf(msg, args);
+ va_end(args);
+}
+
+__declspec(weak) void OSVReport(const char* msg, va_list list) { vprintf(msg, list); }
+
+__declspec(weak) void OSPanic(const char* file, int line, const char* msg, ...) {
+ va_list marker;
+ u32 i;
+ u32* p;
+
+ OSDisableInterrupts();
+ va_start(marker, msg);
+ vprintf(msg, marker);
+ va_end(marker);
+ OSReport(" in \"%s\" on line %d.\n", file, line);
+
+ OSReport("\nAddress: Back Chain LR Save\n");
+ for (i = 0, p = (u32*)OSGetStackPointer(); p && (u32)p != 0xffffffff && i++ < 16; p = (u32*)*p) {
+ OSReport("0x%08x: 0x%08x 0x%08x\n", p, p[0], p[1]);
+ }
+
+ PPCHalt();
+}
+
+#ifdef FULL_FRANK
+OSErrorHandler OSSetErrorHandler(OSError error, OSErrorHandler handler) {
+ OSErrorHandler oldHandler;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ oldHandler = __OSErrorTable[error];
+ __OSErrorTable[error] = handler;
+
+ if (error == OS_ERROR_FPE) {
+ u32 msr;
+ u32 fpscr;
+ OSThread* thread;
+
+ msr = PPCMfmsr();
+ PPCMtmsr(msr | MSR_FP);
+ fpscr = PPCMffpscr();
+ if (handler) {
+ for (thread = __OSActiveThreadQueue.head; thread; thread = thread->linkActive.next) {
+ thread->context.srr1 |= MSR_FE0 | MSR_FE1;
+ if ((thread->context.state & OS_CONTEXT_STATE_FPSAVED) == 0) {
+ int i;
+ thread->context.state |= OS_CONTEXT_STATE_FPSAVED;
+ for (i = 0; i < 32; ++i) {
+ *(u64*)&thread->context.fpr[i] = (u64)0xffffffffffffffffLL;
+ *(u64*)&thread->context.psf[i] = (u64)0xffffffffffffffffLL;
+ }
+ thread->context.fpscr = FPSCR_NI;
+ }
+ thread->context.fpscr |= __OSFpscrEnableBits & FPSCR_ENABLE;
+ thread->context.fpscr &=
+ ~(FPSCR_VXVC | FPSCR_VXIMZ | FPSCR_VXZDZ | FPSCR_VXIDI | FPSCR_VXISI | FPSCR_VXSNAN |
+ FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI | FPSCR_XX | FPSCR_ZX | FPSCR_UX |
+ FPSCR_OX | FPSCR_FX | FPSCR_FI);
+ }
+ fpscr |= __OSFpscrEnableBits & FPSCR_ENABLE;
+ msr |= MSR_FE0 | MSR_FE1;
+ } else {
+ for (thread = __OSActiveThreadQueue.head; thread; thread = thread->linkActive.next) {
+ thread->context.srr1 &= ~(MSR_FE0 | MSR_FE1);
+ thread->context.fpscr &= ~FPSCR_ENABLE;
+ thread->context.fpscr &=
+ ~(FPSCR_VXVC | FPSCR_VXIMZ | FPSCR_VXZDZ | FPSCR_VXIDI | FPSCR_VXISI | FPSCR_VXSNAN |
+ FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI | FPSCR_XX | FPSCR_ZX | FPSCR_UX |
+ FPSCR_OX | FPSCR_FX | FPSCR_FI);
+ }
+ fpscr &= ~FPSCR_ENABLE;
+ msr &= ~(MSR_FE0 | MSR_FE1);
+ }
+
+ fpscr &= ~(FPSCR_VXVC | FPSCR_VXIMZ | FPSCR_VXZDZ | FPSCR_VXIDI | FPSCR_VXISI | FPSCR_VXSNAN |
+ FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI | FPSCR_XX | FPSCR_ZX | FPSCR_UX |
+ FPSCR_OX | FPSCR_FX | FPSCR_FI);
+
+ PPCMtfpscr(fpscr);
+ PPCMtmsr(msr);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return oldHandler;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm OSErrorHandler OSSetErrorHandler(OSError error, OSErrorHandler handler) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x30(r1)
+ stw r31, 0x2c(r1)
+ stw r30, 0x28(r1)
+ stw r29, 0x24(r1)
+ addi r29, r3, 0
+ stw r28, 0x20(r1)
+ addi r28, r4, 0
+ bl OSDisableInterrupts
+ lis r4, __OSErrorTable@ha
+ rlwinm r5, r29, 2, 0xe, 0x1d
+ addi r0, r4, __OSErrorTable@l
+ clrlwi r6, r29, 0x10
+ add r4, r0, r5
+ lwz r30, 0(r4)
+ cmplwi r6, 0x10
+ mr r29, r3
+ stw r28, 0(r4)
+ bne lbl_8037FD44
+ bl PPCMfmsr
+ addi r31, r3, 0
+ ori r3, r31, 0x2000
+ bl PPCMtmsr
+ bl PPCMffpscr
+ cmplwi r28, 0
+ beq lbl_8037FCD8
+ lis r5, __OSActiveThreadQueue@ha
+ lis r4, 0x6005F8FF@ha
+ lwz r6, __OSActiveThreadQueue@l(r5)
+ addi r4, r4, 0x6005F8FF@l
+ b lbl_8037FCBC
+lbl_8037FBD8:
+ lwz r0, 0x19c(r6)
+ ori r0, r0, 0x900
+ stw r0, 0x19c(r6)
+ lhz r5, 0x1a2(r6)
+ clrlwi. r0, r5, 0x1f
+ bne lbl_8037FC98
+ ori r5, r5, 1
+ li r0, 4
+ sth r5, 0x1a2(r6)
+ mtctr r0
+ addi r5, r6, 0
+lbl_8037FC04:
+ li r0, -1
+ stw r0, 0x94(r5)
+ stw r0, 0x90(r5)
+ stw r0, 0x1cc(r5)
+ stw r0, 0x1c8(r5)
+ stw r0, 0x9c(r5)
+ stw r0, 0x98(r5)
+ stw r0, 0x1d4(r5)
+ stw r0, 0x1d0(r5)
+ stw r0, 0xa4(r5)
+ stw r0, 0xa0(r5)
+ stw r0, 0x1dc(r5)
+ stw r0, 0x1d8(r5)
+ stw r0, 0xac(r5)
+ stw r0, 0xa8(r5)
+ stw r0, 0x1e4(r5)
+ stw r0, 0x1e0(r5)
+ stw r0, 0xb4(r5)
+ stw r0, 0xb0(r5)
+ stw r0, 0x1ec(r5)
+ stw r0, 0x1e8(r5)
+ stw r0, 0xbc(r5)
+ stw r0, 0xb8(r5)
+ stw r0, 0x1f4(r5)
+ stw r0, 0x1f0(r5)
+ stw r0, 0xc4(r5)
+ stw r0, 0xc0(r5)
+ stw r0, 0x1fc(r5)
+ stw r0, 0x1f8(r5)
+ stw r0, 0xcc(r5)
+ stw r0, 0xc8(r5)
+ stw r0, 0x204(r5)
+ stw r0, 0x200(r5)
+ addi r5, r5, 0x40
+ bdnz lbl_8037FC04
+ li r0, 4
+ stw r0, 0x194(r6)
+lbl_8037FC98:
+ lwz r0, __OSFpscrEnableBits
+ lwz r5, 0x194(r6)
+ rlwinm r0, r0, 0, 0x18, 0x1c
+ or r0, r5, r0
+ stw r0, 0x194(r6)
+ lwz r0, 0x194(r6)
+ and r0, r0, r4
+ stw r0, 0x194(r6)
+ lwz r6, 0x2fc(r6)
+lbl_8037FCBC:
+ cmplwi r6, 0
+ bne lbl_8037FBD8
+ lwz r0, __OSFpscrEnableBits
+ ori r31, r31, 0x900
+ rlwinm r0, r0, 0, 0x18, 0x1c
+ or r3, r3, r0
+ b lbl_8037FD2C
+lbl_8037FCD8:
+ lis r5, __OSActiveThreadQueue@ha
+ lis r4, 0x6005F8FF@ha
+ lwz r6, __OSActiveThreadQueue@l(r5)
+ addi r4, r4, 0x6005F8FF@l
+ li r5, -2305
+ b lbl_8037FD18
+lbl_8037FCF0:
+ lwz r0, 0x19c(r6)
+ and r0, r0, r5
+ stw r0, 0x19c(r6)
+ lwz r0, 0x194(r6)
+ rlwinm r0, r0, 0, 0x1d, 0x17
+ stw r0, 0x194(r6)
+ lwz r0, 0x194(r6)
+ and r0, r0, r4
+ stw r0, 0x194(r6)
+ lwz r6, 0x2fc(r6)
+lbl_8037FD18:
+ cmplwi r6, 0
+ bne lbl_8037FCF0
+ li r0, -2305
+ rlwinm r3, r3, 0, 0x1d, 0x17
+ and r31, r31, r0
+lbl_8037FD2C:
+ lis r4, 0x6005F8FF@ha
+ addi r0, r4, 0x6005F8FF@l
+ and r3, r3, r0
+ bl PPCMtfpscr
+ mr r3, r31
+ bl PPCMtmsr
+lbl_8037FD44:
+ mr r3, r29
+ bl OSRestoreInterrupts
+ mr r3, r30
+ lwz r0, 0x34(r1)
+ lwz r31, 0x2c(r1)
+ lwz r30, 0x28(r1)
+ lwz r29, 0x24(r1)
+ lwz r28, 0x20(r1)
+ addi r1, r1, 0x30
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+void __OSUnhandledException(__OSException exception, OSContext* context, u32 dsisr, u32 dar) {
+ OSTime now;
+
+ now = OSGetTime();
+
+ if (!(context->srr1 & MSR_RI)) {
+ OSReport("Non-recoverable Exception %d", exception);
+ } else {
+ if (exception == __OS_EXCEPTION_PROGRAM && (context->srr1 & (0x80000000 >> 11)) &&
+ __OSErrorTable[OS_ERROR_FPE] != 0) {
+ u32 fpscr;
+ u32 msr;
+
+ exception = OS_ERROR_FPE;
+
+ msr = PPCMfmsr();
+ PPCMtmsr(msr | MSR_FP);
+
+ if (__OSFPUContext) {
+ OSSaveFPUContext((OSContext*)__OSFPUContext);
+ }
+
+ fpscr = PPCMffpscr();
+ fpscr &= ~(FPSCR_VXVC | FPSCR_VXIMZ | FPSCR_VXZDZ | FPSCR_VXIDI | FPSCR_VXISI | FPSCR_VXSNAN |
+ FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI | FPSCR_XX | FPSCR_ZX | FPSCR_UX |
+ FPSCR_OX | FPSCR_FX | FPSCR_FI);
+ PPCMtfpscr(fpscr);
+
+ PPCMtmsr(msr);
+
+ if (__OSFPUContext == context) {
+ OSDisableScheduler();
+ __OSErrorTable[exception](exception, context, dsisr, dar);
+ context->srr1 &= ~MSR_FP;
+ __OSFPUContext = NULL;
+
+ context->fpscr &= ~(FPSCR_VXVC | FPSCR_VXIMZ | FPSCR_VXZDZ | FPSCR_VXIDI | FPSCR_VXISI |
+ FPSCR_VXSNAN | FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI | FPSCR_XX |
+ FPSCR_ZX | FPSCR_UX | FPSCR_OX | FPSCR_FX | FPSCR_FI);
+ OSEnableScheduler();
+ __OSReschedule();
+ } else {
+ context->srr1 &= ~MSR_FP;
+ __OSFPUContext = NULL;
+ }
+
+ OSLoadContext(context);
+ }
+
+ if (__OSErrorTable[exception]) {
+ OSDisableScheduler();
+ __OSErrorTable[exception](exception, context, dsisr, dar);
+ OSEnableScheduler();
+ __OSReschedule();
+ OSLoadContext(context);
+ }
+
+ if (exception == OS_ERROR_DECREMENTER) {
+ OSLoadContext(context);
+ }
+
+ OSReport("Unhandled Exception %d", exception);
+ }
+
+ OSReport("\n");
+ OSDumpContext(context);
+ OSReport("\nDSISR = 0x%08x DAR = 0x%08x\n", dsisr, dar);
+ OSReport("TB = 0x%016llx\n", now);
+
+ switch (exception) {
+ case __OS_EXCEPTION_DSI:
+ OSReport("\nInstruction at 0x%x (read from SRR0) attempted to access "
+ "invalid address 0x%x (read from DAR)\n",
+ context->srr0, dar);
+ break;
+ case __OS_EXCEPTION_ISI:
+ OSReport("\nAttempted to fetch instruction from invalid address 0x%x "
+ "(read from SRR0)\n",
+ context->srr0);
+ break;
+ case __OS_EXCEPTION_ALIGNMENT:
+ OSReport("\nInstruction at 0x%x (read from SRR0) attempted to access "
+ "unaligned address 0x%x (read from DAR)\n",
+ context->srr0, dar);
+ break;
+ case __OS_EXCEPTION_PROGRAM:
+ OSReport("\nProgram exception : Possible illegal instruction/operation "
+ "at or around 0x%x (read from SRR0)\n",
+ context->srr0, dar);
+ break;
+ case OS_ERROR_PROTECTION:
+ OSReport("\n");
+ OSReport("AI DMA Address = 0x%04x%04x\n", __DSPRegs[0x00000018], __DSPRegs[0x00000018 + 1]);
+ OSReport("ARAM DMA Address = 0x%04x%04x\n", __DSPRegs[0x00000010], __DSPRegs[0x00000010 + 1]);
+ OSReport("DI DMA Address = 0x%08x\n", __DIRegs[0x00000005]);
+ break;
+ }
+
+ OSReport("\nLast interrupt (%d): SRR0 = 0x%08x TB = 0x%016llx\n", __OSLastInterrupt,
+ __OSLastInterruptSrr0, __OSLastInterruptTime);
+
+ PPCHalt();
+}
diff --git a/src/Dolphin/os/OSFont.c b/src/Dolphin/os/OSFont.c
new file mode 100644
index 0000000..34224bf
--- /dev/null
+++ b/src/Dolphin/os/OSFont.c
@@ -0,0 +1,757 @@
+#include <OS.h>
+#include <gx.h>
+#include <vi.h>
+
+#define ROM_FONT_SJIS_START ((void*)0x001AFF00)
+#define ROM_FONT_SJIS_SIZE 0x0004D000
+#define ROM_FONT_ANSI_START ((void*)0x001FCF00)
+#define ROM_FONT_ANSI_SIZE 0x00003000
+
+typedef const u8* (*ParseStringFunc)(u16, const u8*, OSFontData**, u32*);
+
+static u16 FontEncode = 0xFFFF;
+
+static OSFontData* FontDataAnsi;
+static OSFontData* FontDataSjis;
+static BOOL FixedPitch;
+static ParseStringFunc ParseString;
+
+extern u16 HankakuToCode[];
+extern u16 Zenkaku2Code[];
+
+static const u8* ParseStringS(u16, const u8*, OSFontData**, u32*);
+static const u8* ParseStringW(u16, const u8*, OSFontData**, u32*);
+
+static BOOL IsSjisLeadByte(u8 ch) {
+ return (0x81 <= ch && ch <= 0x9F) || (0xE0 <= ch && ch <= 0xFC);
+}
+
+static BOOL IsSjisTrailByte(u8 ch) {
+ return (0x40 <= ch && ch <= 0xFC) && (ch != 0x7F);
+}
+
+static u32 GetFontCode(u16 encode, u16 code) {
+ u32 tmp;
+ s32 trail;
+
+ if (encode == OS_FONT_ENCODE_SJIS) {
+ if (code >= 0x20 && code <= 0xDF) {
+ return HankakuToCode[code - 0x20];
+ } else if (code > 0x889E && code <= 0x9872) {
+ tmp = ((code >> 8) - 0x88) * 0xBC;
+ trail = code & 0xFF;
+
+ if (!IsSjisTrailByte(trail)) {
+ return 0;
+ }
+
+ trail -= 0x40;
+ if (trail >= 0x40) {
+ trail--;
+ }
+
+ return tmp + trail + 0x2BE;
+ } else if (code >= 0x8140 && code < 0x879E) {
+ tmp = ((code >> 8) - 0x81) * 0xBC;
+ trail = code & 0xFF;
+
+ if (!IsSjisTrailByte(trail)) {
+ return 0;
+ }
+
+ trail -= 0x40;
+ if (trail >= 0x40) {
+ trail--;
+ }
+
+ return Zenkaku2Code[tmp + trail];
+ }
+ } else if (code > 0x20 && code <= 0xFF) {
+ return code - 0x20;
+ }
+
+ return 0;
+}
+
+// 'Yay0' decompression (See YAGCD sections 16.1.1, 16.1.2)
+static void Decode(u8* src, u8* dst) {
+ int j;
+ s32 linkOfs;
+ s32 chunkPos;
+ int i;
+ s32 chunksOfs;
+ u32 maskTblPos;
+ s32 expandSize;
+ s32 linkTblOfs;
+ s32 count;
+ u32 maskBits;
+ u32 mask;
+
+ expandSize = *(s32*)(src + 0x4);
+ linkTblOfs = *(s32*)(src + 0x8);
+ chunksOfs = *(s32*)(src + 0xC);
+
+ i = 0;
+ maskBits = 0;
+ maskTblPos = 16;
+
+ do {
+ // Get next mask
+ if (maskBits == 0) {
+ mask = *(u32*)(src + maskTblPos);
+ maskTblPos += sizeof(u32);
+ maskBits = sizeof(u32) * 8;
+ }
+
+ // Non-linked chunk
+ if (mask & 0x80000000) {
+ dst[i++] = src[chunksOfs++];
+ }
+ // Linked chunk
+ else {
+ // Read offset from link table
+ linkOfs = src[linkTblOfs] << 8 | src[linkTblOfs + 1];
+ linkTblOfs += sizeof(u16);
+
+ // Apply offset
+ chunkPos = i - (linkOfs & 0x0FFF);
+ count = linkOfs >> 12;
+ if (count == 0) {
+ count = src[chunksOfs++] + 0x12;
+ } else {
+ count += 2;
+ }
+
+ // Copy chunk
+ for (j = 0; j < count; j++, i++, chunkPos++) {
+ dst[i] = dst[chunkPos - 1];
+ }
+ }
+
+ // Prepare next mask bit
+ mask <<= 1;
+ maskBits--;
+ } while (i < expandSize);
+}
+
+static u32 GetFontSize(const u8* font) {
+ if (font[0] == 'Y' && font[1] == 'a' && font[2] == 'y') {
+ return *(u32*)(font + 0x4);
+ }
+
+ return 0;
+}
+
+u16 OSGetFontEncode(void) {
+ if (FontEncode != 0xFFFF) {
+ return FontEncode;
+ }
+
+ switch (*(u32*)OSPhysicalToCached(0xcc)) {
+ case VI_NTSC:
+ FontEncode = ((__VIRegs[55] & 2) != 0)
+ ? OS_FONT_ENCODE_SJIS
+ : OS_FONT_ENCODE_ANSI;
+ break;
+ case VI_PAL:
+ case VI_MPAL:
+ case VI_DEBUG:
+ case VI_DEBUG_PAL:
+ case VI_EURGB60:
+ default:
+ FontEncode = OS_FONT_ENCODE_ANSI;
+ }
+
+ ParseString = ParseStringS;
+
+ return FontEncode;
+}
+
+u16 OSSetFontEncode(u16 encode) {
+ u16 old = OSGetFontEncode();
+
+ if (encode <= OS_FONT_ENCODE_UTF32) {
+ FontEncode = encode;
+
+ if (encode >= OS_FONT_ENCODE_UTF8 && encode <= OS_FONT_ENCODE_UTF32) {
+ ParseString = ParseStringW;
+ }
+ }
+
+ return old;
+}
+
+static void ReadROM(void* dst, s32 size, const void* src) {
+ s32 blockSize;
+
+ while (size > 0) {
+ blockSize = (size <= 256) ? size : 256;
+ size -= blockSize;
+
+ while (!__OSReadROM(dst, blockSize, src)) {
+ ;
+ }
+
+ src = (u8*)src + blockSize;
+ dst = (u8*)dst + blockSize;
+ }
+}
+
+static u32 ReadFont(void* dst, u16 encode, OSFontData* font) {
+ u8* tex;
+ int i;
+ u32 code;
+ u32 size;
+ s32 sheet;
+ s32 numRestTex;
+ s32 row;
+ s32 col;
+ u8* tmp;
+
+ if (encode == OS_FONT_ENCODE_SJIS) {
+ ReadROM(dst, ROM_FONT_SJIS_SIZE, ROM_FONT_SJIS_START);
+ } else {
+ ReadROM(dst, ROM_FONT_ANSI_SIZE, ROM_FONT_ANSI_START);
+ }
+
+ size = GetFontSize(dst);
+ if (size == 0) {
+ return 0;
+ }
+
+ Decode(dst, (u8*)font);
+
+ if (encode == OS_FONT_ENCODE_SJIS) {
+ u16 sp28[] = {0x2ABE, 0x003D, 0x003D, 0x003D};
+
+ /**
+ * Find 'T' texture (See OSGetFontTexture)
+ */
+ code = GetFontCode(encode, 'T');
+ // Font sheet on which the texture resides
+ sheet = (s32)code / (font->texNumCol * font->texNumRow);
+ // Number of succeeding textures on the sheet
+ numRestTex = code - (sheet * (font->texNumCol * font->texNumRow));
+ // Texture position on sheet
+ row = numRestTex / font->texNumCol;
+ col = numRestTex - row * font->texNumCol;
+ // Texture position
+ row *= font->cellHeight;
+ col *= font->cellWidth;
+ // Font code texture
+ tex = (u8*)font + font->fontSheetOfs;
+ tex += sheet * font->texSize / 2;
+
+ // Editing the texture at runtime?
+ for (i = 4; i < 8; i++) {
+ tmp = tex + (((font->texWidth / 8) * 32) / 2) * ((row + i) / 8);
+ tmp += (col / 8) * 16;
+ tmp += ((row + i) % 8) * 2;
+ tmp += (col % 8) / 4;
+ *(u16*)tmp = sp28[i - 4];
+ }
+ }
+
+ return size;
+}
+
+u32 OSLoadFont(OSFontData* font, void* dst) {
+ u32 size;
+
+ switch (OSGetFontEncode()) {
+ case OS_FONT_ENCODE_ANSI:
+ FontDataAnsi = font;
+ size = ReadFont(dst, OS_FONT_ENCODE_ANSI, FontDataAnsi);
+ break;
+ case OS_FONT_ENCODE_SJIS:
+ FontDataSjis = font;
+ size = ReadFont(dst, OS_FONT_ENCODE_SJIS, FontDataSjis);
+ break;
+ case OS_FONT_ENCODE_UTF8:
+ case OS_FONT_ENCODE_UTF16:
+ case OS_FONT_ENCODE_UTF32:
+ FontDataAnsi = font;
+ size = ReadFont(dst, OS_FONT_ENCODE_ANSI, FontDataAnsi);
+ if (size == 0) {
+ break;
+ }
+
+ FontDataSjis = (OSFontData*)((u8*)FontDataAnsi + size);
+ size += ReadFont(dst, OS_FONT_ENCODE_SJIS, FontDataSjis);
+ break;
+ case OS_FONT_ENCODE_2:
+ default:
+ size = 0;
+ break;
+ }
+
+ return size;
+}
+
+static const u8* ParseStringS(u16 encode, const u8* str, OSFontData** fontOut,
+ u32* codeOut) {
+ OSFontData* font;
+ u16 code = 0;
+
+ switch (encode) {
+ case OS_FONT_ENCODE_ANSI:
+ font = FontDataAnsi;
+ code = *str;
+ if (code != 0) {
+ str++;
+ }
+ break;
+ case OS_FONT_ENCODE_SJIS:
+ font = FontDataSjis;
+ code = *str;
+ if (code == 0) {
+ break;
+ }
+ str++;
+
+ if (IsSjisLeadByte(code) && IsSjisTrailByte(*str)) {
+ code = (code << 8 | *str++);
+ }
+ break;
+ }
+
+ *fontOut = font;
+ *codeOut = GetFontCode(encode, code);
+
+ return str;
+}
+
+static const u8* ParseStringW(u16 encode, const u8* str, OSFontData** fontOut,
+ u32* codeOut) {
+ OSFontData* font;
+ u16 code = 0;
+ u32 utf32 = 0;
+
+ switch (encode) {
+ case OS_FONT_ENCODE_ANSI:
+ font = FontDataAnsi;
+ code = *str;
+ if (code != 0) {
+ str++;
+ }
+ break;
+ case OS_FONT_ENCODE_SJIS:
+ font = FontDataSjis;
+ code = *str;
+ if (code == 0) {
+ break;
+ }
+ str++;
+
+ if (IsSjisLeadByte(code) && IsSjisTrailByte(*str)) {
+ code = (code << 8 | *str++);
+ }
+ break;
+ case OS_FONT_ENCODE_UTF8:
+ str = (u8 *)OSUTF8to32(str, &utf32);
+ break;
+ case OS_FONT_ENCODE_UTF16:
+ str = (const u8*)OSUTF16to32((const u16*)str, &utf32);
+ break;
+ case OS_FONT_ENCODE_UTF32:
+ utf32 = *(u32*)str;
+ if (utf32 != 0) {
+ str += sizeof(u32);
+ }
+ break;
+ }
+
+ if (utf32 != 0) {
+ encode = OS_FONT_ENCODE_ANSI;
+ font = FontDataAnsi;
+ code = OSUTF32toANSI(utf32);
+
+ if (code == 0 || (FixedPitch && utf32 <= 0x7F)) {
+ code = OSUTF32toSJIS(utf32);
+ if (code != 0) {
+ encode = OS_FONT_ENCODE_SJIS;
+ font = FontDataSjis;
+ }
+ }
+ }
+
+ *fontOut = font;
+ *codeOut = GetFontCode(encode, code);
+
+ return str;
+}
+
+const char* OSGetFontTexel(const char* str, void* dst, s32 xOfs, s32 arg3,
+ u32* widthOut) {
+ OSFontData* font;
+ s32 numRestTex;
+ u8* local_24;
+ s32 row;
+ u8* local_20;
+ s32 col;
+ s32 local_48;
+ u32 code;
+ int j;
+ int i;
+ u32 sheet;
+ u8* local_4C;
+ u8* font_u8;
+ u8* tex;
+ s32 local_44;
+
+ str = (const char*)ParseString(OSGetFontEncode(), (const u8*)str, &font,
+ &code);
+ local_4C = (u8*)font + sizeof(OSFontData);
+
+ /**
+ * Find font code texture (See OSGetFontTexture)
+ */
+ // Font sheet on which the texture resides
+ sheet = (s32)code / (font->texNumCol * font->texNumRow);
+ // Number of succeeding textures on the sheet
+ numRestTex = code - (sheet * (font->texNumCol * font->texNumRow));
+ // Texture position on sheet
+ row = numRestTex / font->texNumCol;
+ col = numRestTex - row * font->texNumCol;
+ // Texture position
+ row *= font->cellHeight;
+ col *= font->cellWidth;
+ // Font code texture
+ tex = (u8*)font + font->fontSheetOfs;
+ tex += sheet * font->texSize / 2;
+
+ for (i = 0; i < font->cellHeight; i++) {
+ for (j = 0; j < font->cellWidth; j++) {
+ local_20 =
+ tex + (((font->texWidth / 8) * 32) / 2) * ((row + i) / 8);
+ local_20 += ((col + j) / 8) * 16;
+ local_20 += ((row + i) % 8) * 2;
+ local_20 += ((col + j) % 8) / 4;
+
+ local_44 = (col + j) % 4;
+
+ local_24 = (u8*)dst + ((i / 8) * (((arg3 * 4) / 8) * 32));
+ local_24 += (((xOfs + j) / 8) * 32);
+ local_24 += ((i % 8) * 4);
+ local_24 += ((xOfs + j) % 8) / 2;
+
+ local_48 = (xOfs + j) % 2;
+
+ *local_24 |=
+ (u8)(local_4C[(*local_20 >> (6 - (local_44 * 2))) & 3] &
+ (local_48 != 0 ? 0x0F : 0xF0));
+ }
+ }
+
+ if (widthOut != NULL) {
+ // TODO: Permuter fake(?)match
+ font_u8 = (u8*)font;
+ *widthOut = (font_u8 + font->charWidthTblOfs)[code];
+ }
+
+ return str;
+}
+
+static void ExpandFontSheet(const OSFontData* font, u8* src, u8* dst) {
+ int i;
+ const u8* tmp = (const u8*)font + sizeof(OSFontData);
+
+ if (font->texFmt == GX_TF_I4) {
+ for (i = (s32)font->fontSheetSize / 2 - 1; i >= 0; i--) {
+ dst[i * 2 + 0] =
+ tmp[src[i] >> 6 & 3] & 0xF0 | tmp[src[i] >> 4 & 3] & 0x0F;
+ dst[i * 2 + 1] =
+ tmp[src[i] >> 2 & 3] & 0xF0 | tmp[src[i] >> 0 & 3] & 0x0F;
+ }
+ } else if (font->texFmt == GX_TF_IA4) {
+ for (i = (s32)font->fontSheetSize / 4 - 1; i >= 0; i--) {
+ dst[i * 4 + 0] = tmp[src[i] >> 6 & 3];
+ dst[i * 4 + 1] = tmp[src[i] >> 4 & 3];
+ dst[i * 4 + 2] = tmp[src[i] >> 2 & 3];
+ dst[i * 4 + 3] = tmp[src[i] >> 0 & 3];
+ }
+ }
+
+ DCStoreRange(dst, font->fontSheetSize);
+}
+
+BOOL OSInitFont(OSFontData* font) {
+ u8* sheets;
+
+ switch (OSGetFontEncode()) {
+ case OS_FONT_ENCODE_ANSI:
+ FontDataAnsi = font;
+ if (ReadFont((u8*)font + 0x1D120, OS_FONT_ENCODE_ANSI, FontDataAnsi) ==
+ 0) {
+ return FALSE;
+ }
+
+ sheets = (u8*)FontDataAnsi + FontDataAnsi->fontSheetOfs;
+ FontDataAnsi->fontSheetOfs = ROUND_UP(FontDataAnsi->fontSheetOfs, 32);
+ ExpandFontSheet(FontDataAnsi, sheets,
+ (u8*)FontDataAnsi + FontDataAnsi->fontSheetOfs);
+ break;
+ case OS_FONT_ENCODE_SJIS:
+ FontDataSjis = font;
+ if (ReadFont((u8*)font + 0xD3F00, OS_FONT_ENCODE_SJIS, FontDataSjis) ==
+ 0) {
+ return FALSE;
+ }
+
+ sheets = (u8*)FontDataSjis + FontDataSjis->fontSheetOfs;
+ FontDataSjis->fontSheetOfs = ROUND_UP(FontDataSjis->fontSheetOfs, 32);
+ ExpandFontSheet(FontDataSjis, sheets,
+ (u8*)FontDataSjis + FontDataSjis->fontSheetOfs);
+ break;
+ case OS_FONT_ENCODE_2:
+ break;
+ case OS_FONT_ENCODE_UTF8:
+ case OS_FONT_ENCODE_UTF16:
+ case OS_FONT_ENCODE_UTF32:
+ FontDataAnsi = font;
+ if (ReadFont((u8*)font + 0xF4020, OS_FONT_ENCODE_ANSI, FontDataAnsi) ==
+ 0) {
+ return FALSE;
+ }
+
+ sheets = (u8*)FontDataAnsi + FontDataAnsi->fontSheetOfs;
+ FontDataAnsi->fontSheetOfs = ROUND_UP(FontDataAnsi->fontSheetOfs, 32);
+ ExpandFontSheet(FontDataAnsi, sheets,
+ (u8*)FontDataAnsi + FontDataAnsi->fontSheetOfs);
+
+ FontDataSjis = (OSFontData*)((u8*)FontDataAnsi + 0x20120);
+ if (ReadFont((u8*)font + 0xF4020, OS_FONT_ENCODE_SJIS, FontDataSjis) ==
+ 0) {
+ return FALSE;
+ }
+
+ sheets = (u8*)FontDataSjis + FontDataSjis->fontSheetOfs;
+ FontDataSjis->fontSheetOfs = ROUND_UP(FontDataSjis->fontSheetOfs, 32);
+ ExpandFontSheet(FontDataSjis, sheets,
+ (u8*)FontDataSjis + FontDataSjis->fontSheetOfs);
+ break;
+ }
+
+ return TRUE;
+}
+
+const char* OSGetFontTexture(const char* str, void** texOut, u32* xOut,
+ u32* yOut, u32* widthOut) {
+ OSFontData* font;
+ s32 numRestTex;
+ u8* font_u8;
+ u32 code;
+ u32 sheet;
+ u32 row;
+ u32 col;
+ u32 tmp;
+
+ str = (const char*)ParseString(OSGetFontEncode(), (const u8*)str, &font,
+ &code);
+
+ // Font sheet on which the texture resides
+ sheet = (s32)code / (font->texNumCol * font->texNumRow);
+ // Font code texture
+ *texOut = (font->texSize * sheet) + ((u8*)font + font->fontSheetOfs);
+
+ // Number of succeeding textures on the sheet
+ // TODO: Permuter fake(?)match
+ tmp = font->texNumRow;
+ numRestTex = code - (sheet * (font->texNumCol * tmp));
+
+ // Sheet row on which the texure resides
+ row = numRestTex / font->texNumCol;
+ // Sheet column on which the texture resides
+ col = numRestTex - row * font->texNumCol;
+
+ // Texture position
+ *xOut = col * font->cellWidth;
+ *yOut = row * font->cellHeight;
+
+ if (widthOut != NULL) {
+ // TODO: Permuter fake(?)match
+ font_u8 = (u8*)font;
+ *widthOut = (font_u8 + font->charWidthTblOfs)[code];
+ }
+
+ return str;
+}
+
+const char* OSGetFontWidth(const char* str, u32* widthOut) {
+ OSFontData* font;
+ u8* font_u8;
+ u32 code;
+
+ str = (const char*)ParseString(OSGetFontEncode(), (const u8*)str, &font,
+ &code);
+
+ if (widthOut != NULL) {
+ // TODO: Permuter fake(?)match
+ font_u8 = (u8*)font;
+ *widthOut = (font_u8 + font->charWidthTblOfs)[code];
+ }
+
+ return str;
+}
+
+static u16 HankakuToCode[] = {
+ 0x020C, 0x020D, 0x020E, 0x020F, 0x0210, 0x0211, 0x0212, 0x0213, 0x0214,
+ 0x0215, 0x0216, 0x0217, 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D,
+ 0x021E, 0x021F, 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226,
+ 0x0227, 0x0228, 0x0229, 0x022A, 0x022B, 0x022C, 0x022D, 0x022E, 0x022F,
+ 0x0230, 0x0231, 0x0232, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237, 0x0238,
+ 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F, 0x0240, 0x0241,
+ 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0249, 0x024A,
+ 0x024B, 0x024C, 0x024D, 0x024E, 0x024F, 0x0250, 0x0251, 0x0252, 0x0253,
+ 0x0254, 0x0255, 0x0256, 0x0257, 0x0258, 0x0259, 0x025A, 0x025B, 0x025C,
+ 0x025D, 0x025E, 0x025F, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265,
+ 0x0266, 0x0267, 0x0268, 0x0269, 0x026A, 0x020C, 0x020C, 0x020C, 0x020C,
+ 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C,
+ 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C,
+ 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C, 0x020C,
+ 0x020C, 0x020C, 0x020C, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F, 0x0270,
+ 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279,
+ 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F, 0x0280, 0x0281, 0x0282,
+ 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028A, 0x028B,
+ 0x028C, 0x028D, 0x028E, 0x028F, 0x0290, 0x0291, 0x0292, 0x0293, 0x0294,
+ 0x0295, 0x0296, 0x0297, 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D,
+ 0x029E, 0x029F, 0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6,
+ 0x02A7, 0x02A8, 0x02A9};
+
+static u16 Zenkaku2Code[] = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
+ 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011,
+ 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A,
+ 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C,
+ 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+ 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E,
+ 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
+ 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059,
+ 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062,
+ 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072,
+ 0x0073, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083,
+ 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E,
+ 0x008F, 0x0090, 0x0091, 0x0000, 0x0000, 0x0000, 0x0000, 0x0092, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0093, 0x0094, 0x0095, 0x0096,
+ 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1,
+ 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA,
+ 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3,
+ 0x00B4, 0x00B5, 0x00B6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8,
+ 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6,
+ 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8,
+ 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1,
+ 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA,
+ 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, 0x0100, 0x0101, 0x0102, 0x0103,
+ 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010B, 0x010C,
+ 0x010D, 0x010E, 0x010F, 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115,
+ 0x0116, 0x0117, 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E,
+ 0x011F, 0x0120, 0x0121, 0x0122, 0x0123, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0124, 0x0125,
+ 0x0126, 0x0127, 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E,
+ 0x012F, 0x0130, 0x0131, 0x0132, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137,
+ 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x013F, 0x0140,
+ 0x0141, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, 0x0148, 0x0149,
+ 0x014A, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, 0x0150, 0x0151, 0x0152,
+ 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, 0x0158, 0x0159, 0x015A, 0x015B,
+ 0x015C, 0x015D, 0x015E, 0x015F, 0x0160, 0x0161, 0x0162, 0x0163, 0x0164,
+ 0x0165, 0x0166, 0x0167, 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D,
+ 0x016E, 0x016F, 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176,
+ 0x0177, 0x0178, 0x0179, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, 0x0180,
+ 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, 0x0188, 0x0189,
+ 0x018A, 0x018B, 0x018C, 0x018D, 0x018E, 0x018F, 0x0190, 0x0191, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0193,
+ 0x0194, 0x0195, 0x0196, 0x0197, 0x0198, 0x0199, 0x019A, 0x019B, 0x019C,
+ 0x019D, 0x019E, 0x019F, 0x01A0, 0x01A1, 0x01A2, 0x01A3, 0x01A4, 0x01A5,
+ 0x01A6, 0x01A7, 0x01A8, 0x01A9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01AA, 0x01AB, 0x01AC,
+ 0x01AD, 0x01AE, 0x01AF, 0x01B0, 0x01B1, 0x01B2, 0x01B3, 0x01B4, 0x01B5,
+ 0x01B6, 0x01B7, 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BD, 0x01BE,
+ 0x01BF, 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C4, 0x01C5, 0x01C6, 0x01C7,
+ 0x01C8, 0x01C9, 0x01CA, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x01CB, 0x01CC, 0x01CD, 0x01CE, 0x01CF, 0x01D0, 0x01D1, 0x01D2, 0x01D3,
+ 0x01D4, 0x01D5, 0x01D6, 0x01D7, 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC,
+ 0x01DD, 0x01DE, 0x01DF, 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E4, 0x01E5,
+ 0x01E6, 0x01E7, 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x01EC, 0x01ED, 0x01EE, 0x01EF, 0x01F0, 0x01F1, 0x01F2, 0x01F3,
+ 0x01F4, 0x01F5, 0x01F6, 0x01F7, 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC,
+ 0x01FD, 0x01FE, 0x01FF, 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205,
+ 0x0206, 0x0207, 0x0208, 0x0209, 0x020A, 0x020B, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x020C, 0x020D, 0x020E, 0x020F, 0x0210,
+ 0x0211, 0x0212, 0x0213, 0x0214, 0x0215, 0x0216, 0x0217, 0x0218, 0x0219,
+ 0x021A, 0x021B, 0x021C, 0x021D, 0x021E, 0x021F, 0x0220, 0x0221, 0x0222,
+ 0x0223, 0x0224, 0x0225, 0x0226, 0x0227, 0x0228, 0x0229, 0x022A, 0x022B,
+ 0x022C, 0x022D, 0x022E, 0x022F, 0x0230, 0x0231, 0x0232, 0x0233, 0x0234,
+ 0x0235, 0x0236, 0x0237, 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D,
+ 0x023E, 0x023F, 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246,
+ 0x0247, 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F,
+ 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, 0x0258,
+ 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F, 0x0260, 0x0261,
+ 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, 0x0268, 0x0269, 0x026A,
+ 0x026B, 0x026C, 0x026D, 0x026E, 0x026F, 0x0270, 0x0271, 0x0272, 0x0273,
+ 0x0274, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279, 0x027A, 0x027B, 0x027C,
+ 0x027D, 0x027E, 0x027F, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285,
+ 0x0286, 0x0287, 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E,
+ 0x028F, 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
+ 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F, 0x02A0,
+ 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7, 0x02A8, 0x02A9,
+ 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF, 0x02B0, 0x02B1, 0x02B2,
+ 0x02B3, 0x02B4, 0x02B5, 0x02B6, 0x02B7, 0x02B8, 0x02B9, 0x02BA, 0x02BB,
+ 0x02BC, 0x02BD, 0x02BE, 0x02BF, 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4,
+ 0x02C5, 0x02C6, 0x02C7, 0x02C8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE,
+ 0x02CF, 0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7,
+ 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x02DE, 0x02DF, 0x02E0,
+ 0x02E1, 0x02E2, 0x02E3, 0x02E4, 0x02E5, 0x02E6, 0x0000, 0x02E7, 0x02E8,
+ 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF, 0x02F0, 0x02F1,
+ 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7, 0x02F8, 0x02F9, 0x02FA,
+ 0x02FB, 0x02FC, 0x02FD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x02FE, 0x02FF, 0x0300, 0x0301, 0x0302, 0x0303, 0x0304,
+ 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D,
+ 0x030E, 0x030F, 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316,
+ 0x0317, 0x0318, 0x0319, 0x031A, 0x031B, 0x0000};
diff --git a/src/Dolphin/os/OSInterrupt.c b/src/Dolphin/os/OSInterrupt.c
new file mode 100644
index 0000000..30b6e81
--- /dev/null
+++ b/src/Dolphin/os/OSInterrupt.c
@@ -0,0 +1,433 @@
+#include <dolphin/os.h>
+
+static asm void ExternalInterruptHandler(register __OSException exception,
+ register OSContext* context);
+
+// TODO: Move these to a more appropriate location
+vu32 __PIRegs[12] : 0xCC003000;
+vu32 __EXIRegs[16] : 0xCC006800;
+vu16 __MEMRegs[64] : 0xCC004000;
+vu16 __DSPRegs[32] : 0xCC005000;
+vu32 __AIRegs[8] : 0xCC006C00;
+
+extern void __RAS_OSDisableInterrupts_begin(void);
+extern void __RAS_OSDisableInterrupts_end(void);
+
+static __OSInterruptHandler* InterruptHandlerTable;
+
+static OSInterruptMask InterruptPrioTable[] = {
+ OS_INTERRUPTMASK_PI_ERROR,
+ OS_INTERRUPTMASK_PI_DEBUG,
+ OS_INTERRUPTMASK_MEM,
+ OS_INTERRUPTMASK_PI_RSW,
+ OS_INTERRUPTMASK_PI_VI,
+ OS_INTERRUPTMASK_PI_PE,
+ OS_INTERRUPTMASK_PI_HSP,
+ OS_INTERRUPTMASK_DSP_ARAM | OS_INTERRUPTMASK_DSP_DSP | OS_INTERRUPTMASK_AI |
+ OS_INTERRUPTMASK_EXI | OS_INTERRUPTMASK_PI_SI | OS_INTERRUPTMASK_PI_DI,
+ OS_INTERRUPTMASK_DSP_AI,
+ OS_INTERRUPTMASK_PI_CP,
+ 0xFFFFFFFF,
+};
+
+asm BOOL OSDisableInterrupts(void) {
+ // clang-format off
+ nofralloc
+entry __RAS_OSDisableInterrupts_begin
+ mfmsr r3
+ rlwinm r4, r3, 0, 17, 15
+ mtmsr r4
+entry __RAS_OSDisableInterrupts_end
+ rlwinm r3, r3, 17, 31, 31
+ blr
+ // clang-format on
+}
+asm BOOL OSEnableInterrupts(void) {
+ // clang-format off
+ nofralloc
+
+ mfmsr r3
+ ori r4, r3, 0x8000
+ mtmsr r4
+ rlwinm r3, r3, 17, 31, 31
+ blr
+ // clang-format on
+}
+
+asm BOOL OSRestoreInterrupts(register BOOL level){
+ // clang-format off
+
+ nofralloc
+
+ cmpwi level, 0
+ mfmsr r4
+ beq _disable
+ ori r5, r4, 0x8000
+ b _restore
+_disable:
+ rlwinm r5, r4, 0, 17, 15
+_restore:
+ mtmsr r5
+ rlwinm r3, r4, 17, 31, 31
+ blr
+ // clang-format on
+}
+
+__OSInterruptHandler
+ __OSSetInterruptHandler(__OSInterrupt interrupt, __OSInterruptHandler handler) {
+ __OSInterruptHandler oldHandler;
+
+ oldHandler = InterruptHandlerTable[interrupt];
+ InterruptHandlerTable[interrupt] = handler;
+ return oldHandler;
+}
+
+__OSInterruptHandler __OSGetInterruptHandler(__OSInterrupt interrupt) {
+ return InterruptHandlerTable[interrupt];
+}
+
+void __OSInterruptInit(void) {
+ InterruptHandlerTable = OSPhysicalToCached(0x3040);
+ memset(InterruptHandlerTable, 0, __OS_INTERRUPT_MAX * sizeof(__OSInterruptHandler));
+
+ *(OSInterruptMask*)OSPhysicalToCached(0x00C4) = 0;
+
+ *(OSInterruptMask*)OSPhysicalToCached(0x00C8) = 0;
+
+ __PIRegs[1] = 0xf0;
+
+ __OSMaskInterrupts(OS_INTERRUPTMASK_MEM | OS_INTERRUPTMASK_DSP | OS_INTERRUPTMASK_AI |
+ OS_INTERRUPTMASK_EXI | OS_INTERRUPTMASK_PI);
+
+ __OSSetExceptionHandler(4, ExternalInterruptHandler);
+}
+
+u32 SetInterruptMask(OSInterruptMask mask, OSInterruptMask current) {
+ u32 reg;
+
+ switch (__cntlzw(mask)) {
+ case __OS_INTERRUPT_MEM_0:
+ case __OS_INTERRUPT_MEM_1:
+ case __OS_INTERRUPT_MEM_2:
+ case __OS_INTERRUPT_MEM_3:
+ case __OS_INTERRUPT_MEM_ADDRESS:
+ reg = 0;
+ if (!(current & OS_INTERRUPTMASK_MEM_0))
+ reg |= 0x1;
+ if (!(current & OS_INTERRUPTMASK_MEM_1))
+ reg |= 0x2;
+ if (!(current & OS_INTERRUPTMASK_MEM_2))
+ reg |= 0x4;
+ if (!(current & OS_INTERRUPTMASK_MEM_3))
+ reg |= 0x8;
+ if (!(current & OS_INTERRUPTMASK_MEM_ADDRESS))
+ reg |= 0x10;
+ __MEMRegs[0x0000000e] = (u16)reg;
+ mask &= ~OS_INTERRUPTMASK_MEM;
+ break;
+ case __OS_INTERRUPT_DSP_AI:
+ case __OS_INTERRUPT_DSP_ARAM:
+ case __OS_INTERRUPT_DSP_DSP:
+ reg = __DSPRegs[0x00000005];
+ reg &= ~0x1F8;
+ if (!(current & OS_INTERRUPTMASK_DSP_AI))
+ reg |= 0x10;
+ if (!(current & OS_INTERRUPTMASK_DSP_ARAM))
+ reg |= 0x40;
+ if (!(current & OS_INTERRUPTMASK_DSP_DSP))
+ reg |= 0x100;
+ __DSPRegs[0x00000005] = (u16)reg;
+ mask &= ~OS_INTERRUPTMASK_DSP;
+ break;
+ case __OS_INTERRUPT_AI_AI:
+ reg = __AIRegs[0];
+ reg &= ~0x2C;
+ if (!(current & OS_INTERRUPTMASK_AI_AI))
+ reg |= 0x4;
+ __AIRegs[0] = reg;
+ mask &= ~OS_INTERRUPTMASK_AI;
+ break;
+ case __OS_INTERRUPT_EXI_0_EXI:
+ case __OS_INTERRUPT_EXI_0_TC:
+ case __OS_INTERRUPT_EXI_0_EXT:
+ reg = __EXIRegs[0];
+ reg &= ~0x2C0F;
+ if (!(current & OS_INTERRUPTMASK_EXI_0_EXI))
+ reg |= 0x1;
+ if (!(current & OS_INTERRUPTMASK_EXI_0_TC))
+ reg |= 0x4;
+ if (!(current & OS_INTERRUPTMASK_EXI_0_EXT))
+ reg |= 0x400;
+ __EXIRegs[0] = reg;
+ mask &= ~OS_INTERRUPTMASK_EXI_0;
+ break;
+ case __OS_INTERRUPT_EXI_1_EXI:
+ case __OS_INTERRUPT_EXI_1_TC:
+ case __OS_INTERRUPT_EXI_1_EXT:
+ reg = __EXIRegs[5];
+ reg &= ~0xC0F;
+
+ if (!(current & OS_INTERRUPTMASK_EXI_1_EXI))
+ reg |= 0x1;
+ if (!(current & OS_INTERRUPTMASK_EXI_1_TC))
+ reg |= 0x4;
+ if (!(current & OS_INTERRUPTMASK_EXI_1_EXT))
+ reg |= 0x400;
+ __EXIRegs[5] = reg;
+ mask &= ~OS_INTERRUPTMASK_EXI_1;
+ break;
+ case __OS_INTERRUPT_EXI_2_EXI:
+ case __OS_INTERRUPT_EXI_2_TC:
+ reg = __EXIRegs[10];
+ reg &= ~0xF;
+ if (!(current & OS_INTERRUPTMASK_EXI_2_EXI))
+ reg |= 0x1;
+ if (!(current & OS_INTERRUPTMASK_EXI_2_TC))
+ reg |= 0x4;
+
+ __EXIRegs[10] = reg;
+ mask &= ~OS_INTERRUPTMASK_EXI_2;
+ break;
+ case __OS_INTERRUPT_PI_CP:
+ case __OS_INTERRUPT_PI_SI:
+ case __OS_INTERRUPT_PI_DI:
+ case __OS_INTERRUPT_PI_RSW:
+ case __OS_INTERRUPT_PI_ERROR:
+ case __OS_INTERRUPT_PI_VI:
+ case __OS_INTERRUPT_PI_DEBUG:
+ case __OS_INTERRUPT_PI_PE_TOKEN:
+ case __OS_INTERRUPT_PI_PE_FINISH:
+ case __OS_INTERRUPT_PI_HSP:
+ reg = 0xF0;
+
+ if (!(current & OS_INTERRUPTMASK_PI_CP)) {
+ reg |= 0x800;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_SI)) {
+ reg |= 0x8;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_DI)) {
+ reg |= 0x4;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_RSW)) {
+ reg |= 0x2;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_ERROR)) {
+ reg |= 0x1;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_VI)) {
+ reg |= 0x100;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_DEBUG)) {
+ reg |= 0x1000;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_PE_TOKEN)) {
+ reg |= 0x200;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_PE_FINISH)) {
+ reg |= 0x400;
+ }
+ if (!(current & OS_INTERRUPTMASK_PI_HSP)) {
+ reg |= 0x2000;
+ }
+ __PIRegs[1] = reg;
+ mask &= ~OS_INTERRUPTMASK_PI;
+ break;
+ default:
+ break;
+ }
+ return mask;
+}
+
+OSInterruptMask OSGetInterruptMask(void) { return *(OSInterruptMask*)OSPhysicalToCached(0x00C8); }
+
+OSInterruptMask OSSetInterruptMask(OSInterruptMask local) {
+ BOOL enabled;
+ OSInterruptMask global;
+ OSInterruptMask prev;
+ OSInterruptMask mask;
+
+ enabled = OSDisableInterrupts();
+ global = *(OSInterruptMask*)OSPhysicalToCached(0x00C4);
+ prev = *(OSInterruptMask*)OSPhysicalToCached(0x00C8);
+ mask = (global | prev) ^ local;
+ *(OSInterruptMask*)OSPhysicalToCached(0x00C8) = local;
+ while (mask) {
+ mask = SetInterruptMask(mask, global | local);
+ }
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+OSInterruptMask __OSMaskInterrupts(OSInterruptMask global) {
+ BOOL enabled;
+ OSInterruptMask prev;
+ OSInterruptMask local;
+ OSInterruptMask mask;
+
+ enabled = OSDisableInterrupts();
+ prev = *(OSInterruptMask*)OSPhysicalToCached(0x00C4);
+ local = *(OSInterruptMask*)OSPhysicalToCached(0x00C8);
+ mask = ~(prev | local) & global;
+ global |= prev;
+ *(OSInterruptMask*)OSPhysicalToCached(0x00C4) = global;
+ while (mask) {
+ mask = SetInterruptMask(mask, global | local);
+ }
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+OSInterruptMask __OSUnmaskInterrupts(OSInterruptMask global) {
+ BOOL enabled;
+ OSInterruptMask prev;
+ OSInterruptMask local;
+ OSInterruptMask mask;
+
+ enabled = OSDisableInterrupts();
+ prev = *(OSInterruptMask*)OSPhysicalToCached(0x00C4);
+ local = *(OSInterruptMask*)OSPhysicalToCached(0x00C8);
+ mask = (prev | local) & global;
+ global = prev & ~global;
+ *(OSInterruptMask*)OSPhysicalToCached(0x00C4) = global;
+ while (mask) {
+ mask = SetInterruptMask(mask, global | local);
+ }
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+volatile OSTime __OSLastInterruptTime;
+volatile __OSInterrupt __OSLastInterrupt;
+volatile u32 __OSLastInterruptSrr0;
+
+void __OSDispatchInterrupt(__OSException exception, OSContext* context) {
+ u32 intsr;
+ u32 reg;
+ OSInterruptMask cause;
+ OSInterruptMask unmasked;
+ OSInterruptMask* prio;
+ __OSInterrupt interrupt;
+ __OSInterruptHandler handler;
+ intsr = __PIRegs[0];
+ intsr &= ~0x00010000;
+
+ if (intsr == 0 || (intsr & __PIRegs[1]) == 0) {
+ OSLoadContext(context);
+ }
+
+ cause = 0;
+
+ if (intsr & 0x00000080) {
+ reg = __MEMRegs[15];
+ if (reg & 0x1)
+ cause |= OS_INTERRUPTMASK_MEM_0;
+ if (reg & 0x2)
+ cause |= OS_INTERRUPTMASK_MEM_1;
+ if (reg & 0x4)
+ cause |= OS_INTERRUPTMASK_MEM_2;
+ if (reg & 0x8)
+ cause |= OS_INTERRUPTMASK_MEM_3;
+ if (reg & 0x10)
+ cause |= OS_INTERRUPTMASK_MEM_ADDRESS;
+ }
+
+ if (intsr & 0x00000040) {
+ reg = __DSPRegs[5];
+ if (reg & 0x8)
+ cause |= OS_INTERRUPTMASK_DSP_AI;
+ if (reg & 0x20)
+ cause |= OS_INTERRUPTMASK_DSP_ARAM;
+ if (reg & 0x80)
+ cause |= OS_INTERRUPTMASK_DSP_DSP;
+ }
+
+ if (intsr & 0x00000020) {
+ reg = __AIRegs[0];
+ if (reg & 0x8)
+ cause |= OS_INTERRUPTMASK_AI_AI;
+ }
+
+ if (intsr & 0x00000010) {
+ reg = __EXIRegs[0];
+ if (reg & 0x2)
+ cause |= OS_INTERRUPTMASK_EXI_0_EXI;
+ if (reg & 0x8)
+ cause |= OS_INTERRUPTMASK_EXI_0_TC;
+ if (reg & 0x800)
+ cause |= OS_INTERRUPTMASK_EXI_0_EXT;
+ reg = __EXIRegs[5];
+ if (reg & 0x2)
+ cause |= OS_INTERRUPTMASK_EXI_1_EXI;
+ if (reg & 0x8)
+ cause |= OS_INTERRUPTMASK_EXI_1_TC;
+ if (reg & 0x800)
+ cause |= OS_INTERRUPTMASK_EXI_1_EXT;
+ reg = __EXIRegs[10];
+ if (reg & 0x2)
+ cause |= OS_INTERRUPTMASK_EXI_2_EXI;
+ if (reg & 0x8)
+ cause |= OS_INTERRUPTMASK_EXI_2_TC;
+ }
+
+ if (intsr & 0x00002000)
+ cause |= OS_INTERRUPTMASK_PI_HSP;
+ if (intsr & 0x00001000)
+ cause |= OS_INTERRUPTMASK_PI_DEBUG;
+ if (intsr & 0x00000400)
+ cause |= OS_INTERRUPTMASK_PI_PE_FINISH;
+ if (intsr & 0x00000200)
+ cause |= OS_INTERRUPTMASK_PI_PE_TOKEN;
+ if (intsr & 0x00000100)
+ cause |= OS_INTERRUPTMASK_PI_VI;
+ if (intsr & 0x00000008)
+ cause |= OS_INTERRUPTMASK_PI_SI;
+ if (intsr & 0x00000004)
+ cause |= OS_INTERRUPTMASK_PI_DI;
+ if (intsr & 0x00000002)
+ cause |= OS_INTERRUPTMASK_PI_RSW;
+ if (intsr & 0x00000800)
+ cause |= OS_INTERRUPTMASK_PI_CP;
+ if (intsr & 0x00000001)
+ cause |= OS_INTERRUPTMASK_PI_ERROR;
+
+ unmasked = cause & ~(*(OSInterruptMask*)OSPhysicalToCached(0x00C4) |
+ *(OSInterruptMask*)OSPhysicalToCached(0x00C8));
+ if (unmasked) {
+ for (prio = InterruptPrioTable;; ++prio) {
+ if (unmasked & *prio) {
+ interrupt = (__OSInterrupt)__cntlzw(unmasked & *prio);
+ break;
+ }
+ }
+
+ handler = __OSGetInterruptHandler(interrupt);
+ if (handler) {
+ if (__OS_INTERRUPT_MEM_ADDRESS < interrupt) {
+ __OSLastInterrupt = interrupt;
+ __OSLastInterruptTime = OSGetTime();
+ __OSLastInterruptSrr0 = context->srr0;
+ }
+
+ OSDisableScheduler();
+ handler(interrupt, context);
+ OSEnableScheduler();
+ __OSReschedule();
+ OSLoadContext(context);
+ }
+ }
+
+ OSLoadContext(context);
+}
+
+static asm void ExternalInterruptHandler(register __OSException exception,
+ register OSContext* context) {
+#pragma unused(exception)
+ // clang-format off
+ nofralloc
+ OS_EXCEPTION_SAVE_GPRS(context)
+
+ stwu r1, -8(r1)
+ b __OSDispatchInterrupt
+ // clang-format on
+}
diff --git a/src/Dolphin/os/OSLink.c b/src/Dolphin/os/OSLink.c
new file mode 100644
index 0000000..933e55b
--- /dev/null
+++ b/src/Dolphin/os/OSLink.c
@@ -0,0 +1,556 @@
+#include "dolphin/os.h"
+
+#define SHN_UNDEF 0
+#define SHN_LORESERVE 0xff00
+#define SHN_LOPROC 0xff00
+#define SHN_HIPROC 0xff1f
+#define SHN_ABS 0xfff1
+#define SHN_COMMON 0xfff2
+#define SHN_HIRESERVE 0xffff
+
+#define ELF32_R_SYM(i) ((i) >> 8)
+#define ELF32_R_TYPE(i) ((unsigned char)(i))
+#define ELF32_R_INFO(s, t) (((s) << 8) + (unsigned char)(t))
+
+// Name Value Field Calculation
+#define R_PPC_NONE 0 // none none
+#define R_PPC_ADDR32 1 // word32 S + A
+#define R_PPC_ADDR24 2 // low24* (S + A) >> 2
+#define R_PPC_ADDR16 3 // half16* S + A
+#define R_PPC_ADDR16_LO 4 // half16 #lo(S + A)
+#define R_PPC_ADDR16_HI 5 // half16 #hi(S + A)
+#define R_PPC_ADDR16_HA 6 // half16 #ha(S + A)
+#define R_PPC_ADDR14 7 // low14* (S + A) >> 2
+#define R_PPC_ADDR14_BRTAKEN 8 // low14* (S + A) >> 2
+#define R_PPC_ADDR14_BRNTAKEN 9 // low14* (S + A) >> 2
+#define R_PPC_REL24 10 // low24* (S + A - P) >> 2
+#define R_PPC_REL14 11 // low14* (S + A - P) >> 2
+#define R_PPC_REL14_BRTAKEN 12 // low14* (S + A - P) >> 2
+#define R_PPC_REL14_BRNTAKEN 13 // low14* (S + A - P) >> 2
+
+#define R_PPC_GOT16 14 // half16* G + A
+#define R_PPC_GOT16_LO 15 // half16 #lo(G + A)
+#define R_PPC_GOT16_HI 16 // half16 #hi(G + A)
+#define R_PPC_GOT16_HA 17 // half16 #ha(G + A)
+#define R_PPC_PLTREL24 18 // low24* (L + A - P) >> 2
+#define R_PPC_COPY 19 // none none
+#define R_PPC_GLOB_DAT 20 // word32 S + A
+#define R_PPC_JMP_SLOT 21 // none
+#define R_PPC_RELATIVE 22 // word32 B + A
+
+#define R_PPC_LOCAL24PC 23 // low24*
+
+#define R_PPC_UADDR32 24 // word32 S + A
+#define R_PPC_UADDR16 25 // half16* S + A
+#define R_PPC_REL32 26 // word32 S + A - P
+
+#define R_PPC_PLT32 27 // word32 L + A
+#define R_PPC_PLTREL32 28 // word32 L + A - P
+#define R_PPC_PLT16_LO 29 // half16 #lo(L + A)
+#define R_PPL_PLT16_HI 30 // half16 #hi(L + A)
+#define R_PPC_PLT16_HA 31 // half16 #ha(L + A)
+
+#define R_PPC_SDAREL16 32 // half16* S + A - _SDA_BASE_
+#define R_PPC_SECTOFF 33 // half16* R + A
+#define R_PPC_SECTOFF_LO 34 // half16 #lo(R + A)
+#define R_PPC_SECTOFF_HI 35 // half16 #hi(R + A)
+#define R_PPC_SECTOFF_HA 36 // half16 #ha(R + A)
+#define R_PPC_ADDR30 37 // word30 (S + A - P) >> 2
+
+#define R_PPC_EMB_NADDR32 101 // uword32 N (A - S)
+#define R_PPC_EMB_NADDR16 102 // uhalf16 Y (A - S)
+#define R_PPC_EMB_NADDR16_LO 103 // uhalf16 N #lo(A - S)
+#define R_PPC_EMB_NADDR16_HI 104 // uhalf16 N #hi(A - S)
+#define R_PPC_EMB_NADDR16_HA 105 // uhalf16 N #ha(A - S)
+#define R_PPC_EMB_SDAI16 106 // uhalf16 Y T
+#define R_PPC_EMB_SDA2I16 107 // uhalf16 Y U
+#define R_PPC_EMB_SDA2REL 108 // uhalf16 Y S + A - _SDA2_BASE_
+#define R_PPC_EMB_SDA21 109 // ulow21 N
+#define R_PPC_EMB_MRKREF 110 // none N
+#define R_PPC_EMB_RELSEC16 111 // uhalf16 Y V + A
+#define R_PPC_EMB_RELST_LO 112 // uhalf16 N #lo(W + A)
+#define R_PPC_EMB_RELST_HI 113 // uhalf16 N #hi(W + A)
+#define R_PPC_EMB_RELST_HA 114 // uhalf16 N #ha(W + A)
+#define R_PPC_EMB_BIT_FLD 115 // uword32 Y
+#define R_PPC_EMB_RELSDA 116 // uhalf16 Y
+
+OSModuleQueue __OSModuleInfoList : (OS_BASE_CACHED | 0x30C8);
+const void* __OSStringTable : (OS_BASE_CACHED | 0x30D0);
+
+#pragma dont_inline on
+__declspec(weak) void OSNotifyLink(OSModuleInfo* module) {}
+
+__declspec(weak) void OSNotifyUnlink(OSModuleInfo* module) {}
+
+#pragma dont_inline reset
+
+#define EnqueueTail(queue, moduleInfo, link) \
+ do { \
+ OSModuleInfo* __prev; \
+ \
+ __prev = (queue)->tail; \
+ if (__prev == NULL) \
+ (queue)->head = (moduleInfo); \
+ else \
+ __prev->link.next = (moduleInfo); \
+ (moduleInfo)->link.prev = __prev; \
+ (moduleInfo)->link.next = NULL; \
+ (queue)->tail = (moduleInfo); \
+ } while (0)
+
+#define DequeueItem(queue, moduleInfo, link) \
+ do { \
+ OSModuleInfo* __next; \
+ OSModuleInfo* __prev; \
+ \
+ __next = (moduleInfo)->link.next; \
+ __prev = (moduleInfo)->link.prev; \
+ \
+ if (__next == NULL) \
+ (queue)->tail = __prev; \
+ else \
+ __next->link.prev = __prev; \
+ \
+ if (__prev == NULL) \
+ (queue)->head = __next; \
+ else \
+ __prev->link.next = __next; \
+ } while (0)
+
+void OSSetStringTable(const void* stringTable) { __OSStringTable = stringTable; }
+
+static BOOL Relocate(OSModuleHeader* newModule, OSModuleHeader* module) {
+ OSModuleID idNew;
+ OSImportInfo* imp;
+ OSRel* rel;
+ OSSectionInfo* si;
+ OSSectionInfo* siFlush;
+ u32* p;
+ u32 offset;
+ u32 x;
+
+ idNew = newModule ? newModule->info.id : 0;
+ for (imp = (OSImportInfo*)module->impOffset;
+ imp < (OSImportInfo*)(module->impOffset + module->impSize); imp++) {
+ if (imp->id == idNew) {
+ goto Found;
+ }
+ }
+ return FALSE;
+
+Found:
+ siFlush = 0;
+ for (rel = (OSRel*)imp->offset; rel->type != R_DOLPHIN_END; rel++) {
+ (u8*)p += rel->offset;
+ if (idNew) {
+ si = &OSGetSectionInfo(newModule)[rel->section];
+ offset = OS_SECTIONINFO_OFFSET(si->offset);
+ } else {
+ offset = 0;
+ }
+ switch (rel->type) {
+ case R_PPC_NONE:
+ break;
+ case R_PPC_ADDR32:
+ x = offset + rel->addend;
+ *p = x;
+ break;
+ case R_PPC_ADDR24:
+ x = offset + rel->addend;
+ *p = (*p & ~0x03fffffc) | (x & 0x03fffffc);
+ break;
+ case R_PPC_ADDR16:
+ x = offset + rel->addend;
+ *(u16*)p = (u16)(x & 0xffff);
+ break;
+ case R_PPC_ADDR16_LO:
+ x = offset + rel->addend;
+ *(u16*)p = (u16)(x & 0xffff);
+ break;
+ case R_PPC_ADDR16_HI:
+ x = offset + rel->addend;
+ *(u16*)p = (u16)(((x >> 16) & 0xffff));
+ break;
+ case R_PPC_ADDR16_HA:
+ x = offset + rel->addend;
+ *(u16*)p = (u16)(((x >> 16) + ((x & 0x8000) ? 1 : 0)) & 0xffff);
+ break;
+ case R_PPC_ADDR14:
+ case R_PPC_ADDR14_BRTAKEN:
+ case R_PPC_ADDR14_BRNTAKEN:
+ x = offset + rel->addend;
+ *p = (*p & ~0x0000fffc) | (x & 0x0000fffc);
+ break;
+ case R_PPC_REL24:
+ x = offset + rel->addend - (u32)p;
+ *p = (*p & ~0x03fffffc) | (x & 0x03fffffc);
+ break;
+ case R_PPC_REL14:
+ case R_PPC_REL14_BRTAKEN:
+ case R_PPC_REL14_BRNTAKEN:
+ x = offset + rel->addend - (u32)p;
+ *p = (*p & ~0x0000fffc) | (x & 0x0000fffc);
+ break;
+ case R_DOLPHIN_NOP:
+ break;
+ case R_DOLPHIN_SECTION:
+ si = &OSGetSectionInfo(module)[rel->section];
+ p = (u32*)OS_SECTIONINFO_OFFSET(si->offset);
+ if (siFlush) {
+ offset = OS_SECTIONINFO_OFFSET(siFlush->offset);
+ DCFlushRange((void*)offset, siFlush->size);
+ ICInvalidateRange((void*)offset, siFlush->size);
+ }
+ siFlush = (si->offset & OS_SECTIONINFO_EXEC) ? si : 0;
+ break;
+ default:
+ OSReport("OSLink: unknown relocation type %3d\n", rel->type);
+ break;
+ }
+ }
+
+ if (siFlush) {
+ offset = OS_SECTIONINFO_OFFSET(siFlush->offset);
+ DCFlushRange((void*)offset, siFlush->size);
+ ICInvalidateRange((void*)offset, siFlush->size);
+ }
+
+ return TRUE;
+}
+
+#if OS_MODULE_VERSION >= 3
+static BOOL Link(OSModuleInfo* newModule, void* bss, BOOL fixed) {
+ u32 i;
+ OSSectionInfo* si;
+ OSModuleHeader* moduleHeader;
+ OSModuleInfo* moduleInfo;
+ OSImportInfo* imp;
+
+ moduleHeader = (OSModuleHeader*)newModule;
+ moduleHeader->bssSection = 0;
+
+ if (OS_MODULE_VERSION < newModule->version ||
+ 2 <= newModule->version &&
+ (moduleHeader->align && (u32)newModule % moduleHeader->align != 0 ||
+ moduleHeader->bssAlign && (u32)bss % moduleHeader->bssAlign != 0)) {
+ return FALSE;
+ }
+
+ EnqueueTail(&__OSModuleInfoList, newModule, link);
+ newModule->sectionInfoOffset += (u32)moduleHeader;
+ moduleHeader->relOffset += (u32)moduleHeader;
+ moduleHeader->impOffset += (u32)moduleHeader;
+ if (3 <= newModule->version) {
+ moduleHeader->fixSize += (u32)moduleHeader;
+ }
+ for (i = 1; i < newModule->numSections; i++) {
+ si = &OSGetSectionInfo(newModule)[i];
+ if (si->offset != 0) {
+ si->offset += (u32)moduleHeader;
+ } else if (si->size != 0) {
+ moduleHeader->bssSection = (u8)i;
+ si->offset = (u32)bss;
+ bss = (void*)((u32)bss + si->size);
+ }
+ }
+ for (imp = (OSImportInfo*)moduleHeader->impOffset;
+ imp < (OSImportInfo*)(moduleHeader->impOffset + moduleHeader->impSize); imp++) {
+ imp->offset += (u32)moduleHeader;
+ }
+ if (moduleHeader->prologSection != SHN_UNDEF) {
+ moduleHeader->prolog +=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(newModule)[moduleHeader->prologSection].offset);
+ }
+ if (moduleHeader->epilogSection != SHN_UNDEF) {
+ moduleHeader->epilog +=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(newModule)[moduleHeader->epilogSection].offset);
+ }
+ if (moduleHeader->unresolvedSection != SHN_UNDEF) {
+ moduleHeader->unresolved +=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(newModule)[moduleHeader->unresolvedSection].offset);
+ }
+ if (__OSStringTable) {
+ newModule->nameOffset += (u32)__OSStringTable;
+ }
+
+ Relocate(0, moduleHeader);
+
+ for (moduleInfo = __OSModuleInfoList.head; moduleInfo; moduleInfo = moduleInfo->link.next) {
+ Relocate(moduleHeader, (OSModuleHeader*)moduleInfo);
+ if (moduleInfo != newModule) {
+ Relocate((OSModuleHeader*)moduleInfo, moduleHeader);
+ }
+ }
+
+ if (fixed) {
+ for (imp = (OSImportInfo*)moduleHeader->impOffset;
+ imp < (OSImportInfo*)(moduleHeader->impOffset + moduleHeader->impSize); imp++) {
+ if (imp->id == 0 || imp->id == newModule->id) {
+ moduleHeader->impSize = (u32)((u8*)imp - (u8*)moduleHeader->impOffset);
+ break;
+ }
+ }
+ }
+
+ memset(bss, 0, moduleHeader->bssSize);
+
+ OSNotifyLink(newModule);
+
+ return TRUE;
+}
+
+BOOL OSLink(OSModuleInfo* newModule, void* bss) { return Link(newModule, bss, FALSE); }
+
+BOOL OSLinkFixed(OSModuleInfo* newModule, void* bss) {
+ if (OS_MODULE_VERSION < newModule->version || newModule->version < 3) {
+ return FALSE;
+ }
+ return Link(newModule, bss, TRUE);
+}
+#else
+BOOL OSLink(OSModuleInfo* newModule, void* bss) {
+ u32 i;
+ OSSectionInfo* si;
+ OSModuleHeader* moduleHeader;
+ OSModuleInfo* moduleInfo;
+ OSImportInfo* imp;
+
+ moduleHeader = (OSModuleHeader*)newModule;
+ moduleHeader->bssSection = 0;
+
+ if (OS_MODULE_VERSION < newModule->version ||
+ 2 <= newModule->version &&
+ (moduleHeader->align && (u32)newModule % moduleHeader->align != 0 ||
+ moduleHeader->bssAlign && (u32)bss % moduleHeader->bssAlign != 0)) {
+ return FALSE;
+ }
+
+ EnqueueTail(&__OSModuleInfoList, newModule, link);
+ memset(bss, 0, moduleHeader->bssSize);
+ newModule->sectionInfoOffset += (u32)moduleHeader;
+ moduleHeader->relOffset += (u32)moduleHeader;
+ moduleHeader->impOffset += (u32)moduleHeader;
+
+ for (i = 1; i < newModule->numSections; i++) {
+ si = &OSGetSectionInfo(newModule)[i];
+ if (si->offset != 0) {
+ si->offset += (u32)moduleHeader;
+ } else if (si->size != 0) {
+ moduleHeader->bssSection = (u8)i;
+ si->offset = (u32)bss;
+ bss = (void*)((u32)bss + si->size);
+ }
+ }
+ for (imp = (OSImportInfo*)moduleHeader->impOffset;
+ imp < (OSImportInfo*)(moduleHeader->impOffset + moduleHeader->impSize); imp++) {
+ imp->offset += (u32)moduleHeader;
+ }
+ if (moduleHeader->prologSection != SHN_UNDEF) {
+ moduleHeader->prolog +=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(newModule)[moduleHeader->prologSection].offset);
+ }
+ if (moduleHeader->epilogSection != SHN_UNDEF) {
+ moduleHeader->epilog +=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(newModule)[moduleHeader->epilogSection].offset);
+ }
+ if (moduleHeader->unresolvedSection != SHN_UNDEF) {
+ moduleHeader->unresolved +=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(newModule)[moduleHeader->unresolvedSection].offset);
+ }
+ if (__OSStringTable) {
+ newModule->nameOffset += (u32)__OSStringTable;
+ }
+
+ Relocate(0, moduleHeader);
+
+ for (moduleInfo = __OSModuleInfoList.head; moduleInfo; moduleInfo = moduleInfo->link.next) {
+ Relocate(moduleHeader, (OSModuleHeader*)moduleInfo);
+ if (moduleInfo != newModule) {
+ Relocate((OSModuleHeader*)moduleInfo, moduleHeader);
+ }
+ }
+
+ OSNotifyLink(newModule);
+
+ return TRUE;
+}
+#endif
+
+static BOOL Undo(OSModuleHeader* newModule, OSModuleHeader* module) {
+ OSModuleID idNew;
+ OSImportInfo* imp;
+ OSRel* rel;
+ OSSectionInfo* si;
+ OSSectionInfo* siFlush;
+ u32* p;
+ u32 offset;
+ u32 x;
+
+ idNew = newModule->info.id;
+ for (imp = (OSImportInfo*)module->impOffset;
+ imp < (OSImportInfo*)(module->impOffset + module->impSize); imp++) {
+ if (imp->id == idNew) {
+ goto Found;
+ }
+ }
+ return FALSE;
+
+Found:
+ siFlush = 0;
+ for (rel = (OSRel*)imp->offset; rel->type != R_DOLPHIN_END; rel++) {
+ (u8*)p += rel->offset;
+ si = &OSGetSectionInfo(newModule)[rel->section];
+ offset = OS_SECTIONINFO_OFFSET(si->offset);
+ x = 0;
+ switch (rel->type) {
+ case R_PPC_NONE:
+ break;
+ case R_PPC_ADDR32:
+ *p = x;
+ break;
+ case R_PPC_ADDR24:
+ *p = (*p & ~0x03fffffc) | (x & 0x03fffffc);
+ break;
+ case R_PPC_ADDR16:
+ *(u16*)p = (u16)(x & 0xffff);
+ break;
+ case R_PPC_ADDR16_LO:
+ *(u16*)p = (u16)(x & 0xffff);
+ break;
+ case R_PPC_ADDR16_HI:
+ *(u16*)p = (u16)(((x >> 16) & 0xffff));
+ break;
+ case R_PPC_ADDR16_HA:
+ *(u16*)p = (u16)(((x >> 16) + ((x & 0x8000) ? 1 : 0)) & 0xffff);
+ break;
+ case R_PPC_ADDR14:
+ case R_PPC_ADDR14_BRTAKEN:
+ case R_PPC_ADDR14_BRNTAKEN:
+ *p = (*p & ~0x0000fffc) | (x & 0x0000fffc);
+ break;
+ case R_PPC_REL24:
+ if (module->unresolvedSection != SHN_UNDEF) {
+ x = (u32)module->unresolved - (u32)p;
+ }
+ *p = (*p & ~0x03fffffc) | (x & 0x03fffffc);
+ break;
+ case R_PPC_REL14:
+ case R_PPC_REL14_BRTAKEN:
+ case R_PPC_REL14_BRNTAKEN:
+ *p = (*p & ~0x0000fffc) | (x & 0x0000fffc);
+ break;
+ case R_DOLPHIN_NOP:
+ break;
+ case R_DOLPHIN_SECTION:
+ si = &OSGetSectionInfo(module)[rel->section];
+ p = (u32*)OS_SECTIONINFO_OFFSET(si->offset);
+ if (siFlush) {
+ offset = OS_SECTIONINFO_OFFSET(siFlush->offset);
+ DCFlushRange((void*)offset, siFlush->size);
+ ICInvalidateRange((void*)offset, siFlush->size);
+ }
+ siFlush = (si->offset & OS_SECTIONINFO_EXEC) ? si : 0;
+ break;
+ default:
+ OSReport("OSUnlink: unknown relocation type %3d\n", rel->type);
+ break;
+ }
+ }
+
+ if (siFlush) {
+ offset = OS_SECTIONINFO_OFFSET(siFlush->offset);
+ DCFlushRange((void*)offset, siFlush->size);
+ ICInvalidateRange((void*)offset, siFlush->size);
+ }
+
+ return TRUE;
+}
+
+BOOL OSUnlink(OSModuleInfo* oldModule) {
+ OSModuleHeader* moduleHeader;
+ OSModuleInfo* moduleInfo;
+ u32 i;
+ OSSectionInfo* si;
+ OSImportInfo* imp;
+
+ moduleHeader = (OSModuleHeader*)oldModule;
+
+ DequeueItem(&__OSModuleInfoList, oldModule, link);
+
+ for (moduleInfo = __OSModuleInfoList.head; moduleInfo; moduleInfo = moduleInfo->link.next) {
+ Undo(moduleHeader, (OSModuleHeader*)moduleInfo);
+ }
+
+ OSNotifyUnlink(oldModule);
+
+ if (__OSStringTable) {
+ oldModule->nameOffset -= (u32)__OSStringTable;
+ }
+ if (moduleHeader->prologSection != SHN_UNDEF) {
+ moduleHeader->prolog -=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(oldModule)[moduleHeader->prologSection].offset);
+ }
+ if (moduleHeader->epilogSection != SHN_UNDEF) {
+ moduleHeader->epilog -=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(oldModule)[moduleHeader->epilogSection].offset);
+ }
+ if (moduleHeader->unresolvedSection != SHN_UNDEF) {
+ moduleHeader->unresolved -=
+ OS_SECTIONINFO_OFFSET(OSGetSectionInfo(oldModule)[moduleHeader->unresolvedSection].offset);
+ }
+ for (imp = (OSImportInfo*)moduleHeader->impOffset;
+ imp < (OSImportInfo*)(moduleHeader->impOffset + moduleHeader->impSize); imp++) {
+ imp->offset -= (u32)moduleHeader;
+ }
+ for (i = 1; i < oldModule->numSections; i++) {
+ si = &OSGetSectionInfo(oldModule)[i];
+ if (i == moduleHeader->bssSection) {
+ moduleHeader->bssSection = 0;
+ si->offset = 0;
+ } else if (si->offset != 0) {
+ si->offset -= (u32)moduleHeader;
+ }
+ }
+ moduleHeader->relOffset -= (u32)moduleHeader;
+ moduleHeader->impOffset -= (u32)moduleHeader;
+ oldModule->sectionInfoOffset -= (u32)moduleHeader;
+
+ return TRUE;
+}
+
+void __OSModuleInit(void) {
+ __OSModuleInfoList.head = __OSModuleInfoList.tail = 0;
+ __OSStringTable = 0;
+}
+
+OSModuleInfo* OSSearchModule(void* ptr, u32* section, u32* offset) {
+ OSModuleInfo* moduleInfo;
+ OSSectionInfo* sectionInfo;
+ u32 i;
+ u32 baseSection;
+
+ if (ptr == NULL) {
+ return NULL;
+ }
+
+ for (moduleInfo = __OSModuleInfoList.head; moduleInfo; moduleInfo = moduleInfo->link.next) {
+ sectionInfo = OSGetSectionInfo(moduleInfo);
+ for (i = 0; i < moduleInfo->numSections; ++i) {
+ if (sectionInfo->size) {
+ baseSection = OS_SECTIONINFO_OFFSET(sectionInfo->offset);
+ if (baseSection <= (u32)ptr && (u32)ptr < baseSection + sectionInfo->size) {
+ if (section) {
+ *section = i;
+ }
+ if (offset) {
+ *offset = (u32)ptr - baseSection;
+ }
+ return moduleInfo;
+ }
+ }
+ sectionInfo++;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/Dolphin/os/OSMemory.c b/src/Dolphin/os/OSMemory.c
new file mode 100644
index 0000000..8f76aa8
--- /dev/null
+++ b/src/Dolphin/os/OSMemory.c
@@ -0,0 +1,247 @@
+#include <dolphin/os.h>
+
+#define TRUNC(n, a) (((u32)(n)) & ~((a)-1))
+#define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1))
+
+vu16 __MEMRegs[64] : 0xCC004000;
+extern OSErrorHandler __OSErrorTable[16];
+
+static BOOL OnReset(BOOL final);
+
+static OSResetFunctionInfo ResetFunctionInfo = {
+ OnReset,
+ 127,
+};
+
+#ifdef FULL_FRANK
+static BOOL OnReset(BOOL final) {
+ if (final != FALSE) {
+ __MEMRegs[8] = 0xFF;
+ __OSMaskInterrupts(0xf0000000);
+ }
+ return TRUE;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+static asm BOOL OnReset(BOOL final) {
+ nofralloc
+ mflr r0
+ cmpwi r3, 0
+ stw r0, 4(r1)
+ stwu r1, -8(r1)
+ beq @1
+ lis r3, __MEMRegs+16@ha
+ li r0, 0xff
+ sth r0, __MEMRegs+16@l(r3)
+ lis r3, 0xf000
+ bl __OSMaskInterrupts
+@1
+ li r3, 1
+ lwz r0, 0xc(r1)
+ addi r1, r1, 8
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+u32 OSGetPhysicalMemSize() { return *(u32*)(OSPhysicalToCached(0x0028)); }
+
+u32 OSGetConsoleSimulatedMemSize() { return *(u32*)(OSPhysicalToCached(0x00F0)); }
+
+static void MEMIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ u32 addr;
+ u32 cause;
+
+ cause = __MEMRegs[0xf];
+ addr = (((u32)__MEMRegs[0x12] & 0x3ff) << 16) | __MEMRegs[0x11];
+ __MEMRegs[0x10] = 0;
+
+ if (__OSErrorTable[OS_ERROR_PROTECTION]) {
+ __OSErrorTable[OS_ERROR_PROTECTION](OS_ERROR_PROTECTION, context, cause, addr);
+ return;
+ }
+
+ __OSUnhandledException(OS_ERROR_PROTECTION, context, cause, addr);
+}
+
+void OSProtectRange(u32 chan, void* addr, u32 nBytes, u32 control) {
+ BOOL enabled;
+ u32 start;
+ u32 end;
+ u16 reg;
+ if (4 <= chan) {
+ return;
+ }
+
+ control &= OS_PROTECT_CONTROL_RDWR;
+
+ end = (u32)addr + nBytes;
+ start = TRUNC(addr, 1u << 10);
+ end = ROUND(end, 1u << 10);
+
+ DCFlushRange((void*)start, end - start);
+
+ enabled = OSDisableInterrupts();
+
+ __OSMaskInterrupts(OS_INTERRUPTMASK(__OS_INTERRUPT_MEM_0 + chan));
+
+ __MEMRegs[0 + 2 * chan] = (u16)(start >> 10);
+ __MEMRegs[1 + 2 * chan] = (u16)(end >> 10);
+
+ reg = __MEMRegs[8];
+ reg &= ~(OS_PROTECT_CONTROL_RDWR << 2 * chan);
+ reg |= control << 2 * chan;
+ __MEMRegs[8] = reg;
+
+ if (control != OS_PROTECT_CONTROL_RDWR) {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK(__OS_INTERRUPT_MEM_0 + chan));
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+asm void Config24MB() {
+ // clang-format off
+ nofralloc
+
+ addi r7,r0,0
+
+ addis r4,r0,0x00000002@ha
+ addi r4,r4,0x00000002@l
+ addis r3,r0,0x800001ff@ha
+ addi r3,r3,0x800001ff@l
+
+ addis r6,r0,0x01000002@ha
+ addi r6,r6,0x01000002@l
+ addis r5,r0,0x810000ff@ha
+ addi r5,r5,0x810000ff@l
+
+ isync
+
+ mtspr dbat0u,r7
+ mtspr dbat0l,r4
+ mtspr dbat0u,r3
+ isync
+
+ mtspr ibat0u,r7
+ mtspr ibat0l,r4
+ mtspr ibat0u,r3
+ isync
+
+ mtspr dbat2u,r7
+ mtspr dbat2l,r6
+ mtspr dbat2u,r5
+ isync
+
+ mtspr ibat2u,r7
+ mtspr ibat2l,r6
+ mtspr ibat2u,r5
+ isync
+
+ mfmsr r3
+ ori r3, r3, 0x30
+ mtsrr1 r3
+
+ mflr r3
+ mtsrr0 r3
+ rfi
+ // clang-format on
+}
+
+asm void Config48MB() {
+ // clang-format off
+ nofralloc
+
+ addi r7,r0,0x0000
+
+ addis r4,r0,0x00000002@ha
+ addi r4,r4,0x00000002@l
+ addis r3,r0,0x800003ff@ha
+ addi r3,r3,0x800003ff@l
+
+ addis r6,r0,0x02000002@ha
+ addi r6,r6,0x02000002@l
+ addis r5,r0,0x820001ff@ha
+ addi r5,r5,0x820001ff@l
+
+ isync
+
+ mtspr dbat0u,r7
+ mtspr dbat0l,r4
+ mtspr dbat0u,r3
+ isync
+
+ mtspr ibat0u,r7
+ mtspr ibat0l,r4
+ mtspr ibat0u,r3
+ isync
+
+ mtspr dbat2u,r7
+ mtspr dbat2l,r6
+ mtspr dbat2u,r5
+ isync
+
+ mtspr ibat2u,r7
+ mtspr ibat2l,r6
+ mtspr ibat2u,r5
+ isync
+
+ mfmsr r3
+ ori r3, r3, 0x30
+ mtsrr1 r3
+
+ mflr r3
+ mtsrr0 r3
+ rfi
+ // clang-format on
+}
+
+asm void RealMode(register u32 addr) {
+ // clang-format off
+ nofralloc
+ clrlwi r3, r3, 2
+ mtsrr0 r3
+ mfmsr r3
+ rlwinm r3, r3, 0, 28, 25
+ mtsrr1 r3
+ rfi
+ // clang-format on
+}
+
+void __OSInitMemoryProtection() {
+ u32 padding[8];
+ u32 simulatedSize;
+ BOOL enabled;
+ simulatedSize = OSGetConsoleSimulatedMemSize();
+ enabled = OSDisableInterrupts();
+ if (simulatedSize <= 0x1800000) {
+ RealMode((u32)&Config24MB);
+ } else if (simulatedSize <= 0x3000000) {
+ RealMode((u32)&Config48MB);
+ }
+
+ __MEMRegs[16] = 0;
+ __MEMRegs[8] = 0xFF;
+
+ __OSMaskInterrupts(OS_INTERRUPTMASK_MEM_0 | OS_INTERRUPTMASK_MEM_1 | OS_INTERRUPTMASK_MEM_2 |
+ OS_INTERRUPTMASK_MEM_3);
+ __OSSetInterruptHandler(__OS_INTERRUPT_MEM_0, MEMIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_MEM_1, MEMIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_MEM_2, MEMIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_MEM_3, MEMIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_MEM_ADDRESS, MEMIntrruptHandler);
+ OSRegisterResetFunction(&ResetFunctionInfo);
+
+ if (OSGetConsoleSimulatedMemSize() < OSGetPhysicalMemSize() &&
+ OSGetConsoleSimulatedMemSize() == 0x1800000) {
+ __MEMRegs[20] = 2;
+ }
+
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_MEM_ADDRESS);
+ OSRestoreInterrupts(enabled);
+}
diff --git a/src/Dolphin/os/OSMessage.c b/src/Dolphin/os/OSMessage.c
new file mode 100644
index 0000000..db4d2fd
--- /dev/null
+++ b/src/Dolphin/os/OSMessage.c
@@ -0,0 +1,86 @@
+#include <dolphin/os.h>
+
+void OSInitMessageQueue(OSMessageQueue* mq, OSMessage* msgArray, s32 msgCount) {
+ OSInitThreadQueue(&mq->queueSend);
+ OSInitThreadQueue(&mq->queueReceive);
+ mq->msgArray = msgArray;
+ mq->msgCount = msgCount;
+ mq->firstIndex = 0;
+ mq->usedCount = 0;
+}
+
+BOOL OSSendMessage(OSMessageQueue* mq, OSMessage msg, s32 flags) {
+ BOOL enabled;
+ s32 lastIndex;
+
+ enabled = OSDisableInterrupts();
+
+ while (mq->msgCount <= mq->usedCount) {
+ if (!(flags & OS_MESSAGE_BLOCK)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ } else {
+ OSSleepThread(&mq->queueSend);
+ }
+ }
+
+ lastIndex = (mq->firstIndex + mq->usedCount) % mq->msgCount;
+ mq->msgArray[lastIndex] = msg;
+ mq->usedCount++;
+
+ OSWakeupThread(&mq->queueReceive);
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL OSReceiveMessage(OSMessageQueue* mq, OSMessage* msg, s32 flags) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ while (mq->usedCount == 0) {
+ if (!(flags & OS_MESSAGE_BLOCK)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ } else {
+ OSSleepThread(&mq->queueReceive);
+ }
+ }
+
+ if (msg != NULL) {
+ *msg = mq->msgArray[mq->firstIndex];
+ }
+ mq->firstIndex = (mq->firstIndex + 1) % mq->msgCount;
+ mq->usedCount--;
+
+ OSWakeupThread(&mq->queueSend);
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL OSJamMessage(OSMessageQueue* mq, OSMessage msg, s32 flags) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ while (mq->msgCount <= mq->usedCount)
+ {
+ if (!(flags & OS_MESSAGE_BLOCK)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ } else {
+ OSSleepThread(&mq->queueSend);
+ }
+ }
+
+ mq->firstIndex = (mq->firstIndex + mq->msgCount - 1) % mq->msgCount;
+ mq->msgArray[mq->firstIndex] = msg;
+ mq->usedCount++;
+
+ OSWakeupThread(&mq->queueReceive);
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
diff --git a/src/Dolphin/os/OSMutex.c b/src/Dolphin/os/OSMutex.c
new file mode 100644
index 0000000..f853729
--- /dev/null
+++ b/src/Dolphin/os/OSMutex.c
@@ -0,0 +1,223 @@
+#include "dolphin/os.h"
+
+#define PushTail(queue, mutex, link) \
+ do { \
+ OSMutex* __prev; \
+ \
+ __prev = (queue)->tail; \
+ if (__prev == NULL) \
+ (queue)->head = (mutex); \
+ else \
+ __prev->link.next = (mutex); \
+ (mutex)->link.prev = __prev; \
+ (mutex)->link.next = NULL; \
+ (queue)->tail = (mutex); \
+ } while (0)
+
+#define PopHead(queue, mutex, link) \
+ do { \
+ OSMutex* __next; \
+ \
+ (mutex) = (queue)->head; \
+ __next = (mutex)->link.next; \
+ if (__next == NULL) \
+ (queue)->tail = NULL; \
+ else \
+ __next->link.prev = NULL; \
+ (queue)->head = __next; \
+ } while (0)
+
+#define PopItem(queue, mutex, link) \
+ do { \
+ OSMutex* __next; \
+ OSMutex* __prev; \
+ \
+ __next = (mutex)->link.next; \
+ __prev = (mutex)->link.prev; \
+ \
+ if (__next == NULL) \
+ (queue)->tail = __prev; \
+ else \
+ __next->link.prev = __prev; \
+ \
+ if (__prev == NULL) \
+ (queue)->head = __next; \
+ else \
+ __prev->link.next = __next; \
+ } while (0)
+
+void OSInitMutex(OSMutex* mutex) {
+ OSInitThreadQueue(&mutex->queue);
+ mutex->thread = 0;
+ mutex->count = 0;
+}
+
+void OSLockMutex(OSMutex* mutex) {
+ BOOL enabled = OSDisableInterrupts();
+ OSThread* currentThread = OSGetCurrentThread();
+ OSThread* ownerThread;
+
+ while (TRUE) {
+ ownerThread = ((OSMutex*)mutex)->thread;
+ if (ownerThread == 0) {
+ mutex->thread = currentThread;
+ mutex->count++;
+ PushTail(&currentThread->queueMutex, mutex, link);
+ break;
+ } else if (ownerThread == currentThread) {
+ mutex->count++;
+ break;
+ } else {
+ currentThread->mutex = mutex;
+ __OSPromoteThread(mutex->thread, currentThread->priority);
+ OSSleepThread(&mutex->queue);
+ currentThread->mutex = 0;
+ }
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+void OSUnlockMutex(OSMutex* mutex) {
+ BOOL enabled = OSDisableInterrupts();
+ OSThread* currentThread = OSGetCurrentThread();
+
+ if (mutex->thread == currentThread && --mutex->count == 0) {
+ PopItem(&currentThread->queueMutex, mutex, link);
+ mutex->thread = NULL;
+ if (currentThread->priority < currentThread->base) {
+ currentThread->priority = __OSGetEffectivePriority(currentThread);
+ }
+
+ OSWakeupThread(&mutex->queue);
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+void __OSUnlockAllMutex(OSThread* thread) {
+ OSMutex* mutex;
+
+ while (thread->queueMutex.head) {
+ PopHead(&thread->queueMutex, mutex, link);
+ mutex->count = 0;
+ mutex->thread = NULL;
+ OSWakeupThread(&mutex->queue);
+ }
+}
+
+BOOL OSTryLockMutex(OSMutex* mutex) {
+ BOOL enabled = OSDisableInterrupts();
+ OSThread* currentThread = OSGetCurrentThread();
+ BOOL locked;
+ if (mutex->thread == 0) {
+ mutex->thread = currentThread;
+ mutex->count++;
+ PushTail(&currentThread->queueMutex, mutex, link);
+ locked = TRUE;
+ } else if (mutex->thread == currentThread) {
+ mutex->count++;
+ locked = TRUE;
+ } else {
+ locked = FALSE;
+ }
+ OSRestoreInterrupts(enabled);
+ return locked;
+}
+
+void OSInitCond(OSCond* cond) { OSInitThreadQueue(&cond->queue); }
+
+void OSWaitCond(OSCond* cond, OSMutex* mutex) {
+ BOOL enabled = OSDisableInterrupts();
+ OSThread* currentThread = OSGetCurrentThread();
+ s32 count;
+
+ if (mutex->thread == currentThread) {
+ count = mutex->count;
+ mutex->count = 0;
+ PopItem(&currentThread->queueMutex, mutex, link);
+ mutex->thread = NULL;
+
+ if (currentThread->priority < currentThread->base) {
+ currentThread->priority = __OSGetEffectivePriority(currentThread);
+ }
+
+ OSDisableScheduler();
+ OSWakeupThread(&mutex->queue);
+ OSEnableScheduler();
+ OSSleepThread(&cond->queue);
+ OSLockMutex(mutex);
+ mutex->count = count;
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+void OSSignalCond(OSCond* cond) { OSWakeupThread(&cond->queue); }
+
+static BOOL IsMember(OSMutexQueue* queue, OSMutex* mutex) {
+ OSMutex* member;
+
+ for (member = queue->head; member; member = member->link.next) {
+ if (mutex == member)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL __OSCheckMutex(OSMutex* mutex) {
+ OSThread* thread;
+ OSThreadQueue* queue;
+ OSPriority priority = 0;
+
+ queue = &mutex->queue;
+ if (!(queue->head == NULL || queue->head->link.prev == NULL))
+ return FALSE;
+ if (!(queue->tail == NULL || queue->tail->link.next == NULL))
+ return FALSE;
+ for (thread = queue->head; thread; thread = thread->link.next) {
+ if (!(thread->link.next == NULL || thread == thread->link.next->link.prev))
+ return FALSE;
+ if (!(thread->link.prev == NULL || thread == thread->link.prev->link.next))
+ return FALSE;
+
+ if (thread->state != OS_THREAD_STATE_WAITING)
+ return FALSE;
+
+ if (thread->priority < priority)
+ return FALSE;
+ priority = thread->priority;
+ }
+
+ if (mutex->thread) {
+ if (mutex->count <= 0)
+ return FALSE;
+ } else {
+ if (0 != mutex->count)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL __OSCheckDeadLock(OSThread* thread) {
+ OSMutex* mutex;
+
+ mutex = thread->mutex;
+ while (mutex && mutex->thread) {
+ if (mutex->thread == thread)
+ return TRUE;
+ mutex = mutex->thread->mutex;
+ }
+ return FALSE;
+}
+
+BOOL __OSCheckMutexes(OSThread* thread) {
+ OSMutex* mutex;
+
+ for (mutex = thread->queueMutex.head; mutex; mutex = mutex->link.next) {
+ if (mutex->thread != thread)
+ return FALSE;
+ if (!__OSCheckMutex(mutex))
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/src/Dolphin/os/OSReboot.c b/src/Dolphin/os/OSReboot.c
new file mode 100644
index 0000000..4b952cf
--- /dev/null
+++ b/src/Dolphin/os/OSReboot.c
@@ -0,0 +1,166 @@
+#include <os.h>
+#include <dvd.h>
+
+/*#include <dolphin/dvd/dvd.h>
+#include <dolphin/os/OSCache.h>
+#include <dolphin/os/OSInterrupt.h>
+#include <dolphin/os/OSReset.h>*/
+
+static void *Header[0x20 / sizeof(void *)];
+static void *SaveStart;
+static void *SaveEnd;
+static BOOL Prepared;
+
+void Run(register Event callback)
+{
+ OSDisableInterrupts();
+ ICFlashInvalidate();
+ __sync();
+ __isync();
+ asm {
+ mtlr callback
+ blr
+ }
+}
+
+static void Callback(void)
+{
+ Prepared = TRUE;
+}
+
+#pragma push
+asm void __OSReboot(u32 resetCode, BOOL forceMenu)
+{ // clang-format off
+ nofralloc
+/* 80348144 00344D24 7C 08 02 A6 */ mflr r0
+/* 80348148 00344D28 90 01 00 04 */ stw r0, 4(r1)
+/* 8034814C 00344D2C 94 21 FC B8 */ stwu r1, -0x348(r1)
+/* 80348150 00344D30 93 E1 03 44 */ stw r31, 0x344(r1)
+/* 80348154 00344D34 93 C1 03 40 */ stw r30, 0x340(r1)
+/* 80348158 00344D38 93 A1 03 3C */ stw r29, 0x33c(r1)
+/* 8034815C 00344D3C 7C 7D 1B 78 */ mr r29, r3
+/* 80348160 00344D40 3C 60 80 4A */ lis r3, Header@ha
+/* 80348164 00344D44 3B C3 7D 40 */ addi r30, r3, Header@l
+/* 80348168 00344D48 4B FF F1 FD */ bl OSDisableInterrupts
+/* 8034816C 00344D4C 80 AD BC F0 */ lwz r5, SaveStart(r13)
+/* 80348170 00344D50 3F E0 81 80 */ lis r31, 0x817FFFFC@ha
+/* 80348174 00344D54 38 60 00 00 */ li r3, 0
+/* 80348178 00344D58 80 0D BC F4 */ lwz r0, SaveEnd(r13)
+/* 8034817C 00344D5C 3C 80 81 30 */ lis r4, 0x812FDFF0@ha
+/* 80348180 00344D60 38 E0 00 01 */ li r7, 1
+/* 80348184 00344D64 93 BF FF FC */ stw r29, 0x817FFFFC@l(r31)
+/* 80348188 00344D68 3C C0 80 00 */ lis r6, 0x800030E2@ha
+/* 8034818C 00344D6C 90 7F FF F8 */ stw r3, -8(r31)
+/* 80348190 00344D70 38 61 00 70 */ addi r3, r1, 0x70
+/* 80348194 00344D74 98 E6 30 E2 */ stb r7, 0x800030E2@l(r6)
+/* 80348198 00344D78 90 A4 DF F0 */ stw r5, 0x812FDFF0@l(r4)
+/* 8034819C 00344D7C 90 04 DF EC */ stw r0, -0x2014(r4)
+/* 803481A0 00344D80 4B FF D0 B5 */ bl OSClearContext
+/* 803481A4 00344D84 38 61 00 70 */ addi r3, r1, 0x70
+/* 803481A8 00344D88 4B FF CE E5 */ bl OSSetCurrentContext
+/* 803481AC 00344D8C 4B FE FC 3D */ bl DVDInit
+/* 803481B0 00344D90 38 60 00 01 */ li r3, 1
+/* 803481B4 00344D94 4B FF 1A 45 */ bl DVDSetAutoInvalidation
+/* 803481B8 00344D98 3C 60 80 35 */ lis r3, Callback@ha
+/* 803481BC 00344D9C 38 63 81 38 */ addi r3, r3, Callback@l
+/* 803481C0 00344DA0 4B FF 1E 75 */ bl __DVDPrepareResetAsync
+/* 803481C4 00344DA4 4B FF 1D 8D */ bl DVDCheckDisk
+/* 803481C8 00344DA8 2C 03 00 00 */ cmpwi r3, 0
+/* 803481CC 00344DAC 40 82 00 0C */ bne lbl_803481D8
+/* 803481D0 00344DB0 80 7F FF FC */ lwz r3, -4(r31)
+/* 803481D4 00344DB4 48 00 02 31 */ bl __OSDoHotReset
+lbl_803481D8:
+/* 803481D8 00344DB8 38 60 FF E0 */ li r3, -32
+/* 803481DC 00344DBC 4B FF F5 51 */ bl __OSMaskInterrupts
+/* 803481E0 00344DC0 38 60 04 00 */ li r3, 0x400
+/* 803481E4 00344DC4 4B FF F5 D1 */ bl __OSUnmaskInterrupts
+/* 803481E8 00344DC8 4B FF F1 91 */ bl OSEnableInterrupts
+/* 803481EC 00344DCC 48 00 00 04 */ b lbl_803481F0
+lbl_803481F0:
+/* 803481F0 00344DD0 48 00 00 04 */ b lbl_803481F4
+lbl_803481F4:
+/* 803481F4 00344DD4 80 0D BC F8 */ lwz r0, Prepared(r13)
+/* 803481F8 00344DD8 2C 00 00 00 */ cmpwi r0, 0
+/* 803481FC 00344DDC 41 82 FF F8 */ beq lbl_803481F4
+/* 80348200 00344DE0 7F C4 F3 78 */ mr r4, r30
+/* 80348204 00344DE4 38 61 00 40 */ addi r3, r1, 0x40
+/* 80348208 00344DE8 38 A0 00 20 */ li r5, 0x20
+/* 8034820C 00344DEC 38 C0 24 40 */ li r6, 0x2440
+/* 80348210 00344DF0 38 E0 00 00 */ li r7, 0
+/* 80348214 00344DF4 4B FF 17 51 */ bl DVDReadAbsAsyncForBS
+/* 80348218 00344DF8 3F E0 81 80 */ lis r31, 0x8180
+/* 8034821C 00344DFC 48 00 00 04 */ b lbl_80348220
+lbl_80348220:
+/* 80348220 00344E00 48 00 00 04 */ b lbl_80348224
+lbl_80348224:
+/* 80348224 00344E04 80 01 00 4C */ lwz r0, 0x4c(r1)
+/* 80348228 00344E08 2C 00 00 01 */ cmpwi r0, 1
+/* 8034822C 00344E0C 41 82 FF F8 */ beq lbl_80348224
+/* 80348230 00344E10 40 80 00 14 */ bge lbl_80348244
+/* 80348234 00344E14 2C 00 FF FF */ cmpwi r0, -1
+/* 80348238 00344E18 41 82 00 18 */ beq lbl_80348250
+/* 8034823C 00344E1C 40 80 00 20 */ bge lbl_8034825C
+/* 80348240 00344E20 4B FF FF E4 */ b lbl_80348224
+lbl_80348244:
+/* 80348244 00344E24 2C 00 00 0C */ cmpwi r0, 0xc
+/* 80348248 00344E28 40 80 FF DC */ bge lbl_80348224
+/* 8034824C 00344E2C 48 00 00 04 */ b lbl_80348250
+lbl_80348250:
+/* 80348250 00344E30 80 7F FF FC */ lwz r3, -4(r31)
+/* 80348254 00344E34 48 00 01 B1 */ bl __OSDoHotReset
+/* 80348258 00344E38 4B FF FF CC */ b lbl_80348224
+lbl_8034825C:
+/* 8034825C 00344E3C 80 7E 00 18 */ lwz r3, 0x18(r30)
+/* 80348260 00344E40 80 9E 00 14 */ lwz r4, 0x14(r30)
+/* 80348264 00344E44 38 03 00 1F */ addi r0, r3, 0x1f
+/* 80348268 00344E48 38 84 00 20 */ addi r4, r4, 0x20
+/* 8034826C 00344E4C 54 1E 00 34 */ rlwinm r30, r0, 0, 0, 0x1a
+/* 80348270 00344E50 48 00 00 04 */ b lbl_80348274
+lbl_80348274:
+/* 80348274 00344E54 48 00 00 04 */ b lbl_80348278
+lbl_80348278:
+/* 80348278 00344E58 80 0D BC F8 */ lwz r0, Prepared(r13)
+/* 8034827C 00344E5C 2C 00 00 00 */ cmpwi r0, 0
+/* 80348280 00344E60 41 82 FF F8 */ beq lbl_80348278
+/* 80348284 00344E64 7F C5 F3 78 */ mr r5, r30
+/* 80348288 00344E68 38 61 00 10 */ addi r3, r1, 0x10
+/* 8034828C 00344E6C 38 C4 24 40 */ addi r6, r4, 0x2440
+/* 80348290 00344E70 3C 80 81 30 */ lis r4, 0x8130
+/* 80348294 00344E74 38 E0 00 00 */ li r7, 0
+/* 80348298 00344E78 4B FF 16 CD */ bl DVDReadAbsAsyncForBS
+/* 8034829C 00344E7C 3F E0 81 80 */ lis r31, 0x8180
+/* 803482A0 00344E80 48 00 00 04 */ b lbl_803482A4
+lbl_803482A4:
+/* 803482A4 00344E84 48 00 00 04 */ b lbl_803482A8
+lbl_803482A8:
+/* 803482A8 00344E88 80 01 00 1C */ lwz r0, 0x1c(r1)
+/* 803482AC 00344E8C 2C 00 00 01 */ cmpwi r0, 1
+/* 803482B0 00344E90 41 82 FF F8 */ beq lbl_803482A8
+/* 803482B4 00344E94 40 80 00 14 */ bge lbl_803482C8
+/* 803482B8 00344E98 2C 00 FF FF */ cmpwi r0, -1
+/* 803482BC 00344E9C 41 82 00 18 */ beq lbl_803482D4
+/* 803482C0 00344EA0 40 80 00 20 */ bge lbl_803482E0
+/* 803482C4 00344EA4 4B FF FF E4 */ b lbl_803482A8
+lbl_803482C8:
+/* 803482C8 00344EA8 2C 00 00 0C */ cmpwi r0, 0xc
+/* 803482CC 00344EAC 40 80 FF DC */ bge lbl_803482A8
+/* 803482D0 00344EB0 48 00 00 04 */ b lbl_803482D4
+lbl_803482D4:
+/* 803482D4 00344EB4 80 7F FF FC */ lwz r3, -4(r31)
+/* 803482D8 00344EB8 48 00 01 2D */ bl __OSDoHotReset
+/* 803482DC 00344EBC 4B FF FF CC */ b lbl_803482A8
+lbl_803482E0:
+/* 803482E0 00344EC0 3C 60 81 30 */ lis r3, 0x8130
+/* 803482E4 00344EC4 7F C4 F3 78 */ mr r4, r30
+/* 803482E8 00344EC8 4B FF C5 ED */ bl ICInvalidateRange
+/* 803482EC 00344ECC 3C 60 81 30 */ lis r3, 0x8130
+/* 803482F0 00344ED0 4B FF FE 09 */ bl Run
+/* 803482F4 00344ED4 80 01 03 4C */ lwz r0, 0x34c(r1)
+/* 803482F8 00344ED8 83 E1 03 44 */ lwz r31, 0x344(r1)
+/* 803482FC 00344EDC 83 C1 03 40 */ lwz r30, 0x340(r1)
+/* 80348300 00344EE0 7C 08 03 A6 */ mtlr r0
+/* 80348304 00344EE4 83 A1 03 3C */ lwz r29, 0x33c(r1)
+/* 80348308 00344EE8 38 21 03 48 */ addi r1, r1, 0x348
+/* 8034830C 00344EEC 4E 80 00 20 */ blr
+} // clang-format on
+#pragma pop
diff --git a/src/Dolphin/os/OSReset.c b/src/Dolphin/os/OSReset.c
index a9cc186..ebd0b04 100644
--- a/src/Dolphin/os/OSReset.c
+++ b/src/Dolphin/os/OSReset.c
@@ -1,5 +1,190 @@
-#include "dolphin/OSReset.h"
+#include "dolphin/OSRtcPriv.h"
+#include "dolphin/os.h"
+#include "dolphin/vi.h"
-void OSResetSystem(int reset, unsigned int resetCode, int forceMenu) {
+volatile u8 DAT_800030e2 : 0x800030e2;
+typedef struct Unk {
+ u8 pad[0x24];
+ u32 resetCode;
+} Unk;
+volatile Unk DAT_cc003000 : 0xcc003000;
+typedef struct Unk2 {
+ u16 _0;
+ u16 _2;
+} Unk2;
+
+volatile Unk2 DAT_cc002000 : 0xcc002000;
+
+typedef struct OSResetQueue {
+ OSResetFunctionInfo* first;
+ OSResetFunctionInfo* last;
+} OSResetQueue;
+
+OSResetQueue ResetFunctionQueue;
+
+void OSRegisterResetFunction(OSResetFunctionInfo* func) {
+ OSResetFunctionInfo* tmp;
+ OSResetFunctionInfo* iter;
+
+ for (iter = ResetFunctionQueue.first; iter && iter->priority <= func->priority; iter = iter->next)
+ ;
+
+ if (iter == NULL) {
+ tmp = ResetFunctionQueue.last;
+ if (tmp == NULL) {
+ ResetFunctionQueue.first = func;
+ } else {
+ tmp->next = func;
+ }
+ func->prev = tmp;
+ func->next = NULL;
+ ResetFunctionQueue.last = func;
+ return;
+ }
+
+ func->next = iter;
+ tmp = iter->prev;
+ iter->prev = func;
+ func->prev = tmp;
+ if (tmp == NULL) {
+ ResetFunctionQueue.first = func;
+ return;
+ }
+ tmp->next = func;
+}
+
+BOOL __OSCallResetFunctions(u32 arg0) {
+ OSResetFunctionInfo* iter;
+ s32 retCode = 0;
+
+ for (iter = ResetFunctionQueue.first; iter != NULL; iter = iter->next) {
+ retCode |= !iter->func(arg0);
+ }
+ retCode |= !__OSSyncSram();
+ if (retCode) {
+ return 0;
+ }
+ return 1;
+}
+
+asm void Reset(register s32 resetCode) {
+ // clang-format off
+ nofralloc
+ b lbl_8038315C
+lbl_80383140:
+ mfspr r8, HID0
+ ori r8, r8, 8
+ mtspr HID0, r8
+ isync
+ sync
+ nop
+ b lbl_80383160
+lbl_8038315C:
+ b lbl_8038317C
+lbl_80383160:
+ mftb r5, 268
+lbl_80383164:
+ mftb r6, 268
+ subf r7, r5, r6
+ cmplwi r7, 0x1124
+ blt lbl_80383164
+ nop
+ b lbl_80383180
+lbl_8038317C:
+ b lbl_8038319C
+lbl_80383180:
+ lis r8, 0xCC003000@h
+ ori r8, r8, 0xCC003000@l
+ li r4, 3
+ stw r4, 0x24(r8)
+ stw r3, 0x24(r8)
+ nop
+ b lbl_803831A0
+lbl_8038319C:
+ b lbl_803831A8
+lbl_803831A0:
+ nop
+ b lbl_803831A0
+lbl_803831A8:
+ b lbl_80383140
+ // clang-format on
+}
+
+OSThreadQueue __OSActiveThreadQueue : (OS_BASE_CACHED | 0x00DC);
+
+static void KillThreads(void) {
+ OSThread* thread;
+ OSThread* next;
+
+ for (thread = __OSActiveThreadQueue.head; thread; thread = next) {
+ next = thread->linkActive.next;
+ switch (thread->state) {
+ case 1:
+ case 4:
+ OSCancelThread(thread);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void __OSDoHotReset(s32 arg0) {
+ OSDisableInterrupts();
+ __VIRegs[1] = 0;
+ ICFlashInvalidate();
+ Reset(arg0 * 8);
+}
+
+void OSResetSystem(int reset, u32 resetCode, BOOL forceMenu) {
+ BOOL rc;
+ BOOL disableRecalibration;
+ u32 unk[3];
+ OSDisableScheduler();
+ __OSStopAudioSystem();
+
+ if (reset == OS_RESET_SHUTDOWN) {
+ disableRecalibration = __PADDisableRecalibration(TRUE);
+ }
+
+ while (!__OSCallResetFunctions(FALSE))
+ ;
+
+ if (reset == OS_RESET_HOTRESET && forceMenu) {
+ OSSram* sram;
+
+ sram = __OSLockSram();
+ sram->flags |= 0x40;
+ __OSUnlockSram(TRUE);
+
+ while (!__OSSyncSram())
+ ;
+ }
+ OSDisableInterrupts();
+ __OSCallResetFunctions(TRUE);
+ LCDisable();
+ if (reset == OS_RESET_HOTRESET) {
+ __OSDoHotReset(resetCode);
+ } else if (reset == OS_RESET_RESTART) {
+ KillThreads();
+ OSEnableScheduler();
+ __OSReboot(resetCode, forceMenu);
+ }
+ KillThreads();
+ memset(OSPhysicalToCached(0x40), 0, 0xcc - 0x40);
+ memset(OSPhysicalToCached(0xd4), 0, 0xe8 - 0xd4);
+ memset(OSPhysicalToCached(0xf4), 0, 0xf8 - 0xf4);
+ memset(OSPhysicalToCached(0x3000), 0, 0xc0);
+ memset(OSPhysicalToCached(0x30c8), 0, 0xd4 - 0xc8);
+ memset(OSPhysicalToCached(0x30e2), 0, 1);
+
+ __PADDisableRecalibration(disableRecalibration);
+}
+
+u32 OSGetResetCode(void) {
+ if (DAT_800030e2 != 0) {
+ return 0x80000000;
+ }
+ return ((DAT_cc003000.resetCode & ~7) >> 3);
}
diff --git a/src/Dolphin/os/OSResetSW.c b/src/Dolphin/os/OSResetSW.c
new file mode 100644
index 0000000..9752213
--- /dev/null
+++ b/src/Dolphin/os/OSResetSW.c
@@ -0,0 +1,281 @@
+#include <dolphin/os.h>
+
+extern OSTime __OSGetSystemTime();
+
+u8 GameChoice : (OS_BASE_CACHED | 0x30E3);
+
+vu32 __PIRegs[12] : 0xCC003000;
+
+extern OSTime __OSStartTime;
+
+static OSResetCallback ResetCallback;
+static BOOL Down;
+static BOOL LastState;
+static OSTime HoldUp;
+static OSTime HoldDown;
+
+void __OSResetSWInterruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSResetCallback callback;
+
+ HoldDown = __OSGetSystemTime();
+ while (__OSGetSystemTime() - HoldDown < OSMicrosecondsToTicks(100) &&
+ !(__PIRegs[0] & 0x00010000)) {
+ ;
+ }
+ if (!(__PIRegs[0] & 0x00010000)) {
+ LastState = Down = TRUE;
+ __OSMaskInterrupts(OS_INTERRUPTMASK_PI_RSW);
+ if (ResetCallback) {
+ callback = ResetCallback;
+ ResetCallback = NULL;
+ callback();
+ }
+ }
+ __PIRegs[0] = 2;
+}
+
+#ifdef FULL_FRANK
+BOOL OSGetResetButtonState(void) {
+ BOOL enabled;
+ BOOL state;
+ u32 reg;
+ OSTime now;
+
+ enabled = OSDisableInterrupts();
+
+ now = __OSGetSystemTime();
+
+ reg = __PIRegs[0];
+ if (!(reg & 0x00010000)) {
+ if (!Down) {
+ Down = TRUE;
+ state = HoldUp ? TRUE : FALSE;
+ HoldDown = now;
+ } else {
+ state = (HoldUp || (OSMicrosecondsToTicks(100) < now - HoldDown)) ? TRUE : FALSE;
+ }
+ } else if (Down) {
+ Down = FALSE;
+ state = LastState;
+ if (state) {
+ HoldUp = now;
+ } else {
+ HoldUp = 0;
+ }
+ } else if (HoldUp && (now - HoldUp < OSMillisecondsToTicks(40))) {
+ state = TRUE;
+ } else {
+ state = FALSE;
+ HoldUp = 0;
+ }
+
+ LastState = state;
+
+ if (GameChoice & 0x3f) {
+ OSTime fire = (GameChoice & 0x3f) * 60;
+ fire = __OSStartTime + OSSecondsToTicks(fire);
+ if (fire < now) {
+ now -= fire;
+ now = OSTicksToSeconds(now) / 2;
+ if ((now & 1) == 0) {
+ state = TRUE;
+ } else {
+ state = FALSE;
+ }
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return state;
+}
+#else
+extern void __div2i(void);
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm BOOL OSGetResetButtonState(void) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ stw r29, 0xc(r1)
+ bl OSDisableInterrupts
+ mr r30, r3
+ bl __OSGetSystemTime
+ lis r5, __PIRegs@ha
+ lwz r0, __PIRegs@l(r5)
+ rlwinm. r0, r0, 0, 0xf, 0xf
+ bne lbl_8038369C
+ lwz r0, Down
+ cmpwi r0, 0
+ bne lbl_8038360C
+ lwz r0, HoldUp
+ li r6, 0
+ lwz r5, HoldUp+4
+ li r7, 1
+ xor r0, r0, r6
+ xor r5, r5, r6
+ stw r7, Down
+ or. r0, r5, r0
+ beq lbl_803835F8
+ b lbl_803835FC
+lbl_803835F8:
+ mr r7, r6
+lbl_803835FC:
+ stw r4, HoldDown+4
+ mr r29, r7
+ stw r3, HoldDown
+ b lbl_80383750
+lbl_8038360C:
+ lwz r0, HoldUp
+ li r9, 0
+ lwz r5, HoldUp+4
+ li r10, 1
+ xor r0, r0, r9
+ xor r5, r5, r9
+ or. r0, r5, r0
+ bne lbl_80383680
+ lis r6, __OSBusClock@ha
+ lwz r5, HoldDown+4
+ lwz r7, __OSBusClock@l(r6)
+ lis r6, 0x431BDE83@ha
+ addi r8, r6, 0x431BDE83@l
+ lwz r0, HoldDown
+ srwi r6, r7, 2
+ mulhwu r6, r8, r6
+ srwi r6, r6, 0xf
+ mulli r6, r6, 0x64
+ subfc r7, r5, r4
+ subfe r0, r0, r3
+ srwi r8, r6, 3
+ xoris r5, r0, 0x8000
+ xoris r6, r9, 0x8000
+ subfc r0, r7, r8
+ subfe r5, r5, r6
+ subfe r5, r6, r6
+ neg. r5, r5
+ bne lbl_80383680
+ mr r10, r9
+lbl_80383680:
+ cmpwi r10, 0
+ beq lbl_80383690
+ li r0, 1
+ b lbl_80383694
+lbl_80383690:
+ li r0, 0
+lbl_80383694:
+ mr r29, r0
+ b lbl_80383750
+lbl_8038369C:
+ lwz r0, Down
+ cmpwi r0, 0
+ beq lbl_803836D8
+ lwz r5, LastState
+ li r0, 0
+ stw r0, Down
+ cmpwi r5, 0
+ addi r29, r5, 0
+ beq lbl_803836CC
+ stw r4, HoldUp+4
+ stw r3, HoldUp
+ b lbl_80383750
+lbl_803836CC:
+ stw r0, HoldUp+4
+ stw r0, HoldUp
+ b lbl_80383750
+lbl_803836D8:
+ lwz r6, HoldUp
+ li r8, 0
+ lwz r7, HoldUp+4
+ xor r0, r6, r8
+ xor r5, r7, r8
+ or. r0, r5, r0
+ beq lbl_80383740
+ lis r5, __OSBusClock@ha
+ lwz r0, __OSBusClock@l(r5)
+ lis r5, 0x10624DD3@ha
+ addi r5, r5, 0x10624DD3@l
+ srwi r0, r0, 2
+ mulhwu r0, r5, r0
+ srwi r0, r0, 6
+ mulli r0, r0, 0x28
+ subfc r7, r7, r4
+ subfe r5, r6, r3
+ xoris r6, r5, 0x8000
+ xoris r5, r8, 0x8000
+ subfc r0, r0, r7
+ subfe r5, r5, r6
+ subfe r5, r6, r6
+ neg. r5, r5
+ beq lbl_80383740
+ li r29, 1
+ b lbl_80383750
+lbl_80383740:
+ li r0, 0
+ stw r0, HoldUp+4
+ li r29, 0
+ stw r0, HoldUp
+lbl_80383750:
+ lis r5, GameChoice@ha
+ stw r29, LastState
+ lbz r0, GameChoice@l(r5)
+ clrlwi. r0, r0, 0x1a
+ beq lbl_80383800
+ mulli r10, r0, 0x3c
+ lwz r0, 0xf8(r5)
+ lwz r9, __OSStartTime+4
+ lwz r8, __OSStartTime
+ srwi r6, r0, 2
+ srawi r0, r10, 0x1f
+ mullw r7, r0, r6
+ mulhwu r0, r10, r6
+ mullw r5, r10, r6
+ addc r9, r9, r5
+ li r31, 0
+ add r7, r7, r0
+ mullw r0, r10, r31
+ add r0, r7, r0
+ adde r8, r8, r0
+ xoris r7, r8, 0x8000
+ xoris r5, r3, 0x8000
+ subfc r0, r4, r9
+ subfe r5, r5, r7
+ subfe r5, r7, r7
+ neg. r5, r5
+ beq lbl_80383800
+ subfc r4, r9, r4
+ subfe r3, r8, r3
+ li r5, 0
+ bl __div2i
+ li r5, 0
+ li r6, 2
+ bl __div2i
+ li r0, 1
+ and r4, r4, r0
+ and r0, r3, r31
+ xor r3, r4, r31
+ xor r0, r0, r31
+ or. r0, r3, r0
+ bne lbl_803837FC
+ li r29, 1
+ b lbl_80383800
+lbl_803837FC:
+ li r29, 0
+lbl_80383800:
+ mr r3, r30
+ bl OSRestoreInterrupts
+ mr r3, r29
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ lwz r29, 0xc(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
diff --git a/src/Dolphin/os/OSRtc.c b/src/Dolphin/os/OSRtc.c
new file mode 100644
index 0000000..be14670
--- /dev/null
+++ b/src/Dolphin/os/OSRtc.c
@@ -0,0 +1,544 @@
+#include "dolphin/OSRtcPriv.h"
+#include "dolphin/os.h"
+
+#define RTC_CMD_READ 0x20000000
+#define RTC_CMD_WRITE 0xa0000000
+
+#define RTC_SRAM_ADDR 0x00000100
+#define RTC_SRAM_SIZE 64
+
+#define RTC_CHAN 0
+#define RTC_DEV 1
+#define RTC_FREQ 3 // EXI_FREQ_8M
+
+typedef struct SramControlBlock {
+ u8 sram[RTC_SRAM_SIZE];
+ u32 offset;
+ BOOL enabled;
+ BOOL locked;
+ BOOL sync;
+ void (*callback)(void);
+} SramControlBlock;
+
+static SramControlBlock Scb ATTRIBUTE_ALIGN(32);
+
+static BOOL GetRTC(u32* rtc) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXILock(RTC_CHAN, RTC_DEV, 0)) {
+ return FALSE;
+ }
+ if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
+ EXIUnlock(RTC_CHAN);
+ return FALSE;
+ }
+
+ cmd = RTC_CMD_READ;
+ err = FALSE;
+ err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIImm(RTC_CHAN, &cmd, 4, 0, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIDeselect(RTC_CHAN);
+ EXIUnlock(RTC_CHAN);
+
+ *rtc = cmd;
+
+ return !err;
+}
+
+BOOL __OSGetRTC(u32* rtc) {
+ BOOL err;
+ u32 t0;
+ u32 t1;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ err = FALSE;
+ err |= !GetRTC(&t0);
+ err |= !GetRTC(&t1);
+ if (err) {
+ break;
+ }
+ if (t0 == t1) {
+ *rtc = t0;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL __OSSetRTC(u32 rtc) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXILock(RTC_CHAN, RTC_DEV, 0)) {
+ return FALSE;
+ }
+ if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
+ EXIUnlock(RTC_CHAN);
+ return FALSE;
+ }
+
+ cmd = RTC_CMD_WRITE;
+ err = FALSE;
+ err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIImm(RTC_CHAN, &rtc, 4, 1, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIDeselect(RTC_CHAN);
+ EXIUnlock(RTC_CHAN);
+
+ return !err;
+}
+
+static BOOL ReadSram(void* buffer) {
+ BOOL err;
+ u32 cmd;
+
+ DCInvalidateRange(buffer, RTC_SRAM_SIZE);
+
+ if (!EXILock(RTC_CHAN, RTC_DEV, 0)) {
+ return FALSE;
+ }
+ if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
+ EXIUnlock(RTC_CHAN);
+ return FALSE;
+ }
+
+ cmd = RTC_CMD_READ | RTC_SRAM_ADDR;
+ err = FALSE;
+ err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIDma(RTC_CHAN, buffer, RTC_SRAM_SIZE, 0, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIDeselect(RTC_CHAN);
+ EXIUnlock(RTC_CHAN);
+
+ return !err;
+}
+
+BOOL WriteSram(void* buffer, u32 offset, u32 size);
+static void WriteSramCallback(s32 chan, OSContext* context) {
+ Scb.sync = WriteSram(Scb.sram + Scb.offset, Scb.offset, RTC_SRAM_SIZE - Scb.offset);
+ if (Scb.sync) {
+ Scb.offset = RTC_SRAM_SIZE;
+ }
+}
+
+BOOL WriteSram(void* buffer, u32 offset, u32 size) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXILock(RTC_CHAN, RTC_DEV, WriteSramCallback)) {
+ return FALSE;
+ }
+ if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
+ EXIUnlock(RTC_CHAN);
+ return FALSE;
+ }
+
+ offset <<= 6;
+ cmd = RTC_CMD_WRITE | RTC_SRAM_ADDR + offset;
+ err = FALSE;
+ err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIImmEx(RTC_CHAN, buffer, (s32)size, 1);
+ err |= !EXIDeselect(RTC_CHAN);
+ EXIUnlock(RTC_CHAN);
+
+ return !err;
+}
+
+void __OSInitSram() {
+ Scb.locked = Scb.enabled = FALSE;
+ Scb.sync = ReadSram(Scb.sram);
+ Scb.offset = RTC_SRAM_SIZE;
+}
+
+static void* LockSram(u32 offset) {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+
+ if (Scb.locked != FALSE) {
+ OSRestoreInterrupts(enabled);
+ return NULL;
+ }
+
+ Scb.enabled = enabled;
+ Scb.locked = TRUE;
+
+ return Scb.sram + offset;
+}
+
+#ifdef FULL_FRANK
+OSSram* __OSLockSram() { return LockSram(0); }
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm OSSram* __OSLockSram() {
+ nofralloc
+ mflr r0
+ lis r3, Scb@ha
+ stw r0, 4(r1)
+ stwu r1, -0x10(r1)
+ stw r31, 0xc(r1)
+ addi r31, r3, Scb@l
+ bl OSDisableInterrupts
+ lwz r0, 0x48(r31)
+ addi r4, r31, 0x48
+ cmpwi r0, 0
+ beq lbl_80383B0C
+ bl OSRestoreInterrupts
+ li r31, 0
+ b lbl_80383B18
+lbl_80383B0C:
+ stw r3, 0x44(r31)
+ li r0, 1
+ stw r0, 0(r4)
+lbl_80383B18:
+ mr r3, r31
+ lwz r0, 0x14(r1)
+ lwz r31, 0xc(r1)
+ addi r1, r1, 0x10
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+OSSramEx* __OSLockSramEx() { return LockSram(sizeof(OSSram)); }
+
+static BOOL UnlockSram(BOOL commit, u32 offset) {
+ u16* p;
+
+ if (commit) {
+ if (offset == 0) {
+ OSSram* sram = (OSSram*)Scb.sram;
+
+ if (2u < (sram->flags & 3)) {
+ sram->flags &= ~3;
+ }
+
+ sram->checkSum = sram->checkSumInv = 0;
+ for (p = (u16*)&sram->counterBias; p < (u16*)(Scb.sram + sizeof(OSSram)); p++) {
+ sram->checkSum += *p;
+ sram->checkSumInv += ~*p;
+ }
+ }
+
+ if (offset < Scb.offset) {
+ Scb.offset = offset;
+ }
+
+ Scb.sync = WriteSram(Scb.sram + Scb.offset, Scb.offset, RTC_SRAM_SIZE - Scb.offset);
+ if (Scb.sync) {
+ Scb.offset = RTC_SRAM_SIZE;
+ }
+ }
+ Scb.locked = FALSE;
+ OSRestoreInterrupts(Scb.enabled);
+ return Scb.sync;
+}
+
+BOOL __OSUnlockSram(BOOL commit) { return UnlockSram(commit, 0); }
+
+BOOL __OSUnlockSramEx(BOOL commit) { return UnlockSram(commit, sizeof(OSSram)); }
+
+BOOL __OSSyncSram() { return Scb.sync; }
+
+BOOL __OSReadROM(void* buffer, s32 length, s32 offset) {
+ BOOL err;
+ u32 cmd;
+
+ DCInvalidateRange(buffer, (u32)length);
+
+ if (!EXILock(RTC_CHAN, RTC_DEV, 0)) {
+ return FALSE;
+ }
+ if (!EXISelect(RTC_CHAN, RTC_DEV, RTC_FREQ)) {
+ EXIUnlock(RTC_CHAN);
+ return FALSE;
+ }
+
+ cmd = (u32)(offset << 6);
+ err = FALSE;
+ err |= !EXIImm(RTC_CHAN, &cmd, 4, 1, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIDma(RTC_CHAN, buffer, length, 0, NULL);
+ err |= !EXISync(RTC_CHAN);
+ err |= !EXIDeselect(RTC_CHAN);
+ EXIUnlock(RTC_CHAN);
+
+ return !err;
+}
+
+inline OSSram* __OSLockSramHACK() { return LockSram(0); }
+#ifdef FULL_FRANK
+u32 OSGetSoundMode() {
+ OSSram* sram;
+ u32 mode;
+
+ sram = __OSLockSramHACK();
+ mode = (sram->flags & 0x4) ? OS_SOUND_MODE_STEREO : OS_SOUND_MODE_MONO;
+ __OSUnlockSram(FALSE);
+ return mode;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm u32 OSGetSoundMode() {
+ nofralloc
+ mflr r0
+ lis r3, Scb@ha
+ stw r0, 4(r1)
+ stwu r1, -0x20(r1)
+ stw r31, 0x1c(r1)
+ addi r31, r3, Scb@l
+ bl OSDisableInterrupts
+ lwz r0, 0x48(r31)
+ addi r4, r31, 0x48
+ cmpwi r0, 0
+ beq lbl_80384048
+ bl OSRestoreInterrupts
+ li r31, 0
+ b lbl_80384054
+lbl_80384048:
+ stw r3, 0x44(r31)
+ li r0, 1
+ stw r0, 0(r4)
+lbl_80384054:
+ lbz r0, 0x13(r31)
+ rlwinm. r0, r0, 0, 0x1d, 0x1d
+ beq lbl_80384068
+ li r31, 1
+ b lbl_8038406C
+lbl_80384068:
+ li r31, 0
+lbl_8038406C:
+ li r3, 0
+ li r4, 0
+ bl UnlockSram
+ mr r3, r31
+ lwz r0, 0x24(r1)
+ lwz r31, 0x1c(r1)
+ addi r1, r1, 0x20
+ mtlr r0
+ blr
+
+}
+#pragma pop
+/* clang-format on */
+#endif
+void OSSetSoundMode(u32 mode) {
+ OSSram* sram;
+ mode <<= 2;
+ mode &= 4;
+
+ sram = __OSLockSramHACK();
+ if (mode == (sram->flags & 4)) {
+ __OSUnlockSram(FALSE);
+ return;
+ }
+
+ sram->flags &= ~4;
+ sram->flags |= mode;
+ __OSUnlockSram(TRUE);
+}
+
+#ifdef FULL_FRANK
+u32 OSGetProgressiveMode() {
+ OSSram* sram;
+ u32 mode;
+
+ sram = __OSLockSramHACK();
+ mode = (sram->flags & 0x80) >> 7;
+ __OSUnlockSram(FALSE);
+ return mode;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm u32 OSGetProgressiveMode() {
+ nofralloc
+/* 80384134 00381094 7C 08 02 A6 */ mflr r0
+/* 80384138 00381098 3C 60 80 54 */ lis r3, Scb@ha
+/* 8038413C 0038109C 90 01 00 04 */ stw r0, 4(r1)
+/* 80384140 003810A0 94 21 FF E8 */ stwu r1, -0x18(r1)
+/* 80384144 003810A4 93 E1 00 14 */ stw r31, 0x14(r1)
+/* 80384148 003810A8 3B E3 15 A0 */ addi r31, r3, Scb@l
+/* 8038414C 003810AC 4B FF D5 15 */ bl OSDisableInterrupts
+/* 80384150 003810B0 80 1F 00 48 */ lwz r0, 0x48(r31)
+/* 80384154 003810B4 38 9F 00 48 */ addi r4, r31, 0x48
+/* 80384158 003810B8 2C 00 00 00 */ cmpwi r0, 0
+/* 8038415C 003810BC 41 82 00 10 */ beq lbl_8038416C
+/* 80384160 003810C0 4B FF D5 29 */ bl OSRestoreInterrupts
+/* 80384164 003810C4 3B E0 00 00 */ li r31, 0
+/* 80384168 003810C8 48 00 00 10 */ b lbl_80384178
+lbl_8038416C:
+/* 8038416C 003810CC 90 7F 00 44 */ stw r3, 0x44(r31)
+/* 80384170 003810D0 38 00 00 01 */ li r0, 1
+/* 80384174 003810D4 90 04 00 00 */ stw r0, 0(r4)
+lbl_80384178:
+/* 80384178 003810D8 88 1F 00 13 */ lbz r0, 0x13(r31)
+/* 8038417C 003810DC 38 60 00 00 */ li r3, 0
+/* 80384180 003810E0 38 80 00 00 */ li r4, 0
+/* 80384184 003810E4 54 1F CF FE */ rlwinm r31, r0, 0x19, 0x1f, 0x1f
+/* 80384188 003810E8 4B FF FA 05 */ bl UnlockSram
+/* 8038418C 003810EC 7F E3 FB 78 */ mr r3, r31
+/* 80384190 003810F0 80 01 00 1C */ lwz r0, 0x1c(r1)
+/* 80384194 003810F4 83 E1 00 14 */ lwz r31, 0x14(r1)
+/* 80384198 003810F8 38 21 00 18 */ addi r1, r1, 0x18
+/* 8038419C 003810FC 7C 08 03 A6 */ mtlr r0
+/* 803841A0 00381100 4E 80 00 20 */ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+void OSSetProgressiveMode(u32 mode) {
+ OSSram* sram;
+ mode <<= 7;
+ mode &= 0x80;
+
+ sram = __OSLockSramHACK();
+ if (mode == (sram->flags & 0x80)) {
+ __OSUnlockSram(FALSE);
+ return;
+ }
+
+ sram->flags &= ~0x80;
+ sram->flags |= mode;
+ __OSUnlockSram(TRUE);
+}
+
+#ifdef FULL_FRANK
+u8 OSGetLanguage() {
+ OSSram* sram;
+ u8 language;
+
+ sram = __OSLockSramHACK();
+ language = sram->language;
+ __OSUnlockSram(FALSE);
+ return language;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm u8 OSGetLanguage() {
+ nofralloc
+ mflr r0
+ lis r3, Scb@ha
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ addi r31, r3, Scb@l
+ bl OSDisableInterrupts
+ lwz r0, 0x48(r31)
+ addi r4, r31, 0x48
+ cmpwi r0, 0
+ beq lbl_80384280
+ bl OSRestoreInterrupts
+ li r31, 0
+ b lbl_8038428C
+lbl_80384280:
+ stw r3, 0x44(r31)
+ li r0, 1
+ stw r0, 0(r4)
+lbl_8038428C:
+ lbz r31, 0x12(r31)
+ li r3, 0
+ li r4, 0
+ bl UnlockSram
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+#ifdef FULL_FRANK
+u16 OSGetWirelessID(s32 channel) {
+ OSSramEx* sram;
+ u16 id;
+
+ sram = __OSLockSramEx();
+ id = sram->wirelessPadID[channel];
+ __OSUnlockSramEx(FALSE);
+ return id;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+
+asm u16 OSGetWirelessID(s32 channel) {
+ nofralloc
+ mflr r0
+ lis r4, Scb@ha
+ stw r0, 4(r1)
+ stwu r1, -0x20(r1)
+ stw r31, 0x1c(r1)
+ addi r31, r4, Scb@l
+ stw r30, 0x18(r1)
+ addi r30, r3, 0
+ bl OSDisableInterrupts
+ lwz r0, 0x48(r31)
+ addi r4, r31, 0x48
+ cmpwi r0, 0
+ beq lbl_803842F4
+ bl OSRestoreInterrupts
+ li r3, 0
+ b lbl_80384304
+lbl_803842F4:
+ stw r3, 0x44(r31)
+ li r0, 1
+ addi r3, r31, 0x14
+ stw r0, 0(r4)
+lbl_80384304:
+ slwi r0, r30, 1
+ add r3, r3, r0
+ lhz r31, 0x1c(r3)
+ li r3, 0
+ li r4, 0x14
+ bl UnlockSram
+ mr r3, r31
+ lwz r0, 0x24(r1)
+ lwz r31, 0x1c(r1)
+ lwz r30, 0x18(r1)
+ addi r1, r1, 0x20
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+
+void OSSetWirelessID(s32 channel, u16 id) {
+ OSSramEx* sram;
+
+ sram = __OSLockSramEx();
+ if (sram->wirelessPadID[channel] != id) {
+ sram->wirelessPadID[channel] = id;
+ __OSUnlockSramEx(TRUE);
+ return;
+ }
+
+ __OSUnlockSramEx(FALSE);
+}
diff --git a/src/Dolphin/os/OSSync.c b/src/Dolphin/os/OSSync.c
new file mode 100644
index 0000000..b5aeccf
--- /dev/null
+++ b/src/Dolphin/os/OSSync.c
@@ -0,0 +1,29 @@
+#include "string.h"
+#include "dolphin/PPCArch.h"
+#include "dolphin/os.h"
+
+void __OSSystemCallVectorStart();
+void __OSSystemCallVectorEnd();
+static asm void SystemCallVector() {
+ nofralloc
+entry __OSSystemCallVectorStart
+ mfspr r9, HID0
+ ori r10, r9, 8
+ mtspr HID0, r10
+ isync
+ sync
+ mtspr HID0, r9
+
+ rfi
+
+entry __OSSystemCallVectorEnd
+ nop
+}
+
+void __OSInitSystemCall() {
+ void* addr = OSPhysicalToCached(0x00C00);
+ memcpy(addr, __OSSystemCallVectorStart, (size_t)__OSSystemCallVectorEnd - (size_t)__OSSystemCallVectorStart);
+ DCFlushRangeNoSync(addr, 0x100);
+ __sync();
+ ICInvalidateRange(addr, 0x100);
+}
diff --git a/src/Dolphin/os/OSThread.c b/src/Dolphin/os/OSThread.c
new file mode 100644
index 0000000..3bd1e5d
--- /dev/null
+++ b/src/Dolphin/os/OSThread.c
@@ -0,0 +1,852 @@
+#include "dolphin/os/OSPriv.h"
+
+static vu32 RunQueueBits;
+static volatile BOOL RunQueueHint;
+static vs32 Reschedule;
+
+static OSThreadQueue RunQueue[32];
+static OSThread IdleThread;
+static OSThread DefaultThread;
+static OSContext IdleContext;
+static void DefaultSwitchThreadCallback(OSThread* from, OSThread* to);
+static OSSwitchThreadCallback SwitchThreadCallback = DefaultSwitchThreadCallback;
+
+OSThread* __OSCurrentThread : OS_BASE_CACHED + 0x00E4;
+OSThreadQueue __OSActiveThreadQueue : OS_BASE_CACHED + 0x00DC;
+volatile OSContext __OSCurrentContext : OS_BASE_CACHED + 0x00D4;
+volatile OSContext* __OSFPUContext : OS_BASE_CACHED + 0x00D8;
+
+static void DefaultSwitchThreadCallback(OSThread* from, OSThread* to) {}
+
+extern u8 _stack_addr[];
+extern u8 _stack_end[];
+
+#define AddTail(queue, thread, link) \
+ do { \
+ OSThread* prev; \
+ \
+ prev = (queue)->tail; \
+ if (prev == NULL) \
+ (queue)->head = (thread); \
+ else \
+ prev->link.next = (thread); \
+ (thread)->link.prev = prev; \
+ (thread)->link.next = NULL; \
+ (queue)->tail = (thread); \
+ } while (0)
+
+#define AddPrio(queue, thread, link) \
+ do { \
+ OSThread *prev, *next; \
+ \
+ for (next = (queue)->head; next && next->priority <= thread->priority; next = next->link.next) \
+ ; \
+ if (next == NULL) \
+ AddTail(queue, thread, link); \
+ else { \
+ (thread)->link.next = next; \
+ prev = next->link.prev; \
+ next->link.prev = (thread); \
+ (thread)->link.prev = prev; \
+ if (prev == NULL) \
+ (queue)->head = (thread); \
+ else \
+ prev->link.next = (thread); \
+ } \
+ } while (0)
+
+#define RemoveItem(queue, thread, link) \
+ do { \
+ OSThread *next, *prev; \
+ next = (thread)->link.next; \
+ prev = (thread)->link.prev; \
+ if (next == NULL) \
+ (queue)->tail = prev; \
+ else \
+ next->link.prev = prev; \
+ if (prev == NULL) \
+ (queue)->head = next; \
+ else \
+ prev->link.next = next; \
+ } while (0)
+
+#define RemoveHead(queue, thread, link) \
+ do { \
+ OSThread* __next; \
+ (thread) = (queue)->head; \
+ __next = (thread)->link.next; \
+ if (__next == NULL) \
+ (queue)->tail = NULL; \
+ else \
+ __next->link.prev = NULL; \
+ (queue)->head = __next; \
+ } while (0)
+
+static inline void OSInitMutexQueue(OSMutexQueue* queue) { queue->head = queue->tail = NULL; }
+
+static inline void OSSetCurrentThread(OSThread* thread) {
+ SwitchThreadCallback(__OSCurrentThread, thread);
+ __OSCurrentThread = thread;
+}
+
+void __OSThreadInit() {
+ OSThread* thread = &DefaultThread;
+ int prio;
+
+ thread->state = OS_THREAD_STATE_RUNNING;
+ thread->attr = OS_THREAD_ATTR_DETACH;
+ thread->priority = thread->base = 16;
+ thread->suspend = 0;
+ thread->val = (void*)-1;
+ thread->mutex = NULL;
+ OSInitThreadQueue(&thread->queueJoin);
+ OSInitMutexQueue(&thread->queueMutex);
+
+ __OSFPUContext = &thread->context;
+
+ OSClearContext(&thread->context);
+ OSSetCurrentContext(&thread->context);
+ thread->stackBase = (void*)_stack_addr;
+ thread->stackEnd = (void*)_stack_end;
+ *(thread->stackEnd) = OS_THREAD_STACK_MAGIC;
+
+ OSSetCurrentThread(thread);
+ OSClearStack(0);
+
+ RunQueueBits = 0;
+ RunQueueHint = FALSE;
+ for (prio = OS_PRIORITY_MIN; prio <= OS_PRIORITY_MAX; ++prio) {
+ OSInitThreadQueue(&RunQueue[prio]);
+ }
+
+ OSInitThreadQueue(&__OSActiveThreadQueue);
+ AddTail(&__OSActiveThreadQueue, thread, linkActive);
+ OSClearContext(&IdleContext);
+ Reschedule = 0;
+}
+
+void OSInitThreadQueue(OSThreadQueue* queue) { queue->head = queue->tail = NULL; }
+
+OSThread* OSGetCurrentThread() { return __OSCurrentThread; }
+
+#ifdef FULL_FRANK
+/* Code matches, stack epilogue bug*/
+s32 OSDisableScheduler() {
+ BOOL enabled;
+ s32 count;
+
+ enabled = OSDisableInterrupts();
+ count = Reschedule++;
+ OSRestoreInterrupts(enabled);
+ return count;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm s32 OSDisableScheduler() {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x10(r1)
+ stw r31, 0xc(r1)
+ bl OSDisableInterrupts
+ lwz r4, Reschedule
+ addi r0, r4, 1
+ stw r0, Reschedule
+ mr r31, r4
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x14(r1)
+ lwz r31, 0xc(r1)
+ addi r1, r1, 0x10
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+#ifdef FULL_FRANK
+/* Code matches, stack epilogue bug*/
+s32 OSEnableScheduler() {
+ BOOL enabled;
+ s32 count;
+
+ enabled = OSDisableInterrupts();
+ count = Reschedule--;
+ OSRestoreInterrupts(enabled);
+ return count;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm s32 OSEnableScheduler() {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x10(r1)
+ stw r31, 0xc(r1)
+ bl OSDisableInterrupts
+ lwz r4, Reschedule
+ subi r0, r4, 1
+ stw r0, Reschedule
+ mr r31, r4
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x14(r1)
+ lwz r31, 0xc(r1)
+ addi r1, r1, 0x10
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+static void SetRun(OSThread* thread) {
+ thread->queue = &RunQueue[thread->priority];
+ AddTail(thread->queue, thread, link);
+ RunQueueBits |= 1u << (OS_PRIORITY_MAX - thread->priority);
+ RunQueueHint = TRUE;
+}
+#pragma dont_inline on
+static void UnsetRun(OSThread* thread) {
+ OSThreadQueue* queue;
+ queue = thread->queue;
+ RemoveItem(queue, thread, link);
+ if (queue->head == 0)
+ RunQueueBits &= ~(1u << (OS_PRIORITY_MAX - thread->priority));
+ thread->queue = NULL;
+}
+#pragma dont_inline reset
+
+OSPriority __OSGetEffectivePriority(OSThread* thread) {
+ OSPriority priority;
+ OSMutex* mutex;
+ OSThread* blocked;
+
+ priority = thread->base;
+ for (mutex = thread->queueMutex.head; mutex; mutex = mutex->link.next) {
+ blocked = mutex->queue.head;
+ if (blocked && blocked->priority < priority) {
+ priority = blocked->priority;
+ }
+ }
+ return priority;
+}
+
+static OSThread* SetEffectivePriority(OSThread* thread, OSPriority priority) {
+ switch (thread->state) {
+ case OS_THREAD_STATE_READY:
+ UnsetRun(thread);
+ thread->priority = priority;
+ SetRun(thread);
+ break;
+ case OS_THREAD_STATE_WAITING:
+ RemoveItem(thread->queue, thread, link);
+ thread->priority = priority;
+ AddPrio(thread->queue, thread, link);
+ if (thread->mutex) {
+ return thread->mutex->thread;
+ }
+ break;
+ case OS_THREAD_STATE_RUNNING:
+ RunQueueHint = TRUE;
+ thread->priority = priority;
+ break;
+ }
+ return NULL;
+}
+
+static void UpdatePriority(OSThread* thread) {
+ OSPriority priority;
+
+ do {
+ if (0 < thread->suspend) {
+ break;
+ }
+ priority = __OSGetEffectivePriority(thread);
+ if (thread->priority == priority) {
+ break;
+ }
+ thread = SetEffectivePriority(thread, priority);
+ } while (thread);
+}
+
+static void __OSSwitchThread(OSThread* nextThread) {
+ OSSetCurrentThread(nextThread);
+ OSSetCurrentContext(&nextThread->context);
+ OSLoadContext(&nextThread->context);
+}
+
+static OSThread* SelectThread(BOOL yield) {
+ OSContext* currentContext;
+ OSThread* currentThread;
+ OSThread* nextThread;
+ OSPriority priority;
+ OSThreadQueue* queue;
+
+ if (0 < Reschedule) {
+ return 0;
+ }
+
+ currentContext = OSGetCurrentContext();
+ currentThread = OSGetCurrentThread();
+ if (currentContext != &currentThread->context) {
+ return 0;
+ }
+
+ if (currentThread) {
+ if (currentThread->state == OS_THREAD_STATE_RUNNING) {
+ if (!yield) {
+ priority = __cntlzw(RunQueueBits);
+ if (currentThread->priority <= priority) {
+ return 0;
+ }
+ }
+ currentThread->state = OS_THREAD_STATE_READY;
+ SetRun(currentThread);
+ }
+
+ if (!(currentThread->context.state & OS_CONTEXT_STATE_EXC) &&
+ OSSaveContext(&currentThread->context)) {
+ return 0;
+ }
+ }
+
+ OSSetCurrentThread(NULL);
+ if (RunQueueBits == 0) {
+ OSSetCurrentContext(&IdleContext);
+ do {
+ OSEnableInterrupts();
+ while (RunQueueBits == 0)
+ ;
+ OSDisableInterrupts();
+ } while (RunQueueBits == 0);
+
+ OSClearContext(&IdleContext);
+ }
+
+ RunQueueHint = FALSE;
+
+ priority = __cntlzw(RunQueueBits);
+ queue = &RunQueue[priority];
+ RemoveHead(queue, nextThread, link);
+ if (queue->head == 0) {
+ RunQueueBits &= ~(1u << (OS_PRIORITY_MAX - priority));
+ }
+ nextThread->queue = NULL;
+ nextThread->state = OS_THREAD_STATE_RUNNING;
+ __OSSwitchThread(nextThread);
+ return nextThread;
+}
+
+void __OSReschedule() {
+ if (!RunQueueHint) {
+ return;
+ }
+
+ SelectThread(FALSE);
+}
+
+void OSYieldThread(void) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ SelectThread(TRUE);
+ OSRestoreInterrupts(enabled);
+}
+
+void OSCancelThread(OSThread* thread) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ switch (thread->state) {
+ case OS_THREAD_STATE_READY:
+ if (!(0 < thread->suspend)) {
+ UnsetRun(thread);
+ }
+ break;
+ case OS_THREAD_STATE_RUNNING:
+ RunQueueHint = TRUE;
+ break;
+ case OS_THREAD_STATE_WAITING:
+ RemoveItem(thread->queue, thread, link);
+ thread->queue = NULL;
+ if (!(0 < thread->suspend) && thread->mutex) {
+ UpdatePriority(thread->mutex->thread);
+ }
+ break;
+ default:
+ OSRestoreInterrupts(enabled);
+ return;
+ }
+
+ OSClearContext(&thread->context);
+ if (thread->attr & OS_THREAD_ATTR_DETACH) {
+ RemoveItem(&__OSActiveThreadQueue, thread, linkActive);
+ thread->state = 0;
+ } else {
+ thread->state = OS_THREAD_STATE_MORIBUND;
+ }
+
+ __OSUnlockAllMutex(thread);
+
+ OSWakeupThread(&thread->queueJoin);
+
+ __OSReschedule();
+ OSRestoreInterrupts(enabled);
+
+ return;
+}
+
+#ifdef FULL_FRANK
+/* Code matches, stack epilogue bug*/
+s32 OSResumeThread(OSThread* thread) {
+ BOOL enabled;
+ s32 suspendCount;
+
+ enabled = OSDisableInterrupts();
+ suspendCount = thread->suspend--;
+ if (thread->suspend < 0) {
+ thread->suspend = 0;
+ } else if (thread->suspend == 0) {
+ switch (thread->state) {
+ case OS_THREAD_STATE_READY:
+ thread->priority = __OSGetEffectivePriority(thread);
+ SetRun(thread);
+ break;
+ case OS_THREAD_STATE_WAITING:
+ RemoveItem(thread->queue, thread, link);
+ thread->priority = __OSGetEffectivePriority(thread);
+ AddPrio(thread->queue, thread, link);
+ if (thread->mutex) {
+ UpdatePriority(thread->mutex->thread);
+ }
+ break;
+ }
+ __OSReschedule();
+ }
+ OSRestoreInterrupts(enabled);
+ return suspendCount;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 OSResumeThread(OSThread* thread) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x28(r1)
+ stw r31, 0x24(r1)
+ stw r30, 0x20(r1)
+ stw r29, 0x1c(r1)
+ mr r29, r3
+ bl OSDisableInterrupts
+ lwz r4, 0x2cc(r29)
+ addi r31, r3, 0
+ addi r0, r4, -1
+ stw r0, 0x2cc(r29)
+ mr r30, r4
+ lwz r0, 0x2cc(r29)
+ cmpwi r0, 0
+ bge lbl_80384D60
+ li r0, 0
+ stw r0, 0x2cc(r29)
+ b lbl_80384F74
+lbl_80384D60:
+ bne lbl_80384F74
+ lhz r0, 0x2c8(r29)
+ cmpwi r0, 4
+ beq lbl_80384E24
+ bge lbl_80384F60
+ cmpwi r0, 1
+ beq lbl_80384D80
+ b lbl_80384F60
+lbl_80384D80:
+ lwz r0, 0x2d4(r29)
+ lwz r3, 0x2f4(r29)
+ b lbl_80384DAC
+lbl_80384D8C:
+ lwz r4, 0(r3)
+ cmplwi r4, 0
+ beq lbl_80384DA8
+ lwz r4, 0x2d0(r4)
+ cmpw r4, r0
+ bge lbl_80384DA8
+ mr r0, r4
+lbl_80384DA8:
+ lwz r3, 0x10(r3)
+lbl_80384DAC:
+ cmplwi r3, 0
+ bne lbl_80384D8C
+ stw r0, 0x2d0(r29)
+ lis r3, RunQueue@ha
+ addi r0, r3, RunQueue@l
+ lwz r3, 0x2d0(r29)
+ slwi r3, r3, 3
+ add r0, r0, r3
+ stw r0, 0x2dc(r29)
+ lwz r4, 0x2dc(r29)
+ lwz r3, 4(r4)
+ cmplwi r3, 0
+ bne lbl_80384DE8
+ stw r29, 0(r4)
+ b lbl_80384DEC
+lbl_80384DE8:
+ stw r29, 0x2e0(r3)
+lbl_80384DEC:
+ stw r3, 0x2e4(r29)
+ li r0, 0
+ li r3, 1
+ stw r0, 0x2e0(r29)
+ lwz r4, 0x2dc(r29)
+ stw r29, 4(r4)
+ lwz r0, 0x2d0(r29)
+ lwz r4, RunQueueBits
+ subfic r0, r0, 0x1f
+ slw r0, r3, r0
+ or r0, r4, r0
+ stw r0, RunQueueBits
+ stw r3, RunQueueHint
+ b lbl_80384F60
+lbl_80384E24:
+ lwz r4, 0x2e0(r29)
+ lwz r5, 0x2e4(r29)
+ cmplwi r4, 0
+ bne lbl_80384E40
+ lwz r3, 0x2dc(r29)
+ stw r5, 4(r3)
+ b lbl_80384E44
+lbl_80384E40:
+ stw r5, 0x2e4(r4)
+lbl_80384E44:
+ cmplwi r5, 0
+ bne lbl_80384E58
+ lwz r3, 0x2dc(r29)
+ stw r4, 0(r3)
+ b lbl_80384E5C
+lbl_80384E58:
+ stw r4, 0x2e0(r5)
+lbl_80384E5C:
+ lwz r0, 0x2d4(r29)
+ lwz r3, 0x2f4(r29)
+ b lbl_80384E88
+lbl_80384E68:
+ lwz r4, 0(r3)
+ cmplwi r4, 0
+ beq lbl_80384E84
+ lwz r4, 0x2d0(r4)
+ cmpw r4, r0
+ bge lbl_80384E84
+ mr r0, r4
+lbl_80384E84:
+ lwz r3, 0x10(r3)
+lbl_80384E88:
+ cmplwi r3, 0
+ bne lbl_80384E68
+ stw r0, 0x2d0(r29)
+ lwz r4, 0x2dc(r29)
+ lwz r5, 0(r4)
+ b lbl_80384EA4
+lbl_80384EA0:
+ lwz r5, 0x2e0(r5)
+lbl_80384EA4:
+ cmplwi r5, 0
+ beq lbl_80384EBC
+ lwz r3, 0x2d0(r5)
+ lwz r0, 0x2d0(r29)
+ cmpw r3, r0
+ ble lbl_80384EA0
+lbl_80384EBC:
+ cmplwi r5, 0
+ bne lbl_80384EF4
+ lwz r3, 4(r4)
+ cmplwi r3, 0
+ bne lbl_80384ED8
+ stw r29, 0(r4)
+ b lbl_80384EDC
+lbl_80384ED8:
+ stw r29, 0x2e0(r3)
+lbl_80384EDC:
+ stw r3, 0x2e4(r29)
+ li r0, 0
+ stw r0, 0x2e0(r29)
+ lwz r3, 0x2dc(r29)
+ stw r29, 4(r3)
+ b lbl_80384F1C
+lbl_80384EF4:
+ stw r5, 0x2e0(r29)
+ lwz r3, 0x2e4(r5)
+ stw r29, 0x2e4(r5)
+ cmplwi r3, 0
+ stw r3, 0x2e4(r29)
+ bne lbl_80384F18
+ lwz r3, 0x2dc(r29)
+ stw r29, 0(r3)
+ b lbl_80384F1C
+lbl_80384F18:
+ stw r29, 0x2e0(r3)
+lbl_80384F1C:
+ lwz r3, 0x2f0(r29)
+ cmplwi r3, 0
+ beq lbl_80384F60
+ lwz r29, 8(r3)
+lbl_80384F2C:
+ lwz r0, 0x2cc(r29)
+ cmpwi r0, 0
+ bgt lbl_80384F60
+ mr r3, r29
+ bl __OSGetEffectivePriority
+ lwz r0, 0x2d0(r29)
+ addi r4, r3, 0
+ cmpw r0, r4
+ beq lbl_80384F60
+ mr r3, r29
+ bl SetEffectivePriority
+ or. r29, r3, r3
+ bne lbl_80384F2C
+lbl_80384F60:
+ lwz r0, RunQueueHint
+ cmpwi r0, 0
+ beq lbl_80384F74
+ li r3, 0
+ bl SelectThread
+lbl_80384F74:
+ mr r3, r31
+ bl OSRestoreInterrupts
+ mr r3, r30
+ lwz r0, 0x2c(r1)
+ lwz r31, 0x24(r1)
+ lwz r30, 0x20(r1)
+ lwz r29, 0x1c(r1)
+ addi r1, r1, 0x28
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+
+#endif
+
+#ifdef FULL_FRANK
+/* Code matches, stack epilogue bug*/
+s32 OSSuspendThread(OSThread* thread) {
+ BOOL enabled;
+ s32 suspendCount;
+
+ enabled = OSDisableInterrupts();
+ suspendCount = thread->suspend++;
+ if (suspendCount == 0) {
+ switch (thread->state) {
+ case OS_THREAD_STATE_RUNNING:
+ RunQueueHint = TRUE;
+ thread->state = OS_THREAD_STATE_READY;
+ break;
+ case OS_THREAD_STATE_READY:
+ UnsetRun(thread);
+ break;
+ case OS_THREAD_STATE_WAITING:
+ RemoveItem(thread->queue, thread, link);
+ thread->priority = 32;
+ AddTail(thread->queue, thread, link);
+ if (thread->mutex) {
+ UpdatePriority(thread->mutex->thread);
+ }
+ break;
+ }
+
+ __OSReschedule();
+ }
+ OSRestoreInterrupts(enabled);
+ return suspendCount;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 OSSuspendThread(OSThread* thread) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x20(r1)
+ stw r31, 0x1c(r1)
+ stw r30, 0x18(r1)
+ stw r29, 0x14(r1)
+ mr r29, r3
+ bl OSDisableInterrupts
+ lwz r4, 0x2cc(r29)
+ addi r31, r3, 0
+ addi r0, r4, 1
+ or. r30, r4, r4
+ stw r0, 0x2cc(r29)
+ bne lbl_803850E4
+ lhz r0, 0x2c8(r29)
+ cmpwi r0, 3
+ beq lbl_803850D0
+ bge lbl_80384FF4
+ cmpwi r0, 1
+ beq lbl_80385010
+ bge lbl_80385000
+ b lbl_803850D0
+lbl_80384FF4:
+ cmpwi r0, 5
+ bge lbl_803850D0
+ b lbl_8038501C
+lbl_80385000:
+ li r0, 1
+ stw r0, RunQueueHint
+ sth r0, 0x2c8(r29)
+ b lbl_803850D0
+lbl_80385010:
+ mr r3, r29
+ bl UnsetRun
+ b lbl_803850D0
+lbl_8038501C:
+ lwz r4, 0x2e0(r29)
+ lwz r5, 0x2e4(r29)
+ cmplwi r4, 0
+ bne lbl_80385038
+ lwz r3, 0x2dc(r29)
+ stw r5, 4(r3)
+ b lbl_8038503C
+lbl_80385038:
+ stw r5, 0x2e4(r4)
+lbl_8038503C:
+ cmplwi r5, 0
+ bne lbl_80385050
+ lwz r3, 0x2dc(r29)
+ stw r4, 0(r3)
+ b lbl_80385054
+lbl_80385050:
+ stw r4, 0x2e0(r5)
+lbl_80385054:
+ li r0, 0x20
+ stw r0, 0x2d0(r29)
+ lwz r4, 0x2dc(r29)
+ lwz r3, 4(r4)
+ cmplwi r3, 0
+ bne lbl_80385074
+ stw r29, 0(r4)
+ b lbl_80385078
+lbl_80385074:
+ stw r29, 0x2e0(r3)
+lbl_80385078:
+ stw r3, 0x2e4(r29)
+ li r0, 0
+ stw r0, 0x2e0(r29)
+ lwz r3, 0x2dc(r29)
+ stw r29, 4(r3)
+ lwz r3, 0x2f0(r29)
+ cmplwi r3, 0
+ beq lbl_803850D0
+ lwz r29, 8(r3)
+lbl_8038509C:
+ lwz r0, 0x2cc(r29)
+ cmpwi r0, 0
+ bgt lbl_803850D0
+ mr r3, r29
+ bl __OSGetEffectivePriority
+ lwz r0, 0x2d0(r29)
+ addi r4, r3, 0
+ cmpw r0, r4
+ beq lbl_803850D0
+ mr r3, r29
+ bl SetEffectivePriority
+ or. r29, r3, r3
+ bne lbl_8038509C
+lbl_803850D0:
+ lwz r0, RunQueueHint
+ cmpwi r0, 0
+ beq lbl_803850E4
+ li r3, 0
+ bl SelectThread
+lbl_803850E4:
+ mr r3, r31
+ bl OSRestoreInterrupts
+ mr r3, r30
+ lwz r0, 0x24(r1)
+ lwz r31, 0x1c(r1)
+ lwz r30, 0x18(r1)
+ lwz r29, 0x14(r1)
+ addi r1, r1, 0x20
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+void OSSleepThread(OSThreadQueue* queue) {
+ BOOL enabled;
+ OSThread* currentThread;
+
+ enabled = OSDisableInterrupts();
+ currentThread = OSGetCurrentThread();
+
+ currentThread->state = OS_THREAD_STATE_WAITING;
+ currentThread->queue = queue;
+ AddPrio(queue, currentThread, link);
+ RunQueueHint = TRUE;
+ __OSReschedule();
+ OSRestoreInterrupts(enabled);
+}
+
+void OSWakeupThread(OSThreadQueue* queue) {
+ BOOL enabled;
+ OSThread* thread;
+
+ enabled = OSDisableInterrupts();
+ while (queue->head) {
+ RemoveHead(queue, thread, link);
+ thread->state = OS_THREAD_STATE_READY;
+ if (!(0 < thread->suspend)) {
+ SetRun(thread);
+ }
+ }
+ __OSReschedule();
+ OSRestoreInterrupts(enabled);
+}
+
+BOOL OSSetThreadPriority(OSThread* thread, s32 prio) {
+ BOOL enabled;
+
+ if (prio < OS_PRIORITY_MIN || prio > OS_PRIORITY_MAX) {
+ return FALSE;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ if (thread->base != prio) {
+ thread->base = prio;
+ UpdatePriority(thread);
+ __OSReschedule();
+ }
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+OSPriority OSGetThreadPriority(OSThread *thread) {
+ return thread->base;
+}
+
+void OSClearStack(u8 val) {
+ register u32 sp;
+ register u32* p;
+ register u32 pattern;
+
+ pattern = ((u32)val << 24) | ((u32)val << 16) | ((u32)val << 8) | (u32)val;
+ sp = OSGetStackPointer();
+ for (p = __OSCurrentThread->stackEnd + 1; p < (u32*)sp; ++p) {
+ *p = pattern;
+ }
+}
diff --git a/src/Dolphin/os/OSTime.c b/src/Dolphin/os/OSTime.c
new file mode 100644
index 0000000..4250798
--- /dev/null
+++ b/src/Dolphin/os/OSTime.c
@@ -0,0 +1,137 @@
+#include <dolphin/os.h>
+
+#define OS_TIME_MONTH_MAX 12
+#define OS_TIME_WEEK_DAY_MAX 7
+#define OS_TIME_YEAR_DAY_MAX 365
+
+// End of each month in standard year
+static s32 YearDays[OS_TIME_MONTH_MAX] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+// End of each month in leap year
+static s32 LeapYearDays[OS_TIME_MONTH_MAX] = {0, 31, 60, 91, 121, 152,
+ 182, 213, 244, 274, 305, 335};
+
+asm OSTime OSGetTime(void) {
+ // clang-format off
+ nofralloc
+
+ mftbu r3
+ mftb r4
+
+ // Check for possible carry from TBL to TBU
+ mftbu r5
+ cmpw r3, r5
+ bne OSGetTime
+
+ blr
+ // clang-format on
+}
+
+asm OSTick OSGetTick(void){
+ // clang-format off
+ nofralloc
+
+ mftb r3
+ blr
+ // clang-format on
+}
+
+#define OS_SYSTEMTIME_BASE 0x30D8
+
+OSTime __OSGetSystemTime(void) {
+ BOOL enabled;
+ OSTime* timeAdjustAddr = (OSTime*)(OS_BASE_CACHED + OS_SYSTEMTIME_BASE);
+ OSTime result;
+
+ enabled = OSDisableInterrupts();
+ result = *timeAdjustAddr + OSGetTime();
+ OSRestoreInterrupts(enabled);
+
+ return result;
+}
+
+OSTime __OSTimeToSystemTime(OSTime time) {
+ BOOL enabled;
+ OSTime* timeAdjustAddr = (OSTime*)(OS_BASE_CACHED + OS_SYSTEMTIME_BASE);
+ OSTime result;
+
+ enabled = OSDisableInterrupts();
+ result = *timeAdjustAddr + time;
+ OSRestoreInterrupts(enabled);
+
+ return result;
+}
+
+static BOOL IsLeapYear(s32 year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }
+
+static s32 GetYearDays(s32 year, s32 mon) {
+ return (IsLeapYear(year) ? LeapYearDays : YearDays)[mon];
+}
+
+static s32 GetLeapDays(s32 year) {
+ if (year < 1) {
+ return 0;
+ }
+ return (year + 3) / 4 - (year - 1) / 100 + (year - 1) / 400;
+}
+
+static void GetDates(s32 days, OSCalendarTime* cal) {
+ s32 year;
+ s32 totalDays;
+ s32* p_days;
+ s32 month;
+ cal->wday = (days + 6) % OS_TIME_WEEK_DAY_MAX;
+
+ for (year = days / OS_TIME_YEAR_DAY_MAX;
+ days < (totalDays = year * OS_TIME_YEAR_DAY_MAX + GetLeapDays(year));) {
+ year--;
+ }
+
+ days -= totalDays;
+ cal->year = year;
+ cal->yday = days;
+
+ p_days = IsLeapYear(year) ? LeapYearDays : YearDays;
+ month = OS_TIME_MONTH_MAX;
+ while (days < p_days[--month]) {
+ ;
+ }
+ cal->mon = month;
+ cal->mday = days - p_days[month] + 1;
+}
+
+#define BIAS (2000 * 365 + (2000 + 3) / 4 - (2000 - 1) / 100 + (2000 - 1) / 400)
+
+#pragma push
+#pragma dont_inline on
+void OSTicksToCalendarTime(OSTime ticks, OSCalendarTime* td) {
+ int days;
+ int secs;
+ OSTime d;
+
+ d = ticks % OSSecondsToTicks(1);
+ if (d < 0) {
+ d += OSSecondsToTicks(1);
+ }
+ td->usec = (int)(OSTicksToMicroseconds(d) % 1000);
+ td->msec = (int)(OSTicksToMilliseconds(d) % 1000);
+
+ ticks -= d;
+ days = (int)(OSTicksToSeconds(ticks) / 86400 + BIAS);
+ secs = (int)(OSTicksToSeconds(ticks) % 86400);
+ if (secs < 0) {
+ days -= 1;
+ secs += 24 * 60 * 60;
+ }
+
+ GetDates(days, td);
+
+ td->hour = secs / 60 / 60;
+ td->min = (secs / 60) % 60;
+ td->sec = secs % 60;
+}
+#pragma dont_inline reset
+
+OSTime OSCalendarTimeToTicks(OSCalendarTime* time) {
+ ;
+ ;
+}
diff --git a/src/Dolphin/os/__ppc_eabi_init.cpp b/src/Dolphin/os/__ppc_eabi_init.cpp
index a0e7574..67c85be 100644
--- a/src/Dolphin/os/__ppc_eabi_init.cpp
+++ b/src/Dolphin/os/__ppc_eabi_init.cpp
@@ -1,5 +1,3 @@
-// This file was taken from the Metroid Prime decompilation project.
-// https://github.com/PrimeDecomp/prime/blob/main/src/Dolphin/os/__ppc_eabi_init.cpp
#include "dolphin/__ppc_eabi_init.h"
#include "dolphin/PPCArch.h"
@@ -10,37 +8,39 @@ void __OSPSInit();
void __OSCacheInit();
asm void __init_hardware(void) {
- nofralloc
- mfmsr r0
- ori r0, r0, 0x2000
- mtmsr r0
+ // clang-format off
+ nofralloc
+ mfmsr r0
+ ori r0, r0, 0x2000
+ mtmsr r0
- mflr r31
- bl __OSPSInit
- bl __OSCacheInit
- mtlr r31
- blr
+ mflr r31
+ bl __OSPSInit
+ bl __OSCacheInit
+ mtlr r31
+ blr
+ // clang-format on
}
asm void __flush_cache(register void* address, register unsigned int size) {
- // clang-format off
- nofralloc
- lis r5, ~0
- ori r5, r5, ~14
- and r5, r5, r3
- subf r3, r5, r3
- add r4, r4, r3
+ // clang-format off
+ nofralloc
+ lis r5, ~0
+ ori r5, r5, ~14
+ and r5, r5, r3
+ subf r3, r5, r3
+ add r4, r4, r3
-loop:
- dcbst r0, r5
- sync
- icbi r0, r5
- addic r5, r5, 8
- subic. r4, r4, 8
- bge loop
- isync
- blr
- // clang-format on
+loop:
+ dcbst r0, r5
+ sync
+ icbi r0, r5
+ addic r5, r5, 8
+ subic. r4, r4, 8
+ bge loop
+ isync
+ blr
+ // clang-format on
}
void __init_user() { __init_cpp(); }
@@ -50,14 +50,14 @@ __declspec(section ".init") extern voidfunctionptr _ctors[];
__declspec(section ".init") extern voidfunctionptr _dtors[];
void __init_cpp(void) {
- voidfunctionptr* constructor;
+ voidfunctionptr* constructor;
- /*
- * call static initializers
- */
- for (constructor = _ctors; *constructor; constructor++) {
- (*constructor)();
- }
+ /*
+ * call static initializers
+ */
+ for (constructor = _ctors; *constructor; constructor++) {
+ (*constructor)();
+ }
}
void _ExitProcess(void) { PPCHalt(); }
diff --git a/src/Dolphin/os/__start.c b/src/Dolphin/os/__start.c
index 4cb44e2..f180de8 100644
--- a/src/Dolphin/os/__start.c
+++ b/src/Dolphin/os/__start.c
@@ -1,54 +1,47 @@
-// This file was taken from the Super Mario Sunshine decompilation project.
-// https://github.com/doldecomp/sms/blob/master/src/os/__start.c
#include "dolphin/__start.h"
+#include "__ppc_eabi_linker.h"
-#pragma section code_type ".init"
-
-void __check_pad3(void)
-{
- if ((Pad3Button & 0x0eef) == 0x0eef) {
- OSResetSystem(OS_RESET_RESTART, 0, FALSE);
- }
- return;
+void __check_pad3(void) {
+ if ((Pad3Button & 0x0eef) == 0x0eef) {
+ OSResetSystem(OS_RESET_RESTART, 0, FALSE);
+ }
+ return;
}
-#ifndef LD_TEST
-// clang-format off
-
-__declspec (weak) asm void __start(void)
-{
- nofralloc
+__declspec(weak) asm void __start(void) {
+ // clang-format off
+ nofralloc
bl __init_registers
- bl __init_hardware
- li r0, -1
- stwu r1, -8(r1)
- stw r0, 4(r1)
- stw r0, 0(r1)
- bl __init_data
- li r0, 0
- lis r6, EXCEPTIONMASK_ADDR@ha
- addi r6, r6, EXCEPTIONMASK_ADDR@l
- stw r0, 0(r6)
- lis r6, BOOTINFO2_ADDR@ha
- addi r6, r6, BOOTINFO2_ADDR@l
- lwz r6, 0(r6)
+ bl __init_hardware
+ li r0, -1
+ stwu r1, -8(r1)
+ stw r0, 4(r1)
+ stw r0, 0(r1)
+ bl __init_data
+ li r0, 0
+ lis r6, EXCEPTIONMASK_ADDR@ha
+ addi r6, r6, EXCEPTIONMASK_ADDR@l
+ stw r0, 0(r6)
+ lis r6, BOOTINFO2_ADDR@ha
+ addi r6, r6, BOOTINFO2_ADDR@l
+ lwz r6, 0(r6)
_check_TRK:
- cmplwi r6, 0
- beq _load_lomem_debug_flag
- lwz r7, OS_BI2_DEBUGFLAG_OFFSET(r6)
- b _check_debug_flag
-
+ cmplwi r6, 0
+ beq _load_lomem_debug_flag
+ lwz r7, OS_BI2_DEBUGFLAG_OFFSET(r6)
+ b _check_debug_flag
+
_load_lomem_debug_flag:
lis r5, ARENAHI_ADDR@ha
addi r5, r5, ARENAHI_ADDR@l
- lwz r5, 0(r5)
- cmplwi r5, 0
- beq _goto_main
- lis r7, DEBUGFLAG_ADDR@ha
- addi r7, r7, DEBUGFLAG_ADDR@l
- lwz r7, 0(r7)
-
+ lwz r5, 0(r5)
+ cmplwi r5, 0
+ beq _goto_main
+ lis r7, DEBUGFLAG_ADDR@ha
+ addi r7, r7, DEBUGFLAG_ADDR@l
+ lwz r7, 0(r7)
+
_check_debug_flag:
li r5, 0
cmplwi r7, 2
@@ -62,7 +55,7 @@ _goto_inittrk:
addi r6, r6, InitMetroTRK@l
mtlr r6
blrl
-
+
_goto_main:
lis r6, BOOTINFO2_ADDR@ha
addi r6, r6, BOOTINFO2_ADDR@l
@@ -105,31 +98,22 @@ _end_of_parseargs:
beq _check_pad3
andi. r3, r3, 0x7fff
cmplwi r3, 1
- bne _skip_crc
+ bne _goto_skip_init_bba
_check_pad3:
bl __check_pad3
-_skip_crc:
+_goto_skip_init_bba:
bl __init_user
mr r3, r14
mr r4, r15
bl main
b exit
+ // clang-format on
}
-#else
-__declspec(weak) void __start() {
- int ret;
- int argc = 0;
- char **argv = NULL;
- __init_user();
- ret = main(argc, argv);
- exit(ret);
-}
-#endif
-asm static void __init_registers(void)
-{
+asm static void __init_registers(void) {
+ // clang-format off
nofralloc
lis r1, _stack_addr@h
ori r1, r1, _stack_addr@l
@@ -138,51 +122,40 @@ asm static void __init_registers(void)
lis r13, _SDA_BASE_@h
ori r13, r13, _SDA_BASE_@l
blr
+ // clang-format on
}
-__declspec(section ".init") extern __rom_copy_info _rom_copy_info[];
-__declspec(section ".init") extern __bss_init_info _bss_init_info[];
-
-// clang-format on
-
-inline static void __copy_rom_section(void* dst, const void* src,
- unsigned long size)
-{
- if (size && (dst != src)) {
- memcpy(dst, src, size);
- __flush_cache(dst, size);
- }
+inline static void __copy_rom_section(void* dst, const void* src, unsigned long size) {
+ if (size && (dst != src)) {
+ memcpy(dst, src, size);
+ __flush_cache(dst, size);
+ }
}
-inline static void __init_bss_section(void* dst, unsigned long size)
-{
- if (size) {
- memset(dst, 0, size);
- }
+inline static void __init_bss_section(void* dst, unsigned long size) {
+ if (size) {
+ memset(dst, 0, size);
+ }
}
#pragma scheduling off
-#pragma peephole off
-// peephole might have been turned off due to the inline asm peephole bug
-// which turns off peephole optimizations after an inline-asm function
-void __init_data(void)
-{
- __rom_copy_info* dci;
- __bss_init_info* bii;
-
- dci = _rom_copy_info;
- while (TRUE) {
- if (dci->size == 0)
- break;
- __copy_rom_section(dci->addr, dci->rom, dci->size);
- dci++;
- }
-
- bii = _bss_init_info;
- while (TRUE) {
- if (bii->size == 0)
- break;
- __init_bss_section(bii->addr, bii->size);
- bii++;
- }
+void __init_data(void) {
+ __rom_copy_info* dci;
+ __bss_init_info* bii;
+
+ dci = _rom_copy_info;
+ while (TRUE) {
+ if (dci->size == 0)
+ break;
+ __copy_rom_section(dci->addr, dci->rom, dci->size);
+ dci++;
+ }
+
+ bii = _bss_init_info;
+ while (TRUE) {
+ if (bii->size == 0)
+ break;
+ __init_bss_section(bii->addr, bii->size);
+ bii++;
+ }
}