Support flash game saves
Jeffrey Pfau jeffrey@endrift.com
Mon, 23 Sep 2013 23:04:15 -0700
4 files changed,
168 insertions(+),
10 deletions(-)
M
src/gba/gba-memory.c
→
src/gba/gba-memory.c
@@ -273,8 +273,11 @@ wait = gbaMemory->waitstates16[address >> BASE_OFFSET];
if (gbaMemory->savedata.type == SAVEDATA_NONE) { GBASavedataInit(&gbaMemory->savedata, gbaMemory->p->savefile); GBASavedataInitSRAM(&gbaMemory->savedata); + } else if (gbaMemory->savedata.type == SAVEDATA_SRAM) { + value = gbaMemory->savedata.data[address & (SIZE_CART_SRAM - 1)]; + } else if (gbaMemory->savedata.type == SAVEDATA_FLASH512 || gbaMemory->savedata.type == SAVEDATA_FLASH1M) { + value = GBASavedataReadFlash(&gbaMemory->savedata, address); } - value = gbaMemory->savedata.data[address & (SIZE_CART_SRAM - 1)]; default: break; }@@ -409,7 +412,7 @@ GBASavedataInitSRAM(&gbaMemory->savedata);
} } if (gbaMemory->savedata.type == SAVEDATA_FLASH512 || gbaMemory->savedata.type == SAVEDATA_FLASH1M) { - GBASavedataWriteFlash(&gbaMemory->savedata, value); + GBASavedataWriteFlash(&gbaMemory->savedata, address, value); } else if (gbaMemory->savedata.type == SAVEDATA_SRAM) { gbaMemory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value; }
M
src/gba/gba-savedata.c
→
src/gba/gba-savedata.c
@@ -8,9 +8,15 @@ #include <string.h>
#include <sys/mman.h> #include <unistd.h> +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, const char* filename) { savedata->type = SAVEDATA_NONE; savedata->data = 0; + savedata->command = EEPROM_COMMAND_NULL; + savedata->flashState = FLASH_STATE_RAW; savedata->fd = -1; savedata->filename = filename; }@@ -53,6 +59,7 @@ }
} // mmap enough so that we can expand the file if we need to savedata->data = mmap(0, SIZE_CART_FLASH1M, PROT_READ | PROT_WRITE, flags, savedata->fd, 0); + savedata->currentBank = savedata->data; if (end < SIZE_CART_FLASH512) { memset(&savedata->data[end], 0xFF, SIZE_CART_FLASH512 - end); }@@ -100,11 +107,102 @@ memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
} } +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); + } + } + } + return savedata->currentBank[address]; +} -void GBASavedataWriteFlash(struct GBASavedata* savedata, uint8_t value) { - (void)(savedata); - (void)(value); - GBALog(0, GBA_LOG_STUB, "Flash memory unimplemented"); +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->currentBank[address] = value; + savedata->command = FLASH_COMMAND_NONE; + break; + case FLASH_COMMAND_SWITCH_BANK: + if (address == 0 && value < 2) { + _flashSwitchBank(savedata, value); + } else { + GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash bank switch"); + savedata->command = FLASH_COMMAND_NONE; + } + break; + default: + if (address == FLASH_BASE_HI && value == FLASH_COMMAND_START) { + savedata->flashState = FLASH_STATE_START; + } else { + GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value); + } + break; + } + break; + case FLASH_STATE_START: + if (address == FLASH_BASE_LO && value == FLASH_COMMAND_CONTINUE) { + savedata->flashState = FLASH_STATE_CONTINUE; + } else { + GBALog(0, GBA_LOG_GAME_ERROR, "Bad flash write: %#04x = %#02x", address, value); + 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: + GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash operation: %#02x", value); + break; + } + break; + case FLASH_COMMAND_ERASE: + switch (value) { + case FLASH_COMMAND_ERASE_CHIP: + _flashErase(savedata); + break; + default: + GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value); + break; + } + savedata->command = FLASH_COMMAND_NONE; + break; + case FLASH_COMMAND_ID: + if (value == FLASH_COMMAND_TERMINATE) { + savedata->command = FLASH_COMMAND_NONE; + } + break; + default: + GBALog(0, GBA_LOG_ERROR, "Flash entered bad state: %#02x", savedata->command); + 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 { + GBALog(0, GBA_LOG_GAME_ERROR, "Unsupported flash erase operation: %#02x", value); + } + } + break; + } } void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {@@ -172,3 +270,26 @@ return data & 0x1;
} return 0; } + +void _flashSwitchBank(struct GBASavedata* savedata, int bank) { + savedata->currentBank = &savedata->data[bank << 16]; + if (bank > 0) { + savedata->type = SAVEDATA_FLASH1M; + } +} + +void _flashErase(struct GBASavedata* savedata) { + size_t size = 0x10000; + if (savedata->type == SAVEDATA_FLASH1M) { + size = 0x20000; + } + memset(savedata->data, 0xFF, size); +} + +void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) { + size_t size = 0x1000; + if (savedata->type == SAVEDATA_FLASH1M) { + GBALog(0, GBA_LOG_DEBUG, "Performing unknown sector-size erase at %#04x", sectorStart); + } + memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size); +}
M
src/gba/gba-savedata.h
→
src/gba/gba-savedata.h
@@ -16,11 +16,38 @@ EEPROM_COMMAND_NULL = 0,
EEPROM_COMMAND_PENDING = 1, EEPROM_COMMAND_WRITE = 2, EEPROM_COMMAND_READ_PENDING = 3, - EEPROM_COMMAND_READ = 4 + EEPROM_COMMAND_READ = 4, + + FLASH_COMMAND_START = 0xAA, + FLASH_COMMAND_CONTINUE = 0x55, + + FLASH_COMMAND_ERASE_CHIP = 0x10, + FLASH_COMMAND_ERASE_SECTOR = 0x30, + + FLASH_COMMAND_NONE = 0, + FLASH_COMMAND_ERASE = 0x80, + FLASH_COMMAND_ID = 0x90, + FLASH_COMMAND_PROGRAM = 0xA0, + FLASH_COMMAND_SWITCH_BANK = 0xB0, + FLASH_COMMAND_TERMINATE = 0xF0 +}; + +enum FlashStateMachine { + FLASH_STATE_RAW = 0, + FLASH_STATE_START, + FLASH_STATE_CONTINUE +}; + +enum FlashManufacturer { + FLASH_MFG_PANASONIC = 0x1B32, + FLASH_MFG_SANYO = 0x1362 }; enum { - SAVEDATA_FLASH_BASE = 0x0E005555 + SAVEDATA_FLASH_BASE = 0x0E005555, + + FLASH_BASE_HI = 0x5555, + FLASH_BASE_LO = 0x2AAA }; struct GBASavedata {@@ -35,6 +62,10 @@ int readAddress;
int writeAddress; int writePending; int addressBits; + + uint8_t* currentBank; + + enum FlashStateMachine flashState; }; void GBASavedataInit(struct GBASavedata* savedata, const char* filename);@@ -44,7 +75,8 @@ void GBASavedataInitFlash(struct GBASavedata* savedata);
void GBASavedataInitEEPROM(struct GBASavedata* savedata); void GBASavedataInitSRAM(struct GBASavedata* savedata); -void GBASavedataWriteFlash(struct GBASavedata* savedata, uint8_t value); +uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address); +void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8_t value); uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata); void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize);
M
src/gba/gba.h
→
src/gba/gba.h
@@ -33,7 +33,9 @@ GBA_LOG_STUB,
GBA_LOG_DEBUG, GBA_LOG_INFO, GBA_LOG_WARN, - GBA_LOG_ERROR + GBA_LOG_ERROR, + + GBA_LOG_GAME_ERROR }; enum GBAKey {