#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";
}
}