src/gba/gba-savedata.c (view raw)
1#include "gba-savedata.h"
2
3#include "gba.h"
4
5#include "util/memory.h"
6#include "util/vfile.h"
7
8#include <errno.h>
9#include <fcntl.h>
10
11static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
12static void _flashErase(struct GBASavedata* savedata);
13static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
14
15void GBASavedataInit(struct GBASavedata* savedata, const char* filename) {
16 savedata->type = SAVEDATA_NONE;
17 savedata->data = 0;
18 savedata->command = EEPROM_COMMAND_NULL;
19 savedata->flashState = FLASH_STATE_RAW;
20 savedata->vf = 0;
21 savedata->filename = filename;
22}
23
24void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) {
25 if (savedata->type != SAVEDATA_NONE) {
26 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
27 return;
28 }
29 savedata->type = type;
30}
31
32void GBASavedataDeinit(struct GBASavedata* savedata) {
33 if (savedata->vf) {
34 switch (savedata->type) {
35 case SAVEDATA_SRAM:
36 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_SRAM);
37 break;
38 case SAVEDATA_FLASH512:
39 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512);
40 break;
41 case SAVEDATA_FLASH1M:
42 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH1M);
43 break;
44 case SAVEDATA_EEPROM:
45 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM);
46 break;
47 case SAVEDATA_NONE:
48 break;
49 }
50 savedata->vf->close(savedata->vf);
51 savedata->vf = 0;
52 } else {
53 switch (savedata->type) {
54 case SAVEDATA_SRAM:
55 mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
56 break;
57 case SAVEDATA_FLASH512:
58 mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
59 break;
60 case SAVEDATA_FLASH1M:
61 mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M);
62 break;
63 case SAVEDATA_EEPROM:
64 mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
65 break;
66 case SAVEDATA_NONE:
67 break;
68 }
69 }
70 savedata->type = SAVEDATA_NONE;
71}
72
73void GBASavedataInitFlash(struct GBASavedata* savedata) {
74 if (savedata->type == SAVEDATA_NONE) {
75 savedata->type = SAVEDATA_FLASH512;
76 }
77 if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
78 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
79 return;
80 }
81 savedata->vf = VFileOpen(savedata->filename, O_RDWR | O_CREAT);
82 off_t end;
83 if (!savedata->vf) {
84 GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
85 end = 0;
86 savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
87 } else {
88 end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
89 if (end < SIZE_CART_FLASH512) {
90 savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
91 }
92 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MEMORY_WRITE);
93 }
94
95 savedata->currentBank = savedata->data;
96 if (end < SIZE_CART_FLASH512) {
97 memset(&savedata->data[end], 0xFF, SIZE_CART_FLASH512 - end);
98 }
99}
100
101void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
102 if (savedata->type == SAVEDATA_NONE) {
103 savedata->type = SAVEDATA_EEPROM;
104 } else {
105 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
106 return;
107 }
108 savedata->vf = VFileOpen(savedata->filename, O_RDWR | O_CREAT);
109 off_t end;
110 if (!savedata->vf) {
111 GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
112 end = 0;
113 savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
114 } else {
115 end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
116 if (end < SIZE_CART_EEPROM) {
117 savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
118 }
119 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, MEMORY_WRITE);
120 }
121 if (end < SIZE_CART_EEPROM) {
122 memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
123 }
124}
125
126void GBASavedataInitSRAM(struct GBASavedata* savedata) {
127 if (savedata->type == SAVEDATA_NONE) {
128 savedata->type = SAVEDATA_SRAM;
129 } else {
130 GBALog(0, GBA_LOG_WARN, "Can't re-initialize savedata");
131 return;
132 }
133 savedata->vf = VFileOpen(savedata->filename, O_RDWR | O_CREAT);
134 off_t end;
135 if (!savedata->vf) {
136 GBALog(0, GBA_LOG_ERROR, "Cannot open savedata file %s (errno: %d)", savedata->filename, errno);
137 end = 0;
138 savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
139 } else {
140 end = savedata->vf->seek(savedata->vf, 0, SEEK_END);
141 if (end < SIZE_CART_SRAM) {
142 savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
143 }
144 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, MEMORY_WRITE);
145 }
146
147 if (end < SIZE_CART_SRAM) {
148 memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
149 }
150}
151
152uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
153 if (savedata->command == FLASH_COMMAND_ID) {
154 if (savedata->type == SAVEDATA_FLASH512) {
155 if (address < 2) {
156 return FLASH_MFG_PANASONIC >> (address * 8);
157 }
158 } else if (savedata->type == SAVEDATA_FLASH1M) {
159 if (address < 2) {
160 return FLASH_MFG_SANYO >> (address * 8);
161 }
162 }
163 }
164 return savedata->currentBank[address];
165}
166
167void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value) {
168 switch (savedata->flashState) {
169 case FLASH_STATE_RAW:
170 switch (savedata->command) {
171 case FLASH_COMMAND_PROGRAM:
172 savedata->currentBank[address] = value;
173 savedata->command = FLASH_COMMAND_NONE;
174 break;
175 case FLASH_COMMAND_SWITCH_BANK:
176 if (address == 0 && value < 2) {
177 _flashSwitchBank(savedata, value);
178 } else {
179 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash bank switch");
180 savedata->command = FLASH_COMMAND_NONE;
181 }
182 savedata->command = FLASH_COMMAND_NONE;
183 break;
184 default:
185 if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
186 savedata->flashState = FLASH_STATE_START;
187 } else {
188 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
189 }
190 break;
191 }
192 break;
193 case FLASH_STATE_START:
194 if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
195 savedata->flashState = FLASH_STATE_CONTINUE;
196 } else {
197 GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
198 savedata->flashState = FLASH_STATE_RAW;
199 }
200 break;
201 case FLASH_STATE_CONTINUE:
202 savedata->flashState = FLASH_STATE_RAW;
203 if (address == FLASH_BASE_HI) {
204 switch (savedata->command) {
205 case FLASH_COMMAND_NONE:
206 switch (value) {
207 case FLASH_COMMAND_ERASE:
208 case FLASH_COMMAND_ID:
209 case FLASH_COMMAND_PROGRAM:
210 case FLASH_COMMAND_SWITCH_BANK:
211 savedata->command = value;
212 break;
213 default:
214 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash operation: %#02x", value);
215 break;
216 }
217 break;
218 case FLASH_COMMAND_ERASE:
219 switch (value) {
220 case FLASH_COMMAND_ERASE_CHIP:
221 _flashErase(savedata);
222 break;
223 default:
224 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
225 break;
226 }
227 savedata->command = FLASH_COMMAND_NONE;
228 break;
229 case FLASH_COMMAND_ID:
230 if (value == FLASH_COMMAND_TERMINATE) {
231 savedata->command = FLASH_COMMAND_NONE;
232 }
233 break;
234 default:
235 GBALog(0, GBA_LOG_ERROR, "Flash entered bad state: %#02x", savedata->command);
236 savedata->command = FLASH_COMMAND_NONE;
237 break;
238 }
239 } else if (savedata->command == FLASH_COMMAND_ERASE) {
240 if (value == FLASH_COMMAND_ERASE_SECTOR) {
241 _flashEraseSector(savedata, address);
242 savedata->command = FLASH_COMMAND_NONE;
243 } else {
244 GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
245 }
246 }
247 break;
248 }
249}
250
251void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
252 switch (savedata->command) {
253 // Read header
254 case EEPROM_COMMAND_NULL:
255 default:
256 savedata->command = value & 0x1;
257 break;
258 case EEPROM_COMMAND_PENDING:
259 savedata->command <<= 1;
260 savedata->command |= value & 0x1;
261 if (savedata->command == EEPROM_COMMAND_WRITE) {
262 savedata->addressBits = writeSize - 64 - 2;
263 savedata->writeAddress = 0;
264 } else {
265 savedata->addressBits = writeSize - 2;
266 savedata->readAddress = 0;
267 }
268 break;
269 // Do commands
270 case EEPROM_COMMAND_WRITE:
271 // Write
272 if (writeSize > 65) {
273 savedata->writeAddress <<= 1;
274 savedata->writeAddress |= (value & 0x1) << 6;
275 } else if (writeSize == 1) {
276 savedata->command = EEPROM_COMMAND_NULL;
277 savedata->writePending = 1;
278 } else {
279 uint8_t current = savedata->data[savedata->writeAddress >> 3];
280 current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
281 current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
282 savedata->data[savedata->writeAddress >> 3] = current;
283 ++savedata->writeAddress;
284 }
285 break;
286 case EEPROM_COMMAND_READ_PENDING:
287 // Read
288 if (writeSize > 1) {
289 savedata->readAddress <<= 1;
290 if (value & 0x1) {
291 savedata->readAddress |= 0x40;
292 }
293 } else {
294 savedata->readBitsRemaining = 68;
295 savedata->command = EEPROM_COMMAND_READ;
296 }
297 break;
298 }
299}
300
301uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
302 if (savedata->command != EEPROM_COMMAND_READ) {
303 return 1;
304 }
305 --savedata->readBitsRemaining;
306 if (savedata->readBitsRemaining < 64) {
307 int step = 63 - savedata->readBitsRemaining;
308 uint8_t data = savedata->data[(savedata->readAddress + step) >> 3] >> (0x7 - (step & 0x7));
309 if (!savedata->readBitsRemaining) {
310 savedata->command = EEPROM_COMMAND_NULL;
311 }
312 return data & 0x1;
313 }
314 return 0;
315}
316
317void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
318 savedata->currentBank = &savedata->data[bank << 16];
319 if (bank > 0) {
320 savedata->type = SAVEDATA_FLASH1M;
321 savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
322 }
323}
324
325void _flashErase(struct GBASavedata* savedata) {
326 size_t size = 0x10000;
327 if (savedata->type == SAVEDATA_FLASH1M) {
328 size = 0x20000;
329 }
330 memset(savedata->data, 0xFF, size);
331}
332
333void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
334 size_t size = 0x1000;
335 if (savedata->type == SAVEDATA_FLASH1M) {
336 GBALog(0, GBA_LOG_DEBUG, "Performing unknown sector-size erase at %#04x", sectorStart);
337 }
338 memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
339}