Merge pull request #638 from Arisotura/melonDSi

merge melonDSi
This commit is contained in:
Arisotura 2020-06-02 00:39:09 +02:00 committed by GitHub
commit d6332f96f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 7854 additions and 148 deletions

View File

@ -18,6 +18,7 @@
#include <stdio.h> #include <stdio.h>
#include "NDS.h" #include "NDS.h"
#include "DSi.h"
#include "ARM.h" #include "ARM.h"
#include "ARMInterpreter.h" #include "ARMInterpreter.h"
#include "AREngine.h" #include "AREngine.h"
@ -101,10 +102,55 @@ void ARM::Reset()
void ARMv5::Reset() void ARMv5::Reset()
{ {
if (NDS::ConsoleType == 1)
{
BusRead8 = DSi::ARM9Read8;
BusRead16 = DSi::ARM9Read16;
BusRead32 = DSi::ARM9Read32;
BusWrite8 = DSi::ARM9Write8;
BusWrite16 = DSi::ARM9Write16;
BusWrite32 = DSi::ARM9Write32;
GetMemRegion = DSi::ARM9GetMemRegion;
}
else
{
BusRead8 = NDS::ARM9Read8;
BusRead16 = NDS::ARM9Read16;
BusRead32 = NDS::ARM9Read32;
BusWrite8 = NDS::ARM9Write8;
BusWrite16 = NDS::ARM9Write16;
BusWrite32 = NDS::ARM9Write32;
GetMemRegion = NDS::ARM9GetMemRegion;
}
CP15Reset(); CP15Reset();
ARM::Reset(); ARM::Reset();
} }
void ARMv4::Reset()
{
if (NDS::ConsoleType)
{
BusRead8 = DSi::ARM7Read8;
BusRead16 = DSi::ARM7Read16;
BusRead32 = DSi::ARM7Read32;
BusWrite8 = DSi::ARM7Write8;
BusWrite16 = DSi::ARM7Write16;
BusWrite32 = DSi::ARM7Write32;
}
else
{
BusRead8 = NDS::ARM7Read8;
BusRead16 = NDS::ARM7Read16;
BusRead32 = NDS::ARM7Read32;
BusWrite8 = NDS::ARM7Write8;
BusWrite16 = NDS::ARM7Write16;
BusWrite32 = NDS::ARM7Write32;
}
ARM::Reset();
}
void ARM::DoSavestate(Savestate* file) void ARM::DoSavestate(Savestate* file)
{ {
@ -222,9 +268,6 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr)
CPSR &= ~0x20; CPSR &= ~0x20;
} }
// TODO: investigate this
// firmware jumps to region 01FFxxxx, but region 5 (01000000-02000000) is set to non-executable
// is melonDS fucked up somewhere, or is the DS PU just incomplete/crapoed?
/*if (!(PU_Map[addr>>12] & 0x04)) /*if (!(PU_Map[addr>>12] & 0x04))
{ {
printf("jumped to %08X. very bad\n", addr); printf("jumped to %08X. very bad\n", addr);

View File

@ -132,6 +132,14 @@ public:
NDS::MemRegion CodeMem; NDS::MemRegion CodeMem;
static u32 ConditionTable[16]; static u32 ConditionTable[16];
protected:
u8 (*BusRead8)(u32 addr);
u16 (*BusRead16)(u32 addr);
u32 (*BusRead32)(u32 addr);
void (*BusWrite8)(u32 addr, u8 val);
void (*BusWrite16)(u32 addr, u16 val);
void (*BusWrite32)(u32 addr, u32 val);
}; };
class ARMv5 : public ARM class ARMv5 : public ARM
@ -259,6 +267,8 @@ public:
s32 RegionCodeCycles; s32 RegionCodeCycles;
u8* CurICacheLine; u8* CurICacheLine;
bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region);
}; };
class ARMv4 : public ARM class ARMv4 : public ARM
@ -266,23 +276,25 @@ class ARMv4 : public ARM
public: public:
ARMv4(); ARMv4();
void Reset();
void JumpTo(u32 addr, bool restorecpsr = false); void JumpTo(u32 addr, bool restorecpsr = false);
void Execute(); void Execute();
u16 CodeRead16(u32 addr) u16 CodeRead16(u32 addr)
{ {
return NDS::ARM7Read16(addr); return BusRead16(addr);
} }
u32 CodeRead32(u32 addr) u32 CodeRead32(u32 addr)
{ {
return NDS::ARM7Read32(addr); return BusRead32(addr);
} }
void DataRead8(u32 addr, u32* val) void DataRead8(u32 addr, u32* val)
{ {
*val = NDS::ARM7Read8(addr); *val = BusRead8(addr);
DataRegion = addr >> 24; DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][0]; DataCycles = NDS::ARM7MemTimings[DataRegion][0];
} }
@ -291,7 +303,7 @@ public:
{ {
addr &= ~1; addr &= ~1;
*val = NDS::ARM7Read16(addr); *val = BusRead16(addr);
DataRegion = addr >> 24; DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][0]; DataCycles = NDS::ARM7MemTimings[DataRegion][0];
} }
@ -300,7 +312,7 @@ public:
{ {
addr &= ~3; addr &= ~3;
*val = NDS::ARM7Read32(addr); *val = BusRead32(addr);
DataRegion = addr >> 24; DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][2]; DataCycles = NDS::ARM7MemTimings[DataRegion][2];
} }
@ -309,13 +321,13 @@ public:
{ {
addr &= ~3; addr &= ~3;
*val = NDS::ARM7Read32(addr); *val = BusRead32(addr);
DataCycles += NDS::ARM7MemTimings[DataRegion][3]; DataCycles += NDS::ARM7MemTimings[DataRegion][3];
} }
void DataWrite8(u32 addr, u8 val) void DataWrite8(u32 addr, u8 val)
{ {
NDS::ARM7Write8(addr, val); BusWrite8(addr, val);
DataRegion = addr >> 24; DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][0]; DataCycles = NDS::ARM7MemTimings[DataRegion][0];
} }
@ -324,7 +336,7 @@ public:
{ {
addr &= ~1; addr &= ~1;
NDS::ARM7Write16(addr, val); BusWrite16(addr, val);
DataRegion = addr >> 24; DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][0]; DataCycles = NDS::ARM7MemTimings[DataRegion][0];
} }
@ -333,7 +345,7 @@ public:
{ {
addr &= ~3; addr &= ~3;
NDS::ARM7Write32(addr, val); BusWrite32(addr, val);
DataRegion = addr >> 24; DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][2]; DataCycles = NDS::ARM7MemTimings[DataRegion][2];
} }
@ -342,7 +354,7 @@ public:
{ {
addr &= ~3; addr &= ~3;
NDS::ARM7Write32(addr, val); BusWrite32(addr, val);
DataCycles += NDS::ARM7MemTimings[DataRegion][3]; DataCycles += NDS::ARM7MemTimings[DataRegion][3];
} }

View File

@ -13,6 +13,14 @@ add_library(core STATIC
CP15.cpp CP15.cpp
CRC32.cpp CRC32.cpp
DMA.cpp DMA.cpp
DSi.cpp
DSi_AES.cpp
DSi_Camera.cpp
DSi_I2C.cpp
DSi_NDMA.cpp
DSi_NWifi.cpp
DSi_SD.cpp
DSi_SPI_TSC.cpp
FIFO.h FIFO.h
GBACart.cpp GBACart.cpp
GPU.cpp GPU.cpp
@ -36,6 +44,8 @@ add_library(core STATIC
version.h version.h
Wifi.cpp Wifi.cpp
WifiAP.cpp WifiAP.cpp
tiny-AES-c/aes.c
) )
if (WIN32) if (WIN32)

View File

@ -19,6 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "NDS.h" #include "NDS.h"
#include "DSi.h"
#include "ARM.h" #include "ARM.h"
@ -719,7 +720,7 @@ u32 ARMv5::CodeRead32(u32 addr, bool branch)
if (CodeMem.Mem) return *(u32*)&CodeMem.Mem[addr & CodeMem.Mask]; if (CodeMem.Mem) return *(u32*)&CodeMem.Mem[addr & CodeMem.Mask];
return NDS::ARM9Read32(addr); return BusRead32(addr);
} }
@ -738,7 +739,7 @@ void ARMv5::DataRead8(u32 addr, u32* val)
return; return;
} }
*val = NDS::ARM9Read8(addr); *val = BusRead8(addr);
DataCycles = MemTimings[addr >> 12][1]; DataCycles = MemTimings[addr >> 12][1];
} }
@ -759,7 +760,7 @@ void ARMv5::DataRead16(u32 addr, u32* val)
return; return;
} }
*val = NDS::ARM9Read16(addr); *val = BusRead16(addr);
DataCycles = MemTimings[addr >> 12][1]; DataCycles = MemTimings[addr >> 12][1];
} }
@ -780,7 +781,7 @@ void ARMv5::DataRead32(u32 addr, u32* val)
return; return;
} }
*val = NDS::ARM9Read32(addr); *val = BusRead32(addr);
DataCycles = MemTimings[addr >> 12][2]; DataCycles = MemTimings[addr >> 12][2];
} }
@ -801,7 +802,7 @@ void ARMv5::DataRead32S(u32 addr, u32* val)
return; return;
} }
*val = NDS::ARM9Read32(addr); *val = BusRead32(addr);
DataCycles += MemTimings[addr >> 12][3]; DataCycles += MemTimings[addr >> 12][3];
} }
@ -820,7 +821,7 @@ void ARMv5::DataWrite8(u32 addr, u8 val)
return; return;
} }
NDS::ARM9Write8(addr, val); BusWrite8(addr, val);
DataCycles = MemTimings[addr >> 12][1]; DataCycles = MemTimings[addr >> 12][1];
} }
@ -841,7 +842,7 @@ void ARMv5::DataWrite16(u32 addr, u16 val)
return; return;
} }
NDS::ARM9Write16(addr, val); BusWrite16(addr, val);
DataCycles = MemTimings[addr >> 12][1]; DataCycles = MemTimings[addr >> 12][1];
} }
@ -862,7 +863,7 @@ void ARMv5::DataWrite32(u32 addr, u32 val)
return; return;
} }
NDS::ARM9Write32(addr, val); BusWrite32(addr, val);
DataCycles = MemTimings[addr >> 12][2]; DataCycles = MemTimings[addr >> 12][2];
} }
@ -883,7 +884,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val)
return; return;
} }
NDS::ARM9Write32(addr, val); BusWrite32(addr, val);
DataCycles += MemTimings[addr >> 12][3]; DataCycles += MemTimings[addr >> 12][3];
} }
@ -896,6 +897,6 @@ void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region)
return; return;
}*/ }*/
NDS::ARM9GetMemRegion(addr, false, &CodeMem); GetMemRegion(addr, false, &CodeMem);
} }

View File

@ -32,11 +32,10 @@ char BIOS9Path[1024];
char BIOS7Path[1024]; char BIOS7Path[1024];
char FirmwarePath[1024]; char FirmwarePath[1024];
int _3DRenderer; char DSiBIOS9Path[1024];
int Threaded3D; char DSiBIOS7Path[1024];
char DSiFirmwarePath[1024];
int GL_ScaleFactor; char DSiNANDPath[1024];
int GL_Antialias;
ConfigEntry ConfigFile[] = ConfigEntry ConfigFile[] =
{ {
@ -44,11 +43,10 @@ ConfigEntry ConfigFile[] =
{"BIOS7Path", 1, BIOS7Path, 0, "", 1023}, {"BIOS7Path", 1, BIOS7Path, 0, "", 1023},
{"FirmwarePath", 1, FirmwarePath, 0, "", 1023}, {"FirmwarePath", 1, FirmwarePath, 0, "", 1023},
{"3DRenderer", 0, &_3DRenderer, 1, NULL, 0}, {"DSiBIOS9Path", 1, DSiBIOS9Path, 0, "", 1023},
{"Threaded3D", 0, &Threaded3D, 1, NULL, 0}, {"DSiBIOS7Path", 1, DSiBIOS7Path, 0, "", 1023},
{"DSiFirmwarePath", 1, DSiFirmwarePath, 0, "", 1023},
{"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, NULL, 0}, {"DSiNANDPath", 1, DSiNANDPath, 0, "", 1023},
{"GL_Antialias", 0, &GL_Antialias, 0, NULL, 0},
{"", -1, NULL, 0, NULL, 0} {"", -1, NULL, 0, NULL, 0}
}; };

View File

@ -46,11 +46,10 @@ extern char BIOS9Path[1024];
extern char BIOS7Path[1024]; extern char BIOS7Path[1024];
extern char FirmwarePath[1024]; extern char FirmwarePath[1024];
extern int _3DRenderer; extern char DSiBIOS9Path[1024];
extern int Threaded3D; extern char DSiBIOS7Path[1024];
extern char DSiFirmwarePath[1024];
extern int GL_ScaleFactor; extern char DSiNANDPath[1024];
extern int GL_Antialias;
} }

View File

@ -18,16 +18,11 @@
#include <stdio.h> #include <stdio.h>
#include "NDS.h" #include "NDS.h"
#include "DSi.h"
#include "DMA.h" #include "DMA.h"
#include "NDSCart.h"
#include "GPU.h" #include "GPU.h"
// NOTES ON DMA SHIT
//
// * could use optimized code paths for common types of DMA transfers. for example, VRAM
// have to profile it to see if it's actually worth doing
// DMA TIMINGS // DMA TIMINGS
// //
@ -58,8 +53,6 @@ DMA::DMA(u32 cpu, u32 num)
CountMask = 0x001FFFFF; CountMask = 0x001FFFFF;
else else
CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF); CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF);
Reset();
} }
DMA::~DMA() DMA::~DMA()
@ -82,6 +75,21 @@ void DMA::Reset()
Running = false; Running = false;
InProgress = false; InProgress = false;
if (NDS::ConsoleType == 1)
{
BusRead16 = (CPU==0) ? DSi::ARM9Read16 : DSi::ARM7Read16;
BusRead32 = (CPU==0) ? DSi::ARM9Read32 : DSi::ARM7Read32;
BusWrite16 = (CPU==0) ? DSi::ARM9Write16 : DSi::ARM7Write16;
BusWrite32 = (CPU==0) ? DSi::ARM9Write32 : DSi::ARM7Write32;
}
else
{
BusRead16 = (CPU==0) ? NDS::ARM9Read16 : NDS::ARM7Read16;
BusRead32 = (CPU==0) ? NDS::ARM9Read32 : NDS::ARM7Read32;
BusWrite16 = (CPU==0) ? NDS::ARM9Write16 : NDS::ARM7Write16;
BusWrite32 = (CPU==0) ? NDS::ARM9Write32 : NDS::ARM7Write32;
}
} }
void DMA::DoSavestate(Savestate* file) void DMA::DoSavestate(Savestate* file)
@ -232,7 +240,7 @@ void DMA::Run9()
{ {
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift); NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
NDS::ARM9Write16(CurDstAddr, NDS::ARM9Read16(CurSrcAddr)); BusWrite16(CurDstAddr, BusRead16(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<1; CurSrcAddr += SrcAddrInc<<1;
CurDstAddr += DstAddrInc<<1; CurDstAddr += DstAddrInc<<1;
@ -268,7 +276,7 @@ void DMA::Run9()
{ {
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift); NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
NDS::ARM9Write32(CurDstAddr, NDS::ARM9Read32(CurSrcAddr)); BusWrite32(CurDstAddr, BusRead32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2; CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2; CurDstAddr += DstAddrInc<<2;
@ -344,7 +352,7 @@ void DMA::Run7()
{ {
NDS::ARM7Timestamp += unitcycles; NDS::ARM7Timestamp += unitcycles;
NDS::ARM7Write16(CurDstAddr, NDS::ARM7Read16(CurSrcAddr)); BusWrite16(CurDstAddr, BusRead16(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<1; CurSrcAddr += SrcAddrInc<<1;
CurDstAddr += DstAddrInc<<1; CurDstAddr += DstAddrInc<<1;
@ -380,7 +388,7 @@ void DMA::Run7()
{ {
NDS::ARM7Timestamp += unitcycles; NDS::ARM7Timestamp += unitcycles;
NDS::ARM7Write32(CurDstAddr, NDS::ARM7Read32(CurSrcAddr)); BusWrite32(CurDstAddr, BusRead32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2; CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2; CurDstAddr += DstAddrInc<<2;

View File

@ -86,6 +86,11 @@ private:
bool Stall; bool Stall;
bool IsGXFIFODMA; bool IsGXFIFODMA;
u16 (*BusRead16)(u32 addr);
u32 (*BusRead32)(u32 addr);
void (*BusWrite16)(u32 addr, u16 val);
void (*BusWrite32)(u32 addr, u32 val);
}; };
#endif #endif

1806
src/DSi.cpp Normal file

File diff suppressed because it is too large Load Diff

93
src/DSi.h Normal file
View File

@ -0,0 +1,93 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_H
#define DSI_H
#include "NDS.h"
#include "DSi_SD.h"
namespace DSi
{
extern u8 ARM9iBIOS[0x10000];
extern u8 ARM7iBIOS[0x10000];
extern u8 eMMC_CID[16];
extern u64 ConsoleID;
extern DSi_SDHost* SDMMC;
extern DSi_SDHost* SDIO;
bool Init();
void DeInit();
void Reset();
void SoftReset();
bool LoadBIOS();
bool LoadNAND();
void RunNDMAs(u32 cpu);
void StallNDMAs();
bool NDMAsInMode(u32 cpu, u32 mode);
bool NDMAsRunning(u32 cpu);
void CheckNDMAs(u32 cpu, u32 mode);
void StopNDMAs(u32 cpu, u32 mode);
void MapNWRAM_A(u32 num, u8 val);
void MapNWRAM_B(u32 num, u8 val);
void MapNWRAM_C(u32 num, u8 val);
void MapNWRAMRange(u32 cpu, u32 num, u32 val);
u8 ARM9Read8(u32 addr);
u16 ARM9Read16(u32 addr);
u32 ARM9Read32(u32 addr);
void ARM9Write8(u32 addr, u8 val);
void ARM9Write16(u32 addr, u16 val);
void ARM9Write32(u32 addr, u32 val);
bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region);
u8 ARM7Read8(u32 addr);
u16 ARM7Read16(u32 addr);
u32 ARM7Read32(u32 addr);
void ARM7Write8(u32 addr, u8 val);
void ARM7Write16(u32 addr, u16 val);
void ARM7Write32(u32 addr, u32 val);
bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region);
u8 ARM9IORead8(u32 addr);
u16 ARM9IORead16(u32 addr);
u32 ARM9IORead32(u32 addr);
void ARM9IOWrite8(u32 addr, u8 val);
void ARM9IOWrite16(u32 addr, u16 val);
void ARM9IOWrite32(u32 addr, u32 val);
u8 ARM7IORead8(u32 addr);
u16 ARM7IORead16(u32 addr);
u32 ARM7IORead32(u32 addr);
void ARM7IOWrite8(u32 addr, u8 val);
void ARM7IOWrite16(u32 addr, u16 val);
void ARM7IOWrite32(u32 addr, u32 val);
}
#endif // DSI_H

17
src/DSiCrypto.cpp Normal file
View File

@ -0,0 +1,17 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/

24
src/DSiCrypto.h Normal file
View File

@ -0,0 +1,24 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSICRYPTO_H
#define DSICRYPTO_H
//
#endif // DSICRYPTO_H

555
src/DSi_AES.cpp Normal file
View File

@ -0,0 +1,555 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_AES.h"
#include "FIFO.h"
#include "tiny-AES-c/aes.hpp"
#include "Platform.h"
namespace DSi_AES
{
u32 Cnt;
u32 BlkCnt;
u32 RemBlocks;
bool OutputFlush;
u32 InputDMASize, OutputDMASize;
u32 AESMode;
FIFO<u32>* InputFIFO;
FIFO<u32>* OutputFIFO;
u8 IV[16];
u8 MAC[16];
u8 KeyNormal[4][16];
u8 KeyX[4][16];
u8 KeyY[4][16];
u8 CurKey[16];
u8 CurMAC[16];
AES_ctx Ctx;
void Swap16(u8* dst, u8* src)
{
for (int i = 0; i < 16; i++)
dst[i] = src[15-i];
}
void ROL16(u8* val, u32 n)
{
u32 n_coarse = n >> 3;
u32 n_fine = n & 7;
u8 tmp[16];
for (u32 i = 0; i < 16; i++)
{
tmp[i] = val[(i - n_coarse) & 0xF];
}
for (u32 i = 0; i < 16; i++)
{
val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine));
}
}
#define _printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); }
#define _printhex2(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); }
#define _printhexR(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); printf("\n"); }
#define _printhex2R(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); }
bool Init()
{
InputFIFO = new FIFO<u32>(16);
OutputFIFO = new FIFO<u32>(16);
const u8 zero[16] = {0};
AES_init_ctx_iv(&Ctx, zero, zero);
return true;
}
void DeInit()
{
delete InputFIFO;
delete OutputFIFO;
}
void Reset()
{
Cnt = 0;
BlkCnt = 0;
RemBlocks = 0;
OutputFlush = false;
InputDMASize = 0;
OutputDMASize = 0;
AESMode = 0;
InputFIFO->Clear();
OutputFIFO->Clear();
memset(IV, 0, sizeof(IV));
memset(MAC, 0, sizeof(MAC));
memset(KeyNormal, 0, sizeof(KeyNormal));
memset(KeyX, 0, sizeof(KeyX));
memset(KeyY, 0, sizeof(KeyY));
memset(CurKey, 0, sizeof(CurKey));
memset(CurMAC, 0, sizeof(CurMAC));
// initialize keys
// slot 0: modcrypt
*(u32*)&KeyX[0][0] = 0x746E694E;
*(u32*)&KeyX[0][4] = 0x6F646E65;
// slot 1: 'Tad'/dev.kp
*(u32*)&KeyX[1][0] = 0x4E00004A;
*(u32*)&KeyX[1][4] = 0x4A00004E;
*(u32*)&KeyX[1][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xC80C4B72;
*(u32*)&KeyX[1][12] = (u32)DSi::ConsoleID;
// slot 3: console-unique eMMC crypto
*(u32*)&KeyX[3][0] = (u32)DSi::ConsoleID;
*(u32*)&KeyX[3][4] = (u32)DSi::ConsoleID ^ 0x24EE6906;
*(u32*)&KeyX[3][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xE65B601D;
*(u32*)&KeyX[3][12] = (u32)(DSi::ConsoleID >> 32);
*(u32*)&KeyY[3][0] = 0x0AB9DC76;
*(u32*)&KeyY[3][4] = 0xBD4DC4D3;
*(u32*)&KeyY[3][8] = 0x202DDD1D;
}
void ProcessBlock_CCM_Decrypt()
{
u8 data[16];
u8 data_rev[16];
*(u32*)&data[0] = InputFIFO->Read();
*(u32*)&data[4] = InputFIFO->Read();
*(u32*)&data[8] = InputFIFO->Read();
*(u32*)&data[12] = InputFIFO->Read();
//printf("AES-CCM: "); _printhex2(data, 16);
Swap16(data_rev, data);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_ECB_encrypt(&Ctx, CurMAC);
Swap16(data, data_rev);
//printf(" -> "); _printhex2(data, 16);
OutputFIFO->Write(*(u32*)&data[0]);
OutputFIFO->Write(*(u32*)&data[4]);
OutputFIFO->Write(*(u32*)&data[8]);
OutputFIFO->Write(*(u32*)&data[12]);
}
void ProcessBlock_CTR()
{
u8 data[16];
u8 data_rev[16];
*(u32*)&data[0] = InputFIFO->Read();
*(u32*)&data[4] = InputFIFO->Read();
*(u32*)&data[8] = InputFIFO->Read();
*(u32*)&data[12] = InputFIFO->Read();
//printf("AES-CTR: "); _printhex2(data, 16);
Swap16(data_rev, data);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
Swap16(data, data_rev);
//printf(" -> "); _printhex(data, 16);
OutputFIFO->Write(*(u32*)&data[0]);
OutputFIFO->Write(*(u32*)&data[4]);
OutputFIFO->Write(*(u32*)&data[8]);
OutputFIFO->Write(*(u32*)&data[12]);
}
u32 ReadCnt()
{
u32 ret = Cnt;
ret |= InputFIFO->Level();
ret |= (OutputFIFO->Level() << 5);
return ret;
}
void WriteCnt(u32 val)
{
u32 oldcnt = Cnt;
Cnt = val & 0xFC1FF000;
/*if (val & (3<<10))
{
if (val & (1<<11)) OutputFlush = true;
Update();
}*/
u32 dmasize_in[4] = {0, 4, 8, 12};
u32 dmasize_out[4] = {4, 8, 12, 16};
InputDMASize = dmasize_in[(val >> 12) & 0x3];
OutputDMASize = dmasize_out[(val >> 14) & 0x3];
AESMode = (val >> 28) & 0x3;
if (AESMode == 1) printf("AES-CCM TODO\n");
if (val & (1<<24))
{
u32 slot = (val >> 26) & 0x3;
memcpy(CurKey, KeyNormal[slot], 16);
}
if (!(oldcnt & (1<<31)) && (val & (1<<31)))
{
// transfer start (checkme)
RemBlocks = BlkCnt >> 16;
if (RemBlocks > 0)
{
u8 key[16];
u8 iv[16];
Swap16(key, CurKey);
Swap16(iv, IV);
if (AESMode < 2)
{
if (BlkCnt & 0xFFFF) printf("AES: CCM EXTRA LEN TODO\n");
u32 maclen = (val >> 16) & 0x7;
if (maclen < 1) maclen = 1;
iv[0] = 0x02;
for (int i = 0; i < 12; i++) iv[1+i] = iv[4+i];
iv[13] = 0x00;
iv[14] = 0x00;
iv[15] = 0x01;
AES_init_ctx_iv(&Ctx, key, iv);
iv[0] |= (maclen << 3) | ((BlkCnt & 0xFFFF) ? (1<<6) : 0);
iv[13] = RemBlocks >> 12;
iv[14] = RemBlocks >> 4;
iv[15] = RemBlocks << 4;
memcpy(CurMAC, iv, 16);
AES_ECB_encrypt(&Ctx, CurMAC);
}
else
{
AES_init_ctx_iv(&Ctx, key, iv);
}
DSi::CheckNDMAs(1, 0x2A);
}
else
{
// no blocks to process? oh well. mark it finished
// CHECKME: does this trigger any IRQ or shit?
Cnt &= ~(1<<31);
}
}
//printf("AES CNT: %08X / mode=%d key=%d inDMA=%d outDMA=%d blocks=%d\n",
// val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks);
}
void WriteBlkCnt(u32 val)
{
BlkCnt = val;
}
u32 ReadOutputFIFO()
{
if (OutputFIFO->IsEmpty()) printf("!!! AES OUTPUT FIFO EMPTY\n");
u32 ret = OutputFIFO->Read();
if (Cnt & (1<<31))
{
CheckInputDMA();
CheckOutputDMA();
}
else
{
if (OutputFIFO->Level() > 0)
DSi::CheckNDMAs(1, 0x2B);
else
DSi::StopNDMAs(1, 0x2B);
}
return ret;
}
void WriteInputFIFO(u32 val)
{
// TODO: add some delay to processing
if (InputFIFO->IsFull()) printf("!!! AES INPUT FIFO FULL\n");
InputFIFO->Write(val);
if (!(Cnt & (1<<31))) return;
Update();
}
void CheckInputDMA()
{
if (RemBlocks == 0) return;
if (InputFIFO->Level() <= InputDMASize)
{
// trigger input DMA
DSi::CheckNDMAs(1, 0x2A);
}
Update();
}
void CheckOutputDMA()
{
if (OutputFIFO->Level() >= OutputDMASize)
{
// trigger output DMA
DSi::CheckNDMAs(1, 0x2B);
}
}
void Update()
{
while (InputFIFO->Level() >= 4 && OutputFIFO->Level() <= 12 && RemBlocks > 0)
{
switch (AESMode)
{
case 0: ProcessBlock_CCM_Decrypt(); break;
case 2:
case 3: ProcessBlock_CTR(); break;
default:
// dorp
OutputFIFO->Write(InputFIFO->Read());
OutputFIFO->Write(InputFIFO->Read());
OutputFIFO->Write(InputFIFO->Read());
OutputFIFO->Write(InputFIFO->Read());
}
RemBlocks--;
}
CheckOutputDMA();
if (RemBlocks == 0)
{
if (AESMode == 0)
{
Ctx.Iv[13] = 0x00;
Ctx.Iv[14] = 0x00;
Ctx.Iv[15] = 0x00;
AES_CTR_xcrypt_buffer(&Ctx, CurMAC, 16);
//printf("FINAL MAC: "); _printhexR(CurMAC, 16);
//printf("INPUT MAC: "); _printhex(MAC, 16);
Cnt |= (1<<21);
for (int i = 0; i < 16; i++)
{
if (CurMAC[15-i] != MAC[i]) Cnt &= ~(1<<21);
}
}
else
{
// CHECKME
Cnt &= ~(1<<21);
}
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ2(NDS::IRQ2_DSi_AES);
DSi::StopNDMAs(1, 0x2A);
if (OutputFIFO->Level() > 0)
DSi::CheckNDMAs(1, 0x2B);
else
DSi::StopNDMAs(1, 0x2B);
OutputFlush = false;
}
}
void WriteIV(u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&IV[offset];
*(u32*)&IV[offset] = (old & ~mask) | (val & mask);
//printf("AES: IV: "); _printhex(IV, 16);
}
void WriteMAC(u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&MAC[offset];
*(u32*)&MAC[offset] = (old & ~mask) | (val & mask);
//printf("AES: MAC: "); _printhex(MAC, 16);
}
void DeriveNormalKey(u32 slot)
{
const u8 key_const[16] = {0xFF, 0xFE, 0xFB, 0x4E, 0x29, 0x59, 0x02, 0x58, 0x2A, 0x68, 0x0F, 0x5F, 0x1A, 0x4F, 0x3E, 0x79};
u8 tmp[16];
//printf("slot%d keyX: ", slot); _printhex(KeyX[slot], 16);
//printf("slot%d keyY: ", slot); _printhex(KeyY[slot], 16);
for (int i = 0; i < 16; i++)
tmp[i] = KeyX[slot][i] ^ KeyY[slot][i];
u32 carry = 0;
for (int i = 0; i < 16; i++)
{
u32 res = tmp[i] + key_const[15-i] + carry;
tmp[i] = res & 0xFF;
carry = res >> 8;
}
ROL16(tmp, 42);
//printf("derive normalkey %d\n", slot); _printhex(tmp, 16);
memcpy(KeyNormal[slot], tmp, 16);
}
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyNormal[slot][offset];
*(u32*)&KeyNormal[slot][offset] = (old & ~mask) | (val & mask);
//printf("KeyNormal(%d): ", slot); _printhex(KeyNormal[slot], 16);
}
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyX[slot][offset];
*(u32*)&KeyX[slot][offset] = (old & ~mask) | (val & mask);
//printf("KeyX(%d): ", slot); _printhex(KeyX[slot], 16);
}
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyY[slot][offset];
*(u32*)&KeyY[slot][offset] = (old & ~mask) | (val & mask);
//printf("[%08X] KeyY(%d): ", NDS::GetPC(1), slot); _printhex(KeyY[slot], 16);
if (offset >= 0xC)
{
DeriveNormalKey(slot);
}
}
// utility
void GetModcryptKey(u8* romheader, u8* key)
{
if ((romheader[0x01C] & 0x04) || (romheader[0x1BF] & 0x80))
{
// dev key
memcpy(key, &romheader[0x000], 16);
return;
}
u8 oldkeys[16*3];
memcpy(&oldkeys[16*0], KeyX[0], 16);
memcpy(&oldkeys[16*1], KeyY[0], 16);
memcpy(&oldkeys[16*2], KeyNormal[0], 16);
KeyX[0][8] = romheader[0x00C];
KeyX[0][9] = romheader[0x00D];
KeyX[0][10] = romheader[0x00E];
KeyX[0][11] = romheader[0x00F];
KeyX[0][12] = romheader[0x00F];
KeyX[0][13] = romheader[0x00E];
KeyX[0][14] = romheader[0x00D];
KeyX[0][15] = romheader[0x00C];
memcpy(KeyY[0], &romheader[0x350], 16);
DeriveNormalKey(0);
memcpy(key, KeyNormal[0], 16);
memcpy(KeyX[0], &oldkeys[16*0], 16);
memcpy(KeyY[0], &oldkeys[16*1], 16);
memcpy(KeyNormal[0], &oldkeys[16*2], 16);
}
void ApplyModcrypt(u8* data, u32 len, u8* key, u8* iv)
{
u8 key_rev[16], iv_rev[16];
u8 data_rev[16];
u8 oldkeys[16*2];
memcpy(&oldkeys[16*0], Ctx.RoundKey, 16);
memcpy(&oldkeys[16*1], Ctx.Iv, 16);
Swap16(key_rev, key);
Swap16(iv_rev, iv);
AES_init_ctx_iv(&Ctx, key_rev, iv_rev);
for (u32 i = 0; i < len; i += 16)
{
Swap16(data_rev, &data[i]);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
Swap16(&data[i], data_rev);
}
memcpy(Ctx.RoundKey, &oldkeys[16*0], 16);
memcpy(Ctx.Iv, &oldkeys[16*1], 16);
}
}

54
src/DSi_AES.h Normal file
View File

@ -0,0 +1,54 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_AES_H
#define DSI_AES_H
#include "types.h"
namespace DSi_AES
{
extern u32 Cnt;
bool Init();
void DeInit();
void Reset();
u32 ReadCnt();
void WriteCnt(u32 val);
void WriteBlkCnt(u32 val);
u32 ReadOutputFIFO();
void WriteInputFIFO(u32 val);
void CheckInputDMA();
void CheckOutputDMA();
void Update();
void WriteIV(u32 offset, u32 val, u32 mask);
void WriteMAC(u32 offset, u32 val, u32 mask);
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask);
void GetModcryptKey(u8* romheader, u8* key);
void ApplyModcrypt(u8* data, u32 len, u8* key, u8* iv);
}
#endif // DSI_AES_H

166
src/DSi_Camera.cpp Normal file
View File

@ -0,0 +1,166 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi_Camera.h"
DSi_Camera* DSi_Camera0; // 78 / facing outside
DSi_Camera* DSi_Camera1; // 7A / selfie cam
bool DSi_Camera::Init()
{
DSi_Camera0 = new DSi_Camera(0);
DSi_Camera1 = new DSi_Camera(1);
return true;
}
void DSi_Camera::DeInit()
{
delete DSi_Camera0;
delete DSi_Camera1;
}
void DSi_Camera::Reset()
{
DSi_Camera0->ResetCam();
DSi_Camera1->ResetCam();
}
DSi_Camera::DSi_Camera(u32 num)
{
Num = num;
}
DSi_Camera::~DSi_Camera()
{
//
}
void DSi_Camera::ResetCam()
{
DataPos = 0;
RegAddr = 0;
RegData = 0;
PLLCnt = 0;
StandbyCnt = 0x4029; // checkme
}
void DSi_Camera::Start()
{
}
u8 DSi_Camera::Read(bool last)
{
u8 ret;
if (DataPos < 2)
{
printf("DSi_Camera: WHAT??\n");
ret = 0;
}
else
{
if (DataPos & 0x1)
{
ret = RegData & 0xFF;
RegAddr += 2; // checkme
}
else
{
RegData = ReadReg(RegAddr);
ret = RegData >> 8;
}
}
if (last) DataPos = 0;
else DataPos++;
return ret;
}
void DSi_Camera::Write(u8 val, bool last)
{
if (DataPos < 2)
{
if (DataPos == 0)
RegAddr = val << 8;
else
RegAddr |= val;
if (RegAddr & 0x1) printf("DSi_Camera: !! UNALIGNED REG ADDRESS %04X\n", RegAddr);
}
else
{
if (DataPos & 0x1)
{
RegData |= val;
WriteReg(RegAddr, RegData);
RegAddr += 2; // checkme
}
else
{
RegData = val << 8;
}
}
if (last) DataPos = 0;
else DataPos++;
}
u16 DSi_Camera::ReadReg(u16 addr)
{
switch (addr)
{
case 0x0000: return 0x2280; // chip ID
case 0x0014: return PLLCnt;
case 0x0018: return StandbyCnt;
case 0x301A: return ((~StandbyCnt) & 0x4000) >> 12;
}
printf("DSi_Camera%d: unknown read %04X\n", Num, addr);
return 0;
}
void DSi_Camera::WriteReg(u16 addr, u16 val)
{
switch (addr)
{
case 0x0014:
// shouldn't be instant either?
val &= 0x7FFF;
val |= ((val & 0x0002) << 14);
PLLCnt = val;
return;
case 0x0018:
// TODO: this shouldn't be instant, but uh
val &= 0x003F;
val |= ((val & 0x0001) << 14);
StandbyCnt = val;
return;
}
printf("DSi_Camera%d: unknown write %04X %04X\n", Num, addr, val);
}

58
src/DSi_Camera.h Normal file
View File

@ -0,0 +1,58 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_CAMERA_H
#define DSI_CAMERA_H
#include "types.h"
class DSi_Camera
{
public:
static bool Init();
static void DeInit();
static void Reset();
DSi_Camera(u32 num);
~DSi_Camera();
void ResetCam();
void Start();
u8 Read(bool last);
void Write(u8 val, bool last);
private:
u32 Num;
u32 DataPos;
u32 RegAddr;
u16 RegData;
u16 ReadReg(u16 addr);
void WriteReg(u16 addr, u16 val);
u16 PLLCnt;
u16 StandbyCnt;
};
extern DSi_Camera* DSi_Camera0;
extern DSi_Camera* DSi_Camera1;
#endif // DSI_CAMERA_H

255
src/DSi_I2C.cpp Normal file
View File

@ -0,0 +1,255 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_I2C.h"
#include "DSi_Camera.h"
namespace DSi_BPTWL
{
u8 Registers[0x100];
u32 CurPos;
bool Init()
{
return true;
}
void DeInit()
{
}
void Reset()
{
CurPos = -1;
memset(Registers, 0x5A, 0x100);
Registers[0x00] = 0x33; // TODO: support others??
Registers[0x01] = 0x00;
Registers[0x02] = 0x50;
Registers[0x10] = 0x00; // power btn
Registers[0x11] = 0x00; // reset
Registers[0x12] = 0x00; // power btn tap
Registers[0x20] = 0x83; // battery
Registers[0x21] = 0x07;
Registers[0x30] = 0x13;
Registers[0x31] = 0x00; // camera power
Registers[0x40] = 0x1F; // volume
Registers[0x41] = 0x04; // backlight
Registers[0x60] = 0x00;
Registers[0x61] = 0x01;
Registers[0x62] = 0x50;
Registers[0x63] = 0x00;
Registers[0x70] = 0x00; // boot flag
Registers[0x71] = 0x00;
Registers[0x72] = 0x00;
Registers[0x73] = 0x00;
Registers[0x74] = 0x00;
Registers[0x75] = 0x00;
Registers[0x76] = 0x00;
Registers[0x77] = 0x00;
Registers[0x80] = 0x10;
Registers[0x81] = 0x64;
}
void Start()
{
//printf("BPTWL: start\n");
}
u8 Read(bool last)
{
if (last)
{
CurPos = -1;
return 0;
}
//printf("BPTWL: read %02X -> %02X\n", CurPos, Registers[CurPos]);
return Registers[CurPos++];
}
void Write(u8 val, bool last)
{
if (last)
{
CurPos = -1;
return;
}
if (CurPos == -1)
{
CurPos = val;
//printf("BPTWL: reg=%02X\n", val);
return;
}
if (CurPos == 0x11 && val == 0x01)
{
printf("BPTWL: soft-reset\n");
val = 0; // checkme
// TODO: soft-reset might need to be scheduled later!
DSi::SoftReset();
CurPos = -1;
return;
}
if (CurPos == 0x11 || CurPos == 0x12 ||
CurPos == 0x21 ||
CurPos == 0x30 || CurPos == 0x31 ||
CurPos == 0x40 || CurPos == 0x31 ||
CurPos == 0x60 || CurPos == 0x63 ||
(CurPos >= 0x70 && CurPos <= 0x77) ||
CurPos == 0x80 || CurPos == 0x81)
{
Registers[CurPos] = val;
}
//printf("BPTWL: write %02X -> %02X\n", CurPos, val);
CurPos++; // CHECKME
}
}
namespace DSi_I2C
{
u8 Cnt;
u8 Data;
u32 Device;
bool Init()
{
if (!DSi_BPTWL::Init()) return false;
if (!DSi_Camera::Init()) return false;
return true;
}
void DeInit()
{
DSi_BPTWL::DeInit();
DSi_Camera::DeInit();
}
void Reset()
{
Cnt = 0;
Data = 0;
Device = -1;
DSi_BPTWL::Reset();
DSi_Camera::Reset();
}
void WriteCnt(u8 val)
{
//printf("I2C: write CNT %02X, %08X\n", val, NDS::GetPC(1));
// TODO: check ACK flag
// TODO: transfer delay
// TODO: IRQ
// TODO: check read/write direction
if (val & (1<<7))
{
bool islast = val & (1<<0);
if (val & (1<<5))
{
// read
val &= 0xF7;
switch (Device)
{
case 0x4A: Data = DSi_BPTWL::Read(islast); break;
case 0x78: Data = DSi_Camera0->Read(islast); break;
case 0x7A: Data = DSi_Camera1->Read(islast); break;
default:
printf("I2C: read on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, 0, islast);
Data = 0xFF;
break;
}
//printf("I2C read, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
}
else
{
// write
val &= 0xE7;
bool ack = true;
if (val & (1<<1))
{
Device = Data & 0xFE;
//printf("I2C: %s start, device=%02X\n", (Data&0x01)?"read":"write", Device);
switch (Device)
{
case 0x4A: DSi_BPTWL::Start(); break;
case 0x78: DSi_Camera0->Start(); break;
case 0x7A: DSi_Camera1->Start(); break;
default:
printf("I2C: %s start on unknown device %02X\n", (Data&0x01)?"read":"write", Device);
ack = false;
break;
}
}
else
{
//printf("I2C write, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
switch (Device)
{
case 0x4A: DSi_BPTWL::Write(Data, islast); break;
case 0x78: DSi_Camera0->Write(Data, islast); break;
case 0x7A: DSi_Camera1->Write(Data, islast); break;
default:
printf("I2C: write on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
ack = false;
break;
}
}
if (ack) val |= (1<<4);
}
val &= 0x7F;
}
Cnt = val;
}
u8 ReadData()
{
return Data;
}
void WriteData(u8 val)
{
Data = val;
}
}

41
src/DSi_I2C.h Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_I2C_H
#define DSI_I2C_H
namespace DSi_I2C
{
extern u8 Cnt;
bool Init();
void DeInit();
void Reset();
//void DoSavestate(Savestate* file);
void WriteCnt(u8 val);
u8 ReadData();
void WriteData(u8 val);
//void TransferDone(u32 param);
}
#endif // DSI_I2C_H

341
src/DSi_NDMA.cpp Normal file
View File

@ -0,0 +1,341 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include "NDS.h"
#include "DSi.h"
#include "DSi_NDMA.h"
#include "GPU.h"
#include "DSi_AES.h"
DSi_NDMA::DSi_NDMA(u32 cpu, u32 num)
{
CPU = cpu;
Num = num;
Reset();
}
DSi_NDMA::~DSi_NDMA()
{
}
void DSi_NDMA::Reset()
{
SrcAddr = 0;
DstAddr = 0;
TotalLength = 0;
BlockLength = 0;
SubblockTimer = 0;
FillData = 0;
Cnt = 0;
StartMode = 0;
CurSrcAddr = 0;
CurDstAddr = 0;
SubblockLength = 0;
RemCount = 0;
IterCount = 0;
TotalRemCount = 0;
SrcAddrInc = 0;
DstAddrInc = 0;
Running = false;
InProgress = false;
}
void DSi_NDMA::DoSavestate(Savestate* file)
{
// TODO!
}
void DSi_NDMA::WriteCnt(u32 val)
{
u32 oldcnt = Cnt;
Cnt = val;
if ((!(oldcnt & 0x80000000)) && (val & 0x80000000)) // checkme
{
CurSrcAddr = SrcAddr;
CurDstAddr = DstAddr;
TotalRemCount = TotalLength;
switch ((Cnt >> 10) & 0x3)
{
case 0: DstAddrInc = 1; break;
case 1: DstAddrInc = -1; break;
case 2: DstAddrInc = 0; break;
case 3: DstAddrInc = 1; printf("BAD NDMA DST INC MODE 3\n"); break;
}
switch ((Cnt >> 13) & 0x3)
{
case 0: SrcAddrInc = 1; break;
case 1: SrcAddrInc = -1; break;
case 2: SrcAddrInc = 0; break;
case 3: SrcAddrInc = 0; break; // fill mode
}
StartMode = (Cnt >> 24) & 0x1F;
if (StartMode > 0x10) StartMode = 0x10;
if (CPU == 1) StartMode |= 0x20;
if ((StartMode & 0x1F) == 0x10)
Start();
if (StartMode != 0x10 && StartMode != 0x30 &&
StartMode != 0x04 && StartMode != 0x06 && StartMode != 0x07 && StartMode != 0x08 && StartMode != 0x09 &&
StartMode != 0x24 && StartMode != 0x26 && StartMode != 0x28 && StartMode != 0x29 && StartMode != 0x2A && StartMode != 0x2B)
printf("UNIMPLEMENTED ARM%d NDMA%d START MODE %02X, %08X->%08X LEN=%d BLK=%d CNT=%08X\n",
CPU?7:9, Num, StartMode, SrcAddr, DstAddr, TotalLength, BlockLength, Cnt);
}
}
void DSi_NDMA::Start()
{
if (Running) return;
if (!InProgress)
{
RemCount = BlockLength;
if (!RemCount)
RemCount = 0x1000000;
}
// TODO: how does GXFIFO DMA work with all the block shito?
IterCount = RemCount;
if (((StartMode & 0x1F) != 0x10) && !(Cnt & (1<<29)))
{
if (IterCount > TotalRemCount)
{
IterCount = TotalRemCount;
RemCount = IterCount;
}
}
if (Cnt & (1<<12)) CurDstAddr = DstAddr;
if (Cnt & (1<<15)) CurSrcAddr = SrcAddr;
//printf("ARM%d NDMA%d %08X %02X %08X->%08X %d bytes, total=%d\n", CPU?7:9, Num, Cnt, StartMode, CurSrcAddr, CurDstAddr, RemCount*4, TotalRemCount*4);
//IsGXFIFODMA = (CPU == 0 && (CurSrcAddr>>24) == 0x02 && CurDstAddr == 0x04000400 && DstAddrInc == 0);
// TODO eventually: not stop if we're running code in ITCM
//if (SubblockTimer & 0xFFFF)
// printf("TODO! NDMA SUBBLOCK TIMER: %08X\n", SubblockTimer);
if (NDS::DMAsRunning(CPU))
Running = 1;
else
Running = 2;
InProgress = true;
NDS::StopCPU(CPU, 1<<(Num+4));
}
void DSi_NDMA::Run()
{
if (!Running) return;
if (CPU == 0) return Run9();
else return Run7();
}
void DSi_NDMA::Run9()
{
if (NDS::ARM9Timestamp >= NDS::ARM9Target) return;
Executing = true;
// add NS penalty for first accesses in burst
bool burststart = (Running == 2);
Running = 1;
s32 unitcycles;
//s32 lastcycles = cycles;
bool dofill = ((Cnt >> 13) & 0x3) == 3;
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2];
}
else
{
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][3] + NDS::ARM9MemTimings[CurDstAddr >> 14][3];
if ((CurSrcAddr >> 24) == (CurDstAddr >> 24))
unitcycles++;
else if ((CurSrcAddr >> 24) == 0x02)
unitcycles--;
/*if (burststart)
{
cycles -= 2;
cycles -= (NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2]);
cycles += unitcycles;
}*/
}
while (IterCount > 0 && !Stall)
{
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
if (dofill)
DSi::ARM9Write32(CurDstAddr, FillData);
else
DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
IterCount--;
RemCount--;
TotalRemCount--;
if (NDS::ARM9Timestamp >= NDS::ARM9Target) break;
}
Executing = false;
Stall = false;
if (RemCount)
{
if (IterCount == 0)
{
Running = 0;
NDS::ResumeCPU(0, 1<<(Num+4));
//if (StartMode == 0x07)
// GPU3D::CheckFIFODMA();
}
return;
}
if ((StartMode & 0x1F) == 0x10) // CHECKME
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num);
}
else if (!(Cnt & (1<<29)))
{
if (TotalRemCount == 0)
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num);
}
}
Running = 0;
InProgress = false;
NDS::ResumeCPU(0, 1<<(Num+4));
}
void DSi_NDMA::Run7()
{
if (NDS::ARM7Timestamp >= NDS::ARM7Target) return;
Executing = true;
// add NS penalty for first accesses in burst
bool burststart = (Running == 2);
Running = 1;
s32 unitcycles;
//s32 lastcycles = cycles;
bool dofill = ((Cnt >> 13) & 0x3) == 3;
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2];
}
else
{
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][3] + NDS::ARM7MemTimings[CurDstAddr >> 15][3];
if ((CurSrcAddr >> 23) == (CurDstAddr >> 23))
unitcycles++;
else if ((CurSrcAddr >> 24) == 0x02)
unitcycles--;
/*if (burststart)
{
cycles -= 2;
cycles -= (NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2]);
cycles += unitcycles;
}*/
}
while (IterCount > 0 && !Stall)
{
NDS::ARM7Timestamp += unitcycles;
if (dofill)
DSi::ARM7Write32(CurDstAddr, FillData);
else
DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
IterCount--;
RemCount--;
TotalRemCount--;
if (NDS::ARM7Timestamp >= NDS::ARM7Target) break;
}
Executing = false;
Stall = false;
if (RemCount)
{
if (IterCount == 0)
{
Running = 0;
NDS::ResumeCPU(1, 1<<(Num+4));
DSi_AES::CheckInputDMA();
DSi_AES::CheckOutputDMA();
}
return;
}
if ((StartMode & 0x1F) == 0x10) // CHECKME
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num);
}
else if (!(Cnt & (1<<29)))
{
if (TotalRemCount == 0)
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num);
}
}
Running = 0;
InProgress = false;
NDS::ResumeCPU(1, 1<<(Num+4));
DSi_AES::CheckInputDMA();
DSi_AES::CheckOutputDMA();
}

