all repos — mgba @ 0bf0975a5d040bcc00741b25ebe8f18110ba08a0

mGBA Game Boy Advance Emulator

GB: Restore savestates
Jeffrey Pfau jeffrey@endrift.com
Mon, 19 Dec 2016 19:40:16 -0800
commit

0bf0975a5d040bcc00741b25ebe8f18110ba08a0

parent

9aa6d8fe3c299c0e6690b61439dff6c235df8846

M src/core/timing.csrc/core/timing.c

@@ -54,6 +54,19 @@ next = next->next;

} } +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 @@ }

return *timing->nextEvent; } -int32_t mTimingCurrentTime(struct mTiming* timing) { +int32_t mTimingCurrentTime(const struct mTiming* timing) { return timing->masterCycles + *timing->relativeCycles; }
M src/core/timing.hsrc/core/timing.h

@@ -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
M src/gb/memory.csrc/gb/memory.c

@@ -592,6 +592,9 @@ STORE_16LE(memory->hdmaRemaining, 0, &state->memory.hdmaRemaining);

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

@@ -623,6 +626,18 @@

LOAD_16LE(memory->hdmaRemaining, 0, &state->memory.hdmaRemaining); 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);
M src/gb/serialize.csrc/gb/serialize.c

@@ -17,11 +17,12 @@ #include <sys/time.h>

#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 @@ GBSerializedCpuFlags flags = 0;

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 @@ if (check >= (int32_t) DMG_LR35902_FREQUENCY) {

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 @@ gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags);

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;
M src/gb/serialize.hsrc/gb/serialize.h

@@ -20,7 +20,8 @@ /* Savestate format:

* 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 @@ * | 0x00044 - 0x00047: Flags

* | 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 @@ * | bit 4: Is channel 1 sweep enabled?

* | 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 @@ * | 0x000D1 - 0x000D3: OCP index

* | 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 @@ GBSerializedAudioEnvelope envelope;

int32_t nextFrame; int32_t nextCh3Fade; int32_t reserved; - int32_t nextEvent; + uint32_t nextEvent; } ch1; struct { GBSerializedAudioEnvelope envelope;

@@ -198,13 +199,13 @@ struct {

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 @@ uint8_t executionState;

uint16_t irqVector; - int32_t eiPending; + uint32_t eiPending; int32_t reservedDiPending; GBSerializedCpuFlags flags; } cpu;

@@ -273,21 +278,21 @@ struct {

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 @@ uint16_t palette[64];

} 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 @@ uint16_t currentBank;

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;
M src/gb/timer.csrc/gb/timer.c

@@ -96,10 +96,28 @@ void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) {

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); + } }
M src/gb/video.csrc/gb/video.c

@@ -315,6 +315,7 @@ video->p->memory.io[REG_STAT] = video->stat;

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 @@ GBSerializedVideoFlags flags = 0;

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

@@ -506,6 +509,9 @@ size_t i;

for (i = 0; i < 64; ++i) { 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);

@@ -525,6 +531,33 @@ LOAD_16LE(video->bcpIndex, 0, &state->video.bcpIndex);

video->bcpIndex &= 0x3F; 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) {
M src/gba/serialize.hsrc/gba/serialize.h

@@ -21,7 +21,7 @@ /* Savestate format:

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