Same treatment as the other subscribers: thread list/wake/stop, function and
data symbol list/add/remove/rename/scan, and backtrace now route their
kernel thread, symbol map, and disassembly manager access through
Core_RunOnCPUThread() instead of touching that state directly from the
WebSocket handler thread.
Memory::IsValidRange() checks that only depend on the request's own
address/size params (not on anything CPU-thread-owned) stay outside the
queued callback and fail fast, rather than making a pointless round trip
through the CPU thread for a request already known to be invalid.
hle.func.scan carries the same unbounded-range caveat already noted for
memory.search: no cap on 'size' beyond valid memory range.
Replaced remaining `auto` locals with concrete types.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
Every memory.read*/write*/search endpoint used to call LockMemoryAndCPU(),
which - unless the CPU was already stepping - busy-waited for coreState to
settle, force-paused a running game with Core_Break(), and blocked on
Core_WaitInactive() before touching memory, just to get exclusive access
from the WebSocket handler thread. It also took MIPSComp::jitLock around
saving/restoring emuhack ops for the same reason.
Now the whole body of each handler runs inside Core_RunOnCPUThread(), so
none of that is needed for CPU-thread exclusivity: reads/writes happen
inline on the CPU thread itself, whether the game is running or stepping,
without ever pausing it. Confirmed live that memory reads/search now
complete while coreState stays CORE_RUNNING_CPU throughout - no more
stepping flicker on every debugger memory poll.
Kept Memory::MemoryInitedLock (guards against Memory::Shutdown() racing in
from a different thread, e.g. the UI thread stopping the game - unrelated
to the WebSocket-thread-vs-CPU-thread problem) and MIPSComp::jitLock around
the emuhack save/restore (guards against a UI-triggered CPU core switch,
also a different thread than the one Core_RunOnCPUThread targets).
Same caveats as previous conversions: memory.read for a very large 'size'
now base64-encodes on the CPU thread itself, and memory.search still has no
size cap - both will now block the CPU thread's own frame pump for their
duration on a large enough request. Noted inline, not fixed here.
Replaced remaining `auto` locals with concrete types.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
CPUCoreSubscriber through the CPU thread
Continues the pattern started with SteppingSubscriber: route breakpoint,
disassembly, symbol, and register access through Core_RunOnCPUThread()
instead of touching that state directly from the WebSocket handler thread.
Two intentional exceptions, matching the reasoning already used for
cpu.stepInto's "not currently stepping" branch:
- cpu.stepping's Core_Break() call stays unqueued - it's what makes the CPU
thread start reaching the queue drain point in the first place.
- cpu.status stays unqueued - it's meant to be a cheap, frequently-pollable
status check, and its "pc" field is already documented as inaccurate unless
stepping. Matches how SteppingBroadcaster already reads the same state
directly from the WebSocket thread.
Where a handler's response doesn't depend on anything the queued lambda
computed (plain add/remove endpoints), moved req.Respond() back out after
the Core_RunOnCPUThread() call for readability - the JSON building and
socket write happen later in Finish() regardless of where Respond() is
called, so there's no thread-safety difference either way, just clarity
about what actually needs to run on the CPU thread.
memory.searchDisasm carries the same caveat flagged for memory.search: its
scan range has no size cap, so if the CPU is stepping, a very large range
will now block the CPU thread's own frame pump for the scan's duration
rather than running unqueued on the WebSocket thread as before. Not fixed
here - noted in a comment at the call site.
Replaced remaining `auto` locals in these three files with concrete types.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
Extends the cpu.stepInto treatment to cpu.stepOver, cpu.stepOut, cpu.runUntil,
and cpu.nextHLE: each now routes its breakpoint/stepping manipulation through
Core_RunOnCPUThread() instead of touching it directly from the WebSocket
handler thread. cpu.runUntil didn't have an explicit "must be stepping"
guard to begin with; since the CPU-thread queue is now drained unconditionally
at the top of every Core_RunLoopUntil() iteration (not just while stepping),
queuing from it is safe regardless of current core state.
Also corrects a stale comment on Core_RunOnCPUThread() left over from before
the drain point moved from Core_ProcessStepping() to the top of
Core_RunLoopUntil() - it's not limited to the stepping/paused case.
Replaced remaining `auto` locals in this file with concrete types.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
The WebSocket debugger's cpu.stepInto handler ran entirely on the WebSocket
handler thread, directly manipulating breakpoints and stepping state (via
Core_RequestCPUStep, g_breakpoints.SetSkipFirst, etc.) that's otherwise only
ever touched from the CPU thread (the one that calls Core_RunLoopUntil, and
thus indirectly NativeFrame).
Adds Core_RunOnCPUThread() - queues a function to run on the CPU thread and
blocks the caller until it's done. The queue is drained at the top of
Core_RunLoopUntil()'s loop, so it's reached continuously (in a tight spin)
while the CPU is stepping/paused, and at least once per call even while fully
running.
cpu.stepInto is the first consumer: once the CPU is already stepping, the
breakpoint/stepping manipulation is now routed through Core_RunOnCPUThread
instead of happening directly on the WebSocket thread. The "not currently
stepping" path still calls Core_Break() directly from the WebSocket thread,
since it's already documented free-threaded and is what makes the CPU thread
start reaching the queue-drain point in the first place.
More WebSocket debugger commands can be converted the same way going forward.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
DisassemblyManager used to fuse lui+addiu/load/store into single pseudo-
instructions ("li", fused loads/stores) for display. This only applied to a
handful of opcodes, complicated DisassemblyManager, and was the root cause of
a stepping bug: Core_PerformCPUStep's Into/Over cases treated stepSize as a
byte count, while the WebSocket cpu.stepInto handler computed it as an
instruction count (needed to step over a whole fused macro in one go) - so a
plain, non-fused stepInto silently executed zero instructions.
Removed the fusion logic entirely (DisassemblyMacro, DISTYPE_MACRO) - every
disassembly line is now exactly one 4-byte instruction. With that,
"how many instructions does this line span" is always 1, so the
getInstructionSizeAt() byte-size queries in the legacy Windows and ImGui
debuggers are gone too; step requests just pass 1. Core_RequestCPUStep's
stepSize is now consistently in instructions everywhere.
Also fixes the PPSSPPHeadless build, broken since 0ed1f3e added
OpenWebDebugger() (which calls System_LaunchUrl) without a headless stub.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hqm11k99viLfbJm2MkH4BH
GLEW resolves its entry points through GLX unless built with GLEW_EGL. On a
display stack that provides no GLX -- Wayland, or GBM -- glxewInit() returns
GLEW_ERROR_NO_GLX_DISPLAY, glewInit() fails, and the GL backend never starts.
Building the EGL backend resolves them through eglGetProcAddress instead, which
works on both. It is opt-in rather than the default so existing GLX builds are
untouched.
Note that changing this needs ext/glew/glew.o deleted to take effect, since the
Makefile is not a prerequisite of the object.
Adds SceKernelLoadExecVSHParam (matching JPCSP's reference layout - its
first four fields line up with the existing SceKernelLoadExecParam,
which is why the plain sceKernelLoadExec already worked for
sceKernelLoadExecVSHMs2) and fills in the rest of LoadExecForKernel's
NIDs from JPCSP: real implementations for sceKernelExitVSHVSH/Kernel
(mirrors sceKernelExitGame) and sceKernelLoadExecBufferVSHUsbWlan (loads
an exec from an in-RAM buffer instead of a file - the VSH's "push a game
over USB/WLAN" path), plus UNIMPL stubs for everything JPCSP itself only
knows by NID.
sceKernelLoadExecBufferVSHUsbWlan needed __KernelLoadExec split into a
file-reading front end and a shared __KernelLoadExecFromPtr back end
that both it and the new buffer-based path call into - a pure
extract-method refactor of the single most heavily used boot path in the
emulator. Verified no regression: same 11 passed / 9 pre-existing-failed
split on pspautotests/tests/cpu/*, and loader/bss still passes, before
and after this change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSNaZnHCjmryS3ziVN9gZU