mirror of https://github.com/mgba-emu/mgba.git
GBA: Restore savestates
This commit is contained in:
parent
81416863a5
commit
9aa6d8fe3c
|
@ -891,6 +891,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
||||||
uint32_t ch4Flags = 0;
|
uint32_t ch4Flags = 0;
|
||||||
|
|
||||||
flags = GBSerializedAudioFlagsSetFrame(flags, audio->frame);
|
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 = GBSerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume);
|
||||||
flags = GBSerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead);
|
flags = GBSerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead);
|
||||||
|
@ -901,6 +902,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
||||||
ch1Flags = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep);
|
ch1Flags = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep);
|
||||||
ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency);
|
ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency);
|
||||||
STORE_32LE(ch1Flags, 0, &state->ch1.envelope);
|
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 = GBSerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume);
|
||||||
flags = GBSerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead);
|
flags = GBSerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead);
|
||||||
|
@ -908,9 +910,13 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
||||||
ch2Flags = GBSerializedAudioEnvelopeSetLength(ch2Flags, audio->ch2.control.length);
|
ch2Flags = GBSerializedAudioEnvelopeSetLength(ch2Flags, audio->ch2.control.length);
|
||||||
ch2Flags = GBSerializedAudioEnvelopeSetNextStep(ch2Flags, audio->ch2.envelope.nextStep);
|
ch2Flags = GBSerializedAudioEnvelopeSetNextStep(ch2Flags, audio->ch2.envelope.nextStep);
|
||||||
STORE_32LE(ch2Flags, 0, &state->ch2.envelope);
|
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));
|
memcpy(state->ch3.wavebanks, audio->ch3.wavedata32, sizeof(state->ch3.wavebanks));
|
||||||
STORE_16LE(audio->ch3.length, 0, &state->ch3.length);
|
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 = GBSerializedAudioFlagsSetCh4Volume(flags, audio->ch4.envelope.currentVolume);
|
||||||
flags = GBSerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead);
|
flags = GBSerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead);
|
||||||
|
@ -918,6 +924,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
||||||
ch4Flags = GBSerializedAudioEnvelopeSetLength(ch4Flags, audio->ch4.length);
|
ch4Flags = GBSerializedAudioEnvelopeSetLength(ch4Flags, audio->ch4.length);
|
||||||
ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep);
|
ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep);
|
||||||
STORE_32LE(ch4Flags, 0, &state->ch4.envelope);
|
STORE_32LE(ch4Flags, 0, &state->ch4.envelope);
|
||||||
|
STORE_32LE(audio->ch4Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch4.nextEvent);
|
||||||
|
|
||||||
STORE_32LE(flags, 0, flagsOut);
|
STORE_32LE(flags, 0, flagsOut);
|
||||||
}
|
}
|
||||||
|
@ -927,6 +934,7 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
||||||
uint32_t ch1Flags = 0;
|
uint32_t ch1Flags = 0;
|
||||||
uint32_t ch2Flags = 0;
|
uint32_t ch2Flags = 0;
|
||||||
uint32_t ch4Flags = 0;
|
uint32_t ch4Flags = 0;
|
||||||
|
uint32_t when;
|
||||||
|
|
||||||
audio->playingCh1 = !!(*audio->nr52 & 0x0001);
|
audio->playingCh1 = !!(*audio->nr52 & 0x0001);
|
||||||
audio->playingCh2 = !!(*audio->nr52 & 0x0002);
|
audio->playingCh2 = !!(*audio->nr52 & 0x0002);
|
||||||
|
@ -944,6 +952,11 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
||||||
audio->ch1.control.length = GBSerializedAudioEnvelopeGetLength(ch1Flags);
|
audio->ch1.control.length = GBSerializedAudioEnvelopeGetLength(ch1Flags);
|
||||||
audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags);
|
audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags);
|
||||||
audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(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);
|
LOAD_32LE(ch2Flags, 0, &state->ch2.envelope);
|
||||||
audio->ch2.envelope.currentVolume = GBSerializedAudioFlagsGetCh2Volume(flags);
|
audio->ch2.envelope.currentVolume = GBSerializedAudioFlagsGetCh2Volume(flags);
|
||||||
|
@ -951,11 +964,25 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
||||||
audio->ch2.control.hi = GBSerializedAudioFlagsGetCh2Hi(flags);
|
audio->ch2.control.hi = GBSerializedAudioFlagsGetCh2Hi(flags);
|
||||||
audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags);
|
audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags);
|
||||||
audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(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);
|
audio->ch3.readable = GBSerializedAudioFlagsGetCh3Readable(flags);
|
||||||
// TODO: Big endian?
|
// TODO: Big endian?
|
||||||
memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32));
|
memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32));
|
||||||
LOAD_16LE(audio->ch3.length, 0, &state->ch3.length);
|
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);
|
LOAD_32LE(ch4Flags, 0, &state->ch4.envelope);
|
||||||
audio->ch4.envelope.currentVolume = GBSerializedAudioFlagsGetCh4Volume(flags);
|
audio->ch4.envelope.currentVolume = GBSerializedAudioFlagsGetCh4Volume(flags);
|
||||||
|
@ -963,12 +990,22 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
||||||
audio->ch4.length = GBSerializedAudioEnvelopeGetLength(ch4Flags);
|
audio->ch4.length = GBSerializedAudioEnvelopeGetLength(ch4Flags);
|
||||||
audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags);
|
audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags);
|
||||||
LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr);
|
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) {
|
void GBAudioSerialize(const struct GBAudio* audio, struct GBSerializedState* state) {
|
||||||
GBAudioPSGSerialize(audio, &state->audio.psg, &state->audio.flags);
|
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) {
|
void GBAudioDeserialize(struct GBAudio* audio, const struct GBSerializedState* state) {
|
||||||
GBAudioPSGDeserialize(audio, &state->audio.psg, &state->audio.flags);
|
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 GBSerializedState {
|
||||||
struct {
|
struct {
|
||||||
struct GBSerializedPSGState psg;
|
struct GBSerializedPSGState psg;
|
||||||
GBSerializedAudioFlags flags;
|
GBSerializedAudioFlags flags;
|
||||||
int32_t nextEvent;
|
int32_t reserved[2];
|
||||||
int32_t eventDiff;
|
|
||||||
int32_t nextSample;
|
int32_t nextSample;
|
||||||
} audio;
|
} audio;
|
||||||
|
|
||||||
|
|
|
@ -320,6 +320,7 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
|
||||||
CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB));
|
CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB));
|
||||||
uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo);
|
uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo);
|
||||||
STORE_32(fifoSize, 0, &state->audio.fifoSize);
|
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) {
|
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) {
|
||||||
|
@ -337,6 +338,11 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
|
||||||
CircleBufferWrite8(&audio->chA.fifo, state->audio.fifoA[i]);
|
CircleBufferWrite8(&audio->chA.fifo, state->audio.fifoA[i]);
|
||||||
CircleBufferWrite8(&audio->chB.fifo, state->audio.fifoB[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) {
|
float GBAAudioCalculateRatio(float inputSampleRate, float desiredFPS, float desiredSampleRate) {
|
||||||
|
|
17
src/gba/io.c
17
src/gba/io.c
|
@ -912,13 +912,14 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
|
||||||
STORE_16(gba->memory.io[(REG_DMA0CNT_LO + i * 12) >> 1], (REG_DMA0CNT_LO + i * 12), state->io);
|
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].reload, 0, &state->timers[i].reload);
|
||||||
STORE_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload);
|
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].overflowInterval, 0, &state->timers[i].overflowInterval);
|
||||||
STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags);
|
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].nextSource, 0, &state->dma[i].nextSource);
|
||||||
STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
|
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].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);
|
GBAHardwareSerialize(&gba->memory.hw, state);
|
||||||
|
@ -936,6 +937,7 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t when;
|
||||||
for (i = 0; i < 4; ++i) {
|
for (i = 0; i < 4; ++i) {
|
||||||
LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload);
|
LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload);
|
||||||
LOAD_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload);
|
LOAD_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload);
|
||||||
|
@ -945,13 +947,20 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||||
// Overwrite invalid values in savestate
|
// Overwrite invalid values in savestate
|
||||||
gba->timers[i].lastEvent = 0;
|
gba->timers[i].lastEvent = 0;
|
||||||
} else {
|
} 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_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].nextSource, 0, &state->dma[i].nextSource);
|
||||||
LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
|
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].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) {
|
if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) {
|
||||||
GBADMASchedule(gba, i, &gba->memory.dma[i]);
|
GBADMASchedule(gba, i, &gba->memory.dma[i]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
|
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");
|
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_SAVESTATE_MAGIC + GBA_SAVESTATE_VERSION, 0, &state->versionMagic);
|
||||||
STORE_32(gba->biosChecksum, 0, &state->biosChecksum);
|
STORE_32(gba->biosChecksum, 0, &state->biosChecksum);
|
||||||
STORE_32(gba->romCrc32, 0, &state->romCrc32);
|
STORE_32(gba->romCrc32, 0, &state->romCrc32);
|
||||||
|
STORE_32(gba->timing.masterCycles, 0, &state->masterCycles);
|
||||||
|
|
||||||
if (gba->memory.rom) {
|
if (gba->memory.rom) {
|
||||||
state->id = ((struct GBACartridge*) gba->memory.rom)->id;
|
state->id = ((struct GBACartridge*) gba->memory.rom)->id;
|
||||||
|
@ -144,11 +145,6 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||||
mLOG(GBA_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
|
mLOG(GBA_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
|
||||||
error = true;
|
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);
|
LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
|
||||||
int region = (check >> BASE_OFFSET);
|
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) {
|
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 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||||
if (error) {
|
if (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles);
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < 16; ++i) {
|
for (i = 0; i < 16; ++i) {
|
||||||
LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);
|
LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);
|
||||||
|
|
|
@ -64,8 +64,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||||
* 0x0018C - 0x001AB: Audio FIFO 1
|
* 0x0018C - 0x001AB: Audio FIFO 1
|
||||||
* 0x001AC - 0x001CB: Audio FIFO 2
|
* 0x001AC - 0x001CB: Audio FIFO 2
|
||||||
* 0x001CC - 0x001DF: Audio miscellaneous state
|
* 0x001CC - 0x001DF: Audio miscellaneous state
|
||||||
* | 0x001CC - 0x001CF: Next event
|
* | 0x001CC - 0x001D3: Reserved
|
||||||
* | 0x001D0 - 0x001D3: Event diff
|
|
||||||
* | 0x001D4 - 0x001D7: Next sample
|
* | 0x001D4 - 0x001D7: Next sample
|
||||||
* | 0x001D8 - 0x001DB: FIFO size
|
* | 0x001D8 - 0x001DB: FIFO size
|
||||||
* | TODO: Fix this, they're in big-endian order, but field is little-endian
|
* | TODO: Fix this, they're in big-endian order, but field is little-endian
|
||||||
|
@ -90,12 +89,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||||
* | bits 6 - 7: Reserved
|
* | bits 6 - 7: Reserved
|
||||||
* 0x001E0 - 0x001FF: Video miscellaneous state
|
* 0x001E0 - 0x001FF: Video miscellaneous state
|
||||||
* | 0x001E0 - 0x001E3: Next event
|
* | 0x001E0 - 0x001E3: Next event
|
||||||
* | 0x001E4 - 0x001E7: Event diff
|
* | 0x001E4 - 0x001FB: Reserved
|
||||||
* | 0x001E8 - 0x001EB: Last hblank
|
|
||||||
* | 0x001EC - 0x001EF: Next hblank
|
|
||||||
* | 0x001F0 - 0x001F3: Next hblank IRQ
|
|
||||||
* | 0x001F4 - 0x001F7: Next vblank IRQ
|
|
||||||
* | 0x001F8 - 0x001FB: Next vcounter IRQ
|
|
||||||
* | 0x001FC - 0x001FF: Frame counter
|
* | 0x001FC - 0x001FF: Frame counter
|
||||||
* 0x00200 - 0x00213: Timer 0
|
* 0x00200 - 0x00213: Timer 0
|
||||||
* | 0x00200 - 0x00201: Reload value
|
* | 0x00200 - 0x00201: Reload value
|
||||||
|
@ -232,7 +226,7 @@ struct GBASerializedState {
|
||||||
uint32_t versionMagic;
|
uint32_t versionMagic;
|
||||||
uint32_t biosChecksum;
|
uint32_t biosChecksum;
|
||||||
uint32_t romCrc32;
|
uint32_t romCrc32;
|
||||||
uint32_t reservedHeader;
|
uint32_t masterCycles;
|
||||||
|
|
||||||
char title[12];
|
char title[12];
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
@ -253,8 +247,7 @@ struct GBASerializedState {
|
||||||
struct GBSerializedPSGState psg;
|
struct GBSerializedPSGState psg;
|
||||||
uint8_t fifoA[32];
|
uint8_t fifoA[32];
|
||||||
uint8_t fifoB[32];
|
uint8_t fifoB[32];
|
||||||
int32_t nextEvent;
|
int32_t reserved[2];
|
||||||
int32_t eventDiff;
|
|
||||||
int32_t nextSample;
|
int32_t nextSample;
|
||||||
uint32_t fifoSize;
|
uint32_t fifoSize;
|
||||||
GBSerializedAudioFlags flags;
|
GBSerializedAudioFlags flags;
|
||||||
|
@ -262,22 +255,24 @@ struct GBASerializedState {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int32_t nextEvent;
|
int32_t nextEvent;
|
||||||
int32_t eventDiff;
|
int32_t reserved[6];
|
||||||
int32_t lastHblank;
|
|
||||||
int32_t nextHblank;
|
|
||||||
int32_t nextHblankIRQ;
|
|
||||||
int32_t nextVblankIRQ;
|
|
||||||
int32_t nextVcounterIRQ;
|
|
||||||
int32_t frameCounter;
|
int32_t frameCounter;
|
||||||
} video;
|
} 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 {
|
struct {
|
||||||
uint32_t nextSource;
|
uint32_t nextSource;
|
||||||
uint32_t nextDest;
|
uint32_t nextDest;
|
||||||
int32_t nextCount;
|
int32_t nextCount;
|
||||||
int32_t nextEvent;
|
int32_t when;
|
||||||
} dma[4];
|
} dma[4];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
@ -131,6 +131,7 @@ void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
|
||||||
currentTimer->oldReload = currentTimer->reload;
|
currentTimer->oldReload = currentTimer->reload;
|
||||||
currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles;
|
currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles;
|
||||||
} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
|
} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
|
||||||
|
mTimingDeschedule(&gba->timing, ¤tTimer->event);
|
||||||
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
|
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
|
||||||
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale);
|
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*
|
||||||
memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
|
memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
|
||||||
memcpy(state->oam, video->oam.raw, SIZE_OAM);
|
memcpy(state->oam, video->oam.raw, SIZE_OAM);
|
||||||
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
|
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);
|
STORE_32(video->frameCounter, 0, &state->video.frameCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,6 +316,18 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
|
||||||
GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0);
|
GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0);
|
||||||
}
|
}
|
||||||
LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
|
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);
|
LOAD_16(video->vcount, REG_VCOUNT, state->io);
|
||||||
video->renderer->reset(video->renderer);
|
video->renderer->reset(video->renderer);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue