GBA: Restore savestates
@@ -891,6 +891,7 @@ uint32_t ch2Flags = 0;
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 @@ ch1Flags = GBSerializedAudioEnvelopeSetLength(ch1Flags, audio->ch1.control.length);
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 @@ flags = GBSerializedAudioFlagsSetCh2Hi(flags, audio->ch2.control.hi);
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 @@ STORE_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr);
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 @@ uint32_t flags;
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 @@ audio->ch1.sweep.occurred = GBSerializedAudioFlagsGetCh1SweepOccurred(flags);
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 @@ audio->ch2.envelope.dead = GBSerializedAudioFlagsGetCh2Dead(flags);
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 @@ audio->ch4.envelope.dead = GBSerializedAudioFlagsGetCh4Dead(flags);
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); }
@@ -272,8 +272,7 @@
struct { struct GBSerializedPSGState psg; GBSerializedAudioFlags flags; - int32_t nextEvent; - int32_t eventDiff; + int32_t reserved[2]; int32_t nextSample; } audio;
@@ -320,6 +320,7 @@ CircleBufferDump(&audio->chA.fifo, state->audio.fifoA, sizeof(state->audio.fifoA));
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 @@ for (i = 0; i < fifoSize; ++i) {
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) {
@@ -912,13 +912,14 @@ for (i = 0; i < 4; ++i) {
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 @@ GBAIOWrite(gba, i, reg);
} } + 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 @@ if (i > 0 && GBATimerFlagsIsCountUp(gba->timers[i].flags)) {
// 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]); }
@@ -23,7 +23,7 @@ #include <sys/time.h>
#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 @@ if (check >= (int32_t) GBA_ARM7TDMI_FREQUENCY) {
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 @@ }
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);
@@ -64,8 +64,7 @@ * | 0x00188 - 0x0018B: Next event
* 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 @@ * | bit 5: Has channel 1 sweep occurred?
* | 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 {
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 @@ } audio;
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 {
@@ -131,6 +131,7 @@ gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload;
currentTimer->oldReload = currentTimer->reload; currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles; } else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) { + mTimingDeschedule(&gba->timing, ¤tTimer->event); if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale); }
@@ -299,6 +299,7 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) {
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 @@ LOAD_16(value, i, state->pram);
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); }