mirror of https://github.com/mgba-emu/mgba.git
GB Serialize: Support serializing/deserializing SGB
This commit is contained in:
parent
129af69105
commit
05c2fc3e0d
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue