mirror of https://github.com/mgba-emu/mgba.git
GB: Restore savestates
This commit is contained in:
parent
9aa6d8fe3c
commit
0bf0975a5d
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue