Files
Pokemon-Firered/src/ereader_helpers.c
T
2020-02-11 16:26:50 -05:00

407 lines
11 KiB
C

#include "global.h"
#include "link.h"
#include "ereader_helpers.h"
struct SendRecvMgr
{
u8 master_slave; // 0: clock slave; 1: clock master
u8 state; // EREADER_XFR_STATE_*
u8 xferState; // EREADER_XFER_*
u8 checksumResult; // EREADER_CHECKSUM_*
u8 cancellationReason; // EREADER_CANCEL_*
u32 * dataptr; // Payload source or destination
int cursor; // Index of the next word
int size; // Last word index
u32 checksum; // Validation checksum
};
static bool16 DetermineSendRecvState(u8);
static void SetUpTransferManager(size_t, const void *, void *);
static void StartTm3(void);
static void EnableSio(void);
static void DisableTm3(void);
static void GetKeyInput(void);
static struct SendRecvMgr sSendRecvMgr;
static u16 sJoyNewOrRepeated;
static u16 sJoyNew;
static u16 sSendRecvStatus;
static u16 sCounter1;
static u32 sCounter2;
static u16 sSavedIme;
static u16 sSavedIe;
static u16 sSavedTm3Cnt;
static u16 sSavedSioCnt;
static u16 sSavedRCnt;
int EReader_Send(size_t size, const void * src)
{
int result;
EReaderHelper_SaveRegsState();
while (1)
{
GetKeyInput();
if (TEST_BUTTON(sJoyNew, B_BUTTON))
gShouldAdvanceLinkState = 2;
sSendRecvStatus = EReaderHandleTransfer(1, size, src, NULL);
if ((sSendRecvStatus & 0x13) == 0x10) // checksum OK and xfer off
{
result = 0;
break;
}
else if (sSendRecvStatus & 8) // cancelled by player
{
result = 1;
break;
}
else if (sSendRecvStatus & 4) // timed out
{
result = 2;
break;
}
else
{
gShouldAdvanceLinkState = 0;
VBlankIntrWait();
}
}
CpuFill32(0, &sSendRecvMgr, sizeof(sSendRecvMgr));
EReaderHelper_RestoreRegsState();
return result;
}
int EReader_Recv(void * dest)
{
int result;
EReaderHelper_SaveRegsState();
while (1)
{
GetKeyInput();
if (TEST_BUTTON(sJoyNew, B_BUTTON))
gShouldAdvanceLinkState = 2;
sSendRecvStatus = EReaderHandleTransfer(0, 0, NULL, dest);
if ((sSendRecvStatus & 0x13) == 0x10) // checksum OK and xfer off
{
result = 0;
break;
}
else if (sSendRecvStatus & 8) // cancelled by player
{
result = 1;
break;
}
else if (sSendRecvStatus & 4) // timed out
{
result = 2;
break;
}
else
{
gShouldAdvanceLinkState = 0;
VBlankIntrWait();
}
}
CpuFill32(0, &sSendRecvMgr, sizeof(sSendRecvMgr));
EReaderHelper_RestoreRegsState();
return result;
}
static void CloseSerial(void)
{
REG_IME = 0;
REG_IE &= ~(INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL);
REG_IME = 1;
REG_SIOCNT = 0;
REG_TM3CNT_H = 0;
REG_IF = INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL;
}
static void OpenSerialMulti(void)
{
REG_IME = 0;
REG_IE &= ~(INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL);
REG_IME = 1;
REG_RCNT = 0;
REG_SIOCNT = SIO_MULTI_MODE;
REG_SIOCNT |= SIO_INTR_ENABLE | SIO_115200_BPS;
REG_IME = 0;
REG_IE |= INTR_FLAG_SERIAL;
REG_IME = 1;
if (sSendRecvMgr.state == 0)
CpuFill32(0, &sSendRecvMgr, sizeof(sSendRecvMgr));
}
static void OpenSerial32(void)
{
REG_RCNT = 0;
REG_SIOCNT = SIO_INTR_ENABLE | SIO_32BIT_MODE;
REG_SIOCNT |= SIO_MULTI_SD;
gShouldAdvanceLinkState = 0;
sCounter1 = 0;
sCounter2 = 0;
}
u16 EReaderHandleTransfer(u8 mode, size_t size, const void * data, void * recvBuffer)
{
switch (sSendRecvMgr.state)
{
case EREADER_XFR_STATE_INIT:
OpenSerialMulti();
sSendRecvMgr.xferState = EREADER_XFER_EXE;
sSendRecvMgr.state = EREADER_XFR_STATE_HANDSHAKE;
break;
case EREADER_XFR_STATE_HANDSHAKE:
if (DetermineSendRecvState(mode))
EnableSio();
if (gShouldAdvanceLinkState == 2)
{
sSendRecvMgr.cancellationReason = EREADER_CANCEL_KEY;
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
}
// Progression is handled by the serial callback
break;
case EREADER_XFR_STATE_START:
OpenSerial32();
SetUpTransferManager(size, data, recvBuffer);
sSendRecvMgr.state = EREADER_XFR_STATE_TRANSFER;
// fallthrough
case EREADER_XFR_STATE_TRANSFER:
if (gShouldAdvanceLinkState == 2)
{
sSendRecvMgr.cancellationReason = EREADER_CANCEL_KEY;
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
}
else
{
sCounter1++;
sCounter2++;
if (sSendRecvMgr.master_slave == 0 && sCounter2 > 60)
{
sSendRecvMgr.cancellationReason = EREADER_CANCEL_TIMEOUT;
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
}
if (sSendRecvMgr.xferState != EREADER_XFER_CHK)
{
if (sSendRecvMgr.master_slave != 0 && sCounter1 > 2)
{
EnableSio();
sSendRecvMgr.xferState = EREADER_XFER_CHK;
}
else
{
EnableSio();
sSendRecvMgr.xferState = EREADER_XFER_CHK;
}
}
}
// Progression is handled by the serial callback
break;
case EREADER_XFR_STATE_TRANSFER_DONE:
OpenSerialMulti();
sSendRecvMgr.state = EREADER_XFR_STATE_CHECKSUM;
break;
case EREADER_XFR_STATE_CHECKSUM:
if (sSendRecvMgr.master_slave == 1 && sCounter1 > 2)
EnableSio();
if (++sCounter1 > 60)
{
sSendRecvMgr.cancellationReason = EREADER_CANCEL_TIMEOUT;
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
}
break;
// Progression is handled by the serial callback
case EREADER_XFR_STATE_DONE:
if (sSendRecvMgr.xferState != 0)
{
CloseSerial();
sSendRecvMgr.xferState = 0;
}
break;
}
return
(sSendRecvMgr.xferState << EREADER_XFER_SHIFT)
| (sSendRecvMgr.cancellationReason << EREADER_CANCEL_SHIFT)
| (sSendRecvMgr.checksumResult << EREADER_CHECKSUM_SHIFT);
}
static bool16 DetermineSendRecvState(u8 mode)
{
bool16 resp;
if ((*(vu32 *)REG_ADDR_SIOCNT & (SIO_MULTI_SI | SIO_MULTI_SD)) == SIO_MULTI_SD && mode)
resp = sSendRecvMgr.master_slave = TRUE;
else
resp = sSendRecvMgr.master_slave = FALSE;
return resp;
}
static void SetUpTransferManager(size_t size, const void * data, void * recvBuffer)
{
if (sSendRecvMgr.master_slave)
{
REG_SIOCNT |= SIO_38400_BPS;
sSendRecvMgr.dataptr = (void *)data;
REG_SIODATA32 = size;
sSendRecvMgr.size = size / 4 + 1;
StartTm3();
}
else
{
REG_SIOCNT |= SIO_9600_BPS;
sSendRecvMgr.dataptr = recvBuffer;
}
}
static void StartTm3(void)
{
REG_TM3CNT_L = -601;
REG_TM3CNT_H = TIMER_INTR_ENABLE;
REG_IME = 0;
REG_IE |= INTR_FLAG_TIMER3;
REG_IME = 1;
}
void EReaderHelper_Timer3Callback(void)
{
DisableTm3();
EnableSio();
}
void EReaderHelper_SerialCallback(void)
{
u16 recv[4];
u16 i;
u16 cnt1;
u16 cnt2;
u32 recv32;
switch (sSendRecvMgr.state)
{
case EREADER_XFR_STATE_HANDSHAKE:
REG_SIOMLT_SEND = 0xCCD0;
*(u64 *)recv = REG_SIOMLT_RECV;
for (i = 0, cnt1 = 0, cnt2 = 0; i < 4; i++)
{
if (recv[i] == 0xCCD0)
cnt1++;
else if (recv[i] != 0xFFFF)
cnt2++;
}
if (cnt1 == 2 && cnt2 == 0)
sSendRecvMgr.state = EREADER_XFR_STATE_START;
break;
// Progression is handled by software
case EREADER_XFR_STATE_TRANSFER:
recv32 = REG_SIODATA32;
// The first value sent by the EReader is the payload size
if (sSendRecvMgr.cursor == 0 && sSendRecvMgr.master_slave == 0)
sSendRecvMgr.size = recv32 / 4 + 1;
if (sSendRecvMgr.master_slave == 1)
{
// Send mode
if (sSendRecvMgr.cursor < sSendRecvMgr.size)
{
REG_SIODATA32 = sSendRecvMgr.dataptr[sSendRecvMgr.cursor];
sSendRecvMgr.checksum += sSendRecvMgr.dataptr[sSendRecvMgr.cursor];
}
else
REG_SIODATA32 = sSendRecvMgr.checksum;
}
else
{
// Receive mode
if (sSendRecvMgr.cursor > 0 && sSendRecvMgr.cursor < sSendRecvMgr.size + 1)
{
// Receive next word
sSendRecvMgr.dataptr[sSendRecvMgr.cursor - 1] = recv32;
sSendRecvMgr.checksum += recv32;
}
else if (sSendRecvMgr.cursor != 0)
{
// Reached the end, test the received checksum
if (sSendRecvMgr.checksum == recv32)
sSendRecvMgr.checksumResult = EREADER_CHECKSUM_OK;
else
sSendRecvMgr.checksumResult = EREADER_CHECKSUM_ERR;
}
sCounter2 = 0;
}
sSendRecvMgr.cursor++;
if (sSendRecvMgr.cursor < sSendRecvMgr.size + 2)
{
if (sSendRecvMgr.master_slave != 0)
// Clock master; start timer
REG_TM3CNT_H |= TIMER_ENABLE;
else
// Clock slave; reset
EnableSio();
}
else
{
sSendRecvMgr.state = EREADER_XFR_STATE_TRANSFER_DONE;
sCounter1 = 0;
}
break;
// Progression is handled by the software
case EREADER_XFR_STATE_CHECKSUM:
if (sSendRecvMgr.master_slave == 0)
// Clock slave
REG_SIODATA8 = sSendRecvMgr.checksumResult;
*(vu64 *)recv = REG_SIOMLT_RECV;
if (recv[1] == EREADER_CHECKSUM_OK || recv[1] == EREADER_CHECKSUM_ERR)
{
if (sSendRecvMgr.master_slave == 1)
// EReader has (in)validated the payload
sSendRecvMgr.checksumResult = recv[1];
sSendRecvMgr.state = EREADER_XFR_STATE_DONE;
}
break;
}
}
static void EnableSio(void)
{
REG_SIOCNT |= SIO_ENABLE;
}
static void DisableTm3(void)
{
REG_TM3CNT_H &= ~TIMER_ENABLE;
REG_TM3CNT_L = -601;
}
static void GetKeyInput(void)
{
u16 rawKeys = REG_KEYINPUT ^ 0x3FF;
sJoyNew = rawKeys & ~sJoyNewOrRepeated;
sJoyNewOrRepeated = rawKeys;
}
void EReaderHelper_SaveRegsState(void)
{
sSavedIme = REG_IME;
sSavedIe = REG_IE;
sSavedTm3Cnt = REG_TM3CNT_H;
sSavedSioCnt = REG_SIOCNT;
sSavedRCnt = REG_RCNT;
}
void EReaderHelper_RestoreRegsState(void)
{
REG_IME = sSavedIme;
REG_IE = sSavedIe;
REG_TM3CNT_H = sSavedTm3Cnt;
REG_SIOCNT = sSavedSioCnt;
REG_RCNT = sSavedRCnt;
}
void EReaderHelper_ClearsSendRecvMgr(void)
{
CpuFill32(0, &sSendRecvMgr, sizeof(sSendRecvMgr));
}