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
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue