summaryrefslogtreecommitdiff
path: root/src/Dolphin/ai.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Dolphin/ai.c')
-rw-r--r--src/Dolphin/ai.c378
1 files changed, 378 insertions, 0 deletions
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())
+ ;
+}