96
src/DSi_NDMA.h Normal file
View File

@ -0,0 +1,96 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_NDMA_H
#define DSI_NDMA_H
#include "types.h"
class DSi_NDMA
{
public:
DSi_NDMA(u32 cpu, u32 num);
~DSi_NDMA();
void Reset();
void DoSavestate(Savestate* file);
void WriteCnt(u32 val);
void Start();
void Run();
void Run9();
void Run7();
bool IsInMode(u32 mode)
{
return ((mode == StartMode) && (Cnt & 0x80000000));
}
bool IsRunning() { return Running!=0; }
void StartIfNeeded(u32 mode)
{
if ((mode == StartMode) && (Cnt & 0x80000000))
Start();
}
void StopIfNeeded(u32 mode)
{
if (mode == StartMode)
Cnt &= ~0x80000000;
}
void StallIfRunning()
{
if (Executing) Stall = true;
}
u32 SrcAddr;
u32 DstAddr;
u32 TotalLength; // total length, when transferring multiple blocks
u32 BlockLength; // length of one transfer
u32 SubblockTimer; // optional delay between subblocks (only in round-robin mode)
u32 FillData;
u32 Cnt;
private:
u32 CPU, Num;
u32 StartMode;
u32 CurSrcAddr;
u32 CurDstAddr;
u32 SubblockLength; // length transferred per run when delay is used
u32 RemCount;
u32 IterCount;
u32 TotalRemCount;
u32 SrcAddrInc;
u32 DstAddrInc;
u32 Running;
bool InProgress;
bool Executing;
bool Stall;
bool IsGXFIFODMA;
};
#endif // DSI_NDMA_H

881
src/DSi_NWifi.cpp Normal file
View File

