all repos — mgba @ 7cb30ba83e0805cba80ee6ccca8fe9c1bdc61b39

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