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.
This commit is contained in:
Vicki Pfau 2024-06-25 03:13:33 -07:00
parent e8bfaa210a
commit 1a3873da67
4 changed files with 26 additions and 3 deletions

View File

@ -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:

View File

@ -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];

View File

@ -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);

View File

@ -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);