diff --git a/src/gba/gba-memory.c b/src/gba/gba-memory.c index 9bfbeaaa9..dce3214be 100644 --- a/src/gba/gba-memory.c +++ b/src/gba/gba-memory.c @@ -40,6 +40,8 @@ void GBAMemoryInit(struct GBAMemory* memory) { memory->p->errstr = GBA_CANNOT_MMAP; } + GBASavedataInit(&memory->savedata, "test.sav"); + int i; for (i = 0; i < 16; ++i) { memory->waitstates16[i] = GBA_BASE_WAITSTATES[i]; @@ -65,6 +67,7 @@ void GBAMemoryInit(struct GBAMemory* memory) { void GBAMemoryDeinit(struct GBAMemory* memory) { munmap(memory->wram, SIZE_WORKING_RAM); munmap(memory->iwram, SIZE_WORKING_IRAM); + GBASavedataDeinit(&memory->savedata); } static void GBASetActiveRegion(struct ARMMemory* memory, uint32_t address) { @@ -230,7 +233,10 @@ int8_t GBALoad8(struct ARMMemory* memory, uint32_t address) { case BASE_CART2_EX: return ((int8_t*) gbaMemory->rom)[address & (SIZE_CART0 - 1)]; case BASE_CART_SRAM: - break; + if (gbaMemory->savedata.type == SAVEDATA_NONE) { + GBASavedataInitSRAM(&gbaMemory->savedata); + } + return gbaMemory->savedata.data[address & (SIZE_CART_SRAM - 1)]; default: break; } @@ -266,7 +272,10 @@ uint8_t GBALoadU8(struct ARMMemory* memory, uint32_t address) { case BASE_CART2_EX: return ((uint8_t*) gbaMemory->rom)[address & (SIZE_CART0 - 1)]; case BASE_CART_SRAM: - break; + if (gbaMemory->savedata.type == SAVEDATA_NONE) { + GBASavedataInitSRAM(&gbaMemory->savedata); + } + return gbaMemory->savedata.data[address & (SIZE_CART_SRAM - 1)]; default: break; } @@ -367,6 +376,18 @@ void GBAStore8(struct ARMMemory* memory, uint32_t address, int8_t value) { case BASE_CART2_EX: break; case BASE_CART_SRAM: + if (gbaMemory->savedata.type == SAVEDATA_NONE) { + if (address == SAVEDATA_FLASH_BASE) { + GBASavedataInitFlash(&gbaMemory->savedata); + } else { + GBASavedataInitSRAM(&gbaMemory->savedata); + } + } + if (gbaMemory->savedata.type == SAVEDATA_FLASH512 || gbaMemory->savedata.type == SAVEDATA_FLASH1M) { + GBASavedataWriteFlash(&gbaMemory->savedata, value); + } else if (gbaMemory->savedata.type == SAVEDATA_SRAM) { + gbaMemory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value; + } break; default: break; diff --git a/src/gba/gba-memory.h b/src/gba/gba-memory.h index 8a30b91e8..72d0653f5 100644 --- a/src/gba/gba-memory.h +++ b/src/gba/gba-memory.h @@ -3,6 +3,8 @@ #include "arm.h" +#include "gba-savedata.h" + enum GBAMemoryRegion { REGION_BIOS = 0x0, REGION_WORKING_RAM = 0x2, @@ -108,6 +110,8 @@ struct GBAMemory { uint32_t* rom; uint16_t io[SIZE_IO >> 1]; + struct GBASavedata savedata; + char waitstates32[256]; char waitstates16[256]; char waitstatesSeq32[256]; diff --git a/src/gba/gba-savedata.c b/src/gba/gba-savedata.c new file mode 100644 index 000000000..e669b499a --- /dev/null +++ b/src/gba/gba-savedata.c @@ -0,0 +1,80 @@ +#include "gba-savedata.h" + +#include "gba.h" + +#include +#include +#include +#include + +void GBASavedataInit(struct GBASavedata* savedata, const char* filename) { + savedata->type = SAVEDATA_NONE; + savedata->data = 0; + savedata->fd = -1; + savedata->filename = filename; +} + +void GBASavedataDeinit(struct GBASavedata* savedata) { + switch (savedata->type) { + case SAVEDATA_SRAM: + munmap(savedata->data, SIZE_CART_SRAM); + break; + case SAVEDATA_FLASH512: + munmap(savedata->data, SIZE_CART_FLASH512); + break; + case SAVEDATA_FLASH1M: + munmap(savedata->data, SIZE_CART_FLASH1M); + break; + case SAVEDATA_EEPROM: + munmap(savedata->data, SIZE_CART_EEPROM); + break; + default: + break; + } + close(savedata->fd); + savedata->type = SAVEDATA_NONE; +} + +void GBASavedataInitFlash(struct GBASavedata* savedata) { + savedata->type = SAVEDATA_FLASH512; + savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666); + if (savedata->fd < 0) { + GBALog(GBA_LOG_WARN, "Cannot open savedata file %s", savedata->filename); + return; + } + // mmap enough so that we can expand the file if we need to + savedata->data = mmap(0, SIZE_CART_FLASH1M, PROT_READ | PROT_WRITE, 0, savedata->fd, 0); + + off_t end = lseek(savedata->fd, 0, SEEK_END); + if (end < SIZE_CART_FLASH512) { + ftruncate(savedata->fd, SIZE_CART_SRAM); + memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end); + } +} + +void GBASavedataInitSRAM(struct GBASavedata* savedata) { + savedata->type = SAVEDATA_SRAM; + savedata->fd = open(savedata->filename, O_RDWR | O_CREAT, 0666); + off_t end; + int flags = MAP_SHARED; + if (savedata->fd < 0) { + GBALog(GBA_LOG_WARN, "Cannot open savedata file %s", savedata->filename); + end = 0; + flags |= MAP_ANON; + } else { + end = lseek(savedata->fd, 0, SEEK_END); + if (end < SIZE_CART_SRAM) { + ftruncate(savedata->fd, SIZE_CART_SRAM); + } + } + savedata->data = mmap(0, SIZE_CART_SRAM, PROT_READ | PROT_WRITE, flags, savedata->fd, 0); + if (end < SIZE_CART_SRAM) { + memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end); + } +} + +void GBASavedataWriteFlash(struct GBASavedata* savedata, uint8_t value) { + (void)(savedata); + (void)(value); + GBALog(GBA_LOG_STUB, "Flash memory unimplemented"); +} diff --git a/src/gba/gba-savedata.h b/src/gba/gba-savedata.h new file mode 100644 index 000000000..106144fb7 --- /dev/null +++ b/src/gba/gba-savedata.h @@ -0,0 +1,33 @@ +#ifndef GBA_SAVEDATA_H +#define GBA_SAVEDATA_H + +#include + +enum SavedataType { + SAVEDATA_NONE = 0, + SAVEDATA_SRAM, + SAVEDATA_FLASH512, + SAVEDATA_FLASH1M, + SAVEDATA_EEPROM +}; + +enum { + SAVEDATA_FLASH_BASE = 0x0E005555 +}; + +struct GBASavedata { + enum SavedataType type; + uint8_t* data; + const char* filename; + int fd; +}; + +void GBASavedataInit(struct GBASavedata* savedata, const char* filename); +void GBASavedataDeinit(struct GBASavedata* savedata); + +void GBASavedataInitFlash(struct GBASavedata* savedata); +void GBASavedataInitSRAM(struct GBASavedata* savedata); + +void GBASavedataWriteFlash(struct GBASavedata* savedata, uint8_t value); + +#endif