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