diff --git a/desmume/src/addons/gbagame.cpp b/desmume/src/addons/gbagame.cpp index e68252432..f0dbcf4c3 100644 --- a/desmume/src/addons/gbagame.cpp +++ b/desmume/src/addons/gbagame.cpp @@ -29,12 +29,217 @@ //SRAM is going to be stored just above the rom. //that is convenient for us, since it mirrors the nds memory map +#define GBA_ROMSIZE (32 * 1024 * 1024) + 1 +#define GBA_SAVESIZE (512 * 1024) + 1 static u8 *GBArom = NULL; +static u8 *saveData = NULL; +static u8 saveType = 0xFF; -#define GBA_ROMSIZE (32 * 1024 * 1024) -#define GBA_RAMSIZE (64 * 1024) -#define GBA_SIZE (GBA_ROMSIZE+GBA_RAMSIZE) +//================================================================================== Flash GBA +typedef struct +{ + u8 state; + u8 cmd; + u32 size; + u8 idDevice; + u8 idManufacturer; + u8 bank; +} FLASH_GBA; + +FLASH_GBA gbaFlash = {0}; + +static void gbaWriteFlash(u32 adr, u8 val) +{ + switch (gbaFlash.state) + { + case 0: + if (adr == 0x0A005555) + { + if (val == 0xF0) + { + //INFO("GBAslot: 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("GBAslot: 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("GBAslot: Flash: send command flash 0x%02X\n", val); + switch (val) + { + case 0x80: // Erase + gbaFlash.state = 0x80; + 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("GBAslot: Flash: erase from 0x%08X to 0x%08X\n", ofs + 0x0A000000, ofs + 0x0A001000); + for (int i = ofs; i < (ofs + 0x1000); i++) + saveData[i] = 0xFF; + } + gbaFlash.state = 0; + gbaFlash.cmd = 0; + return; + } + + if (gbaFlash.cmd == 0xA0) // write + { + saveData[(adr & 0x1FFFF)+(0x10000*gbaFlash.bank)] = val; + gbaFlash.state = 0; + gbaFlash.cmd = 0; + return; + } + //INFO("GBAslot: Flash: write unknown atn 0x%08X = 0x%02X\n", adr, val); +} + +static u8 gbaReadFlash(u32 adr) +{ + if (gbaFlash.cmd == 0) + { + //INFO("GBAslot: flash read at 0x%08X = 0x%02X\n", adr, saveData[(adr & 0x1FFFF)+(0x10000*gbaFlash.bank)]); + return saveData[(adr & 0x1FFFF)+(0x10000*gbaFlash.bank)]; + } + + //INFO("GBAslot: 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: // + //INFO("GBAslot: Flash: reset2\n"); + gbaFlash.state = 0; + gbaFlash.cmd = 0; + break; + + case 0xB0: // Bank switching + break; + + default: + INFO("GBAslot: Flash: read - unknown command at 0x%08X = 0x%02X\n", adr, gbaFlash.cmd); + break; + } + + return 0xFF; +} +//================================================================================== + +static u8 getSaveTypeGBA(const u8 *data, const u32 size) +{ + u8 type = 0; + u8 *dat = (u8 *)data; + + for (int i = 0; i < (size / 4); i++) + { + u32 tmp = T1ReadLong(dat, i); + + if (tmp == 0x52504545) + { + if(memcmp(dat, "EEPROM_", 7) == 0) + { + return 1; + } + } + + if (tmp == 0x4D415253) + { + if(memcmp(dat, "SRAM_", 5) == 0) + { + return 2; + } + } + + if (tmp == 0x53414C46) + { + if(memcmp(dat, "FLASH1M_", 8) == 0) + { + return 3; + } + } + + if (tmp == 0x52494953) + { + if(memcmp(dat, "SIIRTC_V", 8) == 0) + { + return 4; + } + } + + if(memcmp(dat, "FLASH", 5) == 0) + { + return 5; + } + dat += 4; + } + + return 0xFF; // NONE +} static BOOL GBAgame_init(void) { @@ -43,20 +248,29 @@ static BOOL GBAgame_init(void) static void GBAgame_reset(void) { + memset(&gbaFlash, 0, sizeof(gbaFlash)); + if (GBArom) { delete [] GBArom; GBArom = NULL; } - GBArom = new u8 [GBA_SIZE]; - memset(GBArom, 0, GBA_SIZE); + GBArom = new u8 [GBA_ROMSIZE]; + memset(GBArom, 0xFF, GBA_ROMSIZE); + + if (saveData) + { + delete [] saveData; + saveData = NULL; + } + saveData = new u8 [GBA_SAVESIZE]; + memset(saveData, 0xFF, GBA_SAVESIZE); if (!strlen(GBAgameName)) return; FILE *fgame = 0; fgame = fopen(GBAgameName,"rb"); if (!fgame) return; - INFO("Loaded \"%s\" in GBA slot\n", GBAgameName); fseek(fgame, 0, SEEK_END); u32 size = ftell(fgame); rewind(fgame); @@ -66,9 +280,11 @@ static void GBAgame_reset(void) fclose(fgame); return; } - fclose(fgame); + saveType = getSaveTypeGBA(GBArom, size); + INFO("Loaded \"%s\" in GBA slot (save type %i)\n", GBAgameName, saveType); + //try loading the sram char * dot = strrchr(GBAgameName,'.'); if(!dot) return; @@ -76,11 +292,32 @@ static void GBAgame_reset(void) sram_fname.resize(dot-GBAgameName); sram_fname += ".sav"; fgame = fopen(sram_fname.c_str(),"rb"); - if(fgame) + if(!fgame) return; + + fseek(fgame, 0, SEEK_END); + size = ftell(fgame); + rewind(fgame); + + if (!fread(saveData, 1, size, fgame)) { - fread(GBArom+GBA_ROMSIZE,1,GBA_RAMSIZE,fgame); fclose(fgame); + return; } + fclose(fgame); + + gbaFlash.size = size; + if (gbaFlash.size <= (64 * 1024)) + { + gbaFlash.idDevice = 0x1B; + gbaFlash.idManufacturer = 0x32; + } + else + { + gbaFlash.idDevice = 0x09; + gbaFlash.idManufacturer = 0xC2; + } + + INFO("Loaded save \"%s\" in GBA slot\n", sram_fname.c_str()); } static void GBAgame_close(void) @@ -90,35 +327,109 @@ static void GBAgame_close(void) delete [] GBArom; GBArom = NULL; } + + if (saveData) + { + delete [] saveData; + saveData = NULL; + } } static void GBAgame_config(void) {} -static void GBAgame_write08(u32 adr, u8 val){} -static void GBAgame_write16(u32 adr, u16 val) {} -static void GBAgame_write32(u32 adr, u32 val) {} + +static void GBAgame_write08(u32 adr, u8 val) +{ + //INFO("GBAslot: write08 at 0x%08X val=0x%02X\n", adr, val); + if ( (adr >= 0x0A000000) && (adr < 0x0A010000) ) + { + switch (saveType) + { + case 3: // Flash + case 5: + gbaWriteFlash(adr, val); + break; + + default: + break; + } + //return (u8)T1ReadByte(saveData, (adr - 0x0A000000)); + } +} +static void GBAgame_write16(u32 adr, u16 val) +{ + //INFO("GBAgame: write16 at 0x%08X val=0x%04X\n", adr, val); +} + +static void GBAgame_write32(u32 adr, u32 val) +{ + //INFO("GBAgame: write32 at 0x%08X val=0x%08X\n", adr, val); +} static u8 GBAgame_read08(u32 adr) { - //INFO("Read08 at 0x%08X value 0x%02X\n", adr, (u8)T1ReadByte(GBArom, (adr - 0x08000000))); if ( (adr >= 0x08000004) && (adr < 0x080000A0) ) return MMU.MMU_MEM[0][0xFF][(adr +0x1C) & MMU.MMU_MASK[0][0xFF]]; - return (u8)T1ReadByte(GBArom, (adr - 0x08000000)); + + //INFO("GBAgame: read08 at 0x%08X value 0x%02X\n", adr, (u8)T1ReadByte(GBArom, (adr - 0x08000000))); + + if (adr < 0x0A000000) + return (u8)T1ReadByte(GBArom, (adr - 0x08000000)); + + if (adr < 0x0A010000) + { + switch (saveType) + { + case 3: // Flash + case 5: + return gbaReadFlash(adr); + + default: + break; + } + + //INFO("Read08 at 0x%08X val=0x%08X\n", adr, (u8)T1ReadByte(GBArom, (adr - 0x08000000)) ); + return (u8)T1ReadByte(saveData, (adr - 0x0A000000)); + } + + //INFO("Read08 at 0x%08X val=0x%08X\n", adr, (u8)T1ReadByte(GBArom, (adr - 0x08000000)) ); + + return 0xFF; } static u16 GBAgame_read16(u32 adr) { - //INFO("Read16 at 0x%08X value 0x%04X\n", adr, (u16)T1ReadWord(GBArom, (adr - 0x08000000))); if ( (adr >= 0x08000004) && (adr < 0x080000A0) ) return T1ReadWord(MMU.MMU_MEM[0][0xFF], (adr +0x1C) & MMU.MMU_MASK[0][0xFF]); - return (u16)T1ReadWord(GBArom, (adr - 0x08000000)); + + //INFO("GBAgame: read16 at 0x%08X value 0x%04X\n", adr, (u16)T1ReadWord(GBArom, (adr - 0x08000000))); + + if (adr < 0x0A000000) + return (u16)T1ReadWord(GBArom, (adr - 0x08000000)); + + if (adr < 0x0A010000) + { + //INFO("GBAslot: flash read16 at 0x%08X\n", adr); + return (u16)T1ReadWord(saveData, (adr - 0x0A000000)); + } + return 0xFFFF; } static u32 GBAgame_read32(u32 adr) { - //INFO("Read32 at 0x%08X value 0x%08X\n", adr, (u32)T1ReadLong(GBArom, (adr - 0x08000000))); if ( (adr >= 0x08000004) && (adr < 0x080000A0) ) return T1ReadLong(MMU.MMU_MEM[0][0xFF], (adr +0x1C) & MMU.MMU_MASK[0][0xFF]); - return (u32)T1ReadLong(GBArom, (adr - 0x08000000)); + + //INFO("GBAgame: read32 at 0x%08X value 0x%08X\n", adr, (u32)T1ReadLong(GBArom, (adr - 0x08000000))); + + if (adr < 0x0A000000) + return (u32)T1ReadLong(GBArom, (adr - 0x08000000)); + + if (adr < 0x0A010000) + { + //INFO("GBAslot: flash read32 at 0x%08X\n", adr); + return (u32)T1ReadLong(saveData, (adr - 0x0A000000)); + } + return 0xFFFFFFFF; } static void GBAgame_info(char *info)