@ -0,0 +1,881 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <string.h>
#include <stdio.h>
#include "DSi.h"
#include "DSi_NWifi.h"
#include "SPI.h"
const u8 CIS0[256] =
{
0x01, 0x03, 0xD9, 0x01, 0xFF,
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
0x21, 0x02, 0x0C, 0x00,
0x22, 0x04, 0x00, 0x00, 0x08, 0x32,
0x1A, 0x05, 0x01, 0x01, 0x00, 0x02, 0x07,
0x1B, 0x08, 0xC1, 0x41, 0x30, 0x30, 0xFF, 0xFF, 0x32, 0x00,
0x14, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
const u8 CIS1[256] =
{
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
0x21, 0x02, 0x0C, 0x00,
0x22, 0x2A, 0x01,
0x01, 0x11,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08,
0x00, 0x00, 0xFF, 0x80,
0x00, 0x00, 0x00,
0x00, 0x01, 0x0A,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01,
0x80, 0x01, 0x06,
0x81, 0x01, 0x07,
0x82, 0x01, 0xDF,
0xFF,
0x01,
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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// hax
DSi_NWifi* hax_wifi;
void triggerirq(u32 param)
{
hax_wifi->SetIRQ_F1_Counter(0);
}
DSi_NWifi::DSi_NWifi(DSi_SDHost* host) : DSi_SDDevice(host)
{
TransferCmd = 0xFFFFFFFF;
RemSize = 0;
F0_IRQEnable = 0;
F0_IRQStatus = 0;
F1_IRQEnable = 0; F1_IRQEnable_CPU = 0; F1_IRQEnable_Error = 0; F1_IRQEnable_Counter = 0;
F1_IRQStatus = 0; F1_IRQStatus_CPU = 0; F1_IRQStatus_Error = 0; F1_IRQStatus_Counter = 0;
WindowData = 0;
WindowReadAddr = 0;
WindowWriteAddr = 0;
// TODO: check the actual mailbox size (presumably 0x200)
for (int i = 0; i < 8; i++)
Mailbox[i] = new FIFO<u8>(0x200);
u8* mac = SPI_Firmware::GetWifiMAC();
printf("NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
memset(EEPROM, 0, 0x400);
*(u32*)&EEPROM[0x000] = 0x300;
*(u16*)&EEPROM[0x008] = 0x8348; // TODO: determine properly (country code)
memcpy(&EEPROM[0x00A], mac, 6);
*(u32*)&EEPROM[0x010] = 0x60000000;
memset(&EEPROM[0x03C], 0xFF, 0x70);
memset(&EEPROM[0x140], 0xFF, 0x8);
u16 chk = 0xFFFF;
for (int i = 0; i < 0x300; i+=2)
chk ^= *(u16*)&EEPROM[i];
*(u16*)&EEPROM[0x004] = chk;
EEPROMReady = 0;
BootPhase = 0;
}
DSi_NWifi::~DSi_NWifi()
{
for (int i = 0; i < 8; i++)
delete Mailbox[i];
}
// CHECKME
// can IRQ status bits be set when the corresponding IRQs are disabled in the enable register?
// otherwise, does disabling them clear the status register?
void DSi_NWifi::UpdateIRQ()
{
F0_IRQStatus = 0;
IRQ = false;
if (F1_IRQStatus & F1_IRQEnable)
F0_IRQStatus |= (1<<1);
if (F0_IRQEnable & (1<<0))
{
if (F0_IRQStatus & F0_IRQEnable)
IRQ = true;
}
Host->SetCardIRQ();
}
void DSi_NWifi::UpdateIRQ_F1()
{
F1_IRQStatus = 0;
if (!Mailbox[4]->IsEmpty()) F1_IRQStatus |= (1<<0);
if (!Mailbox[5]->IsEmpty()) F1_IRQStatus |= (1<<1);
if (!Mailbox[6]->IsEmpty()) F1_IRQStatus |= (1<<2);
if (!Mailbox[7]->IsEmpty()) F1_IRQStatus |= (1<<3);
if (F1_IRQStatus_Counter & F1_IRQEnable_Counter) F1_IRQStatus |= (1<<4);
if (F1_IRQStatus_CPU & F1_IRQEnable_CPU) F1_IRQStatus |= (1<<6);
if (F1_IRQStatus_Error & F1_IRQEnable_Error) F1_IRQStatus |= (1<<7);
UpdateIRQ();
}
void DSi_NWifi::SetIRQ_F1_Counter(u32 n)
{
F1_IRQStatus_Counter |= (1<<n);
UpdateIRQ_F1();
}
void DSi_NWifi::ClearIRQ_F1_Counter(u32 n)
{
F1_IRQStatus_Counter &= ~(1<<n);
UpdateIRQ_F1();
}
void DSi_NWifi::SetIRQ_F1_CPU(u32 n)
{
F1_IRQStatus_CPU |= (1<<n);
UpdateIRQ_F1();
}
u8 DSi_NWifi::F0_Read(u32 addr)
{
switch (addr)
{
case 0x00000: return 0x11;
case 0x00001: return 0x00;
case 0x00002: return 0x02; // writable??
case 0x00003: return 0x02;
case 0x00004: return F0_IRQEnable;
case 0x00005: return F0_IRQStatus;
case 0x00008: return 0x17;
case 0x00009: return 0x00;
case 0x0000A: return 0x10;
case 0x0000B: return 0x00;
case 0x00012: return 0x03;
case 0x00109: return 0x00;
case 0x0010A: return 0x11;
case 0x0010B: return 0x00;
}
if (addr >= 0x01000 && addr < 0x01100)
{
return CIS0[addr & 0xFF];
}
if (addr >= 0x01100 && addr < 0x01200)
{
return CIS1[addr & 0xFF];
}
printf("NWIFI: unknown func0 read %05X\n", addr);
return 0;
}
void DSi_NWifi::F0_Write(u32 addr, u8 val)
{
switch (addr)
{
case 0x00004:
F0_IRQEnable = val;
UpdateIRQ();
return;
}
printf("NWIFI: unknown func0 write %05X %02X\n", addr, val);
}
u8 DSi_NWifi::F1_Read(u32 addr)
{
if (addr < 0x100)
{
u8 ret = Mailbox[4]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x200)
{
u8 ret = Mailbox[5]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x300)
{
u8 ret = Mailbox[6]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x400)
{
u8 ret = Mailbox[7]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x800)
{
switch (addr)
{
case 0x00400: return F1_IRQStatus;
case 0x00401: return F1_IRQStatus_CPU;
case 0x00402: return F1_IRQStatus_Error;
case 0x00403: return F1_IRQStatus_Counter;
case 0x00405:
{
u8 ret = 0;
if (Mailbox[4]->Level() >= 4) ret |= (1<<0);
if (Mailbox[5]->Level() >= 4) ret |= (1<<1);
if (Mailbox[6]->Level() >= 4) ret |= (1<<2);
if (Mailbox[7]->Level() >= 4) ret |= (1<<3);
return ret;
}
case 0x00408: return Mailbox[4]->Peek(0);
case 0x00409: return Mailbox[4]->Peek(1);
case 0x0040A: return Mailbox[4]->Peek(2);
case 0x0040B: return Mailbox[4]->Peek(3);
case 0x00418: return F1_IRQEnable;
case 0x00419: return F1_IRQEnable_CPU;
case 0x0041A: return F1_IRQEnable_Error;
case 0x0041B: return F1_IRQEnable_Counter;
// GROSS FUCKING HACK
case 0x00440: ClearIRQ_F1_Counter(0); return 0;
case 0x00450: return 1; // HAX!!
case 0x00474: return WindowData & 0xFF;
case 0x00475: return (WindowData >> 8) & 0xFF;
case 0x00476: return (WindowData >> 16) & 0xFF;
case 0x00477: return WindowData >> 24;
}
}
else if (addr < 0x1000)
{
u8 ret = Mailbox[4]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x1800)
{
u8 ret = Mailbox[5]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x2000)
{
u8 ret = Mailbox[6]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x2800)
{
u8 ret = Mailbox[7]->Read();
UpdateIRQ_F1();
return ret;
}
else
{
u8 ret = Mailbox[4]->Read();
UpdateIRQ_F1();
return ret;
}
printf("NWIFI: unknown func1 read %05X\n", addr);
return 0;
}
void DSi_NWifi::F1_Write(u32 addr, u8 val)
{
if (addr < 0x100)
{
if (Mailbox[0]->IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0]->Write(val);
if (addr == 0xFF) HandleCommand();
UpdateIRQ_F1();
return;
}
else if (addr < 0x200)
{
if (Mailbox[1]->IsFull()) printf("!!! NWIFI: MBOX1 FULL\n");
Mailbox[1]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x300)
{
if (Mailbox[2]->IsFull()) printf("!!! NWIFI: MBOX2 FULL\n");
Mailbox[2]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x400)
{
if (Mailbox[3]->IsFull()) printf("!!! NWIFI: MBOX3 FULL\n");
Mailbox[3]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x800)
{
switch (addr)
{
case 0x00418: F1_IRQEnable = val; UpdateIRQ_F1(); return;
case 0x00419: F1_IRQEnable_CPU = val; UpdateIRQ_F1(); return;
case 0x0041A: F1_IRQEnable_Error = val; UpdateIRQ_F1(); return;
case 0x0041B: F1_IRQEnable_Counter = val; UpdateIRQ_F1(); return;
// GROSS FUCKING HACK
case 0x00440: ClearIRQ_F1_Counter(0); return;
case 0x00474: WindowData = (WindowData & 0xFFFFFF00) | val; return;
case 0x00475: WindowData = (WindowData & 0xFFFF00FF) | (val << 8); return;
case 0x00476: WindowData = (WindowData & 0xFF00FFFF) | (val << 16); return;
case 0x00477: WindowData = (WindowData & 0x00FFFFFF) | (val << 24); return;
case 0x00478:
WindowWriteAddr = (WindowWriteAddr & 0xFFFFFF00) | val;
WindowWrite(WindowWriteAddr, WindowData);
return;
case 0x00479: WindowWriteAddr = (WindowWriteAddr & 0xFFFF00FF) | (val << 8); return;
case 0x0047A: WindowWriteAddr = (WindowWriteAddr & 0xFF00FFFF) | (val << 16); return;
case 0x0047B: WindowWriteAddr = (WindowWriteAddr & 0x00FFFFFF) | (val << 24); return;
case 0x0047C:
WindowReadAddr = (WindowReadAddr & 0xFFFFFF00) | val;
WindowData = WindowRead(WindowReadAddr);
return;
case 0x0047D: WindowReadAddr = (WindowReadAddr & 0xFFFF00FF) | (val << 8); return;
case 0x0047E: WindowReadAddr = (WindowReadAddr & 0xFF00FFFF) | (val << 16); return;
case 0x0047F: WindowReadAddr = (WindowReadAddr & 0x00FFFFFF) | (val << 24); return;
}
}
else if (addr < 0x1000)
{
if (Mailbox[0]->IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0]->Write(val);
if (addr == 0xFFF) HandleCommand();
UpdateIRQ_F1();
return;
}
else if (addr < 0x1800)
{
if (Mailbox[1]->IsFull()) printf("!!! NWIFI: MBOX1 FULL\n");
Mailbox[1]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x2000)
{
if (Mailbox[2]->IsFull()) printf("!!! NWIFI: MBOX2 FULL\n");
Mailbox[2]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x2800)
{
if (Mailbox[3]->IsFull()) printf("!!! NWIFI: MBOX3 FULL\n");
Mailbox[3]->Write(val);
UpdateIRQ_F1();
return;
}
else
{
if (Mailbox[0]->IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0]->Write(val);
if (addr == 0x3FFF) HandleCommand(); // CHECKME
UpdateIRQ_F1();
return;
}
printf("NWIFI: unknown func1 write %05X %02X\n", addr, val);
}
u8 DSi_NWifi::SDIO_Read(u32 func, u32 addr)
{
switch (func)
{
case 0: return F0_Read(addr);
case 1: return F1_Read(addr);
}
printf("NWIFI: unknown SDIO read %d %05X\n", func, addr);
return 0;
}
void DSi_NWifi::SDIO_Write(u32 func, u32 addr, u8 val)
{
switch (func)
{
case 0: return F0_Write(addr, val);
case 1: return F1_Write(addr, val);
}
printf("NWIFI: unknown SDIO write %d %05X %02X\n", func, addr, val);
}
void DSi_NWifi::SendCMD(u8 cmd, u32 param)
{
switch (cmd)
{
case 12:
// stop command
// CHECKME: does the SDIO controller actually send those??
// DSi firmware sets it to send them
return;
case 52: // IO_RW_DIRECT
{
u32 func = (param >> 28) & 0x7;
u32 addr = (param >> 9) & 0x1FFFF;
if (param & (1<<31))
{
// write
u8 val = param & 0xFF;
SDIO_Write(func, addr, val);
if (param & (1<<27))
val = SDIO_Read(func, addr); // checkme
Host->SendResponse(val | 0x1000, true);
}
else
{
// read
u8 val = SDIO_Read(func, addr);
Host->SendResponse(val | 0x1000, true);
}
}
return;
case 53: // IO_RW_EXTENDED
{
u32 addr = (param >> 9) & 0x1FFFF;
TransferCmd = param;
TransferAddr = addr;
if (param & (1<<27))
{
RemSize = (param & 0x1FF) << 9; // checkme
}
else
{
RemSize = (param & 0x1FF);
if (!RemSize) RemSize = 0x200;
}
if (param & (1<<31))
{
// write
WriteBlock();
Host->SendResponse(0x1000, true);
}
else
{
// read
ReadBlock();
Host->SendResponse(0x1000, true);
}
}
return;
}
printf("NWIFI: unknown CMD %d %08X\n", cmd, param);
}
void DSi_NWifi::SendACMD(u8 cmd, u32 param)
{
printf("NWIFI: unknown ACMD %d %08X\n", cmd, param);
}
void DSi_NWifi::ContinueTransfer()
{
if (TransferCmd & (1<<31))
WriteBlock();
else
ReadBlock();
}
void DSi_NWifi::ReadBlock()
{
u32 func = (TransferCmd >> 28) & 0x7;
u32 len = (TransferCmd & (1<<27)) ? 0x200 : RemSize;
len = Host->GetTransferrableLen(len);
u8 data[0x200];
for (u32 i = 0; i < len; i++)
{
data[i] = SDIO_Read(func, TransferAddr);
if (TransferCmd & (1<<26))
{
TransferAddr++;
TransferAddr &= 0x1FFFF; // checkme
}
}
len = Host->DataRX(data, len);
if (RemSize > 0)
{
RemSize -= len;
if (RemSize == 0)
{
// TODO?
}
}
}
void DSi_NWifi::WriteBlock()
{
u32 func = (TransferCmd >> 28) & 0x7;
u32 len = (TransferCmd & (1<<27)) ? 0x200 : RemSize;
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (len = Host->DataTX(data, len))
{
for (u32 i = 0; i < len; i++)
{
SDIO_Write(func, TransferAddr, data[i]);
if (TransferCmd & (1<<26))
{
TransferAddr++;
TransferAddr &= 0x1FFFF; // checkme
}
}
if (RemSize > 0)
{
RemSize -= len;
if (RemSize == 0)
{
// TODO?
}
}
}
}
void DSi_NWifi::HandleCommand()
{
switch (BootPhase)
{
case 0: return BMI_Command();
case 1: return WMI_Command();
}
}
void DSi_NWifi::BMI_Command()
{
// HLE command handling stub
u32 cmd = MB_Read32(0);
switch (cmd)
{
case 0x01: // BMI_DONE
{
printf("BMI_DONE\n");
EEPROMReady = 1; // GROSS FUCKING HACK
u8 ready_msg[8] = {0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00};
SendWMIFrame(ready_msg, 8, 0, 0x00, 0x0000);
BootPhase = 1;
}
return;
case 0x03: // BMI_WRITE_MEMORY
{
u32 addr = MB_Read32(0);
u32 len = MB_Read32(0);
printf("BMI mem write %08X %08X\n", addr, len);
for (int i = 0; i < len; i++)
{
u8 val = Mailbox[0]->Read();
// TODO: do something with it!!
}
}
return;
case 0x04: // BMI_EXECUTE
{
u32 entry = MB_Read32(0);
u32 arg = MB_Read32(0);
printf("BMI_EXECUTE %08X %08X\n", entry, arg);
}
return;
case 0x06: // BMI_READ_SOC_REGISTER
{
u32 addr = MB_Read32(0);
u32 val = WindowRead(addr);
MB_Write32(4, val);
}
return;
case 0x07: // BMI_WRITE_SOC_REGISTER
{
u32 addr = MB_Read32(0);
u32 val = MB_Read32(0);
WindowWrite(addr, val);
}
return;
case 0x08: // BMI_GET_TARGET_ID
MB_Write32(4, 0xFFFFFFFF);
MB_Write32(4, 0x0000000C);
//MB_Write32(4, 0x20000118);
MB_Write32(4, 0x23000024); // ROM version (TODO: how to determine correct one?)
MB_Write32(4, 0x00000002);
return;
case 0x0D: // BMI_LZ_STREAM_START
{
u32 addr = MB_Read32(0);
printf("BMI_LZ_STREAM_START %08X\n", addr);
}
return;
case 0x0E: // BMI_LZ_DATA
{
u32 len = MB_Read32(0);
printf("BMI LZ write %08X\n", len);
//FILE* f = fopen("wififirm.bin", "ab");
for (int i = 0; i < len; i++)
{
u8 val = Mailbox[0]->Read();
// TODO: do something with it!!
//fwrite(&val, 1, 1, f);
}
//fclose(f);
}
return;
default:
printf("unknown BMI command %08X\n", cmd);
return;
}
}
void DSi_NWifi::WMI_Command()
{
// HLE command handling stub
u16 h0 = MB_Read16(0);
u16 len = MB_Read16(0);
u16 h2 = MB_Read16(0);
u16 cmd = MB_Read16(0);
printf("WMI: cmd %04X\n", cmd);
switch (cmd)
{
case 0x0002: // service connect
{
u16 svc_id = MB_Read16(0);
u16 conn_flags = MB_Read16(0);
u8 svc_resp[10];
*(u16*)&svc_resp[0] = 0x0003;
*(u16*)&svc_resp[2] = svc_id;
svc_resp[4] = 0;
svc_resp[5] = (svc_id & 0xFF) + 1;
*(u16*)&svc_resp[6] = 0x0001;
*(u16*)&svc_resp[8] = 0x0001;
SendWMIFrame(svc_resp, 10, 0, 0x00, 0x0000);
}
break;
case 0x0004: // setup complete
{
u8 ready_evt[14];
memset(ready_evt, 0, 14);
*(u16*)&ready_evt[0] = 0x1001;
memcpy(&ready_evt[2], SPI_Firmware::GetWifiMAC(), 6);
ready_evt[8] = 0x02;
*(u32*)&ready_evt[10] = 0x23000024;
// ctrl[0] = trailer size
// trailer[1] = trailer extra size
// trailer[0] = trailer type???
SendWMIFrame(ready_evt, 14, 1, 0x00, 0x0000);
}
break;
default:
printf("unknown WMI command %04X\n", cmd);
break;
}
MB_Drain(0);
}
void DSi_NWifi::SendWMIFrame(u8* data, u32 len, u8 ep, u8 flags, u16 ctrl)
{
u32 wlen = 0;
Mailbox[4]->Write(ep); // eid
Mailbox[4]->Write(flags); // flags
MB_Write16(4, len); // payload length
MB_Write16(4, ctrl); // ctrl
wlen += 6;
for (int i = 0; i < len; i++)
{
Mailbox[4]->Write(data[i]);
wlen++;
}
for (; wlen & 0x7F; wlen++)
Mailbox[4]->Write(0);
}
u32 DSi_NWifi::WindowRead(u32 addr)
{
printf("NWifi: window read %08X\n", addr);
if ((addr & 0xFFFF00) == 0x520000)
{
// RAM host interest area
// TODO: different base based on hardware version
switch (addr & 0xFF)
{
case 0x54:
// base address of EEPROM data
// TODO find what the actual address is!
return 0x1FFC00;
case 0x58: return EEPROMReady; // hax
}
return 0;
}
// hax
if ((addr & 0x1FFC00) == 0x1FFC00)
{
return *(u32*)&EEPROM[addr & 0x3FF];
}
switch (addr)
{
case 0x40EC: // chip ID
// 0D000000 / 0D000001 == AR6013
// TODO: check firmware.bin to determine the correct value
return 0x0D000001;
// SOC_RESET_CAUSE
case 0x40C0: return 2;
}
return 0;
}
void DSi_NWifi::WindowWrite(u32 addr, u32 val)
{
printf("NWifi: window write %08X %08X\n", addr, val);
}

121
src/DSi_NWifi.h Normal file
View File

@ -0,0 +1,121 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_NWIFI_H
#define DSI_NWIFI_H
#include "DSi_SD.h"
#include "FIFO.h"
class DSi_NWifi : public DSi_SDDevice
{
public:
DSi_NWifi(DSi_SDHost* host);
~DSi_NWifi();
void SendCMD(u8 cmd, u32 param);
void SendACMD(u8 cmd, u32 param);
void ContinueTransfer();
void SetIRQ_F1_Counter(u32 n);
private:
u32 TransferCmd;
u32 TransferAddr;
u32 RemSize;
void UpdateIRQ();
void UpdateIRQ_F1();
//void SetIRQ_F1_Counter(u32 n);
void ClearIRQ_F1_Counter(u32 n);
void SetIRQ_F1_CPU(u32 n);
u8 F0_Read(u32 addr);
void F0_Write(u32 addr, u8 val);
u8 F1_Read(u32 addr);
void F1_Write(u32 addr, u8 val);
u8 SDIO_Read(u32 func, u32 addr);
void SDIO_Write(u32 func, u32 addr, u8 val);
void ReadBlock();
void WriteBlock();
void HandleCommand();
void BMI_Command();
void WMI_Command();
void SendWMIFrame(u8* data, u32 len, u8 ep, u8 flags, u16 ctrl);
u32 WindowRead(u32 addr);
void WindowWrite(u32 addr, u32 val);
u16 MB_Read16(int n)
{
u16 ret = Mailbox[n]->Read();
ret |= (Mailbox[n]->Read() << 8);
return ret;
}
void MB_Write16(int n, u16 val)
{
Mailbox[n]->Write(val & 0xFF); val >>= 8;
Mailbox[n]->Write(val & 0xFF);
}
u32 MB_Read32(int n)
{
u32 ret = Mailbox[n]->Read();
ret |= (Mailbox[n]->Read() << 8);
ret |= (Mailbox[n]->Read() << 16);
ret |= (Mailbox[n]->Read() << 24);
return ret;
}
void MB_Write32(int n, u32 val)
{
Mailbox[n]->Write(val & 0xFF); val >>= 8;
Mailbox[n]->Write(val & 0xFF); val >>= 8;
Mailbox[n]->Write(val & 0xFF); val >>= 8;
Mailbox[n]->Write(val & 0xFF);
}
void MB_Drain(int n)
{
while (!Mailbox[n]->IsEmpty()) Mailbox[n]->Read();
}
FIFO<u8>* Mailbox[8];
u8 F0_IRQEnable;
u8 F0_IRQStatus;
u8 F1_IRQEnable, F1_IRQEnable_CPU, F1_IRQEnable_Error, F1_IRQEnable_Counter;
u8 F1_IRQStatus, F1_IRQStatus_CPU, F1_IRQStatus_Error, F1_IRQStatus_Counter;
u32 WindowData, WindowReadAddr, WindowWriteAddr;
u8 EEPROM[0x400];
u32 EEPROMReady;
u32 BootPhase;
};
#endif // DSI_NWIFI_H

934
src/DSi_SD.cpp Normal file
View File

@ -0,0 +1,934 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_SD.h"
#include "DSi_NWifi.h"
#include "Platform.h"
// observed IRQ behavior during transfers
//
// during reads:
// * bit23 is cleared during the first block, always set otherwise. weird
// * bit24 (RXRDY) gets set when the FIFO is full
//
// during reads with FIFO32:
// * FIFO16 drains directly into FIFO32
// * when bit24 is set, FIFO32 is already full (with contents from the other FIFO)
// * reading from an empty FIFO just wraps around (and sets bit21)
// * FIFO32 starts filling when bit24 would be set?
//
//
// TX:
// * when sending command, if current FIFO full
// * upon ContinueTransfer(), if current FIFO full
// * -> upon DataTX() if current FIFO full
// * when filling FIFO
#define SD_DESC Num?"SDIO":"SD/MMC"
DSi_SDHost::DSi_SDHost(u32 num)
{
Num = num;
DataFIFO[0] = new FIFO<u16>(0x100);
DataFIFO[1] = new FIFO<u16>(0x100);
DataFIFO32 = new FIFO<u32>(0x80);
Ports[0] = NULL;
Ports[1] = NULL;
}
DSi_SDHost::~DSi_SDHost()
{
delete DataFIFO[0];
delete DataFIFO[1];
delete DataFIFO32;
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
}
void DSi_SDHost::Reset()
{
if (Num == 0)
{
PortSelect = 0x0200; // CHECKME
}
else
{
PortSelect = 0x0100; // CHECKME
}
SoftReset = 0x0007; // CHECKME
SDClock = 0;
SDOption = 0;
Command = 0;
Param = 0;
memset(ResponseBuffer, 0, sizeof(ResponseBuffer));
DataFIFO[0]->Clear();
DataFIFO[1]->Clear();
CurFIFO = 0;
DataFIFO32->Clear();
IRQStatus = 0;
IRQMask = 0x8B7F031D;
CardIRQStatus = 0;
CardIRQMask = 0xC007;
CardIRQCtl = 0;
DataCtl = 0;
Data32IRQ = 0;
DataMode = 0;
BlockCount16 = 0; BlockCount32 = 0; BlockCountInternal = 0;
BlockLen16 = 0; BlockLen32 = 0;
StopAction = 0;
TXReq = false;
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
Ports[0] = NULL;
Ports[1] = NULL;
if (Num == 0)
{
// TODO: eventually pull from host filesystem
/*DSi_MMCStorage* sd = new DSi_MMCStorage(this, false, "sd.bin");
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
sd->SetCID(sd_cid);*/
DSi_MMCStorage* sd = NULL;
DSi_MMCStorage* mmc = new DSi_MMCStorage(this, true, "nand.bin");
mmc->SetCID(DSi::eMMC_CID);
Ports[0] = sd;
Ports[1] = mmc;
}
else
{
DSi_NWifi* nwifi = new DSi_NWifi(this);
Ports[0] = nwifi;
}
}
void DSi_SDHost::DoSavestate(Savestate* file)
{
// TODO!
}
void DSi_SDHost::UpdateData32IRQ()
{
if (DataMode == 0) return;
u32 oldflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2);
oldflags &= (Data32IRQ >> 11);
Data32IRQ &= ~0x0300;
if (DataFIFO32->Level() >= (BlockLen32>>2)) Data32IRQ |= (1<<8);
if (!DataFIFO32->IsEmpty()) Data32IRQ |= (1<<9);
u32 newflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2);
newflags &= (Data32IRQ >> 11);
if ((oldflags == 0) && (newflags != 0))
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::ClearIRQ(u32 irq)
{
IRQStatus &= ~(1<<irq);
}
void DSi_SDHost::SetIRQ(u32 irq)
{
u32 oldflags = IRQStatus & ~IRQMask;
IRQStatus |= (1<<irq);
u32 newflags = IRQStatus & ~IRQMask;
if ((oldflags == 0) && (newflags != 0))
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::UpdateIRQ(u32 oldmask)
{
u32 oldflags = IRQStatus & ~oldmask;
u32 newflags = IRQStatus & ~IRQMask;
if ((oldflags == 0) && (newflags != 0))
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::SetCardIRQ()
{
if (!(CardIRQCtl & (1<<0))) return;
u16 oldflags = CardIRQStatus & ~CardIRQMask;
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev->IRQ) CardIRQStatus |= (1<<0);
else CardIRQStatus &= ~(1<<0);
u16 newflags = CardIRQStatus & ~CardIRQMask;
if ((oldflags == 0) && (newflags != 0)) // checkme
{
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1);
}
}
void DSi_SDHost::SendResponse(u32 val, bool last)
{
*(u32*)&ResponseBuffer[6] = *(u32*)&ResponseBuffer[4];
*(u32*)&ResponseBuffer[4] = *(u32*)&ResponseBuffer[2];
*(u32*)&ResponseBuffer[2] = *(u32*)&ResponseBuffer[0];
*(u32*)&ResponseBuffer[0] = val;
if (last) SetIRQ(0);
}
void DSi_SDHost::FinishRX(u32 param)
{
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
host->CheckSwapFIFO();
if (host->DataMode == 1)
host->UpdateFIFO32();
else
host->SetIRQ(24);
}
u32 DSi_SDHost::DataRX(u8* data, u32 len)
{
if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; }
bool last = (BlockCountInternal == 0);
u32 f = CurFIFO ^ 1;
for (u32 i = 0; i < len; i += 2)
DataFIFO[f]->Write(*(u16*)&data[i]);
//CurFIFO = f;
//SetIRQ(24);
// TODO: determine what the delay should be!
// for now, this is a placeholder
// we need a delay because DSi boot2 will send a command and then wait for IRQ0
// but if IRQ24 is thrown instantly, the handler clears IRQ0 before the
// send-command function starts polling IRQ status
u32 param = Num | (last << 1);
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
false, 512, FinishRX, param);
return len;
}
void DSi_SDHost::FinishTX(u32 param)
{
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1];
if (host->BlockCountInternal == 0)
{
if (host->StopAction & (1<<8))
{
if (dev) dev->SendCMD(12, 0);
}
// CHECKME: presumably IRQ2 should not trigger here, but rather
// when the data transfer is done
//SetIRQ(0);
host->SetIRQ(2);
host->TXReq = false;
}
else
{
if (dev) dev->ContinueTransfer();
}
}
u32 DSi_SDHost::DataTX(u8* data, u32 len)
{
TXReq = true;
u32 f = CurFIFO;
if (DataMode == 1)
{
if ((DataFIFO32->Level() << 2) < len)
{
if (DataFIFO32->IsEmpty())
{
SetIRQ(25);
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
}
return 0;
}
// drain FIFO32 into FIFO16
if (!DataFIFO[f]->IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO32 INTO FIFO16 BUT IT CONTAINS SHIT ALREADY\n");
for (;;)
{
u32 f = CurFIFO;
if ((DataFIFO[f]->Level() << 1) >= BlockLen16) break;
if (DataFIFO32->IsEmpty()) break;
u32 val = DataFIFO32->Read();
DataFIFO[f]->Write(val & 0xFFFF);
DataFIFO[f]->Write(val >> 16);
}
UpdateData32IRQ();
if (BlockCount32 > 1)
BlockCount32--;
}
else
{
if ((DataFIFO[f]->Level() << 1) < len)
{
if (DataFIFO[f]->IsEmpty()) SetIRQ(25);
return 0;
}
}
for (u32 i = 0; i < len; i += 2)
*(u16*)&data[i] = DataFIFO[f]->Read();
CurFIFO ^= 1;
BlockCountInternal--;
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
false, 512, FinishTX, Num);
return len;
}
u32 DSi_SDHost::GetTransferrableLen(u32 len)
{
if (len > BlockLen16) len = BlockLen16; // checkme
return len;
}
void DSi_SDHost::CheckRX()
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
CheckSwapFIFO();
if (BlockCountInternal <= 1)
{
if (StopAction & (1<<8))
{
if (dev) dev->SendCMD(12, 0);
}
// CHECKME: presumably IRQ2 should not trigger here, but rather
// when the data transfer is done
//SetIRQ(0);
SetIRQ(2);
}
else
{
BlockCountInternal--;
if (dev) dev->ContinueTransfer();
}
}
void DSi_SDHost::CheckTX()
{
if (!TXReq) return;
if (DataMode == 1)
{
if ((DataFIFO32->Level() << 2) < BlockLen32)
return;
}
else
{
u32 f = CurFIFO;
if ((DataFIFO[f]->Level() << 1) < BlockLen16)
return;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev) dev->ContinueTransfer();
}
u16 DSi_SDHost::Read(u32 addr)
{
switch (addr & 0x1FF)
{
case 0x000: return Command;
case 0x002: return PortSelect & 0x030F;
case 0x004: return Param & 0xFFFF;
case 0x006: return Param >> 16;
case 0x008: return StopAction;
case 0x00A: return BlockCount16;
case 0x00C: return ResponseBuffer[0];
case 0x00E: return ResponseBuffer[1];
case 0x010: return ResponseBuffer[2];
case 0x012: return ResponseBuffer[3];
case 0x014: return ResponseBuffer[4];
case 0x016: return ResponseBuffer[5];
case 0x018: return ResponseBuffer[6];
case 0x01A: return ResponseBuffer[7];
case 0x01C:
{
u16 ret = (IRQStatus & 0x031D);
if (!Num)
{
if (Ports[0]) // basic check of whether the SD card is inserted
ret |= 0x0030;
else
ret |= 0x0008;
}
else
{
// SDIO wifi is always inserted, I guess
ret |= 0x0030;
}
return ret;
}
case 0x01E: return ((IRQStatus >> 16) & 0x8B7F);
case 0x020: return IRQMask & 0x031D;
case 0x022: return (IRQMask >> 16) & 0x8B7F;
case 0x024: return SDClock;
case 0x026: return BlockLen16;
case 0x028: return SDOption;
case 0x02C: return 0; // TODO
case 0x034: return CardIRQCtl;
case 0x036: return CardIRQStatus;
case 0x038: return CardIRQMask;
case 0x030: return ReadFIFO16();
case 0x0D8: return DataCtl;
case 0x0E0: return SoftReset;
case 0x0F6: return 0; // MMC write protect (always 0)
case 0x100: return Data32IRQ;
case 0x104: return BlockLen32;
case 0x108: return BlockCount32;
}
printf("unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1));
return 0;
}
u16 DSi_SDHost::ReadFIFO16()
{
u32 f = CurFIFO;
if (DataFIFO[f]->IsEmpty())
{
// TODO
// on hardware it seems to wrap around. underflow bit is set upon the first 'empty' read.
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u16 ret = DataFIFO[f]->Read();
if (DataFIFO[f]->IsEmpty())
{
CheckRX();
}
return ret;
}
u32 DSi_SDHost::ReadFIFO32()
{
if (DataMode != 1) return 0;
if (DataFIFO32->IsEmpty())
{
// TODO
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 ret = DataFIFO32->Read();
if (DataFIFO32->IsEmpty())
{
CheckRX();
}
UpdateData32IRQ();
return ret;
}
void DSi_SDHost::Write(u32 addr, u16 val)
{
switch (addr & 0x1FF)
{
case 0x000:
{
Command = val;
u8 cmd = Command & 0x3F;
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev)
{
// CHECKME
// "Setting Command Type to "ACMD" is automatically sending an APP_CMD prefix prior to the command number"
// except DSi boot2 manually sends an APP_CMD prefix AND sets the next command to be ACMD
switch ((Command >> 6) & 0x3)
{
case 0: dev->SendCMD(cmd, Param); break;
case 1: /*dev->SendCMD(55, 0);*/ dev->SendCMD(cmd, Param); break;
default:
printf("%s: unknown command type %d, %02X %08X\n", SD_DESC, (Command>>6)&0x3, cmd, Param);
break;
}
}
else printf("%s: SENDING CMD %04X TO NULL DEVICE\n", SD_DESC, val);
}
return;
case 0x002: PortSelect = (val & 0x040F) | (PortSelect & 0x0300); return;
case 0x004: Param = (Param & 0xFFFF0000) | val; return;
case 0x006: Param = (Param & 0x0000FFFF) | (val << 16); return;
case 0x008: StopAction = val & 0x0101; return;
case 0x00A: BlockCount16 = val; BlockCountInternal = val; return;
case 0x01C: IRQStatus &= (val | 0xFFFF0000); return;
case 0x01E: IRQStatus &= ((val << 16) | 0xFFFF); return;
case 0x020:
{
u32 oldmask = IRQMask;
IRQMask = (IRQMask & 0x8B7F0000) | (val & 0x031D);
UpdateIRQ(oldmask);
}
return;
case 0x022:
{
u32 oldmask = IRQMask;
IRQMask = (IRQMask & 0x0000031D) | ((val & 0x8B7F) << 16);
UpdateIRQ(oldmask);
if (!DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(24); // checkme
if (DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(25); // checkme
}
return;
case 0x024: SDClock = val & 0x03FF; return;
case 0x026:
BlockLen16 = val & 0x03FF;
if (BlockLen16 > 0x200) BlockLen16 = 0x200;
return;
case 0x028: SDOption = val & 0xC1FF; return;
case 0x030: WriteFIFO16(val); return;
case 0x034:
CardIRQCtl = val & 0x0305;
SetCardIRQ();
return;
case 0x036:
CardIRQStatus &= val;
return;
case 0x038:
CardIRQMask = val & 0xC007;
SetCardIRQ();
return;
case 0x0D8:
DataCtl = (val & 0x0022);
DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1);
return;
case 0x0E0:
if ((SoftReset & 0x0001) && !(val & 0x0001))
{
printf("%s: RESET\n", SD_DESC);
StopAction = 0;
memset(ResponseBuffer, 0, sizeof(ResponseBuffer));
IRQStatus = 0;
// TODO: ERROR_DETAIL_STATUS
SDClock &= ~0x0500;
SDOption = 0x40EE;
// TODO: CARD_IRQ_STAT
// TODO: FIFO16 shit
}
SoftReset = 0x0006 | (val & 0x0001);
return;
case 0x100:
Data32IRQ = (val & 0x1802) | (Data32IRQ & 0x0300);
if (val & (1<<10)) DataFIFO32->Clear();
DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1);
return;
case 0x104: BlockLen32 = val & 0x03FF; return;
case 0x108: BlockCount32 = val; return;
}
printf("unknown %s write %08X %04X\n", SD_DESC, addr, val);
}
void DSi_SDHost::WriteFIFO16(u16 val)
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 f = CurFIFO;
if (DataFIFO[f]->IsFull())
{
// TODO
printf("!!!! %s FIFO (16) FULL\n", SD_DESC);
return;
}
DataFIFO[f]->Write(val);
CheckTX();
}
void DSi_SDHost::WriteFIFO32(u32 val)
{
if (DataMode != 1) return;
if (DataFIFO32->IsFull())
{
// TODO
printf("!!!! %s FIFO (32) FULL\n", SD_DESC);
return;
}
DataFIFO32->Write(val);
CheckTX();
UpdateData32IRQ();
}
void DSi_SDHost::UpdateFIFO32()
{
// check whether we can drain FIFO32 into FIFO16, or vice versa
if (DataMode != 1) return;
if (!DataFIFO32->IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO16 INTO FIFO32 BUT IT CONTAINS SHIT ALREADY\n");
for (;;)
{
u32 f = CurFIFO;
if ((DataFIFO32->Level() << 2) >= BlockLen32) break;
if (DataFIFO[f]->IsEmpty()) break;
u32 val = DataFIFO[f]->Read();
val |= (DataFIFO[f]->Read() << 16);
DataFIFO32->Write(val);
}
UpdateData32IRQ();
if ((DataFIFO32->Level() << 2) >= BlockLen32)
{
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
}
}
void DSi_SDHost::CheckSwapFIFO()
{
// check whether we can swap the FIFOs
u32 f = CurFIFO;
bool cur_empty = (DataMode == 1) ? DataFIFO32->IsEmpty() : DataFIFO[f]->IsEmpty();
if (cur_empty && ((DataFIFO[f^1]->Level() << 1) >= BlockLen16))
{
CurFIFO ^= 1;
}
}
#define MMC_DESC (Internal?"NAND":"SDcard")
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const char* path) : DSi_SDDevice(host)
{
Internal = internal;
strncpy(FilePath, path, 1023); FilePath[1023] = '\0';
File = Platform::OpenLocalFile(path, "r+b");
if (!File)
{
if (internal)
{
// TODO: proper failure
printf("!! MMC file %s does not exist\n", path);
}
else
{
File = Platform::OpenLocalFile(path, "w+b");
}
}
CSR = 0x00000100; // checkme
// TODO: busy bit
// TODO: SDHC/SDXC bit
OCR = 0x80FF8000;
// TODO: customize based on card size etc
u8 csd_template[16] = {0x40, 0x40, 0x96, 0xE9, 0x7F, 0xDB, 0xF6, 0xDF, 0x01, 0x59, 0x0F, 0x2A, 0x01, 0x26, 0x90, 0x00};
memcpy(CSD, csd_template, 16);
// checkme
memset(SCR, 0, 8);
*(u32*)&SCR[0] = 0x012A0000;
memset(SSR, 0, 64);
BlockSize = 0;
RWAddress = 0;
RWCommand = 0;
}
DSi_MMCStorage::~DSi_MMCStorage()
{
if (File) fclose(File);
}
void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
{
if (CSR & (1<<5))
{
CSR &= ~(1<<5);
return SendACMD(cmd, param);
}
switch (cmd)
{
case 0: // reset/etc
Host->SendResponse(CSR, true);
return;
case 2:
case 10: // get CID
Host->SendResponse(*(u32*)&CID[12], false);
Host->SendResponse(*(u32*)&CID[8], false);
Host->SendResponse(*(u32*)&CID[4], false);
Host->SendResponse(*(u32*)&CID[0], true);
if (cmd == 2) SetState(0x02);
return;
case 3: // get/set RCA
if (Internal)
{
RCA = param >> 16;
Host->SendResponse(CSR|0x10000, true); // huh??
}
else
{
// TODO
printf("CMD3 on SD card: TODO\n");
Host->SendResponse((CSR & 0x1FFF) | ((CSR >> 6) & 0x2000) | ((CSR >> 8) & 0xC000) | (1 << 16), true);
}
return;
case 7: // select card (by RCA)
Host->SendResponse(CSR, true);
return;
case 8: // set voltage
Host->SendResponse(param, true);
return;
case 9: // get CSD
Host->SendResponse(*(u32*)&CSD[12], false);
Host->SendResponse(*(u32*)&CSD[8], false);
Host->SendResponse(*(u32*)&CSD[4], false);
Host->SendResponse(*(u32*)&CSD[0], true);
return;
case 12: // stop operation
SetState(0x04);
if (File) fflush(File);
RWCommand = 0;
Host->SendResponse(CSR, true);
return;
case 13: // get status
Host->SendResponse(CSR, true);
return;
case 16: // set block size
BlockSize = param;
if (BlockSize > 0x200)
{
// TODO! raise error
printf("!! SD/MMC: BAD BLOCK LEN %d\n", BlockSize);
BlockSize = 0x200;
}
SetState(0x04); // CHECKME
Host->SendResponse(CSR, true);
return;
case 18: // read multiple blocks
//printf("READ_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize);
RWAddress = param;
if (OCR & (1<<30))
{
RWAddress <<= 9;
BlockSize = 512;
}
RWCommand = 18;
Host->SendResponse(CSR, true);
ReadBlock(RWAddress);
RWAddress += BlockSize;
SetState(0x05);
return;
case 25: // write multiple blocks
//printf("WRITE_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize);
RWAddress = param;
if (OCR & (1<<30))
{
RWAddress <<= 9;
BlockSize = 512;
}
RWCommand = 25;
Host->SendResponse(CSR, true);
WriteBlock(RWAddress);
RWAddress += BlockSize;
SetState(0x04);
return;
case 55: // appcmd prefix
CSR |= (1<<5);
Host->SendResponse(CSR, true);
return;
}
printf("MMC: unknown CMD %d %08X\n", cmd, param);
}
void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
{
switch (cmd)
{
case 6: // set bus width (TODO?)
//printf("SET BUS WIDTH %08X\n", param);
Host->SendResponse(CSR, true);
return;
case 13: // get SSR
Host->SendResponse(CSR, true);
Host->DataRX(SSR, 64);
return;
case 41: // set operating conditions
// CHECKME:
// DSi boot2 sets this to 0x40100000 (hardcoded)
// then has two codepaths depending on whether bit30 did get set
// is it settable at all on the MMC? probably not.
if (Internal) param &= ~(1<<30);
OCR &= 0xBF000000;
OCR |= (param & 0x40FFFFFF);
Host->SendResponse(OCR, true);
SetState(0x01);
return;
case 42: // ???
Host->SendResponse(CSR, true);
return;
case 51: // get SCR
Host->SendResponse(CSR, true);
Host->DataRX(SCR, 8);
return;
}
printf("MMC: unknown ACMD %d %08X\n", cmd, param);
}
void DSi_MMCStorage::ContinueTransfer()
{
if (RWCommand == 0) return;
u32 len = 0;
switch (RWCommand)
{
case 18:
len = ReadBlock(RWAddress);
break;
case 25:
len = WriteBlock(RWAddress);
break;
}
RWAddress += len;
}
u32 DSi_MMCStorage::ReadBlock(u64 addr)
{
u32 len = BlockSize;
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (File)
{
fseek(File, addr, SEEK_SET);
fread(data, 1, len, File);
}
return Host->DataRX(data, len);
}
u32 DSi_MMCStorage::WriteBlock(u64 addr)
{
u32 len = BlockSize;
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (len = Host->DataTX(data, len))
{
if (File)
{
fseek(File, addr, SEEK_SET);
fwrite(data, 1, len, File);
}
}
return len;
}

154
src/DSi_SD.h Normal file
View File

@ -0,0 +1,154 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_SD_H
#define DSI_SD_H
#include <string.h>
#include "FIFO.h"
class DSi_SDDevice;
class DSi_SDHost
{
public:
DSi_SDHost(u32 num);
~DSi_SDHost();
void Reset();
void DoSavestate(Savestate* file);
static void FinishRX(u32 param);
static void FinishTX(u32 param);
void SendResponse(u32 val, bool last);
u32 DataRX(u8* data, u32 len);
u32 DataTX(u8* data, u32 len);
u32 GetTransferrableLen(u32 len);
void CheckRX();
void CheckTX();
bool TXReq;
void SetCardIRQ();
u16 Read(u32 addr);
void Write(u32 addr, u16 val);
u16 ReadFIFO16();
void WriteFIFO16(u16 val);
u32 ReadFIFO32();
void WriteFIFO32(u32 val);
void UpdateFIFO32();
void CheckSwapFIFO();
private:
u32 Num;
u16 PortSelect;
u16 SoftReset;
u16 SDClock;
u16 SDOption;
u32 IRQStatus; // IF
u32 IRQMask; // ~IE
u16 CardIRQStatus;
u16 CardIRQMask;
u16 CardIRQCtl;
u16 DataCtl;
u16 Data32IRQ;
u32 DataMode; // 0=16bit 1=32bit
u16 BlockCount16, BlockCount32, BlockCountInternal;
u16 BlockLen16, BlockLen32;
u16 StopAction;
u16 Command;
u32 Param;
u16 ResponseBuffer[8];
FIFO<u16>* DataFIFO[2];
u32 CurFIFO; // FIFO accessible for read/write
FIFO<u32>* DataFIFO32;
DSi_SDDevice* Ports[2];
void UpdateData32IRQ();
void ClearIRQ(u32 irq);
void SetIRQ(u32 irq);
void UpdateIRQ(u32 oldmask);
};
class DSi_SDDevice
{
public:
DSi_SDDevice(DSi_SDHost* host) { Host = host; IRQ = false; }
~DSi_SDDevice() {}
virtual void SendCMD(u8 cmd, u32 param) = 0;
virtual void ContinueTransfer() = 0;
bool IRQ;
protected:
DSi_SDHost* Host;
};
class DSi_MMCStorage : public DSi_SDDevice
{
public:
DSi_MMCStorage(DSi_SDHost* host, bool internal, const char* path);
~DSi_MMCStorage();
void SetCID(u8* cid) { memcpy(CID, cid, 16); }
void SendCMD(u8 cmd, u32 param);
void SendACMD(u8 cmd, u32 param);
void ContinueTransfer();
private:
bool Internal;
char FilePath[1024];
FILE* File;
u8 CID[16];
u8 CSD[16];
u32 CSR;
u32 OCR;
u32 RCA;
u8 SCR[8];
u8 SSR[64];
u32 BlockSize;
u64 RWAddress;
u32 RWCommand;
void SetState(u32 state) { CSR &= ~(0xF << 9); CSR |= (state << 9); }
u32 ReadBlock(u64 addr);
u32 WriteBlock(u64 addr);
};
#endif // DSI_SD_H

231
src/DSi_SPI_TSC.cpp Normal file
View File

@ -0,0 +1,231 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "SPI.h"
#include "DSi_SPI_TSC.h"
namespace DSi_SPI_TSC
{
u32 DataPos;
u8 Index;
u8 Bank;
u8 Data;
u8 Bank3Regs[0x80];
u8 TSCMode;
u16 TouchX, TouchY;
bool Init()
{
return true;
}
void DeInit()
{
}
void Reset()
{
DataPos = 0;
Bank = 0;
Index = 0;
Data = 0;
memset(Bank3Regs, 0, 0x80);
Bank3Regs[0x02] = 0x18;
Bank3Regs[0x03] = 0x87;
Bank3Regs[0x04] = 0x22;
Bank3Regs[0x05] = 0x04;
Bank3Regs[0x06] = 0x20;
Bank3Regs[0x09] = 0x40;
Bank3Regs[0x0E] = 0xAD;
Bank3Regs[0x0F] = 0xA0;
Bank3Regs[0x10] = 0x88;
Bank3Regs[0x11] = 0x81;
TSCMode = 0x01; // DSi mode
}
void DoSavestate(Savestate* file)
{
/*file->Section("SPTi");
file->Var32(&DataPos);
file->Var8(&ControlByte);
file->Var8(&Data);
file->Var16(&ConvResult);*/
// TODO!!
}
void SetTouchCoords(u16 x, u16 y)
{
if (TSCMode == 0x00)
{
if (y == 0xFFF) NDS::KeyInput |= (1 << (16+6));
else NDS::KeyInput &= ~(1 << (16+6));
return SPI_TSC::SetTouchCoords(x, y);
}
TouchX = x;
TouchY = y;
u8 oldpress = Bank3Regs[0x0E] & 0x01;
if (y == 0xFFF)
{
// released
// TODO: GBAtek says it can also be 1000 or 3000??
TouchX = 0x7000;
TouchY = 0x7000;
Bank3Regs[0x09] = 0x40;
//Bank3Regs[0x09] &= ~0x80;
Bank3Regs[0x0E] |= 0x01;
}
else
{
// pressed
TouchX <<= 4;
TouchY <<= 4;
Bank3Regs[0x09] = 0x80;
//Bank3Regs[0x09] |= 0x80;
Bank3Regs[0x0E] &= ~0x01;
}
if (oldpress ^ (Bank3Regs[0x0E] & 0x01))
{
TouchX |= 0x8000;
TouchY |= 0x8000;
}
}
void MicInputFrame(s16* data, int samples)
{
if (TSCMode == 0x00) return SPI_TSC::MicInputFrame(data, samples);
// otherwise we don't handle mic input
// TODO: handle it where it needs to be
}
u8 Read()
{
if (TSCMode == 0x00) return SPI_TSC::Read();
return Data;
}
void Write(u8 val, u32 hold)
{
if (TSCMode == 0x00) return SPI_TSC::Write(val, hold);
#define READWRITE(var) { if (Index & 0x01) Data = var; else var = val; }
if (DataPos == 0)
{
Index = val;
}
else
{
u8 id = Index >> 1;
if (id == 0)
{
READWRITE(Bank);
}
else if (Bank == 0x03)
{
if (Index & 0x01) Data = Bank3Regs[id];
else
{
if (id == 0x0D || id == 0x0E)
Bank3Regs[id] = (Bank3Regs[id] & 0x03) | (val & 0xFC);
}
}
else if ((Bank == 0xFC) && (Index & 0x01))
{
if (id < 0x0B)
{
// X coordinates
if (id & 0x01) Data = TouchX >> 8;
else Data = TouchX & 0xFF;
TouchX &= 0x7FFF;
}
else if (id < 0x15)
{
// Y coordinates
if (id & 0x01) Data = TouchY >> 8;
else Data = TouchY & 0xFF;
TouchY &= 0x7FFF; // checkme
}
else
{
// whatever (TODO)
Data = 0;
}
}
else if (Bank == 0xFF)
{
if (id == 0x05)
{
// TSC mode register
// 01: normal (DSi) mode
// 00: compatibility (DS) mode
if (Index & 0x01) Data = TSCMode;
else
{
TSCMode = val;
if (TSCMode == 0x00)
{
printf("DSi_SPI_TSC: DS-compatibility mode\n");
DataPos = 0;
NDS::KeyInput |= (1 << (16+6));
return;
}
}
}
}
else
{
printf("DSi_SPI_TSC: unknown IO, bank=%02X, index=%02X (%02X %s)\n", Bank, Index, Index>>1, (Index&1)?"read":"write");
}
Index += (1<<1); // increment index
}
if (hold) DataPos++;
else DataPos = 0;
}
}

40
src/DSi_SPI_TSC.h Normal file
View File

@ -0,0 +1,40 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_SPI_TSC
#define DSI_SPI_TSC
namespace DSi_SPI_TSC
{
extern u32 DataPos;
bool Init();
void DeInit();
void Reset();
void DoSavestate(Savestate* file);
void SetTouchCoords(u16 x, u16 y);
void MicInputFrame(s16* data, int samples);
u8 Read();
void Write(u8 val, u32 hold);
}
#endif // DSI_SPI_TSC

View File

@ -89,6 +89,15 @@ public:
return Entries[ReadPos]; return Entries[ReadPos];
} }
T Peek(u32 offset)
{
u32 pos = ReadPos + offset;
if (pos >= NumEntries)
pos -= NumEntries;
return Entries[pos];
}
u32 Level() { return NumOccupied; } u32 Level() { return NumOccupied; }
bool IsEmpty() { return NumOccupied == 0; } bool IsEmpty() { return NumOccupied == 0; }
bool IsFull() { return NumOccupied >= NumEntries; } bool IsFull() { return NumOccupied >= NumEntries; }

