From 5dbe9bea912a37bb8fece2133f7fee5f22749049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 13 Aug 2026 06:47:39 +0200 Subject: [PATCH] Add dummy interpreter implementations for the basic COP0 instructions mfc0/mtc0/rdpgpr/mfmc0/wrpgpr had no interpreter execution function at all - ordinary PSP user-mode code never executes COP0 instructions directly so nobody needed one. Real kernel-mode boot code does the opposite: flash0:/reboot.bin's very first instruction is mfc0. Int_Cop0 (Interpreter.cpp) backs these with a small file-scope shadow register array - not real COP0 semantics (no interrupts/exceptions/ TLB), just enough to not fault and give plausible write-then-read-back-same-value behavior for boot code that pokes Status and other registers. Might later be added to MIPSState. Also regenerated Core/MIPS/InterpreterDispatch.cpp (`PPSSPPHeadless --generate-interpreter-dispatch`). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9 --- Core/MIPS/Interpreter.cpp | 44 +++++++++++++++++++++++++++++++ Core/MIPS/Interpreter.h | 1 + Core/MIPS/InterpreterDispatch.cpp | 8 ++++++ Core/MIPS/MIPSTables.cpp | 14 ++++++---- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Core/MIPS/Interpreter.cpp b/Core/MIPS/Interpreter.cpp index ef9d7bc184..f507d8e020 100644 --- a/Core/MIPS/Interpreter.cpp +++ b/Core/MIPS/Interpreter.cpp @@ -685,6 +685,50 @@ namespace MIPSInt { PC += 4; } + // Dummy COP0 register file, for kernel-mode code (e.g. flash0:/reboot.bin) that reads/ + // writes COP0 state directly - ordinary PSP user-mode code never executes these + // instructions (kernel state is reached via HLE syscalls instead), so this doesn't need + // to model real COP0 semantics (interrupts, exceptions, TLB, ...), just be non-fatal and + // give writes-then-reads-back-same-value behavior. Not part of MIPSState (which is + // layout-sensitive for JIT register allocation) and not save-stated, in keeping with + // "dummy" - revisit if this ever needs to be more than a bail-out. + static u32 g_cop0Regs[32]; + + void Int_Cop0(MIPSState *mips, MIPSOpcode op) { + int rt = _RT; + int rd = _RD; + + switch ((op >> 21) & 0x1f) { + case 0: //mfc0 + if (rt != 0) + R(rt) = g_cop0Regs[rd]; + break; + + case 4: //mtc0 + g_cop0Regs[rd] = R(rt); + break; + + case 10: //rdpgpr + if (rt != 0) + R(rt) = R(rd); + break; + + case 11: //mfmc0 (di/ei) + if (rt != 0) + R(rt) = g_cop0Regs[12]; // Status + break; + + case 14: //wrpgpr + R(rd) = R(rt); + break; + + default: + _dbg_assert_msg_(false, "Trying to interpret COP0 instruction that can't be interpreted"); + break; + } + PC += 4; + } + void Int_RType2(MIPSState *mips, MIPSOpcode op) { int rs = _RS; int rd = _RD; diff --git a/Core/MIPS/Interpreter.h b/Core/MIPS/Interpreter.h index 02dc35244c..de1ec80cde 100644 --- a/Core/MIPS/Interpreter.h +++ b/Core/MIPS/Interpreter.h @@ -27,6 +27,7 @@ namespace MIPSInt void Int_Syscall(MIPSState *mips, MIPSOpcode op); void Int_mxc1(MIPSState *mips, MIPSOpcode op); + void Int_Cop0(MIPSState *mips, MIPSOpcode op); void Int_RelBranch(MIPSState *mips, MIPSOpcode op); void Int_RelBranchRI(MIPSState *mips, MIPSOpcode op); void Int_IType(MIPSState *mips, MIPSOpcode op); diff --git a/Core/MIPS/InterpreterDispatch.cpp b/Core/MIPS/InterpreterDispatch.cpp index 30885ec9bf..9539e6d0ea 100644 --- a/Core/MIPS/InterpreterDispatch.cpp +++ b/Core/MIPS/InterpreterDispatch.cpp @@ -144,6 +144,14 @@ int ExecInstruction(MIPSState *mips, MIPSOpcode op) { case 16: { switch ((op.encoding >> 21) & 0x1f) { + // mfc0, mtc0, rdpgpr, mfmc0, wrpgpr + case 0: + case 4: + case 10: + case 11: + case 14: + MIPSInt::Int_Cop0(mips, op); + return 1; case 16: case 17: case 18: diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index e0da966bf0..a2cbde1642 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -368,23 +368,27 @@ static const MIPSInstruction tableCop2BC2[4] = // 010010 01000 ...xx ........... static const MIPSInstruction tableCop0[32] = // 010000 xxxxx ..... ................ { - INSTR("mfc0", JITFUNC(Comp_Generic), Dis_Generic, 0, OUT_RT), // unused + // Dummy interpreter-only implementations (Int_Cop0, Interpreter.cpp) - real hardware + // semantics aren't modeled, just enough to not fault. Ordinary user-mode PSP code never + // executes these; kernel-mode boot code (flash0:/reboot.bin) does. See + // docs/VSHBootInvestigation.md. + INSTR("mfc0", JITFUNC(Comp_Generic), Dis_Generic, Int_Cop0, OUT_RT), INVALID, INVALID, INVALID, - INSTR("mtc0", JITFUNC(Comp_Generic), Dis_Generic, 0, IN_RT), // unused + INSTR("mtc0", JITFUNC(Comp_Generic), Dis_Generic, Int_Cop0, IN_RT), INVALID, INVALID, INVALID, //8 INVALID, INVALID, - INSTR("rdpgpr", JITFUNC(Comp_Generic), Dis_Generic, 0, 0), - INSTR("mfmc0", JITFUNC(Comp_Generic), Dis_Generic, 0, 0), + INSTR("rdpgpr", JITFUNC(Comp_Generic), Dis_Generic, Int_Cop0, OUT_RT), + INSTR("mfmc0", JITFUNC(Comp_Generic), Dis_Generic, Int_Cop0, OUT_RT), INVALID, INVALID, - INSTR("wrpgpr", JITFUNC(Comp_Generic), Dis_Generic, 0, 0), + INSTR("wrpgpr", JITFUNC(Comp_Generic), Dis_Generic, Int_Cop0, IN_RT), INVALID, //16 ENCODING(Cop0CO), ENCODING(Cop0CO), ENCODING(Cop0CO), ENCODING(Cop0CO), ENCODING(Cop0CO), ENCODING(Cop0CO), ENCODING(Cop0CO), ENCODING(Cop0CO),