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