The cache was accepted forever once any cmake ran: default min version was
empty, so a Deck (or any host) that cached a pre-sysroot pack (<=1.0.13)
never re-downloaded and kept compiling against host headers — absent on
stock SteamOS — with objects that mis-link after a pack upgrade
(__isoc23_strtoul).
Both resolvers now default the floor to 1.0.14, the first pack with the
Linux build sysroot. This is a framework floor, not a per-title pin:
RETCOMM_TOOLCHAIN_MIN_VERSION still overrides in either direction, and
"0" / "off" / "none" disables it (pre-floor behavior).
- tools/toolchain_pack.py: DEFAULT_MIN_VERSION in default_min_version();
the ensure_toolchain stale-probe passes min_version="0" so its log line
still sees below-floor packs.
- host: toolchain_min_version() mirrors the same default and disable
tokens; below-floor caches now fail host_portable_cmake_ready and the
wizard downloads /releases/latest.
Verified: Python — floor/override/disable semantics, real v1.0.10 pack
rejected and v1.0.14 accepted, an isolated cache holding only v1.0.10 no
longer resolves (visible again with floor disabled), full cache resolves
to v1.0.14. C — same matrix via a unit harness incl. manifest-less dirs;
both platform branches syntax-checked.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vc7zPEnfUFMhMtJzUEruyb
(cherry picked from commit 00f0a9a9bb)
Stock SteamOS (Steam Deck) has no /usr/include and no GL dev symlinks, so
the wizard rebuild died at recomp-ui's find_package(OpenGL REQUIRED) with
"Could NOT find OpenGL (missing: OPENGL_INCLUDE_DIR)" — reported to the
user only as "psxrecomp rebuild failed (exit 1)" (recomp-ui issue #34).
The cmake-clang-v1 >=1.0.14 pack already bundles a jammy sysroot with GL
headers and a legacy libGL.so stub, but only clang.cfg used it; CMake's
find_* still searched the host.
- psxrecomp_cli: when the active pack carries sysroot/usr/include (Linux),
pass -DCMAKE_SYSROOT=<pack>/sysroot -DOpenGL_GL_PREFERENCE=LEGACY to both
the game and the emitters configure. LEGACY is required: the sysroot has
legacy libGL.so + GL/gl.h but no GLVND dev files (glx.h/libOpenGL.so/
libGLX.so), so the GLVND path cannot complete and OpenGL::GL would never
be created. Skipped when the caller already sets CMAKE_SYSROOT.
- codegen host: run_cli_posix/run_cli_win now route the child's stderr
into the progress pipe and keep a last-lines capture (CliTail); on
failure err_msg carries the most recent error-looking line — JSON
progress rows contribute their message, raw rows (compiler output,
Python tracebacks) contribute as-is. Exit-3 disc-verify text unchanged.
Verified against a stock-SteamOS simulation (bwrap: tmpfs over
/usr/include, GL dev symlinks removed) using the shipped v1.0.14 pack:
configure previously failed exactly as on the Deck; with the sysroot args
FindOpenGL resolves from the pack, the build links, and the binary runs.
CliTail paths exercised with real compact-JSON rows and raw stderr.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vc7zPEnfUFMhMtJzUEruyb
(cherry picked from commit 0bb45442f8)
The overlay-region model assumed the boot EXE sits at the bottom of RAM:
[KERNEL_END, text_end) is main-EXE text, [text_end, RAM) is runtime-loaded
overlay code. That holds for a load_address of 0x80010000, where the text
base equals the kernel-window end and the region below the text is empty.
It is false for a boot EXE that loads HIGH and streams its gameplay code
into the RAM beneath itself. Klonoa loads a 46 KB stub at 0x80180000 and
runs its overlays from 0x10000-0x130000, so the floor landed at 0x18B000
and the entire overlay region fell below it:
- dirty_ram_clear_image_baseline() wiped the dirty bits across
[0x10000, 0x18B000) believing it was the clean compiled text image.
- the dirty_ram_dispatch_inner admit heuristic requires phys >= floor,
so a JALR into an overlay page the CD had DMA'd there (never touched
by the CPU write hooks, so never marked dirty) was refused by the
interpreter, fell through to psx_unknown_dispatch and fail-fast
exit(1) — Klonoa, frame 687, target 0x80123D00.
Pin the text BASE (g_text_image_lo) alongside the floor and route the
gates through one predicate: the overlay region is both sides of the text
image, not just above it. Street Fighter Alpha 3 (0x80113B00) and
Bomberman Fantasy Race (0x8003004C) load high too.
No-op for bottom-loading games: g_text_image_lo stays at its 0x10000
default, the below-text clause is empty, and the baseline clear starts at
the same page as before.
Klonoa now runs 6874+ frames at a locked 60 fps with fail-fast on and
zero unknown dispatches; it previously died at frame 687.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit cf742f3190)
(cherry picked from commit 30e8a71f93539c4e10ff6767c44c24137c9dd714)
(cherry picked from commit 94f184f744)
Legend of Mana's land-creation effect rendered as hard-edged coloured
wedges instead of a soft bloom. The cause was in the CD-ROM controller,
not the renderer.
The game streams a file by issuing Setloc(next)+ReadN roughly 10,800
guest cycles after each sector arrives. Every ReadN called
start_read_stream(), which clears the sector buffer -- destroying the
sector that had already been delivered and announced but not yet drained
by the guest. Measured at the palette load:
buffer psx-runtime DuckStation
0x0E1F18 125111 125111
0x0E2718 125113 125112 <- the effect's colour palette
0x0DCF18 125113 125113
Sector 125112 carries the effect's 5-colour palette (0x0888F8 =
(248,136,8), 0xB0F8F8 = (248,248,176), verified against the disc image).
Destroyed before the guest could take it, its buffer received 125113's
raw file bytes instead, and the colour routine faithfully scaled those
into 216 distinct vertex colours -- the wedges. The table now holds 5.
DuckStation only calls BeginReading -- the function that clears the
buffers and re-arms the read-start latency -- when the request actually
moves the drive:
if ((!setloc_pending || setloc_position.ToLBA() == GetNextSectorToBeRead())
&& (drive_state == Reading || ...))
"Ignoring read command with pending/same setloc, already reading"
else
BeginReading();
A Read issued while the drive already streams the sector the pending
Setloc names is the game saying "keep going", not "start over". The
command is still ACKed with the same INT3; only the restart is skipped.
Two things fall out of it. The un-drained sector survives, so the game
receives the consecutive sectors it expects (109,110,111,112,113 rather
than 109,111,111,113,113). And each request stops paying
initial_read_delay_cycles() again: inter-sector gaps drop from ~456,000
cycles to 225,792 -- exactly the 2x cadence -- so this load takes 2 guest
frames instead of 13, matching DuckStation. int1_pended goes 10 -> 0.
Also included, developed alongside and verified in the same runs:
- An 8-slot sector buffer ring, as the hardware has (nocash;
DuckStation NUM_SECTOR_BUFFERS = 8). A delivered sector fills the
NEXT slot and the INT1 that announces it latches the read slot, so a
sector arriving mid-drain no longer clobbers the FIFO. The read
pointer follows the slot each INT1 announces, captured when the INT1
is RAISED: this drive free-runs, so latching "newest" at
presentation time handed the guest a sector seven ahead of the one
it asked for.
- The seamless no-IRQ refill advances the read pointer, because it is
a continuation of the drain already in progress. Without it a
multi-sector DMA starves mid-transfer (measured: 1,128,960 starved
drains).
- A pended data-ready is released on a short schedule rather than
inside the guest's ack store, matching DuckStation's
QueueDeliverAsyncInterrupt.
- Flow control for accelerated (non-1x) reads: never overwrite a
sector the guest has not consumed while running faster than
hardware, capped at the authentic sector period. Inert at authentic
timing.
New cdrom_state counters ring_starved and ring_dropped, plus
cdrom_timing_dump for per-sector deadline/delivery records, are the
regression tripwires: ring_starved and int1_lost must stay 0.
Verified on Legend of Mana (SLUS-01013): the effect renders correctly,
the palette table holds 5 colour words, sectors arrive at the 2x
cadence, and no notification is lost.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>