GB: Restore savestates

This commit is contained in:
Jeffrey Pfau 2016-12-19 19:40:16 -08:00
parent 9aa6d8fe3c
commit 0bf0975a5d
8 changed files with 122 additions and 31 deletions

View File

@ -54,6 +54,19 @@ void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent* event) {
}
}
bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent* event) {
struct mTimingEvent* const* previous = &timing->root;
const struct mTimingEvent* next = timing->root;
while (next) {
if (next == event) {
return true;
}
previous = &next->next;
next = next->next;
}
return false;
}
int32_t mTimingTick(struct mTiming* timing, int32_t cycles) {
timing->masterCycles += cycles;
uint32_t masterCycles = timing->masterCycles;
@ -69,7 +82,7 @@ int32_t mTimingTick(struct mTiming* timing, int32_t cycles) {
return *timing->nextEvent;
}
int32_t mTimingCurrentTime(struct mTiming* timing) {
int32_t mTimingCurrentTime(const struct mTiming* timing) {
return timing->masterCycles + *timing->relativeCycles;
}

View File

@ -32,8 +32,9 @@ void mTimingDeinit(struct mTiming* timing);
void mTimingClear(struct mTiming* timing);
void mTimingSchedule(struct mTiming* timing, struct mTimingEvent*, int32_t when);
void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent*);
bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent*);
int32_t mTimingTick(struct mTiming* timing, int32_t cycles);
int32_t mTimingCurrentTime(struct mTiming* timing);
int32_t mTimingCurrentTime(const struct mTiming* timing);
int32_t mTimingNextEvent(struct mTiming* timing);
#endif

View File

@ -592,6 +592,9 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
state->memory.dmaRemaining = memory->dmaRemaining;
memcpy(state->memory.rtcRegs, memory->rtcRegs, sizeof(state->memory.rtcRegs));
STORE_32LE(memory->dmaEvent.when - mTimingCurrentTime(&gb->timing), 0, &state->memory.dmaNext);
STORE_32LE(memory->hdmaEvent.when - mTimingCurrentTime(&gb->timing), 0, &state->memory.hdmaNext);
GBSerializedMemoryFlags flags = 0;
flags = GBSerializedMemoryFlagsSetSramAccess(flags, memory->sramAccess);
flags = GBSerializedMemoryFlagsSetRtcAccess(flags, memory->rtcAccess);
@ -624,6 +627,18 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
memory->dmaRemaining = state->memory.dmaRemaining;
memcpy(memory->rtcRegs, state->memory.rtcRegs, sizeof(state->memory.rtcRegs));
uint32_t when;
LOAD_32LE(when, 0, &state->memory.dmaNext);
mTimingDeschedule(&gb->timing, &memory->dmaEvent);
if (memory->dmaRemaining) {
mTimingSchedule(&gb->timing, &memory->dmaEvent, when);
}
LOAD_32LE(when, 0, &state->memory.hdmaNext);
mTimingDeschedule(&gb->timing, &memory->hdmaEvent);
if (memory->hdmaRemaining) {
mTimingSchedule(&gb->timing, &memory->hdmaEvent, when);
}
GBSerializedMemoryFlags flags;
LOAD_16LE(flags, 0, &state->memory.flags);
memory->sramAccess = GBSerializedMemoryFlagsGetSramAccess(flags);

View File

@ -17,11 +17,12 @@ mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate");
#endif
const uint32_t GB_SAVESTATE_MAGIC = 0x00400000;
const uint32_t GB_SAVESTATE_VERSION = 0x00000000;
const uint32_t GB_SAVESTATE_VERSION = 0x00000001;
void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
STORE_32LE(GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, 0, &state->versionMagic);
STORE_32LE(gb->romCrc32, 0, &state->romCrc32);
STORE_32LE(gb->timing.masterCycles, 0, &state->masterCycles);
if (gb->memory.rom) {
memcpy(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title));
@ -54,7 +55,9 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
flags = GBSerializedCpuFlagsSetCondition(flags, gb->cpu->condition);
flags = GBSerializedCpuFlagsSetIrqPending(flags, gb->cpu->irqPending);
flags = GBSerializedCpuFlagsSetDoubleSpeed(flags, gb->doubleSpeed);
flags = GBSerializedCpuFlagsSetEiPending(flags, mTimingIsScheduled(&gb->timing, &gb->eiPending));
STORE_32LE(flags, 0, &state->cpu.flags);
STORE_32LE(gb->eiPending.when - mTimingCurrentTime(&gb->timing), 0, &state->cpu.eiPending);
GBMemorySerialize(gb, state);
GBIOSerialize(gb, state);
@ -120,11 +123,6 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
error = true;
}
LOAD_32LE(check, 0, &state->video.eventDiff);
if (check < 0) {
mLOG(GB_STATE, WARN, "Savestate is corrupted: video eventDiff is negative");
error = true;
}
LOAD_16LE(check16, 0, &state->video.x);
if (check16 < 0 || check16 > GB_VIDEO_HORIZONTAL_PIXELS) {
mLOG(GB_STATE, WARN, "Savestate is corrupted: video x is out of range");
@ -175,8 +173,16 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags);
gb->audio.timingFactor = gb->doubleSpeed + 1;
uint32_t when;
LOAD_32LE(when, 0, &state->cpu.eiPending);
mTimingDeschedule(&gb->timing, &gb->eiPending);
if (GBSerializedCpuFlagsIsEiPending(flags)) {
mTimingSchedule(&gb->timing, &gb->eiPending, when);
}
LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent);
LOAD_32LE(gb->timing.masterCycles, 0, &state->masterCycles);
gb->model = state->model;

View File

@ -20,7 +20,8 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* 0x00000 - 0x00003: Version Magic (0x01000001)
* 0x00004 - 0x00007: ROM CRC32
* 0x00008: Game Boy model
* 0x00009 - 0x0000F: Reserved (leave zero)
* 0x00009 - 0x0000B: Reserved (leave zero)
* 0x0000C - 0x0000F: Master cycles
* 0x00010 - 0x0001F: Game title/code (e.g. PM_CRYSTALBYTE)
* 0x00020 - 0x00047: CPU state:
* | 0x00020: A register
@ -46,7 +47,8 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | bit 0: Is condition met?
* | bit 1: Is condition IRQ pending?
* | bit 2: Double speed
* | bits 3 - 31: Reserved
* | bit 3: Is EI pending?
* | bits 4 - 31: Reserved
* 0x00048 - 0x0005B: Audio channel 1/framer state
* | 0x00048 - 0x0004B: Envelepe timing
* | bits 0 - 6: Remaining length
@ -99,14 +101,13 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | bit 5: Has channel 1 sweep occurred?
* | bit 6: Is channel 3's memory readable?
* | bit 7: Reserved
* | 0x000A8 - 0x000AB: Next event
* | 0x000AC - 0x000AF: Event diff
* | 0x000A8 - 0x000AF: Rserved
* | 0x000B0 - 0x000B3: Next sample
* 0x000B4 - 0x000153: Video state
* | 0x000B4 - 0x000B5: Current x
* | 0x000B6 - 0x000B7: Current y (ly)
* | 0x000B8 - 0x000BB: Next event
* | 0x000BC - 0x000BF: Event diff
* | 0x000B8 - 0x000BB: Next frame
* | 0x000BC - 0x000BF: Reserved
* | 0x000C0 - 0x000C3: Next mode
* | 0x000C4 - 0x000C7: Dot cycle counter
* | 0x000C8 - 0x000CB: Frame counter
@ -122,7 +123,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | 0x000D4 - 0x00153: Palette entries
* 0x00154 - 0x000167: Timer state
* | 0x00154 - 0x00157: Next event
* | 0x00158 - 0x0015B: Event diff
* | 0x00158 - 0x0015B: Next IRQ
* | 0x0015C - 0x0015F: Next DIV
* | 0x00160 - 0x00163: Inernal DIV
* | 0x00164: TIMA period
@ -187,7 +188,7 @@ struct GBSerializedPSGState {
int32_t nextFrame;
int32_t nextCh3Fade;
int32_t reserved;
int32_t nextEvent;
uint32_t nextEvent;
} ch1;
struct {
GBSerializedAudioEnvelope envelope;
@ -198,13 +199,13 @@ struct GBSerializedPSGState {
uint32_t wavebanks[8];
int16_t length;
int16_t reserved;
int32_t nextEvent;
uint32_t nextEvent;
} ch3;
struct {
int32_t lfsr;
GBSerializedAudioEnvelope envelope;
int32_t reserved;
int32_t nextEvent;
uint32_t nextEvent;
} ch4;
};
@ -212,6 +213,7 @@ DECL_BITFIELD(GBSerializedCpuFlags, uint32_t);
DECL_BIT(GBSerializedCpuFlags, Condition, 0);
DECL_BIT(GBSerializedCpuFlags, IrqPending, 1);
DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2);
DECL_BIT(GBSerializedCpuFlags, EiPending, 1);
DECL_BITFIELD(GBSerializedTimerFlags, uint8_t);
DECL_BIT(GBSerializedTimerFlags, IrqPending, 0);
@ -220,6 +222,8 @@ DECL_BITFIELD(GBSerializedVideoFlags, uint8_t);
DECL_BIT(GBSerializedVideoFlags, BcpIncrement, 0);
DECL_BIT(GBSerializedVideoFlags, OcpIncrement, 1);
DECL_BITS(GBSerializedVideoFlags, Mode, 2, 2);
DECL_BIT(GBSerializedVideoFlags, NotModeEventScheduled, 4);
DECL_BIT(GBSerializedVideoFlags, NotFrameEventScheduled, 5);
DECL_BITFIELD(GBSerializedMBC7Flags, uint8_t);
DECL_BITS(GBSerializedMBC7Flags, Command, 0, 2);
@ -238,7 +242,8 @@ struct GBSerializedState {
uint32_t versionMagic;
uint32_t romCrc32;
uint8_t model;
uint8_t reservedHeader[7];
uint8_t reservedHeader[3];
uint32_t masterCycles;
char title[16];
@ -264,7 +269,7 @@ struct GBSerializedState {
uint16_t irqVector;
int32_t eiPending;
uint32_t eiPending;
int32_t reservedDiPending;
GBSerializedCpuFlags flags;
} cpu;
@ -273,21 +278,21 @@ struct GBSerializedState {
struct GBSerializedPSGState psg;
GBSerializedAudioFlags flags;
int32_t reserved[2];
int32_t nextSample;
uint32_t nextSample;
} audio;
struct {
int16_t x;
int16_t ly;
int32_t nextEvent;
int32_t eventDiff;
int32_t nextMode;
uint32_t nextFrame;
uint32_t reserved;
uint32_t nextMode;
int32_t dotCounter;
int32_t frameCounter;
uint8_t vramCurrentBank;
GBSerializedVideoFlags flags;
uint16_t reserved;
uint16_t reserved2;
uint16_t bcpIndex;
uint16_t ocpIndex;
@ -296,10 +301,10 @@ struct GBSerializedState {
} video;
struct {
int32_t nextEvent;
int32_t eventDiff;
uint32_t nextEvent;
uint32_t nextIRQ;
int32_t nextDiv;
uint32_t nextDiv;
uint32_t internalDiv;
uint8_t timaPeriod;
GBSerializedTimerFlags flags;
@ -311,11 +316,11 @@ struct GBSerializedState {
uint8_t wramCurrentBank;
uint8_t sramCurrentBank;
int32_t dmaNext;
uint32_t dmaNext;
uint16_t dmaSource;
uint16_t dmaDest;
int32_t hdmaNext;
uint32_t hdmaNext;
uint16_t hdmaSource;
uint16_t hdmaDest;

View File

@ -96,10 +96,28 @@ void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* sta
STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
STORE_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
STORE_32LE(timer->event.when - mTimingCurrentTime(&timer->p->timing), 0, &state->timer.nextEvent);
STORE_32LE(timer->irq.when - mTimingCurrentTime(&timer->p->timing), 0, &state->timer.nextIRQ);
GBSerializedTimerFlags flags = GBSerializedTimerFlagsSetIrqPending(0, mTimingIsScheduled(&timer->p->timing, &timer->irq));
STORE_32LE(flags, 0, &state->timer.flags);
}
void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) {
LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
LOAD_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
uint32_t when;
LOAD_32LE(when, 0, &state->timer.nextEvent);
mTimingDeschedule(&timer->p->timing, &timer->event);
mTimingSchedule(&timer->p->timing, &timer->event, when);
GBSerializedTimerFlags flags;
LOAD_32LE(flags, 0, &state->timer.flags);
mTimingDeschedule(&timer->p->timing, &timer->irq);
if (GBSerializedTimerFlagsIsIrqPending(flags)) {
LOAD_32LE(when, 0, &state->timer.nextIRQ);
mTimingSchedule(&timer->p->timing, &timer->irq, when);
}
}

View File

@ -315,6 +315,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
mTimingDeschedule(&video->p->timing, &video->frameEvent);
}
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
// TODO: Fix serialization; this gets internal and visible modes out of sync
video->stat = GBRegisterSTATSetMode(video->stat, 0);
video->p->memory.io[REG_STAT] = video->stat;
video->ly = 0;
@ -498,6 +499,8 @@ void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* sta
flags = GBSerializedVideoFlagsSetBcpIncrement(flags, video->bcpIncrement);
flags = GBSerializedVideoFlagsSetOcpIncrement(flags, video->ocpIncrement);
flags = GBSerializedVideoFlagsSetMode(flags, video->mode);
flags = GBSerializedVideoFlagsSetNotModeEventScheduled(flags, !mTimingIsScheduled(&video->p->timing, &video->modeEvent));
flags = GBSerializedVideoFlagsSetNotFrameEventScheduled(flags, !mTimingIsScheduled(&video->p->timing, &video->frameEvent));
state->video.flags = flags;
STORE_16LE(video->bcpIndex, 0, &state->video.bcpIndex);
STORE_16LE(video->ocpIndex, 0, &state->video.ocpIndex);
@ -507,6 +510,9 @@ void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* sta
STORE_16LE(video->palette[i], i * 2, state->video.palette);
}
STORE_32LE(video->modeEvent.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextMode);
STORE_32LE(video->frameEvent.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextFrame);
memcpy(state->vram, video->vram, GB_SIZE_VRAM);
memcpy(state->oam, &video->oam.raw, GB_SIZE_OAM);
}
@ -526,6 +532,33 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s
LOAD_16LE(video->ocpIndex, 0, &state->video.ocpIndex);
video->ocpIndex &= 0x3F;
switch (video->mode) {
case 0:
video->modeEvent.callback = _endMode0;
break;
case 1:
video->modeEvent.callback = _endMode1;
break;
case 2:
video->modeEvent.callback = _endMode2;
break;
case 3:
video->modeEvent.callback = _endMode3;
break;
}
uint32_t when;
mTimingDeschedule(&video->p->timing, &video->modeEvent);
if (!GBSerializedVideoFlagsIsNotModeEventScheduled(flags)) {
LOAD_32LE(when, 0, &state->video.nextMode);
mTimingSchedule(&video->p->timing, &video->modeEvent, when);
}
mTimingDeschedule(&video->p->timing, &video->frameEvent);
if (!GBSerializedVideoFlagsIsNotFrameEventScheduled(flags)) {
LOAD_32LE(when, 0, &state->video.nextFrame);
mTimingSchedule(&video->p->timing, &video->frameEvent, when);
}
size_t i;
for (i = 0; i < 64; ++i) {
LOAD_16LE(video->palette[i], i * 2, state->video.palette);

View File

@ -21,7 +21,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* 0x00000 - 0x00003: Version Magic (0x01000001)
* 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS)
* 0x00008 - 0x0000B: ROM CRC32
* 0x0000C - 0x0000F: Reserved (leave zero)
* 0x0000C - 0x0000F: Master cycles
* 0x00010 - 0x0001B: Game title (e.g. METROID4USA)
* 0x0001C - 0x0001F: Game code (e.g. AMTE)
* 0x00020 - 0x0012F: CPU state: