all repos — mgba @ 46782f16a06d03de5fc85a15193f2238b9dca661

mGBA Game Boy Advance Emulator

GBA Savedata: Transition saving to new timoing system
Vicki Pfau vi@endrift.com
Fri, 27 Jan 2017 16:36:01 -0800
commit

46782f16a06d03de5fc85a15193f2238b9dca661

parent

243c2b330f87388a9645801de5828bed00761165

M include/mgba/internal/gba/savedata.hinclude/mgba/internal/gba/savedata.h

@@ -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;
M include/mgba/internal/gba/serialize.hinclude/mgba/internal/gba/serialize.h

@@ -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;
M src/gba/gba.csrc/gba/gba.c

@@ -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);
M src/gba/savedata.csrc/gba/savedata.c

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