From 1a3873da671958901e1ea26cbef7e501f7761af9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 25 Jun 2024 03:13:33 -0700 Subject: [PATCH] GBA Serialize: Fix some minor save state edge cases There are two edge cases fixed in this commit. The first one involves audio FIFO DMA state not being properly updated if the game reconfigured it between save and load. This doesn't happen often, but it could conceivably affect custom sound engines. The second case is the extremely rare case of a save state being taken directly after a DMA ending but before an open bus read. The chances of this happening are negligible, but it's still a bug regargless. --- CHANGES | 1 + include/mgba/internal/gba/serialize.h | 14 +++++++++++--- src/gba/audio.c | 12 ++++++++++++ src/gba/io.c | 2 ++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 6119069f3..132d5ae44 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Emulation fixes: - GBA: Add baseline CP0 (Wii U VC) and CP1 (DCC) implementations - GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141) - GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309) + - GBA Serialize: Fix some minor save state edge cases - GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 25bd170d6..b31fd0f5a 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -20,7 +20,7 @@ extern MGBA_EXPORT const uint32_t GBASavestateVersion; mLOG_DECLARE_CATEGORY(GBA_STATE); /* Savestate format: - * 0x00000 - 0x00003: Version Magic (0x01000006) + * 0x00000 - 0x00003: Version Magic (0x01000007) * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) * 0x00008 - 0x0000B: ROM CRC32 * 0x0000C - 0x0000F: Master cycles @@ -107,6 +107,9 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | 0x001E0 - 0x001E3: Last sample * | 0x001E4 - 0x001E7: Additional audio flags * | bits 0 - 3: Current sample index + * | bits 4 - 5: Channel A DMA source + * | bits 6 - 7: Channel B DMA source + * | bits 8 - 31: Reserved * | 0x001E8 - 0x001EF: Reserved * 0x001F0 - 0x001FF: Video miscellaneous state * | 0x001F0 - 0x001F3: Reserved @@ -232,7 +235,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * 0x00370 - 0x0037F: Audio FIFO A samples * 0x00380 - 0x0038F: Audio FIFO B samples * 0x00390 - 0x003CF: Audio rendered samples - * 0x003D0 - 0x003FF: Reserved (leave zero) + * 0x003D0 - 0x003D3: Memory bus value + * 0x003D4 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM @@ -250,6 +254,8 @@ DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3); DECL_BITFIELD(GBASerializedAudioFlags2, uint32_t); DECL_BITS(GBASerializedAudioFlags2, SampleIndex, 0, 4); +DECL_BITS(GBASerializedAudioFlags2, ChASource, 4, 2); +DECL_BITS(GBASerializedAudioFlags2, ChBSource, 6, 2); DECL_BITFIELD(GBASerializedVideoFlags, uint32_t); DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2); @@ -405,7 +411,9 @@ struct GBASerializedState { struct mStereoSample currentSamples[GBA_MAX_SAMPLES]; - uint32_t reserved[12]; + uint32_t bus; + + uint32_t reserved[11]; uint16_t io[GBA_SIZE_IO >> 1]; uint16_t pram[GBA_SIZE_PALETTE_RAM >> 1]; diff --git a/src/gba/audio.c b/src/gba/audio.c index e6b495382..aca2f2765 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -475,6 +475,10 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* GBASerializedAudioFlags2 flags2 = 0; flags2 = GBASerializedAudioFlags2SetSampleIndex(flags2, audio->sampleIndex); + // This flag was introduced in 0.11 and will only ever be 0, 1 or 2, so we + // add 1 and use a non-zero value to mark its presence in the state file + flags2 = GBASerializedAudioFlags2SetChASource(flags2, audio->chA.dmaSource + 1); + flags2 = GBASerializedAudioFlags2SetChBSource(flags2, audio->chB.dmaSource + 1); STORE_32(flags2, 0, &state->audio.gbaFlags2); STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample); @@ -526,6 +530,14 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState GBASerializedAudioFlags2 flags2; LOAD_32(flags2, 0, &state->audio.gbaFlags2); audio->sampleIndex = GBASerializedAudioFlags2GetSampleIndex(flags2); + // This flag was introduced in 0.11 and will only ever be 0, 1 or 2, so we + // add 1 and use a non-zero value to mark its presence in the state file + if (GBASerializedAudioFlags2GetChASource(flags2) > 0) { + audio->chA.dmaSource = GBASerializedAudioFlags2GetChASource(flags2) - 1; + } + if (GBASerializedAudioFlags2GetChBSource(flags2) > 0) { + audio->chB.dmaSource = GBASerializedAudioFlags2GetChBSource(flags2) - 1; + } uint32_t when; LOAD_32(when, 0, &state->audio.nextSample); diff --git a/src/gba/io.c b/src/gba/io.c index 184146511..397eb9253 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -941,6 +941,7 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { STORE_32(gba->memory.dmaTransferRegister, 0, &state->dmaTransferRegister); STORE_32(gba->dmaPC, 0, &state->dmaBlockPC); + STORE_32(gba->bus, 0, &state->bus); GBAHardwareSerialize(&gba->memory.hw, state); } @@ -987,6 +988,7 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { LOAD_32(gba->memory.dmaTransferRegister, 0, &state->dmaTransferRegister); LOAD_32(gba->dmaPC, 0, &state->dmaBlockPC); + LOAD_32(gba->bus, 0, &state->bus); GBADMAUpdate(gba); GBAHardwareDeserialize(&gba->memory.hw, state);