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; uint32_t ch4Flags = 0;
flags = GBSerializedAudioFlagsSetFrame(flags, audio->frame); 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 = GBSerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume);
flags = GBSerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead); 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 = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep);
ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency); ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency);
STORE_32LE(ch1Flags, 0, &state->ch1.envelope); 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 = GBSerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume);
flags = GBSerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead); 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 = GBSerializedAudioEnvelopeSetLength(ch2Flags, audio->ch2.control.length);
ch2Flags = GBSerializedAudioEnvelopeSetNextStep(ch2Flags, audio->ch2.envelope.nextStep); ch2Flags = GBSerializedAudioEnvelopeSetNextStep(ch2Flags, audio->ch2.envelope.nextStep);
STORE_32LE(ch2Flags, 0, &state->ch2.envelope); 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)); memcpy(state->ch3.wavebanks, audio->ch3.wavedata32, sizeof(state->ch3.wavebanks));
STORE_16LE(audio->ch3.length, 0, &state->ch3.length); 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 = GBSerializedAudioFlagsSetCh4Volume(flags, audio->ch4.envelope.currentVolume);
flags = GBSerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead); 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 = GBSerializedAudioEnvelopeSetLength(ch4Flags, audio->ch4.length);
ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep); ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep);
STORE_32LE(ch4Flags, 0, &state->ch4.envelope); STORE_32LE(ch4Flags, 0, &state->ch4.envelope);
STORE_32LE(audio->ch4Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch4.nextEvent);
STORE_32LE(flags, 0, flagsOut); STORE_32LE(flags, 0, flagsOut);
} }
@ -927,6 +934,7 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
uint32_t ch1Flags = 0; uint32_t ch1Flags = 0;
uint32_t ch2Flags = 0; uint32_t ch2Flags = 0;
uint32_t ch4Flags = 0; uint32_t ch4Flags = 0;
uint32_t when;
audio->playingCh1 = !!(*audio->nr52 & 0x0001); audio->playingCh1 = !!(*audio->nr52 & 0x0001);
audio->playingCh2 = !!(*audio->nr52 & 0x0002); 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.control.length = GBSerializedAudioEnvelopeGetLength(ch1Flags);
audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags); audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags);
audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(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); LOAD_32LE(ch2Flags, 0, &state->ch2.envelope);
audio->ch2.envelope.currentVolume = GBSerializedAudioFlagsGetCh2Volume(flags); 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.hi = GBSerializedAudioFlagsGetCh2Hi(flags);
audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags); audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags);
audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(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); audio->ch3.readable = GBSerializedAudioFlagsGetCh3Readable(flags);
// TODO: Big endian? // TODO: Big endian?
memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32)); memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32));
LOAD_16LE(audio->ch3.length, 0, &state->ch3.length); 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); LOAD_32LE(ch4Flags, 0, &state->ch4.envelope);
audio->ch4.envelope.currentVolume = GBSerializedAudioFlagsGetCh4Volume(flags); 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.length = GBSerializedAudioEnvelopeGetLength(ch4Flags);
audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags); audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags);
LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr); 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) { void GBAudioSerialize(const struct GBAudio* audio, struct GBSerializedState* state) {
GBAudioPSGSerialize(audio, &state->audio.psg, &state->audio.flags); 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) { void GBAudioDeserialize(struct GBAudio* audio, const struct GBSerializedState* state) {
GBAudioPSGDeserialize(audio, &state->audio.psg, &state->audio.flags); 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 {
struct GBSerializedPSGState psg; struct GBSerializedPSGState psg;
GBSerializedAudioFlags flags; GBSerializedAudioFlags flags;
int32_t nextEvent; int32_t reserved[2];
int32_t eventDiff;
int32_t nextSample; int32_t nextSample;
} audio; } 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)); CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB));
uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo); uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo);
STORE_32(fifoSize, 0, &state->audio.fifoSize); 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) { 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->chA.fifo, state->audio.fifoA[i]);
CircleBufferWrite8(&audio->chB.fifo, state->audio.fifoB[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) { 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->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].reload, 0, &state->timers[i].reload);
STORE_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload); 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].overflowInterval, 0, &state->timers[i].overflowInterval);
STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags); 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].nextSource, 0, &state->dma[i].nextSource);
STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest); 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].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); 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) { for (i = 0; i < 4; ++i) {
LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload); LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload);
LOAD_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload); 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 // Overwrite invalid values in savestate
gba->timers[i].lastEvent = 0; gba->timers[i].lastEvent = 0;
} else { } 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_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].nextSource, 0, &state->dma[i].nextSource);
LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest); 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].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) { if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) {
GBADMASchedule(gba, i, &gba->memory.dma[i]); GBADMASchedule(gba, i, &gba->memory.dma[i]);
} }

View File

