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