aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DSi_I2C.cpp7
-rw-r--r--src/DSi_SD.cpp14
-rw-r--r--src/GBACart.cpp532
-rw-r--r--src/GBACart.h142
-rw-r--r--src/NDS.cpp634
-rw-r--r--src/NDS.h2
-rw-r--r--src/NDSCart.cpp1762
-rw-r--r--src/NDSCart.h161
-rw-r--r--src/ROMList.h6
-rw-r--r--src/Savestate.h4
-rw-r--r--src/frontend/Util_ROM.cpp2
-rw-r--r--src/frontend/qt_sdl/main.cpp22
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)
diff --git a/src/NDS.h b/src/NDS.h
index c73a2ca..8aa1397 100644
--- a/src/NDS.h
+++ b/src/NDS.h
@@ -82,7 +82,7 @@ enum
IRQ_IPCSync,
IRQ_IPCSendDone,
IRQ_IPCRecv,
- IRQ_CartSendDone, // TODO: less misleading name
+ IRQ_CartXferDone,
IRQ_CartIREQMC, // IRQ triggered by game cart (example: Pokémon Typing Adventure, BT controller)
IRQ_GXFIFO,
IRQ_LidOpen,
diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp
index 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)