start decoupling the cart classes from the global NDSCart state

This commit is contained in:
Arisotura
2021-04-14 22:27:54 +02:00
parent 02620026dd
commit e423c234e3
2 changed files with 116 additions and 112 deletions

View File

@ -623,10 +623,14 @@ void ApplyModcrypt(u32 addr, u32 len, u8* iv)
} }
CartCommon::CartCommon(u8* rom, u32 len) CartCommon::CartCommon(u8* rom, u32 len, u32 chipid)
{ {
ROM = rom; ROM = rom;
ROMLength = len; ROMLength = len;
ChipID = chipid;
u8 unitcode = ROM[0x12];
IsDSi = (unitcode & 0x02) != 0;
} }
CartCommon::~CartCommon() CartCommon::~CartCommon()
@ -654,47 +658,44 @@ void CartCommon::FlushSRAMFile()
{ {
} }
void CartCommon::ROMCommandStart(u8* cmd) int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len)
{ {
switch (cmd[0]) switch (cmd[0])
{ {
case 0x9F: case 0x9F:
memset(TransferData, 0xFF, TransferLen); memset(data, 0xFF, len);
break; return 0;
case 0x00: case 0x00:
memset(TransferData, 0, TransferLen); memset(data, 0, len);
if (TransferLen > 0x1000) if (len > 0x1000)
{ {
ReadROM(0, 0x1000, 0); ReadROM(0, 0x1000, data, 0);
for (u32 pos = 0x1000; pos < TransferLen; pos += 0x1000) for (u32 pos = 0x1000; pos < len; pos += 0x1000)
memcpy(TransferData+pos, TransferData, 0x1000); memcpy(data+pos, data, 0x1000);
} }
else else
ReadROM(0, TransferLen, 0); ReadROM(0, len, data, 0);
break; return 0;
case 0x90: case 0x90:
case 0xB8: case 0xB8:
for (u32 pos = 0; pos < TransferLen; pos += 4) for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&TransferData[pos] = CartID; *(u32*)&data[pos] = ChipID;
break; return 0;
case 0x3C: case 0x3C:
if (CartInserted)
{
CmdEncMode = 1; CmdEncMode = 1;
Key1_InitKeycode(false, *(u32*)&CartROM[0xC], 2, 2); Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2);
} return 0;
break;
case 0x3D: case 0x3D:
if (CartInserted && CartIsDSi) if (IsDSi)
{ {
CmdEncMode = 11; CmdEncMode = 11;
Key1_InitKeycode(true, *(u32*)&CartROM[0xC], 1, 2); Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2);
} }
break; return 0;
default: default:
if (CmdEncMode == 1 || CmdEncMode == 11) if (CmdEncMode == 1 || CmdEncMode == 11)
@ -703,12 +704,12 @@ void CartCommon::ROMCommandStart(u8* cmd)
{ {
case 0x40: case 0x40:
DataEncMode = 2; DataEncMode = 2;
break; return 0;
case 0x10: case 0x10:
for (u32 pos = 0; pos < TransferLen; pos += 4) for (u32 pos = 0; pos < len; pos += 4)
*(u32*)&TransferData[pos] = CartID; *(u32*)&data[pos] = ChipID;
break; return 0;
case 0x20: case 0x20:
{ {
@ -721,20 +722,20 @@ void CartCommon::ROMCommandStart(u8* cmd)
addr -= 0x4000; addr -= 0x4000;
addr += arm9i_base; addr += arm9i_base;
} }
ReadROM(addr, 0x1000, 0); ReadROM(addr, 0x1000, data, 0);
} }
break; return 0;
case 0xA0: case 0xA0:
CmdEncMode = 2; CmdEncMode = 2;
break; return 0;
} }
} }
break; return 0;
} }
} }
void CartCommon::ROMCommandFinish(u8* cmd) void CartCommon::ROMCommandFinish(u8* cmd, u8* data, u32 len)
{ {
} }
@ -743,17 +744,17 @@ u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last)
return 0xFF; return 0xFF;
} }
void CartCommon::ReadROM(u32 addr, u32 len, u32 offset) void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset)
{ {
if (addr >= ROMLength) return; if (addr >= ROMLength) return;
if ((addr+len) > ROMLength) if ((addr+len) > ROMLength)
len = ROMLength - addr; len = ROMLength - addr;
memcpy(TransferData+offset, ROM+addr, len); memcpy(data+offset, ROM+addr, len);
} }
CartRetail::CartRetail(u8* rom, u32 len) : CartCommon(rom, len) CartRetail::CartRetail(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid)
{ {
SRAM = nullptr; SRAM = nullptr;
} }
@ -849,28 +850,28 @@ void CartRetail::FlushSRAMFile()
NDSCart_SRAMManager::RequestFlush(); NDSCart_SRAMManager::RequestFlush();
} }
void CartRetail::ROMCommandStart(u8* cmd) int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len)
{ {
switch (cmd[0]) switch (cmd[0])
{ {
case 0xB7: case 0xB7:
{ {
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
memset(TransferData, 0, TransferLen); memset(data, 0, len);
if (((addr + TransferLen - 1) >> 12) != (addr >> 12)) if (((addr + len - 1) >> 12) != (addr >> 12))
{ {
u32 len1 = 0x1000 - (addr & 0xFFF); u32 len1 = 0x1000 - (addr & 0xFFF);
ReadROM_B7(addr, len1, 0); ReadROM_B7(addr, len1, data, 0);
ReadROM_B7(addr+len1, TransferLen-len1, len1); ReadROM_B7(addr+len1, len-len1, data, len1);
} }
else else
ReadROM_B7(addr, TransferLen, 0); ReadROM_B7(addr, len, data, 0);
} }
break; return 0;
default: default:
return CartCommon::ROMCommandStart(cmd); return CartCommon::ROMCommandStart(cmd, data, len);
} }
} }
@ -910,7 +911,7 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last)
//return ret; //return ret;
} }
void CartRetail::ReadROM_B7(u32 addr, u32 len, u32 offset) void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
{ {
addr &= (ROMLength-1); addr &= (ROMLength-1);
@ -921,7 +922,7 @@ void CartRetail::ReadROM_B7(u32 addr, u32 len, u32 offset)
// also protect DSi region if not unlocked // also protect DSi region if not unlocked
// and other security shenanigans // and other security shenanigans
memcpy(TransferData+offset, ROM+addr, len); memcpy(data+offset, ROM+addr, len);
} }
u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
@ -1147,9 +1148,8 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
} }
CartRetailNAND::CartRetailNAND(u8* rom, u32 len) : CartRetail(rom, len) CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid)
{ {
printf("ohai I am a NAND cart\n");
} }
CartRetailNAND::~CartRetailNAND() CartRetailNAND::~CartRetailNAND()
@ -1191,7 +1191,7 @@ void CartRetailNAND::LoadSave(const char* path, u32 type)
} }
} }
void CartRetailNAND::ROMCommandStart(u8* cmd) int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len)
{ {
// ROM header 94/96 = save addr start / 0x20000 // ROM header 94/96 = save addr start / 0x20000
@ -1199,11 +1199,11 @@ void CartRetailNAND::ROMCommandStart(u8* cmd)
{ {
case 0x85: // write enable? case 0x85: // write enable?
SRAMStatus |= (1<<4); SRAMStatus |= (1<<4);
break; return 0;
case 0x8B: // revert to ROM read mode case 0x8B: // revert to ROM read mode
SRAMReadWindow = 0; SRAMReadWindow = 0;
break; return 0;
case 0x94: // return ID data case 0x94: // return ID data
{ {
@ -1219,12 +1219,12 @@ void CartRetailNAND::ROMCommandStart(u8* cmd)
if (SRAMLength) memcpy(&iddata[0x18], &SRAM[SRAMLength - 0x800], 16); if (SRAMLength) memcpy(&iddata[0x18], &SRAM[SRAMLength - 0x800], 16);
memset(TransferData, 0, TransferLen); memset(data, 0, len);
memcpy(TransferData, iddata, std::min(TransferLen, 0x30u)); memcpy(data, iddata, std::min(len, 0x30u));
} }
break; return 0;
case 0xB2: // set window for reading SRAM case 0xB2: // set window for accessing SRAM
{ {
u32 addr = (cmd[1]<<24) | ((cmd[2]&0xFE)<<16); u32 addr = (cmd[1]<<24) | ((cmd[2]&0xFE)<<16);
@ -1234,7 +1234,7 @@ void CartRetailNAND::ROMCommandStart(u8* cmd)
SRAMReadWindow = addr; SRAMReadWindow = addr;
} }
break; return 0;
case 0xB7: case 0xB7:
{ {
@ -1242,20 +1242,20 @@ void CartRetailNAND::ROMCommandStart(u8* cmd)
if (SRAMReadWindow == 0) if (SRAMReadWindow == 0)
{ {
memset(TransferData, 0, TransferLen); memset(data, 0, len);
if (((addr + TransferLen - 1) >> 12) != (addr >> 12)) if (((addr + len - 1) >> 12) != (addr >> 12))
{ {
u32 len1 = 0x1000 - (addr & 0xFFF); u32 len1 = 0x1000 - (addr & 0xFFF);
ReadROM_B7(addr, len1, 0); ReadROM_B7(addr, len1, data, 0);
ReadROM_B7(addr+len1, TransferLen-len1, len1); ReadROM_B7(addr+len1, len-len1, data, len1);
} }
else else
ReadROM_B7(addr, TransferLen, 0); ReadROM_B7(addr, len, data, 0);
} }
else else
{ {
memset(TransferData, 0xFF, TransferLen); memset(data, 0xFF, len);
u32 sramstart = *(u16*)&ROM[0x96] << 17; u32 sramstart = *(u16*)&ROM[0x96] << 17;
u32 sramend = sramstart + 0x800000; // CHECKME u32 sramend = sramstart + 0x800000; // CHECKME
@ -1267,13 +1267,13 @@ void CartRetailNAND::ROMCommandStart(u8* cmd)
if (addr == (sramstart+0x7FF800)) if (addr == (sramstart+0x7FF800))
{ {
u8 iddata[0x10] = {0xEC, 0x00, 0x9E, 0xA1, 0x51, 0x65, 0x34, 0x35, 0x30, 0x35, 0x30, 0x31, 0x19, 0x19, 0x02, 0x0A}; u8 iddata[0x10] = {0xEC, 0x00, 0x9E, 0xA1, 0x51, 0x65, 0x34, 0x35, 0x30, 0x35, 0x30, 0x31, 0x19, 0x19, 0x02, 0x0A};
memcpy(TransferData, iddata, std::min(TransferLen, 0x10u)); memcpy(data, iddata, std::min(len, 0x10u));
printf("READING ID BLOCK @ %08X\n", addr); printf("READING ID BLOCK @ %08X\n", addr);
} }
} }
} }
} }
break; return 0;
case 0xD6: // read NAND status case 0xD6: // read NAND status
{ {
@ -1281,28 +1281,28 @@ void CartRetailNAND::ROMCommandStart(u8* cmd)
// bit5: ready // bit5: ready
// bit4: write enable // bit4: write enable
printf("NAND STATUS %02X\n", SRAMStatus); printf("NAND STATUS %02X\n", SRAMStatus);
for (u32 i = 0; i < TransferLen; i+=4) for (u32 i = 0; i < len; i+=4)
*(u32*)&TransferData[i] = SRAMStatus * 0x01010101; *(u32*)&data[i] = SRAMStatus * 0x01010101;
} }
break; return 0;
default: default:
/*if (cmd[0] != 0xB8) /*if (cmd[0] != 0xB8)
printf("shitty command %02X %02X %02X %02X %02X %02X %02X %02X - %08X\n", printf("shitty command %02X %02X %02X %02X %02X %02X %02X %02X - %08X\n",
cmd[0], cmd[1], cmd[2], cmd[3], cmd[0], cmd[1], cmd[2], cmd[3],
cmd[4], cmd[5], cmd[6], cmd[7], TransferLen);*/ cmd[4], cmd[5], cmd[6], cmd[7], len);*/
return CartRetail::ROMCommandStart(cmd); return CartRetail::ROMCommandStart(cmd, data, len);
} }
} }
void CartRetailNAND::ROMCommandFinish(u8* cmd) void CartRetailNAND::ROMCommandFinish(u8* cmd, u8* data, u32 len)
{ {
switch (cmd[0]) switch (cmd[0])
{ {
// TODO! // TODO!
default: default:
return CartCommon::ROMCommandFinish(cmd); return CartCommon::ROMCommandFinish(cmd, data, len);
} }
} }
@ -1315,8 +1315,10 @@ u8 CartRetailNAND::SPIWrite(u8 val, u32 pos, bool last)
// //
CartHomebrew::CartHomebrew(u8* rom, u32 len) : CartCommon(rom, len) CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid)
{ {
// TODO: presumably CartSD loading should go here
if (Config::DLDIEnable) if (Config::DLDIEnable)
ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI)); ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI));
} }
@ -1334,25 +1336,25 @@ void CartHomebrew::DoSavestate(Savestate* file)
// TODO? // TODO?
} }
void CartHomebrew::ROMCommandStart(u8* cmd) int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len)
{ {
switch (cmd[0]) switch (cmd[0])
{ {
case 0xB7: case 0xB7:
{ {
u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
memset(TransferData, 0, TransferLen); memset(data, 0, len);
if (((addr + TransferLen - 1) >> 12) != (addr >> 12)) if (((addr + len - 1) >> 12) != (addr >> 12))
{ {
u32 len1 = 0x1000 - (addr & 0xFFF); u32 len1 = 0x1000 - (addr & 0xFFF);
ReadROM_B7(addr, len1, 0); ReadROM_B7(addr, len1, data, 0);
ReadROM_B7(addr+len1, TransferLen-len1, len1); ReadROM_B7(addr+len1, len-len1, data, len1);
} }
else else
ReadROM_B7(addr, TransferLen, 0); ReadROM_B7(addr, len, data, 0);
} }
break; return 0;
case 0xC0: // SD read case 0xC0: // SD read
{ {
@ -1362,23 +1364,20 @@ void CartHomebrew::ROMCommandStart(u8* cmd)
if (CartSD) if (CartSD)
{ {
fseek(CartSD, addr, SEEK_SET); fseek(CartSD, addr, SEEK_SET);
fread(TransferData, TransferLen, 1, CartSD); fread(data, len, 1, CartSD);
} }
} }
break; return 0;
case 0xC1: // SD write case 0xC1: // SD write
{ return 1;
TransferDir = 1;
}
break;
default: default:
return CartCommon::ROMCommandStart(cmd); return CartCommon::ROMCommandStart(cmd, data, len);
} }
} }
void CartHomebrew::ROMCommandFinish(u8* cmd) void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
{ {
switch (cmd[0]) switch (cmd[0])
{ {
@ -1390,13 +1389,13 @@ void CartHomebrew::ROMCommandFinish(u8* cmd)
if (CartSD) if (CartSD)
{ {
fseek(CartSD, addr, SEEK_SET); fseek(CartSD, addr, SEEK_SET);
fwrite(TransferData, TransferLen, 1, CartSD); fwrite(data, len, 1, CartSD);
} }
} }
break; break;
default: default:
return CartCommon::ROMCommandFinish(cmd); return CartCommon::ROMCommandFinish(cmd, data, len);
} }
} }
@ -1520,13 +1519,13 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 len)
printf("applied DLDI patch\n"); printf("applied DLDI patch\n");
} }
void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u32 offset) void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
{ {
// TODO: how strict should this be for homebrew? // TODO: how strict should this be for homebrew?
addr &= (ROMLength-1); addr &= (ROMLength-1);
memcpy(TransferData+offset, ROM+addr, len); memcpy(data+offset, ROM+addr, len);
} }
@ -1786,13 +1785,13 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct)
ROMCommandHandler = ROMCommand_Retail;*/ ROMCommandHandler = ROMCommand_Retail;*/
// TODO: add case for pokémon typing game // TODO: add case for pokémon typing game
if (CartIsHomebrew) if (CartIsHomebrew)
Cart = new CartHomebrew(CartROM, CartROMSize); Cart = new CartHomebrew(CartROM, CartROMSize, CartID);
else if (CartID & 0x08000000) else if (CartID & 0x08000000)
Cart = new CartRetailNAND(CartROM, CartROMSize); Cart = new CartRetailNAND(CartROM, CartROMSize, CartID);
//else if (CartID & 0x00010000) //else if (CartID & 0x00010000)
// Cart = new CartRetailIR(CartROM, CartROMSize); // Cart = new CartRetailIR(CartROM, CartROMSize, CartID);
else else
Cart = new CartRetail(CartROM, CartROMSize); Cart = new CartRetail(CartROM, CartROMSize, CartID);
if (Cart) Cart->Reset(); if (Cart) Cart->Reset();
@ -1971,7 +1970,7 @@ void ROMEndTransfer(u32 param)
break; break;
} }
}*/ }*/
if (Cart) Cart->ROMCommandFinish(TransferCmd); if (Cart) Cart->ROMCommandFinish(TransferCmd, TransferData, TransferLen);
} }
void ROMPrepareData(u32 param) void ROMPrepareData(u32 param)
@ -2265,7 +2264,10 @@ void WriteROMCnt(u32 val)
ROMCommandHandler(cmd); ROMCommandHandler(cmd);
break; break;
}*/ }*/
if (Cart) Cart->ROMCommandStart(TransferCmd); // 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); ROMCnt &= ~(1<<23);

