mirror of https://github.com/mgba-emu/mgba.git
GBA Savedata: Transition saving to new timoing system
This commit is contained in:
parent
243c2b330f
commit
46782f16a0
|
@ -11,6 +11,7 @@
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
#include <mgba/core/log.h>
|
#include <mgba/core/log.h>
|
||||||
|
#include <mgba/core/timing.h>
|
||||||
|
|
||||||
mLOG_DECLARE_CATEGORY(GBA_SAVE);
|
mLOG_DECLARE_CATEGORY(GBA_SAVE);
|
||||||
|
|
||||||
|
@ -79,15 +80,16 @@ struct GBASavedata {
|
||||||
bool maskWriteback;
|
bool maskWriteback;
|
||||||
struct VFile* realVf;
|
struct VFile* realVf;
|
||||||
|
|
||||||
int32_t readBitsRemaining;
|
int8_t readBitsRemaining;
|
||||||
uint32_t readAddress;
|
uint32_t readAddress;
|
||||||
uint32_t writeAddress;
|
uint32_t writeAddress;
|
||||||
|
|
||||||
uint8_t* currentBank;
|
uint8_t* currentBank;
|
||||||
|
|
||||||
|
struct mTiming* timing;
|
||||||
bool realisticTiming;
|
bool realisticTiming;
|
||||||
unsigned settling;
|
unsigned settling;
|
||||||
int dust;
|
struct mTimingEvent dust;
|
||||||
|
|
||||||
enum SavedataDirty dirty;
|
enum SavedataDirty dirty;
|
||||||
uint32_t dirtAge;
|
uint32_t dirtAge;
|
||||||
|
|
|
@ -177,13 +177,14 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||||
* | bits 0 - 1: Flash state machine
|
* | bits 0 - 1: Flash state machine
|
||||||
* | bits 2 - 3: Reserved
|
* | bits 2 - 3: Reserved
|
||||||
* | bit 4: Flash bank
|
* | bit 4: Flash bank
|
||||||
* | bits 5 - 7: Reserved
|
* | bit 5: Is settling occurring?
|
||||||
* | 0x002E3 - 0x002E3: Reserved
|
* | bits 6 - 7: Reserved
|
||||||
* | 0x002E4 - 0x002E7: EEPROM read bits remaining
|
* | 0x002E3 - 0x002E3: EEPROM read bits remaining
|
||||||
|
* | 0x002E4 - 0x002E7: Settling cycles remaining
|
||||||
* | 0x002E8 - 0x002EB: EEPROM read address
|
* | 0x002E8 - 0x002EB: EEPROM read address
|
||||||
* | 0x002EC - 0x002EF: EEPROM write address
|
* | 0x002EC - 0x002EF: EEPROM write address
|
||||||
* | 0x002F0 - 0x002F1: Flash settling sector
|
* | 0x002F0 - 0x002F1: Flash settling sector
|
||||||
* | 0x002F2 - 0x002F3: Flash settling remaining
|
* | 0x002F2 - 0x002F3: Reserved
|
||||||
* 0x002F4 - 0x002FF: Prefetch
|
* 0x002F4 - 0x002FF: Prefetch
|
||||||
* | 0x002F4 - 0x002F7: GBA BIOS bus prefetch
|
* | 0x002F4 - 0x002F7: GBA BIOS bus prefetch
|
||||||
* | 0x002F8 - 0x002FB: CPU prefecth (decode slot)
|
* | 0x002F8 - 0x002FB: CPU prefecth (decode slot)
|
||||||
|
@ -220,6 +221,7 @@ DECL_BITFIELD(GBASerializedHWFlags3, uint16_t);
|
||||||
DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t);
|
DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t);
|
||||||
DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2);
|
DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2);
|
||||||
DECL_BIT(GBASerializedSavedataFlags, FlashBank, 4);
|
DECL_BIT(GBASerializedSavedataFlags, FlashBank, 4);
|
||||||
|
DECL_BIT(GBASerializedSavedataFlags, DustSettling, 5);
|
||||||
|
|
||||||
DECL_BITFIELD(GBASerializedMiscFlags, uint32_t);
|
DECL_BITFIELD(GBASerializedMiscFlags, uint32_t);
|
||||||
DECL_BIT(GBASerializedMiscFlags, Halted, 0);
|
DECL_BIT(GBASerializedMiscFlags, Halted, 0);
|
||||||
|
@ -298,12 +300,12 @@ struct GBASerializedState {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t command;
|
uint8_t command;
|
||||||
GBASerializedSavedataFlags flags;
|
GBASerializedSavedataFlags flags;
|
||||||
uint8_t reserved;
|
int8_t readBitsRemaining;
|
||||||
int32_t readBitsRemaining;
|
uint32_t settlingDust;
|
||||||
uint32_t readAddress;
|
uint32_t readAddress;
|
||||||
uint32_t writeAddress;
|
uint32_t writeAddress;
|
||||||
uint16_t settlingSector;
|
uint16_t settlingSector;
|
||||||
uint16_t settlingDust;
|
uint16_t reserved;
|
||||||
} savedata;
|
} savedata;
|
||||||
|
|
||||||
uint32_t biosPrefetch;
|
uint32_t biosPrefetch;
|
||||||
|
|
|
@ -61,7 +61,9 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
||||||
|
|
||||||
GBAInterruptHandlerInit(&gba->cpu->irqh);
|
GBAInterruptHandlerInit(&gba->cpu->irqh);
|
||||||
GBAMemoryInit(gba);
|
GBAMemoryInit(gba);
|
||||||
GBASavedataInit(&gba->memory.savedata, 0);
|
|
||||||
|
gba->memory.savedata.timing = &gba->timing;
|
||||||
|
GBASavedataInit(&gba->memory.savedata, NULL);
|
||||||
|
|
||||||
gba->video.p = gba;
|
gba->video.p = gba;
|
||||||
GBAVideoInit(&gba->video);
|
GBAVideoInit(&gba->video);
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
// Other games vary from very little, with a fairly solid 20500 cycle count. (Observed on a SST (D4BF) 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.
|
// An average estimation is as follows.
|
||||||
#define FLASH_ERASE_CYCLES 30000
|
#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
|
// This needs real testing, and is only an estimation currently
|
||||||
#define EEPROM_SETTLE_CYCLES 1450
|
#define EEPROM_SETTLE_CYCLES 115000
|
||||||
#define CLEANUP_THRESHOLD 15
|
#define CLEANUP_THRESHOLD 15
|
||||||
|
|
||||||
mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata");
|
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 _flashErase(struct GBASavedata* savedata);
|
||||||
static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
|
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) {
|
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
|
||||||
savedata->type = SAVEDATA_AUTODETECT;
|
savedata->type = SAVEDATA_AUTODETECT;
|
||||||
savedata->data = 0;
|
savedata->data = 0;
|
||||||
|
@ -42,6 +49,10 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
|
||||||
savedata->mapMode = MAP_WRITE;
|
savedata->mapMode = MAP_WRITE;
|
||||||
savedata->dirty = 0;
|
savedata->dirty = 0;
|
||||||
savedata->dirtAge = 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) {
|
void GBASavedataDeinit(struct GBASavedata* savedata) {
|
||||||
|
@ -237,7 +248,6 @@ void GBASavedataInitFlash(struct GBASavedata* savedata, bool realisticTiming) {
|
||||||
}
|
}
|
||||||
|
|
||||||
savedata->currentBank = savedata->data;
|
savedata->currentBank = savedata->data;
|
||||||
savedata->dust = 0;
|
|
||||||
savedata->realisticTiming = realisticTiming;
|
savedata->realisticTiming = realisticTiming;
|
||||||
if (end < SIZE_CART_FLASH512) {
|
if (end < SIZE_CART_FLASH512) {
|
||||||
memset(&savedata->data[end], 0xFF, flashSize - end);
|
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->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
||||||
}
|
}
|
||||||
savedata->dust = 0;
|
|
||||||
savedata->realisticTiming = realisticTiming;
|
savedata->realisticTiming = realisticTiming;
|
||||||
if (end < SIZE_CART_EEPROM) {
|
if (end < SIZE_CART_EEPROM) {
|
||||||
memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
|
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) {
|
if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) {
|
||||||
// Give some overhead for waitstates and the comparison
|
|
||||||
// This estimation can probably be improved
|
|
||||||
savedata->dust -= 5000;
|
|
||||||
return 0x5F;
|
return 0x5F;
|
||||||
}
|
}
|
||||||
return savedata->currentBank[address];
|
return savedata->currentBank[address];
|
||||||
|
@ -323,7 +329,8 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8
|
||||||
savedata->currentBank[address] = value;
|
savedata->currentBank[address] = value;
|
||||||
savedata->command = FLASH_COMMAND_NONE;
|
savedata->command = FLASH_COMMAND_NONE;
|
||||||
if (savedata->realisticTiming) {
|
if (savedata->realisticTiming) {
|
||||||
savedata->dust = FLASH_PROGRAM_CYCLES;
|
mTimingDeschedule(savedata->timing, &savedata->dust);
|
||||||
|
mTimingSchedule(savedata->timing, &savedata->dust, FLASH_PROGRAM_CYCLES);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FLASH_COMMAND_SWITCH_BANK:
|
case FLASH_COMMAND_SWITCH_BANK:
|
||||||
|
@ -433,7 +440,8 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
|
||||||
savedata->dirty |= SAVEDATA_DIRT_NEW;
|
savedata->dirty |= SAVEDATA_DIRT_NEW;
|
||||||
savedata->data[savedata->writeAddress >> 3] = current;
|
savedata->data[savedata->writeAddress >> 3] = current;
|
||||||
if (savedata->realisticTiming) {
|
if (savedata->realisticTiming) {
|
||||||
savedata->dust = EEPROM_SETTLE_CYCLES;
|
mTimingDeschedule(savedata->timing, &savedata->dust);
|
||||||
|
mTimingSchedule(savedata->timing, &savedata->dust, EEPROM_SETTLE_CYCLES);
|
||||||
}
|
}
|
||||||
++savedata->writeAddress;
|
++savedata->writeAddress;
|
||||||
} else {
|
} else {
|
||||||
|
@ -457,12 +465,9 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
|
||||||
|
|
||||||
uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
|
uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
|
||||||
if (savedata->command != EEPROM_COMMAND_READ) {
|
if (savedata->command != EEPROM_COMMAND_READ) {
|
||||||
if (!savedata->realisticTiming || savedata->dust <= 0) {
|
if (!savedata->realisticTiming || !mTimingIsScheduled(savedata->timing, &savedata->dust)) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
// Give some overhead for waitstates and the comparison
|
|
||||||
// This estimation can probably be improved
|
|
||||||
--savedata->dust;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -513,12 +518,18 @@ void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializ
|
||||||
GBASerializedSavedataFlags flags = 0;
|
GBASerializedSavedataFlags flags = 0;
|
||||||
flags = GBASerializedSavedataFlagsSetFlashState(flags, savedata->flashState);
|
flags = GBASerializedSavedataFlagsSetFlashState(flags, savedata->flashState);
|
||||||
flags = GBASerializedSavedataFlagsTestFillFlashBank(flags, savedata->currentBank == &savedata->data[0x10000]);
|
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;
|
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->readAddress, 0, &state->savedata.readAddress);
|
||||||
STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
|
STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
|
||||||
STORE_16(savedata->settling, 0, &state->savedata.settlingSector);
|
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) {
|
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;
|
savedata->command = state->savedata.command;
|
||||||
GBASerializedSavedataFlags flags = state->savedata.flags;
|
GBASerializedSavedataFlags flags = state->savedata.flags;
|
||||||
savedata->flashState = GBASerializedSavedataFlagsGetFlashState(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->readAddress, 0, &state->savedata.readAddress);
|
||||||
LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
|
LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
|
||||||
LOAD_16(savedata->settling, 0, &state->savedata.settlingSector);
|
LOAD_16(savedata->settling, 0, &state->savedata.settlingSector);
|
||||||
LOAD_16(savedata->dust, 0, &state->savedata.settlingDust);
|
|
||||||
|
|
||||||
if (savedata->type == SAVEDATA_FLASH1M) {
|
if (savedata->type == SAVEDATA_FLASH1M) {
|
||||||
_flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags));
|
_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) {
|
void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
|
||||||
|
@ -571,7 +587,8 @@ void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
|
||||||
}
|
}
|
||||||
savedata->settling = sectorStart >> 12;
|
savedata->settling = sectorStart >> 12;
|
||||||
if (savedata->realisticTiming) {
|
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);
|
memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue