Commit Graph
185 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Sonnet 5 75174af77b Add GPR write breakpoints (break when a register is written, anywhere)
New debugging primitive: break whenever any instruction writes to a
given general-purpose register (0-31), regardless of which address
executes the write. Requested for continuing the reboot.bin trace,
where the actual blocker is "what sets $s3 to this bad value", not
"what happens at a specific address" - existing address/memory
breakpoints can't express that directly.

- GPRBreakpoint (Core/Debugger/Breakpoints.h) mirrors the existing
  BreakPoint/MemCheck shape (result/condition/logFormat/hit count),
  keyed by register index instead of address/range.
- BreakpointManager keeps a u32 bitmask (bit i = register i has an
  active breakpoint) alongside the GPRBreakpoint vector, so the
  interpreter loop can test "would this write trip anything" with a
  single shift+and against a value already cached in a local.
- RunUntilDowncountZeroWithChecks (Core/MIPS/MIPSTables.cpp) computes
  the about-to-be-written register from the current instruction's
  OUT_RT/OUT_RD/OUT_RA flags (GetGPRWriteTarget()) and checks it
  against the mask, same convention as the existing memcheck handling
  right above it (checked before the instruction executes, bails via
  CORE_STEPPING_CPU without running it if tripped).
- New BreakReason::GPRBreakpoint ("cpu.gprBreakpoint") for Core_Break.
- WebSocket API: cpu.gprBreakpoint.add/update/remove/list, accepting
  either a 0-31 'register' index or a case-insensitive 'name' (e.g.
  "s3"), documented in docs/WebSocketDebugger.md.

Interpreter-only for now, deliberately.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-13 11:35:29 +02:00
Henrik RydgårdandClaude Sonnet 5 5dbe9bea91 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-13 11:34:00 +02:00
Henrik Rydgård eb0813c0e3 Add a utility function for all the ABIs to call functions with a pointer arg. Use to call Advance from the JIT with the MIPSContext. Indent some code better. 2026-08-13 08:09:30 +02:00
Henrik Rydgård 5a8e24f583 Interpreter: Hook up dummy MMIO handlers for future experiments 2026-08-12 14:48:23 +02:00
Henrik Rydgård e9a3449ede More MIPSState * plumbing (manual) 2026-08-12 14:02:19 +02:00
Henrik Rydgård b10e4ad1b5 Code style improvement, handle unknown instructions with a pseudo CPU exception 2026-08-12 14:02:19 +02:00
Henrik RydgårdandClaude Sonnet 5 ffad0acea1 Plumb an explicit MIPSState *mips through the interpreter's Int_* handlers
All ~82 MIPSInt::Int_* functions (Interpreter.cpp/.h,
InterpreterVFPU.cpp/.h) now take an explicit MIPSState *mips instead
of reaching for the global currentMIPS internally, along with their
file-local helpers (DelayBranchTo, SkipLikely, ApplySwizzleS/T,
ApplyPrefixD/ST, RetainInvalidSwizzleST, EatPrefixes). MIPSInterpretFunc,
Interpret(), ExecInstruction()/InterpreterDispatch.cpp (regenerated),
and RunUntilFast() all thread mips through accordingly.

Deliberately left on currentMIPS for now: MIPSVFPUUtils.cpp's
ReadVector/WriteVector/ReadMatrix/WriteMatrix/VFPURewritePrefix -
these are shared with every JIT backend's compile-time VFPU code, so
parameterizing them would balloon this into a JIT-wide refactor. This
is a partial refactor; that's the next boundary to push on.

Several JIT backends (x86 Jit.cpp, ARM/ArmJit.cpp, ARM64/Arm64Jit.cpp,
x86/X64IRJit.cpp, RiscV/RiscVJit.cpp, LoongArch64/LoongArch64Jit.cpp,
ARM64/Arm64IRJit.cpp) bake the raw interpreter function pointer
directly into JIT-generated machine code as their "fall back to the
interpreter for this one op" mechanism, with only a single argument
register set up for the call. Rather than hand-editing register
allocation across four architectures that can't be build-tested here,
added MIPSInterpretTrampoline(MIPSOpcode op) - a 1-arg wrapper around
MIPSInterpret(currentMIPS, op) - and pointed all 7 such call sites at
it instead, leaving that codegen untouched. Two other call sites
(JitLogMiss, JitBranchLog) were plain C++ calls and just got the
extra argument directly.

Verified (Windows x64): PPSSPPWindows/PPSSPPHeadless/UnitTest all
build clean, 49/49 unit tests pass. `test.py -g --graphics=software`:
interpreter 312/314 (cpu/fpu/fpu is the pre-existing, unrelated
interpreter-vs-JIT denormal difference; gpu/rendertarget/copy passes
standalone, so was cross-test state bleed in the batch run, not a
regression), default JIT 314/314, jit-ir 313/314 (gpu/vertices/morph
is an expected difference from the vertex decoder taking a different
mode with this core change, not a bug).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
2026-08-12 14:02:19 +02:00
Henrik RydgårdandClaude Sonnet 5 ff864be3e2 Make ExecInstruction's unhandled case a plain -1 return, not an embedded fallback
The generated dispatch tree previously fell back to the old
MIPSGetInstruction()-based slow path (via a shared goto label) for
anything it didn't recognize - both genuinely invalid encodings and
the handful of real instructions with no interpreter implementation
(tge/tlt/teq/...). That baked policy ("what to do when unhandled")
into mechanically generated code, which is the wrong layer for it.

ExecInstruction() is now honestly partial: every unmatched case
returns -1, and callers are responsible for handling that. The
generated file no longer calls back into MIPSInterpret()/
MIPSGetInstructionCycleEstimate() at all, and no longer needs
MIPSTables.h.

RunUntilFast()'s -1 handling also skips re-walking MIPSGetInstruction()
entirely: since ExecInstruction() is generated from those exact same
tables, a -1 can only mean "no MIPSInstruction::interpret for this
op" - MIPSGetInstruction() would just rediscover the same thing.
Extracted that shared "log + disassemble + assert + skip" behavior
into HandleUnknownInstruction(), called directly instead.

Verified with `test.py -g --cpu=interpreter --graphics=software`:
still 313/314 (same pre-existing, unrelated cpu/fpu/fpu failure).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
2026-08-12 12:31:18 +02:00
Henrik Rydgård cc90546301 Prep for plumbing the MIPS context pointer into the interpreter. 2026-08-12 11:25:06 +02:00
Henrik RydgårdandClaude Sonnet 5 d000d5366a Wire the generated ExecInstruction dispatcher into the interpreter's hot loop
Adds Core/MIPS/InterpreterDispatch.cpp, the checked-in output of
GenerateInterpreterDispatch() (see the previous commit), and hooks it
into RunUntilFast() in MIPSTables.cpp in place of the old
MIPSGetInstruction()-based table walk + indirect call through
instr->interpret. The checked-with-breakpoints/memchecks path
(RunUntilWithChecks) is untouched for now, since it inspects
MIPSInstruction flags directly and correctness there matters most.

Also fixes a real crash in headless.cpp found while testing this:
cmdLineOptions.gpuBackend.value() would throw when unset (e.g. with
--graphics=software), now uses value_or().

Verified with `test.py -g --cpu=interpreter --graphics=software`:
313/314 pass; the one failure (cpu/fpu/fpu) is a pre-existing
interpreter-vs-JIT denormal (flush-to-zero) difference, confirmed to
fail identically with the old table-walking dispatch, so unrelated to
this change.

Adds Tools/update-dispatcher.py to regenerate InterpreterDispatch.cpp
from a built PPSSPPHeadless binary whenever the MIPSTables.cpp tables
change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
2026-08-12 10:52:47 +02:00
Henrik RydgårdandClaude Sonnet 5 65495c76c0 Add a codegen tool to generate a fast switch-tree interpreter dispatcher
MIPSTables.cpp has walked a tree of tables on every single interpreted
instruction since forever, with a standing TODO asking for exactly this:
"generate smart dispatcher functions from above tables instead of this
slow method." GenerateInterpreterDispatch() does that - it walks the
same tables MIPSGetInstruction() walks at runtime, but resolves the
walk into a nested switch tree once, at generation time, with each
leaf calling straight into the existing MIPSInt::Int_* handlers and
returning that instruction's fixed cycle count. Anything not covered
(invalid opcodes, and the handful of instructions with no interpreter
implemented at all, e.g. tge/tlt/teq) falls back to the existing
MIPSInterpret()/MIPSGetInstructionCycleEstimate() slow path, so the
result is total over all 32-bit inputs, same as the table-walking path.

