mirror of https://github.com/mgba-emu/mgba.git
GBA: Restore savestates
This commit is contained in:
parent
61378f3926
commit
adf20bea09
|
@ -327,33 +327,44 @@ static void _sample(struct GBAAudio* audio) {
|
|||
|
||||
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 GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
|
|||
|
||||
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);
|
||||
|
|
|
@ -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 @@
|
|||
#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 @@ struct GBAExtdataHeader {
|
|||
};
|
||||
|
||||
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 GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
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) {
|
||||
|
|
|
@ -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 "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 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | 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 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | 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 @@ mLOG_DECLARE_CATEGORY(GBA_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 @@ struct GBASerializedState {
|
|||
|
||||
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];
|
||||
|
|
Loading…
Reference in New Issue