From daf12994dbb999ecfcda4020ec4de86aaedd6f38 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 19 Jan 2014 01:59:35 -0800 Subject: [PATCH] Start savestates --- src/gba/gba-memory.c | 11 ++ src/gba/gba-memory.h | 4 + src/gba/gba-serialize.c | 205 ++++++++++++---------------------- src/gba/gba-serialize.h | 150 ++++++++++++++++++++++++- src/platform/sdl/sdl-events.c | 54 ++++++++- 5 files changed, 286 insertions(+), 138 deletions(-) diff --git a/src/gba/gba-memory.c b/src/gba/gba-memory.c index 8be4c2e0d..7c45a65ea 100644 --- a/src/gba/gba-memory.c +++ b/src/gba/gba-memory.c @@ -2,6 +2,7 @@ #include "gba-gpio.h" #include "gba-io.h" +#include "gba-serialize.h" #include "hle-bios.h" #include "memory.h" @@ -780,3 +781,13 @@ void GBAMemoryServiceDMA(struct GBAMemory* memory, int number, struct GBADMA* in GBAMemoryScheduleDMA(memory, number, info); } } + +void GBAMemorySerialize(struct GBAMemory* memory, struct GBASerializedState* state) { + memcpy(state->wram, memory->wram, SIZE_WORKING_RAM); + memcpy(state->iwram, memory->iwram, SIZE_WORKING_IRAM); +} + +void GBAMemoryDeserialize(struct GBAMemory* memory, struct GBASerializedState* state) { + memcpy(memory->wram, state->wram, SIZE_WORKING_RAM); + memcpy(memory->iwram, state->iwram, SIZE_WORKING_IRAM); +} diff --git a/src/gba/gba-memory.h b/src/gba/gba-memory.h index fddc98d07..98e5afe7c 100644 --- a/src/gba/gba-memory.h +++ b/src/gba/gba-memory.h @@ -157,4 +157,8 @@ void GBAMemoryServiceDMA(struct GBAMemory* memory, int number, struct GBADMA* in void GBAMemoryRunHblankDMAs(struct GBAMemory* memory); void GBAMemoryRunVblankDMAs(struct GBAMemory* memory); +struct GBASerializedState; +void GBAMemorySerialize(struct GBAMemory* memory, struct GBASerializedState* state); +void GBAMemoryDeserialize(struct GBAMemory* memory, struct GBASerializedState* state); + #endif diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index a1f88c7de..2645e0ab6 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -1,142 +1,85 @@ #include "gba-serialize.h" -/* Savestate format: - * 0x00000 - 0x00003: Version Magic (0x01000000) - * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) - * 0x00008 - 0x0000F: Reserved (leave zero) - * 0x00010 - 0x0001B: Game title (e.g. METROID4USA) - * 0x0001C - 0x0001F: Game code (e.g. AMTE) - * 0x00020 - 0x0012F: CPU state: - * | 0x00020 - 0x0005F: GPRs - * | 0x00060 - 0x00063: CPSR - * | 0x00064 - 0x00067: SPSR - * | 0x00068 - 0x0006B: Cycles since last event - * | 0x0006C - 0x0006F: Cycles until next event - * | 0x00070 - 0x00117: Banked registers - * | 0x00118 - 0x0012F: Banked SPSRs - * 0x00130 - 0x00147: Audio channel 1 state - * | 0x00130 - 0x00130: Current volume - * | 0x00131 - 0x00131: Is channel dead? - * | 0x00132 - 0x00132: Is channel high? - * | 0x00133 - 0x00133: Reserved - * | 0x00134 - 0x00137: Next envelope step - * | 0x00137 - 0x0013B: Next square wave step - * | 0x0013C - 0x0013G: Next sweep step - * | 0x00140 - 0x00143: Channel end cycle - * | 0x00144 - 0x00147: Next event - * 0x00148 - 0x0015F: Audio channel 2/4 state - * | 0x00148 - 0x00148: Current volume - * | 0x00149 - 0x00149: Is channel dead? - * | 0x0014A - 0x0014A: Is channel high? - * | 0x0014B - 0x0014B: Reserved - * | 0x0014C - 0x0014F: Next envelope step - * | 0x00150 - 0x00153: Next square wave step - * | 0x00154 - 0x00157: Audio channel 4 LFSR - * | 0x00158 - 0x0015B: Channel end cycle - * | 0x0015C - 0x0015F: Next Event - * 0x00160 - 0x0017F: Audio channel 3 wave banks - * 0x00180 - 0x0019F: Audio FIFO 1 - * 0x001A0 - 0x001BF: Audio FIFO 2 - * 0x001C0 - 0x001DF: Audio miscellaneous state - * | 0x001C0 - 0x001C3: Next event - * | 0x001C4 - 0x001C7: Event diff - * | 0x001C8 - 0x001CB: Next channel 3 event - * | 0x001CC - 0x001CF: Next channel 4 event - * | 0x001D0 - 0x001D3: Next sample - * | 0x001D4 - 0x001D7: FIFO size - * | 0x001D8 - 0x001DF: Reserved - * 0x001E0 - 0x001FF: Video miscellaneous state - * | 0x001E0 - 0x001E3: Next event - * | 0x001E4 - 0x001E7: Event diff - * | 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: Reserved - * 0x00200 - 0x003FF: Reserved (leave zero) - * 0x00400 - 0x007FF: I/O memory - * 0x00800 - 0x00BFF: Palette - * 0x00C00 - 0x00FFF: OAM - * 0x01000 - 0x18FFF: VRAM - * 0x19000 - 0x20FFF: IWRAM - * 0x21000 - 0x60FFF: WRAM - * Total size: 0x61000 (397,312) bytes - */ +#include "memory.h" -struct GBASerializedState { - uint32_t versionMagic; - uint32_t biosChecksum; - uint32_t reservedHeader[2]; +#include +#include +#include +#include +#include - char title[12]; - uint32_t id; +const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; - struct { - int32_t gprs[16]; - union PSR cpsr; - union PSR spsr; +void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { + memcpy(state->cpu.gprs, gba->cpu.gprs, sizeof(state->cpu.gprs)); + state->cpu.cpsr = gba->cpu.cpsr; + state->cpu.spsr = gba->cpu.spsr; + state->cpu.cycles = gba->cpu.cycles; + state->cpu.nextEvent = gba->cpu.nextEvent; + memcpy(state->cpu.bankedRegisters, gba->cpu.bankedRegisters, 6 * 7 * sizeof(int32_t)); + memcpy(state->cpu.bankedSPSRs, gba->cpu.bankedSPSRs, 6 * sizeof(int32_t)); - int32_t cycles; - int32_t nextEvent; + GBAMemorySerialize(&gba->memory, state); +} - int32_t bankedRegisters[6][7]; - int32_t bankedSPSRs[6]; - } cpu; +void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) { + memcpy(gba->cpu.gprs, state->cpu.gprs, sizeof(gba->cpu.gprs)); + gba->cpu.cpsr = state->cpu.cpsr; + gba->cpu.spsr = state->cpu.spsr; + gba->cpu.cycles = state->cpu.cycles; + gba->cpu.nextEvent = state->cpu.nextEvent; + memcpy(gba->cpu.bankedRegisters, state->cpu.bankedRegisters, 6 * 7 * sizeof(int32_t)); + memcpy(gba->cpu.bankedSPSRs, state->cpu.bankedSPSRs, 6 * sizeof(int32_t)); + gba->cpu.executionMode = gba->cpu.cpsr.t ? MODE_THUMB : MODE_ARM; + ARMSetPrivilegeMode(&gba->cpu, gba->cpu.cpsr.priv); + gba->cpu.memory->setActiveRegion(gba->cpu.memory, gba->cpu.gprs[ARM_PC]); - struct { - struct { - int8_t volume; - int8_t dead; - int8_t hi; - int8_t : 8; - int32_t envelopeNextStep; - int32_t waveNextStep; - int32_t sweepNextStep; - int32_t endTime; - int32_t nextEvent; - } ch1; - struct { - int8_t volume; - int8_t dead; - int8_t hi; - int8_t : 8; - int32_t envelopeNextStep; - int32_t waveNextStep; - int32_t ch4Lfsr; - int32_t endTime; - int32_t nextEvent; - } ch2; - uint32_t ch3[8]; - uint32_t fifoA[8]; - uint32_t fifoB[8]; - int32_t nextEvent; - int32_t eventDiff; - int32_t nextCh3; - int32_t nextCh4; - int32_t nextSample; - int32_t fifoSize; - int32_t : 32; - int32_t : 32; - } audio; + GBAMemoryDeserialize(&gba->memory, state); +} - struct { - int32_t nextEvent; - int32_t eventDiff; - int32_t lastHblank; - int32_t nextHblank; - int32_t nextHblankIRQ; - int32_t nextVblankIRQ; - int32_t nextVcounterIRQ; - int32_t : 32; - } video; +static int _getStateFd(struct GBA* gba, int slot) { + char path[PATH_MAX]; + path[PATH_MAX - 1] = '\0'; + snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot); + int fd = open(path, O_CREAT | O_RDWR, 0777); + if (fd >= 0) { + ftruncate(fd, sizeof(struct GBASerializedState)); + } + return fd; +} - uint32_t reservedGpio[128]; +int GBASaveState(struct GBA* gba, int slot) { + int fd = _getStateFd(gba, slot); + if (fd < 0) { + return 0; + } + struct GBASerializedState* state = GBAMapState(fd); + GBASerialize(gba, state); + GBADeallocateState(state); + close(fd); + return 1; +} - uint16_t io[SIZE_IO >> 1]; - uint16_t pram[SIZE_PALETTE_RAM >> 1]; - uint16_t oam[SIZE_PALETTE_RAM >> 1]; - uint16_t vram[SIZE_PALETTE_RAM >> 1]; - uint8_t iwram[SIZE_WORKING_IRAM]; - uint8_t wram[SIZE_WORKING_RAM]; -}; +int GBALoadState(struct GBA* gba, int slot) { + int fd = _getStateFd(gba, slot); + if (fd < 0) { + return 0; + } + struct GBASerializedState* state = GBAMapState(fd); + GBADeserialize(gba, state); + GBADeallocateState(state); + close(fd); + return 1; +} + +struct GBASerializedState* GBAMapState(int fd) { + return fileMemoryMap(fd, sizeof(struct GBASerializedState), MEMORY_WRITE); +} + +struct GBASerializedState* GBAAloocateState(void) { + return anonymousMemoryMap(sizeof(struct GBASerializedState)); +} + +void GBADeallocateState(struct GBASerializedState* state) { + mappedMemoryFree(state, sizeof(struct GBASerializedState)); +} diff --git a/src/gba/gba-serialize.h b/src/gba/gba-serialize.h index 8adaa62d5..e7f455713 100644 --- a/src/gba/gba-serialize.h +++ b/src/gba/gba-serialize.h @@ -1,6 +1,154 @@ #include "gba.h" -struct GBASerializedState; +const uint32_t GBA_SAVESTATE_MAGIC; + +/* Savestate format: + * 0x00000 - 0x00003: Version Magic (0x01000000) + * 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS) + * 0x00008 - 0x0000F: Reserved (leave zero) + * 0x00010 - 0x0001B: Game title (e.g. METROID4USA) + * 0x0001C - 0x0001F: Game code (e.g. AMTE) + * 0x00020 - 0x0012F: CPU state: + * | 0x00020 - 0x0005F: GPRs + * | 0x00060 - 0x00063: CPSR + * | 0x00064 - 0x00067: SPSR + * | 0x00068 - 0x0006B: Cycles since last event + * | 0x0006C - 0x0006F: Cycles until next event + * | 0x00070 - 0x00117: Banked registers + * | 0x00118 - 0x0012F: Banked SPSRs + * 0x00130 - 0x00147: Audio channel 1 state + * | 0x00130 - 0x00130: Current volume + * | 0x00131 - 0x00131: Is channel dead? + * | 0x00132 - 0x00132: Is channel high? + * | 0x00133 - 0x00133: Reserved + * | 0x00134 - 0x00137: Next envelope step + * | 0x00137 - 0x0013B: Next square wave step + * | 0x0013C - 0x0013G: Next sweep step + * | 0x00140 - 0x00143: Channel end cycle + * | 0x00144 - 0x00147: Next event + * 0x00148 - 0x0015F: Audio channel 2/4 state + * | 0x00148 - 0x00148: Current volume + * | 0x00149 - 0x00149: Is channel dead? + * | 0x0014A - 0x0014A: Is channel high? + * | 0x0014B - 0x0014B: Reserved + * | 0x0014C - 0x0014F: Next envelope step + * | 0x00150 - 0x00153: Next square wave step + * | 0x00154 - 0x00157: Audio channel 4 LFSR + * | 0x00158 - 0x0015B: Channel end cycle + * | 0x0015C - 0x0015F: Next Event + * 0x00160 - 0x0017F: Audio channel 3 wave banks + * 0x00180 - 0x0019F: Audio FIFO 1 + * 0x001A0 - 0x001BF: Audio FIFO 2 + * 0x001C0 - 0x001DF: Audio miscellaneous state + * | 0x001C0 - 0x001C3: Next event + * | 0x001C4 - 0x001C7: Event diff + * | 0x001C8 - 0x001CB: Next channel 3 event + * | 0x001CC - 0x001CF: Next channel 4 event + * | 0x001D0 - 0x001D3: Next sample + * | 0x001D4 - 0x001D7: FIFO size + * | 0x001D8 - 0x001DF: Reserved + * 0x001E0 - 0x001FF: Video miscellaneous state + * | 0x001E0 - 0x001E3: Next event + * | 0x001E4 - 0x001E7: Event diff + * | 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: Reserved + * 0x00200 - 0x003FF: Reserved (leave zero) + * 0x00400 - 0x007FF: I/O memory + * 0x00800 - 0x00BFF: Palette + * 0x00C00 - 0x00FFF: OAM + * 0x01000 - 0x18FFF: VRAM + * 0x19000 - 0x20FFF: IWRAM + * 0x21000 - 0x60FFF: WRAM + * Total size: 0x61000 (397,312) bytes + */ + +struct GBASerializedState { + uint32_t versionMagic; + uint32_t biosChecksum; + uint32_t reservedHeader[2]; + + char title[12]; + uint32_t id; + + struct { + int32_t gprs[16]; + union PSR cpsr; + union PSR spsr; + + int32_t cycles; + int32_t nextEvent; + + int32_t bankedRegisters[6][7]; + int32_t bankedSPSRs[6]; + } cpu; + + struct { + struct { + int8_t volume; + int8_t dead; + int8_t hi; + int8_t : 8; + int32_t envelopeNextStep; + int32_t waveNextStep; + int32_t sweepNextStep; + int32_t endTime; + int32_t nextEvent; + } ch1; + struct { + int8_t volume; + int8_t dead; + int8_t hi; + int8_t : 8; + int32_t envelopeNextStep; + int32_t waveNextStep; + int32_t ch4Lfsr; + int32_t endTime; + int32_t nextEvent; + } ch2; + uint32_t ch3[8]; + uint32_t fifoA[8]; + uint32_t fifoB[8]; + int32_t nextEvent; + int32_t eventDiff; + int32_t nextCh3; + int32_t nextCh4; + int32_t nextSample; + int32_t fifoSize; + int32_t : 32; + int32_t : 32; + } audio; + + struct { + int32_t nextEvent; + int32_t eventDiff; + int32_t lastHblank; + int32_t nextHblank; + int32_t nextHblankIRQ; + int32_t nextVblankIRQ; + int32_t nextVcounterIRQ; + int32_t : 32; + } video; + + uint32_t reservedGpio[128]; + + uint16_t io[SIZE_IO >> 1]; + uint16_t pram[SIZE_PALETTE_RAM >> 1]; + uint16_t oam[SIZE_OAM >> 1]; + uint16_t vram[SIZE_VRAM >> 1]; + uint8_t iwram[SIZE_WORKING_IRAM]; + uint8_t wram[SIZE_WORKING_RAM]; +}; void GBASerialize(struct GBA* gba, struct GBASerializedState* state); void GBADeserialize(struct GBA* gba, struct GBASerializedState* state); + +int GBASaveState(struct GBA* gba, int slot); +int GBALoadState(struct GBA* gba, int slot); + +struct GBASerializedState* GBAMapState(int fd); +struct GBASerializedState* GBAAllocateState(void); +void GBADeallocateState(struct GBASerializedState* state); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 515af493a..d7dde1251 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -2,6 +2,7 @@ #include "debugger.h" #include "gba-io.h" +#include "gba-serialize.h" #include "gba-video.h" int GBASDLInitEvents(struct GBASDLEvents* context) { @@ -62,12 +63,53 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, const struct SDL_Ke context->sync.audioWait = !context->sync.audioWait; return; default: - if (event->keysym.mod & KMOD_CTRL && event->type == SDL_KEYDOWN) { - switch (event->keysym.sym) { - case SDLK_p: - GBAThreadTogglePause(context); - default: - break; + if (event->type == SDL_KEYDOWN) { + if (event->keysym.mod & KMOD_CTRL) { + switch (event->keysym.sym) { + case SDLK_p: + GBAThreadTogglePause(context); + default: + break; + } + } + if (event->keysym.mod & KMOD_SHIFT) { + switch (event->keysym.sym) { + case SDLK_F1: + case SDLK_F2: + case SDLK_F3: + case SDLK_F4: + case SDLK_F5: + case SDLK_F6: + case SDLK_F7: + case SDLK_F8: + case SDLK_F9: + case SDLK_F10: + GBAThreadPause(context); + GBASaveState(context->gba, event->keysym.sym - SDLK_F1); + GBAThreadUnpause(context); + break; + default: + break; + } + } else { + switch (event->keysym.sym) { + case SDLK_F1: + case SDLK_F2: + case SDLK_F3: + case SDLK_F4: + case SDLK_F5: + case SDLK_F6: + case SDLK_F7: + case SDLK_F8: + case SDLK_F9: + case SDLK_F10: + GBAThreadPause(context); + GBALoadState(context->gba, event->keysym.sym - SDLK_F1); + GBAThreadUnpause(context); + break; + default: + break; + } } } return;