GBA Savedata: Transition saving to new timoing system
@@ -11,6 +11,7 @@
CXX_GUARD_START #include <mgba/core/log.h> +#include <mgba/core/timing.h> mLOG_DECLARE_CATEGORY(GBA_SAVE);@@ -79,15 +80,16 @@ int mapMode;
bool maskWriteback; struct VFile* realVf; - int32_t readBitsRemaining; + int8_t readBitsRemaining; uint32_t readAddress; uint32_t writeAddress; uint8_t* currentBank; + struct mTiming* timing; bool realisticTiming; unsigned settling; - int dust; + struct mTimingEvent dust; enum SavedataDirty dirty; uint32_t dirtAge;
@@ -177,13 +177,14 @@ * | 0x002E2 - 0x002E2: Flags
* | bits 0 - 1: Flash state machine * | bits 2 - 3: Reserved * | bit 4: Flash bank - * | bits 5 - 7: Reserved - * | 0x002E3 - 0x002E3: Reserved - * | 0x002E4 - 0x002E7: EEPROM read bits remaining + * | bit 5: Is settling occurring? + * | bits 6 - 7: Reserved + * | 0x002E3 - 0x002E3: EEPROM read bits remaining + * | 0x002E4 - 0x002E7: Settling cycles remaining * | 0x002E8 - 0x002EB: EEPROM read address * | 0x002EC - 0x002EF: EEPROM write address * | 0x002F0 - 0x002F1: Flash settling sector - * | 0x002F2 - 0x002F3: Flash settling remaining + * | 0x002F2 - 0x002F3: Reserved * 0x002F4 - 0x002FF: Prefetch * | 0x002F4 - 0x002F7: GBA BIOS bus prefetch * | 0x002F8 - 0x002FB: CPU prefecth (decode slot)@@ -220,6 +221,7 @@
DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t); DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2); DECL_BIT(GBASerializedSavedataFlags, FlashBank, 4); +DECL_BIT(GBASerializedSavedataFlags, DustSettling, 5); DECL_BITFIELD(GBASerializedMiscFlags, uint32_t); DECL_BIT(GBASerializedMiscFlags, Halted, 0);@@ -298,12 +300,12 @@ struct {
uint8_t type; uint8_t command; GBASerializedSavedataFlags flags; - uint8_t reserved; - int32_t readBitsRemaining; + int8_t readBitsRemaining; + uint32_t settlingDust; uint32_t readAddress; uint32_t writeAddress; uint16_t settlingSector; - uint16_t settlingDust; + uint16_t reserved; } savedata; uint32_t biosPrefetch;
@@ -61,7 +61,9 @@ gba->sync = 0;
GBAInterruptHandlerInit(&gba->cpu->irqh); GBAMemoryInit(gba); - GBASavedataInit(&gba->memory.savedata, 0); + + gba->memory.savedata.timing = &gba->timing; + GBASavedataInit(&gba->memory.savedata, NULL); gba->video.p = gba; GBAVideoInit(&gba->video);
@@ -21,9 +21,9 @@ // Some games may vary anywhere between about 2000 cycles to up to 30000 cycles. (Observed on a Macronix (09C2) chip).
// Other games vary from very little, with a fairly solid 20500 cycle count. (Observed on a SST (D4BF) chip). // An average estimation is as follows. #define FLASH_ERASE_CYCLES 30000 -#define FLASH_PROGRAM_CYCLES 18000 +#define FLASH_PROGRAM_CYCLES 650 // This needs real testing, and is only an estimation currently -#define EEPROM_SETTLE_CYCLES 1450 +#define EEPROM_SETTLE_CYCLES 115000 #define CLEANUP_THRESHOLD 15 mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata");@@ -32,6 +32,13 @@ static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
static void _flashErase(struct GBASavedata* savedata); static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart); +static void _ashesToAshes(struct mTiming* timing, void* user, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(user); + UNUSED(cyclesLate); + // Funk to funky +} + void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { savedata->type = SAVEDATA_AUTODETECT; savedata->data = 0;@@ -42,6 +49,10 @@ savedata->realVf = vf;
savedata->mapMode = MAP_WRITE; savedata->dirty = 0; savedata->dirtAge = 0; + savedata->dust.name = "GBA Savedata Settling"; + savedata->dust.priority = 0x70; + savedata->dust.context = savedata; + savedata->dust.callback = _ashesToAshes; } void GBASavedataDeinit(struct GBASavedata* savedata) {@@ -237,7 +248,6 @@ savedata->data = savedata->vf->map(savedata->vf, flashSize, savedata->mapMode);
} savedata->currentBank = savedata->data; - savedata->dust = 0; savedata->realisticTiming = realisticTiming; if (end < SIZE_CART_FLASH512) { memset(&savedata->data[end], 0xFF, flashSize - end);@@ -262,7 +272,6 @@ savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
} savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode); } - savedata->dust = 0; savedata->realisticTiming = realisticTiming; if (end < SIZE_CART_EEPROM) { memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);@@ -305,10 +314,7 @@ return FLASH_MFG_SANYO >> (address * 8);
} } } - if (savedata->dust > 0 && (address >> 12) == savedata->settling) { - // Give some overhead for waitstates and the comparison - // This estimation can probably be improved - savedata->dust -= 5000; + if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) { return 0x5F; } return savedata->currentBank[address];@@ -323,7 +329,8 @@ savedata->dirty |= SAVEDATA_DIRT_NEW;
savedata->currentBank[address] = value; savedata->command = FLASH_COMMAND_NONE; if (savedata->realisticTiming) { - savedata->dust = FLASH_PROGRAM_CYCLES; + mTimingDeschedule(savedata->timing, &savedata->dust); + mTimingSchedule(savedata->timing, &savedata->dust, FLASH_PROGRAM_CYCLES); } break; case FLASH_COMMAND_SWITCH_BANK:@@ -433,7 +440,8 @@ current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
savedata->dirty |= SAVEDATA_DIRT_NEW; savedata->data[savedata->writeAddress >> 3] = current; if (savedata->realisticTiming) { - savedata->dust = EEPROM_SETTLE_CYCLES; + mTimingDeschedule(savedata->timing, &savedata->dust); + mTimingSchedule(savedata->timing, &savedata->dust, EEPROM_SETTLE_CYCLES); } ++savedata->writeAddress; } else {@@ -457,12 +465,9 @@ }
uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) { if (savedata->command != EEPROM_COMMAND_READ) { - if (!savedata->realisticTiming || savedata->dust <= 0) { + if (!savedata->realisticTiming || !mTimingIsScheduled(savedata->timing, &savedata->dust)) { return 1; } else { - // Give some overhead for waitstates and the comparison - // This estimation can probably be improved - --savedata->dust; return 0; } }@@ -513,12 +518,18 @@ state->savedata.command = savedata->command;
GBASerializedSavedataFlags flags = 0; flags = GBASerializedSavedataFlagsSetFlashState(flags, savedata->flashState); flags = GBASerializedSavedataFlagsTestFillFlashBank(flags, savedata->currentBank == &savedata->data[0x10000]); + + if (mTimingIsScheduled(savedata->timing, &savedata->dust)) { + STORE_32(savedata->dust.when - mTimingCurrentTime(savedata->timing), 0, &state->savedata.settlingDust); + flags = GBASerializedSavedataFlagsFillDustSettling(flags); + } + state->savedata.flags = flags; - STORE_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining); + state->savedata.readBitsRemaining = savedata->readBitsRemaining; STORE_32(savedata->readAddress, 0, &state->savedata.readAddress); STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress); STORE_16(savedata->settling, 0, &state->savedata.settlingSector); - STORE_16(savedata->dust, 0, &state->savedata.settlingDust); + } void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state) {@@ -529,14 +540,19 @@ }
savedata->command = state->savedata.command; GBASerializedSavedataFlags flags = state->savedata.flags; savedata->flashState = GBASerializedSavedataFlagsGetFlashState(flags); - LOAD_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining); + savedata->readBitsRemaining = state->savedata.readBitsRemaining; LOAD_32(savedata->readAddress, 0, &state->savedata.readAddress); LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress); LOAD_16(savedata->settling, 0, &state->savedata.settlingSector); - LOAD_16(savedata->dust, 0, &state->savedata.settlingDust); if (savedata->type == SAVEDATA_FLASH1M) { _flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags)); + } + + if (GBASerializedSavedataFlagsIsDustSettling(flags)) { + uint32_t when; + LOAD_32(when, 0, &state->savedata.settlingDust); + mTimingSchedule(savedata->timing, &savedata->dust, when); } }@@ -571,7 +587,8 @@ mLOG(GBA_SAVE, DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart);
} savedata->settling = sectorStart >> 12; if (savedata->realisticTiming) { - savedata->dust = FLASH_ERASE_CYCLES; + mTimingDeschedule(savedata->timing, &savedata->dust); + mTimingSchedule(savedata->timing, &savedata->dust, FLASH_ERASE_CYCLES); } memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size); }