diff --git a/AGENTS.md b/AGENTS.md index 50b59a3dc0..7c55bde36f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,8 +40,19 @@ necessarily after every edit), run these too: - Windows: build the `UnitTest` project (unittest/UnitTests.vcxproj), then run `Windows/x64/Debug/UnitTest.exe all` - Linux/Mac: configure with `-DUNITTEST=ON`, then run `build/PPSSPPUnitTest all` -This runs all tests in `availableTests` in unittest/UnitTest.cpp. You can run a single test by -passing its name instead of `all`; no arguments lists the available tests. +This runs all tests in `availableTests` in unittest/UnitTest.cpp. You can run one or more +specific tests by passing their names instead of `all` (space-separated, e.g. `UnitTest.exe +CmdLine Path Utf8`); no arguments lists the available tests. + +Known environment-specific issue: in at least one sandboxed dev environment, the `Jit` test +(`unittest/JitHarness.cpp`) hangs indefinitely specifically during the `CPUCore::JIT_IR` +phase - confirmed unrelated to source changes (reproduces identically on unmodified checkouts) +and not a memory-access fault (`Memory::HandleFault` is never entered). Root cause wasn't +pinned down further (would need a native debugger attached to the hung process, not available +in that environment) but is very likely specific to that sandbox rather than a real PPSSPP +bug, since CI runs the equivalent of `UnitTest.exe all` on every commit across multiple +platforms without apparent issue. If `all`/`Jit` hangs in your environment, run every other +test by name instead (skip `Jit`) to still get real coverage. ## Multiplatform considerations diff --git a/Common/ABI.cpp b/Common/ABI.cpp index 60e8c927e1..583a357128 100644 --- a/Common/ABI.cpp +++ b/Common/ABI.cpp @@ -442,7 +442,7 @@ void XEmitter::ABI_CallFunctionPPC(const void *func, void *param1, void *param2, // Pass a register as a parameter. void XEmitter::ABI_CallFunctionR(const void *func, X64Reg reg1) { if (reg1 != ABI_PARAM1) - MOV(32, R(ABI_PARAM1), R(reg1)); + MOV(64, R(ABI_PARAM1), R(reg1)); u64 distance = u64(func) - (u64(code) + 5); if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL) { diff --git a/Common/Arm64Emitter.cpp b/Common/Arm64Emitter.cpp index afddd61045..6cdab2596b 100644 --- a/Common/Arm64Emitter.cpp +++ b/Common/Arm64Emitter.cpp @@ -1044,8 +1044,21 @@ void ARM64XEmitter::BL(const void* ptr) } void ARM64XEmitter::QuickCallFunction(ARM64Reg scratchreg, const void *func) { - s64 distance = (s64)func - (s64)m_code; - distance >>= 2; // Can only branch to opcode-aligned (4) addresses + s64 distance = ((s64)func - (s64)m_code) >> 2; + if (!IsInRangeImm26(distance)) { + // WARN_LOG(Log::JIT, "Distance too far in function call (%p to %p)! Using scratch.", m_code, func); + MOVI2R(scratchreg, (uintptr_t)func); + BLR(scratchreg); + } else { + BL(func); + } +} + +void ARM64XEmitter::QuickCallFunctionR(ARM64Reg scratchreg, const void *func, ARM64Reg arg) { + s64 distance = ((s64)func - (s64)m_code) >> 2; + if (arg != X0) { + MOV(X0, arg); + } if (!IsInRangeImm26(distance)) { // WARN_LOG(Log::JIT, "Distance too far in function call (%p to %p)! Using scratch.", m_code, func); MOVI2R(scratchreg, (uintptr_t)func); diff --git a/Common/Arm64Emitter.h b/Common/Arm64Emitter.h index b1d325871f..19fef73745 100644 --- a/Common/Arm64Emitter.h +++ b/Common/Arm64Emitter.h @@ -776,6 +776,11 @@ public: template void QuickCallFunction(ARM64Reg scratchreg, T func) { QuickCallFunction(scratchreg, (const void *)func); } + void QuickCallFunctionR(ARM64Reg scratchreg, const void *func, ARM64Reg arg); + template void QuickCallFunctionR(ARM64Reg scratchreg, T func, ARM64Reg arg) { + QuickCallFunctionR(scratchreg, (const void *)func, arg); + } + }; class ARM64FloatEmitter diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index e974196f44..cfa1b8af1e 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -557,6 +557,18 @@ void ARMXEmitter::QuickCallFunction(ARMReg reg, const void *func) { } } +void ARMXEmitter::QuickCallFunctionR(ARMReg reg, const void *func, ARMReg arg) { + if (arg != R0) { + MOV(R0, arg); + } + if (BLInRange(func)) { + BL(func); + } else { + MOVP2R(reg, func); + BL(reg); + } +} + void ARMXEmitter::SetCodePointer(u8 *ptr, u8 *writePtr) { code = ptr; diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index c6fe15dc17..f2512bf1cf 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -854,6 +854,10 @@ public: template void QuickCallFunction(ARMReg scratchreg, T func) { QuickCallFunction(scratchreg, (const void *)func); } + void QuickCallFunctionR(ARMReg scratchreg, const void *func, ARMReg arg); + template void QuickCallFunctionR(ARMReg scratchreg, T func, ARMReg arg) { + QuickCallFunctionR(scratchreg, (const void *)func, arg); + } // Wrapper around MOVT/MOVW with fallbacks. void MOVI2R(ARMReg reg, u32 val, bool optimize = true); diff --git a/Common/LoongArch64Emitter.h b/Common/LoongArch64Emitter.h index 30de62f12b..134b5de61d 100644 --- a/Common/LoongArch64Emitter.h +++ b/Common/LoongArch64Emitter.h @@ -165,7 +165,15 @@ public: static_assert(std::is_function::value, "QuickCallFunction without function"); QuickCallFunction((const u8 *)func, scratchreg); } - + void QuickCallFunctionR(const u8 *func, LoongArch64Reg arg, LoongArch64Reg scratchreg = R_RA) { + MOVE(LoongArch64Reg::X4, arg); + QuickJump(scratchreg, R_RA, func); + } + template + void QuickCallFunctionR(T *func, LoongArch64Reg arg, LoongArch64Reg scratchreg = R_RA) { + static_assert(std::is_function::value, "QuickCallFunction without function"); + QuickCallFunctionR((const u8 *)func, arg, scratchreg); + } // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html // https://github.com/loongson-community/loongarch-opcodes/ diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 651ab727c9..be6e2e986d 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -226,6 +226,17 @@ public: QuickCallFunction((const u8 *)func, scratchreg); } + void QuickCallFunctionR(const u8 *func, RiscVReg arg, RiscVReg scratchreg = R_RA) { + if (arg != RiscVReg::X10) { // A0 + MV(RiscVReg::X10, arg); + } + QuickJAL(scratchreg, R_RA, func); + } + template + void QuickCallFunctionR(T *func, RiscVReg arg, RiscVReg scratchreg = R_RA) { + static_assert(std::is_function::value, "QuickCallFunction without function"); + QuickCallFunctionR((const u8 *)func, arg, scratchreg); + } void LUI(RiscVReg rd, s32 simm32); void AUIPC(RiscVReg rd, s32 simm32); diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index a80e919982..b8a775b6a6 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -379,8 +379,7 @@ void ForceCheck(MIPSState *mips) { #endif } -void Advance() { - MIPSState *mips = currentMIPS; // TODO: Move to parameter +void Advance(MIPSState *mips) { PROFILE_THIS_SCOPE("advance"); int cyclesExecuted = slicelength - mips->downcount; globalTimer += cyclesExecuted; diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index 5a97c103ab..725ef6dd72 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -113,7 +113,7 @@ namespace CoreTiming { const Event *GetFirstEvent(); void RemoveEvent(int event_type); bool IsScheduled(int event_type); - void Advance(); + void Advance(MIPSState *mips); void ForceCheck(MIPSState *mips); // Pretend that the main CPU has executed enough cycles to reach the next event. diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 19f65bd9b3..ccb8d0bf69 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -605,8 +605,8 @@ void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction) hleAfterSyscall |= HLE_AFTER_QUEUED_CALLS; } -void hleFlushCalls() { - u32 &sp = currentMIPS->r[MIPS_REG_SP]; +static void hleFlushCalls(MIPSState *mips) { + u32 &sp = mips->r[MIPS_REG_SP]; PSPPointer stackData; _dbg_assert_(g_stackSize == 0); VERBOSE_LOG(Log::HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), g_stackSize ? g_stack[0]->name : "?", sp); @@ -615,15 +615,15 @@ void hleFlushCalls() { sp -= sizeof(HLEMipsCallStack); stackData.ptr = sp; stackData->nextOff = 0xFFFFFFFF; - stackData->ra = currentMIPS->pc; - stackData->v0 = currentMIPS->r[MIPS_REG_V0]; - stackData->v1 = currentMIPS->r[MIPS_REG_V1]; + stackData->ra = mips->pc; + stackData->v0 = mips->r[MIPS_REG_V0]; + stackData->v1 = mips->r[MIPS_REG_V1]; // Now we'll set up the first in the chain. - currentMIPS->pc = enqueuedMipsCalls[0].func; - currentMIPS->r[MIPS_REG_RA] = HLEMipsCallReturnAddress(); + mips->pc = enqueuedMipsCalls[0].func; + mips->r[MIPS_REG_RA] = HLEMipsCallReturnAddress(); for (int i = 0; i < (int)enqueuedMipsCalls[0].args.size(); i++) { - currentMIPS->r[MIPS_REG_A0 + i] = enqueuedMipsCalls[0].args[i]; + mips->r[MIPS_REG_A0 + i] = enqueuedMipsCalls[0].args[i]; } // For stack info, process the first enqueued call last, so we run it first. @@ -653,6 +653,7 @@ void hleFlushCalls() { DEBUG_LOG(Log::HLE, "Executing HLE mips call at %08x, sp=%08x", currentMIPS->pc, sp); } +// This is a HLE function. void HLEReturnFromMipsCall() { u32 &sp = currentMIPS->r[MIPS_REG_SP]; PSPPointer stackData; @@ -777,7 +778,7 @@ static void hleFinishSyscall(const HLEFunction *info) { SetDeadbeefRegs(); if ((hleAfterSyscall & HLE_AFTER_QUEUED_CALLS) != 0) - hleFlushCalls(); + hleFlushCalls(currentMIPS); if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0 && (hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) == 0) __KernelForceCallbacks(); @@ -806,15 +807,13 @@ void hleFinishSyscallAfterGe() { hleFinishSyscall(nullptr); } -static void updateSyscallStats(int modulenum, int funcnum, double total) -{ +static void updateSyscallStats(int modulenum, int funcnum, double total) { const char *name = moduleDB[modulenum].funcTable[funcnum].name; // Ignore this one, especially for msInSyscalls (although that ignores CoreTiming events.) if (0 == strcmp(name, "_sceKernelIdle")) return; - if (total > kernelStats.slowestSyscallTime) - { + if (total > kernelStats.slowestSyscallTime) { kernelStats.slowestSyscallTime = total; kernelStats.slowestSyscallName = name; } @@ -822,20 +821,15 @@ static void updateSyscallStats(int modulenum, int funcnum, double total) KernelStatsSyscall statCall(modulenum, funcnum); auto summedStat = kernelStats.summedMsInSyscalls.find(statCall); - if (summedStat == kernelStats.summedMsInSyscalls.end()) - { + if (summedStat == kernelStats.summedMsInSyscalls.end()) { kernelStats.summedMsInSyscalls[statCall] = total; - if (total > kernelStats.summedSlowestSyscallTime) - { + if (total > kernelStats.summedSlowestSyscallTime) { kernelStats.summedSlowestSyscallTime = total; kernelStats.summedSlowestSyscallName = name; } - } - else - { + } else { double newTotal = kernelStats.summedMsInSyscalls[statCall] += total; - if (newTotal > kernelStats.summedSlowestSyscallTime) - { + if (newTotal > kernelStats.summedSlowestSyscallTime) { kernelStats.summedSlowestSyscallTime = newTotal; kernelStats.summedSlowestSyscallName = name; } diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index b1b3f5f7a5..970949ad3a 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -651,42 +651,6 @@ void __IoInit() { asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify); syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify); - // TODO(scoped): This won't work if memStickDirectory points at the contents of /PSP... -#if defined(USING_WIN_UI) || defined(APPLE) - auto flash0System = std::make_shared(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH); -#else - auto flash0System = std::make_shared(&pspFileSystem, "flash0"); -#endif - FileSystemFlags memstickFlags = FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD; - - Path pspDir = GetSysDirectory(DIRECTORY_PSP); - if (pspDir == g_Config.memStickDirectory) { - // Initially tried to do this with dual mounts, but failed due to save state compatibility issues. - INFO_LOG(Log::sceIo, "Enabling /PSP compatibility mode"); - memstickFlags |= FileSystemFlags::STRIP_PSP; - } - - auto memstickSystem = std::make_shared(&pspFileSystem, g_Config.memStickDirectory, memstickFlags); - - pspFileSystem.Mount("ms0:", memstickSystem); - pspFileSystem.Mount("fatms0:", memstickSystem); - pspFileSystem.Mount("fatms:", memstickSystem); - pspFileSystem.Mount("pfat0:", memstickSystem); - - pspFileSystem.Mount("flash0:", flash0System); - - if (g_RemasterMode) { - const std::string gameId = g_paramSFO.GetDiscID(); - const Path exdataPath = GetSysDirectory(DIRECTORY_EXDATA) / gameId; - if (File::Exists(exdataPath)) { - auto exdataSystem = std::make_shared(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); - pspFileSystem.Mount("exdata0:", exdataSystem); - INFO_LOG(Log::sceIo, "Mounted exdata/%s/ under memstick for exdata0:/", gameId.c_str()); - } else { - INFO_LOG(Log::sceIo, "Did not find exdata/%s/ under memstick for exdata0:/", gameId.c_str()); - } - } - __KernelListenThreadEnd(&TellFsThreadEnded); memset(fds, 0, sizeof(fds)); @@ -703,6 +667,30 @@ void __IoInit() { lastMemStickFatState = MemoryStick_FatState(); } +void __IoShutdown() { + ioManagerThreadEnabled = false; + ioManager.SyncThread(); + ioManager.FinishEventLoop(); + if (ioManagerThread.joinable()) { + ioManagerThread.join(); + ioManager.Shutdown(); + } + + for (int i = 0; i < PSP_COUNT_FDS; ++i) { + asyncParams[i].op = IoAsyncOp::NONE; + asyncParams[i].priority = -1; + if (asyncThreads[i]) + asyncThreads[i]->Forget(); + delete asyncThreads[i]; + asyncThreads[i] = nullptr; + } + asyncDefaultPriority = -1; + + MemoryStick_Shutdown(); + memStickCallbacks.clear(); + memStickFatCallbacks.clear(); +} + void __IoDoState(PointerWrap &p) { auto s = p.Section("sceIo", 1, 5); if (!s) @@ -771,37 +759,6 @@ void __IoDoState(PointerWrap &p) { } } -void __IoShutdown() { - ioManagerThreadEnabled = false; - ioManager.SyncThread(); - ioManager.FinishEventLoop(); - if (ioManagerThread.joinable()) { - ioManagerThread.join(); - ioManager.Shutdown(); - } - - for (int i = 0; i < PSP_COUNT_FDS; ++i) { - asyncParams[i].op = IoAsyncOp::NONE; - asyncParams[i].priority = -1; - if (asyncThreads[i]) - asyncThreads[i]->Forget(); - delete asyncThreads[i]; - asyncThreads[i] = nullptr; - } - asyncDefaultPriority = -1; - - pspFileSystem.Unmount("ms0:"); - pspFileSystem.Unmount("fatms0:"); - pspFileSystem.Unmount("fatms:"); - pspFileSystem.Unmount("pfat0:"); - pspFileSystem.Unmount("flash0:"); - pspFileSystem.Unmount("exdata0:"); - - MemoryStick_Shutdown(); - memStickCallbacks.clear(); - memStickFatCallbacks.clear(); -} - static std::string IODetermineFilename(const FileNode *f) { uint64_t offset = pspFileSystem.GetSeekPos(f->handle); if ((pspFileSystem.DevType(f->handle) & PSPDevType::BLOCK) != 0) { diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index c92155a614..4c0e141f4c 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1638,7 +1638,7 @@ void __KernelReSchedule(const char *reason) __KernelCheckCallbacks(); // Execute any pending events while we're doing scheduling. - CoreTiming::Advance(); + CoreTiming::Advance(currentMIPS); if (__IsInInterrupt() || !__KernelIsDispatchEnabled()) { // Threads don't get changed within interrupts or while dispatch is disabled. reason = "In Interrupt Or Callback"; diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 6773e01123..df4735c5ea 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -159,7 +159,7 @@ void ArmJit::GenerateFixedCode() { outerLoop = GetCodePtr(); SaveDowncount(); RestoreRoundingMode(true); - QuickCallFunction(R0, &CoreTiming::Advance); + QuickCallFunctionR(R1, &CoreTiming::Advance, CTXREG); ApplyRoundingMode(true); RestoreDowncount(); FixupBranch skipToCoreStateCheck = B(); //skip the downcount check diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index 245d99c2e1..d2c83eaffd 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -204,7 +204,7 @@ void Arm64Jit::GenerateFixedCode(const JitOptions &jo) { outerLoop = GetCodePtr(); SaveStaticRegisters(); // Advance can change the downcount, so must save/restore RestoreRoundingMode(true); - QuickCallFunction(SCRATCH1_64, &CoreTiming::Advance); + QuickCallFunctionR(SCRATCH1_64, &CoreTiming::Advance, CTXREG); ApplyRoundingMode(true); LoadStaticRegisters(); FixupBranch skipToCoreStateCheck = B(); //skip the downcount check diff --git a/Core/MIPS/ARM64/Arm64IRAsm.cpp b/Core/MIPS/ARM64/Arm64IRAsm.cpp index fd518d4a35..2b64b48c72 100644 --- a/Core/MIPS/ARM64/Arm64IRAsm.cpp +++ b/Core/MIPS/ARM64/Arm64IRAsm.cpp @@ -169,7 +169,7 @@ void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) { SaveStaticRegisters(); // Advance can change the downcount, so must save/restore RestoreRoundingMode(true); WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE); - QuickCallFunction(SCRATCH1_64, &CoreTiming::Advance); + QuickCallFunctionR(SCRATCH1_64, &CoreTiming::Advance, CTXREG); WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT); ApplyRoundingMode(true); LoadStaticRegisters(); diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index b647ae6cb0..b5611d210e 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -175,7 +175,7 @@ void IRJit::RunLoopUntil(u64 globalticks) { MIPSState *mips = mips_; while (true) { // RestoreRoundingMode(true); - CoreTiming::Advance(); + CoreTiming::Advance(currentMIPS); // ApplyRoundingMode(true); if (coreState != 0) { break; diff --git a/Core/MIPS/LoongArch64/LoongArch64Asm.cpp b/Core/MIPS/LoongArch64/LoongArch64Asm.cpp index dac27da7c5..2caeb770c1 100644 --- a/Core/MIPS/LoongArch64/LoongArch64Asm.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64Asm.cpp @@ -134,7 +134,7 @@ void LoongArch64JitBackend::GenerateFixedCode(MIPSState *mipsState) { SaveStaticRegisters(); RestoreRoundingMode(true); WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE); - QuickCallFunction(&CoreTiming::Advance, R20); + QuickCallFunctionR(&CoreTiming::Advance, CTXREG, R20); WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT); ApplyRoundingMode(true); LoadStaticRegisters(); diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index 57b7aad9b5..adf6e08480 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -325,7 +325,7 @@ void MIPSState::DoState(PointerWrap &p) { void MIPSState::SingleStep() { int cycles = MIPS_SingleStep(this); downcount -= cycles; - CoreTiming::Advance(); + CoreTiming::Advance(currentMIPS); } // returns 1 if reached ticks limit diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 75f4c937eb..e0da966bf0 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -1197,9 +1197,9 @@ static void RunUntilDowncountZeroWithChecks(MIPSState *mips, u64 globalTicks) { int MIPSInterpret_RunUntil(MIPSState *mips, u64 globalTicks) { while (coreState == CORE_RUNNING_CPU) { - CoreTiming::Advance(); + CoreTiming::Advance(mips); - uint64_t ticksLeft = globalTicks - CoreTiming::GetTicks(currentMIPS); + uint64_t ticksLeft = globalTicks - CoreTiming::GetTicks(mips); if (g_breakpoints.HasBreakPoints() || g_breakpoints.HasMemChecks() || ticksLeft <= mips->downcount) { RunUntilDowncountZeroWithChecks(mips, globalTicks); } else { diff --git a/Core/MIPS/RiscV/RiscVAsm.cpp b/Core/MIPS/RiscV/RiscVAsm.cpp index c88d961a97..396c45ec20 100644 --- a/Core/MIPS/RiscV/RiscVAsm.cpp +++ b/Core/MIPS/RiscV/RiscVAsm.cpp @@ -143,7 +143,7 @@ void RiscVJitBackend::GenerateFixedCode(MIPSState *mipsState) { SaveStaticRegisters(); RestoreRoundingMode(true); WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE); - QuickCallFunction(&CoreTiming::Advance, X7); + QuickCallFunctionR(&CoreTiming::Advance, CTXREG, X7); WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT); ApplyRoundingMode(true); LoadStaticRegisters(); diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 0c5d5924f0..126d915ea8 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -125,7 +125,8 @@ void Jit::GenerateFixedCode(JitOptions &jo) { outerLoop = GetCodePtr(); RestoreRoundingMode(true); - ABI_CallFunction(reinterpret_cast(&CoreTiming::Advance)); + LEA(PTRBITS, ECX, MDisp(CTXREG, -(s32)offsetof(MIPSState, f[0]))); // Adjust to get the real pointer. + ABI_CallFunctionR(reinterpret_cast(&CoreTiming::Advance), ECX); ApplyRoundingMode(true); FixupBranch skipToCoreStateCheck = J(); //skip the downcount check diff --git a/Core/MIPS/x86/X64IRAsm.cpp b/Core/MIPS/x86/X64IRAsm.cpp index 5cf4136930..a3b273f587 100644 --- a/Core/MIPS/x86/X64IRAsm.cpp +++ b/Core/MIPS/x86/X64IRAsm.cpp @@ -167,7 +167,8 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { SaveStaticRegisters(); RestoreRoundingMode(true); WriteDebugProfilerStatus(IRProfilerStatus::TIMER_ADVANCE); - ABI_CallFunction(reinterpret_cast(&CoreTiming::Advance)); + LEA(PTRBITS, ECX, MDisp(CTXREG, -(s32)offsetof(MIPSState, f[0]))); // Adjust to get the real pointer. + ABI_CallFunctionR(reinterpret_cast(&CoreTiming::Advance), ECX); WriteDebugProfilerStatus(IRProfilerStatus::IN_JIT); ApplyRoundingMode(true); LoadStaticRegisters(); diff --git a/Core/System.cpp b/Core/System.cpp index e33ef65b1e..fe22a31080 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -63,15 +63,15 @@ #include "Core/CoreTiming.h" #include "Core/CoreParameter.h" #include "Core/FileLoaders/RamCachingFileLoader.h" -#include "Core/LuaContext.h" #include "Core/FileSystems/MetaFileSystem.h" +#include "Core/FileSystems/ISOFileSystem.h" +#include "Core/FileSystems/DirectoryFileSystem.h" +#include "Core/LuaContext.h" #include "Core/Loaders.h" #include "Core/PSPLoaders.h" -#include "Core/FileSystems/ISOFileSystem.h" #include "Core/ELF/ParamSFO.h" #include "Core/SaveState.h" #include "Core/Util/RecentFiles.h" -#include "Common/StringUtils.h" #include "Common/ExceptionHandlerSetup.h" #include "GPU/GPUCommon.h" #include "GPU/Debugger/Playback.h" @@ -276,6 +276,44 @@ static void ShowCompatWarnings(const Compatibility &compat) { extern const std::string INDEX_FILENAME; +static void MountFileSystems() { + // TODO(scoped): This won't work if memStickDirectory points at the contents of /PSP... +#if defined(USING_WIN_UI) || defined(APPLE) + auto flash0System = std::make_shared(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH); +#else + auto flash0System = std::make_shared(&pspFileSystem, "flash0"); +#endif + FileSystemFlags memstickFlags = FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD; + + Path pspDir = GetSysDirectory(DIRECTORY_PSP); + if (pspDir == g_Config.memStickDirectory) { + // Initially tried to do this with dual mounts, but failed due to save state compatibility issues. + INFO_LOG(Log::sceIo, "Enabling /PSP compatibility mode"); + memstickFlags |= FileSystemFlags::STRIP_PSP; + } + + auto memstickSystem = std::make_shared(&pspFileSystem, g_Config.memStickDirectory, memstickFlags); + + pspFileSystem.Mount("ms0:", memstickSystem); + pspFileSystem.Mount("fatms0:", memstickSystem); + pspFileSystem.Mount("fatms:", memstickSystem); + pspFileSystem.Mount("pfat0:", memstickSystem); + + pspFileSystem.Mount("flash0:", flash0System); + + if (g_RemasterMode) { + const std::string gameId = g_paramSFO.GetDiscID(); + const Path exdataPath = GetSysDirectory(DIRECTORY_EXDATA) / gameId; + if (File::Exists(exdataPath)) { + auto exdataSystem = std::make_shared(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); + pspFileSystem.Mount("exdata0:", exdataSystem); + INFO_LOG(Log::sceIo, "Mounted exdata/%s/ under memstick for exdata0:/", gameId.c_str()); + } else { + INFO_LOG(Log::sceIo, "Did not find exdata/%s/ under memstick for exdata0:/", gameId.c_str()); + } + } +} + // NOTE: The loader has already been fully resolved (ResolveFileLoaderTarget) and identified here. static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::string *errorString) { // Default memory settings @@ -449,6 +487,8 @@ static bool CPU_Init(FileLoader *fileLoader, IdentifiedFileType type, std::strin g_CoreParameter.mountIsoLoader = ConstructFileLoader(g_CoreParameter.mountIso); } + MountFileSystems(); + // Game-specific settings are load from for example Load_PSP_ISO (which calls g_Config.LoadGameConfig). // We can't do things that depend on these before the below switch. So for example, the adjustment of the GPU core // to software has now been moved below it. @@ -541,7 +581,8 @@ void CPU_Shutdown(bool success) { DisplayHWShutdown(); - pspFileSystem.Shutdown(); + pspFileSystem.Shutdown(); // This unmounts all filesystems. + mipsr4k.Shutdown(); Memory::Shutdown(); HLEPlugins::Shutdown(); diff --git a/unittest/JitHarness.cpp b/unittest/JitHarness.cpp index a73ed48d18..a1098e6bef 100644 --- a/unittest/JitHarness.cpp +++ b/unittest/JitHarness.cpp @@ -147,16 +147,6 @@ bool TestJit() { u32 addr = currentMIPS->pc; DebugInterface *dbg = currentDebugMIPS; for (int i = 0; i < 100; ++i) { - /* - // VFPU ops aren't supported by MIPSAsm yet. - *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8); - *p++ = 0xD03C0000 | (1 << 7) | (1 << 15); - *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8); - *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8); - *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8); - *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8); - *p++ = 0xD03C0000 | (1 << 7) | (1 << 15) | (7 << 8); - */ std::string error; for (size_t j = 0; j < ARRAY_SIZE(lines); ++j) { p++; @@ -192,7 +182,7 @@ bool TestJit() { jit_speed = ExecCPUTest(); #if !PPSSPP_PLATFORM(MAC) mipsr4k.UpdateCore(CPUCore::JIT_IR); - jit_ir_speed = ExecCPUTest(false); + jit_ir_speed = ExecCPUTest(false); // not clearing, so the below can do things. #endif // Disassemble diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index f8c3578d6d..a0df995093 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -1617,57 +1617,67 @@ int main(int argc, const char *argv[]) { g_Config.bEnableLogging = true; g_logManager.DisableOutput(LogOutput::DebugString); // not really needed - bool allTests = false; - TestFunc testFunc = nullptr; - if (argc >= 2) { - if (!strcasecmp(argv[1], "all")) { - allTests = true; + // Collect the set of tests to run: "all", or one or more test names by + // (case-insensitive) name. Every non-"all" argument must match a known test name, or we + // bail out with the usage text - a silent partial run (e.g. from a typo) would be worse + // than an error. + std::vector testsToRun; + bool badArg = false; + if (argc == 2 && !strcasecmp(argv[1], "all")) { + for (const auto &f : availableTests) { + testsToRun.push_back(f); } - for (auto f : availableTests) { - if (!strcasecmp(argv[1], f.name)) { - testFunc = f.func; - break; + } else { + for (int i = 1; i < argc; ++i) { + const TestItem *found = nullptr; + for (const auto &f : availableTests) { + if (!strcasecmp(argv[i], f.name)) { + found = &f; + break; + } + } + if (found) { + testsToRun.push_back(*found); + } else { + fprintf(stderr, "Unknown test: %s\n", argv[i]); + badArg = true; } } } - if (allTests) { - int passes = 0; - int fails = 0; - std::vector failedTests; - for (const auto &f : availableTests) { - printf("\n**** Running test %s ****\n", f.name); - if (f.func()) { - ++passes; - } else { - printf("%s: FAILED\n", f.name); - failedTests.push_back(f.name); - ++fails; - } - } - if (passes > 0) { - printf("%d tests passed.\n", passes); - } - if (fails > 0) { - printf("%d tests failed!\n", fails); - for (auto testName : failedTests) { - printf(" * %s\n", testName); - } - return 2; - } - } else if (!testFunc) { - fprintf(stderr, "You may select a test to run by passing an argument, either \"all\" or one or more of the below.\n"); + if (testsToRun.empty() || badArg) { + fprintf(stderr, "You may select tests to run by passing one or more arguments, either \"all\" or one or more of the below.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Available tests:\n"); for (auto f : availableTests) { fprintf(stderr, " * %s\n", f.name); } return 1; - } else { - if (!testFunc()) { - return 2; + } + + int passes = 0; + int fails = 0; + std::vector failedTests; + for (const auto &f : testsToRun) { + printf("\n**** Running test %s ****\n", f.name); + if (f.func()) { + ++passes; + } else { + printf("%s: FAILED\n", f.name); + failedTests.push_back(f.name); + ++fails; } } + if (passes > 0) { + printf("%d tests passed.\n", passes); + } + if (fails > 0) { + printf("%d tests failed!\n", fails); + for (auto testName : failedTests) { + printf(" * %s\n", testName); + } + return 2; + } return 0; }