Merge branch 'gameboy-payload-update' into pr/TimoVM/146

This commit is contained in:
GearsProgress
2026-06-11 16:38:49 -04:00
4 changed files with 461 additions and 168 deletions

View File

@@ -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,28 +220,31 @@ 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();
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
#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;

View File

@@ -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();
}
}
@@ -56,59 +70,81 @@ 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);
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)
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);
}
}
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,42 +158,62 @@ 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 = 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;
}
}
}
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,49 +288,41 @@ 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[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)(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[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;
}
}
@@ -282,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;
@@ -308,15 +589,59 @@ void LinkConnection::handleStateLogic()
subStateChanged = false;
}
if (nextCompState != compState)
globalStateCounter++;
enterState = exitState;
outData = nextOutData;
}
/*
* 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)
{
compStateCounter = 0;
compStateChanged = true;
}
else
{
compStateCounter++;
compStateChanged = false;
// 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);
}
}
}
@@ -654,6 +979,7 @@ 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)

View File

@@ -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
}

View File

@@ -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);
@@ -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);
@@ -801,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;
@@ -810,12 +811,13 @@ 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();
}
}