It reported BreakReason::DebugBreak, the same reason a user hitting pause
produces, so a client that asked to run until a point in emulated time couldn't
tell its deadline landing from someone stopping the core by hand - the one
piece of information the cpu.stepping event exists to convey.
Reports "cpu.runUntilTime" now, matching the request that armed it.
No client depended on the old string: nothing outside Core.cpp names
"cpu.debugbreak", and wsdbg's resume handling keys off the event, not the
reason.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
Lining a scripted repro up with a bug report ("about five seconds in, press
X") had no support at all. The only way to do it was to poll cpu.status in a
loop from the client, which is slow - a process spawn per poll, minutes for a
single run - and lands somewhere different every time, so the repro isn't one.
cpu.runUntilTime takes either an absolute `us` (as reported by cpu.status) or
`relativeUs` from now, resumes, and breaks when emulated time gets there. It
answers immediately with the target, and the usual cpu.stepping event follows
when it arrives. Anything else that stops the CPU first - a breakpoint, an
exception - cancels the deadline, the same way it cancels a pending step.
The deadline is held in microseconds, not ticks, and recomputed whenever
SetClockFrequencyHz() runs. Converting to a tick count once up front looks
right and isn't: games change the CPU clock while running, and CrossCraft
Classic goes 222 -> 333MHz during startup, which made a request for 3.0s stop
at 2.24s. With the recompute it stops at exactly 3000000us. Advance() also
shortens its slice to land on the deadline instead of up to a slice past it,
so repeated runs stop at the same instruction rather than somewhere in the
following frame.
Nothing is added to CoreTiming's event list, so savestates are unaffected -
the deadline is debugger session state and isn't serialized.
Also adds DebuggerRequest::ParamF64, since microseconds outgrow 32 bits after
about 71 minutes. Like the other Param* helpers it fails loudly on a missing
or unparseable value rather than defaulting.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
cpu.status only reported raw CPU ticks, which a client can't turn into a time:
the PSP's clock frequency is changeable and games do change it, so the
ticks-per-second ratio isn't fixed over a run. CrossCraft Classic runs at
333MHz, so assuming the default 222MHz reads 9.1s where the truth is 6.3s -
enough to put scripted input injection in the wrong place entirely.
Adds "us" (emulated microseconds) and "clockHz" alongside "ticks".
CoreTiming::GetGlobalTimeUs() can't be used directly for this: it rebases its
own internal counters as a side effect, and cpu.status is deliberately served
straight from the WebSocket thread rather than queued to the CPU thread (it's
meant to be cheap and frequently pollable). So PeekGlobalTimeUs() computes the
same value without the rebasing.
AGENTS.md picks up the things that cost time while driving headless over the
websocket API: --sync silently desynchronises on raw JSON lines because only
wsdbg's key=value shorthand gets a ticket; headless reports HAS_DEBUGGER as
false so anything gated on it silently does nothing there; the memstick is
hardcoded next to the executable; a leftover headless process turns a build
into an LNK1168 that looks like a compile error; wrapping the launcher in
`timeout` kills the emulator along with it, losing the crash you stopped at;
response field names aren't uniform (value vs uintValue); broadcast.config.set
rejects two of the four keys the docs list.
Also writes down the ELF-as-oracle technique that cracked the relocation bug -
when homebrew ships app.elf next to app.prx, the pre-link ELF still has the
symbols and the relocation symbol indices the PRX format discards, so the
loader's output can be checked exhaustively offline instead of by re-running.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
* 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
We haven't used these "threadsafe" events since we removed our first attempt
at GPU threading, so like 10 years, and maybe some experimentation in the
networking code according to some comments. It's unlikely that any
savestates that used these events would load anyway.