GBA: Restore savestates

This commit is contained in:
Jeffrey Pfau 2016-12-19 18:24:24 -08:00
parent 81416863a5
commit 9aa6d8fe3c
8 changed files with 88 additions and 31 deletions

View File

@ -891,6 +891,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
uint32_t ch4Flags = 0;
flags = GBSerializedAudioFlagsSetFrame(flags, audio->frame);
STORE_32LE(audio->frameEvent.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextFrame);
flags = GBSerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume);
flags = GBSerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead);
@ -901,6 +902,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
ch1Flags = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep);
ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency);
STORE_32LE(ch1Flags, 0, &state->ch1.envelope);
STORE_32LE(audio->ch1Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextEvent);
flags = GBSerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume);
flags = GBSerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead);
@ -908,9 +910,13 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
ch2Flags = GBSerializedAudioEnvelopeSetLength(ch2Flags, audio->ch2.control.length);
ch2Flags = GBSerializedAudioEnvelopeSetNextStep(ch2Flags, audio->ch2.envelope.nextStep);
STORE_32LE(ch2Flags, 0, &state->ch2.envelope);
STORE_32LE(audio->ch2Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch2.nextEvent);
flags = GBSerializedAudioFlagsSetCh3Readable(flags, audio->ch3.readable);
memcpy(state->ch3.wavebanks, audio->ch3.wavedata32, sizeof(state->ch3.wavebanks));
STORE_16LE(audio->ch3.length, 0, &state->ch3.length);
STORE_32LE(audio->ch3Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch3.nextEvent);
STORE_32LE(audio->ch3Fade.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextCh3Fade);
flags = GBSerializedAudioFlagsSetCh4Volume(flags, audio->ch4.envelope.currentVolume);
flags = GBSerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead);
@ -918,6 +924,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
ch4Flags = GBSerializedAudioEnvelopeSetLength(ch4Flags, audio->ch4.length);
ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep);
STORE_32LE(ch4Flags, 0, &state->ch4.envelope);
STORE_32LE(audio->ch4Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch4.nextEvent);
STORE_32LE(flags, 0, flagsOut);
}
@ -927,6 +934,7 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
uint32_t ch1Flags = 0;
uint32_t ch2Flags = 0;
uint32_t ch4Flags = 0;
uint32_t when;
audio->playingCh1 = !!(*audio->nr52 & 0x0001);
audio->playingCh2 = !!(*audio->nr52 & 0x0002);
@ -944,6 +952,11 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
audio->ch1.control.length = GBSerializedAudioEnvelopeGetLength(ch1Flags);
audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags);
audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags);
LOAD_32LE(when, 0, &state->ch1.nextEvent);
mTimingDeschedule(audio->timing, &audio->ch1Event);
if (audio->ch1.envelope.dead < 2 && audio->playingCh1) {
mTimingSchedule(audio->timing, &audio->ch1Event, when);
}
LOAD_32LE(ch2Flags, 0, &state->ch2.envelope);
audio->ch2.envelope.currentVolume = GBSerializedAudioFlagsGetCh2Volume(flags);
@ -951,11 +964,25 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
audio->ch2.control.hi = GBSerializedAudioFlagsGetCh2Hi(flags);
audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags);
audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch2Flags);
LOAD_32LE(when, 0, &state->ch2.nextEvent);
mTimingDeschedule(audio->timing, &audio->ch2Event);
if (audio->ch2.envelope.dead < 2 && audio->playingCh2) {
mTimingSchedule(audio->timing, &audio->ch2Event, when);
}
audio->ch3.readable = GBSerializedAudioFlagsGetCh3Readable(flags);
// TODO: Big endian?
memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32));
LOAD_16LE(audio->ch3.length, 0, &state->ch3.length);
LOAD_32LE(when, 0, &state->ch3.nextEvent);
mTimingDeschedule(audio->timing, &audio->ch3Event);
if (audio->playingCh3) {
mTimingSchedule(audio->timing, &audio->ch3Event, when);
}
LOAD_32LE(when, 0, &state->ch1.nextCh3Fade);
if (audio->ch3.readable && audio->style == GB_AUDIO_DMG) {
mTimingSchedule(audio->timing, &audio->ch3Fade, when);
}
LOAD_32LE(ch4Flags, 0, &state->ch4.envelope);
audio->ch4.envelope.currentVolume = GBSerializedAudioFlagsGetCh4Volume(flags);
@ -963,12 +990,22 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
audio->ch4.length = GBSerializedAudioEnvelopeGetLength(ch4Flags);
audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags);
LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr);
LOAD_32LE(when, 0, &state->ch4.nextEvent);
mTimingDeschedule(audio->timing, &audio->ch4Event);
if (audio->ch4.envelope.dead < 2 && audio->playingCh4) {
mTimingSchedule(audio->timing, &audio->ch4Event, when);
}
}
void GBAudioSerialize(const struct GBAudio* audio, struct GBSerializedState* state) {
GBAudioPSGSerialize(audio, &state->audio.psg, &state->audio.flags);
STORE_32LE(audio->sampleEvent.when - mTimingCurrentTime(audio->timing), 0, &state->audio.nextSample);
}
void GBAudioDeserialize(struct GBAudio* audio, const struct GBSerializedState* state) {
GBAudioPSGDeserialize(audio, &state->audio.psg, &state->audio.flags);
uint32_t when;
LOAD_32LE(when, 0, &state->audio.nextSample);
mTimingDeschedule(audio->timing, &audio->sampleEvent);
mTimingSchedule(audio->timing, &audio->sampleEvent, when);
}

