beetle_main.cpp has defaulted to port 4382 since 4380 began colliding
with other oracles, but its header and the psx-beetle block in
runtime/CMakeLists.txt still said 4380. beetle_debug_server.c now says
4380 is only its fallback for a port <= 0. Comments only.
A correction to 9ef516a7 ("psx-beetle: build on macOS and send large
replies whole"): its message says none of its changes affect MinGW or
Linux. The send_raw change does. On EWOULDBLOCK every platform now waits
for the socket (up to 15 s) instead of dropping the client mid-reply.
That only turns a truncated reply into a complete one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Boot anchors (reset, the kernel copy, the first A0/B0/C0 calls, the shell)
pass within the first frames, before a TCP cyc_watch arm can land, so a
native-vs-Beetle boot comparison could not be run from the tree.
psx-runtime (debug_server.c) and psx-beetle (beetle_main.cpp) now read
PSX_CYC_WATCH ("<pc>" or "<pc>-<end>" for region mode) and PSX_CYC_WATCH_N
(default 16) at start-up and arm the watch before the first guest
instruction. The native arm shares the TCP handler's code path
(cyc_watch_arm); psx-beetle calls beetle_cyc_watch_arm. With neither
variable set nothing changes. tools/cycle_compare.py --no-arm then reads
both rings.
PSX_BEETLE_UNPACED=1 skips psx-beetle's 59.94 Hz wall-clock pacing for
headless oracle runs. Guest timing is unaffected.
docs/beetle-macos.md step 7 documents the variables and the anchor-key
difference for BIOS copy windows declared dispatch_key = "rom": the shell
is keyed natively at its ROM address (OpenBIOS 0xBFC0A500, SCPH-1001
0xBFC18000) while Beetle sees 0x80030000. Checked on macOS: the documented
example records the OpenBIOS shell entry at 5,015,670 in Beetle, the value
of the #429 land gate, and the native watch arms from boot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
beetle_cyc_watch_* were link stubs that always answered hits=0: the
Beetle-side sampler lived in a local beetle-psx tree that never reached
docs/. tools/cycle_compare.py and tools/cycle_testrom/measure.py (ruler
#2) therefore had no oracle to compare against.
Implement the comparator here, driven by the new per-instruction PC hook
in docs/beetle_guest_cycles_hook.patch. Semantics match the native
cyc_watch in debug_server.c: an entry is taken before the anchor
instruction is fetched and carries the absolute guest cycle count of
everything before it; with an end anchor each entry is one A->B pass;
n is clamped to the native ring size (1024). The hook is installed only
while a watch is armed, so an idle oracle pays one untaken branch per
instruction.
This makes psx-beetle require the patched Beetle tree; the stubs hid
that, but the other oracle hooks (rtrace/irq/guest cycles/CDC peeks)
already needed hand-landed Beetle changes. exc_ring stays a stub: no
tool reads it and its hook was never published.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three portability fixes for the psx-beetle oracle target, none of which
changes behaviour on MinGW or Linux:
- beetle_cdc_decode_volume redeclared PSX_CDC at block scope inside an
extern "C" function. GCC accepts that; clang gives the declaration C
linkage and rejects it against psx.h's C++ declaration. psx.h already
declares PSX_CDC, so drop the redeclaration.
- The debug server's client socket is non-blocking, and send_raw dropped
the connection on the first EWOULDBLOCK. macOS starts a TCP send buffer
at 128 KB, so a large reply (read_ram of the BIOS ROM or all of RAM)
reached the client as a truncated JSON line. Wait for the socket to
drain (select, 15 s budget, as debug_server.c's send_all_blocking does)
and only drop the client on a real error.
- .gitignore had "beetle-psx/", which matches a directory but not the
symlink docs recommend when the Beetle checkout lives outside the tree.
"beetle-psx" matches both.
Also note the non-Windows make platforms in the target's comment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The plain present path copied the VRAM image into the swapchain with
vkCmdBlitImage, alpha included. VRAM alpha is the PSX mask/STP bit, 0 on
most pixels, and AMD's compositor honours it even with an OPAQUE
compositeAlpha: parts of the window went see-through (8x8 black/white
blocks of whatever sat behind it). Reported in #411.
vk_renderer_present_vram/_wide now draw the displayed rect with a
fullscreen triangle whose fragment shader writes alpha = 1 (new
present.vert/.frag). The pass is self-contained and created lazily; if
any of it fails to build, presents fall back to the old blit. Its render
pass ends in TRANSFER_DST_OPTIMAL so finish_present (OSD + present
barrier) is unchanged. Wide surfaces gain SAMPLED usage for it.
Checked on an RX 7900 XT: intro/logo screens render correctly, no new
validation-layer messages.
Most of this was written by Claude Opus 5.5; I reviewed and tested it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Winsock sockets are inheritable by default. Any child process the runtime
spawns (e.g. the overlay autocompile's `cmd.exe /C python compile_overlays.py`)
inherits the debug server's listening socket; if the game exits while the
child is still running, the child keeps 127.0.0.1:<debug_port> bound and the
next launch cannot bind it (clients get connection refused / talk to a dead
listener).
Observed: after closing the game mid-autocompile, `netstat -ano` showed :4370
LISTENING under the exited game's PID, held by the surviving python child;
killing the compile freed it. Fix: SetHandleInformation(HANDLE_FLAG_INHERIT, 0)
right after socket(). No behavior change on non-Windows.
This change was made with AI assistance (Claude Opus 5.5, Anthropic) and
reviewed/tested by the submitter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found with the Khronos validation layer on AMD RX 7900 XT (Windows, mingw),
where D24S8 is unavailable and choose_ds_format() picks the combined
D32_SFLOAT_S8_UINT:
- VUID-VkImageMemoryBarrier-image-03320: every view/barrier/clear on the
canonical and wide depth-stencil images named only the STENCIL aspect.
Without separateDepthStencilLayouts, a combined depth/stencil format must
be transitioned with both aspects. Added DS_ASPECT and used it for the
image views, barriers and vkCmdClearDepthStencilImage ranges.
(vkCmdClearAttachments keeps the stencil-only mask - that is legal.)
- VUID-vkCmdClearDepthStencilImage-pRanges-02659: the canonical DS image is
cleared with vkCmdClearDepthStencilImage but lacked
VK_IMAGE_USAGE_TRANSFER_DST_BIT (the wide DS images already had it).
- VUID-vkCmdCopyImageToBuffer-dstBuffer-00191: staging buffers are used for
readbacks (vkCmdCopyImageToBuffer) but were created with TRANSFER_SRC only.
Validation messages over the same 20 s boot (SmackDown 2, Vulkan):
53 -> 3 (the remaining VUID-vkQueueSubmit-pSignalSemaphores-00067 swapchain
semaphore reuse is untouched). The fix also cleared the downstream
VkImageMemoryBarrier-oldLayout-01213 and vkCmdDraw-None-09600 reports.
This change was made with AI assistance (Claude Opus 5.5, Anthropic) and
reviewed/tested by the submitter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adapt the 8 MB RAM work from the rbengine branch into a master-targeted change. The builtin package is disabled by default, activation requests expanded RAM before memory init, and runtime/state/digest/dispatch paths respect the live RAM map.
The setup host located itself with realpath("/proc/self/exe"), which only
exists on Linux. On macOS it failed, so a host launched from Finder (cwd =
$HOME) could not find its project tree: Generate & rebuild never appeared
and forward-to-built-game never fired. Take the path from dyld's
_NSGetExecutablePath there (as recomp-ui's launcher_model.c already does);
Linux and Windows are unchanged.
The netplay rollback "binary path" diagnostic had the same /proc read and
logged "(unknown)" on macOS; it uses the same source now.
No emulation or runtime behaviour changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
recomp-net now carries the peer-facing rollback modules that used to live in
retcomm-rbengine (RetroPortingToolKit/recomp-net#14). Switch the MotK shims and
psx_netplay_sched.c from rbe_sched_* / rbe_ih_* / rbe_hc_* /
rbe_rb_peer_post_tip_ok to their rnet_* names and recomp_net/ headers, and bump
lib/recomp-net to c2338c6.
The netplay_* / np_sched_* surface the rest of the runtime calls is unchanged.
The snap ring and rbe_mono_ms still come from retcomm-rbengine, which stays at
a7b9850 for now; the three moved-module unit tests link recomp_net directly
instead of reaching it through rbengine. RBE_RB_* env knobs keep their names.
Checked with PSX_NETPLAY=ON: psx-runtime links and the netplay tests pass
against both the current rbengine and the split one (rbengine
refactor/launcher-core), and the binary's 33 scheduler / history /
hash-confirm functions now resolve to rnet_* where they were rbe_*.
lobby_chat_ring_test does not compile on master either (chat_push arity) and
is untouched here.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Default = largest multiple of 240 lines within 2/3 of the usable height;
an explicit window_width is still honoured.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Gaps the dump does not store were dropped, so every later track (and GetTD)
sat 150 sectors early; games that seek via GetTD landed in the previous track.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The reinsert hotkey needs a focused window, so no test could exercise
the lid. This runs the same tray cycle on the same image from the
debug server.
It was added to isolate a wedge seen after a savestate save, and it
established that the wedge predates the disc-change work: a save
followed by this command alone reproduces it, with no disc change.
The index derivation is guarded by !disc_override_path, so an explicit
--disc left selected_disc_index at its default of 1. Every savestate
then carried the _disc1 token whatever was mounted.
Measured on a two-disc set: booting disc 2 with --disc wrote
state_801255AC_disc1_slot08.pst. With this change the same run writes
state_801255AC_disc2_slot08.pst.
This is independent of the disc-change feature. It is in this branch
because the roster registration already computes the mounted index.
main() sets savestate_set_disc_scope() once, from the boot disc. A
mid-session disc change left it behind, so a state saved after a swap
was filed under the disc the game booted from. That is the exact
mix-up the scoping exists to prevent, and nothing in the slot list
would have shown it, because every disc of a set shares one entry_pc.
cdrom_disc_select() now updates the scope, rather than the one caller
doing it, so any future caller stays correct.
Two smaller fixes in the same function. A failed iso_open() left
s_disc_selected naming a disc that is no longer mounted; it now
reports none. A roster path longer than the buffer was silently
truncated and would open the wrong file or nothing; it is now dropped.
The runtime mounts one disc image at startup. No code path changes the
mounted image after that. Some multi-disc games prompt for a disc
change during play and then poll the drive. That poll never succeeds.
The CD-reinsert path reopens the same image path, so it cannot mount
the other disc.
cdrom_disc_select() closes the current image and opens another roster
entry, inside the existing lid window. cdrom_lid_media_ready() returns
0 for the whole window, so the guest cannot read the image until the
window ends. The existing timed close then ends the window.
cdrom_init() is not called. It also resets warm-route, trace and timing
state, which would discard a running session. cdrom_disc_select()
resets the sector buffers, the seek position, and the warm-route
arming. Registered warm routes are configuration and are not reset.
begin_tray_open_cycle() now holds the tray-open code, moved out of
debug_force_cd_reinsert(). Both paths call it.
Add a disc_select debug command. It returns the roster and mounts entry
n when n is given. It also returns the sector count and track count of
the mounted image, which confirms that the reader opened another image.
Tested on Ace Combat 3: Electrosphere (SLPS-02020 / SLPS-02021). Boot
disc 2 with no disc-2 save. The game prompts for disc 1. At that prompt
the CD read count is static at 2955 and frames run at 60.5 Hz, so the
drive is idle. disc_select n=1 mounts disc 1. The game resumes and
plays its first cutscene. Reads restart within two seconds and pass
19971. The read addresses change to disc-1 values. The frame rate does
not drop.
Co-Authored-By: Claude <noreply@anthropic.com>