tremble upon the NDMA implementation

that doesn't do much beyond getting stuck
This commit is contained in:
Arisotura 2019-06-18 22:54:07 +02:00
parent 841122bc51
commit d6bbc6f0f1
7 changed files with 556 additions and 9 deletions

View File

@ -20,15 +20,9 @@
#include "NDS.h"
#include "DSi.h"
#include "DMA.h"
#include "NDSCart.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
//

View File

@ -25,6 +25,7 @@
#include "sha1/sha1.h"
#include "Platform.h"
#include "DSi_NDMA.h"
#include "DSi_I2C.h"
#include "DSi_SD.h"
@ -57,6 +58,9 @@ u32 NWRAMStart[2][3];
u32 NWRAMEnd[2][3];
u32 NWRAMMask[2][3];
u32 NDMACnt[2];
DSi_NDMA* NDMAs[8];
DSi_SDHost* SDMMC;
DSi_SDHost* SDIO;
@ -68,6 +72,15 @@ bool Init()
{
if (!DSi_I2C::Init()) return false;
NDMAs[0] = new DSi_NDMA(0, 0);
NDMAs[1] = new DSi_NDMA(0, 1);
NDMAs[2] = new DSi_NDMA(0, 2);
NDMAs[3] = new DSi_NDMA(0, 3);
NDMAs[4] = new DSi_NDMA(1, 0);
NDMAs[5] = new DSi_NDMA(1, 1);
NDMAs[6] = new DSi_NDMA(1, 2);
NDMAs[7] = new DSi_NDMA(1, 3);
SDMMC = new DSi_SDHost(0);
SDIO = new DSi_SDHost(1);
@ -78,6 +91,8 @@ void DeInit()
{
DSi_I2C::DeInit();
for (int i = 0; i < 8; i++) delete NDMAs[i];
delete SDMMC;
delete SDIO;
}
@ -91,6 +106,9 @@ void Reset()
NDS::ARM9->JumpTo(BootAddr[0]);
NDS::ARM7->JumpTo(BootAddr[1]);
NDMACnt[0] = 0; NDMACnt[1] = 0;
for (int i = 0; i < 8; i++) NDMAs[i]->Reset();
DSi_I2C::Reset();
SDMMC->Reset();
@ -279,6 +297,40 @@ bool LoadNAND()
}
void RunNDMAs(u32 cpu)
{
// TODO: round-robin mode (requires DMA channels to have a subblock delay set)
if (cpu == 0)
{
if (NDS::ARM9Timestamp >= NDS::ARM9Target) return;
//
}
else
{
if (NDS::ARM7Timestamp >= NDS::ARM7Target) return;
//
}
}
void StallNDMAs()
{
// TODO
}
bool NDMAsRunning(u32 cpu)
{
cpu <<= 2;
if (NDMAs[cpu+0]->IsRunning()) return true;
if (NDMAs[cpu+1]->IsRunning()) return true;
if (NDMAs[cpu+2]->IsRunning()) return true;
if (NDMAs[cpu+3]->IsRunning()) return true;
return false;
}
// new WRAM mapping
// TODO: find out what happens upon overlapping slots!!
@ -872,6 +924,36 @@ u32 ARM9IORead32(u32 addr)
switch (addr)
{
case 0x04004010: return 1; // todo
case 0x04004100: return NDMACnt[0];
case 0x04004104: return NDMAs[0]->SrcAddr;
case 0x04004108: return NDMAs[0]->DstAddr;
case 0x0400410C: return NDMAs[0]->TotalLength;
case 0x04004110: return NDMAs[0]->BlockLength;
case 0x04004114: return NDMAs[0]->SubblockTimer;
case 0x04004118: return NDMAs[0]->FillData;
case 0x0400411C: return NDMAs[0]->Cnt;
case 0x04004120: return NDMAs[1]->SrcAddr;
case 0x04004124: return NDMAs[1]->DstAddr;
case 0x04004128: return NDMAs[1]->TotalLength;
case 0x0400412C: return NDMAs[1]->BlockLength;
case 0x04004130: return NDMAs[1]->SubblockTimer;
case 0x04004134: return NDMAs[1]->FillData;
case 0x04004138: return NDMAs[1]->Cnt;
case 0x0400413C: return NDMAs[2]->SrcAddr;
case 0x04004140: return NDMAs[2]->DstAddr;
case 0x04004144: return NDMAs[2]->TotalLength;
case 0x04004148: return NDMAs[2]->BlockLength;
case 0x0400414C: return NDMAs[2]->SubblockTimer;
case 0x04004150: return NDMAs[2]->FillData;
case 0x04004154: return NDMAs[2]->Cnt;
case 0x04004158: return NDMAs[3]->SrcAddr;
case 0x0400415C: return NDMAs[3]->DstAddr;
case 0x04004160: return NDMAs[3]->TotalLength;
case 0x04004164: return NDMAs[3]->BlockLength;
case 0x04004168: return NDMAs[3]->SubblockTimer;
case 0x0400416C: return NDMAs[3]->FillData;
case 0x04004170: return NDMAs[3]->Cnt;
}
return NDS::ARM9IORead32(addr);
@ -899,6 +981,35 @@ void ARM9IOWrite32(u32 addr, u32 val)
{
switch (addr)
{
case 0x04004100: NDMACnt[0] = val & 0x800F0000; return;
case 0x04004104: NDMAs[0]->SrcAddr = val & 0xFFFFFFFC; return;
case 0x04004108: NDMAs[0]->DstAddr = val & 0xFFFFFFFC; return;
case 0x0400410C: NDMAs[0]->TotalLength = val & 0x0FFFFFFF; return;
case 0x04004110: NDMAs[0]->BlockLength = val & 0x00FFFFFF; return;
case 0x04004114: NDMAs[0]->SubblockTimer = val & 0x0003FFFF; return;
case 0x04004118: NDMAs[0]->FillData = val; return;
case 0x0400411C: NDMAs[0]->WriteCnt(val); return;
case 0x04004120: NDMAs[1]->SrcAddr = val & 0xFFFFFFFC; return;
case 0x04004124: NDMAs[1]->DstAddr = val & 0xFFFFFFFC; return;
case 0x04004128: NDMAs[1]->TotalLength = val & 0x0FFFFFFF; return;
case 0x0400412C: NDMAs[1]->BlockLength = val & 0x00FFFFFF; return;
case 0x04004130: NDMAs[1]->SubblockTimer = val & 0x0003FFFF; return;
case 0x04004134: NDMAs[1]->FillData = val; return;
case 0x04004138: NDMAs[1]->WriteCnt(val); return;
case 0x0400413C: NDMAs[2]->SrcAddr = val & 0xFFFFFFFC; return;
case 0x04004140: NDMAs[2]->DstAddr = val & 0xFFFFFFFC; return;
case 0x04004144: NDMAs[2]->TotalLength = val & 0x0FFFFFFF; return;
case 0x04004148: NDMAs[2]->BlockLength = val & 0x00FFFFFF; return;
case 0x0400414C: NDMAs[2]->SubblockTimer = val & 0x0003FFFF; return;
case 0x04004150: NDMAs[2]->FillData = val; return;
case 0x04004154: NDMAs[2]->WriteCnt(val); return;
case 0x04004158: NDMAs[3]->SrcAddr = val & 0xFFFFFFFC; return;
case 0x0400415C: NDMAs[3]->DstAddr = val & 0xFFFFFFFC; return;
case 0x04004160: NDMAs[3]->TotalLength = val & 0x0FFFFFFF; return;
case 0x04004164: NDMAs[3]->BlockLength = val & 0x00FFFFFF; return;
case 0x04004168: NDMAs[3]->SubblockTimer = val & 0x0003FFFF; return;
case 0x0400416C: NDMAs[3]->FillData = val; return;
case 0x04004170: NDMAs[3]->WriteCnt(val); return;
}
return NDS::ARM9IOWrite32(addr, val);
@ -950,6 +1061,36 @@ u32 ARM7IORead32(u32 addr)
case 0x0400021C: return NDS::IF2;
case 0x04004008: return 0x80000000; // HAX
case 0x04004100: return NDMACnt[1];
case 0x04004104: return NDMAs[4]->SrcAddr;
case 0x04004108: return NDMAs[4]->DstAddr;
case 0x0400410C: return NDMAs[4]->TotalLength;
case 0x04004110: return NDMAs[4]->BlockLength;
case 0x04004114: return NDMAs[4]->SubblockTimer;
case 0x04004118: return NDMAs[4]->FillData;
case 0x0400411C: return NDMAs[4]->Cnt;
case 0x04004120: return NDMAs[5]->SrcAddr;
case 0x04004124: return NDMAs[5]->DstAddr;
case 0x04004128: return NDMAs[5]->TotalLength;
case 0x0400412C: return NDMAs[5]->BlockLength;
case 0x04004130: return NDMAs[5]->SubblockTimer;
case 0x04004134: return NDMAs[5]->FillData;
case 0x04004138: return NDMAs[5]->Cnt;
case 0x0400413C: return NDMAs[6]->SrcAddr;
case 0x04004140: return NDMAs[6]->DstAddr;
case 0x04004144: return NDMAs[6]->TotalLength;
case 0x04004148: return NDMAs[6]->BlockLength;
case 0x0400414C: return NDMAs[6]->SubblockTimer;
case 0x04004150: return NDMAs[6]->FillData;
case 0x04004154: return NDMAs[6]->Cnt;
case 0x04004158: return NDMAs[7]->SrcAddr;
case 0x0400415C: return NDMAs[7]->DstAddr;
case 0x04004160: return NDMAs[7]->TotalLength;
case 0x04004164: return NDMAs[7]->BlockLength;
case 0x04004168: return NDMAs[7]->SubblockTimer;
case 0x0400416C: return NDMAs[7]->FillData;
case 0x04004170: return NDMAs[7]->Cnt;
}
if (addr >= 0x04004800 && addr < 0x04004A00)
@ -1003,6 +1144,36 @@ void ARM7IOWrite32(u32 addr, u32 val)
{
case 0x04000218: NDS::IE2 = (val & 0x7FF7); NDS::UpdateIRQ(1); return;
case 0x0400021C: NDS::IF2 &= ~(val & 0x7FF7); NDS::UpdateIRQ(1); return;
case 0x04004100: NDMACnt[1] = val & 0x800F0000; return;
case 0x04004104: NDMAs[4]->SrcAddr = val & 0xFFFFFFFC; return;
case 0x04004108: NDMAs[4]->DstAddr = val & 0xFFFFFFFC; return;
case 0x0400410C: NDMAs[4]->TotalLength = val & 0x0FFFFFFF; return;
case 0x04004110: NDMAs[4]->BlockLength = val & 0x00FFFFFF; return;
case 0x04004114: NDMAs[4]->SubblockTimer = val & 0x0003FFFF; return;
case 0x04004118: NDMAs[4]->FillData = val; return;
case 0x0400411C: NDMAs[4]->WriteCnt(val); return;
case 0x04004120: NDMAs[5]->SrcAddr = val & 0xFFFFFFFC; return;
case 0x04004124: NDMAs[5]->DstAddr = val & 0xFFFFFFFC; return;
case 0x04004128: NDMAs[5]->TotalLength = val & 0x0FFFFFFF; return;
case 0x0400412C: NDMAs[5]->BlockLength = val & 0x00FFFFFF; return;
case 0x04004130: NDMAs[5]->SubblockTimer = val & 0x0003FFFF; return;
case 0x04004134: NDMAs[5]->FillData = val; return;
case 0x04004138: NDMAs[5]->WriteCnt(val); return;
case 0x0400413C: NDMAs[6]->SrcAddr = val & 0xFFFFFFFC; return;
case 0x04004140: NDMAs[6]->DstAddr = val & 0xFFFFFFFC; return;
case 0x04004144: NDMAs[6]->TotalLength = val & 0x0FFFFFFF; return;
case 0x04004148: NDMAs[6]->BlockLength = val & 0x00FFFFFF; return;
case 0x0400414C: NDMAs[6]->SubblockTimer = val & 0x0003FFFF; return;
case 0x04004150: NDMAs[6]->FillData = val; return;
case 0x04004154: NDMAs[6]->WriteCnt(val); return;
case 0x04004158: NDMAs[7]->SrcAddr = val & 0xFFFFFFFC; return;
case 0x0400415C: NDMAs[7]->DstAddr = val & 0xFFFFFFFC; return;
case 0x04004160: NDMAs[7]->TotalLength = val & 0x0FFFFFFF; return;
case 0x04004164: NDMAs[7]->BlockLength = val & 0x00FFFFFF; return;
case 0x04004168: NDMAs[7]->SubblockTimer = val & 0x0003FFFF; return;
case 0x0400416C: NDMAs[7]->FillData = val; return;
case 0x04004170: NDMAs[7]->WriteCnt(val); return;
}
if (addr >= 0x04004800 && addr < 0x04004A00)