View File

@ -69,7 +69,7 @@ namespace NDSCart
class CartCommon class CartCommon
{ {
public: public:
CartCommon(u8* rom, u32 len); CartCommon(u8* rom, u32 len, u32 chipid);
virtual ~CartCommon(); virtual ~CartCommon();
virtual void Reset(); virtual void Reset();
@ -80,23 +80,25 @@ public:
virtual void RelocateSave(const char* path, bool write); virtual void RelocateSave(const char* path, bool write);
virtual void FlushSRAMFile(); virtual void FlushSRAMFile();
virtual void ROMCommandStart(u8* cmd); virtual int ROMCommandStart(u8* cmd, u8* data, u32 len);
virtual void ROMCommandFinish(u8* cmd); virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len);
virtual u8 SPIWrite(u8 val, u32 pos, bool last); virtual u8 SPIWrite(u8 val, u32 pos, bool last);
protected: protected:
void ReadROM(u32 addr, u32 len, u32 offset); void ReadROM(u32 addr, u32 len, u8* data, u32 offset);
u8* ROM; u8* ROM;
u32 ROMLength; u32 ROMLength;
u32 ChipID;
bool IsDSi;
}; };
// CartRetail -- regular retail cart (ROM, SPI SRAM) // CartRetail -- regular retail cart (ROM, SPI SRAM)
class CartRetail : public CartCommon class CartRetail : public CartCommon
{ {
public: public:
CartRetail(u8* rom, u32 len); CartRetail(u8* rom, u32 len, u32 chipid);
virtual ~CartRetail(); virtual ~CartRetail();
virtual void Reset(); virtual void Reset();
@ -107,12 +109,12 @@ public:
virtual void RelocateSave(const char* path, bool write); virtual void RelocateSave(const char* path, bool write);
virtual void FlushSRAMFile(); virtual void FlushSRAMFile();
virtual void ROMCommandStart(u8* cmd); virtual int ROMCommandStart(u8* cmd, u8* data, u32 len);
virtual u8 SPIWrite(u8 val, u32 pos, bool last); virtual u8 SPIWrite(u8 val, u32 pos, bool last);
protected: protected:
void ReadROM_B7(u32 addr, u32 len, u32 offset); void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
u8 SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last); u8 SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last);
u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last); u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last);
@ -134,7 +136,7 @@ protected:
class CartRetailNAND : public CartRetail class CartRetailNAND : public CartRetail
{ {
public: public:
CartRetailNAND(u8* rom, u32 len); CartRetailNAND(u8* rom, u32 len, u32 chipid);
~CartRetailNAND(); ~CartRetailNAND();
void Reset(); void Reset();
@ -143,8 +145,8 @@ public:
void LoadSave(const char* path, u32 type); void LoadSave(const char* path, u32 type);
void ROMCommandStart(u8* cmd); int ROMCommandStart(u8* cmd, u8* data, u32 len);
void ROMCommandFinish(u8* cmd); void ROMCommandFinish(u8* cmd, u8* data, u32 len);
u8 SPIWrite(u8 val, u32 pos, bool last); u8 SPIWrite(u8 val, u32 pos, bool last);
@ -156,7 +158,7 @@ private:
class CartRetailIR : public CartRetail class CartRetailIR : public CartRetail
{ {
public: public:
CartRetailIR(u8* rom, u32 len); CartRetailIR(u8* rom, u32 len, u32 chipid);
~CartRetailIR(); ~CartRetailIR();
void Reset(); void Reset();
@ -173,7 +175,7 @@ private:
class CartRetailBT : public CartRetail class CartRetailBT : public CartRetail
{ {
public: public:
CartRetailBT(u8* rom, u32 len); CartRetailBT(u8* rom, u32 len, u32 chipid);
~CartRetailBT(); ~CartRetailBT();
void Reset(); void Reset();
@ -187,19 +189,19 @@ public:
class CartHomebrew : public CartCommon class CartHomebrew : public CartCommon
{ {
public: public:
CartHomebrew(u8* rom, u32 len); CartHomebrew(u8* rom, u32 len, u32 chipid);
~CartHomebrew(); ~CartHomebrew();
void Reset(); void Reset();
void DoSavestate(Savestate* file); void DoSavestate(Savestate* file);
void ROMCommandStart(u8* cmd); int ROMCommandStart(u8* cmd, u8* data, u32 len);
void ROMCommandFinish(u8* cmd); void ROMCommandFinish(u8* cmd, u8* data, u32 len);
private: private:
void ApplyDLDIPatch(const u8* patch, u32 len); void ApplyDLDIPatch(const u8* patch, u32 len);
void ReadROM_B7(u32 addr, u32 len, u32 offset); void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
}; };
extern u16 SPICnt; extern u16 SPICnt;