GB Serialize: Support serializing/deserializing SGB

This commit is contained in:
Vicki Pfau 2017-08-02 23:41:30 -07:00
parent 129af69105
commit 05c2fc3e0d
6 changed files with 127 additions and 9 deletions

View File

@ -19,7 +19,7 @@ extern const uint32_t GB_SAVESTATE_VERSION;
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 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* 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 @@ struct GBSerializedState {
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)

View File

@ -409,11 +409,8 @@ void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtda
}
#endif
ssize_t stateSize = core->stateSize(core);
vf->seek(vf, 0, SEEK_SET);
if (vf->size(vf) < stateSize) {
return false;
}
void* state = anonymousMemoryMap(stateSize);
vf->seek(vf, 0, SEEK_SET);
if (vf->read(vf, state, stateSize) != stateSize) {
mappedMemoryFree(state, stateSize);
return 0;

View File

@ -647,5 +647,10 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
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];
}

View File

@ -270,6 +270,9 @@ static void GBVideoSoftwareRendererWriteSGBPacket(struct GBVideoRenderer* render
memcpy(softwareRenderer->sgbPartialDataSet, &softwareRenderer->sgbPacket[i], 16 - i);
}
break;
case SGB_ATRC_EN:
_regenerateSGBBorder(softwareRenderer);
break;
}
}
@ -474,6 +477,7 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
if (softwareRenderer->sgbTransfer == 5) {
softwareRenderer->sgbCommandHeader = 0;
}
break;
default:
break;
}

View File

@ -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 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* 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 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
} 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 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* 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);
}

View File

@ -562,10 +562,14 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) {
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]);
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
@ -578,7 +582,6 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) {
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 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) {
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 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) {
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 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) {
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: