diff --git a/.vscode/settings.json b/.vscode/settings.json index 8887fcd4..ac8f4052 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,7 +49,8 @@ "osmodule.h": "c", "dsp_regs.h": "c", "dsp.h": "c", - "ar.h": "c" + "ar.h": "c", + "osrtcpriv.h": "c" }, "files.autoSave": "onFocusChange", "files.insertFinalNewline": true, diff --git a/asm/Dolphin/si/SIBios.s b/asm/Dolphin/si/SIBios.s index 9749f18a..5a601e47 100644 --- a/asm/Dolphin/si/SIBios.s +++ b/asm/Dolphin/si/SIBios.s @@ -2,22 +2,16 @@ .section .bss .balign 8 -.global Packet Packet: .skip 0x80 -.global lbl_80569D18 lbl_80569D18: .skip 0xA0 -.global lbl_80569DB8 lbl_80569DB8: .skip 0x20 -.global lbl_80569DD8 lbl_80569DD8: .skip 0x20 -.global lbl_80569DF8 lbl_80569DF8: .skip 0x40 -.global lbl_80569E38 lbl_80569E38: .skip 0x60 @@ -25,13 +19,11 @@ lbl_80569E38: .section .data, "wa" .balign 8 -.global lbl_803F6EA0 lbl_803F6EA0: .asciz "<< Dolphin SDK - SI\trelease build: Sep 5 2002 05:33:08 (0x2301) >>" .balign 4 -.global Si Si: # ROM: 0x3F3EE4 .4byte 0xFFFFFFFF @@ -41,7 +33,6 @@ Si: .4byte 0 -.global Type Type: # ROM: 0x3F3EF8 .4byte 0x00000008 @@ -65,28 +56,23 @@ Type: .asciz "Keyboard" .balign 4 .asciz "Steering" - .balign 4 - .4byte 0 .section .sdata, "wa" .balign 8 -.global __SIVersion __SIVersion: .4byte lbl_803F6EA0 .skip 4 .section .sbss, "wa" .balign 8 -.global lbl_805A9BD0 lbl_805A9BD0: .skip 0x4 -.global lbl_805A9BD4 lbl_805A9BD4: .skip 0x4 .global __PADFixBits __PADFixBits: - .skip 0x8 + .skip 0x4 @@ -1196,7 +1182,7 @@ lbl_803BF5EC: /* 803BF604 003BC564 7C 08 03 A6 */ mtlr r0 /* 803BF608 003BC568 4E 80 00 20 */ blr -AlarmHandler: +.fn AlarmHandler, local /* 803BF60C 003BC56C 7C 08 02 A6 */ mflr r0 /* 803BF610 003BC570 3C 80 80 57 */ lis r4, lbl_80569D18@ha /* 803BF614 003BC574 90 01 00 04 */ stw r0, 4(r1) @@ -1233,6 +1219,7 @@ lbl_803BF684: /* 803BF68C 003BC5EC 38 21 00 20 */ addi r1, r1, 0x20 /* 803BF690 003BC5F0 7C 08 03 A6 */ mtlr r0 /* 803BF694 003BC5F4 4E 80 00 20 */ blr +.endfn AlarmHandler .global SITransfer SITransfer: diff --git a/include/dolphin/OSRtcPriv.h b/include/dolphin/OSRtcPriv.h index 78beefcd..949c71e7 100644 --- a/include/dolphin/OSRtcPriv.h +++ b/include/dolphin/OSRtcPriv.h @@ -28,5 +28,7 @@ typedef struct OSSramEx { OSSram* __OSLockSram(); OSSramEx* __OSLockSramEx(); +void OSSetWirelessID(s32 chan, u16 id); +u16 OSGetWirelessID(s32 chan); #endif // _DOLPHIN_OSRTCPRIV diff --git a/src/Dolphin/os/OSRtc.c b/src/Dolphin/os/OSRtc.c index ef579185..4fff32a8 100644 --- a/src/Dolphin/os/OSRtc.c +++ b/src/Dolphin/os/OSRtc.c @@ -472,7 +472,7 @@ lbl_8038428C: #endif #if NONMATCHING -u32 OSGetWirelessID(u32 channel) { +u16 OSGetWirelessID(s32 channel) { OSSramEx* sram; u16 id; @@ -487,7 +487,7 @@ u32 OSGetWirelessID(u32 channel) { #pragma optimization_level #pragma optimizewithasm off -asm u32 OSGetWirelessID(u32 channel) { +asm u16 OSGetWirelessID(s32 channel) { nofralloc mflr r0 lis r4, Scb@ha @@ -530,7 +530,7 @@ lbl_80384304: #endif -void OSSetWirelessID(u32 channel, u16 id) { +void OSSetWirelessID(s32 channel, u16 id) { OSSramEx* sram; sram = __OSLockSramEx(); diff --git a/src/Dolphin/si/SIBios.c b/src/Dolphin/si/SIBios.c index a1339425..00c6447e 100644 --- a/src/Dolphin/si/SIBios.c +++ b/src/Dolphin/si/SIBios.c @@ -1,3 +1,4 @@ +#include #include #include @@ -16,9 +17,29 @@ typedef struct SIControl { } SIControl; static SIControl Si = { - -1, + -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] = { @@ -287,3 +308,465 @@ void SIInit(void) { 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"; + } +}