all repos — mgba @ d708a1025d7b8246ae531828728114dad327e1ce

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