View File

@ -21,6 +21,7 @@
#include "NDS.h" #include "NDS.h"
#include "GPU.h" #include "GPU.h"
namespace GPU namespace GPU
{ {

View File

@ -33,6 +33,9 @@
#include "AREngine.h" #include "AREngine.h"
#include "Platform.h" #include "Platform.h"
#include "DSi.h"
#include "DSi_SPI_TSC.h"
namespace NDS namespace NDS
{ {
@ -59,6 +62,8 @@ namespace NDS
// //
// timings for GBA slot and wifi are set up at runtime // timings for GBA slot and wifi are set up at runtime
int ConsoleType;
u8 ARM9MemTimings[0x40000][4]; u8 ARM9MemTimings[0x40000][4];
u8 ARM7MemTimings[0x20000][4]; u8 ARM7MemTimings[0x20000][4];
@ -88,7 +93,8 @@ u32 CPUStop;
u8 ARM9BIOS[0x1000]; u8 ARM9BIOS[0x1000];
u8 ARM7BIOS[0x4000]; u8 ARM7BIOS[0x4000];
u8 MainRAM[MAIN_RAM_SIZE]; u8 MainRAM[0x1000000];
u32 MainRAMMask;
u8 SharedWRAM[0x8000]; u8 SharedWRAM[0x8000];
u8 WRAMCnt; u8 WRAMCnt;
@ -108,6 +114,7 @@ u8 ROMSeed1[2*8];
// IO shit // IO shit
u32 IME[2]; u32 IME[2];
u32 IE[2], IF[2]; u32 IE[2], IF[2];
u32 IE2, IF2;
u8 PostFlag9; u8 PostFlag9;
u8 PostFlag7; u8 PostFlag7;
@ -181,6 +188,8 @@ bool Init()
if (!RTC::Init()) return false; if (!RTC::Init()) return false;
if (!Wifi::Init()) return false; if (!Wifi::Init()) return false;
if (!DSi::Init()) return false;
if (!AREngine::Init()) return false; if (!AREngine::Init()) return false;
return true; return true;
@ -205,6 +214,8 @@ void DeInit()
RTC::DeInit(); RTC::DeInit();
Wifi::DeInit(); Wifi::DeInit();
DSi::DeInit();
AREngine::DeInit(); AREngine::DeInit();
} }
@ -238,7 +249,11 @@ void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq,
ARM9MemTimings[i][3] = S32; ARM9MemTimings[i][3] = S32;
} }
ARM9->UpdateRegionTimings(addrstart<<14, addrend<<14); addrstart <<= 14;
addrend <<= 14;
if (!addrend) addrend = 0xFFFFFFFF;
ARM9->UpdateRegionTimings(addrstart, addrend);
} }
void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq) void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq)
@ -283,6 +298,8 @@ void InitTimings()
// TODO: +3c nonseq waitstate doesn't apply to DMA! // TODO: +3c nonseq waitstate doesn't apply to DMA!
// but of course mainRAM always gets 8c nonseq waitstate // but of course mainRAM always gets 8c nonseq waitstate
// TODO: DSi-specific timings!!
SetARM9RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1 + 3, 1); // void SetARM9RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1 + 3, 1); // void
SetARM9RegionTimings(0xFFFF0000, 0xFFFFFFFF, 32, 1 + 3, 1); // BIOS SetARM9RegionTimings(0xFFFF0000, 0xFFFFFFFF, 32, 1 + 3, 1); // BIOS
@ -308,6 +325,12 @@ void InitTimings()
void SetupDirectBoot() void SetupDirectBoot()
{ {
if (ConsoleType == 1)
{
printf("!! DIRECT BOOT NOT SUPPORTED IN DSI MODE\n");
return;
}
u32 bootparams[8]; u32 bootparams[8];
memcpy(bootparams, &NDSCart::CartROM[0x20], 8*4); memcpy(bootparams, &NDSCart::CartROM[0x20], 8*4);
@ -416,6 +439,12 @@ void Reset()
RunningGame = false; RunningGame = false;
LastSysClockCycles = 0; LastSysClockCycles = 0;
memset(ARM9BIOS, 0, 0x1000);
memset(ARM7BIOS, 0, 0x4000);
// DS BIOSes are always loaded, even in DSi mode
// we need them for DS-compatible mode
f = Platform::OpenLocalFile(Config::BIOS9Path, "rb"); f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
if (!f) if (!f)
{ {
@ -450,8 +479,19 @@ void Reset()
fclose(f); fclose(f);
} }
// TODO for later: configure this when emulating a DSi if (ConsoleType == 1)
{
DSi::LoadBIOS();
DSi::LoadNAND();
ARM9ClockShift = 2;
MainRAMMask = 0xFFFFFF;
}
else
{
ARM9ClockShift = 1; ARM9ClockShift = 1;
MainRAMMask = 0x3FFFFF;
}
ARM9Timestamp = 0; ARM9Target = 0; ARM9Timestamp = 0; ARM9Target = 0;
ARM7Timestamp = 0; ARM7Target = 0; ARM7Timestamp = 0; ARM7Target = 0;
@ -459,14 +499,14 @@ void Reset()
InitTimings(); InitTimings();
memset(MainRAM, 0, MAIN_RAM_SIZE); memset(MainRAM, 0, 0x1000000);
memset(SharedWRAM, 0, 0x8000); memset(SharedWRAM, 0, 0x8000);
memset(ARM7WRAM, 0, 0x10000); memset(ARM7WRAM, 0, 0x10000);
MapSharedWRAM(0); MapSharedWRAM(0);
ExMemCnt[0] = 0; ExMemCnt[0] = 0x4000;
ExMemCnt[1] = 0; ExMemCnt[1] = 0x4000;
memset(ROMSeed0, 0, 2*8); memset(ROMSeed0, 0, 2*8);
memset(ROMSeed1, 0, 2*8); memset(ROMSeed1, 0, 2*8);
SetGBASlotTimings(); SetGBASlotTimings();
@ -477,6 +517,8 @@ void Reset()
IME[1] = 0; IME[1] = 0;
IE[1] = 0; IE[1] = 0;
IF[1] = 0; IF[1] = 0;
IE2 = 0;
IF2 = 0;
PostFlag9 = 0x00; PostFlag9 = 0x00;
PostFlag7 = 0x00; PostFlag7 = 0x00;
@ -527,6 +569,12 @@ void Reset()
RTC::Reset(); RTC::Reset();
Wifi::Reset(); Wifi::Reset();
if (ConsoleType == 1)
{
DSi::Reset();
KeyInput &= ~(1 << (16+6));
}
AREngine::Reset(); AREngine::Reset();
} }
@ -635,6 +683,11 @@ bool DoSavestate(Savestate* file)
{ {
file->Section("NDSG"); file->Section("NDSG");
// TODO:
// * do something for bool's (sizeof=1)
// * do something for 'loading DSi-mode savestate in DS mode' and vice-versa
// * add IE2/IF2 there
file->VarArray(MainRAM, 0x400000); file->VarArray(MainRAM, 0x400000);
file->VarArray(SharedWRAM, 0x8000); file->VarArray(SharedWRAM, 0x8000);
file->VarArray(ARM7WRAM, 0x10000); file->VarArray(ARM7WRAM, 0x10000);
@ -737,6 +790,11 @@ bool DoSavestate(Savestate* file)
return true; return true;
} }
void SetConsoleType(int type)
{
ConsoleType = type;
}
bool LoadROM(const char* path, const char* sram, bool direct) bool LoadROM(const char* path, const char* sram, bool direct)
{ {
if (NDSCart::LoadROM(path, sram, direct)) if (NDSCart::LoadROM(path, sram, direct))
@ -848,6 +906,7 @@ u32 RunFrame()
if (!(CPUStop & 0x80000000)) DMAs[1]->Run(); if (!(CPUStop & 0x80000000)) DMAs[1]->Run();
if (!(CPUStop & 0x80000000)) DMAs[2]->Run(); if (!(CPUStop & 0x80000000)) DMAs[2]->Run();
if (!(CPUStop & 0x80000000)) DMAs[3]->Run(); if (!(CPUStop & 0x80000000)) DMAs[3]->Run();
if (ConsoleType == 1) DSi::RunNDMAs(0);
} }
else else
{ {
@ -870,6 +929,7 @@ u32 RunFrame()
DMAs[5]->Run(); DMAs[5]->Run();
DMAs[6]->Run(); DMAs[6]->Run();
DMAs[7]->Run(); DMAs[7]->Run();
if (ConsoleType == 1) DSi::RunNDMAs(1);
} }
else else
{ {
@ -953,14 +1013,28 @@ void CancelEvent(u32 id)
void TouchScreen(u16 x, u16 y) void TouchScreen(u16 x, u16 y)
{ {
KeyInput &= ~(1<<22); if (ConsoleType == 1)
{
DSi_SPI_TSC::SetTouchCoords(x, y);
}
else
{
SPI_TSC::SetTouchCoords(x, y); SPI_TSC::SetTouchCoords(x, y);
KeyInput &= ~(1 << (16+6));
}
} }
void ReleaseScreen() void ReleaseScreen()
{ {
KeyInput |= (1<<22); if (ConsoleType == 1)
{
DSi_SPI_TSC::SetTouchCoords(0x000, 0xFFF);
}
else
{
SPI_TSC::SetTouchCoords(0x000, 0xFFF); SPI_TSC::SetTouchCoords(0x000, 0xFFF);
KeyInput |= (1 << (16+6));
}
} }
@ -1093,6 +1167,8 @@ void UpdateIRQ(u32 cpu)
if (IME[cpu] & 0x1) if (IME[cpu] & 0x1)
{ {
arm->IRQ = IE[cpu] & IF[cpu]; arm->IRQ = IE[cpu] & IF[cpu];
if ((ConsoleType == 1) && cpu)
arm->IRQ |= (IE2 & IF2);
} }
else else
{ {
@ -1112,6 +1188,18 @@ void ClearIRQ(u32 cpu, u32 irq)
UpdateIRQ(cpu); UpdateIRQ(cpu);
} }
void SetIRQ2(u32 irq)
{
IF2 |= (1 << irq);
UpdateIRQ(1);
}
void ClearIRQ2(u32 irq)
{
IF2 &= ~(1 << irq);
UpdateIRQ(1);
}
bool HaltInterrupted(u32 cpu) bool HaltInterrupted(u32 cpu)
{ {
if (cpu == 0) if (cpu == 0)
@ -1159,6 +1247,7 @@ void GXFIFOStall()
DMAs[1]->StallIfRunning(); DMAs[1]->StallIfRunning();
DMAs[2]->StallIfRunning(); DMAs[2]->StallIfRunning();
DMAs[3]->StallIfRunning(); DMAs[3]->StallIfRunning();
if (ConsoleType == 1) DSi::StallNDMAs();
} }
} }
@ -1296,6 +1385,7 @@ void NocashPrint(u32 ncpu, u32 addr)
void MonitorARM9Jump(u32 addr) void MonitorARM9Jump(u32 addr)
{ {
// checkme: can the entrypoint addr be THUMB? // checkme: can the entrypoint addr be THUMB?
// also TODO: make it work in DSi mode
if ((!RunningGame) && NDSCart::CartROM) if ((!RunningGame) && NDSCart::CartROM)
{ {
@ -1372,6 +1462,29 @@ void RunTimers(u32 cpu)
// matching NDMA modes for DSi
const u32 NDMAModes[] =
{
// ARM9
0x10, // immediate
0x06, // VBlank
0x07, // HBlank
0x08, // scanline start
0x09, // mainmem FIFO
0x04, // DS cart slot
0xFF, // GBA cart slot
0x0A, // GX FIFO
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// ARM7
0x30, // immediate
0x26, // VBlank
0x24, // DS cart slot
0xFF, // wifi / GBA cart slot (TODO)
};
bool DMAsInMode(u32 cpu, u32 mode) bool DMAsInMode(u32 cpu, u32 mode)
{ {
cpu <<= 2; cpu <<= 2;
@ -1379,6 +1492,13 @@ bool DMAsInMode(u32 cpu, u32 mode)
if (DMAs[cpu+1]->IsInMode(mode)) return true; if (DMAs[cpu+1]->IsInMode(mode)) return true;
if (DMAs[cpu+2]->IsInMode(mode)) return true; if (DMAs[cpu+2]->IsInMode(mode)) return true;
if (DMAs[cpu+3]->IsInMode(mode)) return true; if (DMAs[cpu+3]->IsInMode(mode)) return true;
if (ConsoleType == 1)
{
cpu >>= 2;
return DSi::NDMAsInMode(cpu, NDMAModes[mode]);
}
return false; return false;
} }
@ -1389,6 +1509,10 @@ bool DMAsRunning(u32 cpu)
if (DMAs[cpu+1]->IsRunning()) return true; if (DMAs[cpu+1]->IsRunning()) return true;
if (DMAs[cpu+2]->IsRunning()) return true; if (DMAs[cpu+2]->IsRunning()) return true;
if (DMAs[cpu+3]->IsRunning()) return true; if (DMAs[cpu+3]->IsRunning()) return true;
if (ConsoleType == 1)
{
if (DSi::NDMAsRunning(cpu>>2)) return true;
}
return false; return false;
} }
@ -1399,6 +1523,12 @@ void CheckDMAs(u32 cpu, u32 mode)
DMAs[cpu+1]->StartIfNeeded(mode); DMAs[cpu+1]->StartIfNeeded(mode);
DMAs[cpu+2]->StartIfNeeded(mode); DMAs[cpu+2]->StartIfNeeded(mode);
DMAs[cpu+3]->StartIfNeeded(mode); DMAs[cpu+3]->StartIfNeeded(mode);
if (ConsoleType == 1)
{
cpu >>= 2;
DSi::CheckNDMAs(cpu, NDMAModes[mode]);
}
} }
void StopDMAs(u32 cpu, u32 mode) void StopDMAs(u32 cpu, u32 mode)
@ -1408,6 +1538,12 @@ void StopDMAs(u32 cpu, u32 mode)
DMAs[cpu+1]->StopIfNeeded(mode); DMAs[cpu+1]->StopIfNeeded(mode);
DMAs[cpu+2]->StopIfNeeded(mode); DMAs[cpu+2]->StopIfNeeded(mode);
DMAs[cpu+3]->StopIfNeeded(mode); DMAs[cpu+3]->StopIfNeeded(mode);
if (ConsoleType == 1)
{
cpu >>= 2;
DSi::StopNDMAs(cpu, NDMAModes[mode]);
}
} }
@ -1600,12 +1736,12 @@ void debug(u32 param)
printf("ARM7 PC=%08X LR=%08X %08X\n", ARM7->R[15], ARM7->R[14], ARM7->R_IRQ[1]); printf("ARM7 PC=%08X LR=%08X %08X\n", ARM7->R[15], ARM7->R[14], ARM7->R_IRQ[1]);
printf("ARM9 IME=%08X IE=%08X IF=%08X\n", IME[0], IE[0], IF[0]); printf("ARM9 IME=%08X IE=%08X IF=%08X\n", IME[0], IE[0], IF[0]);
printf("ARM7 IME=%08X IE=%08X IF=%08X\n", IME[1], IE[1], IF[1]); printf("ARM7 IME=%08X IE=%08X IF=%08X IE2=%04X IF2=%04X\n", IME[1], IE[1], IF[1], IE2, IF2);
//for (int i = 0; i < 9; i++) //for (int i = 0; i < 9; i++)
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]); // printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
FILE* /*FILE*
shit = fopen("debug/party.bin", "wb"); shit = fopen("debug/party.bin", "wb");
fwrite(ARM9->ITCM, 0x8000, 1, shit); fwrite(ARM9->ITCM, 0x8000, 1, shit);
for (u32 i = 0x02000000; i < 0x02400000; i+=4) for (u32 i = 0x02000000; i < 0x02400000; i+=4)
@ -1618,6 +1754,21 @@ void debug(u32 param)
u32 val = ARM7Read32(i); u32 val = ARM7Read32(i);
fwrite(&val, 4, 1, shit); fwrite(&val, 4, 1, shit);
} }
fclose(shit);*/
FILE*
/*shit = fopen("debug/dump9.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
u32 val = DSi::ARM9Read32(i);
fwrite(&val, 4, 1, shit);
}
fclose(shit);*/
shit = fopen("debug/dump7_2.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
u32 val = DSi::ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
fclose(shit); fclose(shit);
} }
@ -1633,7 +1784,7 @@ u8 ARM9Read8(u32 addr)
switch (addr & 0xFF000000) switch (addr & 0xFF000000)
{ {
case 0x02000000: case 0x02000000:
return *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)]; return *(u8*)&MainRAM[addr & MainRAMMask];
case 0x03000000: case 0x03000000:
if (SWRAM_ARM9) if (SWRAM_ARM9)
@ -1698,7 +1849,7 @@ u16 ARM9Read16(u32 addr)
switch (addr & 0xFF000000) switch (addr & 0xFF000000)
{ {
case 0x02000000: case 0x02000000:
return *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)]; return *(u16*)&MainRAM[addr & MainRAMMask];
case 0x03000000: case 0x03000000:
if (SWRAM_ARM9) if (SWRAM_ARM9)
@ -1763,7 +1914,7 @@ u32 ARM9Read32(u32 addr)
switch (addr & 0xFF000000) switch (addr & 0xFF000000)
{ {
case 0x02000000: case 0x02000000:
return *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)]; return *(u32*)&MainRAM[addr & MainRAMMask];
case 0x03000000: case 0x03000000:
if (SWRAM_ARM9) if (SWRAM_ARM9)
@ -1823,7 +1974,7 @@ void ARM9Write8(u32 addr, u8 val)
switch (addr & 0xFF000000) switch (addr & 0xFF000000)
{ {
case 0x02000000: case 0x02000000:
*(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val; *(u8*)&MainRAM[addr & MainRAMMask] = val;
return; return;
case 0x03000000: case 0x03000000:
@ -1873,7 +2024,7 @@ void ARM9Write16(u32 addr, u16 val)
switch (addr & 0xFF000000) switch (addr & 0xFF000000)
{ {
case 0x02000000: case 0x02000000:
*(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val; *(u16*)&MainRAM[addr & MainRAMMask] = val;
return; return;
case 0x03000000: case 0x03000000:
@ -1939,7 +2090,7 @@ void ARM9Write32(u32 addr, u32 val)
switch (addr & 0xFF000000) switch (addr & 0xFF000000)
{ {
case 0x02000000: case 0x02000000:
*(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val; *(u32*)&MainRAM[addr & MainRAMMask] = val;
return ; return ;
case 0x03000000: case 0x03000000:
@ -2007,7 +2158,7 @@ bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
{ {
case 0x02000000: case 0x02000000:
region->Mem = MainRAM; region->Mem = MainRAM;
region->Mask = MAIN_RAM_SIZE-1; region->Mask = MainRAMMask;
return true; return true;
case 0x03000000: case 0x03000000:
@ -2037,7 +2188,8 @@ u8 ARM7Read8(u32 addr)
{ {
if (addr < 0x00004000) if (addr < 0x00004000)
{ {
if (ARM7->R[15] >= 0x4000) // TODO: check the boundary? is it 4000 or higher on regular DS?
if (ARM7->R[15] >= 0x00004000)
return 0xFF; return 0xFF;
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
return 0xFF; return 0xFF;
@ -2049,7 +2201,7 @@ u8 ARM7Read8(u32 addr)
{ {
case 0x02000000: case 0x02000000:
case 0x02800000: case 0x02800000:
return *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)]; return *(u8*)&MainRAM[addr & MainRAMMask];
case 0x03000000: case 0x03000000:
if (SWRAM_ARM7) if (SWRAM_ARM7)
@ -2097,7 +2249,7 @@ u16 ARM7Read16(u32 addr)
{ {
if (addr < 0x00004000) if (addr < 0x00004000)
{ {
if (ARM7->R[15] >= 0x4000) if (ARM7->R[15] >= 0x00004000)
return 0xFFFF; return 0xFFFF;
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
return 0xFFFF; return 0xFFFF;
@ -2109,7 +2261,7 @@ u16 ARM7Read16(u32 addr)
{ {
case 0x02000000: case 0x02000000:
case 0x02800000: case 0x02800000:
return *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)]; return *(u16*)&MainRAM[addr & MainRAMMask];
case 0x03000000: case 0x03000000:
if (SWRAM_ARM7) if (SWRAM_ARM7)
@ -2164,7 +2316,7 @@ u32 ARM7Read32(u32 addr)
{ {
if (addr < 0x00004000) if (addr < 0x00004000)
{ {
if (ARM7->R[15] >= 0x4000) if (ARM7->R[15] >= 0x00004000)
return 0xFFFFFFFF; return 0xFFFFFFFF;
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
return 0xFFFFFFFF; return 0xFFFFFFFF;
@ -2176,7 +2328,7 @@ u32 ARM7Read32(u32 addr)
{ {
case 0x02000000: case 0x02000000:
case 0x02800000: case 0x02800000:
return *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)]; return *(u32*)&MainRAM[addr & MainRAMMask];
case 0x03000000: case 0x03000000:
if (SWRAM_ARM7) if (SWRAM_ARM7)
@ -2233,7 +2385,7 @@ void ARM7Write8(u32 addr, u8 val)
{ {
case 0x02000000: case 0x02000000:
case 0x02800000: case 0x02800000:
*(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val; *(u8*)&MainRAM[addr & MainRAMMask] = val;
return; return;
case 0x03000000: case 0x03000000:
@ -2292,7 +2444,7 @@ void ARM7Write16(u32 addr, u16 val)
{ {
case 0x02000000: case 0x02000000:
case 0x02800000: case 0x02800000:
*(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val; *(u16*)&MainRAM[addr & MainRAMMask] = val;
return; return;
case 0x03000000: case 0x03000000:
@ -2361,7 +2513,7 @@ void ARM7Write32(u32 addr, u32 val)
{ {
case 0x02000000: case 0x02000000:
case 0x02800000: case 0x02800000:
*(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val; *(u32*)&MainRAM[addr & MainRAMMask] = val;
return; return;
case 0x03000000: case 0x03000000:
@ -2433,7 +2585,7 @@ bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region)
case 0x02000000: case 0x02000000:
case 0x02800000: case 0x02800000:
region->Mem = MainRAM; region->Mem = MainRAM;
region->Mask = MAIN_RAM_SIZE-1; region->Mask = MainRAMMask;
return true; return true;
case 0x03000000: case 0x03000000:

View File

@ -42,6 +42,11 @@ enum
Event_Div, Event_Div,
Event_Sqrt, Event_Sqrt,
// DSi
Event_DSi_SDMMCTransfer,
Event_DSi_SDIOTransfer,
Event_DSi_NWifi,
Event_MAX Event_MAX
}; };
@ -74,12 +79,42 @@ enum
IRQ_IPCSync, IRQ_IPCSync,
IRQ_IPCSendDone, IRQ_IPCSendDone,
IRQ_IPCRecv, IRQ_IPCRecv,
IRQ_CartSendDone, IRQ_CartSendDone, // TODO: less misleading name
IRQ_CartIREQMC, IRQ_CartIREQMC, // IRQ triggered by game cart (example: Pokémon Typing Adventure, BT controller)
IRQ_GXFIFO, IRQ_GXFIFO,
IRQ_LidOpen, IRQ_LidOpen,
IRQ_SPI, IRQ_SPI,
IRQ_Wifi IRQ_Wifi,
// DSi IRQs
IRQ_DSi_DSP = 24,
IRQ_DSi_Camera,
IRQ_DSi_Unk26,
IRQ_DSi_Unk27,
IRQ_DSi_NDMA0,
IRQ_DSi_NDMA1,
IRQ_DSi_NDMA2,
IRQ_DSi_NDMA3,
};
enum
{
// DSi ARM7-side IE2/IF2
IRQ2_DSi_GPIO18_0 = 0,
IRQ2_DSi_GPIO18_1,
IRQ2_DSi_GPIO18_2,
IRQ2_DSi_Unused3,
IRQ2_DSi_GPIO33_0,
IRQ2_DSi_Headphone,
IRQ2_DSi_PowerButton,
IRQ2_DSi_GPIO33_3, // "sound enable input"
IRQ2_DSi_SDMMC,
IRQ2_DSi_SD_Data1,
IRQ2_DSi_SDIO,
IRQ2_DSi_SDIO_Data1,
IRQ2_DSi_AES,
IRQ2_DSi_I2C,
IRQ2_DSi_MicExt
}; };
typedef struct typedef struct
@ -98,6 +133,8 @@ typedef struct
} MemRegion; } MemRegion;
extern int ConsoleType;
extern u8 ARM9MemTimings[0x40000][4]; extern u8 ARM9MemTimings[0x40000][4];
extern u8 ARM7MemTimings[0x20000][4]; extern u8 ARM7MemTimings[0x20000][4];
@ -105,12 +142,15 @@ extern u64 ARM9Timestamp, ARM9Target;
extern u64 ARM7Timestamp, ARM7Target; extern u64 ARM7Timestamp, ARM7Target;
extern u32 ARM9ClockShift; extern u32 ARM9ClockShift;
// hax
extern u32 IME[2]; extern u32 IME[2];
extern u32 IE[2]; extern u32 IE[2];
extern u32 IF[2]; extern u32 IF[2];
extern u32 IE2;
extern u32 IF2;
extern Timer Timers[8]; extern Timer Timers[8];
extern u32 CPUStop;
extern u16 PowerControl9; extern u16 PowerControl9;
extern u16 ExMemCnt[2]; extern u16 ExMemCnt[2];
@ -119,10 +159,12 @@ extern u8 ROMSeed1[2*8];
extern u8 ARM9BIOS[0x1000]; extern u8 ARM9BIOS[0x1000];
extern u8 ARM7BIOS[0x4000]; extern u8 ARM7BIOS[0x4000];
extern u16 ARM7BIOSProt;
#define MAIN_RAM_SIZE 0x400000 extern u8 MainRAM[0x1000000];
extern u32 MainRAMMask;
extern u8 MainRAM[MAIN_RAM_SIZE]; extern u32 KeyInput;
bool Init(); bool Init();
void DeInit(); void DeInit();
@ -134,6 +176,9 @@ bool DoSavestate(Savestate* file);
void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq); void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq); void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
// 0=DS 1=DSi
void SetConsoleType(int type);
bool LoadROM(const char* path, const char* sram, bool direct); bool LoadROM(const char* path, const char* sram, bool direct);
bool LoadGBAROM(const char* path, const char* sram); bool LoadGBAROM(const char* path, const char* sram);
void LoadBIOS(); void LoadBIOS();
@ -161,8 +206,11 @@ void Halt();
void MapSharedWRAM(u8 val); void MapSharedWRAM(u8 val);
void UpdateIRQ(u32 cpu);
void SetIRQ(u32 cpu, u32 irq); void SetIRQ(u32 cpu, u32 irq);
void ClearIRQ(u32 cpu, u32 irq); void ClearIRQ(u32 cpu, u32 irq);
void SetIRQ2(u32 irq);
void ClearIRQ2(u32 irq);
bool HaltInterrupted(u32 cpu); bool HaltInterrupted(u32 cpu);
void StopCPU(u32 cpu, u32 mask); void StopCPU(u32 cpu, u32 mask);
void ResumeCPU(u32 cpu, u32 mask); void ResumeCPU(u32 cpu, u32 mask);

View File

@ -19,9 +19,11 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "NDS.h" #include "NDS.h"
#include "DSi.h"
#include "NDSCart.h" #include "NDSCart.h"
#include "ARM.h" #include "ARM.h"
#include "CRC32.h" #include "CRC32.h"
#include "DSi_AES.h"
#include "Platform.h" #include "Platform.h"
#include "ROMList.h" #include "ROMList.h"
@ -474,6 +476,7 @@ u32 CartROMSize;
u32 CartCRC; u32 CartCRC;
u32 CartID; u32 CartID;
bool CartIsHomebrew; bool CartIsHomebrew;
bool CartIsDSi;
u32 CmdEncMode; u32 CmdEncMode;
u32 DataEncMode; u32 DataEncMode;
@ -556,8 +559,12 @@ void Key1_ApplyKeycode(u32* keycode, u32 mod)
} }
} }
void Key1_InitKeycode(u32 idcode, u32 level, u32 mod) 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 memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax
u32 keycode[3] = {idcode, idcode>>1, idcode<<1}; u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
@ -593,6 +600,15 @@ void Key2_Encrypt(u8* data, u32 len)
} }
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);
}
bool Init() bool Init()
{ {
if (!NDSCart_SRAM::Init()) return false; if (!NDSCart_SRAM::Init()) return false;
@ -611,32 +627,19 @@ void DeInit()
void Reset() void Reset()
{ {
SPICnt = 0;
ROMCnt = 0;
memset(ROMCommand, 0, 8);
ROMDataOut = 0;
Key2_X = 0;
Key2_Y = 0;
memset(DataOut, 0, 0x4000);
DataOutPos = 0;
DataOutLen = 0;
CartInserted = false; CartInserted = false;
if (CartROM) delete[] CartROM; if (CartROM) delete[] CartROM;
CartROM = NULL; CartROM = NULL;
CartROMSize = 0; CartROMSize = 0;
CartID = 0; CartID = 0;
CartIsHomebrew = false; CartIsHomebrew = false;
CartIsDSi = false;
ROMCommandHandler = NULL; ROMCommandHandler = NULL;
CmdEncMode = 0;
DataEncMode = 0;
NDSCart_SRAM::Reset(); NDSCart_SRAM::Reset();
ResetCart();
} }
void DoSavestate(Savestate* file) void DoSavestate(Savestate* file)
@ -863,10 +866,10 @@ void DecryptSecureArea(u8* out)
memcpy(out, &CartROM[arm9base], 0x800); memcpy(out, &CartROM[arm9base], 0x800);
Key1_InitKeycode(gamecode, 2, 2); Key1_InitKeycode(false, gamecode, 2, 2);
Key1_Decrypt((u32*)&out[0]); Key1_Decrypt((u32*)&out[0]);
Key1_InitKeycode(gamecode, 3, 2); Key1_InitKeycode(false, gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8) for (u32 i = 0; i < 0x800; i += 8)
Key1_Decrypt((u32*)&out[i]); Key1_Decrypt((u32*)&out[i]);
@ -911,6 +914,11 @@ bool LoadROM(const char* path, const char* sram, bool direct)
fread(&gamecode, 4, 1, f); fread(&gamecode, 4, 1, f);
printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24); printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24);
u8 unitcode;
fseek(f, 0x12, SEEK_SET);
fread(&unitcode, 1, 1, f);
CartIsDSi = (unitcode & 0x02) != 0;
CartROM = new u8[CartROMSize]; CartROM = new u8[CartROMSize];
memset(CartROM, 0, CartROMSize); memset(CartROM, 0, CartROMSize);
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
@ -953,6 +961,9 @@ bool LoadROM(const char* path, const char* sram, bool direct)
if (romparams.SaveMemType == 8) if (romparams.SaveMemType == 8)
CartID |= 0x08000000; // NAND flag CartID |= 0x08000000; // NAND flag
if (CartIsDSi)
CartID |= 0x40000000;
printf("Cart ID: %08X\n", CartID); printf("Cart ID: %08X\n", CartID);
u32 arm9base = *(u32*)&CartROM[0x20]; u32 arm9base = *(u32*)&CartROM[0x20];
@ -968,11 +979,11 @@ bool LoadROM(const char* path, const char* sram, bool direct)
strncpy((char*)&CartROM[arm9base], "encryObj", 8); strncpy((char*)&CartROM[arm9base], "encryObj", 8);
Key1_InitKeycode(gamecode, 3, 2); Key1_InitKeycode(false, gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8) for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&CartROM[arm9base + i]); Key1_Encrypt((u32*)&CartROM[arm9base + i]);
Key1_InitKeycode(gamecode, 2, 2); Key1_InitKeycode(false, gamecode, 2, 2);
Key1_Encrypt((u32*)&CartROM[arm9base]); Key1_Encrypt((u32*)&CartROM[arm9base]);
} }
} }
@ -1000,8 +1011,7 @@ bool LoadROM(const char* path, const char* sram, bool direct)
ROMCommandHandler = ROMCommand_Retail; ROMCommandHandler = ROMCommand_Retail;
// encryption // encryption
Key1_InitKeycode(gamecode, 2, 2); Key1_InitKeycode(false, gamecode, 2, 2);
// save // save
printf("Save file: %s\n", sram); printf("Save file: %s\n", sram);
@ -1016,6 +1026,27 @@ void RelocateSave(const char* path, bool write)
NDSCart_SRAM::RelocateSave(path, write); NDSCart_SRAM::RelocateSave(path, write);
} }
void ResetCart()
{
// CHECKME: what if there is a transfer in progress?
SPICnt = 0;
ROMCnt = 0;
memset(ROMCommand, 0, 8);
ROMDataOut = 0;
Key2_X = 0;
Key2_Y = 0;
memset(DataOut, 0, 0x4000);
DataOutPos = 0;
DataOutLen = 0;
CmdEncMode = 0;
DataEncMode = 0;
}
void ReadROM(u32 addr, u32 len, u32 offset) void ReadROM(u32 addr, u32 len, u32 offset)
{ {
if (!CartInserted) return; if (!CartInserted) return;
@ -1190,7 +1221,7 @@ void WriteROMCnt(u32 val)
// handle KEY1 encryption as needed. // handle KEY1 encryption as needed.
// KEY2 encryption is implemented in hardware and doesn't need to be handled. // KEY2 encryption is implemented in hardware and doesn't need to be handled.
u8 cmd[8]; u8 cmd[8];
if (CmdEncMode == 1) if (CmdEncMode == 1 || CmdEncMode == 11)
{ {
*(u32*)&cmd[0] = ByteSwap(*(u32*)&ROMCommand[4]); *(u32*)&cmd[0] = ByteSwap(*(u32*)&ROMCommand[4]);
*(u32*)&cmd[4] = ByteSwap(*(u32*)&ROMCommand[0]); *(u32*)&cmd[4] = ByteSwap(*(u32*)&ROMCommand[0]);
@ -1236,11 +1267,23 @@ void WriteROMCnt(u32 val)
break; break;
case 0x3C: case 0x3C:
if (CartInserted) CmdEncMode = 1; 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; break;
default: default:
if (CmdEncMode == 1) if (CmdEncMode == 1 || CmdEncMode == 11)
{ {
switch (cmd[0] & 0xF0) switch (cmd[0] & 0xF0)
{ {
@ -1256,6 +1299,12 @@ void WriteROMCnt(u32 val)
case 0x20: case 0x20:
{ {
u32 addr = (cmd[2] & 0xF0) << 8; u32 addr = (cmd[2] & 0xF0) << 8;
if (CmdEncMode == 11)
{
u32 arm9i_base = *(u32*)&CartROM[0x1C0];
addr -= 0x4000;
addr += arm9i_base;
}
ReadROM(addr, 0x1000, 0); ReadROM(addr, 0x1000, 0);
} }
break; break;

View File

@ -48,6 +48,8 @@ void DecryptSecureArea(u8* out);
bool LoadROM(const char* path, const char* sram, bool direct); bool LoadROM(const char* path, const char* sram, bool direct);
void RelocateSave(const char* path, bool write); void RelocateSave(const char* path, bool write);
void ResetCart();
void WriteROMCnt(u32 val); void WriteROMCnt(u32 val);
u32 ReadROMData(); u32 ReadROMData();

View File

@ -22,6 +22,7 @@
#include "Config.h" #include "Config.h"
#include "NDS.h" #include "NDS.h"
#include "SPI.h" #include "SPI.h"
#include "DSi_SPI_TSC.h"
#include "Platform.h" #include "Platform.h"
@ -92,6 +93,9 @@ void Reset()
if (Firmware) delete[] Firmware; if (Firmware) delete[] Firmware;
Firmware = NULL; Firmware = NULL;
if (NDS::ConsoleType == 1)
strncpy(FirmwarePath, Config::DSiFirmwarePath, 1023);
else
strncpy(FirmwarePath, Config::FirmwarePath, 1023); strncpy(FirmwarePath, Config::FirmwarePath, 1023);
FILE* f = Platform::OpenLocalFile(FirmwarePath, "rb"); FILE* f = Platform::OpenLocalFile(FirmwarePath, "rb");
@ -159,6 +163,7 @@ void Reset()
UserSettings = userdata; UserSettings = userdata;
// fix touchscreen coords // fix touchscreen coords
#if 0
*(u16*)&Firmware[userdata+0x58] = 0; *(u16*)&Firmware[userdata+0x58] = 0;
*(u16*)&Firmware[userdata+0x5A] = 0; *(u16*)&Firmware[userdata+0x5A] = 0;
Firmware[userdata+0x5C] = 0; Firmware[userdata+0x5C] = 0;
@ -181,12 +186,12 @@ void Reset()
Firmware[0x39] = rand()&0xFF; Firmware[0x39] = rand()&0xFF;
Firmware[0x3A] = rand()&0xFF; Firmware[0x3A] = rand()&0xFF;
Firmware[0x3B] = rand()&0xFF; Firmware[0x3B] = rand()&0xFF;
#endif
printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
Firmware[0x36], Firmware[0x37], Firmware[0x38], Firmware[0x36], Firmware[0x37], Firmware[0x38],
Firmware[0x39], Firmware[0x3A], Firmware[0x3B]); Firmware[0x39], Firmware[0x3A], Firmware[0x3B]);
*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000); //*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
// verify shit // verify shit
printf("FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware[0x2C], 0x2A)?"GOOD":"BAD"); printf("FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware[0x2C], 0x2A)?"GOOD":"BAD");
@ -233,6 +238,7 @@ void SetupDirectBoot()
u8 GetConsoleType() { return Firmware[0x1D]; } u8 GetConsoleType() { return Firmware[0x1D]; }
u8 GetWifiVersion() { return Firmware[0x2F]; } u8 GetWifiVersion() { return Firmware[0x2F]; }
u8 GetRFVersion() { return Firmware[0x40]; } u8 GetRFVersion() { return Firmware[0x40]; }
u8* GetWifiMAC() { return &Firmware[0x36]; }
u8 Read() u8 Read()
{ {
@ -589,7 +595,7 @@ namespace SPI
u16 Cnt; u16 Cnt;
u32 CurDevice; u32 CurDevice; // remove me
bool Init() bool Init()
@ -597,6 +603,7 @@ bool Init()
if (!SPI_Firmware::Init()) return false; if (!SPI_Firmware::Init()) return false;
if (!SPI_Powerman::Init()) return false; if (!SPI_Powerman::Init()) return false;
if (!SPI_TSC::Init()) return false; if (!SPI_TSC::Init()) return false;
if (!DSi_SPI_TSC::Init()) return false;
return true; return true;
} }
@ -606,6 +613,7 @@ void DeInit()
SPI_Firmware::DeInit(); SPI_Firmware::DeInit();
SPI_Powerman::DeInit(); SPI_Powerman::DeInit();
SPI_TSC::DeInit(); SPI_TSC::DeInit();
DSi_SPI_TSC::DeInit();
} }
void Reset() void Reset()
@ -615,6 +623,7 @@ void Reset()
SPI_Firmware::Reset(); SPI_Firmware::Reset();
SPI_Powerman::Reset(); SPI_Powerman::Reset();
SPI_TSC::Reset(); SPI_TSC::Reset();
if (NDS::ConsoleType == 1) DSi_SPI_TSC::Reset();
} }
void DoSavestate(Savestate* file) void DoSavestate(Savestate* file)
@ -627,6 +636,7 @@ void DoSavestate(Savestate* file)
SPI_Firmware::DoSavestate(file); SPI_Firmware::DoSavestate(file);
SPI_Powerman::DoSavestate(file); SPI_Powerman::DoSavestate(file);
SPI_TSC::DoSavestate(file); SPI_TSC::DoSavestate(file);
if (NDS::ConsoleType == 1) DSi_SPI_TSC::DoSavestate(file);
} }
@ -640,7 +650,12 @@ void WriteCnt(u16 val)
{ {
case 0x0000: SPI_Powerman::Hold = 0; break; case 0x0000: SPI_Powerman::Hold = 0; break;
case 0x0100: SPI_Firmware::Hold = 0; break; case 0x0100: SPI_Firmware::Hold = 0; break;
case 0x0200: SPI_TSC::DataPos = 0; break; case 0x0200:
if (NDS::ConsoleType == 1)
DSi_SPI_TSC::DataPos = 0;
else
SPI_TSC::DataPos = 0;
break;
} }
} }
@ -666,7 +681,11 @@ u8 ReadData()
{ {
case 0x0000: return SPI_Powerman::Read(); case 0x0000: return SPI_Powerman::Read();
case 0x0100: return SPI_Firmware::Read(); case 0x0100: return SPI_Firmware::Read();
case 0x0200: return SPI_TSC::Read(); case 0x0200:
if (NDS::ConsoleType == 1)
return DSi_SPI_TSC::Read();
else
return SPI_TSC::Read();
default: return 0; default: return 0;
} }
} }
@ -682,7 +701,12 @@ void WriteData(u8 val)
{ {
case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break; case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break;
case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break; case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break;
case 0x0200: SPI_TSC::Write(val, Cnt&(1<<11)); break; case 0x0200:
if (NDS::ConsoleType == 1)
DSi_SPI_TSC::Write(val, Cnt&(1<<11));
else
SPI_TSC::Write(val, Cnt&(1<<11));
break;
default: printf("SPI to unknown device %04X %02X\n", Cnt, val); break; default: printf("SPI to unknown device %04X %02X\n", Cnt, val); break;
} }

View File

@ -29,6 +29,7 @@ void SetupDirectBoot();
u8 GetConsoleType(); u8 GetConsoleType();
u8 GetWifiVersion(); u8 GetWifiVersion();
u8 GetRFVersion(); u8 GetRFVersion();
u8* GetWifiMAC();
} }
@ -38,6 +39,9 @@ namespace SPI_TSC
void SetTouchCoords(u16 x, u16 y); void SetTouchCoords(u16 x, u16 y);
void MicInputFrame(s16* data, int samples); void MicInputFrame(s16* data, int samples);
u8 Read();
void Write(u8 val, u32 hold);
} }
namespace SPI namespace SPI
@ -50,7 +54,6 @@ void DeInit();
void Reset(); void Reset();
void DoSavestate(Savestate* file); void DoSavestate(Savestate* file);
u16 ReadCnt();
void WriteCnt(u16 val); void WriteCnt(u16 val);
u8 ReadData(); u8 ReadData();