View File

@ -272,8 +272,7 @@ struct GBSerializedState {
struct {
struct GBSerializedPSGState psg;
GBSerializedAudioFlags flags;
int32_t nextEvent;
int32_t eventDiff;
int32_t reserved[2];
int32_t nextSample;
} audio;

View File

@ -320,6 +320,7 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB));
uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo);
STORE_32(fifoSize, 0, &state->audio.fifoSize);
STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample);
}
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) {
@ -337,6 +338,11 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
CircleBufferWrite8(&audio->chA.fifo, state->audio.fifoA[i]);
CircleBufferWrite8(&audio->chB.fifo, state->audio.fifoB[i]);
}
uint32_t when;
LOAD_32(when, 0, &state->audio.nextSample);
mTimingDeschedule(&audio->p->timing, &audio->sampleEvent);
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when);
}
float GBAAudioCalculateRatio(float inputSampleRate, float desiredFPS, float desiredSampleRate) {

View File

@ -912,13 +912,14 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
STORE_16(gba->memory.io[(REG_DMA0CNT_LO + i * 12) >> 1], (REG_DMA0CNT_LO + i * 12), state->io);
STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload);
STORE_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload);
STORE_32(gba->timers[i].lastEvent, 0, &state->timers[i].lastEvent);
STORE_32(gba->timers[i].lastEvent - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].lastEvent);
STORE_32(gba->timers[i].event.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextEvent);
STORE_32(gba->timers[i].overflowInterval, 0, &state->timers[i].overflowInterval);
STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags);
STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
STORE_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount);
STORE_32(gba->memory.dma[i].when, 0, &state->dma[i].nextEvent);
STORE_32(gba->memory.dma[i].when, 0, &state->dma[i].when);
}
GBAHardwareSerialize(&gba->memory.hw, state);
@ -936,6 +937,7 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
}
}
uint32_t when;
for (i = 0; i < 4; ++i) {
LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload);
LOAD_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload);
@ -945,13 +947,20 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
// Overwrite invalid values in savestate
gba->timers[i].lastEvent = 0;
} else {
LOAD_32(gba->timers[i].lastEvent, 0, &state->timers[i].lastEvent);
LOAD_32(when, 0, &state->timers[i].lastEvent);
gba->timers[i].lastEvent = when + mTimingCurrentTime(&gba->timing);
}
LOAD_32(when, 0, &state->timers[i].nextEvent);
mTimingDeschedule(&gba->timing, &gba->timers[i].event);
if (GBATimerFlagsIsEnable(gba->timers[i].flags)) {
mTimingSchedule(&gba->timing, &gba->timers[i].event, when);
}
LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io);
LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount);
LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].nextEvent);
LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when);
if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) {
GBADMASchedule(gba, i, &gba->memory.dma[i]);
}

View File

