diff --git a/desmume/src/addons/slot2_gbagame.cpp b/desmume/src/addons/slot2_gbagame.cpp index fcc5e7c98..16b55897c 100644 --- a/desmume/src/addons/slot2_gbagame.cpp +++ b/desmume/src/addons/slot2_gbagame.cpp @@ -16,16 +16,416 @@ along with the this software. If not, see . */ +#include +#include "../mem.h" +#include "../MMU.h" +#include "../NDSSystem.h" +#include "../path.h" #include "../slot2.h" +#define EEPROM 0x52504545 +#define SRAM_ 0x4D415253 +#define FLASH 0x53414C46 +#define FLASH1M_ 0x5F4D3148 +#define SIIRTC_V 0x52494953 + +static const char *saveTypes[] = { + "EEPROM", + "SRAM", + "FLASH", + "FLASH1M", + "SIIRTC_V", +}; + class Slot2_GbaCart : public ISlot2Interface { +private: + FILE *fROM; + FILE *fSRAM; + u32 romSize; + u32 currentROMPos; + u32 sramSize; + u32 currentSRAMPos; + u32 saveType; + + struct + { + u32 size; + u8 state; + u8 cmd; + u8 idDevice; + u8 idManufacturer; + u8 bank; + } gbaFlash; + + u32 readRom(const u32 pos, const u8 size) + { + if (fROM) + { + if (currentROMPos != pos) + fseek(fROM, pos, SEEK_SET); + + u32 data = 0xFFFFFFFF; + u32 readed = fread(&data, 1, size, fROM); + currentROMPos = (pos + readed); + return data; + } + return 0xFFFFFFFF; + } + + u32 readSRAM(const u32 pos, const u8 size) + { + if (fSRAM) + { + if (currentSRAMPos != pos) + fseek(fSRAM, pos, SEEK_SET); + + u32 data = 0xFFFFFFFF; + u32 readed = fread(&data, 1, size, fSRAM); + currentSRAMPos = (pos + readed); + return data; + } + return 0xFFFFFFFF; + } + + void writeSRAM(const u32 pos, const u8 *data, u32 size) + { + if (fSRAM) + { + if (currentSRAMPos != pos) + fseek(fSRAM, pos, SEEK_SET); + + u32 writed = fwrite(&data, 1, size, fSRAM); + currentSRAMPos = (pos + writed); + } + } + + + u32 getSaveTypeGBA() + { + if (!fROM) return 0xFF; + + u32 saveROMPos = currentROMPos; + u32 tmp = 0; + + fseek(fROM, 0, SEEK_SET); + + while (!feof(fROM)) + { + u32 readed = fread(&tmp, 1, 4, fROM); + if (readed < 4) break; + + switch (tmp) + { + case EEPROM: fseek(fROM, saveROMPos, SEEK_SET); return 1; + case SRAM_: fseek(fROM, saveROMPos, SEEK_SET); return 2; + case FLASH: + { + fread(&tmp, 1, 4, fROM); + fseek(fROM, saveROMPos, SEEK_SET); + return ((tmp == FLASH1M_)?3:5); + } + case SIIRTC_V: fseek(fROM, saveROMPos, SEEK_SET); return 4; + } + } + + return 0xFF; + } + + void gbaWriteFlash(u32 adr, u8 val) + { + if (!fSRAM) return; + switch (gbaFlash.state) + { + case 0: + if (adr == 0x0A005555) + { + if (val == 0xF0) + { + //INFO("GBAgame: Flash: reset\n"); + gbaFlash.state = 0; + gbaFlash.cmd = 0; + return; + } + if (val == 0xAA) + { + gbaFlash.state = 1; + return; + } + } + + if (adr == 0x0A000000) + { + if (gbaFlash.cmd == 0xB0) + { + gbaFlash.bank = val; + gbaFlash.cmd = 0; + //INFO("GBAgame: Flash: change bank %i\n", val); + return; + } + } + break; + case 1: + if ( (adr == 0x0A002AAA) && (val == 0x55) ) + { + gbaFlash.state = 2; + return; + } + gbaFlash.state = 0; + break; + case 2: + if (adr == 0x0A005555) + { + //INFO("GBAgame: Flash: send command flash 0x%02X\n", val); + switch (val) + { + case 0x80: // Erase + gbaFlash.state = 0x80; + break; + + case 0x90: // Chip Identification + gbaFlash.state = 0x90; + break; + + case 0xA0: // Write + gbaFlash.state = 0; + break; + + default: + gbaFlash.state = 0; + break; + } + gbaFlash.cmd = val; + return; + } + gbaFlash.state = 0; + break; + + // erase + case 0x80: + if ( (adr == 0x0A005555) && (val == 0xAA) ) + { + gbaFlash.state = 0x81; + return; + } + gbaFlash.state = 0; + break; + + case 0x81: + if ( (adr == 0x0A002AAA) && (val == 0x55) ) + { + gbaFlash.state = 0x82; + return; + } + gbaFlash.state = 0; + break; + + case 0x82: + if (val == 0x30) + { + u32 ofs = (adr & 0x0000F000); + //INFO("GBAgame: Flash: erase from 0x%08X to 0x%08X\n", ofs + 0x0A000000, ofs + 0x0A001000); + u8 *tmp = new u8[0x1000]; + memset(tmp, 0xFF, 0x1000); + writeSRAM(ofs, tmp, 0x1000); + delete [] tmp; + } + gbaFlash.state = 0; + gbaFlash.cmd = 0; + return; + + // Chip Identification + case 0x90: + if ( (adr == 0x0A005555) && (val == 0xAA) ) + { + gbaFlash.state = 0x91; + return; + } + gbaFlash.state = 0; + break; + + case 0x91: + if ( (adr == 0x0A002AAA) && (val == 0x55) ) + { + gbaFlash.state = 0x92; + return; + } + gbaFlash.state = 0; + break; + + case 0x92: + gbaFlash.state = 0; + gbaFlash.cmd = 0; + return; + } + + if (gbaFlash.cmd == 0xA0) // write + { + writeSRAM((adr & 0x1FFFF) + (0x10000 * gbaFlash.bank), &val, 1); + gbaFlash.state = 0; + gbaFlash.cmd = 0; + return; + } + INFO("GBAgame: Flash: write unknown atn 0x%08X = 0x%02X\n", adr, val); + } + + u8 gbaReadFlash(u32 adr) + { + if (!fSRAM) return 0xFF; + if (gbaFlash.cmd == 0) + return readSRAM((adr & 0x1FFFF) + (0x10000 * gbaFlash.bank), 1); + + //INFO("GBAgame: flash read at 0x%08X\n", adr); + + switch (gbaFlash.cmd) + { + case 0x90: // Chip Identification + if (adr == 0x0A000000) return gbaFlash.idManufacturer; + if (adr == 0x0A000001) return gbaFlash.idDevice; + break; + + case 0xF0: // + gbaFlash.state = 0; + gbaFlash.cmd = 0; + break; + + case 0xB0: // Bank switching + break; + + default: + INFO("GBAgame: Flash: read - unknown command at 0x%08X = 0x%02X\n", adr, gbaFlash.cmd); + break; + } + + return 0xFF; + } + public: virtual Slot2Info const* info() { static Slot2InfoSimple info("GBA cartridge", "GBA cartridge in slot"); return &info; } + + virtual void connect() + { + if (fROM) fclose(fROM); + if (fSRAM) fclose(fSRAM); + fROM = NULL; fSRAM = NULL; + romSize = 0; + currentROMPos = 0; + sramSize = 0; + currentSRAMPos = 0; + + if (gameInfo.romsize == 0) return; + + if (!strcasecmp(GBAgameName, "self")) + strcpy(GBAgameName, path.path.c_str()); + + if (!strlen(GBAgameName)) return; + + printf("GBASlot opening ROM %s", GBAgameName); + + fROM = fopen(GBAgameName, "rb"); + if (fROM) + { + fseek(fROM, 0, SEEK_END); + romSize = ftell(fROM); + fseek(fROM, 0, SEEK_SET); + printf(" - Success (%u bytes)\n", romSize); + + //try loading the sram + char *dot = strrchr(GBAgameName, '.'); + if(!dot) return; + + std::string sram_fname = GBAgameName; + sram_fname.resize(dot-GBAgameName); + sram_fname += ".sav"; + fSRAM = fopen(sram_fname.c_str(), "rb+"); + if(!fSRAM) return; + fseek(fSRAM, 0, SEEK_END); + sramSize = ftell(fSRAM); + fseek(fSRAM, 0, SEEK_SET); + saveType = getSaveTypeGBA(); + printf("GBASlot found SRAM %s (%s - %u bytes)\n", sram_fname.c_str(), (saveType == 0xFF)?"Unknown":saveTypes[saveType], sramSize); + gbaFlash.size = sramSize; + if (gbaFlash.size <= (64 * 1024)) + { + gbaFlash.idDevice = 0x1B; + gbaFlash.idManufacturer = 0x32; + } + else + { + gbaFlash.idDevice = 0x09; + gbaFlash.idManufacturer = 0xC2; + } + } + else + printf(" - Failed\n"); + } + + virtual void disconnect() + { + if (fROM) fclose(fROM); + if (fSRAM) fclose(fSRAM); + fROM = NULL; fSRAM = NULL; + romSize = 0; + currentROMPos = 0; + sramSize = 0; + currentSRAMPos = 0; + } + + virtual void writeByte(u8 PROCNUM, u32 addr, u8 val) + { + if ((addr >= 0x0A000000) && (addr < 0x0A010000)) + { + switch (saveType) + { + case 3: // Flash + case 5: + gbaWriteFlash(addr, val); + break; + + default: break; + } + } + } + + virtual u8 readByte(u8 PROCNUM, u32 addr) + { + if (addr < 0x0A000000) + return (u8)readRom(addr - 0x08000000, 1); + + if (addr < 0x0A010000) + { + if ((saveType == 3) || (saveType == 5)) + return gbaReadFlash(addr); + + return (u8)readSRAM(addr - 0x0A000000, 1); + } + + return 0xFF; + } + virtual u16 readWord(u8 PROCNUM, u32 addr) + { + if (addr < 0x0A000000) + return (u16)readRom(addr - 0x08000000, 2); + + if (addr < 0x0A010000) + return (u16)readSRAM(addr - 0x0A000000, 2); + + return 0xFFFF; + } + virtual u32 readLong(u8 PROCNUM, u32 addr) + { + if (addr < 0x0A000000) + return (u32)readRom(addr - 0x08000000, 4); + + if (addr < 0x0A010000) + return (u32)readSRAM(addr - 0x0A000000, 4); + + return 0xFFFFFFFF; + } }; ISlot2Interface* construct_Slot2_GbaCart() { return new Slot2_GbaCart(); }