View File

@ -19,6 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "NDS.h" #include "NDS.h"
#include "DSi.h"
#include "SPU.h" #include "SPU.h"
@ -154,6 +155,11 @@ Channel::~Channel()
void Channel::Reset() void Channel::Reset()
{ {
if (NDS::ConsoleType == 1)
BusRead32 = DSi::ARM7Read32;
else
BusRead32 = NDS::ARM7Read32;
SetCnt(0); SetCnt(0);
SrcAddr = 0; SrcAddr = 0;
TimerReload = 0; TimerReload = 0;
@ -216,7 +222,7 @@ void Channel::FIFO_BufferData()
for (u32 i = 0; i < burstlen; i += 4) for (u32 i = 0; i < burstlen; i += 4)
{ {
FIFO[FIFOWritePos] = NDS::ARM7Read32(SrcAddr + FIFOReadOffset); FIFO[FIFOWritePos] = BusRead32(SrcAddr + FIFOReadOffset);
FIFOReadOffset += 4; FIFOReadOffset += 4;
FIFOWritePos++; FIFOWritePos++;
FIFOWritePos &= 0x7; FIFOWritePos &= 0x7;
@ -464,6 +470,11 @@ CaptureUnit::~CaptureUnit()
void CaptureUnit::Reset() void CaptureUnit::Reset()
{ {
if (NDS::ConsoleType == 1)
BusWrite32 = DSi::ARM7Write32;
else
BusWrite32 = NDS::ARM7Write32;
SetCnt(0); SetCnt(0);
DstAddr = 0; DstAddr = 0;
TimerReload = 0; TimerReload = 0;
@ -499,7 +510,7 @@ void CaptureUnit::FIFO_FlushData()
{ {
for (u32 i = 0; i < 4; i++) for (u32 i = 0; i < 4; i++)
{ {
NDS::ARM7Write32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]); BusWrite32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]);
FIFOReadPos++; FIFOReadPos++;
FIFOReadPos &= 0x3; FIFOReadPos &= 0x3;

View File

@ -142,6 +142,9 @@ public:
} }
void PanOutput(s32* inbuf, u32 samples, s32* leftbuf, s32* rightbuf); void PanOutput(s32* inbuf, u32 samples, s32* leftbuf, s32* rightbuf);
private:
u32 (*BusRead32)(u32 addr);
}; };
class CaptureUnit class CaptureUnit
@ -196,6 +199,9 @@ public:
} }
void Run(s32 sample); void Run(s32 sample);
private:
void (*BusWrite32)(u32 addr, u32 val);
}; };
} }

