summaryrefslogtreecommitdiff
path: root/src/Dolphin/si
diff options
context:
space:
mode:
Diffstat (limited to 'src/Dolphin/si')
-rw-r--r--src/Dolphin/si/SIBios.c772
-rw-r--r--src/Dolphin/si/SISamplingRate.c54
2 files changed, 826 insertions, 0 deletions
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