diff options
Diffstat (limited to 'src/Dolphin/GBA/GBAXfer.c')
-rw-r--r-- | src/Dolphin/GBA/GBAXfer.c | 163 |
1 files changed, 163 insertions, 0 deletions
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; +} |