View File

@ -38,6 +38,10 @@ void Reset();
bool LoadBIOS();
bool LoadNAND();
void RunNDMAs(u32 cpu);
void StallNDMAs();
bool NDMAsRunning(u32 cpu);
void MapNWRAM_A(u32 num, u8 val);
void MapNWRAM_B(u32 num, u8 val);
void MapNWRAM_C(u32 num, u8 val);

View File

@ -15,3 +15,304 @@
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"
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)
printf("UNIMPLEMENTED ARM%d NDMA%d START MODE %02X, %08X->%08X\n", CPU?7:9, Num, StartMode, SrcAddr, DstAddr);
}
}
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 (IterCount > TotalRemCount)
IterCount = TotalRemCount;
if (Cnt & (1<<12)) CurDstAddr = DstAddr;
if (Cnt & (1<<15)) CurSrcAddr = SrcAddr;
printf("ARM%d NDMA%d %08X %02X %08X->%08X %d bytes\n", CPU?7:9, Num, Cnt, StartMode, CurSrcAddr, CurDstAddr, RemCount*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);
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--;
if (NDS::ARM7Timestamp >= NDS::ARM7Target) break;
}
Executing = false;
Stall = false;
if (RemCount)
{
if (IterCount == 0)
{
Running = 0;
NDS::ResumeCPU(1, 1<<(Num+4));
}
return;
}
if ((StartMode & 0x1F) == 0x10) // CHECKME
Cnt &= ~(1<<31);
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));
}

