diff --git a/AGENTS.md b/AGENTS.md index 29897b015a..9c4661f916 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -288,7 +288,7 @@ state, input injection, log tailing), served at `/debugger` on the Remote ISO po Read [docs/WebSocketDebugger.md](docs/WebSocketDebugger.md) before changing the interface, and update it when you add a command. -- Breakpoints are most reliable on the interpreter (`-i`): under the JITs, memory breakpoints only work +- Breakpoints are most reliable on the interpreter (`--cpu=interpreter`): under the JITs, memory breakpoints only work for constant addresses and register breakpoints never trip at all. - **Debugger code that runs off the CPU thread has a lock order that has deadlocked for real** - `Core_RunOnCPUThread()` for mutations, `g_frameMutex` for hot reads. Read diff --git a/Core/CmdLine.cpp b/Core/CmdLine.cpp index 87c6be4287..02c1248873 100644 --- a/Core/CmdLine.cpp +++ b/Core/CmdLine.cpp @@ -310,10 +310,6 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe PRINT_STDOUT(" --state=FILE load state from FILE\n"); PRINT_STDOUT(" --cpu=CPU use the specified CPU core (interpreter, ir, jit, jit-ir)\n"); - PRINT_STDOUT(" -i use the interpreter\n"); - PRINT_STDOUT(" -r use IR interpreter\n"); - PRINT_STDOUT(" -j use JIT\n"); - PRINT_STDOUT(" -J use IR JIT\n"); for (const auto ¶m : g_autoParams) { if (param.mode != CmdLineMode::Both && param.mode != mode) { @@ -402,19 +398,12 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C case 'h': PrintUsage(argv[0], nullptr); return CommandLineParseResult::Exit; - // Legacy cpucore options. - case 'j': - cpuCore = CPUCore::JIT; - break; case 'i': - cpuCore = CPUCore::INTERPRETER; - break; - case 'r': - cpuCore = CPUCore::IR_INTERPRETER; - break; + case 'j': case 'J': - cpuCore = CPUCore::JIT_IR; - break; + // Removed cpu core options. Fail rather than quietly run on the default core. + PRINT_STDERR("Error: %s was removed, use --cpu=interpreter, ir, jit or jit-ir.\n", argv[i]); + return CommandLineParseResult::Error; } } @@ -452,12 +441,6 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C printf("%s\n", PPSSPP_GIT_VERSION); return CommandLineParseResult::Exit; // Commands with parameters. TODO: Should support both space and equals, like --config=foo.ini and --config foo.ini - } else if (equals(argv[i], "--jit-ir")) { - // Headless legacy - cpuCore = CPUCore::JIT_IR; - } else if (equals(argv[i], "--ir")) { - // Headless legacy - cpuCore = CPUCore::IR_INTERPRETER; } else if (startsWith(argv[i], gpuBackendStr)) { const std::string restOfOption = argv[i] + gpuBackendStr.size(); // Force software rendering off, as picking gles implies HW acceleration. diff --git a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp index 99071cb1cf..5c9e4b882e 100644 --- a/Core/Debugger/WebSocket/BreakpointSubscriber.cpp +++ b/Core/Debugger/WebSocket/BreakpointSubscriber.cpp @@ -750,7 +750,7 @@ struct WebSocketRegBreakpointParams { // Add a new register write breakpoint (cpu.regBreakpoint.add) // // Interpreter-only for now - see RegBreakpoint in Core/Debugger/Breakpoints.h. Has no effect -// while running under a JIT backend (force the interpreter core, e.g. -i on the command line). +// while running under a JIT backend (force the interpreter core, e.g. --cpu=interpreter on the command line). // // Parameters: // - register: unsigned integer 0-31 GPR index to break on write to. Ignored if name given. diff --git a/Tools/wsdbg/README.md b/Tools/wsdbg/README.md index bdd2e90b4b..65a504c421 100644 --- a/Tools/wsdbg/README.md +++ b/Tools/wsdbg/README.md @@ -28,7 +28,7 @@ For headless scripting, `--launch` removes all of that. It starts PPSSPP, learns from its output, connects once the socket accepts, and kills it again on the way out: ```bash -wsdbg --sync --compact --quiet --launch ./PPSSPPHeadless.exe --vsh -i --graphics=vulkan < script.txt +wsdbg --sync --compact --quiet --launch ./PPSSPPHeadless.exe --vsh --cpu=interpreter --graphics=vulkan < script.txt ``` `--launch` takes the executable and all of its arguments, so it has to come last - everything after diff --git a/Tools/wsdbg/src/main.rs b/Tools/wsdbg/src/main.rs index c8d441878d..8ac74b7c97 100644 --- a/Tools/wsdbg/src/main.rs +++ b/Tools/wsdbg/src/main.rs @@ -99,7 +99,7 @@ struct Args { /// for this session and kill it on the way out. Takes the executable and all of its /// arguments, so it has to come last: everything after it belongs to PPSSPP, not to wsdbg. /// - /// wsdbg --sync --compact --launch ./PPSSPPHeadless.exe --vsh -i --graphics=vulkan + /// wsdbg --sync --compact --launch ./PPSSPPHeadless.exe --vsh --cpu=interpreter --graphics=vulkan /// /// --debugger=0 is appended unless the given arguments already ask for a debugger port, and /// the port is read straight off the child's stdout rather than scraped from a log file. diff --git a/docs/VSHBootInvestigation.md b/docs/VSHBootInvestigation.md index f50cb97660..76d4a882ca 100644 --- a/docs/VSHBootInvestigation.md +++ b/docs/VSHBootInvestigation.md @@ -14,7 +14,7 @@ finds the debugger port, connects, and cleans up after itself - no wrapper scrip ```bash printf 'cpu.runUntilTime us=6000000\ncpu.status\n:quit\n' | \ Tools/wsdbg/target/release/wsdbg.exe --sync --compact --quiet \ - --launch Windows/x64/Debug/PPSSPPHeadless.exe --vsh -i --graphics=vulkan --loglevel=3 2>emu.log + --launch Windows/x64/Debug/PPSSPPHeadless.exe --vsh --cpu=interpreter --graphics=vulkan --loglevel=3 2>emu.log ``` That takes a few seconds. Protocol replies come back on stdout, the emulator's log on stderr. @@ -23,7 +23,7 @@ Put more commands in the script to inspect state once it stops. Three things to 1. **Don't add `:wait cpu.stepping` after `cpu.runUntilTime`** (or any resume/step command). `--sync` already waits for it; a second one never arrives and burns the whole timeout. This is the single most likely reason a run seems to take minutes - emulation itself is ~real time. -2. **`-i` (interpreter)** is what this path is known to work with, and breakpoints are most +2. **`--cpu=interpreter`** is what this path is known to work with, and breakpoints are most reliable there. 3. Boot needs a real firmware dump in `assets/flash0/` (and optionally `assets/flash1/`), neither of which is in the repo. diff --git a/docs/cpu-equivalence.md b/docs/cpu-equivalence.md index 7d5925afdd..9b6fc793f6 100644 --- a/docs/cpu-equivalence.md +++ b/docs/cpu-equivalence.md @@ -335,5 +335,5 @@ Against the other hosts: ## Unrelated -In headless, `-r` is both the IR interpreter and `--root`, and `--root` wins: it takes the next -argument as a directory. Use `--cpu=ir`. +Headless used to take `-r` for the IR interpreter, but `--root` (whose short form is `-r`) won and took +the next argument as a directory. The short cpu core flags are gone now; use `--cpu=ir`. diff --git a/docs/debugging.md b/docs/debugging.md index b3c93c2fa3..c10ff6f092 100644 --- a/docs/debugging.md +++ b/docs/debugging.md @@ -47,7 +47,7 @@ platforms. Where arch is x64 or ARM64. A working invocation, and the traps around it: ```bash -./Windows/x64/Debug/PPSSPPHeadless.exe -i --debugger=34567 --timeout-wall=100000 --graphics=software --log \ +./Windows/x64/Debug/PPSSPPHeadless.exe --cpu=interpreter --debugger=34567 --timeout-wall=100000 --graphics=software --log \ --root pspautotests/tests/../ pspautotests/tests/cpu/cpu_alu/cpu_alu.prx > hl.log 2>&1 & # wait for "Listening on port" in hl.log, then: ./Tools/wsdbg/target/release/wsdbg.exe 34567 --sync --sync-timeout 15 < script.txt @@ -87,11 +87,8 @@ A working invocation, and the traps around it: that can't be honored is fatal in headless (exit 1) instead of falling back to a random port. If you still suspect you're talking to the wrong process, the `version` response carries `pid` and `path` - check them. - **The headless build defaults to JIT** (`Headless.cpp`, `CPUCore cpuCore = CPUCore::JIT`), despite - `g_Config.iCpuCore` being force-set to INTERPRETER just above - `ApplyToConfig()` has the final say. Pass `-i` for - the interpreter. -- **`-r` is ambiguous in headless**: it's both "use IR interpreter" (legacy short cpu-core flag) and `--root`'s short - form. Passing `-r` makes it eat the *next* argument as the root path, silently dropping e.g. `--debugger=PORT` so - the server never starts. Use `--cpu=ir` instead. (`-i`, `-j`, `-J` are unambiguous.) + `g_Config.iCpuCore` being force-set to INTERPRETER just above - `ApplyToConfig()` has the final say. Pass + `--cpu=interpreter` for the interpreter. (`-r` is `--root`, which eats the next argument as the root path.) - When a test finishes, headless exits and the WebSocket connection closes (`CloseFrame { code: Away }`). So "the connection just closed" after a `cpu.resume` normally means **the breakpoint you were counting on never tripped** and the game ran to completion - not a transport problem. @@ -292,7 +289,7 @@ as reliable, but are maybe not quite there. Concretely, as measured against the headless build (2026-08-16), per CPU backend: -| | interpreter (`-i`) | JIT (`-j`) / IR JIT (`-J`) | +| | interpreter | JIT / IR JIT | |---|---|---| | `cpu.breakpoint.*` (exec) | works | works | | `cpu.stepInto/Over/Out`, `runUntil`, `nextHLE` | works | works | @@ -322,7 +319,7 @@ first-chance exception in Visual Studio. To keep the debugger out of the way whe - **gdb (Linux/Android)**: `handle SIGSEGV nostop noprint pass`. The `pspautotests/tests/cpu/crash/crash_*.prx` binaries each make one bad access (read, write, float, bad jump), -which makes them the quickest check that the handler works on a platform: run one with `-j` in headless. They +which makes them the quickest check that the handler works on a platform: run one with `--cpu=jit` in headless. They should print the guest exception and exit 0, not die with signal 11. ## Debugging a game that works on hardware but not in PPSSPP @@ -365,7 +362,7 @@ Finding the function: 1. **Get to the right moment.** Confirm the thing is actually happening before breaking on it - SSX's videos only start 11.5s in, and a screenshot from earlier just shows the paper background. `memory.read` of the decode buffer at a few `cpu.runUntilTime` points tells you when it starts changing. -2. **Break on the write**, under the interpreter (`-i`), since JIT memory breakpoints only work for constant +2. **Break on the write**, under the interpreter (`--cpu=interpreter`), since JIT memory breakpoints only work for constant addresses: `memory.breakpoint.add address= size=4 write=true enabled=true`, then `cpu.resume`. Pick a pixel in the middle of the frame, not the first one, which clears also hit. 3. **`hle.backtrace`** gives the function's entry address. Addresses come back @@ -388,7 +385,7 @@ hle.backtrace ```bash Tools/wsdbg/target/release/wsdbg.exe --sync --compact --quiet --launch \ - Windows/x64/Release/PPSSPPHeadless.exe GAME.iso --memstick=memstick -i --graphics=vulkan \ + Windows/x64/Release/PPSSPPHeadless.exe GAME.iso --memstick=memstick --cpu=interpreter --graphics=vulkan \ --timeout-wall=240 < script.txt 2>/dev/null | grep -v '^<- log' ``` diff --git a/docs/pkg_notes.md b/docs/pkg_notes.md index 720e35ff08..fc8932793b 100644 --- a/docs/pkg_notes.md +++ b/docs/pkg_notes.md @@ -153,7 +153,7 @@ disc before deciding to boot the patch. `PBOOT.PBP` to `EBOOT.PBP`, and boot it headless with the UMD mounted - ``` - ./build/PPSSPPHeadless -i --graphics=software --memstick= \ + ./build/PPSSPPHeadless --cpu=interpreter --graphics=software --memstick= \ --mount="Hatsune Miku - Project Diva 2nd (Japan).iso" \ /PSP/GAME/ULJM05681/EBOOT.PBP ``` diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 4a44e2e51e..d0f6a6751c 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -180,7 +180,6 @@ std::string NativeLoadSecret(std::string_view nameOfSecret) { int printUsage(const CommandLineOptions &options, const char *progname, const char *reason) { options.PrintUsage(progname, reason); - fprintf(stderr, " --ir use ir interpreter\n"); fprintf(stderr, " --flash0=DIR use DIR as flash0: instead of the bundled assets (e.g. a real dump)\n"); return 1; } diff --git a/headless/README.md b/headless/README.md index 7c6a17bebd..0d148b3040 100644 --- a/headless/README.md +++ b/headless/README.md @@ -52,9 +52,7 @@ PPSSPPHeadless file.elf|file.prx|file.ppdmp [...] [options] | `--timeout=` | Abort test if it takes longer than this. | | `-v`, `--verbose` | Print full pass/fail details. | | `--bench` | Run multiple times and report average speed. | -| `-i` | Use interpreter CPU core. | -| `--ir` | Use IR interpreter CPU core. | -| `-j` | Use JIT CPU core (default). | +| `--cpu=` | CPU core: `interpreter`, `ir`, `jit` (default), `jit-ir`. | | `--debugger=` | Enable WebSocket debugger and break at start. | | `--state=` | Load a save state before running. | | `--old-atrac` | Use the old Atrac3+ audio decoder. | diff --git a/test.py b/test.py index 730444c6ab..d8b974add4 100755 --- a/test.py +++ b/test.py @@ -736,12 +736,9 @@ def init(): def cpu_backend(args): # Which backend headless will end up on, given the args we hand through to it. Headless defaults # to the JIT, and a later flag overrides an earlier one, like its own parsing in Core/CmdLine.cpp. - short_flags = {'-i': 'interpreter', '-r': 'ir', '-j': 'jit', '-J': 'jit-ir'} backend = 'jit' for arg in args: - if arg in short_flags: - backend = short_flags[arg] - elif arg.startswith('--cpu='): + if arg.startswith('--cpu='): backend = arg[len('--cpu='):] return backend