aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ARM.cpp461
-rw-r--r--src/ARM.h401
-rw-r--r--src/ARMInterpreter.cpp32
-rw-r--r--src/ARMInterpreter.h2
-rw-r--r--src/ARMInterpreter_ALU.cpp210
-rw-r--r--src/ARMInterpreter_ALU.h2
-rw-r--r--src/ARMInterpreter_Branch.cpp5
-rw-r--r--src/ARMInterpreter_Branch.h2
-rw-r--r--src/ARMInterpreter_LoadStore.cpp218
-rw-r--r--src/ARMInterpreter_LoadStore.h2
-rw-r--r--src/ARM_InstrTable.h2
-rw-r--r--src/CP15.cpp560
-rw-r--r--src/CRC32.cpp69
-rw-r--r--src/CRC32.h26
-rw-r--r--src/Config.cpp108
-rw-r--r--src/Config.h21
-rw-r--r--src/DMA.cpp264
-rw-r--r--src/DMA.h18
-rw-r--r--src/FIFO.h12
-rw-r--r--src/GPU.cpp54
-rw-r--r--src/GPU.h8
-rw-r--r--src/GPU2D.cpp166
-rw-r--r--src/GPU2D.h8
-rw-r--r--src/GPU3D.cpp704
-rw-r--r--src/GPU3D.h6
-rw-r--r--src/GPU3D_Soft.cpp95
-rw-r--r--src/NDS.cpp1228
-rw-r--r--src/NDS.h53
-rw-r--r--src/NDSCart.cpp789
-rw-r--r--src/NDSCart.h13
-rw-r--r--src/Platform.h2
-rw-r--r--src/RTC.cpp26
-rw-r--r--src/RTC.h4
-rw-r--r--src/SPI.cpp105
-rw-r--r--src/SPI.h8
-rw-r--r--src/SPU.cpp112
-rw-r--r--src/SPU.h11
-rw-r--r--src/Savestate.cpp275
-rw-r--r--src/Savestate.h62
-rw-r--r--src/Wifi.cpp36
-rw-r--r--src/Wifi.h5
-rw-r--r--src/WifiAP.cpp2
-rw-r--r--src/WifiAP.h2
-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.cpp13
-rw-r--r--src/libui_sdl/DlgEmuSettings.h2
-rw-r--r--src/libui_sdl/DlgInputConfig.cpp256
-rw-r--r--src/libui_sdl/DlgInputConfig.h4
-rw-r--r--src/libui_sdl/Platform.cpp2
-rw-r--r--src/libui_sdl/libui/ui.h2
-rw-r--r--src/libui_sdl/libui/unix/area.c30
-rw-r--r--src/libui_sdl/libui/unix/menu.c17
-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.cpp869
-rw-r--r--src/melon_fopen.cpp176
-rw-r--r--src/melon_fopen.h31
-rw-r--r--src/types.h2
-rw-r--r--src/version.h4
61 files changed, 6271 insertions, 1565 deletions
diff --git a/src/ARM.cpp b/src/ARM.cpp
index bbfb7f7..f537fb4 100644
--- a/src/ARM.cpp
+++ b/src/ARM.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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,11 +92,77 @@ void ARM::Reset()
ExceptionBase = Num ? 0x00000000 : 0xFFFF0000;
+ CodeMem.Mem = NULL;
+
// zorp
JumpTo(ExceptionBase);
}
-void ARM::JumpTo(u32 addr, bool restorecpsr)
+void ARMv5::Reset()
+{
+ CP15Reset();
+ ARM::Reset();
+}
+
+
+void ARM::DoSavestate(Savestate* file)
+{
+ file->Section((char*)(Num ? "ARM7" : "ARM9"));
+
+ file->Var32((u32*)&Cycles);
+ file->Var32((u32*)&CyclesToRun);
+ file->Var32(&Halted);
+
+ file->VarArray(R, 16*sizeof(u32));
+ file->Var32(&CPSR);
+ file->VarArray(R_FIQ, 8*sizeof(u32));
+ file->VarArray(R_SVC, 3*sizeof(u32));
+ file->VarArray(R_ABT, 3*sizeof(u32));
+ file->VarArray(R_IRQ, 3*sizeof(u32));
+ file->VarArray(R_UND, 3*sizeof(u32));
+ file->Var32(&CurInstr);
+ 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 ARMv5::JumpTo(u32 addr, bool restorecpsr)
{
if (restorecpsr)
{
@@ -174,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;
}
}
@@ -301,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()
@@ -318,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)
{
@@ -326,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;
}
}
@@ -354,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
@@ -378,31 +511,127 @@ s32 ARM::Execute()
{
ARMInterpreter::A_BLX_IMM(this);
}
+ else
+ AddCycles_C();
}
- if (Num==0)
+ //s32 diff = Cycles - lastcycles;arm9timer+=(diff>>1);
+ //NDS::RunTightTimers(0, diff >> ClockShift);
+ //lastcycles = Cycles - (diff & ClockDiffMask);
+
+ // TODO optimize this shit!!!
+ if (Halted)
{
- s32 diff = Cycles - lastcycles;
- NDS::RunTimingCriticalDevices(0, diff >> 1);
- lastcycles = Cycles - (diff&1);
+ 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;
+
+ while (Cycles < CyclesToRun)
+ {
+ if (CPSR & 0x20) // THUMB
+ {
+ // 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();
}
}
@@ -410,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 564c7bb..9a9b03c 100644
--- a/src/ARM.h
+++ b/src/ARM.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -19,26 +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 JumpTo(u32 addr, bool restorecpsr = false);
+ virtual void DoSavestate(Savestate* file);
+
+ 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)
@@ -47,6 +57,7 @@ public:
Halted = halt;
}
+ // TODO: is this actually used??
void CheckIRQ()
{
if (!(NDS::IME[Num] & 0x1)) return;
@@ -56,7 +67,7 @@ public:
}
}
- s32 Execute();
+ virtual s32 Execute() = 0;
bool CheckCondition(u32 code)
{
@@ -91,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
+
+ // code/16N/32N/32S
+ u8 MemTimings[0x100000][4];
- u8 DataRead8(u32 addr, u32 forceuser=0)
+ 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);
+ }
+
+ u32 CodeRead32(u32 addr)
+ {
+ return NDS::ARM7Read32(addr);
+ }
- Cycles += Waitstates[2][(addr>>24)&0xF];
- return val;
+ 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;
+
+ *val = NDS::ARM7Read32(addr);
+ DataCycles += NDS::ARM7MemTimings[DataRegion][3];
+ }
- Cycles += Waitstates[2][(addr>>24)&0xF];
+ void DataWrite8(u32 addr, u8 val)
+ {
+ NDS::ARM7Write8(addr, val);
+ DataRegion = addr >> 24;
+ DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
- void DataWrite16(u32 addr, u16 val, u32 forceuser=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;
- u32 Num;
+ NDS::ARM7Write32(addr, val);
+ DataCycles += NDS::ARM7MemTimings[DataRegion][3];
+ }
- // waitstates:
- // 0=code16 1=code32 2=data16 3=data32
- // TODO eventually: nonsequential waitstates
- s32 Waitstates[4][16];
- s32 Cycles;
- s32 CyclesToRun;
- u32 Halted;
+ void AddCycles_C()
+ {
+ // code only. this code fetch is sequential.
+ Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3];
+ }
- 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_CI(s32 num)
+ {
+ // code+internal. results in a nonseq code fetch.
+ Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num;
+ }
- u32 ExceptionBase;
+ void AddCycles_CDI()
+ {
+ // LDR/LDM cycles.
+ s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2];
+ s32 numD = DataCycles;
- static u32 ConditionTable[16];
+ 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;
+ }
+ }
- u32 debug;
+ 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;
+
+ 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 16eb111..1312771 100644
--- a/src/ARMInterpreter.cpp
+++ b/src/ARMInterpreter.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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.h b/src/ARMInterpreter.h
index 2d4c1a8..2982011 100644
--- a/src/ARMInterpreter.h
+++ b/src/ARMInterpreter.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp
index 4f76b8b..f70763d 100644
--- a/src/ARMInterpreter_ALU.cpp
+++ b/src/ARMInterpreter_ALU.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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_ALU.h b/src/ARMInterpreter_ALU.h
index 4cc3760..813135d 100644
--- a/src/ARMInterpreter_ALU.h
+++ b/src/ARMInterpreter_ALU.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/ARMInterpreter_Branch.cpp b/src/ARMInterpreter_Branch.cpp
index 88f316d..5e2ef2c 100644
--- a/src/ARMInterpreter_Branch.cpp
+++ b/src/ARMInterpreter_Branch.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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_Branch.h b/src/ARMInterpreter_Branch.h
index 202f490..aa5b995 100644
--- a/src/ARMInterpreter_Branch.h
+++ b/src/ARMInterpreter_Branch.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp
index 7edf88c..5a1b88d 100644
--- a/src/ARMInterpreter_LoadStore.cpp
+++ b/src/ARMInterpreter_LoadStore.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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/ARMInterpreter_LoadStore.h b/src/ARMInterpreter_LoadStore.h
index 4ea0e54..58da587 100644
--- a/src/ARMInterpreter_LoadStore.h
+++ b/src/ARMInterpreter_LoadStore.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/ARM_InstrTable.h b/src/ARM_InstrTable.h
index 47b3e1c..afcc3b2 100644
--- a/src/ARM_InstrTable.h
+++ b/src/ARM_InstrTable.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/CP15.cpp b/src/CP15.cpp
index cc6a6af..fb0da0d 100644
--- a/src/CP15.cpp
+++ b/src/CP15.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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,12 +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 ARMv5::CP15DoSavestate(Savestate* file)
+{
+ file->Section("CP15");
+
+ file->Var32(&CP15Control);
+
+ file->Var32(&DTCMSetting);
+ file->Var32(&ITCMSetting);
+
+ file->VarArray(ITCM, 0x8000);
+ file->VarArray(DTCM, 0x4000);
-void UpdateDTCMSetting()
+ 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();
+ }
+}
+
+
+void ARMv5::UpdateDTCMSetting()
{
- if (Control & (1<<16))
+ if (CP15Control & (1<<16))
{
DTCMBase = DTCMSetting & 0xFFFFF000;
DTCMSize = 0x200 << ((DTCMSetting >> 1) & 0x1F);
@@ -74,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);
@@ -89,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;
@@ -129,6 +411,7 @@ void Write(u32 id, u32 val)
DTCMSetting = val;
UpdateDTCMSetting();
return;
+
case 0x911:
ITCMSetting = val;
UpdateITCMSetting();
@@ -139,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]);
@@ -161,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:
@@ -178,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;
}
- return false;
+ *val = NDS::ARM9Read16(addr);
+ DataCycles = MemTimings[addr >> 12][1];
}
-bool HandleDataRead32(u32 addr, u32* val, u32 forceuser)
+void ARMv5::DataRead32(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][2];
}
-bool HandleDataWrite8(u32 addr, u8 val, u32 forceuser)
+void ARMv5::DataRead32S(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;
+ }
+
+ *val = NDS::ARM9Read32(addr);
+ DataCycles += MemTimings[addr >> 12][3];
+}
+
+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;
+ }
+
+ NDS::ARM9Write16(addr, val);
+ DataCycles = MemTimings[addr >> 12][1];
+}
+
+void ARMv5::DataWrite32(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;
}
- return false;
+ NDS::ARM9Write32(addr, val);
+ DataCycles = MemTimings[addr >> 12][2];
}
-bool HandleDataWrite32(u32 addr, u32 val, u32 forceuser)
+void ARMv5::DataWrite32S(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][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 660ff8c..4836cf5 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -20,18 +20,23 @@
#include <string.h>
#include <stdlib.h>
#include "Config.h"
-#include <string>
-#ifndef _WIN32
-#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;
@@ -47,6 +52,14 @@ int Threaded3D;
int SocketBindAnyAddr;
+int SavestateRelocSRAM;
+
+int AudioVolume;
+int MicInputType;
+char MicWavPath[512];
+
+char LastROMFolder[512];
+
typedef struct
{
char Name[16];
@@ -54,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;
@@ -69,7 +82,7 @@ ConfigEntry ConfigFile[] =
{"Key_Up", 0, &KeyMapping[6], 328, NULL, 0},
{"Key_Down", 0, &KeyMapping[7], 336, NULL, 0},
{"Key_R", 0, &KeyMapping[8], 54, NULL, 0},
- {"Key_L", 0, &KeyMapping[9], 42, NULL, 0},
+ {"Key_L", 0, &KeyMapping[9], 86, NULL, 0},
{"Key_X", 0, &KeyMapping[10], 17, NULL, 0},
{"Key_Y", 0, &KeyMapping[11], 30, NULL, 0},
@@ -86,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},
@@ -101,49 +120,17 @@ ConfigEntry ConfigFile[] =
{"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0},
- {"", -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
+ {"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 1, NULL, 0},
- 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;
- std::string path = std::string(appDataPath) + "\\melonds\\" + fileName;
- f = fopen(path, permissions);
- if (f) return f;
-#else
- // Now check XDG_CONFIG_HOME
- 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;
+ {"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}
+};
-bool HasConfigFile(const char* fileName)
-{
- FILE* f = GetConfigFile(fileName, "rb");
- if (f)
- {
- fclose(f);
- return true;
- }
- else
- return false;
-}
void Load()
{
@@ -155,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];
@@ -196,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 0f5ca9e..236c1a3 100644
--- a/src/Config.h
+++ b/src/Config.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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;
@@ -46,6 +57,14 @@ extern int Threaded3D;
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 39dcb85..7bbf980 100644
--- a/src/DMA.cpp
+++ b/src/DMA.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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();
}
@@ -118,6 +82,29 @@ void DMA::Reset()
InProgress = false;
}
+void DMA::DoSavestate(Savestate* file)
+{
+ char magic[5] = "DMAx";
+ magic[3] = '0' + Num + (CPU*4);
+ file->Section(magic);
+
+ file->Var32(&SrcAddr);
+ file->Var32(&DstAddr);
+ file->Var32(&Cnt);
+
+ file->Var32(&StartMode);
+ file->Var32(&CurSrcAddr);
+ file->Var32(&CurDstAddr);
+ file->Var32(&RemCount);
+ file->Var32(&IterCount);
+ file->Var32(&SrcAddrInc);
+ file->Var32(&DstAddrInc);
+
+ file->Var32(&Running);
+ file->Var32((u32*)&InProgress);
+ file->Var32((u32*)&IsGXFIFODMA);
+}
+
void DMA::WriteCnt(u32 val)
{
u32 oldcnt = Cnt;
@@ -190,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);
}
@@ -200,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;
- s32 c = (Waitstates[1][(CurSrcAddr >> 24) & 0xF] + Waitstates[1][(CurDstAddr >> 24) & 0xF]);
- cycles -= c;
- NDS::RunTimingCriticalDevices(CPU, c);
+ NDS::RunTightTimers(CPU, lastcycles-cycles);
+
+ 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;
}
@@ -276,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 11ee1ad..1d1992a 100644
--- a/src/DMA.h
+++ b/src/DMA.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -29,6 +29,8 @@ public:
void Reset();
+ void DoSavestate(Savestate* file);
+
void WriteCnt(u32 val);
void Start();
@@ -39,6 +41,8 @@ public:
return ((mode == StartMode) && (Cnt & 0x80000000));
}
+ bool IsRunning() { return Running!=0; }
+
void StartIfNeeded(u32 mode)
{
if ((mode == StartMode) && (Cnt & 0x80000000))
@@ -51,6 +55,11 @@ public:
Cnt &= ~0x80000000;
}
+ void StallIfRunning()
+ {
+ if (Executing) Stall = true;
+ }
+
u32 SrcAddr;
u32 DstAddr;
u32 Cnt;
@@ -58,8 +67,6 @@ public:
private:
u32 CPU, Num;
- s32 Waitstates[2][16];
-
u32 StartMode;
u32 CurSrcAddr;
u32 CurDstAddr;
@@ -69,9 +76,12 @@ private:
u32 DstAddrInc;
u32 CountMask;
- bool Running;
+ u32 Running;
bool InProgress;
+ bool Executing;
+ bool Stall;
+
bool IsGXFIFODMA;
};
diff --git a/src/FIFO.h b/src/FIFO.h
index 4130b85..7ce67e9 100644
--- a/src/FIFO.h
+++ b/src/FIFO.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -47,6 +47,16 @@ public:
}
+ void DoSavestate(Savestate* file)
+ {
+ file->Var32(&NumOccupied);
+ file->Var32(&ReadPos);
+ file->Var32(&WritePos);
+
+ file->VarArray(Entries, sizeof(T)*NumEntries);
+ }
+
+
void Write(T val)
{
if (IsFull()) return;
diff --git a/src/GPU.cpp b/src/GPU.cpp
index 8ac3e74..aba97a5 100644
--- a/src/GPU.cpp
+++ b/src/GPU.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -156,6 +156,58 @@ void Stop()
memset(Framebuffer, 0, 256*192*2*4);
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("GPUG");
+
+ file->Var16(&VCount);
+ file->Var32(&NextVCount);
+ file->Var16(&TotalScanlines);
+
+ file->Var16(&DispStat[0]);
+ file->Var16(&DispStat[1]);
+ file->Var16(&VMatch[0]);
+ file->Var16(&VMatch[1]);
+
+ file->VarArray(Palette, 2*1024);
+ file->VarArray(OAM, 2*1024);
+
+ file->VarArray(VRAM_A, 128*1024);
+ file->VarArray(VRAM_B, 128*1024);
+ file->VarArray(VRAM_C, 128*1024);
+ file->VarArray(VRAM_D, 128*1024);
+ file->VarArray(VRAM_E, 64*1024);
+ file->VarArray(VRAM_F, 16*1024);
+ file->VarArray(VRAM_G, 16*1024);
+ file->VarArray(VRAM_H, 32*1024);
+ file->VarArray(VRAM_I, 16*1024);
+
+ file->VarArray(VRAMCNT, 9);
+ file->Var8(&VRAMSTAT);
+
+ file->Var32(&VRAMMap_LCDC);
+
+ file->VarArray(VRAMMap_ABG, sizeof(VRAMMap_ABG));
+ file->VarArray(VRAMMap_AOBJ, sizeof(VRAMMap_AOBJ));
+ file->VarArray(VRAMMap_BBG, sizeof(VRAMMap_BBG));
+ file->VarArray(VRAMMap_BOBJ, sizeof(VRAMMap_BOBJ));
+
+ file->VarArray(VRAMMap_ABGExtPal, sizeof(VRAMMap_ABGExtPal));
+ file->Var32(&VRAMMap_AOBJExtPal);
+ file->VarArray(VRAMMap_BBGExtPal, sizeof(VRAMMap_BBGExtPal));
+ file->Var32(&VRAMMap_BOBJExtPal);
+
+ file->VarArray(VRAMMap_Texture, sizeof(VRAMMap_Texture));
+ file->VarArray(VRAMMap_TexPal, sizeof(VRAMMap_TexPal));
+
+ file->Var32(&VRAMMap_ARM7[0]);
+ file->Var32(&VRAMMap_ARM7[1]);
+
+ GPU2D_A->DoSavestate(file);
+ GPU2D_B->DoSavestate(file);
+ GPU3D::DoSavestate(file);
+}
+
// VRAM mapping notes
//
diff --git a/src/GPU.h b/src/GPU.h
index d45f383..2ef33d9 100644
--- a/src/GPU.h
+++ b/src/GPU.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -72,6 +72,9 @@ void DeInit();
void Reset();
void Stop();
+void DoSavestate(Savestate* file);
+
+
void MapVRAM_AB(u32 bank, u8 cnt);
void MapVRAM_CD(u32 bank, u8 cnt);
void MapVRAM_E(u32 bank, u8 cnt);
@@ -389,6 +392,9 @@ void DisplaySwap(u32 val);
void StartFrame();
void FinishFrame(u32 lines);
void StartScanline(u32 line);
+void StartHBlank(u32 line);
+
+void DisplayFIFO(u32 x);
void SetDispStat(u32 cpu, u16 val);
diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp
index 7550562..4951404 100644
--- a/src/GPU2D.cpp
+++ b/src/GPU2D.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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;
@@ -138,6 +141,75 @@ void GPU2D::Reset()
OBJExtPalStatus = 0;
}
+void GPU2D::DoSavestate(Savestate* file)
+{
+ file->Section((char*)(Num ? "GP2B" : "GP2A"));
+
+ file->Var32(&DispCnt);
+ file->VarArray(BGCnt, 4*2);
+ file->VarArray(BGXPos, 4*2);
+ file->VarArray(BGYPos, 4*2);
+ file->VarArray(BGXRef, 2*4);
+ file->VarArray(BGYRef, 2*4);
+ file->VarArray(BGXRefInternal, 2*4);
+ file->VarArray(BGYRefInternal, 2*4);
+ file->VarArray(BGRotA, 2*2);
+ file->VarArray(BGRotB, 2*2);
+ file->VarArray(BGRotC, 2*2);
+ file->VarArray(BGRotD, 2*2);
+
+ file->VarArray(Win0Coords, 4);
+ file->VarArray(Win1Coords, 4);
+ file->VarArray(WinCnt, 4);
+
+ file->VarArray(BGMosaicSize, 2);
+ file->VarArray(OBJMosaicSize, 2);
+ file->Var8(&BGMosaicY);
+ file->Var8(&BGMosaicYMax);
+ file->Var8(&OBJMosaicY);
+ file->Var8(&OBJMosaicYMax);
+
+ file->Var16(&BlendCnt);
+ file->Var16(&BlendAlpha);
+ file->Var8(&EVA);
+ file->Var8(&EVB);
+ file->Var8(&EVY);
+
+ file->Var16(&MasterBrightness);
+
+ if (!Num)
+ {
+ file->VarArray(DispFIFO, 16*2);
+ file->Var32(&DispFIFOReadPtr);
+ file->Var32(&DispFIFOWritePtr);
+
+ file->VarArray(DispFIFOBuffer, 256*2);
+
+ file->Var32(&CaptureCnt);
+ }
+
+ if (file->IsAtleastVersion(2, 1))
+ {
+ file->Var32(&Win0Active);
+ file->Var32(&Win1Active);
+ }
+ else
+ {
+ Win0Active = 0;
+ Win1Active = 0;
+ }
+
+ if (!file->Saving)
+ {
+ // refresh those
+ BGExtPalStatus[0] = 0;
+ BGExtPalStatus[1] = 0;
+ BGExtPalStatus[2] = 0;
+ BGExtPalStatus[3] = 0;
+ OBJExtPalStatus = 0;
+ }
+}
+
void GPU2D::SetFramebuffer(u32* buf)
{
Framebuffer = buf;
@@ -480,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);
@@ -614,12 +695,12 @@ void GPU2D::DrawScanline(u32 line)
for (int i = 0; i < 256; i++)
{
u32 c = dst[i];
-
+
u32 r = c << 18;
u32 g = (c << 2) & 0xFC00;
u32 b = (c >> 14) & 0xFC;
c = r | g | b;
-
+
dst[i] = c | ((c & 0x00C0C0C0) >> 6) | 0xFF000000;
}
}
@@ -894,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)
@@ -905,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 (int i = 0; i < 256; i++)
+ {
+ if (i == x2) Win1Active &= ~0x2;
+ else if (i == x1) Win1Active |= 0x2;
- for (u32 i = x1; i < x2; i++)
- mask[i] = WinCnt[1];
+ 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 (int i = 0; i < 256; i++)
+ {
+ if (i == x2) Win0Active &= ~0x2;
+ else if (i == x1) Win0Active |= 0x2;
- for (u32 i = x1; i < x2; i++)
- mask[i] = WinCnt[0];
+ if (Win0Active == 0x3) mask[i] = WinCnt[0];
+ }
}
}
@@ -1087,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
@@ -1134,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))
{
@@ -2096,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;
}
}
@@ -2160,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;
}
}
@@ -2218,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;
}
}
@@ -2332,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;
}
@@ -2359,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;
}
@@ -2419,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;
}
@@ -2448,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;
}
@@ -2497,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;
}
@@ -2531,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 d059fa0..5a1192e 100644
--- a/src/GPU2D.h
+++ b/src/GPU2D.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -27,6 +27,8 @@ public:
void Reset();
+ void DoSavestate(Savestate* file);
+
void SetFramebuffer(u32* buf);
u8 Read8(u32 addr);
@@ -88,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 b890f7f..df27913 100644
--- a/src/GPU3D.cpp
+++ b/src/GPU3D.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -45,7 +45,7 @@
// the added bias affects interpolation.
//
// depth buffer:
-// Z-buffering mode: val = ((Z * 0x800 * 0x1000) / W) + 0x7FFEFF
+// Z-buffering mode: val = ((Z * 0x800 * 0x1000) / W) + 0x7FFEFF (nope, wrong. TODO update)
// W-buffering mode: val = W
//
// formula for clear depth: (GBAtek is wrong there)
@@ -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,53 +134,22 @@ const u32 CmdNumParams[256] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-const s32 CmdNumCycles[256] =
+typedef union
{
- // 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 struct
-{
- u8 Command;
- u32 Param;
+ u64 _contents;
+ struct
+ {
+ u32 Param;
+ u8 Command;
+ };
} CmdFIFOEntry;
FIFO<CmdFIFOEntry>* CmdFIFO;
FIFO<CmdFIFOEntry>* CmdPIPE;
+FIFO<CmdFIFOEntry>* CmdStallQueue;
+
u32 NumCommands, CurCommand, ParamCount, TotalParams;
u32 DispCnt;
@@ -188,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;
@@ -207,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;
@@ -272,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;
@@ -283,6 +281,8 @@ void DeInit()
delete CmdFIFO;
delete CmdPIPE;
+
+ delete CmdStallQueue;
}
void Reset()
@@ -290,6 +290,8 @@ void Reset()
CmdFIFO->Clear();
CmdPIPE->Clear();
+ CmdStallQueue->Clear();
+
NumCommands = 0;
CurCommand = 0;
ParamCount = 0;
@@ -305,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;
@@ -351,6 +359,201 @@ void Reset()
SoftRenderer::Reset();
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("GP3D");
+
+ CmdFIFO->DoSavestate(file);
+ CmdPIPE->DoSavestate(file);
+
+ file->Var32(&NumCommands);
+ file->Var32(&CurCommand);
+ file->Var32(&ParamCount);
+ file->Var32(&TotalParams);
+
+ file->Var32(&NumPushPopCommands);
+ file->Var32(&NumTestCommands);
+
+ file->Var32(&DispCnt);
+ file->Var8(&AlphaRef);
+
+ file->Var32(&GXStat);
+
+ file->VarArray(ExecParams, 32*4);
+ file->Var32(&ExecParamCount);
+ file->Var32((u32*)&CycleCount);
+
+ file->Var32(&MatrixMode);
+
+ file->VarArray(ProjMatrix, 16*4);
+ file->VarArray(PosMatrix, 16*4);
+ file->VarArray(VecMatrix, 16*4);
+ file->VarArray(TexMatrix, 16*4);
+
+ file->VarArray(ProjMatrixStack, 16*4);
+ file->VarArray(PosMatrixStack, 32*16*4);
+ file->VarArray(VecMatrixStack, 32*16*4);
+ file->VarArray(TexMatrixStack, 16*4);
+
+ file->Var32((u32*)&ProjMatrixStackPointer);
+ file->Var32((u32*)&PosMatrixStackPointer);
+ file->Var32((u32*)&TexMatrixStackPointer);
+
+ file->VarArray(Viewport, sizeof(Viewport));
+
+ file->VarArray(PosTestResult, 4*4);
+ file->VarArray(VecTestResult, 2*3);
+
+ file->Var32(&VertexNum);
+ file->Var32(&VertexNumInPoly);
+ file->Var32(&NumConsecutivePolygons);
+
+ for (int i = 0; i < 4; i++)
+ {
+ Vertex* vtx = &TempVertexBuffer[i];
+
+ file->VarArray(vtx->Position, sizeof(s32)*4);
+ file->VarArray(vtx->Color, sizeof(s32)*3);
+ file->VarArray(vtx->TexCoords, sizeof(s16)*2);
+
+ file->Var32((u32*)&vtx->Clipped);
+
+ file->VarArray(vtx->FinalPosition, sizeof(s32)*2);
+ file->VarArray(vtx->FinalColor, sizeof(s32)*3);
+ }
+
+ if (file->Saving)
+ {
+ u32 id;
+ if (LastStripPolygon) id = (u32)((LastStripPolygon - (&PolygonRAM[0])) / sizeof(Polygon));
+ else id = -1;
+ file->Var32(&id);
+ }
+ else
+ {
+ u32 id;
+ file->Var32(&id);
+ if (id == -1) LastStripPolygon = NULL;
+ else LastStripPolygon = &PolygonRAM[id];
+ }
+
+ file->Var32(&CurRAMBank);
+ file->Var32(&NumVertices);
+ file->Var32(&NumPolygons);
+ file->Var32(&NumOpaquePolygons);
+
+ file->Var32(&ClearAttr1);
+ file->Var32(&ClearAttr2);
+
+ file->Var32(&FlushRequest);
+ file->Var32(&FlushAttributes);
+
+ for (int i = 0; i < 6144*2; i++)
+ {
+ Vertex* vtx = &VertexRAM[i];
+
+ file->VarArray(vtx->Position, sizeof(s32)*4);
+ file->VarArray(vtx->Color, sizeof(s32)*3);
+ file->VarArray(vtx->TexCoords, sizeof(s16)*2);
+
+ file->Var32((u32*)&vtx->Clipped);
+
+ file->VarArray(vtx->FinalPosition, sizeof(s32)*2);
+ file->VarArray(vtx->FinalColor, sizeof(s32)*3);
+ }
+
+ for(int i = 0; i < 2048*2; i++)
+ {
+ Polygon* poly = &PolygonRAM[i];
+
+ // this is a bit ugly, but eh
+ // we can't save the pointers as-is, that's a bad idea
+ if (file->Saving)
+ {
+ for (int j = 0; j < 10; j++)
+ {
+ Vertex* ptr = poly->Vertices[j];
+ u32 id;
+ if (ptr) id = (u32)((ptr - (&VertexRAM[0])) / sizeof(Vertex));
+ else id = -1;
+ file->Var32(&id);
+ }
+ }
+ else
+ {
+ for (int j = 0; j < 10; j++)
+ {
+ u32 id = -1;
+ file->Var32(&id);
+ if (id == -1) poly->Vertices[j] = NULL;
+ else poly->Vertices[j] = &VertexRAM[id];
+ }
+ }
+
+ file->Var32(&poly->NumVertices);
+
+ file->VarArray(poly->FinalZ, sizeof(s32)*10);
+ file->VarArray(poly->FinalW, sizeof(s32)*10);
+ file->Var32((u32*)&poly->WBuffer);
+
+ file->Var32(&poly->Attr);
+ file->Var32(&poly->TexParam);
+ file->Var32(&poly->TexPalette);
+
+ file->Var32((u32*)&poly->FacingView);
+ file->Var32((u32*)&poly->Translucent);
+
+ file->Var32((u32*)&poly->IsShadowMask);
+ file->Var32((u32*)&poly->IsShadow);
+
+ file->Var32(&poly->VTop);
+ file->Var32(&poly->VBottom);
+ file->Var32((u32*)&poly->YTop);
+ file->Var32((u32*)&poly->YBottom);
+ file->Var32((u32*)&poly->XTop);
+ file->Var32((u32*)&poly->XBottom);
+
+ file->Var32(&poly->SortKey);
+ }
+
+ // 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;
+ UpdateClipMatrix();
+
+ CurVertexRAM = &VertexRAM[CurRAMBank ? 6144 : 0];
+ CurPolygonRAM = &PolygonRAM[CurRAMBank ? 2048 : 0];
+
+ // better safe than sorry, I guess
+ // might cause a blank frame but atleast it won't shit itself
+ RenderNumPolygons = 0;
+ }
+}
+
void MatrixLoadIdentity(s32* m)
@@ -487,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)
{
@@ -634,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
@@ -683,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)
@@ -916,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;
@@ -996,9 +1316,12 @@ void SubmitVertex()
}
break;
}
+
+ VertexPipeline = 7;
+ AddCycles(3);
}
-s32 CalculateLighting()
+void CalculateLighting()
{
if ((TexParam >> 30) == 2)
{
@@ -1067,8 +1390,9 @@ s32 CalculateLighting()
c++;
}
- // checkme: cycle count
- return c;
+ if (c < 1) c = 1;
+ NormalPipeline = 7;
+ AddCycles(c);
}
@@ -1078,6 +1402,7 @@ void BoxTest(u32* params)
Vertex face[10];
int res;
+ AddCycles(254);
GXStat &= ~(1<<1);
s16 x0 = (s16)(params[0] & 0xFFFF);
@@ -1177,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)
@@ -1196,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);
}
@@ -1210,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);
@@ -1249,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();
}
@@ -1264,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++;
@@ -1272,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)
{
@@ -1288,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;
@@ -1383,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
@@ -1400,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;
@@ -1426,6 +1793,7 @@ void ExecuteCommand()
{
MatrixLoadIdentity(ProjMatrix);
ClipMatrixDirty = true;
+ AddCycles(18);
}
else if (MatrixMode == 3)
MatrixLoadIdentity(TexMatrix);
@@ -1435,6 +1803,7 @@ void ExecuteCommand()
if (MatrixMode == 2)
MatrixLoadIdentity(VecMatrix);
ClipMatrixDirty = true;
+ AddCycles(18);
}
break;
@@ -1443,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;
@@ -1460,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;
@@ -1477,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;
@@ -1497,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;
@@ -1517,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;
@@ -1537,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;
@@ -1552,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;
@@ -1580,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
@@ -1662,6 +2069,7 @@ void ExecuteCommand()
VertexColor[1] = MatDiffuse[1];
VertexColor[2] = MatDiffuse[2];
}
+ AddCycles(3);
break;
case 0x31: // specular/emission material
@@ -1672,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
@@ -1685,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
@@ -1694,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
@@ -1710,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;
@@ -1718,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
@@ -1753,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;
@@ -1782,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);
@@ -2077,7 +2527,7 @@ void Write8(u32 addr, u8 val)
GXStat &= ~0x8000;
ProjMatrixStackPointer = 0;
//PosMatrixStackPointer = 0;
- TexMatrixStackPointer = 0;
+ TexMatrixStackPointer = 0; // CHECKME
}
return;
case 0x04000603:
@@ -2142,7 +2592,7 @@ void Write16(u32 addr, u16 val)
GXStat &= ~0x8000;
ProjMatrixStackPointer = 0;
//PosMatrixStackPointer = 0;
- TexMatrixStackPointer = 0;
+ TexMatrixStackPointer = 0; // CHECKME
}
return;
case 0x04000602:
@@ -2212,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 3ee1a7f..b74e421 100644
--- a/src/GPU3D.h
+++ b/src/GPU3D.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -20,6 +20,7 @@
#define GPU3D_H
#include <array>
+#include "Savestate.h"
namespace GPU3D
{
@@ -85,8 +86,11 @@ bool Init();
void DeInit();
void Reset();
+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 8f7d041..daf5cdd 100644
--- a/src/GPU3D_Soft.cpp
+++ b/src/GPU3D_Soft.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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++)
{
@@ -1229,12 +1244,21 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
continue;
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
- StencilBuffer[256*(y&0x1) + x] = 1;
+ StencilBuffer[256*(y&0x1) + x] |= 0x1;
+
+ if (dstattr & 0x3)
+ {
+ pixeladdr += BufferSize;
+ if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr]))
+ StencilBuffer[256*(y&0x1) + x] |= 0x2;
+ }
}
// 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++)
{
@@ -1247,11 +1271,19 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
StencilBuffer[256*(y&0x1) + x] = 1;
+
+ if (dstattr & 0x3)
+ {
+ pixeladdr += BufferSize;
+ if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr]))
+ StencilBuffer[256*(y&0x1) + x] |= 0x2;
+ }
}
// 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++)
{
@@ -1268,6 +1300,13 @@ void RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
StencilBuffer[256*(y&0x1) + x] = 1;
+
+ if (dstattr & 0x3)
+ {
+ pixeladdr += BufferSize;
+ if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr]))
+ StencilBuffer[256*(y&0x1) + x] |= 0x2;
+ }
}
rp->XL = rp->SlopeL.Step();
@@ -1286,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
@@ -1415,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;
@@ -1425,18 +1466,23 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y)
for (; x < xlimit; x++)
{
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
+ u32 dstattr = AttrBuffer[pixeladdr];
// check stencil buffer for shadows
if (polygon->IsShadow)
{
- if (StencilBuffer[256*(y&0x1) + x] == 0)
+ u8 stencil = StencilBuffer[256*(y&0x1) + x];
+ if (!stencil)
continue;
+ if (!(stencil & 0x1))
+ pixeladdr += BufferSize;
+ if (!(stencil & 0x2))
+ dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges
}
interpX.SetX(x);
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
- u32 dstattr = AttrBuffer[pixeladdr];
// if depth test against the topmost pixel fails, test
// against the pixel underneath
@@ -1509,23 +1555,30 @@ 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++)
{
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
+ u32 dstattr = AttrBuffer[pixeladdr];
// check stencil buffer for shadows
if (polygon->IsShadow)
{
- if (StencilBuffer[256*(y&0x1) + x] == 0)
+ u8 stencil = StencilBuffer[256*(y&0x1) + x];
+ if (!stencil)
continue;
+ if (!(stencil & 0x1))
+ pixeladdr += BufferSize;
+ if (!(stencil & 0x2))
+ dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges
}
interpX.SetX(x);
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
- u32 dstattr = AttrBuffer[pixeladdr];
// if depth test against the topmost pixel fails, test
// against the pixel underneath
@@ -1572,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;
@@ -1582,18 +1636,23 @@ void RenderPolygonScanline(RendererPolygon* rp, s32 y)
for (; x < xlimit; x++)
{
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
+ u32 dstattr = AttrBuffer[pixeladdr];
// check stencil buffer for shadows
if (polygon->IsShadow)
{
- if (StencilBuffer[256*(y&0x1) + x] == 0)
+ u8 stencil = StencilBuffer[256*(y&0x1) + x];
+ if (!stencil)
continue;
+ if (!(stencil & 0x1))
+ pixeladdr += BufferSize;
+ if (!(stencil & 0x2))
+ dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges
}
interpX.SetX(x);
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
- u32 dstattr = AttrBuffer[pixeladdr];
// if depth test against the topmost pixel fails, test
// against the pixel underneath
diff --git a/src/NDS.cpp b/src/NDS.cpp
index 34eccf7..98c2924 100644
--- a/src/NDS.cpp
+++ b/src/NDS.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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;
@@ -57,7 +88,7 @@ u32 CPUStop;
u8 ARM9BIOS[0x1000];
u8 ARM7BIOS[0x4000];
-u8 MainRAM[0x400000];
+u8 MainRAM[MAIN_RAM_SIZE];
u8 SharedWRAM[0x8000];
u8 WRAMCnt;
@@ -82,6 +113,8 @@ u8 PostFlag7;
u16 PowerControl9;
u16 PowerControl7;
+u16 WifiWaitCnt;
+
u16 ARM7BIOSProt;
Timer Timers[8];
@@ -112,10 +145,17 @@ u16 RCnt;
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);
@@ -159,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];
@@ -189,15 +324,15 @@ void SetupDirectBoot()
ARM9Write32(0x027FFE00+i, tmp);
}
- ARM9Write32(0x027FF800, 0x00001FC2);
- ARM9Write32(0x027FF804, 0x00001FC2);
+ ARM9Write32(0x027FF800, NDSCart::CartID);
+ ARM9Write32(0x027FF804, NDSCart::CartID);
ARM9Write16(0x027FF808, *(u16*)&NDSCart::CartROM[0x15E]);
ARM9Write16(0x027FF80A, *(u16*)&NDSCart::CartROM[0x6C]);
ARM9Write16(0x027FF850, 0x5835);
- ARM9Write32(0x027FFC00, 0x00001FC2);
- ARM9Write32(0x027FFC04, 0x00001FC2);
+ ARM9Write32(0x027FFC00, NDSCart::CartID);
+ ARM9Write32(0x027FFC04, NDSCart::CartID);
ARM9Write16(0x027FFC08, *(u16*)&NDSCart::CartROM[0x15E]);
ARM9Write16(0x027FFC0A, *(u16*)&NDSCart::CartROM[0x6C]);
@@ -205,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;
@@ -233,8 +368,12 @@ void SetupDirectBoot()
// checkme
RCnt = 0x8000;
+ NDSCart::SPICnt = 0x8000;
+
SPU::SetBias(0x200);
+ SetWifiWaitCnt(0x0030);
+
ARM7BIOSProt = 0x1204;
SPI_Firmware::SetupDirectBoot();
@@ -245,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");
@@ -262,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");
@@ -279,7 +429,12 @@ void Reset()
fclose(f);
}
- memset(MainRAM, 0, 0x400000);
+ ARM9->SetClockShift(1);
+ ARM7->SetClockShift(0);
+
+ InitTimings();
+
+ memset(MainRAM, 0, MAIN_RAM_SIZE);
memset(SharedWRAM, 0, 0x8000);
memset(ARM7WRAM, 0, 0x10000);
@@ -289,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;
@@ -302,6 +458,9 @@ void Reset()
PowerControl9 = 0x0001;
PowerControl7 = 0x0001;
+ WifiWaitCnt = 0xFFFF; // temp
+ SetWifiWaitCnt(0);
+
ARM7BIOSProt = 0;
IPCSync9 = 0;
@@ -316,7 +475,6 @@ void Reset()
ARM9->Reset();
ARM7->Reset();
- CP15::Reset();
CPUStop = 0;
@@ -354,9 +512,198 @@ void Stop()
SPU::Stop();
}
-bool LoadROM(const char* path, bool direct)
+bool DoSavestate_Scheduler(Savestate* file)
{
- if (NDSCart::LoadROM(path, direct))
+ // this is a bit of a hack
+ // but uh, your local coder realized that the scheduler list contains function pointers
+ // and that storing those as-is is not a very good idea
+ // unless you want it to crash and burn
+
+ // this is the solution your local coder came up with.
+ // it's gross but I think it's the best solution for this problem.
+ // just remember to add here if you add more event callbacks, kay?
+ // atleast until we come up with something more elegant.
+
+ void (*eventfuncs[])(u32) =
+ {
+ GPU::StartScanline, GPU::StartHBlank, GPU::FinishFrame,
+ SPU::Mix,
+ Wifi::USTimer,
+
+ GPU::DisplayFIFO,
+ NDSCart::ROMPrepareData, NDSCart::ROMEndTransfer,
+ NDSCart::SPITransferDone,
+ SPI::TransferDone,
+ DivDone,
+ SqrtDone,
+
+ NULL
+ };
+
+ int len = Event_MAX;
+ if (file->Saving)
+ {
+ for (int i = 0; i < len; i++)
+ {
+ SchedEvent* evt = &SchedList[i];
+
+ u32 funcid = -1;
+ if (evt->Func)
+ {
+ for (int j = 0; eventfuncs[j]; j++)
+ {
+ if (evt->Func == eventfuncs[j])
+ {
+ funcid = j;
+ break;
+ }
+ }
+ if (funcid == -1)
+ {
+ printf("savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK STAPLEBUTTER.\n", i);
+ return false;
+ }
+ }
+
+ file->Var32(&funcid);
+ file->Var32((u32*)&evt->WaitCycles);
+ file->Var32(&evt->Param);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < len; i++)
+ {
+ SchedEvent* evt = &SchedList[i];
+
+ u32 funcid;
+ file->Var32(&funcid);
+
+ if (funcid != -1)
+ {
+ for (int j = 0; ; j++)
+ {
+ if (!eventfuncs[j])
+ {
+ printf("savestate: VERY BAD!!!!!! EVENT FUNCTION POINTER ID %d IS OUT OF RANGE. HAX?????\n", j);
+ return false;
+ }
+ if (j == funcid) break;
+ }
+
+ evt->Func = eventfuncs[funcid];
+ }
+ else
+ evt->Func = NULL;
+
+ file->Var32((u32*)&evt->WaitCycles);
+ file->Var32(&evt->Param);
+ }
+ }
+
+ return true;
+}
+
+bool DoSavestate(Savestate* file)
+{
+ file->Section("NDSG");
+
+ file->VarArray(MainRAM, 0x400000);
+ file->VarArray(SharedWRAM, 0x8000);
+ file->VarArray(ARM7WRAM, 0x10000);
+
+ file->VarArray(ExMemCnt, 2*sizeof(u16));
+ 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));
+
+ file->Var8(&PostFlag9);
+ file->Var8(&PostFlag7);
+ file->Var16(&PowerControl9);
+ file->Var16(&PowerControl7);
+
+ file->Var16(&ARM7BIOSProt);
+
+ file->Var16(&IPCSync9);
+ file->Var16(&IPCSync7);
+ file->Var16(&IPCFIFOCnt9);
+ file->Var16(&IPCFIFOCnt7);
+ IPCFIFO9->DoSavestate(file);
+ IPCFIFO7->DoSavestate(file);
+
+ file->Var16(&DivCnt);
+ file->Var16(&SqrtCnt);
+
+ file->Var32(&CPUStop);
+
+ for (int i = 0; i < 8; i++)
+ {
+ Timer* timer = &Timers[i];
+
+ file->Var16(&timer->Reload);
+ file->Var16(&timer->Cnt);
+ file->Var32(&timer->Counter);
+ file->Var32(&timer->CycleShift);
+ }
+ file->VarArray(TimerCheckMask, 2*sizeof(u8));
+
+ file->VarArray(DMA9Fill, 4*sizeof(u32));
+
+ if (!DoSavestate_Scheduler(file)) return false;
+ file->Var32(&SchedListMask);
+ file->Var32((u32*)&CurIterationCycles);
+ file->Var32((u32*)&ARM7Offset);
+
+ // TODO: save KeyInput????
+ file->Var16(&KeyCnt);
+ file->Var16(&RCnt);
+
+
+ for (int i = 0; i < 8; i++)
+ DMAs[i]->DoSavestate(file);
+
+ file->Var8(&WRAMCnt);
+
+ if (!file->Saving)
+ {
+ // 'dept of redundancy dept'
+ // but we do need to update the mappings
+ 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);
+
+ NDSCart::DoSavestate(file);
+ GPU::DoSavestate(file);
+ SPU::DoSavestate(file);
+ SPI::DoSavestate(file);
+ RTC::DoSavestate(file);
+ Wifi::DoSavestate(file);
+
+ return true;
+}
+
+bool LoadROM(const char* path, const char* sram, bool direct)
+{
+ if (NDSCart::LoadROM(path, sram, direct))
{
Running = true;
return true;
@@ -374,6 +721,12 @@ void LoadBIOS()
Running = true;
}
+void RelocateSave(const char* path, bool write)
+{
+ printf("SRAM: relocating to %s (write=%s)\n", path, write?"true":"false");
+ NDSCart::RelocateSave(path, write);
+}
+
void CalcIterationCycles()
{
@@ -408,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 & 0xFFFF)
+ if (CPUStop & 0x80000000)
+ {
+ // GXFIFO stall
+ // we just run the GPU and the timers.
+ // the rest of the hardware is driven by the event scheduler.
+
+ 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)
@@ -478,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;
@@ -525,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()
{
@@ -570,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);
@@ -614,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;
@@ -666,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];
@@ -675,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);
}
@@ -693,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;
@@ -735,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));
}
@@ -890,7 +1576,7 @@ void debug(u32 param)
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
/*FILE*
- shit = fopen("debug/pictochat.bin", "wb");
+ shit = fopen("debug/jam.bin", "wb");
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
{
u32 val = ARM7Read32(i);
@@ -916,11 +1602,17 @@ u8 ARM9Read8(u32 addr)
switch (addr & 0xFF000000)
{
case 0x02000000:
- return *(u8*)&MainRAM[addr & 0x3FFFFF];
+ 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);
@@ -929,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;
}
@@ -965,11 +1661,17 @@ u16 ARM9Read16(u32 addr)
switch (addr & 0xFF000000)
{
case 0x02000000:
- return *(u16*)&MainRAM[addr & 0x3FFFFF];
+ 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);
@@ -978,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;
}
@@ -1014,11 +1720,17 @@ u32 ARM9Read32(u32 addr)
switch (addr & 0xFF000000)
{
case 0x02000000:
- return *(u32*)&MainRAM[addr & 0x3FFFFF];
+ 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);
@@ -1027,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;
}
@@ -1058,11 +1774,14 @@ void ARM9Write8(u32 addr, u8 val)
switch (addr & 0xFF000000)
{
case 0x02000000:
- *(u8*)&MainRAM[addr & 0x3FFFFF] = val;
+ *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = 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:
@@ -1072,6 +1791,7 @@ void ARM9Write8(u32 addr, u8 val)
case 0x05000000:
case 0x06000000:
case 0x07000000:
+ // checkme
return;
}
@@ -1083,11 +1803,14 @@ void ARM9Write16(u32 addr, u16 val)
switch (addr & 0xFF000000)
{
case 0x02000000:
- *(u16*)&MainRAM[addr & 0x3FFFFF] = val;
+ *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = 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:
@@ -1101,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;
@@ -1122,11 +1844,14 @@ void ARM9Write32(u32 addr, u32 val)
switch (addr & 0xFF000000)
{
case 0x02000000:
- *(u32*)&MainRAM[addr & 0x3FFFFF] = val;
- return;
+ *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
+ 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:
@@ -1140,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;
@@ -1156,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)
@@ -1174,11 +1928,17 @@ u8 ARM7Read8(u32 addr)
{
case 0x02000000:
case 0x02800000:
- return *(u8*)&MainRAM[addr & 0x3FFFFF];
+ 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];
@@ -1189,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]);
@@ -1211,11 +1984,17 @@ u16 ARM7Read16(u32 addr)
{
case 0x02000000:
case 0x02800000:
- return *(u16*)&MainRAM[addr & 0x3FFFFF];
+ 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];
@@ -1224,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]);
@@ -1251,11 +2047,17 @@ u32 ARM7Read32(u32 addr)
{
case 0x02000000:
case 0x02800000:
- return *(u32*)&MainRAM[addr & 0x3FFFFF];
+ 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];
@@ -1264,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]);
@@ -1281,13 +2100,20 @@ void ARM7Write8(u32 addr, u8 val)
{
case 0x02000000:
case 0x02800000:
- *(u8*)&MainRAM[addr & 0x3FFFFF] = val;
+ *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = 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;
@@ -1312,13 +2138,20 @@ void ARM7Write16(u32 addr, u16 val)
{
case 0x02000000:
case 0x02800000:
- *(u16*)&MainRAM[addr & 0x3FFFFF] = val;
+ *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = 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;
@@ -1329,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:
@@ -1347,13 +2184,20 @@ void ARM7Write32(u32 addr, u32 val)
{
case 0x02000000:
case 0x02800000:
- *(u32*)&MainRAM[addr & 0x3FFFFF] = val;
+ *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = 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;
@@ -1364,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:
@@ -1377,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;
+}
+
@@ -1454,7 +2347,7 @@ u8 ARM9IORead8(u32 addr)
return GPU3D::Read8(addr);
}
- printf("unknown ARM9 IO read8 %08X\n", addr);
+ printf("unknown ARM9 IO read8 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@@ -1616,6 +2509,8 @@ u32 ARM9IORead32(u32 addr)
case 0x04000130: return (KeyInput & 0xFFFF) | (KeyCnt << 16);
+ case 0x04000180: return IPCSync9;
+
case 0x040001A0: return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16);
case 0x040001A4: return NDSCart::ROMCnt;
@@ -1651,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)
{
@@ -1690,7 +2588,7 @@ u32 ARM9IORead32(u32 addr)
return GPU3D::Read32(addr);
}
- printf("unknown ARM9 IO read32 %08X\n", addr);
+ printf("unknown ARM9 IO read32 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@@ -1770,7 +2668,7 @@ void ARM9IOWrite8(u32 addr, u8 val)
return;
}
- printf("unknown ARM9 IO write8 %08X %02X\n", addr, val);
+ printf("unknown ARM9 IO write8 %08X %02X %08X\n", addr, val, ARM9->R[15]);
}
void ARM9IOWrite16(u32 addr, u16 val)
@@ -1872,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;
@@ -1931,7 +2830,7 @@ void ARM9IOWrite16(u32 addr, u16 val)
return;
}
- printf("unknown ARM9 IO write16 %08X %04X %08X\n", addr, val, ARM9->R[14]);
+ printf("unknown ARM9 IO write16 %08X %04X %08X\n", addr, val, ARM9->R[15]);
}
void ARM9IOWrite32(u32 addr, u32 val)
@@ -1983,6 +2882,9 @@ void ARM9IOWrite32(u32 addr, u32 val)
case 0x04000130:
KeyCnt = val >> 16;
return;
+ case 0x04000180:
+ ARM9IOWrite16(addr, val);
+ return;
case 0x04000188:
if (IPCFIFOCnt9 & 0x8000)
@@ -2081,7 +2983,7 @@ void ARM9IOWrite32(u32 addr, u32 val)
return;
}
- printf("unknown ARM9 IO write32 %08X %08X\n", addr, val);
+ printf("unknown ARM9 IO write32 %08X %08X %08X\n", addr, val, ARM9->R[15]);
}
@@ -2126,7 +3028,7 @@ u8 ARM7IORead8(u32 addr)
return SPU::Read8(addr);
}
- printf("unknown ARM7 IO read8 %08X\n", addr);
+ printf("unknown ARM7 IO read8 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -2189,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;
@@ -2203,7 +3107,7 @@ u16 ARM7IORead16(u32 addr)
return SPU::Read16(addr);
}
- printf("unknown ARM7 IO read16 %08X %08X\n", addr, ARM9->R[15]);
+ printf("unknown ARM7 IO read16 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -2235,6 +3139,8 @@ u32 ARM7IORead32(u32 addr)
case 0x04000134: return RCnt | (KeyCnt & 0xFFFF0000);
case 0x04000138: return RTC::Read();
+ case 0x04000180: return IPCSync7;
+
case 0x040001A0: return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16);
case 0x040001A4: return NDSCart::ROMCnt;
@@ -2287,7 +3193,7 @@ u32 ARM7IORead32(u32 addr)
return SPU::Read32(addr);
}
- printf("unknown ARM7 IO read32 %08X\n", addr);
+ printf("unknown ARM7 IO read32 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -2349,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;
}
@@ -2359,7 +3268,7 @@ void ARM7IOWrite8(u32 addr, u8 val)
return;
}
- printf("unknown ARM7 IO write8 %08X %02X\n", addr, val);
+ printf("unknown ARM7 IO write8 %08X %02X %08X\n", addr, val, ARM7->R[15]);
}
void ARM7IOWrite16(u32 addr, u16 val)
@@ -2452,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;
@@ -2480,7 +3393,7 @@ void ARM7IOWrite16(u32 addr, u16 val)
return;
}
- printf("unknown ARM7 IO write16 %08X %04X\n", addr, val);
+ printf("unknown ARM7 IO write16 %08X %04X %08X\n", addr, val, ARM7->R[15]);
}
void ARM7IOWrite32(u32 addr, u32 val)
@@ -2521,6 +3434,9 @@ void ARM7IOWrite32(u32 addr, u32 val)
case 0x04000134: RCnt = val & 0xFFFF; return;
case 0x04000138: RTC::Write(val & 0xFFFF, false); return;
+ case 0x04000180:
+ ARM7IOWrite16(addr, val);
+ return;
case 0x04000188:
if (IPCFIFOCnt7 & 0x8000)
{
@@ -2579,7 +3495,7 @@ void ARM7IOWrite32(u32 addr, u32 val)
return;
}
- printf("unknown ARM7 IO write32 %08X %08X\n", addr, val);
+ printf("unknown ARM7 IO write32 %08X %08X %08X\n", addr, val, ARM7->R[15]);
}
}
diff --git a/src/NDS.h b/src/NDS.h
index 71f6d41..bfb390f 100644
--- a/src/NDS.h
+++ b/src/NDS.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -19,11 +19,24 @@
#ifndef NDS_H
#define NDS_H
+#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,
@@ -86,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];
@@ -101,16 +124,24 @@ extern u8 ROMSeed1[2*8];
extern u8 ARM9BIOS[0x1000];
extern u8 ARM7BIOS[0x4000];
-extern u8 MainRAM[0x400000];
+#define MAIN_RAM_SIZE 0x400000
+
+extern u8 MainRAM[MAIN_RAM_SIZE];
bool Init();
void DeInit();
void Reset();
void Stop();
-bool LoadROM(const char* path, bool direct);
+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();
+void RelocateSave(const char* path, bool write);
u32 RunFrame();
@@ -121,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);
@@ -135,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);
@@ -151,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);
@@ -158,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 474a13b..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"
@@ -31,19 +32,10 @@ namespace NDSCart_SRAM
u8* SRAM;
u32 SRAMLength;
-char SRAMPath[256];
+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;
@@ -63,36 +55,70 @@ 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 LoadSave(char* path)
+void DoSavestate(Savestate* file)
{
- if (SRAM) delete[] SRAM;
- if (Discover_Buffer) delete[] Discover_Buffer;
+ file->Section("NDCS");
+
+ // 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????
+
+ // also the SRAM size shouldn't change. unless something something autodetect something but fuck that code.
+
+ //if (!file->Saving && SRAMLength)
+ // delete[] SRAM;
+
+ u32 oldlen = SRAMLength;
+
+ file->Var32(&SRAMLength);
+ if (SRAMLength != oldlen)
+ {
+ printf("savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength);
+ printf("oh well. loading it anyway. adsfgdsf\n");
+
+ if (oldlen) delete[] SRAM;
+ if (SRAMLength) SRAM = new u8[SRAMLength];
+ }
+ if (SRAMLength)
+ {
+ //if (!file->Saving)
+ // SRAM = new u8[SRAMLength];
- Discover_Buffer = NULL;
- Discover_LastPC = 0;
- Discover_AddrLength = 0x7FFFFFFF;
- Discover_LikelySize = 0;
+ file->VarArray(SRAM, SRAMLength);
+ }
+
+ // SPI status shito
+
+ file->Var32(&Hold);
+ file->Var8(&CurCmd);
+ file->Var32(&DataPos);
+ file->Var8(&Data);
- strncpy(SRAMPath, path, 255);
- SRAMPath[255] = '\0';
+ file->Var8(&StatusReg);
+ file->Var32(&Addr);
+}
+
+void LoadSave(const char* path, u32 type)
+{
+ if (SRAM) delete[] SRAM;
+
+ strncpy(SRAMPath, path, 1023);
+ SRAMPath[1023] = '\0';
FILE* f = melon_fopen(path, "rb");
if (f)
@@ -105,32 +131,35 @@ void LoadSave(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;
@@ -139,243 +168,31 @@ void LoadSave(char* path)
StatusReg = 0x00;
}
-u8 Read()
-{
- return Data;
-}
-
-void SetMemoryType()
+void RelocateSave(const char* path, bool write)
{
- SRAMLength = 0;
-
- if (Discover_LikelySize)
+ if (!write)
{
- 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)
+ LoadSave(path, 0); // lazy
return;
+ }
- SRAM = new u8[SRAMLength];
+ strncpy(SRAMPath, path, 1023);
+ SRAMPath[1023] = '\0';
- // replay writes that occured during discovery
- u8 prev_cmd = CurCmd;
- u32 pos = 0;
- while (pos < 256*1024)
+ FILE* f = melon_fopen(path, "wb");
+ if (!f)
{
- 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++;
- }
+ printf("NDSCart_SRAM::RelocateSave: failed to create new file. fuck\n");
+ return;
}
- CurCmd = prev_cmd;
-
- delete[] Discover_Buffer;
- Discover_Buffer = NULL;
+ fwrite(SRAM, SRAMLength, 1, f);
+ fclose(f);
}
-void Write_Discover(u8 val, bool islast)
+u8 Read()
{
- // 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);
- }
- }
+ return Data;
}
void Write_Null(u8 val, bool islast) {}
@@ -654,6 +471,7 @@ u32 DataOutLen;
bool CartInserted;
u8* CartROM;
u32 CartROMSize;
+u32 CartCRC;
u32 CartID;
bool CartIsHomebrew;
@@ -666,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);
@@ -807,14 +631,242 @@ void Reset()
CartID = 0;
CartIsHomebrew = false;
+ ROMCommandHandler = NULL;
+
CmdEncMode = 0;
DataEncMode = 0;
NDSCart_SRAM::Reset();
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("NDSC");
-bool LoadROM(const char* path, bool direct)
+ file->Var16(&SPICnt);
+ file->Var32(&ROMCnt);
+
+ file->VarArray(ROMCommand, 8);
+ file->Var32(&ROMDataOut);
+
+ file->VarArray(DataOut, 0x4000);
+ file->Var32(&DataOutPos);
+ file->Var32(&DataOutLen);
+
+ // cart inserted/len/ROM/etc should be already populated
+ // savestate should be loaded after the right game is loaded
+ // (TODO: system to verify that indeed the right ROM is loaded)
+ // (what to CRC? whole ROM? code binaries? latter would be more convenient for ie. romhaxing)
+
+ file->Var32(&CmdEncMode);
+ file->Var32(&DataEncMode);
+
+ // TODO: check KEY1 shit??
+
+ NDSCart_SRAM::DoSavestate(file);
+}
+
+
+void ApplyDLDIPatch()
+{
+ // TODO: embed patches? let the user choose? default to some builtin driver?
+
+ u32 offset = *(u32*)&CartROM[0x20];
+ u32 size = *(u32*)&CartROM[0x2C];
+
+ u8* binary = &CartROM[offset];
+ u32 dldioffset = 0;
+
+ for (u32 i = 0; i < size; i++)
+ {
+ if (*(u32*)&binary[i ] == 0xBF8DA5ED &&
+ *(u32*)&binary[i+4] == 0x69684320 &&
+ *(u32*)&binary[i+8] == 0x006D6873)
+ {
+ dldioffset = i;
+ break;
+ }
+ }
+
+ if (!dldioffset)
+ {
+ return;
+ }
+
+ printf("DLDI shit found at %08X (%08X)\n", dldioffset, offset+dldioffset);
+
+ FILE* f = fopen("dldi.bin", "rb");
+ if (!f)
+ {
+ printf("no DLDI patch available. oh well\n");
+ return;
+ }
+
+ u32 dldisize;
+ fseek(f, 0, SEEK_END);
+ dldisize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ u8* patch = new u8[dldisize];
+ fread(patch, dldisize, 1, f);
+ fclose(f);
+
+ if (*(u32*)&patch[0] != 0xBF8DA5ED ||
+ *(u32*)&patch[4] != 0x69684320 ||
+ *(u32*)&patch[8] != 0x006D6873)
+ {
+ printf("bad DLDI patch\n");
+ delete[] patch;
+ return;
+ }
+
+ if (patch[0x0D] > binary[dldioffset+0x0F])
+ {
+ printf("DLDI driver ain't gonna fit, sorry\n");
+ delete[] patch;
+ return;
+ }
+
+ printf("existing driver is: %s\n", &binary[dldioffset+0x10]);
+ printf("new driver is: %s\n", &patch[0x10]);
+
+ u32 memaddr = *(u32*)&binary[dldioffset+0x40];
+ if (memaddr == 0)
+ memaddr = *(u32*)&binary[dldioffset+0x68] - 0x80;
+
+ u32 patchbase = *(u32*)&patch[0x40];
+ u32 delta = memaddr - patchbase;
+
+ u32 patchsize = 1 << patch[0x0D];
+ u32 patchend = patchbase + patchsize;
+
+ memcpy(&binary[dldioffset], patch, dldisize);
+
+ *(u32*)&binary[dldioffset+0x40] += delta;
+ *(u32*)&binary[dldioffset+0x44] += delta;
+ *(u32*)&binary[dldioffset+0x48] += delta;
+ *(u32*)&binary[dldioffset+0x4C] += delta;
+ *(u32*)&binary[dldioffset+0x50] += delta;
+ *(u32*)&binary[dldioffset+0x54] += delta;
+ *(u32*)&binary[dldioffset+0x58] += delta;
+ *(u32*)&binary[dldioffset+0x5C] += delta;
+
+ *(u32*)&binary[dldioffset+0x68] += delta;
+ *(u32*)&binary[dldioffset+0x6C] += delta;
+ *(u32*)&binary[dldioffset+0x70] += delta;
+ *(u32*)&binary[dldioffset+0x74] += delta;
+ *(u32*)&binary[dldioffset+0x78] += delta;
+ *(u32*)&binary[dldioffset+0x7C] += delta;
+
+ u8 fixmask = patch[0x0E];
+
+ if (fixmask & 0x01)
+ {
+ u32 fixstart = *(u32*)&patch[0x40] - patchbase;
+ u32 fixend = *(u32*)&patch[0x44] - patchbase;
+
+ for (u32 addr = fixstart; addr < fixend; addr+=4)
+ {
+ u32 val = *(u32*)&binary[dldioffset+addr];
+ if (val >= patchbase && val < patchend)
+ *(u32*)&binary[dldioffset+addr] += delta;
+ }
+ }
+ if (fixmask & 0x02)
+ {
+ u32 fixstart = *(u32*)&patch[0x48] - patchbase;
+ u32 fixend = *(u32*)&patch[0x4C] - patchbase;
+
+ for (u32 addr = fixstart; addr < fixend; addr+=4)
+ {
+ u32 val = *(u32*)&binary[dldioffset+addr];
+ if (val >= patchbase && val < patchend)
+ *(u32*)&binary[dldioffset+addr] += delta;
+ }
+ }
+ if (fixmask & 0x04)
+ {
+ u32 fixstart = *(u32*)&patch[0x50] - patchbase;
+ u32 fixend = *(u32*)&patch[0x54] - patchbase;
+
+ for (u32 addr = fixstart; addr < fixend; addr+=4)
+ {
+ u32 val = *(u32*)&binary[dldioffset+addr];
+ if (val >= patchbase && val < patchend)
+ *(u32*)&binary[dldioffset+addr] += delta;
+ }
+ }
+ if (fixmask & 0x08)
+ {
+ u32 fixstart = *(u32*)&patch[0x58] - patchbase;
+ u32 fixend = *(u32*)&patch[0x5C] - patchbase;
+
+ memset(&binary[dldioffset+fixstart], 0, fixend-fixstart);
+ }
+
+ delete[] patch;
+ printf("applied DLDI patch\n");
+}
+
+
+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
// for now we're lazy
@@ -846,19 +898,62 @@ bool LoadROM(const char* path, 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)
+ {
+ //ApplyDLDIPatch();
+ }
if (direct)
{
+ // TODO: in the case of an already-encrypted secure area, direct boot
+ // needs it decrypted
NDS::SetupDirectBoot();
CmdEncMode = 2;
}
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)
{
@@ -888,16 +983,18 @@ bool LoadROM(const char* path, bool direct)
// save
- char savepath[256];
- strncpy(savepath, path, 255);
- savepath[255] = '\0';
- strncpy(savepath + strlen(path) - 3, "sav", 3);
- printf("Save file: %s\n", savepath);
- NDSCart_SRAM::LoadSave(savepath);
+ printf("Save file: %s\n", sram);
+ NDSCart_SRAM::LoadSave(sram, romparams[1]);
return true;
}
+void RelocateSave(const char* path, bool write)
+{
+ // herp derp
+ NDSCart_SRAM::RelocateSave(path, write);
+}
+
void ReadROM(u32 addr, u32 len, u32 offset)
{
if (!CartInserted) return;
@@ -949,6 +1046,89 @@ void ROMPrepareData(u32 param)
NDS::CheckDMAs(0, 0x05);
}
+
+void ROMCommand_Retail(u8* cmd)
+{
+ switch (cmd[0])
+ {
+ case 0xB7:
+ {
+ u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
+ memset(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)
{
ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x00800000);
@@ -1038,45 +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;
- 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 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/NDSCart.h b/src/NDSCart.h
index 5bec38d..ac8f8fe 100644
--- a/src/NDSCart.h
+++ b/src/NDSCart.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -36,11 +36,16 @@ extern u8 EncSeed1[5];
extern u8* CartROM;
extern u32 CartROMSize;
+extern u32 CartID;
+
bool Init();
void DeInit();
void Reset();
-bool LoadROM(const char* path, bool direct);
+void DoSavestate(Savestate* file);
+
+bool LoadROM(const char* path, const char* sram, bool direct);
+void RelocateSave(const char* path, bool write);
void WriteROMCnt(u32 val);
u32 ReadROMData();
@@ -49,6 +54,10 @@ void WriteSPICnt(u16 val);
u8 ReadSPIData();
void WriteSPIData(u8 val);
+void ROMPrepareData(u32 param);
+void ROMEndTransfer(u32 param);
+void SPITransferDone(u32 param);
+
}
#endif
diff --git a/src/Platform.h b/src/Platform.h
index 7fb6793..091e607 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/RTC.cpp b/src/RTC.cpp
index 3d45bef..cd35643 100644
--- a/src/RTC.cpp
+++ b/src/RTC.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -73,6 +73,30 @@ void Reset()
FreeReg = 0;
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("RTC.");
+
+ file->Var16(&IO);
+
+ file->Var8(&Input);
+ file->Var32(&InputBit);
+ file->Var32(&InputPos);
+
+ file->VarArray(Output, sizeof(Output));
+ file->Var32(&OutputBit);
+ file->Var32(&OutputPos);
+
+ file->Var8(&CurCmd);
+
+ file->Var8(&StatusReg1);
+ file->Var8(&StatusReg2);
+ file->VarArray(Alarm1, sizeof(Alarm1));
+ file->VarArray(Alarm2, sizeof(Alarm2));
+ file->Var8(&ClockAdjust);
+ file->Var8(&FreeReg);
+}
+
u8 BCD(u8 val)
{
diff --git a/src/RTC.h b/src/RTC.h
index 6ada5c1..ebfcda8 100644
--- a/src/RTC.h
+++ b/src/RTC.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -20,6 +20,7 @@
#define RTC_H
#include "types.h"
+#include "Savestate.h"
namespace RTC
{
@@ -27,6 +28,7 @@ namespace RTC
bool Init();
void DeInit();
void Reset();
+void DoSavestate(Savestate* file);
u16 Read();
void Write(u16 val, bool byte);
diff --git a/src/SPI.cpp b/src/SPI.cpp
index 12e9291..7c49555 100644
--- a/src/SPI.cpp
+++ b/src/SPI.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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
{
@@ -193,6 +194,22 @@ void Reset()
StatusReg = 0x00;
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("SPFW");
+
+ // CHECKME/TODO: trust the firmware to stay the same?????
+ // embedding the whole firmware in the savestate would be derpo tho??
+
+ file->Var32(&Hold);
+ file->Var8(&CurCmd);
+ file->Var32(&DataPos);
+ file->Var8(&Data);
+
+ file->Var8(&StatusReg);
+ file->Var32(&Addr);
+}
+
void SetupDirectBoot()
{
NDS::ARM9Write32(0x027FF864, 0);
@@ -308,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;
@@ -360,6 +377,19 @@ void Reset()
RegMasks[4] = 0x0F;
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("SPPW");
+
+ file->Var32(&Hold);
+ file->Var32(&DataPos);
+ file->Var8(&Index);
+ file->Var8(&Data);
+
+ file->VarArray(Registers, 8);
+ file->VarArray(RegMasks, 8); // is that needed??
+}
+
u8 Read()
{
return Data;
@@ -423,6 +453,9 @@ u16 ConvResult;
u16 TouchX, TouchY;
+s16 MicBuffer[1024];
+int MicBufferLen;
+
bool Init()
{
@@ -439,6 +472,19 @@ void Reset()
Data = 0;
ConvResult = 0;
+
+ MicBufferLen = 0;
+}
+
+void DoSavestate(Savestate* file)
+{
+ file->Section("SPTS");
+
+ file->Var32(&DataPos);
+ file->Var8(&ControlByte);
+ file->Var8(&Data);
+
+ file->Var16(&ConvResult);
}
void SetTouchCoords(u16 x, u16 y)
@@ -456,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;
@@ -479,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;
}
@@ -526,6 +609,18 @@ void Reset()
SPI_TSC::Reset();
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("SPIG");
+
+ file->Var16(&Cnt);
+ file->Var32(&CurDevice);
+
+ SPI_Firmware::DoSavestate(file);
+ SPI_Powerman::DoSavestate(file);
+ SPI_TSC::DoSavestate(file);
+}
+
void WriteCnt(u16 val)
{
diff --git a/src/SPI.h b/src/SPI.h
index d122da8..87ed63d 100644
--- a/src/SPI.h
+++ b/src/SPI.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -19,6 +19,8 @@
#ifndef SPI_H
#define SPI_H
+#include "Savestate.h"
+
namespace SPI_Firmware
{
@@ -34,6 +36,7 @@ namespace SPI_TSC
{
void SetTouchCoords(u16 x, u16 y);
+void MicInputFrame(s16* data, int samples);
}
@@ -45,6 +48,7 @@ extern u16 Cnt;
bool Init();
void DeInit();
void Reset();
+void DoSavestate(Savestate* file);
u16 ReadCnt();
void WriteCnt(u16 val);
@@ -52,6 +56,8 @@ void WriteCnt(u16 val);
u8 ReadData();
void WriteData(u8 val);
+void TransferDone(u32 param);
+
}
#endif
diff --git a/src/SPU.cpp b/src/SPU.cpp
index 7f70e65..9862077 100644
--- a/src/SPU.cpp
+++ b/src/SPU.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -24,8 +24,6 @@
// SPU TODO
// * loop mode 3, what does it do?
-// * the FIFO, whatever it is. GBAtek mentions it but gives no details.
-// * consider mixing every sample instead of every 16?
namespace SPU
@@ -121,6 +119,21 @@ void Stop()
memset(OutputBuffer, 0, 2*OutputBufferSize*2);
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("SPU.");
+
+ file->Var16(&Cnt);
+ file->Var8(&MasterVolume);
+ file->Var16(&Bias);
+
+ for (int i = 0; i < 16; i++)
+ Channels[i]->DoSavestate(file);
+
+ Capture[0]->DoSavestate(file);
+ Capture[1]->DoSavestate(file);
+}
+
void SetBias(u16 bias)
{
@@ -154,6 +167,36 @@ void Channel::Reset()
FIFOLevel = 0;
}
+void Channel::DoSavestate(Savestate* file)
+{
+ file->Var32(&Cnt);
+ file->Var32(&SrcAddr);
+ file->Var16(&TimerReload);
+ file->Var32(&LoopPos);
+ file->Var32(&Length);
+
+ file->Var8(&Volume);
+ file->Var8(&VolumeShift);
+ file->Var8(&Pan);
+
+ file->Var32(&Timer);
+ file->Var32((u32*)&Pos);
+ file->Var16((u16*)&CurSample);
+ file->Var16(&NoiseVal);
+
+ file->Var32((u32*)&ADPCMVal);
+ file->Var32((u32*)&ADPCMIndex);
+ file->Var32((u32*)&ADPCMValLoop);
+ file->Var32((u32*)&ADPCMIndexLoop);
+ file->Var8(&ADPCMCurByte);
+
+ file->Var32(&FIFOReadPos);
+ file->Var32(&FIFOWritePos);
+ file->Var32(&FIFOReadOffset);
+ file->Var32(&FIFOLevel);
+ file->VarArray(FIFO, 8*4);
+}
+
void Channel::FIFO_BufferData()
{
u32 totallen = LoopPos + Length;
@@ -161,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;
@@ -212,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();
@@ -226,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>();
@@ -250,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>();
@@ -290,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
{
@@ -367,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++)
@@ -439,6 +476,23 @@ void CaptureUnit::Reset()
FIFOLevel = 0;
}
+void CaptureUnit::DoSavestate(Savestate* file)
+{
+ file->Var8(&Cnt);
+ file->Var32(&DstAddr);
+ file->Var16(&TimerReload);
+ file->Var32(&Length);
+
+ file->Var32(&Timer);
+ file->Var32((u32*)&Pos);
+
+ file->Var32(&FIFOReadPos);
+ file->Var32(&FIFOWritePos);
+ file->Var32(&FIFOWriteOffset);
+ file->Var32(&FIFOLevel);
+ file->VarArray(FIFO, 4*4);
+}
+
void CaptureUnit::FIFO_FlushData()
{
for (u32 i = 0; i < 4; i++)
diff --git a/src/SPU.h b/src/SPU.h
index 85c27ee..a7afbf7 100644
--- a/src/SPU.h
+++ b/src/SPU.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -19,6 +19,8 @@
#ifndef SPU_H
#define SPU_H
+#include "Savestate.h"
+
namespace SPU
{
@@ -27,6 +29,8 @@ void DeInit();
void Reset();
void Stop();
+void DoSavestate(Savestate* file);
+
void SetBias(u16 bias);
void Mix(u32 samples);
@@ -46,6 +50,7 @@ public:
Channel(u32 num);
~Channel();
void Reset();
+ void DoSavestate(Savestate* file);
u32 Num;
@@ -116,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;
@@ -137,6 +145,7 @@ public:
CaptureUnit(u32 num);
~CaptureUnit();
void Reset();
+ void DoSavestate(Savestate* file);
u32 Num;
diff --git a/src/Savestate.cpp b/src/Savestate.cpp
new file mode 100644
index 0000000..e295f4a
--- /dev/null
+++ b/src/Savestate.cpp
@@ -0,0 +1,275 @@
+/*
+ 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 "Savestate.h"
+#include "melon_fopen.h"
+
+/*
+ Savestate format
+
+ header:
+ 00 - magic MELN
+ 04 - version major
+ 06 - version minor
+ 08 - length
+ 0C - game serial
+ 10 - ARM9 binary checksum
+ 14 - ARM7 binary checksum
+ 18 - reserved
+ 1C - reserved
+
+ section header:
+ 00 - section magic
+ 04 - section length
+ 08 - reserved
+ 0C - reserved
+
+ Implementation details
+
+ version difference:
+ * different major means savestate file is incompatible
+ * different minor means adjustments may have to be made
+*/
+
+Savestate::Savestate(char* filename, bool save)
+{
+ char* magic = "MELN";
+
+ Error = false;
+
+ if (save)
+ {
+ Saving = true;
+ file = melon_fopen(filename, "wb");
+ if (!file)
+ {
+ printf("savestate: file %s doesn't exist\n", filename);
+ Error = true;
+ return;
+ }
+
+ VersionMajor = SAVESTATE_MAJOR;
+ VersionMinor = SAVESTATE_MINOR;
+
+ fwrite(magic, 4, 1, file);
+ fwrite(&VersionMajor, 2, 1, file);
+ fwrite(&VersionMinor, 2, 1, file);
+ fseek(file, 8, SEEK_CUR); // length to be fixed later
+ }
+ else
+ {
+ Saving = false;
+ file = melon_fopen(filename, "rb");
+ if (!file)
+ {
+ printf("savestate: file %s doesn't exist\n", filename);
+ Error = true;
+ return;
+ }
+
+ u32 len;
+ fseek(file, 0, SEEK_END);
+ len = (u32)ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ u32 buf = 0;
+
+ fread(&buf, 4, 1, file);
+ if (buf != ((u32*)magic)[0])
+ {
+ printf("savestate: invalid magic %08X\n", buf);
+ Error = true;
+ return;
+ }
+
+ VersionMajor = 0;
+ VersionMinor = 0;
+
+ fread(&VersionMajor, 2, 1, file);
+ if (VersionMajor != SAVESTATE_MAJOR)
+ {
+ printf("savestate: bad version major %d, expecting %d\n", VersionMajor, SAVESTATE_MAJOR);
+ Error = true;
+ return;
+ }
+
+ fread(&VersionMinor, 2, 1, file);
+ // TODO: handle it???
+
+ buf = 0;
+ fread(&buf, 4, 1, file);
+ if (buf != len)
+ {
+ printf("savestate: bad length %d\n", buf);
+ Error = true;
+ return;
+ }
+
+ fseek(file, 4, SEEK_CUR);
+ }
+
+ CurSection = -1;
+}
+
+Savestate::~Savestate()
+{
+ if (Error) return;
+
+ if (Saving)
+ {
+ if (CurSection != -1)
+ {
+ u32 pos = (u32)ftell(file);
+ fseek(file, CurSection+4, SEEK_SET);
+
+ u32 len = pos - CurSection;
+ fwrite(&len, 4, 1, file);
+
+ fseek(file, pos, SEEK_SET);
+ }
+
+ fseek(file, 0, SEEK_END);
+ u32 len = (u32)ftell(file);
+ fseek(file, 8, SEEK_SET);
+ fwrite(&len, 4, 1, file);
+ }
+
+ if (file) fclose(file);
+}
+
+void Savestate::Section(char* magic)
+{
+ if (Error) return;
+
+ if (Saving)
+ {
+ if (CurSection != -1)
+ {
+ u32 pos = (u32)ftell(file);
+ fseek(file, CurSection+4, SEEK_SET);
+
+ u32 len = pos - CurSection;
+ fwrite(&len, 4, 1, file);
+
+ fseek(file, pos, SEEK_SET);
+ }
+
+ CurSection = (u32)ftell(file);
+
+ fwrite(magic, 4, 1, file);
+ fseek(file, 12, SEEK_CUR);
+ }
+ else
+ {
+ fseek(file, 0x10, SEEK_SET);
+
+ for (;;)
+ {
+ u32 buf = 0;
+
+ fread(&buf, 4, 1, file);
+ if (buf != ((u32*)magic)[0])
+ {
+ if (buf == 0)
+ {
+ printf("savestate: section %s not found. blarg\n", magic);
+ return;
+ }
+
+ buf = 0;
+ fread(&buf, 4, 1, file);
+ fseek(file, buf-8, SEEK_CUR);
+ continue;
+ }
+
+ fseek(file, 12, SEEK_CUR);
+ break;
+ }
+ }
+}
+
+void Savestate::Var8(u8* var)
+{
+ if (Error) return;
+
+ if (Saving)
+ {
+ fwrite(var, 1, 1, file);
+ }
+ else
+ {
+ fread(var, 1, 1, file);
+ }
+}
+
+void Savestate::Var16(u16* var)
+{
+ if (Error) return;
+
+ if (Saving)
+ {
+ fwrite(var, 2, 1, file);
+ }
+ else
+ {
+ fread(var, 2, 1, file);
+ }
+}
+
+void Savestate::Var32(u32* var)
+{
+ if (Error) return;
+
+ if (Saving)
+ {
+ fwrite(var, 4, 1, file);
+ }
+ else
+ {
+ fread(var, 4, 1, file);
+ }
+}
+
+void Savestate::Var64(u64* var)
+{
+ if (Error) return;
+
+ if (Saving)
+ {
+ fwrite(var, 8, 1, file);
+ }
+ else
+ {
+ fread(var, 8, 1, file);
+ }
+}
+
+void Savestate::VarArray(void* data, u32 len)
+{
+ if (Error) return;
+
+ if (Saving)
+ {
+ fwrite(data, len, 1, file);
+ }
+ else
+ {
+ fread(data, len, 1, file);
+ }
+}
diff --git a/src/Savestate.h b/src/Savestate.h
new file mode 100644
index 0000000..be96d78
--- /dev/null
+++ b/src/Savestate.h
@@ -0,0 +1,62 @@
+/*
+ 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 SAVESTATE_H
+#define SAVESTATE_H
+
+#include <stdio.h>
+#include "types.h"
+
+#define SAVESTATE_MAJOR 3
+#define SAVESTATE_MINOR 0
+
+class Savestate
+{
+public:
+ Savestate(char* filename, bool save);
+ ~Savestate();
+
+ bool Error;
+
+ bool Saving;
+ u32 VersionMajor;
+ u32 VersionMinor;
+
+ u32 CurSection;
+
+ void Section(char* magic);
+
+ void Var8(u8* var);
+ void Var16(u16* var);
+ void Var32(u32* var);
+ void Var64(u64* var);
+
+ 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;
+};
+
+#endif // SAVESTATE_H
diff --git a/src/Wifi.cpp b/src/Wifi.cpp
index ed92ad8..0e73422 100644
--- a/src/Wifi.cpp
+++ b/src/Wifi.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -213,6 +213,40 @@ void Reset()
WifiAP::Reset();
}
+void DoSavestate(Savestate* file)
+{
+ file->Section("WIFI");
+
+ // berp.
+ // not sure we're saving enough shit at all there.
+ // also: savestate and wifi can't fucking work together!!
+ // or it can but you would be disconnected
+
+ file->VarArray(RAM, 0x2000);
+ file->VarArray(IO, 0x1000);
+
+ file->Var16(&Random);
+
+ file->VarArray(BBRegs, 0x100);
+ file->VarArray(BBRegsRO, 0x100);
+
+ file->Var8(&RFVersion);
+ file->VarArray(RFRegs, 4*0x40);
+
+ file->Var64(&USCounter);
+ file->Var64(&USCompare);
+ file->Var32((u32*)&BlockBeaconIRQ14);
+
+ file->Var32(&ComStatus);
+ file->Var32(&TXCurSlot);
+ file->Var32(&RXCounter);
+
+ file->Var32((u32*)&MPReplyTimer);
+ file->Var32((u32*)&MPNumReplies);
+
+ file->Var32(&CmdCounter);
+}
+
void SetIRQ(u32 irq)
{
diff --git a/src/Wifi.h b/src/Wifi.h
index 72334e0..72517a1 100644
--- a/src/Wifi.h
+++ b/src/Wifi.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -19,6 +19,8 @@
#ifndef WIFI_H
#define WIFI_H
+#include "Savestate.h"
+
namespace Wifi
{
@@ -146,6 +148,7 @@ extern bool MPInited;
bool Init();
void DeInit();
void Reset();
+void DoSavestate(Savestate* file);
void StartTX_Beacon();
diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp
index 963b4d2..3e8bdb0 100644
--- a/src/WifiAP.cpp
+++ b/src/WifiAP.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/WifiAP.h b/src/WifiAP.h
index fccfa1a..6f963c4 100644
--- a/src/WifiAP.h
+++ b/src/WifiAP.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
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 eedea10..2333967 100644
--- a/src/CP15.h
+++ b/src/libui_sdl/DlgAudioSettings.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -16,29 +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 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 8cb2ccc..42c95b8 100644
--- a/src/libui_sdl/DlgEmuSettings.cpp
+++ b/src/libui_sdl/DlgEmuSettings.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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/DlgEmuSettings.h b/src/libui_sdl/DlgEmuSettings.h
index f26ae14..f8da148 100644
--- a/src/libui_sdl/DlgEmuSettings.h
+++ b/src/libui_sdl/DlgEmuSettings.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/libui_sdl/DlgInputConfig.cpp b/src/libui_sdl/DlgInputConfig.cpp
index 5a98a91..287d7e3 100644
--- a/src/libui_sdl/DlgInputConfig.cpp
+++ b/src/libui_sdl/DlgInputConfig.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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;
+ }
- memcpy(keymap, Config::KeyMapping, sizeof(int)*12);
- memcpy(joymap, Config::JoyMapping, sizeof(int)*12);
+ openedmask |= mask;
- win = uiNewWindow("Input config - melonDS", 600, 400, 0, 0);
- uiWindowSetMargined(win, 1);
- uiWindowOnClosing(win, OnCloseWindow, NULL);
- uiWindowOnGetFocus(win, OnGetFocus, NULL);
- uiWindowOnLoseFocus(win, OnLoseFocus, NULL);
+ dlg->type = type;
+ dlg->pollid = -1;
- areahandler.Draw = OnAreaDraw;
- areahandler.MouseEvent = OnAreaMouseEvent;
- areahandler.MouseCrossed = OnAreaMouseCrossed;
- areahandler.DragBroken = OnAreaDragBroken;
- areahandler.KeyEvent = OnAreaKeyEvent;
- areahandler.Resize = OnAreaResize;
+ 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);
+ }
+
+ uiControl(dlg->win)->UserData = dlg;
+
+ uiWindowSetMargined(dlg->win, 1);
+ uiWindowOnClosing(dlg->win, OnCloseWindow, NULL);
+ uiWindowOnGetFocus(dlg->win, OnGetFocus, NULL);
+ uiWindowOnLoseFocus(dlg->win, OnLoseFocus, NULL);
+
+ 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));
{
@@ -309,22 +376,23 @@ void Open()
uiBoxAppend(in_ctrl, uiControl(g_key), 1);
uiGrid* b_key = uiNewGrid();
uiGroupSetChild(g_key, uiControl(b_key));
-
+
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 33529c2..2b0b6cb 100644
--- a/src/libui_sdl/DlgInputConfig.h
+++ b/src/libui_sdl/DlgInputConfig.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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 1702685..733114e 100644
--- a/src/libui_sdl/Platform.cpp
+++ b/src/libui_sdl/Platform.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
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 ea31676..2da9bab 100644
--- a/src/libui_sdl/libui/unix/area.c
+++ b/src/libui_sdl/libui/unix/area.c
@@ -77,6 +77,10 @@ static void areaWidget_init(areaWidget *aw)
static void areaWidget_dispose(GObject *obj)
{
+ // remove any draw order that might still be pending
+ areaWidget *aw = areaWidget(obj);
+ while (g_idle_remove_by_data(aw->a));
+
G_OBJECT_CLASS(areaWidget_parent_class)->dispose(obj);
}
@@ -519,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/menu.c b/src/libui_sdl/libui/unix/menu.c
index d6aa398..d641426 100644
--- a/src/libui_sdl/libui/unix/menu.c
+++ b/src/libui_sdl/libui/unix/menu.c
@@ -305,13 +305,13 @@ static void appendMenuItem(GtkMenuShell *submenu, uiMenuItem *item, uiWindow *w)
{
int j;
uiMenu* m;
- GtkWidget *submenu;
+ GtkWidget *c_submenu;
m = item->popupchild;
- submenu = gtk_menu_new();
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+ c_submenu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), c_submenu);
for (j = 0; j < m->items->len; j++)
- appendMenuItem(GTK_MENU_SHELL(submenu), g_array_index(m->items, uiMenuItem *, j), w);
+ appendMenuItem(GTK_MENU_SHELL(c_submenu), g_array_index(m->items, uiMenuItem *, j), w);
}
}
@@ -347,6 +347,7 @@ GtkWidget *makeMenubar(uiWindow *w)
struct freeMenuItemData {
GArray *items;
guint i;
+ guint* parent_i;
};
static void freeMenu(GtkWidget *widget, gpointer data);
@@ -359,7 +360,7 @@ static void freeMenuItem(GtkWidget *widget, gpointer data)
item = g_array_index(fmi->items, uiMenuItem *, fmi->i);
if (item->popupchild != NULL)
- freeMenu(widget, &item->popupchild->id);
+ freeMenu(widget, fmi->parent_i);//&item->popupchild->id);
w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget);
if (g_hash_table_remove(item->windows, widget) == FALSE)
implbug("GtkMenuItem %p not in menu item's item/window map", widget);
@@ -374,14 +375,16 @@ static void freeMenu(GtkWidget *widget, gpointer data)
GtkMenuItem *item;
GtkWidget *submenu;
struct freeMenuItemData fmi;
-
+
m = g_array_index(menus, uiMenu *, *i);
item = GTK_MENU_ITEM(widget);
submenu = gtk_menu_item_get_submenu(item);
fmi.items = m->items;
fmi.i = 0;
- gtk_container_foreach(GTK_CONTAINER(submenu), freeMenuItem, &fmi);
(*i)++;
+ fmi.parent_i = i;
+ gtk_container_foreach(GTK_CONTAINER(submenu), freeMenuItem, &fmi);
+ //(*i)++;
}
void freeMenubar(GtkWidget *mb)
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 6736fbc..b30c81b 100644
--- a/src/libui_sdl/main.cpp
+++ b/src/libui_sdl/main.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -19,16 +19,19 @@
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
+#include <string.h>
#include <SDL2/SDL.h>
#include "libui/ui.h"
#include "../types.h"
+#include "../melon_fopen.h"
#include "../version.h"
#include "../Config.h"
#include "DlgEmuSettings.h"
#include "DlgInputConfig.h"
+#include "DlgAudioSettings.h"
#include "../NDS.h"
#include "../GPU.h"
@@ -37,6 +40,13 @@
#include "../Platform.h"
#include "../Config.h"
+#include "../Savestate.h"
+
+
+// savestate slot mapping
+// 1-8: regular slots (quick access)
+// '9': load/save arbitrary file
+const int kSavestateNum[9] = {1, 2, 3, 4, 5, 6, 7, 8, 0};
const int kScreenRot[4] = {0, 1, 2, 3};
const int kScreenGap[6] = {0, 1, 8, 64, 90, 128};
@@ -44,13 +54,25 @@ const int kScreenLayout[3] = {0, 1, 2};
const int kScreenSizing[4] = {0, 1, 2, 3};
+char* EmuDirectory;
+
+
uiWindow* MainWindow;
uiArea* MainDrawArea;
+uiMenuItem* MenuItem_SaveState;
+uiMenuItem* MenuItem_LoadState;
+uiMenuItem* MenuItem_UndoStateLoad;
+
+uiMenuItem* MenuItem_SaveStateSlot[9];
+uiMenuItem* MenuItem_LoadStateSlot[9];
+
uiMenuItem* MenuItem_Pause;
uiMenuItem* MenuItem_Reset;
uiMenuItem* MenuItem_Stop;
+uiMenuItem* MenuItem_SavestateSRAMReloc;
+
uiMenuItem* MenuItem_ScreenRot[4];
uiMenuItem* MenuItem_ScreenGap[6];
uiMenuItem* MenuItem_ScreenLayout[3];
@@ -62,6 +84,10 @@ volatile int EmuStatus;
bool RunningSomething;
char ROMPath[1024];
+char SRAMPath[1024];
+char PrevSRAMPath[1024]; // for savestate 'undo load'
+
+bool SavestateLoaded;
bool ScreenDrawInited = false;
uiDrawBitmap* ScreenBitmap = NULL;
@@ -83,11 +109,121 @@ 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);
+void SaveState(int slot);
+void LoadState(int slot);
+void UndoStateLoad();
+void GetSavestateName(int slot, char* filename, int len);
+
+
+
+bool FileExists(const char* name)
+{
+ FILE* f = melon_fopen(name, "rb");
+ if (!f) return false;
+ fclose(f);
+ 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)
@@ -101,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;
@@ -124,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)
@@ -139,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();
@@ -150,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;
@@ -190,6 +413,7 @@ int EmuThreadFunc(void* burp)
{
EmuStatus = 1;
+
// poll input
u32 keymask = KeyInputMask;
u32 joymask = 0xFFF;
@@ -201,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();
@@ -301,8 +546,6 @@ int EmuThreadFunc(void* burp)
}
else
{
- EmuStatus = 2;
-
// paused
nframes = 0;
lasttick = SDL_GetTicks();
@@ -310,16 +553,20 @@ int EmuThreadFunc(void* burp)
lastmeasuretick = lasttick;
fpslimitcount = 0;
- uiAreaQueueRedrawAll(MainDrawArea);
+ if (EmuRunning == 2)
+ {
+ uiAreaQueueRedrawAll(MainDrawArea);
+ }
+
+ EmuStatus = EmuRunning;
+
SDL_Delay(100);
}
}
EmuStatus = 0;
- if (Joystick) SDL_JoystickClose(Joystick);
-
- if (audio) SDL_CloseAudioDevice(audio);
+ if (joybuttons) delete[] joybuttons;
NDS::DeInit();
@@ -438,20 +685,51 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
if (evt->Modifiers == 0x2) // ALT+key
return 0;
+ // d0rp
+ if (!RunningSomething)
+ return 1;
+
if (evt->Up)
{
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)
{
+ // F keys: 3B-44, 57-58 | SHIFT: mod. 0x4
+ if (evt->Scancode >= 0x3B && evt->Scancode <= 0x42) // F1-F8, quick savestate
+ {
+ if (evt->Modifiers == 0x4) SaveState(1 + (evt->Scancode - 0x3B));
+ else if (evt->Modifiers == 0x0) LoadState(1 + (evt->Scancode - 0x3B));
+ }
+ else if (evt->Scancode == 0x43) // F9, savestate from/to file
+ {
+ if (evt->Modifiers == 0x4) SaveState(0);
+ else if (evt->Modifiers == 0x0) LoadState(0);
+ }
+ else if (evt->Scancode == 0x58) // F12, undo savestate
+ {
+ if (evt->Modifiers == 0x0) UndoStateLoad();
+ }
+
for (int i = 0; i < 12; i++)
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;
@@ -710,6 +988,25 @@ void Run()
EmuRunning = 1;
RunningSomething = true;
+ uiMenuItemEnable(MenuItem_SaveState);
+ uiMenuItemEnable(MenuItem_LoadState);
+
+ if (SavestateLoaded)
+ uiMenuItemEnable(MenuItem_UndoStateLoad);
+ else
+ uiMenuItemDisable(MenuItem_UndoStateLoad);
+
+ for (int i = 0; i < 8; i++)
+ {
+ char ssfile[1024];
+ GetSavestateName(i+1, ssfile, 1024);
+ if (FileExists(ssfile)) uiMenuItemEnable(MenuItem_LoadStateSlot[i]);
+ else uiMenuItemDisable(MenuItem_LoadStateSlot[i]);
+ }
+
+ for (int i = 0; i < 9; i++) uiMenuItemEnable(MenuItem_SaveStateSlot[i]);
+ uiMenuItemEnable(MenuItem_LoadStateSlot[8]);
+
uiMenuItemEnable(MenuItem_Pause);
uiMenuItemEnable(MenuItem_Reset);
uiMenuItemEnable(MenuItem_Stop);
@@ -723,6 +1020,12 @@ void Stop(bool internal)
while (EmuStatus != 2);
RunningSomething = false;
+ uiWindowSetTitle(MainWindow, "melonDS " MELONDS_VERSION);
+
+ for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_SaveStateSlot[i]);
+ for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_LoadStateSlot[i]);
+ uiMenuItemDisable(MenuItem_UndoStateLoad);
+
uiMenuItemDisable(MenuItem_Pause);
uiMenuItemDisable(MenuItem_Reset);
uiMenuItemDisable(MenuItem_Stop);
@@ -732,16 +1035,33 @@ void Stop(bool internal)
uiAreaQueueRedrawAll(MainDrawArea);
}
+void SetupSRAMPath()
+{
+ strncpy(SRAMPath, ROMPath, 1023);
+ SRAMPath[1023] = '\0';
+ strncpy(SRAMPath + strlen(ROMPath) - 3, "sav", 3);
+}
+
void TryLoadROM(char* file, int prevstatus)
{
char oldpath[1024];
+ char oldsram[1024];
strncpy(oldpath, ROMPath, 1024);
+ strncpy(oldsram, SRAMPath, 1024);
strncpy(ROMPath, file, 1023);
ROMPath[1023] = '\0';
- if (NDS::LoadROM(ROMPath, Config::DirectBoot))
+ SetupSRAMPath();
+
+ if (NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot))
+ {
+ SavestateLoaded = false;
+ uiMenuItemDisable(MenuItem_UndoStateLoad);
+
+ strncpy(PrevSRAMPath, SRAMPath, 1024); // safety
Run();
+ }
else
{
uiMsgBoxError(MainWindow,
@@ -749,18 +1069,200 @@ void TryLoadROM(char* file, int prevstatus)
"Make sure the file can be accessed and isn't opened in another application.");
strncpy(ROMPath, oldpath, 1024);
+ strncpy(SRAMPath, oldsram, 1024);
EmuRunning = prevstatus;
}
}
-int OnCloseWindow(uiWindow* window, void* blarg)
+// SAVESTATE TODO
+// * configurable paths. not everyone wants their ROM directory to be polluted, I guess.
+
+void GetSavestateName(int slot, char* filename, int len)
{
- if (RunningSomething)
+ int pos;
+
+ if (ROMPath[0] == '\0') // running firmware, no ROM
{
- EmuRunning = 2;
- while (EmuStatus != 2);
+ strcpy(filename, "firmware");
+ pos = 8;
+ }
+ else
+ {
+ int l = strlen(ROMPath);
+ pos = l;
+ while (ROMPath[pos] != '.' && pos > 0) pos--;
+ if (pos == 0) pos = l;
+
+ // avoid buffer overflow. shoddy
+ if (pos > len-5) pos = len-5;
+
+ strncpy(&filename[0], ROMPath, pos);
}
+ strcpy(&filename[pos], ".ml");
+ filename[pos+3] = '0'+slot;
+ filename[pos+4] = '\0';
+}
+
+void LoadState(int slot)
+{
+ int prevstatus = EmuRunning;
+ EmuRunning = 2;
+ while (EmuStatus != 2);
+
+ char filename[1024];
+
+ if (slot > 0)
+ {
+ GetSavestateName(slot, filename, 1024);
+ }
+ else
+ {
+ char* file = uiOpenFile(MainWindow, "melonDS savestate (any)|*.ml1;*.ml2;*.ml3;*.ml4;*.ml5;*.ml6;*.ml7;*.ml8;*.mln", Config::LastROMFolder);
+ if (!file)
+ {
+ EmuRunning = prevstatus;
+ return;
+ }
+
+ strncpy(filename, file, 1023);
+ filename[1023] = '\0';
+ uiFreeText(file);
+ }
+
+ if (!FileExists(filename))
+ {
+ EmuRunning = prevstatus;
+ return;
+ }
+
+ // backup
+ Savestate* backup = new Savestate("timewarp.mln", true);
+ NDS::DoSavestate(backup);
+ delete backup;
+
+ bool failed = false;
+
+ Savestate* state = new Savestate(filename, false);
+ if (state->Error)
+ {
+ delete state;
+
+ uiMsgBoxError(MainWindow, "Error", "Could not load savestate file.");
+
+ // current state might be crapoed, so restore from sane backup
+ state = new Savestate("timewarp.mln", false);
+ failed = true;
+ }
+
+ NDS::DoSavestate(state);
+ delete state;
+
+ if (!failed)
+ {
+ 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';
+
+ NDS::RelocateSave(SRAMPath, false);
+ }
+
+ SavestateLoaded = true;
+ uiMenuItemEnable(MenuItem_UndoStateLoad);
+ }
+
+ EmuRunning = prevstatus;
+}
+
+void SaveState(int slot)
+{
+ int prevstatus = EmuRunning;
+ EmuRunning = 2;
+ while (EmuStatus != 2);
+
+ char filename[1024];
+
+ if (slot > 0)
+ {
+ GetSavestateName(slot, filename, 1024);
+ }
+ else
+ {
+ char* file = uiSaveFile(MainWindow, "melonDS savestate (*.mln)|*.mln", Config::LastROMFolder);
+ if (!file)
+ {
+ EmuRunning = prevstatus;
+ return;
+ }
+
+ strncpy(filename, file, 1023);
+ filename[1023] = '\0';
+ uiFreeText(file);
+ }
+
+ Savestate* state = new Savestate(filename, true);
+ if (state->Error)
+ {
+ delete state;
+
+ uiMsgBoxError(MainWindow, "Error", "Could not save state.");
+ }
+ else
+ {
+ NDS::DoSavestate(state);
+ delete state;
+
+ if (slot > 0)
+ uiMenuItemEnable(MenuItem_LoadStateSlot[slot-1]);
+
+ if (Config::SavestateRelocSRAM && ROMPath[0]!='\0')
+ {
+ strncpy(SRAMPath, filename, 1019);
+ int len = strlen(SRAMPath);
+ strcpy(&SRAMPath[len], ".sav");
+ SRAMPath[len+4] = '\0';
+
+ NDS::RelocateSave(SRAMPath, true);
+ }
+ }
+
+ EmuRunning = prevstatus;
+}
+
+void UndoStateLoad()
+{
+ if (!SavestateLoaded) return;
+
+ int prevstatus = EmuRunning;
+ EmuRunning = 2;
+ while (EmuStatus != 2);
+
+ // pray that this works
+ // what do we do if it doesn't???
+ // but it should work.
+ Savestate* backup = new Savestate("timewarp.mln", false);
+ NDS::DoSavestate(backup);
+ delete backup;
+
+ if (ROMPath[0]!='\0')
+ {
+ strncpy(SRAMPath, PrevSRAMPath, 1024);
+ NDS::RelocateSave(SRAMPath, false);
+ }
+
+ EmuRunning = prevstatus;
+}
+
+
+int OnCloseWindow(uiWindow* window, void* blarg)
+{
+ EmuRunning = 3;
+ while (EmuStatus != 3);
uiQuit();
return 1;
@@ -795,6 +1297,9 @@ void OnLoseFocus(uiWindow* window, void* blarg)
void OnCloseByMenu(uiMenuItem* item, uiWindow* window, void* blarg)
{
+ EmuRunning = 3;
+ while (EmuStatus != 3);
+
uiControlDestroy(uiControl(window));
uiQuit();
}
@@ -805,17 +1310,39 @@ 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);
}
+void OnSaveState(uiMenuItem* item, uiWindow* window, void* param)
+{
+ int slot = *(int*)param;
+ SaveState(slot);
+}
+
+void OnLoadState(uiMenuItem* item, uiWindow* window, void* param)
+{
+ int slot = *(int*)param;
+ LoadState(slot);
+}
+
+void OnUndoStateLoad(uiMenuItem* item, uiWindow* window, void* param)
+{
+ UndoStateLoad();
+}
+
void OnRun(uiMenuItem* item, uiWindow* window, void* blarg)
{
if (!RunningSomething)
@@ -852,10 +1379,16 @@ void OnReset(uiMenuItem* item, uiWindow* window, void* blarg)
EmuRunning = 2;
while (EmuStatus != 2);
+ SavestateLoaded = false;
+ uiMenuItemDisable(MenuItem_UndoStateLoad);
+
if (ROMPath[0] == '\0')
NDS::LoadBIOS();
else
- NDS::LoadROM(ROMPath, Config::DirectBoot);
+ {
+ SetupSRAMPath();
+ NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot);
+ }
Run();
}
@@ -874,7 +1407,23 @@ 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();
+}
+
+
+void OnSetSavestateSRAMReloc(uiMenuItem* item, uiWindow* window, void* param)
+{
+ Config::SavestateRelocSRAM = uiMenuItemChecked(item) ? 1:0;
}
@@ -1005,15 +1554,6 @@ void ApplyNewSettings()
}
-bool _fileexists(char* name)
-{
- FILE* f = fopen(name, "rb");
- if (!f) return false;
- fclose(f);
- return true;
-}
-
-
int main(int argc, char** argv)
{
srand(time(NULL));
@@ -1021,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");
@@ -1048,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,
@@ -1072,6 +1628,48 @@ int main(int argc, char** argv)
menuitem = uiMenuAppendItem(menu, "Open ROM...");
uiMenuItemOnClicked(menuitem, OnOpenFile, NULL);
uiMenuAppendSeparator(menu);
+ {
+ uiMenu* submenu = uiNewMenu("Save state");
+
+ for (int i = 0; i < 9; i++)
+ {
+ char name[32];
+ if (i < 8)
+ sprintf(name, "%d\tShift+F%d", kSavestateNum[i], kSavestateNum[i]);
+ else
+ strcpy(name, "File...\tShift+F9");
+
+ uiMenuItem* ssitem = uiMenuAppendItem(submenu, name);
+ uiMenuItemOnClicked(ssitem, OnSaveState, (void*)&kSavestateNum[i]);
+
+ MenuItem_SaveStateSlot[i] = ssitem;
+ }
+
+ MenuItem_SaveState = uiMenuAppendSubmenu(menu, submenu);
+ }
+ {
+ uiMenu* submenu = uiNewMenu("Load state");
+
+ for (int i = 0; i < 9; i++)
+ {
+ char name[32];
+ if (i < 8)
+ sprintf(name, "%d\tF%d", kSavestateNum[i], kSavestateNum[i]);
+ else
+ strcpy(name, "File...\tF9");
+
+ uiMenuItem* ssitem = uiMenuAppendItem(submenu, name);
+ uiMenuItemOnClicked(ssitem, OnLoadState, (void*)&kSavestateNum[i]);
+
+ MenuItem_LoadStateSlot[i] = ssitem;
+ }
+
+ MenuItem_LoadState = uiMenuAppendSubmenu(menu, submenu);
+ }
+ menuitem = uiMenuAppendItem(menu, "Undo state load\tF12");
+ uiMenuItemOnClicked(menuitem, OnUndoStateLoad, NULL);
+ MenuItem_UndoStateLoad = menuitem;
+ uiMenuAppendSeparator(menu);
menuitem = uiMenuAppendItem(menu, "Quit");
uiMenuItemOnClicked(menuitem, OnCloseByMenu, NULL);
@@ -1090,10 +1688,25 @@ 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");
+
+ MenuItem_SavestateSRAMReloc = uiMenuAppendCheckItem(submenu, "Separate savefiles");
+ uiMenuItemOnClicked(MenuItem_SavestateSRAMReloc, OnSetSavestateSRAMReloc, NULL);
+
+ uiMenuAppendSubmenu(menu, submenu);
+ }
uiMenuAppendSeparator(menu);
{
uiMenu* submenu = uiNewMenu("Screen rotation");
@@ -1167,6 +1780,12 @@ int main(int argc, char** argv)
uiWindowOnGetFocus(MainWindow, OnGetFocus, NULL);
uiWindowOnLoseFocus(MainWindow, OnLoseFocus, NULL);
+ //uiMenuItemDisable(MenuItem_SaveState);
+ //uiMenuItemDisable(MenuItem_LoadState);
+ for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_SaveStateSlot[i]);
+ for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_LoadStateSlot[i]);
+ uiMenuItemDisable(MenuItem_UndoStateLoad);
+
uiMenuItemDisable(MenuItem_Pause);
uiMenuItemDisable(MenuItem_Reset);
uiMenuItemDisable(MenuItem_Stop);
@@ -1195,6 +1814,8 @@ int main(int argc, char** argv)
SANITIZE(ScreenSizing, 0, 3);
#undef SANITIZE
+ uiMenuItemSetChecked(MenuItem_SavestateSRAMReloc, Config::SavestateRelocSRAM?1:0);
+
uiMenuItemSetChecked(MenuItem_ScreenRot[ScreenRotation], 1);
uiMenuItemSetChecked(MenuItem_ScreenLayout[ScreenLayout], 1);
uiMenuItemSetChecked(MenuItem_ScreenSizing[ScreenSizing], 1);
@@ -1207,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);
@@ -1221,7 +1889,9 @@ int main(int argc, char** argv)
strncpy(ROMPath, file, 1023);
ROMPath[1023] = '\0';
- if (NDS::LoadROM(ROMPath, Config::DirectBoot))
+ SetupSRAMPath();
+
+ if (NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot))
Run();
}
}
@@ -1233,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;
@@ -1244,6 +1920,7 @@ int main(int argc, char** argv)
uiUninit();
SDL_Quit();
+ delete[] EmuDirectory;
return 0;
}
@@ -1253,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 8e79e8b..40709e1 100644
--- a/src/melon_fopen.h
+++ b/src/melon_fopen.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -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/types.h b/src/types.h
index 8a6c7e3..1c4f140 100644
--- a/src/types.h
+++ b/src/types.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
diff --git a/src/version.h b/src/version.h
index 9e2d867..d1abc3f 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,5 +1,5 @@
/*
- Copyright 2016-2017 StapleButter
+ Copyright 2016-2019 StapleButter
This file is part of melonDS.
@@ -19,7 +19,7 @@
#ifndef VERSION_H
#define VERSION_H
-#define MELONDS_VERSION "0.6b"
+#define MELONDS_VERSION "0.7.1"
#define MELONDS_URL "http://melonds.kuribo64.net/"