View File

@ -19,6 +19,78 @@
#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

View File

@ -827,6 +827,7 @@ u32 RunFrame()
if (!(CPUStop & 0x80000000)) DMAs[1]->Run();
if (!(CPUStop & 0x80000000)) DMAs[2]->Run();
if (!(CPUStop & 0x80000000)) DMAs[3]->Run();
DSi::RunNDMAs(0);
}
else
{
@ -849,6 +850,7 @@ u32 RunFrame()
DMAs[5]->Run();
DMAs[6]->Run();
DMAs[7]->Run();
DSi::RunNDMAs(1);
}
else
{
@ -1153,6 +1155,7 @@ void GXFIFOStall()
DMAs[1]->StallIfRunning();
DMAs[2]->StallIfRunning();
DMAs[3]->StallIfRunning();
DSi::StallNDMAs();
}
}
@ -1367,6 +1370,7 @@ bool DMAsRunning(u32 cpu)
if (DMAs[cpu+1]->IsRunning()) return true;
if (DMAs[cpu+2]->IsRunning()) return true;
if (DMAs[cpu+3]->IsRunning()) return true;
if (DSi::NDMAsRunning(cpu>>2)) return true;
return false;
}

View File

@ -101,7 +101,7 @@ enum
IRQ2_DSi_GPIO18_0 = 0,
IRQ2_DSi_GPIO18_1,
IRQ2_DSi_GPIO18_2,
IRQ2_DSi_Unused35,
IRQ2_DSi_Unused3,
IRQ2_DSi_GPIO33_0,
IRQ2_DSi_Headphone,
IRQ2_DSi_PowerButton,
@ -138,7 +138,6 @@ extern u64 ARM9Timestamp, ARM9Target;
extern u64 ARM7Timestamp, ARM7Target;
extern u32 ARM9ClockShift;
// hax
extern u32 IME[2];
extern u32 IE[2];
extern u32 IF[2];
@ -146,6 +145,8 @@ extern u32 IE2;
extern u32 IF2;
extern Timer Timers[8];
extern u32 CPUStop;
extern u16 PowerControl9;
extern u16 ExMemCnt[2];