View File

@ -46,6 +46,15 @@ enum
Load_FirmwareBad, Load_FirmwareBad,
Load_FirmwareNotBootable, Load_FirmwareNotBootable,
Load_DSiBIOS9Missing,
Load_DSiBIOS9Bad,
Load_DSiBIOS7Missing,
Load_DSiBIOS7Bad,
Load_DSiNANDMissing,
Load_DSiNANDBad,
// TODO: more precise errors for ROM loading // TODO: more precise errors for ROM loading
Load_ROMLoadError, Load_ROMLoadError,
}; };

View File

@ -96,6 +96,42 @@ int VerifyDSBIOS()
return Load_OK; return Load_OK;
} }
int VerifyDSiBIOS()
{
FILE* f;
long len;
// TODO: check the first 32 bytes
f = Platform::OpenLocalFile(Config::DSiBIOS9Path, "rb");
if (!f) return Load_DSiBIOS9Missing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len != 0x10000)
{
fclose(f);
return Load_DSiBIOS9Bad;
}
fclose(f);
f = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
if (!f) return Load_DSiBIOS7Missing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len != 0x10000)
{
fclose(f);
return Load_DSiBIOS7Bad;
}
fclose(f);
return Load_OK;
}
int VerifyDSFirmware() int VerifyDSFirmware()
{ {
FILE* f; FILE* f;
@ -123,6 +159,45 @@ int VerifyDSFirmware()
return Load_OK; return Load_OK;
} }
int VerifyDSiFirmware()
{
FILE* f;
long len;
f = Platform::OpenLocalFile(Config::DSiFirmwarePath, "rb");
if (!f) return Load_FirmwareMissing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len != 0x20000)
{
// not 128KB
// TODO: check whether those work
fclose(f);
return Load_FirmwareBad;
}
fclose(f);
return Load_OK;
}
int VerifyDSiNAND()
{
FILE* f;
long len;
f = Platform::OpenLocalFile(Config::DSiNANDPath, "rb");
if (!f) return Load_DSiNANDMissing;
// TODO: some basic checks
// check that it has the nocash footer, and all
fclose(f);
return Load_OK;
}
int LoadBIOS() int LoadBIOS()
{ {
int res; int res;
@ -130,8 +205,22 @@ int LoadBIOS()
res = VerifyDSBIOS(); res = VerifyDSBIOS();
if (res != Load_OK) return res; if (res != Load_OK) return res;
if (Config::ConsoleType == 1)
{
res = VerifyDSiBIOS();
if (res != Load_OK) return res;
res = VerifyDSiFirmware();
if (res != Load_OK) return res;
res = VerifyDSiNAND();
if (res != Load_OK) return res;
}
else
{
res = VerifyDSFirmware(); res = VerifyDSFirmware();
if (res != Load_OK) return res; if (res != Load_OK) return res;
}
// TODO: // TODO:
// original code in the libui frontend called NDS::LoadGBAROM() if needed // original code in the libui frontend called NDS::LoadGBAROM() if needed
@ -141,6 +230,7 @@ int LoadBIOS()
ROMPath[ROMSlot_NDS][0] = '\0'; ROMPath[ROMSlot_NDS][0] = '\0';
SRAMPath[ROMSlot_NDS][0] = '\0'; SRAMPath[ROMSlot_NDS][0] = '\0';
NDS::SetConsoleType(Config::ConsoleType);
NDS::LoadBIOS(); NDS::LoadBIOS();
SavestateLoaded = false; SavestateLoaded = false;
@ -153,9 +243,31 @@ int LoadROM(const char* file, int slot)
int res; int res;
bool directboot = Config::DirectBoot != 0; bool directboot = Config::DirectBoot != 0;
if (Config::ConsoleType == 1 && slot == 1)
{
// cannot load a GBA ROM into a DSi
return Load_ROMLoadError;
}
res = VerifyDSBIOS(); res = VerifyDSBIOS();
if (res != Load_OK) return res; if (res != Load_OK) return res;
if (Config::ConsoleType == 1)
{
res = VerifyDSiBIOS();
if (res != Load_OK) return res;
res = VerifyDSiFirmware();
if (res != Load_OK) return res;
res = VerifyDSiNAND();
if (res != Load_OK) return res;
GBACart::Eject();
ROMPath[ROMSlot_GBA][0] = '\0';
}
else
{
res = VerifyDSFirmware(); res = VerifyDSFirmware();
if (res != Load_OK) if (res != Load_OK)
{ {
@ -164,6 +276,7 @@ int LoadROM(const char* file, int slot)
else else
return res; return res;
} }
}
char oldpath[1024]; char oldpath[1024];
char oldsram[1024]; char oldsram[1024];
@ -176,6 +289,8 @@ int LoadROM(const char* file, int slot)
SetupSRAMPath(0); SetupSRAMPath(0);
SetupSRAMPath(1); SetupSRAMPath(1);
NDS::SetConsoleType(Config::ConsoleType);
if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], directboot)) if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], directboot))
{ {
SavestateLoaded = false; SavestateLoaded = false;
@ -224,6 +339,22 @@ int Reset()
res = VerifyDSBIOS(); res = VerifyDSBIOS();
if (res != Load_OK) return res; if (res != Load_OK) return res;
if (Config::ConsoleType == 1)
{
res = VerifyDSiBIOS();
if (res != Load_OK) return res;
res = VerifyDSiFirmware();
if (res != Load_OK) return res;
res = VerifyDSiNAND();
if (res != Load_OK) return res;
GBACart::Eject();
ROMPath[ROMSlot_GBA][0] = '\0';
}
else
{
res = VerifyDSFirmware(); res = VerifyDSFirmware();
if (res != Load_OK) if (res != Load_OK)
{ {
@ -232,9 +363,12 @@ int Reset()
else else
return res; return res;
} }
}
SavestateLoaded = false; SavestateLoaded = false;
NDS::SetConsoleType(Config::ConsoleType);
if (ROMPath[ROMSlot_NDS][0] == '\0') if (ROMPath[ROMSlot_NDS][0] == '\0')
{ {
NDS::LoadBIOS(); NDS::LoadBIOS();

View File

@ -42,6 +42,16 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
ui->txtBIOS9Path->setText(Config::BIOS9Path); ui->txtBIOS9Path->setText(Config::BIOS9Path);
ui->txtBIOS7Path->setText(Config::BIOS7Path); ui->txtBIOS7Path->setText(Config::BIOS7Path);
ui->txtFirmwarePath->setText(Config::FirmwarePath); ui->txtFirmwarePath->setText(Config::FirmwarePath);
ui->txtDSiBIOS9Path->setText(Config::DSiBIOS9Path);
ui->txtDSiBIOS7Path->setText(Config::DSiBIOS7Path);
ui->txtDSiFirmwarePath->setText(Config::DSiFirmwarePath);
ui->txtDSiNANDPath->setText(Config::DSiNANDPath);
ui->cbxConsoleType->addItem("DS");
ui->cbxConsoleType->addItem("DSi (experimental)");
ui->cbxConsoleType->setCurrentIndex(Config::ConsoleType);
ui->chkDirectBoot->setChecked(Config::DirectBoot != 0); ui->chkDirectBoot->setChecked(Config::DirectBoot != 0);
} }
@ -99,7 +109,15 @@ void EmuSettingsDialog::on_EmuSettingsDialog_accepted()
strncpy(Config::BIOS9Path, ui->txtBIOS9Path->text().toStdString().c_str(), 1023); Config::BIOS9Path[1023] = '\0'; strncpy(Config::BIOS9Path, ui->txtBIOS9Path->text().toStdString().c_str(), 1023); Config::BIOS9Path[1023] = '\0';
strncpy(Config::BIOS7Path, ui->txtBIOS7Path->text().toStdString().c_str(), 1023); Config::BIOS7Path[1023] = '\0'; strncpy(Config::BIOS7Path, ui->txtBIOS7Path->text().toStdString().c_str(), 1023); Config::BIOS7Path[1023] = '\0';
strncpy(Config::FirmwarePath, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); Config::FirmwarePath[1023] = '\0'; strncpy(Config::FirmwarePath, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); Config::FirmwarePath[1023] = '\0';
strncpy(Config::DSiBIOS9Path, ui->txtDSiBIOS9Path->text().toStdString().c_str(), 1023); Config::DSiBIOS9Path[1023] = '\0';
strncpy(Config::DSiBIOS7Path, ui->txtDSiBIOS7Path->text().toStdString().c_str(), 1023); Config::DSiBIOS7Path[1023] = '\0';
strncpy(Config::DSiFirmwarePath, ui->txtDSiFirmwarePath->text().toStdString().c_str(), 1023); Config::DSiFirmwarePath[1023] = '\0';
strncpy(Config::DSiNANDPath, ui->txtDSiNANDPath->text().toStdString().c_str(), 1023); Config::DSiNANDPath[1023] = '\0';
Config::ConsoleType = ui->cbxConsoleType->currentIndex();
Config::DirectBoot = ui->chkDirectBoot->isChecked() ? 1:0; Config::DirectBoot = ui->chkDirectBoot->isChecked() ? 1:0;
Config::Save(); Config::Save();
closeDlg(); closeDlg();
@ -145,3 +163,51 @@ void EmuSettingsDialog::on_btnFirmwareBrowse_clicked()
ui->txtFirmwarePath->setText(file); ui->txtFirmwarePath->setText(file);
} }
void EmuSettingsDialog::on_btnDSiBIOS9Browse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DSi-mode ARM9 BIOS...",
EmuDirectory,
"BIOS files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtDSiBIOS9Path->setText(file);
}
void EmuSettingsDialog::on_btnDSiBIOS7Browse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DSi-mode ARM7 BIOS...",
EmuDirectory,
"BIOS files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtDSiBIOS7Path->setText(file);
}
void EmuSettingsDialog::on_btnDSiFirmwareBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DSi DS-mode firmware...",
EmuDirectory,
"Firmware files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtDSiFirmwarePath->setText(file);
}
void EmuSettingsDialog::on_btnDSiNANDBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DSi NAND...",
EmuDirectory,
"NAND files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtDSiNANDPath->setText(file);
}

