GBA: Restore savestates
Jeffrey Pfau jeffrey@endrift.com
Sat, 13 Feb 2016 01:00:24 -0800
3 files changed,
94 insertions(+),
49 deletions(-)
M
src/gba/audio.c
→
src/gba/audio.c
@@ -327,33 +327,44 @@ }
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) { uint32_t flags = 0; + uint32_t ch1Flags = 0; + uint32_t ch2Flags = 0; + uint32_t ch4Flags = 0; + + flags = GBASerializedAudioFlagsSetFrame(flags, audio->psg.frame); flags = GBASerializedAudioFlagsSetCh1Volume(flags, audio->psg.ch1.envelope.currentVolume); flags = GBASerializedAudioFlagsSetCh1Dead(flags, audio->psg.ch1.envelope.dead); flags = GBASerializedAudioFlagsSetCh1Hi(flags, audio->psg.ch1.control.hi); - /*STORE_32(audio->psg.ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep); - STORE_32(audio->psg.ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep); - STORE_32(audio->psg.ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep); - STORE_32(audio->psg.ch1.control.endTime, 0, &state->audio.ch1.endTime);*/ + flags = GBASerializedAudioFlagsSetCh1SweepEnabled(flags, audio->psg.ch1.sweepEnable); + flags = GBASerializedAudioFlagsSetCh1SweepOccurred(flags, audio->psg.ch1.sweepOccurred); + ch1Flags = GBASerializedAudioEnvelopeSetLength(ch1Flags, audio->psg.ch1.control.length); + ch1Flags = GBASerializedAudioEnvelopeSetNextStep(ch1Flags, audio->psg.ch1.envelope.nextStep); + ch1Flags = GBASerializedAudioEnvelopeSetFrequency(ch1Flags, audio->psg.ch1.realFrequency); + STORE_32(ch1Flags, 0, &state->audio.ch1.envelope); + STORE_32(audio->psg.nextFrame, 0, &state->audio.ch1.nextFrame); STORE_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent); flags = GBASerializedAudioFlagsSetCh2Volume(flags, audio->psg.ch2.envelope.currentVolume); flags = GBASerializedAudioFlagsSetCh2Dead(flags, audio->psg.ch2.envelope.dead); flags = GBASerializedAudioFlagsSetCh2Hi(flags, audio->psg.ch2.control.hi); - /*STORE_32(audio->psg.ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep); - STORE_32(audio->psg.ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep); - STORE_32(audio->psg.ch2.control.endTime, 0, &state->audio.ch2.endTime);*/ + ch2Flags = GBASerializedAudioEnvelopeSetLength(ch2Flags, audio->psg.ch2.control.length); + ch2Flags = GBASerializedAudioEnvelopeSetNextStep(ch2Flags, audio->psg.ch2.envelope.nextStep); + STORE_32(ch2Flags, 0, &state->audio.ch2.envelope); STORE_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent); memcpy(state->audio.ch3.wavebanks, audio->psg.ch3.wavedata, sizeof(state->audio.ch3.wavebanks)); - //STORE_32(audio->psg.ch3.endTime, 0, &state->audio.ch3.endTime); + STORE_16(audio->psg.ch3.length, 0, &state->audio.ch3.length); STORE_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent); + flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->psg.ch4.envelope.currentVolume); + flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->psg.ch4.envelope.dead); state->audio.flags = GBASerializedAudioFlagsSetCh4Volume(flags, audio->psg.ch4.envelope.currentVolume); state->audio.flags = GBASerializedAudioFlagsSetCh4Dead(flags, audio->psg.ch4.envelope.dead); - STORE_32(audio->psg.ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep); STORE_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr); - //STORE_32(audio->psg.ch4.endTime, 0, &state->audio.ch4.endTime); + ch4Flags = GBASerializedAudioEnvelopeSetLength(ch4Flags, audio->psg.ch4.length); + ch4Flags = GBASerializedAudioEnvelopeSetNextStep(ch4Flags, audio->psg.ch4.envelope.nextStep); + STORE_32(ch4Flags, 0, &state->audio.ch4.envelope); STORE_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent); STORE_32(flags, 0, &state->audio.flags);@@ -370,34 +381,42 @@ }
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) { uint32_t flags; + uint32_t ch1Flags = 0; + uint32_t ch2Flags = 0; + uint32_t ch4Flags = 0; + LOAD_32(flags, 0, &state->audio.flags); + LOAD_32(ch1Flags, 0, &state->audio.ch1.envelope); audio->psg.ch1.envelope.currentVolume = GBASerializedAudioFlagsGetCh1Volume(flags); audio->psg.ch1.envelope.dead = GBASerializedAudioFlagsGetCh1Dead(flags); audio->psg.ch1.control.hi = GBASerializedAudioFlagsGetCh1Hi(flags); - LOAD_32(audio->psg.ch1.envelope.nextStep, 0, &state->audio.ch1.envelopeNextStep); - /*LOAD_32(audio->psg.ch1.control.nextStep, 0, &state->audio.ch1.waveNextStep); - LOAD_32(audio->psg.ch1.nextSweep, 0, &state->audio.ch1.sweepNextStep); - LOAD_32(audio->psg.ch1.control.endTime, 0, &state->audio.ch1.endTime);*/ + audio->psg.ch1.sweepEnable = GBASerializedAudioFlagsGetCh1SweepEnabled(flags); + audio->psg.ch1.sweepOccurred = GBASerializedAudioFlagsGetCh1SweepOccurred(flags); + audio->psg.ch1.control.length = GBASerializedAudioEnvelopeGetLength(ch1Flags); + audio->psg.ch1.envelope.nextStep = GBASerializedAudioEnvelopeGetNextStep(ch1Flags); + audio->psg.ch1.realFrequency = GBASerializedAudioEnvelopeGetFrequency(ch1Flags); + LOAD_32(audio->psg.nextFrame, 0, &state->audio.ch1.nextFrame); LOAD_32(audio->psg.nextCh1, 0, &state->audio.ch1.nextEvent); + LOAD_32(ch2Flags, 0, &state->audio.ch1.envelope); audio->psg.ch2.envelope.currentVolume = GBASerializedAudioFlagsGetCh2Volume(flags); audio->psg.ch2.envelope.dead = GBASerializedAudioFlagsGetCh2Dead(flags); audio->psg.ch2.control.hi = GBASerializedAudioFlagsGetCh2Hi(flags); - LOAD_32(audio->psg.ch2.envelope.nextStep, 0, &state->audio.ch2.envelopeNextStep); - /*LOAD_32(audio->psg.ch2.control.nextStep, 0, &state->audio.ch2.waveNextStep); - LOAD_32(audio->psg.ch2.control.endTime, 0, &state->audio.ch2.endTime);*/ + audio->psg.ch2.control.length = GBASerializedAudioEnvelopeGetLength(ch2Flags); + audio->psg.ch2.envelope.nextStep = GBASerializedAudioEnvelopeGetNextStep(ch2Flags); LOAD_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent); // TODO: Big endian? memcpy(audio->psg.ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->psg.ch3.wavedata)); - //LOAD_32(audio->psg.ch3.endTime, 0, &state->audio.ch3.endTime); + LOAD_16(audio->psg.ch3.length, 0, &state->audio.ch3.length); LOAD_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent); + LOAD_32(ch4Flags, 0, &state->audio.ch1.envelope); audio->psg.ch4.envelope.currentVolume = GBASerializedAudioFlagsGetCh4Volume(flags); audio->psg.ch4.envelope.dead = GBASerializedAudioFlagsGetCh4Dead(flags); - LOAD_32(audio->psg.ch4.envelope.nextStep, 0, &state->audio.ch4.envelopeNextStep); + audio->psg.ch4.length = GBASerializedAudioEnvelopeGetLength(ch4Flags); + audio->psg.ch4.envelope.nextStep = GBASerializedAudioEnvelopeGetNextStep(ch4Flags); LOAD_32(audio->psg.ch4.lfsr, 0, &state->audio.ch4.lfsr); - //LOAD_32(audio->psg.ch4.endTime, 0, &state->audio.ch4.endTime); LOAD_32(audio->psg.nextCh4, 0, &state->audio.ch4.nextEvent); CircleBufferClear(&audio->chA.fifo);
M
src/gba/serialize.c
→
src/gba/serialize.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2016 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this@@ -25,6 +25,7 @@ #include <zlib.h>
#endif const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; +const uint32_t GBA_SAVESTATE_VERSION = 0x00000001; mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate");@@ -40,7 +41,7 @@ int64_t offset;
}; void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { - STORE_32(GBA_SAVESTATE_MAGIC, 0, &state->versionMagic); + 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);@@ -97,9 +98,14 @@ bool error = false;
int32_t check; uint32_t ucheck; LOAD_32(ucheck, 0, &state->versionMagic); - if (ucheck != GBA_SAVESTATE_MAGIC) { - mLOG(GBA_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC, ucheck); + if (ucheck > GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION) { + mLOG(GBA_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck); + error = true; + } else if (ucheck < GBA_SAVESTATE_MAGIC) { + mLOG(GBA_STATE, WARN, "Invalid savestate: expected %08X, got %08X", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck); error = true; + } else { + mLOG(GBA_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GBA_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, ucheck); } LOAD_32(ucheck, 0, &state->biosChecksum); if (ucheck != gba->biosChecksum) {
M
src/gba/serialize.h
→
src/gba/serialize.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2016 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this@@ -12,11 +12,12 @@ #include "core/core.h"
#include "gba/gba.h" extern const uint32_t GBA_SAVESTATE_MAGIC; +extern const uint32_t GBA_SAVESTATE_VERSION; mLOG_DECLARE_CATEGORY(GBA_STATE); /* Savestate format: - * 0x00000 - 0x00003: Version Magic (0x01000000) + * 0x00000 - 0x00003: Version Magic (0x01000001) * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) * 0x00008 - 0x0000B: ROM CRC32 * 0x0000C - 0x0000F: Reserved (leave zero)@@ -30,25 +31,34 @@ * | 0x00068 - 0x0006B: Cycles since last event
* | 0x0006C - 0x0006F: Cycles until next event * | 0x00070 - 0x00117: Banked registers * | 0x00118 - 0x0012F: Banked SPSRs - * 0x00130 - 0x00143: Audio channel 1 state - * | 0x00130 - 0x00133: Next envelope step - * | 0x00134 - 0x00137: Next square wave step - * | 0x00138 - 0x0013B: Next sweep step - * | 0x0013C - 0x0013F: Channel end cycle + * 0x00130 - 0x00143: Audio channel 1/framer state + * | 0x00130 - 0x00133: Envelepe timing + * | bits 0 - 6: Remaining length + * | bits 7 - 9: Next step + * | bits 10 - 20: Shadow frequency register + * | bits 21 - 31: Reserved + * | 0x00134 - 0x00137: Next frame + * | 0x00138 - 0x0013F: Reserved * | 0x00140 - 0x00143: Next event * 0x00144 - 0x00153: Audio channel 2 state - * | 0x00144 - 0x00147: Next envelope step - * | 0x00148 - 0x0014B: Next square wave step - * | 0x0014C - 0x0014F: Channel end cycle + * | 0x00144 - 0x00147: Envelepe timing + * | bits 0 - 2: Remaining length + * | bits 3 - 5: Next step + * | bits 6 - 31: Reserved + * | 0x00148 - 0x0014F: Reserved * | 0x00150 - 0x00153: Next event * 0x00154 - 0x0017B: Audio channel 3 state * | 0x00154 - 0x00173: Wave banks - * | 0x00174 - 0x00177: Channel end cycle + * | 0x00174 - 0x00175: Remaining length + * | 0x00176 - 0x00177: Reserved * | 0x00178 - 0x0017B: Next event * 0x0017C - 0x0018B: Audio channel 4 state * | 0x0017C - 0x0017F: Linear feedback shift register state - * | 0x00180 - 0x00183: Next enveleope step - * | 0x00184 - 0x00187: Channel end cycle + * | 0x00180 - 0x00183: Envelepe timing + * | bits 0 - 2: Remaining length + * | bits 3 - 5: Next step + * | bits 6 - 31: Reserved + * | 0x00184 - 0x00187: Reserved * | 0x00188 - 0x0018B: Next event * 0x0018C - 0x001AB: Audio FIFO 1 * 0x001AC - 0x001CB: Audio FIFO 2@@ -57,11 +67,13 @@ * | 0x001CC - 0x001CF: Next event
* | 0x001D0 - 0x001D3: Event diff * | 0x001D4 - 0x001D7: Next sample * | 0x001D8 - 0x001DB: FIFO size + * | TODO: Fix this, they're in big-endian order, but field is little-endian * | 0x001DC - 0x001DC: Channel 1 envelope state * | bits 0 - 3: Current volume * | bit 4: Is dead? * | bit 5: Is high? - * | bits 6 - 7: Reserved + * | bit 6: Is sweep enabled? + * | bit 7: Has sweep occurred? * | 0x001DD - 0x001DD: Channel 2 envelope state * | bits 0 - 3: Current volume * | bit 4: Is dead?@@ -71,7 +83,8 @@ * | 0x001DE - 0x001DE: Channel 4 envelope state
* | bits 0 - 3: Current volume * | bit 4: Is dead? * | bits 5 - 7: Reserved - * | 0x001DF - 0x001DF: Reserved + * | 0x001DF - 0x001DF: Miscellaneous audio flags + * | bits 0 - 3: Current frame * 0x001E0 - 0x001FF: Video miscellaneous state * | 0x001E0 - 0x001E3: Next event * | 0x001E4 - 0x001E7: Event diff@@ -193,11 +206,19 @@ DECL_BITFIELD(GBASerializedAudioFlags, uint32_t);
DECL_BITS(GBASerializedAudioFlags, Ch1Volume, 0, 4); DECL_BIT(GBASerializedAudioFlags, Ch1Dead, 4); DECL_BIT(GBASerializedAudioFlags, Ch1Hi, 5); +DECL_BIT(GBASerializedAudioFlags, Ch1SweepEnabled, 6); +DECL_BIT(GBASerializedAudioFlags, Ch1SweepOccurred, 7); DECL_BITS(GBASerializedAudioFlags, Ch2Volume, 8, 4); DECL_BIT(GBASerializedAudioFlags, Ch2Dead, 12); DECL_BIT(GBASerializedAudioFlags, Ch2Hi, 13); DECL_BITS(GBASerializedAudioFlags, Ch4Volume, 16, 4); DECL_BIT(GBASerializedAudioFlags, Ch4Dead, 20); +DECL_BITS(GBASerializedAudioFlags, Frame, 21, 3); + +DECL_BITFIELD(GBASerializedAudioEnvelope, uint32_t); +DECL_BITS(GBASerializedAudioEnvelope, Length, 0, 7); +DECL_BITS(GBASerializedAudioEnvelope, NextStep, 7, 3); +DECL_BITS(GBASerializedAudioEnvelope, Frequency, 10, 11); DECL_BITFIELD(GBASerializedHWFlags1, uint16_t); DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0);@@ -239,27 +260,26 @@ } cpu;
struct { struct { - int32_t envelopeNextStep; - int32_t waveNextStep; - int32_t sweepNextStep; - int32_t endTime; + GBASerializedAudioEnvelope envelope; + int32_t nextFrame; + int32_t reserved[2]; int32_t nextEvent; } ch1; struct { - int32_t envelopeNextStep; - int32_t waveNextStep; - int32_t endTime; + GBASerializedAudioEnvelope envelope; + int32_t reserved[2]; int32_t nextEvent; } ch2; struct { uint32_t wavebanks[8]; - int32_t endTime; + int16_t length; + int16_t reserved; int32_t nextEvent; } ch3; struct { int32_t lfsr; - int32_t envelopeNextStep; - int32_t endTime; + GBASerializedAudioEnvelope envelope; + int32_t reserved; int32_t nextEvent; } ch4; uint8_t fifoA[32];