@ -23,7 +23,7 @@
#endif #endif
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; 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"); 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_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, 0, &state->versionMagic);
STORE_32(gba->biosChecksum, 0, &state->biosChecksum); STORE_32(gba->biosChecksum, 0, &state->biosChecksum);
STORE_32(gba->romCrc32, 0, &state->romCrc32); STORE_32(gba->romCrc32, 0, &state->romCrc32);
STORE_32(gba->timing.masterCycles, 0, &state->masterCycles);
if (gba->memory.rom) { if (gba->memory.rom) {
state->id = ((struct GBACartridge*) gba->memory.rom)->id; 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"); mLOG(GBA_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
error = true; 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); LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
int region = (check >> BASE_OFFSET); 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) { 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) { if (error) {
return false; return false;
} }
LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles);
size_t i; size_t i;
for (i = 0; i < 16; ++i) { for (i = 0; i < 16; ++i) {
LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs); 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 * 0x0018C - 0x001AB: Audio FIFO 1
* 0x001AC - 0x001CB: Audio FIFO 2 * 0x001AC - 0x001CB: Audio FIFO 2
* 0x001CC - 0x001DF: Audio miscellaneous state * 0x001CC - 0x001DF: Audio miscellaneous state
* | 0x001CC - 0x001CF: Next event * | 0x001CC - 0x001D3: Reserved
* | 0x001D0 - 0x001D3: Event diff
* | 0x001D4 - 0x001D7: Next sample * | 0x001D4 - 0x001D7: Next sample
* | 0x001D8 - 0x001DB: FIFO size * | 0x001D8 - 0x001DB: FIFO size
* | TODO: Fix this, they're in big-endian order, but field is little-endian * | 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 * | bits 6 - 7: Reserved
* 0x001E0 - 0x001FF: Video miscellaneous state * 0x001E0 - 0x001FF: Video miscellaneous state
* | 0x001E0 - 0x001E3: Next event * | 0x001E0 - 0x001E3: Next event
* | 0x001E4 - 0x001E7: Event diff * | 0x001E4 - 0x001FB: Reserved
* | 0x001E8 - 0x001EB: Last hblank
* | 0x001EC - 0x001EF: Next hblank
* | 0x001F0 - 0x001F3: Next hblank IRQ
* | 0x001F4 - 0x001F7: Next vblank IRQ
* | 0x001F8 - 0x001FB: Next vcounter IRQ
* | 0x001FC - 0x001FF: Frame counter * | 0x001FC - 0x001FF: Frame counter
* 0x00200 - 0x00213: Timer 0 * 0x00200 - 0x00213: Timer 0
* | 0x00200 - 0x00201: Reload value * | 0x00200 - 0x00201: Reload value
@ -232,7 +226,7 @@ struct GBASerializedState {
uint32_t versionMagic; uint32_t versionMagic;
uint32_t biosChecksum; uint32_t biosChecksum;
uint32_t romCrc32; uint32_t romCrc32;
uint32_t reservedHeader; uint32_t masterCycles;
char title[12]; char title[12];
uint32_t id; uint32_t id;
@ -253,8 +247,7 @@ struct GBASerializedState {
struct GBSerializedPSGState psg; struct GBSerializedPSGState psg;
uint8_t fifoA[32]; uint8_t fifoA[32];
uint8_t fifoB[32]; uint8_t fifoB[32];
int32_t nextEvent; int32_t reserved[2];
int32_t eventDiff;
int32_t nextSample; int32_t nextSample;
uint32_t fifoSize; uint32_t fifoSize;
GBSerializedAudioFlags flags; GBSerializedAudioFlags flags;
@ -262,22 +255,24 @@ struct GBASerializedState {
struct { struct {
int32_t nextEvent; int32_t nextEvent;
int32_t eventDiff; int32_t reserved[6];
int32_t lastHblank;
int32_t nextHblank;
int32_t nextHblankIRQ;
int32_t nextVblankIRQ;
int32_t nextVcounterIRQ;
int32_t frameCounter; int32_t frameCounter;
} video; } 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 { struct {
uint32_t nextSource; uint32_t nextSource;
uint32_t nextDest; uint32_t nextDest;
int32_t nextCount; int32_t nextCount;
int32_t nextEvent; int32_t when;
} dma[4]; } dma[4];
struct { struct {

View File

@ -131,6 +131,7 @@ void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
currentTimer->oldReload = currentTimer->reload; currentTimer->oldReload = currentTimer->reload;
currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles; currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles;
} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) { } else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event);
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale); 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->vram, video->renderer->vram, SIZE_VRAM);
memcpy(state->oam, video->oam.raw, SIZE_OAM); memcpy(state->oam, video->oam.raw, SIZE_OAM);
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); 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); 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); GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0);
} }
LOAD_32(video->frameCounter, 0, &state->video.frameCounter); 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); LOAD_16(video->vcount, REG_VCOUNT, state->io);
video->renderer->reset(video->renderer); video->renderer->reset(video->renderer);
} }