@ -23,7 +23,7 @@
#endif
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000001;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000002;
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate");
@ -36,6 +36,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
STORE_32(GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, 0, &state->versionMagic);
STORE_32(gba->biosChecksum, 0, &state->biosChecksum);
STORE_32(gba->romCrc32, 0, &state->romCrc32);
STORE_32(gba->timing.masterCycles, 0, &state->masterCycles);
if (gba->memory.rom) {
state->id = ((struct GBACartridge*) gba->memory.rom)->id;
@ -144,11 +145,6 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
mLOG(GBA_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
error = true;
}
LOAD_32(check, 0, &state->video.eventDiff);
if (check < 0) {
mLOG(GBA_STATE, WARN, "Savestate is corrupted: video eventDiff is negative");
error = true;
}
LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
int region = (check >> BASE_OFFSET);
if ((region == REGION_CART0 || region == REGION_CART1 || region == REGION_CART2) && ((check - WORD_SIZE_ARM) & SIZE_CART0) >= gba->memory.romSize - WORD_SIZE_ARM) {
@ -158,6 +154,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
if (error) {
return false;
}
LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles);
size_t i;
for (i = 0; i < 16; ++i) {
LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);

View File

@ -64,8 +64,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* 0x0018C - 0x001AB: Audio FIFO 1
* 0x001AC - 0x001CB: Audio FIFO 2
* 0x001CC - 0x001DF: Audio miscellaneous state
* | 0x001CC - 0x001CF: Next event
* | 0x001D0 - 0x001D3: Event diff
* | 0x001CC - 0x001D3: Reserved
* | 0x001D4 - 0x001D7: Next sample
* | 0x001D8 - 0x001DB: FIFO size
* | TODO: Fix this, they're in big-endian order, but field is little-endian
@ -90,12 +89,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bits 6 - 7: Reserved
* 0x001E0 - 0x001FF: Video miscellaneous state
* | 0x001E0 - 0x001E3: Next event
* | 0x001E4 - 0x001E7: Event diff
* | 0x001E8 - 0x001EB: Last hblank
* | 0x001EC - 0x001EF: Next hblank
* | 0x001F0 - 0x001F3: Next hblank IRQ
* | 0x001F4 - 0x001F7: Next vblank IRQ
* | 0x001F8 - 0x001FB: Next vcounter IRQ
* | 0x001E4 - 0x001FB: Reserved
* | 0x001FC - 0x001FF: Frame counter
* 0x00200 - 0x00213: Timer 0
* | 0x00200 - 0x00201: Reload value
@ -232,7 +226,7 @@ struct GBASerializedState {
uint32_t versionMagic;
uint32_t biosChecksum;
uint32_t romCrc32;
uint32_t reservedHeader;
uint32_t masterCycles;
char title[12];
uint32_t id;
@ -253,8 +247,7 @@ struct GBASerializedState {
struct GBSerializedPSGState psg;
uint8_t fifoA[32];
uint8_t fifoB[32];
int32_t nextEvent;
int32_t eventDiff;
int32_t reserved[2];
int32_t nextSample;
uint32_t fifoSize;
GBSerializedAudioFlags flags;
@ -262,22 +255,24 @@ struct GBASerializedState {
struct {
int32_t nextEvent;
int32_t eventDiff;
int32_t lastHblank;
int32_t nextHblank;
int32_t nextHblankIRQ;
int32_t nextVblankIRQ;
int32_t nextVcounterIRQ;
int32_t reserved[6];
int32_t frameCounter;
} video;
struct GBATimer timers[4];
struct {
uint16_t reload;
uint16_t oldReload;
uint32_t lastEvent;
uint32_t nextEvent;
int32_t overflowInterval;
GBATimerFlags flags;
} timers[4];
struct {
uint32_t nextSource;
uint32_t nextDest;
int32_t nextCount;
int32_t nextEvent;
int32_t when;
} dma[4];
struct {

View File

@ -131,6 +131,7 @@ void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
currentTimer->oldReload = currentTimer->reload;
currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles;
} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event);
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale);
}

View File

@ -299,6 +299,7 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState*
memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
memcpy(state->oam, video->oam.raw, SIZE_OAM);
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent);
STORE_32(video->frameCounter, 0, &state->video.frameCounter);
}
@ -315,6 +316,18 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0);
}
LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
uint32_t when;
LOAD_32(when, 0, &state->video.nextEvent);
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
if (GBARegisterDISPSTATIsInHblank(dispstat)) {
video->event.callback = _startHdraw;
} else {
video->event.callback = _startHblank;
}
mTimingDeschedule(&video->p->timing, &video->event);
mTimingSchedule(&video->p->timing, &video->event, when);
LOAD_16(video->vcount, REG_VCOUNT, state->io);
video->renderer->reset(video->renderer);
}