all repos — mgba @ b1618cbed35ebdd82b5dc311e0f92537db9951ab

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_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}