diff options
author | YamaArashi <shadow962@live.com> | 2016-06-03 23:27:19 -0700 |
---|---|---|
committer | YamaArashi <shadow962@live.com> | 2016-06-05 13:43:46 -0700 |
commit | 2998cc719a41e38568468006d41539d1886412df (patch) | |
tree | 8cd0b9e274caf61efd70c6ace731637ad16e3df6 /src | |
parent | 2405230037bdc7804834eb10aed91bd538dabb4e (diff) |
link.c
Diffstat (limited to 'src')
-rw-r--r-- | src/link.c | 1810 | ||||
-rw-r--r-- | src/main.c | 38 | ||||
-rw-r--r-- | src/string_util.c | 15 | ||||
-rw-r--r-- | src/text.c | 4 |
4 files changed, 1835 insertions, 32 deletions
diff --git a/src/link.c b/src/link.c new file mode 100644 index 000000000..64e33fdfc --- /dev/null +++ b/src/link.c @@ -0,0 +1,1810 @@ +#include "global.h" +#include "main.h" +#include "task.h" +#include "text.h" +#include "sprite.h" +#include "songs.h" +#include "link.h" + +#define SIO_MULTI_CNT ((struct SioMultiCnt *)REG_ADDR_SIOCNT) + +struct BlockTransfer +{ + u16 pos; + u16 size; + void *src; + bool8 active; + u8 multiplayerId; +}; + +struct LinkTestBGInfo +{ + u32 screenBaseBlock; + u32 paletteNum; + u32 dummy_8; + u32 dummy_C; +}; + +extern void gpu_pal_apply(const u16 *, u16, u32); +extern void sub_8071C4C(const struct WindowConfig *); +extern u16 Random(void); +extern void SeedRng(u16); +extern s32 fade_and_return_progress_probably(void); +extern void copy_pal_bg_faded_to_pal_ram(void); +extern void sub_80516C4(u8, u16); + +extern u8 unk_2000000[]; +extern u8 unk_2004000[]; +extern u16 gUnknown_020239F8; +extern u16 word_203855E; + +extern u16 word_3004858; + +extern u8 gUnknown_0842C34C[]; + +static void InitLinkTestBG(u8, u8, u8, u8); +static void InitLinkTestBG_Unused(u8, u8, u8, u8); +static void LinkTestScreen(); +static void InitLocalLinkPlayer(void); +static void VBlankCB_LinkTest(void); +static void InitLink(void); +static void Task_TriggerHandshake(u8); +static void TestBlockTransfer(u32, u32, u32); +static void LinkTestProcessKeyInput(void); +static void CB2_LinkTest(void); +static void HandleReceiveRemoteLinkPlayer(u8); +static void ProcessRecvCmds(u8); +static void BuildSendCmd(u16); +static void sub_8007B44(void); +static void ResetBlockSend(void); +static bool8 InitBlockSend(void *, u32); +static void LinkCB_BlockSendBegin(void); +static void LinkCB_BlockSend(void); +static void LinkCB_BlockSendEnd(void); +static void sub_8007E04(void); +static u32 sub_8007E40(void); +static void SetBlockReceivedFlag(u8); +static u16 LinkTestCalcBlockChecksum(void *, u16); +static void PrintHexDigit(u8, u8, u8); +static void PrintHex(u32, u8, u8, u8); +static void LinkCB_RequestPlayerDataExchange(void); +static void Task_PrintTestData(u8); +static bool8 sub_8008224(void); +static u8 GetDummy2(void); +static void sub_8008350(void); +static void sub_800837C(void); +static void sub_80083E0(void); +static void sub_8008454(void); +static void sub_80084C8(void); +static void sub_80084F4(void); +static void CheckErrorStatus(void); +static void CB2_PrintErrorMessage(void); +static u8 IsSioMultiMaster(void); +static void DisableSerial(void); +static void EnableSerial(void); +static void CheckMasterOrSlave(void); +static void InitTimer(void); +static void EnqueueSendCmd(u16 *); +static void DequeueRecvCmds(u16[CMD_LENGTH][MAX_LINK_PLAYERS]); +static void StartTransfer(void); +static bool8 DoHandshake(void); +static void DoRecv(void); +static void DoSend(void); +static void StopTimer(void); +static void SendRecvDone(void); +static void ResetSendBuffer(void); +static void ResetRecvBuffer(void); + +static struct BlockTransfer sBlockSend; +static struct BlockTransfer sBlockRecv[MAX_LINK_PLAYERS]; +static u32 sBlockSendDelayCounter; +static u32 sDummy1; +static u8 sDummy2; +static u32 sPlayerDataExchangeStatus; +static u32 sErrorLinkStatus; +static u32 sErrorLastRecvQueueCount; +static u32 sErrorLastSendQueueCount; +static u32 sDummy3; +static u8 sLinkTestLastBlockSendPos; +static u8 sLinkTestLastBlockRecvPos[MAX_LINK_PLAYERS]; +static u8 sNumVBlanksWithoutSerialIntr; +static bool8 sSendBufferEmpty; +static u16 sSendNonzeroCheck; +static u16 sRecvNonzeroCheck; +static u8 sChecksumAvailable; +static u8 sHandshakePlayerCount; + +COMM_3(u16 word_3002910[MAX_LINK_PLAYERS]) +COMM_2(u32 gLinkDebugValue1) +COMM_4(struct LinkPlayerBlock localLinkPlayerBlock) +COMM_2(bool8 gLinkErrorOccurred) +COMM_2(u32 gFiller_3002960) +COMM_2(u32 gLinkDebugValue2) +COMM_2(u32 gFiller_3002968) +COMM_2(bool8 gLinkPlayerPending[MAX_LINK_PLAYERS]) +COMM_4(struct LinkPlayer gLinkPlayers[MAX_LINK_PLAYERS]) +COMM_2(bool8 gBlockReceived[MAX_LINK_PLAYERS]) +COMM_2(u32 gFiller_30029E4) +COMM_2(u16 gLinkHeldKeys) +COMM_2(u16 gLinkTimeOutCounter) +COMM_2(u32 gFiller_30029F0) +COMM_4(struct LinkPlayer localLinkPlayer) +COMM_4(u16 gRecvCmds[CMD_LENGTH][MAX_LINK_PLAYERS]) +COMM_2(u32 gLinkStatus) +COMM_2(bool8 gLinkDummyBool) +COMM_2(u8 byte_3002A68) +COMM_4(u8 gBlockSendBuffer[BLOCK_BUFFER_SIZE]) +COMM_2(bool8 u8_array_3002B70[MAX_LINK_PLAYERS]) +COMM_2(u16 gLinkType) +COMM_2(bool8 u8_array_3002B78[MAX_LINK_PLAYERS]) +COMM_4(u16 gBlockRecvBuffer[MAX_LINK_PLAYERS][BLOCK_BUFFER_SIZE / 2]) +COMM_2(bool8 gSuppressLinkErrorMessage) +COMM_2(u8 gSavedLinkPlayerCount) +COMM_4(u16 gSendCmd[CMD_LENGTH]) +COMM_2(u8 gSavedMultiplayerId) +COMM_2(bool8 gReceivedRemoteLinkPlayers) +COMM_4(struct LinkTestBGInfo gLinkTestBGInfo) +COMM_2(void (*gLinkCallback)(void)) +COMM_4(struct LinkPlayer gSavedLinkPlayers[MAX_LINK_PLAYERS]) +COMM_2(u8 gShouldAdvanceLinkState) +COMM_3(u16 gLinkTestBlockChecksums[MAX_LINK_PLAYERS]) +COMM_2(u32 gFiller_3003050) +COMM_2(u8 gBlockRequestType) +COMM_2(u32 gFiller_3003058) +COMM_2(u32 gFiller_300305C) +COMM_2(u8 gLastSendQueueCount) +COMM_4(struct Link gLink) +COMM_2(u8 gLastRecvQueueCount) +COMM_2(u16 gLinkSavedIme) + +EWRAM_DATA bool8 gLinkTestDebugValuesEnabled = {0}; +EWRAM_DATA bool8 gLinkTestDummyBool = {0}; +EWRAM_DATA u32 gFiller_20238B8 = {0}; +EWRAM_DATA u32 dword_20238BC = {0}; +EWRAM_DATA bool8 gLinkOpen = {0}; + +#include "link/digit_palette.h" +#include "link/digit_tiles.h" + +static const u8 sDebugMessages[7][12] = +{ + _"せつぞく ちゅうです", + _"せつぞく できません", + _"かくにん ちゅうです", + _"かくにん できました", + _"かくにん できません", + _"かくにん を かくにん", + _"かくにん は しっぱい", +}; + +static const u8 sColorCodes[] = _"{HIGHLIGHT TRANSPARENT}{COLOR WHITE2}"; + +static const u32 sBlockRequestLookupTable[5 * 2] = +{ + (u32)gBlockSendBuffer, 200, + (u32)gBlockSendBuffer, 200, + (u32)gBlockSendBuffer, 100, + (u32)gBlockSendBuffer, 220, + (u32)gBlockSendBuffer, 40, +}; + +static const u8 sTestString[] = _"テストな"; + +ALIGNED(4) static const u8 sMagic[] = "GameFreak inc."; + +ALIGNED(4) static const u8 sEmptyString[] = _""; + +void Task_DestroySelf(u8 taskId) +{ + DestroyTask(taskId); +} + +static void InitLinkTestBG(u8 paletteNum, u8 bgNum, u8 screenBaseBlock, u8 charBaseBlock) +{ + gpu_pal_apply(sLinkTestDigitPalette, 16 * paletteNum, 32); + DmaCopy16(3, sLinkTestDigitTiles, BG_CHAR_ADDR(charBaseBlock), 0x220); + + gLinkTestBGInfo.screenBaseBlock = screenBaseBlock; + gLinkTestBGInfo.paletteNum = paletteNum; + + switch (bgNum) + { + case 1: + REG_BG1CNT = 1 | (screenBaseBlock << 8) | (charBaseBlock << 2); + break; + case 2: + REG_BG2CNT = 1 | (screenBaseBlock << 8) | (charBaseBlock << 2); + break; + case 3: + REG_BG3CNT = 1 | (screenBaseBlock << 8) | (charBaseBlock << 2); + break; + } +} + +static void InitLinkTestBG_Unused(u8 paletteNum, u8 bgNum, u8 screenBaseBlock, u8 charBaseBlock) +{ + gpu_pal_apply(sLinkTestDigitPalette, 16 * paletteNum, 32); + DmaCopy16(3, sLinkTestDigitTiles, BG_CHAR_ADDR(charBaseBlock), 0x220); + + gLinkTestBGInfo.screenBaseBlock = screenBaseBlock; + gLinkTestBGInfo.paletteNum = paletteNum; + + *gBGControlRegs[bgNum] = (screenBaseBlock << 8) | (charBaseBlock << 2); +} + +static void LinkTestScreen(void) +{ + s32 i; + ResetSpriteData(); + FreeAllSpritePalettes(); + ResetTasks(); + SetVBlankCallback(VBlankCB_LinkTest); + SetUpWindowConfig(&gWindowConfig_81E6CE4); + sub_8071C4C(&gWindowConfig_81E6CE4); + ResetBlockSend(); + gLinkType = 0x1111; + OpenLink(); + SeedRng(gMain.vblankCounter1); + + for (i = 0; i < 4; i++) + { + // Very weird code, but nothing else seems to match. + // The following would have the same effect: + // gSaveBlock2.playerTrainerId[i] = Random(); + u8 *trainerId = gSaveBlock2.playerTrainerId; + s32 r; + s32 mask = 0xFF; + trainerId[i] = (r = Random()) & mask; + } + + InitLinkTestBG(0, 2, 4, 0); + REG_DISPCNT = DISPCNT_OBJ_ON | DISPCNT_BG0_ON | DISPCNT_BG2_ON | DISPCNT_OBJ_1D_MAP; + CreateTask(Task_DestroySelf, 0); + RunTasks(); + AnimateSprites(); + BuildOamBuffer(); + fade_and_return_progress_probably(); + sDummy3 = 0; + InitLocalLinkPlayer(); + CreateTask(Task_PrintTestData, 0); + SetMainCallback2(CB2_LinkTest); +} + +void sub_8007270(u8 a1) +{ + localLinkPlayer.lp_field_18 = a1; +} + +static void InitLocalLinkPlayer(void) +{ + s32 i; + + localLinkPlayer.trainerId = gSaveBlock2.playerTrainerId[0] + | (gSaveBlock2.playerTrainerId[1] << 8) + | (gSaveBlock2.playerTrainerId[2] << 16) + | (gSaveBlock2.playerTrainerId[3] << 24); + + for (i = 0; i < (s32)sizeof(localLinkPlayer.name); i++) + localLinkPlayer.name[i] = gSaveBlock2.playerName[i]; // UB: reads past the end of "playerName" array + + localLinkPlayer.gender = gSaveBlock2.playerGender; + localLinkPlayer.linkType = gLinkType; + localLinkPlayer.language = gGameLanguage; + localLinkPlayer.version = gGameVersion + 0x4000; + localLinkPlayer.lp_field_2 = 0; +} + +static void VBlankCB_LinkTest(void) +{ + LoadOam(); + ProcessSpriteCopyRequests(); + copy_pal_bg_faded_to_pal_ram(); +} + +static void InitLink(void) +{ + s32 i; + + for (i = 0; i < CMD_LENGTH; i++) + gSendCmd[i] = 0xEFFF; + + gLinkOpen = TRUE; + EnableSerial(); +} + +static void Task_TriggerHandshake(u8 taskId) +{ + gTasks[taskId].data[0]++; + + if (gTasks[taskId].data[0] == 5) + { + gShouldAdvanceLinkState = 1; + DestroyTask(taskId); + } +} + +void OpenLink(void) +{ + s32 i; + + ResetSerial(); + InitLink(); + + gLinkCallback = LinkCB_RequestPlayerDataExchange; + gLinkVSyncDisabled = FALSE; + gLinkErrorOccurred = FALSE; + gSuppressLinkErrorMessage = FALSE; + + ResetBlockReceivedFlags(); + + sDummy1 = 0; + byte_3002A68 = 0; + gLinkDummyBool = FALSE; + gReceivedRemoteLinkPlayers = FALSE; + + for (i = 0; i < 4; i++) + { + gLinkPlayerPending[i] = TRUE; + u8_array_3002B78[i] = 0; + u8_array_3002B70[i] = 0; + } + + CreateTask(Task_TriggerHandshake, 2); +} + +void CloseLink(void) +{ + gReceivedRemoteLinkPlayers = FALSE; + gLinkOpen = FALSE; + DisableSerial(); +} + +static void TestBlockTransfer(u32 a1, u32 a2, u32 a3) +{ + u8 i; + u8 val; + + if (sLinkTestLastBlockSendPos != sBlockSend.pos) + { + PrintHex(sBlockSend.pos, 2, 3, 2); + sLinkTestLastBlockSendPos = sBlockSend.pos; + } + + for (i = 0; i < MAX_LINK_PLAYERS; i++) + { + if (sLinkTestLastBlockRecvPos[i] != sBlockRecv[i].pos) + { + PrintHex(sBlockRecv[i].pos, 2, i + 4, 2); + sLinkTestLastBlockRecvPos[i] = sBlockRecv[i].pos; + } + } + + val = GetBlockRecievedStatus(); + + if (val == 0xF) + { + for (i = 0; i < MAX_LINK_PLAYERS; i++) + { + if ((val >> i) & 1) + { + gLinkTestBlockChecksums[i] = LinkTestCalcBlockChecksum(&gBlockRecvBuffer[i], sBlockRecv[i].size); + ResetBlockReceivedFlag(i); + if (gLinkTestBlockChecksums[i] != 834) + { + gLinkTestDebugValuesEnabled = FALSE; + gLinkTestDummyBool = FALSE; + } + } + } + } +} + +static void LinkTestProcessKeyInput(void) +{ + if (gMain.newKeys & A_BUTTON) + gShouldAdvanceLinkState = 1; + if (gMain.heldKeys & B_BUTTON) + InitBlockSend(unk_2004000, 0x2004); + if (gMain.newKeys & L_BUTTON) + pal_fade_maybe(-1, 0, 0x10, 0, 2); + if (gMain.newKeys & START_BUTTON) + SetSuppressLinkErrorMessage(TRUE); + if (gMain.newKeys & R_BUTTON) + sub_8125D44(1); + if (gMain.newKeys & SELECT_BUTTON) + sub_800832C(); + if (gLinkTestDebugValuesEnabled) + { + u32 vblankCounter1 = gMain.vblankCounter1; + u8 val = gLinkVSyncDisabled; + if (!gLinkCallback) + val = gLinkVSyncDisabled | 0x10; + SetLinkDebugValues(vblankCounter1, val); + } +} + +static void CB2_LinkTest(void) +{ + LinkTestProcessKeyInput(); + TestBlockTransfer(1, 1, 0); + RunTasks(); + AnimateSprites(); + BuildOamBuffer(); + fade_and_return_progress_probably(); +} + +u16 LinkMain2(u16 *heldKeys) +{ + u8 i; + + if (!gLinkOpen) + return 0; + + for (i = 0; i < CMD_LENGTH; i++) + gSendCmd[i] = 0; + + gLinkHeldKeys = *heldKeys; + + if (gLinkStatus & LINK_STAT_CONN_ESTABLISHED) + { + ProcessRecvCmds(SIO_MULTI_CNT->id); + if (gLinkCallback) + gLinkCallback(); + CheckErrorStatus(); + } + + return gLinkStatus; +} + +static void HandleReceiveRemoteLinkPlayer(u8 multiplayerId) +{ + u32 pendingLinkPlayerCount = 0; + s32 i; + + gLinkPlayerPending[multiplayerId] = FALSE; + + for (i = 0; i < GetLinkPlayerCount_2(); i++) + pendingLinkPlayerCount += gLinkPlayerPending[i]; + + if (pendingLinkPlayerCount == 0 && !gReceivedRemoteLinkPlayers) + gReceivedRemoteLinkPlayers = TRUE; +} + +static void ProcessRecvCmds(u8 a1) +{ + u16 i; + for (i = 0; i < MAX_LINK_PLAYERS; i++) + { + word_3002910[i] = 0; + if (!gRecvCmds[0][i]) + continue; + switch (gRecvCmds[0][i]) + { + case 0x2222: + InitLocalLinkPlayer(); + localLinkPlayerBlock.linkPlayer = localLinkPlayer; + memcpy(localLinkPlayerBlock.magic1, sMagic, 15); + memcpy(localLinkPlayerBlock.magic2, sMagic, 15); + InitBlockSend(&localLinkPlayerBlock, sizeof(localLinkPlayerBlock)); + break; + case 0x5555: + byte_3002A68 = 1; + break; + case 0x5566: + byte_3002A68 = 1; + break; + case 0xBBBB: + { + struct BlockTransfer *blockRecv = &sBlockRecv[i]; + blockRecv->pos = 0; + blockRecv->size = gRecvCmds[1][i]; + blockRecv->multiplayerId = gRecvCmds[2][i]; + break; + } + case 0x8888: + if (sBlockRecv[i].size > BLOCK_BUFFER_SIZE) + { + u16 *buffer = (u16 *)unk_2000000; + u16 j; + for (j = 0; j < CMD_LENGTH - 1; j++) + buffer[(sBlockRecv[i].pos / 2) + j] = gRecvCmds[j + 1][i]; + } + else + { + u16 j; + for (j = 0; j < CMD_LENGTH - 1; j++) + gBlockRecvBuffer[i][(sBlockRecv[i].pos / 2) + j] = gRecvCmds[j + 1][i]; + } + + sBlockRecv[i].pos += (CMD_LENGTH - 1) * 2; + + if (sBlockRecv[i].pos >= sBlockRecv[i].size) + { + if (gLinkPlayerPending[i] == TRUE) + { + struct LinkPlayerBlock *block = (struct LinkPlayerBlock *)&gBlockRecvBuffer[i]; + struct LinkPlayer *linkPlayer = &gLinkPlayers[i]; + *linkPlayer = block->linkPlayer; + + if (strcmp(block->magic1, sMagic) + || strcmp(block->magic2, sMagic)) + { + SetMainCallback2(CB2_LinkError); + } + else + { + HandleReceiveRemoteLinkPlayer(i); + } + + ConvertInternationalString(gLinkPlayers[i].name, gLinkPlayers[i].language); + } + else + { + SetBlockReceivedFlag(i); + } + } + break; + case 0x5FFF: + u8_array_3002B78[i] = 1; + break; + case 0x2FFE: + u8_array_3002B70[i] = 1; + break; + case 0xAAAA: + sub_8007E24(); + break; + case 0xAAAB: + sub_80516C4(i, gRecvCmds[1][i]); + break; + case 0xCCCC: + { + const u32 *addresses; + const u32 *sizes; + void *data; + u16 size; + + addresses = sBlockRequestLookupTable; + data = (void *)addresses[gRecvCmds[1][i] * 2]; + + sizes = addresses + 1; + size = sizes[gRecvCmds[1][i] * 2]; + + SendBlock(0, data, size); + break; + } + case 0x4444: + word_3002910[i] = gRecvCmds[1][i]; + break; + case 0xCAFE: + word_3002910[i] = gRecvCmds[1][i]; + break; + } + } +} + +static void BuildSendCmd(u16 code) +{ + switch (code) + { + case 0x2222: + gSendCmd[0] = 0x2222; + gSendCmd[1] = gLinkType; + break; + case 0x2FFE: + gSendCmd[0] = 0x2FFE; + break; + case 0x4444: + gSendCmd[0] = 0x4444; + gSendCmd[1] = gMain.heldKeys; + break; + case 0x5555: + gSendCmd[0] = 0x5555; + break; + case 0x5566: + gSendCmd[0] = 0x5566; + break; + case 0x6666: + gSendCmd[0] = 0x6666; + gSendCmd[1] = 0; + break; + case 0x7777: + { + u8 i; + + gSendCmd[0] = 0x7777; + + for (i = 0; i < 5; i++) + gSendCmd[i + 1] = 0xEE; + + break; + } + case 0xBBBB: + gSendCmd[0] = 0xBBBB; + gSendCmd[1] = sBlockSend.size; + gSendCmd[2] = sBlockSend.multiplayerId + 128; + break; + case 0xAAAA: + gSendCmd[0] = 0xAAAA; + break; + case 0xAAAB: + gSendCmd[0] = 0xAAAB; + gSendCmd[1] = word_203855E; + break; + case 0xCCCC: + gSendCmd[0] = 0xCCCC; + gSendCmd[1] = gBlockRequestType; + break; + case 0x5FFF: + gSendCmd[0] = 0x5FFF; + break; + case 0xCAFE: + if (!word_3004858 || gUnknown_3001764) + break; + gSendCmd[0] = 0xCAFE; + gSendCmd[1] = word_3004858; + break; + } +} + +void sub_8007B14(void) +{ + gLinkCallback = sub_8007B44; +} + +bool32 sub_8007B24(void) +{ + if (gLinkCallback == sub_8007B44) + return TRUE; + else + return FALSE; +} + +static void sub_8007B44(void) +{ + if (gReceivedRemoteLinkPlayers == TRUE) + BuildSendCmd(0xCAFE); +} + +void ClearLinkCallback(void) +{ + gLinkCallback = NULL; +} + +void ClearLinkCallback_2(void) +{ + gLinkCallback = NULL; +} + +u8 GetLinkPlayerCount(void) +{ + return EXTRACT_PLAYER_COUNT(gLinkStatus); +} + +void OpenLinkTimed(void) +{ + sPlayerDataExchangeStatus = EXCHANGE_NOT_STARTED; + gLinkTimeOutCounter = 0; + OpenLink(); +} + +u8 GetLinkPlayerDataExchangeStatusTimed(void) +{ + s32 i; + s32 count = 0; + u32 index; + + if (gReceivedRemoteLinkPlayers == TRUE) + { + if (GetLinkPlayerCount() == 0) + { + gLinkErrorOccurred = TRUE; + CloseLink(); + } + + i = 0; + index = 0; + + while (i < GetLinkPlayerCount()) + { + if (gLinkPlayers[index].linkType == gLinkPlayers[0].linkType) + ++count; + ++index; + ++i; + } + + if (count == GetLinkPlayerCount()) + sPlayerDataExchangeStatus = EXCHANGE_COMPLETE; + else + sPlayerDataExchangeStatus = EXCHANGE_IN_PROGRESS; + } + else + { + gLinkTimeOutCounter++; + if (gLinkTimeOutCounter > 600) + sPlayerDataExchangeStatus = EXCHANGE_TIMED_OUT; + } + + return sPlayerDataExchangeStatus; +} + +bool8 IsLinkPlayerDataExchangeComplete(void) +{ + u8 i; + u8 count = 0; + u8 isComplete; + + for (i = 0; i < GetLinkPlayerCount(); i++) + if (gLinkPlayers[i].linkType == gLinkPlayers[0].linkType) + count++; + + if (count == GetLinkPlayerCount()) + { + isComplete = TRUE; + sPlayerDataExchangeStatus = EXCHANGE_COMPLETE; + } + else + { + isComplete = FALSE; + sPlayerDataExchangeStatus = EXCHANGE_IN_PROGRESS; + } + + return isComplete; +} + +u32 GetLinkPlayerTrainerId(u8 multiplayerId) +{ + return gLinkPlayers[multiplayerId].trainerId; +} + +void ResetLinkPlayers(void) +{ + s32 i; + for (i = 0; i < 4; i++) + memset(&gLinkPlayers[i], 0, sizeof(struct LinkPlayer)); +} + +static void ResetBlockSend(void) +{ + sBlockSend.active = FALSE; + sBlockSend.pos = 0; + sBlockSend.size = 0; + sBlockSend.src = NULL; +} + +static bool8 InitBlockSend(void *data, u32 size) +{ + if (sBlockSend.active) + { + return FALSE; + } + else + { + sBlockSend.multiplayerId = GetMultiplayerId(); + sBlockSend.active = TRUE; + sBlockSend.size = size; + sBlockSend.pos = 0; + + if (size > BLOCK_BUFFER_SIZE) + { + sBlockSend.src = data; + } + else + { + if (data != gBlockSendBuffer) + memcpy(gBlockSendBuffer, data, size); + sBlockSend.src = gBlockSendBuffer; + } + + BuildSendCmd(0xBBBB); + gLinkCallback = LinkCB_BlockSendBegin; + sBlockSendDelayCounter = 0; + return TRUE; + } +} + +static void LinkCB_BlockSendBegin(void) +{ + sBlockSendDelayCounter++; + if (sBlockSendDelayCounter > 2) + gLinkCallback = LinkCB_BlockSend; +} + +static void LinkCB_BlockSend(void) +{ + s32 i; + u8 *buffer = sBlockSend.src; + + gSendCmd[0] = 0x8888; + + for (i = 0; i < CMD_LENGTH - 1; i++) + { + s32 offset = sBlockSend.pos + 2 * i; + gSendCmd[i + 1] = (buffer[offset + 1] << 8) | buffer[offset]; + } + + sBlockSend.pos += (CMD_LENGTH - 1) * 2; + + if (sBlockSend.size <= sBlockSend.pos) + { + sBlockSend.active = FALSE; + gLinkCallback = LinkCB_BlockSendEnd; + } +} + +static void LinkCB_BlockSendEnd(void) +{ + gLinkCallback = NULL; +} + +static void sub_8007E04(void) +{ + GetMultiplayerId(); + BuildSendCmd(0x4444); + dword_20238BC++; +} + +void sub_8007E24(void) +{ + dword_20238BC = 0; + gLinkCallback = sub_8007E04; +} + +static u32 sub_8007E40(void) +{ + return dword_20238BC; +} + +void sub_8007E4C(void) +{ + BuildSendCmd(0xAAAA); +} + +u8 GetMultiplayerId(void) +{ + return SIO_MULTI_CNT->id; +} + +u8 bitmask_all_link_players_but_self(void) +{ + return ((1 << GetMultiplayerId()) ^ 0xF); +} + +bool8 SendBlock(u8 a1, void *a2, u16 a3) +{ + return InitBlockSend(a2, a3); +} + +bool8 sub_8007E9C(u8 a1) +{ + if (!gLinkCallback) + { + gBlockRequestType = a1; + BuildSendCmd(0xCCCC); + return TRUE; + } + else + { + return FALSE; + } +} + +bool8 sub_8007ECC(void) +{ + u8 retVal = FALSE; + + if (!gLinkCallback) + retVal = TRUE; + + return retVal; +} + +u8 GetBlockRecievedStatus(void) +{ + return (gBlockReceived[3] << 3) + | (gBlockReceived[2] << 2) + | (gBlockReceived[1] << 1) + | gBlockReceived[0]; +} + +static void SetBlockReceivedFlag(u8 multiplayerId) +{ + gBlockReceived[multiplayerId] = TRUE; +} + +void ResetBlockReceivedFlags(void) +{ + s32 i; + for (i = 0; i < 4; i++) + gBlockReceived[i] = FALSE; +} + +void ResetBlockReceivedFlag(u8 multiplayerId) +{ + if (gBlockReceived[multiplayerId]) + gBlockReceived[multiplayerId] = FALSE; +} + +void sub_8007F4C(void) +{ + if ((gLinkStatus & LINK_STAT_MASTER) && EXTRACT_PLAYER_COUNT(gLinkStatus) > 1) + gShouldAdvanceLinkState = 1; +} + +static u16 LinkTestCalcBlockChecksum(void *data, u16 size) +{ + u16 sum = 0; + u16 i; + + for (i = 0; i < size / 2; i++) + sum += ((u16 *)data)[i]; + + return sum; +} + +static void PrintHexDigit(u8 tileNum, u8 x, u8 y) +{ + u16 *tilemap = (u16 *)BG_SCREEN_ADDR(gLinkTestBGInfo.screenBaseBlock); + tilemap[(32 * y) + x] = (gLinkTestBGInfo.paletteNum << 12) | (tileNum + 1); +} + +static void PrintHex(u32 num, u8 x, u8 y, u8 maxDigits) +{ + u8 buffer[16]; + s32 i; + + for (i = 0; i < maxDigits; i++) + { + buffer[i] = num & 0xF; + num >>= 4; + } + + for (i = maxDigits - 1; i >= 0; i--) + { + PrintHexDigit(buffer[i], x, y); + x++; + } +} + +static void LinkCB_RequestPlayerDataExchange(void) +{ + // Only one request needs to be sent, so only the master sends it. + if (gLinkStatus & LINK_STAT_MASTER) + BuildSendCmd(0x2222); + gLinkCallback = NULL; +} + +void Task_PrintTestData(u8 taskId) +{ + s32 i; + + PrintHex(gShouldAdvanceLinkState, 2, 1, 2); + PrintHex(gLinkStatus, 15, 1, 8); + PrintHex(gLink.state, 2, 10, 2); + PrintHex(EXTRACT_PLAYER_COUNT(gLinkStatus), 15, 10, 2); + PrintHex(GetMultiplayerId(), 15, 12, 2); + PrintHex(gLastSendQueueCount, 25, 1, 2); + PrintHex(gLastRecvQueueCount, 25, 2, 2); + PrintHex(GetBlockRecievedStatus(), 15, 5, 2); + PrintHex(gLinkDebugValue1, 2, 12, 8); + PrintHex(gLinkDebugValue2, 2, 13, 8); + PrintHex(GetSioMultiSI(), 25, 5, 1); + PrintHex(IsSioMultiMaster(), 25, 6, 1); + PrintHex(IsLinkConnectionEstablished(), 25, 7, 1); + PrintHex(HasLinkErrorOccurred(), 25, 8, 1); + + for (i = 0; i < MAX_LINK_PLAYERS; i++) + PrintHex(gLinkTestBlockChecksums[i], 10, 4 + i, 4); +} + +void SetLinkDebugValues(u32 value1, u32 value2) +{ + gLinkDebugValue1 = value1; + gLinkDebugValue2 = value2; +} + +u8 sub_8008198(void) +{ + u8 result = 0; + s32 i; + + for (i = 0; i < gSavedLinkPlayerCount; i++) + result |= (1 << i); + + return result; +} + +void sub_80081C8(u8 playerCount) +{ + s32 i; + + gSavedLinkPlayerCount = playerCount; + gSavedMultiplayerId = GetMultiplayerId(); + + for (i = 0; i < 4; i++) + gSavedLinkPlayers[i] = gLinkPlayers[i]; +} + +u8 sub_800820C(void) +{ + return gSavedLinkPlayerCount; +} + +u8 sub_8008218(void) +{ + return gSavedMultiplayerId; +} + +static bool8 sub_8008224(void) +{ + s32 count = 0; + s32 i; + + for (i = 0; i < gSavedLinkPlayerCount; i++) + if (gLinkPlayers[i].trainerId == gSavedLinkPlayers[i].trainerId) + count++; + + if (count == gSavedLinkPlayerCount) + return TRUE; + else + return FALSE; +} + +void sub_800826C(void) +{ + u8 i; + + for (i = 0; i < gSavedLinkPlayerCount; i++) + { + if (gSavedLinkPlayers[i].trainerId != gLinkPlayers[i].trainerId + || StringCompareWithoutExtCtrlCodes(gSavedLinkPlayers[i].name, gLinkPlayers[i].name)) + { + gLinkErrorOccurred = TRUE; + CloseLink(); + SetMainCallback2(CB2_LinkError); + } + } +} + +void sub_80082EC(void) +{ + gSavedLinkPlayerCount = 0; + gSavedMultiplayerId = 0; +} + +u8 GetLinkPlayerCount_2(void) +{ + return EXTRACT_PLAYER_COUNT(gLinkStatus); +} + +bool8 IsLinkMaster(void) +{ + return EXTRACT_MASTER(gLinkStatus); +} + +static u8 GetDummy2(void) +{ + return sDummy2; +} + +void sub_800832C(void) +{ + if (!gLinkCallback) + { + gLinkCallback = sub_8008350; + gLinkDummyBool = FALSE; + } +} + +static void sub_8008350(void) +{ + if (gLastRecvQueueCount == 0) + { + BuildSendCmd(0x5FFF); + gLinkCallback = sub_800837C; + } +} + +static void sub_800837C(void) +{ + s32 i; + s32 totalCount = GetLinkPlayerCount(); + s32 count = 0; + + for (i = 0; i < totalCount; i++) + if (u8_array_3002B78[i]) + count++; + + if (count == totalCount) + { + gUnknown_020239F8 &= 0xFFDF; + gLinkVSyncDisabled = TRUE; + CloseLink(); + gLinkCallback = NULL; + gLinkDummyBool = TRUE; + } +} + +static void sub_80083E0(void) +{ + s32 i; + s32 totalCount = GetLinkPlayerCount(); + s32 count = 0; + + for (i = 0; i < totalCount; i++) + { + if (gLinkPlayers[i].language == 1) + count++; + else if (u8_array_3002B78[i]) + count++; + } + + if (count == totalCount) + { + gUnknown_020239F8 &= 0xFFDF; + gLinkVSyncDisabled = TRUE; + CloseLink(); + gLinkCallback = 0; + gLinkDummyBool = TRUE; + } +} + +static void sub_8008454(void) +{ + if (gLastRecvQueueCount == 0) + { + BuildSendCmd(0x5FFF); + gLinkCallback = sub_80083E0; + } +} + +void sub_8008480(void) +{ + if (!gLinkCallback) + { + gLinkCallback = sub_8008454; + gLinkDummyBool = FALSE; + } +} + +void sub_80084A4(void) +{ + if (!gLinkCallback) + gLinkCallback = sub_80084C8; + gLinkDummyBool = FALSE; +} + +static void sub_80084C8(void) +{ + if (gLastRecvQueueCount == 0) + { + BuildSendCmd(0x2FFE); + gLinkCallback = sub_80084F4; + } +} + +static void sub_80084F4(void) +{ + u8 totalCount = GetLinkPlayerCount(); + u8 count = 0; + + while (count < totalCount && u8_array_3002B70[count]) + count++; + + if (count == totalCount) + { + u8 i; + for (i = 0; i < 4; i++) + u8_array_3002B70[i] = 0; + gLinkCallback = NULL; + } +} + +static void CheckErrorStatus(void) +{ + if (gLinkOpen) + { + if (gLinkStatus & LINK_STAT_ERRORS) + { + if (!gSuppressLinkErrorMessage) + { + sErrorLinkStatus = gLinkStatus; + sErrorLastRecvQueueCount = gLastRecvQueueCount; + sErrorLastSendQueueCount = gLastSendQueueCount; + SetMainCallback2(CB2_LinkError); + } + gLinkErrorOccurred = TRUE; + CloseLink(); + } + } +} + +void CB2_LinkError(void) +{ + ResetSpriteData(); + FreeAllSpritePalettes(); + pal_fade_control_reset_maybe(); + rboxid_80040B8(0, 0, 2); + ResetTasks(); + SetVBlankCallback(VBlankCB_LinkTest); + SetUpWindowConfig(&gWindowConfig_81E7198); + sub_8071C4C(&gWindowConfig_81E7198); + sub_8071EF4(); + REG_BLDALPHA = 0; + REG_BG0VOFS = 0; + REG_BG0HOFS = 0; + REG_DISPCNT = 320; + gUnknown_3001BB4 = 0; + CreateTask(Task_DestroySelf, 0); + sub_8074D08(); + RunTasks(); + AnimateSprites(); + BuildOamBuffer(); + fade_and_return_progress_probably(); + SetMainCallback2(CB2_PrintErrorMessage); +} + +static void CB2_PrintErrorMessage(void) +{ + u8 array[64]; // unused + + switch (gMain.state) + { + case 0: + sub_80729D8(gUnknown_0842C34C, 20, 56, 1); + break; + case 30: + case 60: + audio_play(SE_BOO); + break; + case 90: + audio_play(SE_BOO); + break; + } + + if (gMain.state != 200) + gMain.state++; +} + +u8 GetSioMultiSI(void) +{ + return (REG_SIOCNT >> SIO_MULTI_SI_SHIFT) & SIO_MULTI_SI_MASK; +} + +static bool8 IsSioMultiMaster(void) +{ + bool8 isMaster = FALSE; + + if ((REG_SIOCNT & SIO_MULTI_SD) && !(REG_SIOCNT & SIO_MULTI_SI)) + isMaster = TRUE; + + return isMaster; +} + +bool8 IsLinkConnectionEstablished(void) +{ + return EXTRACT_CONN_ESTABLISHED(gLinkStatus); +} + +void SetSuppressLinkErrorMessage(bool8 value) +{ + gSuppressLinkErrorMessage = value; +} + +bool8 HasLinkErrorOccurred(void) +{ + return gLinkErrorOccurred; +} + +static void DisableSerial(void) +{ + gLinkSavedIme = REG_IME; + REG_IME = 0; + REG_IE &= ~(INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL); + REG_IME = gLinkSavedIme; + + REG_SIOCNT = 0; + REG_TM3CNT_H = 0; + REG_IF = INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL; + + CpuFill32(&gLink, 0, sizeof(gLink)); +} + +static void EnableSerial(void) +{ + gLinkSavedIme = REG_IME; + REG_IME = 0; + REG_IE &= ~(INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL); + REG_IME = gLinkSavedIme; + + REG_RCNT = 0; + + REG_SIOCNT = SIO_MULTI_MODE; + REG_SIOCNT |= SIO_INTR_ENABLE | SIO_115200_BPS; + + gLinkSavedIme = REG_IME; + REG_IME = 0; + REG_IE |= INTR_FLAG_SERIAL; + REG_IME = gLinkSavedIme; + + REG_SIOMLT_SEND = 0; + + CpuFill32(&gLink, 0, sizeof(gLink)); + + sNumVBlanksWithoutSerialIntr = 0; + sSendNonzeroCheck = 0; + sRecvNonzeroCheck = 0; + sChecksumAvailable = FALSE; + sHandshakePlayerCount = 0; + gLastSendQueueCount = 0; + gLastRecvQueueCount = 0; +} + +void ResetSerial(void) +{ + EnableSerial(); + DisableSerial(); +} + +u32 LinkMain1(u8 *shouldAdvanceLinkState, u16 *sendCmd, u16 recvCmds[CMD_LENGTH][MAX_LINK_PLAYERS]) +{ + u32 retVal; + u32 retVal2; + + switch (gLink.state) + { + case LINK_STATE_START0: + DisableSerial(); + gLink.state = LINK_STATE_START1; + break; + case LINK_STATE_START1: + if (*shouldAdvanceLinkState == 1) + { + EnableSerial(); + gLink.state = LINK_STATE_HANDSHAKE; + } + break; + case LINK_STATE_HANDSHAKE: + switch (*shouldAdvanceLinkState) + { + case 1: + if (gLink.isMaster == 8 && gLink.playerCount > 1) + gLink.handshakeAsMaster = TRUE; + break; + case 2: + gLink.state = LINK_STATE_START0; + REG_SIOMLT_SEND = 0; + break; + default: + CheckMasterOrSlave(); + break; + } + break; + case LINK_STATE_INIT_TIMER: + InitTimer(); + gLink.state = LINK_STATE_CONN_ESTABLISHED; + case LINK_STATE_CONN_ESTABLISHED: + EnqueueSendCmd(sendCmd); + DequeueRecvCmds(recvCmds); + break; + } + + *shouldAdvanceLinkState = 0; + + retVal = gLink.localId; + retVal |= (gLink.playerCount << 2); + + if (gLink.isMaster == 8) + retVal |= 0x20; + + { + u32 receivedNothing = gLink.receivedNothing << 8; + u32 link_field_F = gLink.link_field_F << 9; + u32 hardwareError = gLink.hardwareError << 12; + u32 badChecksum = gLink.badChecksum << 13; + u32 queueFull = gLink.queueFull << 14; + u32 val; + + if (gLink.state == LINK_STATE_CONN_ESTABLISHED) + { + val = 0x40; + val |= receivedNothing; + val |= retVal; + val |= link_field_F; + val |= hardwareError; + val |= badChecksum; + val |= queueFull; + } + else + { + val = retVal; + val |= receivedNothing; + val |= link_field_F; + val |= hardwareError; + val |= badChecksum; + val |= queueFull; + } + + retVal = val; + } + + if (gLink.lag == LAG_MASTER) + retVal |= 0x10000; + + if (gLink.localId > 3) + retVal |= 0x20000; + + retVal2 = retVal; + if (gLink.lag == LAG_SLAVE) + retVal2 |= 0x40000; + + return retVal2; +} + +static void CheckMasterOrSlave(void) +{ + u32 terminals = *(u32 *)REG_ADDR_SIOCNT & (SIO_MULTI_SD | SIO_MULTI_SI); + + if (terminals == SIO_MULTI_SD && !gLink.localId) + gLink.isMaster = 8; + else + gLink.isMaster = 0; +} + +static void InitTimer(void) +{ + if (gLink.isMaster) + { + REG_TM3CNT_L = 65339; + REG_TM3CNT_H = TIMER_INTR_ENABLE | TIMER_64CLK; + + gLinkSavedIme = REG_IME; + REG_IME = 0; + REG_IE |= INTR_FLAG_TIMER3; + REG_IME = gLinkSavedIme; + } +} + +static void EnqueueSendCmd(u16 *sendCmd) +{ + gLinkSavedIme = REG_IME; + REG_IME = 0; + + if (gLink.sendQueue.count < QUEUE_CAPACITY) + { + u8 i; + u8 offset = gLink.sendQueue.pos + gLink.sendQueue.count; + + if (offset >= QUEUE_CAPACITY) + offset -= QUEUE_CAPACITY; + + for (i = 0; i < CMD_LENGTH; i++) + { + sSendNonzeroCheck |= *sendCmd; + gLink.sendQueue.data[i][offset] = *sendCmd; + *sendCmd = 0; + sendCmd++; + } + } + else + { + gLink.queueFull = QUEUE_FULL_SEND; + } + + if (sSendNonzeroCheck) + { + gLink.sendQueue.count++; + sSendNonzeroCheck = 0; + } + + REG_IME = gLinkSavedIme; + gLastSendQueueCount = gLink.sendQueue.count; +} + +void DequeueRecvCmds(u16 recvCmds[CMD_LENGTH][MAX_LINK_PLAYERS]) +{ + u8 i; + u8 j; + + gLinkSavedIme = REG_IME; + REG_IME = 0; + + if (gLink.recvQueue.count == 0) + { + for (i = 0; i < CMD_LENGTH; i++) + for (j = 0; j < gLink.playerCount; j++) + recvCmds[i][j] = 0; + + gLink.receivedNothing = TRUE; + } + else + { + for (i = 0; i < CMD_LENGTH; i++) + for (j = 0; j < gLink.playerCount; j++) + recvCmds[i][j] = gLink.recvQueue.data[j][i][gLink.recvQueue.pos]; + + gLink.recvQueue.count--; + gLink.recvQueue.pos++; + + if (gLink.recvQueue.pos >= QUEUE_CAPACITY) + gLink.recvQueue.pos = 0; + + gLink.receivedNothing = FALSE; + } + + REG_IME = gLinkSavedIme; +} + +void LinkVSync(void) +{ + if (gLink.isMaster) + { + switch (gLink.state) + { + case LINK_STATE_HANDSHAKE: + StartTransfer(); + break; + case LINK_STATE_CONN_ESTABLISHED: + if (gLink.serialIntrCounter > 8) + { + if (gLink.lag == LAG_MASTER) + return; + gLink.serialIntrCounter = 0; + } + else if (gLink.hardwareError != TRUE) + { + gLink.lag = LAG_MASTER; + return; + } + StartTransfer(); + break; + } + } + else if (gLink.state == LINK_STATE_CONN_ESTABLISHED + || gLink.state == LINK_STATE_HANDSHAKE) + { + sNumVBlanksWithoutSerialIntr++; + + if (sNumVBlanksWithoutSerialIntr > 10) + { + if (gLink.state == LINK_STATE_CONN_ESTABLISHED) + gLink.lag = LAG_SLAVE; + + if (gLink.state == LINK_STATE_HANDSHAKE) + { + gLink.playerCount = 0; + gLink.link_field_F = 0; + } + } + } +} + +void Timer3Intr(void) +{ + StopTimer(); + StartTransfer(); +} + +void SerialCB(void) +{ + gLink.localId = SIO_MULTI_CNT->id; + + switch (gLink.state) + { + case LINK_STATE_CONN_ESTABLISHED: + gLink.hardwareError = SIO_MULTI_CNT->error; + DoRecv(); + DoSend(); + SendRecvDone(); + break; + case LINK_STATE_HANDSHAKE: + if (DoHandshake()) + { + if (gLink.isMaster) + { + gLink.state = LINK_STATE_INIT_TIMER; + gLink.serialIntrCounter = 8; + } + else + { + gLink.state = LINK_STATE_CONN_ESTABLISHED; + } + } + break; + } + + gLink.serialIntrCounter++; + sNumVBlanksWithoutSerialIntr = 0; + + if (gLink.serialIntrCounter == 8) + gLastRecvQueueCount = gLink.recvQueue.count; +} + +static void StartTransfer(void) +{ + REG_SIOCNT |= SIO_START; +} + +static bool8 DoHandshake(void) +{ + u8 i; + u8 playerCount = 0; + u16 minRecv = 0xFFFF; + + if (gLink.handshakeAsMaster == TRUE) + REG_SIOMLT_SEND = MASTER_HANDSHAKE; + else + REG_SIOMLT_SEND = SLAVE_HANDSHAKE; + + *(u64 *)&gLink.tempRecvBuffer[0] = REG_SIOMLT_RECV; + REG_SIOMLT_RECV = 0; + + gLink.handshakeAsMaster = FALSE; + + for (i = 0; i < 4; i++) + { + if ((gLink.tempRecvBuffer[i] & ~0x3) == SLAVE_HANDSHAKE + || gLink.tempRecvBuffer[i] == MASTER_HANDSHAKE) + { + playerCount++; + + if (minRecv > gLink.tempRecvBuffer[i] && gLink.tempRecvBuffer[i] != 0) + minRecv = gLink.tempRecvBuffer[i]; + } + else + { + if (gLink.tempRecvBuffer[i] != 0xFFFF) + playerCount = 0; + break; + } + } + + gLink.playerCount = playerCount; + + // The handshake is successful when: + // 1. There are multiple players. + // 2. The number of players agrees with the last attempt. + // 3. Player no. 0 is identifying as the master. + if (gLink.playerCount > 1 + && gLink.playerCount == sHandshakePlayerCount + && gLink.tempRecvBuffer[0] == MASTER_HANDSHAKE) + return TRUE; + + if (gLink.playerCount > 1) + gLink.link_field_F = (minRecv & 3) + 1; + else + gLink.link_field_F = 0; + + sHandshakePlayerCount = gLink.playerCount; + + return FALSE; +} + +static void DoRecv(void) +{ + u16 recvBuffer[4]; + u8 i; + + *(u64 *)&recvBuffer[0] = REG_SIOMLT_RECV; + + if (gLink.sendCmdIndex == 0) + { + for (i = 0; i < gLink.playerCount; i++) + if (gLink.checksum != recvBuffer[i] && sChecksumAvailable) + gLink.badChecksum = TRUE; + + gLink.checksum = 0; + sChecksumAvailable = TRUE; + } + else + { + u8 index = gLink.recvQueue.pos + gLink.recvQueue.count; + + if (index >= QUEUE_CAPACITY) + index -= QUEUE_CAPACITY; + + if (gLink.recvQueue.count < QUEUE_CAPACITY) + { + for (i = 0; i < gLink.playerCount; i++) + { + gLink.checksum += recvBuffer[i]; + sRecvNonzeroCheck |= recvBuffer[i]; + gLink.recvQueue.data[i][gLink.recvCmdIndex][index] = recvBuffer[i]; + } + } + else + { + gLink.queueFull = QUEUE_FULL_RECV; + } + + gLink.recvCmdIndex++; + + if (gLink.recvCmdIndex == CMD_LENGTH && sRecvNonzeroCheck) + { + gLink.recvQueue.count++; + sRecvNonzeroCheck = 0; + } + } +} + +static void DoSend(void) +{ + if (gLink.sendCmdIndex == CMD_LENGTH) + { + REG_SIOMLT_SEND = gLink.checksum; + + if (!sSendBufferEmpty) + { + gLink.sendQueue.count--; + gLink.sendQueue.pos++; + + if (gLink.sendQueue.pos >= QUEUE_CAPACITY) + gLink.sendQueue.pos = 0; + } + else + { + sSendBufferEmpty = FALSE; + } + } + else + { + if (!sSendBufferEmpty && gLink.sendQueue.count == 0) + sSendBufferEmpty = TRUE; + + if (sSendBufferEmpty) + REG_SIOMLT_SEND = 0; + else + REG_SIOMLT_SEND = gLink.sendQueue.data[gLink.sendCmdIndex][gLink.sendQueue.pos]; + + gLink.sendCmdIndex++; + } +} + +static void StopTimer(void) +{ + if (gLink.isMaster) + { + REG_TM3CNT_H &= ~TIMER_ENABLE; + REG_TM3CNT_L = 65339; + } +} + +static void SendRecvDone(void) +{ + if (gLink.recvCmdIndex == CMD_LENGTH) + { + gLink.sendCmdIndex = 0; + gLink.recvCmdIndex = 0; + } + else if (gLink.isMaster) + { + REG_TM3CNT_H |= TIMER_ENABLE; + } +} + +static void ResetSendBuffer(void) +{ + u8 i; + u8 j; + + gLink.sendQueue.count = 0; + gLink.sendQueue.pos = 0; + + for (i = 0; i < CMD_LENGTH; i++) + for (j = 0; j < QUEUE_CAPACITY; j++) + gLink.sendQueue.data[i][j] = 0xEFFF; +} + +static void ResetRecvBuffer(void) +{ + u8 i; + u8 j; + u8 k; + + gLink.recvQueue.count = 0; + gLink.recvQueue.pos = 0; + + for (i = 0; i < 4; i++) + for (j = 0; j < CMD_LENGTH; j++) + for (k = 0; k < QUEUE_CAPACITY; k++) + gLink.recvQueue.data[i][j][k] = 0xEFFF; +} diff --git a/src/main.c b/src/main.c index 0c5055e60..ca04f14c5 100644 --- a/src/main.c +++ b/src/main.c @@ -2,15 +2,11 @@ #include "gba/m4a_internal.h" #include "gba/flash_internal.h" #include "siirtc.h" -#include "rtc.h" #include "main.h" +#include "rtc.h" +#include "link.h" extern struct SoundInfo gSoundInfo; -extern u16 gUnknown_3002A20; -extern u32 gUnknown_3002A60; -extern u16 gUnknown_3002F90; -extern u8 gUnknown_3003040; -extern u8 gUnknown_30033A9[]; extern u32 gUnknown_3004820; extern u32 IntrMain[]; @@ -53,20 +49,17 @@ COMM_4(struct Main gMain) COMM_2(u16 gKeyRepeatContinueDelay) COMM_2(u8 gUnknown_3001BB4) COMM_4(IntrFunc gIntrTable[INTR_COUNT]) -COMM_2(u8 gUnknown_3001BF8) +COMM_2(bool8 gLinkVSyncDisabled) COMM_4(u32 IntrMain_Buffer[0x200]) COMM_2(u8 gPcmDmaCounter) EWRAM_DATA void (*gFlashTimerIntrFunc)(void) = NULL; -extern void sub_8008BEC(void); extern void sub_800C35C(void); extern u16 Random(void); extern void SeedRng(u16); extern void remove_some_task(void); extern void c2_copyright_1(); -extern u32 sub_8008848(u8 *, u16 *, u16 *); -extern void sub_80075F0(u16 *); extern u32 sub_80558AC(void); extern u32 sub_8055910(void); extern u32 sub_8055940(void); @@ -74,7 +67,7 @@ extern void sound_something(void); extern void CheckForFlashMemory(void); extern void sound_sources_off(void); -void CallCallbacksWrapper(void); +void UpdateLinkAndCallCallbacks(void); void InitMainCallbacks(void); void CallCallbacks(void); void SetMainCallback2(MainCallback callback); @@ -120,24 +113,24 @@ void AgbMain() && (gMain.heldKeysRaw & B_START_SELECT) == B_START_SELECT) DoSoftReset(); - if (gUnknown_30033A9[0] > 1 && sub_8055910() == 1) + if (gLink.sendQueue.count > 1 && sub_8055910() == 1) { gUnknown_3001764 = 1; - CallCallbacksWrapper(); + UpdateLinkAndCallCallbacks(); gUnknown_3001764 = 0; } else { gUnknown_3001764 = 0; - CallCallbacksWrapper(); + UpdateLinkAndCallCallbacks(); - if (gUnknown_30033A9[0xC84] > 1) + if (gLink.recvQueue.count > 1) { if (sub_80558AC() == 1) { gMain.newKeys = 0; gUnknown_3001764 = 1; - CallCallbacksWrapper(); + UpdateLinkAndCallCallbacks(); gUnknown_3001764 = 0; } } @@ -149,12 +142,11 @@ void AgbMain() } } -// The conditions seem to be related to serial communication. -void CallCallbacksWrapper(void) +void UpdateLinkAndCallCallbacks(void) { - gUnknown_3002A60 = sub_8008848(&gUnknown_3003040, &gUnknown_3002F90, &gUnknown_3002A20); - sub_80075F0(&gMain.heldKeys); - if (!(gUnknown_3002A60 & 0x100) || sub_8055940() != 1) + gLinkStatus = LinkMain1(&gShouldAdvanceLinkState, gSendCmd, gRecvCmds); + LinkMain2(&gMain.heldKeys); + if (!(gLinkStatus & LINK_STAT_RECEIVED_NOTHING) || sub_8055940() != 1) CallCallbacks(); } @@ -289,8 +281,8 @@ void VBlankIntr(void) { u16 savedIme; - if (gUnknown_3001BF8 == 0) - sub_8008BEC(); + if (!gLinkVSyncDisabled) + LinkVSync(); savedIme = REG_IME; REG_IME = 0; diff --git a/src/string_util.c b/src/string_util.c index 43195a219..514e7031f 100644 --- a/src/string_util.c +++ b/src/string_util.c @@ -19,11 +19,14 @@ static u8 *ExpandPlaceholder_GoodLeader(void); static u8 *ExpandPlaceholder_EvilLegendary(void); static u8 *ExpandPlaceholder_GoodLegendary(void); +COMM_4(u8 gUnknownStringVar[16]) + const u8 gEmptyString_81E72B0[] = _""; const u8 gRightPointingTriangleString[] = _"▶"; -const u8 sDigits[] = @"0123456789ABCDEF"; -const s32 sPowersOfTen[] = +static const u8 sDigits[] = @"0123456789ABCDEF"; + +static const s32 sPowersOfTen[] = { 1, 10, @@ -37,12 +40,12 @@ const s32 sPowersOfTen[] = 1000000000, }; -const u8 sSetBrailleFont[] = { 0xFC, 0x06, 0x06, 0xFF }; -const u8 sGotoLine2[] = { 0xFE, 0xFC, 0x0E, 0x02, 0xFF }; +static const u8 sSetBrailleFont[] = { 0xFC, 0x06, 0x06, 0xFF }; +static const u8 sGotoLine2[] = { 0xFE, 0xFC, 0x0E, 0x02, 0xFF }; typedef u8 *(*ExpandPlaceholderFunc)(void); -const ExpandPlaceholderFunc sExpandPlaceholderFuncs[] = +static const ExpandPlaceholderFunc sExpandPlaceholderFuncs[] = { ExpandPlaceholder_UnknownStringVar, ExpandPlaceholder_PlayerName, @@ -74,8 +77,6 @@ extern u8 gExpandedPlaceholder_Groudon[]; extern u8 gExpandedPlaceholder_Brendan[]; extern u8 gExpandedPlaceholder_May[]; -extern u8 gUnknownStringVar[]; - u8 *StringCopy10(u8 *dest, u8 *src) { u8 i; diff --git a/src/text.c b/src/text.c index 497f50dbb..c6455f1f0 100644 --- a/src/text.c +++ b/src/text.c @@ -3720,9 +3720,9 @@ u8 GetStringWidthGivenWindowConfig(struct WindowConfig *winConfig, u8 *s) return GetStringWidth(&sTempWindow, s); } -void SetStringCharset(u8 *s, u8 charset) +void ConvertInternationalString(u8 *s, u8 language) { - if (charset == CHARSET_JAPANESE) + if (language == CHARSET_JAPANESE) { u8 i; u8 length; |