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