summaryrefslogtreecommitdiff
path: root/src/Dolphin/exi/EXIBios.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Dolphin/exi/EXIBios.c')
-rw-r--r--src/Dolphin/exi/EXIBios.c692
1 files changed, 692 insertions, 0 deletions
diff --git a/src/Dolphin/exi/EXIBios.c b/src/Dolphin/exi/EXIBios.c
new file mode 100644
index 0000000..8ff770f
--- /dev/null
+++ b/src/Dolphin/exi/EXIBios.c
@@ -0,0 +1,692 @@
+#include "dolphin/os.h"
+
+#pragma scheduling off
+
+vu32 __EXIRegs[16] : 0xCC006800;
+
+static const char* __EXIVersion =
+ "<< Dolphin SDK - EXI\trelease build: Sep 5 2002 05:33:04 (0x2301) >>";
+
+#define MAX_DEV 3
+#define MAX_CHAN 3
+
+#define REG_MAX 5
+#define REG(chan, idx) (__EXIRegs[((chan)*REG_MAX) + (idx)])
+
+#define STATE_IDLE 0x00
+#define STATE_DMA 0x01
+#define STATE_IMM 0x02
+#define STATE_BUSY (STATE_DMA | STATE_IMM)
+#define STATE_SELECTED 0x04
+#define STATE_ATTACHED 0x08
+#define STATE_LOCKED 0x10
+
+#define EXI_0CR(tstart, dma, rw, tlen) \
+ ((((u32)(tstart)) << 0) | (((u32)(dma)) << 1) | (((u32)(rw)) << 2) | (((u32)(tlen)) << 4))
+
+#define CPR_CS(x) ((1u << (x)) << 7)
+#define CPR_CLK(x) ((x) << 4)
+
+typedef struct EXIControl {
+ EXICallback exiCallback;
+ EXICallback tcCallback;
+ EXICallback extCallback;
+ vu32 state;
+ int immLen;
+ u8* immBuf;
+ u32 dev;
+ u32 id;
+ s32 idTime;
+ int items;
+ struct {
+ u32 dev;
+ EXICallback callback;
+ } queue[MAX_DEV];
+} EXIControl;
+
+static EXIControl Ecb[MAX_CHAN];
+
+s32 __EXIProbeStartTime[2] : (OS_BASE_CACHED | 0x30C0);
+
+static void SetExiInterruptMask(s32 chan, EXIControl* exi) {
+ EXIControl* exi2;
+
+ exi2 = &Ecb[2];
+ switch (chan) {
+ case 0:
+ if ((exi->exiCallback == 0 && exi2->exiCallback == 0) || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXI | OS_INTERRUPTMASK_EXI_2_EXI);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXI | OS_INTERRUPTMASK_EXI_2_EXI);
+ }
+ break;
+ case 1:
+ if (exi->exiCallback == 0 || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXI);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXI);
+ }
+ break;
+ case 2:
+ if (__OSGetInterruptHandler(__OS_INTERRUPT_PI_DEBUG) == 0 || (exi->state & STATE_LOCKED)) {
+ __OSMaskInterrupts(OS_INTERRUPTMASK_PI_DEBUG);
+ } else {
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_PI_DEBUG);
+ }
+ break;
+ }
+}
+
+static void CompleteTransfer(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ u8* buf;
+ u32 data;
+ int i;
+ int len;
+
+ if (exi->state & STATE_BUSY) {
+ if ((exi->state & STATE_IMM) && (len = exi->immLen)) {
+ buf = exi->immBuf;
+ data = REG(chan, 4);
+ for (i = 0; i < len; i++) {
+ *buf++ = (u8)((data >> ((3 - i) * 8)) & 0xff);
+ }
+ }
+ exi->state &= ~STATE_BUSY;
+ }
+}
+
+BOOL EXIImm(s32 chan, void* buf, s32 len, u32 type, EXICallback callback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_BUSY) || !(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->tcCallback = callback;
+ if (exi->tcCallback) {
+ EXIClearInterrupts(chan, FALSE, TRUE, FALSE);
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_TC >> (3 * chan));
+ }
+
+ exi->state |= STATE_IMM;
+
+ if (type != EXI_READ) {
+ u32 data;
+ int i;
+
+ data = 0;
+ for (i = 0; i < len; i++) {
+ data |= ((u8*)buf)[i] << ((3 - i) * 8);
+ }
+ REG(chan, 4) = data;
+ }
+
+ exi->immBuf = buf;
+ exi->immLen = (type != EXI_WRITE) ? len : 0;
+
+ REG(chan, 3) = EXI_0CR(1, 0, type, len - 1);
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+BOOL EXIImmEx(s32 chan, void* buf, s32 len, u32 mode) {
+ s32 xLen;
+
+ while (len) {
+ xLen = (len < 4) ? len : 4;
+ if (!EXIImm(chan, buf, xLen, mode, NULL)) {
+ return FALSE;
+ }
+
+ if (!EXISync(chan)) {
+ return FALSE;
+ }
+
+ (u8*)buf += xLen;
+ len -= xLen;
+ }
+ return TRUE;
+}
+
+BOOL EXIDma(s32 chan, void* buf, s32 len, u32 type, EXICallback callback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_BUSY) || !(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->tcCallback = callback;
+ if (exi->tcCallback) {
+ EXIClearInterrupts(chan, FALSE, TRUE, FALSE);
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_TC >> (3 * chan));
+ }
+
+ exi->state |= STATE_DMA;
+
+ REG(chan, 1) = (u32)buf & 0x3ffffe0;
+ REG(chan, 2) = (u32)len;
+ REG(chan, 3) = EXI_0CR(1, 1, type, 0);
+
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+extern u32 __OSGetDIConfig(void);
+
+vu16 __OSDeviceCode : (OS_BASE_CACHED | 0x30E6);
+
+BOOL EXISync(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL rc = FALSE;
+ BOOL enabled;
+
+ while (exi->state & STATE_SELECTED) {
+ if (((REG(chan, 3) & 1) >> 0) == 0) {
+ enabled = OSDisableInterrupts();
+ if (exi->state & STATE_SELECTED) {
+ CompleteTransfer(chan);
+ if (__OSGetDIConfig() != 0xff || exi->immLen != 4 ||
+ (REG(chan, 0) & 0x00000070) != (EXI_FREQ_1M << 4) ||
+ (REG(chan, 4) != EXI_USB_ADAPTER && REG(chan, 4) != EXI_IS_VIEWER &&
+ REG(chan, 4) != 0x04220001) ||
+ __OSDeviceCode == 0x8200) {
+ rc = TRUE;
+ }
+ }
+ OSRestoreInterrupts(enabled);
+ break;
+ }
+ }
+ return rc;
+}
+
+u32 EXIClearInterrupts(s32 chan, BOOL exi, BOOL tc, BOOL ext) {
+ u32 cpr;
+ u32 prev;
+
+ prev = cpr = REG(chan, 0);
+ cpr &= 0x7f5;
+ if (exi)
+ cpr |= 2;
+ if (tc)
+ cpr |= 8;
+ if (ext)
+ cpr |= 0x800;
+ REG(chan, 0) = cpr;
+ return prev;
+}
+
+EXICallback EXISetExiCallback(s32 chan, EXICallback exiCallback) {
+ EXIControl* exi = &Ecb[chan];
+ EXICallback prev;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ prev = exi->exiCallback;
+ exi->exiCallback = exiCallback;
+
+ if (chan != 2) {
+ SetExiInterruptMask(chan, exi);
+ } else {
+ SetExiInterruptMask(0, &Ecb[0]);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return prev;
+}
+
+void EXIProbeReset(void) {
+ __EXIProbeStartTime[0] = __EXIProbeStartTime[1] = 0;
+ Ecb[0].idTime = Ecb[1].idTime = 0;
+ __EXIProbe(0);
+ __EXIProbe(1);
+}
+
+static BOOL __EXIProbe(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+ BOOL rc;
+ u32 cpr;
+ s32 t;
+
+ if (chan == 2) {
+ return TRUE;
+ }
+
+ rc = TRUE;
+ enabled = OSDisableInterrupts();
+ cpr = REG(chan, 0);
+ if (!(exi->state & EXI_STATE_ATTACHED)) {
+ if (cpr & 0x00000800) {
+ EXIClearInterrupts(chan, FALSE, FALSE, TRUE);
+ __EXIProbeStartTime[chan] = exi->idTime = 0;
+ }
+
+ if (cpr & 0x00001000) {
+ t = (s32)(OSTicksToMilliseconds(OSGetTime()) / 100) + 1;
+ if (__EXIProbeStartTime[chan] == 0) {
+ __EXIProbeStartTime[chan] = t;
+ }
+ if (t - __EXIProbeStartTime[chan] < 300 / 100) {
+ rc = FALSE;
+ }
+ } else {
+ __EXIProbeStartTime[chan] = exi->idTime = 0;
+ rc = FALSE;
+ }
+ } else if (!(cpr & 0x00001000) || (cpr & 0x00000800)) {
+ __EXIProbeStartTime[chan] = exi->idTime = 0;
+ rc = FALSE;
+ }
+ OSRestoreInterrupts(enabled);
+
+ return rc;
+}
+
+BOOL EXIProbe(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL rc;
+ u32 id;
+
+ rc = __EXIProbe(chan);
+ if (rc && exi->idTime == 0) {
+ rc = EXIGetID(chan, 0, &id) ? TRUE : FALSE;
+ }
+ return rc;
+}
+
+s32 EXIProbeEx(s32 chan) {
+ if (EXIProbe(chan)) {
+ return 1;
+ } else if (__EXIProbeStartTime[chan] != 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static BOOL __EXIAttach(s32 chan, EXICallback extCallback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & EXI_STATE_ATTACHED) || __EXIProbe(chan) == FALSE) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ EXIClearInterrupts(chan, TRUE, FALSE, FALSE);
+
+ exi->extCallback = extCallback;
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXT >> (3 * chan));
+ exi->state |= STATE_ATTACHED;
+ OSRestoreInterrupts(enabled);
+
+ return TRUE;
+}
+
+BOOL EXIAttach(s32 chan, EXICallback extCallback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+ BOOL rc;
+
+ EXIProbe(chan);
+
+ enabled = OSDisableInterrupts();
+ if (exi->idTime == 0) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ rc = __EXIAttach(chan, extCallback);
+ OSRestoreInterrupts(enabled);
+ return rc;
+}
+
+BOOL EXIDetach(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if (!(exi->state & STATE_ATTACHED)) {
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+ }
+ if ((exi->state & STATE_LOCKED) && exi->dev == 0) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->state &= ~STATE_ATTACHED;
+ __OSMaskInterrupts((OS_INTERRUPTMASK_EXI_0_EXT | OS_INTERRUPTMASK_EXI_0_EXI) >> (3 * chan));
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL EXISelect(s32 chan, u32 dev, u32 freq) {
+ EXIControl* exi = &Ecb[chan];
+ u32 cpr;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if ((exi->state & STATE_SELECTED) ||
+ chan != 2 && (dev == 0 && !(exi->state & STATE_ATTACHED) && !__EXIProbe(chan) ||
+ !(exi->state & STATE_LOCKED) || (exi->dev != dev))) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->state |= STATE_SELECTED;
+ cpr = REG(chan, 0);
+ cpr &= 0x405;
+ cpr |= CPR_CS(dev) | CPR_CLK(freq);
+ REG(chan, 0) = cpr;
+
+ if (exi->state & STATE_ATTACHED) {
+ switch (chan) {
+ case 0:
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXT);
+ break;
+ case 1:
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXT);
+ break;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL EXIDeselect(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ u32 cpr;
+ BOOL enabled;
+
+ enabled = OSDisableInterrupts();
+ if (!(exi->state & STATE_SELECTED)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ exi->state &= ~STATE_SELECTED;
+ cpr = REG(chan, 0);
+ REG(chan, 0) = cpr & 0x405;
+
+ if (exi->state & STATE_ATTACHED) {
+ switch (chan) {
+ case 0:
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXT);
+ break;
+ case 1:
+ __OSUnmaskInterrupts(OS_INTERRUPTMASK_EXI_1_EXT);
+ break;
+ }
+ }
+
+ OSRestoreInterrupts(enabled);
+
+ if (chan != 2 && (cpr & CPR_CS(0))) {
+ return __EXIProbe(chan) ? TRUE : FALSE;
+ }
+
+ return TRUE;
+}
+
+static void EXIIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - __OS_INTERRUPT_EXI_0_EXI) / 3;
+ exi = &Ecb[chan];
+ EXIClearInterrupts(chan, TRUE, FALSE, FALSE);
+ callback = exi->exiCallback;
+ if (callback) {
+ OSContext exceptionContext;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ callback(chan, context);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+static void TCIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ OSContext exceptionContext;
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - __OS_INTERRUPT_EXI_0_TC) / 3;
+ exi = &Ecb[chan];
+ __OSMaskInterrupts(OS_INTERRUPTMASK(interrupt));
+ EXIClearInterrupts(chan, FALSE, TRUE, FALSE);
+ callback = exi->tcCallback;
+ if (callback) {
+ exi->tcCallback = 0;
+ CompleteTransfer(chan);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ callback(chan, context);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+static void EXTIntrruptHandler(__OSInterrupt interrupt, OSContext* context) {
+ s32 chan;
+ EXIControl* exi;
+ EXICallback callback;
+
+ chan = (interrupt - __OS_INTERRUPT_EXI_0_EXT) / 3;
+ __OSMaskInterrupts((OS_INTERRUPTMASK_EXI_0_EXT | OS_INTERRUPTMASK_EXI_0_EXI) >> (3 * chan));
+ exi = &Ecb[chan];
+ callback = exi->extCallback;
+ exi->state &= ~STATE_ATTACHED;
+ if (callback) {
+ OSContext exceptionContext;
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(&exceptionContext);
+
+ exi->extCallback = 0;
+ callback(chan, context);
+
+ OSClearContext(&exceptionContext);
+ OSSetCurrentContext(context);
+ }
+}
+
+void EXIInit(void) {
+ OSRegisterVersion(__EXIVersion);
+
+ __OSMaskInterrupts(OS_INTERRUPTMASK_EXI_0_EXI | OS_INTERRUPTMASK_EXI_0_TC |
+ OS_INTERRUPTMASK_EXI_0_EXT | OS_INTERRUPTMASK_EXI_1_EXI |
+ OS_INTERRUPTMASK_EXI_1_TC | OS_INTERRUPTMASK_EXI_1_EXT |
+ OS_INTERRUPTMASK_EXI_2_EXI | OS_INTERRUPTMASK_EXI_2_TC);
+
+ REG(0, 0) = 0;
+ REG(1, 0) = 0;
+ REG(2, 0) = 0;
+
+ REG(0, 0) = 0x00002000;
+
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_0_EXI, EXIIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_0_TC, TCIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_0_EXT, EXTIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_1_EXI, EXIIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_1_TC, TCIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_1_EXT, EXTIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_2_EXI, EXIIntrruptHandler);
+ __OSSetInterruptHandler(__OS_INTERRUPT_EXI_2_TC, TCIntrruptHandler);
+
+ if ((OSGetConsoleType() & 0x10000000) != 0) {
+ __EXIProbeStartTime[0] = __EXIProbeStartTime[1] = 0;
+ Ecb[0].idTime = Ecb[1].idTime = 0;
+ __EXIProbe(0);
+ __EXIProbe(1);
+ }
+}
+
+BOOL EXILock(s32 chan, u32 dev, EXICallback unlockedCallback) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+ int i;
+
+ enabled = OSDisableInterrupts();
+ if (exi->state & STATE_LOCKED) {
+ if (unlockedCallback) {
+ for (i = 0; i < exi->items; i++) {
+ if (exi->queue[i].dev == dev) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ }
+ exi->queue[exi->items].callback = unlockedCallback;
+ exi->queue[exi->items].dev = dev;
+ exi->items++;
+ }
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+
+ exi->state |= STATE_LOCKED;
+ exi->dev = dev;
+ SetExiInterruptMask(chan, exi);
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+BOOL EXIUnlock(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL enabled;
+ EXICallback unlockedCallback;
+
+ enabled = OSDisableInterrupts();
+ if (!(exi->state & STATE_LOCKED)) {
+ OSRestoreInterrupts(enabled);
+ return FALSE;
+ }
+ exi->state &= ~STATE_LOCKED;
+ SetExiInterruptMask(chan, exi);
+
+ if (0 < exi->items) {
+ unlockedCallback = exi->queue[0].callback;
+ if (0 < --exi->items) {
+ memmove(&exi->queue[0], &exi->queue[1], sizeof(exi->queue[0]) * exi->items);
+ }
+ unlockedCallback(chan, 0);
+ }
+
+ OSRestoreInterrupts(enabled);
+ return TRUE;
+}
+
+u32 EXIGetState(s32 chan) {
+ EXIControl* exi = &Ecb[chan];
+
+ return (u32)exi->state;
+}
+
+static void UnlockedHandler(s32 chan, OSContext* context) {
+ u32 id;
+
+ EXIGetID(chan, 0, &id);
+}
+
+s32 EXIGetID(s32 chan, u32 dev, u32* id) {
+ EXIControl* exi = &Ecb[chan];
+ BOOL err;
+ u32 cmd;
+ s32 startTime;
+ BOOL enabled;
+
+ if (chan < 2 && dev == 0) {
+ if (!__EXIProbe(chan)) {
+ return 0;
+ }
+
+ if (exi->idTime == __EXIProbeStartTime[chan]) {
+ *id = exi->id;
+ return exi->idTime;
+ }
+
+ if (!__EXIAttach(chan, NULL)) {
+ return 0;
+ }
+
+ startTime = __EXIProbeStartTime[chan];
+ }
+
+ err = !EXILock(chan, dev, (chan < 2 && dev == 0) ? UnlockedHandler : NULL);
+ if (!err) {
+ err = !EXISelect(chan, dev, EXI_FREQ_1M);
+ if (!err) {
+ cmd = 0;
+ err |= !EXIImm(chan, &cmd, 2, EXI_WRITE, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIImm(chan, id, 4, EXI_READ, NULL);
+ err |= !EXISync(chan);
+ err |= !EXIDeselect(chan);
+ }
+ EXIUnlock(chan);
+ }
+
+ if (chan < 2 && dev == 0) {
+ EXIDetach(chan);
+ enabled = OSDisableInterrupts();
+ err |= (startTime != __EXIProbeStartTime[chan]);
+ if (!err) {
+ exi->id = *id;
+ exi->idTime = startTime;
+ }
+ OSRestoreInterrupts(enabled);
+
+ return err ? 0 : exi->idTime;
+ }
+
+ return err ? 0 : !0;
+}
+
+char* EXIGetTypeString(u32 type) {
+ switch (type) {
+ case EXI_MEMORY_CARD_59:
+ return "Memory Card 59";
+ case EXI_MEMORY_CARD_123:
+ return "Memory Card 123";
+ case EXI_MEMORY_CARD_251:
+ return "Memory Card 251";
+ case EXI_MEMORY_CARD_507:
+ return "Memory Card 507";
+ case EXI_USB_ADAPTER:
+ return "USB Adapter";
+ case 0x80000000 | EXI_MEMORY_CARD_59:
+ case 0x80000000 | EXI_MEMORY_CARD_123:
+ case 0x80000000 | EXI_MEMORY_CARD_251:
+ case 0x80000000 | EXI_MEMORY_CARD_507:
+ return "Net Card";
+ case EXI_ETHER_VIEWER:
+ return "Artist Ether";
+ case EXI_STREAM_HANGER:
+ return "Stream Hanger";
+ case EXI_IS_VIEWER:
+ return "IS Viewer";
+ }
+}
+
+#pragma scheduling reset