From 0177374f0002649891cec733ede23d695def46ae Mon Sep 17 00:00:00 2001 From: Philippe Symons Date: Mon, 8 Jun 2026 23:01:31 +0200 Subject: [PATCH 1/4] Fix broken Write Cbl Data - CART bug. For some reason, the merge broke the Write Cbl Data - CART functionality in the dbg menu. The main problem is that writing a whole sector of data and erasing a sector is too slow to do in an IRQ handler. Fix: Move the actual flash writing process to the main loop by creating a function called handleCartIO(). The IRQ handler writes to global_memory_buffer, and the handleCartIO() copies that data to a local write buffer while the IRQ may still add more to global_memory_buffer. Then it modifies the IRQ handlers' offset and moves the data that was appended in between to the start of the buffer all while the IRQ handler is still appending new bytes. If we end up at a sector boundary, we also do the erase_sector() call. This was tested with and without the print link and print packets debug options. Without the print options enabled, we end up occupying about 20KB on the cartridge save. (for 1st stage only!) Man, this bug sucked. I rate it 1 star! --- include/link_handler.h | 8 +++- source/link_handler.cpp | 92 ++++++++++++++++++++++++++++++----------- source/save.cpp | 9 ++-- source/script_array.cpp | 4 ++ 4 files changed, 81 insertions(+), 32 deletions(-) diff --git a/include/link_handler.h b/include/link_handler.h index be3e9bf..f73b74d 100644 --- a/include/link_handler.h +++ b/include/link_handler.h @@ -263,6 +263,9 @@ public: void printData(); void writeData(); void handleStateLogic(); + // 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. + void handleCartIO(); private: void load_payload(GB_PayloadsFiles payload); @@ -276,9 +279,10 @@ private: #define NUM_LINES 8 char stuff[NUM_LINES][LINE_WIDTH]; char line[LINE_WIDTH] = "OUT"; - int link_cable_memory_section_index; - int link_cable_array_index; + unsigned link_cable_memory_section_index; + unsigned link_cable_array_index; const u16 *debug_charset; + unsigned writeBufferOffset; }; extern LinkConnection globalLinkCable; diff --git a/source/link_handler.cpp b/source/link_handler.cpp index 5a71189..3de3842 100644 --- a/source/link_handler.cpp +++ b/source/link_handler.cpp @@ -56,6 +56,7 @@ void LinkConnection::setup(const u16 *debug_charset) { link_cable_memory_section_index = 0; link_cable_array_index = 0; + writeBufferOffset = 0; linkSPI->activate(LinkSPI::Mode::MASTER_256KBPS); linkSPI->setWaitModeActive(false); @@ -78,11 +79,11 @@ void LinkConnection::setup(const u16 *debug_charset) ++cur; } } - - if(g_debug_options.load_cable_data_from_save == WRITE_CABLE_DATA_MODE_CART) + else if(g_debug_options.write_cable_data_to_save == WRITE_CABLE_DATA_MODE_CART) { - // if we're loading the cable data from the cart save, we should make sure to load our first section here. - copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); + // before each write, we need to erase the sector. + // so, let's do that for the first one before we start writing anything. + erase_sector(0); } } @@ -141,17 +142,16 @@ void LinkConnection::exchangeBytes() // skip the first 6 bytes, which are for human consumption link_cable_array_index += 6; - inData = global_memory_buffer[link_cable_array_index]; + inData = read_byte_save((0x1000 * link_cable_memory_section_index) + link_cable_array_index); ++link_cable_array_index; - outData = global_memory_buffer[link_cable_array_index]; + outData = read_byte_save((0x1000 * link_cable_memory_section_index) + link_cable_array_index); ++link_cable_array_index; - // we reached the end of our global_memory_buffer, we need to load the next data section from the cart save if(link_cable_array_index >= 0x1000) { + // if we reached the end of the section, we need to load the next section (if there is one) ++link_cable_memory_section_index; link_cable_array_index = 0; - copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); } break; } @@ -254,27 +254,19 @@ void LinkConnection::writeData() // the next 6 bytes are for human consumption when viewed in a hex editor. // they can be useful to correlate the current LinkConnection state with the data that was sent over the cable. // but they're not needed for reconstructing the conversation with load_cable_data_from_save - global_memory_buffer[link_cable_array_index + 0] = compState; - global_memory_buffer[link_cable_array_index + 1] = (compStateCounter >> 8) & 0xFF; - global_memory_buffer[link_cable_array_index + 2] = (compStateCounter >> 0) & 0xFF; - global_memory_buffer[link_cable_array_index + 3] = subState; - global_memory_buffer[link_cable_array_index + 4] = (subStateCounter >> 8) & 0xFF; - global_memory_buffer[link_cable_array_index + 5] = (subStateCounter >> 0) & 0xFF; + global_memory_buffer[writeBufferOffset + 0] = (u8)compState; + global_memory_buffer[writeBufferOffset + 1] = (u8)((compStateCounter >> 8) & 0xFF); + global_memory_buffer[writeBufferOffset + 2] = (u8)((compStateCounter >> 0) & 0xFF); + global_memory_buffer[writeBufferOffset + 3] = (u8)subState; + global_memory_buffer[writeBufferOffset + 4] = (u8)((subStateCounter >> 8) & 0xFF); + global_memory_buffer[writeBufferOffset + 5] = (u8)((subStateCounter >> 0) & 0xFF); // actual data bytes start here. - global_memory_buffer[link_cable_array_index + 6] = inData; - global_memory_buffer[link_cable_array_index + 7] = outData; + global_memory_buffer[writeBufferOffset + 6] = inData; + global_memory_buffer[writeBufferOffset + 7] = outData; - link_cable_array_index += 8; + writeBufferOffset += 8; - // If the buffer is full or we reached nextSubState == END, we save the buffer to the cartridge save - if (link_cable_array_index >= 0x1000 || nextSubState == END) - { - erase_sector(0x1000 * link_cable_memory_section_index); - copy_ram_to_save(&global_memory_buffer[0], 0x1000 * link_cable_memory_section_index, 0x1000); - ++link_cable_memory_section_index; - link_cable_array_index = 0; - } break; } } @@ -320,6 +312,56 @@ void LinkConnection::handleStateLogic() } } +/* + * So, dealing with writing to the cartridge in the IRQ handler was a no-go. + * It just caused too many issues with data corruption, probably because the write and erase_sector + * operation was taking too long. + * + * So, I moved it to the main loop through this function. + * However, we need to be aware that this function can get interrupted by the IRQ handler at any time. + * + * We also need to take care to not have global_memory_buffer overflow as the IRQ handler just keeps adding to it. + */ +void LinkConnection::handleCartIO() +{ + u8 writeBuffer[0x1000]; + unsigned curBufDepth = writeBufferOffset; + + // first copy the current data to a local buffer. Note: the IRQ handler could append new data to global_memory_buffer during this call! + memcpy(writeBuffer, global_memory_buffer, curBufDepth); + // by updating the writeBufferOffset already, we allow the IRQ handler to start writing at the new right position + // immediately. + writeBufferOffset -= curBufDepth; + // now move any data received during the memcpy to before writeBufferOffset, so it will be included in the next batch. + // Note: keep in mind, here too the IRQ handler may be appending new data to global_memory_buffer and increase writeBufferOffset. + // but it's harmless. + memmove(global_memory_buffer, global_memory_buffer + curBufDepth, writeBufferOffset); + + u8 *curWriteBuf = writeBuffer; + const u8 * const endWriteBuf = writeBuffer + curBufDepth; + + while(curWriteBuf < endWriteBuf) + { + // make sure not to write beyond the current flash sector's boundaries. We'll need an erase_sector() call before + // we write to the next sector. + const unsigned bytesRemainingInSector = 0x1000 - link_cable_array_index; + const unsigned bytesToWrite = (curBufDepth < bytesRemainingInSector) ? curBufDepth : bytesRemainingInSector; + + copy_ram_to_save(curWriteBuf, (0x1000 * link_cable_memory_section_index) + link_cable_array_index, bytesToWrite); + curWriteBuf += bytesToWrite; + curBufDepth -= bytesToWrite; + link_cable_array_index += bytesToWrite; + + if(link_cable_array_index >= 0x1000) + { + // we have reached the end of our current sector. Let's erase the next one. + link_cable_array_index = 0; + ++link_cable_memory_section_index; + erase_sector(0x1000 * link_cable_memory_section_index); + } + } +} + bool LinkConnection::earlyExit() { if (g_debug_options.print_link_data && !skipPrint && key_held(KEY_LEFT)) diff --git a/source/save.cpp b/source/save.cpp index 44b4f17..0d3d5bc 100644 --- a/source/save.cpp +++ b/source/save.cpp @@ -95,11 +95,10 @@ IWRAM_CODE void erase_sector(uintptr_t address) { delay_cycles_until(ERASE_TIMEOUT_CYCLES, &save_data[address], 0xFF, SRAM_ACCESS_CYCLES); failed = 0; - for(size_t j = 0; j < SECTOR_SIZE; j++) - if(read_direct_single_byte_save(address+j) != ERASED_BYTE) { - failed = 1; - break; - } + if(read_direct_single_byte_save(address + SECTOR_SIZE - 1) != ERASED_BYTE) + { + failed = 1; + } if(is_macronix && failed) FLASH_TERM_CMD } diff --git a/source/script_array.cpp b/source/script_array.cpp index aebb6b8..8225df6 100644 --- a/source/script_array.cpp +++ b/source/script_array.cpp @@ -778,8 +778,11 @@ bool run_conditional(int index) break; } } + globalLinkCable.handleCartIO(); VBlankIntrWait(); } + globalLinkCable.handleCartIO(); + load_select_sprites(globalLinkCable.currROM); obj_unhide(gba_cart, 0); @@ -816,6 +819,7 @@ bool run_conditional(int index) globalLinkCable.startConnection(PACKET_EXCHANGE); while (globalLinkCable.subState != END) { + globalLinkCable.handleCartIO(); VBlankIntrWait(); } } From bea429e948e7f9c7199581b77f63f6b82eca82e2 Mon Sep 17 00:00:00 2001 From: GearsProgress Date: Wed, 10 Jun 2026 08:12:55 -0400 Subject: [PATCH 2/4] Cleaning up the link handling and preparing for full read chunks --- include/link_handler.h | 66 +--- source/link_handler.cpp | 803 ++++++++++++++++++++-------------------- source/script_array.cpp | 12 +- 3 files changed, 422 insertions(+), 459 deletions(-) diff --git a/include/link_handler.h b/include/link_handler.h index be3e9bf..dd1f33c 100644 --- a/include/link_handler.h +++ b/include/link_handler.h @@ -6,21 +6,6 @@ #include "pokemon_party.h" #include "GB_Payloads.h" -#define DATA_PER_PACKET 8 -#define PACKET_DATA_START 2 -#define PACKET_DATA_AT(i) (PACKET_DATA_START + (i * 2)) -#define PACKET_FLAG_AT(i) (PACKET_DATA_START + (i * 2) + 1) -#define PACKET_CHECKSUM (PACKET_DATA_START + (2 * DATA_PER_PACKET)) -#define PACKET_LOCATION_UPPER (PACKET_CHECKSUM + 1) -#define PACKET_LOCATION_LOWER (PACKET_CHECKSUM + 2) - -// 0xFD, 0x00, data bytes per packet, flag bytes per packet, the checksum, and two location bytes -#define PACKET_SIZE (1 + 1 + (2 * DATA_PER_PACKET) + 1 + 2) // Originally 13 - -#define TIMEOUT 2 -#define TIMEOUT_ONE_LENGTH 1000000 // Maybe keep a 10:1 ratio between ONE and TWO? -#define TIMEOUT_TWO_LENGTH 100000 - #define SPI_TEXT_OUT_ARRAY_ELEMENT_SIZE 64 enum GameBoyROM @@ -126,18 +111,9 @@ const u8 GameBoyROMChecksumTable[][4]{ {0x19, 0x42, 0xF4, CRYSTAL_SP}, }; -enum CompositeState +enum LinkState { - NO_COMPOSITE_STATE, - INITIAL_CONNECTION, - PACKET_EXCHANGE, -}; - -enum SubstateState -{ - NO_SUBSTATE, - - // INITIAL_CONNECTION + INITIAL_CONNECTION = 0x00, CLOCK, SAVE_SUCCESS, MENU_OPEN, @@ -152,11 +128,11 @@ enum SubstateState SEND_SPECIFIC_PAYLOAD, SOFT_RESET, - // PACKET_EXCHANGE + PACKET_EXCHANGE = 0x10, BYTE_EXCHANGE, + PRINT_LAST_PACKET, - END, - + END = 0xFF, }; enum LinkConnectionError @@ -209,21 +185,18 @@ struct LinkPacket class LinkConnection { public: - CompositeState compState = NO_COMPOSITE_STATE; - CompositeState nextCompState = NO_COMPOSITE_STATE; - bool compStateChanged = false; - - SubstateState subState = NO_SUBSTATE; - SubstateState nextSubState = NO_SUBSTATE; + LinkState enterState; + LinkState exitState; bool subStateChanged = false; LinkConnectionError lastError = NO_ERROR; uint8_t inData; uint8_t outData; + uint8_t nextOutData; - int compStateCounter = 0; // the counter for the total number of bytes sent compstate - int subStateCounter = 0; // The counter for the total number of bytes sent in this substate + int globalStateCounter = 0; // The counter for the total number of bytes sent + int subStateCounter = 0; // The counter for the total number of bytes sent in this substate int gen = 0; // The generation we are trading with GameBoyROM currROM = NO_GB_ROM; // The GameBoy ROM we're communicating with @@ -231,24 +204,14 @@ public: int FF_count = 0; // The number of 0xFF bytes that have been in a row int zero_count = 0; // The number of 0x00 bytes that have been in a row - int mosi_delay = 4; // inital delay, speeds up once sending - int received_offset = 0; // The offset contained in the last packet - int next_offset = 0; // The offset we are sending in the next packet - int packet_index = 0; // The index of the current packet - - bool failed_packet = false; // Flags if a packet failed - bool init_packet = true; // Flags if a packet is the inital one - bool end_of_data = false; // Flags if we are at the end of the data - bool test_packet_fail = false; // ??? - - byte data_packet[PACKET_SIZE]; byte payloadBuffer[0x2A0]; int curr_payload_size = 0; byte dataOutBuffer[16]; int dataOutBufferCurrIndex = 0; LinkPacket *currLinkPacketArr; - int currLinkPacketArrNum = 0; + int currLinkPacketArrTotalCount = 0; + int currLinkPacketArrFilledCount = 0; int currLinkPacketArrIndex = 0; bool pauseOnByte = false; // Used for pausing and sending one byte at a time @@ -257,19 +220,18 @@ public: bool newPacket = false; void setup(const u16 *debug_charset); - void startConnection(CompositeState startState); + void startConnection(LinkState startState); bool earlyExit(); void exchangeBytes(); void printData(); void writeData(); void handleStateLogic(); + void prepareForNextCycle(); private: void load_payload(GB_PayloadsFiles payload); void loadCurrGameFromChecksum(); bool processPacket(); - void logicState_initConnection(); - void logicState_packetExchange(); // Used for debug features #define LINE_WIDTH 24 diff --git a/source/link_handler.cpp b/source/link_handler.cpp index 5a71189..5eb1027 100644 --- a/source/link_handler.cpp +++ b/source/link_handler.cpp @@ -32,12 +32,26 @@ LinkConnection globalLinkCable; void linkCableIRQ() { + /* + ---------------- + This handshake process can be a bit weird, so let's break it down: + First we exchange the bytes via handshake. inData will be set to the recieved byte, and the byte we send out will be outData. + + Then we determining what byte we will be sending out next, based on enterState and inByte in handleStateLogic(). + This will set exitState and nextOutByte. + + Then we print our information in the following format: globalCounter enterState:stateCounter:exitState inData outData + + We then prepare for the next cycle. Counters are incremented (or reset), enterState is set to exitState, and outByte is set to nextOutByte. + ---------------- + */ if (!globalLinkCable.earlyExit()) { - globalLinkCable.exchangeBytes(); + globalLinkCable.handleStateLogic(); + if (g_debug_options.print_link_data || g_debug_options.print_link_packets) { globalLinkCable.printData(); @@ -48,7 +62,7 @@ void linkCableIRQ() globalLinkCable.writeData(); } - globalLinkCable.handleStateLogic(); + globalLinkCable.prepareForNextCycle(); } } @@ -62,53 +76,74 @@ void LinkConnection::setup(const u16 *debug_charset) this->debug_charset = debug_charset; + lastError = NO_ERROR; + if (g_debug_options.print_link_data == true) { create_textbox(0, 0, 138, 128, false); } - if(g_debug_options.write_cable_data_to_save == WRITE_CABLE_DATA_MODE_SRAM) + if (g_debug_options.write_cable_data_to_save == WRITE_CABLE_DATA_MODE_SRAM) { // if we're writing the cable data to SRAM, we should clear the SRAM first to make sure there's no leftover data from previous transfers volatile u8 *cur = SRAM_PTR; volatile u8 *end = SRAM_PTR + 0x10000; - while(cur < end) + while (cur < end) { (*cur) = 0; ++cur; } } - if(g_debug_options.load_cable_data_from_save == WRITE_CABLE_DATA_MODE_CART) + if (g_debug_options.load_cable_data_from_save == WRITE_CABLE_DATA_MODE_CART) { // if we're loading the cable data from the cart save, we should make sure to load our first section here. copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); } } -void LinkConnection::startConnection(CompositeState startState) +void LinkConnection::load_payload(GB_PayloadsFiles payload) { - switch (startState) + u32 fileSize; + u8 decompressionBuffer[0x1000]; + const u8 *chunkList[] = {(const u8 *)GB_Payloads_chunk0_lz10_bin}; + FileContainerReader reader(chunkList, 1); + const u32 fileIndex = (u32)payload; + + reader.init(decompressionBuffer, sizeof(decompressionBuffer)); + fileSize = reader.getFileSize(fileIndex); + reader.seekToFile(fileIndex); + reader.read(this->payloadBuffer, fileSize); + + this->curr_payload_size = fileSize; +} + +void LinkConnection::loadCurrGameFromChecksum() +{ + if (((dataOutBuffer[0] + dataOutBuffer[1]) & 0x7F) != dataOutBuffer[3]) { - case INITIAL_CONNECTION: - subState = CLOCK; - REG_TM3D = -0x4000 / 60; - REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; - break; - case PACKET_EXCHANGE: - subState = BYTE_EXCHANGE; - REG_TM3D = -0x0040; - // REG_TM3D = -0x4000 / 2; - REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; - break; - default: - break; + currROM = GB_ROM_ERROR; + }; + + int start = RED_JP_v0; + int end = GOLD_JP_v0; + + if (gen == 2) + { + start = end; + end = NO_GB_ROM; } - nextSubState = subState; - nextCompState = startState; - lastError = NO_ERROR; - irq_enable(II_TIMER3); + for (int i = start; i < end; i++) + { + if (dataOutBuffer[0] == GameBoyROMChecksumTable[i][1] && dataOutBuffer[1] == GameBoyROMChecksumTable[i][2]) + { + currROM = (GameBoyROM)GameBoyROMChecksumTable[i][3]; + return; + } + } + currROM = GB_ROM_ERROR; + return; } void LinkConnection::exchangeBytes() @@ -122,40 +157,61 @@ void LinkConnection::exchangeBytes() global_next_frame(); return --timeout_frames <= 0; }); */ - switch(g_debug_options.load_cable_data_from_save) + switch (g_debug_options.load_cable_data_from_save) { - case WRITE_CABLE_DATA_MODE_OFF: - // Normal transfer :-) - inData = linkSPI->transfer(outData); - break; - case WRITE_CABLE_DATA_MODE_SRAM: - // Pretend transfer, by loading the bytes from SRAM (where we stored them with writeData() in a previous transfer) - inData = (*(SRAM_PTR + link_cable_array_index)); - ++link_cable_array_index; - outData = (*(SRAM_PTR + link_cable_array_index)); - ++link_cable_array_index; - break; - case WRITE_CABLE_DATA_MODE_CART: + case WRITE_CABLE_DATA_MODE_OFF: + // Normal transfer :-) + inData = linkSPI->transfer(outData); + break; + case WRITE_CABLE_DATA_MODE_SRAM: + // Pretend transfer, by loading the bytes from SRAM (where we stored them with writeData() in a previous transfer) + inData = (*(SRAM_PTR + link_cable_array_index)); + ++link_cable_array_index; + outData = (*(SRAM_PTR + link_cable_array_index)); + ++link_cable_array_index; + break; + case WRITE_CABLE_DATA_MODE_CART: + { + // Pretend transfer, by loading the bytes from the cartridge save. (where we stored them with writeData() in a previous transfer) + // skip the first 6 bytes, which are for human consumption + link_cable_array_index += 6; + + inData = global_memory_buffer[link_cable_array_index]; + ++link_cable_array_index; + outData = global_memory_buffer[link_cable_array_index]; + ++link_cable_array_index; + + // we reached the end of our global_memory_buffer, we need to load the next data section from the cart save + if (link_cable_array_index >= 0x1000) { - // Pretend transfer, by loading the bytes from the cartridge save. (where we stored them with writeData() in a previous transfer) - // skip the first 6 bytes, which are for human consumption - link_cable_array_index += 6; - - inData = global_memory_buffer[link_cable_array_index]; - ++link_cable_array_index; - outData = global_memory_buffer[link_cable_array_index]; - ++link_cable_array_index; - - // we reached the end of our global_memory_buffer, we need to load the next data section from the cart save - if(link_cable_array_index >= 0x1000) - { - ++link_cable_memory_section_index; - link_cable_array_index = 0; - copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); - } - break; + ++link_cable_memory_section_index; + link_cable_array_index = 0; + copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); } + break; } + } +} + +void LinkConnection::startConnection(LinkState startState) +{ + switch (startState) + { + case INITIAL_CONNECTION: + REG_TM3D = -0x4000 / 60; + REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; + break; + case PACKET_EXCHANGE: + REG_TM3D = -0x0040; + // REG_TM3D = -0x4000 / 2; + REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; + break; + default: + break; + } + + enterState = startState; + irq_enable(II_TIMER3); } void LinkConnection::printData() @@ -168,13 +224,13 @@ void LinkConnection::printData() { if (g_debug_options.print_link_data) { - n2hexstr(&line[0], compState & 0xFF, 2); - line[2] = ':'; - n2hexstr(&line[3], compStateCounter & 0xFFFF, 4); - line[7] = '|'; - n2hexstr(&line[8], subState & 0xFF, 2); - line[10] = ':'; - n2hexstr(&line[11], subStateCounter & 0xFFFF, 4); + n2hexstr(&line[0], globalStateCounter & 0xFFFF, 4); + line[4] = '|'; + n2hexstr(&line[5], enterState & 0xFF, 2); + line[7] = ':'; + n2hexstr(&line[8], subStateCounter & 0xFFFF, 4); + line[12] = ':'; + n2hexstr(&line[13], exitState & 0xFF, 2); line[15] = '|'; line[16] = 'i'; n2hexstr(&line[17], inData & 0xFF, 2); @@ -232,72 +288,305 @@ void LinkConnection::printData() void LinkConnection::writeData() { - switch(g_debug_options.write_cable_data_to_save) + switch (g_debug_options.write_cable_data_to_save) { - case WRITE_CABLE_DATA_MODE_OFF: - break; - case WRITE_CABLE_DATA_MODE_SRAM: + case WRITE_CABLE_DATA_MODE_OFF: + break; + case WRITE_CABLE_DATA_MODE_SRAM: + { + (*(SRAM_PTR + link_cable_array_index)) = inData; + ++link_cable_array_index; + + (*(SRAM_PTR + link_cable_array_index)) = outData; + ++link_cable_array_index; + break; + } + case WRITE_CABLE_DATA_MODE_CART: + { + // save the data to the cartridge in chunks of 4 KB + // WARNING: If you want to add or remove fields here, + // make sure to keep the number of bytes a clean divider of 4096 (global_memory_buffer_size) + + // the next 6 bytes are for human consumption when viewed in a hex editor. + // they can be useful to correlate the current LinkConnection state with the data that was sent over the cable. + // but they're not needed for reconstructing the conversation with load_cable_data_from_save + global_memory_buffer[link_cable_array_index + 0] = (globalStateCounter >> 8) & 0xFF; + global_memory_buffer[link_cable_array_index + 1] = (globalStateCounter >> 0) & 0xFF; + global_memory_buffer[link_cable_array_index + 2] = enterState; + global_memory_buffer[link_cable_array_index + 3] = (subStateCounter >> 8) & 0xFF; + global_memory_buffer[link_cable_array_index + 4] = (subStateCounter >> 0) & 0xFF; + global_memory_buffer[link_cable_array_index + 5] = exitState; + + // actual data bytes start here. + global_memory_buffer[link_cable_array_index + 6] = inData; + global_memory_buffer[link_cable_array_index + 7] = outData; + + link_cable_array_index += 8; + + // If the buffer is full or we reached nextSubState == END, we save the buffer to the cartridge save + if (link_cable_array_index >= 0x1000 || exitState == END) { - (*(SRAM_PTR + link_cable_array_index)) = inData; - ++link_cable_array_index; - - (*(SRAM_PTR + link_cable_array_index)) = outData; - ++link_cable_array_index; - break; - } - case WRITE_CABLE_DATA_MODE_CART: - { - // save the data to the cartridge in chunks of 4 KB - // WARNING: If you want to add or remove fields here, - // make sure to keep the number of bytes a clean divider of 4096 (global_memory_buffer_size) - - // the next 6 bytes are for human consumption when viewed in a hex editor. - // they can be useful to correlate the current LinkConnection state with the data that was sent over the cable. - // but they're not needed for reconstructing the conversation with load_cable_data_from_save - global_memory_buffer[link_cable_array_index + 0] = compState; - global_memory_buffer[link_cable_array_index + 1] = (compStateCounter >> 8) & 0xFF; - global_memory_buffer[link_cable_array_index + 2] = (compStateCounter >> 0) & 0xFF; - global_memory_buffer[link_cable_array_index + 3] = subState; - global_memory_buffer[link_cable_array_index + 4] = (subStateCounter >> 8) & 0xFF; - global_memory_buffer[link_cable_array_index + 5] = (subStateCounter >> 0) & 0xFF; - - // actual data bytes start here. - global_memory_buffer[link_cable_array_index + 6] = inData; - global_memory_buffer[link_cable_array_index + 7] = outData; - - link_cable_array_index += 8; - - // If the buffer is full or we reached nextSubState == END, we save the buffer to the cartridge save - if (link_cable_array_index >= 0x1000 || nextSubState == END) - { - erase_sector(0x1000 * link_cable_memory_section_index); - copy_ram_to_save(&global_memory_buffer[0], 0x1000 * link_cable_memory_section_index, 0x1000); - ++link_cable_memory_section_index; - link_cable_array_index = 0; - } - break; + erase_sector(0x1000 * link_cable_memory_section_index); + copy_ram_to_save(&global_memory_buffer[0], 0x1000 * link_cable_memory_section_index, 0x1000); + ++link_cable_memory_section_index; + link_cable_array_index = 0; } + break; + } } } void LinkConnection::handleStateLogic() { - subState = nextSubState; - compState = nextCompState; - - switch (compState) + switch (enterState) { case INITIAL_CONNECTION: - logicState_initConnection(); + nextOutData = 0xFF; + exitState = CLOCK; break; + + case CLOCK: + if (inData == 0xFE) + { + exitState = SAVE_SUCCESS; + nextOutData = 0x00; + } + else + { + nextOutData = 0x01; + } + break; + + case SAVE_SUCCESS: + if (inData == 0x60 || inData == 0x61) + { + exitState = MENU_OPEN; + nextOutData = inData; + } + // nextOutData defaults to 0x00 + break; + + case MENU_OPEN: + if (inData == 0xD0 || inData == 0x61) + { + if (inData == 0xD0) + { + gen = 1; + load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN1); + nextOutData = 0xD4; + } + else if (inData == 0x61) + { + gen = 2; + load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN2); + nextOutData = 0x61; + } + exitState = MENU_SUCCESS; + } + break; + + case MENU_SUCCESS: + if (inData == 0xFE) + { + exitState = WAIT_FOR_TRADE; + } + nextOutData = inData; + break; + + case WAIT_FOR_TRADE: + if (inData == 0xFD) + { + REG_TM3D = -0x0040; + exitState = TRADE_PREAMBLE; + // nextOutData defaults to 0x00 + } + else + { + nextOutData = inData; + } + break; + + case TRADE_PREAMBLE: + if (subStateCounter < 2) + { + // nextOutData defaults to 0x00 + } + else if (subStateCounter < 9) + { + nextOutData = 0xFD; + } + else + { + exitState = TRADE; + nextOutData = 0xFD; + }; + break; + + case TRADE: + if (subStateCounter > curr_payload_size) + { + if (this->gen == 2) + { + exitState = MAIL; + } + else + { + exitState = WAIT_FOR_CHECKSUM_PAYLOAD; + } + } + nextOutData = payloadBuffer[subStateCounter]; + break; + + case MAIL: + if (subStateCounter > 0x186) + { + exitState = WAIT_FOR_CHECKSUM_PAYLOAD; + } + nextOutData = 0x00; + break; + + case WAIT_FOR_CHECKSUM_PAYLOAD: + if (inData == 0xFD) + { + exitState = GET_CHECKSUM; + } + // nextOutData defaults to 0x00 + break; + + case GET_CHECKSUM: + if (inData != 0xFD) + { + dataOutBuffer[dataOutBufferCurrIndex] = inData; + dataOutBufferCurrIndex++; + nextOutData = 0x01; + } + else if (inData == 0xFD && dataOutBufferCurrIndex > 0) + { + loadCurrGameFromChecksum(); + load_payload(GB_PayloadsFiles::SPECIFICPAYLOADGEN1_EN_R); + exitState = SEND_SPECIFIC_PAYLOAD; + } + else + { + nextOutData = 0xFD; + } + break; + + case WAIT_FOR_SECOND_PAYLOAD: + if (inData == 0xFD) + { + exitState = SEND_SPECIFIC_PAYLOAD; + } + // nextOutData defaults to 0x00 + break; + + case SEND_SPECIFIC_PAYLOAD: + if (subStateCounter > 255) // The 255 comes from the Universal Payload + { + exitState = END; + } + if (subStateCounter < curr_payload_size) + { + nextOutData = payloadBuffer[subStateCounter]; + } + else + { + nextOutData = 0x01; + } + break; + case PACKET_EXCHANGE: - logicState_packetExchange(); + nextOutData = 0xFF; + exitState = BYTE_EXCHANGE; break; + + case BYTE_EXCHANGE: + { + switch (subStateCounter % TOTAL_PACKET_LENGTH) + { + case 0: + nextOutData = 0xFD; + break; + case 1: + nextOutData = currLinkPacketArrIndex; + break; + case 2: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].command; + break; + case 3: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].argument[0]; + break; + case 4: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].argument[1]; + break; + case 5: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 0; + break; + case 6: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 8; + break; + case TOTAL_PACKET_LENGTH - 1: + currLinkPacketArrIndex++; + default: + nextOutData = 0xFF; + break; + } + + if (currLinkPacketArrIndex >= currLinkPacketArrFilledCount + 2) + { + exitState = PRINT_LAST_PACKET; + } + else if (currLinkPacketArrIndex >= currLinkPacketArrFilledCount) + { + // We don't want to send another packet, we just want the data back + nextOutData = 0xFF; + } + + if ((subStateCounter % TOTAL_PACKET_LENGTH == 0) && (currLinkPacketArrIndex > 1)) + { + newPacket = true; + + if (processPacket() == false) + { + // Packet failed, we need to put it back in the queue + if (currLinkPacketArrFilledCount < currLinkPacketArrTotalCount) + { + currLinkPacketArr[currLinkPacketArrFilledCount] = currLinkPacketArr[currLinkPacketArrIndex]; + currLinkPacketArrFilledCount++; + } + else + { + // We have filled the packet array. Set + } + } + } + else + { + newPacket = false; + } + + dataOutBuffer[subStateCounter % TOTAL_PACKET_LENGTH] = inData; + } + break; + + case PRINT_LAST_PACKET: + nextOutData = 0xFF; + exitState = END; + break; + + case END: + irq_disable(II_TIMER3); + break; + default: + nextOutData = inData; break; } +} - if (nextSubState != subState) +void LinkConnection::prepareForNextCycle() +{ + if (exitState != enterState) { subStateCounter = 0; subStateChanged = true; @@ -308,16 +597,9 @@ void LinkConnection::handleStateLogic() subStateChanged = false; } - if (nextCompState != compState) - { - compStateCounter = 0; - compStateChanged = true; - } - else - { - compStateCounter++; - compStateChanged = false; - } + globalStateCounter++; + enterState = exitState; + outData = nextOutData; } bool LinkConnection::earlyExit() @@ -370,290 +652,11 @@ bool LinkConnection::earlyExit() return pauseOnByte || (pauseOnPacket && newPacket); } -void LinkConnection::logicState_initConnection() -{ - outData = 0x00; - switch (subState) - { - case CLOCK: - if (inData == 0xFE) - { - nextSubState = SAVE_SUCCESS; - // outData defaults to 0x00 - } - else - { - outData = 0x01; - } - break; - - case SAVE_SUCCESS: - if (inData == 0x60 || inData == 0x61) - { - nextSubState = MENU_OPEN; - outData = inData; - } - // outData defaults to 0x00 - break; - - case MENU_OPEN: - if (inData == 0xD0 || inData == 0x61) - { - if (inData == 0xD0) - { - gen = 1; - load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN1); - outData = 0xD4; - } - else if (inData == 0x61) - { - gen = 2; - load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN2); - outData = 0x61; - } - nextSubState = MENU_SUCCESS; - } - break; - - case MENU_SUCCESS: - if (inData == 0xFE) - { - nextSubState = WAIT_FOR_TRADE; - } - outData = inData; - break; - - case WAIT_FOR_TRADE: - if (inData == 0xFD) - { - REG_TM3D = -0x0040; - nextSubState = TRADE_PREAMBLE; - // outData defaults to 0x00 - } - else - { - outData = inData; - } - break; - - case TRADE_PREAMBLE: - if (subStateCounter < 2) - { - // outData defaults to 0x00 - } - else if (subStateCounter < 9) - { - outData = 0xFD; - } - else - { - nextSubState = TRADE; - outData = 0xFD; - }; - break; - - case TRADE: - if (subStateCounter > curr_payload_size) - { - if (this->gen == 2) - { - nextSubState = MAIL; - } - else - { - nextSubState = WAIT_FOR_CHECKSUM_PAYLOAD; - } - } - outData = payloadBuffer[subStateCounter]; - break; - - case MAIL: - if (subStateCounter > 0x186) - { - nextSubState = WAIT_FOR_CHECKSUM_PAYLOAD; - } - outData = 0x00; - break; - - case WAIT_FOR_CHECKSUM_PAYLOAD: - if (inData == 0xFD) - { - nextSubState = GET_CHECKSUM; - } - // outData defaults to 0x00 - break; - - case GET_CHECKSUM: - if (inData != 0xFD) - { - dataOutBuffer[dataOutBufferCurrIndex] = inData; - dataOutBufferCurrIndex++; - outData = 0x01; - } - else if (inData == 0xFD && dataOutBufferCurrIndex > 0) - { - loadCurrGameFromChecksum(); - load_payload(GB_PayloadsFiles::SPECIFICPAYLOADGEN1_EN_R); - nextSubState = SEND_SPECIFIC_PAYLOAD; - } - else - { - outData = 0xFD; - } - break; - - case WAIT_FOR_SECOND_PAYLOAD: - if (inData == 0xFD) - { - nextSubState = SEND_SPECIFIC_PAYLOAD; - } - // outData defaults to 0x00 - break; - - case SEND_SPECIFIC_PAYLOAD: - if (subStateCounter > 255) // The 255 comes from the Universal Payload - { - nextSubState = END; - } - if (subStateCounter < curr_payload_size) - { - outData = payloadBuffer[subStateCounter]; - } - else - { - outData = 0x01; - } - break; - - case END: - irq_disable(II_TIMER3); - break; - - default: - outData = inData; - break; - } -} - -void LinkConnection::logicState_packetExchange() -{ - switch (subState) - { - case BYTE_EXCHANGE: - - if (subStateCounter < TOTAL_PACKET_LENGTH) - { - // Start with OUT_PACKET_LENGTH of bytes of prep to make sure things are set to go - outData = 0xFF; - } - else - { - switch (subStateCounter % TOTAL_PACKET_LENGTH) - { - case 0: - outData = 0xFD; - break; - case 1: - outData = currLinkPacketArrIndex; - break; - case 2: - outData = currLinkPacketArr[currLinkPacketArrIndex].command; - break; - case 3: - outData = currLinkPacketArr[currLinkPacketArrIndex].argument[0]; - break; - case 4: - outData = currLinkPacketArr[currLinkPacketArrIndex].argument[1]; - break; - case 5: - outData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 0; - break; - case 6: - outData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 8; - break; - case TOTAL_PACKET_LENGTH - 1: - currLinkPacketArrIndex++; - default: - outData = 0xFF; - break; - } - if (currLinkPacketArrIndex >= currLinkPacketArrNum) - { - nextSubState = END; - } - - if (subStateCounter % TOTAL_PACKET_LENGTH == 0) - { - newPacket = true; - processPacket(); - } - else - { - newPacket = false; - } - - dataOutBuffer[subStateCounter % TOTAL_PACKET_LENGTH] = inData; - } - break; - - case END: - irq_disable(II_TIMER3); - break; - - default: - outData = inData; - break; - } -} - -void LinkConnection::load_payload(GB_PayloadsFiles payload) -{ - u32 fileSize; - u8 decompressionBuffer[0x1000]; - const u8 *chunkList[] = {(const u8 *)GB_Payloads_chunk0_lz10_bin}; - FileContainerReader reader(chunkList, 1); - const u32 fileIndex = (u32)payload; - - reader.init(decompressionBuffer, sizeof(decompressionBuffer)); - fileSize = reader.getFileSize(fileIndex); - reader.seekToFile(fileIndex); - reader.read(this->payloadBuffer, fileSize); - - this->curr_payload_size = fileSize; -} - -void LinkConnection::loadCurrGameFromChecksum() -{ - if (((dataOutBuffer[0] + dataOutBuffer[1]) & 0x7F) != dataOutBuffer[3]) - { - currROM = GB_ROM_ERROR; - }; - - int start = RED_JP_v0; - int end = GOLD_JP_v0; - - if (gen == 2) - { - start = end; - end = NO_GB_ROM; - } - - for (int i = start; i < end; i++) - { - if (dataOutBuffer[0] == GameBoyROMChecksumTable[i][1] && dataOutBuffer[1] == GameBoyROMChecksumTable[i][2]) - { - currROM = (GameBoyROM)GameBoyROMChecksumTable[i][3]; - return; - } - } - currROM = GB_ROM_ERROR; - return; -} - bool LinkConnection::processPacket() { int checksum = 0; LinkPacket &currPacket = currLinkPacketArr[dataOutBuffer[INP_COUNTER_INDEX]]; + for (int i = INP_DELAY_FROM_OUTP; i < INP_LENGTH; i++) { if (i != INP_CHECKSUM_INDEX) diff --git a/source/script_array.cpp b/source/script_array.cpp index aebb6b8..70f87fa 100644 --- a/source/script_array.cpp +++ b/source/script_array.cpp @@ -760,11 +760,11 @@ bool run_conditional(int index) load_localized_charset(debug_charset, 3, ENGLISH); globalLinkCable.setup(debug_charset); globalLinkCable.startConnection(INITIAL_CONNECTION); - while (globalLinkCable.subState != END) + while (globalLinkCable.exitState != END) { if (globalLinkCable.subStateChanged && !g_debug_options.print_link_data) { - switch (globalLinkCable.subState) + switch (globalLinkCable.exitState) { case SAVE_SUCCESS: general_text_reader.readFile(GENERAL_link_success, lineBuffer); @@ -801,20 +801,18 @@ bool run_conditional(int index) {CMD_ReadDataRequest, 0x00, 0x00, 0xDA98}, {CMD_ReadDataRequest, 0x00, 0x00, 0xDAA0}, {CMD_ReadDataRequest, 0x00, 0x00, 0xDAA8}, - {CMD_ReadDataRequest, 0x00, 0x00, 0xC5DC} - }; globalLinkCable.skipPrint = false; globalLinkCable.pauseOnPacket = true; - while (true) + //while (true) { globalLinkCable.currLinkPacketArr = packets; - globalLinkCable.currLinkPacketArrNum = 6; + globalLinkCable.currLinkPacketArrFilledCount = 6; globalLinkCable.currLinkPacketArrIndex = 0; globalLinkCable.startConnection(PACKET_EXCHANGE); - while (globalLinkCable.subState != END) + while (globalLinkCable.enterState != END) { VBlankIntrWait(); } From cffa086996b0e49b3a4daab43328c99c445343a0 Mon Sep 17 00:00:00 2001 From: GearsProgress Date: Wed, 10 Jun 2026 08:35:59 -0400 Subject: [PATCH 3/4] Revert "Cleaning up the link handling and preparing for full read chunks" This reverts commit bea429e948e7f9c7199581b77f63f6b82eca82e2. --- include/link_handler.h | 66 +++- source/link_handler.cpp | 803 ++++++++++++++++++++-------------------- source/script_array.cpp | 12 +- 3 files changed, 459 insertions(+), 422 deletions(-) diff --git a/include/link_handler.h b/include/link_handler.h index dd1f33c..be3e9bf 100644 --- a/include/link_handler.h +++ b/include/link_handler.h @@ -6,6 +6,21 @@ #include "pokemon_party.h" #include "GB_Payloads.h" +#define DATA_PER_PACKET 8 +#define PACKET_DATA_START 2 +#define PACKET_DATA_AT(i) (PACKET_DATA_START + (i * 2)) +#define PACKET_FLAG_AT(i) (PACKET_DATA_START + (i * 2) + 1) +#define PACKET_CHECKSUM (PACKET_DATA_START + (2 * DATA_PER_PACKET)) +#define PACKET_LOCATION_UPPER (PACKET_CHECKSUM + 1) +#define PACKET_LOCATION_LOWER (PACKET_CHECKSUM + 2) + +// 0xFD, 0x00, data bytes per packet, flag bytes per packet, the checksum, and two location bytes +#define PACKET_SIZE (1 + 1 + (2 * DATA_PER_PACKET) + 1 + 2) // Originally 13 + +#define TIMEOUT 2 +#define TIMEOUT_ONE_LENGTH 1000000 // Maybe keep a 10:1 ratio between ONE and TWO? +#define TIMEOUT_TWO_LENGTH 100000 + #define SPI_TEXT_OUT_ARRAY_ELEMENT_SIZE 64 enum GameBoyROM @@ -111,9 +126,18 @@ const u8 GameBoyROMChecksumTable[][4]{ {0x19, 0x42, 0xF4, CRYSTAL_SP}, }; -enum LinkState +enum CompositeState { - INITIAL_CONNECTION = 0x00, + NO_COMPOSITE_STATE, + INITIAL_CONNECTION, + PACKET_EXCHANGE, +}; + +enum SubstateState +{ + NO_SUBSTATE, + + // INITIAL_CONNECTION CLOCK, SAVE_SUCCESS, MENU_OPEN, @@ -128,11 +152,11 @@ enum LinkState SEND_SPECIFIC_PAYLOAD, SOFT_RESET, - PACKET_EXCHANGE = 0x10, + // PACKET_EXCHANGE BYTE_EXCHANGE, - PRINT_LAST_PACKET, - END = 0xFF, + END, + }; enum LinkConnectionError @@ -185,18 +209,21 @@ struct LinkPacket class LinkConnection { public: - LinkState enterState; - LinkState exitState; + CompositeState compState = NO_COMPOSITE_STATE; + CompositeState nextCompState = NO_COMPOSITE_STATE; + bool compStateChanged = false; + + SubstateState subState = NO_SUBSTATE; + SubstateState nextSubState = NO_SUBSTATE; bool subStateChanged = false; LinkConnectionError lastError = NO_ERROR; uint8_t inData; uint8_t outData; - uint8_t nextOutData; - int globalStateCounter = 0; // The counter for the total number of bytes sent - int subStateCounter = 0; // The counter for the total number of bytes sent in this substate + int compStateCounter = 0; // the counter for the total number of bytes sent compstate + int subStateCounter = 0; // The counter for the total number of bytes sent in this substate int gen = 0; // The generation we are trading with GameBoyROM currROM = NO_GB_ROM; // The GameBoy ROM we're communicating with @@ -204,14 +231,24 @@ public: int FF_count = 0; // The number of 0xFF bytes that have been in a row int zero_count = 0; // The number of 0x00 bytes that have been in a row + int mosi_delay = 4; // inital delay, speeds up once sending + int received_offset = 0; // The offset contained in the last packet + int next_offset = 0; // The offset we are sending in the next packet + int packet_index = 0; // The index of the current packet + + bool failed_packet = false; // Flags if a packet failed + bool init_packet = true; // Flags if a packet is the inital one + bool end_of_data = false; // Flags if we are at the end of the data + bool test_packet_fail = false; // ??? + + byte data_packet[PACKET_SIZE]; byte payloadBuffer[0x2A0]; int curr_payload_size = 0; byte dataOutBuffer[16]; int dataOutBufferCurrIndex = 0; LinkPacket *currLinkPacketArr; - int currLinkPacketArrTotalCount = 0; - int currLinkPacketArrFilledCount = 0; + int currLinkPacketArrNum = 0; int currLinkPacketArrIndex = 0; bool pauseOnByte = false; // Used for pausing and sending one byte at a time @@ -220,18 +257,19 @@ public: bool newPacket = false; void setup(const u16 *debug_charset); - void startConnection(LinkState startState); + void startConnection(CompositeState startState); bool earlyExit(); void exchangeBytes(); void printData(); void writeData(); void handleStateLogic(); - void prepareForNextCycle(); private: void load_payload(GB_PayloadsFiles payload); void loadCurrGameFromChecksum(); bool processPacket(); + void logicState_initConnection(); + void logicState_packetExchange(); // Used for debug features #define LINE_WIDTH 24 diff --git a/source/link_handler.cpp b/source/link_handler.cpp index 5eb1027..5a71189 100644 --- a/source/link_handler.cpp +++ b/source/link_handler.cpp @@ -32,25 +32,11 @@ LinkConnection globalLinkCable; void linkCableIRQ() { - /* - ---------------- - This handshake process can be a bit weird, so let's break it down: - First we exchange the bytes via handshake. inData will be set to the recieved byte, and the byte we send out will be outData. - - Then we determining what byte we will be sending out next, based on enterState and inByte in handleStateLogic(). - This will set exitState and nextOutByte. - - Then we print our information in the following format: globalCounter enterState:stateCounter:exitState inData outData - - We then prepare for the next cycle. Counters are incremented (or reset), enterState is set to exitState, and outByte is set to nextOutByte. - ---------------- - */ if (!globalLinkCable.earlyExit()) { - globalLinkCable.exchangeBytes(); - globalLinkCable.handleStateLogic(); + globalLinkCable.exchangeBytes(); if (g_debug_options.print_link_data || g_debug_options.print_link_packets) { @@ -62,7 +48,7 @@ void linkCableIRQ() globalLinkCable.writeData(); } - globalLinkCable.prepareForNextCycle(); + globalLinkCable.handleStateLogic(); } } @@ -76,74 +62,53 @@ void LinkConnection::setup(const u16 *debug_charset) this->debug_charset = debug_charset; - lastError = NO_ERROR; - if (g_debug_options.print_link_data == true) { create_textbox(0, 0, 138, 128, false); } - if (g_debug_options.write_cable_data_to_save == WRITE_CABLE_DATA_MODE_SRAM) + if(g_debug_options.write_cable_data_to_save == WRITE_CABLE_DATA_MODE_SRAM) { // if we're writing the cable data to SRAM, we should clear the SRAM first to make sure there's no leftover data from previous transfers volatile u8 *cur = SRAM_PTR; volatile u8 *end = SRAM_PTR + 0x10000; - while (cur < end) + while(cur < end) { (*cur) = 0; ++cur; } } - if (g_debug_options.load_cable_data_from_save == WRITE_CABLE_DATA_MODE_CART) + if(g_debug_options.load_cable_data_from_save == WRITE_CABLE_DATA_MODE_CART) { // if we're loading the cable data from the cart save, we should make sure to load our first section here. copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); } } -void LinkConnection::load_payload(GB_PayloadsFiles payload) +void LinkConnection::startConnection(CompositeState startState) { - u32 fileSize; - u8 decompressionBuffer[0x1000]; - const u8 *chunkList[] = {(const u8 *)GB_Payloads_chunk0_lz10_bin}; - FileContainerReader reader(chunkList, 1); - const u32 fileIndex = (u32)payload; - - reader.init(decompressionBuffer, sizeof(decompressionBuffer)); - fileSize = reader.getFileSize(fileIndex); - reader.seekToFile(fileIndex); - reader.read(this->payloadBuffer, fileSize); - - this->curr_payload_size = fileSize; -} - -void LinkConnection::loadCurrGameFromChecksum() -{ - if (((dataOutBuffer[0] + dataOutBuffer[1]) & 0x7F) != dataOutBuffer[3]) + switch (startState) { - currROM = GB_ROM_ERROR; - }; - - int start = RED_JP_v0; - int end = GOLD_JP_v0; - - if (gen == 2) - { - start = end; - end = NO_GB_ROM; + case INITIAL_CONNECTION: + subState = CLOCK; + REG_TM3D = -0x4000 / 60; + REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; + break; + case PACKET_EXCHANGE: + subState = BYTE_EXCHANGE; + REG_TM3D = -0x0040; + // REG_TM3D = -0x4000 / 2; + REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; + break; + default: + break; } - for (int i = start; i < end; i++) - { - if (dataOutBuffer[0] == GameBoyROMChecksumTable[i][1] && dataOutBuffer[1] == GameBoyROMChecksumTable[i][2]) - { - currROM = (GameBoyROM)GameBoyROMChecksumTable[i][3]; - return; - } - } - currROM = GB_ROM_ERROR; - return; + nextSubState = subState; + nextCompState = startState; + lastError = NO_ERROR; + irq_enable(II_TIMER3); } void LinkConnection::exchangeBytes() @@ -157,61 +122,40 @@ void LinkConnection::exchangeBytes() global_next_frame(); return --timeout_frames <= 0; }); */ - switch (g_debug_options.load_cable_data_from_save) + switch(g_debug_options.load_cable_data_from_save) { - case WRITE_CABLE_DATA_MODE_OFF: - // Normal transfer :-) - inData = linkSPI->transfer(outData); - break; - case WRITE_CABLE_DATA_MODE_SRAM: - // Pretend transfer, by loading the bytes from SRAM (where we stored them with writeData() in a previous transfer) - inData = (*(SRAM_PTR + link_cable_array_index)); - ++link_cable_array_index; - outData = (*(SRAM_PTR + link_cable_array_index)); - ++link_cable_array_index; - break; - case WRITE_CABLE_DATA_MODE_CART: - { - // Pretend transfer, by loading the bytes from the cartridge save. (where we stored them with writeData() in a previous transfer) - // skip the first 6 bytes, which are for human consumption - link_cable_array_index += 6; - - inData = global_memory_buffer[link_cable_array_index]; - ++link_cable_array_index; - outData = global_memory_buffer[link_cable_array_index]; - ++link_cable_array_index; - - // we reached the end of our global_memory_buffer, we need to load the next data section from the cart save - if (link_cable_array_index >= 0x1000) + case WRITE_CABLE_DATA_MODE_OFF: + // Normal transfer :-) + inData = linkSPI->transfer(outData); + break; + case WRITE_CABLE_DATA_MODE_SRAM: + // Pretend transfer, by loading the bytes from SRAM (where we stored them with writeData() in a previous transfer) + inData = (*(SRAM_PTR + link_cable_array_index)); + ++link_cable_array_index; + outData = (*(SRAM_PTR + link_cable_array_index)); + ++link_cable_array_index; + break; + case WRITE_CABLE_DATA_MODE_CART: { - ++link_cable_memory_section_index; - link_cable_array_index = 0; - copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); + // Pretend transfer, by loading the bytes from the cartridge save. (where we stored them with writeData() in a previous transfer) + // skip the first 6 bytes, which are for human consumption + link_cable_array_index += 6; + + inData = global_memory_buffer[link_cable_array_index]; + ++link_cable_array_index; + outData = global_memory_buffer[link_cable_array_index]; + ++link_cable_array_index; + + // we reached the end of our global_memory_buffer, we need to load the next data section from the cart save + if(link_cable_array_index >= 0x1000) + { + ++link_cable_memory_section_index; + link_cable_array_index = 0; + copy_save_to_ram(0x1000 * link_cable_memory_section_index, &global_memory_buffer[0], 0x1000); + } + break; } - break; } - } -} - -void LinkConnection::startConnection(LinkState startState) -{ - switch (startState) - { - case INITIAL_CONNECTION: - REG_TM3D = -0x4000 / 60; - REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; - break; - case PACKET_EXCHANGE: - REG_TM3D = -0x0040; - // REG_TM3D = -0x4000 / 2; - REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; - break; - default: - break; - } - - enterState = startState; - irq_enable(II_TIMER3); } void LinkConnection::printData() @@ -224,13 +168,13 @@ void LinkConnection::printData() { if (g_debug_options.print_link_data) { - n2hexstr(&line[0], globalStateCounter & 0xFFFF, 4); - line[4] = '|'; - n2hexstr(&line[5], enterState & 0xFF, 2); - line[7] = ':'; - n2hexstr(&line[8], subStateCounter & 0xFFFF, 4); - line[12] = ':'; - n2hexstr(&line[13], exitState & 0xFF, 2); + n2hexstr(&line[0], compState & 0xFF, 2); + line[2] = ':'; + n2hexstr(&line[3], compStateCounter & 0xFFFF, 4); + line[7] = '|'; + n2hexstr(&line[8], subState & 0xFF, 2); + line[10] = ':'; + n2hexstr(&line[11], subStateCounter & 0xFFFF, 4); line[15] = '|'; line[16] = 'i'; n2hexstr(&line[17], inData & 0xFF, 2); @@ -288,305 +232,72 @@ void LinkConnection::printData() void LinkConnection::writeData() { - switch (g_debug_options.write_cable_data_to_save) + switch(g_debug_options.write_cable_data_to_save) { - case WRITE_CABLE_DATA_MODE_OFF: - break; - case WRITE_CABLE_DATA_MODE_SRAM: - { - (*(SRAM_PTR + link_cable_array_index)) = inData; - ++link_cable_array_index; - - (*(SRAM_PTR + link_cable_array_index)) = outData; - ++link_cable_array_index; - break; - } - case WRITE_CABLE_DATA_MODE_CART: - { - // save the data to the cartridge in chunks of 4 KB - // WARNING: If you want to add or remove fields here, - // make sure to keep the number of bytes a clean divider of 4096 (global_memory_buffer_size) - - // the next 6 bytes are for human consumption when viewed in a hex editor. - // they can be useful to correlate the current LinkConnection state with the data that was sent over the cable. - // but they're not needed for reconstructing the conversation with load_cable_data_from_save - global_memory_buffer[link_cable_array_index + 0] = (globalStateCounter >> 8) & 0xFF; - global_memory_buffer[link_cable_array_index + 1] = (globalStateCounter >> 0) & 0xFF; - global_memory_buffer[link_cable_array_index + 2] = enterState; - global_memory_buffer[link_cable_array_index + 3] = (subStateCounter >> 8) & 0xFF; - global_memory_buffer[link_cable_array_index + 4] = (subStateCounter >> 0) & 0xFF; - global_memory_buffer[link_cable_array_index + 5] = exitState; - - // actual data bytes start here. - global_memory_buffer[link_cable_array_index + 6] = inData; - global_memory_buffer[link_cable_array_index + 7] = outData; - - link_cable_array_index += 8; - - // If the buffer is full or we reached nextSubState == END, we save the buffer to the cartridge save - if (link_cable_array_index >= 0x1000 || exitState == END) + case WRITE_CABLE_DATA_MODE_OFF: + break; + case WRITE_CABLE_DATA_MODE_SRAM: { - erase_sector(0x1000 * link_cable_memory_section_index); - copy_ram_to_save(&global_memory_buffer[0], 0x1000 * link_cable_memory_section_index, 0x1000); - ++link_cable_memory_section_index; - link_cable_array_index = 0; + (*(SRAM_PTR + link_cable_array_index)) = inData; + ++link_cable_array_index; + + (*(SRAM_PTR + link_cable_array_index)) = outData; + ++link_cable_array_index; + break; + } + case WRITE_CABLE_DATA_MODE_CART: + { + // save the data to the cartridge in chunks of 4 KB + // WARNING: If you want to add or remove fields here, + // make sure to keep the number of bytes a clean divider of 4096 (global_memory_buffer_size) + + // the next 6 bytes are for human consumption when viewed in a hex editor. + // they can be useful to correlate the current LinkConnection state with the data that was sent over the cable. + // but they're not needed for reconstructing the conversation with load_cable_data_from_save + global_memory_buffer[link_cable_array_index + 0] = compState; + global_memory_buffer[link_cable_array_index + 1] = (compStateCounter >> 8) & 0xFF; + global_memory_buffer[link_cable_array_index + 2] = (compStateCounter >> 0) & 0xFF; + global_memory_buffer[link_cable_array_index + 3] = subState; + global_memory_buffer[link_cable_array_index + 4] = (subStateCounter >> 8) & 0xFF; + global_memory_buffer[link_cable_array_index + 5] = (subStateCounter >> 0) & 0xFF; + + // actual data bytes start here. + global_memory_buffer[link_cable_array_index + 6] = inData; + global_memory_buffer[link_cable_array_index + 7] = outData; + + link_cable_array_index += 8; + + // If the buffer is full or we reached nextSubState == END, we save the buffer to the cartridge save + if (link_cable_array_index >= 0x1000 || nextSubState == END) + { + erase_sector(0x1000 * link_cable_memory_section_index); + copy_ram_to_save(&global_memory_buffer[0], 0x1000 * link_cable_memory_section_index, 0x1000); + ++link_cable_memory_section_index; + link_cable_array_index = 0; + } + break; } - break; - } } } void LinkConnection::handleStateLogic() { - switch (enterState) + subState = nextSubState; + compState = nextCompState; + + switch (compState) { case INITIAL_CONNECTION: - nextOutData = 0xFF; - exitState = CLOCK; + logicState_initConnection(); break; - - case CLOCK: - if (inData == 0xFE) - { - exitState = SAVE_SUCCESS; - nextOutData = 0x00; - } - else - { - nextOutData = 0x01; - } - break; - - case SAVE_SUCCESS: - if (inData == 0x60 || inData == 0x61) - { - exitState = MENU_OPEN; - nextOutData = inData; - } - // nextOutData defaults to 0x00 - break; - - case MENU_OPEN: - if (inData == 0xD0 || inData == 0x61) - { - if (inData == 0xD0) - { - gen = 1; - load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN1); - nextOutData = 0xD4; - } - else if (inData == 0x61) - { - gen = 2; - load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN2); - nextOutData = 0x61; - } - exitState = MENU_SUCCESS; - } - break; - - case MENU_SUCCESS: - if (inData == 0xFE) - { - exitState = WAIT_FOR_TRADE; - } - nextOutData = inData; - break; - - case WAIT_FOR_TRADE: - if (inData == 0xFD) - { - REG_TM3D = -0x0040; - exitState = TRADE_PREAMBLE; - // nextOutData defaults to 0x00 - } - else - { - nextOutData = inData; - } - break; - - case TRADE_PREAMBLE: - if (subStateCounter < 2) - { - // nextOutData defaults to 0x00 - } - else if (subStateCounter < 9) - { - nextOutData = 0xFD; - } - else - { - exitState = TRADE; - nextOutData = 0xFD; - }; - break; - - case TRADE: - if (subStateCounter > curr_payload_size) - { - if (this->gen == 2) - { - exitState = MAIL; - } - else - { - exitState = WAIT_FOR_CHECKSUM_PAYLOAD; - } - } - nextOutData = payloadBuffer[subStateCounter]; - break; - - case MAIL: - if (subStateCounter > 0x186) - { - exitState = WAIT_FOR_CHECKSUM_PAYLOAD; - } - nextOutData = 0x00; - break; - - case WAIT_FOR_CHECKSUM_PAYLOAD: - if (inData == 0xFD) - { - exitState = GET_CHECKSUM; - } - // nextOutData defaults to 0x00 - break; - - case GET_CHECKSUM: - if (inData != 0xFD) - { - dataOutBuffer[dataOutBufferCurrIndex] = inData; - dataOutBufferCurrIndex++; - nextOutData = 0x01; - } - else if (inData == 0xFD && dataOutBufferCurrIndex > 0) - { - loadCurrGameFromChecksum(); - load_payload(GB_PayloadsFiles::SPECIFICPAYLOADGEN1_EN_R); - exitState = SEND_SPECIFIC_PAYLOAD; - } - else - { - nextOutData = 0xFD; - } - break; - - case WAIT_FOR_SECOND_PAYLOAD: - if (inData == 0xFD) - { - exitState = SEND_SPECIFIC_PAYLOAD; - } - // nextOutData defaults to 0x00 - break; - - case SEND_SPECIFIC_PAYLOAD: - if (subStateCounter > 255) // The 255 comes from the Universal Payload - { - exitState = END; - } - if (subStateCounter < curr_payload_size) - { - nextOutData = payloadBuffer[subStateCounter]; - } - else - { - nextOutData = 0x01; - } - break; - case PACKET_EXCHANGE: - nextOutData = 0xFF; - exitState = BYTE_EXCHANGE; + logicState_packetExchange(); break; - - case BYTE_EXCHANGE: - { - switch (subStateCounter % TOTAL_PACKET_LENGTH) - { - case 0: - nextOutData = 0xFD; - break; - case 1: - nextOutData = currLinkPacketArrIndex; - break; - case 2: - nextOutData = currLinkPacketArr[currLinkPacketArrIndex].command; - break; - case 3: - nextOutData = currLinkPacketArr[currLinkPacketArrIndex].argument[0]; - break; - case 4: - nextOutData = currLinkPacketArr[currLinkPacketArrIndex].argument[1]; - break; - case 5: - nextOutData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 0; - break; - case 6: - nextOutData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 8; - break; - case TOTAL_PACKET_LENGTH - 1: - currLinkPacketArrIndex++; - default: - nextOutData = 0xFF; - break; - } - - if (currLinkPacketArrIndex >= currLinkPacketArrFilledCount + 2) - { - exitState = PRINT_LAST_PACKET; - } - else if (currLinkPacketArrIndex >= currLinkPacketArrFilledCount) - { - // We don't want to send another packet, we just want the data back - nextOutData = 0xFF; - } - - if ((subStateCounter % TOTAL_PACKET_LENGTH == 0) && (currLinkPacketArrIndex > 1)) - { - newPacket = true; - - if (processPacket() == false) - { - // Packet failed, we need to put it back in the queue - if (currLinkPacketArrFilledCount < currLinkPacketArrTotalCount) - { - currLinkPacketArr[currLinkPacketArrFilledCount] = currLinkPacketArr[currLinkPacketArrIndex]; - currLinkPacketArrFilledCount++; - } - else - { - // We have filled the packet array. Set - } - } - } - else - { - newPacket = false; - } - - dataOutBuffer[subStateCounter % TOTAL_PACKET_LENGTH] = inData; - } - break; - - case PRINT_LAST_PACKET: - nextOutData = 0xFF; - exitState = END; - break; - - case END: - irq_disable(II_TIMER3); - break; - default: - nextOutData = inData; break; } -} -void LinkConnection::prepareForNextCycle() -{ - if (exitState != enterState) + if (nextSubState != subState) { subStateCounter = 0; subStateChanged = true; @@ -597,9 +308,16 @@ void LinkConnection::prepareForNextCycle() subStateChanged = false; } - globalStateCounter++; - enterState = exitState; - outData = nextOutData; + if (nextCompState != compState) + { + compStateCounter = 0; + compStateChanged = true; + } + else + { + compStateCounter++; + compStateChanged = false; + } } bool LinkConnection::earlyExit() @@ -652,11 +370,290 @@ bool LinkConnection::earlyExit() return pauseOnByte || (pauseOnPacket && newPacket); } +void LinkConnection::logicState_initConnection() +{ + outData = 0x00; + switch (subState) + { + case CLOCK: + if (inData == 0xFE) + { + nextSubState = SAVE_SUCCESS; + // outData defaults to 0x00 + } + else + { + outData = 0x01; + } + break; + + case SAVE_SUCCESS: + if (inData == 0x60 || inData == 0x61) + { + nextSubState = MENU_OPEN; + outData = inData; + } + // outData defaults to 0x00 + break; + + case MENU_OPEN: + if (inData == 0xD0 || inData == 0x61) + { + if (inData == 0xD0) + { + gen = 1; + load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN1); + outData = 0xD4; + } + else if (inData == 0x61) + { + gen = 2; + load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN2); + outData = 0x61; + } + nextSubState = MENU_SUCCESS; + } + break; + + case MENU_SUCCESS: + if (inData == 0xFE) + { + nextSubState = WAIT_FOR_TRADE; + } + outData = inData; + break; + + case WAIT_FOR_TRADE: + if (inData == 0xFD) + { + REG_TM3D = -0x0040; + nextSubState = TRADE_PREAMBLE; + // outData defaults to 0x00 + } + else + { + outData = inData; + } + break; + + case TRADE_PREAMBLE: + if (subStateCounter < 2) + { + // outData defaults to 0x00 + } + else if (subStateCounter < 9) + { + outData = 0xFD; + } + else + { + nextSubState = TRADE; + outData = 0xFD; + }; + break; + + case TRADE: + if (subStateCounter > curr_payload_size) + { + if (this->gen == 2) + { + nextSubState = MAIL; + } + else + { + nextSubState = WAIT_FOR_CHECKSUM_PAYLOAD; + } + } + outData = payloadBuffer[subStateCounter]; + break; + + case MAIL: + if (subStateCounter > 0x186) + { + nextSubState = WAIT_FOR_CHECKSUM_PAYLOAD; + } + outData = 0x00; + break; + + case WAIT_FOR_CHECKSUM_PAYLOAD: + if (inData == 0xFD) + { + nextSubState = GET_CHECKSUM; + } + // outData defaults to 0x00 + break; + + case GET_CHECKSUM: + if (inData != 0xFD) + { + dataOutBuffer[dataOutBufferCurrIndex] = inData; + dataOutBufferCurrIndex++; + outData = 0x01; + } + else if (inData == 0xFD && dataOutBufferCurrIndex > 0) + { + loadCurrGameFromChecksum(); + load_payload(GB_PayloadsFiles::SPECIFICPAYLOADGEN1_EN_R); + nextSubState = SEND_SPECIFIC_PAYLOAD; + } + else + { + outData = 0xFD; + } + break; + + case WAIT_FOR_SECOND_PAYLOAD: + if (inData == 0xFD) + { + nextSubState = SEND_SPECIFIC_PAYLOAD; + } + // outData defaults to 0x00 + break; + + case SEND_SPECIFIC_PAYLOAD: + if (subStateCounter > 255) // The 255 comes from the Universal Payload + { + nextSubState = END; + } + if (subStateCounter < curr_payload_size) + { + outData = payloadBuffer[subStateCounter]; + } + else + { + outData = 0x01; + } + break; + + case END: + irq_disable(II_TIMER3); + break; + + default: + outData = inData; + break; + } +} + +void LinkConnection::logicState_packetExchange() +{ + switch (subState) + { + case BYTE_EXCHANGE: + + if (subStateCounter < TOTAL_PACKET_LENGTH) + { + // Start with OUT_PACKET_LENGTH of bytes of prep to make sure things are set to go + outData = 0xFF; + } + else + { + switch (subStateCounter % TOTAL_PACKET_LENGTH) + { + case 0: + outData = 0xFD; + break; + case 1: + outData = currLinkPacketArrIndex; + break; + case 2: + outData = currLinkPacketArr[currLinkPacketArrIndex].command; + break; + case 3: + outData = currLinkPacketArr[currLinkPacketArrIndex].argument[0]; + break; + case 4: + outData = currLinkPacketArr[currLinkPacketArrIndex].argument[1]; + break; + case 5: + outData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 0; + break; + case 6: + outData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 8; + break; + case TOTAL_PACKET_LENGTH - 1: + currLinkPacketArrIndex++; + default: + outData = 0xFF; + break; + } + if (currLinkPacketArrIndex >= currLinkPacketArrNum) + { + nextSubState = END; + } + + if (subStateCounter % TOTAL_PACKET_LENGTH == 0) + { + newPacket = true; + processPacket(); + } + else + { + newPacket = false; + } + + dataOutBuffer[subStateCounter % TOTAL_PACKET_LENGTH] = inData; + } + break; + + case END: + irq_disable(II_TIMER3); + break; + + default: + outData = inData; + break; + } +} + +void LinkConnection::load_payload(GB_PayloadsFiles payload) +{ + u32 fileSize; + u8 decompressionBuffer[0x1000]; + const u8 *chunkList[] = {(const u8 *)GB_Payloads_chunk0_lz10_bin}; + FileContainerReader reader(chunkList, 1); + const u32 fileIndex = (u32)payload; + + reader.init(decompressionBuffer, sizeof(decompressionBuffer)); + fileSize = reader.getFileSize(fileIndex); + reader.seekToFile(fileIndex); + reader.read(this->payloadBuffer, fileSize); + + this->curr_payload_size = fileSize; +} + +void LinkConnection::loadCurrGameFromChecksum() +{ + if (((dataOutBuffer[0] + dataOutBuffer[1]) & 0x7F) != dataOutBuffer[3]) + { + currROM = GB_ROM_ERROR; + }; + + int start = RED_JP_v0; + int end = GOLD_JP_v0; + + if (gen == 2) + { + start = end; + end = NO_GB_ROM; + } + + for (int i = start; i < end; i++) + { + if (dataOutBuffer[0] == GameBoyROMChecksumTable[i][1] && dataOutBuffer[1] == GameBoyROMChecksumTable[i][2]) + { + currROM = (GameBoyROM)GameBoyROMChecksumTable[i][3]; + return; + } + } + currROM = GB_ROM_ERROR; + return; +} + bool LinkConnection::processPacket() { int checksum = 0; LinkPacket &currPacket = currLinkPacketArr[dataOutBuffer[INP_COUNTER_INDEX]]; - for (int i = INP_DELAY_FROM_OUTP; i < INP_LENGTH; i++) { if (i != INP_CHECKSUM_INDEX) diff --git a/source/script_array.cpp b/source/script_array.cpp index 70f87fa..aebb6b8 100644 --- a/source/script_array.cpp +++ b/source/script_array.cpp @@ -760,11 +760,11 @@ bool run_conditional(int index) load_localized_charset(debug_charset, 3, ENGLISH); globalLinkCable.setup(debug_charset); globalLinkCable.startConnection(INITIAL_CONNECTION); - while (globalLinkCable.exitState != END) + while (globalLinkCable.subState != END) { if (globalLinkCable.subStateChanged && !g_debug_options.print_link_data) { - switch (globalLinkCable.exitState) + switch (globalLinkCable.subState) { case SAVE_SUCCESS: general_text_reader.readFile(GENERAL_link_success, lineBuffer); @@ -801,18 +801,20 @@ bool run_conditional(int index) {CMD_ReadDataRequest, 0x00, 0x00, 0xDA98}, {CMD_ReadDataRequest, 0x00, 0x00, 0xDAA0}, {CMD_ReadDataRequest, 0x00, 0x00, 0xDAA8}, + {CMD_ReadDataRequest, 0x00, 0x00, 0xC5DC} + }; globalLinkCable.skipPrint = false; globalLinkCable.pauseOnPacket = true; - //while (true) + while (true) { globalLinkCable.currLinkPacketArr = packets; - globalLinkCable.currLinkPacketArrFilledCount = 6; + globalLinkCable.currLinkPacketArrNum = 6; globalLinkCable.currLinkPacketArrIndex = 0; globalLinkCable.startConnection(PACKET_EXCHANGE); - while (globalLinkCable.enterState != END) + while (globalLinkCable.subState != END) { VBlankIntrWait(); } From 261d2d8c55b3c1b971f89c5116e1e68347c1cafb Mon Sep 17 00:00:00 2001 From: GearsProgress Date: Wed, 10 Jun 2026 22:43:16 -0400 Subject: [PATCH 4/4] Cleaning up the link handling and preparing for full read chunks (for real this time) --- include/link_handler.h | 66 +--- source/link_handler.cpp | 736 ++++++++++++++++++++-------------------- source/script_array.cpp | 10 +- 3 files changed, 388 insertions(+), 424 deletions(-) diff --git a/include/link_handler.h b/include/link_handler.h index f73b74d..8ec07d4 100644 --- a/include/link_handler.h +++ b/include/link_handler.h @@ -6,21 +6,6 @@ #include "pokemon_party.h" #include "GB_Payloads.h" -#define DATA_PER_PACKET 8 -#define PACKET_DATA_START 2 -#define PACKET_DATA_AT(i) (PACKET_DATA_START + (i * 2)) -#define PACKET_FLAG_AT(i) (PACKET_DATA_START + (i * 2) + 1) -#define PACKET_CHECKSUM (PACKET_DATA_START + (2 * DATA_PER_PACKET)) -#define PACKET_LOCATION_UPPER (PACKET_CHECKSUM + 1) -#define PACKET_LOCATION_LOWER (PACKET_CHECKSUM + 2) - -// 0xFD, 0x00, data bytes per packet, flag bytes per packet, the checksum, and two location bytes -#define PACKET_SIZE (1 + 1 + (2 * DATA_PER_PACKET) + 1 + 2) // Originally 13 - -#define TIMEOUT 2 -#define TIMEOUT_ONE_LENGTH 1000000 // Maybe keep a 10:1 ratio between ONE and TWO? -#define TIMEOUT_TWO_LENGTH 100000 - #define SPI_TEXT_OUT_ARRAY_ELEMENT_SIZE 64 enum GameBoyROM @@ -126,18 +111,9 @@ const u8 GameBoyROMChecksumTable[][4]{ {0x19, 0x42, 0xF4, CRYSTAL_SP}, }; -enum CompositeState +enum LinkState { - NO_COMPOSITE_STATE, - INITIAL_CONNECTION, - PACKET_EXCHANGE, -}; - -enum SubstateState -{ - NO_SUBSTATE, - - // INITIAL_CONNECTION + INITIAL_CONNECTION = 0x00, CLOCK, SAVE_SUCCESS, MENU_OPEN, @@ -152,11 +128,11 @@ enum SubstateState SEND_SPECIFIC_PAYLOAD, SOFT_RESET, - // PACKET_EXCHANGE + PACKET_EXCHANGE = 0x10, BYTE_EXCHANGE, + PRINT_LAST_PACKET, - END, - + END = 0xFF, }; enum LinkConnectionError @@ -209,21 +185,18 @@ struct LinkPacket class LinkConnection { public: - CompositeState compState = NO_COMPOSITE_STATE; - CompositeState nextCompState = NO_COMPOSITE_STATE; - bool compStateChanged = false; - - SubstateState subState = NO_SUBSTATE; - SubstateState nextSubState = NO_SUBSTATE; + LinkState enterState; + LinkState exitState; bool subStateChanged = false; LinkConnectionError lastError = NO_ERROR; uint8_t inData; uint8_t outData; + uint8_t nextOutData; - int compStateCounter = 0; // the counter for the total number of bytes sent compstate - int subStateCounter = 0; // The counter for the total number of bytes sent in this substate + int globalStateCounter = 0; // The counter for the total number of bytes sent + int subStateCounter = 0; // The counter for the total number of bytes sent in this substate int gen = 0; // The generation we are trading with GameBoyROM currROM = NO_GB_ROM; // The GameBoy ROM we're communicating with @@ -231,24 +204,14 @@ public: int FF_count = 0; // The number of 0xFF bytes that have been in a row int zero_count = 0; // The number of 0x00 bytes that have been in a row - int mosi_delay = 4; // inital delay, speeds up once sending - int received_offset = 0; // The offset contained in the last packet - int next_offset = 0; // The offset we are sending in the next packet - int packet_index = 0; // The index of the current packet - - bool failed_packet = false; // Flags if a packet failed - bool init_packet = true; // Flags if a packet is the inital one - bool end_of_data = false; // Flags if we are at the end of the data - bool test_packet_fail = false; // ??? - - byte data_packet[PACKET_SIZE]; byte payloadBuffer[0x2A0]; int curr_payload_size = 0; byte dataOutBuffer[16]; int dataOutBufferCurrIndex = 0; LinkPacket *currLinkPacketArr; - int currLinkPacketArrNum = 0; + int currLinkPacketArrTotalCount = 0; + int currLinkPacketArrFilledCount = 0; int currLinkPacketArrIndex = 0; bool pauseOnByte = false; // Used for pausing and sending one byte at a time @@ -257,12 +220,13 @@ public: bool newPacket = false; void setup(const u16 *debug_charset); - void startConnection(CompositeState startState); + void startConnection(LinkState startState); bool earlyExit(); void exchangeBytes(); void printData(); void writeData(); void handleStateLogic(); + void prepareForNextCycle(); // 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. void handleCartIO(); @@ -271,8 +235,6 @@ private: void load_payload(GB_PayloadsFiles payload); void loadCurrGameFromChecksum(); bool processPacket(); - void logicState_initConnection(); - void logicState_packetExchange(); // Used for debug features #define LINE_WIDTH 24 diff --git a/source/link_handler.cpp b/source/link_handler.cpp index 3de3842..dd2c741 100644 --- a/source/link_handler.cpp +++ b/source/link_handler.cpp @@ -32,12 +32,26 @@ LinkConnection globalLinkCable; void linkCableIRQ() { + /* + ---------------- + This handshake process can be a bit weird, so let's break it down: + First we exchange the bytes via handshake. inData will be set to the recieved byte, and the byte we send out will be outData. + + Then we determining what byte we will be sending out next, based on enterState and inByte in handleStateLogic(). + This will set exitState and nextOutByte. + + Then we print our information in the following format: globalCounter enterState:stateCounter:exitState inData outData + + We then prepare for the next cycle. Counters are incremented (or reset), enterState is set to exitState, and outByte is set to nextOutByte. + ---------------- + */ if (!globalLinkCable.earlyExit()) { - globalLinkCable.exchangeBytes(); + globalLinkCable.handleStateLogic(); + if (g_debug_options.print_link_data || g_debug_options.print_link_packets) { globalLinkCable.printData(); @@ -48,7 +62,7 @@ void linkCableIRQ() globalLinkCable.writeData(); } - globalLinkCable.handleStateLogic(); + globalLinkCable.prepareForNextCycle(); } } @@ -63,17 +77,19 @@ void LinkConnection::setup(const u16 *debug_charset) this->debug_charset = debug_charset; + lastError = NO_ERROR; + if (g_debug_options.print_link_data == true) { create_textbox(0, 0, 138, 128, false); } - if(g_debug_options.write_cable_data_to_save == WRITE_CABLE_DATA_MODE_SRAM) + if (g_debug_options.write_cable_data_to_save == WRITE_CABLE_DATA_MODE_SRAM) { // if we're writing the cable data to SRAM, we should clear the SRAM first to make sure there's no leftover data from previous transfers volatile u8 *cur = SRAM_PTR; volatile u8 *end = SRAM_PTR + 0x10000; - while(cur < end) + while (cur < end) { (*cur) = 0; ++cur; @@ -87,29 +103,48 @@ void LinkConnection::setup(const u16 *debug_charset) } } -void LinkConnection::startConnection(CompositeState startState) +void LinkConnection::load_payload(GB_PayloadsFiles payload) { - switch (startState) + u32 fileSize; + u8 decompressionBuffer[0x1000]; + const u8 *chunkList[] = {(const u8 *)GB_Payloads_chunk0_lz10_bin}; + FileContainerReader reader(chunkList, 1); + const u32 fileIndex = (u32)payload; + + reader.init(decompressionBuffer, sizeof(decompressionBuffer)); + fileSize = reader.getFileSize(fileIndex); + reader.seekToFile(fileIndex); + reader.read(this->payloadBuffer, fileSize); + + this->curr_payload_size = fileSize; +} + +void LinkConnection::loadCurrGameFromChecksum() +{ + if (((dataOutBuffer[0] + dataOutBuffer[1]) & 0x7F) != dataOutBuffer[3]) { - case INITIAL_CONNECTION: - subState = CLOCK; - REG_TM3D = -0x4000 / 60; - REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; - break; - case PACKET_EXCHANGE: - subState = BYTE_EXCHANGE; - REG_TM3D = -0x0040; - // REG_TM3D = -0x4000 / 2; - REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; - break; - default: - break; + currROM = GB_ROM_ERROR; + }; + + int start = RED_JP_v0; + int end = GOLD_JP_v0; + + if (gen == 2) + { + start = end; + end = NO_GB_ROM; } - nextSubState = subState; - nextCompState = startState; - lastError = NO_ERROR; - irq_enable(II_TIMER3); + for (int i = start; i < end; i++) + { + if (dataOutBuffer[0] == GameBoyROMChecksumTable[i][1] && dataOutBuffer[1] == GameBoyROMChecksumTable[i][2]) + { + currROM = (GameBoyROM)GameBoyROMChecksumTable[i][3]; + return; + } + } + currROM = GB_ROM_ERROR; + return; } void LinkConnection::exchangeBytes() @@ -123,24 +158,24 @@ void LinkConnection::exchangeBytes() global_next_frame(); return --timeout_frames <= 0; }); */ - switch(g_debug_options.load_cable_data_from_save) + switch (g_debug_options.load_cable_data_from_save) { - case WRITE_CABLE_DATA_MODE_OFF: - // Normal transfer :-) - inData = linkSPI->transfer(outData); - break; - case WRITE_CABLE_DATA_MODE_SRAM: - // Pretend transfer, by loading the bytes from SRAM (where we stored them with writeData() in a previous transfer) - inData = (*(SRAM_PTR + link_cable_array_index)); - ++link_cable_array_index; - outData = (*(SRAM_PTR + link_cable_array_index)); - ++link_cable_array_index; - break; - case WRITE_CABLE_DATA_MODE_CART: - { - // Pretend transfer, by loading the bytes from the cartridge save. (where we stored them with writeData() in a previous transfer) - // skip the first 6 bytes, which are for human consumption - link_cable_array_index += 6; + case WRITE_CABLE_DATA_MODE_OFF: + // Normal transfer :-) + inData = linkSPI->transfer(outData); + break; + case WRITE_CABLE_DATA_MODE_SRAM: + // Pretend transfer, by loading the bytes from SRAM (where we stored them with writeData() in a previous transfer) + inData = (*(SRAM_PTR + link_cable_array_index)); + ++link_cable_array_index; + outData = (*(SRAM_PTR + link_cable_array_index)); + ++link_cable_array_index; + break; + case WRITE_CABLE_DATA_MODE_CART: + { + // Pretend transfer, by loading the bytes from the cartridge save. (where we stored them with writeData() in a previous transfer) + // skip the first 6 bytes, which are for human consumption + link_cable_array_index += 6; inData = read_byte_save((0x1000 * link_cable_memory_section_index) + link_cable_array_index); ++link_cable_array_index; @@ -158,6 +193,27 @@ void LinkConnection::exchangeBytes() } } +void LinkConnection::startConnection(LinkState startState) +{ + switch (startState) + { + case INITIAL_CONNECTION: + REG_TM3D = -0x4000 / 60; + REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; + break; + case PACKET_EXCHANGE: + REG_TM3D = -0x0040; + // REG_TM3D = -0x4000 / 2; + REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; + break; + default: + break; + } + + enterState = startState; + irq_enable(II_TIMER3); +} + void LinkConnection::printData() { if (globalLinkCable.skipPrint) @@ -168,13 +224,13 @@ void LinkConnection::printData() { if (g_debug_options.print_link_data) { - n2hexstr(&line[0], compState & 0xFF, 2); - line[2] = ':'; - n2hexstr(&line[3], compStateCounter & 0xFFFF, 4); - line[7] = '|'; - n2hexstr(&line[8], subState & 0xFF, 2); - line[10] = ':'; - n2hexstr(&line[11], subStateCounter & 0xFFFF, 4); + n2hexstr(&line[0], globalStateCounter & 0xFFFF, 4); + line[4] = '|'; + n2hexstr(&line[5], enterState & 0xFF, 2); + line[7] = ':'; + n2hexstr(&line[8], subStateCounter & 0xFFFF, 4); + line[12] = ':'; + n2hexstr(&line[13], exitState & 0xFF, 2); line[15] = '|'; line[16] = 'i'; n2hexstr(&line[17], inData & 0xFF, 2); @@ -232,34 +288,34 @@ void LinkConnection::printData() void LinkConnection::writeData() { - switch(g_debug_options.write_cable_data_to_save) + switch (g_debug_options.write_cable_data_to_save) { - case WRITE_CABLE_DATA_MODE_OFF: - break; - case WRITE_CABLE_DATA_MODE_SRAM: - { - (*(SRAM_PTR + link_cable_array_index)) = inData; - ++link_cable_array_index; + case WRITE_CABLE_DATA_MODE_OFF: + break; + case WRITE_CABLE_DATA_MODE_SRAM: + { + (*(SRAM_PTR + link_cable_array_index)) = inData; + ++link_cable_array_index; - (*(SRAM_PTR + link_cable_array_index)) = outData; - ++link_cable_array_index; - break; - } - case WRITE_CABLE_DATA_MODE_CART: - { - // save the data to the cartridge in chunks of 4 KB - // WARNING: If you want to add or remove fields here, - // make sure to keep the number of bytes a clean divider of 4096 (global_memory_buffer_size) + (*(SRAM_PTR + link_cable_array_index)) = outData; + ++link_cable_array_index; + break; + } + case WRITE_CABLE_DATA_MODE_CART: + { + // save the data to the cartridge in chunks of 4 KB + // WARNING: If you want to add or remove fields here, + // make sure to keep the number of bytes a clean divider of 4096 (global_memory_buffer_size) // the next 6 bytes are for human consumption when viewed in a hex editor. // they can be useful to correlate the current LinkConnection state with the data that was sent over the cable. // but they're not needed for reconstructing the conversation with load_cable_data_from_save - global_memory_buffer[writeBufferOffset + 0] = (u8)compState; - global_memory_buffer[writeBufferOffset + 1] = (u8)((compStateCounter >> 8) & 0xFF); - global_memory_buffer[writeBufferOffset + 2] = (u8)((compStateCounter >> 0) & 0xFF); - global_memory_buffer[writeBufferOffset + 3] = (u8)subState; - global_memory_buffer[writeBufferOffset + 4] = (u8)((subStateCounter >> 8) & 0xFF); - global_memory_buffer[writeBufferOffset + 5] = (u8)((subStateCounter >> 0) & 0xFF); + global_memory_buffer[writeBufferOffset + 0] = (u8)(globalStateCounter >> 8) & 0xFF; + global_memory_buffer[writeBufferOffset + 1] = (u8)(globalStateCounter >> 0) & 0xFF; + global_memory_buffer[writeBufferOffset + 2] = (u8)enterState; + global_memory_buffer[writeBufferOffset + 3] = (u8)(subStateCounter >> 8) & 0xFF; + global_memory_buffer[writeBufferOffset + 4] = (u8)(subStateCounter >> 0) & 0xFF; + global_memory_buffer[writeBufferOffset + 5] = (u8)exitState; // actual data bytes start here. global_memory_buffer[writeBufferOffset + 6] = inData; @@ -274,22 +330,255 @@ void LinkConnection::writeData() void LinkConnection::handleStateLogic() { - subState = nextSubState; - compState = nextCompState; - - switch (compState) + switch (enterState) { case INITIAL_CONNECTION: - logicState_initConnection(); + nextOutData = 0xFF; + exitState = CLOCK; break; + + case CLOCK: + if (inData == 0xFE) + { + exitState = SAVE_SUCCESS; + nextOutData = 0x00; + } + else + { + nextOutData = 0x01; + } + break; + + case SAVE_SUCCESS: + if (inData == 0x60 || inData == 0x61) + { + exitState = MENU_OPEN; + nextOutData = inData; + } + // nextOutData defaults to 0x00 + break; + + case MENU_OPEN: + if (inData == 0xD0 || inData == 0x61) + { + if (inData == 0xD0) + { + gen = 1; + load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN1); + nextOutData = 0xD4; + } + else if (inData == 0x61) + { + gen = 2; + load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN2); + nextOutData = 0x61; + } + exitState = MENU_SUCCESS; + } + break; + + case MENU_SUCCESS: + if (inData == 0xFE) + { + exitState = WAIT_FOR_TRADE; + } + nextOutData = inData; + break; + + case WAIT_FOR_TRADE: + if (inData == 0xFD) + { + REG_TM3D = -0x0040; + exitState = TRADE_PREAMBLE; + // nextOutData defaults to 0x00 + } + else + { + nextOutData = inData; + } + break; + + case TRADE_PREAMBLE: + if (subStateCounter < 2) + { + // nextOutData defaults to 0x00 + } + else if (subStateCounter < 9) + { + nextOutData = 0xFD; + } + else + { + exitState = TRADE; + nextOutData = 0xFD; + }; + break; + + case TRADE: + if (subStateCounter > curr_payload_size) + { + if (this->gen == 2) + { + exitState = MAIL; + } + else + { + exitState = WAIT_FOR_CHECKSUM_PAYLOAD; + } + } + nextOutData = payloadBuffer[subStateCounter]; + break; + + case MAIL: + if (subStateCounter > 0x186) + { + exitState = WAIT_FOR_CHECKSUM_PAYLOAD; + } + nextOutData = 0x00; + break; + + case WAIT_FOR_CHECKSUM_PAYLOAD: + if (inData == 0xFD) + { + exitState = GET_CHECKSUM; + } + // nextOutData defaults to 0x00 + break; + + case GET_CHECKSUM: + if (inData != 0xFD) + { + dataOutBuffer[dataOutBufferCurrIndex] = inData; + dataOutBufferCurrIndex++; + nextOutData = 0x01; + } + else if (inData == 0xFD && dataOutBufferCurrIndex > 0) + { + loadCurrGameFromChecksum(); + load_payload(GB_PayloadsFiles::SPECIFICPAYLOADGEN1_EN_R); + exitState = SEND_SPECIFIC_PAYLOAD; + } + else + { + nextOutData = 0xFD; + } + break; + + case WAIT_FOR_SECOND_PAYLOAD: + if (inData == 0xFD) + { + exitState = SEND_SPECIFIC_PAYLOAD; + } + // nextOutData defaults to 0x00 + break; + + case SEND_SPECIFIC_PAYLOAD: + if (subStateCounter > 255) // The 255 comes from the Universal Payload + { + exitState = END; + } + if (subStateCounter < curr_payload_size) + { + nextOutData = payloadBuffer[subStateCounter]; + } + else + { + nextOutData = 0x01; + } + break; + case PACKET_EXCHANGE: - logicState_packetExchange(); + nextOutData = 0xFF; + exitState = BYTE_EXCHANGE; break; + + case BYTE_EXCHANGE: + { + switch (subStateCounter % TOTAL_PACKET_LENGTH) + { + case 0: + nextOutData = 0xFD; + break; + case 1: + nextOutData = currLinkPacketArrIndex; + break; + case 2: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].command; + break; + case 3: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].argument[0]; + break; + case 4: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].argument[1]; + break; + case 5: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 0; + break; + case 6: + nextOutData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 8; + break; + case TOTAL_PACKET_LENGTH - 1: + currLinkPacketArrIndex++; + default: + nextOutData = 0xFF; + break; + } + + if (currLinkPacketArrIndex >= currLinkPacketArrFilledCount + 2) + { + exitState = PRINT_LAST_PACKET; + } + else if (currLinkPacketArrIndex >= currLinkPacketArrFilledCount) + { + // We don't want to send another packet, we just want the data back + nextOutData = 0xFF; + } + + if ((subStateCounter % TOTAL_PACKET_LENGTH == 0) && (currLinkPacketArrIndex > 1)) + { + newPacket = true; + + if (processPacket() == false) + { + // Packet failed, we need to put it back in the queue + if (currLinkPacketArrFilledCount < currLinkPacketArrTotalCount) + { + currLinkPacketArr[currLinkPacketArrFilledCount] = currLinkPacketArr[currLinkPacketArrIndex]; + currLinkPacketArrFilledCount++; + } + else + { + // We have filled the packet array. Set + } + } + } + else + { + newPacket = false; + } + + dataOutBuffer[subStateCounter % TOTAL_PACKET_LENGTH] = inData; + } + break; + + case PRINT_LAST_PACKET: + nextOutData = 0xFF; + exitState = END; + break; + + case END: + irq_disable(II_TIMER3); + break; + default: + nextOutData = inData; break; } +} - if (nextSubState != subState) +void LinkConnection::prepareForNextCycle() +{ + if (exitState != enterState) { subStateCounter = 0; subStateChanged = true; @@ -300,16 +589,10 @@ void LinkConnection::handleStateLogic() subStateChanged = false; } - if (nextCompState != compState) - { - compStateCounter = 0; - compStateChanged = true; - } - else - { - compStateCounter++; - compStateChanged = false; - } + globalStateCounter++; + enterState = exitState; + outData = nextOutData; + } /* @@ -412,290 +695,11 @@ bool LinkConnection::earlyExit() return pauseOnByte || (pauseOnPacket && newPacket); } -void LinkConnection::logicState_initConnection() -{ - outData = 0x00; - switch (subState) - { - case CLOCK: - if (inData == 0xFE) - { - nextSubState = SAVE_SUCCESS; - // outData defaults to 0x00 - } - else - { - outData = 0x01; - } - break; - - case SAVE_SUCCESS: - if (inData == 0x60 || inData == 0x61) - { - nextSubState = MENU_OPEN; - outData = inData; - } - // outData defaults to 0x00 - break; - - case MENU_OPEN: - if (inData == 0xD0 || inData == 0x61) - { - if (inData == 0xD0) - { - gen = 1; - load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN1); - outData = 0xD4; - } - else if (inData == 0x61) - { - gen = 2; - load_payload(GB_PayloadsFiles::UNIVERSALPAYLOADGEN2); - outData = 0x61; - } - nextSubState = MENU_SUCCESS; - } - break; - - case MENU_SUCCESS: - if (inData == 0xFE) - { - nextSubState = WAIT_FOR_TRADE; - } - outData = inData; - break; - - case WAIT_FOR_TRADE: - if (inData == 0xFD) - { - REG_TM3D = -0x0040; - nextSubState = TRADE_PREAMBLE; - // outData defaults to 0x00 - } - else - { - outData = inData; - } - break; - - case TRADE_PREAMBLE: - if (subStateCounter < 2) - { - // outData defaults to 0x00 - } - else if (subStateCounter < 9) - { - outData = 0xFD; - } - else - { - nextSubState = TRADE; - outData = 0xFD; - }; - break; - - case TRADE: - if (subStateCounter > curr_payload_size) - { - if (this->gen == 2) - { - nextSubState = MAIL; - } - else - { - nextSubState = WAIT_FOR_CHECKSUM_PAYLOAD; - } - } - outData = payloadBuffer[subStateCounter]; - break; - - case MAIL: - if (subStateCounter > 0x186) - { - nextSubState = WAIT_FOR_CHECKSUM_PAYLOAD; - } - outData = 0x00; - break; - - case WAIT_FOR_CHECKSUM_PAYLOAD: - if (inData == 0xFD) - { - nextSubState = GET_CHECKSUM; - } - // outData defaults to 0x00 - break; - - case GET_CHECKSUM: - if (inData != 0xFD) - { - dataOutBuffer[dataOutBufferCurrIndex] = inData; - dataOutBufferCurrIndex++; - outData = 0x01; - } - else if (inData == 0xFD && dataOutBufferCurrIndex > 0) - { - loadCurrGameFromChecksum(); - load_payload(GB_PayloadsFiles::SPECIFICPAYLOADGEN1_EN_R); - nextSubState = SEND_SPECIFIC_PAYLOAD; - } - else - { - outData = 0xFD; - } - break; - - case WAIT_FOR_SECOND_PAYLOAD: - if (inData == 0xFD) - { - nextSubState = SEND_SPECIFIC_PAYLOAD; - } - // outData defaults to 0x00 - break; - - case SEND_SPECIFIC_PAYLOAD: - if (subStateCounter > 255) // The 255 comes from the Universal Payload - { - nextSubState = END; - } - if (subStateCounter < curr_payload_size) - { - outData = payloadBuffer[subStateCounter]; - } - else - { - outData = 0x01; - } - break; - - case END: - irq_disable(II_TIMER3); - break; - - default: - outData = inData; - break; - } -} - -void LinkConnection::logicState_packetExchange() -{ - switch (subState) - { - case BYTE_EXCHANGE: - - if (subStateCounter < TOTAL_PACKET_LENGTH) - { - // Start with OUT_PACKET_LENGTH of bytes of prep to make sure things are set to go - outData = 0xFF; - } - else - { - switch (subStateCounter % TOTAL_PACKET_LENGTH) - { - case 0: - outData = 0xFD; - break; - case 1: - outData = currLinkPacketArrIndex; - break; - case 2: - outData = currLinkPacketArr[currLinkPacketArrIndex].command; - break; - case 3: - outData = currLinkPacketArr[currLinkPacketArrIndex].argument[0]; - break; - case 4: - outData = currLinkPacketArr[currLinkPacketArrIndex].argument[1]; - break; - case 5: - outData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 0; - break; - case 6: - outData = currLinkPacketArr[currLinkPacketArrIndex].pointer >> 8; - break; - case TOTAL_PACKET_LENGTH - 1: - currLinkPacketArrIndex++; - default: - outData = 0xFF; - break; - } - if (currLinkPacketArrIndex >= currLinkPacketArrNum) - { - nextSubState = END; - } - - if (subStateCounter % TOTAL_PACKET_LENGTH == 0) - { - newPacket = true; - processPacket(); - } - else - { - newPacket = false; - } - - dataOutBuffer[subStateCounter % TOTAL_PACKET_LENGTH] = inData; - } - break; - - case END: - irq_disable(II_TIMER3); - break; - - default: - outData = inData; - break; - } -} - -void LinkConnection::load_payload(GB_PayloadsFiles payload) -{ - u32 fileSize; - u8 decompressionBuffer[0x1000]; - const u8 *chunkList[] = {(const u8 *)GB_Payloads_chunk0_lz10_bin}; - FileContainerReader reader(chunkList, 1); - const u32 fileIndex = (u32)payload; - - reader.init(decompressionBuffer, sizeof(decompressionBuffer)); - fileSize = reader.getFileSize(fileIndex); - reader.seekToFile(fileIndex); - reader.read(this->payloadBuffer, fileSize); - - this->curr_payload_size = fileSize; -} - -void LinkConnection::loadCurrGameFromChecksum() -{ - if (((dataOutBuffer[0] + dataOutBuffer[1]) & 0x7F) != dataOutBuffer[3]) - { - currROM = GB_ROM_ERROR; - }; - - int start = RED_JP_v0; - int end = GOLD_JP_v0; - - if (gen == 2) - { - start = end; - end = NO_GB_ROM; - } - - for (int i = start; i < end; i++) - { - if (dataOutBuffer[0] == GameBoyROMChecksumTable[i][1] && dataOutBuffer[1] == GameBoyROMChecksumTable[i][2]) - { - currROM = (GameBoyROM)GameBoyROMChecksumTable[i][3]; - return; - } - } - currROM = GB_ROM_ERROR; - return; -} - bool LinkConnection::processPacket() { int checksum = 0; LinkPacket &currPacket = currLinkPacketArr[dataOutBuffer[INP_COUNTER_INDEX]]; + for (int i = INP_DELAY_FROM_OUTP; i < INP_LENGTH; i++) { if (i != INP_CHECKSUM_INDEX) diff --git a/source/script_array.cpp b/source/script_array.cpp index 8225df6..8cc330c 100644 --- a/source/script_array.cpp +++ b/source/script_array.cpp @@ -760,11 +760,11 @@ bool run_conditional(int index) load_localized_charset(debug_charset, 3, ENGLISH); globalLinkCable.setup(debug_charset); globalLinkCable.startConnection(INITIAL_CONNECTION); - while (globalLinkCable.subState != END) + while (globalLinkCable.exitState != END) { if (globalLinkCable.subStateChanged && !g_debug_options.print_link_data) { - switch (globalLinkCable.subState) + switch (globalLinkCable.exitState) { case SAVE_SUCCESS: general_text_reader.readFile(GENERAL_link_success, lineBuffer); @@ -804,8 +804,6 @@ bool run_conditional(int index) {CMD_ReadDataRequest, 0x00, 0x00, 0xDA98}, {CMD_ReadDataRequest, 0x00, 0x00, 0xDAA0}, {CMD_ReadDataRequest, 0x00, 0x00, 0xDAA8}, - {CMD_ReadDataRequest, 0x00, 0x00, 0xC5DC} - }; globalLinkCable.skipPrint = false; @@ -813,11 +811,11 @@ bool run_conditional(int index) while (true) { globalLinkCable.currLinkPacketArr = packets; - globalLinkCable.currLinkPacketArrNum = 6; + globalLinkCable.currLinkPacketArrFilledCount = 6; globalLinkCable.currLinkPacketArrIndex = 0; globalLinkCable.startConnection(PACKET_EXCHANGE); - while (globalLinkCable.subState != END) + while (globalLinkCable.enterState != END) { globalLinkCable.handleCartIO(); VBlankIntrWait();