Wired up via a new headless --generate-interpreter-dispatch flag,
which prints the generated Core/MIPS/InterpreterDispatch.cpp source to
stdout and exits.

Also widens CmdLine.cpp's --help column formatting, which silently
truncated any option name longer than 24 characters - the new option's
name was the first to hit it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
2026-08-12 10:24:01 +02:00
Henrik RydgårdandClaude Sonnet 5 d718bda178 Rename MIPSInt/MIPSIntVFPU to Interpreter/InterpreterVFPU
Just a file rename (plus updating all build configs and includes) to
give the interpreter source files clearer names. No functional change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
2026-08-12 10:04:40 +02:00
Henrik Rydgård 3bd41376da More memory cleanup 2026-08-11 20:12:10 +02:00
Thomas Lamb bf67ed300d Fix assembly/disassembly for various VFPU opcodes
Update armips for VFPU instruction parsing changes (Kingcom/armips#257)
- fix transposed matrix register encodings (e.g. `vmidt.p E220` was assembling to `vmidt.p E202`)
- fix immediate size for `vwbn.s` (is 8 bits, was limited to 5 bits)
- fix `vd` register size for `vsocp.s/p`
- fix `vhtfm2.p` and `vhtfm3.t` encoding
- add missing instruction `vhtfm4.q`

Fix `v(h)tfmX` disassembly (handle irregular vector size encoding)

Fix `vsocp.s/p` disassembly (`vd` is double the size of `vs`)
2026-08-11 10:10:56 -04:00
Thomas Lamb d1656d425a Improve debugger assemble/disassemble compatiblity
Update armips for VFPU instruction parsing changes:
- Kingcom/armips#255
- Kingcom/armips#256

Remove brackets from VFPU instruction `vpfx*` params

Rename VFPU instruction `vuc2i.s` to `vuc2ifs.s`

Add brackets to VFPU instruction `vpfxd` saturation operations (i.e. [0:1] & [-1:1])

Rename FPU instructions `c.$OP` to `c.$OP.s`

Fix VFPU instruction `vuc2ifs.s` `vd` size in disassembly

Replace `CC[imm3]` with `imm3` in VFPU instructions `vcmov*` & `bv*` disassembly
2026-07-07 20:30:51 -04:00
Henrik Rydgård 962bd8239d Remove more excessive error reporting. 2025-03-02 02:28:45 +01:00
Henrik Rydgård 31cf5771f4 Turn the break reason into an enum, fix some minor issues 2025-02-19 16:01:11 -06:00
Henrik Rydgård 3e198c53b2 More include cleanup 2024-12-18 13:57:26 +01:00
Henrik Rydgård 96c4a10e8c Add two new core states, rename RUNNING to RUNNING_CPU and similar for stepping. 2024-12-01 21:04:21 +01:00
Henrik Rydgård 7992ff4627 Make CBreakpoints an object 2024-11-25 00:22:53 +01:00
Henrik Rydgård d3e9398cb3 Split Core_EnableStepping into Core_Break and Core_Resume 2024-11-03 17:53:42 +01:00
Henrik Rydgård e01ca5b057 Logging API change (refactor) (#19324)
* Rename LogType to Log

* Explicitly use the Log:: enum when logging. Allows for autocomplete when editing.

* Mac/ARM64 buildfix

* Do the same with the hle result log macros

* Rename the log names to mixed case while at it.

* iOS buildfix

* Qt buildfix attempt, ARM32 buildfix
2024-07-14 14:42:59 +02:00
Henrik Rydgård d1a00f61de Improve disassembly of CallReplacement IR op 2024-06-06 15:24:58 +02:00
Henrik Rydgård e3177ac870 Make some global string pointers const, not just the strings.
Minor cleanup.
2023-12-29 14:09:45 +01:00
Unknown W. Brackets 74e5e43fdc jit: Skip known prefix writes.
If we already know what's in memory and it's default, we can skip
overwriting with default values.  This is common, actually.
2023-08-22 23:26:31 -07:00
Unknown W. Brackets b6d2e64aca Debugger: Fix disasm of ll/sc. 2023-07-29 18:50:09 -07:00
Unknown W. Brackets df2462b1d9 irjit: Implement ll/sc.
These occur more than I expected in LittleBigPlanet while loading.
2023-07-29 17:57:44 -07:00
Unknown W. Brackets 5b1235537f Debugger: Make disasm more thread safe. 2023-04-29 09:56:17 -07:00
Unknown W. Brackets 46101581c0 Core: Cleanup disasm buffer usage. 2023-04-29 09:07:25 -07:00
Henrik Rydgård 6945deec01 Replace a LOT of sprintf with snprintf, and a few strcpy with truncate_cpy 2023-04-28 21:04:05 +02:00
Unknown W. Brackets 0f79afa172 interp: Support memory breakpoints too. 2022-11-13 17:45:43 -08:00
Unknown W. Brackets f9da9e6b60 interp: Centralize memory size handling. 2022-11-13 17:38:53 -08:00
Unknown W. Brackets 76cf4dbf12 interp: Allow breakpoints in release mode. 2022-11-13 16:52:40 -08:00
Unknown W. Brackets 1662bd3bb8 interp: Allow resume from breakpoint. 2022-11-13 16:03:29 -08:00
Unknown W. Brackets 2bd13c5d9d Debugger: Track reason for entering stepping. 2021-10-23 16:56:15 -07:00
kotcrab 450d0ef015 Remove .s suffix from vwbn disassembly 2021-09-27 22:42:10 +02:00
kotcrab 4bdba8ae6f Fix disassembly of vmfvc and vmtvc 2021-09-25 16:33:07 +02:00
Unknown W. Brackets c4eafcf008 jit: Increase the cycle cost of div.s.
This largely matches tests on a real PSP.
2021-04-12 07:06:18 -07:00
Unknown W. Brackets 53104639ff jit: Increase the cycle cost of VFPU ops.
It seems like they all take at least 2 cycles, which kinda makes sense.
2021-04-12 07:06:18 -07:00
Unknown W. Brackets bc16a55028 jit: Count delay slot cycles separately.
This makes it easier to count cycles per instruction, instead of ignoring
the delay slot's instruction for cycle count.
2021-04-12 07:04:22 -07:00
Unknown W. Brackets f32f89dd90 Global: Remove some unused variables. 2021-02-15 11:59:45 -08:00
Henrik Rydgård 6f1915110f Remove base/logging from UI and more 2020-08-15 19:08:54 +02:00
Henrik Rydgård c5e0b799d9 Remove category from _assert_msg_ functions. We don't filter these by category anyway.
Fixes the inconsistency where we _assert_ didn't take a category but
_assert_msg_ did.
2020-07-19 20:33:25 +02:00
Unknown W. Brackets 940066db68 Core: Track exception on bad jumps. 2020-07-12 21:59:08 -07:00
Unknown W. Brackets aff1d8e8b2 interp: Implement vsbz and vlgb.
Not sure any games actually use them, but good to have the remaining vfpu
ops all implemented.
2019-02-23 15:08:49 -08:00
Unknown W. Brackets 520f850e09 Jit: Correct VFPU prefix eating flags.
Verified by tests.  Also, confirmed that the old vxbz encoding had no
output, but the new position (as used by gas) does.
2019-02-23 15:08:49 -08:00
Unknown W. Brackets 25136eb019 Remove an unused function. 2016-05-31 11:40:39 -07:00
Henrik Rydgard 6e44e97ffa Refactor prep: Split JitInterface into MIPSFrontendInterface and JitInterface 2016-05-09 19:41:39 +02:00
Henrik Rydgard 3c5510e5a3 Disable debug file logging, fix issue with replacement functions, etc 2016-05-07 21:35:12 +02:00
Henrik Rydgard 12edfcea5a Enough to run cpu_alu.prx. 2016-05-07 21:00:30 +02:00