aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ARM.cpp438
-rw-r--r--src/ARM.h399
-rw-r--r--src/ARMInterpreter.cpp30
-rw-r--r--src/ARMInterpreter_ALU.cpp208
-rw-r--r--src/ARMInterpreter_Branch.cpp3
-rw-r--r--src/ARMInterpreter_LoadStore.cpp216
-rw-r--r--src/CP15.cpp549
-rw-r--r--src/CRC32.cpp69
-rw-r--r--src/CRC32.h26
-rw-r--r--src/Config.cpp132
-rw-r--r--src/Config.h17
-rw-r--r--src/DMA.cpp241
-rw-r--r--src/DMA.h14
-rw-r--r--src/GPU2D.cpp102
-rw-r--r--src/GPU2D.h4
-rw-r--r--src/GPU3D.cpp521
-rw-r--r--src/GPU3D.h1
-rw-r--r--src/GPU3D_Soft.cpp43
-rw-r--r--src/NDS.cpp974
-rw-r--r--src/NDS.h41
-rw-r--r--src/NDSCart.cpp596
-rw-r--r--src/SPI.cpp51
-rw-r--r--src/SPI.h1
-rw-r--r--src/SPU.cpp46
-rw-r--r--src/SPU.h3
-rw-r--r--src/Savestate.h9
-rw-r--r--src/libui_sdl/DlgAudioSettings.cpp183
-rw-r--r--src/libui_sdl/DlgAudioSettings.h (renamed from src/CP15.h)27
-rw-r--r--src/libui_sdl/DlgEmuSettings.cpp11
-rw-r--r--src/libui_sdl/DlgInputConfig.cpp252
-rw-r--r--src/libui_sdl/DlgInputConfig.h2
-rw-r--r--src/libui_sdl/Platform.cpp4
-rw-r--r--src/libui_sdl/libui/ui.h2
-rw-r--r--src/libui_sdl/libui/unix/area.c26
-rw-r--r--src/libui_sdl/libui/unix/stddialogs.c3
-rw-r--r--src/libui_sdl/libui/unix/window.c7
-rw-r--r--src/libui_sdl/libui/windows/button.cpp19
-rw-r--r--src/libui_sdl/main.cpp494
-rw-r--r--src/melon_fopen.cpp176
-rw-r--r--src/melon_fopen.h29
-rw-r--r--src/version.h2
41 files changed, 4415 insertions, 1556 deletions
diff --git a/src/ARM.cpp b/src/ARM.cpp
index 2368a1b..f537fb4 100644
--- a/src/ARM.cpp
+++ b/src/ARM.cpp
@@ -22,6 +22,20 @@
#include "ARMInterpreter.h"
+// instruction timing notes
+//
+// * simple instruction: 1S (code)
+// * LDR: 1N+1N+1I (code/data/internal)
+// * STR: 1N+1N (code/data)
+// * LDM: 1N+1N+(n-1)S+1I
+// * STM: 1N+1N+(n-1)S
+// * MUL/etc: 1N+xI (code/internal)
+// * branch: 1N+1S (code/code) (pipeline refill)
+//
+// MUL/MLA seems to take 1I on ARM9
+
+
+
u32 ARM::ConditionTable[16] =
{
0xF0F0, // EQ
@@ -48,96 +62,7 @@ ARM::ARM(u32 num)
// well uh
Num = num;
- for (int i = 0; i < 16; i++)
- {
- Waitstates[0][i] = 1;
- Waitstates[1][i] = 1;
- Waitstates[2][i] = 1;
- Waitstates[3][i] = 1;
- }
-
- if (!num)
- {
- // ARM9
- Waitstates[0][0x2] = 1; // main RAM timing, assuming cache hit
- Waitstates[0][0x3] = 4;
- Waitstates[0][0x4] = 4;
- Waitstates[0][0x5] = 5;
- Waitstates[0][0x6] = 5;
- Waitstates[0][0x7] = 4;
- Waitstates[0][0x8] = 19;
- Waitstates[0][0x9] = 19;
- Waitstates[0][0xF] = 4;
-
- Waitstates[1][0x2] = 1;
- Waitstates[1][0x3] = 8;
- Waitstates[1][0x4] = 8;
- Waitstates[1][0x5] = 10;
- Waitstates[1][0x6] = 10;
- Waitstates[1][0x7] = 8;
- Waitstates[1][0x8] = 38;
- Waitstates[1][0x9] = 38;
- Waitstates[1][0xF] = 8;
-
- Waitstates[2][0x2] = 1;
- Waitstates[2][0x3] = 2;
- Waitstates[2][0x4] = 2;
- Waitstates[2][0x5] = 2;
- Waitstates[2][0x6] = 2;
- Waitstates[2][0x7] = 2;
- Waitstates[2][0x8] = 12;
- Waitstates[2][0x9] = 12;
- Waitstates[2][0xA] = 20;
- Waitstates[2][0xF] = 2;
-
- Waitstates[3][0x2] = 1;
- Waitstates[3][0x3] = 2;
- Waitstates[3][0x4] = 2;
- Waitstates[3][0x5] = 4;
- Waitstates[3][0x6] = 4;
- Waitstates[3][0x7] = 2;
- Waitstates[3][0x8] = 24;
- Waitstates[3][0x9] = 24;
- Waitstates[3][0xA] = 20;
- Waitstates[3][0xF] = 2;
- }
- else
- {
- // ARM7
- Waitstates[0][0x0] = 1;
- Waitstates[0][0x2] = 1;
- Waitstates[0][0x3] = 1;
- Waitstates[0][0x4] = 1;
- Waitstates[0][0x6] = 1;
- Waitstates[0][0x8] = 6;
- Waitstates[0][0x9] = 6;
-
- Waitstates[1][0x0] = 1;
- Waitstates[1][0x2] = 2;
- Waitstates[1][0x3] = 1;
- Waitstates[1][0x4] = 1;
- Waitstates[1][0x6] = 2;
- Waitstates[1][0x8] = 12;
- Waitstates[1][0x9] = 12;
-
- Waitstates[2][0x0] = 1;
- Waitstates[2][0x2] = 1;
- Waitstates[2][0x3] = 1;
- Waitstates[2][0x4] = 1;
- Waitstates[2][0x6] = 1;
- Waitstates[2][0x8] = 6;
- Waitstates[2][0x9] = 6;
- Waitstates[2][0xA] = 10;
-
- Waitstates[3][0x0] = 1;
- Waitstates[3][0x2] = 2;
- Waitstates[3][0x3] = 1;
- Waitstates[3][0x4] = 1;
- Waitstates[3][0x6] = 2;
- Waitstates[3][0x8] = 12;
- Waitstates[3][0x9] = 12;
- Waitstates[3][0xA] = 10;
- }
+ SetClockShift(0); // safe default
}
ARM::~ARM()
@@ -145,6 +70,16 @@ ARM::~ARM()
// dorp
}
+ARMv5::ARMv5() : ARM(0)
+{
+ //
+}
+
+ARMv4::ARMv4() : ARM(1)
+{
+ //
+}
+
void ARM::Reset()
{
Cycles = 0;
@@ -157,10 +92,19 @@ void ARM::Reset()
ExceptionBase = Num ? 0x00000000 : 0xFFFF0000;
+ CodeMem.Mem = NULL;
+
// zorp
JumpTo(ExceptionBase);
}
+void ARMv5::Reset()
+{
+ CP15Reset();
+ ARM::Reset();
+}
+
+
void ARM::DoSavestate(Savestate* file)
{
file->Section((char*)(Num ? "ARM7" : "ARM9"));
@@ -180,9 +124,45 @@ void ARM::DoSavestate(Savestate* file)
file->VarArray(NextInstr, 2*sizeof(u32));
file->Var32(&ExceptionBase);
+
+ if (!file->Saving)
+ {
+ if (!Num)
+ {
+ SetupCodeMem(R[15]); // should fix it
+ ((ARMv5*)this)->RegionCodeCycles = ((ARMv5*)this)->MemTimings[R[15] >> 12][0];
+ }
+ else
+ {
+ CodeRegion = R[15] >> 24;
+ CodeCycles = R[15] >> 15; // cheato
+ }
+ }
+}
+
+void ARMv5::DoSavestate(Savestate* file)
+{
+ ARM::DoSavestate(file);
+ CP15DoSavestate(file);
+}
+
+
+void ARM::SetupCodeMem(u32 addr)
+{
+ if (!Num)
+ {
+ ((ARMv5*)this)->GetCodeMemRegion(addr, &CodeMem);
+ }
+ else
+ {
+ // not sure it's worth it for the ARM7
+ // esp. as everything there generally runs on WRAM
+ // and due to how it's mapped, we can't use this optimization
+ //NDS::ARM7GetMemRegion(addr, false, &CodeMem);
+ }
}
-void ARM::JumpTo(u32 addr, bool restorecpsr)
+void ARMv5::JumpTo(u32 addr, bool restorecpsr)
{
if (restorecpsr)
{
@@ -195,21 +175,106 @@ void ARM::JumpTo(u32 addr, bool restorecpsr)
// aging cart debug crap
//if (addr == 0x0201764C) printf("capture test %d: R1=%08X\n", R[6], R[1]);
//if (addr == 0x020175D8) printf("capture test %d: res=%08X\n", R[6], R[0]);
+ // R0=DMA# R1=src R2=size
+
+ u32 oldregion = R[15] >> 24;
+ u32 newregion = addr >> 24;
+
+ RegionCodeCycles = MemTimings[addr >> 12][0];
+
+ s32 cycles;
+
+ if (addr & 0x1)
+ {
+ addr &= ~0x1;
+ R[15] = addr+2;
+
+ if (newregion != oldregion) SetupCodeMem(addr);
+
+ // two-opcodes-at-once fetch
+ // doesn't matter if we put garbage in the MSbs there
+ if (addr & 0x2)
+ {
+ NextInstr[0] = CodeRead32(addr-2) >> 16;
+ NextInstr[1] = CodeRead32(addr+2);
+ cycles = CodeCycles * 2;
+ }
+ else
+ {
+ NextInstr[0] = CodeRead32(addr);
+ NextInstr[1] = NextInstr[0] >> 16;
+ cycles = CodeCycles;
+ }
+
+ CPSR |= 0x20;
+ }
+ else
+ {
+ addr &= ~0x3;
+ R[15] = addr+4;
+
+ if (newregion != oldregion) SetupCodeMem(addr);
+
+ NextInstr[0] = CodeRead32(addr);
+ NextInstr[1] = CodeRead32(addr+4);
+ cycles = CodeCycles * 2;
+
+ 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))
+ {
+ printf("jumped to %08X. very bad\n", addr);
+ PrefetchAbort();
+ return;
+ }*/
+
+ Cycles += cycles;
+}
+
+void ARMv4::JumpTo(u32 addr, bool restorecpsr)
+{
+ if (restorecpsr)
+ {
+ RestoreCPSR();
+
+ if (CPSR & 0x20) addr |= 0x1;
+ else addr &= ~0x1;
+ }
+
+ u32 oldregion = R[15] >> 23;
+ u32 newregion = addr >> 23;
+
+ CodeRegion = addr >> 24;
+ CodeCycles = addr >> 15; // cheato
if (addr & 0x1)
{
addr &= ~0x1;
R[15] = addr+2;
+
+ //if (newregion != oldregion) SetupCodeMem(addr);
+
NextInstr[0] = CodeRead16(addr);
NextInstr[1] = CodeRead16(addr+2);
+ Cycles += NDS::ARM7MemTimings[CodeCycles][0] + NDS::ARM7MemTimings[CodeCycles][1];
+
CPSR |= 0x20;
}
else
{
addr &= ~0x3;
R[15] = addr+4;
+
+ //if (newregion != oldregion) SetupCodeMem(addr);
+
NextInstr[0] = CodeRead32(addr);
NextInstr[1] = CodeRead32(addr+4);
+ Cycles += NDS::ARM7MemTimings[CodeCycles][2] + NDS::ARM7MemTimings[CodeCycles][3];
+
CPSR &= ~0x20;
}
}
@@ -322,6 +387,15 @@ void ARM::UpdateMode(u32 oldmode, u32 newmode)
}
#undef SWAP
+
+ if (Num == 0)
+ {
+ /*if ((newmode & 0x1F) == 0x16)
+ ((ARMv5*)this)->PU_Map = ((ARMv5*)this)->PU_UserMap;
+ else
+ ((ARMv5*)this)->PU_Map = ((ARMv5*)this)->PU_PrivMap;*/
+ //if ((newmode & 0x1F) == 0x10) printf("!! USER MODE\n");
+ }
}
void ARM::TriggerIRQ()
@@ -339,7 +413,44 @@ void ARM::TriggerIRQ()
JumpTo(ExceptionBase + 0x18);
}
-s32 ARM::Execute()
+void ARMv5::PrefetchAbort()
+{
+ printf("prefetch abort\n");
+
+ u32 oldcpsr = CPSR;
+ CPSR &= ~0xBF;
+ CPSR |= 0x97;
+ UpdateMode(oldcpsr, CPSR);
+
+ // this shouldn't happen, but if it does, we're stuck in some nasty endless loop
+ // so better take care of it
+ if (!(PU_Map[ExceptionBase>>12] & 0x04))
+ {
+ printf("!!!!! EXCEPTION REGION NOT READABLE. THIS IS VERY BAD!!\n");
+ NDS::Stop();
+ return;
+ }
+
+ R_IRQ[2] = oldcpsr;
+ R[14] = R[15] + (oldcpsr & 0x20 ? 2 : 0);
+ JumpTo(ExceptionBase + 0x0C);
+}
+
+void ARMv5::DataAbort()
+{
+ printf("data abort\n");
+
+ u32 oldcpsr = CPSR;
+ CPSR &= ~0xBF;
+ CPSR |= 0x97;
+ UpdateMode(oldcpsr, CPSR);
+
+ R_IRQ[2] = oldcpsr;
+ R[14] = R[15] + (oldcpsr & 0x20 ? 6 : 4);
+ JumpTo(ExceptionBase + 0x10);
+}
+
+s32 ARMv5::Execute()
{
if (Halted)
{
@@ -347,19 +458,19 @@ s32 ARM::Execute()
{
Halted = 0;
}
- else if (NDS::HaltInterrupted(Num))
+ else if (NDS::HaltInterrupted(0))
{
Halted = 0;
- if (NDS::IME[Num] & 0x1)
+ if (NDS::IME[0] & 0x1)
TriggerIRQ();
}
else
{
Cycles = CyclesToRun;
-
- if (Num == 0) NDS::RunTimingCriticalDevices(0, CyclesToRun >> 1);
- else NDS::RunTimingCriticalDevices(1, CyclesToRun);
-
+#ifdef DEBUG_CHECK_DESYNC
+ NDS::dbg_CyclesARM9 += (CyclesToRun >> ClockShift);
+#endif // DEBUG_CHECK_DESYNC
+ //NDS::RunTightTimers(0, CyclesToRun >> ClockShift);
return Cycles;
}
}
@@ -375,10 +486,11 @@ s32 ARM::Execute()
R[15] += 2;
CurInstr = NextInstr[0];
NextInstr[0] = NextInstr[1];
- NextInstr[1] = CodeRead16(R[15]);
+ if (R[15] & 0x2) { NextInstr[1] >>= 16; CodeCycles = 0; }
+ else NextInstr[1] = CodeRead32(R[15]);
// actually execute
- u32 icode = (CurInstr >> 6);
+ u32 icode = (CurInstr >> 6) & 0x3FF;
ARMInterpreter::THUMBInstrTable[icode](this);
}
else
@@ -399,31 +511,127 @@ s32 ARM::Execute()
{
ARMInterpreter::A_BLX_IMM(this);
}
+ else
+ AddCycles_C();
+ }
+
+ //s32 diff = Cycles - lastcycles;arm9timer+=(diff>>1);
+ //NDS::RunTightTimers(0, diff >> ClockShift);
+ //lastcycles = Cycles - (diff & ClockDiffMask);
+
+ // TODO optimize this shit!!!
+ if (Halted)
+ {
+ if (Halted == 1 && Cycles < CyclesToRun)
+ {
+ //s32 diff = CyclesToRun - Cycles;
+ Cycles = CyclesToRun;
+ //NDS::RunTightTimers(0, diff >> ClockShift);
+ //arm9timer += (diff>>1);
+ }
+ break;
+ }
+ if (NDS::IF[0] & NDS::IE[0])
+ {
+ if (NDS::IME[0] & 0x1)
+ TriggerIRQ();
+ }
+ }
+
+ if (Halted == 2)
+ Halted = 0;
+
+ /*if (Cycles > lastcycles)
+ {
+ //s32 diff = Cycles - lastcycles;arm9timer+=(diff>>1);
+ //NDS::RunTightTimers(0, diff >> ClockShift);
+ }*/
+#ifdef DEBUG_CHECK_DESYNC
+ NDS::dbg_CyclesARM9 += (Cycles >> ClockShift);
+#endif // DEBUG_CHECK_DESYNC
+
+ return Cycles;
+}
+
+s32 ARMv4::Execute()
+{
+ if (Halted)
+ {
+ if (Halted == 2)
+ {
+ Halted = 0;
}
+ else if (NDS::HaltInterrupted(1))
+ {
+ Halted = 0;
+ if (NDS::IME[1] & 0x1)
+ TriggerIRQ();
+ }
+ else
+ {
+ Cycles = CyclesToRun;
+#ifdef DEBUG_CHECK_DESYNC
+ NDS::dbg_CyclesARM7 += CyclesToRun;
+#endif // DEBUG_CHECK_DESYNC
+ //NDS::RunTightTimers(1, CyclesToRun);
+ return Cycles;
+ }
+ }
+
+ Cycles = 0;
+ s32 lastcycles = 0;
- if (Num==0)
+ while (Cycles < CyclesToRun)
+ {
+ if (CPSR & 0x20) // THUMB
{
- s32 diff = Cycles - lastcycles;
- NDS::RunTimingCriticalDevices(0, diff >> 1);
- lastcycles = Cycles - (diff&1);
+ // prefetch
+ R[15] += 2;
+ CurInstr = NextInstr[0];
+ NextInstr[0] = NextInstr[1];
+ NextInstr[1] = CodeRead16(R[15]);
+
+ // actually execute
+ u32 icode = (CurInstr >> 6);
+ ARMInterpreter::THUMBInstrTable[icode](this);
}
else
{
- s32 diff = Cycles - lastcycles;
- NDS::RunTimingCriticalDevices(1, diff);
- lastcycles = Cycles;
+ // prefetch
+ R[15] += 4;
+ CurInstr = NextInstr[0];
+ NextInstr[0] = NextInstr[1];
+ NextInstr[1] = CodeRead32(R[15]);
+
+ // actually execute
+ if (CheckCondition(CurInstr >> 28))
+ {
+ u32 icode = ((CurInstr >> 4) & 0xF) | ((CurInstr >> 16) & 0xFF0);
+ ARMInterpreter::ARMInstrTable[icode](this);
+ }
+ else
+ AddCycles_C();
}
+ //s32 diff = Cycles - lastcycles;arm7timer+=diff;
+ //NDS::RunTightTimers(1, diff);
+ //lastcycles = Cycles;
+
// TODO optimize this shit!!!
if (Halted)
{
- if (Halted == 1)
+ if (Halted == 1 && Cycles < CyclesToRun)
+ {
+ //s32 diff = CyclesToRun - Cycles;
Cycles = CyclesToRun;
+ //NDS::RunTightTimers(1, diff);
+ //arm7timer += diff;
+ }
break;
}
- if (NDS::IF[Num] & NDS::IE[Num])
+ if (NDS::IF[1] & NDS::IE[1])
{
- if (NDS::IME[Num] & 0x1)
+ if (NDS::IME[1] & 0x1)
TriggerIRQ();
}
}
@@ -431,5 +639,15 @@ s32 ARM::Execute()
if (Halted == 2)
Halted = 0;
+ /*if (Cycles > lastcycles)
+ {
+ //s32 diff = Cycles - lastcycles;arm7timer+=(diff);
+ //NDS::RunTightTimers(1, diff);
+ }*/
+
+#ifdef DEBUG_CHECK_DESYNC
+ NDS::dbg_CyclesARM7 += Cycles;
+#endif // DEBUG_CHECK_DESYNC
+
return Cycles;
}
diff --git a/src/ARM.h b/src/ARM.h
index 92d47bb..9a9b03c 100644
--- a/src/ARM.h
+++ b/src/ARM.h
@@ -19,28 +19,36 @@
#ifndef ARM_H
#define ARM_H
+#include <algorithm>
+
#include "types.h"
#include "NDS.h"
-#include "CP15.h"
-
-// lame
-#define C_S(x) x
-#define C_N(x) x
-#define C_I(x) x
#define ROR(x, n) (((x) >> (n)) | ((x) << (32-(n))))
+enum
+{
+ RWFlags_Nonseq = (1<<5),
+ RWFlags_ForceUser = (1<<21),
+};
+
class ARM
{
public:
ARM(u32 num);
~ARM(); // destroy shit
- void Reset();
+ virtual void Reset();
- void DoSavestate(Savestate* file);
+ virtual void DoSavestate(Savestate* file);
- void JumpTo(u32 addr, bool restorecpsr = false);
+ void SetClockShift(u32 shift)
+ {
+ ClockShift = shift;
+ ClockDiffMask = (1<<shift) - 1;
+ }
+
+ virtual void JumpTo(u32 addr, bool restorecpsr = false) = 0;
void RestoreCPSR();
void Halt(u32 halt)
@@ -49,6 +57,7 @@ public:
Halted = halt;
}
+ // TODO: is this actually used??
void CheckIRQ()
{
if (!(NDS::IME[Num] & 0x1)) return;
@@ -58,7 +67,7 @@ public:
}
}
- s32 Execute();
+ virtual s32 Execute() = 0;
bool CheckCondition(u32 code)
{
@@ -93,154 +102,318 @@ public:
void TriggerIRQ();
+ void SetupCodeMem(u32 addr);
- u16 CodeRead16(u32 addr)
+
+ virtual void DataRead8(u32 addr, u32* val) = 0;
+ virtual void DataRead16(u32 addr, u32* val) = 0;
+ virtual void DataRead32(u32 addr, u32* val) = 0;
+ virtual void DataRead32S(u32 addr, u32* val) = 0;
+ virtual void DataWrite8(u32 addr, u8 val) = 0;
+ virtual void DataWrite16(u32 addr, u16 val) = 0;
+ virtual void DataWrite32(u32 addr, u32 val) = 0;
+ virtual void DataWrite32S(u32 addr, u32 val) = 0;
+
+ virtual void AddCycles_C() = 0;
+ virtual void AddCycles_CI(s32 numI) = 0;
+ virtual void AddCycles_CDI() = 0;
+ virtual void AddCycles_CD() = 0;
+
+
+ u32 Num;
+
+ // shift relative to system clock
+ // 0=33MHz 1=66MHz 2=133MHz
+ u32 ClockShift;
+ u32 ClockDiffMask;
+
+ s32 Cycles;
+ s32 CyclesToRun;
+ u32 Halted;
+
+ u32 CodeRegion;
+ s32 CodeCycles;
+
+ u32 DataRegion;
+ s32 DataCycles;
+
+ u32 R[16]; // heh
+ u32 CPSR;
+ u32 R_FIQ[8]; // holding SPSR too
+ u32 R_SVC[3];
+ u32 R_ABT[3];
+ u32 R_IRQ[3];
+ u32 R_UND[3];
+ u32 CurInstr;
+ u32 NextInstr[2];
+
+ u32 ExceptionBase;
+
+ NDS::MemRegion CodeMem;
+
+ static u32 ConditionTable[16];
+};
+
+class ARMv5 : public ARM
+{
+public:
+ ARMv5();
+
+ void Reset();
+
+ void DoSavestate(Savestate* file);
+
+ void UpdateRegionTimings(u32 addrstart, u32 addrend);
+
+ void JumpTo(u32 addr, bool restorecpsr = false);
+
+ void PrefetchAbort();
+ void DataAbort();
+
+ s32 Execute();
+
+ // all code accesses are forced nonseq 32bit
+ u32 CodeRead32(u32 addr);
+
+ void DataRead8(u32 addr, u32* val);
+ void DataRead16(u32 addr, u32* val);
+ void DataRead32(u32 addr, u32* val);
+ void DataRead32S(u32 addr, u32* val);
+ void DataWrite8(u32 addr, u8 val);
+ void DataWrite16(u32 addr, u16 val);
+ void DataWrite32(u32 addr, u32 val);
+ void DataWrite32S(u32 addr, u32 val);
+
+ void AddCycles_C()
{
- u16 val;
- // TODO eventually: on ARM9, THUMB opcodes are prefetched with 32bit reads
- if (!Num)
- {
- if (!CP15::HandleCodeRead16(addr, &val))
- val = NDS::ARM9Read16(addr);
- }
- else
- val = NDS::ARM7Read16(addr);
+ // code only. always nonseq 32-bit for ARM9.
+ s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
+ Cycles += numC;
+ }
- Cycles += Waitstates[0][(addr>>24)&0xF];
- return val;
+ void AddCycles_CI(s32 numI)
+ {
+ // code+internal
+ s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
+ Cycles += numC + numI;
}
- u32 CodeRead32(u32 addr)
+ void AddCycles_CDI()
{
- u32 val;
- if (!Num)
- {
- if (!CP15::HandleCodeRead32(addr, &val))
- val = NDS::ARM9Read32(addr);
- }
- else
- val = NDS::ARM7Read32(addr);
+ // LDR/LDM cycles. ARM9 seems to skip the internal cycle there.
+ // TODO: ITCM data fetches shouldn't be parallelized, they say
+ s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
+ s32 numD = DataCycles;
+
+ //if (DataRegion != CodeRegion)
+ Cycles += std::max(numC + numD - 6, std::max(numC, numD));
+ //else
+ // Cycles += numC + numD;
+ }
- Cycles += Waitstates[1][(addr>>24)&0xF];
- return val;
+ void AddCycles_CD()
+ {
+ // TODO: ITCM data fetches shouldn't be parallelized, they say
+ s32 numC = (R[15] & 0x2) ? 0 : CodeCycles;
+ s32 numD = DataCycles;
+
+ //if (DataRegion != CodeRegion)
+ Cycles += std::max(numC + numD - 6, std::max(numC, numD));
+ //else
+ // Cycles += numC + numD;
}
+ void GetCodeMemRegion(u32 addr, NDS::MemRegion* region);
+
+ void CP15Reset();
+ void CP15DoSavestate(Savestate* file);
+
+ void UpdateDTCMSetting();
+ void UpdateITCMSetting();
+
+ void UpdatePURegions();
+
+ void CP15Write(u32 id, u32 val);
+ u32 CP15Read(u32 id);
+
+ u32 CP15Control;
+
+ u32 DTCMSetting, ITCMSetting;
+
+ u8 ITCM[0x8000];
+ u32 ITCMSize;
+ u8 DTCM[0x4000];
+ u32 DTCMBase, DTCMSize;
+
+ u32 PU_CodeCacheable;
+ u32 PU_DataCacheable;
+ u32 PU_DataCacheWrite;
+
+ u32 PU_CodeRW;
+ u32 PU_DataRW;
+
+ u32 PU_Region[8];
+
+ // 0=dataR 1=dataW 2=codeR 4=datacache 5=datawrite 6=codecache
+ u8 PU_PrivMap[0x100000];
+ u8 PU_UserMap[0x100000];
+
+ // games operate under system mode, generally
+ #define PU_Map PU_PrivMap
- u8 DataRead8(u32 addr, u32 forceuser=0)
+ // code/16N/32N/32S
+ u8 MemTimings[0x100000][4];
+
+ s32 RegionCodeCycles;
+};
+
+class ARMv4 : public ARM
+{
+public:
+ ARMv4();
+
+ void JumpTo(u32 addr, bool restorecpsr = false);
+
+ s32 Execute();
+
+ u16 CodeRead16(u32 addr)
{
- u8 val;
- if (!Num)
- {
- if (!CP15::HandleDataRead8(addr, &val, forceuser))
- val = NDS::ARM9Read8(addr);
- }
- else
- val = NDS::ARM7Read8(addr);
+ return NDS::ARM7Read16(addr);
+ }
- Cycles += Waitstates[2][(addr>>24)&0xF];
- return val;
+ u32 CodeRead32(u32 addr)
+ {
+ return NDS::ARM7Read32(addr);
+ }
+
+ void DataRead8(u32 addr, u32* val)
+ {
+ *val = NDS::ARM7Read8(addr);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
- u16 DataRead16(u32 addr, u32 forceuser=0)
+ void DataRead16(u32 addr, u32* val)
{
- u16 val;
addr &= ~1;
- if (!Num)
- {
- if (!CP15::HandleDataRead16(addr, &val, forceuser))
- val = NDS::ARM9Read16(addr);
- }
- else
- val = NDS::ARM7Read16(addr);
- Cycles += Waitstates[2][(addr>>24)&0xF];
- return val;
+ *val = NDS::ARM7Read16(addr);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
- u32 DataRead32(u32 addr, u32 forceuser=0)
+ void DataRead32(u32 addr, u32* val)
{
- u32 val;
addr &= ~3;
- if (!Num)
- {
- if (!CP15::HandleDataRead32(addr, &val, forceuser))
- val = NDS::ARM9Read32(addr);
- }
- else
- val = NDS::ARM7Read32(addr);
- Cycles += Waitstates[3][(addr>>24)&0xF];
- return val;
+ *val = NDS::ARM7Read32(addr);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][2];
}
- void DataWrite8(u32 addr, u8 val, u32 forceuser=0)
+ void DataRead32S(u32 addr, u32* val)
{
- if (!Num)
- {
- if (!CP15::HandleDataWrite8(addr, val, forceuser))
- NDS::ARM9Write8(addr, val);
- }
- else
- NDS::ARM7Write8(addr, val);
+ addr &= ~3;
- Cycles += Waitstates[2][(addr>>24)&0xF];
+ *val = NDS::ARM7Read32(addr);
+ DataCycles += NDS::ARM7MemTimings[DataRegion][3];
}
- void DataWrite16(u32 addr, u16 val, u32 forceuser=0)
+ void DataWrite8(u32 addr, u8 val)
+ {
+ NDS::ARM7Write8(addr, val);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
+ }
+
+ void DataWrite16(u32 addr, u16 val)
{
addr &= ~1;
- if (!Num)
- {
- if (!CP15::HandleDataWrite16(addr, val, forceuser))
- NDS::ARM9Write16(addr, val);
- }
- else
- NDS::ARM7Write16(addr, val);
- Cycles += Waitstates[2][(addr>>24)&0xF];
+ NDS::ARM7Write16(addr, val);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
- void DataWrite32(u32 addr, u32 val, u32 forceuser=0)
+ void DataWrite32(u32 addr, u32 val)
{
addr &= ~3;
- if (!Num)
- {
- if (!CP15::HandleDataWrite32(addr, val, forceuser))
- NDS::ARM9Write32(addr, val);
- }
- else
- NDS::ARM7Write32(addr, val);
- Cycles += Waitstates[3][(addr>>24)&0xF];
+ NDS::ARM7Write32(addr, val);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][2];
}
+ void DataWrite32S(u32 addr, u32 val)
+ {
+ addr &= ~3;
+
+ NDS::ARM7Write32(addr, val);
+ DataCycles += NDS::ARM7MemTimings[DataRegion][3];
+ }
- u32 Num;
- // waitstates:
- // 0=code16 1=code32 2=data16 3=data32
- // TODO eventually: nonsequential waitstates
- s32 Waitstates[4][16];
+ void AddCycles_C()
+ {
+ // code only. this code fetch is sequential.
+ Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3];
+ }
- s32 Cycles;
- s32 CyclesToRun;
- u32 Halted;
+ void AddCycles_CI(s32 num)
+ {
+ // code+internal. results in a nonseq code fetch.
+ Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num;
+ }
- u32 R[16]; // heh
- u32 CPSR;
- u32 R_FIQ[8]; // holding SPSR too
- u32 R_SVC[3];
- u32 R_ABT[3];
- u32 R_IRQ[3];
- u32 R_UND[3];
- u32 CurInstr;
- u32 NextInstr[2];
+ void AddCycles_CDI()
+ {
+ // LDR/LDM cycles.
+ s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
+ s32 numD = DataCycles;
- u32 ExceptionBase;
+ if (DataRegion == 0x02) // mainRAM
+ {
+ if (CodeRegion == 0x02)
+ Cycles += numC + numD;
+ else
+ {
+ numC++;
+ Cycles += std::max(numC + numD - 3, std::max(numC, numD));
+ }
+ }
+ else if (CodeRegion == 0x02)
+ {
+ numD++;
+ Cycles += std::max(numC + numD - 3, std::max(numC, numD));
+ }
+ else
+ {
+ Cycles += numC + numD + 1;
+ }
+ }
- static u32 ConditionTable[16];
+ void AddCycles_CD()
+ {
+ // TODO: max gain should be 5c when writing to mainRAM
+ s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
+ s32 numD = DataCycles;
- u32 debug;
+ if (DataRegion == 0x02)
+ {
+ if (CodeRegion == 0x02)
+ Cycles += numC + numD;
+ else
+ Cycles += std::max(numC + numD - 3, std::max(numC, numD));
+ }
+ else if (CodeRegion == 0x02)
+ {
+ Cycles += std::max(numC + numD - 3, std::max(numC, numD));
+ }
+ else
+ {
+ Cycles += numC + numD;
+ }
+ }
};
namespace ARMInterpreter
diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp
index 32b5658..1312771 100644
--- a/src/ARMInterpreter.cpp
+++ b/src/ARMInterpreter.cpp
@@ -18,7 +18,6 @@
#include <stdio.h>
#include "NDS.h"
-#include "CP15.h"
#include "ARMInterpreter.h"
#include "ARMInterpreter_ALU.h"
#include "ARMInterpreter_Branch.h"
@@ -35,8 +34,8 @@ void A_UNK(ARM* cpu)
//for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]);
//NDS::Halt();
u32 oldcpsr = cpu->CPSR;
- cpu->CPSR &= ~0xFF;
- cpu->CPSR |= 0xDB;
+ cpu->CPSR &= ~0xBF;
+ cpu->CPSR |= 0x9B;
cpu->UpdateMode(oldcpsr, cpu->CPSR);
cpu->R_UND[2] = oldcpsr;
@@ -49,8 +48,8 @@ void T_UNK(ARM* cpu)
printf("undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4);
//NDS::Halt();
u32 oldcpsr = cpu->CPSR;
- cpu->CPSR &= ~0xFF;
- cpu->CPSR |= 0xDB;
+ cpu->CPSR &= ~0xBF;
+ cpu->CPSR |= 0x9B;
cpu->UpdateMode(oldcpsr, cpu->CPSR);
cpu->R_UND[2] = oldcpsr;
@@ -98,6 +97,8 @@ void A_MSR_IMM(ARM* cpu)
if (!(cpu->CurInstr & (1<<22)))
cpu->UpdateMode(oldpsr, cpu->CPSR);
+
+ cpu->AddCycles_C();
}
void A_MSR_REG(ARM* cpu)
@@ -138,6 +139,8 @@ void A_MSR_REG(ARM* cpu)
if (!(cpu->CurInstr & (1<<22)))
cpu->UpdateMode(oldpsr, cpu->CPSR);
+
+ cpu->AddCycles_C();
}
void A_MRS(ARM* cpu)
@@ -159,6 +162,7 @@ void A_MRS(ARM* cpu)
psr = cpu->CPSR;
cpu->R[(cpu->CurInstr>>12) & 0xF] = psr;
+ cpu->AddCycles_C();
}
@@ -172,7 +176,7 @@ void A_MCR(ARM* cpu)
if (cpu->Num==0 && cp==15)
{
- CP15::Write((cn<<8)|(cm<<4)|cpinfo, cpu->R[(cpu->CurInstr>>12)&0xF]);
+ ((ARMv5*)cpu)->CP15Write((cn<<8)|(cm<<4)|cpinfo, cpu->R[(cpu->CurInstr>>12)&0xF]);
}
else if (cpu->Num==1 && cp==14)
{
@@ -184,7 +188,7 @@ void A_MCR(ARM* cpu)
return A_UNK(cpu); // TODO: check what kind of exception it really is
}
- cpu->Cycles += 2; // TODO: checkme
+ cpu->AddCycles_CI(1 + 1); // TODO: checkme
}
void A_MRC(ARM* cpu)
@@ -197,7 +201,7 @@ void A_MRC(ARM* cpu)
if (cpu->Num==0 && cp==15)
{
- cpu->R[(cpu->CurInstr>>12)&0xF] = CP15::Read((cn<<8)|(cm<<4)|cpinfo);
+ cpu->R[(cpu->CurInstr>>12)&0xF] = ((ARMv5*)cpu)->CP15Read((cn<<8)|(cm<<4)|cpinfo);
}
else if (cpu->Num==1 && cp==14)
{
@@ -209,7 +213,7 @@ void A_MRC(ARM* cpu)
return A_UNK(cpu); // TODO: check what kind of exception it really is
}
- cpu->Cycles += 3; // TODO: checkme
+ cpu->AddCycles_CI(2 + 1); // TODO: checkme
}
@@ -217,8 +221,8 @@ void A_MRC(ARM* cpu)
void A_SVC(ARM* cpu)
{
u32 oldcpsr = cpu->CPSR;
- cpu->CPSR &= ~0xFF;
- cpu->CPSR |= 0xD3;
+ cpu->CPSR &= ~0xBF;
+ cpu->CPSR |= 0x93;
cpu->UpdateMode(oldcpsr, cpu->CPSR);
cpu->R_SVC[2] = oldcpsr;
@@ -229,8 +233,8 @@ void A_SVC(ARM* cpu)
void T_SVC(ARM* cpu)
{
u32 oldcpsr = cpu->CPSR;
- cpu->CPSR &= ~0xFF;
- cpu->CPSR |= 0xD3;
+ cpu->CPSR &= ~0xBF;
+ cpu->CPSR |= 0x93;
cpu->UpdateMode(oldcpsr, cpu->CPSR);
cpu->R_SVC[2] = oldcpsr;
diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp
index 58bf94d..f70763d 100644
--- a/src/ARMInterpreter_ALU.cpp
+++ b/src/ARMInterpreter_ALU.cpp
@@ -282,7 +282,7 @@ void A_##x##_REG_ROR_REG(ARM* cpu) \
#define A_AND(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = a & b; \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -297,7 +297,7 @@ void A_##x##_REG_ROR_REG(ARM* cpu) \
u32 res = a & b; \
cpu->SetNZ(res & 0x80000000, \
!res); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -313,7 +313,7 @@ A_IMPLEMENT_ALU_OP(AND,_S)
#define A_EOR(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = a ^ b; \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -328,7 +328,7 @@ A_IMPLEMENT_ALU_OP(AND,_S)
u32 res = a ^ b; \
cpu->SetNZ(res & 0x80000000, \
!res); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -344,7 +344,7 @@ A_IMPLEMENT_ALU_OP(EOR,_S)
#define A_SUB(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = a - b; \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -361,7 +361,7 @@ A_IMPLEMENT_ALU_OP(EOR,_S)
!res, \
CARRY_SUB(a, b), \
OVERFLOW_SUB(a, b, res)); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -377,7 +377,7 @@ A_IMPLEMENT_ALU_OP(SUB,)
#define A_RSB(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = b - a; \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -394,7 +394,7 @@ A_IMPLEMENT_ALU_OP(SUB,)
!res, \
CARRY_SUB(b, a), \
OVERFLOW_SUB(b, a, res)); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -410,7 +410,7 @@ A_IMPLEMENT_ALU_OP(RSB,)
#define A_ADD(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = a + b; \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -427,7 +427,7 @@ A_IMPLEMENT_ALU_OP(RSB,)
!res, \
CARRY_ADD(a, b), \
OVERFLOW_ADD(a, b, res)); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -443,7 +443,7 @@ A_IMPLEMENT_ALU_OP(ADD,)
#define A_ADC(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = a + b + (cpu->CPSR&0x20000000 ? 1:0); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -462,7 +462,7 @@ A_IMPLEMENT_ALU_OP(ADD,)
!res, \
CARRY_ADD(a, b) | CARRY_ADD(res_tmp, carry), \
OVERFLOW_ADD(a, b, res_tmp) | OVERFLOW_ADD(res_tmp, carry, res)); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -478,7 +478,7 @@ A_IMPLEMENT_ALU_OP(ADC,)
#define A_SBC(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = a - b - (cpu->CPSR&0x20000000 ? 0:1); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -497,7 +497,7 @@ A_IMPLEMENT_ALU_OP(ADC,)
!res, \
CARRY_SUB(a, b) & CARRY_SUB(res_tmp, carry), \
OVERFLOW_SUB(a, b, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res)); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -513,7 +513,7 @@ A_IMPLEMENT_ALU_OP(SBC,)
#define A_RSC(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = b - a - (cpu->CPSR&0x20000000 ? 0:1); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -532,7 +532,7 @@ A_IMPLEMENT_ALU_OP(SBC,)
!res, \
CARRY_SUB(b, a) & CARRY_SUB(res_tmp, carry), \
OVERFLOW_SUB(b, a, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res)); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -550,7 +550,7 @@ A_IMPLEMENT_ALU_OP(RSC,)
u32 res = a & b; \
cpu->SetNZ(res & 0x80000000, \
!res); \
- cpu->Cycles += c;
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
A_IMPLEMENT_ALU_TEST(TST,_S)
@@ -560,7 +560,7 @@ A_IMPLEMENT_ALU_TEST(TST,_S)
u32 res = a ^ b; \
cpu->SetNZ(res & 0x80000000, \
!res); \
- cpu->Cycles += c;
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
A_IMPLEMENT_ALU_TEST(TEQ,_S)
@@ -572,7 +572,7 @@ A_IMPLEMENT_ALU_TEST(TEQ,_S)
!res, \
CARRY_SUB(a, b), \
OVERFLOW_SUB(a, b, res)); \
- cpu->Cycles += c;
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
A_IMPLEMENT_ALU_TEST(CMP,)
@@ -584,7 +584,7 @@ A_IMPLEMENT_ALU_TEST(CMP,)
!res, \
CARRY_ADD(a, b), \
OVERFLOW_ADD(a, b, res)); \
- cpu->Cycles += c;
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C();
A_IMPLEMENT_ALU_TEST(CMN,)
@@ -592,7 +592,7 @@ A_IMPLEMENT_ALU_TEST(CMN,)
#define A_ORR(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = a | b; \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -607,7 +607,7 @@ A_IMPLEMENT_ALU_TEST(CMN,)
u32 res = a | b; \
cpu->SetNZ(res & 0x80000000, \
!res); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -621,7 +621,7 @@ A_IMPLEMENT_ALU_OP(ORR,_S)
#define A_MOV(c) \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(b); \
@@ -634,7 +634,7 @@ A_IMPLEMENT_ALU_OP(ORR,_S)
#define A_MOV_S(c) \
cpu->SetNZ(b & 0x80000000, \
!b); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(b, true); \
@@ -650,7 +650,7 @@ A_IMPLEMENT_ALU_OP(MOV,_S)
#define A_BIC(c) \
u32 a = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
u32 res = a & ~b; \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res); \
@@ -665,7 +665,7 @@ A_IMPLEMENT_ALU_OP(MOV,_S)
u32 res = a & ~b; \
cpu->SetNZ(res & 0x80000000, \
!res); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(res, true); \
@@ -680,7 +680,7 @@ A_IMPLEMENT_ALU_OP(BIC,_S)
#define A_MVN(c) \
b = ~b; \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(b); \
@@ -694,7 +694,7 @@ A_IMPLEMENT_ALU_OP(BIC,_S)
b = ~b; \
cpu->SetNZ(b & 0x80000000, \
!b); \
- cpu->Cycles += c; \
+ if (c) cpu->AddCycles_CI(c); else cpu->AddCycles_C(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
cpu->JumpTo(b, true); \
@@ -724,12 +724,17 @@ void A_MUL(ARM* cpu)
}
u32 cycles;
- if ((rs & 0xFFFFFF00) == 0x00000000 || (rs & 0xFFFFFF00) == 0xFFFFFF00) cycles = 1;
- else if ((rs & 0xFFFF0000) == 0x00000000 || (rs & 0xFFFF0000) == 0xFFFF0000) cycles = 2;
- else if ((rs & 0xFF000000) == 0x00000000 || (rs & 0xFF000000) == 0xFF000000) cycles = 3;
- else cycles = 4;
+ if (cpu->Num == 0)
+ cycles = (cpu->CurInstr & (1<<20)) ? 3 : 1;
+ else
+ {
+ if ((rs & 0xFFFFFF00) == 0x00000000 || (rs & 0xFFFFFF00) == 0xFFFFFF00) cycles = 1;
+ else if ((rs & 0xFFFF0000) == 0x00000000 || (rs & 0xFFFF0000) == 0xFFFF0000) cycles = 2;
+ else if ((rs & 0xFF000000) == 0x00000000 || (rs & 0xFF000000) == 0xFF000000) cycles = 3;
+ else cycles = 4;
+ }
- cpu->Cycles += cycles;
+ cpu->AddCycles_CI(cycles);
}
void A_MLA(ARM* cpu)
@@ -749,12 +754,17 @@ void A_MLA(ARM* cpu)
}
u32 cycles;
- if ((rs & 0xFFFFFF00) == 0x00000000 || (rs & 0xFFFFFF00) == 0xFFFFFF00) cycles = 2;
- else if ((rs & 0xFFFF0000) == 0x00000000 || (rs & 0xFFFF0000) == 0xFFFF0000) cycles = 3;
- else if ((rs & 0xFF000000) == 0x00000000 || (rs & 0xFF000000) == 0xFF000000) cycles = 4;
- else cycles = 5;
+ if (cpu->Num == 0)
+ cycles = (cpu->CurInstr & (1<<20)) ? 3 : 1;
+ else
+ {
+ if ((rs & 0xFFFFFF00) == 0x00000000 || (rs & 0xFFFFFF00) == 0xFFFFFF00) cycles = 2;
+ else if ((rs & 0xFFFF0000) == 0x00000000 || (rs & 0xFFFF0000) == 0xFFFF0000) cycles = 3;
+ else if ((rs & 0xFF000000) == 0x00000000 || (rs & 0xFF000000) == 0xFF000000) cycles = 4;
+ else cycles = 5;
+ }
- cpu->Cycles += cycles;
+ cpu->AddCycles_CI(cycles);
}
void A_UMULL(ARM* cpu)
@@ -774,12 +784,17 @@ void A_UMULL(ARM* cpu)
}
u32 cycles;
- if ((rs & 0xFFFFFF00) == 0x00000000) cycles = 2;
- else if ((rs & 0xFFFF0000) == 0x00000000) cycles = 3;
- else if ((rs & 0xFF000000) == 0x00000000) cycles = 4;
- else cycles = 5;
+ if (cpu->Num == 0)
+ cycles = (cpu->CurInstr & (1<<20)) ? 3 : 1;
+ else
+ {
+ if ((rs & 0xFFFFFF00) == 0x00000000) cycles = 2;
+ else if ((rs & 0xFFFF0000) == 0x00000000) cycles = 3;
+ else if ((rs & 0xFF000000) == 0x00000000) cycles = 4;
+ else cycles = 5;
+ }
- cpu->Cycles += cycles;
+ cpu->AddCycles_CI(cycles);
}
void A_UMLAL(ARM* cpu)
@@ -802,12 +817,17 @@ void A_UMLAL(ARM* cpu)
}
u32 cycles;
- if ((rs & 0xFFFFFF00) == 0x00000000) cycles = 2;
- else if ((rs & 0xFFFF0000) == 0x00000000) cycles = 3;
- else if ((rs & 0xFF000000) == 0x00000000) cycles = 4;
- else cycles = 5;
+ if (cpu->Num == 0)
+ cycles = (cpu->CurInstr & (1<<20)) ? 3 : 1;
+ else
+ {
+ if ((rs & 0xFFFFFF00) == 0x00000000) cycles = 2;
+ else if ((rs & 0xFFFF0000) == 0x00000000) cycles = 3;
+ else if ((rs & 0xFF000000) == 0x00000000) cycles = 4;
+ else cycles = 5;
+ }
- cpu->Cycles += cycles;
+ cpu->AddCycles_CI(cycles);
}
void A_SMULL(ARM* cpu)
@@ -827,12 +847,17 @@ void A_SMULL(ARM* cpu)
}
u32 cycles;
- if ((rs & 0xFFFFFF00) == 0x00000000 || (rs & 0xFFFFFF00) == 0xFFFFFF00) cycles = 2;
- else if ((rs & 0xFFFF0000) == 0x00000000 || (rs & 0xFFFF0000) == 0xFFFF0000) cycles = 3;
- else if ((rs & 0xFF000000) == 0x00000000 || (rs & 0xFF000000) == 0xFF000000) cycles = 4;
- else cycles = 5;
+ if (cpu->Num == 0)
+ cycles = (cpu->CurInstr & (1<<20)) ? 3 : 1;
+ else
+ {
+ if ((rs & 0xFFFFFF00) == 0x00000000 || (rs & 0xFFFFFF00) == 0xFFFFFF00) cycles = 2;
+ else if ((rs & 0xFFFF0000) == 0x00000000 || (rs & 0xFFFF0000) == 0xFFFF0000) cycles = 3;
+ else if ((rs & 0xFF000000) == 0x00000000 || (rs & 0xFF000000) == 0xFF000000) cycles = 4;
+ else cycles = 5;
+ }
- cpu->Cycles += cycles;
+ cpu->AddCycles_CI(cycles);
}
void A_SMLAL(ARM* cpu)
@@ -855,12 +880,17 @@ void A_SMLAL(ARM* cpu)
}
u32 cycles;
- if ((rs & 0xFFFFFF00) == 0x00000000 || (rs & 0xFFFFFF00) == 0xFFFFFF00) cycles = 2;
- else if ((rs & 0xFFFF0000) == 0x00000000 || (rs & 0xFFFF0000) == 0xFFFF0000) cycles = 3;
- else if ((rs & 0xFF000000) == 0x00000000 || (rs & 0xFF000000) == 0xFF000000) cycles = 4;
- else cycles = 5;
+ if (cpu->Num == 0)
+ cycles = (cpu->CurInstr & (1<<20)) ? 3 : 1;
+ else
+ {
+ if ((rs & 0xFFFFFF00) == 0x00000000 || (rs & 0xFFFFFF00) == 0xFFFFFF00) cycles = 2;
+ else if ((rs & 0xFFFF0000) == 0x00000000 || (rs & 0xFFFF0000) == 0xFFFF0000) cycles = 3;
+ else if ((rs & 0xFF000000) == 0x00000000 || (rs & 0xFF000000) == 0xFF000000) cycles = 4;
+ else cycles = 5;
+ }
- cpu->Cycles += cycles;
+ cpu->AddCycles_CI(cycles);
}
void A_SMLAxy(ARM* cpu)
@@ -882,6 +912,8 @@ void A_SMLAxy(ARM* cpu)
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
if (OVERFLOW_ADD(res_mul, rn, res))
cpu->CPSR |= 0x08000000;
+
+ cpu->AddCycles_C(); // TODO: interlock??
}
void A_SMLAWy(ARM* cpu)
@@ -901,6 +933,8 @@ void A_SMLAWy(ARM* cpu)
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
if (OVERFLOW_ADD(res_mul, rn, res))
cpu->CPSR |= 0x08000000;
+
+ cpu->AddCycles_C(); // TODO: interlock??
}
void A_SMULxy(ARM* cpu)
@@ -918,6 +952,7 @@ void A_SMULxy(ARM* cpu)
u32 res = ((s16)rm * (s16)rs);
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
+ cpu->AddCycles_C(); // TODO: interlock??
}
void A_SMULWy(ARM* cpu)
@@ -933,6 +968,7 @@ void A_SMULWy(ARM* cpu)
u32 res = ((s64)(s32)rm * (s16)rs) >> 16;
cpu->R[(cpu->CurInstr >> 16) & 0xF] = res;
+ cpu->AddCycles_C(); // TODO: interlock??
}
void A_SMLALxy(ARM* cpu)
@@ -955,7 +991,7 @@ void A_SMLALxy(ARM* cpu)
cpu->R[(cpu->CurInstr >> 12) & 0xF] = (u32)res;
cpu->R[(cpu->CurInstr >> 16) & 0xF] = (u32)(res >> 32ULL);
- cpu->Cycles += 1;
+ cpu->AddCycles_CI(1); // TODO: interlock??
}
@@ -981,6 +1017,7 @@ void A_CLZ(ARM* cpu)
}
cpu->R[(cpu->CurInstr >> 12) & 0xF] = res;
+ cpu->AddCycles_C();
}
void A_QADD(ARM* cpu)
@@ -998,6 +1035,7 @@ void A_QADD(ARM* cpu)
}
cpu->R[(cpu->CurInstr >> 12) & 0xF] = res;
+ cpu->AddCycles_C(); // TODO: interlock??
}
void A_QSUB(ARM* cpu)
@@ -1015,6 +1053,7 @@ void A_QSUB(ARM* cpu)
}
cpu->R[(cpu->CurInstr >> 12) & 0xF] = res;
+ cpu->AddCycles_C(); // TODO: interlock??
}
void A_QDADD(ARM* cpu)
@@ -1040,6 +1079,7 @@ void A_QDADD(ARM* cpu)
}
cpu->R[(cpu->CurInstr >> 12) & 0xF] = res;
+ cpu->AddCycles_C(); // TODO: interlock??
}
void A_QDSUB(ARM* cpu)
@@ -1065,6 +1105,7 @@ void A_QDSUB(ARM* cpu)
}
cpu->R[(cpu->CurInstr >> 12) & 0xF] = res;
+ cpu->AddCycles_C(); // TODO: interlock??
}
@@ -1081,6 +1122,7 @@ void T_LSL_IMM(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = op;
cpu->SetNZ(op & 0x80000000,
!op);
+ cpu->AddCycles_C();
}
void T_LSR_IMM(ARM* cpu)
@@ -1091,6 +1133,7 @@ void T_LSR_IMM(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = op;
cpu->SetNZ(op & 0x80000000,
!op);
+ cpu->AddCycles_C();
}
void T_ASR_IMM(ARM* cpu)
@@ -1101,6 +1144,7 @@ void T_ASR_IMM(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = op;
cpu->SetNZ(op & 0x80000000,
!op);
+ cpu->AddCycles_C();
}
void T_ADD_REG_(ARM* cpu)
@@ -1113,6 +1157,7 @@ void T_ADD_REG_(ARM* cpu)
!res,
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
+ cpu->AddCycles_C();
}
void T_SUB_REG_(ARM* cpu)
@@ -1125,6 +1170,7 @@ void T_SUB_REG_(ARM* cpu)
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
+ cpu->AddCycles_C();
}
void T_ADD_IMM_(ARM* cpu)
@@ -1137,6 +1183,7 @@ void T_ADD_IMM_(ARM* cpu)
!res,
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
+ cpu->AddCycles_C();
}
void T_SUB_IMM_(ARM* cpu)
@@ -1149,6 +1196,7 @@ void T_SUB_IMM_(ARM* cpu)
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
+ cpu->AddCycles_C();
}
void T_MOV_IMM(ARM* cpu)
@@ -1157,6 +1205,7 @@ void T_MOV_IMM(ARM* cpu)
cpu->R[(cpu->CurInstr >> 8) & 0x7] = b;
cpu->SetNZ(0,
!b);
+ cpu->AddCycles_C();
}
void T_CMP_IMM(ARM* cpu)
@@ -1168,6 +1217,7 @@ void T_CMP_IMM(ARM* cpu)
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
+ cpu->AddCycles_C();
}
void T_ADD_IMM(ARM* cpu)
@@ -1180,6 +1230,7 @@ void T_ADD_IMM(ARM* cpu)
!res,
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
+ cpu->AddCycles_C();
}
void T_SUB_IMM(ARM* cpu)
@@ -1192,6 +1243,7 @@ void T_SUB_IMM(ARM* cpu)
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
+ cpu->AddCycles_C();
}
@@ -1203,6 +1255,7 @@ void T_AND_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZ(res & 0x80000000,
!res);
+ cpu->AddCycles_C();
}
void T_EOR_REG(ARM* cpu)
@@ -1213,6 +1266,7 @@ void T_EOR_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZ(res & 0x80000000,
!res);
+ cpu->AddCycles_C();
}
void T_LSL_REG(ARM* cpu)
@@ -1223,7 +1277,7 @@ void T_LSL_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = a;
cpu->SetNZ(a & 0x80000000,
!a);
- cpu->Cycles += 1;
+ cpu->AddCycles_CI(1);
}
void T_LSR_REG(ARM* cpu)
@@ -1234,7 +1288,7 @@ void T_LSR_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = a;
cpu->SetNZ(a & 0x80000000,
!a);
- cpu->Cycles += 1;
+ cpu->AddCycles_CI(1);
}
void T_ASR_REG(ARM* cpu)
@@ -1245,7 +1299,7 @@ void T_ASR_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = a;
cpu->SetNZ(a & 0x80000000,
!a);
- cpu->Cycles += 1;
+ cpu->AddCycles_CI(1);
}
void T_ADC_REG(ARM* cpu)
@@ -1260,6 +1314,7 @@ void T_ADC_REG(ARM* cpu)
!res,
CARRY_ADD(a, b) | CARRY_ADD(res_tmp, carry),
OVERFLOW_ADD(a, b, res_tmp) | OVERFLOW_ADD(res_tmp, carry, res));
+ cpu->AddCycles_C();
}
void T_SBC_REG(ARM* cpu)
@@ -1274,6 +1329,7 @@ void T_SBC_REG(ARM* cpu)
!res,
CARRY_SUB(a, b) & CARRY_SUB(res_tmp, carry),
OVERFLOW_SUB(a, b, res_tmp) | OVERFLOW_SUB(res_tmp, carry, res));
+ cpu->AddCycles_C();
}
void T_ROR_REG(ARM* cpu)
@@ -1284,7 +1340,7 @@ void T_ROR_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = a;
cpu->SetNZ(a & 0x80000000,
!a);
- cpu->Cycles += 1;
+ cpu->AddCycles_CI(1);
}
void T_TST_REG(ARM* cpu)
@@ -1294,6 +1350,7 @@ void T_TST_REG(ARM* cpu)
u32 res = a & b;
cpu->SetNZ(res & 0x80000000,
!res);
+ cpu->AddCycles_C();
}
void T_NEG_REG(ARM* cpu)
@@ -1305,6 +1362,7 @@ void T_NEG_REG(ARM* cpu)
!res,
CARRY_SUB(0, b),
OVERFLOW_SUB(0, b, res));
+ cpu->AddCycles_C();
}
void T_CMP_REG(ARM* cpu)
@@ -1316,6 +1374,7 @@ void T_CMP_REG(ARM* cpu)
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
+ cpu->AddCycles_C();
}
void T_CMN_REG(ARM* cpu)
@@ -1327,6 +1386,7 @@ void T_CMN_REG(ARM* cpu)
!res,
CARRY_ADD(a, b),
OVERFLOW_ADD(a, b, res));
+ cpu->AddCycles_C();
}
void T_ORR_REG(ARM* cpu)
@@ -1337,6 +1397,7 @@ void T_ORR_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZ(res & 0x80000000,
!res);
+ cpu->AddCycles_C();
}
void T_MUL_REG(ARM* cpu)
@@ -1361,7 +1422,7 @@ void T_MUL_REG(ARM* cpu)
else if (a & 0x0000FF00) cycles += 2;
else cycles += 1;
}
- cpu->Cycles += cycles;
+ cpu->AddCycles_CI(cycles);
}
void T_BIC_REG(ARM* cpu)
@@ -1372,6 +1433,7 @@ void T_BIC_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZ(res & 0x80000000,
!res);
+ cpu->AddCycles_C();
}
void T_MVN_REG(ARM* cpu)
@@ -1381,6 +1443,7 @@ void T_MVN_REG(ARM* cpu)
cpu->R[cpu->CurInstr & 0x7] = res;
cpu->SetNZ(res & 0x80000000,
!res);
+ cpu->AddCycles_C();
}
@@ -1395,6 +1458,8 @@ void T_ADD_HIREG(ARM* cpu)
u32 a = cpu->R[rd];
u32 b = cpu->R[rs];
+ cpu->AddCycles_C();
+
if (rd == 15)
{
cpu->JumpTo((a + b) | 1);
@@ -1418,6 +1483,7 @@ void T_CMP_HIREG(ARM* cpu)
!res,
CARRY_SUB(a, b),
OVERFLOW_SUB(a, b, res));
+ cpu->AddCycles_C();
}
void T_MOV_HIREG(ARM* cpu)
@@ -1425,6 +1491,8 @@ void T_MOV_HIREG(ARM* cpu)
u32 rd = (cpu->CurInstr & 0x7) | ((cpu->CurInstr >> 4) & 0x8);
u32 rs = (cpu->CurInstr >> 3) & 0xF;
+ cpu->AddCycles_C();
+
if (rd == 15)
{
cpu->JumpTo(cpu->R[rs] | 1);
@@ -1433,6 +1501,15 @@ void T_MOV_HIREG(ARM* cpu)
{
cpu->R[rd] = cpu->R[rs];
}
+
+ // nocash-style debugging hook
+ if ((cpu->CurInstr & 0xFFFF) == 0x46E4 && // mov r12, r12
+ (cpu->NextInstr[0] & 0xF800) == 0xE000 && // branch
+ (cpu->NextInstr[1] & 0xFFFF) == 0x6464)
+ {
+ u32 addr = cpu->R[15] + 2;
+ NDS::NocashPrint(cpu->Num, addr);
+ }
}
@@ -1441,6 +1518,7 @@ void T_ADD_PCREL(ARM* cpu)
u32 val = cpu->R[15] & ~2;
val += ((cpu->CurInstr & 0xFF) << 2);
cpu->R[(cpu->CurInstr >> 8) & 0x7] = val;
+ cpu->AddCycles_C();
}
void T_ADD_SPREL(ARM* cpu)
@@ -1448,6 +1526,7 @@ void T_ADD_SPREL(ARM* cpu)
u32 val = cpu->R[13];
val += ((cpu->CurInstr & 0xFF) << 2);
cpu->R[(cpu->CurInstr >> 8) & 0x7] = val;
+ cpu->AddCycles_C();
}
void T_ADD_SP(ARM* cpu)
@@ -1458,6 +1537,7 @@ void T_ADD_SP(ARM* cpu)
else
val += ((cpu->CurInstr & 0x7F) << 2);
cpu->R[13] = val;
+ cpu->AddCycles_C();
}
diff --git a/src/ARMInterpreter_Branch.cpp b/src/ARMInterpreter_Branch.cpp
index 740375d..5e2ef2c 100644
--- a/src/ARMInterpreter_Branch.cpp
+++ b/src/ARMInterpreter_Branch.cpp
@@ -66,6 +66,8 @@ void T_BCOND(ARM* cpu)
s32 offset = (s32)(cpu->CurInstr << 24) >> 23;
cpu->JumpTo(cpu->R[15] + offset + 1);
}
+ else
+ cpu->AddCycles_C();
}
void T_BX(ARM* cpu)
@@ -96,6 +98,7 @@ void T_BL_LONG_1(ARM* cpu)
{
s32 offset = (s32)((cpu->CurInstr & 0x7FF) << 21) >> 9;
cpu->R[14] = cpu->R[15] + offset;
+ cpu->AddCycles_C();
}
void T_BL_LONG_2(ARM* cpu)
diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp
index adb44a9..5a1b88d 100644
--- a/src/ARMInterpreter_LoadStore.cpp
+++ b/src/ARMInterpreter_LoadStore.cpp
@@ -63,28 +63,35 @@ namespace ARMInterpreter
#define A_STR \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
- if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset;
+ if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
+ cpu->AddCycles_CD();
+// TODO: user mode (bit21)
#define A_STR_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
- cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF], cpu->CurInstr & (1<<21)); \
- cpu->R[(cpu->CurInstr>>16) & 0xF] += offset;
+ cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
+ cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
+ cpu->AddCycles_CD();
#define A_STRB \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite8(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
- if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset;
+ if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
+ cpu->AddCycles_CD();
+// TODO: user mode (bit21)
#define A_STRB_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
- cpu->DataWrite8(addr, cpu->R[(cpu->CurInstr>>12) & 0xF], cpu->CurInstr & (1<<21)); \
- cpu->R[(cpu->CurInstr>>16) & 0xF] += offset;
+ cpu->DataWrite8(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
+ cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
+ cpu->AddCycles_CD();
#define A_LDR \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
- u32 val = cpu->DataRead32(offset); val = ROR(val, ((offset&0x3)<<3)); \
+ u32 val; cpu->DataRead32(offset, &val); \
+ val = ROR(val, ((offset&0x3)<<3)); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
- cpu->Cycles += 1; \
+ cpu->AddCycles_CDI(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
if (cpu->Num==1) val &= ~0x1; \
@@ -95,11 +102,13 @@ namespace ARMInterpreter
cpu->R[(cpu->CurInstr>>12) & 0xF] = val; \
}
+// TODO: user mode
#define A_LDR_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
- u32 val = cpu->DataRead32(addr, cpu->CurInstr & (1<<21)); val = ROR(val, ((addr&0x3)<<3)); \
+ u32 val; cpu->DataRead32(addr, &val); \
+ val = ROR(val, ((addr&0x3)<<3)); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
- cpu->Cycles += 1; \
+ cpu->AddCycles_CDI(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) \
{ \
if (cpu->Num==1) val &= ~0x1; \
@@ -112,17 +121,18 @@ namespace ARMInterpreter
#define A_LDRB \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
- u32 val = cpu->DataRead8(offset); \
+ u32 val; cpu->DataRead8(offset, &val); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
- cpu->Cycles += 1; \
+ cpu->AddCycles_CDI(); \
cpu->R[(cpu->CurInstr>>12) & 0xF] = val; \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRB PC %08X\n", cpu->R[15]); \
+// TODO: user mode
#define A_LDRB_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
- u32 val = cpu->DataRead8(addr, cpu->CurInstr & (1<<21)); \
+ u32 val; cpu->DataRead8(addr, &val); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
- cpu->Cycles += 1; \
+ cpu->AddCycles_CDI(); \
cpu->R[(cpu->CurInstr>>12) & 0xF] = val; \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRB PC %08X\n", cpu->R[15]); \
@@ -211,11 +221,13 @@ A_IMPLEMENT_WB_LDRSTR(LDRB)
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite16(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
+ cpu->AddCycles_CD();
#define A_STRH_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->DataWrite16(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
+ cpu->AddCycles_CD();
// TODO: CHECK LDRD/STRD TIMINGS!!
@@ -223,69 +235,85 @@ A_IMPLEMENT_WB_LDRSTR(LDRB)
if (cpu->Num != 0) return; \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
- cpu->Cycles += 1; \
u32 r = (cpu->CurInstr>>12) & 0xF; \
- cpu->R[r ] = cpu->DataRead32(offset ); \
- cpu->R[r+1] = cpu->DataRead32(offset+4); \
+ if (r&1) { r--; printf("!! MISALIGNED LDRD_POST %d\n", r); } \
+ cpu->DataRead32 (offset , &cpu->R[r ]); \
+ cpu->DataRead32S(offset+4, &cpu->R[r+1]); \
+ cpu->AddCycles_CDI();
#define A_LDRD_POST \
if (cpu->Num != 0) return; \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
- cpu->Cycles += 1; \
u32 r = (cpu->CurInstr>>12) & 0xF; \
- cpu->R[r ] = cpu->DataRead32(addr ); \
- cpu->R[r+1] = cpu->DataRead32(addr+4); \
+ if (r&1) { r--; printf("!! MISALIGNED LDRD_POST %d\n", r); } \
+ cpu->DataRead32 (addr , &cpu->R[r ]); \
+ cpu->DataRead32S(addr+4, &cpu->R[r+1]); \
+ cpu->AddCycles_CDI();
#define A_STRD \
if (cpu->Num != 0) return; \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
u32 r = (cpu->CurInstr>>12) & 0xF; \
- cpu->DataWrite32(offset , cpu->R[r ]); \
- cpu->DataWrite32(offset+4, cpu->R[r+1]); \
+ if (r&1) { r--; printf("!! MISALIGNED LDRD_POST %d\n", r); } \
+ cpu->DataWrite32 (offset , cpu->R[r ]); \
+ cpu->DataWrite32S(offset+4, cpu->R[r+1]); \
+ cpu->AddCycles_CD();
#define A_STRD_POST \
if (cpu->Num != 0) return; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
u32 r = (cpu->CurInstr>>12) & 0xF; \
- cpu->DataWrite32(offset , cpu->R[r ]); \
- cpu->DataWrite32(offset+4, cpu->R[r+1]); \
+ if (r&1) { r--; printf("!! MISALIGNED LDRD_POST %d\n", r); } \
+ cpu->DataWrite32 (offset , cpu->R[r ]); \
+ cpu->DataWrite32S(offset+4, cpu->R[r+1]); \
+ cpu->AddCycles_CD();
#define A_LDRH \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
- cpu->R[(cpu->CurInstr>>12) & 0xF] = cpu->DataRead16(offset); \
+ cpu->DataRead16(offset, &cpu->R[(cpu->CurInstr>>12) & 0xF]); \
+ cpu->AddCycles_CDI(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRH PC %08X\n", cpu->R[15]); \
#define A_LDRH_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
- cpu->R[(cpu->CurInstr>>12) & 0xF] = cpu->DataRead16(addr); \
+ cpu->DataRead16(addr, &cpu->R[(cpu->CurInstr>>12) & 0xF]); \
+ cpu->AddCycles_CDI(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRH PC %08X\n", cpu->R[15]); \
#define A_LDRSB \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
- cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s8)cpu->DataRead8(offset); \
+ cpu->DataRead8(offset, &cpu->R[(cpu->CurInstr>>12) & 0xF]); \
+ cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s8)cpu->R[(cpu->CurInstr>>12) & 0xF]; \
+ cpu->AddCycles_CDI(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRSB PC %08X\n", cpu->R[15]); \
#define A_LDRSB_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
- cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s8)cpu->DataRead8(addr); \
+ cpu->DataRead8(addr, &cpu->R[(cpu->CurInstr>>12) & 0xF]); \
+ cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s8)cpu->R[(cpu->CurInstr>>12) & 0xF]; \
+ cpu->AddCycles_CDI(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRSB PC %08X\n", cpu->R[15]); \
#define A_LDRSH \
offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \
if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \
- cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s16)cpu->DataRead16(offset); \
+ cpu->DataRead16(offset, &cpu->R[(cpu->CurInstr>>12) & 0xF]); \
+ cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s16)cpu->R[(cpu->CurInstr>>12) & 0xF]; \
+ cpu->AddCycles_CDI(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRSH PC %08X\n", cpu->R[15]); \
#define A_LDRSH_POST \
u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \
cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \
- cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s16)cpu->DataRead16(addr); \
+ cpu->DataRead16(addr, &cpu->R[(cpu->CurInstr>>12) & 0xF]); \
+ cpu->R[(cpu->CurInstr>>12) & 0xF] = (s32)(s16)cpu->R[(cpu->CurInstr>>12) & 0xF]; \
+ cpu->AddCycles_CDI(); \
if (((cpu->CurInstr>>12) & 0xF) == 15) printf("!! LDRSH PC %08X\n", cpu->R[15]); \
@@ -328,12 +356,15 @@ void A_SWP(ARM* cpu)
u32 base = cpu->R[(cpu->CurInstr >> 16) & 0xF];
u32 rm = cpu->R[cpu->CurInstr & 0xF];
- u32 val = cpu->DataRead32(base);
+ u32 val;
+ cpu->DataRead32(base, &val);
cpu->R[(cpu->CurInstr >> 12) & 0xF] = ROR(val, 8*(base&0x3));
+ u32 numD = cpu->DataCycles;
cpu->DataWrite32(base, rm);
+ cpu->DataCycles += numD;
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
void A_SWPB(ARM* cpu)
@@ -341,11 +372,13 @@ void A_SWPB(ARM* cpu)
u32 base = cpu->R[(cpu->CurInstr >> 16) & 0xF];
u32 rm = cpu->R[cpu->CurInstr & 0xF] & 0xFF;
- cpu->R[(cpu->CurInstr >> 12) & 0xF] = cpu->DataRead8(base);
+ cpu->DataRead8(base, &cpu->R[(cpu->CurInstr >> 12) & 0xF]);
+ u32 numD = cpu->DataCycles;
cpu->DataWrite8(base, rm);
+ cpu->DataCycles += numD;
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
@@ -356,6 +389,7 @@ void A_LDM(ARM* cpu)
u32 base = cpu->R[baseid];
u32 wbbase;
u32 preinc = (cpu->CurInstr & (1<<24));
+ bool first = true;
if (!(cpu->CurInstr & (1<<23)))
{
@@ -374,8 +408,6 @@ void A_LDM(ARM* cpu)
preinc = !preinc;
}
- cpu->Cycles += 1;
-
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
cpu->UpdateMode(cpu->CPSR, (cpu->CPSR&~0x1F)|0x10);
@@ -384,15 +416,19 @@ void A_LDM(ARM* cpu)
if (cpu->CurInstr & (1<<i))
{
if (preinc) base += 4;
- cpu->R[i] = cpu->DataRead32(base);
+ if (first) cpu->DataRead32 (base, &cpu->R[i]);
+ else cpu->DataRead32S(base, &cpu->R[i]);
+ first = false;
if (!preinc) base += 4;
}
}
if (cpu->CurInstr & (1<<15))
{
+ u32 pc;
if (preinc) base += 4;
- u32 pc = cpu->DataRead32(base);
+ if (first) cpu->DataRead32 (base, &pc);
+ else cpu->DataRead32S(base, &pc);
if (!preinc) base += 4;
if (cpu->Num == 1)
@@ -422,6 +458,8 @@ void A_LDM(ARM* cpu)
else
cpu->R[baseid] = wbbase;
}
+
+ cpu->AddCycles_CDI();
}
void A_STM(ARM* cpu)
@@ -430,6 +468,7 @@ void A_STM(ARM* cpu)
u32 base = cpu->R[baseid];
u32 oldbase = base;
u32 preinc = (cpu->CurInstr & (1<<24));
+ bool first = true;
if (!(cpu->CurInstr & (1<<23)))
{
@@ -466,12 +505,14 @@ void A_STM(ARM* cpu)
if (i == baseid && !isbanked)
{
if ((cpu->Num == 0) || (!(cpu->CurInstr & ((1<<i)-1))))
- cpu->DataWrite32(base, oldbase);
+ first ? cpu->DataWrite32(base, oldbase) : cpu->DataWrite32S(base, oldbase);
else
- cpu->DataWrite32(base, base); // checkme
+ first ? cpu->DataWrite32(base, base) : cpu->DataWrite32S(base, base); // checkme
}
else
- cpu->DataWrite32(base, cpu->R[i]);
+ first ? cpu->DataWrite32(base, cpu->R[i]) : cpu->DataWrite32S(base, cpu->R[i]);
+
+ first = false;
if (!preinc) base += 4;
}
@@ -482,6 +523,8 @@ void A_STM(ARM* cpu)
if ((cpu->CurInstr & (1<<23)) && (cpu->CurInstr & (1<<21)))
cpu->R[baseid] = base;
+
+ cpu->AddCycles_CD();
}
@@ -494,9 +537,9 @@ void A_STM(ARM* cpu)
void T_LDR_PCREL(ARM* cpu)
{
u32 addr = (cpu->R[15] & ~0x2) + ((cpu->CurInstr & 0xFF) << 2);
- cpu->R[(cpu->CurInstr >> 8) & 0x7] = cpu->DataRead32(addr);
+ cpu->DataRead32(addr, &cpu->R[(cpu->CurInstr >> 8) & 0x7]);
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
@@ -504,30 +547,35 @@ void T_STR_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->DataWrite32(addr, cpu->R[cpu->CurInstr & 0x7]);
+
+ cpu->AddCycles_CD();
}
void T_STRB_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->DataWrite8(addr, cpu->R[cpu->CurInstr & 0x7]);
+
+ cpu->AddCycles_CD();
}
void T_LDR_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
- u32 val = cpu->DataRead32(addr);
+ u32 val;
+ cpu->DataRead32(addr, &val);
cpu->R[cpu->CurInstr & 0x7] = ROR(val, 8*(addr&0x3));
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
void T_LDRB_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
- cpu->R[cpu->CurInstr & 0x7] = cpu->DataRead8(addr);
+ cpu->DataRead8(addr, &cpu->R[cpu->CurInstr & 0x7]);
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
@@ -535,30 +583,34 @@ void T_STRH_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
cpu->DataWrite16(addr, cpu->R[cpu->CurInstr & 0x7]);
+
+ cpu->AddCycles_CD();
}
void T_LDRSB_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
- cpu->R[cpu->CurInstr & 0x7] = (s32)(s8)cpu->DataRead8(addr);
+ cpu->DataRead8(addr, &cpu->R[cpu->CurInstr & 0x7]);
+ cpu->R[cpu->CurInstr & 0x7] = (s32)(s8)cpu->R[cpu->CurInstr & 0x7];
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
void T_LDRH_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
- cpu->R[cpu->CurInstr & 0x7] = cpu->DataRead16(addr);
+ cpu->DataRead16(addr, &cpu->R[cpu->CurInstr & 0x7]);
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
void T_LDRSH_REG(ARM* cpu)
{
u32 addr = cpu->R[(cpu->CurInstr >> 3) & 0x7] + cpu->R[(cpu->CurInstr >> 6) & 0x7];
- cpu->R[cpu->CurInstr & 0x7] = (s32)(s16)cpu->DataRead16(addr);
+ cpu->DataRead16(addr, &cpu->R[cpu->CurInstr & 0x7]);
+ cpu->R[cpu->CurInstr & 0x7] = (s32)(s16)cpu->R[cpu->CurInstr & 0x7];
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
@@ -568,6 +620,7 @@ void T_STR_IMM(ARM* cpu)
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
cpu->DataWrite32(offset, cpu->R[cpu->CurInstr & 0x7]);
+ cpu->AddCycles_CD();
}
void T_LDR_IMM(ARM* cpu)
@@ -575,9 +628,10 @@ void T_LDR_IMM(ARM* cpu)
u32 offset = (cpu->CurInstr >> 4) & 0x7C;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
- u32 val = cpu->DataRead32(offset);
+ u32 val;
+ cpu->DataRead32(offset, &val);
cpu->R[cpu->CurInstr & 0x7] = ROR(val, 8*(offset&0x3));
- cpu->Cycles += 1;
+ cpu->AddCycles_CDI();
}
void T_STRB_IMM(ARM* cpu)
@@ -586,6 +640,7 @@ void T_STRB_IMM(ARM* cpu)
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
cpu->DataWrite8(offset, cpu->R[cpu->CurInstr & 0x7]);
+ cpu->AddCycles_CD();
}
void T_LDRB_IMM(ARM* cpu)
@@ -593,8 +648,8 @@ void T_LDRB_IMM(ARM* cpu)
u32 offset = (cpu->CurInstr >> 6) & 0x1F;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
- cpu->R[cpu->CurInstr & 0x7] = cpu->DataRead8(offset);
- cpu->Cycles += 1;
+ cpu->DataRead8(offset, &cpu->R[cpu->CurInstr & 0x7]);
+ cpu->AddCycles_CDI();
}
@@ -604,6 +659,7 @@ void T_STRH_IMM(ARM* cpu)
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
cpu->DataWrite16(offset, cpu->R[cpu->CurInstr & 0x7]);
+ cpu->AddCycles_CD();
}
void T_LDRH_IMM(ARM* cpu)
@@ -611,8 +667,8 @@ void T_LDRH_IMM(ARM* cpu)
u32 offset = (cpu->CurInstr >> 5) & 0x3E;
offset += cpu->R[(cpu->CurInstr >> 3) & 0x7];
- cpu->R[cpu->CurInstr & 0x7] = cpu->DataRead16(offset);
- cpu->Cycles += 1;
+ cpu->DataRead16(offset, &cpu->R[cpu->CurInstr & 0x7]);
+ cpu->AddCycles_CDI();
}
@@ -622,6 +678,7 @@ void T_STR_SPREL(ARM* cpu)
offset += cpu->R[13];
cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr >> 8) & 0x7]);
+ cpu->AddCycles_CD();
}
void T_LDR_SPREL(ARM* cpu)
@@ -629,14 +686,15 @@ void T_LDR_SPREL(ARM* cpu)
u32 offset = (cpu->CurInstr << 2) & 0x3FC;
offset += cpu->R[13];
- cpu->R[(cpu->CurInstr >> 8) & 0x7] = cpu->DataRead32(offset);
- cpu->Cycles += 1;
+ cpu->DataRead32(offset, &cpu->R[(cpu->CurInstr >> 8) & 0x7]);
+ cpu->AddCycles_CDI();
}
void T_PUSH(ARM* cpu)
{
int nregs = 0;
+ bool first = true;
for (int i = 0; i < 8; i++)
{
@@ -655,77 +713,93 @@ void T_PUSH(ARM* cpu)
{
if (cpu->CurInstr & (1<<i))
{
- cpu->DataWrite32(base, cpu->R[i]);
+ if (first) cpu->DataWrite32 (base, cpu->R[i]);
+ else cpu->DataWrite32S(base, cpu->R[i]);
+ first = false;
base += 4;
}
}
if (cpu->CurInstr & (1<<8))
{
- cpu->DataWrite32(base, cpu->R[14]);
+ if (first) cpu->DataWrite32 (base, cpu->R[14]);
+ else cpu->DataWrite32S(base, cpu->R[14]);
}
+
+ cpu->AddCycles_CD();
}
void T_POP(ARM* cpu)
{
u32 base = cpu->R[13];
-
- cpu->Cycles += 1;
+ bool first = true;
for (int i = 0; i < 8; i++)
{
if (cpu->CurInstr & (1<<i))
{
- cpu->R[i] = cpu->DataRead32(base);
+ if (first) cpu->DataRead32 (base, &cpu->R[i]);
+ else cpu->DataRead32S(base, &cpu->R[i]);
+ first = false;
base += 4;
}
}
if (cpu->CurInstr & (1<<8))
{
- u32 pc = cpu->DataRead32(base);
+ u32 pc;
+ if (first) cpu->DataRead32 (base, &pc);
+ else cpu->DataRead32S(base, &pc);
if (cpu->Num==1) pc |= 0x1;
cpu->JumpTo(pc);
base += 4;
}
cpu->R[13] = base;
+ cpu->AddCycles_CDI();
}
void T_STMIA(ARM* cpu)
{
u32 base = cpu->R[(cpu->CurInstr >> 8) & 0x7];
+ bool first = true;
for (int i = 0; i < 8; i++)
{
if (cpu->CurInstr & (1<<i))
{
- cpu->DataWrite32(base, cpu->R[i]);
+ if (first) cpu->DataWrite32 (base, cpu->R[i]);
+ else cpu->DataWrite32S(base, cpu->R[i]);
+ first = false;
base += 4;
}
}
// TODO: check "Rb included in Rlist" case
cpu->R[(cpu->CurInstr >> 8) & 0x7] = base;
+ cpu->AddCycles_CD();
}
void T_LDMIA(ARM* cpu)
{
u32 base = cpu->R[(cpu->CurInstr >> 8) & 0x7];
-
- cpu->Cycles += 1;
+ bool first = true;
for (int i = 0; i < 8; i++)
{
if (cpu->CurInstr & (1<<i))
{
- cpu->R[i] = cpu->DataRead32(base);
+ if (first) cpu->DataRead32 (base, &cpu->R[i]);
+ else cpu->DataRead32S(base, &cpu->R[i]);
+ first = false;
base += 4;
}
}
if (!(cpu->CurInstr & (1<<((cpu->CurInstr >> 8) & 0x7))))
cpu->R[(cpu->CurInstr >> 8) & 0x7] = base;
+
+ cpu->AddCycles_CDI();
}
diff --git a/src/CP15.cpp b/src/CP15.cpp
index e3f0bae..fb0da0d 100644
--- a/src/CP15.cpp
+++ b/src/CP15.cpp
@@ -20,31 +20,21 @@
#include <string.h>
#include "NDS.h"
#include "ARM.h"
-#include "CP15.h"
-// derp
-namespace NDS
-{
-extern ARM* ARM9;
-}
-
-namespace CP15
-{
-
-u32 Control;
+// access timing for cached regions
+// this would be an average between cache hits and cache misses
+// this was measured to be close to hardware average
+// a value of 1 would represent a perfect cache, but that causes
+// games to run too fast, causing a number of issues
+// code cache timing can get as low as 3
+const int kDataCacheTiming = 2;
+const int kCodeCacheTiming = 5;
-u32 DTCMSetting, ITCMSetting;
-u8 ITCM[0x8000];
-u32 ITCMSize;
-u8 DTCM[0x4000];
-u32 DTCMBase, DTCMSize;
-
-
-void Reset()
+void ARMv5::CP15Reset()
{
- Control = 0x78; // dunno
+ CP15Control = 0x2078; // dunno
DTCMSetting = 0;
ITCMSetting = 0;
@@ -55,31 +45,51 @@ void Reset()
ITCMSize = 0;
DTCMBase = 0xFFFFFFFF;
DTCMSize = 0;
+
+ PU_CodeCacheable = 0;
+ PU_DataCacheable = 0;
+ PU_DataCacheWrite = 0;
+
+ PU_CodeRW = 0;
+ PU_DataRW = 0;
+
+ memset(PU_Region, 0, 8*sizeof(u32));
+ UpdatePURegions();
}
-void DoSavestate(Savestate* file)
+void ARMv5::CP15DoSavestate(Savestate* file)
{
file->Section("CP15");
- file->Var32(&Control);
+ file->Var32(&CP15Control);
file->Var32(&DTCMSetting);
file->Var32(&ITCMSetting);
+ file->VarArray(ITCM, 0x8000);
+ file->VarArray(DTCM, 0x4000);
+
+ file->Var32(&PU_CodeCacheable);
+ file->Var32(&PU_DataCacheable);
+ file->Var32(&PU_DataCacheWrite);
+
+ file->Var32(&PU_CodeRW);
+ file->Var32(&PU_DataRW);
+
+ file->VarArray(PU_Region, 8*sizeof(u32));
+
if (!file->Saving)
{
UpdateDTCMSetting();
UpdateITCMSetting();
+ UpdatePURegions();
}
-
- file->VarArray(ITCM, 0x8000);
- file->VarArray(DTCM, 0x4000);
}
-void UpdateDTCMSetting()
+void ARMv5::UpdateDTCMSetting()
{
- if (Control & (1<<16))
+ if (CP15Control & (1<<16))
{
DTCMBase = DTCMSetting & 0xFFFFF000;
DTCMSize = 0x200 << ((DTCMSetting >> 1) & 0x1F);
@@ -93,9 +103,9 @@ void UpdateDTCMSetting()
}
}
-void UpdateITCMSetting()
+void ARMv5::UpdateITCMSetting()
{
- if (Control & (1<<18))
+ if (CP15Control & (1<<18))
{
ITCMSize = 0x200 << ((ITCMSetting >> 1) & 0x1F);
//printf("ITCM [%08X] enabled at %08X, size %X\n", ITCMSetting, 0, ITCMSize);
@@ -108,24 +118,277 @@ void UpdateITCMSetting()
}
-void Write(u32 id, u32 val)
+void ARMv5::UpdatePURegions()
+{
+ if (!(CP15Control & (1<<0)))
+ {
+ // PU disabled
+
+ u8 mask = 0x07;
+ if (CP15Control & (1<<2)) mask |= 0x30;
+ if (CP15Control & (1<<12)) mask |= 0x40;
+
+ memset(PU_UserMap, mask, 0x100000);
+ memset(PU_PrivMap, mask, 0x100000);
+
+ return;
+ }
+
+ memset(PU_UserMap, 0, 0x100000);
+ memset(PU_PrivMap, 0, 0x100000);
+
+ u32 coderw = PU_CodeRW;
+ u32 datarw = PU_DataRW;
+
+ u32 codecache, datacache, datawrite;
+
+ // datacache/datawrite
+ // 0/0: goes to memory
+ // 0/1: goes to memory
+ // 1/0: goes to memory and cache
+ // 1/1: goes to cache
+
+ if (CP15Control & (1<<12))
+ codecache = PU_CodeCacheable;
+ else
+ codecache = 0;
+
+ if (CP15Control & (1<<2))
+ {
+ datacache = PU_DataCacheable;
+ datawrite = PU_DataCacheWrite;
+ }
+ else
+ {
+ datacache = 0;
+ datawrite = 0;
+ }
+
+ for (int n = 0; n < 8; n++)
+ {
+ u32 rgn = PU_Region[n];
+ if (!(rgn & (1<<0))) continue;
+
+ u32 start = rgn >> 12;
+ u32 sz = 2 << ((rgn >> 1) & 0x1F);
+ u32 end = start + (sz >> 12);
+ // TODO: check alignment of start
+
+ u8 usermask = 0;
+ u8 privmask = 0;
+
+ switch (datarw & 0xF)
+ {
+ case 0: break;
+ case 1: privmask |= 0x03; break;
+ case 2: privmask |= 0x03; usermask |= 0x01; break;
+ case 3: privmask |= 0x03; usermask |= 0x03; break;
+ case 5: privmask |= 0x01; break;
+ case 6: privmask |= 0x01; usermask |= 0x01; break;
+ default: printf("!! BAD DATARW VALUE %d\n", datarw&0xF);
+ }
+
+ switch (coderw & 0xF)
+ {
+ case 0: break;
+ case 1: privmask |= 0x04; break;
+ case 2: privmask |= 0x04; usermask |= 0x04; break;
+ case 3: privmask |= 0x04; usermask |= 0x04; break;
+ case 5: privmask |= 0x04; break;
+ case 6: privmask |= 0x04; usermask |= 0x04; break;
+ default: printf("!! BAD CODERW VALUE %d\n", datarw&0xF);
+ }
+
+ if (datacache & 0x1)
+ {
+ privmask |= 0x10;
+ usermask |= 0x10;
+
+ if (datawrite & 0x1)
+ {
+ privmask |= 0x20;
+ usermask |= 0x20;
+ }
+ }
+
+ if (codecache & 0x1)
+ {
+ privmask |= 0x40;
+ usermask |= 0x40;
+ }
+
+ printf("PU region %d: %08X-%08X, user=%02X priv=%02X\n", n, start<<12, end<<12, usermask, privmask);
+
+ for (u32 i = start; i < end; i++)
+ {
+ PU_UserMap[i] = usermask;
+ PU_PrivMap[i] = privmask;
+ }
+
+ coderw >>= 4;
+ datarw >>= 4;
+ codecache >>= 1;
+ datacache >>= 1;
+ datawrite >>= 1;
+
+ // TODO: this will not be enough if they change their PU regions after the intial setup
+ //UpdateRegionTimings(start<<12, end<<12);
+ }
+
+ // TODO: this is way unoptimized
+ // should be okay unless the game keeps changing shit, tho
+ UpdateRegionTimings(0x00000000, 0xFFFFFFFF);
+}
+
+void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend)
+{
+ addrstart >>= 12;
+ addrend >>= 12;
+
+ if (addrend == 0xFFFFF) addrend++;
+
+ for (u32 i = addrstart; i < addrend; i++)
+ {
+ u8 pu = PU_Map[i];
+ u8* bustimings = NDS::ARM9MemTimings[i >> 2];
+
+ if (pu & 0x40)
+ {
+ MemTimings[i][0] = 0xFF;//kCodeCacheTiming;
+ }
+ else
+ {
+ MemTimings[i][0] = bustimings[2] << ClockShift;
+ }
+
+ if (pu & 0x10)
+ {
+ MemTimings[i][1] = kDataCacheTiming;
+ MemTimings[i][2] = kDataCacheTiming;
+ MemTimings[i][3] = 1;
+ }
+ else
+ {
+ MemTimings[i][1] = bustimings[0] << ClockShift;
+ MemTimings[i][2] = bustimings[2] << ClockShift;
+ MemTimings[i][3] = bustimings[3] << ClockShift;
+ }
+ }
+}
+
+
+void ARMv5::CP15Write(u32 id, u32 val)
{
//printf("CP15 write op %03X %08X %08X\n", id, val, NDS::ARM9->R[15]);
switch (id)
{
case 0x100:
- val &= 0x000FF085;
- Control &= ~0x000FF085;
- Control |= val;
- UpdateDTCMSetting();
- UpdateITCMSetting();
+ {
+ u32 old = CP15Control;
+ val &= 0x000FF085;
+ CP15Control &= ~0x000FF085;
+ CP15Control |= val;
+ printf("CP15Control = %08X (%08X->%08X)\n", CP15Control, old, val);
+ UpdateDTCMSetting();
+ UpdateITCMSetting();
+ if ((old & 0x1005) != (val & 0x1005)) UpdatePURegions();
+ if (val & (1<<7)) printf("!!!! ARM9 BIG ENDIAN MODE. VERY BAD. SHIT GONNA ASPLODE NOW\n");
+ if (val & (1<<13)) ExceptionBase = 0xFFFF0000;
+ else ExceptionBase = 0x00000000;
+ }
+ return;
+
+
+ case 0x200: // data cacheable
+ PU_DataCacheable = val;
+ printf("PU: DataCacheable=%08X\n", val);
+ UpdatePURegions();
+ return;
+
+ case 0x201: // code cacheable
+ PU_CodeCacheable = val;
+ printf("PU: CodeCacheable=%08X\n", val);
+ UpdatePURegions();
+ return;
+
+
+ case 0x300: // data cache write-buffer
+ PU_DataCacheWrite = val;
+ printf("PU: DataCacheWrite=%08X\n", val);
+ UpdatePURegions();
+ return;
+
+
+ case 0x500: // legacy data permissions
+ PU_DataRW = 0;
+ PU_DataRW |= (val & 0x0003);
+ PU_DataRW |= ((val & 0x000C) << 2);
+ PU_DataRW |= ((val & 0x0030) << 4);
+ PU_DataRW |= ((val & 0x00C0) << 6);
+ PU_DataRW |= ((val & 0x0300) << 8);
+ PU_DataRW |= ((val & 0x0C00) << 10);
+ PU_DataRW |= ((val & 0x3000) << 12);
+ PU_DataRW |= ((val & 0xC000) << 14);
+ printf("PU: DataRW=%08X (legacy %08X)\n", PU_DataRW, val);
+ UpdatePURegions();
+ return;
+
+ case 0x501: // legacy code permissions
+ PU_CodeRW = 0;
+ PU_CodeRW |= (val & 0x0003);
+ PU_CodeRW |= ((val & 0x000C) << 2);
+ PU_CodeRW |= ((val & 0x0030) << 4);
+ PU_CodeRW |= ((val & 0x00C0) << 6);
+ PU_CodeRW |= ((val & 0x0300) << 8);
+ PU_CodeRW |= ((val & 0x0C00) << 10);
+ PU_CodeRW |= ((val & 0x3000) << 12);
+ PU_CodeRW |= ((val & 0xC000) << 14);
+ printf("PU: CodeRW=%08X (legacy %08X)\n", PU_CodeRW, val);
+ UpdatePURegions();
+ return;
+
+ case 0x502: // data permissions
+ PU_DataRW = val;
+ printf("PU: DataRW=%08X\n", PU_DataRW);
+ UpdatePURegions();
+ return;
+
+ case 0x503: // code permissions
+ PU_CodeRW = val;
+ printf("PU: CodeRW=%08X\n", PU_CodeRW);
+ UpdatePURegions();
+ return;
+
+
+ case 0x600:
+ case 0x601:
+ case 0x610:
+ case 0x611:
+ case 0x620:
+ case 0x621:
+ case 0x630:
+ case 0x631:
+ case 0x640:
+ case 0x641:
+ case 0x650:
+ case 0x651:
+ case 0x660:
+ case 0x661:
+ case 0x670:
+ case 0x671:
+ PU_Region[(id >> 4) & 0xF] = val;
+ printf("PU: region %d = %08X : ", (id>>4)&0xF, val);
+ printf("%s, ", val&1 ? "enabled":"disabled");
+ printf("%08X-", val&0xFFFFF000);
+ printf("%08X\n", (val&0xFFFFF000)+(2<<((val&0x3E)>>1)));
+ UpdatePURegions();
return;
case 0x704:
case 0x782:
- NDS::ARM9->Halt(1);
+ Halt(1);
return;
@@ -148,6 +411,7 @@ void Write(u32 id, u32 val)
DTCMSetting = val;
UpdateDTCMSetting();
return;
+
case 0x911:
ITCMSetting = val;
UpdateITCMSetting();
@@ -158,7 +422,7 @@ void Write(u32 id, u32 val)
printf("unknown CP15 write op %03X %08X\n", id, val);
}
-u32 Read(u32 id)
+u32 ARMv5::CP15Read(u32 id)
{
//printf("CP15 read op %03X %08X\n", id, NDS::ARM9->R[15]);
@@ -180,7 +444,66 @@ u32 Read(u32 id)
case 0x100: // control reg
- return Control;
+ return CP15Control;
+
+
+ case 0x200:
+ return PU_DataCacheable;
+ case 0x201:
+ return PU_CodeCacheable;
+ case 0x300:
+ return PU_DataCacheWrite;
+
+
+ case 0x500:
+ {
+ u32 ret = 0;
+ ret |= (PU_DataRW & 0x00000003);
+ ret |= ((PU_DataRW & 0x00000030) >> 2);
+ ret |= ((PU_DataRW & 0x00000300) >> 4);
+ ret |= ((PU_DataRW & 0x00003000) >> 6);
+ ret |= ((PU_DataRW & 0x00030000) >> 8);
+ ret |= ((PU_DataRW & 0x00300000) >> 10);
+ ret |= ((PU_DataRW & 0x03000000) >> 12);
+ ret |= ((PU_DataRW & 0x30000000) >> 14);
+ return ret;
+ }
+ case 0x501:
+ {
+ u32 ret = 0;
+ ret |= (PU_CodeRW & 0x00000003);
+ ret |= ((PU_CodeRW & 0x00000030) >> 2);
+ ret |= ((PU_CodeRW & 0x00000300) >> 4);
+ ret |= ((PU_CodeRW & 0x00003000) >> 6);
+ ret |= ((PU_CodeRW & 0x00030000) >> 8);
+ ret |= ((PU_CodeRW & 0x00300000) >> 10);
+ ret |= ((PU_CodeRW & 0x03000000) >> 12);
+ ret |= ((PU_CodeRW & 0x30000000) >> 14);
+ return ret;
+ }
+ case 0x502:
+ return PU_DataRW;
+ case 0x503:
+ return PU_CodeRW;
+
+
+ case 0x600:
+ case 0x601:
+ case 0x610:
+ case 0x611:
+ case 0x620:
+ case 0x621:
+ case 0x630:
+ case 0x631:
+ case 0x640:
+ case 0x641:
+ case 0x650:
+ case 0x651:
+ case 0x660:
+ case 0x661:
+ case 0x670:
+ case 0x671:
+ return PU_Region[(id >> 4) & 0xF];
case 0x910:
@@ -197,123 +520,201 @@ u32 Read(u32 id)
// TCM are handled here.
// TODO: later on, handle PU, and maybe caches
-bool HandleCodeRead16(u32 addr, u16* val)
+u32 ARMv5::CodeRead32(u32 addr)
{
if (addr < ITCMSize)
{
- *val = *(u16*)&ITCM[addr & 0x7FFF];
- return true;
+ CodeCycles = 1;
+ return *(u32*)&ITCM[addr & 0x7FFF];
}
- return false;
-}
-
-bool HandleCodeRead32(u32 addr, u32* val)
-{
- if (addr < ITCMSize)
+ CodeCycles = RegionCodeCycles;
+ if (CodeCycles == 0xFF)
{
- *val = *(u32*)&ITCM[addr & 0x7FFF];
- return true;
+ // sort of code cache hit/miss average
+ if (!(addr & 0x1F)) CodeCycles = kCodeCacheTiming;
+ else CodeCycles = 1;
}
- return false;
+ if (CodeMem.Mem) return *(u32*)&CodeMem.Mem[addr & CodeMem.Mask];
+
+ return NDS::ARM9Read32(addr);
}
-bool HandleDataRead8(u32 addr, u8* val, u32 forceuser)
+void ARMv5::DataRead8(u32 addr, u32* val)
{
if (addr < ITCMSize)
{
+ DataCycles = 1;
*val = *(u8*)&ITCM[addr & 0x7FFF];
- return true;
+ return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
+ DataCycles = 1;
*val = *(u8*)&DTCM[(addr - DTCMBase) & 0x3FFF];
- return true;
+ return;
}
- return false;
+ *val = NDS::ARM9Read8(addr);
+ DataCycles = MemTimings[addr >> 12][1];
}
-bool HandleDataRead16(u32 addr, u16* val, u32 forceuser)
+void ARMv5::DataRead16(u32 addr, u32* val)
{
+ addr &= ~1;
+
if (addr < ITCMSize)
{
+ DataCycles = 1;
*val = *(u16*)&ITCM[addr & 0x7FFF];
- return true;
+ return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
+ DataCycles = 1;
*val = *(u16*)&DTCM[(addr - DTCMBase) & 0x3FFF];
- return true;
+ return;
+ }
+
+ *val = NDS::ARM9Read16(addr);
+ DataCycles = MemTimings[addr >> 12][1];
+}
+
+void ARMv5::DataRead32(u32 addr, u32* val)
+{
+ addr &= ~3;
+
+ if (addr < ITCMSize)
+ {
+ DataCycles = 1;
+ *val = *(u32*)&ITCM[addr & 0x7FFF];
+ return;
+ }
+ if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
+ {
+ DataCycles = 1;
+ *val = *(u32*)&DTCM[(addr - DTCMBase) & 0x3FFF];
+ return;
}
- return false;
+ *val = NDS::ARM9Read32(addr);
+ DataCycles = MemTimings[addr >> 12][2];
}
-bool HandleDataRead32(u32 addr, u32* val, u32 forceuser)
+void ARMv5::DataRead32S(u32 addr, u32* val)
{
+ addr &= ~3;
+
if (addr < ITCMSize)
{
+ DataCycles += 1;
*val = *(u32*)&ITCM[addr & 0x7FFF];
- return true;
+ return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
+ DataCycles += 1;
*val = *(u32*)&DTCM[(addr - DTCMBase) & 0x3FFF];
- return true;
+ return;
}
- return false;
+ *val = NDS::ARM9Read32(addr);
+ DataCycles += MemTimings[addr >> 12][3];
}
-bool HandleDataWrite8(u32 addr, u8 val, u32 forceuser)
+void ARMv5::DataWrite8(u32 addr, u8 val)
{
if (addr < ITCMSize)
{
+ DataCycles = 1;
*(u8*)&ITCM[addr & 0x7FFF] = val;
- return true;
+ return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
+ DataCycles = 1;
*(u8*)&DTCM[(addr - DTCMBase) & 0x3FFF] = val;
- return true;
+ return;
}
- return false;
+ NDS::ARM9Write8(addr, val);
+ DataCycles = MemTimings[addr >> 12][1];
}
-bool HandleDataWrite16(u32 addr, u16 val, u32 forceuser)
+void ARMv5::DataWrite16(u32 addr, u16 val)
{
+ addr &= ~1;
+
if (addr < ITCMSize)
{
+ DataCycles = 1;
*(u16*)&ITCM[addr & 0x7FFF] = val;
- return true;
+ return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
+ DataCycles = 1;
*(u16*)&DTCM[(addr - DTCMBase) & 0x3FFF] = val;
- return true;
+ return;
}
- return false;
+ NDS::ARM9Write16(addr, val);
+ DataCycles = MemTimings[addr >> 12][1];
}
-bool HandleDataWrite32(u32 addr, u32 val, u32 forceuser)
+void ARMv5::DataWrite32(u32 addr, u32 val)
{
+ addr &= ~3;
+
if (addr < ITCMSize)
{
+ DataCycles = 1;
*(u32*)&ITCM[addr & 0x7FFF] = val;
- return true;
+ return;
}
if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
{
+ DataCycles = 1;
*(u32*)&DTCM[(addr - DTCMBase) & 0x3FFF] = val;
- return true;
+ return;
}
- return false;
+ NDS::ARM9Write32(addr, val);
+ DataCycles = MemTimings[addr >> 12][2];
}
+void ARMv5::DataWrite32S(u32 addr, u32 val)
+{
+ addr &= ~3;
+
+ if (addr < ITCMSize)
+ {
+ DataCycles += 1;
+ *(u32*)&ITCM[addr & 0x7FFF] = val;
+ return;
+ }
+ if (addr >= DTCMBase && addr < (DTCMBase + DTCMSize))
+ {
+ DataCycles += 1;
+ *(u32*)&DTCM[(addr - DTCMBase) & 0x3FFF] = val;
+ return;
+ }
+
+ NDS::ARM9Write32(addr, val);
+ DataCycles += MemTimings[addr >> 12][3];
+}
+
+void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region)
+{
+ /*if (addr < ITCMSize)
+ {
+ region->Mem = ITCM;
+ region->Mask = 0x7FFF;
+ return;
+ }*/
+
+ NDS::ARM9GetMemRegion(addr, false, &CodeMem);
}
+
diff --git a/src/CRC32.cpp b/src/CRC32.cpp
new file mode 100644
index 0000000..62e334f
--- /dev/null
+++ b/src/CRC32.cpp
@@ -0,0 +1,69 @@
+/*
+ Copyright 2016-2019 StapleButter
+
+ 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 "CRC32.h"
+
+// http://www.codeproject.com/KB/recipes/crc32_large.aspx
+
+u32 crctable[256];
+bool tableinited = false;
+
+u32 _reflect(u32 refl, char ch)
+{
+ u32 value = 0;
+
+ for(int i = 1; i < (ch + 1); i++)
+ {
+ if (refl & 1)
+ value |= 1 << (ch - i);
+ refl >>= 1;
+ }
+
+ return value;
+}
+
+void _inittable()
+{
+ u32 polynomial = 0x04C11DB7;
+
+ for (int i = 0; i < 0x100; i++)
+ {
+ crctable[i] = _reflect(i, 8) << 24;
+
+ for (int j = 0; j < 8; j++)
+ crctable[i] = (crctable[i] << 1) ^ (crctable[i] & (1 << 31) ? polynomial : 0);
+
+ crctable[i] = _reflect(crctable[i], 32);
+ }
+}
+
+u32 CRC32(u8 *data, int len)
+{
+ if (!tableinited)
+ {
+ _inittable();
+ tableinited = true;
+ }
+
+ u32 crc = 0xFFFFFFFF;
+
+ while (len--)
+ crc = (crc >> 8) ^ crctable[(crc & 0xFF) ^ *data++];
+
+ return (crc ^ 0xFFFFFFFF);
+}
diff --git a/src/CRC32.h b/src/CRC32.h
new file mode 100644
index 0000000..639a8a3
--- /dev/null
+++ b/src/CRC32.h
@@ -0,0 +1,26 @@
+/*
+ Copyright 2016-2019 StapleButter
+
+ 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 CRC32_H
+#define CRC32_H
+
+#include "types.h"
+
+u32 CRC32(u8* data, int len);
+
+#endif // CRC32_H
diff --git a/src/Config.cpp b/src/Config.cpp
index fea92e9..4836cf5 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -20,24 +20,23 @@
#include <string.h>
#include <stdlib.h>
#include "Config.h"
-#include <string>
-#ifdef _WIN32
-#define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
-#include <windows.h>
-//#include <knownfolders.h> // FUCK THAT SHIT
-extern "C" const GUID DECLSPEC_SELECTANY FOLDERID_RoamingAppData = {0x3eb685db, 0x65f9, 0x4cf6, {0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d}};
-#include <shlobj.h>
-#else
-#include <glib.h>
-#endif
+#include "melon_fopen.h"
+bool LocalFileExists(const char* name);
+extern char* EmuDirectory;
+
namespace Config
{
+const char* kConfigFile = "melonDS.ini";
+
int KeyMapping[12];
int JoyMapping[12];
+int HKKeyMapping[HK_MAX];
+int HKJoyMapping[HK_MAX];
+
int WindowWidth;
int WindowHeight;
@@ -55,6 +54,12 @@ int SocketBindAnyAddr;
int SavestateRelocSRAM;
+int AudioVolume;
+int MicInputType;
+char MicWavPath[512];
+
+char LastROMFolder[512];
+
typedef struct
{
char Name[16];
@@ -62,7 +67,7 @@ typedef struct
void* Value;
int DefaultInt;
char* DefaultStr;
- int StrLength;
+ int StrLength; // should be set to actual array length minus one
} ConfigEntry;
@@ -94,6 +99,12 @@ ConfigEntry ConfigFile[] =
{"Joy_X", 0, &JoyMapping[10], -1, NULL, 0},
{"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0},
+ {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], 0x0E, NULL, 0},
+ {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], 0x35, NULL, 0},
+
+ {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
+ {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
+
{"WindowWidth", 0, &WindowWidth, 256, NULL, 0},
{"WindowHeight", 0, &WindowHeight, 384, NULL, 0},
@@ -111,75 +122,15 @@ ConfigEntry ConfigFile[] =
{"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 1, NULL, 0},
+ {"AudioVolume", 0, &AudioVolume, 255, NULL, 0},
+ {"MicInputType", 0, &MicInputType, 1, NULL, 0},
+ {"MicWavPath", 1, MicWavPath, 0, "", 511},
+
+ {"LastROMFolder", 1, LastROMFolder, 0, "", 511},
+
{"", -1, NULL, 0, NULL, 0}
};
-FILE* GetConfigFile(const char* fileName, const char* permissions)
-{
- // Locations are application directory, and XDG_CONFIG_HOME/melonds or AppData/MelonDS on windows
-
- FILE* f;
-
- // First check application directory
- f = fopen(fileName, permissions);
- if (f) return f;
-#ifdef _WIN32
- // Now check AppData
- PWSTR appDataPath = NULL;
- SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath);
- if (!appDataPath)
- return NULL;
-
- const WCHAR* appdir = L"\\melonDS\\";
-
- int fnlen = MultiByteToWideChar(CP_UTF8, 0, fileName, -1, NULL, 0);
- if (fnlen < 1) return NULL;
- WCHAR* wfileName = new WCHAR[fnlen];
- int res = MultiByteToWideChar(CP_UTF8, 0, fileName, -1, wfileName, fnlen);
- if (res != fnlen) { delete[] wfileName; return NULL; } // checkme?
-
- int pos = wcslen(appDataPath);
- void* ptr = CoTaskMemRealloc(appDataPath, (pos+wcslen(appdir)+fnlen+1)*sizeof(WCHAR));
- if (!ptr) { delete[] wfileName; return NULL; } // oh well
- appDataPath = (PWSTR)ptr;
-
- wcscpy(&appDataPath[pos], appdir); pos += wcslen(appdir);
- wcscpy(&appDataPath[pos], wfileName);
-
- // this will be more than enough
- WCHAR fatperm[4];
- fatperm[0] = permissions[0];
- fatperm[1] = permissions[1];
- fatperm[2] = permissions[2];
- fatperm[3] = 0;
-
- f = _wfopen(appDataPath, fatperm);
- CoTaskMemFree(appDataPath);
- delete[] wfileName;
- if (f) return f;
-#else
- // Now check XDG_CONFIG_HOME
- // TODO: check for memory leak there
- std::string path = std::string(g_get_user_config_dir()) + "/melonds/" + fileName;
- f = fopen(path.c_str(), permissions);
- if (f) return f;
-#endif
-
- return NULL;
-
-}
-
-bool HasConfigFile(const char* fileName)
-{
- FILE* f = GetConfigFile(fileName, "rb");
- if (f)
- {
- fclose(f);
- return true;
- }
- else
- return false;
-}
void Load()
{
@@ -191,12 +142,15 @@ void Load()
if (entry->Type == 0)
*(int*)entry->Value = entry->DefaultInt;
else
+ {
strncpy((char*)entry->Value, entry->DefaultStr, entry->StrLength);
+ ((char*)entry->Value)[entry->StrLength] = '\0';
+ }
entry++;
}
- FILE* f = Config::GetConfigFile("melonDS.ini", "r");
+ FILE* f = melon_fopen_local(kConfigFile, "r");
if (!f) return;
char linebuf[1024];
@@ -232,8 +186,26 @@ void Load()
void Save()
{
- FILE* f = Config::GetConfigFile("melonDS.ini", "w");
- if (!f) return;
+ FILE* f;
+ if (LocalFileExists(kConfigFile))
+ {
+ f = melon_fopen_local(kConfigFile, "w");
+ if (!f) return;
+ }
+ else
+ {
+ int dirlen = strlen(EmuDirectory);
+ int filelen = strlen(kConfigFile);
+ char* path = new char[dirlen + 1 + filelen + 1];
+ strncpy(&path[0], EmuDirectory, dirlen);
+ path[dirlen] = '/';
+ strncpy(&path[dirlen+1], kConfigFile, filelen);
+ path[dirlen+1+filelen] = '\0';
+
+ f = melon_fopen(path, "w");
+ delete[] path;
+ if (!f) return;
+ }
ConfigEntry* entry = &ConfigFile[0];
for (;;)
diff --git a/src/Config.h b/src/Config.h
index d7b0858..236c1a3 100644
--- a/src/Config.h
+++ b/src/Config.h
@@ -21,6 +21,14 @@
#include "types.h"
+enum
+{
+ HK_Lid = 0,
+ HK_Mic,
+
+ HK_MAX
+};
+
namespace Config
{
FILE* GetConfigFile(const char* fileName, const char* permissions);
@@ -31,6 +39,9 @@ void Save();
extern int KeyMapping[12];
extern int JoyMapping[12];
+extern int HKKeyMapping[HK_MAX];
+extern int HKJoyMapping[HK_MAX];
+
extern int WindowWidth;
extern int WindowHeight;
@@ -48,6 +59,12 @@ extern int SocketBindAnyAddr;
extern int SavestateRelocSRAM;
+extern int AudioVolume;
+extern int MicInputType;
+extern char MicWavPath[512];
+
+extern char LastROMFolder[512];
+
}
#endif // CONFIG_H
diff --git a/src/DMA.cpp b/src/DMA.cpp
index f0c22b5..7bbf980 100644
--- a/src/DMA.cpp
+++ b/src/DMA.cpp
@@ -26,6 +26,25 @@
// 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
+//
+// sequential timing:
+// * 1 cycle per read or write
+// * in 32bit mode, accessing a 16bit bus (mainRAM, palette, VRAM) incurs 1 cycle of penalty
+// * in 32bit mode, transferring from mainRAM to another bank is 1 cycle faster
+// * if source and destination are the same memory bank, there is a 1 cycle penalty
+// * transferring from mainRAM to mainRAM is a trainwreck (all accesses are made nonsequential)
+//
+// nonsequential timing:
+// * nonseq penalty is applied to the first read and write
+// * I also figure it gets nonseq penalty again when resuming, after having been interrupted by
+// another DMA (TODO: check)
+// * applied to all accesses for mainRAM->mainRAM, resulting in timings of 16-18 cycles per unit
+//
+// TODO: GBA slot
DMA::DMA(u32 cpu, u32 num)
@@ -38,61 +57,6 @@ DMA::DMA(u32 cpu, u32 num)
else
CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF);
- // TODO: merge with the one in ARM.cpp, somewhere
- for (int i = 0; i < 16; i++)
- {
- Waitstates[0][i] = 1;
- Waitstates[1][i] = 1;
- }
-
- if (!cpu)
- {
- // ARM9
- // note: 33MHz cycles
- Waitstates[0][0x2] = 1;
- Waitstates[0][0x3] = 1;
- Waitstates[0][0x4] = 1;
- Waitstates[0][0x5] = 1;
- Waitstates[0][0x6] = 1;
- Waitstates[0][0x7] = 1;
- Waitstates[0][0x8] = 6;
- Waitstates[0][0x9] = 6;
- Waitstates[0][0xA] = 10;
- Waitstates[0][0xF] = 1;
-
- Waitstates[1][0x2] = 2;
- Waitstates[1][0x3] = 1;
- Waitstates[1][0x4] = 1;
- Waitstates[1][0x5] = 2;
- Waitstates[1][0x6] = 2;
- Waitstates[1][0x7] = 1;
- Waitstates[1][0x8] = 12;
- Waitstates[1][0x9] = 12;
- Waitstates[1][0xA] = 10;
- Waitstates[1][0xF] = 1;
- }
- else
- {
- // ARM7
- Waitstates[0][0x0] = 1;
- Waitstates[0][0x2] = 1;
- Waitstates[0][0x3] = 1;
- Waitstates[0][0x4] = 1;
- Waitstates[0][0x6] = 1;
- Waitstates[0][0x8] = 6;
- Waitstates[0][0x9] = 6;
- Waitstates[0][0xA] = 10;
-
- Waitstates[1][0x0] = 1;
- Waitstates[1][0x2] = 2;
- Waitstates[1][0x3] = 1;
- Waitstates[1][0x4] = 1;
- Waitstates[1][0x6] = 2;
- Waitstates[1][0x8] = 12;
- Waitstates[1][0x9] = 12;
- Waitstates[1][0xA] = 10;
- }
-
Reset();
}
@@ -136,7 +100,7 @@ void DMA::DoSavestate(Savestate* file)
file->Var32(&SrcAddrInc);
file->Var32(&DstAddrInc);
- file->Var32((u32*)&Running);
+ file->Var32(&Running);
file->Var32((u32*)&InProgress);
file->Var32((u32*)&IsGXFIFODMA);
}
@@ -213,7 +177,11 @@ void DMA::Start()
// TODO eventually: not stop if we're running code in ITCM
- Running = true;
+ if (NDS::DMAsRunning(CPU))
+ Running = 1;
+ else
+ Running = 2;
+
InProgress = true;
NDS::StopCPU(CPU, 1<<Num);
}
@@ -223,73 +191,177 @@ s32 DMA::Run(s32 cycles)
if (!Running)
return cycles;
+#ifdef DEBUG_CHECK_DESYNC
+ s32 startc = cycles;
+#endif // DEBUG_CHECK_DESYNC
+
+ Executing = true;
+
+ // add NS penalty for first accesses in burst
+ // note: this seems to only apply when starting DMA 'in the void'
+ // for example, the aging cart DMA PRIORITY test:
+ // starts a big DMA immediately, and a small DMA upon HBlank
+ // each pulling from a timer incrementing once per cycle
+ // it expects that the values be increasing linearly (2c/unit)
+ // even as the small DMA starts and ends
+ bool burststart = (Running == 2);
+ Running = 1;
+
+ s32 unitcycles;
+ s32 lastcycles = cycles;
+
if (!(Cnt & 0x04000000))
{
+ if (CPU == 0)
+ {
+ if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
+ {
+ unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][0] + NDS::ARM9MemTimings[CurDstAddr >> 14][0];
+ }
+ else
+ {
+ unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][1] + NDS::ARM9MemTimings[CurDstAddr >> 14][1];
+ if ((CurSrcAddr >> 24) == (CurDstAddr >> 24))
+ unitcycles++;
+
+ if (burststart)
+ {
+ cycles -= 2;
+ cycles -= (NDS::ARM9MemTimings[CurSrcAddr >> 14][0] + NDS::ARM9MemTimings[CurDstAddr >> 14][0]);
+ cycles += unitcycles;
+ }
+ }
+ }
+ else
+ {
+ if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
+ {
+ unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][0] + NDS::ARM7MemTimings[CurDstAddr >> 15][0];
+ }
+ else
+ {
+ unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][1] + NDS::ARM7MemTimings[CurDstAddr >> 15][1];
+ if ((CurSrcAddr >> 23) == (CurDstAddr >> 23))
+ unitcycles++;
+
+ if (burststart)
+ {
+ cycles -= 2;
+ cycles -= (NDS::ARM7MemTimings[CurSrcAddr >> 15][0] + NDS::ARM7MemTimings[CurDstAddr >> 15][0]);
+ cycles += unitcycles;
+ }
+ }
+ }
+
u16 (*readfn)(u32) = CPU ? NDS::ARM7Read16 : NDS::ARM9Read16;
void (*writefn)(u32,u16) = CPU ? NDS::ARM7Write16 : NDS::ARM9Write16;
- while (IterCount > 0 && cycles > 0)
+ while (IterCount > 0 && !Stall)
{
- writefn(CurDstAddr, readfn(CurSrcAddr));
+ cycles -= unitcycles;
+
+ NDS::RunTightTimers(CPU, lastcycles-cycles);
- s32 c = (Waitstates[0][(CurSrcAddr >> 24) & 0xF] + Waitstates[0][(CurDstAddr >> 24) & 0xF]);
- cycles -= c;
- NDS::RunTimingCriticalDevices(CPU, c);
+ lastcycles = cycles;
+
+ writefn(CurDstAddr, readfn(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<1;
CurDstAddr += DstAddrInc<<1;
IterCount--;
RemCount--;
+
+ if (cycles <= 0) break;
}
}
else
{
- // optimized path for typical GXFIFO DMA
- if (IsGXFIFODMA)
+ if (CPU == 0)
{
- while (IterCount > 0 && cycles > 0)
+ if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
- GPU3D::WriteToGXFIFO(*(u32*)&NDS::MainRAM[CurSrcAddr&0x3FFFFF]);
-
- s32 c = (Waitstates[1][0x2] + Waitstates[1][0x4]);
- cycles -= c;
- NDS::RunTimingCriticalDevices(0, c);
-
- CurSrcAddr += SrcAddrInc<<2;
- IterCount--;
- RemCount--;
+ 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;
+ }
+ }
+ }
+ else
+ {
+ 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;
+ }
}
}
u32 (*readfn)(u32) = CPU ? NDS::ARM7Read32 : NDS::ARM9Read32;
void (*writefn)(u32,u32) = CPU ? NDS::ARM7Write32 : NDS::ARM9Write32;
- while (IterCount > 0 && cycles > 0)
+ while (IterCount > 0 && !Stall)
{
- writefn(CurDstAddr, readfn(CurSrcAddr));
+ cycles -= unitcycles;
+
+ NDS::RunTightTimers(CPU, lastcycles-cycles);
- s32 c = (Waitstates[1][(CurSrcAddr >> 24) & 0xF] + Waitstates[1][(CurDstAddr >> 24) & 0xF]);
- cycles -= c;
- NDS::RunTimingCriticalDevices(CPU, c);
+ lastcycles = cycles;
+
+ writefn(CurDstAddr, readfn(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
IterCount--;
RemCount--;
+
+ if (cycles <= 0) break;
}
}
+ Executing = false;
+ Stall = false;
+
if (RemCount)
{
if (IterCount == 0)
{
- Running = false;
+ Running = 0;
NDS::ResumeCPU(CPU, 1<<Num);
if (StartMode == 0x07)
GPU3D::CheckFIFODMA();
}
+#ifdef DEBUG_CHECK_DESYNC
+ if (CPU) NDS::dbg_CyclesARM7 += (startc-cycles);
+ else NDS::dbg_CyclesARM9 += (startc-cycles);
+#endif // DEBUG_CHECK_DESYNC
+
return cycles;
}
@@ -299,9 +371,14 @@ s32 DMA::Run(s32 cycles)
if (Cnt & 0x40000000)
NDS::SetIRQ(CPU, NDS::IRQ_DMA0 + Num);
- Running = false;
+ Running = 0;
InProgress = false;
NDS::ResumeCPU(CPU, 1<<Num);
- return cycles - 2;
+#ifdef DEBUG_CHECK_DESYNC
+ if (CPU) NDS::dbg_CyclesARM7 += (startc-cycles);
+ else NDS::dbg_CyclesARM9 += (startc-cycles);
+#endif // DEBUG_CHECK_DESYNC
+
+ return cycles;
}
diff --git a/src/DMA.h b/src/DMA.h
index 6157431..1d1992a 100644
--- a/src/DMA.h
+++ b/src/DMA.h
@@ -41,6 +41,8 @@ public:
return ((mode == StartMode) && (Cnt & 0x80000000));
}
+ bool IsRunning() { return Running!=0; }
+
void StartIfNeeded(u32 mode)
{
if ((mode == StartMode) && (Cnt & 0x80000000))
@@ -53,6 +55,11 @@ public:
Cnt &= ~0x80000000;
}
+ void StallIfRunning()
+ {
+ if (Executing) Stall = true;
+ }
+
u32 SrcAddr;
u32 DstAddr;
u32 Cnt;
@@ -60,8 +67,6 @@ public:
private:
u32 CPU, Num;
- s32 Waitstates[2][16];
-
u32 StartMode;
u32 CurSrcAddr;
u32 CurDstAddr;
@@ -71,9 +76,12 @@ private:
u32 DstAddrInc;
u32 CountMask;
- bool Running;
+ u32 Running;
bool InProgress;
+ bool Executing;
+ bool Stall;
+
bool IsGXFIFODMA;
};
diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp
index 078374b..4951404 100644
--- a/src/GPU2D.cpp
+++ b/src/GPU2D.cpp
@@ -107,6 +107,9 @@ void GPU2D::Reset()
memset(Win1Coords, 0, 4);
memset(WinCnt, 0, 4);
+ Win0Active = 0;
+ Win1Active = 0;
+
BGMosaicSize[0] = 0;
BGMosaicSize[1] = 0;
OBJMosaicSize[0] = 0;
@@ -185,6 +188,17 @@ void GPU2D::DoSavestate(Savestate* file)
file->Var32(&CaptureCnt);
}
+ if (file->IsAtleastVersion(2, 1))
+ {
+ file->Var32(&Win0Active);
+ file->Var32(&Win1Active);
+ }
+ else
+ {
+ Win0Active = 0;
+ Win1Active = 0;
+ }
+
if (!file->Saving)
{
// refresh those
@@ -538,6 +552,15 @@ void GPU2D::DrawScanline(u32 line)
return;
}
+ // forced blank
+ // (checkme: are there still things that can run under this mode? likely not)
+ if (DispCnt & (1<<7))
+ {
+ for (int i = 0; i < 256; i++)
+ dst[i] = 0xFFFFFFFF;
+ return;
+ }
+
u32 dispmode = DispCnt >> 16;
dispmode &= (Num ? 0x1 : 0x3);
@@ -952,10 +975,10 @@ u16* GPU2D::GetOBJExtPal(u32 pal)
void GPU2D::CheckWindows(u32 line)
{
line &= 0xFF;
- if (line == Win0Coords[3]) Win0Active = false;
- else if (line == Win0Coords[2]) Win0Active = true;
- if (line == Win1Coords[3]) Win1Active = false;
- else if (line == Win1Coords[2]) Win1Active = true;
+ if (line == Win0Coords[3]) Win0Active &= ~0x1;
+ else if (line == Win0Coords[2]) Win0Active |= 0x1;
+ if (line == Win1Coords[3]) Win1Active &= ~0x1;
+ else if (line == Win1Coords[2]) Win1Active |= 0x1;
}
void GPU2D::CalculateWindowMask(u32 line, u8* mask)
@@ -963,41 +986,40 @@ void GPU2D::CalculateWindowMask(u32 line, u8* mask)
for (u32 i = 0; i < 256; i++)
mask[i] = WinCnt[2]; // window outside
- if ((DispCnt & (1<<15)) && (DispCnt & (1<<12)))
+ if (DispCnt & ((1<<15)|(1<<12)))
{
// OBJ window
- u8 objwin[256];
- memset(objwin, 0, 256);
- DrawSpritesWindow(line, objwin);
-
- for (u32 i = 0; i < 256; i++)
- {
- if (objwin[i]) mask[i] = WinCnt[3];
- }
+ DrawSpritesWindow(line, mask);
}
- if ((DispCnt & (1<<14)) && Win1Active)
+ if (DispCnt & (1<<14))
{
// window 1
- u32 x1 = Win1Coords[0];
- u32 x2 = Win1Coords[1];
- if (x2 == 0 && x1 > 0) x2 = 256;
- if (x1 > x2) x2 = 255; // checkme
+ u8 x1 = Win1Coords[0];
+ u8 x2 = Win1Coords[1];
- for (u32 i = x1; i < x2; i++)
- mask[i] = WinCnt[1];
+ for (int i = 0; i < 256; i++)
+ {
+ if (i == x2) Win1Active &= ~0x2;
+ else if (i == x1) Win1Active |= 0x2;
+
+ if (Win1Active == 0x3) mask[i] = WinCnt[1];
+ }
}
- if ((DispCnt & (1<<13)) && Win0Active)
+ if (DispCnt & (1<<13))
{
// window 0
- u32 x1 = Win0Coords[0];
- u32 x2 = Win0Coords[1];
- if (x2 == 0 && x1 > 0) x2 = 256;
- if (x1 > x2) x2 = 255; // checkme
+ u8 x1 = Win0Coords[0];
+ u8 x2 = Win0Coords[1];
- for (u32 i = x1; i < x2; i++)
- mask[i] = WinCnt[0];
+ for (int i = 0; i < 256; i++)
+ {
+ if (i == x2) Win0Active &= ~0x2;
+ else if (i == x1) Win0Active |= 0x2;
+
+ if (Win0Active == 0x3) mask[i] = WinCnt[0];
+ }
}
}
@@ -1145,11 +1167,7 @@ void GPU2D::DrawScanline_Mode1(u32 line, u32* dst)
else if (flag2 & 0x40) target2 = 0x0100;
else target2 = flag2 << 8;
- if (!(windowmask[i] & 0x20))
- {
- coloreffect = 0;
- }
- else if ((flag1 & 0x80) && (BlendCnt & target2))
+ if ((flag1 & 0x80) && (BlendCnt & target2))
{
// sprite blending
@@ -1192,7 +1210,7 @@ void GPU2D::DrawScanline_Mode1(u32 line, u32* dst)
continue;
}
- else if (BlendCnt & flag1)
+ else if ((BlendCnt & flag1) && (windowmask[i] & 0x20))
{
if ((bldcnteffect == 1) && (BlendCnt & target2))
{
@@ -2154,7 +2172,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color & 0x8000)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = color | prio;
}
}
@@ -2218,7 +2236,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = pal[color] | prio;
}
}
@@ -2276,7 +2294,7 @@ void GPU2D::DrawSprite_Rotscale(u16* attrib, u16* rotparams, u32 boundwidth, u32
if (color)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = pal[color] | prio;
}
}
@@ -2390,7 +2408,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos, u32* d
if (color & 0x8000)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = color | prio;
}
@@ -2417,7 +2435,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos, u32* d
if (color & 0x8000)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = color | prio;
}
@@ -2477,7 +2495,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos, u32* d
if (color)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = pal[color] | prio;
}
@@ -2506,7 +2524,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos, u32* d
if (color)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = pal[color] | prio;
}
@@ -2555,7 +2573,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos, u32* d
if (color)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = pal[color] | prio;
}
@@ -2589,7 +2607,7 @@ void GPU2D::DrawSprite_Normal(u16* attrib, u32 width, s32 xpos, s32 ypos, u32* d
if (color)
{
- if (window) ((u8*)dst)[xpos] = 1;
+ if (window) ((u8*)dst)[xpos] = WinCnt[3];
else dst[xpos] = pal[color] | prio;
}
diff --git a/src/GPU2D.h b/src/GPU2D.h
index 307ff33..5a1192e 100644
--- a/src/GPU2D.h
+++ b/src/GPU2D.h
@@ -90,8 +90,8 @@ private:
u8 Win0Coords[4];
u8 Win1Coords[4];
u8 WinCnt[4];
- bool Win0Active;
- bool Win1Active;
+ u32 Win0Active;
+ u32 Win1Active;
u8 BGMosaicSize[2];
u8 OBJMosaicSize[2];
diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp
index 6e4d26a..df27913 100644
--- a/src/GPU3D.cpp
+++ b/src/GPU3D.cpp
@@ -71,6 +71,27 @@
// clear attributes
//
// TODO: check how DISP_1DOT_DEPTH works and whether it's latched
+//
+// TODO: emulate GPU hanging
+// * when calling BEGIN with an incomplete polygon defined
+// * probably same with BOXTEST
+// * when sending vertices immediately after a BOXTEST
+//
+// TODO: test results should probably not be presented immediately, even if we set the busy flag
+
+
+// command execution notes
+//
+// timings given by GBAtek are for individual commands
+// actual display lists have different timing characteristics
+// * vertex pipeline: individual vertex commands are able to execute in parallel
+// with certain other commands
+// * similarly, the normal command can execute in parallel with a subsequent vertex
+// * polygon pipeline: each vertex which completes a polygon takes longer to run
+// and imposes rules on when further vertex commands can run
+// (one every 9-cycle time slot during polygon setup)
+// polygon setup time is 27 cycles for a triangle and 36 for a quad
+// * additionally, some commands (BEGIN, LIGHT_VECTOR, BOXTEST) stall the polygon pipeline
namespace GPU3D
@@ -113,43 +134,6 @@ const u32 CmdNumParams[256] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-const s32 CmdNumCycles[256] =
-{
- // 0x00
- 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 0x10
- 1, 17, 36, 17, 36, 19, 34, 30, 35, 31, 28, 22, 22,
- 0, 0, 0,
- // 0x20
- 1, 9, 1, 9, 8, 8, 8, 8, 8, 1, 1, 1,
- 0, 0, 0, 0,
- // 0x30
- 4, 4, 6, 1, 32,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 0x40
- 1, 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 0x50
- 392,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 0x60
- 1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 0x70
- 103, 9, 5,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // 0x80+
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
typedef union
{
u64 _contents;
@@ -164,6 +148,8 @@ typedef union
FIFO<CmdFIFOEntry>* CmdFIFO;
FIFO<CmdFIFOEntry>* CmdPIPE;
+FIFO<CmdFIFOEntry>* CmdStallQueue;
+
u32 NumCommands, CurCommand, ParamCount, TotalParams;
u32 DispCnt;
@@ -192,7 +178,13 @@ u32 GXStat;
u32 ExecParams[32];
u32 ExecParamCount;
+
s32 CycleCount;
+s32 VertexPipeline;
+s32 NormalPipeline;
+s32 PolygonPipeline;
+s32 VertexSlotCounter;
+u32 VertexSlotsFree;
u32 NumPushPopCommands;
u32 NumTestCommands;
@@ -211,8 +203,8 @@ bool ClipMatrixDirty;
u32 Viewport[6];
s32 ProjMatrixStack[16];
-s32 PosMatrixStack[31][16];
-s32 VecMatrixStack[31][16];
+s32 PosMatrixStack[32][16];
+s32 VecMatrixStack[32][16];
s32 TexMatrixStack[16];
s32 ProjMatrixStackPointer;
s32 PosMatrixStackPointer;
@@ -276,6 +268,8 @@ bool Init()
CmdFIFO = new FIFO<CmdFIFOEntry>(256);
CmdPIPE = new FIFO<CmdFIFOEntry>(4);
+ CmdStallQueue = new FIFO<CmdFIFOEntry>(64);
+
if (!SoftRenderer::Init()) return false;
return true;
@@ -287,6 +281,8 @@ void DeInit()
delete CmdFIFO;
delete CmdPIPE;
+
+ delete CmdStallQueue;
}
void Reset()
@@ -294,6 +290,8 @@ void Reset()
CmdFIFO->Clear();
CmdPIPE->Clear();
+ CmdStallQueue->Clear();
+
NumCommands = 0;
CurCommand = 0;
ParamCount = 0;
@@ -309,7 +307,13 @@ void Reset()
memset(ExecParams, 0, 32*4);
ExecParamCount = 0;
+
CycleCount = 0;
+ VertexPipeline = 0;
+ NormalPipeline = 0;
+ PolygonPipeline = 0;
+ VertexSlotCounter = 0;
+ VertexSlotsFree = 1;
MatrixMode = 0;
@@ -387,8 +391,8 @@ void DoSavestate(Savestate* file)
file->VarArray(TexMatrix, 16*4);
file->VarArray(ProjMatrixStack, 16*4);
- file->VarArray(PosMatrixStack, 31*16*4);
- file->VarArray(VecMatrixStack, 31*16*4);
+ file->VarArray(PosMatrixStack, 32*16*4);
+ file->VarArray(VecMatrixStack, 32*16*4);
file->VarArray(TexMatrixStack, 16*4);
file->Var32((u32*)&ProjMatrixStackPointer);
@@ -514,6 +518,28 @@ void DoSavestate(Savestate* file)
// probably not worth storing the vblank-latched Renderxxxxxx variables
+ if (file->IsAtleastVersion(2, 1))
+ {
+ // command stall queue, only in version 2.1 and up
+ CmdStallQueue->DoSavestate(file);
+ file->Var32((u32*)&VertexPipeline);
+ file->Var32((u32*)&NormalPipeline);
+ file->Var32((u32*)&PolygonPipeline);
+ file->Var32((u32*)&VertexSlotCounter);
+ file->Var32(&VertexSlotsFree);
+ }
+ else
+ {
+ // for version 2.0, just clear it. not having it doesn't matter
+ // if this comes from older melonDS revisions.
+ CmdStallQueue->Clear();
+ VertexPipeline = 0;
+ NormalPipeline = 0;
+ PolygonPipeline = 0;
+ VertexSlotCounter = 0;
+ VertexSlotsFree = 1;
+ }
+
if (!file->Saving)
{
ClipMatrixDirty = true;
@@ -664,6 +690,105 @@ void UpdateClipMatrix()
+void AddCycles(s32 num)
+{
+ CycleCount += num;
+
+ if (VertexPipeline > 0)
+ {
+ if (VertexPipeline > num) VertexPipeline -= num;
+ else VertexPipeline = 0;
+ }
+
+ if (PolygonPipeline > 0)
+ {
+ if (PolygonPipeline > num)
+ {
+ PolygonPipeline -= num;
+ VertexSlotCounter += num;
+ while (VertexSlotCounter > 9)
+ {
+ VertexSlotCounter -= 9;
+ VertexSlotsFree >>= 1;
+ }
+ }
+ else
+ {
+ PolygonPipeline = 0;
+ VertexSlotCounter = 0;
+ VertexSlotsFree = 0x1;
+ }
+ }
+}
+
+void NextVertexSlot()
+{
+ s32 num = (9 - VertexSlotCounter) + 1;
+
+ for (;;)
+ {
+ CycleCount += num;
+
+ if (VertexPipeline > 0)
+ {
+ if (VertexPipeline > num) VertexPipeline -= num;
+ else VertexPipeline = 0;
+ }
+
+ if (PolygonPipeline > 0)
+ {
+ if (PolygonPipeline > num)
+ {
+ PolygonPipeline -= num;
+ VertexSlotCounter = 1;
+ VertexSlotsFree >>= 1;
+ if (VertexSlotsFree & 0x1)
+ {
+ VertexSlotsFree &= ~0x1;
+ break;
+ }
+ else
+ {
+ num = 9;
+ continue;
+ }
+ }
+ else
+ {
+ PolygonPipeline = 0;
+ VertexSlotCounter = 0;
+ VertexSlotsFree = 1;
+ break;
+ }
+ }
+ }
+}
+
+void StallPolygonPipeline(s32 delay, s32 nonstalldelay)
+{
+ if (PolygonPipeline > 0)
+ {
+ CycleCount += PolygonPipeline + delay;
+
+ // can be safely assumed those two will go to zero
+ VertexPipeline = 0;
+ NormalPipeline = 0;
+
+ PolygonPipeline = 0;
+ VertexSlotCounter = 0;
+ VertexSlotsFree = 1;
+ }
+ else
+ {
+ if (VertexPipeline > nonstalldelay)
+ AddCycles((VertexPipeline - nonstalldelay) + 1);
+ else
+ AddCycles(NormalPipeline + 1);
+ }
+}
+
+
+
template<int comp, s32 plane, bool attribs>
void ClipSegment(Vertex* outbuf, Vertex* vout, Vertex* vin)
{
@@ -811,6 +936,22 @@ void SubmitPolygon()
int nverts = PolygonMode & 0x1 ? 4:3;
int prev, next;
+ // submitting a polygon starts the polygon pipeline
+ if (nverts == 4)
+ {
+ PolygonPipeline = 35;
+ VertexSlotCounter = 1;
+ if (PolygonMode & 0x2) VertexSlotsFree = 0b11100;
+ else VertexSlotsFree = 0b11110;
+ }
+ else
+ {
+ PolygonPipeline = 26;
+ VertexSlotCounter = 1;
+ if (PolygonMode & 0x2) VertexSlotsFree = 0b1000;
+ else VertexSlotsFree = 0b1110;
+ }
+
// culling
// TODO: work out how it works on the real thing
// the normalization part is a wild guess
@@ -860,7 +1001,7 @@ void SubmitPolygon()
}
// for strips, check whether we can attach to the previous polygon
- // this requires two vertices shared with the previous polygon, and that
+ // this requires two original vertices shared with the previous polygon, and that
// the two polygons be of the same type
if (PolygonMode >= 2 && LastStripPolygon)
@@ -1093,6 +1234,8 @@ void SubmitVertex()
vertextrans->Position[2] = (vertex[0]*ClipMatrix[2] + vertex[1]*ClipMatrix[6] + vertex[2]*ClipMatrix[10] + vertex[3]*ClipMatrix[14]) >> 12;
vertextrans->Position[3] = (vertex[0]*ClipMatrix[3] + vertex[1]*ClipMatrix[7] + vertex[2]*ClipMatrix[11] + vertex[3]*ClipMatrix[15]) >> 12;
+ // this probably shouldn't be.
+ // the way color is handled during clipping needs investigation. TODO
vertextrans->Color[0] = (VertexColor[0] << 12) + 0xFFF;
vertextrans->Color[1] = (VertexColor[1] << 12) + 0xFFF;
vertextrans->Color[2] = (VertexColor[2] << 12) + 0xFFF;
@@ -1173,9 +1316,12 @@ void SubmitVertex()
}
break;
}
+
+ VertexPipeline = 7;
+ AddCycles(3);
}
-s32 CalculateLighting()
+void CalculateLighting()
{
if ((TexParam >> 30) == 2)
{
@@ -1244,8 +1390,9 @@ s32 CalculateLighting()
c++;
}
- // checkme: cycle count
- return c;
+ if (c < 1) c = 1;
+ NormalPipeline = 7;
+ AddCycles(c);
}
@@ -1255,6 +1402,7 @@ void BoxTest(u32* params)
Vertex face[10];
int res;
+ AddCycles(254);
GXStat &= ~(1<<1);
s16 x0 = (s16)(params[0] & 0xFFFF);
@@ -1354,6 +1502,8 @@ void PosTest()
PosTestResult[1] = (vertex[0]*ClipMatrix[1] + vertex[1]*ClipMatrix[5] + vertex[2]*ClipMatrix[9] + vertex[3]*ClipMatrix[13]) >> 12;
PosTestResult[2] = (vertex[0]*ClipMatrix[2] + vertex[1]*ClipMatrix[6] + vertex[2]*ClipMatrix[10] + vertex[3]*ClipMatrix[14]) >> 12;
PosTestResult[3] = (vertex[0]*ClipMatrix[3] + vertex[1]*ClipMatrix[7] + vertex[2]*ClipMatrix[11] + vertex[3]*ClipMatrix[15]) >> 12;
+
+ AddCycles(5);
}
void VecTest(u32* params)
@@ -1373,6 +1523,8 @@ void VecTest(u32* params)
if (VecTestResult[0] & 0x1000) VecTestResult[0] |= 0xF000;
if (VecTestResult[1] & 0x1000) VecTestResult[1] |= 0xF000;
if (VecTestResult[2] & 0x1000) VecTestResult[2] |= 0xF000;
+
+ AddCycles(4);
}
@@ -1387,17 +1539,13 @@ void CmdFIFOWrite(CmdFIFOEntry& entry)
{
if (CmdFIFO->IsFull())
{
- //printf("!!! GX FIFO FULL\n");
- //return;
+ // store it to the stall queue. stall the system.
+ // worst case is if a STMxx opcode causes this, which is why our stall queue
+ // has 64 entries. this is less complicated than trying to make STMxx stall-able.
- // temp. hack
- // SM64DS seems to overflow the FIFO occasionally
- // either leftover bugs in our implementation, or the game accidentally doing that
- // TODO: investigate.
- // TODO: implement this behavior properly (freezes the bus until the FIFO isn't full anymore)
-
- while (CmdFIFO->IsFull())
- ExecuteCommand();
+ CmdStallQueue->Write(entry);
+ NDS::GXFIFOStall();
+ return;
}
CmdFIFO->Write(entry);
@@ -1426,6 +1574,21 @@ CmdFIFOEntry CmdFIFORead()
if (!CmdFIFO->IsEmpty())
CmdPIPE->Write(CmdFIFO->Read());
+ // empty stall queue if needed
+ // CmdFIFO should not be full at this point.
+ if (!CmdStallQueue->IsEmpty())
+ {
+ while (!CmdStallQueue->IsEmpty())
+ {
+ if (CmdFIFO->IsFull()) break;
+ CmdFIFOEntry entry = CmdStallQueue->Read();
+ CmdFIFOWrite(entry);
+ }
+
+ if (CmdStallQueue->IsEmpty())
+ NDS::GXFIFOUnstall();
+ }
+
CheckFIFODMA();
CheckFIFOIRQ();
}
@@ -1441,6 +1604,63 @@ void ExecuteCommand()
//printf("FIFO: processing %02X %08X. Levels: FIFO=%d, PIPE=%d\n", entry.Command, entry.Param, CmdFIFO->Level(), CmdPIPE->Level());
+ // each FIFO entry takes 1 cycle to be processed
+ // commands (presumably) run when all the needed parameters have been read
+ // which is where we add the remaining cycles if any
+ if (ExecParamCount == 0)
+ {
+ // delay the first command entry as needed
+ switch (entry.Command)
+ {
+ // commands that stall the polygon pipeline
+ case 0x32: StallPolygonPipeline(8 + 1, 2); break; // 32 can run 6 cycles after a vertex
+ case 0x40: StallPolygonPipeline(1, 0); break;
+ case 0x70: StallPolygonPipeline(10 + 1, 0); break;
+
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ // vertex
+ if (!(VertexSlotsFree & 0x1)) NextVertexSlot();
+ else AddCycles(1);
+ NormalPipeline = 0;
+ break;
+
+ case 0x20:
+ case 0x30:
+ case 0x31:
+ case 0x72:
+ // commands that can run 6 cycles after a vertex
+ if (VertexPipeline > 2) AddCycles((VertexPipeline - 2) + 1);
+ else AddCycles(NormalPipeline + 1);
+ break;
+
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x33:
+ case 0x34:
+ case 0x41:
+ case 0x60:
+ case 0x71:
+ // command that can run 8 cycles after a vertex
+ if (VertexPipeline > 0) AddCycles(VertexPipeline + 1);
+ else AddCycles(NormalPipeline + 1);
+ break;
+
+ default:
+ // all other commands can run 4 cycles after a vertex
+ // no need to do much here since that is the minimum
+ AddCycles(NormalPipeline + 1);
+ break;
+ }
+ }
+ else
+ AddCycles(1);
+
ExecParams[ExecParamCount] = entry.Param;
ExecParamCount++;
@@ -1449,11 +1669,8 @@ void ExecuteCommand()
/*printf("0x%02X, ", entry.Command);
for (int k = 0; k < ExecParamCount; k++) printf("0x%08X, ", ExecParams[k]);
printf("\n");*/
- CycleCount += CmdNumCycles[entry.Command];
- ExecParamCount = 0;
- if (CycleCount > 0)
- GXStat |= (1<<27);
+ ExecParamCount = 0;
switch (entry.Command)
{
@@ -1465,86 +1682,65 @@ void ExecuteCommand()
NumPushPopCommands--;
if (MatrixMode == 0)
{
- if (ProjMatrixStackPointer > 0)
- {
- printf("!! PROJ MATRIX STACK OVERFLOW\n");
- GXStat |= (1<<15);
- break;
- }
+ if (ProjMatrixStackPointer > 0) GXStat |= (1<<15);
memcpy(ProjMatrixStack, ProjMatrix, 16*4);
ProjMatrixStackPointer++;
+ ProjMatrixStackPointer &= 0x1;
}
else if (MatrixMode == 3)
{
- if (TexMatrixStackPointer > 0)
- {
- printf("!! TEX MATRIX STACK OVERFLOW\n");
- GXStat |= (1<<15);
- break;
- }
+ if (TexMatrixStackPointer > 0) GXStat |= (1<<15);
memcpy(TexMatrixStack, TexMatrix, 16*4);
TexMatrixStackPointer++;
+ TexMatrixStackPointer &= 0x1;
}
else
{
- if (PosMatrixStackPointer > 30)
- {
- printf("!! POS MATRIX STACK OVERFLOW\n");
- GXStat |= (1<<15);
- break;
- }
+ if (PosMatrixStackPointer > 30) GXStat |= (1<<15);
- memcpy(PosMatrixStack[PosMatrixStackPointer], PosMatrix, 16*4);
- memcpy(VecMatrixStack[PosMatrixStackPointer], VecMatrix, 16*4);
+ memcpy(PosMatrixStack[PosMatrixStackPointer & 0x1F], PosMatrix, 16*4);
+ memcpy(VecMatrixStack[PosMatrixStackPointer & 0x1F], VecMatrix, 16*4);
PosMatrixStackPointer++;
+ PosMatrixStackPointer &= 0x3F;
}
+ AddCycles(16);
break;
case 0x12: // pop matrix
NumPushPopCommands--;
if (MatrixMode == 0)
{
- if (ProjMatrixStackPointer <= 0)
- {
- printf("!! PROJ MATRIX STACK UNDERFLOW\n");
- GXStat |= (1<<15);
- break;
- }
+ if (ProjMatrixStackPointer == 0) GXStat |= (1<<15);
ProjMatrixStackPointer--;
+ ProjMatrixStackPointer &= 0x1;
memcpy(ProjMatrix, ProjMatrixStack, 16*4);
ClipMatrixDirty = true;
+ AddCycles(35);
}
else if (MatrixMode == 3)
{
- if (TexMatrixStackPointer <= 0)
- {
- printf("!! TEX MATRIX STACK UNDERFLOW\n");
- GXStat |= (1<<15);
- break;
- }
+ if (TexMatrixStackPointer == 0) GXStat |= (1<<15);
TexMatrixStackPointer--;
+ TexMatrixStackPointer &= 0x1;
memcpy(TexMatrix, TexMatrixStack, 16*4);
+ AddCycles(17);
}
else
{
s32 offset = (s32)(ExecParams[0] << 26) >> 26;
PosMatrixStackPointer -= offset;
+ PosMatrixStackPointer &= 0x3F;
- if (PosMatrixStackPointer < 0 || PosMatrixStackPointer > 30)
- {
- //printf("!! POS MATRIX STACK UNDER/OVERFLOW %d\n", PosMatrixStackPointer);
- PosMatrixStackPointer += offset;
- GXStat |= (1<<15);
- break;
- }
+ if (PosMatrixStackPointer > 30) GXStat |= (1<<15);
- memcpy(PosMatrix, PosMatrixStack[PosMatrixStackPointer], 16*4);
- memcpy(VecMatrix, VecMatrixStack[PosMatrixStackPointer], 16*4);
+ memcpy(PosMatrix, PosMatrixStack[PosMatrixStackPointer & 0x1F], 16*4);
+ memcpy(VecMatrix, VecMatrixStack[PosMatrixStackPointer & 0x1F], 16*4);
ClipMatrixDirty = true;
+ AddCycles(35);
}
break;
@@ -1560,16 +1756,12 @@ void ExecuteCommand()
else
{
u32 addr = ExecParams[0] & 0x1F;
- if (addr > 30)
- {
- printf("!! POS MATRIX STORE ADDR 31\n");
- GXStat |= (1<<15);
- break;
- }
+ if (addr > 30) GXStat |= (1<<15);
memcpy(PosMatrixStack[addr], PosMatrix, 16*4);
memcpy(VecMatrixStack[addr], VecMatrix, 16*4);
}
+ AddCycles(16);
break;
case 0x14: // restore matrix
@@ -1577,24 +1769,22 @@ void ExecuteCommand()
{
memcpy(ProjMatrix, ProjMatrixStack, 16*4);
ClipMatrixDirty = true;
+ AddCycles(35);
}
else if (MatrixMode == 3)
{
memcpy(TexMatrix, TexMatrixStack, 16*4);
+ AddCycles(17);
}
else
{
u32 addr = ExecParams[0] & 0x1F;
- if (addr > 30)
- {
- printf("!! POS MATRIX STORE ADDR 31\n");
- GXStat |= (1<<15);
- break;
- }
+ if (addr > 30) GXStat |= (1<<15);
memcpy(PosMatrix, PosMatrixStack[addr], 16*4);
memcpy(VecMatrix, VecMatrixStack[addr], 16*4);
ClipMatrixDirty = true;
+ AddCycles(35);
}
break;
@@ -1603,6 +1793,7 @@ void ExecuteCommand()
{
MatrixLoadIdentity(ProjMatrix);
ClipMatrixDirty = true;
+ AddCycles(18);
}
else if (MatrixMode == 3)
MatrixLoadIdentity(TexMatrix);
@@ -1612,6 +1803,7 @@ void ExecuteCommand()
if (MatrixMode == 2)
MatrixLoadIdentity(VecMatrix);
ClipMatrixDirty = true;
+ AddCycles(18);
}
break;
@@ -1620,15 +1812,20 @@ void ExecuteCommand()
{
MatrixLoad4x4(ProjMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(18);
}
else if (MatrixMode == 3)
+ {
MatrixLoad4x4(TexMatrix, (s32*)ExecParams);
+ AddCycles(10);
+ }
else
{
MatrixLoad4x4(PosMatrix, (s32*)ExecParams);
if (MatrixMode == 2)
MatrixLoad4x4(VecMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(18);
}
break;
@@ -1637,15 +1834,20 @@ void ExecuteCommand()
{
MatrixLoad4x3(ProjMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(18);
}
else if (MatrixMode == 3)
+ {
MatrixLoad4x3(TexMatrix, (s32*)ExecParams);
+ AddCycles(7);
+ }
else
{
MatrixLoad4x3(PosMatrix, (s32*)ExecParams);
if (MatrixMode == 2)
MatrixLoad4x3(VecMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(18);
}
break;
@@ -1654,17 +1856,22 @@ void ExecuteCommand()
{
MatrixMult4x4(ProjMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(35 - 16);
}
else if (MatrixMode == 3)
+ {
MatrixMult4x4(TexMatrix, (s32*)ExecParams);
+ AddCycles(33 - 16);
+ }
else
{
MatrixMult4x4(PosMatrix, (s32*)ExecParams);
if (MatrixMode == 2)
{
MatrixMult4x4(VecMatrix, (s32*)ExecParams);
- CycleCount += 30;
+ AddCycles(35 + 30 - 16);
}
+ else AddCycles(35 - 16);
ClipMatrixDirty = true;
}
break;
@@ -1674,17 +1881,22 @@ void ExecuteCommand()
{
MatrixMult4x3(ProjMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(35 - 12);
}
else if (MatrixMode == 3)
+ {
MatrixMult4x3(TexMatrix, (s32*)ExecParams);
+ AddCycles(33 - 12);
+ }
else
{
MatrixMult4x3(PosMatrix, (s32*)ExecParams);
if (MatrixMode == 2)
{
MatrixMult4x3(VecMatrix, (s32*)ExecParams);
- CycleCount += 30;
+ AddCycles(35 + 30 - 12);
}
+ else AddCycles(35 - 12);
ClipMatrixDirty = true;
}
break;
@@ -1694,17 +1906,22 @@ void ExecuteCommand()
{
MatrixMult3x3(ProjMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(35 - 9);
}
else if (MatrixMode == 3)
+ {
MatrixMult3x3(TexMatrix, (s32*)ExecParams);
+ AddCycles(33 - 9);
+ }
else
{
MatrixMult3x3(PosMatrix, (s32*)ExecParams);
if (MatrixMode == 2)
{
MatrixMult3x3(VecMatrix, (s32*)ExecParams);
- CycleCount += 30;
+ AddCycles(35 + 30 - 9);
}
+ else AddCycles(35 - 9);
ClipMatrixDirty = true;
}
break;
@@ -1714,13 +1931,18 @@ void ExecuteCommand()
{
MatrixScale(ProjMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(35 - 3);
}
else if (MatrixMode == 3)
+ {
MatrixScale(TexMatrix, (s32*)ExecParams);
+ AddCycles(33 - 3);
+ }
else
{
MatrixScale(PosMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(35 - 3);
}
break;
@@ -1729,14 +1951,22 @@ void ExecuteCommand()
{
MatrixTranslate(ProjMatrix, (s32*)ExecParams);
ClipMatrixDirty = true;
+ AddCycles(35 - 3);
}
else if (MatrixMode == 3)
+ {
MatrixTranslate(TexMatrix, (s32*)ExecParams);
+ AddCycles(33 - 3);
+ }
else
{
MatrixTranslate(PosMatrix, (s32*)ExecParams);
if (MatrixMode == 2)
+ {
MatrixTranslate(VecMatrix, (s32*)ExecParams);
+ AddCycles(35 + 30 - 3);
+ }
+ else AddCycles(35 - 3);
ClipMatrixDirty = true;
}
break;
@@ -1757,7 +1987,7 @@ void ExecuteCommand()
Normal[0] = (s16)((ExecParams[0] & 0x000003FF) << 6) >> 6;
Normal[1] = (s16)((ExecParams[0] & 0x000FFC00) >> 4) >> 6;
Normal[2] = (s16)((ExecParams[0] & 0x3FF00000) >> 14) >> 6;
- CycleCount += CalculateLighting();
+ CalculateLighting();
break;
case 0x22: // texcoord
@@ -1839,6 +2069,7 @@ void ExecuteCommand()
VertexColor[1] = MatDiffuse[1];
VertexColor[2] = MatDiffuse[2];
}
+ AddCycles(3);
break;
case 0x31: // specular/emission material
@@ -1849,6 +2080,7 @@ void ExecuteCommand()
MatEmission[1] = (ExecParams[0] >> 21) & 0x1F;
MatEmission[2] = (ExecParams[0] >> 26) & 0x1F;
UseShininessTable = (ExecParams[0] & 0x8000) != 0;
+ AddCycles(3);
break;
case 0x32: // light direction
@@ -1862,6 +2094,7 @@ void ExecuteCommand()
LightDirection[l][1] = (dir[0]*VecMatrix[1] + dir[1]*VecMatrix[5] + dir[2]*VecMatrix[9]) >> 12;
LightDirection[l][2] = (dir[0]*VecMatrix[2] + dir[1]*VecMatrix[6] + dir[2]*VecMatrix[10]) >> 12;
}
+ AddCycles(5);
break;
case 0x33: // light color
@@ -1871,6 +2104,7 @@ void ExecuteCommand()
LightColor[l][1] = (ExecParams[0] >> 5) & 0x1F;
LightColor[l][2] = (ExecParams[0] >> 10) & 0x1F;
}
+ AddCycles(1);
break;
case 0x34: // shininess table
@@ -1887,6 +2121,8 @@ void ExecuteCommand()
break;
case 0x40: // begin polygons
+ // TODO: check if there was a polygon being defined but incomplete
+ // such cases seem to freeze the GPU
PolygonMode = ExecParams[0] & 0x3;
VertexNum = 0;
VertexNumInPoly = 0;
@@ -1895,10 +2131,24 @@ void ExecuteCommand()
CurPolygonAttr = PolygonAttr;
break;
+ case 0x41: // end polygons
+ // TODO: research this?
+ // it doesn't seem to have any effect whatsoever, but
+ // its timing characteristics are different from those of other
+ // no-op commands
+ break;
+
case 0x50: // flush
FlushRequest = 1;
FlushAttributes = ExecParams[0] & 0x3;
CycleCount = 392;
+ // probably safe to just reset all pipelines
+ // but needs checked
+ VertexPipeline = 0;
+ NormalPipeline = 0;
+ PolygonPipeline = 0;
+ VertexSlotCounter = 0;
+ VertexSlotsFree = 1;
break;
case 0x60: // viewport x1,y1,x2,y2
@@ -1930,18 +2180,41 @@ void ExecuteCommand()
break;
default:
- //if (entry.Command != 0x41)
- //printf("!! UNKNOWN GX COMMAND %02X %08X\n", entry.Command, entry.Param);
+ //printf("!! UNKNOWN GX COMMAND %02X %08X\n", entry.Command, entry.Param);
break;
}
}
+
+ if (CycleCount > 0 || !CmdPIPE->IsEmpty() ||
+ VertexPipeline || NormalPipeline || PolygonPipeline)
+ GXStat |= (1<<27);
+}
+
+s32 CyclesToRunFor()
+{
+ if (CycleCount < 0) return 0;
+ return CycleCount;
+}
+
+void FinishWork(s32 cycles)
+{
+ AddCycles(cycles);
+ if (NormalPipeline)
+ NormalPipeline -= std::min(NormalPipeline, cycles);
+
+ CycleCount = 0;
+
+ if (VertexPipeline || NormalPipeline || PolygonPipeline)
+ return;
+
+ GXStat &= ~(1<<27);
}
void Run(s32 cycles)
{
if (FlushRequest)
return;
- if (CycleCount <= 0 && CmdPIPE->IsEmpty())
+ if (CmdPIPE->IsEmpty() && !(GXStat & (1<<27)))
return;
CycleCount -= cycles;
@@ -1959,8 +2232,8 @@ void Run(s32 cycles)
if (CycleCount <= 0 && CmdPIPE->IsEmpty())
{
- CycleCount = 0;
- GXStat &= ~(1<<27);
+ if (GXStat & (1<<27)) FinishWork(-CycleCount);
+ else CycleCount = 0;
if (NumPushPopCommands == 0) GXStat &= ~(1<<14);
if (NumTestCommands == 0) GXStat &= ~(1<<0);
@@ -2254,7 +2527,7 @@ void Write8(u32 addr, u8 val)
GXStat &= ~0x8000;
ProjMatrixStackPointer = 0;
//PosMatrixStackPointer = 0;
- TexMatrixStackPointer = 0;
+ TexMatrixStackPointer = 0; // CHECKME
}
return;
case 0x04000603:
@@ -2319,7 +2592,7 @@ void Write16(u32 addr, u16 val)
GXStat &= ~0x8000;
ProjMatrixStackPointer = 0;
//PosMatrixStackPointer = 0;
- TexMatrixStackPointer = 0;
+ TexMatrixStackPointer = 0; // CHECKME
}
return;
case 0x04000602:
@@ -2389,7 +2662,7 @@ void Write32(u32 addr, u32 val)
GXStat &= ~0x8000;
ProjMatrixStackPointer = 0;
//PosMatrixStackPointer = 0;
- TexMatrixStackPointer = 0;
+ TexMatrixStackPointer = 0; // CHECKME
}
val &= 0xC0000000;
GXStat &= 0x3FFFFFFF;
diff --git a/src/GPU3D.h b/src/GPU3D.h
index c997a8f..b74e421 100644
--- a/src/GPU3D.h
+++ b/src/GPU3D.h
@@ -90,6 +90,7 @@ void DoSavestate(Savestate* file);
void ExecuteCommand();
+s32 CyclesToRunFor();
void Run(s32 cycles);
void CheckFIFOIRQ();
void CheckFIFODMA();
diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp
index bb48c97..daf5cdd 100644
--- a/src/GPU3D_Soft.cpp
+++ b/src/GPU3D_Soft.cpp
@@ -769,11 +769,24 @@ void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha
// depth test is 'less or equal' instead of 'less than' under the following conditions:
// * when drawing a front-facing pixel over an opaque back-facing pixel
// * when drawing wireframe edges, under certain conditions (TODO)
+//
+// range is different based on depth-buffering mode
+// Z-buffering: +-0x200
+// W-buffering: +-0xFF
+
+bool DepthTest_Equal_Z(s32 dstz, s32 z, u32 dstattr)
+{
+ s32 diff = dstz - z;
+ if ((u32)(diff + 0x200) <= 0x400)
+ return true;
+
+ return false;
+}
-bool DepthTest_Equal(s32 dstz, s32 z, u32 dstattr)
+bool DepthTest_Equal_W(s32 dstz, s32 z, u32 dstattr)
{
s32 diff = dstz - z;
- if ((u32)(diff + 0xFF) <= 0x1FE) // range is +-0xFF
+ if ((u32)(diff + 0xFF) <= 0x1FE)
return true;
return false;
@@ -1099,7 +1112,7 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
bool (*fnDepthTest)(s32 dstz, s32 z, u32 dstattr);
if (polygon->Attr & (1<<14))
- fnDepthTest = DepthTest_Equal;
+ fnDepthTest = polygon->WBuffer ? DepthTest_Equal_W : DepthTest_Equal_Z;
else if (polygon->FacingView)
fnDepthTest = DepthTest_LessThan_FrontFacing;
else
@@ -1213,7 +1226,9 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
// part 1: left edge
edge = yedge | 0x1;
- xlimit = xstart+l_edgelen; if (xlimit > 256) xlimit = 256;
+ xlimit = xstart+l_edgelen;
+ if (xlimit > xend+1) xlimit = xend+1;
+ if (xlimit > 256) xlimit = 256;
for (; x < xlimit; x++)
{
@@ -1241,7 +1256,9 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
// part 2: polygon inside
edge = yedge;
- xlimit = xend-r_edgelen+1; if (xlimit > 256) xlimit = 256;
+ xlimit = xend-r_edgelen+1;
+ if (xlimit > xend+1) xlimit = xend+1;
+ if (xlimit > 256) xlimit = 256;
if (wireframe && !edge) x = xlimit;
else for (; x < xlimit; x++)
{
@@ -1265,7 +1282,8 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
// part 3: right edge
edge = yedge | 0x2;
- xlimit = xend+1; if (xlimit > 256) xlimit = 256;
+ xlimit = xend+1;
+ if (xlimit > 256) xlimit = 256;
for (; x < xlimit; x++)
{
@@ -1307,7 +1325,7 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y)
bool (*fnDepthTest)(s32 dstz, s32 z, u32 dstattr);
if (polygon->Attr & (1<<14))
- fnDepthTest = DepthTest_Equal;
+ fnDepthTest = polygon->WBuffer ? DepthTest_Equal_W : DepthTest_Equal_Z;
else if (polygon->FacingView)
fnDepthTest = DepthTest_LessThan_FrontFacing;
else
@@ -1436,7 +1454,9 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y)
// part 1: left edge
edge = yedge | 0x1;
- xlimit = xstart+l_edgelen; if (xlimit > 256) xlimit = 256;
+ xlimit = xstart+l_edgelen;
+ if (xlimit > xend+1) xlimit = xend+1;
+ if (xlimit > 256) xlimit = 256;
if (l_edgecov & (1<<31))
{
xcov = (l_edgecov >> 12) & 0x3FF;
@@ -1535,7 +1555,9 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y)
// part 2: polygon inside
edge = yedge;
- xlimit = xend-r_edgelen+1; if (xlimit > 256) xlimit = 256;
+ xlimit = xend-r_edgelen+1;
+ if (xlimit > xend+1) xlimit = xend+1;
+ if (xlimit > 256) xlimit = 256;
if (wireframe && !edge) x = xlimit;
else for (; x < xlimit; x++)
{
@@ -1603,7 +1625,8 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y)
// part 3: right edge
edge = yedge | 0x2;
- xlimit = xend+1; if (xlimit > 256) xlimit = 256;
+ xlimit = xend+1;
+ if (xlimit > 256) xlimit = 256;
if (r_edgecov & (1<<31))
{
xcov = (r_edgecov >> 12) & 0x3FF;
diff --git a/src/NDS.cpp b/src/NDS.cpp
index 40f0c2d..98c2924 100644
--- a/src/NDS.cpp
+++ b/src/NDS.cpp
@@ -21,7 +21,6 @@
#include "Config.h"
#include "NDS.h"
#include "ARM.h"
-#include "CP15.h"
#include "NDSCart.h"
#include "DMA.h"
#include "FIFO.h"
@@ -31,23 +30,55 @@
#include "RTC.h"
#include "Wifi.h"
#include "Platform.h"
+#include "melon_fopen.h"
namespace NDS
{
-// TODO LIST
-// * stick all the variables in a big structure?
-// would make it easier to deal with savestates
+#ifdef DEBUG_CHECK_DESYNC
+u64 dbg_CyclesSys;
+u64 dbg_CyclesARM9;
+u64 dbg_CyclesTimer9;
+u64 dbg_CyclesARM7;
+u64 dbg_CyclesTimer7;
+#endif
+
+// timing notes
+//
+// * this implementation is technically wrong for VRAM
+// each bank is considered a separate region
+// but this would only matter in specific VRAM->VRAM DMA transfers or
+// when running code in VRAM, which is way unlikely
+//
+// bus/basedelay/nspenalty
+//
+// bus types:
+// * 0 / 32-bit: nothing special
+// * 1 / 16-bit: 32-bit accesses split into two 16-bit accesses, second is always sequential
+// * 2 / 8-bit/GBARAM: (presumably) split into multiple 8-bit accesses?
+// * 3 / ARM9 internal: cache/TCM
+//
+// ARM9 always gets 3c nonseq penalty when using the bus (except for mainRAM where the penalty is 7c)
+//
+// ARM7 only gets nonseq penalty when accessing mainRAM (7c as for ARM9)
+//
+// timings for GBA slot and wifi are set up at runtime
+
+u8 ARM9MemTimings[0x40000][4];
+u8 ARM7MemTimings[0x20000][4];
+
+ARMv5* ARM9;
+ARMv4* ARM7;
+
+u32 NumFrames;
+u64 SysClockCycles;
+u64 LastSysClockCycles;
+u32 FrameSysClockCycles;
-ARM* ARM9;
-ARM* ARM7;
-
-/*s32 ARM9Cycles, ARM7Cycles;
-s32 CompensatedCycles;
-s32 SchedCycles;*/
s32 CurIterationCycles;
s32 ARM7Offset;
+int CurCPU;
SchedEvent SchedList[Event_MAX];
u32 SchedListMask;
@@ -82,6 +113,8 @@ u8 PostFlag7;
u16 PowerControl9;
u16 PowerControl7;
+u16 WifiWaitCnt;
+
u16 ARM7BIOSProt;
Timer Timers[8];
@@ -114,12 +147,15 @@ bool Running;
void DivDone(u32 param);
void SqrtDone(u32 param);
+void RunTimer(u32 tid, s32 cycles);
+void SetWifiWaitCnt(u16 val);
+void SetGBASlotTimings();
bool Init()
{
- ARM9 = new ARM(0);
- ARM7 = new ARM(1);
+ ARM9 = new ARMv5();
+ ARM7 = new ARMv4();
DMAs[0] = new DMA(0, 0);
DMAs[1] = new DMA(0, 1);
@@ -163,6 +199,101 @@ void DeInit()
}
+void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq)
+{
+ addrstart >>= 14;
+ addrend >>= 14;
+
+ if (addrend == 0x3FFFF) addrend++;
+
+ int N16, S16, N32, S32;
+ N16 = nonseq;
+ S16 = seq;
+ if (buswidth == 16)
+ {
+ N32 = N16 + S16;
+ S32 = S16 + S16;
+ }
+ else
+ {
+ N32 = N16;
+ S32 = S16;
+ }
+
+ for (u32 i = addrstart; i < addrend; i++)
+ {
+ ARM9MemTimings[i][0] = N16;
+ ARM9MemTimings[i][1] = S16;
+ ARM9MemTimings[i][2] = N32;
+ ARM9MemTimings[i][3] = S32;
+ }
+
+ ARM9->UpdateRegionTimings(addrstart<<14, addrend<<14);
+}
+
+void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq)
+{
+ addrstart >>= 15;
+ addrend >>= 15;
+
+ if (addrend == 0x1FFFF) addrend++;
+
+ int N16, S16, N32, S32;
+ N16 = nonseq;
+ S16 = seq;
+ if (buswidth == 16)
+ {
+ N32 = N16 + S16;
+ S32 = S16 + S16;
+ }
+ else
+ {
+ N32 = N16;
+ S32 = S16;
+ }
+
+ for (u32 i = addrstart; i < addrend; i++)
+ {
+ ARM7MemTimings[i][0] = N16;
+ ARM7MemTimings[i][1] = S16;
+ ARM7MemTimings[i][2] = N32;
+ ARM7MemTimings[i][3] = S32;
+ }
+}
+
+void InitTimings()
+{
+ // TODO, eventually:
+ // VRAM is initially unmapped. The timings should be those of void regions.
+ // Similarly for any unmapped VRAM area.
+ // Need to check whether supporting these timing characteristics would impact performance
+ // (especially wrt VRAM mirroring and overlapping and whatnot).
+
+ // ARM9
+
+ SetARM9RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1 + 3, 1); // void
+
+ SetARM9RegionTimings(0xFFFF0000, 0xFFFFFFFF, 32, 1 + 3, 1); // BIOS
+ SetARM9RegionTimings(0x02000000, 0x03000000, 16, 8, 1); // main RAM
+ SetARM9RegionTimings(0x03000000, 0x04000000, 32, 1 + 3, 1); // ARM9/shared WRAM
+ SetARM9RegionTimings(0x04000000, 0x05000000, 32, 1 + 3, 1); // IO
+ SetARM9RegionTimings(0x05000000, 0x06000000, 16, 1 + 3, 1); // palette
+ SetARM9RegionTimings(0x06000000, 0x07000000, 16, 1 + 3, 1); // VRAM
+ SetARM9RegionTimings(0x07000000, 0x08000000, 32, 1 + 3, 1); // OAM
+
+ // ARM7
+
+ SetARM7RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1, 1); // void
+
+ SetARM7RegionTimings(0x00000000, 0x00010000, 32, 1, 1); // BIOS
+ SetARM7RegionTimings(0x02000000, 0x03000000, 16, 8, 1); // main RAM
+ SetARM7RegionTimings(0x03000000, 0x04000000, 32, 1, 1); // ARM7/shared WRAM
+ SetARM7RegionTimings(0x04000000, 0x04800000, 32, 1, 1); // IO
+ SetARM7RegionTimings(0x06000000, 0x07000000, 16, 1, 1); // ARM7 VRAM
+
+ // handled later: GBA slot, wifi
+}
+
void SetupDirectBoot()
{
u32 bootparams[8];
@@ -209,9 +340,9 @@ void SetupDirectBoot()
ARM9Write16(0x027FFC30, 0xFFFF);
ARM9Write16(0x027FFC40, 0x0001);
- CP15::Write(0x910, 0x0300000A);
- CP15::Write(0x911, 0x00000020);
- CP15::Write(0x100, 0x00050000);
+ ARM9->CP15Write(0x910, 0x0300000A);
+ ARM9->CP15Write(0x911, 0x00000020);
+ ARM9->CP15Write(0x100, 0x00050000);
ARM9->R[12] = bootparams[1];
ARM9->R[13] = 0x03002F7C;
@@ -241,6 +372,8 @@ void SetupDirectBoot()
SPU::SetBias(0x200);
+ SetWifiWaitCnt(0x0030);
+
ARM7BIOSProt = 0x1204;
SPI_Firmware::SetupDirectBoot();
@@ -251,7 +384,18 @@ void Reset()
FILE* f;
u32 i;
- f = Config::GetConfigFile("bios9.bin", "rb");
+#ifdef DEBUG_CHECK_DESYNC
+ dbg_CyclesSys = 0;
+ dbg_CyclesARM9 = 0;
+ dbg_CyclesTimer9 = 0;
+ dbg_CyclesARM7 = 0;
+ dbg_CyclesTimer7 = 0;
+#endif // DEBUG_CHECK_DESYNC
+
+ SysClockCycles = 0;
+ LastSysClockCycles = 0;
+
+ f = melon_fopen_local("bios9.bin", "rb");
if (!f)
{
printf("ARM9 BIOS not found\n");
@@ -268,7 +412,7 @@ void Reset()
fclose(f);
}
- f = Config::GetConfigFile("bios7.bin", "rb");
+ f = melon_fopen_local("bios7.bin", "rb");
if (!f)
{
printf("ARM7 BIOS not found\n");
@@ -285,6 +429,11 @@ void Reset()
fclose(f);
}
+ ARM9->SetClockShift(1);
+ ARM7->SetClockShift(0);
+
+ InitTimings();
+
memset(MainRAM, 0, MAIN_RAM_SIZE);
memset(SharedWRAM, 0, 0x8000);
memset(ARM7WRAM, 0, 0x10000);
@@ -295,6 +444,7 @@ void Reset()
ExMemCnt[1] = 0;
memset(ROMSeed0, 0, 2*8);
memset(ROMSeed1, 0, 2*8);
+ SetGBASlotTimings();
IME[0] = 0;
IE[0] = 0;
@@ -308,6 +458,9 @@ void Reset()
PowerControl9 = 0x0001;
PowerControl7 = 0x0001;
+ WifiWaitCnt = 0xFFFF; // temp
+ SetWifiWaitCnt(0);
+
ARM7BIOSProt = 0;
IPCSync9 = 0;
@@ -322,7 +475,6 @@ void Reset()
ARM9->Reset();
ARM7->Reset();
- CP15::Reset();
CPUStop = 0;
@@ -464,6 +616,8 @@ bool DoSavestate(Savestate* file)
file->VarArray(ROMSeed0, 2*8);
file->VarArray(ROMSeed1, 2*8);
+ file->Var16(&WifiWaitCnt);
+
file->VarArray(IME, 2*sizeof(u32));
file->VarArray(IE, 2*sizeof(u32));
file->VarArray(IF, 2*sizeof(u32));
@@ -500,7 +654,6 @@ bool DoSavestate(Savestate* file)
file->VarArray(DMA9Fill, 4*sizeof(u32));
- //file->VarArray(SchedList, sizeof(SchedList));
if (!DoSavestate_Scheduler(file)) return false;
file->Var32(&SchedListMask);
file->Var32((u32*)&CurIterationCycles);
@@ -523,9 +676,20 @@ bool DoSavestate(Savestate* file)
MapSharedWRAM(WRAMCnt);
}
+ if (!file->Saving)
+ {
+ GPU::DisplaySwap(PowerControl9>>15);
+
+ InitTimings();
+ SetGBASlotTimings();
+
+ u16 tmp = WifiWaitCnt;
+ WifiWaitCnt = 0xFFFF;
+ SetWifiWaitCnt(tmp); // force timing table update
+ }
+
ARM9->DoSavestate(file);
ARM7->DoSavestate(file);
- CP15::DoSavestate(file);
NDSCart::DoSavestate(file);
GPU::DoSavestate(file);
@@ -534,11 +698,6 @@ bool DoSavestate(Savestate* file)
RTC::DoSavestate(file);
Wifi::DoSavestate(file);
- if (!file->Saving)
- {
- GPU::DisplaySwap(PowerControl9>>15);
- }
-
return true;
}
@@ -602,64 +761,142 @@ void RunSystem(s32 cycles)
u32 RunFrame()
{
+ FrameSysClockCycles = 0;
+
if (!Running) return 263; // dorp
+ if (CPUStop & 0x40000000) return 263;
GPU::StartFrame();
while (Running && GPU::TotalScanlines==0)
{
- s32 ndscyclestorun;
- s32 ndscycles = 0;
-
// TODO: give it some margin, so it can directly do 17 cycles instead of 16 then 1
CalcIterationCycles();
+ s32 arm9cycles;
+
+ if (CPUStop & 0x80000000)
+ {
+ // GXFIFO stall
+ // we just run the GPU and the timers.
+ // the rest of the hardware is driven by the event scheduler.
- if (CPUStop & 0xFFFF)
+ arm9cycles = GPU3D::CyclesToRunFor();
+ arm9cycles = std::min(CurIterationCycles, arm9cycles);
+ RunTightTimers(0, arm9cycles);
+
+#ifdef DEBUG_CHECK_DESYNC
+ dbg_CyclesARM9 += arm9cycles;
+#endif // DEBUG_CHECK_DESYNC
+ }
+ else if (CPUStop & 0x0FFF)
{
s32 cycles = CurIterationCycles;
+
cycles = DMAs[0]->Run(cycles);
- if (cycles > 0) cycles = DMAs[1]->Run(cycles);
- if (cycles > 0) cycles = DMAs[2]->Run(cycles);
- if (cycles > 0) cycles = DMAs[3]->Run(cycles);
- ndscyclestorun = CurIterationCycles - cycles;
+ if (cycles > 0 && !(CPUStop & 0x80000000))
+ cycles = DMAs[1]->Run(cycles);
+ if (cycles > 0 && !(CPUStop & 0x80000000))
+ cycles = DMAs[2]->Run(cycles);
+ if (cycles > 0 && !(CPUStop & 0x80000000))
+ cycles = DMAs[3]->Run(cycles);
+
+ arm9cycles = CurIterationCycles - cycles;
}
else
{
ARM9->CyclesToRun = CurIterationCycles << 1;
- ARM9->Execute();
- ndscyclestorun = ARM9->Cycles >> 1;
+ CurCPU = 1; ARM9->Execute(); CurCPU = 0;
+ arm9cycles = ARM9->Cycles >> 1;
+ RunTightTimers(0, arm9cycles);
}
- if (CPUStop & 0xFFFF0000)
+ RunLooseTimers(0, arm9cycles);
+ GPU3D::Run(arm9cycles);
+
+ s32 ndscyclestorun = arm9cycles;
+
+ // ARM7Offset > ndscyclestorun means we are too far ahead of the ARM9
+ if (ARM7Offset > ndscyclestorun)
+ {
+ ARM7Offset -= ndscyclestorun;
+ }
+ else
+ if (CPUStop & 0x0FFF0000)
{
s32 cycles = ndscyclestorun - ARM7Offset;
+
cycles = DMAs[4]->Run(cycles);
- if (cycles > 0) cycles = DMAs[5]->Run(cycles);
- if (cycles > 0) cycles = DMAs[6]->Run(cycles);
- if (cycles > 0) cycles = DMAs[7]->Run(cycles);
+ if (cycles > 0)
+ cycles = DMAs[5]->Run(cycles);
+ if (cycles > 0)
+ cycles = DMAs[6]->Run(cycles);
+ if (cycles > 0)
+ cycles = DMAs[7]->Run(cycles);
+
ARM7Offset = -cycles;
}
else
{
ARM7->CyclesToRun = ndscyclestorun - ARM7Offset;
- ARM7->Execute();
+ CurCPU = 2; ARM7->Execute(); CurCPU = 0;
ARM7Offset = ARM7->Cycles - ARM7->CyclesToRun;
+ RunTightTimers(1, ARM7->Cycles);
}
+#ifdef DEBUG_CHECK_DESYNC
+ dbg_CyclesSys += ndscyclestorun;
+#endif // DEBUG_CHECK_DESYNC
+
+ RunLooseTimers(1, ndscyclestorun);
RunSystem(ndscyclestorun);
- //GPU3D::Run(ndscyclestorun);
+
+ SysClockCycles += ndscyclestorun;
+ LastSysClockCycles += ndscyclestorun;
+ FrameSysClockCycles += ndscyclestorun;
+
+ if (CPUStop & 0x40000000)
+ {
+ // checkme: when is sleep mode effective?
+ //CancelEvent(Event_LCD);
+ //GPU::TotalScanlines = 263;
+ break;
+ }
}
+#ifdef DEBUG_CHECK_DESYNC
+ printf("[%08X%08X] ARM9=%ld timer9=%ld, ARM7=%ld timer7=%ld\n",
+ (u32)(dbg_CyclesSys>>32), (u32)dbg_CyclesSys,
+ dbg_CyclesARM9-dbg_CyclesSys,
+ dbg_CyclesTimer9-dbg_CyclesSys,
+ dbg_CyclesARM7-dbg_CyclesSys,
+ dbg_CyclesTimer7-dbg_CyclesSys);
+#endif
+
+ NumFrames++;
+
return GPU::TotalScanlines;
}
void Reschedule()
{
+ s32 oldcycles = CurIterationCycles;
CalcIterationCycles();
- ARM9->CyclesToRun = CurIterationCycles << 1;
- //ARM7->CyclesToRun = CurIterationCycles - ARM7Offset;
- //ARM7->CyclesToRun = (ARM9->Cycles >> 1) - ARM7->Cycles - ARM7Offset;
+ if (CurIterationCycles >= oldcycles)
+ {
+ CurIterationCycles = oldcycles;
+ return;
+ }
+
+ if (CurCPU == 0)
+ {
+ CurIterationCycles = oldcycles;
+ return;
+ }
+
+ if (CurCPU == 1) ARM9->CyclesToRun = CurIterationCycles << 1;
+ else if (CurCPU == 2) ARM7->CyclesToRun = CurIterationCycles - ARM7Offset;
+ // this is all. a reschedule shouldn't happen during DMA or GXFIFO stall.
}
void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param)
@@ -672,8 +909,14 @@ void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 para
SchedEvent* evt = &SchedList[id];
- if (periodic) evt->WaitCycles += delay;
- else evt->WaitCycles = delay + (ARM9->Cycles >> 1);
+ if (periodic)
+ evt->WaitCycles += delay;
+ else
+ {
+ if (CurCPU == 1) evt->WaitCycles = delay + (ARM9->Cycles >> 1);
+ else if (CurCPU == 2) evt->WaitCycles = delay + ARM7->Cycles;
+ else evt->WaitCycles = delay;
+ }
evt->Func = func;
evt->Param = param;
@@ -719,6 +962,25 @@ void SetKeyMask(u32 mask)
KeyInput |= key_lo | (key_hi << 16);
}
+void SetLidClosed(bool closed)
+{
+ if (closed)
+ {
+ KeyInput |= (1<<23);
+ }
+ else
+ {
+ KeyInput &= ~(1<<23);
+ SetIRQ(1, IRQ_LidOpen);
+ CPUStop &= ~0x40000000;
+ }
+}
+
+void MicInputFrame(s16* data, int samples)
+{
+ return SPI_TSC::MicInputFrame(data, samples);
+}
+
void Halt()
{
@@ -764,6 +1026,49 @@ void MapSharedWRAM(u8 val)
}
+void SetWifiWaitCnt(u16 val)
+{
+ if (WifiWaitCnt == val) return;
+
+ WifiWaitCnt = val;
+
+ const int ntimings[4] = {10, 8, 6, 18};
+ SetARM7RegionTimings(0x04800000, 0x04808000, 16, ntimings[val & 0x3], (val & 0x4) ? 4 : 6);
+ SetARM7RegionTimings(0x04808000, 0x04810000, 16, ntimings[(val>>3) & 0x3], (val & 0x20) ? 4 : 10);
+}
+
+void SetGBASlotTimings()
+{
+ int curcpu = (ExMemCnt[0] >> 7) & 0x1;
+
+ const int ntimings[4] = {10, 8, 6, 18};
+
+ u16 curcnt = ExMemCnt[curcpu];
+ int ramN = ntimings[curcnt & 0x3];
+ int romN = ntimings[(curcnt>>2) & 0x3];
+ int romS = (curcnt & 0x10) ? 4 : 6;
+
+ // TODO: PHI pin thing?
+
+ if (curcpu == 0)
+ {
+ SetARM9RegionTimings(0x08000000, 0x0A000000, 16, romN + 3, romS);
+ SetARM9RegionTimings(0x0A000000, 0x0B000000, 8, ramN + 3, ramN);
+
+ SetARM7RegionTimings(0x08000000, 0x0A000000, 32, 1, 1);
+ SetARM7RegionTimings(0x0A000000, 0x0B000000, 32, 1, 1);
+ }
+ else
+ {
+ SetARM9RegionTimings(0x08000000, 0x0A000000, 32, 1, 1);
+ SetARM9RegionTimings(0x0A000000, 0x0B000000, 32, 1, 1);
+
+ SetARM7RegionTimings(0x08000000, 0x0A000000, 16, romN, romS);
+ SetARM7RegionTimings(0x0A000000, 0x0B000000, 8, ramN, ramN);
+ }
+}
+
+
void SetIRQ(u32 cpu, u32 irq)
{
IF[cpu] |= (1 << irq);
@@ -808,21 +1113,172 @@ void ResumeCPU(u32 cpu, u32 mask)
CPUStop &= ~mask;
}
+void GXFIFOStall()
+{
+ if (CPUStop & 0x80000000) return;
+
+ CPUStop |= 0x80000000;
+
+ if (CurCPU == 1) ARM9->Halt(2);
+ else
+ {
+ DMAs[0]->StallIfRunning();
+ DMAs[1]->StallIfRunning();
+ DMAs[2]->StallIfRunning();
+ DMAs[3]->StallIfRunning();
+ }
+}
+
+void GXFIFOUnstall()
+{
+ CPUStop &= ~0x80000000;
+}
+
+void EnterSleepMode()
+{
+ if (CPUStop & 0x40000000) return;
+
+ CPUStop |= 0x40000000;
+ ARM7->Halt(2);
+}
+
u32 GetPC(u32 cpu)
{
return cpu ? ARM7->R[15] : ARM9->R[15];
}
+u64 GetSysClockCycles(int num)
+{
+ u64 ret;
+
+ if (num == 0 || num == 2)
+ {
+ if (num == 0) ret = SysClockCycles;
+ else if (num == 2) ret = FrameSysClockCycles;
+
+ if (CurCPU == 1) ret += (ARM9->Cycles >> 1);
+ else if (CurCPU == 2) ret += ARM7->Cycles;
+ }
+ else if (num == 1)
+ {
+ ret = LastSysClockCycles;
+ LastSysClockCycles = 0;
+
+ if (CurCPU == 1)
+ {
+ ret += (ARM9->Cycles >> 1);
+ LastSysClockCycles = -(ARM9->Cycles >> 1);
+ }
+ else if (CurCPU == 2)
+ {
+ ret += ARM7->Cycles;
+ LastSysClockCycles = -ARM7->Cycles;
+ }
+ }
+
+ return ret;
+}
+
+void NocashPrint(u32 ncpu, u32 addr)
+{
+ // addr: u16 flags (TODO: research? libnds doesn't use those)
+ // addr+2: debug string
+
+ addr += 2;
+
+ ARM* cpu = ncpu ? (ARM*)ARM7 : (ARM*)ARM9;
+ u8 (*readfn)(u32) = ncpu ? NDS::ARM7Read8 : NDS::ARM9Read8;
+
+ char output[1024];
+ int ptr = 0;
+
+ for (int i = 0; i < 120 && ptr < 1023; )
+ {
+ char ch = readfn(addr++);
+ i++;
+
+ if (ch == '%')
+ {
+ char cmd[16]; int j;
+ for (j = 0; j < 15; )
+ {
+ char ch2 = readfn(addr++);
+ i++;
+ if (i >= 120) break;
+ if (ch2 == '%') break;
+ cmd[j++] = ch2;
+ }
+ cmd[j] = '\0';
+
+ char subs[64];
+
+ if (cmd[0] == 'r')
+ {
+ if (!strcmp(cmd, "r0")) sprintf(subs, "%08X", cpu->R[0]);
+ else if (!strcmp(cmd, "r1")) sprintf(subs, "%08X", cpu->R[1]);
+ else if (!strcmp(cmd, "r2")) sprintf(subs, "%08X", cpu->R[2]);
+ else if (!strcmp(cmd, "r3")) sprintf(subs, "%08X", cpu->R[3]);
+ else if (!strcmp(cmd, "r4")) sprintf(subs, "%08X", cpu->R[4]);
+ else if (!strcmp(cmd, "r5")) sprintf(subs, "%08X", cpu->R[5]);
+ else if (!strcmp(cmd, "r6")) sprintf(subs, "%08X", cpu->R[6]);
+ else if (!strcmp(cmd, "r7")) sprintf(subs, "%08X", cpu->R[7]);
+ else if (!strcmp(cmd, "r8")) sprintf(subs, "%08X", cpu->R[8]);
+ else if (!strcmp(cmd, "r9")) sprintf(subs, "%08X", cpu->R[9]);
+ else if (!strcmp(cmd, "r10")) sprintf(subs, "%08X", cpu->R[10]);
+ else if (!strcmp(cmd, "r11")) sprintf(subs, "%08X", cpu->R[11]);
+ else if (!strcmp(cmd, "r12")) sprintf(subs, "%08X", cpu->R[12]);
+ else if (!strcmp(cmd, "r13")) sprintf(subs, "%08X", cpu->R[13]);
+ else if (!strcmp(cmd, "r14")) sprintf(subs, "%08X", cpu->R[14]);
+ else if (!strcmp(cmd, "r15")) sprintf(subs, "%08X", cpu->R[15]);
+ }
+ else
+ {
+ if (!strcmp(cmd, "sp")) sprintf(subs, "%08X", cpu->R[13]);
+ else if (!strcmp(cmd, "lr")) sprintf(subs, "%08X", cpu->R[14]);
+ else if (!strcmp(cmd, "pc")) sprintf(subs, "%08X", cpu->R[15]);
+ else if (!strcmp(cmd, "frame")) sprintf(subs, "%u", NumFrames);
+ else if (!strcmp(cmd, "scanline")) sprintf(subs, "%u", GPU::VCount);
+ else if (!strcmp(cmd, "totalclks")) sprintf(subs, "%lu", GetSysClockCycles(0));
+ else if (!strcmp(cmd, "lastclks")) sprintf(subs, "%lu", GetSysClockCycles(1));
+ else if (!strcmp(cmd, "zeroclks"))
+ {
+ sprintf(subs, "");
+ GetSysClockCycles(1);
+ }
+ }
+
+ int slen = strlen(subs);
+ if ((ptr+slen) > 1023) slen = 1023-ptr;
+ strncpy(&output[ptr], subs, slen);
+ ptr += slen;
+ }
+ else
+ {
+ output[ptr++] = ch;
+ if (ch == '\0') break;
+ }
+ }
+
+ output[ptr] = '\0';
+ printf("%s", output);
+}
+
void HandleTimerOverflow(u32 tid)
{
Timer* timer = &Timers[tid];
+ //if ((timer->Cnt & 0x84) != 0x80) return;
timer->Counter += timer->Reload << 16;
if (timer->Cnt & (1<<6))
SetIRQ(tid >> 2, IRQ_Timer0 + (tid & 0x3));
+ //u32 delay = (0x10000 - timer->Reload) << (16 - timer->CycleShift);
+ //delay -= (timer->Counter - timer->Reload) >> timer->CycleShift;
+ //printf("timer%d IRQ: resched %d, reload=%04X cnt=%08X\n", tid, delay, timer->Reload, timer->Counter);
+ //ScheduleEvent(Event_TimerIRQ_0 + tid, true, delay, HandleTimerOverflow, tid);
+
if ((tid & 0x3) == 3)
return;
@@ -860,7 +1316,7 @@ void RunTimer(u32 tid, s32 cycles)
HandleTimerOverflow(tid);
}
-void RunTimingCriticalDevices(u32 cpu, s32 cycles)
+void RunTightTimers(u32 cpu, s32 cycles)
{
register u32 timermask = TimerCheckMask[cpu];
@@ -869,10 +1325,20 @@ void RunTimingCriticalDevices(u32 cpu, s32 cycles)
if (timermask & 0x4) RunTimer((cpu<<2)+2, cycles);
if (timermask & 0x8) RunTimer((cpu<<2)+3, cycles);
- if (cpu == 0)
- {
- GPU3D::Run(cycles);
- }
+#ifdef DEBUG_CHECK_DESYNC
+ if (cpu) dbg_CyclesTimer7 += cycles;
+ else dbg_CyclesTimer9 += cycles;
+#endif // DEBUG_CHECK_DESYNC
+}
+
+void RunLooseTimers(u32 cpu, s32 cycles)
+{
+ register u32 timermask = TimerCheckMask[cpu];
+
+ if (timermask & 0x10) RunTimer((cpu<<2)+0, cycles);
+ if (timermask & 0x20) RunTimer((cpu<<2)+1, cycles);
+ if (timermask & 0x40) RunTimer((cpu<<2)+2, cycles);
+ if (timermask & 0x80) RunTimer((cpu<<2)+3, cycles);
}
@@ -887,6 +1353,16 @@ bool DMAsInMode(u32 cpu, u32 mode)
return false;
}
+bool DMAsRunning(u32 cpu)
+{
+ cpu <<= 2;
+ if (DMAs[cpu+0]->IsRunning()) return true;
+ if (DMAs[cpu+1]->IsRunning()) return true;
+ if (DMAs[cpu+2]->IsRunning()) return true;
+ if (DMAs[cpu+3]->IsRunning()) return true;
+ return false;
+}
+
void CheckDMAs(u32 cpu, u32 mode)
{
cpu <<= 2;
@@ -929,12 +1405,28 @@ void TimerStart(u32 id, u16 cnt)
if ((!curstart) && newstart)
{
timer->Counter = timer->Reload << 16;
+
+ /*if ((cnt & 0x84) == 0x80)
+ {
+ u32 delay = (0x10000 - timer->Reload) << TimerPrescaler[cnt & 0x03];
+ printf("timer%d IRQ: start %d, reload=%04X cnt=%08X\n", id, delay, timer->Reload, timer->Counter);
+ CancelEvent(Event_TimerIRQ_0 + id);
+ ScheduleEvent(Event_TimerIRQ_0 + id, false, delay, HandleTimerOverflow, id);
+ }*/
}
if ((cnt & 0x84) == 0x80)
- TimerCheckMask[id>>2] |= (1<<(id&0x3));
+ {
+ u32 tmask;
+ if ((cnt & 0x03) == 0)
+ tmask = 0x01 << (id&0x3);
+ else
+ tmask = 0x10 << (id&0x3);
+
+ TimerCheckMask[id>>2] |= tmask;
+ }
else
- TimerCheckMask[id>>2] &= ~(1<<(id&0x3));
+ TimerCheckMask[id>>2] &= ~(0x11 << (id&0x3));
}
@@ -1084,7 +1576,7 @@ void debug(u32 param)
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
/*FILE*
- shit = fopen("debug/dldio.bin", "wb");
+ shit = fopen("debug/jam.bin", "wb");
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
{
u32 val = ARM7Read32(i);
@@ -1113,8 +1605,14 @@ u8 ARM9Read8(u32 addr)
return *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
case 0x03000000:
- if (SWRAM_ARM9) return *(u8*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask];
- else return 0;
+ if (SWRAM_ARM9)
+ {
+ return *(u8*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask];
+ }
+ else
+ {
+ return 0;
+ }
case 0x04000000:
return ARM9IORead8(addr);
@@ -1123,25 +1621,29 @@ u8 ARM9Read8(u32 addr)
return *(u8*)&GPU::Palette[addr & 0x7FF];
case 0x06000000:
+ switch (addr & 0x00E00000)
{
- switch (addr & 0x00E00000)
- {
- case 0x00000000: return GPU::ReadVRAM_ABG<u8>(addr);
- case 0x00200000: return GPU::ReadVRAM_BBG<u8>(addr);
- case 0x00400000: return GPU::ReadVRAM_AOBJ<u8>(addr);
- case 0x00600000: return GPU::ReadVRAM_BOBJ<u8>(addr);
- default: return GPU::ReadVRAM_LCDC<u8>(addr);
- }
+ case 0x00000000: return GPU::ReadVRAM_ABG<u8>(addr);
+ case 0x00200000: return GPU::ReadVRAM_BBG<u8>(addr);
+ case 0x00400000: return GPU::ReadVRAM_AOBJ<u8>(addr);
+ case 0x00600000: return GPU::ReadVRAM_BOBJ<u8>(addr);
+ default: return GPU::ReadVRAM_LCDC<u8>(addr);
}
- return 0;
case 0x07000000:
return *(u8*)&GPU::OAM[addr & 0x7FF];
case 0x08000000:
case 0x09000000:
+ if (ExMemCnt[0] & (1<<7)) return 0xFF; // TODO: proper open bus
//return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
//printf("GBA read8 %08X\n", addr);
+ // TODO!!!
+ return 0xFF;
+
+ case 0x0A000000:
+ if (ExMemCnt[0] & (1<<7)) return 0xFF; // TODO: proper open bus
+ // TODO!!!
return 0xFF;
}
@@ -1162,8 +1664,14 @@ u16 ARM9Read16(u32 addr)
return *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
case 0x03000000:
- if (SWRAM_ARM9) return *(u16*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask];
- else return 0;
+ if (SWRAM_ARM9)
+ {
+ return *(u16*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask];
+ }
+ else
+ {
+ return 0;
+ }
case 0x04000000:
return ARM9IORead16(addr);
@@ -1172,25 +1680,29 @@ u16 ARM9Read16(u32 addr)
return *(u16*)&GPU::Palette[addr & 0x7FF];
case 0x06000000:
+ switch (addr & 0x00E00000)
{
- switch (addr & 0x00E00000)
- {
- case 0x00000000: return GPU::ReadVRAM_ABG<u16>(addr);
- case 0x00200000: return GPU::ReadVRAM_BBG<u16>(addr);
- case 0x00400000: return GPU::ReadVRAM_AOBJ<u16>(addr);
- case 0x00600000: return GPU::ReadVRAM_BOBJ<u16>(addr);
- default: return GPU::ReadVRAM_LCDC<u16>(addr);
- }
+ case 0x00000000: return GPU::ReadVRAM_ABG<u16>(addr);
+ case 0x00200000: return GPU::ReadVRAM_BBG<u16>(addr);
+ case 0x00400000: return GPU::ReadVRAM_AOBJ<u16>(addr);
+ case 0x00600000: return GPU::ReadVRAM_BOBJ<u16>(addr);
+ default: return GPU::ReadVRAM_LCDC<u16>(addr);
}
- return 0;
case 0x07000000:
return *(u16*)&GPU::OAM[addr & 0x7FF];
case 0x08000000:
case 0x09000000:
- //return *(u16*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
- //printf("GBA read16 %08X\n", addr);
+ if (ExMemCnt[0] & (1<<7)) return 0xFFFF; // TODO: proper open bus
+ //return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
+ //printf("GBA read8 %08X\n", addr);
+ // TODO!!!
+ return 0xFFFF;
+
+ case 0x0A000000:
+ if (ExMemCnt[0] & (1<<7)) return 0xFFFF; // TODO: proper open bus
+ // TODO!!!
return 0xFFFF;
}
@@ -1211,8 +1723,14 @@ u32 ARM9Read32(u32 addr)
return *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
case 0x03000000:
- if (SWRAM_ARM9) return *(u32*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask];
- else return 0;
+ if (SWRAM_ARM9)
+ {
+ return *(u32*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask];
+ }
+ else
+ {
+ return 0;
+ }
case 0x04000000:
return ARM9IORead32(addr);
@@ -1221,29 +1739,33 @@ u32 ARM9Read32(u32 addr)
return *(u32*)&GPU::Palette[addr & 0x7FF];
case 0x06000000:
+ switch (addr & 0x00E00000)
{
- switch (addr & 0x00E00000)
- {
- case 0x00000000: return GPU::ReadVRAM_ABG<u32>(addr);
- case 0x00200000: return GPU::ReadVRAM_BBG<u32>(addr);
- case 0x00400000: return GPU::ReadVRAM_AOBJ<u32>(addr);
- case 0x00600000: return GPU::ReadVRAM_BOBJ<u32>(addr);
- default: return GPU::ReadVRAM_LCDC<u32>(addr);
- }
+ case 0x00000000: return GPU::ReadVRAM_ABG<u32>(addr);
+ case 0x00200000: return GPU::ReadVRAM_BBG<u32>(addr);
+ case 0x00400000: return GPU::ReadVRAM_AOBJ<u32>(addr);
+ case 0x00600000: return GPU::ReadVRAM_BOBJ<u32>(addr);
+ default: return GPU::ReadVRAM_LCDC<u32>(addr);
}
- return 0;
case 0x07000000:
return *(u32*)&GPU::OAM[addr & 0x7FF];
case 0x08000000:
case 0x09000000:
- //return *(u32*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
- //printf("GBA read32 %08X\n", addr);
+ if (ExMemCnt[0] & (1<<7)) return 0xFFFFFFFF; // TODO: proper open bus
+ //return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
+ //printf("GBA read8 %08X\n", addr);
+ // TODO!!!
+ return 0xFFFFFFFF;
+
+ case 0x0A000000:
+ if (ExMemCnt[0] & (1<<7)) return 0xFFFFFFFF; // TODO: proper open bus
+ // TODO!!!
return 0xFFFFFFFF;
}
- printf("unknown arm9 read32 %08X | %08X %08X %08X\n", addr, ARM9->R[15], ARM9->R[12], ARM9Read32(0x027FF820));
+ printf("unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9->R[15], ARM9->R[12]);
return 0;
}
@@ -1256,7 +1778,10 @@ void ARM9Write8(u32 addr, u8 val)
return;
case 0x03000000:
- if (SWRAM_ARM9) *(u8*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask] = val;
+ if (SWRAM_ARM9)
+ {
+ *(u8*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask] = val;
+ }
return;
case 0x04000000:
@@ -1266,6 +1791,7 @@ void ARM9Write8(u32 addr, u8 val)
case 0x05000000:
case 0x06000000:
case 0x07000000:
+ // checkme
return;
}
@@ -1281,7 +1807,10 @@ void ARM9Write16(u32 addr, u16 val)
return;
case 0x03000000:
- if (SWRAM_ARM9) *(u16*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask] = val;
+ if (SWRAM_ARM9)
+ {
+ *(u16*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask] = val;
+ }
return;
case 0x04000000:
@@ -1295,13 +1824,12 @@ void ARM9Write16(u32 addr, u16 val)
case 0x06000000:
switch (addr & 0x00E00000)
{
- case 0x00000000: GPU::WriteVRAM_ABG<u16>(addr, val); break;
- case 0x00200000: GPU::WriteVRAM_BBG<u16>(addr, val); break;
- case 0x00400000: GPU::WriteVRAM_AOBJ<u16>(addr, val); break;
- case 0x00600000: GPU::WriteVRAM_BOBJ<u16>(addr, val); break;
- default: GPU::WriteVRAM_LCDC<u16>(addr, val); break;
+ case 0x00000000: GPU::WriteVRAM_ABG<u16>(addr, val); return;
+ case 0x00200000: GPU::WriteVRAM_BBG<u16>(addr, val); return;
+ case 0x00400000: GPU::WriteVRAM_AOBJ<u16>(addr, val); return;
+ case 0x00600000: GPU::WriteVRAM_BOBJ<u16>(addr, val); return;
+ default: GPU::WriteVRAM_LCDC<u16>(addr, val); return;
}
- return;
case 0x07000000:
*(u16*)&GPU::OAM[addr & 0x7FF] = val;
@@ -1317,10 +1845,13 @@ void ARM9Write32(u32 addr, u32 val)
{
case 0x02000000:
*(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
- return;
+ return ;
case 0x03000000:
- if (SWRAM_ARM9) *(u32*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask] = val;
+ if (SWRAM_ARM9)
+ {
+ *(u32*)&SWRAM_ARM9[addr & SWRAM_ARM9Mask] = val;
+ }
return;
case 0x04000000:
@@ -1334,13 +1865,12 @@ void ARM9Write32(u32 addr, u32 val)
case 0x06000000:
switch (addr & 0x00E00000)
{
- case 0x00000000: GPU::WriteVRAM_ABG<u32>(addr, val); break;
- case 0x00200000: GPU::WriteVRAM_BBG<u32>(addr, val); break;
- case 0x00400000: GPU::WriteVRAM_AOBJ<u32>(addr, val); break;
- case 0x00600000: GPU::WriteVRAM_BOBJ<u32>(addr, val); break;
- default: GPU::WriteVRAM_LCDC<u32>(addr, val); break;
+ case 0x00000000: GPU::WriteVRAM_ABG<u32>(addr, val); return;
+ case 0x00200000: GPU::WriteVRAM_BBG<u32>(addr, val); return;
+ case 0x00400000: GPU::WriteVRAM_AOBJ<u32>(addr, val); return;
+ case 0x00600000: GPU::WriteVRAM_BOBJ<u32>(addr, val); return;
+ default: GPU::WriteVRAM_LCDC<u32>(addr, val); return;
}
- return;
case 0x07000000:
*(u32*)&GPU::OAM[addr & 0x7FF] = val;
@@ -1350,6 +1880,36 @@ void ARM9Write32(u32 addr, u32 val)
printf("unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]);
}
+bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
+{
+ switch (addr & 0xFF000000)
+ {
+ case 0x02000000:
+ region->Mem = MainRAM;
+ region->Mask = MAIN_RAM_SIZE-1;
+ return true;
+
+ case 0x03000000:
+ if (SWRAM_ARM9)
+ {
+ region->Mem = SWRAM_ARM9;
+ region->Mask = SWRAM_ARM9Mask;
+ return true;
+ }
+ break;
+ }
+
+ if ((addr & 0xFFFFF000) == 0xFFFF0000 && !write)
+ {
+ region->Mem = ARM9BIOS;
+ region->Mask = 0xFFF;
+ return true;
+ }
+
+ region->Mem = NULL;
+ return false;
+}
+
u8 ARM7Read8(u32 addr)
@@ -1371,8 +1931,14 @@ u8 ARM7Read8(u32 addr)
return *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
case 0x03000000:
- if (SWRAM_ARM7) return *(u8*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask];
- else return *(u8*)&ARM7WRAM[addr & 0xFFFF];
+ if (SWRAM_ARM7)
+ {
+ return *(u8*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask];
+ }
+ else
+ {
+ return *(u8*)&ARM7WRAM[addr & 0xFFFF];
+ }
case 0x03800000:
return *(u8*)&ARM7WRAM[addr & 0xFFFF];
@@ -1383,6 +1949,19 @@ u8 ARM7Read8(u32 addr)
case 0x06000000:
case 0x06800000:
return GPU::ReadVRAM_ARM7<u8>(addr);
+
+ case 0x08000000:
+ case 0x09000000:
+ if (!(ExMemCnt[0] & (1<<7))) return 0xFF; // TODO: proper open bus
+ //return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
+ //printf("GBA read8 %08X\n", addr);
+ // TODO!!!
+ return 0xFF;
+
+ case 0x0A000000:
+ if (!(ExMemCnt[0] & (1<<7))) return 0xFF; // TODO: proper open bus
+ // TODO!!!
+ return 0xFF;
}
printf("unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7->R[15], ARM7->R[0], ARM7->R[1]);
@@ -1408,8 +1987,14 @@ u16 ARM7Read16(u32 addr)
return *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
case 0x03000000:
- if (SWRAM_ARM7) return *(u16*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask];
- else return *(u16*)&ARM7WRAM[addr & 0xFFFF];
+ if (SWRAM_ARM7)
+ {
+ return *(u16*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask];
+ }
+ else
+ {
+ return *(u16*)&ARM7WRAM[addr & 0xFFFF];
+ }
case 0x03800000:
return *(u16*)&ARM7WRAM[addr & 0xFFFF];
@@ -1418,11 +2003,28 @@ u16 ARM7Read16(u32 addr)
return ARM7IORead16(addr);
case 0x04800000:
- return Wifi::Read(addr);
+ if (addr < 0x04810000)
+ {
+ return Wifi::Read(addr);
+ }
+ break;
case 0x06000000:
case 0x06800000:
return GPU::ReadVRAM_ARM7<u16>(addr);
+
+ case 0x08000000:
+ case 0x09000000:
+ if (!(ExMemCnt[0] & (1<<7))) return 0xFFFF; // TODO: proper open bus
+ //return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
+ //printf("GBA read8 %08X\n", addr);
+ // TODO!!!
+ return 0xFFFF;
+
+ case 0x0A000000:
+ if (!(ExMemCnt[0] & (1<<7))) return 0xFFFF; // TODO: proper open bus
+ // TODO!!!
+ return 0xFFFF;
}
printf("unknown arm7 read16 %08X %08X\n", addr, ARM7->R[15]);
@@ -1448,8 +2050,14 @@ u32 ARM7Read32(u32 addr)
return *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
case 0x03000000:
- if (SWRAM_ARM7) return *(u32*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask];
- else return *(u32*)&ARM7WRAM[addr & 0xFFFF];
+ if (SWRAM_ARM7)
+ {
+ return *(u32*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask];
+ }
+ else
+ {
+ return *(u32*)&ARM7WRAM[addr & 0xFFFF];
+ }
case 0x03800000:
return *(u32*)&ARM7WRAM[addr & 0xFFFF];
@@ -1458,11 +2066,28 @@ u32 ARM7Read32(u32 addr)
return ARM7IORead32(addr);
case 0x04800000:
- return Wifi::Read(addr) | (Wifi::Read(addr+2) << 16);
+ if (addr < 0x04810000)
+ {
+ return Wifi::Read(addr) | (Wifi::Read(addr+2) << 16);
+ }
+ break;
case 0x06000000:
case 0x06800000:
return GPU::ReadVRAM_ARM7<u32>(addr);
+
+ case 0x08000000:
+ case 0x09000000:
+ if (!(ExMemCnt[0] & (1<<7))) return 0xFFFFFFFF; // TODO: proper open bus
+ //return *(u8*)&NDSCart::CartROM[addr & (NDSCart::CartROMSize-1)];
+ //printf("GBA read8 %08X\n", addr);
+ // TODO!!!
+ return 0xFFFFFFFF;
+
+ case 0x0A000000:
+ if (!(ExMemCnt[0] & (1<<7))) return 0xFFFFFFFF; // TODO: proper open bus
+ // TODO!!!
+ return 0xFFFFFFFF;
}
printf("unknown arm7 read32 %08X | %08X\n", addr, ARM7->R[15]);
@@ -1479,9 +2104,16 @@ void ARM7Write8(u32 addr, u8 val)
return;
case 0x03000000:
- if (SWRAM_ARM7) *(u8*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask] = val;
- else *(u8*)&ARM7WRAM[addr & 0xFFFF] = val;
- return;
+ if (SWRAM_ARM7)
+ {
+ *(u8*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask] = val;
+ return;
+ }
+ else
+ {
+ *(u8*)&ARM7WRAM[addr & 0xFFFF] = val;
+ return;
+ }
case 0x03800000:
*(u8*)&ARM7WRAM[addr & 0xFFFF] = val;
@@ -1510,9 +2142,16 @@ void ARM7Write16(u32 addr, u16 val)
return;
case 0x03000000:
- if (SWRAM_ARM7) *(u16*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask] = val;
- else *(u16*)&ARM7WRAM[addr & 0xFFFF] = val;
- return;
+ if (SWRAM_ARM7)
+ {
+ *(u16*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask] = val;
+ return;
+ }
+ else
+ {
+ *(u16*)&ARM7WRAM[addr & 0xFFFF] = val;
+ return;
+ }
case 0x03800000:
*(u16*)&ARM7WRAM[addr & 0xFFFF] = val;
@@ -1523,8 +2162,12 @@ void ARM7Write16(u32 addr, u16 val)
return;
case 0x04800000:
- Wifi::Write(addr, val);
- return;
+ if (addr < 0x04810000)
+ {
+ Wifi::Write(addr, val);
+ return;
+ }
+ break;
case 0x06000000:
case 0x06800000:
@@ -1545,9 +2188,16 @@ void ARM7Write32(u32 addr, u32 val)
return;
case 0x03000000:
- if (SWRAM_ARM7) *(u32*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask] = val;
- else *(u32*)&ARM7WRAM[addr & 0xFFFF] = val;
- return;
+ if (SWRAM_ARM7)
+ {
+ *(u32*)&SWRAM_ARM7[addr & SWRAM_ARM7Mask] = val;
+ return;
+ }
+ else
+ {
+ *(u32*)&ARM7WRAM[addr & 0xFFFF] = val;
+ return;
+ }
case 0x03800000:
*(u32*)&ARM7WRAM[addr & 0xFFFF] = val;
@@ -1558,9 +2208,13 @@ void ARM7Write32(u32 addr, u32 val)
return;
case 0x04800000:
- Wifi::Write(addr, val & 0xFFFF);
- Wifi::Write(addr+2, val >> 16);
- return;
+ if (addr < 0x04810000)
+ {
+ Wifi::Write(addr, val & 0xFFFF);
+ Wifi::Write(addr+2, val >> 16);
+ return;
+ }
+ break;
case 0x06000000:
case 0x06800000:
@@ -1571,6 +2225,51 @@ void ARM7Write32(u32 addr, u32 val)
//printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]);
}
+bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region)
+{
+ switch (addr & 0xFF800000)
+ {
+ case 0x02000000:
+ case 0x02800000:
+ region->Mem = MainRAM;
+ region->Mask = MAIN_RAM_SIZE-1;
+ return true;
+
+ case 0x03000000:
+ // note on this, and why we can only cover it in one particular case:
+ // it is typical for games to map all shared WRAM to the ARM7
+ // then access all the WRAM as one contiguous block starting at 0x037F8000
+ // this case needs a bit of a hack to cover
+ // it's not really worth bothering anyway
+ if (!SWRAM_ARM7)
+ {
+ region->Mem = ARM7WRAM;
+ region->Mask = 0xFFFF;
+ return true;
+ }
+ break;
+
+ case 0x03800000:
+ region->Mem = ARM7WRAM;
+ region->Mask = 0xFFFF;
+ return true;
+ }
+
+ // BIOS. ARM7 PC has to be within range.
+ if (addr < 0x00004000 && !write)
+ {
+ if (ARM7->R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7->R[15] < ARM7BIOSProt))
+ {
+ region->Mem = ARM7BIOS;
+ region->Mask = 0x3FFF;
+ return true;
+ }
+ }
+
+ region->Mem = NULL;
+ return false;
+}
+
@@ -1847,6 +2546,9 @@ u32 ARM9IORead32(u32 addr)
case 0x040002B8: return SqrtVal[0];
case 0x040002BC: return SqrtVal[1];
+ case 0x04000300: return PostFlag9;
+ case 0x04000304: return PowerControl9;
+
case 0x04100000:
if (IPCFIFOCnt9 & 0x8000)
{
@@ -2068,6 +2770,7 @@ void ARM9IOWrite16(u32 addr, u16 val)
case 0x04000204:
ExMemCnt[0] = val;
ExMemCnt[1] = (ExMemCnt[1] & 0x007F) | (val & 0xFF80);
+ SetGBASlotTimings();
return;
case 0x04000208: IME[0] = val & 0x1; return;
@@ -2388,6 +3091,8 @@ u16 ARM7IORead16(u32 addr)
case 0x040001C2: return SPI::ReadData();
case 0x04000204: return ExMemCnt[1];
+ case 0x04000206: return WifiWaitCnt;
+
case 0x04000208: return IME[1];
case 0x04000210: return IE[1] & 0xFFFF;
case 0x04000212: return IE[1] >> 16;
@@ -2550,7 +3255,10 @@ void ARM7IOWrite8(u32 addr, u8 val)
return;
case 0x04000301:
- if (val == 0x80) ARM7->Halt(1);
+ val & 0xC0;
+ if (val == 0x40) printf("!! GBA MODE NOT SUPPORTED\n");
+ else if (val == 0x80) ARM7->Halt(1);
+ else if (val == 0xC0) EnterSleepMode();
return;
}
@@ -2653,6 +3361,10 @@ void ARM7IOWrite16(u32 addr, u16 val)
case 0x04000204:
ExMemCnt[1] = (ExMemCnt[1] & 0xFF80) | (val & 0x007F);
+ SetGBASlotTimings();
+ return;
+ case 0x04000206:
+ SetWifiWaitCnt(val);
return;
case 0x04000208: IME[1] = val & 0x1; return;
diff --git a/src/NDS.h b/src/NDS.h
index 1bf8508..bfb390f 100644
--- a/src/NDS.h
+++ b/src/NDS.h
@@ -22,9 +22,21 @@
#include "Savestate.h"
#include "types.h"
+// when touching the main loop/timing code, pls test a lot of shit
+// with this enabled, to make sure it doesn't desync
+//#define DEBUG_CHECK_DESYNC
+
namespace NDS
{
+#ifdef DEBUG_CHECK_DESYNC
+extern u64 dbg_CyclesSys;
+extern u64 dbg_CyclesARM9;
+extern u64 dbg_CyclesTimer9;
+extern u64 dbg_CyclesARM7;
+extern u64 dbg_CyclesTimer7;
+#endif
+
enum
{
Event_LCD = 0,
@@ -87,6 +99,16 @@ typedef struct
} Timer;
+typedef struct
+{
+ u8* Mem;
+ u32 Mask;
+
+} MemRegion;
+
+extern u8 ARM9MemTimings[0x40000][4];
+extern u8 ARM7MemTimings[0x20000][4];
+
// hax
extern u32 IME[2];
extern u32 IE[2];
@@ -113,6 +135,9 @@ void Stop();
bool DoSavestate(Savestate* file);
+void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
+void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
+
bool LoadROM(const char* path, const char* sram, bool direct);
void LoadBIOS();
void SetupDirectBoot();
@@ -127,6 +152,10 @@ void ReleaseScreen();
void SetKeyMask(u32 mask);
+void SetLidClosed(bool closed);
+
+void MicInputFrame(s16* data, int samples);
+
void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param);
void CancelEvent(u32 id);
@@ -141,14 +170,20 @@ void ClearIRQ(u32 cpu, u32 irq);
bool HaltInterrupted(u32 cpu);
void StopCPU(u32 cpu, u32 mask);
void ResumeCPU(u32 cpu, u32 mask);
+void GXFIFOStall();
+void GXFIFOUnstall();
u32 GetPC(u32 cpu);
+u64 GetSysClockCycles(int num);
+void NocashPrint(u32 cpu, u32 addr);
bool DMAsInMode(u32 cpu, u32 mode);
+bool DMAsRunning(u32 cpu);
void CheckDMAs(u32 cpu, u32 mode);
void StopDMAs(u32 cpu, u32 mode);
-void RunTimingCriticalDevices(u32 cpu, s32 cycles);
+void RunTightTimers(u32 cpu, s32 cycles);
+void RunLooseTimers(u32 cpu, s32 cycles);
u8 ARM9Read8(u32 addr);
u16 ARM9Read16(u32 addr);
@@ -157,6 +192,8 @@ void ARM9Write8(u32 addr, u8 val);
void ARM9Write16(u32 addr, u16 val);
void ARM9Write32(u32 addr, u32 val);
+bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region);
+
u8 ARM7Read8(u32 addr);
u16 ARM7Read16(u32 addr);
u32 ARM7Read32(u32 addr);
@@ -164,6 +201,8 @@ void ARM7Write8(u32 addr, u8 val);
void ARM7Write16(u32 addr, u16 val);
void ARM7Write32(u32 addr, u32 val);
+bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region);
+
u8 ARM9IORead8(u32 addr);
u16 ARM9IORead16(u32 addr);
u32 ARM9IORead32(u32 addr);
diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp
index 03dfe4c..c959663 100644
--- a/src/NDSCart.cpp
+++ b/src/NDSCart.cpp
@@ -21,6 +21,7 @@
#include "NDS.h"
#include "NDSCart.h"
#include "ARM.h"
+#include "CRC32.h"
#include "melon_fopen.h"
@@ -28,11 +29,6 @@
namespace NDSCart_SRAM
{
-// BIG SRAM TODO!!!!
-// USE A DATABASE TO IDENTIFY SAVE MEMORY TYPE
-// (and maybe keep autodetect as a last resort??)
-// AUTODETECT IS BLARGY AND IS A MESS
-
u8* SRAM;
u32 SRAMLength;
@@ -40,15 +36,6 @@ char SRAMPath[1024];
void (*WriteFunc)(u8 val, bool islast);
-u32 Discover_MemoryType;
-u32 Discover_Likeliness;
-u8* Discover_Buffer;
-u32 Discover_DataPos;
-u32 Discover_LastPC;
-u32 Discover_AddrLength;
-u32 Discover_Addr;
-u32 Discover_LikelySize;
-
u32 Hold;
u8 CurCmd;
u32 DataPos;
@@ -68,37 +55,25 @@ void Write_Discover(u8 val, bool islast);
bool Init()
{
SRAM = NULL;
- Discover_Buffer = NULL;
return true;
}
void DeInit()
{
if (SRAM) delete[] SRAM;
- if (Discover_Buffer) delete[] Discover_Buffer;
}
void Reset()
{
if (SRAM) delete[] SRAM;
- if (Discover_Buffer) delete[] Discover_Buffer;
SRAM = NULL;
- Discover_Buffer = NULL;
}
void DoSavestate(Savestate* file)
{
file->Section("NDCS");
- // CHECKME/TODO/whatever
- // should the autodetect status shit go in the savestate???
- // I don't think so.
- // worst case is that the savestate was taken before autodetect took place completely
- // and that causes it to yield a different result, fucking things up
- // but that is unlikely
- // and we should just use a goddamn database anyway, this is a trainwreck
-
- // we reload the SRAM contents, tho.
+ // we reload the SRAM contents.
// it should be the same file (as it should be the same ROM, duh)
// but the contents may change
// TODO maybe: possibility to save to a separate file when using savestates????
@@ -138,15 +113,9 @@ void DoSavestate(Savestate* file)
file->Var32(&Addr);
}
-void LoadSave(const char* path)
+void LoadSave(const char* path, u32 type)
{
if (SRAM) delete[] SRAM;
- if (Discover_Buffer) delete[] Discover_Buffer;
-
- Discover_Buffer = NULL;
- Discover_LastPC = 0;
- Discover_AddrLength = 0x7FFFFFFF;
- Discover_LikelySize = 0;
strncpy(SRAMPath, path, 1023);
SRAMPath[1023] = '\0';
@@ -162,32 +131,35 @@ void LoadSave(const char* path)
fread(SRAM, SRAMLength, 1, f);
fclose(f);
+ }
+ else
+ {
+ if (type > 8) type = 0;
+ int sramlen[] = {0, 512, 8192, 65536, 256*1024, 512*1024, 1024*1024, 8192*1024, 32768*1024};
+ SRAMLength = sramlen[type];
- switch (SRAMLength)
+ if (SRAMLength)
{
- case 512: WriteFunc = Write_EEPROMTiny; break;
- case 8192:
- case 65536: WriteFunc = Write_EEPROM; break;
- case 256*1024:
- case 512*1024:
- case 1024*1024:
- case 8192*1024: WriteFunc = Write_Flash; break;
- default:
- printf("!! BAD SAVE LENGTH %d\n", SRAMLength);
- WriteFunc = Write_Null;
- break;
+ SRAM = new u8[SRAMLength];
+ memset(SRAM, 0, SRAMLength);
}
}
- else
+
+ switch (SRAMLength)
{
- SRAMLength = 0;
- WriteFunc = Write_Discover;
- Discover_MemoryType = 2;
- Discover_Likeliness = 0;
-
- Discover_DataPos = 0;
- Discover_Buffer = new u8[256*1024];
- memset(Discover_Buffer, 0, 256*1024);
+ case 512: WriteFunc = Write_EEPROMTiny; break;
+ case 8192:
+ case 65536: WriteFunc = Write_EEPROM; break;
+ case 256*1024:
+ case 512*1024:
+ case 1024*1024:
+ case 8192*1024: WriteFunc = Write_Flash; break;
+ case 32768*1024: WriteFunc = Write_Null; break; // NAND FLASH, handled differently
+ default:
+ printf("!! BAD SAVE LENGTH %d\n", SRAMLength);
+ case 0:
+ WriteFunc = Write_Null;
+ break;
}
Hold = 0;
@@ -200,7 +172,7 @@ void RelocateSave(const char* path, bool write)
{
if (!write)
{
- LoadSave(path); // lazy
+ LoadSave(path, 0); // lazy
return;
}
@@ -223,240 +195,6 @@ u8 Read()
return Data;
}
-void SetMemoryType()
-{
- SRAMLength = 0;
-
- if (Discover_LikelySize)
- {
- if (Discover_LikelySize >= 0x40000)
- {
- Discover_MemoryType = 4; // FLASH
- SRAMLength = Discover_LikelySize;
- }
- else if (Discover_LikelySize == 0x10000 || Discover_MemoryType == 3)
- {
- if (Discover_MemoryType > 3)
- printf("incoherent detection result: type=%d size=%X\n", Discover_MemoryType, Discover_LikelySize);
-
- Discover_MemoryType = 3;
- }
- else if (Discover_LikelySize == 0x2000)
- {
- if (Discover_MemoryType > 3)
- printf("incoherent detection result: type=%d size=%X\n", Discover_MemoryType, Discover_LikelySize);
-
- Discover_MemoryType = 2;
- }
- else if (Discover_LikelySize == 0x200 || Discover_MemoryType == 1)
- {
- if (Discover_MemoryType > 1)
- printf("incoherent detection result: type=%d size=%X\n", Discover_MemoryType, Discover_LikelySize);
-
- Discover_MemoryType = 1;
- }
- else
- {
- printf("bad save size %X. assuming EEPROM 64K\n", Discover_LikelySize);
- Discover_MemoryType = 2;
- }
- }
-
- switch (Discover_MemoryType)
- {
- case 1:
- printf("Save memory type: EEPROM 4k\n");
- WriteFunc = Write_EEPROMTiny;
- SRAMLength = 512;
- break;
-
- case 2:
- printf("Save memory type: EEPROM 64k\n");
- WriteFunc = Write_EEPROM;
- SRAMLength = 8192;
- break;
-
- case 3:
- printf("Save memory type: EEPROM 512k\n");
- WriteFunc = Write_EEPROM;
- SRAMLength = 65536;
- break;
-
- case 4:
- if (!SRAMLength) SRAMLength = 256*1024;
- printf("Save memory type: Flash %dk\n", SRAMLength/1024);
- WriteFunc = Write_Flash;
- break;
-
- case 5:
- printf("Save memory type: ...something else\n");
- WriteFunc = Write_Null;
- SRAMLength = 0;
- break;
- }
-
- if (!SRAMLength)
- return;
-
- SRAM = new u8[SRAMLength];
-
- // replay writes that occured during discovery
- u8 prev_cmd = CurCmd;
- u32 pos = 0;
- while (pos < 256*1024)
- {
- u32 len = *(u32*)&Discover_Buffer[pos];
- pos += 4;
- if (len == 0) break;
-
- CurCmd = Discover_Buffer[pos++];
- DataPos = 0;
- Addr = 0;
- Data = 0;
- for (u32 i = 1; i < len; i++)
- {
- WriteFunc(Discover_Buffer[pos++], (i==(len-1)));
- DataPos++;
- }
- }
-
- CurCmd = prev_cmd;
-
- delete[] Discover_Buffer;
- Discover_Buffer = NULL;
-}
-
-void Write_Discover(u8 val, bool islast)
-{
- // attempt at autodetecting the type of save memory.
- // we basically hope the game will be nice and clear whole pages of memory.
-
- if (CurCmd == 0x03 || CurCmd == 0x0B)
- {
- if (Discover_Likeliness) // writes have occured before this read
- {
- // apply. and pray.
- SetMemoryType();
-
- DataPos = 0;
- Addr = 0;
- Data = 0;
- return WriteFunc(val, islast);
- }
- else
- {
- if (DataPos == 0)
- {
- Discover_LastPC = NDS::GetPC((NDS::ExMemCnt[0] >> 11) & 0x1);
- Discover_Addr = val;
- }
- else
- {
- bool addrlenchange = false;
-
- Discover_Addr <<= 8;
- Discover_Addr |= val;
-
- u32 pc = NDS::GetPC((NDS::ExMemCnt[0] >> 11) & 0x1);
- if ((pc != Discover_LastPC) || islast)
- {
- if (DataPos < Discover_AddrLength)
- {
- Discover_AddrLength = DataPos;
- addrlenchange = true;
- }
- }
-
- if (DataPos == Discover_AddrLength)
- {
- Discover_Addr >>= 8;
-
- // determine the address for this read
- // the idea is to see how far the game reads
- // but we need margins for games that have antipiracy
- // as those will generally read just past the limit
- // and expect it to wrap to zero
-
- u32 likelysize = 0;
-
- if (DataPos == 3) // FLASH
- {
- if (Discover_Addr >= 0x101000)
- likelysize = 0x800000; // 8M
- else if (Discover_Addr >= 0x81000)
- likelysize = 0x100000; // 1M
- else if (Discover_Addr >= 0x41000)
- likelysize = 0x80000; // 512K
- else
- likelysize = 0x40000; // 256K
- }
- else if (DataPos == 2) // EEPROM
- {
- if (Discover_Addr >= 0x3000)
- likelysize = 0x10000; // 64K
- else
- likelysize = 0x2000; // 8K
- }
- else if (DataPos == 1) // tiny EEPROM
- {
- likelysize = 0x200; // always 4K
- }
-
- if ((likelysize > Discover_LikelySize) || addrlenchange)
- Discover_LikelySize = likelysize;
- }
- }
-
- Data = 0;
- return;
- }
- }
-
- if (CurCmd == 0x02 || CurCmd == 0x0A)
- {
- if (DataPos == 0)
- Discover_Buffer[Discover_DataPos + 4] = CurCmd;
-
- Discover_Buffer[Discover_DataPos + 5 + DataPos] = val;
-
- if (islast)
- {
- u32 len = DataPos+1;
-
- *(u32*)&Discover_Buffer[Discover_DataPos] = len+1;
- Discover_DataPos += 5+len;
-
- if (Discover_Likeliness <= len)
- {
- Discover_Likeliness = len;
-
- if (len > 3+256) // bigger Flash, FRAM, whatever
- {
- Discover_MemoryType = 5;
- }
- else if ((len > 2+128) || (len > 1+16 && CurCmd == 0xA)) // Flash
- {
- Discover_MemoryType = 4;
- }
- else if (len > 2+32) // EEPROM 512k
- {
- Discover_MemoryType = 3;
- }
- else if (len > 1+16 || (len != 1+16 && CurCmd != 0x0A)) // EEPROM 64k
- {
- Discover_MemoryType = 2;
- }
- else // EEPROM 4k
- {
- Discover_MemoryType = 1;
- }
- }
-
- printf("discover: type=%d likeliness=%d\n", Discover_MemoryType, Discover_Likeliness);
- }
- }
-}
-
void Write_Null(u8 val, bool islast) {}
void Write_EEPROMTiny(u8 val, bool islast)
@@ -733,6 +471,7 @@ u32 DataOutLen;
bool CartInserted;
u8* CartROM;
u32 CartROMSize;
+u32 CartCRC;
u32 CartID;
bool CartIsHomebrew;
@@ -745,6 +484,12 @@ u64 Key2_X;
u64 Key2_Y;
+void ROMCommand_Retail(u8* cmd);
+void ROMCommand_RetailNAND(u8* cmd);
+
+void (*ROMCommandHandler)(u8* cmd);
+
+
u32 ByteSwap(u32 val)
{
return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24);
@@ -886,6 +631,8 @@ void Reset()
CartID = 0;
CartIsHomebrew = false;
+ ROMCommandHandler = NULL;
+
CmdEncMode = 0;
DataEncMode = 0;
@@ -1062,6 +809,63 @@ void ApplyDLDIPatch()
}
+bool ReadROMParams(u32* params)
+{
+ // format for romlist.bin:
+ // [CRC32] [ROM size] [save type] [reserved]
+ // list must be sorted by CRC
+
+ FILE* f = melon_fopen_local("romlist.bin", "rb");
+ if (!f) return false;
+
+ fseek(f, 0, SEEK_END);
+ u32 len = (u32)ftell(f);
+ u32 maxlen = len;
+ len >>= 4; // 16 bytes per entry
+
+ u32 offset = 0;
+ u32 chk_size = len >> 1;
+ for (;;)
+ {
+ u32 crc = 0;
+ fseek(f, offset + (chk_size << 4), SEEK_SET);
+ fread(&crc, 4, 1, f);
+
+ printf("chk_size=%d, crc=%08X, wanted=%08X, offset=%08X\n", chk_size, crc, CartCRC, offset);
+
+ if (crc == CartCRC)
+ {
+ fread(params, 4, 3, f);
+ fclose(f);
+ return true;
+ }
+ else
+ {
+ if (crc < CartCRC)
+ {
+ if (chk_size == 0)
+ offset += 0x10;
+ else
+ offset += (chk_size << 4);
+ }
+ else if (chk_size == 0)
+ {
+ fclose(f);
+ return false;
+ }
+
+ chk_size >>= 1;
+ }
+
+ if (offset >= maxlen)
+ {
+ fclose(f);
+ return false;
+ }
+ }
+}
+
+
bool LoadROM(const char* path, const char* sram, bool direct)
{
// TODO: streaming mode? for really big ROMs or systems with limited RAM
@@ -1094,10 +898,40 @@ bool LoadROM(const char* path, const char* sram, bool direct)
fclose(f);
//CartROM = f;
+ CartCRC = CRC32(CartROM, CartROMSize);
+ printf("ROM CRC32: %08X\n", CartCRC);
+
+ u32 romparams[3];
+ if (!ReadROMParams(romparams))
+ {
+ // set defaults
+ printf("ROM entry not found\n");
+
+ romparams[0] = CartROMSize;
+ if (*(u32*)&CartROM[0x20] < 0x4000)
+ romparams[1] = 0; // no saveRAM for homebrew
+ else
+ romparams[1] = 2; // assume EEPROM 64k (TODO FIXME)
+ }
+ else
+ printf("ROM entry: %08X %08X %08X\n", romparams[0], romparams[1], romparams[2]);
+
+ if (romparams[0] != len) printf("!! bad ROM size %d (expected %d) rounded to %d\n", len, romparams[0], CartROMSize);
+
// generate a ROM ID
// note: most games don't check the actual value
// it just has to stay the same throughout gameplay
- CartID = 0x00001FC2;
+ CartID = 0x000000C2;
+
+ if (CartROMSize <= 128*1024*1024)
+ CartID |= ((CartROMSize >> 20) - 1) << 8;
+ else
+ CartID |= (0x100 - (CartROMSize >> 28)) << 8;
+
+ if (romparams[1] == 8)
+ CartID |= 0x08000000; // NAND flag
+
+ printf("Cart ID: %08X\n", CartID);
if (*(u32*)&CartROM[0x20] < 0x4000)
{
@@ -1114,6 +948,12 @@ bool LoadROM(const char* path, const char* sram, bool direct)
CartInserted = true;
+ // TODO: support more fancy cart types (homebrew?, flashcarts, etc)
+ if (CartID & 0x08000000)
+ ROMCommandHandler = ROMCommand_RetailNAND;
+ else
+ ROMCommandHandler = ROMCommand_Retail;
+
u32 arm9base = *(u32*)&CartROM[0x20];
if (arm9base < 0x8000)
{
@@ -1144,7 +984,7 @@ bool LoadROM(const char* path, const char* sram, bool direct)
// save
printf("Save file: %s\n", sram);
- NDSCart_SRAM::LoadSave(sram);
+ NDSCart_SRAM::LoadSave(sram, romparams[1]);
return true;
}
@@ -1206,7 +1046,88 @@ void ROMPrepareData(u32 param)
NDS::CheckDMAs(0, 0x05);
}
-u32 sc_addr = 0;
+
+void ROMCommand_Retail(u8* cmd)
+{
+ switch (cmd[0])
+ {
+ case 0xB7:
+ {
+ u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
+ memset(DataOut, 0, DataOutLen);
+
+ if (((addr + DataOutLen - 1) >> 12) != (addr >> 12))
+ {
+ u32 len1 = 0x1000 - (addr & 0xFFF);
+ ReadROM_B7(addr, len1, 0);
+ ReadROM_B7(addr+len1, DataOutLen-len1, len1);
+ }
+ else
+ ReadROM_B7(addr, DataOutLen, 0);
+ }
+ break;
+
+ default:
+ printf("unknown retail cart command %02X\n", cmd[0]);
+ break;
+ }
+}
+
+void ROMCommand_RetailNAND(u8* cmd)
+{
+ switch (cmd[0])
+ {
+ case 0x94: // NAND init
+ {
+ // initial value: should have bit7 clear
+ NDSCart_SRAM::StatusReg = 0;
+
+ // Jam with the Band stores words 6-9 of this at 0x02131BB0
+ // it doesn't seem to use those anywhere later
+ for (u32 pos = 0; pos < DataOutLen; pos += 4)
+ *(u32*)&DataOut[pos] = 0;
+ }
+ break;
+
+ case 0xB2: // set savemem addr
+ {
+ NDSCart_SRAM::StatusReg |= 0x20;
+ }
+ break;
+
+ case 0xB7:
+ {
+ u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
+ memset(DataOut, 0, DataOutLen);
+
+ if (((addr + DataOutLen - 1) >> 12) != (addr >> 12))
+ {
+ u32 len1 = 0x1000 - (addr & 0xFFF);
+ ReadROM_B7(addr, len1, 0);
+ ReadROM_B7(addr+len1, DataOutLen-len1, len1);
+ }
+ else
+ ReadROM_B7(addr, DataOutLen, 0);
+ }
+ break;
+
+ case 0xD6: // NAND status
+ {
+ // status reg bits:
+ // * bit7: busy? error?
+ // * bit5: accessing savemem
+
+ for (u32 pos = 0; pos < DataOutLen; pos += 4)
+ *(u32*)&DataOut[pos] = NDSCart_SRAM::StatusReg * 0x01010101;
+ }
+ break;
+
+ default:
+ printf("unknown NAND command %02X %04Xn", cmd[0], DataOutLen);
+ break;
+ }
+}
+
void WriteROMCnt(u32 val)
{
@@ -1297,81 +1218,34 @@ void WriteROMCnt(u32 val)
if (CartInserted) CmdEncMode = 1;
break;
- case 0xB7:
+ default:
+ if (CmdEncMode == 1)
{
- u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
- memset(DataOut, 0, DataOutLen);
-
- if (((addr + DataOutLen - 1) >> 12) != (addr >> 12))
+ switch (cmd[0] & 0xF0)
{
- u32 len1 = 0x1000 - (addr & 0xFFF);
- ReadROM_B7(addr, len1, 0);
- ReadROM_B7(addr+len1, DataOutLen-len1, len1);
- }
- else
- ReadROM_B7(addr, DataOutLen, 0);
- }
- break;
-
+ case 0x40:
+ DataEncMode = 2;
+ break;
- // SUPERCARD EMULATION TEST
- // TODO: INTEGRATE BETTER!!!!
+ case 0x10:
+ for (u32 pos = 0; pos < DataOutLen; pos += 4)
+ *(u32*)&DataOut[pos] = CartID;
+ break;
- case 0x70: // init??? returns whether SDHC addressing should be used
- for (u32 pos = 0; pos < DataOutLen; pos += 4)
- *(u32*)&DataOut[pos] = 0;
- break;
-
- case 0x53: // set address for read
- sc_addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
- printf("SUPERCARD: read %08X\n", sc_addr);
- break;
-
- case 0x80: // read operation busy, I guess
- // TODO: make it take some time
- for (u32 pos = 0; pos < DataOutLen; pos += 4)
- *(u32*)&DataOut[pos] = 0;
- break;
-
- case 0x81: // read data
- {
- if (DataOutLen != 0x200)
- printf("SUPERCARD: BOGUS READ %d\n", DataOutLen);
-
- // TODO: this is really inefficient. just testing
- FILE* f = fopen("scsd.bin", "rb");
- fseek(f, sc_addr, SEEK_SET);
- fread(DataOut, 1, 0x200, f);
- fclose(f);
- }
- break;
-
- // SUPERCARD EMULATION TEST END
-
-
- default:
- switch (cmd[0] & 0xF0)
- {
- case 0x40:
- DataEncMode = 2;
- break;
-
- case 0x10:
- for (u32 pos = 0; pos < DataOutLen; pos += 4)
- *(u32*)&DataOut[pos] = CartID;
- break;
+ case 0x20:
+ {
+ u32 addr = (cmd[2] & 0xF0) << 8;
+ ReadROM(addr, 0x1000, 0);
+ }
+ break;
- case 0x20:
- {
- u32 addr = (cmd[2] & 0xF0) << 8;
- ReadROM(addr, 0x1000, 0);
+ case 0xA0:
+ CmdEncMode = 2;
+ break;
}
- break;
-
- case 0xA0:
- CmdEncMode = 2;
- break;
}
+ else if (ROMCommandHandler)
+ ROMCommandHandler(cmd);
break;
}
diff --git a/src/SPI.cpp b/src/SPI.cpp
index 3ffb20f..7c49555 100644
--- a/src/SPI.cpp
+++ b/src/SPI.cpp
@@ -22,6 +22,7 @@
#include "Config.h"
#include "NDS.h"
#include "SPI.h"
+#include "melon_fopen.h"
namespace SPI_Firmware
@@ -89,7 +90,7 @@ void Reset()
if (Firmware) delete[] Firmware;
Firmware = NULL;
- FILE* f = Config::GetConfigFile("firmware.bin", "rb");
+ FILE* f = melon_fopen_local("firmware.bin", "rb");
if (!f)
{
printf("firmware.bin not found\n");
@@ -129,7 +130,7 @@ void Reset()
// take a backup
char* firmbkp = "firmware.bin.bak";
- f = Config::GetConfigFile(firmbkp, "rb");
+ f = melon_fopen_local(firmbkp, "rb");
if (f) fclose(f);
else
{
@@ -324,7 +325,7 @@ void Write(u8 val, u32 hold)
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
{
- FILE* f = Config::GetConfigFile("firmware.bin", "r+b");
+ FILE* f = melon_fopen_local("firmware.bin", "r+b");
if (f)
{
u32 cutoff = 0x7FA00 & FirmwareMask;
@@ -452,6 +453,9 @@ u16 ConvResult;
u16 TouchX, TouchY;
+s16 MicBuffer[1024];
+int MicBufferLen;
+
bool Init()
{
@@ -468,6 +472,8 @@ void Reset()
Data = 0;
ConvResult = 0;
+
+ MicBufferLen = 0;
}
void DoSavestate(Savestate* file)
@@ -496,6 +502,19 @@ void SetTouchCoords(u16 x, u16 y)
TouchY <<= 4;
}
+void MicInputFrame(s16* data, int samples)
+{
+ if (!data)
+ {
+ MicBufferLen = 0;
+ return;
+ }
+
+ if (samples > 1024) samples = 1024;
+ memcpy(MicBuffer, data, samples*sizeof(s16));
+ MicBufferLen = samples;
+}
+
u8 Read()
{
return Data;
@@ -519,7 +538,31 @@ void Write(u8 val, u32 hold)
{
case 0x10: ConvResult = TouchY; break;
case 0x50: ConvResult = TouchX; break;
- case 0x60: ConvResult = 0x800; break; // TODO: mic
+
+ case 0x60:
+ {
+ if (MicBufferLen == 0)
+ ConvResult = 0x800;
+ else
+ {
+ // 560190 cycles per frame
+ u32 cyclepos = (u32)NDS::GetSysClockCycles(2);
+ u32 samplepos = (cyclepos * MicBufferLen) / 560190;
+ if (samplepos >= MicBufferLen) samplepos = MicBufferLen-1;
+ s16 sample = MicBuffer[samplepos];
+
+ // make it louder
+ //if (sample > 0x3FFF) sample = 0x7FFF;
+ //else if (sample < -0x4000) sample = -0x8000;
+ //else sample <<= 1;
+
+ // make it unsigned 12-bit
+ sample ^= 0x8000;
+ ConvResult = sample >> 4;
+ }
+ }
+ break;
+
default: ConvResult = 0xFFF; break;
}
diff --git a/src/SPI.h b/src/SPI.h
index f2f8e0a..87ed63d 100644
--- a/src/SPI.h
+++ b/src/SPI.h
@@ -36,6 +36,7 @@ namespace SPI_TSC
{
void SetTouchCoords(u16 x, u16 y);
+void MicInputFrame(s16* data, int samples);
}
diff --git a/src/SPU.cpp b/src/SPU.cpp
index 034e1aa..9862077 100644
--- a/src/SPU.cpp
+++ b/src/SPU.cpp
@@ -204,8 +204,8 @@ void Channel::FIFO_BufferData()
if (FIFOReadOffset >= totallen)
{
u32 repeatmode = (Cnt >> 27) & 0x3;
- if (repeatmode == 2) return; // one-shot sound, we're done
- if (repeatmode == 1) FIFOReadOffset = LoopPos;
+ if (repeatmode & 1) FIFOReadOffset = LoopPos;
+ else if (repeatmode & 2) return; // one-shot sound, we're done
}
u32 burstlen = 16;
@@ -255,7 +255,7 @@ void Channel::Start()
FIFOReadOffset = 0;
FIFOLevel = 0;
- // when starting a channel, two 4-word chunks are buffered
+ // when starting a channel, buffer data
if (((Cnt >> 29) & 0x3) != 3)
{
FIFO_BufferData();
@@ -269,18 +269,17 @@ void Channel::NextSample_PCM8()
if (Pos < 0) return;
if (Pos >= (LoopPos + Length))
{
- // TODO: what happens when mode 3 is used?
u32 repeat = (Cnt >> 27) & 0x3;
- if (repeat == 2)
+ if (repeat & 1)
+ {
+ Pos = LoopPos;
+ }
+ else if (repeat & 2)
{
CurSample = 0;
Cnt &= ~(1<<31);
return;
}
- else if (repeat == 1)
- {
- Pos = LoopPos;
- }
}
s8 val = FIFO_ReadData<s8>();
@@ -293,18 +292,17 @@ void Channel::NextSample_PCM16()
if (Pos < 0) return;
if ((Pos<<1) >= (LoopPos + Length))
{
- // TODO: what happens when mode 3 is used?
u32 repeat = (Cnt >> 27) & 0x3;
- if (repeat == 2)
+ if (repeat & 1)
+ {
+ Pos = LoopPos>>1;
+ }
+ else if (repeat & 2)
{
CurSample = 0;
Cnt &= ~(1<<31);
return;
}
- else if (repeat == 1)
- {
- Pos = LoopPos>>1;
- }
}
s16 val = FIFO_ReadData<s16>();
@@ -333,21 +331,20 @@ void Channel::NextSample_ADPCM()
if ((Pos>>1) >= (LoopPos + Length))
{
- // TODO: what happens when mode 3 is used?
u32 repeat = (Cnt >> 27) & 0x3;
- if (repeat == 2)
- {
- CurSample = 0;
- Cnt &= ~(1<<31);
- return;
- }
- else if (repeat == 1)
+ if (repeat & 1)
{
Pos = LoopPos<<1;
ADPCMVal = ADPCMValLoop;
ADPCMIndex = ADPCMIndexLoop;
ADPCMCurByte = FIFO_ReadData<u8>();
}
+ else if (repeat & 2)
+ {
+ CurSample = 0;
+ Cnt &= ~(1<<31);
+ return;
+ }
}
else
{
@@ -410,9 +407,6 @@ void Channel::NextSample_Noise()
template<u32 type>
void Channel::Run(s32* buf, u32 samples)
{
- for (u32 s = 0; s < samples; s++)
- buf[s] = 0;
-
if (!(Cnt & (1<<31))) return;
for (u32 s = 0; s < samples; s++)
diff --git a/src/SPU.h b/src/SPU.h
index d340a72..a7afbf7 100644
--- a/src/SPU.h
+++ b/src/SPU.h
@@ -121,6 +121,9 @@ public:
void DoRun(s32* buf, u32 samples)
{
+ for (u32 s = 0; s < samples; s++)
+ buf[s] = 0;
+
switch ((Cnt >> 29) & 0x3)
{
case 0: Run<0>(buf, samples); break;
diff --git a/src/Savestate.h b/src/Savestate.h
index fa01f6b..be96d78 100644
--- a/src/Savestate.h
+++ b/src/Savestate.h
@@ -22,7 +22,7 @@
#include <stdio.h>
#include "types.h"
-#define SAVESTATE_MAJOR 1
+#define SAVESTATE_MAJOR 3
#define SAVESTATE_MINOR 0
class Savestate
@@ -48,6 +48,13 @@ public:
void VarArray(void* data, u32 len);
+ bool IsAtleastVersion(u32 major, u32 minor)
+ {
+ if (VersionMajor > major) return true;
+ if (VersionMajor == major && VersionMinor >= minor) return true;
+ return false;
+ }
+
private:
FILE* file;
};
diff --git a/src/libui_sdl/DlgAudioSettings.cpp b/src/libui_sdl/DlgAudioSettings.cpp
new file mode 100644
index 0000000..b02b474
--- /dev/null
+++ b/src/libui_sdl/DlgAudioSettings.cpp
@@ -0,0 +1,183 @@
+/*
+ Copyright 2016-2019 StapleButter
+
+ 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libui/ui.h"
+
+#include "../types.h"
+#include "../Config.h"
+
+#include "DlgAudioSettings.h"
+
+
+void MicLoadWav(char* path);
+
+
+namespace DlgAudioSettings
+{
+
+bool opened;
+uiWindow* win;
+
+uiSlider* slVolume;
+uiRadioButtons* rbMicInputType;
+uiEntry* txMicWavPath;
+
+int oldvolume;
+
+
+int OnCloseWindow(uiWindow* window, void* blarg)
+{
+ opened = false;
+ return 1;
+}
+
+void OnVolumeChanged(uiSlider* slider, void* blarg)
+{
+ Config::AudioVolume = uiSliderValue(slVolume);
+}
+
+void OnMicWavBrowse(uiButton* btn, void* blarg)
+{
+ char* file = uiOpenFile(win, "WAV file (*.wav)|*.wav|Any file|*.*", NULL);
+ if (!file)
+ {
+ return;
+ }
+
+ uiEntrySetText(txMicWavPath, file);
+ uiFreeText(file);
+}
+
+void OnCancel(uiButton* btn, void* blarg)
+{
+ Config::AudioVolume = oldvolume;
+
+ uiControlDestroy(uiControl(win));
+ opened = false;
+}
+
+void OnOk(uiButton* btn, void* blarg)
+{
+ Config::AudioVolume = uiSliderValue(slVolume);
+ Config::MicInputType = uiRadioButtonsSelected(rbMicInputType);
+
+ char* wavpath = uiEntryText(txMicWavPath);
+ strncpy(Config::MicWavPath, wavpath, 511);
+ uiFreeText(wavpath);
+
+ Config::Save();
+
+ if (Config::MicInputType == 3) MicLoadWav(Config::MicWavPath);
+
+ uiControlDestroy(uiControl(win));
+ opened = false;
+}
+
+void Open()
+{
+ if (opened)
+ {
+ uiControlSetFocus(uiControl(win));
+ return;
+ }
+
+ opened = true;
+ win = uiNewWindow("Audio settings - melonDS", 400, 100, 0, 0);
+ uiWindowSetMargined(win, 1);
+ uiWindowOnClosing(win, OnCloseWindow, NULL);
+
+ uiBox* top = uiNewVerticalBox();
+ uiWindowSetChild(win, uiControl(top));
+ uiBoxSetPadded(top, 1);
+
+ {
+ uiGroup* grp = uiNewGroup("Audio output");
+ uiBoxAppend(top, uiControl(grp), 0);
+ uiGroupSetMargined(grp, 1);
+
+ uiBox* in_ctrl = uiNewVerticalBox();
+ uiGroupSetChild(grp, uiControl(in_ctrl));
+
+ uiLabel* label_vol = uiNewLabel("Volume:");
+ uiBoxAppend(in_ctrl, uiControl(label_vol), 0);
+
+ slVolume = uiNewSlider(0, 256);
+ uiSliderOnChanged(slVolume, OnVolumeChanged, NULL);
+ uiBoxAppend(in_ctrl, uiControl(slVolume), 0);
+ }
+
+ {
+ uiGroup* grp = uiNewGroup("Microphone input");
+ uiBoxAppend(top, uiControl(grp), 0);
+ uiGroupSetMargined(grp, 1);
+
+ uiBox* in_ctrl = uiNewVerticalBox();
+ uiGroupSetChild(grp, uiControl(in_ctrl));
+
+ rbMicInputType = uiNewRadioButtons();
+ uiRadioButtonsAppend(rbMicInputType, "None");
+ uiRadioButtonsAppend(rbMicInputType, "Microphone");
+ uiRadioButtonsAppend(rbMicInputType, "White noise");
+ uiRadioButtonsAppend(rbMicInputType, "WAV file:");
+ uiBoxAppend(in_ctrl, uiControl(rbMicInputType), 0);
+
+ uiBox* path_box = uiNewHorizontalBox();
+ uiBoxAppend(in_ctrl, uiControl(path_box), 0);
+
+ txMicWavPath = uiNewEntry();
+ uiBoxAppend(path_box, uiControl(txMicWavPath), 1);
+
+ uiButton* path_browse = uiNewButton("...");
+ uiButtonOnClicked(path_browse, OnMicWavBrowse, NULL);
+ uiBoxAppend(path_box, uiControl(path_browse), 0);
+ }
+
+ {
+ uiBox* in_ctrl = uiNewHorizontalBox();
+ uiBoxSetPadded(in_ctrl, 1);
+ uiBoxAppend(top, uiControl(in_ctrl), 0);
+
+ uiLabel* dummy = uiNewLabel("");
+ uiBoxAppend(in_ctrl, uiControl(dummy), 1);
+
+ uiButton* btncancel = uiNewButton("Cancel");
+ uiButtonOnClicked(btncancel, OnCancel, NULL);
+ uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
+
+ uiButton* btnok = uiNewButton("Ok");
+ uiButtonOnClicked(btnok, OnOk, NULL);
+ uiBoxAppend(in_ctrl, uiControl(btnok), 0);
+ }
+
+ if (Config::AudioVolume < 0) Config::AudioVolume = 0;
+ else if (Config::AudioVolume > 256) Config::AudioVolume = 256;
+
+ oldvolume = Config::AudioVolume;
+
+ uiSliderSetValue(slVolume, Config::AudioVolume);
+ uiRadioButtonsSetSelected(rbMicInputType, Config::MicInputType);
+ uiEntrySetText(txMicWavPath, Config::MicWavPath);
+
+ uiControlShow(uiControl(win));
+}
+
+}
diff --git a/src/CP15.h b/src/libui_sdl/DlgAudioSettings.h
index 1b92191..2333967 100644
--- a/src/CP15.h
+++ b/src/libui_sdl/DlgAudioSettings.h
@@ -16,31 +16,14 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
-#ifndef CP15_H
-#define CP15_H
+#ifndef DLGAUDIOSETTINGS_H
+#define DLGAUDIOSETTINGS_H
-namespace CP15
+namespace DlgAudioSettings
{
-void Reset();
-
-void DoSavestate(Savestate* file);
-
-void UpdateDTCMSetting();
-void UpdateITCMSetting();
-
-void Write(u32 id, u32 val);
-u32 Read(u32 id);
-
-bool HandleCodeRead16(u32 addr, u16* val);
-bool HandleCodeRead32(u32 addr, u32* val);
-bool HandleDataRead8(u32 addr, u8* val, u32 forceuser=0);
-bool HandleDataRead16(u32 addr, u16* val, u32 forceuser=0);
-bool HandleDataRead32(u32 addr, u32* val, u32 forceuser=0);
-bool HandleDataWrite8(u32 addr, u8 val, u32 forceuser=0);
-bool HandleDataWrite16(u32 addr, u16 val, u32 forceuser=0);
-bool HandleDataWrite32(u32 addr, u32 val, u32 forceuser=0);
+void Open();
}
-#endif
+#endif // DLGAUDIOSETTINGS_H
diff --git a/src/libui_sdl/DlgEmuSettings.cpp b/src/libui_sdl/DlgEmuSettings.cpp
index 77bb18e..42c95b8 100644
--- a/src/libui_sdl/DlgEmuSettings.cpp
+++ b/src/libui_sdl/DlgEmuSettings.cpp
@@ -33,6 +33,7 @@ void ApplyNewSettings();
namespace DlgEmuSettings
{
+bool opened;
uiWindow* win;
uiCheckbox* cbDirectBoot;
@@ -42,12 +43,14 @@ uiCheckbox* cbBindAnyAddr;
int OnCloseWindow(uiWindow* window, void* blarg)
{
+ opened = false;
return 1;
}
void OnCancel(uiButton* btn, void* blarg)
{
uiControlDestroy(uiControl(win));
+ opened = false;
}
void OnOk(uiButton* btn, void* blarg)
@@ -59,12 +62,20 @@ void OnOk(uiButton* btn, void* blarg)
Config::Save();
uiControlDestroy(uiControl(win));
+ opened = false;
ApplyNewSettings();
}
void Open()
{
+ if (opened)
+ {
+ uiControlSetFocus(uiControl(win));
+ return;
+ }
+
+ opened = true;
win = uiNewWindow("Emu settings - melonDS", 300, 200, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
diff --git a/src/libui_sdl/DlgInputConfig.cpp b/src/libui_sdl/DlgInputConfig.cpp
index 6496307..287d7e3 100644
--- a/src/libui_sdl/DlgInputConfig.cpp
+++ b/src/libui_sdl/DlgInputConfig.cpp
@@ -35,19 +35,33 @@ extern SDL_Joystick* Joystick;
namespace DlgInputConfig
{
-uiWindow* win;
+typedef struct
+{
+ int type;
+ uiWindow* win;
+
+ uiAreaHandler areahandler;
+ uiArea* keypresscatcher;
+
+ int numkeys;
+ int keymap[32];
+ int joymap[32];
+
+ int pollid;
+ uiButton* pollbtn;
-uiAreaHandler areahandler;
-uiArea* keypresscatcher;
+} InputDlgData;
-int keyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 3, 2};
-char keylabels[12][8] = {"A:", "B:", "Select:", "Start:", "Right:", "Left:", "Up:", "Down:", "R:", "L:", "X:", "Y:"};
-int keymap[12];
-int joymap[12];
+int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 3, 2};
+char dskeylabels[12][8] = {"A:", "B:", "Select:", "Start:", "Right:", "Left:", "Up:", "Down:", "R:", "L:", "X:", "Y:"};
-int pollid;
-uiButton* pollbtn;
+int identity[32] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+
+char hotkeylabels[HK_MAX][32] = {"Close/open lid:", "Microphone:"};
+
+int openedmask;
+InputDlgData inputdlg[2];
void JoyMappingName(int id, char* str)
@@ -97,7 +111,9 @@ void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height)
int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
{
- if (pollid < 0)
+ InputDlgData* dlg = (InputDlgData*)uiControl(area)->UserData;
+
+ if (dlg->pollid < 0)
return 0;
if (evt->Scancode == 0x38) // ALT
@@ -105,27 +121,27 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
if (evt->Modifiers == 0x2) // ALT+key
return 0;
- if (pollid > 12)
+ if (dlg->pollid > 12)
{
- if (pollid < 0x100) return 0;
- int id = pollid & 0xFF;
+ if (dlg->pollid < 0x100) return 0;
+ int id = dlg->pollid & 0xFF;
if (id > 12) return 0;
if (evt->Scancode != 0x1) // ESC
{
if (evt->Scancode == 0xE) // backspace
- joymap[id] = -1;
+ dlg->joymap[id] = -1;
else
return 1;
}
char keyname[16];
- JoyMappingName(joymap[id], keyname);
- uiButtonSetText(pollbtn, keyname);
- uiControlEnable(uiControl(pollbtn));
+ JoyMappingName(dlg->joymap[id], keyname);
+ uiButtonSetText(dlg->pollbtn, keyname);
+ uiControlEnable(uiControl(dlg->pollbtn));
- pollid = -1;
+ dlg->pollid = -1;
- uiControlSetFocus(uiControl(pollbtn));
+ uiControlSetFocus(uiControl(dlg->pollbtn));
return 1;
}
@@ -134,16 +150,16 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
{
// set key.
if (evt->Scancode != 0x1) // ESC
- keymap[pollid] = evt->Scancode;
+ dlg->keymap[dlg->pollid] = evt->Scancode;
- char* keyname = uiKeyName(keymap[pollid]);
- uiButtonSetText(pollbtn, keyname);
- uiControlEnable(uiControl(pollbtn));
+ char* keyname = uiKeyName(dlg->keymap[dlg->pollid]);
+ uiButtonSetText(dlg->pollbtn, keyname);
+ uiControlEnable(uiControl(dlg->pollbtn));
uiFreeText(keyname);
- pollid = -1;
+ dlg->pollid = -1;
- uiControlSetFocus(uiControl(pollbtn));
+ uiControlSetFocus(uiControl(dlg->pollbtn));
}
return 1;
@@ -151,8 +167,10 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
Uint32 JoyPoll(Uint32 interval, void* param)
{
- if (pollid < 0x100) return 0;
- int id = pollid & 0xFF;
+ InputDlgData* dlg = (InputDlgData*)param;
+
+ if (dlg->pollid < 0x100) return 0;
+ int id = dlg->pollid & 0xFF;
if (id > 12) return 0;
SDL_JoystickUpdate();
@@ -165,16 +183,16 @@ Uint32 JoyPoll(Uint32 interval, void* param)
{
if (SDL_JoystickGetButton(joy, i))
{
- joymap[id] = i;
+ dlg->joymap[id] = i;
char keyname[16];
- JoyMappingName(joymap[id], keyname);
- uiButtonSetText(pollbtn, keyname);
- uiControlEnable(uiControl(pollbtn));
+ JoyMappingName(dlg->joymap[id], keyname);
+ uiButtonSetText(dlg->pollbtn, keyname);
+ uiControlEnable(uiControl(dlg->pollbtn));
- pollid = -1;
+ dlg->pollid = -1;
- uiControlSetFocus(uiControl(pollbtn));
+ uiControlSetFocus(uiControl(dlg->pollbtn));
return 0;
}
}
@@ -187,16 +205,16 @@ Uint32 JoyPoll(Uint32 interval, void* param)
else if (blackhat & 0x4) blackhat = 0x4;
else blackhat = 0x8;
- joymap[id] = 0x100 | blackhat;
+ dlg->joymap[id] = 0x100 | blackhat;
char keyname[16];
- JoyMappingName(joymap[id], keyname);
- uiButtonSetText(pollbtn, keyname);
- uiControlEnable(uiControl(pollbtn));
+ JoyMappingName(dlg->joymap[id], keyname);
+ uiButtonSetText(dlg->pollbtn, keyname);
+ uiControlEnable(uiControl(dlg->pollbtn));
- pollid = -1;
+ dlg->pollid = -1;
- uiControlSetFocus(uiControl(pollbtn));
+ uiControlSetFocus(uiControl(dlg->pollbtn));
return 0;
}
@@ -206,98 +224,147 @@ Uint32 JoyPoll(Uint32 interval, void* param)
void OnKeyStartConfig(uiButton* btn, void* data)
{
- if (pollid != -1)
+ InputDlgData* dlg = (InputDlgData*)uiControl(btn)->UserData;
+
+ if (dlg->pollid != -1)
{
// TODO: handle this better?
- if (pollid <= 12)
- uiControlSetFocus(uiControl(keypresscatcher));
+ if (dlg->pollid <= 12)
+ uiControlSetFocus(uiControl(dlg->keypresscatcher));
return;
}
int id = *(int*)data;
- pollid = id;
- pollbtn = btn;
+ dlg->pollid = id;
+ dlg->pollbtn = btn;
uiButtonSetText(btn, "[press key]");
uiControlDisable(uiControl(btn));
- uiControlSetFocus(uiControl(keypresscatcher));
+ uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
void OnJoyStartConfig(uiButton* btn, void* data)
{
- if (pollid != -1)
+ InputDlgData* dlg = (InputDlgData*)uiControl(btn)->UserData;
+
+ if (dlg->pollid != -1)
{
// TODO: handle this better?
- if (pollid <= 12)
- uiControlSetFocus(uiControl(keypresscatcher));
+ if (dlg->pollid <= 12)
+ uiControlSetFocus(uiControl(dlg->keypresscatcher));
return;
}
int id = *(int*)data;
- pollid = id | 0x100;
- pollbtn = btn;
+ dlg->pollid = id | 0x100;
+ dlg->pollbtn = btn;
uiButtonSetText(btn, "[press button]");
uiControlDisable(uiControl(btn));
- SDL_AddTimer(100, JoyPoll, NULL);
- uiControlSetFocus(uiControl(keypresscatcher));
+ SDL_AddTimer(100, JoyPoll, dlg);
+ uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
+ InputDlgData* dlg = (InputDlgData*)(uiControl(window)->UserData);
+ openedmask &= ~(1 << dlg->type);
return 1;
}
void OnGetFocus(uiWindow* window, void* blarg)
{
- if (pollid >= 0)
- uiControlSetFocus(uiControl(keypresscatcher));
+ InputDlgData* dlg = (InputDlgData*)(uiControl(window)->UserData);
+
+ if (dlg->pollid >= 0)
+ uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
void OnLoseFocus(uiWindow* window, void* blarg)
{
}
-void OnCancel(uiButton* btn, void* blarg)
+void OnCancel(uiButton* btn, void* data)
{
- uiControlDestroy(uiControl(win));
+ InputDlgData* dlg = (InputDlgData*)data;
+
+ uiControlDestroy(uiControl(dlg->win));
+ openedmask &= ~(1 << dlg->type);
}
-void OnOk(uiButton* btn, void* blarg)
+void OnOk(uiButton* btn, void* data)
{
- memcpy(Config::KeyMapping, keymap, sizeof(int)*12);
- memcpy(Config::JoyMapping, joymap, sizeof(int)*12);
+ InputDlgData* dlg = (InputDlgData*)data;
+
+ if (dlg->type == 0)
+ {
+ memcpy(Config::KeyMapping, dlg->keymap, sizeof(int)*12);
+ memcpy(Config::JoyMapping, dlg->joymap, sizeof(int)*12);
+ }
+ else if (dlg->type == 1)
+ {
+ memcpy(Config::HKKeyMapping, dlg->keymap, sizeof(int)*HK_MAX);
+ memcpy(Config::HKJoyMapping, dlg->joymap, sizeof(int)*HK_MAX);
+ }
Config::Save();
- uiControlDestroy(uiControl(win));
+ uiControlDestroy(uiControl(dlg->win));
+ openedmask &= ~(1 << dlg->type);
}
-void Open()
+void Open(int type)
{
- pollid = -1;
+ InputDlgData* dlg = &inputdlg[type];
+
+ int mask = 1 << type;
+ if (openedmask & mask)
+ {
+ uiControlSetFocus(uiControl(dlg->win));
+ return;
+ }
+
+ openedmask |= mask;
+
+ dlg->type = type;
+ dlg->pollid = -1;
+
+ if (type == 0)
+ {
+ dlg->numkeys = 12;
+ memcpy(dlg->keymap, Config::KeyMapping, sizeof(int)*12);
+ memcpy(dlg->joymap, Config::JoyMapping, sizeof(int)*12);
+
+ dlg->win = uiNewWindow("Input config - melonDS", 600, 100, 0, 0);
+ }
+ else if (type == 1)
+ {
+ dlg->numkeys = HK_MAX;
+ memcpy(dlg->keymap, Config::HKKeyMapping, sizeof(int)*HK_MAX);
+ memcpy(dlg->joymap, Config::HKJoyMapping, sizeof(int)*HK_MAX);
+
+ dlg->win = uiNewWindow("Hotkey config - melonDS", 600, 100, 0, 0);
+ }
- memcpy(keymap, Config::KeyMapping, sizeof(int)*12);
- memcpy(joymap, Config::JoyMapping, sizeof(int)*12);
+ uiControl(dlg->win)->UserData = dlg;
- win = uiNewWindow("Input config - melonDS", 600, 400, 0, 0);
- uiWindowSetMargined(win, 1);
- uiWindowOnClosing(win, OnCloseWindow, NULL);
- uiWindowOnGetFocus(win, OnGetFocus, NULL);
- uiWindowOnLoseFocus(win, OnLoseFocus, NULL);
+ uiWindowSetMargined(dlg->win, 1);
+ uiWindowOnClosing(dlg->win, OnCloseWindow, NULL);
+ uiWindowOnGetFocus(dlg->win, OnGetFocus, NULL);
+ uiWindowOnLoseFocus(dlg->win, OnLoseFocus, NULL);
- areahandler.Draw = OnAreaDraw;
- areahandler.MouseEvent = OnAreaMouseEvent;
- areahandler.MouseCrossed = OnAreaMouseCrossed;
- areahandler.DragBroken = OnAreaDragBroken;
- areahandler.KeyEvent = OnAreaKeyEvent;
- areahandler.Resize = OnAreaResize;
+ dlg->areahandler.Draw = OnAreaDraw;
+ dlg->areahandler.MouseEvent = OnAreaMouseEvent;
+ dlg->areahandler.MouseCrossed = OnAreaMouseCrossed;
+ dlg->areahandler.DragBroken = OnAreaDragBroken;
+ dlg->areahandler.KeyEvent = OnAreaKeyEvent;
+ dlg->areahandler.Resize = OnAreaResize;
uiBox* top = uiNewVerticalBox();
- uiWindowSetChild(win, uiControl(top));
+ uiWindowSetChild(dlg->win, uiControl(top));
uiControlHide(uiControl(top));
{
@@ -312,19 +379,20 @@ void Open()
const int width = 120;
- for (int i = 0; i < 12; i++)
+ for (int i = 0; i < dlg->numkeys; i++)
{
- int j = keyorder[i];
+ int j = (type==0) ? dskeyorder[i] : i;
- uiLabel* label = uiNewLabel(keylabels[j]);
+ uiLabel* label = uiNewLabel((type==0) ? dskeylabels[j] : hotkeylabels[j]);
uiGridAppend(b_key, uiControl(label), 0, i, 1, 1, 1, uiAlignStart, 1, uiAlignCenter);
uiControlSetMinSize(uiControl(label), width, 1);
- char* keyname = uiKeyName(Config::KeyMapping[j]);
+ char* keyname = uiKeyName(dlg->keymap[j]);
uiButton* btn = uiNewButton(keyname);
+ uiControl(btn)->UserData = dlg;
uiGridAppend(b_key, uiControl(btn), 1, i, 1, 1, 1, uiAlignFill, 1, uiAlignCenter);
- uiButtonOnClicked(btn, OnKeyStartConfig, &keyorder[i]);
+ uiButtonOnClicked(btn, OnKeyStartConfig, (type==0) ? &dskeyorder[i] : &identity[i]);
uiControlSetMinSize(uiControl(btn), width, 1);
uiFreeText(keyname);
@@ -335,20 +403,21 @@ void Open()
uiGrid* b_joy = uiNewGrid();
uiGroupSetChild(g_joy, uiControl(b_joy));
- for (int i = 0; i < 12; i++)
+ for (int i = 0; i < dlg->numkeys; i++)
{
- int j = keyorder[i];
+ int j = (type==0) ? dskeyorder[i] : i;
- uiLabel* label = uiNewLabel(keylabels[j]);
+ uiLabel* label = uiNewLabel((type==0) ? dskeylabels[j] : hotkeylabels[j]);
uiGridAppend(b_joy, uiControl(label), 0, i, 1, 1, 1, uiAlignStart, 1, uiAlignCenter);
uiControlSetMinSize(uiControl(label), width, 1);
char keyname[16];
- JoyMappingName(Config::JoyMapping[j], keyname);
+ JoyMappingName(dlg->joymap[j], keyname);
uiButton* btn = uiNewButton(keyname);
+ uiControl(btn)->UserData = dlg;
uiGridAppend(b_joy, uiControl(btn), 1, i, 1, 1, 1, uiAlignFill, 1, uiAlignCenter);
- uiButtonOnClicked(btn, OnJoyStartConfig, &keyorder[i]);
+ uiButtonOnClicked(btn, OnJoyStartConfig, (type==0) ? &dskeyorder[i] : &identity[i]);
uiControlSetMinSize(uiControl(btn), width, 1);
}
}
@@ -364,21 +433,22 @@ void Open()
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
- keypresscatcher = uiNewArea(&areahandler);
- uiBoxAppend(in_ctrl, uiControl(keypresscatcher), 0);
+ dlg->keypresscatcher = uiNewArea(&dlg->areahandler);
+ uiControl(dlg->keypresscatcher)->UserData = dlg;
+ uiBoxAppend(in_ctrl, uiControl(dlg->keypresscatcher), 0);
uiButton* btncancel = uiNewButton("Cancel");
- uiButtonOnClicked(btncancel, OnCancel, NULL);
+ uiButtonOnClicked(btncancel, OnCancel, dlg);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
- uiButtonOnClicked(btnok, OnOk, NULL);
+ uiButtonOnClicked(btnok, OnOk, dlg);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
uiControlShow(uiControl(top));
- uiControlShow(uiControl(win));
+ uiControlShow(uiControl(dlg->win));
}
}
diff --git a/src/libui_sdl/DlgInputConfig.h b/src/libui_sdl/DlgInputConfig.h
index c044016..2b0b6cb 100644
--- a/src/libui_sdl/DlgInputConfig.h
+++ b/src/libui_sdl/DlgInputConfig.h
@@ -22,7 +22,7 @@
namespace DlgInputConfig
{
-void Open();
+void Open(int type);
}
diff --git a/src/libui_sdl/Platform.cpp b/src/libui_sdl/Platform.cpp
index 0c29f6b..733114e 100644
--- a/src/libui_sdl/Platform.cpp
+++ b/src/libui_sdl/Platform.cpp
@@ -95,7 +95,9 @@ const char* PCapLibNames[] =
// TODO: name for npcap in non-WinPCap mode
"wpcap.dll",
#else
- // TODO: Linux lib names
+ // Linux lib names
+ "libpcap.so.1",
+ "libpcap.so",
#endif
NULL
};
diff --git a/src/libui_sdl/libui/ui.h b/src/libui_sdl/libui/ui.h
index e5a866d..1af8f59 100644
--- a/src/libui_sdl/libui/ui.h
+++ b/src/libui_sdl/libui/ui.h
@@ -76,6 +76,8 @@ struct uiControl {
void (*SetMinSize)(uiControl*, int, int);
int MinWidth, MinHeight;
+
+ void* UserData;
};
// TOOD add argument names to all arguments
#define uiControl(this) ((uiControl *) (this))
diff --git a/src/libui_sdl/libui/unix/area.c b/src/libui_sdl/libui/unix/area.c
index 40f8624..2da9bab 100644
--- a/src/libui_sdl/libui/unix/area.c
+++ b/src/libui_sdl/libui/unix/area.c
@@ -523,16 +523,26 @@ char* uiKeyName(int scancode)
{
scancode = scancode_normal2unix(scancode);
+ char* ret;
guint* keyvals; int num;
- GdkKeymap* keymap = gdk_keymap_get_default();
- gdk_keymap_get_entries_for_keycode(keymap, scancode, NULL, &keyvals, &num);
-
- // TODO: pick smarter??
- int keyval = keyvals[0];
-
- g_free(keyvals);
+ GdkKeymap* keymap = gdk_keymap_get_for_display(gdk_display_get_default());
+ if (gdk_keymap_get_entries_for_keycode(keymap, scancode, NULL, &keyvals, &num))
+ {
+ // TODO: pick smarter??
+ int keyval = keyvals[0];
+
+ g_free(keyvals);
+
+ ret = gdk_keyval_name(keyval);
+ }
+ else
+ {
+ char tmp[16];
+ sprintf(tmp, "#%03X", scancode);
+ ret = tmp;
+ }
- return uiUnixStrdupText(gdk_keyval_name(keyval));
+ return uiUnixStrdupText(ret);
}
enum {
diff --git a/src/libui_sdl/libui/unix/stddialogs.c b/src/libui_sdl/libui/unix/stddialogs.c
index b793d06..d2b89b9 100644
--- a/src/libui_sdl/libui/unix/stddialogs.c
+++ b/src/libui_sdl/libui/unix/stddialogs.c
@@ -70,6 +70,9 @@ static char *filedialog(GtkWindow *parent, GtkFileChooserAction mode, const gcha
gtk_file_chooser_set_show_hidden(fc, TRUE);
gtk_file_chooser_set_do_overwrite_confirmation(fc, TRUE);
gtk_file_chooser_set_create_folders(fc, TRUE);
+ if (initpath && strlen(initpath)>0)
+ gtk_file_chooser_set_current_folder(fc, initpath);
+
response = gtk_dialog_run(GTK_DIALOG(fcd));
if (response != GTK_RESPONSE_ACCEPT) {
gtk_widget_destroy(fcd);
diff --git a/src/libui_sdl/libui/unix/window.c b/src/libui_sdl/libui/unix/window.c
index a3dde76..04630bc 100644
--- a/src/libui_sdl/libui/unix/window.c
+++ b/src/libui_sdl/libui/unix/window.c
@@ -145,11 +145,16 @@ static void uiWindowShow(uiControl *c)
gtk_window_resize(w->window, width, height);
}
+static void uiWindowSetFocus(uiControl* c)
+{
+ gtk_window_present(GTK_WINDOW(uiWindow(c)->widget));
+}
+
uiUnixControlDefaultHide(uiWindow)
uiUnixControlDefaultEnabled(uiWindow)
uiUnixControlDefaultEnable(uiWindow)
uiUnixControlDefaultDisable(uiWindow)
-uiUnixControlDefaultSetFocus(uiWindow)
+//uiUnixControlDefaultSetFocus(uiWindow)
uiUnixControlDefaultSetMinSize(uiWindow)
// TODO?
uiUnixControlDefaultSetContainer(uiWindow)
diff --git a/src/libui_sdl/libui/windows/button.cpp b/src/libui_sdl/libui/windows/button.cpp
index b83d6ec..aa34bfc 100644
--- a/src/libui_sdl/libui/windows/button.cpp
+++ b/src/libui_sdl/libui/windows/button.cpp
@@ -6,6 +6,9 @@ struct uiButton {
HWND hwnd;
void (*onClicked)(uiButton *, void *);
void *onClickedData;
+
+ SIZE idealSize;
+ int idealSizeCached;
};
static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult)
@@ -41,6 +44,13 @@ static void uiButtonMinimumSize(uiWindowsControl *c, int *width, int *height)
uiWindowsSizing sizing;
int y;
+ if (b->idealSizeCached)
+ {
+ *width = b->idealSize.cx;
+ *height = b->idealSize.cy;
+ return;
+ }
+
// try the comctl32 version 6 way
size.cx = 0; // explicitly ask for ideal size
size.cy = 0;
@@ -48,6 +58,9 @@ static void uiButtonMinimumSize(uiWindowsControl *c, int *width, int *height)
*width = size.cx;
if (*width < buttonMinWidth) *width = buttonMinWidth;
*height = size.cy;
+ b->idealSize.cx = *width;
+ b->idealSize.cy = *height;
+ b->idealSizeCached = true;
return;
}
@@ -60,6 +73,9 @@ static void uiButtonMinimumSize(uiWindowsControl *c, int *width, int *height)
uiWindowsGetSizing(b->hwnd, &sizing);
uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &y);
*height = y;
+ b->idealSize.cx = *width;
+ b->idealSize.cy = *height;
+ b->idealSizeCached = true;
}
static void defaultOnClicked(uiButton *b, void *data)
@@ -75,6 +91,7 @@ char *uiButtonText(uiButton *b)
void uiButtonSetText(uiButton *b, const char *text)
{
uiWindowsSetWindowText(b->hwnd, text);
+ b->idealSizeCached = 0;
// changing the text might necessitate a change in the button's size
uiWindowsControlMinimumSizeChanged(uiWindowsControl(b));
}
@@ -103,5 +120,7 @@ uiButton *uiNewButton(const char *text)
uiWindowsRegisterWM_COMMANDHandler(b->hwnd, onWM_COMMAND, uiControl(b));
uiButtonOnClicked(b, defaultOnClicked, NULL);
+ b->idealSizeCached = 0;
+
return b;
}
diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp
index 4c19314..b30c81b 100644
--- a/src/libui_sdl/main.cpp
+++ b/src/libui_sdl/main.cpp
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
+#include <string.h>
#include <SDL2/SDL.h>
#include "libui/ui.h"
@@ -30,6 +31,7 @@
#include "DlgEmuSettings.h"
#include "DlgInputConfig.h"
+#include "DlgAudioSettings.h"
#include "../NDS.h"
#include "../GPU.h"
@@ -52,6 +54,9 @@ const int kScreenLayout[3] = {0, 1, 2};
const int kScreenSizing[4] = {0, 1, 2, 3};
+char* EmuDirectory;
+
+
uiWindow* MainWindow;
uiArea* MainDrawArea;
@@ -104,8 +109,18 @@ uiDrawMatrix BottomScreenTrans;
bool Touching = false;
u32 KeyInputMask;
+bool LidCommand, LidStatus;
SDL_Joystick* Joystick;
+u32 MicBufferLength = 2048;
+s16 MicBuffer[2048];
+u32 MicBufferReadPos, MicBufferWritePos;
+
+u32 MicWavLength;
+s16* MicWavBuffer;
+
+u32 MicCommand;
+
void SetupScreenRects(int width, int height);
@@ -116,7 +131,7 @@ void GetSavestateName(int slot, char* filename, int len);
-bool FileExists(char* name)
+bool FileExists(const char* name)
{
FILE* f = melon_fopen(name, "rb");
if (!f) return false;
@@ -124,6 +139,92 @@ bool FileExists(char* name)
return true;
}
+bool LocalFileExists(const char* name)
+{
+ FILE* f = melon_fopen_local(name, "rb");
+ if (!f) return false;
+ fclose(f);
+ return true;
+}
+
+
+void MicLoadWav(char* name)
+{
+ SDL_AudioSpec format;
+ memset(&format, 0, sizeof(SDL_AudioSpec));
+
+ if (MicWavBuffer) delete[] MicWavBuffer;
+ MicWavBuffer = NULL;
+ MicWavLength = 0;
+
+ u8* buf;
+ u32 len;
+ if (!SDL_LoadWAV(name, &format, &buf, &len))
+ return;
+
+ const u64 dstfreq = 44100;
+
+ if (format.format == AUDIO_S16 || format.format == AUDIO_U16)
+ {
+ int srcinc = format.channels;
+ len /= 2;
+
+ MicWavLength = (len * dstfreq) / format.freq;
+ if (MicWavLength < 735) MicWavLength = 735;
+ MicWavBuffer = new s16[MicWavLength];
+
+ float res_incr = len / (float)MicWavLength;
+ float res_timer = 0;
+ int res_pos = 0;
+
+ for (int i = 0; i < MicWavLength; i++)
+ {
+ u16 val = ((u16*)buf)[res_pos];
+ if (SDL_AUDIO_ISUNSIGNED(format.format)) val ^= 0x8000;
+
+ MicWavBuffer[i] = val;
+
+ res_timer += res_incr;
+ while (res_timer >= 1.0)
+ {
+ res_timer -= 1.0;
+ res_pos += srcinc;
+ }
+ }
+ }
+ else if (format.format == AUDIO_S8 || format.format == AUDIO_U8)
+ {
+ int srcinc = format.channels;
+
+ MicWavLength = (len * dstfreq) / format.freq;
+ if (MicWavLength < 735) MicWavLength = 735;
+ MicWavBuffer = new s16[MicWavLength];
+
+ float res_incr = len / (float)MicWavLength;
+ float res_timer = 0;
+ int res_pos = 0;
+
+ for (int i = 0; i < MicWavLength; i++)
+ {
+ u16 val = buf[res_pos] << 8;
+ if (SDL_AUDIO_ISUNSIGNED(format.format)) val ^= 0x8000;
+
+ MicWavBuffer[i] = val;
+
+ res_timer += res_incr;
+ while (res_timer >= 1.0)
+ {
+ res_timer -= 1.0;
+ res_pos += srcinc;
+ }
+ }
+ }
+ else
+ printf("bad WAV format %08X\n", format.format);
+
+ SDL_FreeWAV(buf);
+}
+
void UpdateWindowTitle(void* data)
{
@@ -136,7 +237,6 @@ void AudioCallback(void* data, Uint8* stream, int len)
// buffer length is 1024 samples
// which is 710 samples at the original sample rate
- //SPU::ReadOutput((s16*)stream, len>>2);
s16 buf_in[710*2];
s16* buf_out = (s16*)stream;
@@ -159,11 +259,13 @@ void AudioCallback(void* data, Uint8* stream, int len)
float res_timer = 0;
int res_pos = 0;
+ int volume = Config::AudioVolume;
+
for (int i = 0; i < 1024; i++)
{
// TODO: interp!!
- buf_out[i*2 ] = buf_in[res_pos*2 ];
- buf_out[i*2+1] = buf_in[res_pos*2+1];
+ buf_out[i*2 ] = (buf_in[res_pos*2 ] * volume) >> 8;
+ buf_out[i*2+1] = (buf_in[res_pos*2+1] * volume) >> 8;
res_timer += res_incr;
while (res_timer >= 1.0)
@@ -174,6 +276,106 @@ void AudioCallback(void* data, Uint8* stream, int len)
}
}
+void MicCallback(void* data, Uint8* stream, int len)
+{
+ if (Config::MicInputType != 1) return;
+
+ s16* input = (s16*)stream;
+ len /= sizeof(s16);
+
+ if ((MicBufferWritePos + len) > MicBufferLength)
+ {
+ u32 len1 = MicBufferLength - MicBufferWritePos;
+ memcpy(&MicBuffer[MicBufferWritePos], &input[0], len1*sizeof(s16));
+ memcpy(&MicBuffer[0], &input[len1], (len - len1)*sizeof(s16));
+ MicBufferWritePos = len - len1;
+ }
+ else
+ {
+ memcpy(&MicBuffer[MicBufferWritePos], input, len*sizeof(s16));
+ MicBufferWritePos += len;
+ }
+}
+
+bool JoyButtonPressed(int btnid, int njoybuttons, Uint8* joybuttons, Uint32 hat)
+{
+ bool pressed;
+ if (btnid == 0x101) // up
+ pressed = (hat & SDL_HAT_UP);
+ else if (btnid == 0x104) // down
+ pressed = (hat & SDL_HAT_DOWN);
+ else if (btnid == 0x102) // right
+ pressed = (hat & SDL_HAT_RIGHT);
+ else if (btnid == 0x108) // left
+ pressed = (hat & SDL_HAT_LEFT);
+ else
+ pressed = (btnid < njoybuttons) ? joybuttons[btnid] : false;
+
+ return pressed;
+}
+
+void FeedMicInput()
+{
+ int type = Config::MicInputType;
+ if ((type != 1 && MicCommand == 0) ||
+ (type == 3 && MicWavBuffer == NULL))
+ {
+ type = 0;
+ MicBufferReadPos = 0;
+ }
+
+ switch (type)
+ {
+ case 0: // no mic
+ NDS::MicInputFrame(NULL, 0);
+ break;
+
+ case 1: // host mic
+ if ((MicBufferReadPos + 735) > MicBufferLength)
+ {
+ s16 tmp[735];
+ u32 len1 = MicBufferLength - MicBufferReadPos;
+ memcpy(&tmp[0], &MicBuffer[MicBufferReadPos], len1*sizeof(s16));
+ memcpy(&tmp[len1], &MicBuffer[0], (735 - len1)*sizeof(s16));
+
+ NDS::MicInputFrame(tmp, 735);
+ MicBufferReadPos = 735 - len1;
+ }
+ else
+ {
+ NDS::MicInputFrame(&MicBuffer[MicBufferReadPos], 735);
+ MicBufferReadPos += 735;
+ }
+ break;
+
+ case 2: // white noise
+ {
+ s16 tmp[735];
+ for (int i = 0; i < 735; i++) tmp[i] = rand() & 0xFFFF;
+ NDS::MicInputFrame(tmp, 735);
+ }
+ break;
+
+ case 3: // WAV
+ if ((MicBufferReadPos + 735) > MicWavLength)
+ {
+ s16 tmp[735];
+ u32 len1 = MicWavLength - MicBufferReadPos;
+ memcpy(&tmp[0], &MicWavBuffer[MicBufferReadPos], len1*sizeof(s16));
+ memcpy(&tmp[len1], &MicWavBuffer[0], (735 - len1)*sizeof(s16));
+
+ NDS::MicInputFrame(tmp, 735);
+ MicBufferReadPos = 735 - len1;
+ }
+ else
+ {
+ NDS::MicInputFrame(&MicWavBuffer[MicBufferReadPos], 735);
+ MicBufferReadPos += 735;
+ }
+ break;
+ }
+}
+
int EmuThreadFunc(void* burp)
{
NDS::Init();
@@ -185,32 +387,18 @@ int EmuThreadFunc(void* burp)
ScreenDrawInited = false;
Touching = false;
+ KeyInputMask = 0xFFF;
+ LidCommand = false;
+ LidStatus = false;
+ MicCommand = 0;
- SDL_AudioSpec whatIwant, whatIget;
- memset(&whatIwant, 0, sizeof(SDL_AudioSpec));
- whatIwant.freq = 47340;
- whatIwant.format = AUDIO_S16LSB;
- whatIwant.channels = 2;
- whatIwant.samples = 1024;
- whatIwant.callback = AudioCallback;
- SDL_AudioDeviceID audio = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, 0);
- if (!audio)
- {
- printf("Audio init failed: %s\n", SDL_GetError());
- }
- else
+ Uint8* joybuttons = NULL; int njoybuttons = 0;
+ if (Joystick)
{
- SDL_PauseAudioDevice(audio, 0);
+ njoybuttons = SDL_JoystickNumButtons(Joystick);
+ if (njoybuttons) joybuttons = new Uint8[njoybuttons];
}
- KeyInputMask = 0xFFF;
-
- // TODO: support more joysticks
- if (SDL_NumJoysticks() > 0)
- Joystick = SDL_JoystickOpen(0);
- else
- Joystick = NULL;
-
u32 nframes = 0;
u32 starttick = SDL_GetTicks();
u32 lasttick = starttick;
@@ -225,6 +413,7 @@ int EmuThreadFunc(void* burp)
{
EmuStatus = 1;
+
// poll input
u32 keymask = KeyInputMask;
u32 joymask = 0xFFF;
@@ -236,28 +425,49 @@ int EmuThreadFunc(void* burp)
Sint16 axisX = SDL_JoystickGetAxis(Joystick, 0);
Sint16 axisY = SDL_JoystickGetAxis(Joystick, 1);
+ for (int i = 0; i < njoybuttons; i++)
+ joybuttons[i] = SDL_JoystickGetButton(Joystick, i);
+
for (int i = 0; i < 12; i++)
{
int btnid = Config::JoyMapping[i];
if (btnid < 0) continue;
- bool pressed;
- if (btnid == 0x101) // up
- pressed = (hat & SDL_HAT_UP) || (axisY <= -16384);
- else if (btnid == 0x104) // down
- pressed = (hat & SDL_HAT_DOWN) || (axisY >= 16384);
- else if (btnid == 0x102) // right
- pressed = (hat & SDL_HAT_RIGHT) || (axisX >= 16384);
- else if (btnid == 0x108) // left
- pressed = (hat & SDL_HAT_LEFT) || (axisX <= -16384);
- else
- pressed = SDL_JoystickGetButton(Joystick, btnid);
+ bool pressed = JoyButtonPressed(btnid, njoybuttons, joybuttons, hat);
+
+ if (i == 4) // right
+ pressed = pressed || (axisX >= 16384);
+ else if (i == 5) // left
+ pressed = pressed || (axisX <= -16384);
+ else if (i == 6) // up
+ pressed = pressed || (axisY <= -16384);
+ else if (i == 7) // down
+ pressed = pressed || (axisY >= 16384);
if (pressed) joymask &= ~(1<<i);
}
+
+ if (JoyButtonPressed(Config::HKJoyMapping[HK_Lid], njoybuttons, joybuttons, hat))
+ {
+ LidStatus = !LidStatus;
+ LidCommand = true;
+ }
+ if (JoyButtonPressed(Config::HKJoyMapping[HK_Mic], njoybuttons, joybuttons, hat))
+ MicCommand |= 2;
+ else
+ MicCommand &= ~2;
}
NDS::SetKeyMask(keymask & joymask);
+ if (LidCommand)
+ {
+ NDS::SetLidClosed(LidStatus);
+ LidCommand = false;
+ }
+
+ // microphone input
+ FeedMicInput();
+
// emulate
u32 nlines = NDS::RunFrame();
@@ -356,9 +566,7 @@ int EmuThreadFunc(void* burp)
EmuStatus = 0;
- if (Joystick) SDL_JoystickClose(Joystick);
-
- if (audio) SDL_CloseAudioDevice(audio);
+ if (joybuttons) delete[] joybuttons;
NDS::DeInit();
@@ -486,6 +694,9 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
for (int i = 0; i < 12; i++)
if (evt->Scancode == Config::KeyMapping[i])
KeyInputMask |= (1<<i);
+
+ if (evt->Scancode == Config::HKKeyMapping[HK_Mic])
+ MicCommand &= ~1;
}
else if (!evt->Repeat)
{
@@ -509,8 +720,16 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
if (evt->Scancode == Config::KeyMapping[i])
KeyInputMask &= ~(1<<i);
- //if (evt->Scancode == 0x58) // F12
- // NDS::debug(0);
+ if (evt->Scancode == Config::HKKeyMapping[HK_Lid])
+ {
+ LidStatus = !LidStatus;
+ LidCommand = true;
+ }
+ if (evt->Scancode == Config::HKKeyMapping[HK_Mic])
+ MicCommand |= 1;
+
+ if (evt->Scancode == 0x57) // F11
+ NDS::debug(0);
}
return 1;
@@ -899,7 +1118,7 @@ void LoadState(int slot)
}
else
{
- char* file = uiOpenFile(MainWindow, "melonDS savestate (any)|*.ml1;*.ml2;*.ml3;*.ml4;*.ml5;*.ml6;*.ml7;*.ml8;*.mln", NULL);
+ char* file = uiOpenFile(MainWindow, "melonDS savestate (any)|*.ml1;*.ml2;*.ml3;*.ml4;*.ml5;*.ml6;*.ml7;*.ml8;*.mln", Config::LastROMFolder);
if (!file)
{
EmuRunning = prevstatus;
@@ -922,6 +1141,8 @@ void LoadState(int slot)
NDS::DoSavestate(backup);
delete backup;
+ bool failed = false;
+
Savestate* state = new Savestate(filename, false);
if (state->Error)
{
@@ -931,25 +1152,29 @@ void LoadState(int slot)
// current state might be crapoed, so restore from sane backup
state = new Savestate("timewarp.mln", false);
+ failed = true;
}
NDS::DoSavestate(state);
delete state;
- if (Config::SavestateRelocSRAM && ROMPath[0]!='\0')
+ if (!failed)
{
- strncpy(PrevSRAMPath, SRAMPath, 1024);
+ if (Config::SavestateRelocSRAM && ROMPath[0]!='\0')
+ {
+ strncpy(PrevSRAMPath, SRAMPath, 1024);
- strncpy(SRAMPath, filename, 1019);
- int len = strlen(SRAMPath);
- strcpy(&SRAMPath[len], ".sav");
- SRAMPath[len+4] = '\0';
+ strncpy(SRAMPath, filename, 1019);
+ int len = strlen(SRAMPath);
+ strcpy(&SRAMPath[len], ".sav");
+ SRAMPath[len+4] = '\0';
- NDS::RelocateSave(SRAMPath, false);
- }
+ NDS::RelocateSave(SRAMPath, false);
+ }
- SavestateLoaded = true;
- uiMenuItemEnable(MenuItem_UndoStateLoad);
+ SavestateLoaded = true;
+ uiMenuItemEnable(MenuItem_UndoStateLoad);
+ }
EmuRunning = prevstatus;
}
@@ -968,7 +1193,7 @@ void SaveState(int slot)
}
else
{
- char* file = uiSaveFile(MainWindow, "melonDS savestate (*.mln)|*.mln", NULL);
+ char* file = uiSaveFile(MainWindow, "melonDS savestate (*.mln)|*.mln", Config::LastROMFolder);
if (!file)
{
EmuRunning = prevstatus;
@@ -1085,12 +1310,17 @@ void OnOpenFile(uiMenuItem* item, uiWindow* window, void* blarg)
EmuRunning = 2;
while (EmuStatus != 2);
- char* file = uiOpenFile(window, "DS ROM (*.nds)|*.nds;*.srl|Any file|*.*", NULL);
+ char* file = uiOpenFile(window, "DS ROM (*.nds)|*.nds;*.srl|Any file|*.*", Config::LastROMFolder);
if (!file)
{
EmuRunning = prevstatus;
return;
}
+
+ int pos = strlen(file)-1;
+ while (file[pos] != '/' && file[pos] != '\\' && pos > 0) pos--;
+ strncpy(Config::LastROMFolder, file, pos);
+ Config::LastROMFolder[pos] = '\0';
TryLoadROM(file, prevstatus);
uiFreeText(file);
@@ -1177,7 +1407,17 @@ void OnOpenEmuSettings(uiMenuItem* item, uiWindow* window, void* blarg)
void OnOpenInputConfig(uiMenuItem* item, uiWindow* window, void* blarg)
{
- DlgInputConfig::Open();
+ DlgInputConfig::Open(0);
+}
+
+void OnOpenHotkeyConfig(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ DlgInputConfig::Open(1);
+}
+
+void OnOpenAudioSettings(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ DlgAudioSettings::Open();
}
@@ -1321,6 +1561,19 @@ int main(int argc, char** argv)
printf("melonDS " MELONDS_VERSION "\n");
printf(MELONDS_URL "\n");
+ {
+ int len = strlen(argv[0]);
+ while (len > 0)
+ {
+ if (argv[0][len] == '/') break;
+ if (argv[0][len] == '\\') break;
+ len--;
+ }
+ EmuDirectory = new char[len];
+ strncpy(EmuDirectory, argv[0], len);
+ EmuDirectory[len] = '\0';
+ }
+
// http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
@@ -1348,7 +1601,10 @@ int main(int argc, char** argv)
Config::Load();
- if (!Config::HasConfigFile("bios7.bin") || !Config::HasConfigFile("bios9.bin") || !Config::HasConfigFile("firmware.bin"))
+ if (Config::AudioVolume < 0) Config::AudioVolume = 0;
+ else if (Config::AudioVolume > 256) Config::AudioVolume = 256;
+
+ if (!LocalFileExists("bios7.bin") || !LocalFileExists("bios9.bin") || !LocalFileExists("firmware.bin"))
{
uiMsgBoxError(
NULL,
@@ -1432,10 +1688,16 @@ int main(int argc, char** argv)
MenuItem_Stop = menuitem;
menu = uiNewMenu("Config");
- menuitem = uiMenuAppendItem(menu, "Emu settings");
- uiMenuItemOnClicked(menuitem, OnOpenEmuSettings, NULL);
- menuitem = uiMenuAppendItem(menu, "Input config");
- uiMenuItemOnClicked(menuitem, OnOpenInputConfig, NULL);
+ {
+ menuitem = uiMenuAppendItem(menu, "Emu settings");
+ uiMenuItemOnClicked(menuitem, OnOpenEmuSettings, NULL);
+ menuitem = uiMenuAppendItem(menu, "Input config");
+ uiMenuItemOnClicked(menuitem, OnOpenInputConfig, NULL);
+ menuitem = uiMenuAppendItem(menu, "Hotkey config");
+ uiMenuItemOnClicked(menuitem, OnOpenHotkeyConfig, NULL);
+ menuitem = uiMenuAppendItem(menu, "Audio settings");
+ uiMenuItemOnClicked(menuitem, OnOpenAudioSettings, NULL);
+ }
uiMenuAppendSeparator(menu);
{
uiMenu* submenu = uiNewMenu("Savestate settings");
@@ -1566,6 +1828,53 @@ int main(int argc, char** argv)
OnSetScreenRotation(MenuItem_ScreenRot[ScreenRotation], MainWindow, (void*)&kScreenRot[ScreenRotation]);
+ SDL_AudioSpec whatIwant, whatIget;
+ memset(&whatIwant, 0, sizeof(SDL_AudioSpec));
+ whatIwant.freq = 47340;
+ whatIwant.format = AUDIO_S16LSB;
+ whatIwant.channels = 2;
+ whatIwant.samples = 1024;
+ whatIwant.callback = AudioCallback;
+ SDL_AudioDeviceID audio = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, 0);
+ if (!audio)
+ {
+ printf("Audio init failed: %s\n", SDL_GetError());
+ }
+ else
+ {
+ SDL_PauseAudioDevice(audio, 0);
+ }
+
+ memset(&whatIwant, 0, sizeof(SDL_AudioSpec));
+ whatIwant.freq = 44100;
+ whatIwant.format = AUDIO_S16LSB;
+ whatIwant.channels = 1;
+ whatIwant.samples = 1024;
+ whatIwant.callback = MicCallback;
+ SDL_AudioDeviceID mic = SDL_OpenAudioDevice(NULL, 1, &whatIwant, &whatIget, 0);
+ if (!mic)
+ {
+ printf("Mic init failed: %s\n", SDL_GetError());
+ MicBufferLength = 0;
+ }
+ else
+ {
+ SDL_PauseAudioDevice(mic, 0);
+ }
+
+ memset(MicBuffer, 0, sizeof(MicBuffer));
+ MicBufferReadPos = 0;
+ MicBufferWritePos = 0;
+
+ MicWavBuffer = NULL;
+ if (Config::MicInputType == 3) MicLoadWav(Config::MicWavPath);
+
+ // TODO: support more joysticks
+ if (SDL_NumJoysticks() > 0)
+ Joystick = SDL_JoystickOpen(0);
+ else
+ Joystick = NULL;
+
EmuRunning = 2;
RunningSomething = false;
EmuThread = SDL_CreateThread(EmuThreadFunc, "melonDS magic", NULL);
@@ -1594,6 +1903,12 @@ int main(int argc, char** argv)
EmuRunning = 0;
SDL_WaitThread(EmuThread, NULL);
+ if (Joystick) SDL_JoystickClose(Joystick);
+ if (audio) SDL_CloseAudioDevice(audio);
+ if (mic) SDL_CloseAudioDevice(mic);
+
+ if (MicWavBuffer) delete[] MicWavBuffer;
+
Config::ScreenRotation = ScreenRotation;
Config::ScreenGap = ScreenGap;
Config::ScreenLayout = ScreenLayout;
@@ -1605,6 +1920,7 @@ int main(int argc, char** argv)
uiUninit();
SDL_Quit();
+ delete[] EmuDirectory;
return 0;
}
@@ -1614,41 +1930,35 @@ int main(int argc, char** argv)
int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdshow)
{
- char cmdargs[16][256];
- int arg = 1;
- int j = 0;
- bool inquote = false;
- int len = strlen(cmdline);
- for (int i = 0; i < len; i++)
- {
- char c = cmdline[i];
- if (c == '\0') break;
- if (c == '"') inquote = !inquote;
- if (!inquote && c==' ')
- {
- if (j > 255) j = 255;
- if (arg < 16) cmdargs[arg][j] = '\0';
- arg++;
- j = 0;
- }
- else
- {
- if (arg < 16 && j < 255) cmdargs[arg][j] = c;
- j++;
- }
+ int argc = 0;
+ wchar_t** argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
+ char* nullarg = "";
+
+ char** argv = new char*[argc];
+ for (int i = 0; i < argc; i++)
+ {
+ int len = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL);
+ if (len < 1) return NULL;
+ argv[i] = new char[len];
+ int res = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, argv[i], len, NULL, NULL);
+ if (res != len) { delete[] argv[i]; argv[i] = nullarg; }
+ }
+
+ if (AttachConsole(ATTACH_PARENT_PROCESS))
+ {
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+ printf("\n");
}
- if (j > 255) j = 255;
- if (arg < 16) cmdargs[arg][j] = '\0';
- if (len > 0) arg++;
- // FIXME!!
- strncpy(cmdargs[0], "melonDS.exe", 256);
+ int ret = main(argc, argv);
+
+ printf("\n\n>");
- char* cmdargptr[16];
- for (int i = 0; i < 16; i++)
- cmdargptr[i] = &cmdargs[i][0];
+ for (int i = 0; i < argc; i++) if (argv[i] != nullarg) delete[] argv[i];
+ delete[] argv;
- return main(arg, cmdargptr);
+ return ret;
}
#endif
diff --git a/src/melon_fopen.cpp b/src/melon_fopen.cpp
new file mode 100644
index 0000000..04d3caf
--- /dev/null
+++ b/src/melon_fopen.cpp
@@ -0,0 +1,176 @@
+/*
+ Copyright 2016-2019 StapleButter
+
+ 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 <stdlib.h>
+#include <string>
+#ifdef _WIN32
+#define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
+#include <windows.h>
+//#include <knownfolders.h> // FUCK THAT SHIT
+extern "C" const GUID DECLSPEC_SELECTANY FOLDERID_RoamingAppData = {0x3eb685db, 0x65f9, 0x4cf6, {0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d}};
+#include <shlobj.h>
+#else
+#include <glib.h>
+#endif
+
+extern char* EmuDirectory;
+
+
+#ifdef __WIN32__
+
+
+
+FILE* melon_fopen(const char* path, const char* mode)
+{
+ int len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
+ if (len < 1) return NULL;
+ WCHAR* fatass = new WCHAR[len];
+ int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, fatass, len);
+ if (res != len) { delete[] fatass; return NULL; } // checkme?
+
+ // this will be more than enough
+ WCHAR fatmode[4];
+ fatmode[0] = mode[0];
+ fatmode[1] = mode[1];
+ fatmode[2] = mode[2];
+ fatmode[3] = 0;
+
+ FILE* ret = _wfopen(fatass, fatmode);
+ delete[] fatass;
+ return ret;
+}
+
+FILE* melon_fopen_local(const char* fileName, const char* permissions)
+{
+ // Locations are application directory, and AppData/melonDS on windows
+
+ FILE* f;
+
+ // First check current working directory
+ f = fopen(fileName, permissions);
+ if (f) return f;
+
+ // then emu directory
+ {
+ int dirlen = strlen(EmuDirectory);
+ if (dirlen)
+ {
+ int filelen = strlen(fileName);
+ int len = dirlen + 1 + filelen + 1;
+ char* tmp = new char[len];
+ strncpy(&tmp[0], EmuDirectory, dirlen);
+ tmp[dirlen] = '\\';
+ strncpy(&tmp[dirlen+1], fileName, filelen);
+ tmp[dirlen+1+filelen] = '\0';
+
+ f = melon_fopen(tmp, permissions);
+ delete[] tmp;
+ if (f) return f;
+ }
+ }
+
+ // Now check AppData
+ PWSTR appDataPath = NULL;
+ SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath);
+ if (!appDataPath)
+ return NULL;
+
+ // this will be more than enough
+ WCHAR fatperm[4];
+ fatperm[0] = permissions[0];
+ fatperm[1] = permissions[1];
+ fatperm[2] = permissions[2];
+ fatperm[3] = 0;
+
+ int fnlen = MultiByteToWideChar(CP_UTF8, 0, fileName, -1, NULL, 0);
+ if (fnlen < 1) return NULL;
+ WCHAR* wfileName = new WCHAR[fnlen];
+ int res = MultiByteToWideChar(CP_UTF8, 0, fileName, -1, wfileName, fnlen);
+ if (res != fnlen) { delete[] wfileName; return NULL; } // checkme?
+
+ const WCHAR* appdir = L"\\melonDS\\";
+
+ int pos = wcslen(appDataPath);
+ void* ptr = CoTaskMemRealloc(appDataPath, (pos+wcslen(appdir)+fnlen+1)*sizeof(WCHAR));
+ if (!ptr) { delete[] wfileName; return NULL; } // oh well
+ appDataPath = (PWSTR)ptr;
+
+ wcscpy(&appDataPath[pos], appdir); pos += wcslen(appdir);
+ wcscpy(&appDataPath[pos], wfileName);
+
+ f = _wfopen(appDataPath, fatperm);
+ CoTaskMemFree(appDataPath);
+ delete[] wfileName;
+ if (f) return f;
+
+ return NULL;
+
+}
+
+
+
+#else
+
+
+
+FILE* melon_fopen(const char* filename, const char* perm) { return fopen(filename, perm); }
+
+FILE* melon_fopen_local(const char* fileName, const char* permissions)
+{
+ // Locations are application directory, and XDG_CONFIG_HOME/melonds
+
+ FILE* f;
+
+ // First check current working directory
+ f = fopen(fileName, permissions);
+ if (f) return f;
+
+ // then emu directory
+ {
+ int dirlen = strlen(EmuDirectory);
+ if (dirlen)
+ {
+ int filelen = strlen(fileName);
+ int len = dirlen + 1 + filelen + 1;
+ char* tmp = new char[len];
+ strncpy(&tmp[0], EmuDirectory, dirlen);
+ tmp[dirlen] = '/';
+ strncpy(&tmp[dirlen+1], fileName, filelen);
+ tmp[dirlen+1+filelen] = '\0';
+
+ f = fopen(tmp, permissions);
+ delete[] tmp;
+ if (f) return f;
+ }
+ }
+
+ // Now check XDG_CONFIG_HOME
+ // TODO: check for memory leak there
+ std::string path = std::string(g_get_user_config_dir()) + "/melonds/" + fileName;
+ f = fopen(path.c_str(), permissions);
+ if (f) return f;
+
+ return NULL;
+
+}
+
+
+
+#endif
diff --git a/src/melon_fopen.h b/src/melon_fopen.h
index a45ad97..40709e1 100644
--- a/src/melon_fopen.h
+++ b/src/melon_fopen.h
@@ -19,34 +19,9 @@
#ifndef MELON_FOPEN_H
#define MELON_FOPEN_H
-#ifdef __WIN32__
-#include <windows.h>
+FILE* melon_fopen(const char* filename, const char* perm);
+FILE* melon_fopen_local(const char* filename, const char* perm);
-static FILE* melon_fopen(const char* path, const char* mode)
-{
- int len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
- if (len < 1) return NULL;
- WCHAR* fatass = new WCHAR[len];
- int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, fatass, len);
- if (res != len) { delete[] fatass; return NULL; } // checkme?
-
- // this will be more than enough
- WCHAR fatmode[4];
- fatmode[0] = mode[0];
- fatmode[1] = mode[1];
- fatmode[2] = mode[2];
- fatmode[3] = 0;
-
- FILE* ret = _wfopen(fatass, fatmode);
- delete[] fatass;
- return ret;
-}
-
-#else
-
-#define melon_fopen fopen
-
-#endif
#endif // MELON_FOPEN_H
diff --git a/src/version.h b/src/version.h
index 25156b0..d1abc3f 100644
--- a/src/version.h
+++ b/src/version.h
@@ -19,7 +19,7 @@
#ifndef VERSION_H
#define VERSION_H
-#define MELONDS_VERSION "0.7"
+#define MELONDS_VERSION "0.7.1"
#define MELONDS_URL "http://melonds.kuribo64.net/"