summaryrefslogtreecommitdiff
path: root/src/Dolphin
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
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')
-rw-r--r--src/Dolphin/GBA/GBA.c109
-rw-r--r--src/Dolphin/GBA/GBAGetProcessStatus.c25
-rw-r--r--src/Dolphin/GBA/GBARead.c42
-rw-r--r--src/Dolphin/GBA/GBAWrite.c42
-rw-r--r--src/Dolphin/GBA/GBAXfer.c163
-rw-r--r--src/Dolphin/PPCArch.c563
-rw-r--r--src/Dolphin/ai.c378
-rw-r--r--src/Dolphin/ar/ar.c518
-rw-r--r--src/Dolphin/ar/arq.c170
-rw-r--r--src/Dolphin/card/CARDBios.c775
-rw-r--r--src/Dolphin/card/CARDBlock.c159
-rw-r--r--src/Dolphin/card/CARDCheck.c688
-rw-r--r--src/Dolphin/card/CARDCreate.c115
-rw-r--r--src/Dolphin/card/CARDDelete.c97
-rw-r--r--src/Dolphin/card/CARDDir.c92
-rw-r--r--src/Dolphin/card/CARDFormat.c127
-rw-r--r--src/Dolphin/card/CARDMount.c354
-rw-r--r--src/Dolphin/card/CARDNet.c33
-rw-r--r--src/Dolphin/card/CARDOpen.c123
-rw-r--r--src/Dolphin/card/CARDRdwr.c104
-rw-r--r--src/Dolphin/card/CARDRead.c141
-rw-r--r--src/Dolphin/card/CARDRename.c70
-rw-r--r--src/Dolphin/card/CARDStat.c144
-rw-r--r--src/Dolphin/card/CARDUnlock.c396
-rw-r--r--src/Dolphin/card/CARDWrite.c117
-rw-r--r--src/Dolphin/db.c43
-rw-r--r--src/Dolphin/dsp/dsp.c145
-rw-r--r--src/Dolphin/dsp/dsp_debug.c5
-rw-r--r--src/Dolphin/dsp/dsp_task.c389
-rw-r--r--src/Dolphin/dtk.c357
-rw-r--r--src/Dolphin/dvd/dvd.c1389
-rw-r--r--src/Dolphin/dvd/dvderror.c56
-rw-r--r--src/Dolphin/dvd/dvdfatal.c136
-rw-r--r--src/Dolphin/dvd/dvdfs.c656
-rw-r--r--src/Dolphin/dvd/dvdidutils.c27
-rw-r--r--src/Dolphin/dvd/dvdlow.c109
-rw-r--r--src/Dolphin/dvd/dvdqueue.c142
-rw-r--r--src/Dolphin/dvd/fstload.c67
-rw-r--r--src/Dolphin/exi/EXIBios.c692
-rw-r--r--src/Dolphin/exi/EXIUart.c174
-rw-r--r--src/Dolphin/gx/GXLight.c271
-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
-rw-r--r--src/Dolphin/pad/PadClamp.c166
-rw-r--r--src/Dolphin/pad/pad.c838
-rw-r--r--src/Dolphin/si/SIBios.c772
-rw-r--r--src/Dolphin/si/SISamplingRate.c54
68 files changed, 19112 insertions, 133 deletions
diff --git a/src/Dolphin/GBA/GBA.c b/src/Dolphin/GBA/GBA.c
new file mode 100644
index 0000000..bb89f2e
--- /dev/null
+++ b/src/Dolphin/GBA/GBA.c
@@ -0,0 +1,109 @@
+#include "dolphin/GBAPriv.h"
+
+static GBASecParams SecParams[4];
+GBA __GBA[4];
+BOOL __GBAReset = FALSE;
+
+static BOOL OnReset(BOOL);
+
+static OSResetFunctionInfo ResetFunctionInfo = {
+ OnReset,
+ 127
+};
+
+void ShortCommandProc(s32 chan) {
+ GBA* gba;
+ gba = &__GBA[chan];
+
+ if (gba->result != 0) {
+ return;
+ }
+
+ if (gba->dst[0] != 0 || gba->dst[1] != 4) {
+ gba->result = 1;
+ return;
+ }
+
+ gba->status[0] = gba->dst[2] & GBA_JSTAT_MASK;
+}
+
+void GBAInit() {
+ s32 i;
+ GBA* gba;
+ for (i = 0; i < 4; ++i) {
+ gba = &__GBA[i];
+ gba->delay = OSMicrosecondsToTicks(60);
+ OSInitThreadQueue(&gba->thread_queue);
+ gba->param = &SecParams[i];
+
+ // ASSERTMSG((u32) gba->param % 32 == 0)
+ }
+
+ OSInitAlarm();
+ DSPInit();
+ __GBAReset = FALSE;
+
+ OSRegisterResetFunction(&ResetFunctionInfo);
+}
+
+s32 GBAGetStatusAsync(s32 chan, u8* status, GBACallback callback) {
+ GBA* gba;
+ s32 ret;
+ gba = &__GBA[chan];
+ if (gba->callback != NULL) {
+ ret = GBA_BUSY;
+ } else {
+ gba->command = 0;
+ gba->status = status;
+ gba->callback = callback;
+ ret = __GBATransfer(chan, 1, 3, ShortCommandProc);
+ }
+
+ return ret;
+}
+
+
+s32 GBAGetStatus(s32 chan, u8* status) {
+ s32 ret;
+ ret = GBAGetStatusAsync(chan, status, __GBASyncCallback);
+
+ if (ret != GBA_READY) {
+ return ret;
+ }
+
+ return __GBASync(chan);
+}
+
+
+s32 GBAResetAsync(s32 chan, u8* status, GBACallback callback) {
+ GBA* gba;
+ s32 ret;
+ gba = &__GBA[chan];
+ if (gba->callback != NULL) {
+ ret = GBA_BUSY;
+ } else {
+ gba->command = 0xFF;
+ gba->status = status;
+ gba->callback = callback;
+ ret = __GBATransfer(chan, 1, 3, ShortCommandProc);
+ }
+
+ return ret;
+}
+
+
+s32 GBAReset(s32 chan, u8* status) {
+ s32 ret;
+
+ ret = GBAResetAsync(chan, status, __GBASyncCallback);
+ if (ret != GBA_READY) {
+ return ret;
+ }
+
+ return __GBASync(chan);
+}
+
+BOOL OnReset(BOOL) {
+ __GBAReset = TRUE;
+ return TRUE;
+}
diff --git a/src/Dolphin/GBA/GBAGetProcessStatus.c b/src/Dolphin/GBA/GBAGetProcessStatus.c
new file mode 100644
index 0000000..38d8c38
--- /dev/null
+++ b/src/Dolphin/GBA/GBAGetProcessStatus.c
@@ -0,0 +1,25 @@
+#include "dolphin/GBAPriv.h"
+
+s32 GBAGetProcessStatus(s32 chan, u8* percentp) {
+ GBA* gba;
+ s32 ret;
+ BOOL enabled;
+
+ gba = &__GBA[chan];
+ enabled = OSDisableInterrupts();
+
+ if (gba->jboot_callback == NULL) {
+ if (gba->callback == NULL) {
+ ret = 0;
+ } else {
+ ret = 2;
+ }
+ } else {
+ ret = 2;
+
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return ret;
+}
diff --git a/src/Dolphin/GBA/GBARead.c b/src/Dolphin/GBA/GBARead.c
new file mode 100644
index 0000000..ab19bbb
--- /dev/null
+++ b/src/Dolphin/GBA/GBARead.c
@@ -0,0 +1,42 @@
+#include "dolphin/GBAPriv.h"
+
+void ReadProc(s32 chan) {
+ GBA* gba;
+ gba = &__GBA[chan];
+
+ if (gba->result == 0) {
+ memcpy(gba->buffer, &gba->dst, 4);
+ gba->status[0] = gba->_09 & GBA_JSTAT_MASK;
+ }
+}
+
+s32 GBAReadAsync(s32 chan, u8* dst, u8* status, GBACallback callback) {
+ GBA* gba;
+ s32 ret;
+
+ gba = &__GBA[chan];
+
+ if (gba->callback != NULL) {
+ ret = 2;
+ } else {
+ gba->command = 0x14;
+ gba->buffer = dst;
+ gba->status = status;
+ gba->callback = callback;
+ ret = __GBATransfer(chan, 1, 5, ReadProc);
+ }
+
+ return ret;
+}
+
+
+s32 GBARead(s32 chan, u8* dst, u8* status) {
+ s32 tmp;
+ s32 ret;
+ ret = GBAReadAsync(chan, dst, status, __GBASyncCallback);
+ if (ret != GBA_READY) {
+ return ret;
+ }
+
+ return __GBASync(chan);
+}
diff --git a/src/Dolphin/GBA/GBAWrite.c b/src/Dolphin/GBA/GBAWrite.c
new file mode 100644
index 0000000..f040f92
--- /dev/null
+++ b/src/Dolphin/GBA/GBAWrite.c
@@ -0,0 +1,42 @@
+#include "dolphin/GBAPriv.h"
+
+void WriteProc(s32 chan) {
+ GBA* gba;
+ gba = &__GBA[chan];
+
+ if (gba->result != 0) {
+ return;
+ }
+
+ gba->status[0] = gba->dst[0] & GBA_JSTAT_MASK;
+}
+
+s32 GBAWriteAsync(s32 chan, u8* src, u8* status, GBACallback callback) {
+ GBA* gba;
+ s32 ret;
+ gba = &__GBA[chan];
+
+ if (gba->callback != NULL) {
+ ret = GBA_BUSY;
+ } else {
+ gba->command = 0x15;
+ memcpy(gba->src, src, 4);
+ gba->buffer = src;
+ gba->status = status;
+ gba->callback = callback;
+ ret = __GBATransfer(chan, 5, 1, WriteProc);
+ }
+
+ return ret;
+}
+
+
+s32 GBAWrite(s32 chan, u8* src, u8* status) {
+ s32 ret;
+ s32 tmp;
+ ret = GBAWriteAsync(chan, src, status, __GBASyncCallback);
+ if (ret != GBA_READY) {
+ return ret;
+ }
+ return __GBASync(chan);
+}
diff --git a/src/Dolphin/GBA/GBAXfer.c b/src/Dolphin/GBA/GBAXfer.c
new file mode 100644
index 0000000..c88ffd1
--- /dev/null
+++ b/src/Dolphin/GBA/GBAXfer.c
@@ -0,0 +1,163 @@
+#include "dolphin/GBAPriv.h"
+#include "dolphin/sipriv.h"
+
+void __GBAHandler(s32 chan, u32 sr, OSContext* context) {
+ int tmp;
+ GBA* gba;
+ OSContext tmpCtx;
+ GBACallback callback;
+ GBATransferCallback xferCallback;
+ gba = &__GBA[chan];
+ if (__GBAReset != 0) {
+ return;
+ }
+
+ if ((sr & 0xf)) {
+ gba->result = 1;
+ } else {
+ gba->result = 0;
+ }
+
+ if (gba->_38 != NULL) {
+ xferCallback = gba->_38;
+ gba->_38 = NULL;
+ xferCallback(chan);
+ }
+
+ if (gba->callback == NULL) {
+ return;
+ }
+
+ OSClearContext(&tmpCtx);
+ OSSetCurrentContext(&tmpCtx);
+ callback = gba->callback;
+ gba->callback = NULL;
+ callback(chan, gba->result);
+ OSClearContext(&tmpCtx);
+ OSSetCurrentContext(context);
+}
+
+void __GBASyncCallback(s32 chan, s32 ret) { OSWakeupThread(&__GBA[chan].thread_queue); }
+
+#ifdef FULL_FRANK
+/* This actually does match, but has an epilogue swap */
+s32 __GBASync(s32 chan) {
+ GBA* gba;
+ s32 enabled;
+ s32 ret;
+ gba = &__GBA[chan];
+
+ enabled = OSDisableInterrupts();
+ while (gba->callback != NULL) {
+ OSSleepThread(&gba->thread_queue);
+ }
+
+ ret = gba->result;
+ OSRestoreInterrupts(enabled);
+
+ return ret;
+}
+#else
+extern void OSSleepThread();
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 __GBASync(s32 chan) {
+ nofralloc
+ mflr r0
+ lis r4, __GBA@ha
+ stw r0, 4(r1)
+ slwi r3, r3, 8
+ addi r0, r4, __GBA@l
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ add r31, r0, r3
+ stw r30, 0x10(r1)
+ bl OSDisableInterrupts
+ mr r30, r3
+ b lbl_803CAD50
+lbl_803CAD48:
+ addi r3, r31, 0x24
+ bl OSSleepThread
+lbl_803CAD50:
+ lwz r0, 0x1c(r31)
+ cmplwi r0, 0
+ bne lbl_803CAD48
+ lwz r31, 0x20(r31)
+ mr r3, r30
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+void TypeAndStatusCallback(s32 chan, u32 type) {
+ s32 tmp;
+ GBA* gba;
+ OSContext* context;
+ GBACallback callback;
+ GBATransferCallback xferCallback;
+ OSContext tmpContext;
+ gba = &__GBA[chan];
+ if (__GBAReset != 0) {
+ return;
+ }
+
+ if ((type & 0xFF) != 0 || (type & 0xffff0000) != 0x40000) {
+ gba->result = GBA_NOT_READY;
+ } else {
+ if (SITransfer(chan, &gba->command, gba->_0c, gba->dst, gba->_10, __GBAHandler, gba->delay)) {
+ return;
+ }
+ gba->result = GBA_BUSY;
+ }
+
+ if (gba->_38 != NULL) {
+ xferCallback = gba->_38;
+ gba->_38 = NULL;
+ xferCallback(chan);
+ }
+
+ if (gba->callback != NULL) {
+ context = OSGetCurrentContext();
+ OSClearContext(&tmpContext);
+ OSSetCurrentContext(&tmpContext);
+ callback = gba->callback;
+ gba->callback = NULL;
+ callback(chan, gba->result);
+ OSClearContext(&tmpContext);
+ OSSetCurrentContext(context);
+ __OSReschedule();
+ }
+}
+
+s32 __GBATransfer(s32 chan, s32 w1, s32 w2, GBATransferCallback callback) {
+ s32 enabled;
+ GBA* gba;
+ gba = &__GBA[chan];
+ enabled = OSDisableInterrupts();
+ gba->_38 = callback;
+ gba->_0c = w1;
+ gba->_10 = w2;
+ SIGetTypeAsync(chan, TypeAndStatusCallback);
+ OSRestoreInterrupts(enabled);
+
+ return GBA_READY;
+}
+
+OSTime __GBASetDelay(s32 chan, OSTime delay) {
+ OSTime oldDelay;
+ GBA* gba;
+ gba = &__GBA[chan];
+ oldDelay = gba->delay;
+ gba->delay = delay;
+ return oldDelay;
+}
diff --git a/src/Dolphin/PPCArch.c b/src/Dolphin/PPCArch.c
new file mode 100644
index 0000000..173c685
--- /dev/null
+++ b/src/Dolphin/PPCArch.c
@@ -0,0 +1,563 @@
+#include "types.h"
+#include "asm_types.h"
+// clang-format off
+
+union FpscrUnion
+{
+ f64 f;
+ struct
+ {
+ u32 fpscr_pad;
+ u32 fpscr;
+ } u;
+};
+
+#define HID0_SPD 0x00000200 // Speculative cache access enable (0 enable)
+
+void PPCMthid0 ( u32 newHID0 );
+
+/*
+ * --INFO--
+ * Address: 8036F7D4
+ * Size: 000008
+ */
+asm u32 PPCMfmsr (void)
+{
+ nofralloc
+ mfmsr r3
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F7DC
+ * Size: 000008
+ */
+asm void PPCMtmsr (register u32 newMSR)
+{
+ nofralloc
+ mtmsr newMSR
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 00000C
+ */
+void PPCOrMsr(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 00000C
+ */
+void PPCAndMsr(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 00000C
+ */
+void PPCAndCMsr(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: 8036F7E4
+ * Size: 000008
+ */
+asm u32 PPCMfhid0 (void)
+{
+ nofralloc
+ mfspr r3, HID0
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F7EC
+ * Size: 000008
+ */
+asm void PPCMthid0 (register u32 newHID0)
+{
+ nofralloc
+ mtspr HID0, newHID0
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+void PPCMfhid1(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: 8036F7F4
+ * Size: 000008
+ */
+asm u32 PPCMfl2cr (void)
+{
+ nofralloc
+ mfspr r3, L2CR
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F7FC
+ * Size: 000008
+ */
+asm void PPCMtl2cr (register u32 newL2cr)
+{
+ nofralloc
+ mtspr L2CR, newL2cr
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F804
+ * Size: 000008
+ */
+__declspec ( weak ) asm void PPCMtdec ( register u32 newDec )
+{
+ nofralloc
+ mtdec newDec
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+void PPCMfdec(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: 8036F80C
+ * Size: 000008
+ */
+asm void PPCSync (void)
+{
+ nofralloc
+ sc
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000034
+ */
+asm void PPCEieio(void) {
+ nofralloc
+ mfmsr r5
+ rlwinm r6, r5, 0, 0x11, 0xf
+ mtmsr r6
+ mfspr r3, hid0
+ ori r4, r3, 8
+ mtspr hid0, r4
+ isync
+ eieio
+ isync
+
+ mtspr hid0, r3
+ mtmsr r5
+ isync
+
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F814
+ * Size: 000014
+ */
+__declspec ( weak ) asm void PPCHalt (void) //spins infinitely
+{
+ nofralloc
+
+ sync
+
+_spin:
+ nop
+ li r3, 0
+ nop
+ b _spin
+
+ // NEVER REACHED
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfmmcr0(void)
+{
+ nofralloc
+ mfspr r3, MMCR0
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ * UNUSED
+ */
+asm void PPCMtmmcr0 (register u32 newMmcr0)
+{
+ nofralloc
+ mtspr MMCR0, newMmcr0
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfmmcr1(void)
+{
+ nofralloc
+ mfspr r3, MMCR1
+ blr}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ * UNUSED
+ */
+asm void PPCMtmmcr1 (register u32 newMmcr1)
+{
+ nofralloc
+ mtspr MMCR1, newMmcr1
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfpmc1(void)
+{
+ nofralloc
+ mfspr r3, PMC1
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ * UNUSED
+ */
+asm void PPCMtpmc1 (register u32 newPmc1)
+{
+ nofralloc
+ mtspr PMC1, newPmc1
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfpmc2(void)
+{
+ nofralloc
+ mfspr r3, PMC2
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ * UNUSED
+ */
+asm void PPCMtpmc2 (register u32 newPmc2)
+{
+ nofralloc
+ mtspr PMC2, newPmc2
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfpmc3(void)
+{
+ nofralloc
+ mfspr r3, PMC2
+ blr}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ * UNUSED
+ */
+asm void PPCMtpmc3 (register u32 newPmc3)
+{
+ nofralloc
+ mtspr PMC3, newPmc3
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfpmc4(void)
+{
+ nofralloc
+ mfspr r3, PMC4
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ * UNUSED
+ */
+asm void PPCMtpmc4 (register u32 newPmc4)
+{
+ nofralloc
+ mtspr PMC4, newPmc4
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfsia(void)
+{
+ nofralloc
+ mfspr r3, SIA
+ blr}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMtsia(register u32 newSia)
+{
+ nofralloc
+ mtspr SIA, newSia
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F828
+ * Size: 000020
+ */
+u32 PPCMffpscr(void)
+{
+ union FpscrUnion m;
+
+
+ asm
+ {
+ mffs fp31
+ stfd fp31, m.f;
+ }
+
+ return m.u.fpscr;
+}
+
+/*
+ * --INFO--
+ * Address: 8036F848
+ * Size: 000028
+ */
+void PPCMtfpscr(register u32 newFPSCR)
+{
+ union FpscrUnion m;
+
+ asm
+ {
+ li r4, 0
+ stw r4, m.u.fpscr_pad;
+ stw newFPSCR, m.u.fpscr
+ lfd fp31, m.f
+ mtfsf 0xff, fp31
+ }
+}
+
+/*
+ * --INFO--
+ * Address: 8036F870
+ * Size: 000008
+ */
+asm u32 PPCMfhid2 ( void )
+{
+ nofralloc
+ mfspr r3, HID2
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F878
+ * Size: 000008
+ */
+asm void PPCMthid2 ( register u32 newhid2 )
+{
+ nofralloc
+ mtspr HID2, newhid2
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F880
+ * Size: 00000C
+ */
+asm u32 PPCMfwpar(void)
+{
+ nofralloc
+ sync
+ mfspr r3, WPAR
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: 8036F88C
+ * Size: 000008
+ */
+asm void PPCMtwpar ( register u32 newwpar )
+{
+ nofralloc
+ mtspr WPAR, newwpar
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfdmaU(void)
+{
+ nofralloc
+ mfspr r3, DMA_U
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+asm void PPCMfdmaL(void)
+{
+ nofralloc
+ mfspr r3, DMA_L
+ blr
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+void PPCMtdmaU(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+void PPCMtdmaL(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000008
+ */
+void PPCMfpvr(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: ........
+ * Size: 000028
+ */
+void PPCEnableSpeculation(void)
+{
+ // UNUSED FUNCTION
+}
+
+/*
+ * --INFO--
+ * Address: 8036F894
+ * Size: 000028
+ */
+void PPCDisableSpeculation (void)
+{
+ PPCMthid0(PPCMfhid0() | HID0_SPD);
+}
+
+/*
+ * --INFO--
+ * Address: 8036F8BC
+ * Size: 000008
+ */
+asm void PPCSetFpIEEEMode(void)
+{
+ nofralloc
+ mtfsb0 4*7+1
+ blr
+}
+/*
+ * --INFO--
+ * Address: 8036F8C4
+ * Size: 000008
+ */
+asm void PPCSetFpNonIEEEMode (void)
+{
+ nofralloc
+ mtfsb1 4*7+1
+ blr
+}
+// clang-format on
diff --git a/src/Dolphin/ai.c b/src/Dolphin/ai.c
new file mode 100644
index 0000000..fb39c78
--- /dev/null
+++ b/src/Dolphin/ai.c
@@ -0,0 +1,378 @@
+#include "dolphin/ai.h"
+#include "dolphin/dsp_regs.h"
+#include "dolphin/os.h"
+
+const char* __AIVersion = "<< Dolphin SDK - AI\trelease build: Sep 5 2002 05:34:25 (0x2301) >>";
+
+static AISCallback __AIS_Callback = NULL;
+static AIDCallback __AID_Callback = NULL;
+static u8* __CallbackStack;
+static u8* __OldStack;
+static volatile s32 __AI_init_flag = FALSE;
+static volatile s32 __AID_Active = FALSE;
+
+static OSTime bound_32KHz;
+static OSTime bound_48KHz;
+static OSTime min_wait;
+static OSTime max_wait;
+static OSTime buffer;
+
+void __AISHandler(s16 interrupt, OSContext* context);
+void __AIDHandler(s16 interrupt, OSContext* context);
+void __AICallbackStackSwitch(register AIDCallback cb);
+void __AI_SRC_INIT(void);
+
+#ifdef FULL_FRANK
+AIDCallback AIRegisterDMACallback(AIDCallback callback) {
+ s32 oldInts;
+ AIDCallback ret;
+
+ ret = __AID_Callback;
+ oldInts = OSDisableInterrupts();
+ __AID_Callback = callback;
+ OSRestoreInterrupts(oldInts);
+ return ret;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm AIDCallback AIRegisterDMACallback(AIDCallback callback) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ mr r30, r3
+ lwz r31, __AID_Callback
+ bl OSDisableInterrupts
+ stw r30, __AID_Callback
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+void AIInitDMA(u32 addr, u32 length) {
+ s32 oldInts;
+ oldInts = OSDisableInterrupts();
+ __DSPRegs[24] = (u16)((__DSPRegs[24] & ~0x3FF) | (addr >> 16));
+ __DSPRegs[25] = (u16)((__DSPRegs[25] & ~0xFFE0) | (0xffff & addr));
+ __DSPRegs[27] = (u16)((__DSPRegs[27] & ~0x7FFF) | (u16)((length >> 5) & 0xFFFF));
+ OSRestoreInterrupts(oldInts);
+}
+
+void AIStartDMA() { __DSPRegs[27] |= 0x8000; }
+
+void AIStopDMA(void) { __DSPRegs[27] &= ~0x8000; }
+
+u32 AIGetDMAStartAddr(void) {
+ return (u32)((__DSPRegs[24] & 0x03ff) << 16) | (__DSPRegs[25] & 0xffe0);
+}
+
+#ifdef FULL_FRANK
+AISCallback AIRegisterStreamCallback(AISCallback callback) {
+ AISCallback ret;
+ s32 oldInts;
+
+ ret = __AIS_Callback;
+ oldInts = OSDisableInterrupts();
+ __AIS_Callback = callback;
+ OSRestoreInterrupts(oldInts);
+ return ret;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm AISCallback AIRegisterStreamCallback(AISCallback callback) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ mr r30, r3
+ lwz r31, __AIS_Callback
+ bl OSDisableInterrupts
+ stw r30, __AIS_Callback
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+void AIResetStreamSampleCount(void) { __AIRegs[0] = (__AIRegs[0] & ~0x20) | 0x20; }
+
+void AISetStreamTrigger(u32 trigger) { __AIRegs[3] = trigger; }
+
+void AISetStreamPlayState(u32 state) {
+ s32 oldInts;
+ u8 volRight;
+ u8 volLeft;
+
+ if (state == AIGetStreamPlayState()) {
+ return;
+ }
+ if ((AIGetStreamSampleRate() == 0U) && (state == 1)) {
+ volRight = AIGetStreamVolRight();
+ volLeft = AIGetStreamVolLeft();
+ AISetStreamVolRight(0);
+ AISetStreamVolLeft(0);
+ oldInts = OSDisableInterrupts();
+ __AI_SRC_INIT();
+ __AIRegs[0] = (__AIRegs[0] & ~0x20) | 0x20;
+ __AIRegs[0] = (__AIRegs[0] & ~1) | 1;
+ OSRestoreInterrupts(oldInts);
+ AISetStreamVolLeft(volRight);
+ AISetStreamVolRight(volLeft);
+ } else {
+ __AIRegs[0] = (__AIRegs[0] & ~1) | state;
+ }
+}
+
+u32 AIGetStreamPlayState() { return __AIRegs[0] & 1; }
+
+void AISetDSPSampleRate(u32 rate) {
+ u32 state;
+ s32 oldInts;
+ u8 left;
+ u8 right;
+ u32 sampleRate;
+
+ if (rate == AIGetDSPSampleRate()) {
+ return;
+ }
+
+ __AIRegs[0] &= ~0x40;
+ if (rate == 0) {
+ left = AIGetStreamVolLeft();
+ right = AIGetStreamVolRight();
+ state = AIGetStreamPlayState();
+ sampleRate = AIGetStreamSampleRate();
+ AISetStreamVolLeft(0);
+ AISetStreamVolRight(0);
+ oldInts = OSDisableInterrupts();
+ __AI_SRC_INIT();
+ __AIRegs[0] = (__AIRegs[0] & ~0x20) | 0x20;
+ __AIRegs[0] = (__AIRegs[0] & ~2) | (sampleRate * 2);
+ __AIRegs[0] = (__AIRegs[0] & ~1) | state;
+ __AIRegs[0] |= 0x40;
+ OSRestoreInterrupts(oldInts);
+ AISetStreamVolLeft(left);
+ AISetStreamVolRight(right);
+ }
+}
+
+u32 AIGetDSPSampleRate() { return ((__AIRegs[0] >> 6) & 1) ^ 1; }
+
+void __AI_set_stream_sample_rate(u32 rate) {
+ s32 oldInts;
+ s32 state;
+ u8 left;
+ u8 right;
+ s32 temp_r26;
+
+ if (rate == AIGetStreamSampleRate()) {
+ return;
+ }
+ state = AIGetStreamPlayState();
+ left = AIGetStreamVolLeft();
+ right = AIGetStreamVolRight();
+ AISetStreamVolRight(0);
+ AISetStreamVolLeft(0);
+ temp_r26 = __AIRegs[0] & 0x40;
+ __AIRegs[0] &= ~0x40;
+ oldInts = OSDisableInterrupts();
+ __AI_SRC_INIT();
+ __AIRegs[0] |= temp_r26;
+ __AIRegs[0] = (__AIRegs[0] & ~0x20) | 0x20;
+ __AIRegs[0] = (__AIRegs[0] & ~2) | (rate * 2);
+ OSRestoreInterrupts(oldInts);
+ AISetStreamPlayState(state);
+ AISetStreamVolLeft(left);
+ AISetStreamVolRight(right);
+}
+
+u32 AIGetStreamSampleRate() { return (__AIRegs[0] >> 1) & 1; }
+
+void AISetStreamVolLeft(u8 volume) { __AIRegs[1] = (__AIRegs[1] & ~0xFF) | (volume & 0xFF); }
+
+u8 AIGetStreamVolLeft() { return __AIRegs[1]; }
+
+void AISetStreamVolRight(u8 volume) {
+ __AIRegs[1] = (__AIRegs[1] & ~0xFF00) | ((volume & 0xFF) << 8);
+}
+
+u8 AIGetStreamVolRight() { return __AIRegs[1] >> 8; }
+
+void AIInit(u8* stack) {
+ if (__AI_init_flag == TRUE) {
+ return;
+ }
+
+ OSRegisterVersion(__AIVersion);
+ bound_32KHz = OSNanosecondsToTicks(31524);
+ bound_48KHz = OSNanosecondsToTicks(42024);
+ min_wait = OSNanosecondsToTicks(42000);
+ max_wait = OSNanosecondsToTicks(63000);
+ buffer = OSNanosecondsToTicks(3000);
+
+ AISetStreamVolRight(0);
+ AISetStreamVolLeft(0);
+ AISetStreamTrigger(0);
+ AIResetStreamSampleCount();
+ __AI_set_stream_sample_rate(1);
+ AISetDSPSampleRate(0);
+ __AIS_Callback = 0;
+ __AID_Callback = 0;
+ __CallbackStack = stack;
+ __OSSetInterruptHandler(5, __AIDHandler);
+ __OSUnmaskInterrupts(0x04000000);
+ __OSSetInterruptHandler(8, __AISHandler);
+ __OSUnmaskInterrupts(0x800000);
+ __AI_init_flag = TRUE;
+}
+
+void __AISHandler(s16 interrupt, OSContext* context) {
+ OSContext tmpContext;
+ __AIRegs[0] |= 8;
+ OSClearContext(&tmpContext);
+ OSSetCurrentContext(&tmpContext);
+ if (__AIS_Callback != NULL) {
+ __AIS_Callback(__AIRegs[2]);
+ }
+ OSClearContext(&tmpContext);
+ OSSetCurrentContext(context);
+}
+
+void __AIDHandler(s16 interrupt, OSContext* context) {
+ OSContext tempContext;
+ u32 temp = __DSPRegs[5];
+ __DSPRegs[5] = (temp & ~0xA0) | 8;
+ OSClearContext(&tempContext);
+ OSSetCurrentContext(&tempContext);
+ if (__AID_Callback && !__AID_Active) {
+ __AID_Active = TRUE;
+ if (__CallbackStack) {
+ __AICallbackStackSwitch(__AID_Callback);
+ } else {
+ __AID_Callback();
+ }
+
+ __AID_Active = FALSE;
+ }
+
+ OSClearContext(&tempContext);
+ OSSetCurrentContext(context);
+}
+
+// clang-format off
+asm void __AICallbackStackSwitch(register AIDCallback cb) {
+ // Allocate stack frame
+ fralloc
+
+ // Store current stack
+ lis r5, __OldStack@ha
+ addi r5, r5, __OldStack@l
+ stw r1, 0(r5)
+
+ // Load stack for callback
+ lis r5, __CallbackStack@ha
+ addi r5, r5, __CallbackStack@l
+ lwz r1,0(r5)
+
+ // Move stack down 8 bytes
+ subi r1, r1, 8
+ // Call callback
+ mtlr cb
+ blrl
+
+ // Restore old stack
+ lis r5, __OldStack @ha
+ addi r5, r5, __OldStack@l
+ lwz r1,0(r5)
+
+ // Free stack frame
+ frfree
+
+ blr
+}
+// clang-format on
+
+void __AI_SRC_INIT(void) {
+ OSTime rise32 = 0;
+ OSTime rise48 = 0;
+ OSTime diff = 0;
+ OSTime unused1 = 0;
+ OSTime temp = 0;
+ u32 temp0 = 0;
+ u32 temp1 = 0;
+ u32 done = 0;
+ u32 walking = 0;
+ u32 unused2 = 0;
+ u32 initCnt = 0;
+
+ walking = 0;
+ initCnt = 0;
+ temp = 0;
+
+ while (!done) {
+ __AIRegs[0] = (__AIRegs[0] & ~0x20) | 0x20;
+ __AIRegs[0] &= ~2;
+ __AIRegs[0] = (__AIRegs[0] & ~1) | 1;
+
+ temp0 = __AIRegs[2];
+
+ while (temp0 == __AIRegs[2])
+ ;
+ rise32 = OSGetTime();
+
+ __AIRegs[0] = (__AIRegs[0] & ~2) | 2;
+ __AIRegs[0] = (__AIRegs[0] & ~1) | 1;
+
+ temp1 = __AIRegs[2];
+ while (temp1 == __AIRegs[2])
+ ;
+
+ rise48 = OSGetTime();
+
+ diff = rise48 - rise32;
+ __AIRegs[0] &= ~2;
+ __AIRegs[0] &= ~1;
+
+ if (diff < (bound_32KHz - buffer)) {
+ temp = min_wait;
+ done = 1;
+ ++initCnt;
+ } else if (diff >= (bound_32KHz + buffer) && diff < (bound_48KHz - buffer)) {
+ temp = max_wait;
+ done = 1;
+ ++initCnt;
+ } else {
+ done = 0;
+ walking = 1;
+ ++initCnt;
+ }
+ }
+
+ while ((rise48 + temp) > OSGetTime())
+ ;
+}
diff --git a/src/Dolphin/ar/ar.c b/src/Dolphin/ar/ar.c
new file mode 100644
index 0000000..1d59b30
--- /dev/null
+++ b/src/Dolphin/ar/ar.c
@@ -0,0 +1,518 @@
+#include "dolphin/ar.h"
+
+#include "dolphin/dsp_regs.h"
+#include "dolphin/os.h"
+
+static const char* __ARVersion =
+ "<< Dolphin SDK - AR\trelease build: Sep 5 2002 05:34:27 (0x2301) >>";
+
+static ARCallback __AR_Callback;
+static u32 __AR_Size;
+static u32 __AR_InternalSize;
+static u32 __AR_ExpansionSize;
+
+static u32 __AR_StackPointer;
+static u32 __AR_FreeBlocks;
+static u32* __AR_BlockLength;
+
+static volatile BOOL __AR_init_flag = FALSE;
+
+static void __ARHandler(__OSInterrupt interrupt, OSContext* context);
+static void __ARChecksize(void);
+static void __ARClearArea(u32 start_addr, u32 length);
+
+#ifdef FULL_FRANK
+ARCallback ARRegisterDMACallback(ARCallback callback) {
+ ARCallback oldCb;
+ BOOL enabled;
+ oldCb = __AR_Callback;
+ enabled = OSDisableInterrupts();
+ __AR_Callback = callback;
+ OSRestoreInterrupts(enabled);
+ return oldCb;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm ARCallback ARRegisterDMACallback(ARCallback callback) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ mr r30, r3
+ lwz r31, __AR_Callback
+ bl OSDisableInterrupts
+ stw r30, __AR_Callback
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+
+#pragma pop
+/* clang-format on */
+#endif
+#ifdef FULL_FRANK
+u32 ARGetDMAStatus() {
+ BOOL enabled;
+ u32 val;
+ enabled = OSDisableInterrupts();
+ val = __DSPRegs[5] & 0x0200;
+ OSRestoreInterrupts(enabled);
+ return val;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm u32 ARGetDMAStatus() {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x10(r1)
+ stw r31, 0xc(r1)
+ bl OSDisableInterrupts
+ lis r4, __DSPRegs + (5 * 2)@ha
+ lhz r0, __DSPRegs + (5 * 2)@l(r4)
+ rlwinm r31, r0, 0, 0x16, 0x16
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x14(r1)
+ lwz r31, 0xc(r1)
+ addi r1, r1, 0x10
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+void ARStartDMA(u32 type, u32 mainmem_addr, u32 aram_addr, u32 length) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ __DSPRegs[16] = (u16)(__DSPRegs[16] & ~0x3ff) | (u16)(mainmem_addr >> 16);
+ __DSPRegs[17] = (u16)(__DSPRegs[17] & ~0xffe0) | (u16)(mainmem_addr & 0xffff);
+ __DSPRegs[18] = (u16)(__DSPRegs[18] & ~0x3ff) | (u16)(aram_addr >> 16);
+ __DSPRegs[19] = (u16)(__DSPRegs[19] & ~0xffe0) | (u16)(aram_addr & 0xffff);
+ __DSPRegs[20] = (u16)((__DSPRegs[20] & ~0x8000) | (type << 15));
+ __DSPRegs[20] = (u16)(__DSPRegs[20] & ~0x3ff) | (u16)(length >> 16);
+ __DSPRegs[21] = (u16)(__DSPRegs[21] & ~0xffe0) | (u16)(length & 0xffff);
+ OSRestoreInterrupts(enabled);
+}
+
+#ifdef FULL_FRANK
+u32 ARAlloc(u32 length) {
+ u32 tmp;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ tmp = __AR_StackPointer;
+ __AR_StackPointer += length;
+ *__AR_BlockLength = length;
+ __AR_BlockLength++;
+ __AR_FreeBlocks--;
+ OSRestoreInterrupts(enabled);
+
+ return tmp;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+asm u32 ARAlloc(u32 length) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ mr r30, r3
+ bl OSDisableInterrupts
+ lwz r31, __AR_StackPointer
+ lwz r4, __AR_BlockLength
+ add r0, r31, r30
+ stw r0, __AR_StackPointer
+ stw r30, 0(r4)
+ lwz r5, __AR_BlockLength
+ lwz r4, __AR_FreeBlocks
+ addi r5, r5, 4
+ addi r0, r4, -1
+ stw r5, __AR_BlockLength
+ stw r0, __AR_FreeBlocks
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+#if NONMATCHING
+u32 ARFree(u32* length) {
+ BOOL old;
+
+ old = OSDisableInterrupts();
+
+ __AR_BlockLength--;
+
+ if (length) {
+ *length = *__AR_BlockLength;
+ }
+
+ __AR_StackPointer -= *__AR_BlockLength;
+
+ __AR_FreeBlocks++;
+
+ OSRestoreInterrupts(old);
+
+ return __AR_StackPointer;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm u32 ARFree(u32* length) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ mr r31, r3
+ bl OSDisableInterrupts
+ lwz r4, __AR_BlockLength
+ cmplwi r31, 0
+ addi r0, r4, -4
+ stw r0, __AR_BlockLength
+ beq lbl_8036DAB4
+ lwz r4, __AR_BlockLength
+ lwz r0, 0(r4)
+ stw r0, 0(r31)
+lbl_8036DAB4:
+ lwz r5, __AR_BlockLength
+ lwz r4, __AR_FreeBlocks
+ lwz r6, 0(r5)
+ addi r0, r4, 1
+ lwz r5, __AR_StackPointer
+ stw r0, __AR_FreeBlocks
+ subf r0, r6, r5
+ stw r0, __AR_StackPointer
+ bl OSRestoreInterrupts
+ lwz r3, __AR_StackPointer
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format on */
+#endif
+
+BOOL ARCheckInit() { return __AR_init_flag; }
+
+u32 ARInit(u32* stack_index_addr, u32 num_entries) {
+
+ BOOL old;
+ u16 refresh;
+
+ if (__AR_init_flag == TRUE) {
+ return 0x4000;
+ }
+
+ OSRegisterVersion(__ARVersion);
+
+ old = OSDisableInterrupts();
+
+ __AR_Callback = NULL;
+
+ __OSSetInterruptHandler(__OS_INTERRUPT_DSP_ARAM, __ARHandler);
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_DSP_ARAM);
+
+ __AR_StackPointer = 0x4000;
+ __AR_FreeBlocks = num_entries;
+ __AR_BlockLength = stack_index_addr;
+
+ refresh = (u16)(__DSPRegs[13] & 0x000000ff);
+
+ __DSPRegs[13] = (u16)((__DSPRegs[13] & ~0x000000ff) | (refresh & 0x000000ff));
+
+ __ARChecksize();
+
+ __AR_init_flag = TRUE;
+
+ OSRestoreInterrupts(old);
+
+ return __AR_StackPointer;
+}
+
+u32 ARGetBaseAddress(void) { return 0x4000; }
+
+u32 ARGetSize() { return __AR_Size; }
+
+static void __ARHandler(__OSInterrupt interrupt, OSContext* context) {
+
+ OSContext exceptionContext;
+ u16 tmp;
+
+ tmp = __DSPRegs[5];
+ tmp = (u16)((tmp & ~0x00000088) | 0x20);
+ __DSPRegs[5] = tmp;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ if (__AR_Callback) {
+ (*__AR_Callback)();
+ }
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+}
+
+#define RoundUP32(x) (((u32)(x) + 32 - 1) & ~(32 - 1))
+
+void __ARClearInterrupt(void) {
+
+ u16 tmp;
+ tmp = __DSPRegs[5];
+ tmp = (u16)((tmp & ~(0x00000080 | 0x00000008)) | 0x00000020);
+ __DSPRegs[5] = tmp;
+}
+u16 __ARGetInterruptStatus(void) { return ((u16)(__DSPRegs[5] & 0x0200)); }
+
+static void __ARWaitForDMA(void) {
+
+ while (__DSPRegs[5] & 0x0200) {
+ }
+}
+
+static void __ARWriteDMA(u32 mmem_addr, u32 aram_addr, u32 length) {
+
+ __DSPRegs[16] = (u16)((__DSPRegs[16] & ~0x03ff) | (u16)(mmem_addr >> 16));
+ __DSPRegs[16 + 1] = (u16)((__DSPRegs[16 + 1] & ~0xffe0) | (u16)(mmem_addr & 0xffff));
+
+ __DSPRegs[18] = (u16)((__DSPRegs[18] & ~0x03ff) | (u16)(aram_addr >> 16));
+ __DSPRegs[18 + 1] = (u16)((__DSPRegs[18 + 1] & ~0xffe0) | (u16)(aram_addr & 0xffff));
+
+ __DSPRegs[20] = (u16)(__DSPRegs[20] & ~0x8000);
+
+ __DSPRegs[20] = (u16)((__DSPRegs[20] & ~0x03ff) | (u16)(length >> 16));
+ __DSPRegs[20 + 1] = (u16)((__DSPRegs[20 + 1] & ~0xffe0) | (u16)(length & 0xffff));
+
+ __ARWaitForDMA();
+
+ __ARClearInterrupt();
+}
+
+static void __ARReadDMA(u32 mmem_addr, u32 aram_addr, u32 length) {
+
+ __DSPRegs[16] = (u16)((__DSPRegs[16] & ~0x03ff) | (u16)(mmem_addr >> 16));
+ __DSPRegs[16 + 1] = (u16)((__DSPRegs[16 + 1] & ~0xffe0) | (u16)(mmem_addr & 0xffff));
+
+ __DSPRegs[18] = (u16)((__DSPRegs[18] & ~0x03ff) | (u16)(aram_addr >> 16));
+ __DSPRegs[18 + 1] = (u16)((__DSPRegs[18 + 1] & ~0xffe0) | (u16)(aram_addr & 0xffff));
+
+ __DSPRegs[20] = (u16)(__DSPRegs[20] | 0x8000);
+
+ __DSPRegs[20] = (u16)((__DSPRegs[20] & ~0x03ff) | (u16)(length >> 16));
+ __DSPRegs[20 + 1] = (u16)((__DSPRegs[20 + 1] & ~0xffe0) | (u16)(length & 0xffff));
+
+ __ARWaitForDMA();
+
+ __ARClearInterrupt();
+}
+
+static void __ARChecksize(void) {
+
+ u8 test_data_pad[0x20 + 31];
+ u8 dummy_data_pad[0x20 + 31];
+ u8 buffer_pad[0x20 + 31];
+
+ u8 save_pad_1[0x20 + 31];
+ u8 save_pad_2[0x20 + 31];
+ u8 save_pad_3[0x20 + 31];
+ u8 save_pad_4[0x20 + 31];
+ u8 save_pad_5[0x20 + 31];
+
+ u32* test_data;
+ u32* dummy_data;
+ u32* buffer;
+ u32* save1;
+ u32* save2;
+ u32* save3;
+ u32* save4;
+ u32* save5;
+
+ u16 ARAM_mode = 0;
+ u32 ARAM_size = 0;
+
+ u32 i;
+
+ while (!(__DSPRegs[11] & 1))
+ ;
+
+ ARAM_mode = 3;
+ ARAM_size = __AR_InternalSize = 0x1000000;
+ __DSPRegs[9] = (u16)((__DSPRegs[9] & ~(0x00000007 | 0x00000038)) | 0x20 | 2 | 1);
+
+ test_data = (u32*)(RoundUP32((u32)(test_data_pad)));
+ dummy_data = (u32*)(RoundUP32((u32)(dummy_data_pad)));
+ buffer = (u32*)(RoundUP32((u32)(buffer_pad)));
+
+ save1 = (u32*)(RoundUP32((u32)(save_pad_1)));
+ save2 = (u32*)(RoundUP32((u32)(save_pad_2)));
+ save3 = (u32*)(RoundUP32((u32)(save_pad_3)));
+ save4 = (u32*)(RoundUP32((u32)(save_pad_4)));
+ save5 = (u32*)(RoundUP32((u32)(save_pad_5)));
+
+ for (i = 0; i < 8; i++) {
+ *(test_data + i) = 0xdeadbeef;
+ *(dummy_data + i) = 0xbad0bad0;
+ }
+
+ DCFlushRange((void*)test_data, 0x20);
+ DCFlushRange((void*)dummy_data, 0x20);
+
+ __AR_ExpansionSize = 0;
+
+ DCInvalidateRange((void*)save1, 0x20);
+ __ARReadDMA((u32)save1, ARAM_size + 0, 0x20);
+ PPCSync();
+
+ __ARWriteDMA((u32)test_data, ARAM_size + 0x0000000, 0x20);
+
+ memset((void*)buffer, 0, 0x20);
+ DCFlushRange((void*)buffer, 0x20);
+
+ __ARReadDMA((u32)buffer, ARAM_size + 0x0000000, 0x20);
+ PPCSync();
+
+ if (buffer[0] == test_data[0]) {
+
+ DCInvalidateRange((void*)save2, 0x20);
+ __ARReadDMA((u32)save2, ARAM_size + 0x0200000, 0x20);
+ PPCSync();
+
+ DCInvalidateRange((void*)save3, 0x20);
+ __ARReadDMA((u32)save3, ARAM_size + 0x1000000, 0x20);
+ PPCSync();
+
+ DCInvalidateRange((void*)save4, 0x20);
+ __ARReadDMA((u32)save4, ARAM_size + 0x0000200, 0x20);
+ PPCSync();
+
+ DCInvalidateRange((void*)save5, 0x20);
+ __ARReadDMA((u32)save5, ARAM_size + 0x0400000, 0x20);
+ PPCSync();
+
+ __ARWriteDMA((u32)dummy_data, ARAM_size + 0x0200000, 0x20);
+
+ __ARWriteDMA((u32)test_data, ARAM_size + 0x0000000, 0x20);
+
+ memset((void*)buffer, 0, 0x20);
+ DCFlushRange((void*)buffer, 0x20);
+
+ __ARReadDMA((u32)buffer, ARAM_size + 0x0200000, 0x20);
+ PPCSync();
+
+ if (buffer[0] == test_data[0]) {
+ __ARWriteDMA((u32)save1, ARAM_size + 0x0000000, 0x20);
+
+ ARAM_mode |= 0 << 1;
+ ARAM_size += 0x0200000;
+ __AR_ExpansionSize = 0x0200000;
+ } else {
+ __ARWriteDMA((u32)dummy_data, ARAM_size + 0x1000000, 0x20);
+
+ __ARWriteDMA((u32)test_data, ARAM_size + 0x0000000, 0x20);
+
+ memset((void*)buffer, 0, 0x20);
+ DCFlushRange((void*)buffer, 0x20);
+
+ __ARReadDMA((u32)buffer, ARAM_size + 0x1000000, 0x20);
+ PPCSync();
+
+ if (buffer[0] == test_data[0]) {
+ __ARWriteDMA((u32)save1, ARAM_size + 0x0000000, 0x20);
+ __ARWriteDMA((u32)save2, ARAM_size + 0x0200000, 0x20);
+
+ ARAM_mode |= 4 << 1;
+ ARAM_size += 0x0400000;
+ __AR_ExpansionSize = 0x0400000;
+ } else {
+ __ARWriteDMA((u32)dummy_data, ARAM_size + 0x0000200, 0x20);
+
+ __ARWriteDMA((u32)test_data, ARAM_size + 0x0000000, 0x20);
+
+ memset((void*)buffer, 0, 0x20);
+ DCFlushRange((void*)buffer, 0x20);
+
+ __ARReadDMA((u32)buffer, ARAM_size + 0x0000200, 0x20);
+ PPCSync();
+
+ if (buffer[0] == test_data[0]) {
+ __ARWriteDMA((u32)save1, ARAM_size + 0x0000000, 0x20);
+ __ARWriteDMA((u32)save2, ARAM_size + 0x0200000, 0x20);
+ __ARWriteDMA((u32)save3, ARAM_size + 0x1000000, 0x20);
+
+ ARAM_mode |= 8 << 1;
+ ARAM_size += 0x0800000;
+ __AR_ExpansionSize = 0x0800000;
+ } else {
+ __ARWriteDMA((u32)dummy_data, ARAM_size + 0x0400000, 0x20);
+
+ __ARWriteDMA((u32)test_data, ARAM_size + 0x0000000, 0x20);
+
+ memset((void*)buffer, 0, 0x20);
+ DCFlushRange((void*)buffer, 0x20);
+
+ __ARReadDMA((u32)buffer, ARAM_size + 0x0400000, 0x20);
+ PPCSync();
+
+ if (buffer[0] == test_data[0]) {
+ __ARWriteDMA((u32)save1, ARAM_size + 0x0000000, 0x20);
+ __ARWriteDMA((u32)save2, ARAM_size + 0x0200000, 0x20);
+ __ARWriteDMA((u32)save3, ARAM_size + 0x1000000, 0x20);
+ __ARWriteDMA((u32)save4, ARAM_size + 0x0000200, 0x20);
+
+ ARAM_mode |= 12 << 1;
+ ARAM_size += 0x1000000;
+ __AR_ExpansionSize = 0x1000000;
+ } else {
+ __ARWriteDMA((u32)save1, ARAM_size + 0x0000000, 0x20);
+ __ARWriteDMA((u32)save2, ARAM_size + 0x0200000, 0x20);
+ __ARWriteDMA((u32)save3, ARAM_size + 0x1000000, 0x20);
+ __ARWriteDMA((u32)save4, ARAM_size + 0x0000200, 0x20);
+ __ARWriteDMA((u32)save5, ARAM_size + 0x0400000, 0x20);
+
+ ARAM_mode |= 16 << 1;
+ ARAM_size += 0x2000000;
+ __AR_ExpansionSize = 0x2000000;
+ }
+ }
+ }
+ }
+ __DSPRegs[9] = (u16)((__DSPRegs[9] & ~(0x07 | 0x38)) | ARAM_mode);
+ }
+
+ *(u32*)OSPhysicalToUncached(0x00D0) = ARAM_size;
+
+ __AR_Size = ARAM_size;
+}
diff --git a/src/Dolphin/ar/arq.c b/src/Dolphin/ar/arq.c
new file mode 100644
index 0000000..d157463
--- /dev/null
+++ b/src/Dolphin/ar/arq.c
@@ -0,0 +1,170 @@
+#include "dolphin/arq.h"
+#include "dolphin/os.h"
+
+const char* __ARQVersion = "<< Dolphin SDK - ARQ\trelease build: Sep 5 2002 05:34:29 (0x2301) >>";
+static ARQRequest* __ARQRequestQueueHi;
+static ARQRequest* __ARQRequestTailHi;
+static ARQRequest* __ARQRequestQueueLo;
+static ARQRequest* __ARQRequestTailLo;
+static ARQRequest* __ARQRequestPendingHi;
+static ARQRequest* __ARQRequestPendingLo;
+static ARQCallback __ARQCallbackHi;
+static ARQCallback __ARQCallbackLo;
+static u32 __ARQChunkSize;
+
+static volatile BOOL __ARQ_init_flag = FALSE;
+
+void __ARQPopTaskQueueHi(void);
+void __ARQServiceQueueLo(void);
+void __ARQCallbackHack(void);
+void __ARQInterruptServiceRoutine(void);
+void __ARQInitTempQueue(void);
+void __ARQPushTempQueue(ARQRequest* task);
+
+void __ARQPopTaskQueueHi(void) {
+
+ if (__ARQRequestQueueHi) {
+ if (__ARQRequestQueueHi->type == ARQ_TYPE_MRAM_TO_ARAM) {
+ ARStartDMA(__ARQRequestQueueHi->type, __ARQRequestQueueHi->source, __ARQRequestQueueHi->dest,
+ __ARQRequestQueueHi->length);
+ } else {
+ ARStartDMA(__ARQRequestQueueHi->type, __ARQRequestQueueHi->dest, __ARQRequestQueueHi->source,
+ __ARQRequestQueueHi->length);
+ }
+
+ __ARQCallbackHi = __ARQRequestQueueHi->callback;
+
+ __ARQRequestPendingHi = __ARQRequestQueueHi;
+
+ __ARQRequestQueueHi = __ARQRequestQueueHi->next;
+ }
+}
+
+void __ARQServiceQueueLo(void) {
+
+ if ((__ARQRequestPendingLo == NULL) && (__ARQRequestQueueLo)) {
+ __ARQRequestPendingLo = __ARQRequestQueueLo;
+
+ __ARQRequestQueueLo = __ARQRequestQueueLo->next;
+ }
+
+ if (__ARQRequestPendingLo) {
+ if (__ARQRequestPendingLo->length <= __ARQChunkSize) {
+ if (__ARQRequestPendingLo->type == ARQ_TYPE_MRAM_TO_ARAM)
+ ARStartDMA(__ARQRequestPendingLo->type, __ARQRequestPendingLo->source,
+ __ARQRequestPendingLo->dest, __ARQRequestPendingLo->length);
+ else
+ ARStartDMA(__ARQRequestPendingLo->type, __ARQRequestPendingLo->dest,
+ __ARQRequestPendingLo->source, __ARQRequestPendingLo->length);
+
+ __ARQCallbackLo = __ARQRequestPendingLo->callback;
+ } else {
+ if (__ARQRequestPendingLo->type == ARQ_TYPE_MRAM_TO_ARAM)
+ ARStartDMA(__ARQRequestPendingLo->type, __ARQRequestPendingLo->source,
+ __ARQRequestPendingLo->dest, __ARQChunkSize);
+ else
+ ARStartDMA(__ARQRequestPendingLo->type, __ARQRequestPendingLo->dest,
+ __ARQRequestPendingLo->source, __ARQChunkSize);
+ }
+
+ __ARQRequestPendingLo->length -= __ARQChunkSize;
+ __ARQRequestPendingLo->source += __ARQChunkSize;
+ __ARQRequestPendingLo->dest += __ARQChunkSize;
+ }
+}
+void __ARQCallbackHack(void) { return; }
+
+void __ARQInterruptServiceRoutine(void) {
+
+ if (__ARQCallbackHi) {
+ (*__ARQCallbackHi)((u32)__ARQRequestPendingHi);
+ __ARQRequestPendingHi = NULL;
+ __ARQCallbackHi = NULL;
+ }
+
+ else if (__ARQCallbackLo) {
+ (*__ARQCallbackLo)((u32)__ARQRequestPendingLo);
+ __ARQRequestPendingLo = NULL;
+ __ARQCallbackLo = NULL;
+ }
+
+ __ARQPopTaskQueueHi();
+
+ if (__ARQRequestPendingHi == NULL)
+ __ARQServiceQueueLo();
+}
+
+void ARQInit(void) {
+
+ if (TRUE == __ARQ_init_flag) {
+ return;
+ }
+
+ OSRegisterVersion(__ARQVersion);
+ __ARQRequestQueueHi = __ARQRequestQueueLo = NULL;
+ __ARQChunkSize = ARQ_CHUNK_SIZE_DEFAULT;
+ ARRegisterDMACallback(&__ARQInterruptServiceRoutine);
+ __ARQRequestPendingHi = NULL;
+ __ARQRequestPendingLo = NULL;
+ __ARQCallbackHi = NULL;
+ __ARQCallbackLo = NULL;
+ __ARQ_init_flag = TRUE;
+}
+
+void ARQPostRequest(ARQRequest* request, u32 owner, u32 type, u32 priority, u32 source, u32 dest,
+ u32 length, ARQCallback callback) {
+
+ BOOL enabled;
+
+ request->next = NULL;
+ request->owner = owner;
+ request->type = type;
+ request->source = source;
+ request->dest = dest;
+ request->length = length;
+
+ if (callback) {
+ request->callback = callback;
+ } else {
+ request->callback = (ARQCallback)&__ARQCallbackHack;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ switch (priority) {
+ case ARQ_PRIORITY_LOW:
+
+ if (__ARQRequestQueueLo) {
+ __ARQRequestTailLo->next = request;
+ } else {
+ __ARQRequestQueueLo = request;
+ }
+ __ARQRequestTailLo = request;
+
+ break;
+
+ case ARQ_PRIORITY_HIGH:
+
+ if (__ARQRequestQueueHi) {
+ __ARQRequestTailHi->next = request;
+ } else {
+ __ARQRequestQueueHi = request;
+ }
+
+ __ARQRequestTailHi = request;
+
+ break;
+ }
+
+ if ((__ARQRequestPendingHi == NULL) && (__ARQRequestPendingLo == NULL)) {
+ __ARQPopTaskQueueHi();
+
+ if (__ARQRequestPendingHi == NULL) {
+ __ARQServiceQueueLo();
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+u32 ARQGetChunkSize(void) { return __ARQChunkSize; }
diff --git a/src/Dolphin/card/CARDBios.c b/src/Dolphin/card/CARDBios.c
new file mode 100644
index 0000000..0a1fb45
--- /dev/null
+++ b/src/Dolphin/card/CARDBios.c
@@ -0,0 +1,775 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+const char* __CARDVersion =
+ "<< Dolphin SDK - CARD\trelease build: Sep 5 2002 05:35:20 (0x2301) >>";
+
+CARDControl __CARDBlock[2];
+DVDDiskID __CARDDiskNone;
+
+static u16 __CARDEncode;
+
+s32 __CARDReadStatus(s32 chan, u8* status);
+s32 __CARDClearStatus(s32 chan);
+void __CARDSetDiskID(const DVDDiskID* id);
+static s32 Retry(s32 chan);
+
+BOOL OnReset(BOOL f);
+static OSResetFunctionInfo ResetFunctionInfo = {OnReset, 127};
+
+void __CARDDefaultApiCallback(s32 chan, s32 result) {}
+
+void __CARDExtHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (card->attached) {
+ card->attached = FALSE;
+ EXISetExiCallback(chan, 0);
+ OSCancelAlarm(&card->alarm);
+ callback = card->exiCallback;
+
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, CARD_RESULT_NOCARD);
+ }
+
+ if (card->result != CARD_RESULT_BUSY) {
+ card->result = CARD_RESULT_NOCARD;
+ }
+
+ callback = card->extCallback;
+ if (callback && CARD_MAX_MOUNT_STEP <= card->mountStep) {
+ card->extCallback = 0;
+ callback(chan, CARD_RESULT_NOCARD);
+ }
+ }
+}
+
+void __CARDExiHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+ u8 status;
+ s32 result;
+
+ card = &__CARDBlock[chan];
+
+ OSCancelAlarm(&card->alarm);
+
+ if (!card->attached) {
+ return;
+ }
+
+ if (!EXILock(chan, 0, 0)) {
+ result = CARD_RESULT_FATAL_ERROR;
+ goto fatal;
+ }
+
+ if ((result = __CARDReadStatus(chan, &status)) < 0 || (result = __CARDClearStatus(chan)) < 0) {
+ goto error;
+ }
+
+ if ((result = (status & 0x18) ? CARD_RESULT_IOERROR : CARD_RESULT_READY) == CARD_RESULT_IOERROR &&
+ --card->retry > 0) {
+ result = Retry(chan);
+ if (result >= 0) {
+ return;
+ }
+ goto fatal;
+ }
+
+error:
+ EXIUnlock(chan);
+
+fatal:
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, result);
+ }
+}
+
+void __CARDTxHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+ BOOL err;
+
+ card = &__CARDBlock[chan];
+ err = !EXIDeselect(chan);
+ EXIUnlock(chan);
+ callback = card->txCallback;
+ if (callback) {
+ card->txCallback = 0;
+ callback(chan, (!err && EXIProbe(chan)) ? CARD_RESULT_READY : CARD_RESULT_NOCARD);
+ }
+}
+
+void __CARDUnlockedHandler(s32 chan, OSContext* context) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->unlockCallback;
+ if (callback) {
+ card->unlockCallback = 0;
+ callback(chan, EXIProbe(chan) ? CARD_RESULT_UNLOCKED : CARD_RESULT_NOCARD);
+ }
+}
+
+s32 __CARDEnableInterrupt(s32 chan, BOOL enable) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXISelect(chan, 0, 4)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = enable ? 0x81010000 : 0x81000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 2, 1, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+s32 __CARDReadStatus(s32 chan, u8* status) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXISelect(chan, 0, 4)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = 0x83000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 2, 1, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, status, 1, 0, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+s32 __CARDClearStatus(s32 chan) {
+ BOOL err;
+ u32 cmd;
+
+ if (!EXISelect(chan, 0, 4)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ cmd = 0x89000000;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 1, 1, 0);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+static void TimeoutHandler(OSAlarm* alarm, OSContext* context) {
+ s32 chan;
+ CARDControl* card;
+ CARDCallback callback;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if (alarm == &card->alarm) {
+ break;
+ }
+ }
+
+ if (!card->attached) {
+ return;
+ }
+
+ EXISetExiCallback(chan, NULL);
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, CARD_RESULT_IOERROR);
+ }
+}
+
+static void SetupTimeoutAlarm(CARDControl* card) {
+ OSCancelAlarm(&card->alarm);
+ switch (card->cmd[0]) {
+ case 0xF2:
+ OSSetAlarm(&card->alarm, OSMillisecondsToTicks(100), TimeoutHandler);
+ break;
+ case 0xF3:
+ break;
+ case 0xF4:
+ case 0xF1:
+ OSSetAlarm(&card->alarm, OSSecondsToTicks((OSTime)2) * (card->sectorSize / 0x2000),
+ TimeoutHandler);
+ break;
+ }
+}
+
+static s32 Retry(s32 chan) {
+ CARDControl* card;
+ card = &__CARDBlock[chan];
+
+ if (!EXISelect(chan, 0, 4)) {
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ SetupTimeoutAlarm(card);
+
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1)) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ if (card->cmd[0] == 0x52 &&
+ !EXIImmEx(chan, (u8*)card->workArea + sizeof(CARDID), card->latency, 1)) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ if (card->mode == 0xffffffff) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_READY;
+ }
+
+ if (!EXIDma(chan, card->buffer, (s32)((card->cmd[0] == 0x52) ? 512 : 128), card->mode,
+ __CARDTxHandler)) {
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ return CARD_RESULT_NOCARD;
+ }
+
+ return CARD_RESULT_READY;
+}
+
+static void UnlockedCallback(s32 chan, s32 result) {
+ CARDCallback callback;
+ CARDControl* card;
+
+ card = &__CARDBlock[chan];
+ if (result >= 0) {
+ card->unlockCallback = UnlockedCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ result = CARD_RESULT_READY;
+ } else {
+ card->unlockCallback = 0;
+ result = Retry(chan);
+ }
+ }
+
+ if (result < 0) {
+ switch (card->cmd[0]) {
+ case 0x52:
+ callback = card->txCallback;
+ if (callback) {
+ card->txCallback = 0;
+ callback(chan, result);
+ }
+
+ break;
+ case 0xF2:
+ case 0xF4:
+ case 0xF1:
+ callback = card->exiCallback;
+ if (callback) {
+ card->exiCallback = 0;
+ callback(chan, result);
+ }
+ break;
+ }
+ }
+}
+
+static s32 __CARDStart(s32 chan, CARDCallback txCallback, CARDCallback exiCallback) {
+ BOOL enabled;
+ CARDControl* card;
+ s32 result;
+
+ enabled = OSDisableInterrupts();
+
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ result = CARD_RESULT_NOCARD;
+ } else {
+
+ if (txCallback) {
+ card->txCallback = txCallback;
+ }
+ if (exiCallback) {
+ card->exiCallback = exiCallback;
+ }
+ card->unlockCallback = UnlockedCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ result = CARD_RESULT_BUSY;
+ } else {
+ card->unlockCallback = 0;
+
+ if (!EXISelect(chan, 0, 4)) {
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ SetupTimeoutAlarm(card);
+ result = CARD_RESULT_READY;
+ }
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+#define AD1(x) ((u8)(((x) >> 17) & 0x7f))
+#define AD1EX(x) ((u8)(AD1(x) | 0x80));
+#define AD2(x) ((u8)(((x) >> 9) & 0xff))
+#define AD3(x) ((u8)(((x) >> 7) & 0x03))
+#define BA(x) ((u8)((x)&0x7f))
+
+s32 __CARDReadSegment(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ card = &__CARDBlock[chan];
+ card->cmd[0] = 0x52;
+ card->cmd[1] = AD1(card->addr);
+ card->cmd[2] = AD2(card->addr);
+ card->cmd[3] = AD3(card->addr);
+ card->cmd[4] = BA(card->addr);
+ card->cmdlen = 5;
+ card->mode = 0;
+ card->retry = 0;
+
+ result = __CARDStart(chan, callback, 0);
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1) ||
+ !EXIImmEx(chan, (u8*)card->workArea + sizeof(CARDID), card->latency,
+ 1) || // XXX use DMA if possible
+ !EXIDma(chan, card->buffer, 512, card->mode, __CARDTxHandler)) {
+ card->txCallback = 0;
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+ }
+ return result;
+}
+
+s32 __CARDWritePage(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ card = &__CARDBlock[chan];
+ card->cmd[0] = 0xF2;
+ card->cmd[1] = AD1(card->addr);
+ card->cmd[2] = AD2(card->addr);
+ card->cmd[3] = AD3(card->addr);
+ card->cmd[4] = BA(card->addr);
+ card->cmdlen = 5;
+ card->mode = 1;
+ card->retry = 3;
+
+ result = __CARDStart(chan, 0, callback);
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1) ||
+ !EXIDma(chan, card->buffer, 128, card->mode, __CARDTxHandler)) {
+ card->exiCallback = 0;
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ result = CARD_RESULT_NOCARD;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+ }
+ return result;
+}
+
+#ifdef FULL_FRANK
+/* TODO: Needs frank fix for disconnected stack epilogue */
+s32 __CARDEraseSector(s32 chan, u32 addr, CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+
+ card = &__CARDBlock[chan];
+ card->cmd[0] = 0xF1;
+ card->cmd[1] = AD1(addr);
+ card->cmd[2] = AD2(addr);
+ card->cmdlen = 3;
+ card->mode = -1;
+ card->retry = 3;
+
+ result = __CARDStart(chan, 0, callback);
+
+ if (result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_READY;
+ } else if (result >= 0) {
+ if (!EXIImmEx(chan, card->cmd, card->cmdlen, 1)) {
+ card->exiCallback = NULL;
+ result = CARD_RESULT_NOCARD;
+ } else {
+ result = CARD_RESULT_READY;
+ }
+
+ EXIDeselect(chan);
+ EXIUnlock(chan);
+ }
+ return result;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 __CARDEraseSector(s32 chan, u32 addr, CARDCallback callback) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x28(r1)
+ stw r31, 0x24(r1)
+ stw r30, 0x20(r1)
+ stw r29, 0x1c(r1)
+ addi r29, r3, 0
+ mulli r6, r29, 0x110
+ lis r3, __CARDBlock@ha
+ addi r0, r3, __CARDBlock@l
+ add r31, r0, r6
+ li r0, 0xf1
+ stb r0, 0x94(r31)
+ rlwinm r3, r4, 0xf, 0x19, 0x1f
+ rlwinm r0, r4, 0x17, 0x18, 0x1f
+ stb r3, 0x95(r31)
+ li r6, 3
+ addi r3, r29, 0
+ stb r0, 0x96(r31)
+ li r0, -1
+ li r4, 0
+ stw r6, 0xa0(r31)
+ stw r0, 0xa4(r31)
+ stw r6, 0xa8(r31)
+ bl __CARDStart
+ addi r30, r3, 0
+ cmpwi r30, -1
+ bne lbl_803B8C4C
+ li r30, 0
+ b lbl_803B8C94
+lbl_803B8C4C:
+ cmpwi r30, 0
+ blt lbl_803B8C94
+ lwz r5, 0xa0(r31)
+ addi r3, r29, 0
+ addi r4, r31, 0x94
+ li r6, 1
+ bl EXIImmEx
+ cmpwi r3, 0
+ bne lbl_803B8C80
+ li r0, 0
+ stw r0, 0xcc(r31)
+ li r30, -3
+ b lbl_803B8C84
+lbl_803B8C80:
+ li r30, 0
+lbl_803B8C84:
+ mr r3, r29
+ bl EXIDeselect
+ mr r3, r29
+ bl EXIUnlock
+lbl_803B8C94:
+ 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
+}
+/* clang-format on */
+#pragma pop
+#endif
+void CARDInit(void) {
+ int chan;
+
+ if (__CARDBlock[0].diskID && __CARDBlock[1].diskID) {
+ return;
+ }
+
+ __CARDEncode = OSGetFontEncode();
+
+ OSRegisterVersion(__CARDVersion);
+
+ DSPInit();
+ OSInitAlarm();
+
+ for (chan = 0; chan < 2; ++chan) {
+ CARDControl* card = &__CARDBlock[chan];
+
+ card->result = CARD_RESULT_NOCARD;
+ OSInitThreadQueue(&card->threadQueue);
+ OSCreateAlarm(&card->alarm);
+ }
+ __CARDSetDiskID((DVDDiskID*)OSPhysicalToCached(0x0));
+
+ OSRegisterResetFunction(&ResetFunctionInfo);
+}
+
+u16 __CARDGetFontEncode() { return __CARDEncode; }
+
+void __CARDSetDiskID(const DVDDiskID* id) {
+ __CARDBlock[0].diskID = id ? id : &__CARDDiskNone;
+ __CARDBlock[1].diskID = id ? id : &__CARDDiskNone;
+}
+
+s32 __CARDGetControlBlock(s32 chan, CARDControl** pcard) {
+ BOOL enabled;
+ s32 result;
+ CARDControl* card;
+
+ card = &__CARDBlock[chan];
+ if (chan < 0 || chan >= 2 || card->diskID == NULL) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ enabled = OSDisableInterrupts();
+ if (!card->attached) {
+ result = CARD_RESULT_NOCARD;
+ } else if (card->result == CARD_RESULT_BUSY) {
+ result = CARD_RESULT_BUSY;
+ } else {
+ card->result = CARD_RESULT_BUSY;
+ result = CARD_RESULT_READY;
+ card->apiCallback = 0;
+ *pcard = card;
+ }
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+#ifdef FULL_FRANK
+/* TODO: Needs frank fix for disconnected stack epilogue */
+s32 __CARDPutControlBlock(CARDControl* card, s32 result) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if (card->attached) {
+ card->result = result;
+ } else if (card->result == CARD_RESULT_BUSY) {
+ card->result = result;
+ }
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+#else
+#pragma push
+/* clang-format off */
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 __CARDPutControlBlock(CARDControl* card, s32 result) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ addi r31, r4, 0
+ stw r30, 0x10(r1)
+ addi r30, r3, 0
+ bl OSDisableInterrupts
+ lwz r0, 0(r30)
+ cmpwi r0, 0
+ beq lbl_803B8E8C
+ stw r31, 4(r30)
+ b lbl_803B8E9C
+lbl_803B8E8C:
+ lwz r0, 4(r30)
+ cmpwi r0, -1
+ bne lbl_803B8E9C
+ stw r31, 4(r30)
+lbl_803B8E9C:
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+s32 CARDGetResultCode(s32 chan) {
+ CARDControl* card;
+ if (chan < 0 || chan >= 2) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ card = &__CARDBlock[chan];
+ return card->result;
+}
+
+#ifdef FULL_FRANK
+s32 CARDFreeBlocks(s32 chan, s32* byteNotUsed, s32* filesNotUsed) {
+ CARDControl* card;
+ s32 result;
+ u16* fat;
+ CARDDir* dir;
+ CARDDir* ent;
+ u16 fileNo;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ dir = __CARDGetDirBlock(card);
+ if (fat == 0 || dir == 0) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+
+ if (byteNotUsed) {
+ *byteNotUsed = (s32)(card->sectorSize * fat[CARD_FAT_FREEBLOCKS]);
+ }
+
+ if (filesNotUsed) {
+ *filesNotUsed = 0;
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->fileName[0] == 0xff) {
+ ++*filesNotUsed;
+ }
+ }
+ }
+
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm s32 CARDFreeBlocks(s32 chan, s32* byteNotUsed, s32* filesNotUsed) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x30(r1)
+ stw r31, 0x2c(r1)
+ addi r31, r5, 0
+ stw r30, 0x28(r1)
+ stw r29, 0x24(r1)
+ addi r29, r4, 0
+ addi r4, r1, 0x18
+ bl __CARDGetControlBlock
+ cmpwi r3, 0
+ bge lbl_803B8F20
+ b lbl_803B9020
+lbl_803B8F20:
+ lwz r3, 0x18(r1)
+ bl __CARDGetFatBlock
+ mr r30, r3
+ lwz r3, 0x18(r1)
+ bl __CARDGetDirBlock
+ cmplwi r30, 0
+ beq lbl_803B8F44
+ cmplwi r3, 0
+ bne lbl_803B8F84
+lbl_803B8F44:
+ lwz r30, 0x18(r1)
+ bl OSDisableInterrupts
+ lwz r0, 0(r30)
+ cmpwi r0, 0
+ beq lbl_803B8F64
+ li r0, -6
+ stw r0, 4(r30)
+ b lbl_803B8F78
+lbl_803B8F64:
+ lwz r0, 4(r30)
+ cmpwi r0, -1
+ bne lbl_803B8F78
+ li r0, -6
+ stw r0, 4(r30)
+lbl_803B8F78:
+ bl OSRestoreInterrupts
+ li r3, -6
+ b lbl_803B9020
+lbl_803B8F84:
+ cmplwi r29, 0
+ beq lbl_803B8FA0
+ lwz r4, 0x18(r1)
+ lhz r0, 6(r30)
+ lwz r4, 0xc(r4)
+ mullw r0, r4, r0
+ stw r0, 0(r29)
+lbl_803B8FA0:
+ cmplwi r31, 0
+ beq lbl_803B8FE4
+ li r0, 0
+ stw r0, 0(r31)
+ li r5, 0
+ b lbl_803B8FD8
+lbl_803B8FB8:
+ lbz r0, 8(r3)
+ cmplwi r0, 0xff
+ bne lbl_803B8FD0
+ lwz r4, 0(r31)
+ addi r0, r4, 1
+ stw r0, 0(r31)
+lbl_803B8FD0:
+ addi r3, r3, 0x40
+ addi r5, r5, 1
+lbl_803B8FD8:
+ clrlwi r0, r5, 0x10
+ cmplwi r0, 0x7f
+ blt lbl_803B8FB8
+lbl_803B8FE4:
+ lwz r30, 0x18(r1)
+ bl OSDisableInterrupts
+ lwz r0, 0(r30)
+ cmpwi r0, 0
+ beq lbl_803B9004
+ li r0, 0
+ stw r0, 4(r30)
+ b lbl_803B9018
+lbl_803B9004:
+ lwz r0, 4(r30)
+ cmpwi r0, -1
+ bne lbl_803B9018
+ li r0, 0
+ stw r0, 4(r30)
+lbl_803B9018:
+ bl OSRestoreInterrupts
+ li r3, 0
+lbl_803B9020:
+ lwz r0, 0x34(r1)
+ lwz r31, 0x2c(r1)
+ lwz r30, 0x28(r1)
+ lwz r29, 0x24(r1)
+ addi r1, r1, 0x30
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+static BOOL OnReset(BOOL f) {
+ if (!f) {
+ if (CARDUnmount(0) == CARD_RESULT_BUSY || CARDUnmount(1) == CARD_RESULT_BUSY) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/src/Dolphin/card/CARDBlock.c b/src/Dolphin/card/CARDBlock.c
new file mode 100644
index 0000000..b6305f1
--- /dev/null
+++ b/src/Dolphin/card/CARDBlock.c
@@ -0,0 +1,159 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+u16* __CARDGetFatBlock(CARDControl* card) { return card->currentFat; }
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ u16* fatBack;
+
+ card = &__CARDBlock[chan];
+
+ if (result >= 0) {
+ fat = (u16*)((u8*)card->workArea + 0x6000);
+ fatBack = (u16*)((u8*)card->workArea + 0x8000);
+
+ if (card->currentFat == fat) {
+ card->currentFat = fatBack;
+ memcpy(fatBack, fat, 0x2000);
+ } else {
+ card->currentFat = fat;
+ memcpy(fat, fatBack, 0x2000);
+ }
+ }
+
+ if (card->apiCallback == NULL) {
+ __CARDPutControlBlock(card, result);
+ }
+
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u32 temp[2]; /* this compiler sucks */
+ u16* fat;
+ u32 addr;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ addr = ((u32)fat - (u32)card->workArea) / CARD_SYSTEM_BLOCK_SIZE * card->sectorSize;
+ result = __CARDWrite(chan, addr, CARD_SYSTEM_BLOCK_SIZE, fat, WriteCallback);
+ if (result < 0) {
+ goto error;
+ }
+
+ return;
+
+error:
+ if (card->apiCallback == NULL) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = NULL;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDAllocBlock(s32 chan, u32 cBlock, CARDCallback callback) {
+ CARDControl* card;
+ u16* fat;
+ u16 iBlock;
+ u16 startBlock;
+ u16 prevBlock;
+ u16 count;
+
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ if (fat[3] < cBlock) {
+ return CARD_RESULT_INSSPACE;
+ }
+
+ fat[3] -= cBlock;
+ startBlock = 0xFFFF;
+ iBlock = fat[4];
+ count = 0;
+ while (0 < cBlock) {
+ if (card->cBlock - 5 < ++count) {
+ return CARD_RESULT_BROKEN;
+ }
+
+ iBlock++;
+ if (!CARDIsValidBlockNo(card, iBlock)) {
+ iBlock = 5;
+ }
+
+ if (fat[iBlock] == 0x0000u) {
+ if (startBlock == 0xFFFF) {
+ startBlock = iBlock;
+ } else {
+ fat[prevBlock] = iBlock;
+ }
+ prevBlock = iBlock;
+ fat[iBlock] = 0xFFFF;
+ --cBlock;
+ }
+ }
+ fat[4] = iBlock;
+ card->startBlock = startBlock;
+
+ return __CARDUpdateFatBlock(chan, fat, callback);
+}
+
+s32 __CARDFreeBlock(s32 chan, u16 nBlock, CARDCallback callback) {
+ CARDControl* card;
+ u16* fat;
+ u16 nextBlock;
+
+ card = card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ while (nBlock != 0xFFFF) {
+ if (!CARDIsValidBlockNo(card, nBlock)) {
+ return CARD_RESULT_BROKEN;
+ }
+
+ nextBlock = fat[nBlock];
+ fat[nBlock] = 0;
+ nBlock = nextBlock;
+ ++fat[3];
+ }
+
+ return __CARDUpdateFatBlock(chan, fat, callback);
+}
+
+s32 __CARDUpdateFatBlock(s32 chan, u16* fat, CARDCallback callback) {
+ CARDControl* card;
+
+ card = &__CARDBlock[chan];
+ ++fat[2];
+ __CARDCheckSum(fat + 2, 0x1FFC, fat, fat + 1);
+ DCStoreRange(fat, 0x2000);
+ card->eraseCallback = callback;
+
+ return __CARDEraseSector(chan, (((u32)fat - (u32)card->workArea) / 8192u) * card->sectorSize,
+ EraseCallback);
+}
diff --git a/src/Dolphin/card/CARDCheck.c b/src/Dolphin/card/CARDCheck.c
new file mode 100644
index 0000000..2253d55
--- /dev/null
+++ b/src/Dolphin/card/CARDCheck.c
@@ -0,0 +1,688 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+#include <dolphin/OSRtcPriv.h>
+
+#include "string.h"
+
+#define __CARDGetDirCheck(dir) ((CARDDirCheck*)&(dir)[CARD_MAX_FILE])
+
+void __CARDCheckSum(void* ptr, int length, u16* checksum, u16* checksumInv) {
+ u16* p;
+ int i;
+
+ length /= sizeof(u16);
+ *checksum = *checksumInv = 0;
+ for (i = 0, p = ptr; i < length; i++, p++) {
+ *checksum += *p;
+ *checksumInv += ~*p;
+ }
+ if (*checksum == 0xffff) {
+ *checksum = 0;
+ }
+ if (*checksumInv == 0xffff) {
+ *checksumInv = 0;
+ }
+}
+
+static s32 VerifyID(CARDControl* card) {
+ CARDID* id;
+ u16 checksum;
+ u16 checksumInv;
+ OSSramEx* sramEx;
+ OSTime rand;
+ int i;
+
+ id = card->workArea;
+
+ if (id->deviceID != 0 || id->size != card->size) {
+ return CARD_RESULT_BROKEN;
+ }
+
+ __CARDCheckSum(id, sizeof(CARDID) - sizeof(u32), &checksum, &checksumInv);
+ if (id->checkSum != checksum || id->checkSumInv != checksumInv) {
+ return CARD_RESULT_BROKEN;
+ }
+
+ rand = *(OSTime*)&id->serial[12];
+ sramEx = __OSLockSramEx();
+
+ for (i = 0; i < 12; i++) {
+ rand = (rand * 1103515245 + 12345) >> 16;
+ if (id->serial[i] != (u8)(sramEx->flashID[card - __CARDBlock][i] + rand)) {
+ __OSUnlockSramEx(FALSE);
+ return CARD_RESULT_BROKEN;
+ }
+ rand = ((rand * 1103515245 + 12345) >> 16) & 0x7FFF;
+ }
+
+ __OSUnlockSramEx(FALSE);
+ if (id->encode != __CARDGetFontEncode()) {
+ return CARD_RESULT_ENCODING;
+ }
+
+ return CARD_RESULT_READY;
+}
+
+#ifdef FULL_FRANK
+static s32 VerifyDir(CARDControl* card, int* outCurrent) {
+ CARDDir* dir[2];
+ CARDDirCheck* check[2];
+ u16 checkSum;
+ u16 checkSumInv;
+ int i;
+ int errors;
+ int current;
+
+ current = errors = 0;
+ for (i = 0; i < 2; i++) {
+ dir[i] = (CARDDir*)((u8*)card->workArea + (1 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ check[i] = __CARDGetDirCheck(dir[i]);
+ __CARDCheckSum(dir[i], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &checkSum, &checkSumInv);
+ if (check[i]->checkSum != checkSum || check[i]->checkSumInv != checkSumInv) {
+ ++errors;
+ current = i;
+ card->currentDir = 0;
+ }
+ }
+
+ if (0 == errors) {
+ if (card->currentDir == 0) {
+ if ((check[0]->checkCode - check[1]->checkCode) < 0) {
+ current = 0;
+ } else {
+ current = 1;
+ }
+ card->currentDir = dir[current];
+ memcpy(dir[current], dir[current ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ } else {
+ current = (card->currentDir == dir[0]) ? 0 : 1;
+ }
+ }
+ if (outCurrent) {
+ *outCurrent = current;
+ }
+ return errors;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+static asm s32 VerifyDir(CARDControl* card, int* outCurrent) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x38(r1)
+ stw r31, 0x34(r1)
+ addi r7, r1, 0x1c
+ addi r8, r1, 0x14
+ stw r30, 0x30(r1)
+ li r31, 0
+ li r30, 0
+ stw r29, 0x2c(r1)
+ addi r29, r4, 0
+ li r4, 0
+lbl_803BB038:
+ addi r0, r4, 1
+ lwz r5, 0x80(r3)
+ slwi r0, r0, 0xd
+ add r0, r5, r0
+ stw r0, 0(r7)
+ li r6, 0x1ffc
+ srawi r6, r6, 1
+ lwz r5, 0(r7)
+ addze. r6, r6
+ li r11, 0
+ addi r0, r5, 0x1fc0
+ stw r0, 0(r8)
+ li r10, 0
+ lwz r5, 0(r7)
+ ble lbl_803BB12C
+ rlwinm. r0, r6, 0x1d, 3, 0x1f
+ mtctr r0
+ beq lbl_803BB110
+lbl_803BB080:
+ lhz r9, 0(r5)
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 2(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 4(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 6(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 8(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xa(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xc(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xe(r5)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ add r11, r11, r0
+ addi r5, r5, 0x10
+ bdnz lbl_803BB080
+ andi. r6, r6, 7
+ beq lbl_803BB12C
+lbl_803BB110:
+ mtctr r6
+lbl_803BB114:
+ lhz r9, 0(r5)
+ addi r5, r5, 2
+ nor r0, r9, r9
+ add r10, r10, r9
+ add r11, r11, r0
+ bdnz lbl_803BB114
+lbl_803BB12C:
+ clrlwi r0, r10, 0x10
+ cmplwi r0, 0xffff
+ bne lbl_803BB13C
+ li r10, 0
+lbl_803BB13C:
+ clrlwi r0, r11, 0x10
+ cmplwi r0, 0xffff
+ bne lbl_803BB14C
+ li r11, 0
+lbl_803BB14C:
+ lwz r6, 0(r8)
+ clrlwi r5, r10, 0x10
+ lhz r0, 0x3c(r6)
+ cmplw r5, r0
+ bne lbl_803BB170
+ lhz r0, 0x3e(r6)
+ clrlwi r5, r11, 0x10
+ cmplw r5, r0
+ beq lbl_803BB180
+lbl_803BB170:
+ li r0, 0
+ stw r0, 0x84(r3)
+ addi r30, r4, 0
+ addi r31, r31, 1
+lbl_803BB180:
+ addi r4, r4, 1
+ cmpwi r4, 2
+ addi r7, r7, 4
+ addi r8, r8, 4
+ blt lbl_803BB038
+ cmpwi r31, 0
+ bne lbl_803BB21C
+ lwz r4, 0x84(r3)
+ cmplwi r4, 0
+ bne lbl_803BB200
+ lwz r5, 0x18(r1)
+ lwz r4, 0x14(r1)
+ lha r5, 0x3a(r5)
+ lha r0, 0x3a(r4)
+ subf. r0, r5, r0
+ bge lbl_803BB1C8
+ li r30, 0
+ b lbl_803BB1CC
+lbl_803BB1C8:
+ li r30, 1
+lbl_803BB1CC:
+ slwi r0, r30, 2
+ addi r6, r1, 0x1c
+ add r6, r6, r0
+ lwz r4, 0(r6)
+ xori r0, r30, 1
+ slwi r0, r0, 2
+ stw r4, 0x84(r3)
+ addi r4, r1, 0x1c
+ li r5, 0x2000
+ lwz r3, 0(r6)
+ lwzx r4, r4, r0
+ bl memcpy
+ b lbl_803BB21C
+lbl_803BB200:
+ lwz r0, 0x1c(r1)
+ cmplw r4, r0
+ bne lbl_803BB214
+ li r0, 0
+ b lbl_803BB218
+lbl_803BB214:
+ li r0, 1
+lbl_803BB218:
+ mr r30, r0
+lbl_803BB21C:
+ cmplwi r29, 0
+ beq lbl_803BB228
+ stw r30, 0(r29)
+lbl_803BB228:
+ mr r3, r31
+ lwz r0, 0x3c(r1)
+ lwz r31, 0x34(r1)
+ lwz r30, 0x30(r1)
+ lwz r29, 0x2c(r1)
+ addi r1, r1, 0x38
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
+
+#ifdef FULL_FRANK
+static s32 VerifyFAT(CARDControl* card, int* outCurrent) {
+ u16* fat[2];
+ u16* fatp;
+ u16 nBlock;
+ u16 cFree;
+ int i;
+ u16 checkSum;
+ u16 checkSumInv;
+ int errors;
+ int current;
+
+ current = errors = 0;
+ for (i = 0; i < 2; i++) {
+ fatp = fat[i] = (u16*)((u8*)card->workArea + (3 + i) * CARD_SYSTEM_BLOCK_SIZE);
+
+ __CARDCheckSum(&fatp[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &checkSum,
+ &checkSumInv);
+ if (fatp[CARD_FAT_CHECKSUM] != checkSum || fatp[CARD_FAT_CHECKSUMINV] != checkSumInv) {
+ ++errors;
+ current = i;
+ card->currentFat = 0;
+ continue;
+ }
+
+ cFree = 0;
+ for (nBlock = CARD_NUM_SYSTEM_BLOCK; nBlock < card->cBlock; nBlock++) {
+ if (fatp[nBlock] == CARD_FAT_AVAIL) {
+ cFree++;
+ }
+ }
+ if (cFree != fatp[CARD_FAT_FREEBLOCKS]) {
+ ++errors;
+ current = i;
+ card->currentFat = 0;
+ continue;
+ }
+ }
+
+ if (0 == errors) {
+ if (card->currentFat == 0) {
+ if (((s16)fat[0][CARD_FAT_CHECKCODE] - (s16)fat[1][CARD_FAT_CHECKCODE]) < 0) {
+ current = 0;
+ } else {
+ current = 1;
+ }
+ card->currentFat = fat[current];
+ memcpy(fat[current], fat[current ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ } else {
+ current = (card->currentFat == fat[0]) ? 0 : 1;
+ }
+ }
+ if (outCurrent) {
+ *outCurrent = current;
+ }
+ return errors;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level
+#pragma optimizewithasm off
+
+static asm s32 VerifyFAT(CARDControl* card, int* outCurrent) {
+ nofralloc
+ mflr r0
+ li r5, 0
+ stw r0, 4(r1)
+ stwu r1, -0x28(r1)
+ stw r31, 0x24(r1)
+ li r31, 0
+ stw r30, 0x20(r1)
+ li r30, 0
+ stw r29, 0x1c(r1)
+ addi r29, r4, 0
+ addi r4, r1, 0x10
+lbl_803BB274:
+ li r8, 0x1ffc
+ lwz r6, 0x80(r3)
+ addi r0, r5, 3
+ srawi r8, r8, 1
+ slwi r0, r0, 0xd
+ add r7, r6, r0
+ addze. r8, r8
+ stw r7, 0(r4)
+ addi r6, r7, 4
+ li r11, 0
+ li r10, 0
+ ble lbl_803BB35C
+ rlwinm. r0, r8, 0x1d, 3, 0x1f
+ mtctr r0
+ beq lbl_803BB340
+lbl_803BB2B0:
+ lhz r9, 0(r6)
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 2(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 4(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 6(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 8(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xa(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xc(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ lhz r9, 0xe(r6)
+ add r11, r11, r0
+ nor r0, r9, r9
+ add r10, r10, r9
+ add r11, r11, r0
+ addi r6, r6, 0x10
+ bdnz lbl_803BB2B0
+ andi. r8, r8, 7
+ beq lbl_803BB35C
+lbl_803BB340:
+ mtctr r8
+lbl_803BB344:
+ lhz r9, 0(r6)
+ addi r6, r6, 2
+ nor r0, r9, r9
+ add r10, r10, r9
+ add r11, r11, r0
+ bdnz lbl_803BB344
+lbl_803BB35C:
+ clrlwi r0, r10, 0x10
+ cmplwi r0, 0xffff
+ bne lbl_803BB36C
+ li r10, 0
+lbl_803BB36C:
+ clrlwi r0, r11, 0x10
+ cmplwi r0, 0xffff
+ bne lbl_803BB37C
+ li r11, 0
+lbl_803BB37C:
+ lhz r6, 0(r7)
+ clrlwi r0, r10, 0x10
+ cmplw r6, r0
+ bne lbl_803BB39C
+ lhz r6, 2(r7)
+ clrlwi r0, r11, 0x10
+ cmplw r6, r0
+ beq lbl_803BB3B0
+lbl_803BB39C:
+ li r0, 0
+ stw r0, 0x88(r3)
+ addi r30, r5, 0
+ addi r31, r31, 1
+ b lbl_803BB408
+lbl_803BB3B0:
+ lhz r8, 0x10(r3)
+ addi r6, r7, 0xa
+ li r10, 0
+ li r9, 5
+ b lbl_803BB3DC
+lbl_803BB3C4:
+ lhz r0, 0(r6)
+ cmplwi r0, 0
+ bne lbl_803BB3D4
+ addi r10, r10, 1
+lbl_803BB3D4:
+ addi r6, r6, 2
+ addi r9, r9, 1
+lbl_803BB3DC:
+ clrlwi r0, r9, 0x10
+ cmplw r0, r8
+ blt lbl_803BB3C4
+ lhz r0, 6(r7)
+ clrlwi r6, r10, 0x10
+ cmplw r6, r0
+ beq lbl_803BB408
+ li r0, 0
+ stw r0, 0x88(r3)
+ addi r30, r5, 0
+ addi r31, r31, 1
+lbl_803BB408:
+ addi r5, r5, 1
+ cmpwi r5, 2
+ addi r4, r4, 4
+ blt lbl_803BB274
+ cmpwi r31, 0
+ bne lbl_803BB4A0
+ lwz r4, 0x88(r3)
+ cmplwi r4, 0
+ bne lbl_803BB484
+ lwz r5, 0x14(r1)
+ lwz r4, 0x10(r1)
+ lha r5, 4(r5)
+ lha r0, 4(r4)
+ subf. r0, r5, r0
+ bge lbl_803BB44C
+ li r30, 0
+ b lbl_803BB450
+lbl_803BB44C:
+ li r30, 1
+lbl_803BB450:
+ slwi r0, r30, 2
+ addi r6, r1, 0x10
+ add r6, r6, r0
+ lwz r4, 0(r6)
+ xori r0, r30, 1
+ slwi r0, r0, 2
+ stw r4, 0x88(r3)
+ addi r4, r1, 0x10
+ li r5, 0x2000
+ lwz r3, 0(r6)
+ lwzx r4, r4, r0
+ bl memcpy
+ b lbl_803BB4A0
+lbl_803BB484:
+ lwz r0, 0x10(r1)
+ cmplw r4, r0
+ bne lbl_803BB498
+ li r0, 0
+ b lbl_803BB49C
+lbl_803BB498:
+ li r0, 1
+lbl_803BB49C:
+ mr r30, r0
+lbl_803BB4A0:
+ cmplwi r29, 0
+ beq lbl_803BB4AC
+ stw r30, 0(r29)
+lbl_803BB4AC:
+ mr r3, r31
+ lwz r0, 0x2c(r1)
+ lwz r31, 0x24(r1)
+ lwz r30, 0x20(r1)
+ lwz r29, 0x1c(r1)
+ addi r1, r1, 0x28
+ mtlr r0
+ blr
+}
+
+/* clang-format on */
+#pragma pop
+#endif
+
+s32 __CARDVerify(CARDControl* card) {
+ s32 result;
+ int errors;
+
+ result = VerifyID(card);
+ if (result < 0) {
+ return result;
+ }
+
+ errors = VerifyDir(card, NULL);
+ errors += VerifyFAT(card, NULL);
+ switch (errors) {
+ case 0:
+ return CARD_RESULT_READY;
+ case 1:
+ return CARD_RESULT_BROKEN;
+ default:
+ return CARD_RESULT_BROKEN;
+ }
+}
+
+s32 CARDCheckExAsync(s32 chan, s32* xferBytes, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir[2];
+ u16* fat[2];
+ u16* map;
+ s32 result;
+ int errors;
+ int currentFat;
+ int currentDir;
+ s32 fileNo;
+ u16 iBlock;
+ u16 cBlock;
+ u16 cFree;
+ BOOL updateFat = FALSE;
+ BOOL updateDir = FALSE;
+ BOOL updateOrphan = FALSE;
+
+ if (xferBytes) {
+ *xferBytes = 0;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ result = VerifyID(card);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ errors = VerifyDir(card, &currentDir);
+ errors += VerifyFAT(card, &currentFat);
+ if (1 < errors) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+
+ dir[0] = (CARDDir*)((u8*)card->workArea + (1 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ dir[1] = (CARDDir*)((u8*)card->workArea + (1 + 1) * CARD_SYSTEM_BLOCK_SIZE);
+ fat[0] = (u16*)((u8*)card->workArea + (3 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ fat[1] = (u16*)((u8*)card->workArea + (3 + 1) * CARD_SYSTEM_BLOCK_SIZE);
+
+ switch (errors) {
+ case 0:
+ break;
+ case 1:
+ if (!card->currentDir) {
+ card->currentDir = dir[currentDir];
+ memcpy(dir[currentDir], dir[currentDir ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ updateDir = TRUE;
+ } else {
+ card->currentFat = fat[currentFat];
+ memcpy(fat[currentFat], fat[currentFat ^ 1], CARD_SYSTEM_BLOCK_SIZE);
+ updateFat = TRUE;
+ }
+ break;
+ }
+
+ map = fat[currentFat ^ 1];
+ memset(map, 0, CARD_SYSTEM_BLOCK_SIZE);
+
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ CARDDir* ent;
+
+ ent = &card->currentDir[fileNo];
+ if (ent->gameName[0] == 0xff) {
+ continue;
+ }
+
+ for (iBlock = ent->startBlock, cBlock = 0; iBlock != 0xFFFF && cBlock < ent->length;
+ iBlock = card->currentFat[iBlock], ++cBlock) {
+ if (!CARDIsValidBlockNo(card, iBlock) || 1 < ++map[iBlock]) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+ if (cBlock != ent->length || iBlock != 0xFFFF) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+
+ cFree = 0;
+ for (iBlock = CARD_NUM_SYSTEM_BLOCK; iBlock < card->cBlock; iBlock++) {
+ u16 nextBlock;
+
+ nextBlock = card->currentFat[iBlock];
+ if (map[iBlock] == 0) {
+ if (nextBlock != CARD_FAT_AVAIL) {
+ card->currentFat[iBlock] = CARD_FAT_AVAIL;
+ updateOrphan = TRUE;
+ }
+ cFree++;
+ } else if (!CARDIsValidBlockNo(card, nextBlock) && nextBlock != 0xFFFF) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+ if (cFree != card->currentFat[CARD_FAT_FREEBLOCKS]) {
+ card->currentFat[CARD_FAT_FREEBLOCKS] = cFree;
+ updateOrphan = TRUE;
+ }
+ if (updateOrphan) {
+ __CARDCheckSum(&card->currentFat[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32),
+ &card->currentFat[CARD_FAT_CHECKSUM], &card->currentFat[CARD_FAT_CHECKSUMINV]);
+ }
+
+ memcpy(fat[currentFat ^ 1], fat[currentFat], CARD_SYSTEM_BLOCK_SIZE);
+
+ if (updateDir) {
+ if (xferBytes) {
+ *xferBytes = CARD_SYSTEM_BLOCK_SIZE;
+ }
+ return __CARDUpdateDir(chan, callback);
+ }
+
+ if (updateFat | updateOrphan) {
+ if (xferBytes) {
+ *xferBytes = CARD_SYSTEM_BLOCK_SIZE;
+ }
+ return __CARDUpdateFatBlock(chan, card->currentFat, callback);
+ }
+
+ __CARDPutControlBlock(card, CARD_RESULT_READY);
+ if (callback) {
+ BOOL enabled = OSDisableInterrupts();
+ callback(chan, CARD_RESULT_READY);
+ OSRestoreInterrupts(enabled);
+ }
+ return CARD_RESULT_READY;
+}
+
+s32 CARDCheckAsync(s32 chan, CARDCallback callback) {
+ s32 xferBytes;
+
+ return CARDCheckExAsync(chan, &xferBytes, callback);
+}
diff --git a/src/Dolphin/card/CARDCreate.c b/src/Dolphin/card/CARDCreate.c
new file mode 100644
index 0000000..6802b91
--- /dev/null
+++ b/src/Dolphin/card/CARDCreate.c
@@ -0,0 +1,115 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void CreateCallbackFat(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ if (result < 0) {
+ goto error;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[card->freeNo];
+ memcpy(ent->gameName, card->diskID->gameName, sizeof(ent->gameName));
+ memcpy(ent->company, card->diskID->company, sizeof(ent->company));
+ ent->permission = CARD_ATTR_PUBLIC;
+ ent->copyTimes = 0;
+ ent->startBlock = card->startBlock;
+
+ ent->bannerFormat = 0;
+ ent->iconAddr = 0xffffffff;
+ ent->iconFormat = 0;
+ ent->iconSpeed = 0;
+ ent->commentAddr = 0xffffffff;
+
+ CARDSetIconSpeed(ent, 0, CARD_STAT_SPEED_FAST);
+
+ card->fileInfo->offset = 0;
+ card->fileInfo->iBlock = ent->startBlock;
+
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ __CARDPutControlBlock(card, result);
+ if (callback) {
+ callback(chan, result);
+ }
+}
+
+s32 CARDCreateAsync(s32 chan, const char* fileName, u32 size, CARDFileInfo* fileInfo,
+ CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ u16 fileNo;
+ u16 freeNo;
+ u16* fat;
+
+ if (strlen(fileName) > (u32)CARD_FILENAME_MAX) {
+ return CARD_RESULT_NAMETOOLONG;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ if (size <= 0 || (size % card->sectorSize) != 0) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ freeNo = (u16)-1;
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->gameName[0] == 0xff) {
+ if (freeNo == (u16)-1) {
+ freeNo = fileNo;
+ }
+ } else if (memcmp(ent->gameName, card->diskID->gameName, sizeof(ent->gameName)) == 0 &&
+ memcmp(ent->company, card->diskID->company, sizeof(ent->company)) == 0 &&
+ __CARDCompareFileName(ent, fileName)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_EXIST);
+ }
+ }
+ if (freeNo == (u16)-1) {
+ return __CARDPutControlBlock(card, CARD_RESULT_NOENT);
+ }
+
+ fat = __CARDGetFatBlock(card);
+ if (card->sectorSize * fat[CARD_FAT_FREEBLOCKS] < size) {
+ return __CARDPutControlBlock(card, CARD_RESULT_INSSPACE);
+ }
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ card->freeNo = freeNo;
+ ent = &dir[freeNo];
+ ent->length = (u16)(size / card->sectorSize);
+ strncpy(ent->fileName, fileName, CARD_FILENAME_MAX);
+
+ card->fileInfo = fileInfo;
+ fileInfo->chan = chan;
+ fileInfo->fileNo = freeNo;
+
+ result = __CARDAllocBlock(chan, size / card->sectorSize, CreateCallbackFat);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDDelete.c b/src/Dolphin/card/CARDDelete.c
new file mode 100644
index 0000000..f715751
--- /dev/null
+++ b/src/Dolphin/card/CARDDelete.c
@@ -0,0 +1,97 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void DeleteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+
+ if (result < 0) {
+ goto error;
+ }
+
+ result = __CARDFreeBlock(chan, card->startBlock, callback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ __CARDPutControlBlock(card, result);
+ if (callback) {
+ callback(chan, result);
+ }
+}
+
+s32 CARDFastDeleteAsync(s32 chan, s32 fileNo, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+ if (__CARDIsOpened(card, fileNo)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BUSY);
+ }
+ card->startBlock = ent->startBlock;
+ memset(ent, 0xff, sizeof(CARDDir));
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ result = __CARDUpdateDir(chan, DeleteCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
+
+s32 CARDDeleteAsync(s32 chan, const char* fileName, CARDCallback callback) {
+ CARDControl* card;
+ s32 fileNo;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+ result = __CARDGetFileNo(card, fileName, &fileNo);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+ if (__CARDIsOpened(card, fileNo)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BUSY);
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ card->startBlock = ent->startBlock;
+ memset(ent, 0xff, sizeof(CARDDir));
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ result = __CARDUpdateDir(chan, DeleteCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDDir.c b/src/Dolphin/card/CARDDir.c
new file mode 100644
index 0000000..1daecf8
--- /dev/null
+++ b/src/Dolphin/card/CARDDir.c
@@ -0,0 +1,92 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+CARDDir* __CARDGetDirBlock(CARDControl* card) { return card->currentDir; }
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (0 <= result) {
+ CARDDir* dir0 = (CARDDir*)((u8*)card->workArea + 0x2000);
+ CARDDir* dir1 = (CARDDir*)((u8*)card->workArea + 0x4000);
+
+ if (card->currentDir == dir0) {
+ card->currentDir = dir1;
+ memcpy(dir1, dir0, 0x2000);
+ } else {
+ card->currentDir = dir0;
+ memcpy(dir0, dir1, 0x2000);
+ }
+ }
+
+error:
+ if (card->apiCallback == 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = 0;
+ callback(chan, result);
+ }
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ CARDDir* dir;
+ u32 tmp[2];
+ u32 addr;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ addr = ((u32)dir - (u32)card->workArea) / 0x2000 * card->sectorSize;
+ result = __CARDWrite(chan, addr, 0x2000, dir, WriteCallback);
+ if (result < 0) {
+ goto error;
+ }
+
+ return;
+
+error:
+ if (card->apiCallback == 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->eraseCallback;
+ if (callback) {
+ card->eraseCallback = 0;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDUpdateDir(s32 chan, CARDCallback callback) {
+ CARDControl* card;
+ CARDDirCheck* check;
+ u32 tmp[2];
+ u32 addr;
+ CARDDir* dir;
+
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ check = __CARDGetDirCheck(dir);
+ ++check->checkCode;
+ __CARDCheckSum(dir, 0x2000 - sizeof(u32), &check->checkSum, &check->checkSumInv);
+ DCStoreRange(dir, 0x2000);
+
+ card->eraseCallback = callback;
+ addr = ((u32)dir - (u32)card->workArea) / 0x2000 * card->sectorSize;
+ return __CARDEraseSector(chan, addr, EraseCallback);
+}
diff --git a/src/Dolphin/card/CARDFormat.c b/src/Dolphin/card/CARDFormat.c
new file mode 100644
index 0000000..ebfff8b
--- /dev/null
+++ b/src/Dolphin/card/CARDFormat.c
@@ -0,0 +1,127 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+#include <dolphin/OSRtcPriv.h>
+#include <dolphin/vi.h>
+
+static void FormatCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ ++card->formatStep;
+ if (card->formatStep < CARD_NUM_SYSTEM_BLOCK) {
+ result = __CARDEraseSector(chan, (u32)card->sectorSize * card->formatStep, FormatCallback);
+ if (0 <= result) {
+ return;
+ }
+ } else if (card->formatStep < 2 * CARD_NUM_SYSTEM_BLOCK) {
+ int step = card->formatStep - CARD_NUM_SYSTEM_BLOCK;
+ result = __CARDWrite(chan, (u32)card->sectorSize * step, CARD_SYSTEM_BLOCK_SIZE,
+ (u8*)card->workArea + (CARD_SYSTEM_BLOCK_SIZE * step), FormatCallback);
+ if (result >= 0) {
+ return;
+ }
+ } else {
+ card->currentDir = (CARDDir*)((u8*)card->workArea + (1 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ memcpy(card->currentDir, (u8*)card->workArea + (1 + 1) * CARD_SYSTEM_BLOCK_SIZE,
+ CARD_SYSTEM_BLOCK_SIZE);
+ card->currentFat = (u16*)((u8*)card->workArea + (3 + 0) * CARD_SYSTEM_BLOCK_SIZE);
+ memcpy(card->currentFat, (u8*)card->workArea + (3 + 1) * CARD_SYSTEM_BLOCK_SIZE,
+ CARD_SYSTEM_BLOCK_SIZE);
+ }
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+s32 __CARDFormatRegionAsync(s32 chan, u16 encode, CARDCallback callback) {
+ CARDControl* card;
+ CARDID* id;
+ CARDDir* dir;
+ u16* fat;
+ s16 i;
+ s32 result;
+ OSSram* sram;
+ OSSramEx* sramEx;
+ u16 viDTVStatus;
+ OSTime time;
+ OSTime rand;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ id = (CARDID*)card->workArea;
+ memset(id, 0xff, CARD_SYSTEM_BLOCK_SIZE);
+ viDTVStatus = __VIRegs[55];
+
+ id->encode = encode;
+
+ sram = __OSLockSram();
+ *(u32*)&id->serial[20] = sram->counterBias;
+ *(u32*)&id->serial[24] = sram->language;
+ __OSUnlockSram(FALSE);
+
+ rand = time = OSGetTime();
+
+ sramEx = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ rand = (rand * 1103515245 + 12345) >> 16;
+ id->serial[i] = (u8)(sramEx->flashID[chan][i] + rand);
+ rand = ((rand * 1103515245 + 12345) >> 16) & 0x7FFF;
+ }
+ __OSUnlockSramEx(FALSE);
+
+ *(u32*)&id->serial[28] = viDTVStatus;
+ *(OSTime*)&id->serial[12] = time;
+
+ id->deviceID = 0;
+ id->size = card->size;
+ __CARDCheckSum(id, sizeof(CARDID) - sizeof(u32), &id->checkSum, &id->checkSumInv);
+
+ for (i = 0; i < 2; i++) {
+ CARDDirCheck* check;
+
+ dir = (CARDDir*)((u8*)card->workArea + (1 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ memset(dir, 0xff, CARD_SYSTEM_BLOCK_SIZE);
+ check = __CARDGetDirCheck(dir);
+ check->checkCode = i;
+ __CARDCheckSum(dir, CARD_SYSTEM_BLOCK_SIZE - sizeof(u32), &check->checkSum,
+ &check->checkSumInv);
+ }
+ for (i = 0; i < 2; i++) {
+ fat = (u16*)((u8*)card->workArea + (3 + i) * CARD_SYSTEM_BLOCK_SIZE);
+ memset(fat, 0x00, CARD_SYSTEM_BLOCK_SIZE);
+ fat[CARD_FAT_CHECKCODE] = (u16)i;
+ fat[CARD_FAT_FREEBLOCKS] = (u16)(card->cBlock - CARD_NUM_SYSTEM_BLOCK);
+ fat[CARD_FAT_LASTSLOT] = CARD_NUM_SYSTEM_BLOCK - 1;
+ __CARDCheckSum(&fat[CARD_FAT_CHECKCODE], CARD_SYSTEM_BLOCK_SIZE - sizeof(u32),
+ &fat[CARD_FAT_CHECKSUM], &fat[CARD_FAT_CHECKSUMINV]);
+ }
+
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ DCStoreRange(card->workArea, CARD_WORKAREA_SIZE);
+
+ card->formatStep = 0;
+ result = __CARDEraseSector(chan, (u32)card->sectorSize * card->formatStep, FormatCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
+
+s32 CARDFormatAsync(s32 chan, CARDCallback callback) {
+ return __CARDFormatRegionAsync(chan, __CARDGetFontEncode(), callback);
+}
diff --git a/src/Dolphin/card/CARDMount.c b/src/Dolphin/card/CARDMount.c
new file mode 100644
index 0000000..fd361e9
--- /dev/null
+++ b/src/Dolphin/card/CARDMount.c
@@ -0,0 +1,354 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+#include <dolphin/OSRtcPriv.h>
+
+u8 GameChoice : (OS_BASE_CACHED | 0x000030E3);
+
+static u32 SectorSizeTable[8] = {
+ 8 * 1024, 16 * 1024, 32 * 1024, 64 * 1024, 128 * 1024, 256 * 1024, 0, 0,
+};
+
+static u32 LatencyTable[8] = {
+ 4, 8, 16, 32, 64, 128, 256, 512,
+};
+
+void __CARDMountCallback(s32 chan, s32 result);
+static void DoUnmount(s32 chan, s32 result);
+
+static BOOL IsCard(u32 id) {
+ u32 size;
+ s32 sectorSize;
+ if (id & (0xFFFF0000) && (id != 0x80000004 || __CARDVendorID == 0xFFFF)) {
+ return FALSE;
+ }
+
+ if ((id & 3) != 0) {
+ return FALSE;
+ }
+
+ size = id & 0xfc;
+ switch (size) {
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+
+ sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ if (sectorSize == 0) {
+ return FALSE;
+ }
+
+ if ((size * 1024 * 1024 / 8) / sectorSize < 8) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+s32 CARDProbeEx(s32 chan, s32* memSize, s32* sectorSize) {
+ u32 id;
+ CARDControl* card;
+ BOOL enabled;
+ s32 result;
+ int probe;
+
+ if (chan < 0 || 2 <= chan) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ if (GameChoice & 0x80) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ card = &__CARDBlock[chan];
+ enabled = OSDisableInterrupts();
+
+ probe = EXIProbeEx(chan);
+ if (probe == -1) {
+ result = CARD_RESULT_NOCARD;
+ } else if (probe == 0) {
+ result = CARD_RESULT_BUSY;
+ } else if (card->attached) {
+ if (card->mountStep < 1) {
+ result = CARD_RESULT_BUSY;
+ } else {
+ if (memSize) {
+ *memSize = card->size;
+ }
+ if (sectorSize) {
+ *sectorSize = card->sectorSize;
+ }
+ result = CARD_RESULT_READY;
+ }
+ } else if ((EXIGetState(chan) & 8)) {
+ result = CARD_RESULT_WRONGDEVICE;
+ } else if (!EXIGetID(chan, 0, &id)) {
+ result = CARD_RESULT_BUSY;
+ } else if (IsCard(id)) {
+ if (memSize) {
+ *memSize = (s32)(id & 0xfc);
+ }
+ if (sectorSize) {
+ *sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ }
+ result = CARD_RESULT_READY;
+ } else {
+ result = CARD_RESULT_WRONGDEVICE;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return result;
+}
+
+static s32 DoMount(s32 chan) {
+ CARDControl* card;
+ u32 id;
+ u8 status;
+ s32 result;
+ OSSramEx* sram;
+ int i;
+ u8 checkSum;
+ int step;
+
+ card = &__CARDBlock[chan];
+
+ if (card->mountStep == 0) {
+ if (EXIGetID(chan, 0, &id) == 0) {
+ result = CARD_RESULT_NOCARD;
+ } else if (IsCard(id)) {
+ result = CARD_RESULT_READY;
+ } else {
+ result = CARD_RESULT_WRONGDEVICE;
+ }
+ if (result < 0) {
+ goto error;
+ }
+
+ card->cid = id;
+
+ card->size = (u16)(id & 0xFC);
+ card->sectorSize = SectorSizeTable[(id & 0x00003800) >> 11];
+ card->cBlock = (u16)((card->size * 1024 * 1024 / 8) / card->sectorSize);
+ card->latency = LatencyTable[(id & 0x00000700) >> 8];
+
+ result = __CARDClearStatus(chan);
+ if (result < 0) {
+ goto error;
+ }
+ result = __CARDReadStatus(chan, &status);
+ if (result < 0) {
+ goto error;
+ }
+
+ if (!EXIProbe(chan)) {
+ result = CARD_RESULT_NOCARD;
+ goto error;
+ }
+
+ if (!(status & 0x40)) {
+ result = __CARDUnlock(chan, card->id);
+ if (result < 0) {
+ goto error;
+ }
+
+ checkSum = 0;
+ sram = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ sram->flashID[chan][i] = card->id[i];
+ checkSum += card->id[i];
+ }
+ sram->flashIDCheckSum[chan] = (u8)~checkSum;
+ __OSUnlockSramEx(TRUE);
+
+ return result;
+ } else {
+ card->mountStep = 1;
+
+ checkSum = 0;
+ sram = __OSLockSramEx();
+ for (i = 0; i < 12; i++) {
+ checkSum += sram->flashID[chan][i];
+ }
+ __OSUnlockSramEx(FALSE);
+ if (sram->flashIDCheckSum[chan] != (u8)~checkSum) {
+ result = CARD_RESULT_IOERROR;
+ goto error;
+ }
+ }
+ }
+
+ if (card->mountStep == 1) {
+ if (card->cid == 0x80000004) {
+ u16 vendorID;
+
+ sram = __OSLockSramEx();
+ vendorID = *(u16*)sram->flashID[chan];
+ __OSUnlockSramEx(FALSE);
+
+ if (__CARDVendorID == 0xffff || vendorID != __CARDVendorID) {
+ result = CARD_RESULT_WRONGDEVICE;
+ goto error;
+ }
+ }
+
+ card->mountStep = 2;
+
+ result = __CARDEnableInterrupt(chan, TRUE);
+ if (result < 0) {
+ goto error;
+ }
+
+ EXISetExiCallback(chan, __CARDExiHandler);
+ EXIUnlock(chan);
+ DCInvalidateRange(card->workArea, CARD_WORKAREA_SIZE);
+ }
+
+ step = card->mountStep - 2;
+ result = __CARDRead(chan, (u32)card->sectorSize * step, CARD_SYSTEM_BLOCK_SIZE,
+ (u8*)card->workArea + (CARD_SYSTEM_BLOCK_SIZE * step), __CARDMountCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+
+error:
+ EXIUnlock(chan);
+ DoUnmount(chan, result);
+ return result;
+}
+
+void __CARDMountCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+
+ switch (result) {
+ case CARD_RESULT_READY:
+ if (++card->mountStep < CARD_MAX_MOUNT_STEP) {
+ result = DoMount(chan);
+ if (0 <= result) {
+ return;
+ }
+ } else {
+ result = __CARDVerify(card);
+ }
+ break;
+ case CARD_RESULT_UNLOCKED:
+ card->unlockCallback = __CARDMountCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ return;
+ }
+ card->unlockCallback = 0;
+
+ result = DoMount(chan);
+ if (0 <= result) {
+ return;
+ }
+ break;
+ case CARD_RESULT_IOERROR:
+ case CARD_RESULT_NOCARD:
+ DoUnmount(chan, result);
+ break;
+ }
+
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+s32 CARDMountAsync(s32 chan, void* workArea, CARDCallback detachCallback,
+ CARDCallback attachCallback) {
+ CARDControl* card;
+ BOOL enabled;
+
+ if (chan < 0 || 2 <= chan) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ if (GameChoice & 0x80) {
+ return CARD_RESULT_NOCARD;
+ }
+ card = &__CARDBlock[chan];
+
+ enabled = OSDisableInterrupts();
+ if (card->result == CARD_RESULT_BUSY) {
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_BUSY;
+ }
+
+ if (!card->attached && (EXIGetState(chan) & 0x08)) {
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_WRONGDEVICE;
+ }
+
+ card->result = CARD_RESULT_BUSY;
+ card->workArea = workArea;
+ card->extCallback = detachCallback;
+ card->apiCallback = attachCallback ? attachCallback : __CARDDefaultApiCallback;
+ card->exiCallback = 0;
+
+ if (!card->attached && !EXIAttach(chan, __CARDExtHandler)) {
+ card->result = CARD_RESULT_NOCARD;
+ OSRestoreInterrupts(enabled);
+ return CARD_RESULT_NOCARD;
+ }
+
+ card->mountStep = 0;
+ card->attached = TRUE;
+ EXISetExiCallback(chan, 0);
+ OSCancelAlarm(&card->alarm);
+
+ card->currentDir = 0;
+ card->currentFat = 0;
+
+ OSRestoreInterrupts(enabled);
+
+ card->unlockCallback = __CARDMountCallback;
+ if (!EXILock(chan, 0, __CARDUnlockedHandler)) {
+ return CARD_RESULT_READY;
+ }
+ card->unlockCallback = 0;
+
+ return DoMount(chan);
+}
+
+static void DoUnmount(s32 chan, s32 result) {
+ CARDControl* card;
+ BOOL enabled;
+
+ card = &__CARDBlock[chan];
+ enabled = OSDisableInterrupts();
+ if (card->attached) {
+ EXISetExiCallback(chan, 0);
+ EXIDetach(chan);
+ OSCancelAlarm(&card->alarm);
+ card->attached = FALSE;
+ card->result = result;
+ card->mountStep = 0;
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+s32 CARDUnmount(s32 chan) {
+ CARDControl* card;
+ s32 result;
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+ DoUnmount(chan, CARD_RESULT_NOCARD);
+ return CARD_RESULT_READY;
+}
diff --git a/src/Dolphin/card/CARDNet.c b/src/Dolphin/card/CARDNet.c
new file mode 100644
index 0000000..828dbad
--- /dev/null
+++ b/src/Dolphin/card/CARDNet.c
@@ -0,0 +1,33 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+u16 __CARDVendorID = 0xffff;
+
+s32 CARDGetSerialNo(s32 chan, u64* serialNo) {
+ CARDControl* card;
+ CARDID* id;
+ int i;
+ u64 code;
+ s32 result;
+
+ if (!(0 <= chan && chan < 2)) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ id = (CARDID*)card->workArea;
+ for (code = 0, i = 0; i < sizeof(id->serial) / sizeof(u64); ++i) {
+ code ^= *(u64*)&id->serial[sizeof(u64) * i];
+ }
+ *serialNo = code;
+
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
diff --git a/src/Dolphin/card/CARDOpen.c b/src/Dolphin/card/CARDOpen.c
new file mode 100644
index 0000000..60234bf
--- /dev/null
+++ b/src/Dolphin/card/CARDOpen.c
@@ -0,0 +1,123 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+BOOL __CARDCompareFileName(CARDDir* ent, const char* fileName) {
+ char* entName;
+ char c1;
+ char c2;
+ int n;
+
+ entName = (char*)ent->fileName;
+ n = CARD_FILENAME_MAX;
+ while (0 <= --n) {
+ if ((c1 = *entName++) != (c2 = *fileName++)) {
+ return FALSE;
+ } else if (c2 == '\0') {
+ return TRUE;
+ }
+ }
+
+ if (*fileName == '\0') {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+s32 __CARDAccess(CARDControl* card, CARDDir* ent) {
+ if (ent->gameName[0] == 0xFF) {
+ return CARD_RESULT_NOFILE;
+ }
+
+ if (card->diskID == &__CARDDiskNone || (memcmp(ent->gameName, card->diskID->gameName, 4) == 0 &&
+ memcmp(ent->company, card->diskID->company, 2) == 0)) {
+ return CARD_RESULT_READY;
+ }
+
+ return CARD_RESULT_NOPERM;
+}
+
+BOOL __CARDIsWritable(CARDDir* ent) {
+ if (ent->gameName[0] == 0xFF) {
+ return CARD_RESULT_NOFILE;
+ }
+
+ if ((ent->permission & CARD_ATTR_PUBLIC) != 0) {
+ return CARD_RESULT_READY;
+ }
+
+ return CARD_RESULT_NOPERM;
+}
+
+s32 __CARDGetFileNo(CARDControl* card, const char* fileName, s32* pfileNo) {
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 fileNo;
+ s32 result;
+
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ continue;
+ }
+ if (__CARDCompareFileName(ent, fileName)) {
+ *pfileNo = fileNo;
+ return CARD_RESULT_READY;
+ }
+ }
+
+ return CARD_RESULT_NOFILE;
+}
+
+s32 CARDOpen(s32 chan, const char* fileName, CARDFileInfo* fileInfo) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ s32 fileNo;
+
+ fileInfo->chan = -1;
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+ result = __CARDGetFileNo(card, fileName, &fileNo);
+ if (0 <= result) {
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ if (!CARDIsValidBlockNo(card, ent->startBlock)) {
+ result = CARD_RESULT_BROKEN;
+ } else {
+ fileInfo->chan = chan;
+ fileInfo->fileNo = fileNo;
+ fileInfo->offset = 0;
+ fileInfo->iBlock = ent->startBlock;
+ }
+ }
+ return __CARDPutControlBlock(card, result);
+}
+
+s32 CARDClose(CARDFileInfo* fileInfo) {
+ CARDControl* card;
+ s32 result;
+
+ result = __CARDGetControlBlock(fileInfo->chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ fileInfo->chan = -1;
+ return __CARDPutControlBlock(card, CARD_RESULT_READY);
+}
+
+BOOL __CARDIsOpened(CARDControl* card, s32 fileNo) { return FALSE; }
diff --git a/src/Dolphin/card/CARDRdwr.c b/src/Dolphin/card/CARDRdwr.c
new file mode 100644
index 0000000..2f41be7
--- /dev/null
+++ b/src/Dolphin/card/CARDRdwr.c
@@ -0,0 +1,104 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void BlockReadCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ card->xferred += CARD_SEG_SIZE;
+
+ card->addr += CARD_SEG_SIZE;
+ (u8*)card->buffer += CARD_SEG_SIZE;
+ if (--card->repeat <= 0) {
+ goto error;
+ }
+
+ result = __CARDReadSegment(chan, BlockReadCallback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ if (card->apiCallback == 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->xferCallback;
+ if (callback) {
+ card->xferCallback = 0;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDRead(s32 chan, u32 addr, s32 length, void* dst, CARDCallback callback) {
+ CARDControl* card;
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ card->xferCallback = callback;
+ card->repeat = (int)(length / CARD_SEG_SIZE);
+ card->addr = addr;
+ card->buffer = dst;
+
+ return __CARDReadSegment(chan, BlockReadCallback);
+}
+
+static void BlockWriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ card->xferred += CARD_PAGE_SIZE;
+
+ card->addr += CARD_PAGE_SIZE;
+ (u8*)card->buffer += CARD_PAGE_SIZE;
+ if (--card->repeat <= 0) {
+ goto error;
+ }
+
+ result = __CARDWritePage(chan, BlockWriteCallback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ if (card->apiCallback == 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ callback = card->xferCallback;
+ if (callback) {
+ card->xferCallback = 0;
+ callback(chan, result);
+ }
+}
+
+s32 __CARDWrite(s32 chan, u32 addr, s32 length, void* dst, CARDCallback callback) {
+ CARDControl* card;
+ card = &__CARDBlock[chan];
+ if (!card->attached) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ card->xferCallback = callback;
+ card->repeat = (int)(length / CARD_PAGE_SIZE);
+ card->addr = addr;
+ card->buffer = dst;
+
+ return __CARDWritePage(chan, BlockWriteCallback);
+}
diff --git a/src/Dolphin/card/CARDRead.c b/src/Dolphin/card/CARDRead.c
new file mode 100644
index 0000000..ffffc5f
--- /dev/null
+++ b/src/Dolphin/card/CARDRead.c
@@ -0,0 +1,141 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+s32 __CARDSeek(CARDFileInfo* fileInfo, s32 length, s32 offset, CARDControl** pcard) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ u16* fat;
+
+ result = __CARDGetControlBlock(fileInfo->chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock) ||
+ card->cBlock * card->sectorSize <= fileInfo->offset) {
+ return __CARDPutControlBlock(card, CARD_RESULT_FATAL_ERROR);
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ if (ent->length * card->sectorSize <= offset ||
+ ent->length * card->sectorSize < offset + length) {
+ return __CARDPutControlBlock(card, CARD_RESULT_LIMIT);
+ }
+
+ card->fileInfo = fileInfo;
+ fileInfo->length = length;
+ if (offset < fileInfo->offset) {
+ fileInfo->offset = 0;
+ fileInfo->iBlock = ent->startBlock;
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+ fat = __CARDGetFatBlock(card);
+ while (fileInfo->offset < TRUNC(offset, card->sectorSize)) {
+ fileInfo->offset += card->sectorSize;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ return __CARDPutControlBlock(card, CARD_RESULT_BROKEN);
+ }
+ }
+
+ fileInfo->offset = offset;
+
+ *pcard = card;
+ return CARD_RESULT_READY;
+}
+
+static void ReadCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ CARDFileInfo* fileInfo;
+ s32 length;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ fileInfo = card->fileInfo;
+ if (fileInfo->length < 0) {
+ result = CARD_RESULT_CANCELED;
+ goto error;
+ }
+
+ length = (s32)TRUNC(fileInfo->offset + card->sectorSize, card->sectorSize) - fileInfo->offset;
+ fileInfo->length -= length;
+ if (fileInfo->length <= 0) {
+ goto error;
+ }
+
+ fat = __CARDGetFatBlock(card);
+ fileInfo->offset += length;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ result = CARD_RESULT_BROKEN;
+ goto error;
+ }
+
+ result = __CARDRead(chan, card->sectorSize * (u32)fileInfo->iBlock,
+ (fileInfo->length < card->sectorSize) ? fileInfo->length : card->sectorSize,
+ card->buffer, ReadCallback);
+ if (result < 0) {
+ goto error;
+ }
+
+ return;
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+s32 CARDReadAsync(CARDFileInfo* fileInfo, void* buf, s32 length, s32 offset,
+ CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ if (OFFSET(offset, CARD_SEG_SIZE) != 0 || OFFSET(length, CARD_SEG_SIZE) != 0) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ result = __CARDSeek(fileInfo, length, offset, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ result = __CARDAccess(card, ent);
+ if (result == CARD_RESULT_NOPERM) {
+ result = __CARDIsWritable(ent);
+ }
+
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ DCInvalidateRange(buf, (u32)length);
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+
+ offset = (s32)OFFSET(fileInfo->offset, card->sectorSize);
+ length = (length < card->sectorSize - offset) ? length : card->sectorSize - offset;
+ result = __CARDRead(fileInfo->chan, card->sectorSize * (u32)fileInfo->iBlock + offset, length,
+ buf, ReadCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDRename.c b/src/Dolphin/card/CARDRename.c
new file mode 100644
index 0000000..83d1c8a
--- /dev/null
+++ b/src/Dolphin/card/CARDRename.c
@@ -0,0 +1,70 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+s32 CARDRenameAsync(s32 chan, const char* old, const char* new, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+ int fileNo;
+ int newNo;
+ int oldNo;
+
+ if (*old == 0xff || *new == 0xff || *old == 0x00 || *new == 0x00) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ if (CARD_FILENAME_MAX < (u32)strlen(old) || CARD_FILENAME_MAX < (u32)strlen(new)) {
+ return CARD_RESULT_NAMETOOLONG;
+ }
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ newNo = oldNo = -1;
+ dir = __CARDGetDirBlock(card);
+ for (fileNo = 0; fileNo < CARD_MAX_FILE; fileNo++) {
+ ent = &dir[fileNo];
+ if (ent->gameName[0] == 0xff) {
+ continue;
+ }
+
+ if (memcmp(ent->gameName, card->diskID->gameName, sizeof(ent->gameName)) != 0 ||
+ memcmp(ent->company, card->diskID->company, sizeof(ent->company)) != 0) {
+ continue;
+ }
+
+ if (__CARDCompareFileName(ent, old)) {
+ oldNo = fileNo;
+ }
+ if (__CARDCompareFileName(ent, new)) {
+ newNo = fileNo;
+ }
+ }
+
+ if (oldNo == -1) {
+ return __CARDPutControlBlock(card, CARD_RESULT_NOFILE);
+ }
+ if (newNo != -1) {
+ return __CARDPutControlBlock(card, CARD_RESULT_EXIST);
+ }
+
+ ent = &dir[oldNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ strncpy((char*)ent->fileName, new, CARD_FILENAME_MAX);
+
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDStat.c b/src/Dolphin/card/CARDStat.c
new file mode 100644
index 0000000..152d535
--- /dev/null
+++ b/src/Dolphin/card/CARDStat.c
@@ -0,0 +1,144 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void UpdateIconOffsets(CARDDir* ent, CARDStat* stat) {
+ u32 offset;
+ BOOL iconTlut;
+ int i;
+
+ offset = ent->iconAddr;
+ if (offset == 0xffffffff) {
+ stat->bannerFormat = 0;
+ stat->iconFormat = 0;
+ stat->iconSpeed = 0;
+ offset = 0;
+ }
+
+ iconTlut = FALSE;
+ switch (CARDGetBannerFormat(ent)) {
+ case CARD_STAT_BANNER_C8:
+ stat->offsetBanner = offset;
+ offset += CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
+ stat->offsetBannerTlut = offset;
+ offset += 2 * 256;
+ break;
+ case CARD_STAT_BANNER_RGB5A3:
+ stat->offsetBanner = offset;
+ offset += 2 * CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT;
+ stat->offsetBannerTlut = 0xffffffff;
+ break;
+ default:
+ stat->offsetBanner = 0xffffffff;
+ stat->offsetBannerTlut = 0xffffffff;
+ break;
+ }
+ for (i = 0; i < CARD_ICON_MAX; ++i) {
+ switch (CARDGetIconFormat(ent, i)) {
+ case CARD_STAT_ICON_C8:
+ stat->offsetIcon[i] = offset;
+ offset += CARD_ICON_WIDTH * CARD_ICON_HEIGHT;
+ iconTlut = TRUE;
+ break;
+ case CARD_STAT_ICON_RGB5A3:
+ stat->offsetIcon[i] = offset;
+ offset += 2 * CARD_ICON_WIDTH * CARD_ICON_HEIGHT;
+ break;
+ default:
+ stat->offsetIcon[i] = 0xffffffff;
+ break;
+ }
+ }
+ if (iconTlut) {
+ stat->offsetIconTlut = offset;
+ offset += 2 * 256;
+ } else {
+ stat->offsetIconTlut = 0xffffffff;
+ }
+ stat->offsetData = offset;
+}
+
+s32 CARDGetStatus(s32 chan, s32 fileNo, CARDStat* stat) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+ if (result == CARD_RESULT_NOPERM) {
+ result = __CARDIsWritable(ent);
+ }
+
+ if (result >= 0) {
+ memcpy(stat->gameName, ent->gameName, sizeof(stat->gameName));
+ memcpy(stat->company, ent->company, sizeof(stat->company));
+ stat->length = (u32)ent->length * card->sectorSize;
+ memcpy(stat->fileName, ent->fileName, CARD_FILENAME_MAX);
+ stat->time = ent->time;
+
+ stat->bannerFormat = ent->bannerFormat;
+ stat->iconAddr = ent->iconAddr;
+ stat->iconFormat = ent->iconFormat;
+ stat->iconSpeed = ent->iconSpeed;
+ stat->commentAddr = ent->commentAddr;
+
+ UpdateIconOffsets(ent, stat);
+ }
+ return __CARDPutControlBlock(card, result);
+}
+
+s32 CARDSetStatusAsync(s32 chan, s32 fileNo, CARDStat* stat, CARDCallback callback) {
+ CARDControl* card;
+ CARDDir* dir;
+ CARDDir* ent;
+ s32 result;
+
+ if (fileNo < 0 || CARD_MAX_FILE <= fileNo ||
+ (stat->iconAddr != 0xffffffff && CARD_READ_SIZE <= stat->iconAddr) ||
+ (stat->commentAddr != 0xffffffff &&
+ CARD_SYSTEM_BLOCK_SIZE - CARD_COMMENT_SIZE < stat->commentAddr % CARD_SYSTEM_BLOCK_SIZE)) {
+ return CARD_RESULT_FATAL_ERROR;
+ }
+ result = __CARDGetControlBlock(chan, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ ent->bannerFormat = stat->bannerFormat;
+ ent->iconAddr = stat->iconAddr;
+ ent->iconFormat = stat->iconFormat;
+ ent->iconSpeed = stat->iconSpeed;
+ ent->commentAddr = stat->commentAddr;
+ UpdateIconOffsets(ent, stat);
+
+ if (ent->iconAddr == 0xffffffff) {
+ CARDSetIconSpeed(ent, 0, CARD_STAT_SPEED_FAST);
+ }
+
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ result = __CARDUpdateDir(chan, callback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/card/CARDUnlock.c b/src/Dolphin/card/CARDUnlock.c
new file mode 100644
index 0000000..ebe8300
--- /dev/null
+++ b/src/Dolphin/card/CARDUnlock.c
@@ -0,0 +1,396 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void InitCallback(void* task);
+static void DoneCallback(void* task);
+
+static u8 CardData[] ATTRIBUTE_ALIGN(32) = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x02, 0xFF, 0x00, 0x21,
+ 0x13, 0x06, 0x12, 0x03, 0x12, 0x04, 0x13, 0x05, 0x00, 0x92, 0x00, 0xFF, 0x00, 0x88, 0xFF, 0xFF,
+ 0x00, 0x89, 0xFF, 0xFF, 0x00, 0x8A, 0xFF, 0xFF, 0x00, 0x8B, 0xFF, 0xFF, 0x8F, 0x00, 0x02, 0xBF,
+ 0x00, 0x88, 0x16, 0xFC, 0xDC, 0xD1, 0x16, 0xFD, 0x00, 0x00, 0x16, 0xFB, 0x00, 0x01, 0x02, 0xBF,
+ 0x00, 0x8E, 0x25, 0xFF, 0x03, 0x80, 0xFF, 0x00, 0x02, 0x94, 0x00, 0x27, 0x02, 0xBF, 0x00, 0x8E,
+ 0x1F, 0xDF, 0x24, 0xFF, 0x02, 0x40, 0x0F, 0xFF, 0x00, 0x98, 0x04, 0x00, 0x00, 0x9A, 0x00, 0x10,
+ 0x00, 0x99, 0x00, 0x00, 0x8E, 0x00, 0x02, 0xBF, 0x00, 0x94, 0x02, 0xBF, 0x86, 0x44, 0x02, 0xBF,
+ 0x00, 0x88, 0x16, 0xFC, 0xDC, 0xD1, 0x16, 0xFD, 0x00, 0x03, 0x16, 0xFB, 0x00, 0x01, 0x8F, 0x00,
+ 0x02, 0xBF, 0x00, 0x8E, 0x03, 0x80, 0xCD, 0xD1, 0x02, 0x94, 0x00, 0x48, 0x27, 0xFF, 0x03, 0x80,
+ 0x00, 0x01, 0x02, 0x95, 0x00, 0x5A, 0x03, 0x80, 0x00, 0x02, 0x02, 0x95, 0x80, 0x00, 0x02, 0x9F,
+ 0x00, 0x48, 0x00, 0x21, 0x8E, 0x00, 0x02, 0xBF, 0x00, 0x8E, 0x25, 0xFF, 0x02, 0xBF, 0x00, 0x8E,
+ 0x25, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x25, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC5, 0xFF, 0xFF,
+ 0x03, 0x40, 0x0F, 0xFF, 0x1C, 0x9F, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC7, 0xFF, 0xFF, 0x02, 0xBF,
+ 0x00, 0x8E, 0x00, 0xC6, 0xFF, 0xFF, 0x02, 0xBF, 0x00, 0x8E, 0x00, 0xC0, 0xFF, 0xFF, 0x02, 0xBF,
+ 0x00, 0x8E, 0x20, 0xFF, 0x03, 0x40, 0x0F, 0xFF, 0x1F, 0x5F, 0x02, 0xBF, 0x00, 0x8E, 0x21, 0xFF,
+ 0x02, 0xBF, 0x00, 0x8E, 0x23, 0xFF, 0x12, 0x05, 0x12, 0x06, 0x02, 0x9F, 0x80, 0xB5, 0x00, 0x21,
+ 0x27, 0xFC, 0x03, 0xC0, 0x80, 0x00, 0x02, 0x9D, 0x00, 0x88, 0x02, 0xDF, 0x27, 0xFE, 0x03, 0xC0,
+ 0x80, 0x00, 0x02, 0x9C, 0x00, 0x8E, 0x02, 0xDF, 0x2E, 0xCE, 0x2C, 0xCF, 0x00, 0xF8, 0xFF, 0xCD,
+ 0x00, 0xF9, 0xFF, 0xC9, 0x00, 0xFA, 0xFF, 0xCB, 0x26, 0xC9, 0x02, 0xC0, 0x00, 0x04, 0x02, 0x9D,
+ 0x00, 0x9C, 0x02, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+typedef struct DecodeParameters {
+ u8* inputAddr;
+ u32 inputLength;
+ u32 aramAddr;
+ u8* outputAddr;
+} DecodeParameters;
+
+static unsigned long int next = 1;
+
+static int CARDRand(void) {
+ next = next * 1103515245 + 12345;
+ return (int)((unsigned int)(next / 65536) % 32768);
+}
+
+static void CARDSrand(unsigned int seed) { next = seed; }
+
+static u32 GetInitVal(void) {
+ u32 tmp;
+ u32 tick;
+
+ tick = OSGetTick();
+ CARDSrand(tick);
+ tmp = 0x7fec8000;
+ tmp |= CARDRand();
+ tmp &= 0xfffff000;
+ return tmp;
+}
+
+static u32 exnor_1st(u32 data, u32 rshift) {
+ u32 wk;
+ u32 w;
+ u32 i;
+
+ w = data;
+ for (i = 0; i < rshift; i++) {
+ wk = ~(w ^ (w >> 7) ^ (w >> 15) ^ (w >> 23));
+ w = (w >> 1) | ((wk << 30) & 0x40000000);
+ }
+ return w;
+}
+
+static u32 exnor(u32 data, u32 lshift) {
+ u32 wk;
+ u32 w;
+ u32 i;
+
+ w = data;
+ for (i = 0; i < lshift; i++) {
+ // 1bit Left Shift
+ wk = ~(w ^ (w << 7) ^ (w << 15) ^ (w << 23));
+ w = (w << 1) | ((wk >> 30) & 0x00000002);
+ // printf("i=%d, w=%8x\n", i, w);
+ }
+ return w;
+}
+
+static u32 bitrev(u32 data) {
+ u32 wk;
+ u32 i;
+ u32 k = 0;
+ u32 j = 1;
+
+ wk = 0;
+ for (i = 0; i < 32; i++) {
+ if (i > 15) {
+ if (i == 31) {
+ wk |= (((data & (0x01 << 31)) >> 31) & 0x01);
+ } else {
+ wk |= ((data & (0x01 << i)) >> j);
+ j += 2;
+ }
+ } else {
+ wk |= ((data & (0x01 << i)) << (31 - i - k));
+ k++;
+ }
+ }
+ return wk;
+}
+
+#define SEC_AD1(x) ((u8)(((x) >> 29) & 0x03))
+#define SEC_AD2(x) ((u8)(((x) >> 21) & 0xff))
+#define SEC_AD3(x) ((u8)(((x) >> 19) & 0x03))
+#define SEC_BA(x) ((u8)(((x) >> 12) & 0x7f))
+
+static s32 ReadArrayUnlock(s32 chan, u32 data, void* rbuf, s32 rlen, s32 mode) {
+ CARDControl* card;
+ BOOL err;
+ u8 cmd[5];
+
+ card = &__CARDBlock[chan];
+ if (!EXISelect(chan, 0, 4)) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ data &= 0xfffff000;
+ memset(cmd, 0, 5);
+ cmd[0] = 0x52;
+ if (mode == 0) {
+ cmd[1] = SEC_AD1(data);
+ cmd[2] = SEC_AD2(data);
+ cmd[3] = SEC_AD3(data);
+ cmd[4] = SEC_BA(data);
+ } else {
+ cmd[1] = (u8)((data & 0xff000000) >> 24);
+ cmd[2] = (u8)((data & 0x00ff0000) >> 16);
+ }
+
+ err = FALSE;
+ err |= !EXIImmEx(chan, cmd, 5, 1);
+ err |= !EXIImmEx(chan, (u8*)card->workArea + (u32)sizeof(CARDID), card->latency, 1);
+ err |= !EXIImmEx(chan, rbuf, rlen, 0);
+ err |= !EXIDeselect(chan);
+
+ return err ? CARD_RESULT_NOCARD : CARD_RESULT_READY;
+}
+
+// Calculate Dummy Read Length, 4-32Bytes
+static s32 DummyLen(void) {
+ u32 tick;
+ u32 wk;
+ s32 tmp;
+ u32 max;
+
+ wk = 1;
+ max = 0;
+ tick = OSGetTick();
+ CARDSrand(tick);
+
+ tmp = CARDRand();
+ tmp &= 0x0000001f;
+ tmp += 1;
+ while ((tmp < 4) && (max < 10)) {
+ tick = OSGetTick();
+ tmp = (s32)(tick << wk);
+ wk++;
+ if (wk > 16) {
+ wk = 1;
+ }
+ CARDSrand((u32)tmp);
+ tmp = CARDRand();
+ tmp &= 0x0000001f;
+ tmp += 1;
+ max++;
+ }
+ if (tmp < 4) {
+ tmp = 4;
+ }
+
+ return tmp;
+}
+
+s32 __CARDUnlock(s32 chan, u8 flashID[12]) {
+ u32 init_val;
+ u32 data;
+
+ s32 dummy;
+ s32 rlen;
+ u32 rshift;
+
+ u8 fsts;
+ u32 wk, wk1;
+ u32 Ans1 = 0;
+ u32 Ans2 = 0;
+ u32* dp;
+ u8 rbuf[64];
+ u32 para1A = 0;
+ u32 para1B = 0;
+ u32 para2A = 0;
+ u32 para2B = 0;
+
+ CARDControl* card;
+ DSPTaskInfo* task;
+ DecodeParameters* param;
+ u8* input;
+ u8* output;
+
+ card = &__CARDBlock[chan];
+ task = &card->task;
+ param = (DecodeParameters*)card->workArea;
+ input = (u8*)((u8*)param + sizeof(DecodeParameters));
+ input = (u8*)OSRoundUp32B(input);
+ output = input + 32;
+
+ fsts = 0;
+ init_val = GetInitVal();
+
+ dummy = DummyLen();
+ rlen = dummy;
+ if (ReadArrayUnlock(chan, init_val, rbuf, rlen, 0) < 0) {
+ return CARD_RESULT_NOCARD;
+ }
+
+ rshift = (u32)(dummy * 8 + 1);
+ wk = exnor_1st(init_val, rshift);
+ wk1 = ~(wk ^ (wk >> 7) ^ (wk >> 15) ^ (wk >> 23));
+ card->scramble = (wk | ((wk1 << 31) & 0x80000000));
+ card->scramble = bitrev(card->scramble);
+ dummy = DummyLen();
+ rlen = 20 + dummy;
+ data = 0;
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0) {
+ return CARD_RESULT_NOCARD;
+ }
+ dp = (u32*)rbuf;
+ para1A = *dp++;
+ para1B = *dp++;
+ Ans1 = *dp++;
+ para2A = *dp++;
+ para2B = *dp++;
+ para1A = (para1A ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ para1B = (para1B ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ Ans1 ^= card->scramble;
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ para2A = (para2A ^ card->scramble);
+ rshift = 32;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ para2B = (para2B ^ card->scramble);
+ rshift = (u32)(dummy * 8);
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+ rshift = 32 + 1;
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ *(u32*)&input[0] = para2A;
+ *(u32*)&input[4] = para2B;
+
+ param->inputAddr = input;
+ param->inputLength = 8;
+ param->outputAddr = output;
+ param->aramAddr = 0;
+
+ DCFlushRange(input, 8);
+ DCInvalidateRange(output, 4);
+ DCFlushRange(param, sizeof(DecodeParameters));
+
+ task->priority = 255;
+ task->iram_mmem_addr = (u16*)OSPhysicalToCached(CardData);
+ task->iram_length = 0x160;
+ task->iram_addr = 0;
+ task->dsp_init_vector = 0x10;
+ task->init_cb = InitCallback;
+ task->res_cb = NULL;
+ task->done_cb = DoneCallback;
+ task->req_cb = NULL;
+ DSPAddTask(task);
+
+ dp = (u32*)flashID;
+ *dp++ = para1A;
+ *dp++ = para1B;
+ *dp = Ans1;
+
+ return CARD_RESULT_READY;
+}
+
+static void InitCallback(void* _task) {
+ s32 chan;
+ CARDControl* card;
+ DSPTaskInfo* task;
+ DecodeParameters* param;
+
+ task = _task;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if ((DSPTaskInfo*)&card->task == task) {
+ break;
+ }
+ }
+ param = (DecodeParameters*)card->workArea;
+
+ DSPSendMailToDSP(0xff000000);
+ while (DSPCheckMailToDSP())
+ ;
+
+ DSPSendMailToDSP((u32)param);
+ while (DSPCheckMailToDSP())
+ ;
+}
+
+static void DoneCallback(void* _task) {
+ u8 rbuf[64];
+ u32 data;
+ s32 dummy;
+ s32 rlen;
+ u32 rshift;
+
+ u8 unk;
+ u32 wk, wk1;
+ u32 Ans2;
+
+ s32 chan;
+ CARDControl* card;
+ s32 result;
+ DSPTaskInfo* task;
+ DecodeParameters* param;
+
+ u8* input;
+ u8* output;
+ task = _task;
+ for (chan = 0; chan < 2; ++chan) {
+ card = &__CARDBlock[chan];
+ if ((DSPTaskInfo*)&card->task == task) {
+ break;
+ }
+ }
+
+ param = (DecodeParameters*)card->workArea;
+ input = (u8*)((u8*)param + sizeof(DecodeParameters));
+ input = (u8*)OSRoundUp32B(input);
+ output = input + 32;
+
+ Ans2 = *(u32*)output;
+ dummy = DummyLen();
+ rlen = dummy;
+ data = ((Ans2 ^ card->scramble) & 0xffff0000);
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+
+ rshift = (u32)((dummy + 4 + card->latency) * 8 + 1);
+ wk = exnor(card->scramble, rshift);
+ wk1 = ~(wk ^ (wk << 7) ^ (wk << 15) ^ (wk << 23));
+ card->scramble = (wk | ((wk1 >> 31) & 0x00000001));
+
+ dummy = DummyLen();
+ rlen = dummy;
+ data = (((Ans2 << 16) ^ card->scramble) & 0xffff0000);
+ if (ReadArrayUnlock(chan, data, rbuf, rlen, 1) < 0) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+ result = __CARDReadStatus(chan, &unk);
+ if (!EXIProbe(chan)) {
+ EXIUnlock(chan);
+ __CARDMountCallback(chan, CARD_RESULT_NOCARD);
+ return;
+ }
+ if (result == CARD_RESULT_READY && !(unk & 0x40)) {
+ EXIUnlock(chan);
+ result = CARD_RESULT_IOERROR;
+ }
+ __CARDMountCallback(chan, result);
+}
diff --git a/src/Dolphin/card/CARDWrite.c b/src/Dolphin/card/CARDWrite.c
new file mode 100644
index 0000000..69a11b3
--- /dev/null
+++ b/src/Dolphin/card/CARDWrite.c
@@ -0,0 +1,117 @@
+#include <dolphin/card.h>
+#include <dolphin/dsp.h>
+#include <dolphin/dvd.h>
+#include <dolphin/os.h>
+
+#include <dolphin/CARDPriv.h>
+
+static void EraseCallback(s32 chan, s32 result);
+
+static void WriteCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ u16* fat;
+ CARDDir* dir;
+ CARDDir* ent;
+ CARDFileInfo* fileInfo;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ fileInfo = card->fileInfo;
+ if (fileInfo->length < 0) {
+ result = CARD_RESULT_CANCELED;
+ goto error;
+ }
+
+ fileInfo->length -= card->sectorSize;
+ if (fileInfo->length <= 0) {
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ ent->time = (u32)OSTicksToSeconds(OSGetTime());
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ result = __CARDUpdateDir(chan, callback);
+ } else {
+ fat = __CARDGetFatBlock(card);
+ fileInfo->offset += card->sectorSize;
+ fileInfo->iBlock = fat[fileInfo->iBlock];
+ if (!CARDIsValidBlockNo(card, fileInfo->iBlock)) {
+ result = CARD_RESULT_BROKEN;
+ goto error;
+ }
+ result = __CARDEraseSector(chan, card->sectorSize * (u32)fileInfo->iBlock, EraseCallback);
+ }
+
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+static void EraseCallback(s32 chan, s32 result) {
+ CARDControl* card;
+ CARDCallback callback;
+ CARDFileInfo* fileInfo;
+
+ card = &__CARDBlock[chan];
+ if (result < 0) {
+ goto error;
+ }
+
+ fileInfo = card->fileInfo;
+ result = __CARDWrite(chan, card->sectorSize * (u32)fileInfo->iBlock, card->sectorSize,
+ card->buffer, WriteCallback);
+ if (result < 0) {
+ goto error;
+ }
+ return;
+
+error:
+ callback = card->apiCallback;
+ card->apiCallback = 0;
+ __CARDPutControlBlock(card, result);
+ callback(chan, result);
+}
+
+s32 CARDWriteAsync(CARDFileInfo* fileInfo, const void* buf, s32 length, s32 offset,
+ CARDCallback callback) {
+ CARDControl* card;
+ s32 result;
+ CARDDir* dir;
+ CARDDir* ent;
+
+ result = __CARDSeek(fileInfo, length, offset, &card);
+ if (result < 0) {
+ return result;
+ }
+
+ if (OFFSET(offset, card->sectorSize) != 0 || OFFSET(length, card->sectorSize) != 0) {
+ return __CARDPutControlBlock(card, CARD_RESULT_FATAL_ERROR);
+ }
+
+ dir = __CARDGetDirBlock(card);
+ ent = &dir[fileInfo->fileNo];
+ result = __CARDAccess(card, ent);
+ if (result < 0) {
+ return __CARDPutControlBlock(card, result);
+ }
+
+ DCStoreRange((void*)buf, (u32)length);
+ card->apiCallback = callback ? callback : __CARDDefaultApiCallback;
+ card->buffer = (void*)buf;
+ result =
+ __CARDEraseSector(fileInfo->chan, card->sectorSize * (u32)fileInfo->iBlock, EraseCallback);
+ if (result < 0) {
+ __CARDPutControlBlock(card, result);
+ }
+ return result;
+}
diff --git a/src/Dolphin/db.c b/src/Dolphin/db.c
new file mode 100644
index 0000000..bcf8534
--- /dev/null
+++ b/src/Dolphin/db.c
@@ -0,0 +1,43 @@
+#include <dolphin/db.h>
+#include <dolphin/os.h>
+
+DBInterface* __DBInterface = NULL;
+int DBVerbose;
+
+extern void __DBExceptionStart();
+extern void __DBExceptionEnd();
+extern void __DBExceptionSetNumber();
+
+void DBInit(void) {
+ __DBInterface = (DBInterface*)OSPhysicalToCached(OS_DBINTERFACE_ADDR);
+ __DBInterface->ExceptionDestination = (void (*)())OSCachedToPhysical(__DBExceptionDestination);
+ DBVerbose = TRUE;
+}
+
+void __DBExceptionDestinationAux(void) {
+ u32* contextAddr = (void*)0x00C0;
+ OSContext* context = (OSContext*)OSPhysicalToCached(*contextAddr);
+
+ OSReport("DBExceptionDestination\n");
+ OSDumpContext(context);
+ PPCHalt();
+}
+
+/* clang-format off */
+asm void __DBExceptionDestination(void) {
+ nofralloc
+ mfmsr r3
+ ori r3, r3, 0x10|0x20
+ mtmsr r3
+
+ b __DBExceptionDestinationAux
+}
+/* clang-format on */
+
+BOOL __DBIsExceptionMarked(__OSException exception) {
+ u32 mask = 1 << exception;
+
+ return (BOOL)(__DBInterface->exceptionMask & mask);
+}
+
+void DBPrintf(char* format, ...) {}
diff --git a/src/Dolphin/dsp/dsp.c b/src/Dolphin/dsp/dsp.c
new file mode 100644
index 0000000..cab7a39
--- /dev/null
+++ b/src/Dolphin/dsp/dsp.c
@@ -0,0 +1,145 @@
+#include "dolphin/dsp.h"
+#include "dolphin/os.h"
+
+#include "dolphin/dsp_regs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static const char* __DSPVersion =
+ "<< Dolphin SDK - DSP\trelease build: Sep 5 2002 05:35:13 (0x2301) >>";
+
+static s32 __DSP_init_flag = 0;
+extern DSPTaskInfo* __DSP_tmp_task;
+extern DSPTaskInfo* __DSP_last_task;
+extern DSPTaskInfo* __DSP_first_task;
+extern DSPTaskInfo* __DSP_curr_task;
+
+extern void __DSPHandler(__OSInterrupt, OSContext*);
+extern void __DSP_debug_printf(const char* fmt, ...);
+extern void __DSP_boot_task(DSPTaskInfo* task);
+
+u32 DSPCheckMailToDSP(void) { return (__DSPRegs[0] >> 0xF) & 1; }
+
+u32 DSPCheckMailFromDSP(void) { return (__DSPRegs[2] >> 0xF) & 1; }
+
+u32 DSPReadMailFromDSP() {
+ u16 reg1;
+ u16 reg2;
+ reg1 = __DSPRegs[2];
+ reg2 = __DSPRegs[3];
+ return reg1 << 16 | reg2;
+}
+
+void DSPSendMailToDSP(u32 mail) {
+ __DSPRegs[0] = mail >> 16;
+ __DSPRegs[1] = mail;
+}
+
+void DSPInit(void) {
+ u32 oldInt;
+ u16 reg;
+ __DSP_debug_printf("DSPInit(): Build Date: %s %s\n", "Sep 5 2002", "05:35:13");
+
+ if (__DSP_init_flag == 1) {
+ return;
+ }
+ OSRegisterVersion(__DSPVersion);
+ oldInt = OSDisableInterrupts();
+ __OSSetInterruptHandler(7, __DSPHandler);
+ __OSUnmaskInterrupts(0x1000000);
+ reg = __DSPRegs[5];
+ reg = (reg & ~0xA8) | 0x800;
+ __DSPRegs[5] = reg;
+ reg = __DSPRegs[5];
+ reg = reg & ~0xAC;
+ __DSPRegs[5] = reg;
+ __DSP_tmp_task = 0;
+ __DSP_curr_task = 0;
+ __DSP_last_task = 0;
+ __DSP_first_task = 0;
+ __DSP_init_flag = 1;
+ OSRestoreInterrupts(oldInt);
+}
+
+void DSPReset(void) {
+ u16 reg;
+ u32 oldInt;
+ oldInt = OSDisableInterrupts();
+ reg = __DSPRegs[5];
+ __DSPRegs[5] = (reg & ~0xA8) | 0x801;
+ __DSP_init_flag = 0;
+ OSRestoreInterrupts(oldInt);
+}
+
+void DSPHalt(void) {
+ u16 reg;
+ u32 oldInt;
+ oldInt = OSDisableInterrupts();
+ reg = __DSPRegs[5];
+ __DSPRegs[5] = (reg & ~0xA8) | 4;
+ OSRestoreInterrupts(oldInt);
+}
+
+u32 DSPGetDMAStatus(void) { return __DSPRegs[5] & 0x200; }
+
+#ifdef FULL_FRANK
+DSPTaskInfo* DSPAddTask(DSPTaskInfo* task) {
+ u32 oldInt;
+ oldInt = OSDisableInterrupts();
+ __DSP_insert_task(task);
+ task->state = 0;
+ task->flags = 1;
+ OSRestoreInterrupts(oldInt);
+ if (task == __DSP_first_task) {
+ __DSP_boot_task(task);
+ }
+
+ return task;
+}
+#else
+#pragma push
+#include "__ppc_eabi_linker.h"
+/* clang-format off */
+#pragma optimization_level 0
+#pragma optimizewithasm off
+extern void __DSP_insert_task(DSPTaskInfo* task);
+asm DSPTaskInfo* DSPAddTask(DSPTaskInfo* task) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ mr r30, r3
+ bl OSDisableInterrupts
+ addi r31, r3, 0
+ addi r3, r30, 0
+ bl __DSP_insert_task
+ li r0, 0
+ stw r0, 0(r30)
+ li r0, 1
+ addi r3, r31, 0
+ stw r0, 8(r30)
+ bl OSRestoreInterrupts
+ lwz r0, __DSP_first_task
+ cmplw r30, r0
+ bne lbl_8036FBB4
+ mr r3, r30
+ bl __DSP_boot_task
+lbl_8036FBB4:
+ mr r3, r30
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+#endif
+/* clang-format on */
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/Dolphin/dsp/dsp_debug.c b/src/Dolphin/dsp/dsp_debug.c
new file mode 100644
index 0000000..22455a3
--- /dev/null
+++ b/src/Dolphin/dsp/dsp_debug.c
@@ -0,0 +1,5 @@
+#include "types.h"
+
+void __DSP_debug_printf(const char* fmt, ...) {
+ // UNUSED(fmt);
+}
diff --git a/src/Dolphin/dsp/dsp_task.c b/src/Dolphin/dsp/dsp_task.c
new file mode 100644
index 0000000..2fe7f15
--- /dev/null
+++ b/src/Dolphin/dsp/dsp_task.c
@@ -0,0 +1,389 @@
+#include "dolphin/dsp.h"
+#include "dolphin/dsp_regs.h"
+
+DSPTaskInfo* __DSP_curr_task;
+DSPTaskInfo* __DSP_first_task;
+DSPTaskInfo* __DSP_last_task;
+DSPTaskInfo* __DSP_tmp_task;
+DSPTaskInfo* __DSP_rude_task;
+
+BOOL __DSP_rude_task_pending;
+
+void __DSPHandler(__OSInterrupt, OSContext* context) {
+ DSPTaskInfo* tmp_task;
+ OSContext exceptionContext;
+ u16 tmp;
+ u32 mail;
+
+ tmp = __DSPRegs[5];
+ tmp = (u16)(tmp & ~0x28) | 0x80;
+ __DSPRegs[5] = tmp;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ while (!DSPCheckMailFromDSP())
+ ;
+ mail = DSPReadMailFromDSP();
+
+ if ((__DSP_curr_task->flags & DSP_TASK_FLAG_CANCEL) && (mail == 0xDCD10002)) {
+ mail = 0xDCD10003;
+ }
+
+ switch (mail) {
+ case 0xDCD10000:
+ __DSP_curr_task->state = DSP_TASK_STATE_RUN;
+
+ if (__DSP_curr_task->init_cb) {
+ (*(__DSP_curr_task->init_cb))((void*)(__DSP_curr_task));
+ }
+ break;
+ case 0xDCD10001:
+ __DSP_curr_task->state = DSP_TASK_STATE_RUN;
+ if (__DSP_curr_task->res_cb) {
+ (*(__DSP_curr_task->res_cb))((void*)(__DSP_curr_task));
+ }
+ break;
+ case 0xDCD10002:
+ if (__DSP_rude_task_pending) {
+
+ if (__DSP_curr_task == __DSP_rude_task) {
+ DSPSendMailToDSP(0xCDD10003);
+ while (DSPCheckMailToDSP()) {
+ }
+
+ __DSP_rude_task = NULL;
+ __DSP_rude_task_pending = FALSE;
+
+ if (__DSP_curr_task->res_cb) {
+ (*(__DSP_curr_task->res_cb))((void*)(__DSP_curr_task));
+ }
+
+ break;
+ } else {
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP())
+ ;
+ __DSP_exec_task(__DSP_curr_task, __DSP_rude_task);
+
+ __DSP_curr_task->state = DSP_TASK_STATE_YIELD;
+ __DSP_curr_task = __DSP_rude_task;
+
+ __DSP_rude_task = NULL;
+ __DSP_rude_task_pending = FALSE;
+
+ break;
+ }
+ }
+
+ if (__DSP_curr_task->next == NULL) {
+
+ if (__DSP_curr_task == __DSP_first_task) {
+
+ DSPSendMailToDSP(0xCDD10003);
+ while (DSPCheckMailToDSP())
+ ;
+
+ if (__DSP_curr_task->res_cb) {
+ (*(__DSP_curr_task->res_cb))((void*)(__DSP_curr_task));
+ }
+
+ } else {
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP()) {
+ }
+
+ __DSP_exec_task(__DSP_curr_task, __DSP_first_task);
+
+ __DSP_curr_task->state = DSP_TASK_STATE_YIELD;
+ __DSP_curr_task = __DSP_first_task;
+ }
+
+ } else {
+
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP()) {
+ }
+
+ __DSP_exec_task(__DSP_curr_task, __DSP_curr_task->next);
+
+ __DSP_curr_task->state = DSP_TASK_STATE_YIELD;
+ __DSP_curr_task = __DSP_curr_task->next;
+ }
+ break;
+ case 0xDCD10003:
+ if (__DSP_rude_task_pending) {
+
+ if (__DSP_curr_task->done_cb) {
+ (*(__DSP_curr_task->done_cb))((void*)(__DSP_curr_task));
+ }
+
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP())
+ ;
+
+ __DSP_exec_task(NULL, __DSP_rude_task);
+
+ __DSP_remove_task(__DSP_curr_task);
+ __DSP_curr_task = __DSP_rude_task;
+
+ __DSP_rude_task = NULL;
+ __DSP_rude_task_pending = FALSE;
+
+ break;
+ }
+
+ if (__DSP_curr_task->next == NULL) {
+
+ if (__DSP_curr_task == __DSP_first_task) {
+
+ if (__DSP_curr_task->done_cb) {
+ (*(__DSP_curr_task->done_cb))((void*)(__DSP_curr_task));
+ }
+
+ DSPSendMailToDSP(0xCDD10002);
+ while (DSPCheckMailToDSP())
+ ;
+
+ __DSP_curr_task->state = DSP_TASK_STATE_DONE;
+
+ __DSP_remove_task(__DSP_curr_task);
+
+ } else {
+
+ if (__DSP_curr_task->done_cb) {
+ (*(__DSP_curr_task->done_cb))((void*)(__DSP_curr_task));
+ }
+
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP())
+ ;
+
+ __DSP_curr_task->state = DSP_TASK_STATE_DONE;
+ __DSP_exec_task(NULL, __DSP_first_task);
+
+ __DSP_curr_task = __DSP_first_task;
+ __DSP_remove_task(__DSP_last_task);
+ }
+
+ } else {
+ if (__DSP_curr_task->done_cb) {
+ (*(__DSP_curr_task->done_cb))((void*)(__DSP_curr_task));
+ }
+ DSPSendMailToDSP(0xCDD10001);
+ while (DSPCheckMailToDSP())
+ ;
+
+ __DSP_curr_task->state = DSP_TASK_STATE_DONE;
+ __DSP_exec_task(NULL, __DSP_curr_task->next);
+
+ __DSP_curr_task = __DSP_curr_task->next;
+ __DSP_remove_task(__DSP_curr_task->prev);
+ }
+ break;
+
+ case 0xDCD10004:
+
+ if (__DSP_curr_task->req_cb) {
+ (*(__DSP_curr_task->req_cb))((void*)(__DSP_curr_task));
+ }
+ break;
+ default:
+ break;
+ }
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+}
+
+void __DSP_exec_task(DSPTaskInfo* curr, DSPTaskInfo* next) {
+ if (curr) {
+ DSPSendMailToDSP((u32)(curr->dram_mmem_addr));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(curr->dram_length));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(curr->dram_addr));
+ while (DSPCheckMailToDSP())
+ ;
+ } else {
+
+ DSPSendMailToDSP((u32)(0));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(0));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(0));
+ while (DSPCheckMailToDSP())
+ ;
+ }
+
+ DSPSendMailToDSP((u32)(next->iram_mmem_addr));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(next->iram_length));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(next->iram_addr));
+ while (DSPCheckMailToDSP())
+ ;
+
+ if (DSP_TASK_STATE_INIT == next->state) {
+ DSPSendMailToDSP((u32)(next->dsp_init_vector));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(0));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(0));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(0));
+ while (DSPCheckMailToDSP())
+ ;
+ } else {
+ DSPSendMailToDSP((u32)(next->dsp_resume_vector));
+ while (DSPCheckMailToDSP())
+ ;
+ DSPSendMailToDSP((u32)(next->dram_mmem_addr));
+ while (DSPCheckMailToDSP())
+ ;
+
+ DSPSendMailToDSP((u32)(next->dram_length));
+ while (DSPCheckMailToDSP())
+ ;
+
+ DSPSendMailToDSP((u32)(next->dram_addr));
+ while (DSPCheckMailToDSP())
+ ;
+ }
+}
+
+
+#define MSG_BASE 0x80F30000
+void __DSP_boot_task(DSPTaskInfo* task) {
+
+ volatile u32 mail;
+
+ while (!DSPCheckMailFromDSP())
+ ;
+
+ mail = DSPReadMailFromDSP();
+
+ DSPSendMailToDSP(MSG_BASE | 0xA001);
+ while (DSPCheckMailToDSP()) {
+ }
+ DSPSendMailToDSP((u32)(task->iram_mmem_addr));
+ while (DSPCheckMailToDSP()) {
+ }
+
+ DSPSendMailToDSP(MSG_BASE | 0xC002);
+ while (DSPCheckMailToDSP()) {
+ }
+ DSPSendMailToDSP((u32)(task->iram_addr & 0xffff));
+ while (DSPCheckMailToDSP()) {
+ }
+
+ DSPSendMailToDSP(MSG_BASE | 0xA002);
+ while (DSPCheckMailToDSP()) {
+ }
+ DSPSendMailToDSP(task->iram_length);
+ while (DSPCheckMailToDSP()) {
+ }
+
+ DSPSendMailToDSP(MSG_BASE | 0xB002);
+ while (DSPCheckMailToDSP()) {
+ }
+ DSPSendMailToDSP(0x00000000);
+ while (DSPCheckMailToDSP()) {
+ }
+
+ DSPSendMailToDSP(MSG_BASE | 0xD001);
+ while (DSPCheckMailToDSP()) {
+ }
+ DSPSendMailToDSP((u32)(0xffff & task->dsp_init_vector));
+ while (DSPCheckMailToDSP()) {
+ }
+
+ __DSP_debug_printf("DSP is booting task: 0x%08X\n", task);
+ __DSP_debug_printf("__DSP_boot_task() : IRAM MMEM ADDR: 0x%08X\n", (u32)(task->iram_mmem_addr));
+ __DSP_debug_printf("__DSP_boot_task() : IRAM DSP ADDR : 0x%08X\n", (u32)(task->iram_addr));
+ __DSP_debug_printf("__DSP_boot_task() : IRAM LENGTH : 0x%08X\n", (u32)(task->iram_length));
+ __DSP_debug_printf("__DSP_boot_task() : DRAM MMEM ADDR: 0x%08X\n", (u32)(task->dram_length));
+ __DSP_debug_printf("__DSP_boot_task() : Start Vector : 0x%08X\n", (u32)(task->dsp_init_vector));
+}
+
+void __DSP_insert_task(DSPTaskInfo* task) {
+
+ DSPTaskInfo* temp;
+
+ if (__DSP_first_task == NULL) {
+ __DSP_first_task = __DSP_last_task = __DSP_curr_task = task;
+ task->next = task->prev = NULL;
+ } else {
+ temp = __DSP_first_task;
+
+ while (temp) {
+ if (task->priority < temp->priority) {
+ task->prev = temp->prev;
+ temp->prev = task;
+ task->next = temp;
+ if (task->prev == NULL) {
+ __DSP_first_task = task;
+ } else {
+ (task->prev)->next = task;
+ }
+ break;
+ }
+ temp = temp->next;
+ }
+
+ if (temp == NULL) {
+ __DSP_last_task->next = task;
+ task->next = NULL;
+ task->prev = __DSP_last_task;
+ __DSP_last_task = task;
+ }
+ }
+}
+
+void __DSP_add_task(DSPTaskInfo* task) {
+ if (__DSP_last_task == NULL) {
+ __DSP_first_task = __DSP_last_task = __DSP_curr_task = task;
+ task->next = task->prev = NULL;
+ } else {
+ __DSP_last_task->next = task;
+ task->next = NULL;
+ task->prev = __DSP_last_task;
+ __DSP_last_task = task;
+ }
+
+ task->state = DSP_TASK_STATE_INIT;
+
+ __DSP_debug_printf("__DSP_add_task() : Added task : 0x%08X\n", task);
+}
+
+void __DSP_remove_task(DSPTaskInfo* task) {
+
+ task->flags = DSP_TASK_FLAG_CLEARALL;
+ task->state = DSP_TASK_STATE_DONE;
+
+ if (__DSP_first_task == task) {
+ if (task->next) {
+ __DSP_first_task = (task->next);
+ task->next->prev = NULL;
+ } else {
+ __DSP_first_task = __DSP_last_task = __DSP_curr_task = NULL;
+ }
+ } else if (__DSP_last_task == task) {
+ __DSP_last_task = (task->prev);
+ task->prev->next = NULL;
+ __DSP_curr_task = __DSP_first_task;
+
+ } else {
+ __DSP_curr_task = task->next;
+ task->prev->next = task->next;
+ task->next->prev = task->prev;
+ }
+}
diff --git a/src/Dolphin/dtk.c b/src/Dolphin/dtk.c
new file mode 100644
index 0000000..b59a850
--- /dev/null
+++ b/src/Dolphin/dtk.c
@@ -0,0 +1,357 @@
+#include <dolphin/ai.h>
+#include <dolphin/dtk.h>
+
+static DVDCommandBlock __block_for_run_callback;
+static DVDCommandBlock __block_for_prep_callback;
+static DVDCommandBlock __block_for_stream_status;
+static DVDCommandBlock __block_for_ais_isr;
+static DVDCommandBlock __block_for_flushtracks;
+static DVDCommandBlock __block_for_set_state;
+static DVDCommandBlock __block_for_next_track;
+
+static DTKTrack* __DTKCurrentTrack;
+static DTKTrack* __DTKPlayListHead;
+static DTKTrack* __DTKPlayListTail;
+static vu32 __DTKState;
+static vu32 __DTKTempState;
+static vu32 __DTKRepeatMode;
+static vu32 __DTKPosition;
+static vu32 __DTKInterruptFrequency;
+static vu8 __DTKVolumeL;
+static vu8 __DTKVolumeR;
+static volatile u32 __DTKShutdownFlag;
+static volatile u32 __DTKTrackEnded;
+static DTKFlushCallback __DTKFlushCallback;
+
+static void __DTKStartAi() {
+ AISetStreamVolLeft(__DTKVolumeL);
+ AISetStreamVolRight(__DTKVolumeR);
+ AIResetStreamSampleCount();
+ AISetStreamTrigger(__DTKInterruptFrequency);
+ AISetStreamPlayState(1);
+}
+
+static void __DTKStopAi() {
+ AISetStreamVolLeft(0);
+ AISetStreamVolRight(0);
+ AISetStreamPlayState(0);
+}
+
+static void __DTKCheckUserCallback(DTKTrack* track, u32 event) {
+ if (track == NULL) {
+ return;
+ }
+
+ if (track->callback != NULL && (track->eventMask & event) != 0) {
+ track->callback(track->eventMask & event);
+ }
+}
+
+static void __DTKForward() {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+ if (__DTKCurrentTrack != NULL && __DTKCurrentTrack->next != nullptr) {
+ __DTKCurrentTrack = __DTKCurrentTrack->next;
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+static void __DTKBackward() {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+ if (__DTKCurrentTrack != NULL && __DTKCurrentTrack->prev != nullptr) {
+ __DTKCurrentTrack = __DTKCurrentTrack->prev;
+ }
+
+ OSRestoreInterrupts(enabled);
+}
+
+static void __DTKCallbackForStreamStatus(s32 result, DVDCommandBlock* block) {
+ if ((u8)result) {
+ return;
+ }
+ __DTKTrackEnded = TRUE;
+ __DTKPosition = 0;
+}
+
+static void __DTKCallbackForRun(s32 result, DVDFileInfo* fileInfo) {
+ __DTKStartAi();
+ DVDStopStreamAtEndAsync(&__block_for_run_callback, NULL);
+ __DTKState = 1;
+ __DTKCheckUserCallback(__DTKCurrentTrack, 1);
+}
+
+static void __DTKCallbackForPreparePaused(s32 result, DVDFileInfo* fileInfo) {
+ __DTKStopAi();
+ DVDStopStreamAtEndAsync(&__block_for_prep_callback, NULL);
+ __DTKState = 2;
+ __DTKCheckUserCallback(__DTKCurrentTrack, 32);
+}
+
+static void __DTKPrepareCurrentTrack(DVDCallback callback) {
+ DVDPrepareStreamAsync(&__DTKCurrentTrack->dvdFileInfo, 0, 0, callback);
+}
+static void __DTKCallbackForPlaylist(s32 result, DVDCommandBlock* block) {
+ __DTKPosition = result;
+
+ if (__DTKTrackEnded != FALSE) {
+ __DTKTrackEnded = FALSE;
+ __DTKCheckUserCallback(__DTKCurrentTrack, 16);
+ __DTKState = 3;
+ switch (__DTKRepeatMode) {
+ case 0:
+ if (__DTKCurrentTrack == NULL) {
+ break;
+ }
+
+ if (__DTKCurrentTrack->next != NULL) {
+ __DTKCurrentTrack = __DTKCurrentTrack->next;
+ __DTKStopAi();
+ __DTKPrepareCurrentTrack(__DTKCallbackForRun);
+ } else {
+ __DTKCurrentTrack = __DTKPlayListHead;
+ __DTKStopAi();
+ __DTKState = 0;
+ }
+ break;
+ case 1: {
+ if (__DTKCurrentTrack == NULL) {
+ break;
+ }
+ if (__DTKCurrentTrack->next != NULL) {
+ __DTKCurrentTrack = __DTKCurrentTrack->next;
+ __DTKStopAi();
+ __DTKPrepareCurrentTrack(__DTKCallbackForRun);
+ } else {
+ __DTKCurrentTrack = __DTKPlayListHead;
+ __DTKStopAi();
+ __DTKPrepareCurrentTrack(__DTKCallbackForRun);
+ }
+ break;
+ }
+ case 2: {
+ if (__DTKCurrentTrack == NULL) {
+ break;
+ }
+
+ __DTKStopAi();
+ __DTKPrepareCurrentTrack(__DTKCallbackForRun);
+ break;
+ }
+ }
+ return;
+ }
+
+ DVDGetStreamErrorStatusAsync(&__block_for_stream_status, __DTKCallbackForStreamStatus);
+}
+
+static void __DTKCallbackForAIInterrupt(u32 result) {
+ AISetStreamTrigger(result + __DTKInterruptFrequency);
+
+ if (__DTKCurrentTrack != NULL) {
+ DVDGetStreamPlayAddrAsync(&__block_for_ais_isr, __DTKCallbackForPlaylist);
+ }
+}
+
+static void __DTKCallbackForFlush(s32 result, DVDCommandBlock* block) {
+ DTKTrack* iter;
+ AISetStreamPlayState(0);
+
+ for (iter = __DTKPlayListHead; iter != NULL; iter = iter->next) {
+ DVDClose(&iter->dvdFileInfo);
+ }
+
+ __DTKPlayListHead = NULL;
+ __DTKPlayListTail = NULL;
+ __DTKCurrentTrack = NULL;
+ __DTKState = 0;
+ if (__DTKFlushCallback != NULL) {
+ __DTKFlushCallback();
+ __DTKFlushCallback = NULL;
+ }
+
+ __DTKState = 0;
+ __DTKShutdownFlag = 0;
+}
+
+static void __DTKCallbackForStop() {
+ __DTKCheckUserCallback(__DTKCurrentTrack, 2);
+ __DTKState = 0;
+}
+
+static void __DTKCallbackForNextTrack() {
+ AISetStreamPlayState(0);
+ __DTKForward();
+ __DTKState = 0;
+ DTKSetState(__DTKTempState);
+}
+
+static void __DTKCallbackForPrevTrack() {
+ AISetStreamPlayState(0);
+ __DTKBackward();
+ __DTKState = 0;
+ DTKSetState(__DTKTempState);
+}
+
+void DTKInit() {
+ __DTKCurrentTrack = NULL;
+ __DTKPlayListHead = NULL;
+ __DTKPlayListTail = NULL;
+ __DTKState = 0;
+ __DTKRepeatMode = 0;
+ __DTKPosition = 0;
+ __DTKInterruptFrequency = 48000;
+ __DTKVolumeL = 255;
+ __DTKVolumeR = 255;
+ AISetStreamVolLeft(0);
+ AISetStreamVolRight(0);
+ AIRegisterStreamCallback(__DTKCallbackForAIInterrupt);
+ AIResetStreamSampleCount();
+ AISetStreamPlayState(0);
+}
+
+u32 DTKQueueTrack(char* fileName, DTKTrack* track, u32 eventMask, DTKCallback callback) {
+ BOOL enabled;
+ u32 prepareTrack = FALSE;
+ if (DVDOpen(fileName, &track->dvdFileInfo) == 0) {
+ return 1;
+ }
+
+ enabled = OSDisableInterrupts();
+ track->fileName = fileName;
+ track->eventMask = eventMask;
+ track->callback = callback;
+
+ if (__DTKPlayListHead == NULL) {
+ __DTKPlayListHead = track;
+ __DTKPlayListTail = track;
+ track->prev = NULL;
+ track->next = NULL;
+ if (__DTKState == 1) {
+ prepareTrack = TRUE;
+ }
+ } else {
+ __DTKPlayListTail->next = track;
+ track->prev = __DTKPlayListTail;
+ __DTKPlayListTail = track;
+ track->next = NULL;
+ }
+
+ if (__DTKCurrentTrack == NULL) {
+ __DTKCurrentTrack = track;
+ }
+
+ OSRestoreInterrupts(enabled);
+ __DTKCheckUserCallback(track, 8);
+ if (prepareTrack != 0) {
+ __DTKState = 3;
+ __DTKPrepareCurrentTrack(__DTKCallbackForRun);
+ }
+
+ return 0;
+}
+
+void DTKFlushTracks(DTKFlushCallback callback) {
+ u32 oldState;
+ if (__DTKState != 3) {
+ oldState = __DTKState;
+ __DTKState = 3;
+ __DTKFlushCallback = callback;
+ if (oldState == 1) {
+ DVDCancelStreamAsync(&__block_for_flushtracks, __DTKCallbackForFlush);
+ } else {
+ __DTKCallbackForFlush(0, NULL);
+ }
+ }
+}
+
+void DTKSetSampleRate(u32 rate) {}
+
+void DTKSetRepeatMode(u32 repeat) { __DTKRepeatMode = repeat; }
+
+void DTKSetState(u32 state) {
+ if (__DTKState == state || __DTKState == 3) {
+ return;
+ }
+ switch (state) {
+ case 0: {
+ if (__DTKCurrentTrack == NULL) {
+ break;
+ }
+
+ __DTKState = 3;
+ AISetStreamVolLeft(0);
+ AISetStreamVolRight(0);
+ AISetStreamPlayState(0);
+ DVDCancelStreamAsync(&__block_for_set_state, __DTKCallbackForStop);
+ break;
+ }
+ case 1: {
+ if (__DTKState == 2) {
+ __DTKStartAi();
+ __DTKState = 1;
+ if (__DTKCurrentTrack != NULL) {
+ __DTKCheckUserCallback(__DTKCurrentTrack, 1);
+ }
+ } else if (__DTKCurrentTrack != NULL) {
+ __DTKState = 3;
+ __DTKPrepareCurrentTrack(__DTKCallbackForRun);
+
+ } else {
+ __DTKState = 1;
+ }
+
+ __DTKTrackEnded = 0;
+ break;
+ }
+ case 4: {
+ if (__DTKState != 0) {
+ break;
+ }
+
+ if (__DTKCurrentTrack != NULL) {
+ __DTKState = 3;
+ __DTKPrepareCurrentTrack(__DTKCallbackForPreparePaused);
+ }
+
+ __DTKTrackEnded = FALSE;
+ break;
+ }
+ case 2: {
+ AISetStreamPlayState(0);
+ if (__DTKState == 1) {
+ __DTKState = 2;
+ }
+ __DTKCheckUserCallback(__DTKCurrentTrack, 4);
+ break;
+ }
+ }
+}
+
+void DTKNextTrack() {
+ if (__DTKState != 3 && __DTKCurrentTrack != NULL) {
+ __DTKTempState = __DTKState;
+ __DTKState = 3;
+ if (__DTKTempState == 1) {
+ AISetStreamVolLeft(0);
+ AISetStreamVolRight(0);
+ DVDCancelStreamAsync(&__block_for_next_track, __DTKCallbackForNextTrack);
+
+ } else {
+ __DTKForward();
+ __DTKState = __DTKTempState;
+ }
+ }
+}
+
+u32 DTKGetState() { return __DTKState; }
+
+void DTKSetVolume(u8 left, u8 right) {
+ __DTKVolumeL = left;
+ __DTKVolumeR = right;
+ if (__DTKState == 1) {
+ AISetStreamVolLeft(left);
+ AISetStreamVolRight(right);
+ }
+}
diff --git a/src/Dolphin/dvd/dvd.c b/src/Dolphin/dvd/dvd.c
new file mode 100644
index 0000000..65c67f6
--- /dev/null
+++ b/src/Dolphin/dvd/dvd.c
@@ -0,0 +1,1389 @@
+#include <dolphin/DVDPriv.h>
+#include <dolphin/dvd.h>
+#include <dolphin/dvd_regs.h>
+#include <dolphin/os.h>
+#include <dolphin/os/OSBootInfo.h>
+
+const char* __DVDVersion = "<< Dolphin SDK - DVD\trelease build: Sep 5 2002 05:34:06 (0x2301) >>";
+
+typedef void (*stateFunc)(DVDCommandBlock* block);
+stateFunc LastState;
+
+extern OSThreadQueue __DVDThreadQueue;
+
+static DVDBB2 BB2 ATTRIBUTE_ALIGN(32);
+static DVDDiskID CurrDiskID ATTRIBUTE_ALIGN(32);
+static DVDCommandBlock* executing;
+static DVDDiskID* IDShouldBe;
+static OSBootInfo* bootInfo;
+static BOOL autoInvalidation = TRUE;
+static volatile BOOL PauseFlag = FALSE;
+static volatile BOOL PausingFlag = FALSE;
+static volatile BOOL AutoFinishing = FALSE;
+static volatile BOOL FatalErrorFlag = FALSE;
+static vu32 CurrCommand;
+static vu32 Canceling = FALSE;
+static DVDCBCallback CancelCallback;
+static vu32 ResumeFromHere = 0;
+static vu32 CancelLastError;
+static vu32 LastError;
+static vs32 NumInternalRetry = 0;
+static volatile BOOL ResetRequired;
+static volatile BOOL CancelAllSyncComplete = FALSE;
+static volatile BOOL FirstTimeInBootrom = FALSE;
+
+static DVDCommandBlock DummyCommandBlock;
+static OSAlarm ResetAlarm;
+
+static BOOL DVDInitialized = FALSE;
+
+/* States */
+static void stateReadingFST();
+static void stateTimeout();
+static void stateGettingError();
+static void stateGoToRetry();
+static void stateCheckID();
+static void stateCheckID3();
+static void stateCheckID2a();
+static void stateCheckID2();
+static void stateCoverClosed();
+static void stateCoverClosed_CMD();
+static void stateCoverOpen();
+static void stateMotorStopped();
+static void stateReady();
+static void stateBusy();
+
+/* Callbacks */
+static void cbForStateReadingFST(u32 intType);
+static void cbForStateError(u32 intType);
+static void cbForStateGettingError(u32 intType);
+static void cbForUnrecoveredError(u32 intType);
+static void cbForUnrecoveredErrorRetry(u32 intType);
+static void cbForStateGoToRetry(u32 intType);
+static void cbForStateCheckID2a(u32 intType);
+static void cbForStateCheckID1(u32 intType);
+static void cbForStateCheckID2(u32 intType);
+static void cbForStateCheckID3(u32 intType);
+static void cbForStateCoverClosed(u32 intType);
+static void cbForStateMotorStopped(u32 intType);
+static void cbForStateBusy(u32 intType);
+static void cbForCancelStreamSync(s32 result, DVDCommandBlock* block);
+static void cbForCancelSync(s32 result, DVDCommandBlock* block);
+static void cbForCancelAllSync(s32 result, DVDCommandBlock* block);
+
+static void defaultOptionalCommandChecker(DVDCommandBlock* block, DVDLowCallback cb);
+
+static DVDOptionalCommandChecker checkOptionalCommand = defaultOptionalCommandChecker;
+
+extern void __DVDInterruptHandler(__OSInterrupt interrupt, OSContext* context);
+
+static void defaultOptionalCommandChecker(DVDCommandBlock* block, DVDLowCallback cb) {}
+
+void DVDInit() {
+ if (DVDInitialized) {
+ return;
+ }
+
+ OSRegisterVersion(__DVDVersion);
+ DVDInitialized = TRUE;
+ __DVDFSInit();
+ __DVDClearWaitingQueue();
+ __DVDInitWA();
+ bootInfo = (OSBootInfo*)OSPhysicalToCached(0x0000);
+ IDShouldBe = &(bootInfo->DVDDiskID);
+ __OSSetInterruptHandler(21, __DVDInterruptHandler);
+ __OSUnmaskInterrupts(0x400);
+ OSInitThreadQueue(&__DVDThreadQueue);
+ __DIRegs[0] = 0x2a;
+ __DIRegs[1] = 0;
+ if (bootInfo->magic == 0xE5207C22) {
+ OSReport("load fst\n");
+ __fstLoad();
+ } else if (bootInfo->magic != 0xD15EA5E) {
+ FirstTimeInBootrom = TRUE;
+ }
+}
+
+static void stateReadingFST() {
+ LastState = (stateFunc)stateReadingFST;
+
+ if (bootInfo->FSTMaxLength < BB2.FSTLength) {
+ OSPanic("dvd.c", 630, "DVDChangeDisk(): FST in the new disc is too big. ");
+ }
+
+ DVDLowRead(bootInfo->FSTLocation, OSRoundUp32B(BB2.FSTLength), BB2.FSTPosition,
+ cbForStateReadingFST);
+}
+
+static void cbForStateReadingFST(u32 intType) {
+ DVDCommandBlock* finished;
+
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+ NumInternalRetry = 0;
+ __DVDFSInit();
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+
+ stateReady();
+
+ } else {
+
+ stateGettingError();
+ }
+}
+
+inline static void stateError(u32 error) {
+ __DVDStoreErrorCode(error);
+ DVDLowStopMotor(cbForStateError);
+}
+
+static void cbForStateError(u32 intType) {
+ DVDCommandBlock* finished;
+
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ __DVDPrintFatalMessage();
+
+ FatalErrorFlag = TRUE;
+ finished = executing;
+ executing = &DummyCommandBlock;
+ if (finished->callback) {
+ (finished->callback)(-1, finished);
+ }
+
+ if (Canceling) {
+ Canceling = FALSE;
+ if (CancelCallback)
+ (CancelCallback)(0, finished);
+ }
+
+ stateReady();
+
+ return;
+}
+
+static void stateTimeout() {
+ __DVDStoreErrorCode(0x1234568);
+ DVDReset();
+ cbForStateError(0);
+}
+
+static void stateGettingError() { DVDLowRequestError(cbForStateGettingError); }
+
+static u32 CategorizeError(u32 error) {
+ if (error == 0x20400) {
+ LastError = error;
+ return 1;
+ }
+
+ error &= 0xffffff;
+
+ if ((error == 0x62800) || (error == 0x23a00) || (error == 0xb5a01)) {
+ return 0;
+ }
+
+ ++NumInternalRetry;
+ if (NumInternalRetry == 2) {
+ if (error == LastError) {
+ LastError = error;
+ return 1;
+ } else {
+ LastError = error;
+ return 2;
+ }
+ } else {
+ LastError = error;
+
+ if ((error == 0x31100) || (executing->command == 5)) {
+ return 2;
+ } else {
+ return 3;
+ }
+ }
+}
+
+inline static BOOL CheckCancel(u32 resume) {
+ DVDCommandBlock* finished;
+
+ if (Canceling) {
+ ResumeFromHere = resume;
+ Canceling = FALSE;
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 10;
+ if (finished->callback)
+ (*finished->callback)(-3, finished);
+ if (CancelCallback)
+ (CancelCallback)(0, finished);
+ stateReady();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void cbForStateGettingError(u32 intType) {
+ u32 error;
+ u32 status;
+ u32 errorCategory;
+ u32 resume;
+
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 2) {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+
+ error = __DIRegs[8];
+ status = error & 0xff000000;
+
+ errorCategory = CategorizeError(error);
+
+ if (errorCategory == 1) {
+ executing->state = -1;
+ stateError(error);
+ return;
+ }
+
+ if ((errorCategory == 2) || (errorCategory == 3)) {
+ resume = 0;
+ } else {
+ if (status == 0x01000000)
+ resume = 4;
+ else if (status == 0x02000000)
+ resume = 6;
+ else if (status == 0x03000000)
+ resume = 3;
+ else
+ resume = 5;
+ }
+
+ if (CheckCancel(resume))
+ return;
+
+ if (errorCategory == 2) {
+ __DVDStoreErrorCode(error);
+ stateGoToRetry();
+ return;
+ }
+
+ if (errorCategory == 3) {
+ if ((error & 0x00ffffff) == 0x00031100) {
+ DVDLowSeek(executing->offset, cbForUnrecoveredError);
+ } else {
+ LastState(executing);
+ }
+ return;
+ }
+
+ if (status == 0x01000000) {
+ executing->state = 5;
+ stateMotorStopped();
+ return;
+ } else if (status == 0x02000000) {
+ executing->state = 3;
+ stateCoverClosed();
+ return;
+ } else if (status == 0x03000000) {
+ executing->state = 4;
+ stateMotorStopped();
+ return;
+ } else {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+}
+
+static void cbForUnrecoveredError(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+ stateGoToRetry();
+ return;
+ }
+
+ DVDLowRequestError(cbForUnrecoveredErrorRetry);
+}
+
+static void cbForUnrecoveredErrorRetry(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+ executing->state = -1;
+
+ if (intType & 2) {
+ __DVDStoreErrorCode(0x1234567);
+ DVDLowStopMotor(cbForStateError);
+ return;
+ }
+
+ __DVDStoreErrorCode(__DIRegs[8]);
+ DVDLowStopMotor(cbForStateError);
+}
+
+static void stateGoToRetry() { DVDLowStopMotor(cbForStateGoToRetry); }
+
+static void cbForStateGoToRetry(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 2) {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+
+ NumInternalRetry = 0;
+
+ if ((CurrCommand == 4) || (CurrCommand == 5) || (CurrCommand == 13) || (CurrCommand == 15)) {
+ ResetRequired = TRUE;
+ }
+
+ if (!CheckCancel(2)) {
+ executing->state = 11;
+ stateMotorStopped();
+ }
+}
+
+static void stateCheckID() {
+ switch (CurrCommand) {
+ case 3:
+ if (DVDCompareDiskID(&CurrDiskID, executing->id)) {
+ memcpy(IDShouldBe, &CurrDiskID, sizeof(DVDDiskID));
+
+ executing->state = 1;
+ DCInvalidateRange(&BB2, sizeof(DVDBB2));
+ LastState = stateCheckID2a;
+ stateCheckID2a(executing);
+ return;
+ } else {
+ DVDLowStopMotor(cbForStateCheckID1);
+ }
+ break;
+
+ default:
+ if (memcmp(&CurrDiskID, IDShouldBe, sizeof(DVDDiskID))) {
+ DVDLowStopMotor(cbForStateCheckID1);
+ } else {
+ LastState = stateCheckID3;
+ stateCheckID3(executing);
+ }
+ break;
+ }
+}
+
+static void stateCheckID3() {
+ DVDLowAudioBufferConfig(IDShouldBe->streaming, 10, cbForStateCheckID3);
+}
+
+static void stateCheckID2a() {
+ DVDLowAudioBufferConfig(IDShouldBe->streaming, 10, cbForStateCheckID2a);
+}
+
+static void cbForStateCheckID2a(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+ NumInternalRetry = 0;
+ stateCheckID2(executing);
+ return;
+ }
+
+ DVDLowRequestError(cbForStateGettingError);
+}
+
+static void stateCheckID2() {
+ DVDLowRead(&BB2, OSRoundUp32B(sizeof(BB2)), 0x420, cbForStateCheckID2);
+}
+
+static void cbForStateCheckID1(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 2) {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+
+ NumInternalRetry = 0;
+
+ if (!CheckCancel(1)) {
+ executing->state = 6;
+ stateMotorStopped();
+ }
+}
+
+static void cbForStateCheckID2(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+
+ NumInternalRetry = 0;
+
+ stateReadingFST();
+
+ } else {
+
+ stateGettingError();
+ }
+}
+
+static void cbForStateCheckID3(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+
+ NumInternalRetry = 0;
+
+ if (!CheckCancel(0)) {
+ executing->state = 1;
+ stateBusy(executing);
+ }
+ } else {
+ stateGettingError();
+ }
+}
+
+static void AlarmHandler(OSAlarm* alarm, OSContext* context) {
+ DVDReset();
+ DCInvalidateRange(&CurrDiskID, sizeof(DVDDiskID));
+ LastState = stateCoverClosed_CMD;
+ stateCoverClosed_CMD(executing);
+}
+
+static void stateCoverClosed() {
+ DVDCommandBlock* finished;
+
+ switch (CurrCommand) {
+ case 5:
+ case 4:
+ case 13:
+ case 15:
+ __DVDClearWaitingQueue();
+ finished = executing;
+ executing = &DummyCommandBlock;
+ if (finished->callback) {
+ (finished->callback)(-4, finished);
+ }
+ stateReady();
+ break;
+
+ default:
+ DVDReset();
+ OSCreateAlarm(&ResetAlarm);
+ OSSetAlarm(&ResetAlarm, OSMillisecondsToTicks(1150), AlarmHandler);
+ break;
+ }
+}
+
+static void stateCoverClosed_CMD(DVDCommandBlock* block) {
+ DVDLowReadDiskID(&CurrDiskID, cbForStateCoverClosed);
+}
+
+static void cbForStateCoverClosed(u32 intType) {
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if (intType & 1) {
+ NumInternalRetry = 0;
+ stateCheckID();
+ } else {
+ stateGettingError();
+ }
+}
+
+static void stateMotorStopped(void) { DVDLowWaitCoverClose(cbForStateMotorStopped); }
+
+static void cbForStateMotorStopped(u32 intType) {
+ __DIRegs[1] = 0;
+ executing->state = 3;
+ stateCoverClosed();
+}
+
+static void stateReady() {
+ DVDCommandBlock* finished;
+
+ if (!__DVDCheckWaitingQueue()) {
+ executing = (DVDCommandBlock*)NULL;
+ return;
+ }
+
+ if (PauseFlag) {
+ PausingFlag = TRUE;
+ executing = (DVDCommandBlock*)NULL;
+ return;
+ }
+
+ executing = __DVDPopWaitingQueue();
+
+ if (FatalErrorFlag) {
+ executing->state = -1;
+ finished = executing;
+ executing = &DummyCommandBlock;
+ if (finished->callback) {
+ (finished->callback)(-1, finished);
+ }
+ stateReady();
+ return;
+ }
+
+ CurrCommand = executing->command;
+
+ if (ResumeFromHere) {
+ switch (ResumeFromHere) {
+ case 1:
+ executing->state = 1;
+ stateCoverClosed();
+ break;
+ case 2:
+ executing->state = 11;
+ stateMotorStopped();
+ break;
+
+ case 3:
+ executing->state = 4;
+ stateMotorStopped();
+ break;
+
+ case 4:
+ executing->state = 5;
+ stateMotorStopped();
+ break;
+ case 7:
+ case 6:
+ executing->state = 3;
+ stateCoverClosed();
+ break;
+
+ case 5:
+ executing->state = -1;
+ stateError(CancelLastError);
+ break;
+ }
+
+ ResumeFromHere = 0;
+ } else {
+ executing->state = 1;
+ stateBusy(executing);
+ }
+}
+
+#define MIN(a, b) (((a) > (b)) ? (b) : (a))
+static void stateBusy(DVDCommandBlock* block) {
+ DVDCommandBlock* finished;
+ LastState = stateBusy;
+ switch (block->command) {
+ case 5:
+ __DIRegs[1] = __DIRegs[1];
+ block->currTransferSize = sizeof(DVDDiskID);
+ DVDLowReadDiskID(block->addr, cbForStateBusy);
+ break;
+ case 1:
+ case 4:
+ if (!block->length) {
+ finished = executing;
+ executing = &DummyCommandBlock;
+ finished->state = 0;
+ if (finished->callback) {
+ finished->callback(0, finished);
+ }
+ stateReady();
+ } else {
+ __DIRegs[1] = __DIRegs[1];
+ block->currTransferSize = MIN(block->length - block->transferredSize, 0x80000);
+ DVDLowRead((void*)((u8*)block->addr + block->transferredSize), block->currTransferSize,
+ block->offset + block->transferredSize, cbForStateBusy);
+ }
+ break;
+ case 2:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowSeek(block->offset, cbForStateBusy);
+ break;
+ case 3:
+ DVDLowStopMotor(cbForStateBusy);
+ break;
+ case 15:
+ DVDLowStopMotor(cbForStateBusy);
+ break;
+ case 6:
+ __DIRegs[1] = __DIRegs[1];
+ if (AutoFinishing) {
+ executing->currTransferSize = 0;
+ DVDLowRequestAudioStatus(0, cbForStateBusy);
+ } else {
+ executing->currTransferSize = 1;
+ DVDLowAudioStream(0, block->length, block->offset, cbForStateBusy);
+ }
+ break;
+ case 7:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowAudioStream(0x10000, 0, 0, cbForStateBusy);
+ break;
+ case 8:
+ __DIRegs[1] = __DIRegs[1];
+ AutoFinishing = TRUE;
+ DVDLowAudioStream(0, 0, 0, cbForStateBusy);
+ break;
+ case 9:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowRequestAudioStatus(0, cbForStateBusy);
+ break;
+ case 10:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowRequestAudioStatus(0x10000, cbForStateBusy);
+ break;
+ case 11:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowRequestAudioStatus(0x20000, cbForStateBusy);
+ break;
+ case 12:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowRequestAudioStatus(0x30000, cbForStateBusy);
+ break;
+ case 13:
+ __DIRegs[1] = __DIRegs[1];
+ DVDLowAudioBufferConfig(block->offset, block->length, cbForStateBusy);
+ break;
+ case 14:
+ __DIRegs[1] = __DIRegs[1];
+ block->currTransferSize = sizeof(DVDDriveInfo);
+ DVDLowInquiry(block->addr, cbForStateBusy);
+ break;
+ default:
+ checkOptionalCommand(block, cbForStateBusy);
+ break;
+ }
+}
+
+static u32 ImmCommand[] = {0xffffffff, 0xffffffff, 0xffffffff};
+/* Somehow this got included even though the function is stripped? O.o */
+static char string_DVDChangeDiskAsyncMsg[] =
+ "DVDChangeDiskAsync(): You can't specify NULL to company name. \n";
+static u32 DmaCommand[] = {0xffffffff};
+
+inline static BOOL IsImmCommandWithResult(u32 command) {
+ u32 i;
+
+ if (command == 9 || command == 10 || command == 11 || command == 12) {
+ return TRUE;
+ }
+
+ for (i = 0; i < sizeof(ImmCommand) / sizeof(ImmCommand[0]); i++) {
+ if (command == ImmCommand[i])
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+inline static BOOL IsDmaCommand(u32 command) {
+ u32 i;
+
+ if (command == 1 || command == 4 || command == 5 || command == 14)
+ return TRUE;
+
+ for (i = 0; i < sizeof(DmaCommand) / sizeof(DmaCommand[0]); i++) {
+ if (command == DmaCommand[i])
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void cbForStateBusy(u32 intType) {
+ DVDCommandBlock* finished;
+
+ if (intType == 16) {
+ executing->state = -1;
+ stateTimeout();
+ return;
+ }
+
+ if ((CurrCommand == 3) || (CurrCommand == 15)) {
+ if (intType & 2) {
+ executing->state = -1;
+ stateError(0x1234567);
+ return;
+ }
+
+ NumInternalRetry = 0;
+
+ if (CurrCommand == 15) {
+ ResetRequired = TRUE;
+ }
+
+ if (CheckCancel(7)) {
+ return;
+ }
+
+ executing->state = 7;
+ stateMotorStopped();
+ return;
+ }
+
+ if (IsDmaCommand(CurrCommand)) {
+ executing->transferredSize += executing->currTransferSize - __DIRegs[6];
+ }
+
+ if (intType & 8) {
+ Canceling = FALSE;
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 10;
+ if (finished->callback)
+ (*finished->callback)(-3, finished);
+ if (CancelCallback)
+ (CancelCallback)(0, finished);
+ stateReady();
+
+ return;
+ }
+
+ if (intType & 1) {
+ NumInternalRetry = 0;
+
+ if (CheckCancel(0))
+ return;
+
+ if (IsDmaCommand(CurrCommand)) {
+ if (executing->transferredSize != executing->length) {
+ stateBusy(executing);
+ return;
+ }
+
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)((s32)finished->transferredSize, finished);
+ }
+ stateReady();
+ } else if (IsImmCommandWithResult(CurrCommand)) {
+ s32 result;
+
+ if ((CurrCommand == 11) || (CurrCommand == 10)) {
+ result = (s32)(__DIRegs[8] << 2);
+ } else {
+ result = (s32)__DIRegs[8];
+ }
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(result, finished);
+ }
+ stateReady();
+ } else if (CurrCommand == 6) {
+ if (executing->currTransferSize == 0) {
+ if (__DIRegs[8] & 1) {
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 9;
+ if (finished->callback) {
+ (finished->callback)(-2, finished);
+ }
+ stateReady();
+ } else {
+ AutoFinishing = FALSE;
+ executing->currTransferSize = 1;
+ DVDLowAudioStream(0, executing->length, executing->offset, cbForStateBusy);
+ }
+ } else {
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+ stateReady();
+ }
+ } else {
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)(0, finished);
+ }
+ stateReady();
+ }
+ } else {
+ if (CurrCommand == 14) {
+ executing->state = -1;
+ stateError(0x01234567);
+ return;
+ }
+
+ if ((CurrCommand == 1 || CurrCommand == 4 || CurrCommand == 5 || CurrCommand == 14) &&
+ (executing->transferredSize == executing->length)) {
+ if (CheckCancel(0)) {
+ return;
+ }
+ finished = executing;
+ executing = &DummyCommandBlock;
+
+ finished->state = 0;
+ if (finished->callback) {
+ (finished->callback)((s32)finished->transferredSize, finished);
+ }
+ stateReady();
+ return;
+ }
+
+ stateGettingError();
+ }
+}
+
+static BOOL issueCommand(s32 prio, DVDCommandBlock* block) {
+ BOOL level;
+ BOOL result;
+
+ if (autoInvalidation &&
+ (block->command == 1 || block->command == 4 || block->command == 5 || block->command == 14)) {
+ DCInvalidateRange(block->addr, block->length);
+ }
+
+ level = OSDisableInterrupts();
+
+ block->state = 2;
+ result = __DVDPushWaitingQueue(prio, block);
+
+ if ((executing == (DVDCommandBlock*)NULL) && (PauseFlag == FALSE)) {
+ stateReady();
+ }
+
+ OSRestoreInterrupts(level);
+
+ return result;
+}
+
+BOOL DVDReadAbsAsyncPrio(DVDCommandBlock* block, void* addr, s32 length, s32 offset,
+ DVDCBCallback callback, s32 prio) {
+ BOOL idle;
+ block->command = 1;
+ block->addr = addr;
+ block->length = length;
+ block->offset = offset;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(prio, block);
+ return idle;
+}
+BOOL DVDReadAbsAsyncForBS(DVDCommandBlock* block, void* addr, s32 length, s32 offset,
+ DVDCBCallback callback) {
+ BOOL idle;
+ block->command = 4;
+ block->addr = addr;
+ block->length = length;
+ block->offset = offset;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+ return idle;
+}
+BOOL DVDReadDiskID(DVDCommandBlock* block, DVDDiskID* diskID, DVDCBCallback callback) {
+ BOOL idle;
+ block->command = 5;
+ block->addr = diskID;
+ block->length = sizeof(DVDDiskID);
+ ;
+ block->offset = 0;
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+ return idle;
+}
+BOOL DVDPrepareStreamAbsAsync(DVDCommandBlock* block, u32 length, u32 offset,
+ DVDCBCallback callback) {
+ BOOL idle;
+ block->command = 6;
+ block->length = length;
+ block->offset = offset;
+ block->callback = callback;
+
+ idle = issueCommand(1, block);
+ return idle;
+}
+BOOL DVDCancelStreamAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL idle;
+ block->command = 7;
+ block->callback = callback;
+ idle = issueCommand(1, block);
+ return idle;
+}
+s32 DVDCancelStream(DVDCommandBlock* block) {
+ BOOL result;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ result = DVDCancelStreamAsync(block, cbForCancelStreamSync);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ while (TRUE) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if (state == 0 || state == -1 || state == 10) {
+ retVal = (s32)block->transferredSize;
+ break;
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+static void cbForCancelStreamSync(s32 result, DVDCommandBlock* block) {
+ block->transferredSize = (u32)result;
+ OSWakeupThread(&__DVDThreadQueue);
+}
+BOOL DVDStopStreamAtEndAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL idle;
+
+ block->command = 8;
+ block->callback = callback;
+
+ idle = issueCommand(1, block);
+
+ return idle;
+}
+BOOL DVDGetStreamErrorStatusAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL idle;
+
+ block->command = 9;
+ block->callback = callback;
+
+ idle = issueCommand(1, block);
+
+ return idle;
+}
+BOOL DVDGetStreamPlayAddrAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL idle;
+
+ block->command = 10;
+ block->callback = callback;
+
+ idle = issueCommand(1, block);
+
+ return idle;
+}
+BOOL DVDInquiryAsync(DVDCommandBlock* block, DVDDriveInfo* info, DVDCBCallback callback) {
+ BOOL idle;
+
+ block->command = 14;
+ block->addr = (void*)info;
+ block->length = sizeof(DVDDriveInfo);
+ block->transferredSize = 0;
+ block->callback = callback;
+
+ idle = issueCommand(2, block);
+
+ return idle;
+}
+
+void DVDReset(void) {
+ DVDLowReset();
+ __DIRegs[0] = 0x2a;
+ __DIRegs[1] = __DIRegs[1];
+ ResetRequired = FALSE;
+ ResumeFromHere = 0;
+}
+
+s32 DVDGetCommandBlockStatus(const DVDCommandBlock* block) {
+ BOOL enabled;
+ s32 retVal;
+
+ enabled = OSDisableInterrupts();
+
+ if (block->state == 3) {
+ retVal = 1;
+ } else {
+ retVal = block->state;
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return retVal;
+}
+
+s32 DVDGetDriveStatus() {
+ BOOL enabled;
+ s32 retVal;
+
+ enabled = OSDisableInterrupts();
+
+ if (FatalErrorFlag) {
+ retVal = -1;
+ } else if (PausingFlag) {
+ retVal = 8;
+ } else {
+ if (executing == (DVDCommandBlock*)NULL) {
+ retVal = 0;
+ } else if (executing == &DummyCommandBlock) {
+ retVal = 0;
+ } else {
+ retVal = DVDGetCommandBlockStatus(executing);
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return retVal;
+}
+
+BOOL DVDSetAutoInvalidation(BOOL autoInval) {
+ BOOL prev;
+ prev = autoInvalidation;
+ autoInvalidation = autoInval;
+ return prev;
+}
+
+inline void DVDPause(void) {
+ BOOL level;
+ level = OSDisableInterrupts();
+ PauseFlag = TRUE;
+ if (executing == (DVDCommandBlock*)NULL) {
+ PausingFlag = TRUE;
+ }
+ OSRestoreInterrupts(level);
+}
+
+inline void DVDResume(void) {
+ BOOL level;
+ level = OSDisableInterrupts();
+ PauseFlag = FALSE;
+ if (PausingFlag) {
+ PausingFlag = FALSE;
+ stateReady();
+ }
+ OSRestoreInterrupts(level);
+}
+
+BOOL DVDCancelAsync(DVDCommandBlock* block, DVDCBCallback callback) {
+ BOOL enabled;
+ DVDLowCallback old;
+
+ enabled = OSDisableInterrupts();
+
+ switch (block->state) {
+ case -1:
+ case 0:
+ case 10:
+ if (callback)
+ (*callback)(0, block);
+ break;
+
+ case 1:
+ if (Canceling) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ Canceling = TRUE;
+ CancelCallback = callback;
+ if (block->command == 4 || block->command == 1) {
+ DVDLowBreak();
+ }
+ break;
+
+ case 2:
+ __DVDDequeueWaitingQueue(block);
+ block->state = 10;
+ if (block->callback)
+ (block->callback)(-3, block);
+ if (callback)
+ (*callback)(0, block);
+ break;
+
+ case 3:
+ switch (block->command) {
+ case 5:
+ case 4:
+ case 13:
+ case 15:
+ if (callback)
+ (*callback)(0, block);
+ break;
+
+ default:
+ if (Canceling) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ Canceling = TRUE;
+ CancelCallback = callback;
+ break;
+ }
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 11:
+ old = DVDLowClearCallback();
+ if (old != cbForStateMotorStopped) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ if (block->state == 4)
+ ResumeFromHere = 3;
+ if (block->state == 5)
+ ResumeFromHere = 4;
+ if (block->state == 6)
+ ResumeFromHere = 1;
+ if (block->state == 11)
+ ResumeFromHere = 2;
+ if (block->state == 7)
+ ResumeFromHere = 7;
+
+ block->state = 10;
+ if (block->callback) {
+ (block->callback)(-3, block);
+ }
+ if (callback) {
+ (callback)(0, block);
+ }
+ stateReady();
+ break;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+s32 DVDCancel(DVDCommandBlock* block) {
+ BOOL result;
+ s32 state;
+ u32 command;
+ BOOL enabled;
+
+ result = DVDCancelAsync(block, cbForCancelSync);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ for (;;) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if ((state == 0) || (state == -1) || (state == 10)) {
+ break;
+ }
+
+ if (state == 3) {
+ command = ((volatile DVDCommandBlock*)block)->command;
+
+ if ((command == 4) || (command == 5) || (command == 13) || (command == 15)) {
+ break;
+ }
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 0;
+}
+
+static void cbForCancelSync(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+inline BOOL DVDCancelAllAsync(DVDCBCallback callback) {
+ BOOL enabled;
+ DVDCommandBlock* p;
+ BOOL retVal;
+
+ enabled = OSDisableInterrupts();
+ DVDPause();
+
+ while ((p = __DVDPopWaitingQueue()) != 0) {
+ DVDCancelAsync(p, NULL);
+ }
+
+ if (executing)
+ retVal = DVDCancelAsync(executing, callback);
+ else {
+ retVal = TRUE;
+ if (callback)
+ (*callback)(0, NULL);
+ }
+
+ DVDResume();
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+s32 DVDCancelAll(void) {
+ BOOL result;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ CancelAllSyncComplete = FALSE;
+
+ result = DVDCancelAllAsync(cbForCancelAllSync);
+
+ if (result == FALSE) {
+ OSRestoreInterrupts(enabled);
+ return -1;
+ }
+
+ for (;;) {
+ if (CancelAllSyncComplete)
+ break;
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return 0;
+}
+
+static void cbForCancelAllSync(s32 result, DVDCommandBlock* block) {
+ CancelAllSyncComplete = TRUE;
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+DVDDiskID* DVDGetCurrentDiskID(void) { return (DVDDiskID*)OSPhysicalToCached(0); }
+BOOL DVDCheckDisk(void) {
+ BOOL enabled;
+ s32 retVal;
+ s32 state;
+ u32 coverReg;
+
+ enabled = OSDisableInterrupts();
+
+ if (FatalErrorFlag) {
+ state = -1;
+ } else if (PausingFlag) {
+ state = 8;
+ } else {
+ if (executing == (DVDCommandBlock*)NULL) {
+ state = 0;
+ } else if (executing == &DummyCommandBlock) {
+ state = 0;
+ } else {
+ state = executing->state;
+ }
+ }
+
+ switch (state) {
+ case 1:
+ case 9:
+ case 10:
+ case 2:
+ retVal = TRUE;
+ break;
+
+ case -1:
+ case 11:
+ case 7:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ retVal = FALSE;
+ break;
+
+ case 0:
+ case 8:
+ coverReg = __DIRegs[1];
+ if (((coverReg >> 2) & 1) || (coverReg & 1)) {
+ retVal = FALSE;
+ } else {
+ retVal = TRUE;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return retVal;
+}
+
+void __DVDPrepareResetAsync(DVDCBCallback callback) {
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+
+ __DVDClearWaitingQueue();
+
+ if (Canceling) {
+ CancelCallback = callback;
+ } else {
+ if (executing) {
+ executing->callback = NULL;
+ }
+
+ DVDCancelAllAsync(callback);
+ }
+
+ OSRestoreInterrupts(enabled);
+}
diff --git a/src/Dolphin/dvd/dvderror.c b/src/Dolphin/dvd/dvderror.c
new file mode 100644
index 0000000..c984a8b
--- /dev/null
+++ b/src/Dolphin/dvd/dvderror.c
@@ -0,0 +1,56 @@
+#include "dolphin/DVDPriv.h"
+#include "dolphin/OSRtcPriv.h"
+
+static u32 ErrorTable[] = {
+ 0, 0x00023A00, 0x00062800, 0x00030200, 0x00031100, 0x00052000,
+ 0x00052001, 0x00052100, 0x00052400, 0x00052401, 0x00052402, 0x000B5A01,
+ 0x00056300, 0x00020401, 0x00020400, 0x00040800, 0x00100007, 0,
+};
+
+static u8 ErrorCode2Num(u32 errorCode) {
+ u32 i;
+
+ for (i = 0; i < sizeof(ErrorTable) / sizeof(ErrorTable[0]); i++) {
+ if (ErrorTable[i] == errorCode) {
+ return (u8)i;
+ }
+ }
+
+ if ((errorCode >= 0x00100000) && (errorCode <= 0x00100008)) {
+ return 17;
+ }
+
+ return 29;
+}
+
+static u8 Convert(u32 error) {
+ u32 statusCode;
+ u32 errorCode;
+ u8 errorNum;
+
+ if (error == 0x01234567)
+ return 255;
+
+ if (error == 0x01234568)
+ return 254;
+
+ statusCode = (error & 0xff000000) >> 24;
+ errorCode = error & 0x00ffffff;
+
+ errorNum = ErrorCode2Num(errorCode);
+ if (statusCode >= 6)
+ statusCode = 6;
+
+ return (u8)(statusCode * 30 + errorNum);
+}
+
+void __DVDStoreErrorCode(u32 error) {
+ OSSramEx* sram;
+ u8 num;
+
+ num = Convert(error);
+
+ sram = __OSLockSramEx();
+ sram->dvdErrorCode = num;
+ __OSUnlockSramEx(TRUE);
+}
diff --git a/src/Dolphin/dvd/dvdfatal.c b/src/Dolphin/dvd/dvdfatal.c
new file mode 100644
index 0000000..ea8093c
--- /dev/null
+++ b/src/Dolphin/dvd/dvdfatal.c
@@ -0,0 +1,136 @@
+#include "dolphin/DVDPriv.h"
+#include "dolphin/gx/GXStruct.h"
+#include "dolphin/os.h"
+#include "dolphin/vi.h"
+
+void __DVDPrintFatalMessage(void);
+
+static void (*FatalFunc)(void) = NULL;
+
+const char* Japanese = "\n\n\nƒGƒ‰[‚ª”­¶‚µ‚Ü‚µ‚½B"
+ "\n\n–{‘̂̃pƒ[ƒ{ƒ^ƒ“‚ð‰Ÿ‚µ‚Ä“dŒ¹‚ðOFF‚É‚µA"
+ "\n–{‘Ì‚ÌŽæˆµà–¾‘‚ÌŽwŽ¦‚É]‚Á‚Ä‚­‚¾‚³‚¢B";
+
+const char* English = "\n\n\nAn error has occurred."
+ "\nTurn the power off and refer to the"
+ "\nNintendo GameCube Instruction Booklet"
+ "\nfor further instructions.";
+
+const char* const Europe[] = {
+ // English
+ "\n\n\nAn error has occurred."
+ "\nTurn the power off and refer to the"
+ "\nNintendo GameCube""\x99"" Instruction Booklet"
+ "\nfor further instructions.",
+
+ // German
+ "\n\n\nEin Fehler ist aufgetreten."
+ "\nBitte schalten Sie den NINTENDO GAMECUBE"
+ "\naus und lesen Sie die Bedienungsanleitung,"
+ "\num weitere Informationen zu erhalten.",
+
+ // French
+ "\n\n\nUne erreur est survenue."
+ "\nEteignez la console et r" "\xe9" "f" "\xe9" "rez-vous au"
+ "\nmanuel d'instructions NINTENDO GAMECUBE"
+ "\npour de plus amples informations.",
+
+ // Spanish
+ "\n\n\nSe ha producido un error."
+ "\nApaga la consola y consulta el manual"
+ "\nde instrucciones de NINTENDO GAMECUBE"
+ "\npara obtener m""\xe1""s informaci""\xf3""n.",
+
+ // Italian
+ "\n\n\nSi \xe8 verificato un errore."
+ "\nSpegni (OFF) e controlla il manuale"
+ "\nd'istruzioni del NINTENDO GAMECUBE"
+ "\nper ulteriori indicazioni.",
+
+ // Dutch
+ "\n\n\nEr is een fout opgetreden."
+ "\nZet de NINTENDO GAMECUBE uit en"
+ "\nraadpleeg de handleiding van de"
+ "\nNintendo GameCube voor nadere"
+ "\ninstructies.",
+};
+
+static void ShowMessage(void) {
+ const char* message;
+ GXColor bg = {0, 0, 0, 0};
+ GXColor fg = {255, 255, 255, 0};
+
+ if (VIGetTvFormat() == VI_NTSC) {
+ if (OSGetFontEncode() == OS_FONT_ENCODE_SJIS) {
+ message = Japanese;
+ } else {
+ message = English;
+ }
+ } else {
+ message = Europe[OSGetLanguage()];
+ }
+
+ OSFatal(fg, bg, message);
+}
+
+#ifdef FULL_FRANK
+BOOL DVDSetAutoFatalMessaging(BOOL enable) {
+ BOOL enabled;
+ BOOL prev;
+
+ enabled = OSDisableInterrupts();
+ prev = FatalFunc ? TRUE : FALSE;
+ FatalFunc = enable ? ShowMessage : NULL;
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm BOOL DVDSetAutoFatalMessaging(BOOL enable) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ mr r30, r3
+ bl OSDisableInterrupts
+ lwz r0, FatalFunc
+ cmplwi r0, 0
+ beq lbl_80374DFC
+ li r31, 1
+ b lbl_80374E00
+lbl_80374DFC:
+ li r31, 0
+lbl_80374E00:
+ cmpwi r30, 0
+ beq lbl_80374E14
+ lis r4, ShowMessage@ha
+ addi r0, r4, ShowMessage@l
+ b lbl_80374E18
+lbl_80374E14:
+ li r0, 0
+lbl_80374E18:
+ stw r0, FatalFunc
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+/* clang-format off */
+#endif
+
+void __DVDPrintFatalMessage(void) {
+ if (!FatalFunc) {
+ return;
+ }
+ FatalFunc();
+}
diff --git a/src/Dolphin/dvd/dvdfs.c b/src/Dolphin/dvd/dvdfs.c
new file mode 100644
index 0000000..d0dc27c
--- /dev/null
+++ b/src/Dolphin/dvd/dvdfs.c
@@ -0,0 +1,656 @@
+#include "dolphin/DVDPriv.h"
+#include "dolphin/os.h"
+#include "dolphin/os/OSBootInfo.h"
+
+typedef struct FSTEntry FSTEntry;
+
+struct FSTEntry {
+ unsigned int isDirAndStringOff;
+ unsigned int parentOrPosition;
+ unsigned int nextEntryOrLength;
+};
+
+static OSBootInfo* BootInfo;
+static FSTEntry* FstStart;
+static char* FstStringStart;
+static u32 MaxEntryNum;
+static u32 currentDirectory = 0;
+OSThreadQueue __DVDThreadQueue;
+u32 __DVDLongFileNameFlag = 0;
+
+static void cbForReadAsync(s32 result, DVDCommandBlock* block);
+static void cbForReadSync(s32 result, DVDCommandBlock* block);
+static void cbForSeekAsync(s32 result, DVDCommandBlock* block);
+static void cbForSeekSync(s32 result, DVDCommandBlock* block);
+static void cbForPrepareStreamAsync(s32 result, DVDCommandBlock* block);
+static void cbForPrepareStreamSync(s32 result, DVDCommandBlock* block);
+
+void __DVDFSInit() {
+ BootInfo = (OSBootInfo*)OSPhysicalToCached(0);
+ FstStart = (FSTEntry*)BootInfo->FSTLocation;
+
+ if (FstStart) {
+ MaxEntryNum = FstStart[0].nextEntryOrLength;
+ FstStringStart = (char*)&(FstStart[MaxEntryNum]);
+ }
+}
+
+/* For convenience */
+#define entryIsDir(i) (((FstStart[i].isDirAndStringOff & 0xff000000) == 0) ? FALSE : TRUE)
+#define stringOff(i) (FstStart[i].isDirAndStringOff & ~0xff000000)
+#define parentDir(i) (FstStart[i].parentOrPosition)
+#define nextDir(i) (FstStart[i].nextEntryOrLength)
+#define filePosition(i) (FstStart[i].parentOrPosition)
+#define fileLength(i) (FstStart[i].nextEntryOrLength)
+
+static BOOL isSame(const char* path, const char* string) {
+ while (*string != '\0') {
+ if (tolower(*path++) != tolower(*string++)) {
+ return FALSE;
+ }
+ }
+
+ if ((*path == '/') || (*path == '\0')) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+s32 DVDConvertPathToEntrynum(char* pathPtr) {
+ const char* ptr;
+ char* stringPtr;
+ BOOL isDir;
+ u32 length;
+ u32 dirLookAt;
+ u32 i;
+ const char* origPathPtr = pathPtr;
+ const char* extentionStart;
+ BOOL illegal;
+ BOOL extention;
+
+ dirLookAt = currentDirectory;
+
+ while (1) {
+
+ if (*pathPtr == '\0') {
+ return (s32)dirLookAt;
+ } else if (*pathPtr == '/') {
+ dirLookAt = 0;
+ pathPtr++;
+ continue;
+ } else if (*pathPtr == '.') {
+ if (*(pathPtr + 1) == '.') {
+ if (*(pathPtr + 2) == '/') {
+ dirLookAt = parentDir(dirLookAt);
+ pathPtr += 3;
+ continue;
+ } else if (*(pathPtr + 2) == '\0') {
+ return (s32)parentDir(dirLookAt);
+ }
+ } else if (*(pathPtr + 1) == '/') {
+ pathPtr += 2;
+ continue;
+ } else if (*(pathPtr + 1) == '\0') {
+ return (s32)dirLookAt;
+ }
+ }
+
+ if (__DVDLongFileNameFlag == 0) {
+ extention = FALSE;
+ illegal = FALSE;
+
+ for (ptr = pathPtr; (*ptr != '\0') && (*ptr != '/'); ptr++) {
+ if (*ptr == '.') {
+ if ((ptr - pathPtr > 8) || (extention == TRUE)) {
+ illegal = TRUE;
+ break;
+ }
+ extention = TRUE;
+ extentionStart = ptr + 1;
+
+ } else if (*ptr == ' ')
+ illegal = TRUE;
+ }
+
+ if ((extention == TRUE) && (ptr - extentionStart > 3))
+ illegal = TRUE;
+
+ if (illegal)
+ OSPanic(__FILE__, 379,
+ "DVDConvertEntrynumToPath(possibly DVDOpen or DVDChangeDir or DVDOpenDir): "
+ "specified directory or file (%s) doesn't match standard 8.3 format. This is a "
+ "temporary restriction and will be removed soon\n",
+ origPathPtr);
+ } else {
+ for (ptr = pathPtr; (*ptr != '\0') && (*ptr != '/'); ptr++)
+ ;
+ }
+
+ isDir = (*ptr == '\0') ? FALSE : TRUE;
+ length = (u32)(ptr - pathPtr);
+
+ ptr = pathPtr;
+
+ for (i = dirLookAt + 1; i < nextDir(dirLookAt); i = entryIsDir(i) ? nextDir(i) : (i + 1)) {
+ if ((entryIsDir(i) == FALSE) && (isDir == TRUE)) {
+ continue;
+ }
+
+ stringPtr = FstStringStart + stringOff(i);
+
+ if (isSame(ptr, stringPtr) == TRUE) {
+ goto next_hier;
+ }
+ }
+
+ return -1;
+
+ next_hier:
+ if (!isDir) {
+ return (s32)i;
+ }
+
+ dirLookAt = i;
+ pathPtr += length + 1;
+ }
+}
+
+BOOL DVDFastOpen(s32 entrynum, DVDFileInfo* fileInfo) {
+ if ((entrynum < 0) || (entrynum >= MaxEntryNum) || entryIsDir(entrynum)) {
+ return FALSE;
+ }
+
+ fileInfo->startAddr = filePosition(entrynum);
+ fileInfo->length = fileLength(entrynum);
+ fileInfo->callback = (DVDCallback)NULL;
+ fileInfo->cb.state = DVD_STATE_END;
+
+ return TRUE;
+}
+
+BOOL DVDOpen(char* fileName, DVDFileInfo* fileInfo) {
+ s32 entry;
+ char currentDir[128];
+
+ entry = DVDConvertPathToEntrynum(fileName);
+
+ if (0 > entry) {
+ DVDGetCurrentDir(currentDir, 128);
+ OSReport("Warning: DVDOpen(): file '%s' was not found under %s.\n", fileName, currentDir);
+ return FALSE;
+ }
+
+ if (entryIsDir(entry)) {
+ return FALSE;
+ }
+
+ fileInfo->startAddr = filePosition(entry);
+ fileInfo->length = fileLength(entry);
+ fileInfo->callback = (DVDCallback)NULL;
+ fileInfo->cb.state = DVD_STATE_END;
+
+ return TRUE;
+}
+
+#ifdef FULL_FRANK
+BOOL DVDClose(DVDFileInfo* fileInfo) {
+ DVDCancel(&(fileInfo->cb));
+ return TRUE;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm BOOL DVDClose(DVDFileInfo* fileInfo) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -8(r1)
+ bl DVDCancel
+ li r3, 1
+ lwz r0, 0xc(r1)
+ addi r1, r1, 8
+ mtlr r0
+ blr
+}
+#pragma pop
+#endif
+
+static u32 myStrncpy(char* dest, char* src, u32 maxlen) {
+ u32 i = maxlen;
+
+ while ((i > 0) && (*src != 0)) {
+ *dest++ = *src++;
+ i--;
+ }
+
+ return (maxlen - i);
+}
+
+static u32 entryToPath(u32 entry, char* path, u32 maxlen) {
+ char* name;
+ u32 loc;
+
+ if (entry == 0) {
+ return 0;
+ }
+
+ name = FstStringStart + stringOff(entry);
+
+ loc = entryToPath(parentDir(entry), path, maxlen);
+
+ if (loc == maxlen) {
+ return loc;
+ }
+
+ *(path + loc++) = '/';
+
+ loc += myStrncpy(path + loc, name, maxlen - loc);
+
+ return loc;
+}
+
+static BOOL DVDConvertEntrynumToPath(s32 entrynum, char* path, u32 maxlen) {
+ u32 loc;
+
+ loc = entryToPath((u32)entrynum, path, maxlen);
+
+ if (loc == maxlen) {
+ path[maxlen - 1] = '\0';
+ return FALSE;
+ }
+
+ if (entryIsDir(entrynum)) {
+ if (loc == maxlen - 1) {
+ path[loc] = '\0';
+ return FALSE;
+ }
+
+ path[loc++] = '/';
+ }
+
+ path[loc] = '\0';
+ return TRUE;
+}
+
+BOOL DVDGetCurrentDir(char* path, u32 maxlen) {
+ return DVDConvertEntrynumToPath((s32)currentDirectory, path, maxlen);
+}
+
+BOOL DVDChangeDir(char* dirName) {
+ s32 entry;
+ entry = DVDConvertPathToEntrynum(dirName);
+ if ((entry < 0) || (entryIsDir(entry) == FALSE)) {
+ return FALSE;
+ }
+
+ currentDirectory = (u32)entry;
+
+ return TRUE;
+}
+
+BOOL DVDReadAsyncPrio(DVDFileInfo* fileInfo, void* addr, s32 length, s32 offset,
+ DVDCallback callback, s32 prio) {
+
+ if (!((0 <= offset) && (offset < fileInfo->length))) {
+ OSPanic(__FILE__, 742, "DVDReadAsync(): specified area is out of the file ");
+ }
+
+ if (!((0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE))) {
+ OSPanic(__FILE__, 748, "DVDReadAsync(): specified area is out of the file ");
+ }
+
+ fileInfo->callback = callback;
+ DVDReadAbsAsyncPrio(&(fileInfo->cb), addr, length, (s32)(fileInfo->startAddr + offset),
+ cbForReadAsync, prio);
+
+ return TRUE;
+}
+#ifndef offsetof
+#define offsetof(type, memb) ((u32) & ((type*)0)->memb)
+#endif
+
+static void cbForReadAsync(s32 result, DVDCommandBlock* block) {
+ DVDFileInfo* fileInfo;
+
+ fileInfo = (DVDFileInfo*)((char*)block - offsetof(DVDFileInfo, cb));
+ if (fileInfo->callback) {
+ (fileInfo->callback)(result, fileInfo);
+ }
+}
+
+/* This is based on the revolution SDK, these may not match in all cases I have also left the line numbers at 0 */
+s32 DVDReadPrio(DVDFileInfo* fileInfo, void* addr, s32 length, s32 offset, s32 prio) {
+ BOOL result;
+ DVDCommandBlock* block;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ if (!((0 <= offset) && (offset <= fileInfo->length))) {
+ OSPanic(__FILE__, 0, "DVDRead(): specified area is out of the file ");
+ }
+
+ if (!((0 <= offset + length) && (offset + length < fileInfo->length + DVD_MIN_TRANSFER_SIZE))) {
+ OSPanic(__FILE__, 0, "DVDRead(): specified area is out of the file ");
+ }
+
+ block = &(fileInfo->cb);
+
+ result = DVDReadAbsAsyncPrio(block, addr, length, (s32)(fileInfo->startAddr + offset),
+ cbForReadSync, prio);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ while(1) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if (state == DVD_STATE_END) {
+ retVal = (s32)block->transferredSize;
+ break;
+ }
+ if (state == DVD_STATE_FATAL_ERROR) {
+ retVal = DVD_RESULT_FATAL_ERROR;
+ break;
+ }
+ if (state == DVD_STATE_CANCELED) {
+ retVal = DVD_RESULT_CANCELED;
+ break;
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+static void cbForReadSync(s32 result, DVDCommandBlock* block) { OSWakeupThread(&__DVDThreadQueue); }
+/* This is based on the revolution SDK, these may not match in all cases */
+BOOL DVDSeekAsyncPrio(DVDFileInfo* fileInfo, s32 offset, DVDCallback callback, s32 prio) {
+ if (!((0 <= offset) && (offset <= fileInfo->length))) {
+ OSPanic(__FILE__, 0, "DVDSeek(): offset is out of the file ");
+ }
+
+ fileInfo->callback = callback;
+ DVDSeekAbsAsyncPrio(&(fileInfo->cb), (s32)(fileInfo->startAddr + offset), cbForSeekAsync,
+ prio);
+
+ return TRUE;
+}
+/* This is based on the revolution SDK, these may not match in all cases */
+static void cbForSeekAsync(s32 result, DVDCommandBlock* block) {
+ DVDFileInfo* fileInfo;
+
+ fileInfo = (DVDFileInfo*)((char*)block - offsetof(DVDFileInfo, cb));
+
+ if (fileInfo->callback) {
+ (fileInfo->callback)(result, fileInfo);
+ }
+}
+/* This is based on the revolution SDK, these may not match in all cases */
+s32 DVDSeekPrio(DVDFileInfo* fileInfo, s32 offset, s32 prio) {
+ BOOL result;
+ DVDCommandBlock* block;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+
+ block = &(fileInfo->cb);
+
+ result =
+ DVDSeekAbsAsyncPrio(block, (s32)(fileInfo->startAddr + offset), cbForSeekSync, prio);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ while (1) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if (state == DVD_STATE_END) {
+ retVal = 0;
+ break;
+ }
+ if (state == DVD_STATE_FATAL_ERROR) {
+ retVal = DVD_RESULT_FATAL_ERROR;
+ break;
+ }
+ if (state == DVD_STATE_CANCELED) {
+ retVal = DVD_RESULT_CANCELED;
+ break;
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+/* This is based on the revolution SDK, these may not match in all cases */
+static void cbForSeekSync(s32 result, DVDCommandBlock* block) { OSWakeupThread(&__DVDThreadQueue); }
+
+/* This is based on the revolution SDK, these may not match in all cases */
+s32 DVDGetFileInfoStatus(DVDFileInfo* fileInfo) {
+ return DVDGetCommandBlockStatus(&fileInfo->cb);
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+BOOL DVDFastOpenDir(s32 entrynum, DVDDir* dir) {
+
+ if ((entrynum < 0) || (entrynum >= MaxEntryNum) || !entryIsDir(entrynum)) {
+ return FALSE;
+ }
+
+ dir->entryNum = (u32)entrynum;
+ dir->location = (u32)entrynum + 1;
+ dir->next = nextDir(entrynum);
+
+ return TRUE;
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+BOOL DVDOpenDir(char* dirName, DVDDir* dir) {
+ s32 entry;
+ char currentDir[128];
+ entry = DVDConvertPathToEntrynum(dirName);
+
+ if (entry < 0) {
+ DVDGetCurrentDir(currentDir, 128);
+ OSReport("Warning: DVDOpenDir(): file '%s' was not found under %s.\n", dirName, currentDir);
+ return FALSE;
+ }
+
+ if (!entryIsDir(entry)) {
+ return FALSE;
+ }
+
+ dir->entryNum = (u32)entry;
+ dir->location = (u32)entry + 1;
+ dir->next = nextDir(entry);
+
+ return TRUE;
+}
+
+BOOL DVDReadDir(DVDDir* dir, DVDDirEntry* dirent) {
+ u32 loc = dir->location;
+ if ((loc <= dir->entryNum) || (dir->next <= loc))
+ return FALSE;
+
+ dirent->entryNum = loc;
+ dirent->isDir = entryIsDir(loc);
+ dirent->name = FstStringStart + stringOff(loc);
+
+ dir->location = entryIsDir(loc) ? nextDir(loc) : (loc + 1);
+
+ return TRUE;
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+BOOL DVDCloseDir(DVDDir* dir) { return TRUE; }
+
+/* This is based on the revolution SDK, these may not match in all cases */
+void DVDRewindDir(DVDDir* dir) { dir->location = dir->entryNum + 1; }
+
+/* This is based on the revolution SDK, these may not match in all cases */
+void* DVDGetFSTLocation(void) { return BootInfo->FSTLocation; }
+
+#define RoundUp32KB(x) (((u32)(x) + 32 * 1024 - 1) & ~(32 * 1024 - 1))
+#define Is32KBAligned(x) (((u32)(x) & (32 * 1024 - 1)) == 0)
+
+BOOL DVDPrepareStreamAsync(DVDFileInfo* fileInfo, u32 length, u32 offset, DVDCallback callback) {
+ u32 start;
+
+ start = fileInfo->startAddr + offset;
+
+ if (!Is32KBAligned(start)) {
+ OSPanic(__FILE__, 1189,
+ "DVDPrepareStreamAsync(): Specified start address (filestart(0x%x) + offset(0x%x)) is "
+ "not 32KB aligned",
+ fileInfo->startAddr, offset);
+ }
+
+ if (length == 0)
+ length = fileInfo->length - offset;
+
+ if (!Is32KBAligned(length)) {
+ OSPanic(__FILE__, 1199,
+ "DVDPrepareStreamAsync(): Specified length (0x%x) is not a multiple of 32768(32*1024)",
+ length);
+ }
+
+ if (!((offset < fileInfo->length) && (offset + length <= fileInfo->length))) {
+ OSPanic(__FILE__, 1207,
+ "DVDPrepareStreamAsync(): The area specified (offset(0x%x), length(0x%x)) is out of "
+ "the file",
+ offset, length);
+ }
+
+ fileInfo->callback = callback;
+ return DVDPrepareStreamAbsAsync(&(fileInfo->cb), length, fileInfo->startAddr + offset,
+ cbForPrepareStreamAsync);
+}
+
+static void cbForPrepareStreamAsync(s32 result, DVDCommandBlock* block) {
+ DVDFileInfo* fileInfo;
+
+ fileInfo = (DVDFileInfo*)((char*)block - offsetof(DVDFileInfo, cb));
+
+ if (fileInfo->callback) {
+ (fileInfo->callback)(result, fileInfo);
+ }
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+s32 DVDPrepareStream(DVDFileInfo* fileInfo, u32 length, u32 offset) {
+ BOOL result;
+ DVDCommandBlock* block;
+ s32 state;
+ BOOL enabled;
+ s32 retVal;
+ u32 start;
+ start = fileInfo->startAddr + offset;
+
+ if (!Is32KBAligned(start)) {
+ OSPanic(__FILE__, 0,
+ "DVDPrepareStream(): Specified start address (filestart(0x%x) + offset(0x%x)) is not "
+ "32KB aligned",
+ fileInfo->startAddr, offset);
+ }
+
+ if (length == 0)
+ length = fileInfo->length - offset;
+
+ if (!Is32KBAligned(length)) {
+ OSPanic(__FILE__, 0,
+ "DVDPrepareStream(): Specified length (0x%x) is not a multiple of 32768(32*1024)",
+ length);
+ }
+
+ if (!((offset <= fileInfo->length) && (offset + length <= fileInfo->length))) {
+ OSPanic(
+ __FILE__, 0,
+ "DVDPrepareStream(): The area specified (offset(0x%x), length(0x%x)) is out of the file",
+ offset, length);
+ }
+
+ block = &(fileInfo->cb);
+ result = DVDPrepareStreamAbsAsync(block, length, start, cbForPrepareStreamSync);
+
+ if (result == FALSE) {
+ return -1;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ while(1) {
+ state = ((volatile DVDCommandBlock*)block)->state;
+
+ if (state == DVD_STATE_END) {
+ retVal = 0;
+ break;
+ }
+ if (state == DVD_STATE_FATAL_ERROR) {
+ retVal = DVD_RESULT_FATAL_ERROR;
+ break;
+ }
+ if (state == DVD_STATE_CANCELED) {
+ retVal = DVD_RESULT_CANCELED;
+ break;
+ }
+
+ OSSleepThread(&__DVDThreadQueue);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return retVal;
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+static void cbForPrepareStreamSync(s32 result, DVDCommandBlock* block) {
+ OSWakeupThread(&__DVDThreadQueue);
+}
+
+/* This is based on the revolution SDK, these may not match in all cases */
+s32 DVDGetTransferredSize(DVDFileInfo* fileinfo) {
+ s32 bytes;
+ DVDCommandBlock* cb;
+
+ cb = &(fileinfo->cb);
+
+ switch (cb->state) {
+ case DVD_STATE_END:
+ case DVD_STATE_COVER_CLOSED:
+ case DVD_STATE_NO_DISK:
+ case DVD_STATE_COVER_OPEN:
+ case DVD_STATE_WRONG_DISK:
+ case DVD_STATE_FATAL_ERROR:
+ case DVD_STATE_MOTOR_STOPPED:
+ case DVD_STATE_CANCELED:
+ case DVD_STATE_RETRY:
+ bytes = (s32)cb->transferredSize;
+ break;
+
+ case DVD_STATE_WAITING:
+ bytes = 0;
+ break;
+
+ case DVD_STATE_BUSY:
+ bytes = (s32)(cb->transferredSize + (cb->currTransferSize - DVDLowGetLength()));
+ break;
+
+ default:
+ break;
+ }
+
+ return bytes;
+}
diff --git a/src/Dolphin/dvd/dvdidutils.c b/src/Dolphin/dvd/dvdidutils.c
new file mode 100644
index 0000000..3c58e98
--- /dev/null
+++ b/src/Dolphin/dvd/dvdidutils.c
@@ -0,0 +1,27 @@
+#include <dolphin/DVDPriv.h>
+#include <dolphin/dvd.h>
+#include <dolphin/dvd_regs.h>
+
+#include <string.h>
+
+BOOL DVDCompareDiskID(DVDDiskID* id1, DVDDiskID* id2) {
+
+ if (id1->gameName[0] && id2->gameName[0] && strncmp(&id1->gameName[0], &id2->gameName[0], 4)) {
+ return FALSE;
+ }
+
+ if (!id1->company[0] || !id2->company[0] || strncmp(&id1->company[0], &id2->company[0], 2)) {
+ return FALSE;
+ }
+
+ if (id1->diskNumber != 0xff && id2->diskNumber != 0xff && id1->diskNumber != id2->diskNumber) {
+ return FALSE;
+ }
+
+ if (id1->gameVersion != 0xff && id2->gameVersion != 0xff &&
+ id1->gameVersion != id2->gameVersion) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/Dolphin/dvd/dvdlow.c b/src/Dolphin/dvd/dvdlow.c
new file mode 100644
index 0000000..1a939ba
--- /dev/null
+++ b/src/Dolphin/dvd/dvdlow.c
@@ -0,0 +1,109 @@
+#include "dolphin/DVDPriv.h"
+#include "dolphin/os.h"
+
+static BOOL FirstRead = TRUE;
+static volatile BOOL StopAtNextInt = FALSE;
+static u32 LastLength = 0;
+static DVDLowCallback Callback = NULL;
+static DVDLowCallback ResetCoverCallback = NULL;
+static OSTime LastResetEnd = 0;
+static BOOL ResetOccurred = FALSE;
+static volatile BOOL WaitingCoverClose = FALSE;
+static BOOL Breaking = FALSE;
+static u32 WorkAroundType = 0;
+static u32 WorkAroundSeekLocation = 0;
+static OSTime LastReadFinished = 0;
+static OSTime LastReadIssued = 0;
+static BOOL LastCommandWasRead = FALSE;
+static u32 NextCommandNumber = 0;
+
+typedef struct DVDUnk {
+ u32 _0;
+ u32 _4;
+ u32 _8;
+} DVDUnk;
+
+typedef struct DVDCommand {
+ s32 _0;
+ u32 _4;
+ u32 _8;
+ u32 _c;
+ DVDLowCallback callback;
+} DVDCommand;
+
+static DVDCommand CommandList[4];
+static OSAlarm AlarmForWA;
+static OSAlarm AlarmForTimeout;
+static OSAlarm AlarmForBreak;
+static DVDUnk Prev;
+static DVDUnk Cur;
+
+void __DVDInitWA() {
+ NextCommandNumber = 0;
+ CommandList[0]._0 = -1;
+ __DVDLowSetWAType(0, 0);
+ OSInitAlarm();
+}
+static void Read(u32 tmp1, u32 tmp2, u32 tmp3, DVDLowCallback tmp4);
+void __DVDInterruptHandler(__OSInterrupt interrupt, OSContext* context) {}
+
+static void AlarmHandler(OSAlarm* alarm, OSContext* context) {
+ DVDCommand* cmd;
+ cmd = &CommandList[NextCommandNumber];
+
+ if (cmd->_0 == 1) {
+ ++NextCommandNumber;
+ Read(cmd->_4, cmd->_8, cmd->_c, cmd->callback);
+ } else if (cmd->_0 == 2) {
+ ++NextCommandNumber;
+ DVDLowSeek(cmd->_c, cmd->callback);
+ }
+}
+
+static void AlarmHandlerForTimeout(OSAlarm* alarm, OSContext* context) {
+ OSContext tmpContext;
+ DVDLowCallback callback;
+ __OSMaskInterrupts(0x400);
+ OSClearContext(&tmpContext);
+ OSSetCurrentContext(&tmpContext);
+ callback = Callback;
+ Callback = NULL;
+ if (callback != NULL) {
+ callback(0x10);
+ }
+ OSClearContext(&tmpContext);
+ OSSetCurrentContext(context);
+}
+
+static void Read(u32 tmp1, u32 tmp2, u32 tmp3, DVDLowCallback tmp4) {
+
+}
+
+static void SeekTwiceBeforeRead() {
+
+}
+
+void DVDLowRead(u32 unk, DVDLowCallback callback) {
+
+}
+
+void DVDLowSeek(u32 offset, DVDLowCallback callback) {
+
+}
+
+BOOL DVDLowWaitCoverClose(DVDLowCallback callback) {
+ Callback = callback;
+ WaitingCoverClose = TRUE;
+ StopAtNextInt = FALSE;
+ __DIRegs[1] = 2;
+ return TRUE;
+}
+
+
+void __DVDLowSetWAType(u32 type, u32 location) {
+ BOOL enabled;
+ enabled = OSDisableInterrupts();
+ WorkAroundType = type;
+ WorkAroundSeekLocation = location;
+ OSRestoreInterrupts(enabled);
+}
diff --git a/src/Dolphin/dvd/dvdqueue.c b/src/Dolphin/dvd/dvdqueue.c
new file mode 100644
index 0000000..a381ccf
--- /dev/null
+++ b/src/Dolphin/dvd/dvdqueue.c
@@ -0,0 +1,142 @@
+#include "dolphin/DVDPriv.h"
+
+#define MAX_QUEUES 4
+typedef struct {
+ DVDCommandBlock* next;
+ DVDCommandBlock* prev;
+} DVDQueue;
+
+static DVDQueue WaitingQueue[MAX_QUEUES];
+
+void __DVDClearWaitingQueue(void) {
+ u32 i;
+
+ for (i = 0; i < MAX_QUEUES; i++) {
+ DVDCommandBlock* q;
+
+ q = (DVDCommandBlock*)&(WaitingQueue[i]);
+ q->next = q;
+ q->prev = q;
+ }
+}
+
+BOOL __DVDPushWaitingQueue(s32 prio, DVDCommandBlock* block) {
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+
+ q = (DVDCommandBlock*)&(WaitingQueue[prio]);
+
+ q->prev->next = block;
+ block->prev = q->prev;
+ block->next = q;
+ q->prev = block;
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+static DVDCommandBlock* PopWaitingQueuePrio(s32 prio) {
+ DVDCommandBlock* tmp;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+
+ q = (DVDCommandBlock*)&(WaitingQueue[prio]);
+
+ tmp = q->next;
+ q->next = tmp->next;
+ tmp->next->prev = q;
+
+ OSRestoreInterrupts(enabled);
+
+ tmp->next = (DVDCommandBlock*)NULL;
+ tmp->prev = (DVDCommandBlock*)NULL;
+
+ return tmp;
+}
+
+DVDCommandBlock* __DVDPopWaitingQueue(void) {
+ u32 i;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+
+ for (i = 0; i < MAX_QUEUES; i++) {
+ q = (DVDCommandBlock*)&(WaitingQueue[i]);
+ if (q->next != q) {
+ OSRestoreInterrupts(enabled);
+ return PopWaitingQueuePrio((s32)i);
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return (DVDCommandBlock*)NULL;
+}
+
+BOOL __DVDCheckWaitingQueue(void) {
+ u32 i;
+ BOOL enabled;
+ DVDCommandBlock* q;
+
+ enabled = OSDisableInterrupts();
+
+ for (i = 0; i < MAX_QUEUES; i++) {
+ q = (DVDCommandBlock*)&(WaitingQueue[i]);
+ if (q->next != q) {
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ return FALSE;
+}
+
+BOOL __DVDDequeueWaitingQueue(DVDCommandBlock* block) {
+ BOOL enabled;
+ DVDCommandBlock* prev;
+ DVDCommandBlock* next;
+
+ enabled = OSDisableInterrupts();
+
+ prev = block->prev;
+ next = block->next;
+
+ if ((prev == (DVDCommandBlock*)NULL) || (next == (DVDCommandBlock*)NULL)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ prev->next = next;
+ next->prev = prev;
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+BOOL __DVDIsBlockInWaitingQueue(DVDCommandBlock* block) {
+ u32 i;
+ DVDCommandBlock* start;
+ DVDCommandBlock* q;
+
+ for (i = 0; i < MAX_QUEUES; i++) {
+ start = (DVDCommandBlock*)&(WaitingQueue[i]);
+
+ if (start->next != start) {
+ for (q = start->next; q != start; q = q->next) {
+ if (q == block)
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
diff --git a/src/Dolphin/dvd/fstload.c b/src/Dolphin/dvd/fstload.c
new file mode 100644
index 0000000..1f7a81b
--- /dev/null
+++ b/src/Dolphin/dvd/fstload.c
@@ -0,0 +1,67 @@
+#include <dolphin/DVDPriv.h>
+#include <dolphin/dvd.h>
+#include <dolphin/dvd_regs.h>
+#include <dolphin/os.h>
+#include <dolphin/os/OSBootInfo.h>
+#include <string.h>
+
+static s32 status = 0;
+
+static u8 bb2Buf[OSRoundUp32B(sizeof(DVDBB2)) + 31];
+static DVDBB2* bb2 = 0;
+static DVDDiskID* idTmp = NULL;
+
+static void cb(s32 result, DVDCommandBlock* block) {
+ if (result > 0) {
+ switch (status) {
+ case 0:
+ status = 1;
+ DVDReadAbsAsyncForBS(block, bb2, OSRoundUp32B(sizeof(bb2)), 0x420, cb);
+ break;
+ case 1:
+ status = 2;
+ DVDReadAbsAsyncForBS(block, bb2->FSTAddress, OSRoundUp32B(bb2->FSTLength), bb2->FSTPosition,
+ cb);
+ }
+ } else if (result == -1) {
+
+ } else if (result == -4) {
+ status = 0;
+ DVDReset();
+ DVDReadDiskID(block, idTmp, cb);
+ }
+}
+
+void __fstLoad() {
+ OSBootInfo* bootInfo;
+ DVDDiskID* id;
+ u8 idTmpBuf[sizeof(DVDDiskID) + 31];
+ static DVDCommandBlock block;
+ void* arenaHi;
+
+ arenaHi = OSGetArenaHi();
+ bootInfo = (OSBootInfo*)OSPhysicalToCached(0);
+
+ idTmp = (DVDDiskID*)(OSRoundUp32B(idTmpBuf));
+ bb2 = (DVDBB2*)(OSRoundUp32B(bb2Buf));
+
+ DVDReset();
+ DVDReadDiskID(&block, idTmp, cb);
+ while (DVDGetDriveStatus() != 0);
+
+ bootInfo->FSTLocation = bb2->FSTAddress;
+ bootInfo->FSTMaxLength = bb2->FSTMaxLength;
+
+ id = &bootInfo->DVDDiskID;
+
+ memcpy(id, idTmp, sizeof(DVDDiskID));
+ OSReport("\n");
+ OSReport(" Game Name ... %c%c%c%c\n", id->gameName[0], id->gameName[1], id->gameName[2],
+ id->gameName[3]);
+ OSReport(" Company ..... %c%c\n", id->company[0], id->company[1]);
+ OSReport(" Disk # ...... %d\n", id->diskNumber);
+ OSReport(" Game ver .... %d\n", id->gameVersion);
+ OSReport(" Streaming ... %s\n", (id->streaming == 0) ? "OFF" : "ON");
+ OSReport("\n");
+ OSSetArenaHi(bb2->FSTAddress);
+}
diff --git a/src/Dolphin/exi/EXIBios.c b/src/Dolphin/exi/EXIBios.c
new file mode 100644
index 0000000..8ff770f
--- /dev/null
+++ b/src/Dolphin/exi/EXIBios.c
@@ -0,0 +1,692 @@
+#include "dolphin/os.h"
+
+#pragma scheduling off
+
+vu32 __EXIRegs[16] : 0xCC006800;
+
+static const char* __EXIVersion =
+ "<< Dolphin SDK - EXI\trelease build: Sep 5 2002 05:33:04 (0x2301) >>";
+
+#define MAX_DEV 3
+#define MAX_CHAN 3
+
+#define REG_MAX 5
+#define REG(chan, idx) (__EXIRegs[((chan)*REG_MAX) + (idx)])
+
+#define STATE_IDLE 0x00
+#define STATE_DMA 0x01
+#define STATE_IMM 0x02
+#define STATE_BUSY (STATE_DMA | STATE_IMM)
+#define STATE_SELECTED 0x04
+#define STATE_ATTACHED 0x08
+#define STATE_LOCKED 0x10
+
+#define EXI_0CR(tstart, dma, rw, tlen) \
+ ((((u32)(tstart)) << 0) | (((u32)(dma)) << 1) | (((u32)(rw)) << 2) | (((u32)(tlen)) << 4))
+
+#define CPR_CS(x) ((1u << (x)) << 7)
+#define CPR_CLK(x) ((x) << 4)
+
+typedef struct EXIControl {
+ EXICallback exiCallback;
+ EXICallback tcCallback;
+ EXICallback extCallback;
+ vu32 state;
+ int immLen;
+ u8* immBuf;
+ u32 dev;
+ u32 id;
+ s32 idTime;
+ int items;
+ struct {
+ u32 dev;
+ EXICallback callback;
+ } queue[MAX_DEV];
+} EXIControl;
+
+static EXIControl Ecb[MAX_CHAN];
+
+s32 __EXIProbeStartTime[2] : (OS_BASE_CACHED | 0x30C0);
+
+static void SetExiInterruptMask(s32 chan, EXIControl* exi) {
+ EXIControl* exi2;
+
+ exi2 = &Ecb[2];
+ switch (chan) {
+ case 0:
+ if ((exi->exiCallback == 0 && exi2->exiCallback == 0) || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXI | OS_INTERRUPTMASK_EXI_2_EXI);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXI | OS_INTERRUPTMASK_EXI_2_EXI);
+ }
+ break;
+ case 1:
+ if (exi->exiCallback == 0 || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXI);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXI);
+ }
+ break;
+ case 2:
+ if (__OSGetInterruptHandler(__OS_INTERRUPT_PI_DEBUG) == 0 || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_PI_DEBUG);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_PI_DEBUG);
+ }
+ break;
+ }
+}
+
+static void CompleteTransfer(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ u8* buf;
+ u32 data;
+ int i;
+ int len;
+
+ if (exi->state & STATE_BUSY) {
+ if ((exi->state & STATE_IMM) && (len = exi->immLen)) {
+ buf = exi->immBuf;
+ data = REG(chan, 4);
+ for (i = 0; i < len; i++) {
+ *buf++ = (u8)((data >> ((3 - i) * 8)) & 0xff);
+ }
+ }
+ exi->state &= ~STATE_BUSY;
+ }
+}
+
+BOOL EXIImm(s32 chan, void* buf, s32 len, u32 type, EXICallback callback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_BUSY) || !(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->tcCallback = callback;
+ if (exi->tcCallback) {
+ EXIClearInterrupts(chan, FALSE, TRUE, FALSE);
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_TC >> (3 * chan));
+ }
+
+ exi->state |= STATE_IMM;
+
+ if (type != EXI_READ) {
+ u32 data;
+ int i;
+
+ data = 0;
+ for (i = 0; i < len; i++) {
+ data |= ((u8*)buf)[i] << ((3 - i) * 8);
+ }
+ REG(chan, 4) = data;
+ }
+
+ exi->immBuf = buf;
+ exi->immLen = (type != EXI_WRITE) ? len : 0;
+
+ REG(chan, 3) = EXI_0CR(1, 0, type, len - 1);
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+BOOL EXIImmEx(s32 chan, void* buf, s32 len, u32 mode) {
+ s32 xLen;
+
+ while (len) {
+ xLen = (len < 4) ? len : 4;
+ if (!EXIImm(chan, buf, xLen, mode, NULL)) {
+ return FALSE;
+ }
+
+ if (!EXISync(chan)) {
+ return FALSE;
+ }
+
+ (u8*)buf += xLen;
+ len -= xLen;
+ }
+ return TRUE;
+}
+
+BOOL EXIDma(s32 chan, void* buf, s32 len, u32 type, EXICallback callback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_BUSY) || !(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->tcCallback = callback;
+ if (exi->tcCallback) {
+ EXIClearInterrupts(chan, FALSE, TRUE, FALSE);
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_TC >> (3 * chan));
+ }
+
+ exi->state |= STATE_DMA;
+
+ REG(chan, 1) = (u32)buf & 0x3ffffe0;
+ REG(chan, 2) = (u32)len;
+ REG(chan, 3) = EXI_0CR(1, 1, type, 0);
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+extern u32 __OSGetDIConfig(void);
+
+vu16 __OSDeviceCode : (OS_BASE_CACHED | 0x30E6);
+
+BOOL EXISync(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL rc = FALSE;
+ BOOL enabled;
+
+ while (exi->state & STATE_SELECTED) {
+ if (((REG(chan, 3) & 1) >> 0) == 0) {
+ enabled = OSDisableInterrupts();
+ if (exi->state & STATE_SELECTED) {
+ CompleteTransfer(chan);
+ if (__OSGetDIConfig() != 0xff || exi->immLen != 4 ||
+ (REG(chan, 0) & 0x00000070) != (EXI_FREQ_1M << 4) ||
+ (REG(chan, 4) != EXI_USB_ADAPTER && REG(chan, 4) != EXI_IS_VIEWER &&
+ REG(chan, 4) != 0x04220001) ||
+ __OSDeviceCode == 0x8200) {
+ rc = TRUE;
+ }
+ }
+ OSRestoreInterrupts(enabled);
+ break;
+ }
+ }
+ return rc;
+}
+
+u32 EXIClearInterrupts(s32 chan, BOOL exi, BOOL tc, BOOL ext) {
+ u32 cpr;
+ u32 prev;
+
+ prev = cpr = REG(chan, 0);
+ cpr &= 0x7f5;
+ if (exi)
+ cpr |= 2;
+ if (tc)
+ cpr |= 8;
+ if (ext)
+ cpr |= 0x800;
+ REG(chan, 0) = cpr;
+ return prev;
+}
+
+EXICallback EXISetExiCallback(s32 chan, EXICallback exiCallback) {
+ EXIControl* exi = &Ecb[chan];
+ EXICallback prev;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ prev = exi->exiCallback;
+ exi->exiCallback = exiCallback;
+
+ if (chan != 2) {
+ SetExiInterruptMask(chan, exi);
+ } else {
+ SetExiInterruptMask(0, &Ecb[0]);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+void EXIProbeReset(void) {
+ __EXIProbeStartTime[0] = __EXIProbeStartTime[1] = 0;
+ Ecb[0].idTime = Ecb[1].idTime = 0;
+ __EXIProbe(0);
+ __EXIProbe(1);
+}
+
+static BOOL __EXIProbe(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+ BOOL rc;
+ u32 cpr;
+ s32 t;
+
+ if (chan == 2) {
+ return TRUE;
+ }
+
+ rc = TRUE;
+ enabled = OSDisableInterrupts();
+ cpr = REG(chan, 0);
+ if (!(exi->state & EXI_STATE_ATTACHED)) {
+ if (cpr & 0x00000800) {
+ EXIClearInterrupts(chan, FALSE, FALSE, TRUE);
+ __EXIProbeStartTime[chan] = exi->idTime = 0;
+ }
+
+ if (cpr & 0x00001000) {
+ t = (s32)(OSTicksToMilliseconds(OSGetTime()) / 100) + 1;
+ if (__EXIProbeStartTime[chan] == 0) {
+ __EXIProbeStartTime[chan] = t;
+ }
+ if (t - __EXIProbeStartTime[chan] < 300 / 100) {
+ rc = FALSE;
+ }
+ } else {
+ __EXIProbeStartTime[chan] = exi->idTime = 0;
+ rc = FALSE;
+ }
+ } else if (!(cpr & 0x00001000) || (cpr & 0x00000800)) {
+ __EXIProbeStartTime[chan] = exi->idTime = 0;
+ rc = FALSE;
+ }
+ OSRestoreInterrupts(enabled);
+
+ return rc;
+}
+
+BOOL EXIProbe(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL rc;
+ u32 id;
+
+ rc = __EXIProbe(chan);
+ if (rc && exi->idTime == 0) {
+ rc = EXIGetID(chan, 0, &id) ? TRUE : FALSE;
+ }
+ return rc;
+}
+
+s32 EXIProbeEx(s32 chan) {
+ if (EXIProbe(chan)) {
+ return 1;
+ } else if (__EXIProbeStartTime[chan] != 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static BOOL __EXIAttach(s32 chan, EXICallback extCallback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & EXI_STATE_ATTACHED) || __EXIProbe(chan) == FALSE) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ EXIClearInterrupts(chan, TRUE, FALSE, FALSE);
+
+ exi->extCallback = extCallback;
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXT >> (3 * chan));
+ exi->state |= STATE_ATTACHED;
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+BOOL EXIAttach(s32 chan, EXICallback extCallback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+ BOOL rc;
+
+ EXIProbe(chan);
+
+ enabled = OSDisableInterrupts();
+ if (exi->idTime == 0) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ rc = __EXIAttach(chan, extCallback);
+ OSRestoreInterrupts(enabled);
+ return rc;
+}
+
+BOOL EXIDetach(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if (!(exi->state & STATE_ATTACHED)) {
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ if ((exi->state & STATE_LOCKED) && exi->dev == 0) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->state &= ~STATE_ATTACHED;
+ __OSMaskInterrupts((OS_INTERRUPTMASK_EXI_0_EXT | OS_INTERRUPTMASK_EXI_0_EXI) >> (3 * chan));
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL EXISelect(s32 chan, u32 dev, u32 freq) {
+ EXIControl* exi = &Ecb[chan];
+ u32 cpr;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_SELECTED) ||
+ chan != 2 && (dev == 0 && !(exi->state & STATE_ATTACHED) && !__EXIProbe(chan) ||
+ !(exi->state & STATE_LOCKED) || (exi->dev != dev))) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->state |= STATE_SELECTED;
+ cpr = REG(chan, 0);
+ cpr &= 0x405;
+ cpr |= CPR_CS(dev) | CPR_CLK(freq);
+ REG(chan, 0) = cpr;
+
+ if (exi->state & STATE_ATTACHED) {
+ switch (chan) {
+ case 0:
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXT);
+ break;
+ case 1:
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXT);
+ break;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL EXIDeselect(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ u32 cpr;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if (!(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ exi->state &= ~STATE_SELECTED;
+ cpr = REG(chan, 0);
+ REG(chan, 0) = cpr & 0x405;
+
+ if (exi->state & STATE_ATTACHED) {
+ switch (chan) {
+ case 0:
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXT);
+ break;
+ case 1:
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXT);
+ break;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ if (chan != 2 && (cpr & CPR_CS(0))) {
+ return __EXIProbe(chan) ? TRUE : FALSE;
+ }
+
+ return TRUE;
+}
+
+static void EXIIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - __OS_INTERRUPT_EXI_0_EXI) / 3;
+ exi = &Ecb[chan];
+ EXIClearInterrupts(chan, TRUE, FALSE, FALSE);
+ callback = exi->exiCallback;
+ if (callback) {
+ OSContext exceptionContext;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ callback(chan, context);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+static void TCIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSContext exceptionContext;
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - __OS_INTERRUPT_EXI_0_TC) / 3;
+ exi = &Ecb[chan];
+ __OSMaskInterrupts(OS_INTERRUPTMASK(interrupt));
+ EXIClearInterrupts(chan, FALSE, TRUE, FALSE);
+ callback = exi->tcCallback;
+ if (callback) {
+ exi->tcCallback = 0;
+ CompleteTransfer(chan);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ callback(chan, context);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+static void EXTIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - __OS_INTERRUPT_EXI_0_EXT) / 3;
+ __OSMaskInterrupts((OS_INTERRUPTMASK_EXI_0_EXT | OS_INTERRUPTMASK_EXI_0_EXI) >> (3 * chan));
+ exi = &Ecb[chan];
+ callback = exi->extCallback;
+ exi->state &= ~STATE_ATTACHED;
+ if (callback) {
+ OSContext exceptionContext;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ exi->extCallback = 0;
+ callback(chan, context);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+void EXIInit(void) {
+ OSRegisterVersion(__EXIVersion);
+
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXI | OS_INTERRUPTMASK_EXI_0_TC |
+ OS_INTERRUPTMASK_EXI_0_EXT | OS_INTERRUPTMASK_EXI_1_EXI |
+ OS_INTERRUPTMASK_EXI_1_TC | OS_INTERRUPTMASK_EXI_1_EXT |
+ OS_INTERRUPTMASK_EXI_2_EXI | OS_INTERRUPTMASK_EXI_2_TC);
+
+ REG(0, 0) = 0;
+ REG(1, 0) = 0;
+ REG(2, 0) = 0;
+
+ REG(0, 0) = 0x00002000;
+
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_0_EXI, EXIIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_0_TC, TCIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_0_EXT, EXTIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_1_EXI, EXIIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_1_TC, TCIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_1_EXT, EXTIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_2_EXI, EXIIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_2_TC, TCIntrruptHandler);
+
+ if ((OSGetConsoleType() & 0x10000000) != 0) {
+ __EXIProbeStartTime[0] = __EXIProbeStartTime[1] = 0;
+ Ecb[0].idTime = Ecb[1].idTime = 0;
+ __EXIProbe(0);
+ __EXIProbe(1);
+ }
+}
+
+BOOL EXILock(s32 chan, u32 dev, EXICallback unlockedCallback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+ int i;
+
+ enabled = OSDisableInterrupts();
+ if (exi->state & STATE_LOCKED) {
+ if (unlockedCallback) {
+ for (i = 0; i < exi->items; i++) {
+ if (exi->queue[i].dev == dev) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ }
+ exi->queue[exi->items].callback = unlockedCallback;
+ exi->queue[exi->items].dev = dev;
+ exi->items++;
+ }
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->state |= STATE_LOCKED;
+ exi->dev = dev;
+ SetExiInterruptMask(chan, exi);
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL EXIUnlock(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+ EXICallback unlockedCallback;
+
+ enabled = OSDisableInterrupts();
+ if (!(exi->state & STATE_LOCKED)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ exi->state &= ~STATE_LOCKED;
+ SetExiInterruptMask(chan, exi);
+
+ if (0 < exi->items) {
+ unlockedCallback = exi->queue[0].callback;
+ if (0 < --exi->items) {
+ memmove(&exi->queue[0], &exi->queue[1], sizeof(exi->queue[0]) * exi->items);
+ }
+ unlockedCallback(chan, 0);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+u32 EXIGetState(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+
+ return (u32)exi->state;
+}
+
+static void UnlockedHandler(s32 chan, OSContext* context) {
+ u32 id;
+
+ EXIGetID(chan, 0, &id);
+}
+
+s32 EXIGetID(s32 chan, u32 dev, u32* id) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL err;
+ u32 cmd;
+ s32 startTime;
+ BOOL enabled;
+
+ if (chan < 2 && dev == 0) {
+ if (!__EXIProbe(chan)) {
+ return 0;
+ }
+
+ if (exi->idTime == __EXIProbeStartTime[chan]) {
+ *id = exi->id;
+ return exi->idTime;
+ }
+
+ if (!__EXIAttach(chan, NULL)) {
+ return 0;
+ }
+
+ startTime = __EXIProbeStartTime[chan];
+ }
+
+ err = !EXILock(chan, dev, (chan < 2 && dev == 0) ? UnlockedHandler : NULL);
+ if (!err) {
+ err = !EXISelect(chan, dev, EXI_FREQ_1M);
+ if (!err) {
+ cmd = 0;
+ err |= !EXIImm(chan, &cmd, 2, EXI_WRITE, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, id, 4, EXI_READ, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ }
+ EXIUnlock(chan);
+ }
+
+ if (chan < 2 && dev == 0) {
+ EXIDetach(chan);
+ enabled = OSDisableInterrupts();
+ err |= (startTime != __EXIProbeStartTime[chan]);
+ if (!err) {
+ exi->id = *id;
+ exi->idTime = startTime;
+ }
+ OSRestoreInterrupts(enabled);
+
+ return err ? 0 : exi->idTime;
+ }
+
+ return err ? 0 : !0;
+}
+
+char* EXIGetTypeString(u32 type) {
+ switch (type) {
+ case EXI_MEMORY_CARD_59:
+ return "Memory Card 59";
+ case EXI_MEMORY_CARD_123:
+ return "Memory Card 123";
+ case EXI_MEMORY_CARD_251:
+ return "Memory Card 251";
+ case EXI_MEMORY_CARD_507:
+ return "Memory Card 507";
+ case EXI_USB_ADAPTER:
+ return "USB Adapter";
+ case 0x80000000 | EXI_MEMORY_CARD_59:
+ case 0x80000000 | EXI_MEMORY_CARD_123:
+ case 0x80000000 | EXI_MEMORY_CARD_251:
+ case 0x80000000 | EXI_MEMORY_CARD_507:
+ return "Net Card";
+ case EXI_ETHER_VIEWER:
+ return "Artist Ether";
+ case EXI_STREAM_HANGER:
+ return "Stream Hanger";
+ case EXI_IS_VIEWER:
+ return "IS Viewer";
+ }
+}
+
+#pragma scheduling reset
diff --git a/src/Dolphin/exi/EXIUart.c b/src/Dolphin/exi/EXIUart.c
new file mode 100644
index 0000000..aafc628
--- /dev/null
+++ b/src/Dolphin/exi/EXIUart.c
@@ -0,0 +1,174 @@
+#include "dolphin/os.h"
+
+#define EXI_TX 0x800400u
+#define EXI_MAGIC 0xa5ff005a
+
+static s32 Chan;
+static u32 Dev;
+static u32 Enabled = 0;
+static u32 BarnacleEnabled = 0;
+
+static BOOL ProbeBarnacle(s32 chan, u32 dev, u32* revision) {
+ BOOL err;
+ u32 cmd;
+
+ if (chan != 2 && dev == 0 && !EXIAttach(chan, NULL)) {
+ return FALSE;
+ }
+
+ err = !EXILock(chan, dev, NULL);
+ if (!err) {
+ err = !EXISelect(chan, dev, EXI_FREQ_1M);
+ if (!err) {
+ cmd = 0x20011300;
+ err = FALSE;
+ err |= !EXIImm(chan, &cmd, 4, EXI_WRITE, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, revision, 4, EXI_READ, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ }
+ EXIUnlock(chan);
+ }
+
+ if (chan != 2 && dev == 0) {
+ EXIDetach(chan);
+ }
+
+ if (err) {
+ return FALSE;
+ }
+
+ return (*revision != 0xFFFFFFFF) ? TRUE : FALSE;
+}
+
+void __OSEnableBarnacle(s32 chan, u32 dev) {
+ u32 id;
+
+ if (EXIGetID(chan, dev, &id)) {
+ switch (id) {
+ case 0xffffffff:
+ case EXI_MEMORY_CARD_59:
+ case EXI_MEMORY_CARD_123:
+ case EXI_MEMORY_CARD_251:
+ case EXI_MEMORY_CARD_507:
+ case EXI_USB_ADAPTER:
+ case EXI_NPDP_GDEV:
+ case EXI_MODEM:
+ case EXI_MARLIN:
+ case 0x04220000:
+ case 0x04020100:
+ case 0x04020200:
+ case 0x04020300:
+ case 0x04040404:
+ case 0x04060000:
+ case 0x04120000:
+ case 0x04130000:
+ case 0x80000000 | EXI_MEMORY_CARD_59:
+ case 0x80000000 | EXI_MEMORY_CARD_123:
+ case 0x80000000 | EXI_MEMORY_CARD_251:
+ case 0x80000000 | EXI_MEMORY_CARD_507:
+ break;
+ default:
+ if (ProbeBarnacle(chan, dev, &id)) {
+ Chan = chan;
+ Dev = dev;
+ Enabled = BarnacleEnabled = EXI_MAGIC;
+ }
+ break;
+ }
+ }
+}
+
+u32 InitializeUART(u32 baudRate) {
+ if (BarnacleEnabled == EXI_MAGIC) {
+ return 0;
+ }
+
+ if (!(OSGetConsoleType() & OS_CONSOLE_DEVELOPMENT)) {
+ Enabled = 0;
+ return 2;
+ } else {
+ Chan = 0;
+ Dev = 1;
+ Enabled = EXI_MAGIC;
+ return 0;
+ }
+}
+
+u32 ReadUARTN(void* bytes, unsigned long length) { return 4; }
+
+static int QueueLength(void) {
+ u32 cmd;
+
+ if (!EXISelect(Chan, Dev, EXI_FREQ_8M))
+ return -1;
+
+ cmd = EXI_TX << 6;
+ EXIImm(Chan, &cmd, 4, EXI_WRITE, NULL);
+ EXISync(Chan);
+
+ EXIImm(Chan, &cmd, 1, EXI_READ, NULL);
+ EXISync(Chan);
+ EXIDeselect(Chan);
+
+ return 16 - (int)((cmd >> 24) & 0xff);
+}
+
+u32 WriteUARTN(const void* buf, unsigned long len) {
+ u32 cmd;
+ int qLen;
+ long xLen;
+ char* ptr;
+ BOOL locked;
+ u32 error;
+
+ if (Enabled != EXI_MAGIC)
+ return 2;
+
+ locked = EXILock(Chan, Dev, 0);
+ if (!locked) {
+ return 0;
+ }
+
+ for (ptr = (char*)buf; ptr - buf < len; ptr++) {
+ if (*ptr == '\n')
+ *ptr = '\r';
+ }
+
+ error = 0;
+ cmd = (EXI_TX | 0x2000000) << 6;
+ while (len) {
+ qLen = QueueLength();
+ if (qLen < 0) {
+ error = 3;
+ break;
+ }
+
+ if (qLen < 12 && qLen < len)
+ continue;
+
+ if (!EXISelect(Chan, Dev, EXI_FREQ_8M)) {
+ error = 3;
+ break;
+ }
+
+ EXIImm(Chan, &cmd, 4, EXI_WRITE, NULL);
+ EXISync(Chan);
+
+ while (qLen && len) {
+ if (qLen < 4 && qLen < len)
+ break;
+ xLen = (len < 4) ? (long)len : 4;
+ EXIImm(Chan, (void*)buf, xLen, EXI_WRITE, NULL);
+ (u8*)buf += xLen;
+ len -= xLen;
+ qLen -= xLen;
+ EXISync(Chan);
+ }
+ EXIDeselect(Chan);
+ }
+
+ EXIUnlock(Chan);
+ return error;
+}
diff --git a/src/Dolphin/gx/GXLight.c b/src/Dolphin/gx/GXLight.c
new file mode 100644
index 0000000..a41b90e
--- /dev/null
+++ b/src/Dolphin/gx/GXLight.c
@@ -0,0 +1,271 @@
+#include "dolphin/gx.h"
+#include "dolphin/gx/GXPriv.h"
+
+extern float cosf(float x);
+extern float sqrtf(float x);
+
+#define GX_LARGE_NUMBER 1.0e+18f;
+
+void GXInitLightAttn(GXLightObj* lt_obj, f32 a0, f32 a1, f32 a2, f32 k0, f32 k1, f32 k2) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ obj->a0 = a0;
+ obj->a1 = a1;
+ obj->a2 = a2;
+ obj->k0 = k0;
+ obj->k1 = k1;
+ obj->k2 = k2;
+}
+
+void GXInitLightAttnA(GXLightObj* lt_obj, f32 a0, f32 a1, f32 a2) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ obj->a0 = a0;
+ obj->a1 = a1;
+ obj->a2 = a2;
+}
+
+void GXGetLightAttnA(const GXLightObj* lt_obj, f32* a0, f32* a1, f32* a2) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ *a0 = obj->a0;
+ *a1 = obj->a1;
+ *a2 = obj->a2;
+}
+
+void GXInitLightAttnK(GXLightObj* lt_obj, f32 k0, f32 k1, f32 k2) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ obj->k0 = k0;
+ obj->k1 = k1;
+ obj->k2 = k2;
+}
+
+void GXGetLightAttnK(const GXLightObj* lt_obj, f32* k0, f32* k1, f32* k2) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ *k0 = obj->k0;
+ *k1 = obj->k1;
+ *k2 = obj->k2;
+}
+
+#define PI 3.14159265358979323846F
+
+void GXInitLightSpot(GXLightObj* lt_obj, f32 cutoff, GXSpotFn spot_func) {
+ f32 a0, a1, a2, r, d, cr;
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+
+ if (cutoff <= 0.0f || cutoff > 90.0f) {
+ spot_func = GX_SP_OFF;
+ }
+
+ r = cutoff * PI / 180.0f;
+ cr = cosf(r);
+
+ switch (spot_func) {
+ case GX_SP_FLAT:
+ a0 = -1000.0f * cr;
+ a1 = 1000.0f;
+ a2 = 0.0f;
+ break;
+ case GX_SP_COS:
+ a1 = 1.0f / (1.0f - cr);
+ a0 = -cr * a1;
+ a2 = 0.0f;
+ break;
+ case GX_SP_COS2:
+ a2 = 1.0f / (1.0f - cr);
+ a0 = 0.0f;
+ a1 = -cr * a2;
+ break;
+ case GX_SP_SHARP:
+ d = 1.0F / ((1.0F - cr) * (1.0F - cr));
+ a0 = cr * (cr - 2.0F) * d;
+ a1 = 2.0F * d;
+ a2 = -d;
+ break;
+ case GX_SP_RING1:
+ d = 1.0f / ((1.0f - cr) * (1.0F - cr));
+ a2 = -4.0f * d;
+ a0 = a2 * cr;
+ a1 = 4.0f * (1.0f + cr) * d;
+ break;
+ case GX_SP_RING2:
+ d = 1.0f / ((1.0f - cr) * (1.0F - cr));
+ a0 = 1.0f - 2.0f * cr * cr * d;
+ a1 = 4.0f * cr * d;
+ a2 = -2.0f * d;
+ break;
+ case GX_SP_OFF:
+ default:
+ a0 = 1.0f;
+ a1 = 0.0f;
+ a2 = 0.0f;
+ break;
+ }
+
+ obj->a0 = a0;
+ obj->a1 = a1;
+ obj->a2 = a2;
+}
+
+void GXInitLightDistAttn(GXLightObj* lt_obj, f32 ref_dist, f32 ref_br, GXDistAttnFn dist_func) {
+ f32 k0, k1, k2;
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+
+ if (ref_dist < 0.0F) {
+ dist_func = GX_DA_OFF;
+ }
+
+ if (ref_br <= 0.0F || ref_br >= 1.0F) {
+ dist_func = GX_DA_OFF;
+ }
+
+ switch (dist_func) {
+ case GX_DA_GENTLE:
+ k0 = 1.0F;
+ k1 = (1.0F - ref_br) / (ref_br * ref_dist);
+ k2 = 0.0F;
+ break;
+ case GX_DA_MEDIUM:
+ k0 = 1.0f;
+ k1 = 0.5f * (1.0f - ref_br) / (ref_br * ref_dist);
+ k2 = 0.5f * (1.0f - ref_br) / (ref_br * ref_dist * ref_dist);
+ break;
+ case GX_DA_STEEP:
+ k0 = 1.0f;
+ k1 = 0.0f;
+ k2 = (1.0f - ref_br) / (ref_br * ref_dist * ref_dist);
+ break;
+ case GX_DA_OFF:
+ default:
+ k0 = 1.0f;
+ k1 = 0.0f;
+ k2 = 0.0f;
+ break;
+ }
+
+ obj->k0 = k0;
+ obj->k1 = k1;
+ obj->k2 = k2;
+}
+
+void GXInitLightPos(GXLightObj* lt_obj, f32 x, f32 y, f32 z) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+
+ obj->px = x;
+ obj->py = y;
+ obj->pz = z;
+}
+
+void GXGetLightPos(const GXLightObj* lt_obj, f32* x, f32* y, f32* z) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ *x = obj->px;
+ *y = obj->py;
+ *z = obj->pz;
+}
+
+void GXInitLightDir(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+
+ obj->nx = -nx;
+ obj->ny = -ny;
+ obj->nz = -nz;
+}
+
+void GXGetLightDir(const GXLightObj* lt_obj, f32* nx, f32* ny, f32* nz) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ *nx = -(obj->nx);
+ *ny = -(obj->ny);
+ *nz = -(obj->nz);
+}
+
+void GXInitSpecularDir(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz) {
+ f32 mag;
+ f32 vx, vy, vz;
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+
+ vx = -nx;
+ vy = -ny;
+ vz = (-nz + 1.0F);
+ mag = vx * vx + vy * vy + vz * vz;
+
+ if (mag != 0.0f) {
+ mag = 1.0f / sqrtf(mag);
+ }
+
+ obj->px = vx * mag;
+ obj->py = vy * mag;
+ obj->pz = vz * mag;
+
+ obj->nx = nx * -GX_LARGE_NUMBER;
+ obj->ny = ny * -GX_LARGE_NUMBER;
+ obj->nz = nz * -GX_LARGE_NUMBER;
+}
+
+void GXInitSpecularDirHA(GXLightObj* lt_obj, f32 nx, f32 ny, f32 nz, f32 hx, f32 hy, f32 hz) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+
+ obj->nx = hx;
+ obj->ny = hy;
+ obj->nz = hz;
+
+ obj->px = nx * -GX_LARGE_NUMBER;
+ obj->py = ny * -GX_LARGE_NUMBER;
+ obj->pz = nz * -GX_LARGE_NUMBER;
+}
+
+void GXInitLightColor(GXLightObj* lt_obj, GXColor color) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ obj->color = *(u32*)(&color);
+}
+
+void GXGetLightColor(const GXLightObj* lt_obj, GXColor* color) {
+ GXLightObj_* obj = (GXLightObj_*)lt_obj;
+ *(u32*)color = obj->color;
+}
+
+static inline void PushLight(const register GXLightObj_* lt_obj, register void* dest) {
+ register u32 zero, color;
+ register f32 a0_a1, a2_k0, k1_k2;
+ register f32 px_py, pz_dx, dy_dz;
+
+ asm
+ {
+ lwz color, 12(lt_obj)
+ xor zero, zero, zero
+ psq_l a0_a1, 16(lt_obj), 0, 0
+ psq_l a2_k0, 24(lt_obj), 0, 0
+ psq_l k1_k2, 32(lt_obj), 0, 0
+ psq_l px_py, 40(lt_obj), 0, 0
+ psq_l pz_dx, 48(lt_obj), 0, 0
+ psq_l dy_dz, 56(lt_obj), 0, 0
+
+ stw zero, 0(dest)
+ stw zero, 0(dest)
+ stw zero, 0(dest)
+ stw color, 0(dest)
+ psq_st a0_a1, 0(dest), 0, 0
+ psq_st a2_k0, 0(dest), 0, 0
+ psq_st k1_k2, 0(dest), 0, 0
+ psq_st px_py, 0(dest), 0, 0
+ psq_st pz_dx, 0(dest), 0, 0
+ psq_st dy_dz, 0(dest), 0, 0
+ }
+}
+
+void GXLoadLightObjImm(GXLightObj* lt_obj, GXLightID light) {
+ u32 addr;
+ u32 idx;
+ GXLightObj_* obj;
+ obj = (GXLightObj_*)lt_obj;
+
+ idx = 31 - __cntlzw(light);
+ idx &= 7;
+
+ addr = XF_LIGHT_BASE + idx * XF_LIGHT_SIZE;
+
+ GX_WRITE_U8(16);
+ GX_WRITE_U32(addr | (XF_LIGHT_SIZE - 1) << 16);
+ PushLight(obj, (void*)GX_FIFO_ADDR);
+ __GXData->cpCRreg = 1;
+}
+
+void GXSetChanAmbColor(GXChannelID chan, GXColor color) {
+
+}
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++;
+ }
}
diff --git a/src/Dolphin/pad/PadClamp.c b/src/Dolphin/pad/PadClamp.c
new file mode 100644
index 0000000..6b992ec
--- /dev/null
+++ b/src/Dolphin/pad/PadClamp.c
@@ -0,0 +1,166 @@
+#include <dolphin/pad.h>
+
+#include <math.h>
+
+typedef struct PADClampRegion {
+ u8 minTrigger;
+ u8 maxTrigger;
+ s8 minStick;
+ s8 maxStick;
+ s8 xyStick;
+ s8 minSubstick;
+ s8 maxSubstick;
+ s8 xySubstick;
+ s8 radStick;
+ s8 radSubstick;
+} PADClampRegion;
+
+static const PADClampRegion ClampRegion = {
+ // Triggers
+ 30,
+ 180,
+
+ // Left stick
+ 15,
+ 72,
+ 40,
+
+ // Right stick
+ 15,
+ 59,
+ 31,
+
+ // Stick radii
+ 56,
+ 44,
+};
+
+static void ClampCircle(s8* px, s8* py, s8 radius, s8 min) {
+ s32 x = *px;
+ s32 y = *py;
+ s32 squared;
+ s32 length;
+
+ if (-min < x && x < min) {
+ x = 0;
+ } else if (0 < x) {
+ x -= min;
+ } else {
+ x += min;
+ }
+
+ if (-min < y && y < min) {
+ y = 0;
+ } else if (0 < y) {
+ y -= min;
+ } else {
+ y += min;
+ }
+
+ squared = x * x + y * y;
+ if (radius * radius < squared) {
+ length = sqrt(squared);
+ x = (x * radius) / length;
+ y = (y * radius) / length;
+ }
+
+ *px = x;
+ *py = y;
+}
+
+static void ClampStick(s8* px, s8* py, s8 max, s8 xy, s8 min) {
+ int x = *px;
+ int y = *py;
+ int signX;
+ int signY;
+ int d;
+
+ if (0 <= x) {
+ signX = 1;
+ } else {
+ signX = -1;
+ x = -x;
+ }
+
+ if (0 <= y) {
+ signY = 1;
+ } else {
+ signY = -1;
+ y = -y;
+ }
+
+ if (x <= min) {
+ x = 0;
+ } else {
+ x -= min;
+ }
+ if (y <= min) {
+ y = 0;
+ } else {
+ y -= min;
+ }
+
+ if (x == 0 && y == 0) {
+ *px = *py = 0;
+ return;
+ }
+
+ if (xy * y <= xy * x) {
+ d = xy * x + (max - xy) * y;
+ if (xy * max < d) {
+ x = (s8)(xy * max * x / d);
+ y = (s8)(xy * max * y / d);
+ }
+ } else {
+ d = xy * y + (max - xy) * x;
+ if (xy * max < d) {
+ x = (s8)(xy * max * x / d);
+ y = (s8)(xy * max * y / d);
+ }
+ }
+
+ *px = (s8)(signX * x);
+ *py = (s8)(signY * y);
+}
+
+static void ClampTrigger(u8* trigger, u8 min, u8 max) {
+ if (*trigger <= min) {
+ *trigger = 0;
+ } else {
+ if (max < *trigger) {
+ *trigger = max;
+ }
+ *trigger -= min;
+ }
+}
+
+void PADClamp(PADStatus* status) {
+ int i;
+ for (i = 0; i < PAD_CHANMAX; i++, status++) {
+ if (status->err != PAD_ERR_NONE) {
+ continue;
+ }
+
+ ClampStick(&status->stickX, &status->stickY, ClampRegion.maxStick, ClampRegion.xyStick,
+ ClampRegion.minStick);
+ ClampStick(&status->substickX, &status->substickY, ClampRegion.maxSubstick,
+ ClampRegion.xySubstick, ClampRegion.minSubstick);
+ ClampTrigger(&status->triggerL, ClampRegion.minTrigger, ClampRegion.maxTrigger);
+ ClampTrigger(&status->triggerR, ClampRegion.minTrigger, ClampRegion.maxTrigger);
+ }
+}
+
+void PADClampCircle(PADStatus* status) {
+ u32 i;
+ for (i = 0; i < 4; ++i, status++) {
+ if (status->err != PAD_ERR_NONE) {
+ continue;
+ }
+
+ ClampCircle(&status->stickX, &status->stickY, ClampRegion.radStick, ClampRegion.minStick);
+ ClampCircle(&status->substickX, &status->substickY, ClampRegion.radSubstick,
+ ClampRegion.minSubstick);
+ ClampTrigger(&status->triggerL, ClampRegion.minTrigger, ClampRegion.maxTrigger);
+ ClampTrigger(&status->triggerR, ClampRegion.minTrigger, ClampRegion.maxTrigger);
+ }
+}
diff --git a/src/Dolphin/pad/pad.c b/src/Dolphin/pad/pad.c
new file mode 100644
index 0000000..3ccf7d1
--- /dev/null
+++ b/src/Dolphin/pad/pad.c
@@ -0,0 +1,838 @@
+#include <dolphin/pad.h>
+#include <dolphin/sipriv.h>
+
+const char* __PADVersion = "<< Dolphin SDK - PAD\trelease build: Sep 5 2002 05:34:02 (0x2301) >>";
+
+u8 UnkVal : (OS_BASE_CACHED | 0x30e3);
+u16 __OSWirelessPadFixMode : (OS_BASE_CACHED | 0x30E0);
+
+static void PADTypeAndStatusCallback(s32 chan, u32 type);
+static void PADOriginCallback(s32 chan, u32 error, OSContext* context);
+static void PADProbeCallback(s32 chan, u32 error, OSContext* context);
+static void SPEC0_MakeStatus(s32 chan, PADStatus* status, u32 data[2]);
+static void SPEC1_MakeStatus(s32 chan, PADStatus* status, u32 data[2]);
+static void SPEC2_MakeStatus(s32 chan, PADStatus* status, u32 data[2]);
+static void PADTypeAndStatusCallback(s32 chan, u32 type);
+
+static void PADOriginCallback(s32 chan, u32 error, OSContext* context);
+static void PADProbeCallback(s32 chan, u32 error, OSContext* context);
+
+static void SPEC0_MakeStatus(s32 chan, PADStatus* status, u32 data[2]);
+static void SPEC1_MakeStatus(s32 chan, PADStatus* status, u32 data[2]);
+static void SPEC2_MakeStatus(s32 chan, PADStatus* status, u32 data[2]);
+
+static BOOL Initialized;
+
+static u32 EnabledBits;
+static u32 ResettingBits;
+static s32 ResettingChan = 32;
+static u32 RecalibrateBits;
+static u32 WaitingBits;
+static u32 CheckingBits;
+static u32 PendingBits;
+
+static u32 XPatchBits = PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT;
+
+static u32 AnalogMode = 0x00000300u;
+
+u32 __PADSpec;
+static u32 Spec = 5;
+static void (*MakeStatus)(s32, PADStatus*, u32[2]) = SPEC2_MakeStatus;
+
+static u32 Type[SI_MAX_CHAN];
+static PADStatus Origin[SI_MAX_CHAN];
+
+static u32 CmdReadOrigin = 0x41 << 24;
+static u32 CmdCalibrate = 0x42 << 24;
+static u32 CmdProbeDevice[SI_MAX_CHAN];
+
+static BOOL OnReset(BOOL final);
+
+static OSResetFunctionInfo ResetFunctionInfo = {OnReset, 127};
+
+static void (*SamplingCallback)(void);
+
+static void PADEnable(s32 chan) {
+ u32 cmd;
+ u32 chanBit;
+ u32 data[2];
+
+ chanBit = PAD_CHAN0_BIT >> chan;
+ EnabledBits |= chanBit;
+ SIGetResponse(chan, data);
+ cmd = (0x40 << 16) | AnalogMode;
+ SISetCommand(chan, cmd);
+ SIEnablePolling(EnabledBits);
+}
+
+static void PADDisable(s32 chan) {
+ BOOL enabled;
+ u32 chanBit;
+
+ enabled = OSDisableInterrupts();
+
+ chanBit = PAD_CHAN0_BIT >> chan;
+ SIDisablePolling(chanBit);
+ EnabledBits &= ~chanBit;
+ WaitingBits &= ~chanBit;
+ CheckingBits &= ~chanBit;
+ PendingBits &= ~chanBit;
+ OSSetWirelessID(chan, 0);
+
+ OSRestoreInterrupts(enabled);
+}
+
+static void DoReset(void) {
+ u32 chanBit;
+
+ ResettingChan = __cntlzw(ResettingBits);
+ if (ResettingChan == 32) {
+ return;
+ }
+
+ chanBit = PAD_CHAN0_BIT >> ResettingChan;
+ ResettingBits &= ~chanBit;
+
+ memset(&Origin[ResettingChan], 0, sizeof(PADStatus));
+ SIGetTypeAsync(ResettingChan, PADTypeAndStatusCallback);
+}
+
+static void UpdateOrigin(s32 chan) {
+ PADStatus* origin;
+ u32 chanBit = PAD_CHAN0_BIT >> chan;
+
+ origin = &Origin[chan];
+ switch (AnalogMode & 0x00000700u) {
+ case 0x00000000u:
+ case 0x00000500u:
+ case 0x00000600u:
+ case 0x00000700u:
+ origin->triggerL &= ~15;
+ origin->triggerR &= ~15;
+ origin->analogA &= ~15;
+ origin->analogB &= ~15;
+ break;
+ case 0x00000100u:
+ origin->substickX &= ~15;
+ origin->substickY &= ~15;
+ origin->analogA &= ~15;
+ origin->analogB &= ~15;
+ break;
+ case 0x00000200u:
+ origin->substickX &= ~15;
+ origin->substickY &= ~15;
+ origin->triggerL &= ~15;
+ origin->triggerR &= ~15;
+ break;
+ case 0x00000300u:
+ break;
+ case 0x00000400u:
+ break;
+ }
+
+ origin->stickX -= 128;
+ origin->stickY -= 128;
+ origin->substickX -= 128;
+ origin->substickY -= 128;
+
+ if (XPatchBits & chanBit) {
+ if (64 < origin->stickX && (SIGetType(chan) & 0xffff0000) == SI_GC_CONTROLLER) {
+ origin->stickX = 0;
+ }
+ }
+}
+
+static void PADOriginCallback(s32 chan, u32 error, OSContext* context) {
+ if (!(error &
+ (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION))) {
+ UpdateOrigin(ResettingChan);
+ PADEnable(ResettingChan);
+ }
+ DoReset();
+}
+
+static void PADOriginUpdateCallback(s32 chan, u32 error, OSContext* context) {
+
+ if (!(EnabledBits & (PAD_CHAN0_BIT >> chan))) {
+ return;
+ }
+
+ if (!(error &
+ (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION))) {
+ UpdateOrigin(chan);
+ }
+
+ if (error & SI_ERROR_NO_RESPONSE) {
+ PADDisable(chan);
+ }
+}
+
+static void PADProbeCallback(s32 chan, u32 error, OSContext* context) {
+ if (!(error &
+ (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION))) {
+ PADEnable(ResettingChan);
+ WaitingBits |= PAD_CHAN0_BIT >> ResettingChan;
+ }
+ DoReset();
+}
+
+static void PADTypeAndStatusCallback(s32 chan, u32 type) {
+ u32 chanBit;
+ u32 recalibrate;
+ BOOL rc = TRUE;
+ u32 error;
+ chanBit = PAD_CHAN0_BIT >> ResettingChan;
+ error = type & 0xFF;
+ recalibrate = RecalibrateBits & chanBit;
+ RecalibrateBits &= ~chanBit;
+
+ if (error &
+ (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)) {
+ DoReset();
+ return;
+ }
+
+ type &= ~0xFF;
+
+ Type[ResettingChan] = type;
+
+ if ((type & SI_TYPE_MASK) != SI_TYPE_GC || !(type & SI_GC_STANDARD)) {
+ DoReset();
+ return;
+ }
+
+ if (Spec < PAD_SPEC_2) {
+ PADEnable(ResettingChan);
+ DoReset();
+ return;
+ }
+
+ if (!(type & SI_GC_WIRELESS) || (type & SI_WIRELESS_IR)) {
+ if (recalibrate) {
+ rc = SITransfer(ResettingChan, &CmdCalibrate, 3, &Origin[ResettingChan], 10,
+ PADOriginCallback, 0);
+ } else {
+ rc = SITransfer(ResettingChan, &CmdReadOrigin, 1, &Origin[ResettingChan], 10,
+ PADOriginCallback, 0);
+ }
+ } else if ((type & SI_WIRELESS_FIX_ID) && (type & SI_WIRELESS_CONT_MASK) == SI_WIRELESS_CONT &&
+ !(type & SI_WIRELESS_LITE)) {
+ if (type & SI_WIRELESS_RECEIVED) {
+ rc = SITransfer(ResettingChan, &CmdReadOrigin, 1, &Origin[ResettingChan], 10,
+ PADOriginCallback, 0);
+ } else {
+ rc = SITransfer(ResettingChan, &CmdProbeDevice[ResettingChan], 3, &Origin[ResettingChan], 8,
+ PADProbeCallback, 0);
+ }
+ }
+ if (!rc) {
+ PendingBits |= chanBit;
+ DoReset();
+ return;
+ }
+}
+
+static void PADReceiveCheckCallback(s32 chan, u32 type) {
+ u32 error;
+ u32 chanBit;
+
+ chanBit = PAD_CHAN0_BIT >> chan;
+ if (!(EnabledBits & chanBit)) {
+ return;
+ }
+
+ error = type & 0xFF;
+ type &= ~0xFF;
+
+ WaitingBits &= ~chanBit;
+ CheckingBits &= ~chanBit;
+
+ if (!(error &
+ (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)) &&
+ (type & SI_GC_WIRELESS) && (type & SI_WIRELESS_FIX_ID) && (type & SI_WIRELESS_RECEIVED) &&
+ !(type & SI_WIRELESS_IR) && (type & SI_WIRELESS_CONT_MASK) == SI_WIRELESS_CONT &&
+ !(type & SI_WIRELESS_LITE)) {
+ SITransfer(chan, &CmdReadOrigin, 1, &Origin[chan], 10, PADOriginUpdateCallback, 0);
+ } else {
+ PADDisable(chan);
+ }
+}
+
+BOOL PADReset(u32 mask) {
+ BOOL enabled;
+ u32 diableBits;
+
+ enabled = OSDisableInterrupts();
+
+ mask |= PendingBits;
+ PendingBits = 0;
+ mask &= ~(WaitingBits | CheckingBits);
+ ResettingBits |= mask;
+ diableBits = ResettingBits & EnabledBits;
+ EnabledBits &= ~mask;
+
+ if (Spec == PAD_SPEC_4) {
+ RecalibrateBits |= mask;
+ }
+
+ SIDisablePolling(diableBits);
+
+ if (ResettingChan == 32) {
+ DoReset();
+ }
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL PADRecalibrate(u32 mask) {
+ BOOL enabled;
+ u32 disableBits;
+
+ enabled = OSDisableInterrupts();
+
+ mask |= PendingBits;
+ PendingBits = 0;
+ mask &= ~(WaitingBits | CheckingBits);
+ ResettingBits |= mask;
+ disableBits = ResettingBits & EnabledBits;
+ EnabledBits &= ~mask;
+
+ if (!(UnkVal & 0x40)) {
+ RecalibrateBits |= mask;
+ }
+
+ SIDisablePolling(disableBits);
+ if (ResettingChan == 32) {
+ DoReset();
+ }
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL PADInit() {
+ s32 chan;
+ if (Initialized) {
+ return TRUE;
+ }
+
+ OSRegisterVersion(__PADVersion);
+
+ if (__PADSpec) {
+ PADSetSpec(__PADSpec);
+ }
+
+ Initialized = TRUE;
+
+ if (__PADFixBits != 0) {
+ OSTime time = OSGetTime();
+ __OSWirelessPadFixMode = (u16)((((time)&0xffff) + ((time >> 16) & 0xffff) +
+ ((time >> 32) & 0xffff) + ((time >> 48) & 0xffff)) &
+ 0x3fffu);
+ RecalibrateBits = PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT;
+ }
+
+ for (chan = 0; chan < SI_MAX_CHAN; ++chan) {
+ CmdProbeDevice[chan] = (0x4D << 24) | (chan << 22) | ((__OSWirelessPadFixMode & 0x3fffu) << 8);
+ }
+
+ SIRefreshSamplingRate();
+ OSRegisterResetFunction(&ResetFunctionInfo);
+
+ return PADReset(PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT);
+}
+
+#define offsetof(type, memb) ((u32) & ((type*)0)->memb)
+
+u32 PADRead(PADStatus* status) {
+ BOOL enabled;
+ s32 chan;
+ u32 data[2];
+ u32 chanBit;
+ u32 sr;
+ int chanShift;
+ u32 motor;
+
+ enabled = OSDisableInterrupts();
+
+ motor = 0;
+ for (chan = 0; chan < SI_MAX_CHAN; chan++, status++) {
+ chanBit = PAD_CHAN0_BIT >> chan;
+ chanShift = 8 * (SI_MAX_CHAN - 1 - chan);
+
+ if (PendingBits & chanBit) {
+ PADReset(0);
+ status->err = PAD_ERR_NOT_READY;
+ memset(status, 0, offsetof(PADStatus, err));
+ continue;
+ }
+
+ if ((ResettingBits & chanBit) || ResettingChan == chan) {
+ status->err = PAD_ERR_NOT_READY;
+ memset(status, 0, offsetof(PADStatus, err));
+ continue;
+ }
+
+ if (!(EnabledBits & chanBit)) {
+ status->err = (s8)PAD_ERR_NO_CONTROLLER;
+ memset(status, 0, offsetof(PADStatus, err));
+ continue;
+ }
+
+ if (SIIsChanBusy(chan)) {
+ status->err = PAD_ERR_TRANSFER;
+ memset(status, 0, offsetof(PADStatus, err));
+ continue;
+ }
+
+ sr = SIGetStatus(chan);
+ if (sr & SI_ERROR_NO_RESPONSE) {
+ SIGetResponse(chan, data);
+
+ if (WaitingBits & chanBit) {
+ status->err = (s8)PAD_ERR_NONE;
+ memset(status, 0, offsetof(PADStatus, err));
+
+ if (!(CheckingBits & chanBit)) {
+ CheckingBits |= chanBit;
+ SIGetTypeAsync(chan, PADReceiveCheckCallback);
+ }
+ continue;
+ }
+
+ PADDisable(chan);
+
+ status->err = (s8)PAD_ERR_NO_CONTROLLER;
+ memset(status, 0, offsetof(PADStatus, err));
+ continue;
+ }
+
+ if (!(SIGetType(chan) & SI_GC_NOMOTOR)) {
+ motor |= chanBit;
+ }
+
+ if (!SIGetResponse(chan, data)) {
+ status->err = PAD_ERR_TRANSFER;
+ memset(status, 0, offsetof(PADStatus, err));
+ continue;
+ }
+
+ if (data[0] & 0x80000000) {
+ status->err = PAD_ERR_TRANSFER;
+ memset(status, 0, offsetof(PADStatus, err));
+ continue;
+ }
+
+ MakeStatus(chan, status, data);
+
+ // Check and clear PAD_ORIGIN bit
+ if (status->button & 0x2000) {
+ status->err = PAD_ERR_TRANSFER;
+ memset(status, 0, offsetof(PADStatus, err));
+
+ // Get origin. It is okay if the following transfer fails
+ // since the PAD_ORIGIN bit remains until the read origin
+ // command complete.
+ SITransfer(chan, &CmdReadOrigin, 1, &Origin[chan], 10, PADOriginUpdateCallback, 0);
+ continue;
+ }
+
+ status->err = PAD_ERR_NONE;
+
+ // Clear PAD_INTERFERE bit
+ status->button &= ~0x0080;
+ }
+
+ OSRestoreInterrupts(enabled);
+ return motor;
+}
+
+void PADControlAllMotors(const u32* commandArray) {
+ BOOL enabled;
+ int chan;
+ u32 command;
+ BOOL commit;
+ u32 chanBit;
+
+ enabled = OSDisableInterrupts();
+ commit = FALSE;
+ for (chan = 0; chan < SI_MAX_CHAN; chan++, commandArray++) {
+ chanBit = PAD_CHAN0_BIT >> chan;
+ if ((EnabledBits & chanBit) && !(SIGetType(chan) & SI_GC_NOMOTOR)) {
+ command = *commandArray;
+ if (Spec < PAD_SPEC_2 && command == PAD_MOTOR_STOP_HARD) {
+ command = PAD_MOTOR_STOP;
+ }
+
+ SISetCommand(chan, (0x40 << 16) | AnalogMode | (command & (0x00000001 | 0x00000002)));
+ commit = TRUE;
+ }
+ }
+ if (commit) {
+ SITransferCommands();
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+void PADControlMotor(s32 chan, u32 command) {
+ BOOL enabled;
+ u32 chanBit;
+
+ enabled = OSDisableInterrupts();
+ chanBit = PAD_CHAN0_BIT >> chan;
+ if ((EnabledBits & chanBit) && !(SIGetType(chan) & SI_GC_NOMOTOR)) {
+ if (Spec < PAD_SPEC_2 && command == PAD_MOTOR_STOP_HARD) {
+ command = PAD_MOTOR_STOP;
+ }
+
+ SISetCommand(chan, (0x40 << 16) | AnalogMode | (command & (0x00000001 | 0x00000002)));
+ SITransferCommands();
+ }
+ OSRestoreInterrupts(enabled);
+}
+
+void PADSetSpec(u32 spec) {
+ __PADSpec = 0;
+ switch (spec) {
+ case PAD_SPEC_0:
+ MakeStatus = SPEC0_MakeStatus;
+ break;
+ case PAD_SPEC_1:
+ MakeStatus = SPEC1_MakeStatus;
+ break;
+ case PAD_SPEC_2:
+ case PAD_SPEC_3:
+ case PAD_SPEC_4:
+ case PAD_SPEC_5:
+ MakeStatus = SPEC2_MakeStatus;
+ break;
+ }
+ Spec = spec;
+}
+
+u32 PADGetSpec(void) { return Spec; }
+
+static void SPEC0_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) {
+ status->button = 0;
+ status->button |= ((data[0] >> 16) & 0x0008) ? PAD_BUTTON_A : 0;
+ status->button |= ((data[0] >> 16) & 0x0020) ? PAD_BUTTON_B : 0;
+ status->button |= ((data[0] >> 16) & 0x0100) ? PAD_BUTTON_X : 0;
+ status->button |= ((data[0] >> 16) & 0x0001) ? PAD_BUTTON_Y : 0;
+ status->button |= ((data[0] >> 16) & 0x0010) ? PAD_BUTTON_START : 0;
+ status->stickX = (s8)(data[1] >> 16);
+ status->stickY = (s8)(data[1] >> 24);
+ status->substickX = (s8)(data[1]);
+ status->substickY = (s8)(data[1] >> 8);
+ status->triggerL = (u8)(data[0] >> 8);
+ status->triggerR = (u8)data[0];
+ status->analogA = 0;
+ status->analogB = 0;
+ if (170 <= status->triggerL) {
+ status->button |= PAD_TRIGGER_L;
+ }
+ if (170 <= status->triggerR) {
+ status->button |= PAD_TRIGGER_R;
+ }
+ status->stickX -= 128;
+ status->stickY -= 128;
+ status->substickX -= 128;
+ status->substickY -= 128;
+}
+
+static void SPEC1_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) {
+
+ status->button = 0;
+ status->button |= ((data[0] >> 16) & 0x0080) ? PAD_BUTTON_A : 0;
+ status->button |= ((data[0] >> 16) & 0x0100) ? PAD_BUTTON_B : 0;
+ status->button |= ((data[0] >> 16) & 0x0020) ? PAD_BUTTON_X : 0;
+ status->button |= ((data[0] >> 16) & 0x0010) ? PAD_BUTTON_Y : 0;
+ status->button |= ((data[0] >> 16) & 0x0200) ? PAD_BUTTON_START : 0;
+
+ status->stickX = (s8)(data[1] >> 16);
+ status->stickY = (s8)(data[1] >> 24);
+ status->substickX = (s8)(data[1]);
+ status->substickY = (s8)(data[1] >> 8);
+
+ status->triggerL = (u8)(data[0] >> 8);
+ status->triggerR = (u8)data[0];
+
+ status->analogA = 0;
+ status->analogB = 0;
+
+ if (170 <= status->triggerL) {
+ status->button |= PAD_TRIGGER_L;
+ }
+ if (170 <= status->triggerR) {
+ status->button |= PAD_TRIGGER_R;
+ }
+
+ status->stickX -= 128;
+ status->stickY -= 128;
+ status->substickX -= 128;
+ status->substickY -= 128;
+}
+
+static s8 ClampS8(s8 var, s8 org) {
+ if (0 < org) {
+ s8 min = (s8)(-128 + org);
+ if (var < min) {
+ var = min;
+ }
+ } else if (org < 0) {
+ s8 max = (s8)(127 + org);
+ if (max < var) {
+ var = max;
+ }
+ }
+ return var -= org;
+}
+
+static u8 ClampU8(u8 var, u8 org) {
+ if (var < org) {
+ var = org;
+ }
+ return var -= org;
+}
+
+#define PAD_ALL \
+ (PAD_BUTTON_LEFT | PAD_BUTTON_RIGHT | PAD_BUTTON_DOWN | PAD_BUTTON_UP | PAD_TRIGGER_Z | \
+ PAD_TRIGGER_R | PAD_TRIGGER_L | PAD_BUTTON_A | PAD_BUTTON_B | PAD_BUTTON_X | PAD_BUTTON_Y | \
+ PAD_BUTTON_MENU | 0x2000 | 0x0080)
+
+static void SPEC2_MakeStatus(s32 chan, PADStatus* status, u32 data[2]) {
+ PADStatus* origin;
+
+ status->button = (u16)((data[0] >> 16) & PAD_ALL);
+ status->stickX = (s8)(data[0] >> 8);
+ status->stickY = (s8)(data[0]);
+
+ switch (AnalogMode & 0x00000700) {
+ case 0x00000000:
+ case 0x00000500:
+ case 0x00000600:
+ case 0x00000700:
+ status->substickX = (s8)(data[1] >> 24);
+ status->substickY = (s8)(data[1] >> 16);
+ status->triggerL = (u8)(((data[1] >> 12) & 0x0f) << 4);
+ status->triggerR = (u8)(((data[1] >> 8) & 0x0f) << 4);
+ status->analogA = (u8)(((data[1] >> 4) & 0x0f) << 4);
+ status->analogB = (u8)(((data[1] >> 0) & 0x0f) << 4);
+ break;
+ case 0x00000100:
+ status->substickX = (s8)(((data[1] >> 28) & 0x0f) << 4);
+ status->substickY = (s8)(((data[1] >> 24) & 0x0f) << 4);
+ status->triggerL = (u8)(data[1] >> 16);
+ status->triggerR = (u8)(data[1] >> 8);
+ status->analogA = (u8)(((data[1] >> 4) & 0x0f) << 4);
+ status->analogB = (u8)(((data[1] >> 0) & 0x0f) << 4);
+ break;
+ case 0x00000200:
+ status->substickX = (s8)(((data[1] >> 28) & 0x0f) << 4);
+ status->substickY = (s8)(((data[1] >> 24) & 0x0f) << 4);
+ status->triggerL = (u8)(((data[1] >> 20) & 0x0f) << 4);
+ status->triggerR = (u8)(((data[1] >> 16) & 0x0f) << 4);
+ status->analogA = (u8)(data[1] >> 8);
+ status->analogB = (u8)(data[1] >> 0);
+ break;
+ case 0x00000300:
+ status->substickX = (s8)(data[1] >> 24);
+ status->substickY = (s8)(data[1] >> 16);
+ status->triggerL = (u8)(data[1] >> 8);
+ status->triggerR = (u8)(data[1] >> 0);
+ status->analogA = 0;
+ status->analogB = 0;
+ break;
+ case 0x00000400:
+ status->substickX = (s8)(data[1] >> 24);
+ status->substickY = (s8)(data[1] >> 16);
+ status->triggerL = 0;
+ status->triggerR = 0;
+ status->analogA = (u8)(data[1] >> 8);
+ status->analogB = (u8)(data[1] >> 0);
+ break;
+ }
+
+ status->stickX -= 128;
+ status->stickY -= 128;
+ status->substickX -= 128;
+ status->substickY -= 128;
+
+ origin = &Origin[chan];
+ status->stickX = ClampS8(status->stickX, origin->stickX);
+ status->stickY = ClampS8(status->stickY, origin->stickY);
+ status->substickX = ClampS8(status->substickX, origin->substickX);
+ status->substickY = ClampS8(status->substickY, origin->substickY);
+ status->triggerL = ClampU8(status->triggerL, origin->triggerL);
+ status->triggerR = ClampU8(status->triggerR, origin->triggerR);
+}
+
+BOOL PADGetType(s32 chan, u32* type) {
+ u32 chanBit;
+
+ *type = SIGetType(chan);
+ chanBit = PAD_CHAN0_BIT >> chan;
+ if ((ResettingBits & chanBit) || ResettingChan == chan || !(EnabledBits & chanBit)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL PADSync(void) { return ResettingBits == 0 && ResettingChan == 32 && !SIBusy(); }
+
+void PADSetAnalogMode(u32 mode) {
+ BOOL enabled;
+ u32 mask;
+
+ enabled = OSDisableInterrupts();
+ AnalogMode = mode << 8;
+ mask = EnabledBits;
+
+ EnabledBits &= ~mask;
+ WaitingBits &= ~mask;
+ CheckingBits &= ~mask;
+
+ SIDisablePolling(mask);
+ OSRestoreInterrupts(enabled);
+}
+
+static BOOL OnReset(BOOL f) {
+ static BOOL recalibrated = FALSE;
+ BOOL sync;
+
+ if (SamplingCallback) {
+ PADSetSamplingCallback(NULL);
+ }
+
+ if (!f) {
+ sync = PADSync();
+ if (!recalibrated && sync) {
+ recalibrated = PADRecalibrate(PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT);
+ return FALSE;
+ }
+ return sync;
+ } else {
+ recalibrated = FALSE;
+ }
+
+ return TRUE;
+}
+
+void __PADDisableXPatch(void) { XPatchBits = 0; }
+
+static void SamplingHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSContext exceptionContext;
+
+ if (SamplingCallback) {
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+ SamplingCallback();
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+#ifdef FULL_FRANK
+PADSamplingCallback PADSetSamplingCallback(PADSamplingCallback callback) {
+ PADSamplingCallback prev;
+
+ prev = SamplingCallback;
+ SamplingCallback = callback;
+ if (callback) {
+ SIRegisterPollingHandler(SamplingHandler);
+ } else {
+ SIUnregisterPollingHandler(SamplingHandler);
+ }
+ return prev;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm PADSamplingCallback PADSetSamplingCallback(PADSamplingCallback callback) {
+ nofralloc
+ mflr r0
+ cmplwi r3, 0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ lwz r31, SamplingCallback
+ stw r3, SamplingCallback
+ beq lbl_803875E4
+ lis r3, SamplingHandler@ha
+ addi r3, r3, SamplingHandler@l
+ bl SIRegisterPollingHandler
+ b lbl_803875F0
+lbl_803875E4:
+ lis r3, SamplingHandler@ha
+ addi r3, r3, SamplingHandler@l
+ bl SIUnregisterPollingHandler
+lbl_803875F0:
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+#pragma pop
+#endif
+
+#ifdef FULL_FRANK
+BOOL __PADDisableRecalibration(BOOL disable) {
+ BOOL enabled;
+ BOOL prev;
+
+ enabled = OSDisableInterrupts();
+ prev = (UnkVal & 0x40) ? TRUE : FALSE;
+ UnkVal &= (u8)~0x40;
+ if (disable) {
+ UnkVal |= 0x40;
+ }
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+#else
+/* clang-format off */
+#pragma push
+#pragma optimization_level 0
+#pragma optimizewithasm off
+asm BOOL __PADDisableRecalibration(BOOL disable) {
+ nofralloc
+ mflr r0
+ stw r0, 4(r1)
+ stwu r1, -0x18(r1)
+ stw r31, 0x14(r1)
+ stw r30, 0x10(r1)
+ mr r30, r3
+ bl OSDisableInterrupts
+ lis r4, UnkVal@ha
+ lbz r0, UnkVal@l(r4)
+ rlwinm. r0, r0, 0, 0x19, 0x19
+ beq lbl_8038763C
+ li r31, 1
+ b lbl_80387640
+lbl_8038763C:
+ li r31, 0
+lbl_80387640:
+ lis r4, UnkVal@ha
+ lbz r0, UnkVal@l(r4)
+ andi. r0, r0, 0xbf
+ cmpwi r30, 0
+ stb r0, UnkVal@l(r4)
+ beq lbl_80387664
+ lbz r0, UnkVal@l(r4)
+ ori r0, r0, 0x40
+ stb r0, UnkVal@l(r4)
+lbl_80387664:
+ bl OSRestoreInterrupts
+ mr r3, r31
+ lwz r0, 0x1c(r1)
+ lwz r31, 0x14(r1)
+ lwz r30, 0x10(r1)
+ addi r1, r1, 0x18
+ mtlr r0
+ blr
+}
+/* clang-format on */
+#pragma pop
+#endif
diff --git a/src/Dolphin/si/SIBios.c b/src/Dolphin/si/SIBios.c
new file mode 100644
index 0000000..00c6447
--- /dev/null
+++ b/src/Dolphin/si/SIBios.c
@@ -0,0 +1,772 @@
+#include <dolphin/OSRtcPriv.h>
+#include <dolphin/os.h>
+#include <dolphin/sipriv.h>
+
+vu32 __SIRegs[64] : 0xCC006400;
+
+extern OSTime __OSGetSystemTime();
+
+const char* __SIVersion = "<< Dolphin SDK - SI\trelease build: Sep 5 2002 05:33:08 (0x2301) >>";
+
+typedef struct SIControl {
+ s32 chan;
+ u32 poll;
+ u32 inputBytes;
+ void* input;
+ SICallback callback;
+} SIControl;
+
+static SIControl Si = {
+ -1, 0, 0, NULL, NULL,
+};
+
+typedef struct SIComm_s {
+ u32 tcint : 1;
+ u32 tcintmsk : 1;
+ u32 comerr : 1;
+ u32 rdstint : 1;
+ u32 rdstintmsk : 1;
+ u32 pad0 : 4;
+ u32 outlngth : 7;
+ u32 pad1 : 1;
+ u32 inlngth : 7;
+ u32 pad2 : 5;
+ u32 channel : 2;
+ u32 tstart : 1;
+} SIComm_s;
+
+typedef union SIComm_u {
+ u32 val;
+ SIComm_s f;
+} SIComm_u;
+
+static SIPacket Packet[SI_MAX_CHAN];
+static OSAlarm Alarm[SI_MAX_CHAN];
+static u32 Type[SI_MAX_CHAN] = {
+ SI_ERROR_NO_RESPONSE,
+ SI_ERROR_NO_RESPONSE,
+ SI_ERROR_NO_RESPONSE,
+ SI_ERROR_NO_RESPONSE,
+};
+
+static OSTime TypeTime[SI_MAX_CHAN];
+static OSTime XferTime[SI_MAX_CHAN];
+
+static SITypeAndStatusCallback TypeCallback[SI_MAX_CHAN][4];
+static __OSInterruptHandler RDSTHandler[4];
+
+u32 __PADFixBits;
+
+static BOOL __SITransfer(s32 chan, void* output, u32 outputBytes, void* input, u32 inputBytes,
+ SICallback callback);
+
+static BOOL InputBufferValid[SI_MAX_CHAN];
+static u32 InputBuffer[SI_MAX_CHAN][2];
+static vu32 InputBufferVcount[SI_MAX_CHAN];
+
+static BOOL SIGetResponseRaw(s32 chan);
+static void GetTypeCallback(s32 chan, u32 error, OSContext* context);
+
+BOOL SIBusy() { return Si.chan != -1 ? TRUE : FALSE; }
+
+BOOL SIIsChanBusy(s32 chan) { return (Packet[chan].chan != -1 || Si.chan == chan); }
+
+static void SIClearTCInterrupt() {
+ u32 reg;
+
+ reg = __SIRegs[13];
+ reg |= 0x80000000;
+ reg &= ~0x00000001;
+ __SIRegs[13] = reg;
+}
+
+static u32 CompleteTransfer() {
+ u32 sr;
+ u32 i;
+ u32 rLen;
+ u8* input;
+
+ sr = __SIRegs[14];
+
+ SIClearTCInterrupt();
+
+ if (Si.chan != -1) {
+ XferTime[Si.chan] = __OSGetSystemTime();
+
+ input = Si.input;
+
+ rLen = Si.inputBytes / 4;
+ for (i = 0; i < rLen; i++) {
+ *(u32*)input = __SIRegs[32 + i];
+ input += 4;
+ }
+
+ rLen = Si.inputBytes & 3;
+ if (rLen) {
+ u32 temp = __SIRegs[32 + i];
+ for (i = 0; i < rLen; i++) {
+ *input++ = (u8)((temp >> ((3 - i) * 8)) & 0xff);
+ }
+ }
+
+ if (__SIRegs[13] & 0x20000000) {
+ sr >>= 8 * (3 - Si.chan);
+ sr &= 0xf;
+
+ if ((sr & SI_ERROR_NO_RESPONSE) && !(Type[Si.chan] & SI_ERROR_BUSY)) {
+ Type[Si.chan] = SI_ERROR_NO_RESPONSE;
+ }
+ if (sr == 0) {
+ sr = SI_ERROR_COLLISION;
+ }
+ } else {
+ TypeTime[Si.chan] = __OSGetSystemTime();
+ sr = 0;
+ }
+
+ Si.chan = -1;
+ }
+ return sr;
+}
+
+static void SITransferNext(s32 chan) {
+ int i;
+ SIPacket* packet;
+
+ for (i = 0; i < SI_MAX_CHAN; ++i) {
+ ++chan;
+ chan %= SI_MAX_CHAN;
+ packet = &Packet[chan];
+ if (packet->chan != -1 && packet->fire <= __OSGetSystemTime()) {
+ if (__SITransfer(packet->chan, packet->output, packet->outputBytes, packet->input,
+ packet->inputBytes, packet->callback)) {
+ OSCancelAlarm(&Alarm[chan]);
+ packet->chan = -1;
+ }
+ break;
+ }
+ }
+}
+
+static void SIInterruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ u32 reg;
+
+ reg = __SIRegs[13];
+
+ if ((reg & 0xc0000000) == 0xc0000000) {
+ s32 chan;
+ u32 sr;
+ SICallback callback;
+
+ chan = Si.chan;
+ sr = CompleteTransfer();
+ callback = Si.callback;
+ Si.callback = 0;
+
+ SITransferNext(chan);
+
+ if (callback) {
+ callback(chan, sr, context);
+ }
+
+ sr = __SIRegs[14];
+ sr &= 0xf000000 >> (8 * chan);
+ __SIRegs[14] = sr;
+
+ if (Type[chan] == SI_ERROR_BUSY && !SIIsChanBusy(chan)) {
+ static u32 cmdTypeAndStatus = 0 << 24;
+ SITransfer(chan, &cmdTypeAndStatus, 1, &Type[chan], 3, GetTypeCallback,
+ OSMicrosecondsToTicks(65));
+ }
+ }
+
+ if ((reg & 0x18000000) == 0x18000000) {
+
+ int i;
+ u32 vcount;
+ u32 x;
+
+ vcount = VIGetCurrentLine() + 1;
+ x = (Si.poll & 0x03ff0000) >> 16;
+
+ for (i = 0; i < SI_MAX_CHAN; ++i) {
+ if (SIGetResponseRaw(i)) {
+ InputBufferVcount[i] = vcount;
+ }
+ }
+
+ for (i = 0; i < SI_MAX_CHAN; ++i) {
+ if (!(Si.poll & (SI_CHAN0_BIT >> (31 - 7 + i)))) {
+ continue;
+ }
+ if (InputBufferVcount[i] == 0 || InputBufferVcount[i] + (x / 2) < vcount) {
+ return;
+ }
+ }
+
+ for (i = 0; i < SI_MAX_CHAN; ++i) {
+ InputBufferVcount[i] = 0;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ if (RDSTHandler[i]) {
+ RDSTHandler[i](interrupt, context);
+ }
+ }
+ }
+}
+
+static BOOL SIEnablePollingInterrupt(BOOL enable) {
+ BOOL enabled;
+ BOOL rc;
+ u32 reg;
+ int i;
+
+ enabled = OSDisableInterrupts();
+ reg = __SIRegs[13];
+ rc = (reg & 0x08000000) ? TRUE : FALSE;
+ if (enable) {
+ reg |= 0x08000000;
+ for (i = 0; i < SI_MAX_CHAN; ++i) {
+ InputBufferVcount[i] = 0;
+ }
+ } else {
+ reg &= ~0x08000000;
+ }
+ reg &= ~0x80000001;
+ __SIRegs[13] = reg;
+ OSRestoreInterrupts(enabled);
+ return rc;
+}
+
+BOOL SIRegisterPollingHandler(__OSInterruptHandler handler) {
+ BOOL enabled;
+ int i;
+
+ enabled = OSDisableInterrupts();
+ for (i = 0; i < 4; ++i) {
+ if (RDSTHandler[i] == handler) {
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ }
+ for (i = 0; i < 4; ++i) {
+ if (RDSTHandler[i] == 0) {
+ RDSTHandler[i] = handler;
+ SIEnablePollingInterrupt(TRUE);
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ }
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+}
+
+BOOL SIUnregisterPollingHandler(__OSInterruptHandler handler) {
+ BOOL enabled;
+ int i;
+
+ enabled = OSDisableInterrupts();
+ for (i = 0; i < 4; ++i) {
+ if (RDSTHandler[i] == handler) {
+ RDSTHandler[i] = 0;
+ for (i = 0; i < 4; ++i) {
+ if (RDSTHandler[i]) {
+ break;
+ }
+ }
+ if (i == 4) {
+ SIEnablePollingInterrupt(FALSE);
+ }
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ break;
+ }
+ }
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+}
+
+void SIInit(void) {
+ OSRegisterVersion(__SIVersion);
+
+ Packet[0].chan = Packet[1].chan = Packet[2].chan = Packet[3].chan = -1;
+
+ Si.poll = 0;
+ SISetSamplingRate(0);
+
+ while (__SIRegs[13] & 1)
+ ;
+
+ __SIRegs[13] = 0x80000000;
+
+ __OSSetInterruptHandler(__OS_INTERRUPT_PI_SI, SIInterruptHandler);
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_PI_SI);
+
+ SIGetType(0);
+ SIGetType(1);
+ SIGetType(2);
+ SIGetType(3);
+}
+
+#define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1))
+
+static BOOL __SITransfer(s32 chan, void* output, u32 outputBytes, void* input, u32 inputBytes,
+ SICallback callback) {
+ BOOL enabled;
+ u32 rLen;
+ u32 i;
+ u32 sr;
+ SIComm_u comcsr;
+
+ enabled = OSDisableInterrupts();
+ if (Si.chan != -1) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ sr = __SIRegs[14];
+ sr &= (0xf000000) >> (8 * chan);
+ __SIRegs[14] = sr;
+
+ Si.chan = chan;
+ Si.callback = callback;
+ Si.inputBytes = inputBytes;
+ Si.input = input;
+
+ rLen = ROUND(outputBytes, 4) / 4;
+ for (i = 0; i < rLen; i++) {
+ __SIRegs[32 + i] = ((u32*)output)[i];
+ }
+
+ comcsr.val = __SIRegs[13];
+ comcsr.f.tcint = 1;
+ comcsr.f.tcintmsk = callback ? 1 : 0;
+ comcsr.f.outlngth = (outputBytes == SI_MAX_COMCSR_OUTLNGTH) ? 0 : outputBytes;
+ comcsr.f.inlngth = (inputBytes == SI_MAX_COMCSR_INLNGTH) ? 0 : inputBytes;
+ comcsr.f.channel = chan;
+ comcsr.f.tstart = 1;
+ __SIRegs[13] = comcsr.val;
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+u32 SISync(void) {
+ BOOL enabled;
+ u32 sr;
+
+ while (__SIRegs[13] & 1)
+ ;
+
+ enabled = OSDisableInterrupts();
+ sr = CompleteTransfer();
+
+ SITransferNext(SI_MAX_CHAN);
+
+ OSRestoreInterrupts(enabled);
+
+ return sr;
+}
+
+u32 SIGetStatus(s32 chan) {
+ BOOL enabled;
+ u32 sr;
+ int chanShift;
+
+ enabled = OSDisableInterrupts();
+ sr = __SIRegs[14];
+ chanShift = 8 * (SI_MAX_CHAN - 1 - chan);
+ sr >>= chanShift;
+ if (sr & SI_ERROR_NO_RESPONSE) {
+ if (!(Type[chan] & SI_ERROR_BUSY)) {
+ Type[chan] = SI_ERROR_NO_RESPONSE;
+ }
+ }
+ OSRestoreInterrupts(enabled);
+ return sr;
+}
+
+void SISetCommand(s32 chan, u32 command) { __SIRegs[3 * chan] = command; }
+
+u32 SIGetCommand(s32 chan) { return __SIRegs[3 * chan]; }
+
+void SITransferCommands(void) { __SIRegs[14] = 0x80000000; }
+
+u32 SISetXY(u32 x, u32 y) {
+ u32 poll;
+ BOOL enabled;
+
+ poll = x << 16;
+ poll |= y << 8;
+
+ enabled = OSDisableInterrupts();
+ Si.poll &= ~(0x03ff0000 | 0x0000ff00);
+ Si.poll |= poll;
+ poll = Si.poll;
+ __SIRegs[12] = poll;
+ OSRestoreInterrupts(enabled);
+ return poll;
+}
+
+u32 SIEnablePolling(u32 poll) {
+ BOOL enabled;
+ u32 en;
+
+ if (poll == 0) {
+ return Si.poll;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ poll >>= (31 - 7);
+ en = poll & 0xf0;
+
+ poll &= (en >> 4) | 0x03fffff0;
+
+ poll &= ~0x03ffff00;
+
+ Si.poll &= ~(en >> 4);
+
+ Si.poll |= poll;
+
+ poll = Si.poll;
+
+ SITransferCommands();
+
+ __SIRegs[12] = poll;
+
+ OSRestoreInterrupts(enabled);
+
+ return poll;
+}
+
+u32 SIDisablePolling(u32 poll) {
+ BOOL enabled;
+
+ if (poll == 0) {
+ return Si.poll;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ poll >>= (31 - 7);
+ poll &= 0xf0;
+
+ poll = Si.poll & ~poll;
+
+ __SIRegs[12] = poll;
+ Si.poll = poll;
+
+ OSRestoreInterrupts(enabled);
+ return poll;
+}
+
+static BOOL SIGetResponseRaw(s32 chan) {
+ u32 sr;
+
+ sr = SIGetStatus(chan);
+ if (sr & SI_ERROR_RDST) {
+ InputBuffer[chan][0] = __SIRegs[3 * chan + 1];
+ InputBuffer[chan][1] = __SIRegs[3 * chan + 2];
+ InputBufferValid[chan] = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL SIGetResponse(s32 chan, void* data) {
+ BOOL rc;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ SIGetResponseRaw(chan);
+ rc = InputBufferValid[chan];
+ InputBufferValid[chan] = FALSE;
+ if (rc) {
+ ((u32*)data)[0] = InputBuffer[chan][0];
+ ((u32*)data)[1] = InputBuffer[chan][1];
+ }
+ OSRestoreInterrupts(enabled);
+ return rc;
+}
+
+static void AlarmHandler(OSAlarm* alarm, OSContext* context) {
+#pragma unused(context)
+ s32 chan;
+ SIPacket* packet;
+
+ chan = alarm - Alarm;
+ packet = &Packet[chan];
+ if (packet->chan != -1) {
+ if (__SITransfer(packet->chan, packet->output, packet->outputBytes, packet->input,
+ packet->inputBytes, packet->callback)) {
+ packet->chan = -1;
+ }
+ }
+}
+
+BOOL SITransfer(s32 chan, void* output, u32 outputBytes, void* input, u32 inputBytes,
+ SICallback callback, OSTime delay) {
+ BOOL enabled;
+ SIPacket* packet = &Packet[chan];
+ OSTime now;
+ OSTime fire;
+
+ enabled = OSDisableInterrupts();
+ if (packet->chan != -1 || Si.chan == chan) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ now = __OSGetSystemTime();
+ if (delay == 0) {
+ fire = now;
+ } else {
+ fire = XferTime[chan] + delay;
+ }
+ if (now < fire) {
+ delay = fire - now;
+ OSSetAlarm(&Alarm[chan], delay, AlarmHandler);
+ } else if (__SITransfer(chan, output, outputBytes, input, inputBytes, callback)) {
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+
+ packet->chan = chan;
+ packet->output = output;
+ packet->outputBytes = outputBytes;
+ packet->input = input;
+ packet->inputBytes = inputBytes;
+ packet->callback = callback;
+ packet->fire = fire;
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+static void CallTypeAndStatusCallback(s32 chan, u32 type) {
+ SITypeAndStatusCallback callback;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ callback = TypeCallback[chan][i];
+ if (callback) {
+ TypeCallback[chan][i] = 0;
+ callback(chan, type);
+ }
+ }
+}
+
+static void GetTypeCallback(s32 chan, u32 error, OSContext* context) {
+ static u32 cmdFixDevice[SI_MAX_CHAN];
+ u32 type;
+ u32 chanBit;
+ BOOL fix;
+ u32 id;
+
+ Type[chan] &= ~SI_ERROR_BUSY;
+ Type[chan] |= error;
+ TypeTime[chan] = __OSGetSystemTime();
+
+ type = Type[chan];
+
+ chanBit = SI_CHAN0_BIT >> chan;
+ fix = (BOOL)(__PADFixBits & chanBit);
+ __PADFixBits &= ~chanBit;
+
+ if ((error &
+ (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_NO_RESPONSE | SI_ERROR_COLLISION)) ||
+ (type & SI_TYPE_MASK) != SI_TYPE_DOLPHIN || !(type & SI_GC_WIRELESS) ||
+ (type & SI_WIRELESS_IR)) {
+ OSSetWirelessID(chan, 0);
+ CallTypeAndStatusCallback(chan, Type[chan]);
+ return;
+ }
+
+ id = (u32)(OSGetWirelessID(chan) << 8);
+
+ if (fix && (id & SI_WIRELESS_FIX_ID)) {
+ cmdFixDevice[chan] = 0x4Eu << 24 | (id & SI_WIRELESS_TYPE_ID) | SI_WIRELESS_FIX_ID;
+ Type[chan] = SI_ERROR_BUSY;
+ SITransfer(chan, &cmdFixDevice[chan], 3, &Type[chan], 3, GetTypeCallback, 0);
+ return;
+ }
+
+ if (type & SI_WIRELESS_FIX_ID) {
+ if ((id & SI_WIRELESS_TYPE_ID) != (type & SI_WIRELESS_TYPE_ID)) {
+ if (!(id & SI_WIRELESS_FIX_ID)) {
+ id = type & SI_WIRELESS_TYPE_ID;
+ id |= SI_WIRELESS_FIX_ID;
+ OSSetWirelessID(chan, (u16)((id >> 8) & 0xffff));
+ }
+
+ cmdFixDevice[chan] = 0x4E << 24 | id;
+ Type[chan] = SI_ERROR_BUSY;
+ SITransfer(chan, &cmdFixDevice[chan], 3, &Type[chan], 3, GetTypeCallback, 0);
+ return;
+ }
+ } else if (type & SI_WIRELESS_RECEIVED) {
+ id = type & SI_WIRELESS_TYPE_ID;
+ id |= SI_WIRELESS_FIX_ID;
+
+ OSSetWirelessID(chan, (u16)((id >> 8) & 0xffff));
+
+ cmdFixDevice[chan] = 0x4E << 24 | id;
+ Type[chan] = SI_ERROR_BUSY;
+ SITransfer(chan, &cmdFixDevice[chan], 3, &Type[chan], 3, GetTypeCallback, 0);
+ return;
+ } else {
+ OSSetWirelessID(chan, 0);
+ }
+
+ CallTypeAndStatusCallback(chan, Type[chan]);
+}
+
+u32 SIGetType(s32 chan) {
+ static u32 cmdTypeAndStatus;
+ BOOL enabled;
+ u32 type;
+ OSTime diff;
+
+ enabled = OSDisableInterrupts();
+
+ type = Type[chan];
+ diff = __OSGetSystemTime() - TypeTime[chan];
+ if (Si.poll & (0x80 >> chan)) {
+ if (type != SI_ERROR_NO_RESPONSE) {
+ TypeTime[chan] = __OSGetSystemTime();
+ OSRestoreInterrupts(enabled);
+ return type;
+ } else {
+ type = Type[chan] = SI_ERROR_BUSY;
+ }
+ } else if (diff <= OSMillisecondsToTicks(50) && type != SI_ERROR_NO_RESPONSE) {
+ OSRestoreInterrupts(enabled);
+ return type;
+ } else if (diff <= OSMillisecondsToTicks(75)) {
+ Type[chan] = SI_ERROR_BUSY;
+ } else {
+ type = Type[chan] = SI_ERROR_BUSY;
+ }
+ TypeTime[chan] = __OSGetSystemTime();
+
+ SITransfer(chan, &cmdTypeAndStatus, 1, &Type[chan], 3, GetTypeCallback,
+ OSMicrosecondsToTicks(65));
+
+ OSRestoreInterrupts(enabled);
+ return type;
+}
+
+u32 SIGetTypeAsync(s32 chan, SITypeAndStatusCallback callback) {
+ BOOL enabled;
+ u32 type;
+
+ enabled = OSDisableInterrupts();
+ type = SIGetType(chan);
+ if (Type[chan] & SI_ERROR_BUSY) {
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if (TypeCallback[chan][i] == callback) {
+ break;
+ }
+ if (TypeCallback[chan][i] == 0) {
+ TypeCallback[chan][i] = callback;
+ break;
+ }
+ }
+ } else {
+ callback(chan, type);
+ }
+ OSRestoreInterrupts(enabled);
+ return type;
+}
+
+u32 SIDecodeType(u32 type) {
+ u32 error;
+
+ error = type & 0xff;
+ type &= ~0xff;
+
+ if (error & SI_ERROR_NO_RESPONSE) {
+ return SI_ERROR_NO_RESPONSE;
+ }
+ if (error & (SI_ERROR_UNDER_RUN | SI_ERROR_OVER_RUN | SI_ERROR_COLLISION | SI_ERROR_UNKNOWN)) {
+ return SI_ERROR_UNKNOWN;
+ }
+ if (error) {
+ return SI_ERROR_BUSY;
+ }
+
+ if ((type & SI_TYPE_MASK) == SI_TYPE_N64) {
+ switch (type & 0xffff0000) {
+ case SI_N64_CONTROLLER:
+ case SI_N64_MIC:
+ case SI_N64_KEYBOARD:
+ case SI_N64_MOUSE:
+ case SI_GBA:
+ return type & 0xffff0000;
+ break;
+ }
+ return SI_ERROR_UNKNOWN;
+ }
+
+ if ((type & SI_TYPE_MASK) != SI_TYPE_GC) {
+
+ return SI_ERROR_UNKNOWN;
+ }
+ switch (type & 0xffff0000) {
+ case SI_GC_CONTROLLER:
+ case SI_GC_STEERING:
+ return type & 0xffff0000;
+ break;
+ }
+
+ if ((type & 0xffe00000) == SI_GC_KEYBOARD) {
+ return SI_GC_KEYBOARD;
+ }
+
+ if ((type & SI_GC_WIRELESS) && !(type & SI_WIRELESS_IR)) {
+ if ((type & SI_GC_WAVEBIRD) == SI_GC_WAVEBIRD) {
+ return SI_GC_WAVEBIRD;
+ } else if (!(type & SI_WIRELESS_STATE)) {
+ return SI_GC_RECEIVER;
+ }
+ }
+
+ if ((type & SI_GC_CONTROLLER) == SI_GC_CONTROLLER) {
+ return SI_GC_CONTROLLER;
+ }
+ return SI_ERROR_UNKNOWN;
+}
+
+u32 SIProbe(s32 chan) { return SIDecodeType(SIGetType(chan)); }
+
+char* SIGetTypeString(u32 type) {
+ switch (SIDecodeType(type)) {
+ case SI_ERROR_NO_RESPONSE:
+ return "No response";
+ case SI_N64_CONTROLLER:
+ return "N64 controller";
+ case SI_N64_MIC:
+ return "N64 microphone";
+ case SI_N64_KEYBOARD:
+ return "N64 keyboard";
+ case SI_N64_MOUSE:
+ return "N64 mouse";
+ case SI_GBA:
+ return "GameBoy Advance";
+ case SI_GC_CONTROLLER:
+ return "Standard controller";
+ case SI_GC_RECEIVER:
+ return "Wireless receiver";
+ case SI_GC_WAVEBIRD:
+ return "WaveBird controller";
+ case SI_GC_KEYBOARD:
+ return "Keyboard";
+ case SI_GC_STEERING:
+ return "Steering";
+ }
+}
diff --git a/src/Dolphin/si/SISamplingRate.c b/src/Dolphin/si/SISamplingRate.c
new file mode 100644
index 0000000..c9b96ca
--- /dev/null
+++ b/src/Dolphin/si/SISamplingRate.c
@@ -0,0 +1,54 @@
+#include "dolphin/sipriv.h"
+#include "dolphin/vi.h"
+#pragma dont_inline on
+static u32 SamplingRate;
+
+typedef struct XY {
+ u16 line;
+ u8 count;
+} XY;
+
+static XY XYNTSC[12] = {
+ {263 - 17, 2}, {15, 18}, {30, 9}, {44, 6}, {52, 5}, {65, 4},
+ {87, 3}, {87, 3}, {87, 3}, {131, 2}, {131, 2}, {131, 2},
+};
+
+static XY XYPAL[12] = {
+ {313 - 17, 2}, {15, 21}, {29, 11}, {45, 7}, {52, 6}, {63, 5},
+ {78, 4}, {104, 3}, {104, 3}, {104, 3}, {104, 3}, {156, 2},
+};
+
+void SISetSamplingRate(u32 msec) {
+ XY* xy;
+ BOOL enabled;
+
+ if (msec > 11) {
+ msec = 11;
+ }
+
+ enabled = OSDisableInterrupts();
+
+ SamplingRate = msec;
+
+ switch (VIGetTvFormat()) {
+ case VI_NTSC:
+ case VI_MPAL:
+ case VI_EURGB60:
+ xy = XYNTSC;
+ break;
+ case VI_PAL:
+ xy = XYPAL;
+ break;
+ default:
+ OSReport("SISetSamplingRate: unknown TV format. Use default.");
+ msec = 0;
+ xy = XYNTSC;
+ break;
+ }
+
+ SISetXY((__VIRegs[54] & 1 ? 2u : 1u) * xy[msec].line, xy[msec].count);
+ OSRestoreInterrupts(enabled);
+}
+
+void SIRefreshSamplingRate() { SISetSamplingRate(SamplingRate); }
+#pragma dont_inline reset