src/gb/serialize.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/gb/serialize.h>
7
8#include <mgba/internal/gb/io.h>
9#include <mgba/internal/gb/timer.h>
10#include <mgba/internal/lr35902/lr35902.h>
11
12mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize");
13
14const uint32_t GB_SAVESTATE_MAGIC = 0x00400000;
15const uint32_t GB_SAVESTATE_VERSION = 0x00000001;
16
17void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
18 STORE_32LE(GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, 0, &state->versionMagic);
19 STORE_32LE(gb->romCrc32, 0, &state->romCrc32);
20 STORE_32LE(gb->timing.masterCycles, 0, &state->masterCycles);
21
22 if (gb->memory.rom) {
23 memcpy(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title));
24 } else {
25 memset(state->title, 0, sizeof(state->title));
26 }
27
28 state->model = gb->model;
29
30 state->cpu.a = gb->cpu->a;
31 state->cpu.f = gb->cpu->f.packed;
32 state->cpu.b = gb->cpu->b;
33 state->cpu.c = gb->cpu->c;
34 state->cpu.d = gb->cpu->d;
35 state->cpu.e = gb->cpu->e;
36 state->cpu.h = gb->cpu->h;
37 state->cpu.l = gb->cpu->l;
38 STORE_16LE(gb->cpu->sp, 0, &state->cpu.sp);
39 STORE_16LE(gb->cpu->pc, 0, &state->cpu.pc);
40
41 STORE_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
42 STORE_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent);
43
44 STORE_16LE(gb->cpu->index, 0, &state->cpu.index);
45 state->cpu.bus = gb->cpu->bus;
46 state->cpu.executionState = gb->cpu->executionState;
47 STORE_16LE(gb->cpu->irqVector, 0, &state->cpu.irqVector);
48
49 GBSerializedCpuFlags flags = 0;
50 flags = GBSerializedCpuFlagsSetCondition(flags, gb->cpu->condition);
51 flags = GBSerializedCpuFlagsSetIrqPending(flags, gb->cpu->irqPending);
52 flags = GBSerializedCpuFlagsSetDoubleSpeed(flags, gb->doubleSpeed);
53 flags = GBSerializedCpuFlagsSetEiPending(flags, mTimingIsScheduled(&gb->timing, &gb->eiPending));
54 STORE_32LE(flags, 0, &state->cpu.flags);
55 STORE_32LE(gb->eiPending.when - mTimingCurrentTime(&gb->timing), 0, &state->cpu.eiPending);
56
57 GBMemorySerialize(gb, state);
58 GBIOSerialize(gb, state);
59 GBVideoSerialize(&gb->video, state);
60 GBTimerSerialize(&gb->timer, state);
61 GBAudioSerialize(&gb->audio, state);
62}
63
64bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
65 bool error = false;
66 int32_t check;
67 uint32_t ucheck;
68 int16_t check16;
69 uint16_t ucheck16;
70 LOAD_32LE(ucheck, 0, &state->versionMagic);
71 if (ucheck > GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) {
72 mLOG(GB_STATE, WARN, "Invalid or too new savestate: expected %08X, got %08X", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck);
73 error = true;
74 } else if (ucheck < GB_SAVESTATE_MAGIC) {
75 mLOG(GB_STATE, WARN, "Invalid savestate: expected %08X, got %08X", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck);
76 error = true;
77 } else if (ucheck < GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION) {
78 mLOG(GB_STATE, WARN, "Old savestate: expected %08X, got %08X, continuing anyway", GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, ucheck);
79 }
80
81 if (gb->memory.rom && memcmp(state->title, ((struct GBCartridge*) gb->memory.rom)->titleLong, sizeof(state->title))) {
82 mLOG(GB_STATE, WARN, "Savestate is for a different game");
83 error = true;
84 }
85 LOAD_32LE(ucheck, 0, &state->romCrc32);
86 if (ucheck != gb->romCrc32) {
87 mLOG(GB_STATE, WARN, "Savestate is for a different version of the game");
88 }
89 LOAD_32LE(check, 0, &state->cpu.cycles);
90 if (check < 0) {
91 mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are negative");
92 error = true;
93 }
94 if (state->cpu.executionState != LR35902_CORE_FETCH) {
95 mLOG(GB_STATE, WARN, "Savestate is corrupted: Execution state is not FETCH");
96 error = true;
97 }
98 if (check >= (int32_t) DMG_LR35902_FREQUENCY) {
99 mLOG(GB_STATE, WARN, "Savestate is corrupted: CPU cycles are too high");
100 error = true;
101 }
102 LOAD_16LE(check16, 0, &state->video.x);
103 if (check16 < 0 || check16 > GB_VIDEO_HORIZONTAL_PIXELS) {
104 mLOG(GB_STATE, WARN, "Savestate is corrupted: video x is out of range");
105 error = true;
106 }
107 LOAD_16LE(check16, 0, &state->video.ly);
108 if (check16 < 0 || check16 > GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
109 mLOG(GB_STATE, WARN, "Savestate is corrupted: video y is out of range");
110 error = true;
111 }
112 LOAD_16LE(ucheck16, 0, &state->memory.dmaDest);
113 if (ucheck16 + state->memory.dmaRemaining > GB_SIZE_OAM) {
114 mLOG(GB_STATE, WARN, "Savestate is corrupted: DMA destination is out of range");
115 error = true;
116 }
117 LOAD_16LE(ucheck16, 0, &state->video.bcpIndex);
118 if (ucheck16 >= 0x40) {
119 mLOG(GB_STATE, WARN, "Savestate is corrupted: BCPS is out of range");
120 }
121 LOAD_16LE(ucheck16, 0, &state->video.ocpIndex);
122 if (ucheck16 >= 0x40) {
123 mLOG(GB_STATE, WARN, "Savestate is corrupted: OCPS is out of range");
124 }
125 if (error) {
126 return false;
127 }
128 gb->timing.root = NULL;
129
130 gb->cpu->a = state->cpu.a;
131 gb->cpu->f.packed = state->cpu.f;
132 gb->cpu->b = state->cpu.b;
133 gb->cpu->c = state->cpu.c;
134 gb->cpu->d = state->cpu.d;
135 gb->cpu->e = state->cpu.e;
136 gb->cpu->h = state->cpu.h;
137 gb->cpu->l = state->cpu.l;
138 LOAD_16LE(gb->cpu->sp, 0, &state->cpu.sp);
139 LOAD_16LE(gb->cpu->pc, 0, &state->cpu.pc);
140
141 LOAD_16LE(gb->cpu->index, 0, &state->cpu.index);
142 gb->cpu->bus = state->cpu.bus;
143 gb->cpu->executionState = state->cpu.executionState;
144 LOAD_16LE(gb->cpu->irqVector, 0, &state->cpu.irqVector);
145
146 GBSerializedCpuFlags flags;
147 LOAD_32LE(flags, 0, &state->cpu.flags);
148 gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags);
149 gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags);
150 gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags);
151 gb->audio.timingFactor = gb->doubleSpeed + 1;
152
153 uint32_t when;
154 LOAD_32LE(when, 0, &state->cpu.eiPending);
155 if (GBSerializedCpuFlagsIsEiPending(flags)) {
156 mTimingSchedule(&gb->timing, &gb->eiPending, when);
157 }
158
159 LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
160 LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent);
161 gb->timing.root = NULL;
162
163 gb->model = state->model;
164
165 if (gb->model < GB_MODEL_CGB) {
166 gb->audio.style = GB_AUDIO_DMG;
167 } else {
168 gb->audio.style = GB_AUDIO_CGB;
169 }
170
171 GBMemoryDeserialize(gb, state);
172 GBIODeserialize(gb, state);
173 GBVideoDeserialize(&gb->video, state);
174 GBTimerDeserialize(&gb->timer, state);
175 GBAudioDeserialize(&gb->audio, state);
176
177 gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
178
179 return true;
180}