GB Serialize: Support serializing/deserializing SGB
@@ -19,7 +19,7 @@
mLOG_DECLARE_CATEGORY(GB_STATE); /* Savestate format: - * 0x00000 - 0x00003: Version Magic (0x01000001) + * 0x00000 - 0x00003: Version Magic (0x01000002) * 0x00004 - 0x00007: ROM CRC32 * 0x00008: Game Boy model * 0x00009 - 0x0000B: Reserved (leave zero)@@ -161,7 +161,22 @@ * 0x00380 - 0x003FE: HRAM
* 0x003FF: Interrupts enabled * 0x00400 - 0x043FF: VRAM * 0x04400 - 0x0C3FF: WRAM - * Total size: 0xC400 (50,176) bytes + * 0x0C400 - 0x0C77F: Reserved + * 0x0C780 - 0x117FF: Super Game Boy + * | 0x0C780 - 0x0C7D9: Current attributes + * | 0x0C7DA: Current command + * | 0x0C7DB: Current bit count + * | 0x0C7DC - 0x0C7DF: Flags + * | bits 0 - 1: Current P1 bits + * | bits 2 - 3: Current render mode + * | bits 4 - 31: Reserved (leave 0) + * | 0x0C7E0 - 0x0C7EF: Current packet + * | 0x0C7F0 - 0x0C7FF: Reserved + * | 0x0C800 - 0x0E7FF: Character VRAM + * | 0x0E800 - 0x0F7FF: Tile map VRAM + * | 0x0F800 - 0x107FF: Palette VRAM + * | 0x10800 - 0x117FF: Attribute file + * Total size: 0x11800 (71,680) bytes */ DECL_BITFIELD(GBSerializedAudioFlags, uint32_t);@@ -238,6 +253,10 @@ DECL_BIT(GBSerializedMemoryFlags, Ime, 3);
DECL_BIT(GBSerializedMemoryFlags, IsHdma, 4); DECL_BITS(GBSerializedMemoryFlags, ActiveRtcReg, 5, 3); +DECL_BITFIELD(GBSerializedSGBFlags, uint32_t); +DECL_BITS(GBSerializedSGBFlags, P1Bits, 0, 2); +DECL_BITS(GBSerializedSGBFlags, RenderMode, 2, 2); + #pragma pack(push, 1) struct GBSerializedState { uint32_t versionMagic;@@ -366,6 +385,21 @@ uint8_t ie;
uint8_t vram[GB_SIZE_VRAM]; uint8_t wram[GB_SIZE_WORKING_RAM]; + + uint32_t reserved2[0xE0]; + + struct { + uint8_t attributes[90]; + uint8_t command; + uint8_t bits; + GBSerializedSGBFlags flags; + uint8_t packet[16]; + uint32_t reserved[4]; + uint8_t charRam[SGB_SIZE_CHAR_RAM]; + uint8_t mapRam[SGB_SIZE_MAP_RAM]; + uint8_t palRam[SGB_SIZE_PAL_RAM]; + uint8_t atfRam[SGB_SIZE_ATF_RAM]; + } sgb; }; #pragma pack(pop)
@@ -409,11 +409,8 @@ return _loadPNGState(core, vf, extdata);
} #endif ssize_t stateSize = core->stateSize(core); + void* state = anonymousMemoryMap(stateSize); vf->seek(vf, 0, SEEK_SET); - if (vf->size(vf) < stateSize) { - return false; - } - void* state = anonymousMemoryMap(stateSize); if (vf->read(vf, state, stateSize) != stateSize) { mappedMemoryFree(state, stateSize); return 0;
@@ -647,5 +647,10 @@ gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_SCY, state->io[REG_SCY]);
gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_SCX, state->io[REG_SCX]); gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_WY, state->io[REG_WY]); gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_WX, state->io[REG_WX]); + if (gb->model == GB_MODEL_SGB) { + gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_BGP, state->io[REG_BGP]); + gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_OBP0, state->io[REG_OBP0]); + gb->video.renderer->writeVideoRegister(gb->video.renderer, REG_OBP1, state->io[REG_OBP1]); + } gb->video.stat = state->io[REG_STAT]; }
@@ -270,6 +270,9 @@ if (i < 16 && softwareRenderer->sgbDataSets) {
memcpy(softwareRenderer->sgbPartialDataSet, &softwareRenderer->sgbPacket[i], 16 - i); } break; + case SGB_ATRC_EN: + _regenerateSGBBorder(softwareRenderer); + break; } }@@ -474,6 +477,7 @@ ++softwareRenderer->sgbTransfer;
if (softwareRenderer->sgbTransfer == 5) { softwareRenderer->sgbCommandHeader = 0; } + break; default: break; }
@@ -12,7 +12,10 @@
mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize"); const uint32_t GB_SAVESTATE_MAGIC = 0x00400000; -const uint32_t GB_SAVESTATE_VERSION = 0x00000001; +const uint32_t GB_SAVESTATE_VERSION = 0x00000002; + +static void GBSGBSerialize(struct GB* gb, struct GBSerializedState* state); +static void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state); void GBSerialize(struct GB* gb, struct GBSerializedState* state) { STORE_32LE(GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, 0, &state->versionMagic);@@ -59,6 +62,10 @@ GBIOSerialize(gb, state);
GBVideoSerialize(&gb->video, state); GBTimerSerialize(&gb->timer, state); GBAudioSerialize(&gb->audio, state); + + if (gb->model == GB_MODEL_SGB) { + GBSGBSerialize(gb, state); + } } bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {@@ -77,6 +84,7 @@ error = true;
} else if (ucheck < GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) { mLOG(GB_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck); } + bool canSgb = ucheck >= GB_SAVESTATE_MAGIC + 2; if (gb->memory.rom && memcmp(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title))) { mLOG(GB_STATE, WARN, "Savestate is for a different game");@@ -174,7 +182,71 @@ GBIODeserialize(gb, state);
GBTimerDeserialize(&gb->timer, state); GBAudioDeserialize(&gb->audio, state); + if (gb->model == GB_MODEL_SGB && canSgb) { + GBSGBDeserialize(gb, state); + } + gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); return true; } + +// TODO: Reorganize SGB into its own file +void GBSGBSerialize(struct GB* gb, struct GBSerializedState* state) { + state->sgb.command = gb->video.sgbCommandHeader; + state->sgb.bits = gb->sgbBit; + + GBSerializedSGBFlags flags = 0; + flags = GBSerializedSGBFlagsSetP1Bits(flags, gb->currentSgbBits); + flags = GBSerializedSGBFlagsSetRenderMode(flags, gb->video.renderer->sgbRenderMode); + STORE_32LE(flags, 0, &state->sgb.flags); + + memcpy(state->sgb.packet, gb->sgbPacket, sizeof(state->sgb.packet)); + + if (gb->video.renderer->sgbCharRam) { + memcpy(state->sgb.charRam, gb->video.renderer->sgbCharRam, sizeof(state->sgb.charRam)); + } + if (gb->video.renderer->sgbMapRam) { + memcpy(state->sgb.mapRam, gb->video.renderer->sgbMapRam, sizeof(state->sgb.mapRam)); + } + if (gb->video.renderer->sgbPalRam) { + memcpy(state->sgb.palRam, gb->video.renderer->sgbPalRam, sizeof(state->sgb.palRam)); + } + if (gb->video.renderer->sgbAttributeFiles) { + memcpy(state->sgb.atfRam, gb->video.renderer->sgbAttributeFiles, sizeof(state->sgb.atfRam)); + } + if (gb->video.renderer->sgbAttributes) { + memcpy(state->sgb.attributes, gb->video.renderer->sgbAttributes, sizeof(state->sgb.attributes)); + } +} + +void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state) { + gb->video.sgbCommandHeader = state->sgb.command; + gb->sgbBit = state->sgb.bits; + + GBSerializedSGBFlags flags; + LOAD_32LE(flags, 0, &state->sgb.flags); + gb->currentSgbBits = GBSerializedSGBFlagsGetP1Bits(flags); + gb->video.renderer->sgbRenderMode = GBSerializedSGBFlagsGetRenderMode(flags); + + memcpy(gb->sgbPacket, state->sgb.packet, sizeof(state->sgb.packet)); + + if (gb->video.renderer->sgbCharRam) { + memcpy(gb->video.renderer->sgbCharRam, state->sgb.charRam, sizeof(state->sgb.charRam)); + } + if (gb->video.renderer->sgbMapRam) { + memcpy(gb->video.renderer->sgbMapRam, state->sgb.mapRam, sizeof(state->sgb.mapRam)); + } + if (gb->video.renderer->sgbPalRam) { + memcpy(gb->video.renderer->sgbPalRam, state->sgb.palRam, sizeof(state->sgb.palRam)); + } + if (gb->video.renderer->sgbAttributeFiles) { + memcpy(gb->video.renderer->sgbAttributeFiles, state->sgb.atfRam, sizeof(state->sgb.atfRam)); + } + if (gb->video.renderer->sgbAttributes) { + memcpy(gb->video.renderer->sgbAttributes, state->sgb.attributes, sizeof(state->sgb.attributes)); + } + + GBVideoWriteSGBPacket(&gb->video, (uint8_t[16]) { (SGB_ATRC_EN << 3) | 1, 0 }); + GBVideoWriteSGBPacket(&gb->video, gb->sgbPacket); +}
@@ -562,9 +562,13 @@ video->palette[1] = data[3] | (data[4] << 8);
video->palette[2] = data[5] | (data[6] << 8); video->palette[3] = data[7] | (data[8] << 8); + video->palette[16] = data[1] | (data[2] << 8); video->palette[17] = data[9] | (data[10] << 8); video->palette[18] = data[11] | (data[12] << 8); video->palette[19] = data[13] | (data[14] << 8); + + video->palette[32] = data[1] | (data[2] << 8); + video->palette[48] = data[1] | (data[2] << 8); video->renderer->writePalette(video->renderer, 0, video->palette[0]); video->renderer->writePalette(video->renderer, 1, video->palette[1]);@@ -578,7 +582,6 @@ video->renderer->writePalette(video->renderer, 32, video->palette[0]);
video->renderer->writePalette(video->renderer, 48, video->palette[0]); break; case SGB_PAL23: - video->palette[32] = data[1] | (data[2] << 8); video->palette[33] = data[3] | (data[4] << 8); video->palette[34] = data[5] | (data[6] << 8); video->palette[35] = data[7] | (data[8] << 8);@@ -599,6 +602,9 @@ video->palette[1] = data[3] | (data[4] << 8);
video->palette[2] = data[5] | (data[6] << 8); video->palette[3] = data[7] | (data[8] << 8); + video->palette[16] = data[1] | (data[2] << 8); + video->palette[32] = data[1] | (data[2] << 8); + video->palette[48] = data[1] | (data[2] << 8); video->palette[49] = data[9] | (data[10] << 8); video->palette[50] = data[11] | (data[12] << 8);@@ -615,7 +621,6 @@ video->renderer->writePalette(video->renderer, 50, video->palette[50]);
video->renderer->writePalette(video->renderer, 51, video->palette[51]); break; case SGB_PAL12: - video->palette[16] = data[1] | (data[2] << 8); video->palette[17] = data[3] | (data[4] << 8); video->palette[18] = data[5] | (data[6] << 8); video->palette[19] = data[7] | (data[8] << 8);@@ -649,6 +654,7 @@ }
break; case SGB_ATTR_BLK: case SGB_PAL_TRN: + case SGB_ATRC_EN: case SGB_CHR_TRN: case SGB_PCT_TRN: case SGB_ATTR_TRN: