all repos — mgba @ e2bcd2e05ad4d8fa879a5b32f56c91e1f5e4c964

mGBA Game Boy Advance Emulator

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