View File

@ -58,6 +58,11 @@ private slots:
void on_btnBIOS7Browse_clicked(); void on_btnBIOS7Browse_clicked();
void on_btnFirmwareBrowse_clicked(); void on_btnFirmwareBrowse_clicked();
void on_btnDSiBIOS9Browse_clicked();
void on_btnDSiBIOS7Browse_clicked();
void on_btnDSiFirmwareBrowse_clicked();
void on_btnDSiNANDBrowse_clicked();
private: private:
void verifyFirmware(); void verifyFirmware();

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>490</width> <width>490</width>
<height>217</height> <height>392</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -119,13 +119,138 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>DSi mode</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="2">
<widget class="QPushButton" name="btnDSiBIOS9Browse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>DSi ARM9 BIOS:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="btnDSiFirmwareBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="txtDSiBIOS7Path">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi-mode ARM7 BIOS&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Size should be 64 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="txtDSiFirmwarePath">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi-mode firmware (used for DS-mode backwards compatibility)&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Size should be 128 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>DSi ARM7 BIOS:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>DSi firmware:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnDSiBIOS7Browse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txtDSiBIOS9Path">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi-mode ARM9 BIOS&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Size should be 64 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>DSi NAND:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="txtDSiNANDPath">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi NAND dump&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Should have 'nocash footer' at the end&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="btnDSiNANDBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="groupBox_2">
<property name="title"> <property name="title">
<string>Startup</string> <string>General</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Console type:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cbxConsoleType">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The type of console to emulate&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="chkDirectBoot"> <widget class="QCheckBox" name="chkDirectBoot">
<property name="whatsThis"> <property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When loading a ROM, completely skip the regular boot process (&amp;quot;Nintendo DS&amp;quot; screen) to boot the ROM directly.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Note: if your firmware dump isn't bootable, the ROM will be booted directly regardless of this setting.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When loading a ROM, completely skip the regular boot process (&amp;quot;Nintendo DS&amp;quot; screen) to boot the ROM directly.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Note: if your firmware dump isn't bootable, the ROM will be booted directly regardless of this setting.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>

View File

@ -47,10 +47,17 @@ int ScreenUseGL;
int ScreenVSync; int ScreenVSync;
int ScreenVSyncInterval; int ScreenVSyncInterval;
int _3DRenderer;
int Threaded3D;
int GL_ScaleFactor;
int GL_Antialias;
int LimitFPS; int LimitFPS;
int AudioSync; int AudioSync;
int ShowOSD; int ShowOSD;
int ConsoleType;
int DirectBoot; int DirectBoot;
int SocketBindAnyAddr; int SocketBindAnyAddr;
@ -129,10 +136,17 @@ ConfigEntry PlatformConfigFile[] =
{"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0}, {"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0},
{"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, NULL, 0}, {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, NULL, 0},
{"3DRenderer", 0, &_3DRenderer, 1, NULL, 0},
{"Threaded3D", 0, &Threaded3D, 1, NULL, 0},
{"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, NULL, 0},
{"GL_Antialias", 0, &GL_Antialias, 0, NULL, 0},
{"LimitFPS", 0, &LimitFPS, 0, NULL, 0}, {"LimitFPS", 0, &LimitFPS, 0, NULL, 0},
{"AudioSync", 0, &AudioSync, 1, NULL, 0}, {"AudioSync", 0, &AudioSync, 1, NULL, 0},
{"ShowOSD", 0, &ShowOSD, 1, NULL, 0}, {"ShowOSD", 0, &ShowOSD, 1, NULL, 0},
{"ConsoleType", 0, &ConsoleType, 0, NULL, 0},
{"DirectBoot", 0, &DirectBoot, 1, NULL, 0}, {"DirectBoot", 0, &DirectBoot, 1, NULL, 0},
{"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0}, {"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0},

View File

@ -60,10 +60,17 @@ extern int ScreenUseGL;
extern int ScreenVSync; extern int ScreenVSync;
extern int ScreenVSyncInterval; extern int ScreenVSyncInterval;
extern int _3DRenderer;
extern int Threaded3D;
extern int GL_ScaleFactor;
extern int GL_Antialias;
extern int LimitFPS; extern int LimitFPS;
extern int AudioSync; extern int AudioSync;
extern int ShowOSD; extern int ShowOSD;
extern int ConsoleType;
extern int DirectBoot; extern int DirectBoot;
extern int SocketBindAnyAddr; extern int SocketBindAnyAddr;

View File

@ -1360,17 +1360,40 @@ QString MainWindow::loadErrorStr(int error)
{ {
switch (error) switch (error)
{ {
case Frontend::Load_BIOS9Missing: return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings."; case Frontend::Load_BIOS9Missing:
case Frontend::Load_BIOS9Bad: return "DS ARM9 BIOS is not a valid BIOS dump."; return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
case Frontend::Load_BIOS9Bad:
return "DS ARM9 BIOS is not a valid BIOS dump.";
case Frontend::Load_BIOS7Missing: return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings."; case Frontend::Load_BIOS7Missing:
case Frontend::Load_BIOS7Bad: return "DS ARM7 BIOS is not a valid BIOS dump."; return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
case Frontend::Load_BIOS7Bad:
return "DS ARM7 BIOS is not a valid BIOS dump.";
case Frontend::Load_FirmwareMissing: return "DS firmware was not found or could not be accessed. Check your emu settings."; case Frontend::Load_FirmwareMissing:
case Frontend::Load_FirmwareBad: return "DS firmware is not a valid firmware dump."; return "DS firmware was not found or could not be accessed. Check your emu settings.";
case Frontend::Load_FirmwareNotBootable: return "DS firmware is not bootable."; case Frontend::Load_FirmwareBad:
return "DS firmware is not a valid firmware dump.";
case Frontend::Load_FirmwareNotBootable:
return "DS firmware is not bootable.";
case Frontend::Load_ROMLoadError: return "Failed to load the ROM. Make sure the file is accessible and isn't used by another application."; case Frontend::Load_DSiBIOS9Missing:
return "DSi ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
case Frontend::Load_DSiBIOS9Bad:
return "DSi ARM9 BIOS is not a valid BIOS dump.";
case Frontend::Load_DSiBIOS7Missing:
return "DSi ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
case Frontend::Load_DSiBIOS7Bad:
return "DSi ARM7 BIOS is not a valid BIOS dump.";
case Frontend::Load_DSiNANDMissing:
return "DSi NAND was not found or could not be accessed. Check your emu settings.";
case Frontend::Load_DSiNANDBad:
return "DSi NAND is not a valid NAND dump.";
case Frontend::Load_ROMLoadError:
return "Failed to load the ROM. Make sure the file is accessible and isn't used by another application.";
default: return "Unknown error during launch; smack Arisotura."; default: return "Unknown error during launch; smack Arisotura.";
} }
@ -1384,7 +1407,7 @@ void MainWindow::onOpenFile()
QString filename = QFileDialog::getOpenFileName(this, QString filename = QFileDialog::getOpenFileName(this,
"Open ROM", "Open ROM",
Config::LastROMFolder, Config::LastROMFolder,
"DS ROMs (*.nds *.srl);;GBA ROMs (*.gba);;Any file (*.*)"); "DS ROMs (*.nds *.dsi *.srl);;GBA ROMs (*.gba);;Any file (*.*)");
if (filename.isEmpty()) if (filename.isEmpty())
{ {
emuThread->emuUnpause(); emuThread->emuUnpause();
@ -1889,6 +1912,7 @@ int main(int argc, char** argv)
Config::Load(); Config::Load();
#define SANITIZE(var, min, max) { if (var < min) var = min; else if (var > max) var = max; } #define SANITIZE(var, min, max) { if (var < min) var = min; else if (var > max) var = max; }
SANITIZE(Config::ConsoleType, 0, 1);
SANITIZE(Config::_3DRenderer, 0, 1); SANITIZE(Config::_3DRenderer, 0, 1);
SANITIZE(Config::ScreenVSyncInterval, 1, 20); SANITIZE(Config::ScreenVSyncInterval, 1, 20);
SANITIZE(Config::GL_ScaleFactor, 1, 16); SANITIZE(Config::GL_ScaleFactor, 1, 16);

277
src/sha1/sha1.c Normal file
View File

@ -0,0 +1,277 @@
/* from valgrind tests */
/* ================ sha1.c ================ */
/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
/* #define SHA1HANDSOFF * Copies data before messing with it. */
#define SHA1HANDSOFF
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* for u_int*_t */
#if defined(__sun)
#include "solarisfixes.h"
#endif
#include "sha1.h"
#ifndef BYTE_ORDER
#if (BSD >= 199103)
# include <machine/endian.h>
#else
#if defined(linux) || defined(__linux__)
# include <endian.h>
#else
#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */
#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/
#if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \
defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
defined(__alpha__) || defined(__alpha) || \
defined(__WIN32__)
#define BYTE_ORDER LITTLE_ENDIAN
#endif
#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\
defined(apollo) || defined(__convex__) || defined(_CRAY) || \
defined(__hppa) || defined(__hp9000) || \
defined(__hp9000s300) || defined(__hp9000s700) || \
defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc)
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif /* linux */
#endif /* BSD */
#endif /* BYTE_ORDER */
#if defined(__BYTE_ORDER) && !defined(BYTE_ORDER)
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif
#if !defined(BYTE_ORDER) || \
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN && \
BYTE_ORDER != PDP_ENDIAN)
/* you must determine what the correct bit order is for
* your compiler - the next line is an intentional error
* which will force your compiles to bomb until you fix
* the above macros.
*/
#error "Undefined or invalid BYTE_ORDER"
#endif
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */
#if BYTE_ORDER == LITTLE_ENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|(rol(block->l[i],8)&0x00FF00FF))
#elif BYTE_ORDER == BIG_ENDIAN
#define blk0(i) block->l[i]
#else
#error "Endianness not defined!"
#endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
{
uint32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
uint32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
memcpy(block, buffer, 64);
#else
/* The following had better never be used because it causes the
* pointer-to-const buffer to be cast into a pointer to non-const.
* And the result is written through. I threw a "const" in, hoping
* this will cause a diagnostic.
*/
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
#endif
/* Copy context->state[] to working vars */
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
/* Add the working vars back into context.state[] */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
/* Wipe variables */
a = b = c = d = e = 0;
#ifdef SHA1HANDSOFF
memset(block, '\0', sizeof(block));
#endif
}
/* SHA1Init - Initialize new context */
void SHA1Init(SHA1_CTX* context)
{
/* SHA1 initialization constants */
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
/* Run your data through this. */
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
{
uint32_t i;
uint32_t j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
context->count[1]++;
context->count[1] += (len>>29);
j = (j >> 3) & 63;
if ((j + len) > 63) {
memcpy(&context->buffer[j], data, (i = 64-j));
SHA1Transform(context->state, context->buffer);
for ( ; i + 63 < len; i += 64) {
SHA1Transform(context->state, &data[i]);
}
j = 0;
}
else i = 0;
memcpy(&context->buffer[j], &data[i], len - i);
}
/* Add padding and return the message digest. */
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
{
unsigned i;
unsigned char finalcount[8];
unsigned char c;
#if 0 /* untested "improvement" by DHR */
/* Convert context->count to a sequence of bytes
* in finalcount. Second element first, but
* big-endian order within element.
* But we do it all backwards.
*/
unsigned char *fcp = &finalcount[8];
for (i = 0; i < 2; i++)
{
uint32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
*--fcp = (unsigned char) t
}
#else
for (i = 0; i < 8; i++) {
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
}
#endif
c = 0200;
SHA1Update(context, &c, 1);
while ((context->count[0] & 504) != 448) {
c = 0000;
SHA1Update(context, &c, 1);
}
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
for (i = 0; i < 20; i++) {
digest[i] = (unsigned char)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
/* Wipe variables */
memset(context, '\0', sizeof(*context));
memset(&finalcount, '\0', sizeof(finalcount));
}
/* ================ end of sha1.c ================ */
#if 0
#define BUFSIZE 4096
int
main(int argc, char **argv)
{
SHA1_CTX ctx;
unsigned char hash[20], buf[BUFSIZE];
int i;
for(i=0;i<BUFSIZE;i++)
buf[i] = i;
SHA1Init(&ctx);
for(i=0;i<1000;i++)
SHA1Update(&ctx, buf, BUFSIZE);
SHA1Final(hash, &ctx);
printf("SHA1=");
for(i=0;i<20;i++)
printf("%02x", hash[i]);
printf("\n");
return 0;
}
#endif

19
src/sha1/sha1.h Normal file
View File

@ -0,0 +1,19 @@
/* ================ sha1.h ================ */
/*
SHA-1 in C
By Steve Reid <steve@edmweb.com>
100% Public Domain
*/
#include <stdint.h>
typedef struct {
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);

80
src/tiny-AES-c/README.md Normal file
View File

@ -0,0 +1,80 @@
### Tiny AES in C
This is a small and portable implementation of the AES [ECB](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29), [CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29) and [CBC](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29) encryption algorithms written in C.
You can override the default key-size of 128 bit with 192 or 256 bit by defining the symbols AES192 or AES256 in `aes.h`.
The API is very simple and looks like this (I am using C99 `<stdint.h>`-style annotated types):
```C
/* Initialize context calling one of: */
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
/* ... or reset IV at random point: */
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
/* Then start encrypting and decrypting with the functions below: */
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
/* Same function for encrypting as for decrypting in CTR mode */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
```
Note:
* No padding is provided so for CBC and ECB all buffers should be multiples of 16 bytes. For padding [PKCS7](https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7) is recommendable.
* ECB mode is considered unsafe for most uses and is not implemented in streaming mode. If you need this mode, call the function for every block of 16 bytes you need encrypted. See [wikipedia's article on ECB](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_(ECB)) for more details.
You can choose to use any or all of the modes-of-operations, by defining the symbols CBC, CTR or ECB. See the header file for clarification.
C++ users should `#include` [aes.hpp](https://github.com/kokke/tiny-AES-c/blob/master/aes.hpp) instead of [aes.h](https://github.com/kokke/tiny-AES-c/blob/master/aes.h)
There is no built-in error checking or protection from out-of-bounds memory access errors as a result of malicious input.
The module uses less than 200 bytes of RAM and 1-2K ROM when compiled for ARM, but YMMV depending on which modes are enabled.
It is one of the smallest implementations in C I've seen yet, but do contact me if you know of something smaller (or have improvements to the code here).
I've successfully used the code on 64bit x86, 32bit ARM and 8 bit AVR platforms.
GCC size output when only CTR mode is compiled for ARM:
$ arm-none-eabi-gcc -Os -DCBC=0 -DECB=0 -DCTR=1 -c aes.c
$ size aes.o
text data bss dec hex filename
1343 0 0 1343 53f aes.o
.. and when compiling for the THUMB instruction set, we end up just below 1K in code size.
$ arm-none-eabi-gcc -Os -mthumb -DCBC=0 -DECB=0 -DCTR=1 -c aes.c
$ size aes.o
text data bss dec hex filename
979 0 0 979 3d3 aes.o
I am using the Free Software Foundation, ARM GCC compiler:
$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 8-2018-q4-major) 8.2.1 20181213 (release)
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
This implementation is verified against the data in:
[National Institute of Standards and Technology Special Publication 800-38A 2001 ED](http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf) Appendix F: Example Vectors for Modes of Operation of the AES.
The other appendices in the document are valuable for implementation details on e.g. padding, generation of IVs and nonces in CTR-mode etc.
A heartfelt thank-you to all the nice people out there who have contributed to this project.
All material in this repository is in the public domain.

572
src/tiny-AES-c/aes.c Normal file
View File

@ -0,0 +1,572 @@
/*
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <stdint.h>
#include <string.h> // CBC mode, for memset
#include "aes.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
#if defined(AES256) && (AES256 == 1)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4 // The number of 32 bit words in a key.
#define Nr 10 // The number of rounds in AES Cipher.
#endif
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
/*
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
*/
#define getSBoxValue(num) (sbox[(num)])
/*
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
*/
#define getSBoxInvert(num) (rsbox[(num)])
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];
}
if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
j = i * 4; k=(i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
#endif
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = 1; round < Nr; ++round)
{
SubBytes(state);
ShiftRows(state);
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
SubBytes(state);
ShiftRows(state);
AddRoundKey(Nr, state, RoundKey);
}
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = (Nr - 1); round > 0; --round)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
InvMixColumns(state);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(0, state, RoundKey);
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}
#endif // #if defined(ECB) && (ECB == 1)
#if defined(CBC) && (CBC == 1)
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t *Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
//printf("Step %d - %d", i/16, i);
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uint8_t buffer[AES_BLOCKLEN];
unsigned i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer,ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
#endif // #if defined(CTR) && (CTR == 1)

90
src/tiny-AES-c/aes.h Normal file
View File

@ -0,0 +1,90 @@
#ifndef _AES_H_
#define _AES_H_
#include <stdint.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
#define CBC 1
#endif
#ifndef ECB
#define ECB 1
#endif
#ifndef CTR
#define CTR 1
#endif
#define AES128 1
//#define AES192 1
//#define AES256 1
#define AES_BLOCKLEN 16 //Block length in bytes AES is 128b block only
#if defined(AES256) && (AES256 == 1)
#define AES_KEYLEN 32
#define AES_keyExpSize 240
#elif defined(AES192) && (AES192 == 1)
#define AES_KEYLEN 24
#define AES_keyExpSize 208
#else
#define AES_KEYLEN 16 // Key length in bytes
#define AES_keyExpSize 176
#endif
struct AES_ctx
{
uint8_t RoundKey[AES_keyExpSize];
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
uint8_t Iv[AES_BLOCKLEN];
#endif
};
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif
#if defined(ECB) && (ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes;
// you need only AES_init_ctx as IV is not used in ECB
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
#endif // #if defined(ECB) && (ECB == !)
#if defined(CBC) && (CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
// Same function for encrypting as for decrypting.
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#endif // #if defined(CTR) && (CTR == 1)
#endif //_AES_H_

12
src/tiny-AES-c/aes.hpp Normal file
View File

@ -0,0 +1,12 @@
#ifndef _AES_HPP_
#define _AES_HPP_
#ifndef __cplusplus
#error Do not include the hpp header in a c project!
#endif //__cplusplus
extern "C" {
#include "aes.h"
}
#endif //_AES_HPP_

View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>