diff --git a/include/link_handler.h b/include/link_handler.h index fe4eb07..90742d5 100644 --- a/include/link_handler.h +++ b/include/link_handler.h @@ -247,6 +247,7 @@ enum LinkConnectionError enum PayloadCommand { + CMD_NONE = -1, CMD_ReloadCurrentBox, // no arguments used. Reloads the current box from SRAM. Use this as the first command byte before performing other commands. CMD_TransferPokemon, // 1st argument = secondary payload size, 2nd argument = box that should be transferred from. This command uses a secondary payload. The size of this secondary payload needs to be declared beforehand. Prior to requesting a secondary payload, the program will use the second argument to load a specific box from SRAM. Box numbers are 0-indexed and range from 0x00 (box 1) to 0x0B(box 12). This load procedure currently cannot be skipped. Once the secondary payload arrives, the program will verify its integrity and align the payload. Afterwards, it will use the information within this payload to remove pokémon from the current box. Once all transferred pokémon have been removed, the program will save the current box. CMD_SoftReset, // no arguments used. Instantly soft resets the game. @@ -274,14 +275,27 @@ enum PayloadCommand struct LinkPacket { + bool inUse = false; + // Out packet parameters - PayloadCommand command; + PayloadCommand command = CMD_NONE; byte argument[2] = {0x00, 0x00}; - u16 pointer; + u16 pointer = 0; + byte packetID; // Incoming data LinkConnectionError latestError = NO_ERROR; byte recievedData[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + LinkPacket() {}; + LinkPacket(PayloadCommand cmd, byte arg1, byte arg2, u16 addr) + { + command = cmd; + argument[0] = arg1; + argument[1] = arg2; + pointer = addr; + inUse = true; + }; }; class LinkConnection @@ -315,7 +329,7 @@ public: // This MUST be a power of 2! #define LINK_PACKET_ARRAY_SIZE 4 LinkPacket linkPacketArr[LINK_PACKET_ARRAY_SIZE]; - int linkPacketArrIndex = 0; + byte linkPacketArrIndex = 0; u16 linkPacketDataAddr = 0; u16 linkPacketDataStart = 0; int linkPacketDataSize = 0; @@ -326,6 +340,8 @@ public: bool skipPrint = false; // Skips printing to the screen bool newPacket = false; + bool softResetActivated = false; + void setup(const u16 *debug_charset); void startConnection(LinkState startState); bool earlyExit(); @@ -334,7 +350,16 @@ public: void writeData(); void handleStateLogic(); void prepareForNextCycle(); - bool readMemorySection(u16 dataPointer, byte outArray[], int outArraySize); + void resetLinkPackets(); + + // These are all the Link Commands + bool LinkCommand_InitalizeConnection(bool waitForCompletion = true); + bool LinkCommand_ReloadCurrentBox(bool waitForCompletion = true); + bool LinkCommand_TransferPokemon(bool waitForCompletion = true); + bool LinkCommand_SoftReset(bool waitForCompletion = true); + bool LinkCommand_ModifySRAMAccess(bool enableSRAM, byte SRAMbank, bool waitForCompletion = true); + bool LinkCommand_RunSecondaryPayload(bool waitForCompletion = true); + bool LinkCommand_ReadMemorySection(u16 dataPointer, byte outArray[], int outArraySize, bool waitForCompletion = true); // Some operations are too long to be done within the IRQ. // So we need to handle them in the main loop instead to avoid data corruption. @@ -347,6 +372,7 @@ private: bool allPacketsProcessed(); bool processPacket(); void loadNextPacket(); + void waitForEnd(); // Used for debug features #define LINE_WIDTH 24 diff --git a/source/dbg/debug_mode.cpp b/source/dbg/debug_mode.cpp index d01b0f7..ee7a5bf 100644 --- a/source/dbg/debug_mode.cpp +++ b/source/dbg/debug_mode.cpp @@ -2,8 +2,8 @@ debug_options g_debug_options = { - .print_link_data = (false && DEBUG_MODE), - .print_link_packets = (false && DEBUG_MODE), + .print_link_data = (true && DEBUG_MODE), + .print_link_packets = (true && DEBUG_MODE), .instant_text_speed = (true && DEBUG_MODE), .ignore_game_pak = (true && DEBUG_MODE), .ignore_game_pak_sprites = (false && DEBUG_MODE), diff --git a/source/link_handler.cpp b/source/link_handler.cpp index c305527..108c327 100644 --- a/source/link_handler.cpp +++ b/source/link_handler.cpp @@ -515,24 +515,25 @@ void LinkConnection::handleStateLogic() break; case 1: nextOutData = linkPacketArrIndex; + linkPacketArr[linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE].packetID = linkPacketArrIndex; break; case 2: - nextOutData = linkPacketArr[linkPacketArrIndex].command; + nextOutData = linkPacketArr[linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE].command; break; case 3: - nextOutData = linkPacketArr[linkPacketArrIndex].argument[0]; + nextOutData = linkPacketArr[linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE].argument[0]; break; case 4: - nextOutData = linkPacketArr[linkPacketArrIndex].argument[1]; + nextOutData = linkPacketArr[linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE].argument[1]; break; case 5: - nextOutData = linkPacketArr[linkPacketArrIndex].pointer >> 0; + nextOutData = linkPacketArr[linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE].pointer >> 0; break; case 6: - nextOutData = linkPacketArr[linkPacketArrIndex].pointer >> 8; + nextOutData = linkPacketArr[linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE].pointer >> 8; break; case TOTAL_PACKET_LENGTH - 1: - linkPacketArrIndex = (linkPacketArrIndex + 1) % LINK_PACKET_ARRAY_SIZE; + linkPacketArrIndex = (linkPacketArrIndex + 1) & 0x7F; default: nextOutData = 0xFF; break; @@ -698,7 +699,12 @@ bool LinkConnection::earlyExit() bool LinkConnection::processPacket() { int checksum = 0; - LinkPacket &currPacket = linkPacketArr[dataOutBuffer[INP_COUNTER_INDEX] & (LINK_PACKET_ARRAY_SIZE - 1)]; + LinkPacket &currPacket = linkPacketArr[dataOutBuffer[INP_COUNTER_INDEX] % LINK_PACKET_ARRAY_SIZE]; + if (currPacket.packetID != dataOutBuffer[INP_COUNTER_INDEX]) + { + // This packet is not the correct ID for the response, ignore it + return false; + } for (int i = INP_COUNTER_INDEX; i < INP_LENGTH; i++) { @@ -720,11 +726,19 @@ bool LinkConnection::processPacket() (dataOutBuffer[INP_DATA_INDEX + i] << 1) | ((lsbByte >> (7 - i)) & 0b1); } - if (checksum != dataOutBuffer[INP_CHECKSUM_INDEX]) + // The soft reset command has no response, don't expect one. + if (currPacket.command == CMD_SoftReset) { + // If this is a soft reset packet, that means that we don't care what we recieve from the final packet. + softResetActivated = true; + } + else if (checksum != dataOutBuffer[INP_CHECKSUM_INDEX]) + { + // The checksum has to match in order for it to be valid, if we've made it this far down the line. currPacket.latestError = CHECKSUM_MISMATCH; return false; } + currPacket.latestError = PACKET_SUCCESS; return true; } @@ -748,10 +762,10 @@ void LinkConnection::loadNextPacket() // If this was data, then save the data and replace it with the next one in line currPacket.latestError = PACKET_READ; - if (((currPacket.pointer - linkPacketDataStart) < linkPacketDataSize)) + if (((currPacket.command == CMD_ReadDataRequest) && (currPacket.pointer - linkPacketDataStart) < linkPacketDataSize)) { memcpy(&outDataArrayPtr[currPacket.pointer - linkPacketDataStart], &currPacket.recievedData[0], 8); - currPacket = {CMD_ReadDataRequest, 0x00, 0x00, linkPacketDataAddr}; + currPacket = LinkPacket(CMD_ReadDataRequest, 0x00, 0x00, linkPacketDataAddr); linkPacketDataAddr += 8; } } @@ -763,9 +777,14 @@ void LinkConnection::loadNextPacket() bool LinkConnection::allPacketsProcessed() { + if (softResetActivated) + { + // This counts as processed, return true. + return true; + } for (int i = 0; i < LINK_PACKET_ARRAY_SIZE; i++) { - if (linkPacketArr[i].latestError != PACKET_READ) + if (linkPacketArr[i].inUse && linkPacketArr[i].latestError != PACKET_READ) { return false; } @@ -773,7 +792,117 @@ bool LinkConnection::allPacketsProcessed() return true; } -bool LinkConnection::readMemorySection(u16 dataPointer, byte outArray[], int outArraySize) +void LinkConnection::resetLinkPackets() +{ + for (int i = 0; i < LINK_PACKET_ARRAY_SIZE; i++) + { + linkPacketArr[i] = LinkPacket(); + } +} + +void LinkConnection::waitForEnd() +{ + while (enterState != END) + { + handleCartIO(); + VBlankIntrWait(); + } +} + +bool LinkConnection::LinkCommand_InitalizeConnection(bool waitForCompletion) +{ + globalLinkCable.startConnection(INITIAL_CONNECTION); + + if (waitForCompletion) + { + waitForEnd(); + } + return true; +} + +bool LinkConnection::LinkCommand_ReloadCurrentBox(bool waitForCompletion) +{ + resetLinkPackets(); + + linkPacketArr[0] = LinkPacket(CMD_ReloadCurrentBox, 0x00, 0x00, 0x0000); + + globalLinkCable.startConnection(PACKET_EXCHANGE); + if (waitForCompletion) + { + waitForEnd(); + } + return true; +}; + +bool LinkConnection::LinkCommand_TransferPokemon(bool waitForCompletion) +{ + // Check that box number is correct + resetLinkPackets(); + + linkPacketArr[0] = LinkPacket(CMD_ReloadCurrentBox, 0x00, 0x00, 0x0000); + + globalLinkCable.startConnection(PACKET_EXCHANGE); + if (waitForCompletion) + { + waitForEnd(); + } + return true; +}; + +bool LinkConnection::LinkCommand_SoftReset(bool waitForCompletion) +{ + resetLinkPackets(); + + linkPacketArr[0] = LinkPacket(CMD_SoftReset, 0x00, 0x00, 0x0000); + + globalLinkCable.startConnection(PACKET_EXCHANGE); + if (waitForCompletion) + { + waitForEnd(); + } + return true; +}; + +bool LinkConnection::LinkCommand_ModifySRAMAccess(bool enableSRAM, byte SRAMbank, bool waitForCompletion) +{ + if (SRAMbank > 3) + { + return false; + } + resetLinkPackets(); + + if (enableSRAM) + { + linkPacketArr[0] = LinkPacket(CMD_ModifySRAMAccess, 0x0A, SRAMbank, 0x0000); + } + else + { + linkPacketArr[0] = LinkPacket(CMD_ModifySRAMAccess, 0x00, 0x00, 0x0000); + } + + globalLinkCable.startConnection(PACKET_EXCHANGE); + if (waitForCompletion) + { + waitForEnd(); + } + return true; +}; + +bool LinkConnection::LinkCommand_RunSecondaryPayload(bool waitForCompletion) +{ + resetLinkPackets(); + + linkPacketArr[0] = LinkPacket(CMD_ReloadCurrentBox, 0x00, 0x00, 0x0000); + + globalLinkCable.startConnection(PACKET_EXCHANGE); + if (waitForCompletion) + { + waitForEnd(); + } + return true; +}; + +bool LinkConnection::LinkCommand_ReadMemorySection(u16 dataPointer, byte outArray[], int outArraySize, bool waitForCompletion) { linkPacketDataStart = dataPointer; linkPacketDataSize = outArraySize; @@ -782,8 +911,14 @@ bool LinkConnection::readMemorySection(u16 dataPointer, byte outArray[], int out for (int i = 0; i < LINK_PACKET_ARRAY_SIZE; i++) { - linkPacketArr[i] = {CMD_ReadDataRequest, 0x00, 0x00, linkPacketDataAddr}; + linkPacketArr[i] = LinkPacket(CMD_ReadDataRequest, 0x00, 0x00, linkPacketDataAddr); linkPacketDataAddr += 8; } + + globalLinkCable.startConnection(PACKET_EXCHANGE); + if (waitForCompletion) + { + waitForEnd(); + } return true; } \ No newline at end of file diff --git a/source/script_array.cpp b/source/script_array.cpp index 00b9658..7b556dd 100644 --- a/source/script_array.cpp +++ b/source/script_array.cpp @@ -762,7 +762,8 @@ bool run_conditional(int index) u16 debug_charset[256]; load_localized_charset(debug_charset, 3, ENGLISH); globalLinkCable.setup(debug_charset); - globalLinkCable.startConnection(INITIAL_CONNECTION); + + globalLinkCable.LinkCommand_InitalizeConnection(false); while (globalLinkCable.exitState != END) { if (globalLinkCable.subStateChanged && !g_debug_options.print_link_data) @@ -784,26 +785,23 @@ bool run_conditional(int index) globalLinkCable.handleCartIO(); VBlankIntrWait(); } - globalLinkCable.handleCartIO(); + globalLinkCable.handleCartIO(); // Does this need to be here? load_select_sprites(globalLinkCable.currROM); obj_unhide(gb_flag, 0); obj_set_pos(gb_flag, 1.5 * 8, 14 * 8); + globalLinkCable.LinkCommand_ReadMemorySection(0xDA80, party_data.box_data_array, 1122); + if (g_debug_options.print_link_packets) { globalLinkCable.skipPrint = false; globalLinkCable.pauseOnPacket = true; } - globalLinkCable.readMemorySection(0xDA80, party_data.box_data_array, 1122); - globalLinkCable.startConnection(PACKET_EXCHANGE); - while (globalLinkCable.enterState != END) - { - globalLinkCable.handleCartIO(); - VBlankIntrWait(); - } + globalLinkCable.LinkCommand_SoftReset(); + party_data.box.loadData(globalLinkCable.gen, globalLinkCable.lang, party_data.box_data_array); } reload_textbox_background();