mgba/src/gba/savedata.c

576 lines
18 KiB
C
Raw Normal View History

/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "savedata.h"
2013-04-28 02:59:41 +00:00
#include "gba/gba.h"
#include "gba/serialize.h"
#include "util/memory.h"
2014-07-17 06:55:09 +00:00
#include "util/vfs.h"
2013-04-28 02:59:41 +00:00
#include <errno.h>
2013-04-28 02:59:41 +00:00
#include <fcntl.h>
2015-06-19 11:25:29 +00:00
// Some testing was done here...
// Erase cycles can vary greatly.
// Some games may vary anywhere between about 2000 cycles to up to 30000 cycles. (Observed on a Macronix (09C2) chip).
// Other games vary from very little, with a fairly solid 20500 cycle count. (Observed on a SST (D4BF) chip).
// An average estimation is as follows.
#define FLASH_ERASE_CYCLES 30000
#define FLASH_PROGRAM_CYCLES 18000
// This needs real testing, and is only an estimation currently
#define EEPROM_SETTLE_CYCLES 1450
#define CLEANUP_THRESHOLD 15
2016-02-12 07:50:58 +00:00
mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata");
2013-09-24 06:04:15 +00:00
static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
static void _flashErase(struct GBASavedata* savedata);
static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
savedata->type = SAVEDATA_AUTODETECT;
2013-04-28 02:59:41 +00:00
savedata->data = 0;
2013-09-24 06:04:15 +00:00
savedata->command = EEPROM_COMMAND_NULL;
savedata->flashState = FLASH_STATE_RAW;
savedata->vf = vf;
savedata->realVf = vf;
savedata->mapMode = MAP_WRITE;
savedata->dirty = 0;
savedata->dirtAge = 0;
2013-04-28 02:59:41 +00:00
}
void GBASavedataDeinit(struct GBASavedata* savedata) {
2014-07-16 09:08:54 +00:00
if (savedata->vf) {
2016-08-16 05:12:42 +00:00
size_t size = GBASavedataSize(savedata);
savedata->vf->unmap(savedata->vf, savedata->data, size);
savedata->vf = NULL;
2014-07-16 09:08:54 +00:00
} else {
switch (savedata->type) {
case SAVEDATA_SRAM:
mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
break;
case SAVEDATA_FLASH512:
mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
break;
case SAVEDATA_FLASH1M:
mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M);
break;
case SAVEDATA_EEPROM:
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
break;
case SAVEDATA_FORCE_NONE:
case SAVEDATA_AUTODETECT:
2014-07-16 09:08:54 +00:00
break;
}
}
savedata->data = 0;
savedata->type = SAVEDATA_AUTODETECT;
2013-04-28 02:59:41 +00:00
}
void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback) {
2016-06-07 22:06:44 +00:00
enum SavedataType type = savedata->type;
GBASavedataDeinit(savedata);
savedata->vf = vf;
savedata->mapMode = MAP_READ;
savedata->maskWriteback = writeback;
2016-06-07 22:06:44 +00:00
GBASavedataForceType(savedata, type, savedata->realisticTiming);
}
void GBASavedataUnmask(struct GBASavedata* savedata) {
if (savedata->vf == savedata->realVf) {
return;
}
2016-06-07 22:06:44 +00:00
enum SavedataType type = savedata->type;
struct VFile* vf = savedata->vf;
GBASavedataDeinit(savedata);
savedata->vf = savedata->realVf;
savedata->mapMode = MAP_WRITE;
2016-06-07 22:06:44 +00:00
GBASavedataForceType(savedata, type, savedata->realisticTiming);
if (savedata->maskWriteback) {
GBASavedataLoad(savedata, vf);
savedata->maskWriteback = false;
}
vf->close(vf);
}
bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
if (savedata->data) {
switch (savedata->type) {
case SAVEDATA_SRAM:
return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
case SAVEDATA_FLASH512:
return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
case SAVEDATA_FLASH1M:
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
case SAVEDATA_EEPROM:
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
case SAVEDATA_AUTODETECT:
case SAVEDATA_FORCE_NONE:
return true;
}
} else if (savedata->vf) {
off_t read = 0;
uint8_t buffer[2048];
do {
read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer));
out->write(out, buffer, read);
} while (read == sizeof(buffer));
return read >= 0;
}
return true;
}
2016-05-31 06:16:00 +00:00
size_t GBASavedataSize(struct GBASavedata* savedata) {
switch (savedata->type) {
case SAVEDATA_SRAM:
return SIZE_CART_SRAM;
case SAVEDATA_FLASH512:
return SIZE_CART_FLASH512;
case SAVEDATA_FLASH1M:
return SIZE_CART_FLASH1M;
case SAVEDATA_EEPROM:
return SIZE_CART_EEPROM;
case SAVEDATA_FORCE_NONE:
return 0;
case SAVEDATA_AUTODETECT:
default:
if (savedata->vf) {
return savedata->vf->size(savedata->vf);
}
return 0;
}
}
bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in) {
if (savedata->vf) {
off_t read = 0;
uint8_t buffer[2048];
memset(buffer, 0xFF, sizeof(buffer));
savedata->vf->seek(savedata->vf, 0, SEEK_SET);
while (savedata->vf->seek(savedata->vf, 0, SEEK_CUR) < savedata->vf->size(savedata->vf)) {
savedata->vf->write(savedata->vf, buffer, sizeof(buffer));
}
savedata->vf->seek(savedata->vf, 0, SEEK_SET);
if (in) {
do {
read = in->read(in, buffer, sizeof(buffer));
read = savedata->vf->write(savedata->vf, buffer, read);
} while (read == sizeof(buffer));
}
return read >= 0;
} else if (savedata->data) {
if (!in && savedata->type != SAVEDATA_FORCE_NONE) {
return false;
}
switch (savedata->type) {
case SAVEDATA_SRAM:
return in->read(in, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
case SAVEDATA_FLASH512:
return in->read(in, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
case SAVEDATA_FLASH1M:
return in->read(in, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
case SAVEDATA_EEPROM:
return in->read(in, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
case SAVEDATA_AUTODETECT:
case SAVEDATA_FORCE_NONE:
return true;
}
}
return true;
}
void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type, bool realisticTiming) {
if (savedata->type != SAVEDATA_AUTODETECT) {
struct VFile* vf = savedata->vf;
GBASavedataDeinit(savedata);
GBASavedataInit(savedata, vf);
}
2014-12-31 06:56:17 +00:00
switch (type) {
case SAVEDATA_FLASH512:
case SAVEDATA_FLASH1M:
savedata->type = type;
GBASavedataInitFlash(savedata, realisticTiming);
2014-12-31 06:56:17 +00:00
break;
case SAVEDATA_EEPROM:
GBASavedataInitEEPROM(savedata, realisticTiming);
2014-12-31 06:56:17 +00:00
break;
case SAVEDATA_SRAM:
GBASavedataInitSRAM(savedata);
break;
case SAVEDATA_FORCE_NONE:
savedata->type = SAVEDATA_FORCE_NONE;
break;
case SAVEDATA_AUTODETECT:
2014-12-31 06:56:17 +00:00
break;
}
}
void GBASavedataInitFlash(struct GBASavedata* savedata, bool realisticTiming) {
if (savedata->type == SAVEDATA_AUTODETECT) {
savedata->type = SAVEDATA_FLASH512;
}
if (savedata->type != SAVEDATA_FLASH512 && savedata->type != SAVEDATA_FLASH1M) {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
return;
}
int32_t flashSize = SIZE_CART_FLASH512;
if (savedata->type == SAVEDATA_FLASH1M) {
flashSize = SIZE_CART_FLASH1M;
}
off_t end;
2014-07-16 09:08:54 +00:00
if (!savedata->vf) {
end = 0;
2014-01-15 08:24:06 +00:00
savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
} else {
end = savedata->vf->size(savedata->vf);
if (end < flashSize) {
2014-07-16 09:08:54 +00:00
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
flashSize = SIZE_CART_FLASH1M;
}
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, savedata->mapMode);
2013-04-28 02:59:41 +00:00
}
2014-01-15 08:24:06 +00:00
2013-09-24 06:04:15 +00:00
savedata->currentBank = savedata->data;
savedata->dust = 0;
savedata->realisticTiming = realisticTiming;
2013-04-28 02:59:41 +00:00
if (end < SIZE_CART_FLASH512) {
memset(&savedata->data[end], 0xFF, flashSize - end);
2013-04-28 02:59:41 +00:00
}
}
void GBASavedataInitEEPROM(struct GBASavedata* savedata, bool realisticTiming) {
if (savedata->type == SAVEDATA_AUTODETECT) {
savedata->type = SAVEDATA_EEPROM;
} else {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
return;
}
off_t end;
2014-07-16 09:08:54 +00:00
if (!savedata->vf) {
end = 0;
2014-01-15 08:24:06 +00:00
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
} else {
end = savedata->vf->size(savedata->vf);
if (end < SIZE_CART_EEPROM) {
2014-07-16 09:08:54 +00:00
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
}
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
2013-04-28 03:25:31 +00:00
}
savedata->dust = 0;
savedata->realisticTiming = realisticTiming;
2013-04-28 03:25:31 +00:00
if (end < SIZE_CART_EEPROM) {
memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
}
}
2013-04-28 02:59:41 +00:00
void GBASavedataInitSRAM(struct GBASavedata* savedata) {
if (savedata->type == SAVEDATA_AUTODETECT) {
savedata->type = SAVEDATA_SRAM;
} else {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
return;
}
2013-04-28 02:59:41 +00:00
off_t end;
2014-07-16 09:08:54 +00:00
if (!savedata->vf) {
2013-04-28 02:59:41 +00:00
end = 0;
2014-01-15 08:24:06 +00:00
savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
2013-04-28 02:59:41 +00:00
} else {
end = savedata->vf->size(savedata->vf);
2013-04-28 02:59:41 +00:00
if (end < SIZE_CART_SRAM) {
2014-07-16 09:08:54 +00:00
savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
2013-04-28 02:59:41 +00:00
}
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode);
2013-04-28 02:59:41 +00:00
}
2014-01-15 08:24:06 +00:00
2013-04-28 02:59:41 +00:00
if (end < SIZE_CART_SRAM) {
memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
}
}
2013-09-24 06:04:15 +00:00
uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
if (savedata->command == FLASH_COMMAND_ID) {
if (savedata->type == SAVEDATA_FLASH512) {
if (address < 2) {
return FLASH_MFG_PANASONIC >> (address * 8);
}
} else if (savedata->type == SAVEDATA_FLASH1M) {
if (address < 2) {
return FLASH_MFG_SANYO >> (address * 8);
}
}
}
if (savedata->dust > 0 && (address >> 12) == savedata->settling) {
2015-06-19 11:25:29 +00:00
// Give some overhead for waitstates and the comparison
// This estimation can probably be improved
savedata->dust -= 5000;
return 0x5F;
}
2013-09-24 06:04:15 +00:00
return savedata->currentBank[address];
}
2013-04-28 03:25:31 +00:00
2013-09-24 06:04:15 +00:00
void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value) {
switch (savedata->flashState) {
case FLASH_STATE_RAW:
switch (savedata->command) {
case FLASH_COMMAND_PROGRAM:
savedata->dirty |= SAVEDATA_DIRT_NEW;
2013-09-24 06:04:15 +00:00
savedata->currentBank[address] = value;
savedata->command = FLASH_COMMAND_NONE;
if (savedata->realisticTiming) {
savedata->dust = FLASH_PROGRAM_CYCLES;
}
2013-09-24 06:04:15 +00:00
break;
case FLASH_COMMAND_SWITCH_BANK:
if (address == 0 && value < 2) {
_flashSwitchBank(savedata, value);
} else {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, GAME_ERROR, "Bad flash bank switch");
2013-09-24 06:04:15 +00:00
savedata->command = FLASH_COMMAND_NONE;
}
2013-09-25 11:48:27 +00:00
savedata->command = FLASH_COMMAND_NONE;
2013-09-24 06:04:15 +00:00
break;
default:
if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) {
savedata->flashState = FLASH_STATE_START;
} else {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
2013-09-24 06:04:15 +00:00
}
break;
}
break;
case FLASH_STATE_START:
if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) {
savedata->flashState = FLASH_STATE_CONTINUE;
} else {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value);
2013-09-24 06:04:15 +00:00
savedata->flashState = FLASH_STATE_RAW;
}
break;
case FLASH_STATE_CONTINUE:
savedata->flashState = FLASH_STATE_RAW;
if (address == FLASH_BASE_HI) {
switch (savedata->command) {
case FLASH_COMMAND_NONE:
switch (value) {
case FLASH_COMMAND_ERASE:
case FLASH_COMMAND_ID:
case FLASH_COMMAND_PROGRAM:
case FLASH_COMMAND_SWITCH_BANK:
savedata->command = value;
break;
default:
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash operation: %#02x", value);
2013-09-24 06:04:15 +00:00
break;
}
break;
case FLASH_COMMAND_ERASE:
switch (value) {
case FLASH_COMMAND_ERASE_CHIP:
_flashErase(savedata);
break;
default:
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
2013-09-24 06:04:15 +00:00
break;
}
savedata->command = FLASH_COMMAND_NONE;
break;
case FLASH_COMMAND_ID:
if (value == FLASH_COMMAND_TERMINATE) {
savedata->command = FLASH_COMMAND_NONE;
}
break;
default:
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, ERROR, "Flash entered bad state: %#02x", savedata->command);
2013-09-24 06:04:15 +00:00
savedata->command = FLASH_COMMAND_NONE;
break;
}
} else if (savedata->command == FLASH_COMMAND_ERASE) {
if (value == FLASH_COMMAND_ERASE_SECTOR) {
_flashEraseSector(savedata, address);
savedata->command = FLASH_COMMAND_NONE;
} else {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, GAME_ERROR, "Unsupported flash erase operation: %#02x", value);
2013-09-24 06:04:15 +00:00
}
}
break;
}
2013-04-28 02:59:41 +00:00
}
2013-04-28 03:25:31 +00:00
2013-04-28 04:51:58 +00:00
void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
switch (savedata->command) {
// Read header
case EEPROM_COMMAND_NULL:
default:
savedata->command = value & 0x1;
break;
case EEPROM_COMMAND_PENDING:
savedata->command <<= 1;
savedata->command |= value & 0x1;
if (savedata->command == EEPROM_COMMAND_WRITE) {
savedata->writeAddress = 0;
} else {
savedata->readAddress = 0;
}
break;
// Do commands
case EEPROM_COMMAND_WRITE:
// Write
if (writeSize > 65) {
savedata->writeAddress <<= 1;
savedata->writeAddress |= (value & 0x1) << 6;
} else if (writeSize == 1) {
savedata->command = EEPROM_COMMAND_NULL;
} else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) {
2013-04-28 04:51:58 +00:00
uint8_t current = savedata->data[savedata->writeAddress >> 3];
current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
current |= (value & 0x1) << (0x7 - (savedata->writeAddress & 0x7));
savedata->dirty |= SAVEDATA_DIRT_NEW;
2013-04-28 04:51:58 +00:00
savedata->data[savedata->writeAddress >> 3] = current;
if (savedata->realisticTiming) {
savedata->dust = EEPROM_SETTLE_CYCLES;
}
2013-04-28 04:51:58 +00:00
++savedata->writeAddress;
} else {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, GAME_ERROR, "Writing beyond end of EEPROM: %08X", (savedata->writeAddress >> 3));
2013-04-28 04:51:58 +00:00
}
break;
case EEPROM_COMMAND_READ_PENDING:
// Read
if (writeSize > 1) {
savedata->readAddress <<= 1;
if (value & 0x1) {
savedata->readAddress |= 0x40;
}
} else {
savedata->readBitsRemaining = 68;
savedata->command = EEPROM_COMMAND_READ;
}
break;
}
2013-04-28 03:25:31 +00:00
}
uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
2013-04-28 04:51:58 +00:00
if (savedata->command != EEPROM_COMMAND_READ) {
if (!savedata->realisticTiming || savedata->dust <= 0) {
return 1;
} else {
// Give some overhead for waitstates and the comparison
// This estimation can probably be improved
--savedata->dust;
return 0;
}
2013-04-28 04:51:58 +00:00
}
--savedata->readBitsRemaining;
if (savedata->readBitsRemaining < 64) {
int step = 63 - savedata->readBitsRemaining;
uint32_t address = (savedata->readAddress + step) >> 3;
if (address >= SIZE_CART_EEPROM) {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, GAME_ERROR, "Reading beyond end of EEPROM: %08X", address);
return 0xFF;
}
uint8_t data = savedata->data[address] >> (0x7 - (step & 0x7));
2013-04-28 04:51:58 +00:00
if (!savedata->readBitsRemaining) {
savedata->command = EEPROM_COMMAND_NULL;
}
return data & 0x1;
}
2013-04-28 03:25:31 +00:00
return 0;
}
2013-09-24 06:04:15 +00:00
void GBASavedataClean(struct GBASavedata* savedata, uint32_t frameCount) {
if (!savedata->vf) {
return;
}
if (savedata->dirty & SAVEDATA_DIRT_NEW) {
2016-09-15 00:45:58 +00:00
savedata->dirtAge = frameCount;
savedata->dirty &= ~SAVEDATA_DIRT_NEW;
if (!(savedata->dirty & SAVEDATA_DIRT_SEEN)) {
savedata->dirty |= SAVEDATA_DIRT_SEEN;
}
} else if ((savedata->dirty & SAVEDATA_DIRT_SEEN) && frameCount - savedata->dirtAge > CLEANUP_THRESHOLD) {
if (savedata->maskWriteback) {
GBASavedataUnmask(savedata);
}
2016-08-16 05:12:42 +00:00
size_t size = GBASavedataSize(savedata);
savedata->dirty = 0;
2016-08-16 05:12:42 +00:00
if (savedata->data && savedata->vf->sync(savedata->vf, savedata->data, size)) {
mLOG(GBA_SAVE, INFO, "Savedata synced");
} else {
mLOG(GBA_SAVE, INFO, "Savedata failed to sync!");
}
}
}
void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state) {
state->savedata.type = savedata->type;
state->savedata.command = savedata->command;
GBASerializedSavedataFlags flags = 0;
flags = GBASerializedSavedataFlagsSetFlashState(flags, savedata->flashState);
flags = GBASerializedSavedataFlagsTestFillFlashBank(flags, savedata->currentBank == &savedata->data[0x10000]);
state->savedata.flags = flags;
STORE_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining);
STORE_32(savedata->readAddress, 0, &state->savedata.readAddress);
STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
STORE_16(savedata->settling, 0, &state->savedata.settlingSector);
STORE_16(savedata->dust, 0, &state->savedata.settlingDust);
}
void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state) {
if (savedata->type != state->savedata.type) {
2016-08-16 05:12:42 +00:00
mLOG(GBA_SAVE, DEBUG, "Switching save types");
GBASavedataForceType(savedata, state->savedata.type, savedata->realisticTiming);
}
savedata->command = state->savedata.command;
GBASerializedSavedataFlags flags = state->savedata.flags;
savedata->flashState = GBASerializedSavedataFlagsGetFlashState(flags);
LOAD_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining);
LOAD_32(savedata->readAddress, 0, &state->savedata.readAddress);
LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
LOAD_16(savedata->settling, 0, &state->savedata.settlingSector);
LOAD_16(savedata->dust, 0, &state->savedata.settlingDust);
if (savedata->type == SAVEDATA_FLASH1M) {
_flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags));
}
}
2013-09-24 06:04:15 +00:00
void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, DEBUG, "Performing flash bank switch to bank %i", bank);
2013-09-24 06:04:15 +00:00
savedata->currentBank = &savedata->data[bank << 16];
if (bank > 0 && savedata->type == SAVEDATA_FLASH512) {
2013-09-24 06:04:15 +00:00
savedata->type = SAVEDATA_FLASH1M;
if (savedata->vf) {
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
}
2013-09-24 06:04:15 +00:00
}
}
void _flashErase(struct GBASavedata* savedata) {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, DEBUG, "Performing flash chip erase");
savedata->dirty |= SAVEDATA_DIRT_NEW;
size_t size = SIZE_CART_FLASH512;
2013-09-24 06:04:15 +00:00
if (savedata->type == SAVEDATA_FLASH1M) {
size = SIZE_CART_FLASH1M;
2013-09-24 06:04:15 +00:00
}
memset(savedata->data, 0xFF, size);
}
void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, DEBUG, "Performing flash sector erase at 0x%04x", sectorStart);
savedata->dirty |= SAVEDATA_DIRT_NEW;
2013-09-24 06:04:15 +00:00
size_t size = 0x1000;
if (savedata->type == SAVEDATA_FLASH1M) {
2016-02-12 07:50:58 +00:00
mLOG(GBA_SAVE, DEBUG, "Performing unknown sector-size erase at 0x%04x", sectorStart);
2013-09-24 06:04:15 +00:00
}
savedata->settling = sectorStart >> 12;
if (savedata->realisticTiming) {
savedata->dust = FLASH_ERASE_CYCLES;
}
2013-09-24 06:04:15 +00:00
memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
}