diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/DSi_I2C.cpp | 7 | ||||
-rw-r--r-- | src/DSi_SD.cpp | 14 | ||||
-rw-r--r-- | src/GBACart.cpp | 532 | ||||
-rw-r--r-- | src/GBACart.h | 142 | ||||
-rw-r--r-- | src/NDS.cpp | 634 | ||||
-rw-r--r-- | src/NDS.h | 2 | ||||
-rw-r--r-- | src/NDSCart.cpp | 1762 | ||||
-rw-r--r-- | src/NDSCart.h | 161 | ||||
-rw-r--r-- | src/ROMList.h | 6 | ||||
-rw-r--r-- | src/Savestate.h | 4 | ||||
-rw-r--r-- | src/frontend/Util_ROM.cpp | 2 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.cpp | 22 |
12 files changed, 2006 insertions, 1282 deletions
diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index 16ec1a8..ad01a42 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 aba477d..01b6442 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 65ce8f1..246f986 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;
+CartCommon::~CartCommon()
+{
+}
-char SRAMPath[1024];
+void CartCommon::DoSavestate(Savestate* file)
+{
+ file->Section("GBCS");
+}
-void (*WriteFunc)(u32 addr, u8 val);
+void CartCommon::LoadSave(const char* path, u32 type)
+{
+}
+void CartCommon::RelocateSave(const char* path, bool write)
+{
+}
-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);
+int CartCommon::SetInput(int num, bool pressed)
+{
+ return -1;
+}
+u16 CartCommon::ROMRead(u32 addr)
+{
+ return 0;
+}
-bool Init()
+void CartCommon::ROMWrite(u32 addr, u16 val)
{
- SRAM = NULL;
- SRAMFile = NULL;
- return true;
}
-void DeInit()
+u8 CartCommon::SRAMRead(u32 addr)
{
- if (SRAMFile) fclose(SRAMFile);
- if (SRAM) delete[] SRAM;
+ return 0;
}
-void Reset()
+void CartCommon::SRAMWrite(u32 addr, u8 val)
{
- // do nothing, we don't want to clear GBA SRAM on reset
}
-void Eject()
+
+CartGame::CartGame(u8* rom, u32 len) : CartCommon()
{
- if (SRAMFile) fclose(SRAMFile);
- if (SRAM) delete[] SRAM;
- SRAM = NULL;
- SRAMFile = NULL;
+ 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()
+{
+ if (SRAMFile) fclose(SRAMFile);
+ if (SRAM) delete[] SRAM;
+}
+
+void CartGame::DoSavestate(Savestate* file)
{
- file->Section("GBCS"); // Game Boy [Advance] Cart Save
+ 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)
-{
- if (SRAMType == S_NULL)
- {
- return 0xFF;
- }
-
- if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M)
- {
- return Read_Flash(addr);
- }
- return *(u8*)&SRAM[addr];
-}
+const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183};
-u16 Read16(u32 addr)
+CartGameSolarSensor::CartGameSolarSensor(u8* rom, u32 len) : CartGame(rom, len)
{
- if (SRAMType == S_NULL)
- {
- return 0xFFFF;
- }
-
- if (SRAMType == S_FLASH512K || SRAMType == S_FLASH1M)
- {
- u16 val = Read_Flash(addr + 0) |
- (Read_Flash(addr + 1) << 8);
- return val;
- }
-
- return *(u16*)&SRAM[addr];
+ LightEdge = false;
+ LightCounter = 0;
+ LightSample = 0xFF;
+ LightLevel = 0;
}
-u32 Read32(u32 addr)
+CartGameSolarSensor::~CartGameSolarSensor()
{
- 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)
+void CartGameSolarSensor::DoSavestate(Savestate* file)
{
- u8 prev = *(u8*)&SRAM[addr];
+ CartGame::DoSavestate(file);
- WriteFunc(addr, val);
+ file->Var8((u8*)&LightEdge);
+ file->Var8(&LightCounter);
+ file->Var8(&LightSample);
+ file->Var8(&LightLevel);
}
-void Write16(u32 addr, u16 val)
+int CartGameSolarSensor::SetInput(int num, bool pressed)
{
- u16 prev = *(u16*)&SRAM[addr];
+ if (!pressed) return -1;
- WriteFunc(addr + 0, val & 0xFF);
- WriteFunc(addr + 1, val >> 8 & 0xFF);
-}
+ if (num == Input_SolarSensorDown)
+ {
+ if (LightLevel > 0)
+ LightLevel--;
-void Write32(u32 addr, u32 val)
-{
- u32 prev = *(u32*)&SRAM[addr];
+ return LightLevel;
+ }
+ else if (num == Input_SolarSensorUp)
+ {
+ if (LightLevel < 10)
+ LightLevel++;
- WriteFunc(addr + 0, val & 0xFF);
- WriteFunc(addr + 1, val >> 8 & 0xFF);
- WriteFunc(addr + 2, val >> 16 & 0xFF);
- WriteFunc(addr + 3, val >> 24 & 0xFF);
-}
+ return LightLevel;
+ }
+ return -1;
}
-
-namespace GBACart
-{
-
-const char SOLAR_SENSOR_GAMECODES[10][5] =
+void CartGameSolarSensor::ProcessGPIO()
{
- "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)
-};
+ if (GPIO.data & 4) return; // Boktai chip select
+ if (GPIO.data & 2) // Reset
+ {
+ 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++;
+ LightEdge = !(GPIO.data & 1);
-bool CartInserted;
-bool HasSolarSensor;
-u8* CartROM;
-u32 CartROMSize;
-u32 CartCRC;
-u32 CartID;
-GPIO CartGPIO; // overridden GPIO parameters
+ bool sendBit = LightCounter >= LightSample;
+ if (GPIO.control & 1)
+ {
+ GPIO.data = (GPIO.data & GPIO.direction) | ((sendBit << 3) & ~GPIO.direction & 0xF);
+ }
+}
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)
-{
- 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;
- }
-}
+int SetInput(int num, bool pressed)
+{
+ 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
-
-// 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()
-{
- LightEdge = false;
- LightCounter = 0;
- LightSample = 0xFF;
- LightLevel = 0;
+ return (addr >> 1) & 0xFFFF;
}
-void DoSavestate(Savestate* file)
+void ROMWrite(u32 addr, u16 val)
{
- file->Var8((u8*)&LightEdge);
- file->Var8(&LightCounter);
- file->Var8(&LightSample);
- file->Var8(&LightLevel);
+ if (Cart) Cart->ROMWrite(addr, val);
}
-void Process(GBACart::GPIO* gpio)
+u8 SRAMRead(u32 addr)
{
- 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++;
+ if (Cart) return Cart->SRAMRead(addr);
- LightEdge = !(gpio->data & 1);
+ return 0xFF;
+}
- bool sendBit = LightCounter >= LightSample;
- if (gpio->control & 1)
- {
- gpio->data = (gpio->data & gpio->direction) | ((sendBit << 3) & ~gpio->direction & 0xF);
- }
+void SRAMWrite(u32 addr, u8 val)
+{
+ if (Cart) Cart->SRAMWrite(addr, val);
}
}
diff --git a/src/GBACart.h b/src/GBACart.h index 7f5f916..1f4d258 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -22,39 +22,128 @@ #include "types.h"
#include "Savestate.h"
+namespace GBACart
+{
-namespace GBACart_SRAM
+// CartCommon -- base code shared by all cart types
+class CartCommon
{
+public:
+ CartCommon();
+ virtual ~CartCommon();
-extern u8* SRAM;
-extern u32 SRAMLength;
+ virtual void DoSavestate(Savestate* file);
-void Reset();
-void DoSavestate(Savestate* file);
+ virtual void LoadSave(const char* path, u32 type);
+ virtual void RelocateSave(const char* path, bool write);
-u8 Read8(u32 addr);
-u16 Read16(u32 addr);
-u32 Read32(u32 addr);
+ virtual int SetInput(int num, bool pressed);
-void Write8(u32 addr, u8 val);
-void Write16(u32 addr, u16 val);
-void Write32(u32 addr, u32 val);
+ 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];
+};
-namespace GBACart
+// 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;
-struct GPIO
+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
{
- u16 data;
- u16 direction;
- u16 control;
+ 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 12412eb..fa37c39 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<u8>(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<u16>(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<u32>(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 0x04000188: + ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); + 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)); + 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 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) @@ -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 56a454a..b40529b 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -29,53 +29,349 @@ #include "melonDLDI.h" #include "NDSCart_SRAMManager.h" -namespace NDSCart_SRAM + +namespace NDSCart { -u8* SRAM; -u32 SRAMLength; +// SRAM TODO: emulate write delays??? -char SRAMPath[1024]; -bool SRAMFileDirty; +u16 SPICnt; +u32 ROMCnt; + +u8 SPIData; +u32 SPIDataPos; +bool SPIHold; + +u8 ROMCommand[8]; +u32 ROMData; -void (*WriteFunc)(u8 val, bool islast); +u8 TransferData[0x4000]; +u32 TransferPos; +u32 TransferLen; +u32 TransferDir; +u8 TransferCmd[8]; -u32 Hold; -u8 CurCmd; -u32 DataPos; -u8 Data; +bool CartInserted; +u8* CartROM; +u32 CartROMSize; +u32 CartID; +bool CartIsHomebrew; +bool CartIsDSi; -u8 StatusReg; -u32 Addr; +CartCommon* Cart; +u32 Key1_KeyBuf[0x412]; -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); +u64 Key2_X; +u64 Key2_Y; -bool Init() +u32 ByteSwap(u32 val) { - SRAM = NULL; - return true; + return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); } -void DeInit() +void Key1_Encrypt(u32* data) { - if (SRAM) delete[] SRAM; + u32 y = data[0]; + u32 x = data[1]; + u32 z; + + for (u32 i = 0x0; i <= 0xF; i++) + { + z = Key1_KeyBuf[i] ^ x; + x = Key1_KeyBuf[0x012 + (z >> 24) ]; + x += Key1_KeyBuf[0x112 + ((z >> 16) & 0xFF)]; + x ^= Key1_KeyBuf[0x212 + ((z >> 8) & 0xFF)]; + x += Key1_KeyBuf[0x312 + (z & 0xFF)]; + x ^= y; + y = z; + } + + data[0] = x ^ Key1_KeyBuf[0x10]; + data[1] = y ^ Key1_KeyBuf[0x11]; } -void Reset() +void Key1_Decrypt(u32* data) { - if (SRAM) delete[] SRAM; - SRAM = NULL; + u32 y = data[0]; + u32 x = data[1]; + u32 z; + + for (u32 i = 0x11; i >= 0x2; i--) + { + z = Key1_KeyBuf[i] ^ x; + x = Key1_KeyBuf[0x012 + (z >> 24) ]; + x += Key1_KeyBuf[0x112 + ((z >> 16) & 0xFF)]; + x ^= Key1_KeyBuf[0x212 + ((z >> 8) & 0xFF)]; + x += Key1_KeyBuf[0x312 + (z & 0xFF)]; + x ^= y; + y = z; + } + + data[0] = x ^ Key1_KeyBuf[0x1]; + data[1] = y ^ Key1_KeyBuf[0x0]; } -void DoSavestate(Savestate* file) +void Key1_ApplyKeycode(u32* keycode, u32 mod) +{ + Key1_Encrypt(&keycode[1]); + Key1_Encrypt(&keycode[0]); + + u32 temp[2] = {0,0}; + + for (u32 i = 0; i <= 0x11; i++) + { + Key1_KeyBuf[i] ^= ByteSwap(keycode[i % mod]); + } + for (u32 i = 0; i <= 0x410; i+=2) + { + Key1_Encrypt(temp); + Key1_KeyBuf[i ] = temp[1]; + Key1_KeyBuf[i+1] = temp[0]; + } +} + +void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) +{ + // TODO: source the key data from different possible places + if (dsi && NDS::ConsoleType==1) + memcpy(Key1_KeyBuf, &DSi::ARM7iBIOS[0xC6D0], 0x1048); // hax + else + memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax + + u32 keycode[3] = {idcode, idcode>>1, idcode<<1}; + if (level >= 1) Key1_ApplyKeycode(keycode, mod); + if (level >= 2) Key1_ApplyKeycode(keycode, mod); + if (level >= 3) + { + keycode[1] <<= 1; + keycode[2] >>= 1; + Key1_ApplyKeycode(keycode, mod); + } +} + + +void Key2_Encrypt(u8* data, u32 len) +{ + for (u32 i = 0; i < len; i++) + { + Key2_X = (((Key2_X >> 5) ^ + (Key2_X >> 17) ^ + (Key2_X >> 18) ^ + (Key2_X >> 31)) & 0xFF) + + (Key2_X << 8); + Key2_Y = (((Key2_Y >> 5) ^ + (Key2_Y >> 23) ^ + (Key2_Y >> 18) ^ + (Key2_Y >> 31)) & 0xFF) + + (Key2_Y << 8); + + Key2_X &= 0x0000007FFFFFFFFFULL; + Key2_Y &= 0x0000007FFFFFFFFFULL; + } +} + + +void ApplyModcrypt(u32 addr, u32 len, u8* iv) +{return; + u8 key[16]; + + DSi_AES::GetModcryptKey(&CartROM[0], key); + DSi_AES::ApplyModcrypt(&CartROM[addr], len, key, iv); +} + + +CartCommon::CartCommon(u8* rom, u32 len, u32 chipid) +{ + ROM = rom; + ROMLength = len; + ChipID = chipid; + + u8 unitcode = ROM[0x12]; + IsDSi = (unitcode & 0x02) != 0; +} + +CartCommon::~CartCommon() +{ +} + +void CartCommon::Reset() +{ + CmdEncMode = 0; + DataEncMode = 0; +} + +void CartCommon::SetupDirectBoot() +{ + CmdEncMode = 2; + DataEncMode = 2; +} + +void CartCommon::DoSavestate(Savestate* file) { file->Section("NDCS"); + file->Var32(&CmdEncMode); + file->Var32(&DataEncMode); +} + +void CartCommon::LoadSave(const char* path, u32 type) +{ +} + +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); +} + + +CartRetail::CartRetail(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid) +{ + SRAM = nullptr; +} + +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 @@ -104,79 +400,70 @@ void DoSavestate(Savestate* file) // SPI status shito - file->Var32(&Hold); - file->Var8(&CurCmd); - file->Var32(&DataPos); - file->Var8(&Data); - - file->Var8(&StatusReg); - file->Var32(&Addr); + 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 LoadSave(const char* path, u32 type) +void CartRetail::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) + 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) { - fseek(f, 0, SEEK_END); - SRAMLength = (u32)ftell(f); SRAM = new u8[SRAMLength]; + memset(SRAM, 0xFF, SRAMLength); + } + FILE* f = Platform::OpenFile(path, "rb"); + if (f) + { fseek(f, 0, SEEK_SET); - fread(SRAM, SRAMLength, 1, f); + fread(SRAM, 1, SRAMLength, 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) + switch (type) { - 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; + 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 } - - Hold = 0; - CurCmd = 0; - Data = 0; - StatusReg = 0x00; } -void RelocateSave(const char* path, bool write) +void CartRetail::RelocateSave(const char* path, bool write) { if (!write) { @@ -198,506 +485,733 @@ void RelocateSave(const char* path, bool write) fclose(f); } -u8 Read() +int CartRetail::ImportSRAM(const u8* data, u32 length) { - return Data; + 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 Write_Null(u8 val, bool islast) {} +void CartRetail::FlushSRAMFile() +{ + if (!SRAMFileDirty) return; -void Write_EEPROMTiny(u8 val, bool islast) + SRAMFileDirty = false; + NDSCart_SRAMManager::RequestFlush(); +} + +int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len) { - switch (CurCmd) + switch (cmd[0]) { - case 0x02: - case 0x0A: - if (DataPos < 1) + case 0xB7: { - Addr = val; - Data = 0; + 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 { - SRAM[(Addr + ((CurCmd==0x0A)?0x100:0)) & 0x1FF] = val; - Addr++; + // TODO: implement WP bits! + if (SRAMStatus & (1<<1)) + { + SRAM[(SRAMAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF] = val; + SRAMFileDirty |= last; + } + SRAMAddr++; } - break; + if (last) SRAMStatus &= ~(1<<1); + return 0; - case 0x03: - case 0x0B: - if (DataPos < 1) + case 0x03: // read low + case 0x0B: // read high + if (pos < 2) { - Addr = val; - Data = 0; + SRAMAddr = val; + return 0; } else { - Data = SRAM[(Addr + ((CurCmd==0x0B)?0x100:0)) & 0x1FF]; - Addr++; + u8 ret = SRAM[(SRAMAddr + ((SRAMCmd==0x0B)?0x100:0)) & 0x1FF]; + SRAMAddr++; + return ret; } - break; - case 0x9F: - Data = 0xFF; - break; + case 0x9F: // read JEDEC ID + return 0xFF; default: - if (DataPos==0) - printf("unknown tiny EEPROM save command %02X\n", CurCmd); - break; + if (pos == 1) + printf("unknown tiny EEPROM save command %02X\n", SRAMCmd); + return 0; } } -void Write_EEPROM(u8 val, bool islast) +u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last) { u32 addrsize = 2; if (SRAMLength > 65536) addrsize++; - switch (CurCmd) + switch (SRAMCmd) { - case 0x02: - if (DataPos < addrsize) + 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) { - Addr <<= 8; - Addr |= val; - Data = 0; + SRAMAddr <<= 8; + SRAMAddr |= val; } else { - SRAM[Addr & (SRAMLength-1)] = val; - Addr++; + // TODO: implement WP bits + if (SRAMStatus & (1<<1)) + { + SRAM[SRAMAddr & (SRAMLength-1)] = val; + SRAMFileDirty |= last; + } + SRAMAddr++; } - break; + if (last) SRAMStatus &= ~(1<<1); + return 0; - case 0x03: - if (DataPos < addrsize) + case 0x03: // read + if (pos <= addrsize) { - Addr <<= 8; - Addr |= val; - Data = 0; + SRAMAddr <<= 8; + SRAMAddr |= val; + return 0; } else { - Data = SRAM[Addr & (SRAMLength-1)]; - Addr++; + // TODO: size limit!! + u8 ret = SRAM[SRAMAddr & (SRAMLength-1)]; + SRAMAddr++; + return ret; } - break; - case 0x9F: - Data = 0xFF; - break; + case 0x9F: // read JEDEC ID + // TODO: GBAtek implies it's not always all FF (FRAM) + return 0xFF; default: - if (DataPos==0) - printf("unknown EEPROM save command %02X\n", CurCmd); - break; + if (pos == 1) + printf("unknown EEPROM save command %02X\n", SRAMCmd); + return 0; } } -void Write_Flash(u8 val, bool islast) +u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) { - switch (CurCmd) + switch (SRAMCmd) { - case 0x02: - if (DataPos < 3) + case 0x05: // read status register + return SRAMStatus; + + case 0x02: // page program + if (pos <= 3) { - Addr <<= 8; - Addr |= val; - Data = 0; + SRAMAddr <<= 8; + SRAMAddr |= val; } else { - SRAM[Addr & (SRAMLength-1)] = 0; - Addr++; + if (SRAMStatus & (1<<1)) + { + // CHECKME: should it be &=~val ?? + SRAM[SRAMAddr & (SRAMLength-1)] = 0; + SRAMFileDirty |= last; + } + SRAMAddr++; } - break; + if (last) SRAMStatus &= ~(1<<1); + return 0; - case 0x03: - if (DataPos < 3) + case 0x03: // read + if (pos <= 3) { - Addr <<= 8; - Addr |= val; - Data = 0; + SRAMAddr <<= 8; + SRAMAddr |= val; + return 0; } else { - Data = SRAM[Addr & (SRAMLength-1)]; - Addr++; + u8 ret = SRAM[SRAMAddr & (SRAMLength-1)]; + SRAMAddr++; + return ret; } - break; - case 0x0A: - if (DataPos < 3) + case 0x0A: // page write + if (pos <= 3) { - Addr <<= 8; - Addr |= val; - Data = 0; + SRAMAddr <<= 8; + SRAMAddr |= val; } else { - SRAM[Addr & (SRAMLength-1)] = val; - Addr++; + if (SRAMStatus & (1<<1)) + { + SRAM[SRAMAddr & (SRAMLength-1)] = val; + SRAMFileDirty |= last; + } + SRAMAddr++; } - break; + if (last) SRAMStatus &= ~(1<<1); + return 0; - case 0x9F: - Data = 0xFF; - break; + case 0x9F: // read JEDEC IC + // GBAtek says it should be 0xFF. verify? + return 0xFF; - case 0xD8: - if (DataPos < 3) + case 0xD8: // sector erase + if (pos <= 3) { - Addr <<= 8; - Addr |= val; - Data = 0; + SRAMAddr <<= 8; + SRAMAddr |= val; } - if (DataPos == 2) + if ((pos == 3) && (SRAMStatus & (1<<1))) { for (u32 i = 0; i < 0x10000; i++) { - SRAM[Addr & (SRAMLength-1)] = 0; - Addr++; + SRAM[SRAMAddr & (SRAMLength-1)] = 0; + SRAMAddr++; } + SRAMFileDirty = true; } - break; + if (last) SRAMStatus &= ~(1<<1); + return 0; - case 0xDB: - if (DataPos < 3) + case 0xDB: // page erase + if (pos <= 3) { - Addr <<= 8; - Addr |= val; - Data = 0; + SRAMAddr <<= 8; + SRAMAddr |= val; } - if (DataPos == 2) + if ((pos == 3) && (SRAMStatus & (1<<1))) { for (u32 i = 0; i < 0x100; i++) { - SRAM[Addr & (SRAMLength-1)] = 0; - Addr++; + SRAM[SRAMAddr & (SRAMLength-1)] = 0; + SRAMAddr++; } + SRAMFileDirty = true; } - break; + if (last) SRAMStatus &= ~(1<<1); + return 0; default: - if (DataPos==0) - printf("unknown Flash save command %02X\n", CurCmd); - break; + if (pos == 1) + printf("unknown FLASH save command %02X\n", SRAMCmd); + return 0; } } -void Write(u8 val, u32 hold) + +CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid) { - bool islast = false; +} - if (!hold) - { - if (Hold) islast = true; - else CurCmd = val; - Hold = 0; - } +CartRetailNAND::~CartRetailNAND() +{ +} - if (hold && (!Hold)) - { - CurCmd = val; - Hold = 1; - Data = 0; - DataPos = 0; - Addr = 0; - //printf("save SPI command %02X\n", CurCmd); - return; - } +void CartRetailNAND::Reset() +{ + CartRetail::Reset(); - 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; + SRAMAddr = 0; + SRAMStatus = 0x20; + SRAMWindow = 0; - case 0x02: - case 0x03: - case 0x0A: - case 0x0B: - case 0x9F: - case 0xD8: - case 0xDB: - WriteFunc(val, islast); - DataPos++; - break; + // ROM header 94/96 = SRAM addr start / 0x20000 + SRAMBase = *(u16*)&ROM[0x96] << 17; - case 0x04: // write disable - StatusReg &= ~(1<<1); - Data = 0; - break; + memset(SRAMWriteBuffer, 0, 0x800); +} - case 0x05: // read status reg - Data = StatusReg; - break; +void CartRetailNAND::DoSavestate(Savestate* file) +{ + CartRetail::DoSavestate(file); - case 0x06: // write enable - StatusReg |= (1<<1); - Data = 0; - break; + file->Var32(&SRAMBase); + file->Var32(&SRAMWindow); - default: - if (DataPos==0) - printf("unknown save SPI command %02X %02X %d\n", CurCmd, val, islast); - break; - } + file->VarArray(SRAMWriteBuffer, 0x800); + file->Var32(&SRAMWritePos); - SRAMFileDirty |= islast && (CurCmd == 0x02 || CurCmd == 0x0A) && (SRAMLength > 0); + if (!file->Saving) + BuildSRAMID(); } -void FlushSRAMFile() +void CartRetailNAND::LoadSave(const char* path, u32 type) { - if (!SRAMFileDirty) return; - - SRAMFileDirty = false; - NDSCart_SRAMManager::RequestFlush(); + CartRetail::LoadSave(path, type); + BuildSRAMID(); } +int CartRetailNAND::ImportSRAM(const u8* data, u32 length) +{ + CartRetail::ImportSRAM(data, length); + BuildSRAMID(); } - -namespace NDSCart +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]; -u16 SPICnt; -u32 ROMCnt; + 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; -u8 ROMCommand[8]; -u32 ROMData; + case 0x82: // commit write + if (SRAMAddr && SRAMWritePos) + { + if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000)) + { + memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800); + SRAMFileDirty = true; + } -u8 TransferData[0x4000]; -u32 TransferPos; -u32 TransferLen; -u32 TransferDir; -u8 TransferCmd[8]; + SRAMAddr = 0; + SRAMWritePos = 0; + } + SRAMStatus &= ~(1<<4); + return 0; -bool CartInserted; -u8* CartROM; -u32 CartROMSize; -u32 CartID; -bool CartIsHomebrew; -bool CartIsDSi; + case 0x84: // discard write buffer + SRAMAddr = 0; + SRAMWritePos = 0; + return 0; -FILE* CartSD; + case 0x85: // write enable + if (SRAMWindow) + { + SRAMStatus |= (1<<4); + SRAMWritePos = 0; + } + return 0; -u32 CmdEncMode; -u32 DataEncMode; + case 0x8B: // revert to ROM read mode + SRAMWindow = 0; + return 0; -u32 Key1_KeyBuf[0x412]; + 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 + }; -u64 Key2_X; -u64 Key2_Y; + 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); -void ROMCommand_Retail(u8* cmd); -void ROMCommand_RetailNAND(u8* cmd); -void ROMCommand_Homebrew(u8* cmd); + SRAMWindow = addr; + } + return 0; -void (*ROMCommandHandler)(u8* cmd); + 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); -u32 ByteSwap(u32 val) -{ - return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); + 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 Key1_Encrypt(u32* data) +void CartRetailNAND::ROMCommandFinish(u8* cmd, u8* data, u32 len) { - u32 y = data[0]; - u32 x = data[1]; - u32 z; - - for (u32 i = 0x0; i <= 0xF; i++) + switch (cmd[0]) { - z = Key1_KeyBuf[i] ^ x; - x = Key1_KeyBuf[0x012 + (z >> 24) ]; - x += Key1_KeyBuf[0x112 + ((z >> 16) & 0xFF)]; - x ^= Key1_KeyBuf[0x212 + ((z >> 8) & 0xFF)]; - x += Key1_KeyBuf[0x312 + (z & 0xFF)]; - x ^= y; - y = z; + 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); } +} - data[0] = x ^ Key1_KeyBuf[0x10]; - data[1] = y ^ Key1_KeyBuf[0x11]; +u8 CartRetailNAND::SPIWrite(u8 val, u32 pos, bool last) +{ + return 0xFF; } -void Key1_Decrypt(u32* data) +void CartRetailNAND::BuildSRAMID() { - u32 y = data[0]; - u32 x = data[1]; - u32 z; + // 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. - for (u32 i = 0x11; i >= 0x2; i--) + if (SRAMLength > 0x20000) { - z = Key1_KeyBuf[i] ^ x; - x = Key1_KeyBuf[0x012 + (z >> 24) ]; - x += Key1_KeyBuf[0x112 + ((z >> 16) & 0xFF)]; - x ^= Key1_KeyBuf[0x212 + ((z >> 8) & 0xFF)]; - x += Key1_KeyBuf[0x312 + (z & 0xFF)]; - x ^= y; - y = z; + 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); } +} - data[0] = x ^ Key1_KeyBuf[0x1]; - data[1] = y ^ Key1_KeyBuf[0x0]; + +CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion) : CartRetail(rom, len, chipid) +{ + IRVersion = irversion; } -void Key1_ApplyKeycode(u32* keycode, u32 mod) +CartRetailIR::~CartRetailIR() { - Key1_Encrypt(&keycode[1]); - Key1_Encrypt(&keycode[0]); +} - u32 temp[2] = {0,0}; +void CartRetailIR::Reset() +{ + CartRetail::Reset(); - for (u32 i = 0; i <= 0x11; i++) - { - Key1_KeyBuf[i] ^= ByteSwap(keycode[i % mod]); - } - for (u32 i = 0; i <= 0x410; i+=2) - { - Key1_Encrypt(temp); - Key1_KeyBuf[i ] = temp[1]; - Key1_KeyBuf[i+1] = temp[0]; - } + IRCmd = 0; } -void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) +void CartRetailIR::DoSavestate(Savestate* file) { - // TODO: source the key data from different possible places - if (dsi && NDS::ConsoleType==1) - memcpy(Key1_KeyBuf, &DSi::ARM7iBIOS[0xC6D0], 0x1048); // hax - else - memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax + CartRetail::DoSavestate(file); - u32 keycode[3] = {idcode, idcode>>1, idcode<<1}; - if (level >= 1) Key1_ApplyKeycode(keycode, mod); - if (level >= 2) Key1_ApplyKeycode(keycode, mod); - if (level >= 3) + file->Var8(&IRCmd); +} + +u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last) +{ + if (pos == 0) { - keycode[1] <<= 1; - keycode[2] >>= 1; - Key1_ApplyKeycode(keycode, mod); + IRCmd = val; + return 0; } -} + // TODO: emulate actual IR comm -void Key2_Encrypt(u8* data, u32 len) -{ - for (u32 i = 0; i < len; i++) + switch (IRCmd) { - Key2_X = (((Key2_X >> 5) ^ - (Key2_X >> 17) ^ - (Key2_X >> 18) ^ - (Key2_X >> 31)) & 0xFF) - + (Key2_X << 8); - Key2_Y = (((Key2_Y >> 5) ^ - (Key2_Y >> 23) ^ - (Key2_Y >> 18) ^ - (Key2_Y >> 31)) & 0xFF) - + (Key2_Y << 8); + case 0x00: // pass-through + return CartRetail::SPIWrite(val, pos-1, last); - Key2_X &= 0x0000007FFFFFFFFFULL; - Key2_Y &= 0x0000007FFFFFFFFFULL; + case 0x08: // ID + return 0xAA; } } -void ApplyModcrypt(u32 addr, u32 len, u8* iv) -{return; - u8 key[16]; +CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid) +{ + printf("POKETYPE CART\n"); +} - DSi_AES::GetModcryptKey(&CartROM[0], key); - DSi_AES::ApplyModcrypt(&CartROM[addr], len, key, iv); +CartRetailBT::~CartRetailBT() +{ } +void CartRetailBT::Reset() +{ + CartRetail::Reset(); +} -bool Init() +void CartRetailBT::DoSavestate(Savestate* file) { - if (!NDSCart_SRAM::Init()) return false; + CartRetail::DoSavestate(file); +} - CartROM = NULL; +u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) +{ + printf("POKETYPE SPI: %02X %d %d - %08X\n", val, pos, last, NDS::GetPC(0)); - CartSD = NULL; + /*if (pos == 0) + { + // TODO do something with it?? + if(val==0xFF)SetIRQ(); + } + if(pos==7)SetIRQ();*/ - return true; + return 0; } -void DeInit() -{ - if (CartROM) delete[] CartROM; - - if (CartSD) fclose(CartSD); - NDSCart_SRAM::DeInit(); +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; } -void Reset() +CartHomebrew::~CartHomebrew() { - CartInserted = false; - if (CartROM) delete[] CartROM; - CartROM = NULL; - CartROMSize = 0; - CartID = 0; - CartIsHomebrew = false; - CartIsDSi = false; + if (SDFile) fclose(SDFile); +} - if (CartSD) fclose(CartSD); - CartSD = NULL; +void CartHomebrew::Reset() +{ + CartCommon::Reset(); - ROMCommandHandler = NULL; + if (SDFile) fclose(SDFile); - NDSCart_SRAM::Reset(); + if (Config::DLDIEnable) + SDFile = Platform::OpenLocalFile(Config::DLDISDPath, "r+b"); + else + SDFile = nullptr; +} - ResetCart(); +void CartHomebrew::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); } -void DoSavestate(Savestate* file) +int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len) { - file->Section("NDSC"); + switch (cmd[0]) + { + case 0xB7: + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + memset(data, 0, len); - file->Var16(&SPICnt); - file->Var32(&ROMCnt); + 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; - file->VarArray(ROMCommand, 8); - file->Var32(&ROMData); + case 0xC0: // SD read + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + u64 addr = sector * 0x200ULL; - file->VarArray(TransferData, 0x4000); - file->Var32(&TransferPos); - file->Var32(&TransferLen); - file->Var32(&TransferDir); - file->VarArray(TransferCmd, 8); + if (SDFile) + { + fseek(SDFile, addr, SEEK_SET); + fread(data, len, 1, SDFile); + } + } + return 0; - // 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) + case 0xC1: // SD write + return 1; - file->Var32(&CmdEncMode); - file->Var32(&DataEncMode); + default: + return CartCommon::ROMCommandStart(cmd, data, len); + } +} - // TODO: check KEY1 shit?? +void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len) +{ + // TODO: delayed SD writing? like we have for SRAM - NDSCart_SRAM::DoSavestate(file); -} + 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; -void ApplyDLDIPatch(const u8* patch, u32 len) + default: + return CartCommon::ROMCommandFinish(cmd, data, len); + } +} + +void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen) { - u32 offset = *(u32*)&CartROM[0x20]; - u32 size = *(u32*)&CartROM[0x2C]; + u32 offset = *(u32*)&ROM[0x20]; + u32 size = *(u32*)&ROM[0x2C]; - u8* binary = &CartROM[offset]; + 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) + CartInserted = true; + + u32 irversion = 0; + if ((gamecode & 0xFF) == 'I') { - // TODO: in the case of an already-encrypted secure area, direct boot - // needs it decrypted - NDS::SetupDirectBoot(); - CmdEncMode = 2; + if (((gamecode >> 8) & 0xFF) < 'P') + irversion = 1; // Active Health / Walk with Me + else + irversion = 2; // Pokémon HG/SS, B/W, B2/W2 } - CartInserted = true; - - // TODO: support more fancy cart types (homebrew?, flashcarts, etc) 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); - - if (TransferDir == 1) - { - // finish a write + NDS::SetIRQ((NDS::ExMemCnt[0]>>11)&0x1, NDS::IRQ_CartXferDone); - 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 3c0cebe..d54dc76 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 9a73b78..cd1ef4e 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 bcd813d..bad69cf 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -22,8 +22,8 @@ #include <stdio.h> #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 b5e8a05..40552e2 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 16399d3..9046d3d 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) |