From 1846a712659ed31357e8ae795055dace0bdd951d Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 25 Apr 2021 00:48:02 +0200 Subject: [PATCH] Cart refactor (#1073) complete cart-interface refactor, will make this code a lot easier to deal with --- src/DSi_I2C.cpp | 7 +- src/DSi_SD.cpp | 14 +- src/GBACart.cpp | 570 +++++----- src/GBACart.h | 158 ++- src/NDS.cpp | 648 ++++++----- src/NDS.h | 2 +- src/NDSCart.cpp | 1986 ++++++++++++++++++++-------------- src/NDSCart.h | 161 ++- src/ROMList.h | 6 +- src/Savestate.h | 4 +- src/frontend/Util_ROM.cpp | 2 +- src/frontend/qt_sdl/main.cpp | 22 +- 12 files changed, 2152 insertions(+), 1428 deletions(-) diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index 16ec1a8a..ad01a42c 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -79,14 +79,15 @@ void Start() u8 Read(bool last) { + //printf("BPTWL: read %02X -> %02X @ %08X\n", CurPos, Registers[CurPos], NDS::GetPC(1)); + u8 ret = Registers[CurPos++]; + if (last) { CurPos = -1; - return 0; } - //printf("BPTWL: read %02X -> %02X\n", CurPos, Registers[CurPos]); - return Registers[CurPos++]; + return ret; } void Write(u8 val, bool last) diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index aba477d4..01b64428 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -464,6 +464,10 @@ u16 DSi_SDHost::Read(u32 addr) case 0x102: return 0; case 0x104: return BlockLen32; case 0x108: return BlockCount32; + + // dunno + case 0x106: return 0; + case 0x10A: return 0; } printf("unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1)); @@ -626,6 +630,10 @@ void DSi_SDHost::Write(u32 addr, u16 val) case 0x102: return; case 0x104: BlockLen32 = val & 0x03FF; return; case 0x108: BlockCount32 = val; return; + + // dunno + case 0x106: return; + case 0x10A: return; } printf("unknown %s write %08X %04X\n", SD_DESC, addr, val); @@ -847,8 +855,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) } RWCommand = 18; Host->SendResponse(CSR, true); - ReadBlock(RWAddress); - RWAddress += BlockSize; + RWAddress += ReadBlock(RWAddress); SetState(0x05); return; @@ -862,8 +869,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) } RWCommand = 25; Host->SendResponse(CSR, true); - WriteBlock(RWAddress); - RWAddress += BlockSize; + RWAddress += WriteBlock(RWAddress); SetState(0x04); return; diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 65ce8f1c..246f986a 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -23,77 +23,105 @@ #include "Platform.h" -namespace GBACart_SRAM +namespace GBACart { -enum SaveType { - S_NULL, - S_EEPROM4K, - S_EEPROM64K, - S_SRAM256K, - S_FLASH512K, - S_FLASH1M +const char SOLAR_SENSOR_GAMECODES[10][5] = +{ + "U3IJ", // Bokura no Taiyou - Taiyou Action RPG (Japan) + "U3IE", // Boktai - The Sun Is in Your Hand (USA) + "U3IP", // Boktai - The Sun Is in Your Hand (Europe) + "U32J", // Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan) + "U32E", // Boktai 2 - Solar Boy Django (USA) + "U32P", // Boktai 2 - Solar Boy Django (Europe) + "U33J", // Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan) + "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) }; -// from DeSmuME -struct FlashProperties + +bool CartInserted; +//bool HasSolarSensor; +u8* CartROM; +u32 CartROMSize; +u32 CartCRC; +u32 CartID; +//GPIO CartGPIO; // overridden GPIO parameters + +CartCommon* Cart; + + +CartCommon::CartCommon() { - u8 state; - u8 cmd; - u8 device; - u8 manufacturer; - u8 bank; -}; - -u8* SRAM; -FILE* SRAMFile; -u32 SRAMLength; -SaveType SRAMType; -FlashProperties SRAMFlashState; - -char SRAMPath[1024]; - -void (*WriteFunc)(u32 addr, u8 val); - - -void Write_Null(u32 addr, u8 val); -void Write_EEPROM(u32 addr, u8 val); -void Write_SRAM(u32 addr, u8 val); -void Write_Flash(u32 addr, u8 val); - - -bool Init() -{ - SRAM = NULL; - SRAMFile = NULL; - return true; } -void DeInit() +CartCommon::~CartCommon() { - if (SRAMFile) fclose(SRAMFile); - if (SRAM) delete[] SRAM; } -void Reset() +void CartCommon::DoSavestate(Savestate* file) { - // do nothing, we don't want to clear GBA SRAM on reset + file->Section("GBCS"); } -void Eject() +void CartCommon::LoadSave(const char* path, u32 type) { - if (SRAMFile) fclose(SRAMFile); - if (SRAM) delete[] SRAM; - SRAM = NULL; - SRAMFile = NULL; +} + +void CartCommon::RelocateSave(const char* path, bool write) +{ +} + +int CartCommon::SetInput(int num, bool pressed) +{ + return -1; +} + +u16 CartCommon::ROMRead(u32 addr) +{ + return 0; +} + +void CartCommon::ROMWrite(u32 addr, u16 val) +{ +} + +u8 CartCommon::SRAMRead(u32 addr) +{ + return 0; +} + +void CartCommon::SRAMWrite(u32 addr, u8 val) +{ +} + + +CartGame::CartGame(u8* rom, u32 len) : CartCommon() +{ + ROM = rom; + ROMLength = len; + + memset(&GPIO, 0, sizeof(GPIO)); + + SRAM = nullptr; + SRAMFile = nullptr; SRAMLength = 0; SRAMType = S_NULL; SRAMFlashState = {}; } -void DoSavestate(Savestate* file) +CartGame::~CartGame() { - file->Section("GBCS"); // Game Boy [Advance] Cart Save + if (SRAMFile) fclose(SRAMFile); + if (SRAM) delete[] SRAM; +} + +void CartGame::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + file->Var16(&GPIO.control); + file->Var16(&GPIO.data); + file->Var16(&GPIO.direction); // logic mostly copied from NDSCart_SRAM @@ -117,8 +145,8 @@ void DoSavestate(Savestate* file) // no save data, clear the current state SRAMType = SaveType::S_NULL; if (SRAMFile) fclose(SRAMFile); - SRAM = NULL; - SRAMFile = NULL; + SRAM = nullptr; + SRAMFile = nullptr; return; } @@ -132,7 +160,7 @@ void DoSavestate(Savestate* file) file->Var8((u8*)&SRAMType); } -void LoadSave(const char* path) +void CartGame::LoadSave(const char* path, u32 type) { if (SRAM) delete[] SRAM; @@ -157,29 +185,23 @@ void LoadSave(const char* path) { case 512: SRAMType = S_EEPROM4K; - WriteFunc = Write_EEPROM; break; case 8192: SRAMType = S_EEPROM64K; - WriteFunc = Write_EEPROM; break; case 32768: SRAMType = S_SRAM256K; - WriteFunc = Write_SRAM; break; case 65536: SRAMType = S_FLASH512K; - WriteFunc = Write_Flash; break; case 128*1024: SRAMType = S_FLASH1M; - WriteFunc = Write_Flash; break; default: - printf("!! BAD SAVE LENGTH %d\n", SRAMLength); + printf("!! BAD GBA SAVE LENGTH %d\n", SRAMLength); case 0: SRAMType = S_NULL; - WriteFunc = Write_Null; break; } @@ -197,11 +219,11 @@ void LoadSave(const char* path) } } -void RelocateSave(const char* path, bool write) +void CartGame::RelocateSave(const char* path, bool write) { if (!write) { - LoadSave(path); // lazy + LoadSave(path, 0); // lazy return; } @@ -219,8 +241,114 @@ void RelocateSave(const char* path, bool write) fwrite(SRAM, SRAMLength, 1, SRAMFile); } +u16 CartGame::ROMRead(u32 addr) +{ + addr &= 0x01FFFFFF; + + if (addr >= 0xC4 && addr < 0xCA) + { + if (GPIO.control & 0x1) + { + switch (addr) + { + case 0xC4: return GPIO.data; + case 0xC6: return GPIO.direction; + case 0xC8: return GPIO.control; + } + } + else + return 0; + } + + // CHECKME: does ROM mirror? + if (addr < ROMLength) + return *(u16*)&ROM[addr]; + + return 0; +} + +void CartGame::ROMWrite(u32 addr, u16 val) +{ + addr &= 0x01FFFFFF; + + switch (addr) + { + case 0xC4: + GPIO.data &= ~GPIO.direction; + GPIO.data |= val & GPIO.direction; + ProcessGPIO(); + break; + + case 0xC6: + GPIO.direction = val; + break; + + case 0xC8: + GPIO.control = val; + break; + + default: + printf("Unknown GBA GPIO write 0x%02X @ 0x%04X\n", val, addr); + break; + } +} + +u8 CartGame::SRAMRead(u32 addr) +{ + addr &= 0xFFFF; + + switch (SRAMType) + { + case S_EEPROM4K: + case S_EEPROM64K: + return SRAMRead_EEPROM(addr); + + case S_FLASH512K: + case S_FLASH1M: + return SRAMRead_FLASH(addr); + + case S_SRAM256K: + return SRAMRead_SRAM(addr); + } + + return 0xFF; +} + +void CartGame::SRAMWrite(u32 addr, u8 val) +{ + addr &= 0xFFFF; + + switch (SRAMType) + { + case S_EEPROM4K: + case S_EEPROM64K: + return SRAMWrite_EEPROM(addr, val); + + case S_FLASH512K: + case S_FLASH1M: + return SRAMWrite_FLASH(addr, val); + + case S_SRAM256K: + return SRAMWrite_SRAM(addr, val); + } +} + +void CartGame::ProcessGPIO() +{ +} + +u8 CartGame::SRAMRead_EEPROM(u32 addr) +{ + return 0; +} + +void CartGame::SRAMWrite_EEPROM(u32 addr, u8 val) +{ + // TODO: could be used in homebrew? +} + // mostly ported from DeSmuME -u8 Read_Flash(u32 addr) +u8 CartGame::SRAMRead_FLASH(u32 addr) { if (SRAMFlashState.cmd == 0) // no cmd { @@ -249,15 +377,8 @@ u8 Read_Flash(u32 addr) return 0xFF; } -void Write_Null(u32 addr, u8 val) {} - -void Write_EEPROM(u32 addr, u8 val) -{ - // TODO: could be used in homebrew? -} - // mostly ported from DeSmuME -void Write_Flash(u32 addr, u8 val) +void CartGame::SRAMWrite_FLASH(u32 addr, u8 val) { switch (SRAMFlashState.state) { @@ -380,7 +501,7 @@ void Write_Flash(u32 addr, u8 val) if (SRAMFlashState.cmd == 0xA0) // write { - Write_SRAM(addr + 0x10000 * SRAMFlashState.bank, val); + SRAMWrite_SRAM(addr + 0x10000 * SRAMFlashState.bank, val); SRAMFlashState.state = 0; SRAMFlashState.cmd = 0; return; @@ -390,10 +511,18 @@ void Write_Flash(u32 addr, u8 val) val, addr, SRAMFlashState.state); } -void Write_SRAM(u32 addr, u8 val) +u8 CartGame::SRAMRead_SRAM(u32 addr) { - u8 prev = *(u8*)&SRAM[addr]; + if (addr >= SRAMLength) return 0xFF; + return SRAM[addr]; +} + +void CartGame::SRAMWrite_SRAM(u32 addr, u8 val) +{ + if (addr >= SRAMLength) return; + + u8 prev = *(u8*)&SRAM[addr]; if (prev != val) { *(u8*)&SRAM[addr] = val; @@ -406,115 +535,80 @@ void Write_SRAM(u32 addr, u8 val) } } -u8 Read8(u32 addr) + +const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183}; + +CartGameSolarSensor::CartGameSolarSensor(u8* rom, u32 len) : CartGame(rom, len) { - if (SRAMType == S_NULL) + LightEdge = false; + LightCounter = 0; + LightSample = 0xFF; + LightLevel = 0; +} + +CartGameSolarSensor::~CartGameSolarSensor() +{ +} + +void CartGameSolarSensor::DoSavestate(Savestate* file) +{ + CartGame::DoSavestate(file); + + file->Var8((u8*)&LightEdge); + file->Var8(&LightCounter); + file->Var8(&LightSample); + file->Var8(&LightLevel); +} + +int CartGameSolarSensor::SetInput(int num, bool pressed) +{ + if (!pressed) return -1; + + if (num == Input_SolarSensorDown) { - return 0xFF; + if (LightLevel > 0) + LightLevel--; + + return LightLevel; + } + else if (num == Input_SolarSensorUp) + { + if (LightLevel < 10) + LightLevel++; + + return LightLevel; } - if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M) + return -1; +} + +void CartGameSolarSensor::ProcessGPIO() +{ + if (GPIO.data & 4) return; // Boktai chip select + if (GPIO.data & 2) // Reset { - return Read_Flash(addr); + u8 prev = LightSample; + LightCounter = 0; + LightSample = (0xFF - (0x16 + kLuxLevels[LightLevel])); + printf("Solar sensor reset (sample: 0x%02X -> 0x%02X)\n", prev, LightSample); } + if (GPIO.data & 1 && LightEdge) LightCounter++; - return *(u8*)&SRAM[addr]; -} + LightEdge = !(GPIO.data & 1); -u16 Read16(u32 addr) -{ - if (SRAMType == S_NULL) + bool sendBit = LightCounter >= LightSample; + if (GPIO.control & 1) { - return 0xFFFF; + GPIO.data = (GPIO.data & GPIO.direction) | ((sendBit << 3) & ~GPIO.direction & 0xF); } - - if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M) - { - u16 val = Read_Flash(addr + 0) | - (Read_Flash(addr + 1) << 8); - return val; - } - - return *(u16*)&SRAM[addr]; } -u32 Read32(u32 addr) -{ - if (SRAMType == S_NULL) - { - return 0xFFFFFFFF; - } - - if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M) - { - u32 val = Read_Flash(addr + 0) | - (Read_Flash(addr + 1) << 8) | - (Read_Flash(addr + 2) << 16) | - (Read_Flash(addr + 3) << 24); - return val; - } - - return *(u32*)&SRAM[addr]; -} - -void Write8(u32 addr, u8 val) -{ - u8 prev = *(u8*)&SRAM[addr]; - - WriteFunc(addr, val); -} - -void Write16(u32 addr, u16 val) -{ - u16 prev = *(u16*)&SRAM[addr]; - - WriteFunc(addr + 0, val & 0xFF); - WriteFunc(addr + 1, val >> 8 & 0xFF); -} - -void Write32(u32 addr, u32 val) -{ - u32 prev = *(u32*)&SRAM[addr]; - - WriteFunc(addr + 0, val & 0xFF); - WriteFunc(addr + 1, val >> 8 & 0xFF); - WriteFunc(addr + 2, val >> 16 & 0xFF); - WriteFunc(addr + 3, val >> 24 & 0xFF); -} - -} - - -namespace GBACart -{ - -const char SOLAR_SENSOR_GAMECODES[10][5] = -{ - "U3IJ", // Bokura no Taiyou - Taiyou Action RPG (Japan) - "U3IE", // Boktai - The Sun Is in Your Hand (USA) - "U3IP", // Boktai - The Sun Is in Your Hand (Europe) - "U32J", // Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan) - "U32E", // Boktai 2 - Solar Boy Django (USA) - "U32P", // Boktai 2 - Solar Boy Django (Europe) - "U33J", // Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan) - "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) -}; - - -bool CartInserted; -bool HasSolarSensor; -u8* CartROM; -u32 CartROMSize; -u32 CartCRC; -u32 CartID; -GPIO CartGPIO; // overridden GPIO parameters - bool Init() { - if (!GBACart_SRAM::Init()) return false; + CartROM = nullptr; - CartROM = NULL; + Cart = nullptr; return true; } @@ -523,7 +617,7 @@ void DeInit() { if (CartROM) delete[] CartROM; - GBACart_SRAM::DeInit(); + if (Cart) delete Cart; } void Reset() @@ -533,9 +627,6 @@ void Reset() // This allows resetting a DS game without losing GBA state, // and resetting to firmware without the slot being emptied. // The Stop function will clear the cartridge state via Eject(). - - GBACart_SRAM::Reset(); - GBACart_SolarSensor::Reset(); } void Eject() @@ -543,14 +634,14 @@ void Eject() if (CartROM) delete[] CartROM; CartInserted = false; - HasSolarSensor = false; CartROM = NULL; CartROMSize = 0; CartCRC = 0; CartID = 0; - CartGPIO = {}; - GBACart_SRAM::Eject(); + if (Cart) delete Cart; + Cart = nullptr; + Reset(); } @@ -579,13 +670,6 @@ void DoSavestate(Savestate* file) // delete and reallocate ROM so that it is zero-padded to its full length if (CartROM) delete[] CartROM; CartROM = new u8[CartROMSize]; - - // clear the SRAM file handle; writes will not be committed - if (GBACart_SRAM::SRAMFile) - { - fclose(GBACart_SRAM::SRAMFile); - GBACart_SRAM::SRAMFile = NULL; - } } // only save/load the cartridge header @@ -608,42 +692,44 @@ void DoSavestate(Savestate* file) file->Var32(&CartCRC); file->Var32(&CartID); - file->Var8((u8*)&HasSolarSensor); - - file->Var16(&CartGPIO.control); - file->Var16(&CartGPIO.data); - file->Var16(&CartGPIO.direction); - // now do the rest - GBACart_SRAM::DoSavestate(file); - if (HasSolarSensor) GBACart_SolarSensor::DoSavestate(file); + if (Cart) Cart->DoSavestate(file); } void LoadROMCommon(const char *sram) { char gamecode[5] = { '\0' }; memcpy(&gamecode, CartROM + 0xAC, 4); - printf("Game code: %s\n", gamecode); + printf("GBA game code: %s\n", gamecode); + bool solarsensor = false; for (int i = 0; i < sizeof(SOLAR_SENSOR_GAMECODES)/sizeof(SOLAR_SENSOR_GAMECODES[0]); i++) { - if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) HasSolarSensor = true; + if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) + solarsensor = true; } - if (HasSolarSensor) + if (solarsensor) { printf("GBA solar sensor support detected!\n"); } CartCRC = CRC32(CartROM, CartROMSize); - printf("ROM CRC32: %08X\n", CartCRC); + printf("GBA ROM CRC32: %08X\n", CartCRC); CartInserted = true; + if (solarsensor) + Cart = new CartGameSolarSensor(CartROM, CartROMSize); + else + Cart = new CartGame(CartROM, CartROMSize); + // save - printf("Save file: %s\n", sram); - GBACart_SRAM::LoadSave(sram); + printf("GBA save file: %s\n", sram); + + // TODO: have a list of sorts like in NDSCart? to determine the savemem type + if (Cart) Cart->LoadSave(sram, 0); } bool LoadROM(const char* path, const char* sram) @@ -693,98 +779,40 @@ bool LoadROM(const u8* romdata, u32 filelength, const char *sram) void RelocateSave(const char* path, bool write) { - // derp herp - GBACart_SRAM::RelocateSave(path, write); + if (Cart) Cart->RelocateSave(path, write); } -// referenced from mGBA -void WriteGPIO(u32 addr, u16 val) + +int SetInput(int num, bool pressed) { - switch (addr) - { - case 0xC4: - CartGPIO.data &= ~CartGPIO.direction; - CartGPIO.data |= val & CartGPIO.direction; - if (HasSolarSensor) GBACart_SolarSensor::Process(&CartGPIO); - break; - case 0xC6: - CartGPIO.direction = val; - break; - case 0xC8: - CartGPIO.control = val; - break; - default: - printf("Unknown GBA GPIO write 0x%02X @ 0x%04X\n", val, addr); - } - - // write the GPIO values in the ROM (if writable) - if (CartGPIO.control & 1) - { - *(u16*)&CartROM[0xC4] = CartGPIO.data; - *(u16*)&CartROM[0xC6] = CartGPIO.direction; - *(u16*)&CartROM[0xC8] = CartGPIO.control; - } - else - { - // GBATEK: "in write-only mode, reads return 00h (or [possibly] other data (...))" - // ambiguous, but mGBA sets ROM to 00h when switching to write-only, so do the same - *(u16*)&CartROM[0xC4] = 0; - *(u16*)&CartROM[0xC6] = 0; - *(u16*)&CartROM[0xC8] = 0; - } -} + if (Cart) return Cart->SetInput(num, pressed); + return -1; } -namespace GBACart_SolarSensor +u16 ROMRead(u32 addr) { + if (Cart) return Cart->ROMRead(addr); -bool LightEdge; -u8 LightCounter; -u8 LightSample; -u8 LightLevel; // 0-10 range + return (addr >> 1) & 0xFFFF; +} -// levels from mGBA -const int GBA_LUX_LEVELS[11] = { 0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 }; -#define LIGHT_VALUE (0xFF - (0x16 + GBA_LUX_LEVELS[LightLevel])) - - -void Reset() +void ROMWrite(u32 addr, u16 val) { - LightEdge = false; - LightCounter = 0; - LightSample = 0xFF; - LightLevel = 0; + if (Cart) Cart->ROMWrite(addr, val); } -void DoSavestate(Savestate* file) +u8 SRAMRead(u32 addr) { - file->Var8((u8*)&LightEdge); - file->Var8(&LightCounter); - file->Var8(&LightSample); - file->Var8(&LightLevel); + if (Cart) return Cart->SRAMRead(addr); + + return 0xFF; } -void Process(GBACart::GPIO* gpio) +void SRAMWrite(u32 addr, u8 val) { - if (gpio->data & 4) return; // Boktai chip select - if (gpio->data & 2) // Reset - { - u8 prev = LightSample; - LightCounter = 0; - LightSample = LIGHT_VALUE; - printf("Solar sensor reset (sample: 0x%02X -> 0x%02X)\n", prev, LightSample); - } - if (gpio->data & 1 && LightEdge) LightCounter++; - - LightEdge = !(gpio->data & 1); - - bool sendBit = LightCounter >= LightSample; - if (gpio->control & 1) - { - gpio->data = (gpio->data & gpio->direction) | ((sendBit << 3) & ~gpio->direction & 0xF); - } + if (Cart) Cart->SRAMWrite(addr, val); } } diff --git a/src/GBACart.h b/src/GBACart.h index 7f5f9162..1f4d258c 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -22,39 +22,128 @@ #include "types.h" #include "Savestate.h" - -namespace GBACart_SRAM -{ - -extern u8* SRAM; -extern u32 SRAMLength; - -void Reset(); -void DoSavestate(Savestate* file); - -u8 Read8(u32 addr); -u16 Read16(u32 addr); -u32 Read32(u32 addr); - -void Write8(u32 addr, u8 val); -void Write16(u32 addr, u16 val); -void Write32(u32 addr, u32 val); - -} - - namespace GBACart { -struct GPIO +// CartCommon -- base code shared by all cart types +class CartCommon { - u16 data; - u16 direction; - u16 control; +public: + CartCommon(); + virtual ~CartCommon(); + + virtual void DoSavestate(Savestate* file); + + virtual void LoadSave(const char* path, u32 type); + virtual void RelocateSave(const char* path, bool write); + + virtual int SetInput(int num, bool pressed); + + virtual u16 ROMRead(u32 addr); + virtual void ROMWrite(u32 addr, u16 val); + + virtual u8 SRAMRead(u32 addr); + virtual void SRAMWrite(u32 addr, u8 val); +}; + +// CartGame -- regular retail game cart (ROM, SRAM) +class CartGame : public CartCommon +{ +public: + CartGame(u8* rom, u32 len); + virtual ~CartGame() override; + + virtual void DoSavestate(Savestate* file) override; + + virtual void LoadSave(const char* path, u32 type) override; + virtual void RelocateSave(const char* path, bool write) override; + + virtual u16 ROMRead(u32 addr) override; + virtual void ROMWrite(u32 addr, u16 val) override; + + virtual u8 SRAMRead(u32 addr) override; + virtual void SRAMWrite(u32 addr, u8 val) override; + +protected: + virtual void ProcessGPIO(); + + u8 SRAMRead_EEPROM(u32 addr); + void SRAMWrite_EEPROM(u32 addr, u8 val); + u8 SRAMRead_FLASH(u32 addr); + void SRAMWrite_FLASH(u32 addr, u8 val); + u8 SRAMRead_SRAM(u32 addr); + void SRAMWrite_SRAM(u32 addr, u8 val); + + u8* ROM; + u32 ROMLength; + + struct + { + u16 data; + u16 direction; + u16 control; + + } GPIO; + + enum SaveType + { + S_NULL, + S_EEPROM4K, + S_EEPROM64K, + S_SRAM256K, + S_FLASH512K, + S_FLASH1M + }; + + // from DeSmuME + struct + { + u8 state; + u8 cmd; + u8 device; + u8 manufacturer; + u8 bank; + + } SRAMFlashState; + + u8* SRAM; + FILE* SRAMFile; + u32 SRAMLength; + SaveType SRAMType; + + char SRAMPath[1024]; +}; + +// CartGameSolarSensor -- Boktai game cart +class CartGameSolarSensor : public CartGame +{ +public: + CartGameSolarSensor(u8* rom, u32 len); + virtual ~CartGameSolarSensor() override; + + virtual void DoSavestate(Savestate* file) override; + + virtual int SetInput(int num, bool pressed) override; + +private: + virtual void ProcessGPIO() override; + + static const int kLuxLevels[11]; + + bool LightEdge; + u8 LightCounter; + u8 LightSample; + u8 LightLevel; +}; + +// possible inputs for GBA carts that might accept user input +enum +{ + Input_SolarSensorDown = 0, + Input_SolarSensorUp, }; extern bool CartInserted; -extern bool HasSolarSensor; extern u8* CartROM; extern u32 CartROMSize; extern u32 CartCRC; @@ -69,19 +158,14 @@ bool LoadROM(const char* path, const char* sram); bool LoadROM(const u8* romdata, u32 filelength, const char *sram); void RelocateSave(const char* path, bool write); -void WriteGPIO(u32 addr, u16 val); +// TODO: make more flexible, support nonbinary inputs +int SetInput(int num, bool pressed); -} +u16 ROMRead(u32 addr); +void ROMWrite(u32 addr, u16 val); - -namespace GBACart_SolarSensor -{ - -extern u8 LightLevel; - -void Reset(); -void DoSavestate(Savestate* file); -void Process(GBACart::GPIO* gpio); +u8 SRAMRead(u32 addr); +void SRAMWrite(u32 addr, u8 val); } diff --git a/src/NDS.cpp b/src/NDS.cpp index 12412eb3..fa37c39b 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1943,19 +1943,12 @@ u8 ARM9Read8(u32 addr) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return *(u8*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)]; - } - return 0xFF; // TODO: proper open bus + if (addr & 0x1) return GBACart::ROMRead(addr-1) >> 8; + return GBACart::ROMRead(addr) & 0xFF; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return GBACart_SRAM::Read8(addr & (GBACart_SRAM::SRAMLength-1)); - } - return 0xFF; // TODO: proper open bus + return GBACart::SRAMRead(addr); } printf("unknown arm9 read8 %08X\n", addr); @@ -2008,22 +2001,15 @@ u16 ARM9Read16(u32 addr) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return *(u16*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)]; - } - return 0xFFFF; // TODO: proper open bus + return GBACart::ROMRead(addr); case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return GBACart_SRAM::Read16(addr & (GBACart_SRAM::SRAMLength-1)); - } - return 0xFFFF; // TODO: proper open bus + return GBACart::SRAMRead(addr) | + (GBACart::SRAMRead(addr+1) << 8); } - //printf("unknown arm9 read16 %08X %08X\n", addr, ARM9->R[15]); + if (addr) printf("unknown arm9 read16 %08X %08X\n", addr, ARM9->R[15]); return 0; } @@ -2073,19 +2059,15 @@ u32 ARM9Read32(u32 addr) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return *(u32*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)]; - } - return 0xFFFFFFFF; // TODO: proper open bus + return GBACart::ROMRead(addr) | + (GBACart::ROMRead(addr+2) << 16); case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return GBACart_SRAM::Read32(addr & (GBACart_SRAM::SRAMLength-1)); - } - return 0xFFFFFFFF; // TODO: proper open bus + return GBACart::SRAMRead(addr) | + (GBACart::SRAMRead(addr+1) << 8) | + (GBACart::SRAMRead(addr+2) << 16) | + (GBACart::SRAMRead(addr+3) << 24); } printf("unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9->R[15], ARM9->R[12]); @@ -2120,28 +2102,15 @@ void ARM9Write8(u32 addr, u8 val) case 0x05000000: case 0x06000000: case 0x07000000: - // checkme return; case 0x08000000: case 0x09000000: - if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - if ((addr & 0x00FFFFFF) >= 0xC4 && (addr & 0x00FFFFFF) <= 0xC9) - { - GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val); - return; - } - } - break; + return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - GBACart_SRAM::Write8(addr & (GBACart_SRAM::SRAMLength-1), val); - } + GBACart::SRAMWrite(addr, val); return; } @@ -2199,28 +2168,17 @@ void ARM9Write16(u32 addr, u16 val) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - // Note: the lower bound is adjusted such that a write starting - // there will hit the first byte of the GPIO region. - if ((addr & 0x00FFFFFF) >= 0xC3 && (addr & 0x00FFFFFF) <= 0xC9) - { - GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val); - return; - } - } - break; + GBACart::ROMWrite(addr, val); + return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - GBACart_SRAM::Write16(addr & (GBACart_SRAM::SRAMLength-1), val); - } + GBACart::SRAMWrite(addr, val & 0xFF); + GBACart::SRAMWrite(addr+1, val >> 8); return; } - //printf("unknown arm9 write16 %08X %04X\n", addr, val); + if (addr) printf("unknown arm9 write16 %08X %04X\n", addr, val); } void ARM9Write32(u32 addr, u32 val) @@ -2274,29 +2232,20 @@ void ARM9Write32(u32 addr, u32 val) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - // Note: the lower bound is adjusted such that a write starting - // there will hit the first byte of the GPIO region. - if ((addr & 0x00FFFFFF) >= 0xC1 && (addr & 0x00FFFFFF) <= 0xC9) - { - GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val & 0xFF); - GBACart::WriteGPIO((addr + 2) & (GBACart::CartROMSize-1), (val >> 16) & 0xFF); - return; - } - } - break; + GBACart::ROMWrite(addr, val & 0xFFFF); + GBACart::ROMWrite(addr+2, val >> 16); + return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - GBACart_SRAM::Write32(addr & (GBACart_SRAM::SRAMLength-1), val); - } + GBACart::SRAMWrite(addr, val & 0xFF); + GBACart::SRAMWrite(addr+1, (val >> 8) & 0xFF); + GBACart::SRAMWrite(addr+2, (val >> 16) & 0xFF); + GBACart::SRAMWrite(addr+3, val >> 24); return; } - //printf("unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]); + printf("unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]); } bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) @@ -2366,26 +2315,30 @@ u8 ARM7Read8(u32 addr) case 0x04000000: return ARM7IORead8(addr); + case 0x04800000: + if (addr < 0x04810000) + { + if (addr & 0x1) return Wifi::Read(addr-1) >> 8; + return Wifi::Read(addr) & 0xFF; + } + break; + case 0x06000000: case 0x06800000: return GPU::ReadVRAM_ARM7(addr); case 0x08000000: + case 0x08800000: case 0x09000000: + case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return *(u8*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)]; - } - return 0xFF; // TODO: proper open bus + if (addr & 0x1) return GBACart::ROMRead(addr-1) >> 8; + return GBACart::ROMRead(addr) & 0xFF; case 0x0A000000: + case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return GBACart_SRAM::Read8(addr & (GBACart_SRAM::SRAMLength-1)); - } - return 0xFF; // TODO: proper open bus + return GBACart::SRAMRead(addr); } printf("unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7->R[15], ARM7->R[0], ARM7->R[1]); @@ -2438,21 +2391,17 @@ u16 ARM7Read16(u32 addr) return GPU::ReadVRAM_ARM7(addr); case 0x08000000: + case 0x08800000: case 0x09000000: + case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return *(u16*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)]; - } - return 0xFFFF; // TODO: proper open bus + return GBACart::ROMRead(addr); case 0x0A000000: + case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return GBACart_SRAM::Read16(addr & (GBACart_SRAM::SRAMLength-1)); - } - return 0xFFFF; // TODO: proper open bus + return GBACart::SRAMRead(addr) | + (GBACart::SRAMRead(addr+1) << 8); } printf("unknown arm7 read16 %08X %08X\n", addr, ARM7->R[15]); @@ -2505,21 +2454,20 @@ u32 ARM7Read32(u32 addr) return GPU::ReadVRAM_ARM7(addr); case 0x08000000: + case 0x08800000: case 0x09000000: + case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return *(u32*)&GBACart::CartROM[addr & (GBACart::CartROMSize-1)]; - } - return 0xFFFFFFFF; // TODO: proper open bus + return GBACart::ROMRead(addr) | + (GBACart::ROMRead(addr+2) << 16); case 0x0A000000: + case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled - if (GBACart::CartInserted) - { - return GBACart_SRAM::Read32(addr & (GBACart_SRAM::SRAMLength-1)); - } - return 0xFFFFFFFF; // TODO: proper open bus + return GBACart::SRAMRead(addr) | + (GBACart::SRAMRead(addr+1) << 8) | + (GBACart::SRAMRead(addr+2) << 16) | + (GBACart::SRAMRead(addr+3) << 24); } printf("unknown arm7 read32 %08X | %08X\n", addr, ARM7->R[15]); @@ -2576,24 +2524,15 @@ void ARM7Write8(u32 addr, u8 val) return; case 0x08000000: + case 0x08800000: case 0x09000000: - if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - if ((addr & 0x00FFFFFF) >= 0xC4 && (addr & 0x00FFFFFF) <= 0xC9) - { - GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val); - return; - } - } - break; + case 0x09800000: + return; case 0x0A000000: + case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - GBACart_SRAM::Write8(addr & (GBACart_SRAM::SRAMLength-1), val); - } + GBACart::SRAMWrite(addr, val); return; } @@ -2659,30 +2598,22 @@ void ARM7Write16(u32 addr, u16 val) return; case 0x08000000: + case 0x08800000: case 0x09000000: + case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - // Note: the lower bound is adjusted such that a write starting - // there will hit the first byte of the GPIO region. - if ((addr & 0x00FFFFFF) >= 0xC3 && (addr & 0x00FFFFFF) <= 0xC9) - { - GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val); - return; - } - } - break; + GBACart::ROMWrite(addr, val); + return; case 0x0A000000: + case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - GBACart_SRAM::Write16(addr & (GBACart_SRAM::SRAMLength-1), val); - } + GBACart::SRAMWrite(addr, val & 0xFF); + GBACart::SRAMWrite(addr+1, val >> 8); return; } - //printf("unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]); + printf("unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]); } void ARM7Write32(u32 addr, u32 val) @@ -2744,31 +2675,25 @@ void ARM7Write32(u32 addr, u32 val) return; case 0x08000000: + case 0x08800000: case 0x09000000: + case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - // Note: the lower bound is adjusted such that a write starting - // there will hit the first byte of the GPIO region. - if ((addr & 0x00FFFFFF) >= 0xC1 && (addr & 0x00FFFFFF) <= 0xC9) - { - GBACart::WriteGPIO(addr & (GBACart::CartROMSize-1), val & 0xFF); - GBACart::WriteGPIO((addr + 2) & (GBACart::CartROMSize-1), (val >> 16) & 0xFF); - return; - } - } - break; + GBACart::ROMWrite(addr, val & 0xFFFF); + GBACart::ROMWrite(addr+2, val >> 16); + return; case 0x0A000000: + case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - if (GBACart::CartInserted) - { - GBACart_SRAM::Write32(addr & (GBACart_SRAM::SRAMLength-1), val); - } + GBACart::SRAMWrite(addr, val & 0xFF); + GBACart::SRAMWrite(addr+1, (val >> 8) & 0xFF); + GBACart::SRAMWrite(addr+2, (val >> 16) & 0xFF); + GBACart::SRAMWrite(addr+3, val >> 24); return; } - //printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]); + printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]); } bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) @@ -2838,16 +2763,43 @@ u8 ARM9IORead8(u32 addr) case 0x04000132: return KeyCnt & 0xFF; case 0x04000133: return KeyCnt >> 8; - case 0x040001A2: return NDSCart::ReadSPIData(); + case 0x040001A2: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ReadSPIData(); + return 0; - case 0x040001A8: return NDSCart::ROMCommand[0]; - case 0x040001A9: return NDSCart::ROMCommand[1]; - case 0x040001AA: return NDSCart::ROMCommand[2]; - case 0x040001AB: return NDSCart::ROMCommand[3]; - case 0x040001AC: return NDSCart::ROMCommand[4]; - case 0x040001AD: return NDSCart::ROMCommand[5]; - case 0x040001AE: return NDSCart::ROMCommand[6]; - case 0x040001AF: return NDSCart::ROMCommand[7]; + case 0x040001A8: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[0]; + return 0; + case 0x040001A9: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[1]; + return 0; + case 0x040001AA: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[2]; + return 0; + case 0x040001AB: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[3]; + return 0; + case 0x040001AC: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[4]; + return 0; + case 0x040001AD: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[5]; + return 0; + case 0x040001AE: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[6]; + return 0; + case 0x040001AF: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[7]; + return 0; case 0x04000208: return IME[0]; @@ -2949,17 +2901,35 @@ u16 ARM9IORead16(u32 addr) return val; } - case 0x040001A0: return NDSCart::SPICnt; - case 0x040001A2: return NDSCart::ReadSPIData(); + case 0x040001A0: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::SPICnt; + return 0; + case 0x040001A2: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ReadSPIData(); + return 0; - case 0x040001A8: return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8); - case 0x040001AA: return NDSCart::ROMCommand[2] | - (NDSCart::ROMCommand[3] << 8); - case 0x040001AC: return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8); - case 0x040001AE: return NDSCart::ROMCommand[6] | - (NDSCart::ROMCommand[7] << 8); + case 0x040001A8: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[0] | + (NDSCart::ROMCommand[1] << 8); + return 0; + case 0x040001AA: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[2] | + (NDSCart::ROMCommand[3] << 8); + return 0; + case 0x040001AC: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[4] | + (NDSCart::ROMCommand[5] << 8); + return 0; + case 0x040001AE: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[6] | + (NDSCart::ROMCommand[7] << 8); + return 0; case 0x04000204: return ExMemCnt[0]; case 0x04000208: return IME[0]; @@ -3058,17 +3028,29 @@ u32 ARM9IORead32(u32 addr) case 0x04000180: return IPCSync9; case 0x04000184: return ARM9IORead16(addr); - case 0x040001A0: return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16); - case 0x040001A4: return NDSCart::ROMCnt; + case 0x040001A0: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16); + return 0; + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCnt; + return 0; - case 0x040001A8: return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8) | - (NDSCart::ROMCommand[2] << 16) | - (NDSCart::ROMCommand[3] << 24); - case 0x040001AC: return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8) | - (NDSCart::ROMCommand[6] << 16) | - (NDSCart::ROMCommand[7] << 24); + case 0x040001A8: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[0] | + (NDSCart::ROMCommand[1] << 8) | + (NDSCart::ROMCommand[2] << 16) | + (NDSCart::ROMCommand[3] << 24); + return 0; + case 0x040001AC: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCart::ROMCommand[4] | + (NDSCart::ROMCommand[5] << 8) | + (NDSCart::ROMCommand[6] << 16) | + (NDSCart::ROMCommand[7] << 24); + return 0; case 0x04000208: return IME[0]; case 0x04000210: return IE[0]; @@ -3155,34 +3137,31 @@ void ARM9IOWrite8(u32 addr, u8 val) KeyCnt = (KeyCnt & 0x00FF) | (val << 8); return; - case 0x040001A0: - if (!(ExMemCnt[0] & (1<<11))) - { - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0xFF00) | val); - } - return; - case 0x040001A1: - if (!(ExMemCnt[0] & (1<<11))) - { - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0x00FF) | (val << 8)); - } - return; - case 0x040001A2: - NDSCart::WriteSPIData(val); - return; - case 0x04000188: ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); return; - case 0x040001A8: NDSCart::ROMCommand[0] = val; return; - case 0x040001A9: NDSCart::ROMCommand[1] = val; return; - case 0x040001AA: NDSCart::ROMCommand[2] = val; return; - case 0x040001AB: NDSCart::ROMCommand[3] = val; return; - case 0x040001AC: NDSCart::ROMCommand[4] = val; return; - case 0x040001AD: NDSCart::ROMCommand[5] = val; return; - case 0x040001AE: NDSCart::ROMCommand[6] = val; return; - case 0x040001AF: NDSCart::ROMCommand[7] = val; return; + case 0x040001A0: + if (!(ExMemCnt[0] & (1<<11))) + NDSCart::WriteSPICnt((NDSCart::SPICnt & 0xFF00) | val); + return; + case 0x040001A1: + if (!(ExMemCnt[0] & (1<<11))) + NDSCart::WriteSPICnt((NDSCart::SPICnt & 0x00FF) | (val << 8)); + return; + case 0x040001A2: + if (!(ExMemCnt[0] & (1<<11))) + NDSCart::WriteSPIData(val); + return; + + case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[0] = val; return; + case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[1] = val; return; + case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[2] = val; return; + case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[3] = val; return; + case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[4] = val; return; + case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[5] = val; return; + case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[6] = val; return; + case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[7] = val; return; case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return; @@ -3296,27 +3275,41 @@ void ARM9IOWrite16(u32 addr, u16 val) return; case 0x040001A0: - if (!(ExMemCnt[0] & (1<<11))) NDSCart::WriteSPICnt(val); + if (!(ExMemCnt[0] & (1<<11))) + NDSCart::WriteSPICnt(val); return; case 0x040001A2: - NDSCart::WriteSPIData(val & 0xFF); + if (!(ExMemCnt[0] & (1<<11))) + NDSCart::WriteSPIData(val & 0xFF); return; case 0x040001A8: - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = val >> 8; + if (!(ExMemCnt[0] & (1<<11))) + { + NDSCart::ROMCommand[0] = val & 0xFF; + NDSCart::ROMCommand[1] = val >> 8; + } return; case 0x040001AA: - NDSCart::ROMCommand[2] = val & 0xFF; - NDSCart::ROMCommand[3] = val >> 8; + if (!(ExMemCnt[0] & (1<<11))) + { + NDSCart::ROMCommand[2] = val & 0xFF; + NDSCart::ROMCommand[3] = val >> 8; + } return; case 0x040001AC: - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = val >> 8; + if (!(ExMemCnt[0] & (1<<11))) + { + NDSCart::ROMCommand[4] = val & 0xFF; + NDSCart::ROMCommand[5] = val >> 8; + } return; case 0x040001AE: - NDSCart::ROMCommand[6] = val & 0xFF; - NDSCart::ROMCommand[7] = val >> 8; + if (!(ExMemCnt[0] & (1<<11))) + { + NDSCart::ROMCommand[6] = val & 0xFF; + NDSCart::ROMCommand[7] = val >> 8; + } return; case 0x040001B8: ROMSeed0[4] = val & 0x7F; return; @@ -3470,20 +3463,27 @@ void ARM9IOWrite32(u32 addr, u32 val) } return; case 0x040001A4: - if (!(ExMemCnt[0] & (1<<11))) NDSCart::WriteROMCnt(val); + if (!(ExMemCnt[0] & (1<<11))) + NDSCart::WriteROMCnt(val); return; case 0x040001A8: - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[2] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[3] = val >> 24; + if (!(ExMemCnt[0] & (1<<11))) + { + NDSCart::ROMCommand[0] = val & 0xFF; + NDSCart::ROMCommand[1] = (val >> 8) & 0xFF; + NDSCart::ROMCommand[2] = (val >> 16) & 0xFF; + NDSCart::ROMCommand[3] = val >> 24; + } return; case 0x040001AC: - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[6] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[7] = val >> 24; + if (!(ExMemCnt[0] & (1<<11))) + { + NDSCart::ROMCommand[4] = val & 0xFF; + NDSCart::ROMCommand[5] = (val >> 8) & 0xFF; + NDSCart::ROMCommand[6] = (val >> 16) & 0xFF; + NDSCart::ROMCommand[7] = val >> 24; + } return; case 0x040001B0: *(u32*)&ROMSeed0[0] = val; return; @@ -3528,7 +3528,7 @@ void ARM9IOWrite32(u32 addr, u32 val) return; case 0x04100010: - NDSCart::WriteROMData(val); + if (!(ExMemCnt[0] & (1<<11))) NDSCart::WriteROMData(val); return; } @@ -3567,16 +3567,43 @@ u8 ARM7IORead8(u32 addr) case 0x04000138: return RTC::Read() & 0xFF; - case 0x040001A2: return NDSCart::ReadSPIData(); + case 0x040001A2: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ReadSPIData(); + return 0; - case 0x040001A8: return NDSCart::ROMCommand[0]; - case 0x040001A9: return NDSCart::ROMCommand[1]; - case 0x040001AA: return NDSCart::ROMCommand[2]; - case 0x040001AB: return NDSCart::ROMCommand[3]; - case 0x040001AC: return NDSCart::ROMCommand[4]; - case 0x040001AD: return NDSCart::ROMCommand[5]; - case 0x040001AE: return NDSCart::ROMCommand[6]; - case 0x040001AF: return NDSCart::ROMCommand[7]; + case 0x040001A8: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[0]; + return 0; + case 0x040001A9: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[1]; + return 0; + case 0x040001AA: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[2]; + return 0; + case 0x040001AB: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[3]; + return 0; + case 0x040001AC: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[4]; + return 0; + case 0x040001AD: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[5]; + return 0; + case 0x040001AE: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[6]; + return 0; + case 0x040001AF: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[7]; + return 0; case 0x040001C2: return SPI::ReadData(); @@ -3640,17 +3667,29 @@ u16 ARM7IORead16(u32 addr) return val; } - case 0x040001A0: return NDSCart::SPICnt; - case 0x040001A2: return NDSCart::ReadSPIData(); + case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCart::SPICnt; return 0; + case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCart::ReadSPIData(); return 0; - case 0x040001A8: return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8); - case 0x040001AA: return NDSCart::ROMCommand[2] | - (NDSCart::ROMCommand[3] << 8); - case 0x040001AC: return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8); - case 0x040001AE: return NDSCart::ROMCommand[6] | - (NDSCart::ROMCommand[7] << 8); + case 0x040001A8: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[0] | + (NDSCart::ROMCommand[1] << 8); + return 0; + case 0x040001AA: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[2] | + (NDSCart::ROMCommand[3] << 8); + return 0; + case 0x040001AC: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[4] | + (NDSCart::ROMCommand[5] << 8); + return 0; + case 0x040001AE: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[6] | + (NDSCart::ROMCommand[7] << 8); + return 0; case 0x040001C0: return SPI::Cnt; case 0x040001C2: return SPI::ReadData(); @@ -3707,17 +3746,29 @@ u32 ARM7IORead32(u32 addr) case 0x04000180: return IPCSync7; case 0x04000184: return ARM7IORead16(addr); - case 0x040001A0: return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16); - case 0x040001A4: return NDSCart::ROMCnt; + case 0x040001A0: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16); + return 0; + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCnt; + return 0; - case 0x040001A8: return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8) | - (NDSCart::ROMCommand[2] << 16) | - (NDSCart::ROMCommand[3] << 24); - case 0x040001AC: return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8) | - (NDSCart::ROMCommand[6] << 16) | - (NDSCart::ROMCommand[7] << 24); + case 0x040001A8: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[0] | + (NDSCart::ROMCommand[1] << 8) | + (NDSCart::ROMCommand[2] << 16) | + (NDSCart::ROMCommand[3] << 24); + return 0; + case 0x040001AC: + if (ExMemCnt[0] & (1<<11)) + return NDSCart::ROMCommand[4] | + (NDSCart::ROMCommand[5] << 8) | + (NDSCart::ROMCommand[6] << 16) | + (NDSCart::ROMCommand[7] << 24); + return 0; case 0x040001C0: return SPI::Cnt | (SPI::ReadData() << 16); @@ -3794,22 +3845,21 @@ void ARM7IOWrite8(u32 addr, u8 val) return; case 0x040001A1: if (ExMemCnt[0] & (1<<11)) - { NDSCart::WriteSPICnt((NDSCart::SPICnt & 0x00FF) | (val << 8)); - } return; case 0x040001A2: - NDSCart::WriteSPIData(val); + if (ExMemCnt[0] & (1<<11)) + NDSCart::WriteSPIData(val); return; - case 0x040001A8: NDSCart::ROMCommand[0] = val; return; - case 0x040001A9: NDSCart::ROMCommand[1] = val; return; - case 0x040001AA: NDSCart::ROMCommand[2] = val; return; - case 0x040001AB: NDSCart::ROMCommand[3] = val; return; - case 0x040001AC: NDSCart::ROMCommand[4] = val; return; - case 0x040001AD: NDSCart::ROMCommand[5] = val; return; - case 0x040001AE: NDSCart::ROMCommand[6] = val; return; - case 0x040001AF: NDSCart::ROMCommand[7] = val; return; + case 0x040001A8: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[0] = val; return; + case 0x040001A9: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[1] = val; return; + case 0x040001AA: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[2] = val; return; + case 0x040001AB: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[3] = val; return; + case 0x040001AC: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[4] = val; return; + case 0x040001AD: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[5] = val; return; + case 0x040001AE: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[6] = val; return; + case 0x040001AF: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[7] = val; return; case 0x040001C2: SPI::WriteData(val); @@ -3903,24 +3953,37 @@ void ARM7IOWrite16(u32 addr, u16 val) NDSCart::WriteSPICnt(val); return; case 0x040001A2: - NDSCart::WriteSPIData(val & 0xFF); + if (ExMemCnt[0] & (1<<11)) + NDSCart::WriteSPIData(val & 0xFF); return; case 0x040001A8: - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = val >> 8; + if (ExMemCnt[0] & (1<<11)) + { + NDSCart::ROMCommand[0] = val & 0xFF; + NDSCart::ROMCommand[1] = val >> 8; + } return; case 0x040001AA: - NDSCart::ROMCommand[2] = val & 0xFF; - NDSCart::ROMCommand[3] = val >> 8; + if (ExMemCnt[0] & (1<<11)) + { + NDSCart::ROMCommand[2] = val & 0xFF; + NDSCart::ROMCommand[3] = val >> 8; + } return; case 0x040001AC: - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = val >> 8; + if (ExMemCnt[0] & (1<<11)) + { + NDSCart::ROMCommand[4] = val & 0xFF; + NDSCart::ROMCommand[5] = val >> 8; + } return; case 0x040001AE: - NDSCart::ROMCommand[6] = val & 0xFF; - NDSCart::ROMCommand[7] = val >> 8; + if (ExMemCnt[0] & (1<<11)) + { + NDSCart::ROMCommand[6] = val & 0xFF; + NDSCart::ROMCommand[7] = val >> 8; + } return; case 0x040001B8: ROMSeed0[12] = val & 0x7F; return; @@ -4040,20 +4103,27 @@ void ARM7IOWrite32(u32 addr, u32 val) } return; case 0x040001A4: - if (ExMemCnt[0] & (1<<11)) NDSCart::WriteROMCnt(val); + if (ExMemCnt[0] & (1<<11)) + NDSCart::WriteROMCnt(val); return; case 0x040001A8: - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[2] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[3] = val >> 24; + if (ExMemCnt[0] & (1<<11)) + { + NDSCart::ROMCommand[0] = val & 0xFF; + NDSCart::ROMCommand[1] = (val >> 8) & 0xFF; + NDSCart::ROMCommand[2] = (val >> 16) & 0xFF; + NDSCart::ROMCommand[3] = val >> 24; + } return; case 0x040001AC: - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[6] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[7] = val >> 24; + if (ExMemCnt[0] & (1<<11)) + { + NDSCart::ROMCommand[4] = val & 0xFF; + NDSCart::ROMCommand[5] = (val >> 8) & 0xFF; + NDSCart::ROMCommand[6] = (val >> 16) & 0xFF; + NDSCart::ROMCommand[7] = val >> 24; + } return; case 0x040001B0: *(u32*)&ROMSeed0[8] = val; return; @@ -4074,6 +4144,10 @@ void ARM7IOWrite32(u32 addr, u32 val) if (ARM7BIOSProt == 0) ARM7BIOSProt = val & 0xFFFE; return; + + case 0x04100010: + if (ExMemCnt[0] & (1<<11)) NDSCart::WriteROMData(val); + return; } if (addr >= 0x04000400 && addr < 0x04000520) diff --git a/src/NDS.h b/src/NDS.h index c73a2ca4..8aa1397a 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -82,7 +82,7 @@ enum IRQ_IPCSync, IRQ_IPCSendDone, IRQ_IPCRecv, - IRQ_CartSendDone, // TODO: less misleading name + IRQ_CartXferDone, IRQ_CartIREQMC, // IRQ triggered by game cart (example: Pokémon Typing Adventure, BT controller) IRQ_GXFIFO, IRQ_LidOpen, diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 56a454a5..b40529bc 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -29,449 +29,19 @@ #include "melonDLDI.h" #include "NDSCart_SRAMManager.h" -namespace NDSCart_SRAM -{ - -u8* SRAM; -u32 SRAMLength; - -char SRAMPath[1024]; -bool SRAMFileDirty; - -void (*WriteFunc)(u8 val, bool islast); - -u32 Hold; -u8 CurCmd; -u32 DataPos; -u8 Data; - -u8 StatusReg; -u32 Addr; - - -void Write_Null(u8 val, bool islast); -void Write_EEPROMTiny(u8 val, bool islast); -void Write_EEPROM(u8 val, bool islast); -void Write_Flash(u8 val, bool islast); - - -bool Init() -{ - SRAM = NULL; - return true; -} - -void DeInit() -{ - if (SRAM) delete[] SRAM; -} - -void Reset() -{ - if (SRAM) delete[] SRAM; - SRAM = NULL; -} - -void DoSavestate(Savestate* file) -{ - file->Section("NDCS"); - - // we reload the SRAM contents. - // it should be the same file (as it should be the same ROM, duh) - // but the contents may change - - //if (!file->Saving && SRAMLength) - // delete[] SRAM; - - u32 oldlen = SRAMLength; - - file->Var32(&SRAMLength); - if (SRAMLength != oldlen) - { - printf("savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength); - printf("oh well. loading it anyway. adsfgdsf\n"); - - if (oldlen) delete[] SRAM; - if (SRAMLength) SRAM = new u8[SRAMLength]; - } - if (SRAMLength) - { - //if (!file->Saving) - // SRAM = new u8[SRAMLength]; - - file->VarArray(SRAM, SRAMLength); - } - - // SPI status shito - - file->Var32(&Hold); - file->Var8(&CurCmd); - file->Var32(&DataPos); - file->Var8(&Data); - - file->Var8(&StatusReg); - file->Var32(&Addr); - - // SRAMManager might now have an old buffer (or one from the future or alternate timeline!) - if (!file->Saving) - NDSCart_SRAMManager::RequestFlush(); -} - -void LoadSave(const char* path, u32 type) -{ - if (SRAM) delete[] SRAM; - - strncpy(SRAMPath, path, 1023); - SRAMPath[1023] = '\0'; - - FILE* f = Platform::OpenFile(path, "rb"); - if (f) - { - fseek(f, 0, SEEK_END); - SRAMLength = (u32)ftell(f); - SRAM = new u8[SRAMLength]; - - fseek(f, 0, SEEK_SET); - fread(SRAM, SRAMLength, 1, f); - - fclose(f); - } - else - { - if (type > 9) type = 0; - int sramlen[] = {0, 512, 8192, 65536, 128*1024, 256*1024, 512*1024, 1024*1024, 8192*1024, 32768*1024}; - SRAMLength = sramlen[type]; - - if (SRAMLength) - { - SRAM = new u8[SRAMLength]; - memset(SRAM, 0xFF, SRAMLength); - } - } - - SRAMFileDirty = false; - NDSCart_SRAMManager::Setup(path, SRAM, SRAMLength); - - switch (SRAMLength) - { - case 512: WriteFunc = Write_EEPROMTiny; break; - case 8192: - case 65536: - case 128*1024: WriteFunc = Write_EEPROM; break; - case 256*1024: - case 512*1024: - case 1024*1024: - case 8192*1024: WriteFunc = Write_Flash; break; - case 32768*1024: WriteFunc = Write_Null; break; // NAND FLASH, handled differently - default: - printf("!! BAD SAVE LENGTH %d\n", SRAMLength); - case 0: - WriteFunc = Write_Null; - break; - } - - Hold = 0; - CurCmd = 0; - Data = 0; - StatusReg = 0x00; -} - -void RelocateSave(const char* path, bool write) -{ - if (!write) - { - LoadSave(path, 0); // lazy - return; - } - - strncpy(SRAMPath, path, 1023); - SRAMPath[1023] = '\0'; - - FILE* f = Platform::OpenFile(path, "wb"); - if (!f) - { - printf("NDSCart_SRAM::RelocateSave: failed to create new file. fuck\n"); - return; - } - - fwrite(SRAM, SRAMLength, 1, f); - fclose(f); -} - -u8 Read() -{ - return Data; -} - -void Write_Null(u8 val, bool islast) {} - -void Write_EEPROMTiny(u8 val, bool islast) -{ - switch (CurCmd) - { - case 0x02: - case 0x0A: - if (DataPos < 1) - { - Addr = val; - Data = 0; - } - else - { - SRAM[(Addr + ((CurCmd==0x0A)?0x100:0)) & 0x1FF] = val; - Addr++; - } - break; - - case 0x03: - case 0x0B: - if (DataPos < 1) - { - Addr = val; - Data = 0; - } - else - { - Data = SRAM[(Addr + ((CurCmd==0x0B)?0x100:0)) & 0x1FF]; - Addr++; - } - break; - - case 0x9F: - Data = 0xFF; - break; - - default: - if (DataPos==0) - printf("unknown tiny EEPROM save command %02X\n", CurCmd); - break; - } -} - -void Write_EEPROM(u8 val, bool islast) -{ - u32 addrsize = 2; - if (SRAMLength > 65536) addrsize++; - - switch (CurCmd) - { - case 0x02: - if (DataPos < addrsize) - { - Addr <<= 8; - Addr |= val; - Data = 0; - } - else - { - SRAM[Addr & (SRAMLength-1)] = val; - Addr++; - } - break; - - case 0x03: - if (DataPos < addrsize) - { - Addr <<= 8; - Addr |= val; - Data = 0; - } - else - { - Data = SRAM[Addr & (SRAMLength-1)]; - Addr++; - } - break; - - case 0x9F: - Data = 0xFF; - break; - - default: - if (DataPos==0) - printf("unknown EEPROM save command %02X\n", CurCmd); - break; - } -} - -void Write_Flash(u8 val, bool islast) -{ - switch (CurCmd) - { - case 0x02: - if (DataPos < 3) - { - Addr <<= 8; - Addr |= val; - Data = 0; - } - else - { - SRAM[Addr & (SRAMLength-1)] = 0; - Addr++; - } - break; - - case 0x03: - if (DataPos < 3) - { - Addr <<= 8; - Addr |= val; - Data = 0; - } - else - { - Data = SRAM[Addr & (SRAMLength-1)]; - Addr++; - } - break; - - case 0x0A: - if (DataPos < 3) - { - Addr <<= 8; - Addr |= val; - Data = 0; - } - else - { - SRAM[Addr & (SRAMLength-1)] = val; - Addr++; - } - break; - - case 0x9F: - Data = 0xFF; - break; - - case 0xD8: - if (DataPos < 3) - { - Addr <<= 8; - Addr |= val; - Data = 0; - } - if (DataPos == 2) - { - for (u32 i = 0; i < 0x10000; i++) - { - SRAM[Addr & (SRAMLength-1)] = 0; - Addr++; - } - } - break; - - case 0xDB: - if (DataPos < 3) - { - Addr <<= 8; - Addr |= val; - Data = 0; - } - if (DataPos == 2) - { - for (u32 i = 0; i < 0x100; i++) - { - SRAM[Addr & (SRAMLength-1)] = 0; - Addr++; - } - } - break; - - default: - if (DataPos==0) - printf("unknown Flash save command %02X\n", CurCmd); - break; - } -} - -void Write(u8 val, u32 hold) -{ - bool islast = false; - - if (!hold) - { - if (Hold) islast = true; - else CurCmd = val; - Hold = 0; - } - - if (hold && (!Hold)) - { - CurCmd = val; - Hold = 1; - Data = 0; - DataPos = 0; - Addr = 0; - //printf("save SPI command %02X\n", CurCmd); - return; - } - - switch (CurCmd) - { - case 0x00: - // Pokémon carts have an IR transceiver thing, and send this - // to bypass it and access SRAM. - // TODO: design better - CurCmd = val; - break; - case 0x08: - // see above - // TODO: work out how the IR thing works. emulate it. - Data = 0xAA; - break; - - case 0x02: - case 0x03: - case 0x0A: - case 0x0B: - case 0x9F: - case 0xD8: - case 0xDB: - WriteFunc(val, islast); - DataPos++; - break; - - case 0x04: // write disable - StatusReg &= ~(1<<1); - Data = 0; - break; - - case 0x05: // read status reg - Data = StatusReg; - break; - - case 0x06: // write enable - StatusReg |= (1<<1); - Data = 0; - break; - - default: - if (DataPos==0) - printf("unknown save SPI command %02X %02X %d\n", CurCmd, val, islast); - break; - } - - SRAMFileDirty |= islast && (CurCmd == 0x02 || CurCmd == 0x0A) && (SRAMLength > 0); -} - -void FlushSRAMFile() -{ - if (!SRAMFileDirty) return; - - SRAMFileDirty = false; - NDSCart_SRAMManager::RequestFlush(); -} - -} - namespace NDSCart { +// SRAM TODO: emulate write delays??? + u16 SPICnt; u32 ROMCnt; +u8 SPIData; +u32 SPIDataPos; +bool SPIHold; + u8 ROMCommand[8]; u32 ROMData; @@ -488,10 +58,7 @@ u32 CartID; bool CartIsHomebrew; bool CartIsDSi; -FILE* CartSD; - -u32 CmdEncMode; -u32 DataEncMode; +CartCommon* Cart; u32 Key1_KeyBuf[0x412]; @@ -499,13 +66,6 @@ u64 Key2_X; u64 Key2_Y; -void ROMCommand_Retail(u8* cmd); -void ROMCommand_RetailNAND(u8* cmd); -void ROMCommand_Homebrew(u8* cmd); - -void (*ROMCommandHandler)(u8* cmd); - - u32 ByteSwap(u32 val) { return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); @@ -622,82 +182,1036 @@ void ApplyModcrypt(u32 addr, u32 len, u8* iv) } -bool Init() +CartCommon::CartCommon(u8* rom, u32 len, u32 chipid) { - if (!NDSCart_SRAM::Init()) return false; + ROM = rom; + ROMLength = len; + ChipID = chipid; - CartROM = NULL; - - CartSD = NULL; - - return true; + u8 unitcode = ROM[0x12]; + IsDSi = (unitcode & 0x02) != 0; } -void DeInit() +CartCommon::~CartCommon() { - if (CartROM) delete[] CartROM; - - if (CartSD) fclose(CartSD); - - NDSCart_SRAM::DeInit(); } -void Reset() +void CartCommon::Reset() { - CartInserted = false; - if (CartROM) delete[] CartROM; - CartROM = NULL; - CartROMSize = 0; - CartID = 0; - CartIsHomebrew = false; - CartIsDSi = false; - - if (CartSD) fclose(CartSD); - CartSD = NULL; - - ROMCommandHandler = NULL; - - NDSCart_SRAM::Reset(); - - ResetCart(); + CmdEncMode = 0; + DataEncMode = 0; } -void DoSavestate(Savestate* file) +void CartCommon::SetupDirectBoot() { - file->Section("NDSC"); + CmdEncMode = 2; + DataEncMode = 2; +} - file->Var16(&SPICnt); - file->Var32(&ROMCnt); - - file->VarArray(ROMCommand, 8); - file->Var32(&ROMData); - - file->VarArray(TransferData, 0x4000); - file->Var32(&TransferPos); - file->Var32(&TransferLen); - file->Var32(&TransferDir); - file->VarArray(TransferCmd, 8); - - // cart inserted/len/ROM/etc should be already populated - // savestate should be loaded after the right game is loaded - // (TODO: system to verify that indeed the right ROM is loaded) - // (what to CRC? whole ROM? code binaries? latter would be more convenient for ie. romhaxing) +void CartCommon::DoSavestate(Savestate* file) +{ + file->Section("NDCS"); file->Var32(&CmdEncMode); file->Var32(&DataEncMode); +} - // TODO: check KEY1 shit?? +void CartCommon::LoadSave(const char* path, u32 type) +{ +} - NDSCart_SRAM::DoSavestate(file); +void CartCommon::RelocateSave(const char* path, bool write) +{ +} + +int CartCommon::ImportSRAM(const u8* data, u32 length) +{ + return 0; +} + +void CartCommon::FlushSRAMFile() +{ +} + +int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) +{ + switch (cmd[0]) + { + case 0x9F: + memset(data, 0xFF, len); + return 0; + + case 0x00: + memset(data, 0, len); + if (len > 0x1000) + { + ReadROM(0, 0x1000, data, 0); + for (u32 pos = 0x1000; pos < len; pos += 0x1000) + memcpy(data+pos, data, 0x1000); + } + else + ReadROM(0, len, data, 0); + return 0; + + case 0x90: + case 0xB8: + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = ChipID; + return 0; + + case 0x3C: + CmdEncMode = 1; + Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2); + return 0; + + case 0x3D: + if (IsDSi) + { + CmdEncMode = 11; + Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2); + } + return 0; + + default: + if (CmdEncMode == 1 || CmdEncMode == 11) + { + // decrypt the KEY1 command as needed + // (KEY2 commands do not need decrypted because KEY2 is handled entirely by hardware, + // but KEY1 is not, so DS software is responsible for encrypting KEY1 commands) + u8 cmddec[8]; + *(u32*)&cmddec[0] = ByteSwap(*(u32*)&cmd[4]); + *(u32*)&cmddec[4] = ByteSwap(*(u32*)&cmd[0]); + Key1_Decrypt((u32*)cmddec); + u32 tmp = ByteSwap(*(u32*)&cmddec[4]); + *(u32*)&cmddec[4] = ByteSwap(*(u32*)&cmddec[0]); + *(u32*)&cmddec[0] = tmp; + + // TODO eventually: verify all the command parameters and shit + + switch (cmddec[0] & 0xF0) + { + case 0x40: + DataEncMode = 2; + return 0; + + case 0x10: + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = ChipID; + return 0; + + case 0x20: + { + u32 addr = (cmddec[2] & 0xF0) << 8; + if (CmdEncMode == 11) + { + // the DSi region starts with 0x3000 unreadable bytes + // similarly to how the DS region starts at 0x1000 with 0x3000 unreadable bytes + // these contain data for KEY1 crypto + u32 dsiregion = *(u16*)&ROM[0x92] << 19; + addr -= 0x1000; + addr += dsiregion; + } + ReadROM(addr, 0x1000, data, 0); + } + return 0; + + case 0xA0: + CmdEncMode = 2; + return 0; + } + } + return 0; + } +} + +void CartCommon::ROMCommandFinish(u8* cmd, u8* data, u32 len) +{ +} + +u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last) +{ + return 0xFF; +} + +void CartCommon::SetIRQ() +{ + NDS::SetIRQ(0, NDS::IRQ_CartIREQMC); + NDS::SetIRQ(1, NDS::IRQ_CartIREQMC); +} + +void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) +{ + if (addr >= ROMLength) return; + if ((addr+len) > ROMLength) + len = ROMLength - addr; + + memcpy(data+offset, ROM+addr, len); } -void ApplyDLDIPatch(const u8* patch, u32 len) +CartRetail::CartRetail(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid) { - u32 offset = *(u32*)&CartROM[0x20]; - u32 size = *(u32*)&CartROM[0x2C]; + SRAM = nullptr; +} - u8* binary = &CartROM[offset]; +CartRetail::~CartRetail() +{ + if (SRAM) delete[] SRAM; +} + +void CartRetail::Reset() +{ + CartCommon::Reset(); + + SRAMCmd = 0; + SRAMAddr = 0; + SRAMStatus = 0; +} + +void CartRetail::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + // we reload the SRAM contents. + // it should be the same file (as it should be the same ROM, duh) + // but the contents may change + + //if (!file->Saving && SRAMLength) + // delete[] SRAM; + + u32 oldlen = SRAMLength; + + file->Var32(&SRAMLength); + if (SRAMLength != oldlen) + { + printf("savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength); + printf("oh well. loading it anyway. adsfgdsf\n"); + + if (oldlen) delete[] SRAM; + if (SRAMLength) SRAM = new u8[SRAMLength]; + } + if (SRAMLength) + { + //if (!file->Saving) + // SRAM = new u8[SRAMLength]; + + file->VarArray(SRAM, SRAMLength); + } + + // SPI status shito + + file->Var8(&SRAMCmd); + file->Var32(&SRAMAddr); + file->Var8(&SRAMStatus); + + // SRAMManager might now have an old buffer (or one from the future or alternate timeline!) + if (!file->Saving) + { + SRAMFileDirty = false; + NDSCart_SRAMManager::RequestFlush(); + } +} + +void CartRetail::LoadSave(const char* path, u32 type) +{ + if (SRAM) delete[] SRAM; + + strncpy(SRAMPath, path, 1023); + SRAMPath[1023] = '\0'; + + if (type > 9) type = 0; + int sramlen[] = + { + 0, + 512, + 8192, 65536, 128*1024, + 256*1024, 512*1024, 1024*1024, + 8192*1024, 16384*1024 + }; + SRAMLength = sramlen[type]; + + if (SRAMLength) + { + SRAM = new u8[SRAMLength]; + memset(SRAM, 0xFF, SRAMLength); + } + + FILE* f = Platform::OpenFile(path, "rb"); + if (f) + { + fseek(f, 0, SEEK_SET); + fread(SRAM, 1, SRAMLength, f); + + fclose(f); + } + + SRAMFileDirty = false; + NDSCart_SRAMManager::Setup(path, SRAM, SRAMLength); + + switch (type) + { + case 1: SRAMType = 1; break; // EEPROM, small + case 2: + case 3: + case 4: SRAMType = 2; break; // EEPROM, regular + case 5: + case 6: + case 7: SRAMType = 3; break; // FLASH + case 8: + case 9: SRAMType = 4; break; // NAND + default: SRAMType = 0; break; // ...whatever else + } +} + +void CartRetail::RelocateSave(const char* path, bool write) +{ + if (!write) + { + LoadSave(path, 0); // lazy + return; + } + + strncpy(SRAMPath, path, 1023); + SRAMPath[1023] = '\0'; + + FILE* f = Platform::OpenFile(path, "wb"); + if (!f) + { + printf("NDSCart_SRAM::RelocateSave: failed to create new file. fuck\n"); + return; + } + + fwrite(SRAM, SRAMLength, 1, f); + fclose(f); +} + +int CartRetail::ImportSRAM(const u8* data, u32 length) +{ + memcpy(SRAM, data, std::min(length, SRAMLength)); + FILE* f = Platform::OpenFile(SRAMPath, "wb"); + if (f) + { + fwrite(SRAM, SRAMLength, 1, f); + fclose(f); + } + + return length - SRAMLength; +} + +void CartRetail::FlushSRAMFile() +{ + if (!SRAMFileDirty) return; + + SRAMFileDirty = false; + NDSCart_SRAMManager::RequestFlush(); +} + +int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len) +{ + switch (cmd[0]) + { + case 0xB7: + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + memset(data, 0, len); + + if (((addr + len - 1) >> 12) != (addr >> 12)) + { + u32 len1 = 0x1000 - (addr & 0xFFF); + ReadROM_B7(addr, len1, data, 0); + ReadROM_B7(addr+len1, len-len1, data, len1); + } + else + ReadROM_B7(addr, len, data, 0); + } + return 0; + + default: + return CartCommon::ROMCommandStart(cmd, data, len); + } +} + +u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last) +{ + if (SRAMType == 0) return 0; + + if (pos == 0) + { + // handle generic commands with no parameters + switch (val) + { + case 0x04: // write disable + SRAMStatus &= ~(1<<1); + break; + case 0x06: // write enable + SRAMStatus |= (1<<1); + break; + + default: + SRAMCmd = val; + SRAMAddr = 0; + } + + return 0; + } + + switch (SRAMType) + { + case 1: return SRAMWrite_EEPROMTiny(val, pos, last); + case 2: return SRAMWrite_EEPROM(val, pos, last); + case 3: return SRAMWrite_FLASH(val, pos, last); + default: return 0; + } +} + +void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) +{ + addr &= (ROMLength-1); + + if (addr < 0x8000) + addr = 0x8000 + (addr & 0x1FF); + + // TODO: protect DSi secure area + // also protect DSi region if not unlocked + // and other security shenanigans + + memcpy(data+offset, ROM+addr, len); +} + +u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) +{ + switch (SRAMCmd) + { + case 0x01: // write status register + // TODO: WP bits should be nonvolatile! + if (pos == 1) + SRAMStatus = (SRAMStatus & 0x01) | (val & 0x0C); + return 0; + + case 0x05: // read status register + return SRAMStatus | 0xF0; + + case 0x02: // write low + case 0x0A: // write high + if (pos < 2) + { + SRAMAddr = val; + } + else + { + // TODO: implement WP bits! + if (SRAMStatus & (1<<1)) + { + SRAM[(SRAMAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF] = val; + SRAMFileDirty |= last; + } + SRAMAddr++; + } + if (last) SRAMStatus &= ~(1<<1); + return 0; + + case 0x03: // read low + case 0x0B: // read high + if (pos < 2) + { + SRAMAddr = val; + return 0; + } + else + { + u8 ret = SRAM[(SRAMAddr + ((SRAMCmd==0x0B)?0x100:0)) & 0x1FF]; + SRAMAddr++; + return ret; + } + + case 0x9F: // read JEDEC ID + return 0xFF; + + default: + if (pos == 1) + printf("unknown tiny EEPROM save command %02X\n", SRAMCmd); + return 0; + } +} + +u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last) +{ + u32 addrsize = 2; + if (SRAMLength > 65536) addrsize++; + + switch (SRAMCmd) + { + case 0x01: // write status register + // TODO: WP bits should be nonvolatile! + if (pos == 1) + SRAMStatus = (SRAMStatus & 0x01) | (val & 0x0C); + return 0; + + case 0x05: // read status register + return SRAMStatus; + + case 0x02: // write + if (pos <= addrsize) + { + SRAMAddr <<= 8; + SRAMAddr |= val; + } + else + { + // TODO: implement WP bits + if (SRAMStatus & (1<<1)) + { + SRAM[SRAMAddr & (SRAMLength-1)] = val; + SRAMFileDirty |= last; + } + SRAMAddr++; + } + if (last) SRAMStatus &= ~(1<<1); + return 0; + + case 0x03: // read + if (pos <= addrsize) + { + SRAMAddr <<= 8; + SRAMAddr |= val; + return 0; + } + else + { + // TODO: size limit!! + u8 ret = SRAM[SRAMAddr & (SRAMLength-1)]; + SRAMAddr++; + return ret; + } + + case 0x9F: // read JEDEC ID + // TODO: GBAtek implies it's not always all FF (FRAM) + return 0xFF; + + default: + if (pos == 1) + printf("unknown EEPROM save command %02X\n", SRAMCmd); + return 0; + } +} + +u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) +{ + switch (SRAMCmd) + { + case 0x05: // read status register + return SRAMStatus; + + case 0x02: // page program + if (pos <= 3) + { + SRAMAddr <<= 8; + SRAMAddr |= val; + } + else + { + if (SRAMStatus & (1<<1)) + { + // CHECKME: should it be &=~val ?? + SRAM[SRAMAddr & (SRAMLength-1)] = 0; + SRAMFileDirty |= last; + } + SRAMAddr++; + } + if (last) SRAMStatus &= ~(1<<1); + return 0; + + case 0x03: // read + if (pos <= 3) + { + SRAMAddr <<= 8; + SRAMAddr |= val; + return 0; + } + else + { + u8 ret = SRAM[SRAMAddr & (SRAMLength-1)]; + SRAMAddr++; + return ret; + } + + case 0x0A: // page write + if (pos <= 3) + { + SRAMAddr <<= 8; + SRAMAddr |= val; + } + else + { + if (SRAMStatus & (1<<1)) + { + SRAM[SRAMAddr & (SRAMLength-1)] = val; + SRAMFileDirty |= last; + } + SRAMAddr++; + } + if (last) SRAMStatus &= ~(1<<1); + return 0; + + case 0x9F: // read JEDEC IC + // GBAtek says it should be 0xFF. verify? + return 0xFF; + + case 0xD8: // sector erase + if (pos <= 3) + { + SRAMAddr <<= 8; + SRAMAddr |= val; + } + if ((pos == 3) && (SRAMStatus & (1<<1))) + { + for (u32 i = 0; i < 0x10000; i++) + { + SRAM[SRAMAddr & (SRAMLength-1)] = 0; + SRAMAddr++; + } + SRAMFileDirty = true; + } + if (last) SRAMStatus &= ~(1<<1); + return 0; + + case 0xDB: // page erase + if (pos <= 3) + { + SRAMAddr <<= 8; + SRAMAddr |= val; + } + if ((pos == 3) && (SRAMStatus & (1<<1))) + { + for (u32 i = 0; i < 0x100; i++) + { + SRAM[SRAMAddr & (SRAMLength-1)] = 0; + SRAMAddr++; + } + SRAMFileDirty = true; + } + if (last) SRAMStatus &= ~(1<<1); + return 0; + + default: + if (pos == 1) + printf("unknown FLASH save command %02X\n", SRAMCmd); + return 0; + } +} + + +CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid) +{ +} + +CartRetailNAND::~CartRetailNAND() +{ +} + +void CartRetailNAND::Reset() +{ + CartRetail::Reset(); + + SRAMAddr = 0; + SRAMStatus = 0x20; + SRAMWindow = 0; + + // ROM header 94/96 = SRAM addr start / 0x20000 + SRAMBase = *(u16*)&ROM[0x96] << 17; + + memset(SRAMWriteBuffer, 0, 0x800); +} + +void CartRetailNAND::DoSavestate(Savestate* file) +{ + CartRetail::DoSavestate(file); + + file->Var32(&SRAMBase); + file->Var32(&SRAMWindow); + + file->VarArray(SRAMWriteBuffer, 0x800); + file->Var32(&SRAMWritePos); + + if (!file->Saving) + BuildSRAMID(); +} + +void CartRetailNAND::LoadSave(const char* path, u32 type) +{ + CartRetail::LoadSave(path, type); + BuildSRAMID(); +} + +int CartRetailNAND::ImportSRAM(const u8* data, u32 length) +{ + CartRetail::ImportSRAM(data, length); + BuildSRAMID(); +} + +int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len) +{ + switch (cmd[0]) + { + case 0x81: // write data + if ((SRAMStatus & (1<<4)) && SRAMWindow >= SRAMBase && SRAMWindow < (SRAMBase+SRAMLength)) + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + + if (addr >= SRAMWindow && addr < (SRAMWindow+0x20000)) + { + // the command is issued 4 times, each with the same address + // seems they use the one from the first command (CHECKME) + if (!SRAMAddr) + SRAMAddr = addr; + } + } + else + SRAMAddr = 0; + return 1; + + case 0x82: // commit write + if (SRAMAddr && SRAMWritePos) + { + if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000)) + { + memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800); + SRAMFileDirty = true; + } + + SRAMAddr = 0; + SRAMWritePos = 0; + } + SRAMStatus &= ~(1<<4); + return 0; + + case 0x84: // discard write buffer + SRAMAddr = 0; + SRAMWritePos = 0; + return 0; + + case 0x85: // write enable + if (SRAMWindow) + { + SRAMStatus |= (1<<4); + SRAMWritePos = 0; + } + return 0; + + case 0x8B: // revert to ROM read mode + SRAMWindow = 0; + return 0; + + case 0x94: // return ID data + { + // TODO: check what the data really is. probably the NAND chip's ID. + // also, might be different between different games or even between different carts. + // this was taken from a Jam with the Band cart. + u8 iddata[0x30] = + { + 0xEC, 0xF1, 0x00, 0x95, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + if (SRAMLength) memcpy(&iddata[0x18], &SRAM[SRAMLength - 0x800], 16); + + memset(data, 0, len); + memcpy(data, iddata, std::min(len, 0x30u)); + } + return 0; + + case 0xB2: // set window for accessing SRAM + { + u32 addr = (cmd[1]<<24) | ((cmd[2]&0xFE)<<16); + + // window is 0x20000 bytes, address is aligned to that boundary + // NAND remains stuck 'busy' forever if this is less than the starting SRAM address + // TODO. + if (addr < SRAMBase) printf("NAND: !! BAD ADDR %08X < %08X\n", addr, SRAMBase); + if (addr >= (SRAMBase+SRAMLength)) printf("NAND: !! BAD ADDR %08X > %08X\n", addr, SRAMBase+SRAMLength); + + SRAMWindow = addr; + } + return 0; + + case 0xB7: + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + + if (SRAMWindow == 0) + { + // regular ROM mode + memset(data, 0, len); + + if (((addr + len - 1) >> 12) != (addr >> 12)) + { + u32 len1 = 0x1000 - (addr & 0xFFF); + ReadROM_B7(addr, len1, data, 0); + ReadROM_B7(addr+len1, len-len1, data, len1); + } + else + ReadROM_B7(addr, len, data, 0); + } + else + { + // SRAM mode + memset(data, 0xFF, len); + + if (SRAMWindow >= SRAMBase && SRAMWindow < (SRAMBase+SRAMLength) && + addr >= SRAMWindow && addr < (SRAMWindow+0x20000)) + { + memcpy(data, &SRAM[addr - SRAMBase], len); + } + } + } + return 0; + + case 0xD6: // read NAND status + { + // status bits + // bit5: ready + // bit4: write enable + + for (u32 i = 0; i < len; i+=4) + *(u32*)&data[i] = SRAMStatus * 0x01010101; + } + return 0; + + default: + return CartRetail::ROMCommandStart(cmd, data, len); + } +} + +void CartRetailNAND::ROMCommandFinish(u8* cmd, u8* data, u32 len) +{ + switch (cmd[0]) + { + case 0x81: // write data + if (SRAMAddr) + { + if ((SRAMWritePos + len) > 0x800) + len = 0x800 - SRAMWritePos; + + memcpy(&SRAMWriteBuffer[SRAMWritePos], data, len); + SRAMWritePos += len; + } + return; + + default: + return CartCommon::ROMCommandFinish(cmd, data, len); + } +} + +u8 CartRetailNAND::SPIWrite(u8 val, u32 pos, bool last) +{ + return 0xFF; +} + +void CartRetailNAND::BuildSRAMID() +{ + // the last 128K of the SRAM are read-only. + // most of it is FF, except for the NAND ID at the beginning + // of the last 0x800 bytes. + + if (SRAMLength > 0x20000) + { + memset(&SRAM[SRAMLength - 0x20000], 0xFF, 0x20000); + + // TODO: check what the data is all about! + // this was pulled from a Jam with the Band cart. may be different on other carts. + // WarioWare DIY may have different data or not have this at all. + // the ID data is also found in the response to command 94, and JwtB checks it. + // WarioWare doesn't seem to care. + // there is also more data here, but JwtB doesn't seem to care. + u8 iddata[0x10] = {0xEC, 0x00, 0x9E, 0xA1, 0x51, 0x65, 0x34, 0x35, 0x30, 0x35, 0x30, 0x31, 0x19, 0x19, 0x02, 0x0A}; + memcpy(&SRAM[SRAMLength - 0x800], iddata, 16); + } +} + + +CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion) : CartRetail(rom, len, chipid) +{ + IRVersion = irversion; +} + +CartRetailIR::~CartRetailIR() +{ +} + +void CartRetailIR::Reset() +{ + CartRetail::Reset(); + + IRCmd = 0; +} + +void CartRetailIR::DoSavestate(Savestate* file) +{ + CartRetail::DoSavestate(file); + + file->Var8(&IRCmd); +} + +u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last) +{ + if (pos == 0) + { + IRCmd = val; + return 0; + } + + // TODO: emulate actual IR comm + + switch (IRCmd) + { + case 0x00: // pass-through + return CartRetail::SPIWrite(val, pos-1, last); + + case 0x08: // ID + return 0xAA; + } +} + + +CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid) +{ + printf("POKETYPE CART\n"); +} + +CartRetailBT::~CartRetailBT() +{ +} + +void CartRetailBT::Reset() +{ + CartRetail::Reset(); +} + +void CartRetailBT::DoSavestate(Savestate* file) +{ + CartRetail::DoSavestate(file); +} + +u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) +{ + printf("POKETYPE SPI: %02X %d %d - %08X\n", val, pos, last, NDS::GetPC(0)); + + /*if (pos == 0) + { + // TODO do something with it?? + if(val==0xFF)SetIRQ(); + } + if(pos==7)SetIRQ();*/ + + return 0; +} + + +CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid) +{ + if (Config::DLDIEnable) + { + ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI)); + SDFile = Platform::OpenLocalFile(Config::DLDISDPath, "r+b"); + } + else + SDFile = nullptr; +} + +CartHomebrew::~CartHomebrew() +{ + if (SDFile) fclose(SDFile); +} + +void CartHomebrew::Reset() +{ + CartCommon::Reset(); + + if (SDFile) fclose(SDFile); + + if (Config::DLDIEnable) + SDFile = Platform::OpenLocalFile(Config::DLDISDPath, "r+b"); + else + SDFile = nullptr; +} + +void CartHomebrew::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); +} + +int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len) +{ + switch (cmd[0]) + { + case 0xB7: + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + memset(data, 0, len); + + if (((addr + len - 1) >> 12) != (addr >> 12)) + { + u32 len1 = 0x1000 - (addr & 0xFFF); + ReadROM_B7(addr, len1, data, 0); + ReadROM_B7(addr+len1, len-len1, data, len1); + } + else + ReadROM_B7(addr, len, data, 0); + } + return 0; + + case 0xC0: // SD read + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + u64 addr = sector * 0x200ULL; + + if (SDFile) + { + fseek(SDFile, addr, SEEK_SET); + fread(data, len, 1, SDFile); + } + } + return 0; + + case 0xC1: // SD write + return 1; + + default: + return CartCommon::ROMCommandStart(cmd, data, len); + } +} + +void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len) +{ + // TODO: delayed SD writing? like we have for SRAM + + switch (cmd[0]) + { + case 0xC1: + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + u64 addr = sector * 0x200ULL; + + if (SDFile) + { + fseek(SDFile, addr, SEEK_SET); + fwrite(data, len, 1, SDFile); + } + } + break; + + default: + return CartCommon::ROMCommandFinish(cmd, data, len); + } +} + +void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen) +{ + u32 offset = *(u32*)&ROM[0x20]; + u32 size = *(u32*)&ROM[0x2C]; + + u8* binary = &ROM[offset]; u32 dldioffset = 0; for (u32 i = 0; i < size; i++) @@ -723,14 +1237,12 @@ void ApplyDLDIPatch(const u8* patch, u32 len) *(u32*)&patch[8] != 0x006D6873) { printf("bad DLDI patch\n"); - delete[] patch; return; } if (patch[0x0D] > binary[dldioffset+0x0F]) { printf("DLDI driver ain't gonna fit, sorry\n"); - delete[] patch; return; } @@ -747,7 +1259,7 @@ void ApplyDLDIPatch(const u8* patch, u32 len) u32 patchsize = 1 << patch[0x0D]; u32 patchend = patchbase + patchsize; - memcpy(&binary[dldioffset], patch, len); + memcpy(&binary[dldioffset], patch, patchlen); *(u32*)&binary[dldioffset+0x40] += delta; *(u32*)&binary[dldioffset+0x44] += delta; @@ -814,6 +1326,75 @@ void ApplyDLDIPatch(const u8* patch, u32 len) printf("applied DLDI patch\n"); } +void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) +{ + // TODO: how strict should this be for homebrew? + + addr &= (ROMLength-1); + + memcpy(data+offset, ROM+addr, len); +} + + + +bool Init() +{ + CartROM = nullptr; + Cart = nullptr; + + return true; +} + +void DeInit() +{ + if (CartROM) delete[] CartROM; + if (Cart) delete Cart; +} + +void Reset() +{ + CartInserted = false; + if (CartROM) delete[] CartROM; + CartROM = nullptr; + CartROMSize = 0; + CartID = 0; + CartIsHomebrew = false; + CartIsDSi = false; + + if (Cart) delete Cart; + Cart = nullptr; + + ResetCart(); +} + +void DoSavestate(Savestate* file) +{ + file->Section("NDSC"); + + file->Var16(&SPICnt); + file->Var32(&ROMCnt); + + file->Var8(&SPIData); + file->Var32(&SPIDataPos); + file->Bool32(&SPIHold); + + file->VarArray(ROMCommand, 8); + file->Var32(&ROMData); + + file->VarArray(TransferData, 0x4000); + file->Var32(&TransferPos); + file->Var32(&TransferLen); + file->Var32(&TransferDir); + file->VarArray(TransferCmd, 8); + + // cart inserted/len/ROM/etc should be already populated + // savestate should be loaded after the right game is loaded + // (TODO: system to verify that indeed the right ROM is loaded) + // (what to CRC? whole ROM? code binaries? latter would be more convenient for ie. romhaxing) + + if (Cart) Cart->DoSavestate(file); +} + bool ReadROMParams(u32 gamecode, ROMListEntry* params) { @@ -927,12 +1508,18 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) else CartID |= (0x100 - (CartROMSize >> 28)) << 8; - if (romparams.SaveMemType == 8) + if (romparams.SaveMemType == 8 || romparams.SaveMemType == 9) CartID |= 0x08000000; // NAND flag if (CartIsDSi) CartID |= 0x40000000; + // cart ID for Jam with the Band + // TODO: this kind of ID triggers different KEY1 phase + // (repeats commands a bunch of times) + //CartID = 0x88017FEC; + //CartID = 0x80007FC2; // pokémon typing adventure + printf("Cart ID: %08X\n", CartID); u32 arm9base = *(u32*)&CartROM[0x20]; @@ -961,41 +1548,46 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) if ((arm9base < 0x4000) || (gamecode == 0x23232323)) { CartIsHomebrew = true; - if (Config::DLDIEnable) - ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI)); - } - - if (direct) - { - // TODO: in the case of an already-encrypted secure area, direct boot - // needs it decrypted - NDS::SetupDirectBoot(); - CmdEncMode = 2; } CartInserted = true; - // TODO: support more fancy cart types (homebrew?, flashcarts, etc) + u32 irversion = 0; + if ((gamecode & 0xFF) == 'I') + { + if (((gamecode >> 8) & 0xFF) < 'P') + irversion = 1; // Active Health / Walk with Me + else + irversion = 2; // Pokémon HG/SS, B/W, B2/W2 + } + if (CartIsHomebrew) - ROMCommandHandler = ROMCommand_Homebrew; + Cart = new CartHomebrew(CartROM, CartROMSize, CartID); else if (CartID & 0x08000000) - ROMCommandHandler = ROMCommand_RetailNAND; + Cart = new CartRetailNAND(CartROM, CartROMSize, CartID); + else if (irversion != 0) + Cart = new CartRetailIR(CartROM, CartROMSize, CartID, irversion); + else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx + Cart = new CartRetailBT(CartROM, CartROMSize, CartID); else - ROMCommandHandler = ROMCommand_Retail; + Cart = new CartRetail(CartROM, CartROMSize, CartID); + + if (Cart) + { + Cart->Reset(); + if (direct) + { + NDS::SetupDirectBoot(); + Cart->SetupDirectBoot(); + } + } // encryption Key1_InitKeycode(false, gamecode, 2, 2); // save printf("Save file: %s\n", sram); - NDSCart_SRAM::LoadSave(sram, romparams.SaveMemType); - - if (CartIsHomebrew && Config::DLDIEnable) - { - CartSD = Platform::OpenLocalFile(Config::DLDISDPath, "r+b"); - } - else - CartSD = NULL; + if (Cart) Cart->LoadSave(sram, romparams.SaveMemType); return true; } @@ -1049,26 +1641,18 @@ bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct) void RelocateSave(const char* path, bool write) { - // herp derp - NDSCart_SRAM::RelocateSave(path, write); + if (Cart) Cart->RelocateSave(path, write); } void FlushSRAMFile() { - NDSCart_SRAM::FlushSRAMFile(); + if (Cart) Cart->FlushSRAMFile(); } int ImportSRAM(const u8* data, u32 length) { - memcpy(NDSCart_SRAM::SRAM, data, std::min(length, NDSCart_SRAM::SRAMLength)); - FILE* f = Platform::OpenFile(NDSCart_SRAM::SRAMPath, "wb"); - if (f) - { - fwrite(NDSCart_SRAM::SRAM, NDSCart_SRAM::SRAMLength, 1, f); - fclose(f); - } - - return length - NDSCart_SRAM::SRAMLength; + if (Cart) return Cart->ImportSRAM(data, length); + return 0; } void ResetCart() @@ -1078,6 +1662,10 @@ void ResetCart() SPICnt = 0; ROMCnt = 0; + SPIData = 0; + SPIDataPos = 0; + SPIHold = false; + memset(ROMCommand, 0, 8); ROMData = 0; @@ -1091,33 +1679,7 @@ void ResetCart() memset(TransferCmd, 0, 8); TransferCmd[0] = 0xFF; - CmdEncMode = 0; - DataEncMode = 0; -} - -void ReadROM(u32 addr, u32 len, u32 offset) -{ - if (!CartInserted) return; - - if (addr >= CartROMSize) return; - if ((addr+len) > CartROMSize) - len = CartROMSize - addr; - - memcpy(TransferData+offset, CartROM+addr, len); -} - -void ReadROM_B7(u32 addr, u32 len, u32 offset) -{ - if (!CartInserted) return; - - addr &= (CartROMSize-1); - if (!CartIsHomebrew) - { - if (addr < 0x8000) - addr = 0x8000 + (addr & 0x1FF); - } - - memcpy(TransferData+offset, CartROM+addr, len); + if (Cart) Cart->Reset(); } @@ -1126,29 +1688,10 @@ void ROMEndTransfer(u32 param) ROMCnt &= ~(1<<31); if (SPICnt & (1<<14)) - NDS::SetIRQ((NDS::ExMemCnt[0]>>11)&0x1, NDS::IRQ_CartSendDone); + NDS::SetIRQ((NDS::ExMemCnt[0]>>11)&0x1, NDS::IRQ_CartXferDone); - if (TransferDir == 1) - { - // finish a write - - u8* cmd = TransferCmd; - switch (cmd[0]) - { - case 0xC1: - { - u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - u64 addr = sector * 0x200ULL; - - if (CartSD) - { - fseek(CartSD, addr, SEEK_SET); - fwrite(TransferData, TransferLen, 1, CartSD); - } - } - break; - } - } + if (Cart) + Cart->ROMCommandFinish(TransferCmd, TransferData, TransferLen); } void ROMPrepareData(u32 param) @@ -1171,141 +1714,14 @@ void ROMPrepareData(u32 param) NDS::CheckDMAs(0, 0x05); } - -void ROMCommand_Retail(u8* cmd) -{ - switch (cmd[0]) - { - case 0xB7: - { - u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - memset(TransferData, 0, TransferLen); - - if (((addr + TransferLen - 1) >> 12) != (addr >> 12)) - { - u32 len1 = 0x1000 - (addr & 0xFFF); - ReadROM_B7(addr, len1, 0); - ReadROM_B7(addr+len1, TransferLen-len1, len1); - } - else - ReadROM_B7(addr, TransferLen, 0); - } - break; - - default: - printf("unknown retail cart command %02X\n", cmd[0]); - break; - } -} - -void ROMCommand_RetailNAND(u8* cmd) -{ - switch (cmd[0]) - { - case 0x94: // NAND init - { - // initial value: should have bit7 clear - NDSCart_SRAM::StatusReg = 0; - - // Jam with the Band stores words 6-9 of this at 0x02131BB0 - // it doesn't seem to use those anywhere later - for (u32 pos = 0; pos < TransferLen; pos += 4) - *(u32*)&TransferData[pos] = 0; - } - break; - - case 0xB2: // set savemem addr - { - NDSCart_SRAM::StatusReg |= 0x20; - } - break; - - case 0xB7: - { - u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - memset(TransferData, 0, TransferLen); - - if (((addr + TransferLen - 1) >> 12) != (addr >> 12)) - { - u32 len1 = 0x1000 - (addr & 0xFFF); - ReadROM_B7(addr, len1, 0); - ReadROM_B7(addr+len1, TransferLen-len1, len1); - } - else - ReadROM_B7(addr, TransferLen, 0); - } - break; - - case 0xD6: // NAND status - { - // status reg bits: - // * bit7: busy? error? - // * bit5: accessing savemem - - for (u32 pos = 0; pos < TransferLen; pos += 4) - *(u32*)&TransferData[pos] = NDSCart_SRAM::StatusReg * 0x01010101; - } - break; - - default: - printf("unknown NAND command %02X %04Xn", cmd[0], TransferLen); - break; - } -} - -void ROMCommand_Homebrew(u8* cmd) -{ - switch (cmd[0]) - { - case 0xB7: - { - u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - memset(TransferData, 0, TransferLen); - - if (((addr + TransferLen - 1) >> 12) != (addr >> 12)) - { - u32 len1 = 0x1000 - (addr & 0xFFF); - ReadROM_B7(addr, len1, 0); - ReadROM_B7(addr+len1, TransferLen-len1, len1); - } - else - ReadROM_B7(addr, TransferLen, 0); - } - break; - - case 0xC0: // SD read - { - u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - u64 addr = sector * 0x200ULL; - - if (CartSD) - { - fseek(CartSD, addr, SEEK_SET); - fread(TransferData, TransferLen, 1, CartSD); - } - } - break; - - case 0xC1: // SD write - { - TransferDir = 1; - memcpy(TransferCmd, cmd, 8); - } - break; - - default: - printf("unknown homebrew cart command %02X\n", cmd[0]); - break; - } -} - - void WriteROMCnt(u32 val) { ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x00800000); if (!(SPICnt & (1<<15))) return; + // all this junk would only really be useful if melonDS was interfaced to + // a DS cart reader if (val & (1<<15)) { u32 snum = (NDS::ExMemCnt[0]>>8)&0x8; @@ -1337,110 +1753,24 @@ void WriteROMCnt(u32 val) TransferPos = 0; TransferLen = datasize; - // handle KEY1 encryption as needed. - // KEY2 encryption is implemented in hardware and doesn't need to be handled. - u8 cmd[8]; - if (CmdEncMode == 1 || CmdEncMode == 11) - { - *(u32*)&cmd[0] = ByteSwap(*(u32*)&ROMCommand[4]); - *(u32*)&cmd[4] = ByteSwap(*(u32*)&ROMCommand[0]); - Key1_Decrypt((u32*)cmd); - u32 tmp = ByteSwap(*(u32*)&cmd[4]); - *(u32*)&cmd[4] = ByteSwap(*(u32*)&cmd[0]); - *(u32*)&cmd[0] = tmp; - } - else - { - *(u32*)&cmd[0] = *(u32*)&ROMCommand[0]; - *(u32*)&cmd[4] = *(u32*)&ROMCommand[4]; - } + *(u32*)&TransferCmd[0] = *(u32*)&ROMCommand[0]; + *(u32*)&TransferCmd[4] = *(u32*)&ROMCommand[4]; /*printf("ROM COMMAND %04X %08X %02X%02X%02X%02X%02X%02X%02X%02X SIZE %04X\n", SPICnt, ROMCnt, - cmd[0], cmd[1], cmd[2], cmd[3], - cmd[4], cmd[5], cmd[6], cmd[7], + TransferCmd[0], TransferCmd[1], TransferCmd[2], TransferCmd[3], + TransferCmd[4], TransferCmd[5], TransferCmd[6], TransferCmd[7], datasize);*/ // default is read // commands that do writes will change this TransferDir = 0; - switch (cmd[0]) - { - case 0x9F: - memset(TransferData, 0xFF, TransferLen); - break; - - case 0x00: - memset(TransferData, 0, TransferLen); - if (TransferLen > 0x1000) - { - ReadROM(0, 0x1000, 0); - for (u32 pos = 0x1000; pos < TransferLen; pos += 0x1000) - memcpy(TransferData+pos, TransferData, 0x1000); - } - else - ReadROM(0, TransferLen, 0); - break; - - case 0x90: - case 0xB8: - for (u32 pos = 0; pos < TransferLen; pos += 4) - *(u32*)&TransferData[pos] = CartID; - break; - - case 0x3C: - if (CartInserted) - { - CmdEncMode = 1; - Key1_InitKeycode(false, *(u32*)&CartROM[0xC], 2, 2); - } - break; - - case 0x3D: - if (CartInserted && CartIsDSi) - { - CmdEncMode = 11; - Key1_InitKeycode(true, *(u32*)&CartROM[0xC], 1, 2); - } - break; - - default: - if (CmdEncMode == 1 || CmdEncMode == 11) - { - switch (cmd[0] & 0xF0) - { - case 0x40: - DataEncMode = 2; - break; - - case 0x10: - for (u32 pos = 0; pos < TransferLen; pos += 4) - *(u32*)&TransferData[pos] = CartID; - break; - - case 0x20: - { - u32 addr = (cmd[2] & 0xF0) << 8; - if (CmdEncMode == 11) - { - u32 arm9i_base = *(u32*)&CartROM[0x1C0]; - addr -= 0x4000; - addr += arm9i_base; - } - ReadROM(addr, 0x1000, 0); - } - break; - - case 0xA0: - CmdEncMode = 2; - break; - } - } - else if (ROMCommandHandler) - ROMCommandHandler(cmd); - break; - } + // TODO: how should we detect that the transfer should be a write? + // you're supposed to set bit30 of ROMCNT for a write, but it's also + // possible to do reads just fine when that bit is set + if (Cart) + TransferDir = Cart->ROMCommandStart(TransferCmd, TransferData, TransferLen); ROMCnt &= ~(1<<23); @@ -1454,6 +1784,7 @@ void WriteROMCnt(u32 val) u32 cmddelay = 8; // delays are only applied when the WR bit is cleared + // CHECKME: do the delays apply at the end (instead of start) when WR is set? if (!(ROMCnt & (1<<30))) { cmddelay += (ROMCnt & 0x1FFF); @@ -1517,6 +1848,12 @@ void WriteROMData(u32 val) void WriteSPICnt(u16 val) { + if ((SPICnt & 0x2040) == 0x2040 && (val & 0x2000) == 0x0000) + { + // forcefully reset SPI hold + SPIHold = false; + } + SPICnt = (SPICnt & 0x0080) | (val & 0xE043); if (SPICnt & (1<<7)) printf("!! CHANGING AUXSPICNT DURING TRANSFER: %04X\n", val); @@ -1533,7 +1870,7 @@ u8 ReadSPIData() if (!(SPICnt & (1<<13))) return 0; if (SPICnt & (1<<7)) return 0; // checkme - return NDSCart_SRAM::Read(); + return SPIData; } void WriteSPIData(u8 val) @@ -1544,7 +1881,28 @@ void WriteSPIData(u8 val) if (SPICnt & (1<<7)) printf("!! WRITING AUXSPIDATA DURING PENDING TRANSFER\n"); SPICnt |= (1<<7); - NDSCart_SRAM::Write(val, SPICnt&(1<<6)); + + bool hold = SPICnt&(1<<6); + bool islast = false; + if (!hold) + { + if (SPIHold) SPIDataPos++; + else SPIDataPos = 0; + islast = true; + SPIHold = false; + } + else if (hold && (!SPIHold)) + { + SPIHold = true; + SPIDataPos = 0; + } + else + { + SPIDataPos++; + } + + if (Cart) SPIData = Cart->SPIWrite(val, SPIDataPos, islast); + else SPIData = 0; // SPI transfers one bit per cycle -> 8 cycles per byte u32 delay = 8 * (8 << (SPICnt & 0x3)); diff --git a/src/NDSCart.h b/src/NDSCart.h index 3c0cebe0..d54dc769 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -24,14 +24,167 @@ namespace NDSCart { +// CartCommon -- base code shared by all cart types +class CartCommon +{ +public: + CartCommon(u8* rom, u32 len, u32 chipid); + virtual ~CartCommon(); + + virtual void Reset(); + virtual void SetupDirectBoot(); + + virtual void DoSavestate(Savestate* file); + + virtual void LoadSave(const char* path, u32 type); + virtual void RelocateSave(const char* path, bool write); + virtual int ImportSRAM(const u8* data, u32 length); + virtual void FlushSRAMFile(); + + virtual int ROMCommandStart(u8* cmd, u8* data, u32 len); + virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len); + + virtual u8 SPIWrite(u8 val, u32 pos, bool last); + +protected: + void ReadROM(u32 addr, u32 len, u8* data, u32 offset); + + void SetIRQ(); + + u8* ROM; + u32 ROMLength; + u32 ChipID; + bool IsDSi; + + u32 CmdEncMode; + u32 DataEncMode; +}; + +// CartRetail -- regular retail cart (ROM, SPI SRAM) +class CartRetail : public CartCommon +{ +public: + CartRetail(u8* rom, u32 len, u32 chipid); + virtual ~CartRetail() override; + + virtual void Reset() override; + + virtual void DoSavestate(Savestate* file) override; + + virtual void LoadSave(const char* path, u32 type) override; + virtual void RelocateSave(const char* path, bool write) override; + virtual int ImportSRAM(const u8* data, u32 length) override; + virtual void FlushSRAMFile() override; + + virtual int ROMCommandStart(u8* cmd, u8* data, u32 len) override; + + virtual u8 SPIWrite(u8 val, u32 pos, bool last) override; + +protected: + void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); + + u8 SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last); + u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last); + u8 SRAMWrite_FLASH(u8 val, u32 pos, bool last); + + u8* SRAM; + u32 SRAMLength; + u32 SRAMType; + + char SRAMPath[1024]; + bool SRAMFileDirty; + + u8 SRAMCmd; + u32 SRAMAddr; + u8 SRAMStatus; +}; + +// CartRetailNAND -- retail cart with NAND SRAM (WarioWare DIY, Jam with the Band, ...) +class CartRetailNAND : public CartRetail +{ +public: + CartRetailNAND(u8* rom, u32 len, u32 chipid); + ~CartRetailNAND() override; + + void Reset() override; + + void DoSavestate(Savestate* file); + + void LoadSave(const char* path, u32 type) override; + int ImportSRAM(const u8* data, u32 length) override; + + int ROMCommandStart(u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; + + u8 SPIWrite(u8 val, u32 pos, bool last) override; + +private: + void BuildSRAMID(); + + u32 SRAMBase; + u32 SRAMWindow; + + u8 SRAMWriteBuffer[0x800]; + u32 SRAMWritePos; +}; + +// CartRetailIR -- SPI IR device and SRAM +class CartRetailIR : public CartRetail +{ +public: + CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion); + ~CartRetailIR() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u8 SPIWrite(u8 val, u32 pos, bool last) override; + +private: + u32 IRVersion; + u8 IRCmd; +}; + +// CartRetailBT - Pokémon Typing Adventure (SPI BT controller) +class CartRetailBT : public CartRetail +{ +public: + CartRetailBT(u8* rom, u32 len, u32 chipid); + ~CartRetailBT() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u8 SPIWrite(u8 val, u32 pos, bool last) override; +}; + +// CartHomebrew -- homebrew 'cart' (no SRAM, DLDI) +class CartHomebrew : public CartCommon +{ +public: + CartHomebrew(u8* rom, u32 len, u32 chipid); + ~CartHomebrew() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + int ROMCommandStart(u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; + +private: + void ApplyDLDIPatch(const u8* patch, u32 len); + void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); + + FILE* SDFile; +}; + extern u16 SPICnt; extern u32 ROMCnt; extern u8 ROMCommand[8]; -extern u32 ROMDataOut; - -extern u8 EncSeed0[5]; -extern u8 EncSeed1[5]; extern u8* CartROM; extern u32 CartROMSize; diff --git a/src/ROMList.h b/src/ROMList.h index 9a73b781..cd1ef4e3 100644 --- a/src/ROMList.h +++ b/src/ROMList.h @@ -1590,7 +1590,7 @@ ROMListEntry ROMList[] = {0x45524D43, 0x02000000, 0x00000002}, {0x45524E42, 0x04000000, 0x00000003}, {0x45524E43, 0x04000000, 0x00000002}, - {0x45524F55, 0x08000000, 0x00000008}, + {0x45524F55, 0x08000000, 0x00000009}, {0x45525041, 0x02000000, 0x00000002}, {0x45525042, 0x00800000, 0x00000001}, {0x45525043, 0x04000000, 0x00000002}, @@ -3828,7 +3828,7 @@ ROMListEntry ROMList[] = {0x4A524E54, 0x10000000, 0x00000003}, {0x4A524E59, 0x04000000, 0x00000002}, {0x4A524F42, 0x01000000, 0x00000003}, - {0x4A524F55, 0x02000000, 0x00000008}, + {0x4A524F55, 0x02000000, 0x00000009}, {0x4A525041, 0x02000000, 0x00000002}, {0x4A525142, 0x02000000, 0x00000002}, {0x4A525143, 0x04000000, 0x00000002}, @@ -5773,7 +5773,7 @@ ROMListEntry ROMList[] = {0x50524E41, 0x04000000, 0x00000001}, {0x50524E42, 0x04000000, 0x00000003}, {0x50524E43, 0x04000000, 0x00000002}, - {0x50524F55, 0x08000000, 0x00000008}, + {0x50524F55, 0x08000000, 0x00000009}, {0x50525041, 0x02000000, 0x00000002}, {0x50525042, 0x00800000, 0x00000001}, {0x50525054, 0x02000000, 0x00000001}, diff --git a/src/Savestate.h b/src/Savestate.h index bcd813d8..bad69cf4 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -22,8 +22,8 @@ #include #include "types.h" -#define SAVESTATE_MAJOR 7 -#define SAVESTATE_MINOR 1 +#define SAVESTATE_MAJOR 8 +#define SAVESTATE_MINOR 0 class Savestate { diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index b5e8a058..40552e29 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -561,7 +561,7 @@ int Reset() if (ROMPath[ROMSlot_GBA][0] != '\0') { char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_GBA]); - strncpy(ext, ROMPath[ROMSlot_NDS] + _len - 4, 4); + strncpy(ext, ROMPath[ROMSlot_GBA] + _len - 4, 4); if(!strncmp(ext, ".gba", 4)) { diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 16399d3c..9046d3d6 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -382,7 +382,7 @@ void EmuThread::run() if (Input::HotkeyPressed(HK_SwapScreens)) emit swapScreensToggle(); - if (GBACart::CartInserted && GBACart::HasSolarSensor) + /*if (GBACart::CartInserted && GBACart::HasSolarSensor) { if (Input::HotkeyPressed(HK_SolarSensorDecrease)) { @@ -398,6 +398,26 @@ void EmuThread::run() sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel); OSD::AddMessage(0, msg); } + }*/ + if (Input::HotkeyPressed(HK_SolarSensorDecrease)) + { + int level = GBACart::SetInput(GBACart::Input_SolarSensorDown, true); + if (level != -1) + { + char msg[64]; + sprintf(msg, "Solar sensor level: %d", level); + OSD::AddMessage(0, msg); + } + } + if (Input::HotkeyPressed(HK_SolarSensorIncrease)) + { + int level = GBACart::SetInput(GBACart::Input_SolarSensorUp, true); + if (level != -1) + { + char msg[64]; + sprintf(msg, "Solar sensor level: %d", level); + OSD::AddMessage(0, msg); + } } if (EmuRunning == 1)