GBA Savedata: Transition saving to new timoing system

This commit is contained in:
Vicki Pfau 2017-01-27 16:36:01 -08:00
parent 243c2b330f
commit 46782f16a0
4 changed files with 52 additions and 29 deletions

View File

@ -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 @@ struct GBASavedata {
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;

View File

@ -177,13 +177,14 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | 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(GBASerializedHWFlags3, uint16_t);
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 GBASerializedState {
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;

View File

@ -61,7 +61,9 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
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);

View File

@ -21,9 +21,9 @@
// 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 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* 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 @@ void GBASavedataInitFlash(struct GBASavedata* savedata, bool realisticTiming) {
}
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 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata, bool realisticTiming) {
}
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 @@ uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
}
}
}
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 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8
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 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
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 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
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 @@ void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializ
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,15 +540,20 @@ void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerial
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);
}
}
void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
@ -571,7 +587,8 @@ void _flashEraseSector(struct GBASavedata* savedata, uint16_t 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);
}