src/gba/savedata.c (view raw)
1/* Copyright (c) 2013-2015 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/gba/savedata.h>
7
8#include <mgba/internal/arm/macros.h>
9#include <mgba/internal/gba/gba.h>
10#include <mgba/internal/gba/serialize.h>
11
12#include <mgba-util/memory.h>
13#include <mgba-util/vfs.h>
14
15#include <errno.h>
16#include <fcntl.h>
17
18// Some testing was done here...
19// Erase cycles can vary greatly.
20// Some games may vary anywhere between about 2000 cycles to up to 30000 cycles. (Observed on a Macronix (09C2) chip).
21// Other games vary from very little, with a fairly solid 20500 cycle count. (Observed on a SST (D4BF) chip).
22// An average estimation is as follows.
23#define FLASH_ERASE_CYCLES 30000
24#define FLASH_PROGRAM_CYCLES 650
25// This needs real testing, and is only an estimation currently
26#define EEPROM_SETTLE_CYCLES 115000
27#define CLEANUP_THRESHOLD 15
28
29mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata", "gba.savedata");
30
31static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
32static void _flashErase(struct GBASavedata* savedata);
33static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
34
35static void _ashesToAshes(struct mTiming* timing, void* user, uint32_t cyclesLate) {
36 UNUSED(timing);
37 UNUSED(user);
38 UNUSED(cyclesLate);
39 // Funk to funky
40}
41
42void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
43 savedata->type = SAVEDATA_AUTODETECT;
44 savedata->data = 0;
45 savedata->command = EEPROM_COMMAND_NULL;
46 savedata->flashState = FLASH_STATE_RAW;
47 savedata->vf = vf;
48 savedata->realVf = vf;
49 savedata->mapMode = MAP_WRITE;
50 savedata->maskWriteback = false;
51 savedata->dirty = 0;
52 savedata->dirtAge = 0;
53 savedata->dust.name = "GBA Savedata Settling";
54 savedata->dust.priority = 0x70;
55 savedata->dust.context = savedata;
56 savedata->dust.callback = _ashesToAshes;
57}
58
59void GBASavedataDeinit(struct GBASavedata* savedata) {
60 if (savedata->vf) {
61 size_t size = GBASavedataSize(savedata);
62 if (savedata->data) {
63 savedata->vf->unmap(savedata->vf, savedata->data, size);
64 }
65 savedata->vf = NULL;
66 } else {
67 switch (savedata->type) {
68 case SAVEDATA_SRAM:
69 mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
70 break;
71 case SAVEDATA_FLASH512:
72 mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
73 break;
74 case SAVEDATA_FLASH1M:
75 mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M);
76 break;
77 case SAVEDATA_EEPROM:
78 mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
79 break;
80 case SAVEDATA_FORCE_NONE:
81 case SAVEDATA_AUTODETECT:
82 break;
83 }
84 }
85 savedata->data = 0;
86 savedata->type = SAVEDATA_AUTODETECT;
87}
88
89void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback) {
90 enum SavedataType type = savedata->type;
91 struct VFile* oldVf = savedata->vf;
92 GBASavedataDeinit(savedata);
93 if (oldVf && oldVf != savedata->realVf) {
94 oldVf->close(oldVf);
95 }
96 savedata->vf = vf;
97 savedata->mapMode = MAP_READ;
98 savedata->maskWriteback = writeback;
99 GBASavedataForceType(savedata, type);
100}
101
102void GBASavedataUnmask(struct GBASavedata* savedata) {
103 if (!savedata->realVf || savedata->vf == savedata->realVf) {
104 return;
105 }
106 enum SavedataType type = savedata->type;
107 struct VFile* vf = savedata->vf;
108 GBASavedataDeinit(savedata);
109 savedata->vf = savedata->realVf;
110 savedata->mapMode = MAP_WRITE;
111 GBASavedataForceType(savedata, type);
112 if (savedata->maskWriteback) {
113 GBASavedataLoad(savedata, vf);
114 savedata->maskWriteback = false;
115 }
116 vf->close(vf);
117}
118
119bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
120 if (savedata->data) {
121 switch (savedata->type) {
122 case SAVEDATA_SRAM:
123 return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
124 case SAVEDATA_FLASH512:
125 return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
126 case SAVEDATA_FLASH1M:
127 return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
128 case SAVEDATA_EEPROM:
129 return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
130 case SAVEDATA_AUTODETECT:
131 case SAVEDATA_FORCE_NONE:
132 return true;
133 }
134 } else if (savedata->vf) {
135 off_t read = 0;
136 uint8_t buffer[2048];
137 do {
138 read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer));
139 out->write(out, buffer, read);
140 } while (read == sizeof(buffer));
141 return read >= 0;
142 }
143 return true;
144}
145
146size_t GBASavedataSize(const struct GBASavedata* savedata) {
147 switch (savedata->type) {
148 case SAVEDATA_SRAM:
149 return SIZE_CART_SRAM;
150 case SAVEDATA_FLASH512:
151 return SIZE_CART_FLASH512;
152 case SAVEDATA_FLASH1M:
153 return SIZE_CART_FLASH1M;
154 case SAVEDATA_EEPROM:
155 return (savedata->vf && savedata->vf->size(savedata->vf) == SIZE_CART_EEPROM512) ? SIZE_CART_EEPROM512 : SIZE_CART_EEPROM;
156 case SAVEDATA_FORCE_NONE:
157 return 0;
158 case SAVEDATA_AUTODETECT:
159 default:
160 if (savedata->vf) {
161 return savedata->vf->size(savedata->vf);
162 }
163 return 0;
164 }
165}
166
167bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) {
168 if (savedata->data) {
169 if (!in && savedata->type != SAVEDATA_FORCE_NONE) {
170 return false;
171 }
172 ssize_t size = GBASavedataSize(savedata);
173 in->seek(in, 0, SEEK_SET);
174 return in->read(in, savedata->data, size) == size;
175 } else if (savedata->vf) {
176 off_t read = 0;
177 uint8_t buffer[2048];
178 savedata->vf->seek(savedata->vf, 0, SEEK_SET);
179 if (in) {
180 in->seek(in, 0, SEEK_SET);
181 do {
182 read = in->read(in, buffer, sizeof(buffer));
183 read = savedata->vf->write(savedata->vf, buffer, read);
184 } while (read == sizeof(buffer));
185 }
186 memset(buffer, 0xFF, sizeof(buffer));
187 ssize_t fsize = savedata->vf->size(savedata->vf);
188 ssize_t pos = savedata->vf->seek(savedata->vf, 0, SEEK_CUR);
189 while (fsize - pos >= (ssize_t) sizeof(buffer)) {
190 savedata->vf->write(savedata->vf, buffer, sizeof(buffer));
191 pos = savedata->vf->seek(savedata->vf, 0, SEEK_CUR);
192 }
193 if (fsize - pos > 0) {
194 savedata->vf->write(savedata->vf, buffer, fsize - pos);
195 }
196 return read >= 0;
197 }
198 return true;
199}
200
201void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type) {
202 if (savedata->type == type) {
203 return;
204 }
205 if (savedata->type != SAVEDATA_AUTODETECT) {
206 struct VFile* vf = savedata->vf;
207 int mapMode = savedata->mapMode;
208 bool maskWriteback = savedata->maskWriteback;
209 GBASavedataDeinit(savedata);
210 GBASavedataInit(savedata, vf);
211 savedata->mapMode = mapMode;
212 savedata->maskWriteback = maskWriteback;
213 }
214 switch (type) {
215 case SAVEDATA_FLASH512:
216 case SAVEDATA_FLASH1M:
217 savedata->type = type;
218 GBASavedataInitFlash(savedata);
219 break;
220 case SAVEDATA_EEPROM:
221 GBASavedataInitEEPROM(savedata);
222 break;
223 case SAVEDATA_SRAM:
224 GBASavedataInitSRAM(savedata);
225 break;
226 case SAVEDATA_FORCE_NONE:
227 savedata->type = SAVEDATA_FORCE_NONE;
228 break;
229 case SAVEDATA_AUTODETECT:
230 break;
231 }
232}
233
234void GBASavedataInitFlash(struct GBASavedata* savedata) {
235 if (savedata->type == SAVEDATA_AUTODETECT) {
236 savedata->type = SAVEDATA_FLASH512;
237 }
238 if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
239 mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
240 return;
241 }
242 int32_t flashSize = SIZE_CART_FLASH512;
243 if (savedata->type == SAVEDATA_FLASH1M) {
244 flashSize = SIZE_CART_FLASH1M;
245 }
246 off_t end;
247 if (!savedata->vf) {
248 end = 0;
249 savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
250 } else {
251 end = savedata->vf->size(savedata->vf);
252 if (end < flashSize) {
253 savedata->vf->truncate(savedata->vf, flashSize);
254 }
255 savedata->data = savedata->vf->map(savedata->vf, flashSize, savedata->mapMode);
256 }
257
258 savedata->currentBank = savedata->data;
259 if (end < SIZE_CART_FLASH512) {
260 memset(&savedata->data[end], 0xFF, flashSize - end);
261 }
262}
263
264void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
265 if (savedata->type == SAVEDATA_AUTODETECT) {
266 savedata->type = SAVEDATA_EEPROM;
267 } else {
268 mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
269 return;
270 }
271 int32_t eepromSize = SIZE_CART_EEPROM512;
272 off_t end;
273 if (!savedata->vf) {
274 end = 0;
275 savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
276 } else {
277 end = savedata->vf->size(savedata->vf);
278 if (end < SIZE_CART_EEPROM512) {
279 savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM512);
280 } else if (end > SIZE_CART_EEPROM512) {
281 eepromSize = SIZE_CART_EEPROM;
282 }
283 savedata->data = savedata->vf->map(savedata->vf, eepromSize, savedata->mapMode);
284 }
285 if (end < SIZE_CART_EEPROM512) {
286 memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM512 - end);
287 }
288}
289
290void GBASavedataInitSRAM(struct GBASavedata* savedata) {
291 if (savedata->type == SAVEDATA_AUTODETECT) {
292 savedata->type = SAVEDATA_SRAM;
293 } else {
294 mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
295 return;
296 }
297 off_t end;
298 if (!savedata->vf) {
299 end = 0;
300 savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
301 } else {
302 end = savedata->vf->size(savedata->vf);
303 if (end < SIZE_CART_SRAM) {
304 savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
305 }
306 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode);
307 }
308
309 if (end < SIZE_CART_SRAM) {
310 memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
311 }
312}
313
314uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
315 if (savedata->command == FLASH_COMMAND_ID) {
316 if (savedata->type == SAVEDATA_FLASH512) {
317 if (address < 2) {
318 return FLASH_MFG_PANASONIC >> (address * 8);
319 }
320 } else if (savedata->type == SAVEDATA_FLASH1M) {
321 if (address < 2) {
322 return FLASH_MFG_SANYO >> (address * 8);
323 }
324 }
325 }
326 if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) {
327 return 0x5F;
328 }
329 return savedata->currentBank[address];
330}
331
332void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value) {
333 switch (savedata->flashState) {
334 case FLASH_STATE_RAW:
335 switch (savedata->command) {
336 case FLASH_COMMAND_PROGRAM:
337 savedata->dirty |= SAVEDATA_DIRT_NEW;
338 savedata->currentBank[address] = value;
339 savedata->command = FLASH_COMMAND_NONE;
340 mTimingDeschedule(savedata->timing, &savedata->dust);
341 mTimingSchedule(savedata->timing, &savedata->dust, FLASH_PROGRAM_CYCLES);
342 break;
343 case FLASH_COMMAND_SWITCH_BANK:
344 if (address == 0 && value < 2) {
345 _flashSwitchBank(savedata, value);
346 } else {
347 mLOG(GBA_SAVE, GAME_ERROR, "Bad flash bank switch");
348 savedata->command = FLASH_COMMAND_NONE;
349 }
350 savedata->command = FLASH_COMMAND_NONE;
351 break;
352 default:
353 if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
354 savedata->flashState = FLASH_STATE_START;
355 } else {
356 mLOG(GBA_SAVE, GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
357 }
358 break;
359 }
360 break;
361 case FLASH_STATE_START:
362 if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
363 savedata->flashState = FLASH_STATE_CONTINUE;
364 } else {
365 mLOG(GBA_SAVE, GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
366 savedata->flashState = FLASH_STATE_RAW;
367 }
368 break;
369 case FLASH_STATE_CONTINUE:
370 savedata->flashState = FLASH_STATE_RAW;
371 if (address == FLASH_BASE_HI) {
372 switch (savedata->command) {
373 case FLASH_COMMAND_NONE:
374 switch (value) {
375 case FLASH_COMMAND_ERASE:
376 case FLASH_COMMAND_ID:
377 case FLASH_COMMAND_PROGRAM:
378 case FLASH_COMMAND_SWITCH_BANK:
379 savedata->command = value;
380 break;
381 default:
382 mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash operation: %#02x", value);
383 break;
384 }
385 break;
386 case FLASH_COMMAND_ERASE:
387 switch (value) {
388 case FLASH_COMMAND_ERASE_CHIP:
389 _flashErase(savedata);
390 break;
391 default:
392 mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
393 break;
394 }
395 savedata->command = FLASH_COMMAND_NONE;
396 break;
397 case FLASH_COMMAND_ID:
398 if (value == FLASH_COMMAND_TERMINATE) {
399 savedata->command = FLASH_COMMAND_NONE;
400 }
401 break;
402 default:
403 mLOG(GBA_SAVE, ERROR, "Flash entered bad state: %#02x", savedata->command);
404 savedata->command = FLASH_COMMAND_NONE;
405 break;
406 }
407 } else if (savedata->command == FLASH_COMMAND_ERASE) {
408 if (value == FLASH_COMMAND_ERASE_SECTOR) {
409 _flashEraseSector(savedata, address);
410 savedata->command = FLASH_COMMAND_NONE;
411 } else {
412 mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
413 }
414 }
415 break;
416 }
417}
418
419static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) {
420 if (size < SIZE_CART_EEPROM512) {
421 return;
422 }
423 if (!savedata->vf || savedata->vf->size(savedata->vf) > SIZE_CART_EEPROM512) {
424 return;
425 }
426 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM512);
427 savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
428 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
429 memset(&savedata->data[SIZE_CART_EEPROM512], 0xFF, SIZE_CART_EEPROM - SIZE_CART_EEPROM512);
430}
431
432void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
433 switch (savedata->command) {
434 // Read header
435 case EEPROM_COMMAND_NULL:
436 default:
437 savedata->command = value & 0x1;
438 break;
439 case EEPROM_COMMAND_PENDING:
440 savedata->command <<= 1;
441 savedata->command |= value & 0x1;
442 if (savedata->command == EEPROM_COMMAND_WRITE) {
443 savedata->writeAddress = 0;
444 } else {
445 savedata->readAddress = 0;
446 }
447 break;
448 // Do commands
449 case EEPROM_COMMAND_WRITE:
450 // Write
451 if (writeSize > 65) {
452 savedata->writeAddress <<= 1;
453 savedata->writeAddress |= (value & 0x1) << 6;
454 } else if (writeSize == 1) {
455 savedata->command = EEPROM_COMMAND_NULL;
456 } else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) {
457 _ensureEeprom(savedata, savedata->writeAddress >> 3);
458 uint8_t current = savedata->data[savedata->writeAddress >> 3];
459 current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
460 current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
461 savedata->dirty |= SAVEDATA_DIRT_NEW;
462 savedata->data[savedata->writeAddress >> 3] = current;
463 mTimingDeschedule(savedata->timing, &savedata->dust);
464 mTimingSchedule(savedata->timing, &savedata->dust, EEPROM_SETTLE_CYCLES);
465 ++savedata->writeAddress;
466 } else {
467 mLOG(GBA_SAVE, GAME_ERROR, "Writing beyond end of EEPROM: %08X", (savedata->writeAddress >> 3));
468 }
469 break;
470 case EEPROM_COMMAND_READ_PENDING:
471 // Read
472 if (writeSize > 1) {
473 savedata->readAddress <<= 1;
474 if (value & 0x1) {
475 savedata->readAddress |= 0x40;
476 }
477 } else {
478 savedata->readBitsRemaining = 68;
479 savedata->command = EEPROM_COMMAND_READ;
480 }
481 break;
482 }
483}
484
485uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
486 if (savedata->command != EEPROM_COMMAND_READ) {
487 if (!mTimingIsScheduled(savedata->timing, &savedata->dust)) {
488 return 1;
489 } else {
490 return 0;
491 }
492 }
493 --savedata->readBitsRemaining;
494 if (savedata->readBitsRemaining < 64) {
495 int step = 63 - savedata->readBitsRemaining;
496 uint32_t address = (savedata->readAddress + step) >> 3;
497 _ensureEeprom(savedata, address);
498 if (address >= SIZE_CART_EEPROM) {
499 mLOG(GBA_SAVE, GAME_ERROR, "Reading beyond end of EEPROM: %08X", address);
500 return 0xFF;
501 }
502 uint8_t data = savedata->data[address] >> (0x7 - (step & 0x7));
503 if (!savedata->readBitsRemaining) {
504 savedata->command = EEPROM_COMMAND_NULL;
505 }
506 return data & 0x1;
507 }
508 return 0;
509}
510
511void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) {
512 if (!savedata->vf) {
513 return;
514 }
515 if (savedata->dirty & SAVEDATA_DIRT_NEW) {
516 savedata->dirtAge = frameCount;
517 savedata->dirty &= ~SAVEDATA_DIRT_NEW;
518 if (!(savedata->dirty & SAVEDATA_DIRT_SEEN)) {
519 savedata->dirty |= SAVEDATA_DIRT_SEEN;
520 }
521 } else if ((savedata->dirty & SAVEDATA_DIRT_SEEN) && frameCount - savedata->dirtAge > CLEANUP_THRESHOLD) {
522 if (savedata->maskWriteback) {
523 GBASavedataUnmask(savedata);
524 }
525 if (savedata->mapMode & MAP_WRITE) {
526 size_t size = GBASavedataSize(savedata);
527 savedata->dirty = 0;
528 if (savedata->data && savedata->vf->sync(savedata->vf, savedata->data, size)) {
529 mLOG(GBA_SAVE, INFO, "Savedata synced");
530 } else {
531 mLOG(GBA_SAVE, INFO, "Savedata failed to sync!");
532 }
533 }
534 }
535}
536
537void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state) {
538 state->savedata.type = savedata->type;
539 state->savedata.command = savedata->command;
540 GBASerializedSavedataFlags flags = 0;
541 flags = GBASerializedSavedataFlagsSetFlashState(flags, savedata->flashState);
542 flags = GBASerializedSavedataFlagsTestFillFlashBank(flags, savedata->currentBank == &savedata->data[0x10000]);
543
544 if (mTimingIsScheduled(savedata->timing, &savedata->dust)) {
545 STORE_32(savedata->dust.when - mTimingCurrentTime(savedata->timing), 0, &state->savedata.settlingDust);
546 flags = GBASerializedSavedataFlagsFillDustSettling(flags);
547 }
548
549 state->savedata.flags = flags;
550 state->savedata.readBitsRemaining = savedata->readBitsRemaining;
551 STORE_32(savedata->readAddress, 0, &state->savedata.readAddress);
552 STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
553 STORE_16(savedata->settling, 0, &state->savedata.settlingSector);
554
555}
556
557void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state) {
558 if (savedata->type != state->savedata.type) {
559 mLOG(GBA_SAVE, DEBUG, "Switching save types");
560 GBASavedataForceType(savedata, state->savedata.type);
561 }
562 savedata->command = state->savedata.command;
563 GBASerializedSavedataFlags flags = state->savedata.flags;
564 savedata->flashState = GBASerializedSavedataFlagsGetFlashState(flags);
565 savedata->readBitsRemaining = state->savedata.readBitsRemaining;
566 LOAD_32(savedata->readAddress, 0, &state->savedata.readAddress);
567 LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
568 LOAD_16(savedata->settling, 0, &state->savedata.settlingSector);
569
570 if (savedata->type == SAVEDATA_FLASH1M) {
571 _flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags));
572 }
573
574 if (GBASerializedSavedataFlagsIsDustSettling(flags)) {
575 uint32_t when;
576 LOAD_32(when, 0, &state->savedata.settlingDust);
577 mTimingSchedule(savedata->timing, &savedata->dust, when);
578 }
579}
580
581void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
582 mLOG(GBA_SAVE, DEBUG, "Performing flash bank switch to bank %i", bank);
583 savedata->currentBank = &savedata->data[bank << 16];
584 if (bank > 0 && savedata->type == SAVEDATA_FLASH512) {
585 mLOG(GBA_SAVE, INFO, "Updating flash chip from 512kb to 1Mb");
586 savedata->type = SAVEDATA_FLASH1M;
587 if (savedata->vf) {
588 savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512);
589 if (savedata->vf->size(savedata->vf) == SIZE_CART_FLASH512) {
590 savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
591 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE);
592 memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
593 } else {
594 savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE);
595 }
596 }
597 }
598}
599
600void _flashErase(struct GBASavedata* savedata) {
601 mLOG(GBA_SAVE, DEBUG, "Performing flash chip erase");
602 savedata->dirty |= SAVEDATA_DIRT_NEW;
603 size_t size = SIZE_CART_FLASH512;
604 if (savedata->type == SAVEDATA_FLASH1M) {
605 size = SIZE_CART_FLASH1M;
606 }
607 memset(savedata->data, 0xFF, size);
608}
609
610void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
611 mLOG(GBA_SAVE, DEBUG, "Performing flash sector erase at 0x%04x", sectorStart);
612 savedata->dirty |= SAVEDATA_DIRT_NEW;
613 size_t size = 0x1000;
614 if (savedata->type == SAVEDATA_FLASH1M) {
615 mLOG(GBA_SAVE, DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart);
616 }
617 savedata->settling = sectorStart >> 12;
618 mTimingDeschedule(savedata->timing, &savedata->dust);
619 mTimingSchedule(savedata->timing, &savedata->dust, FLASH_ERASE_CYCLES);
620 memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
621}