Three corrections to framing that made the project read smaller than it is.
- Drop the 'eight titles brought up so far' count from the hero. A number reads
as a ceiling; the point is that bringing up a new game is configuration and
reverse engineering, not new engine work.
- Widescreen: 'six of the seven titles support it' described this ecosystem's
games rather than the capability. The cull-widening, FOV and aspect machinery
is framework tooling any project can enable from a [widescreen] block,
third-party forks included.
- Localization: retitle 'Play a Japanese-only game in English' to 'Swap
language on the fly'. The mechanism has no built-in notion of a default
language -- it is any source to any target, switched live. Describe what is
actually hard about it: substituted text is drawn through the game's own
glyph routine with per-character advances calibrated from real ink width in
VRAM, plus auto-fit condensing, so no engine change or regeneration is needed
to add a language.
Tables are TOML (translations/*.toml, one column per language, hot-reloaded),
verified against text_xlate.cpp and Tsumu's shipped table -- docs/
STRING_TRANSLATION.md still calls them translations.json, which is stale.
Also removes the aging Tomba demo-video embed; the screenshot grid carries the
visual pitch now. Anchors, images and file links all re-verified.
The Tomba demo embed predates most of the ecosystem and sat directly above the
new screenshot grid, which now carries the visual pitch. The write-up link
above it stays as project history.
The grid images are 800px and the widescreen shots 1280px with no width
attribute, so a three-wide table would try to lay out ~2400px and GitHub would
scale the cells unevenly. Pin every screenshot to 100% of its container so the
grid stays square and the hero shots span the content width.
The README opened by explaining what a static recompiler is and how to run the
CLI, and buried the fact that eight titles actually run on it. Restructure so
the catalogue and the shipped features come first, and the build instructions
follow the pitch rather than preceding it.
- Hero: reposition as a general-purpose PS1 static recompiler, plus a 3x2
screenshot grid of the six playable titles.
- Games: add Xenogears (github.com/OpokXeno/xenogears-recomp), marked
community, per issue #83; note the author also contributed widescreen
cull-site work upstream. Per-title notes now name widescreen support.
- New sections: Widescreen (16:9 / 21:9 / adaptive, showing both a 2D sprite
engine and 3D), Mods (versioned .psxmod packages over a verified stock image,
with the launcher panel), on-the-fly localization (the Tsumu translation
layer), and Renderers (OpenGL default, Vulkan experimental, software
reference).
- Promote 'Do I need a PlayStation BIOS?' near the top: bundled MIT OpenBIOS
means most players need no dump, which is the first question a newcomer has.
- Reorder: Games / What It Is / BIOS / features / how-to-build / the rest.
No section was dropped; all 18 are still present.
Screenshots are sized to how they render (800px grid cells, 1280px hero shots)
rather than colour-quantised, so there is no visible loss: 11.6MB of captures
land as 4.6MB of assets. Netplay is deliberately not mentioned.
All image paths, internal anchors and relative file links verified to resolve.
PSXRECOMP_BIOS_STEMS defaults to "OpenBIOS;SCPH1001", so every game link
required a SCPH1001 backend. That backend is compiled from generated C which is
a derivative of a copyrighted Sony BIOS, so it is gitignored and only exists
after a developer regenerates it from their own dump. A checkout that has only
the bundled MIT OpenBIOS -- the documented default, and what a fresh clone gets
-- configured fine and then failed at link with:
undefined reference to `SCPH1001_psx_bios_backend'
Hit while building Tomba 1, Tomba 2 and MMX6 to validate an unrelated branch;
all three needed -DPSXRECOMP_BIOS_STEMS=OpenBIOS as a workaround.
Filter the stem list to those that actually supply a backend, rather than
flipping the default. Flipping it would silently drop SCPH1001 for developers
who HAVE regenerated it; filtering keeps it fully supported when present and
simply absent when not.
Presence of the files is not a sufficient test. A generated/ tree produced
before the backend-descriptor mechanism existed has both <stem>_full.c and
<stem>_dispatch.c yet defines no descriptor -- exactly the local state that
produced the error above. The check therefore probes for the descriptor symbol
itself, in <stem>_dispatch.c (~1MB, where the emitter puts it) rather than the
multi-megabyte <stem>_full.c, so configure stays fast.
Also report what happened instead of leaving it implicit:
- STATUS line naming the linked backends
- STATUS line naming skipped stems with the regen command to fix them
- FATAL_ERROR, with the OpenBIOS regen command, if no backend resolves at all
(previously an empty registry would have surfaced as a confusing link error)
Verified: with default flags and a stale local SCPH1001 tree, Tomba 2 now
reports "BIOS backends skipped ... SCPH1001" / "BIOS backends linked: OpenBIOS",
links, and boots.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Master added runtime/tests/test_fps_telemetry_opt_in.py in bb5fcce without an
add_test() entry. It is a source-only guard over runtime/src/main.cpp and
passes, so it joins the same list as the other 15. 30 tests, still under four
seconds.
This is the pattern the earlier commit was meant to prevent recurring -- worth
noting it recurred within a day, which is the argument for a CI check rather
than a convention.
Comment-only. No compiled code changes -- the sole non-comment diff line moves
a trailing comment off the g_mmx6_hostside declaration into a block above it;
the declaration itself is byte-identical.
A survey of the mmx6-named widescreen symbols found the existing comments
misleading in a way that invites a dangerous cleanup, so the findings are
recorded in the source next to the code they describe.
1. "Compatibility entry points for already-generated MMX6 sources" (gpu.h) was
wrong. These are not MMX6 compat shims and are not safe to delete. They are
the widescreen-2D entry points the recompiler emits for ANY title with a
[widescreen.bg2d] block, and live generated code in several non-MMX6 repos
links them today:
cols/startcol/startx/stream_left/stream_right
-> CrashBash, MegaManX5, Tsumu (+ -release)
undercap
-> Ape, CrashBash, MMX4, MMX5, Tsumu, Vigilante8, THPS2
Each forwards to its psx_ws_bg2d_* twin, which is the real implementation.
Only the name is historical. Renaming is not runtime-only: the names come
from code_generator.cpp, which is in codegen_hash_sources.cmake, so it rolls
the overlay cache tag and forces regen + reshard of every title.
2. Host-side reveal (g_mmx6_hostside) is dead in every shipping build, which
the previous "kept inert pending removal" note understated. Nothing sets it
but the mmx6_reveal debug command, PSX_DEBUG_TOOLS is OFF for Release, and
no generated source anywhere references a host-side symbol. Noted that
removing it is the last step decoupling the generic psx_ws_bg2d_* API from
MMX6 -- mmx6_hostside_active() is currently embedded in the ternaries of
psx_ws_bg2d_cols/startcol/startx -- and listed the symbols that go with it.
3. BG buffer relocation is a parked FEATURE behind a load-bearing SYMBOL, a
distinction the old comment did not draw. With g_mmx6_reloc == 0 both hooks
return their argument unchanged, but MegaManX6Recomp/game.toml names the
bufbase/cap instruction sites, so the emitter writes calls into generated
code and psx_ws_mmx6_bg_bufbase is genuinely called by MMX6's generated C.
The supportable cleanup is to gut the bodies to identity and keep the
symbols, not to delete the functions.
Removal of (2) and (3) is deliberately deferred: it touches the renderer and
wants its own session with MMX6 revalidation.
Verified: gcc -fsyntax-only on gpu.c exits clean (an earlier draft embedded
"*/" inside a block comment and broke the parse -- caught before commit);
ctest 29/29 green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Master's debug_server.c changes brought the registered command count to 293.
gen_tcp_commands.py --check caught the drift on the first run after rebase,
which is the behaviour it exists for.
Correction to an earlier claim of mine: ctest was NOT absent. recompiler/ and
runtime/ already had 21 add_test() calls between them and `ctest` ran 8 tests
green in the recompiler tree. My original survey used a quoted git-grep
pathspec that the shell passed literally, matched nothing, and I read the empty
result as "no test wiring anywhere". The real problem was narrower: partial
registration and zero documentation.
Of 61 first-party test sources (excluding 3 fixtures/harnesses), 37 were
referenced by no CMakeLists at all. Each was run by hand; 21 pass and are now
registered, taking the recompiler tree from 8 to 29 tests -- still under five
seconds, and still requiring no BIOS dump, no disc image, and no generated
code.
The 15 runtime source-invariant guards are registered from
recompiler/CMakeLists.txt rather than runtime/CMakeLists.txt, following the
existing dirty_text_continuation_guards precedent. They only read runtime
source, and the runtime tree cannot configure until a BIOS has been generated,
so registering them there would have made them unreachable from the only tree a
newcomer can build.
Three tests are deliberately NOT registered because they fail today. Wiring a
known-red test would recreate exactly what took CI off pull requests. Detail is
in docs/TESTING.md; briefly:
test_interpreter_perf_guards.py asserts psx_devices_mmio_sync invalidates
the inline cycle limit. It does not. The function delegates either to
psx_devices_service_to_now() (which clears g_psx_cycle_fast_limit,
psx_cycles.c:161) or to psx_devices_recompute_deadline() (:153-157),
and that second branch never clears it. Needs a timing owner: the guard
may have found a real hole, or the invariant may have moved. The guard is
itself partly stale -- it still names s_next_service_cycle, since renamed
to psx_next_service_cycle.
test_runtime_perf_diag_guards.py asserts a substring no longer present in
the runtime source.
test_overlay_pair_dedup_runtime.py needs its companion harness built; not a
source-only guard.
New docs/TESTING.md covers running the suite, running one test, what each group
needs, the known-failing three, and why fixtures must not be registered. It
also records two traps that cost time here: parallel builds can crash cc1plus
on config_loader.cpp under memory pressure (retry with -j 1, it is not a code
error), and the codegen tests need an ABSOLUTE recompiler path -- a relative one
passes their os.path.isfile check and then fails inside subprocess.run on
Windows with WinError 2, which reads as a broken test.
Linked from CONTRIBUTING.md (above the existing manual regression checklist,
noting the suite does not tell you whether a game still runs) and from
docs/BUILDING.md.
Verified: clean configure + build, ctest 29/29 pass in 4.08s.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TCP_COMMANDS.md instructed contributors to "update this file" on every new
handler. It described 47 commands while the two debug servers registered 292
between them (279 native + 61 Beetle, overlapping) -- 15% coverage. A
hand-maintained inventory that size does not stay correct, and a stale
reference is worse than none because people trust it.
New tools/gen_tcp_commands.py parses the s_commands[] (native) and CMDS[]
(Beetle) tables and regenerates a complete index between AUTOGEN markers,
marking which commands the curated prose already covers. The hand-written
"Command inventory" table is untouched -- it carries parameters and context
worth keeping. `--check` exits non-zero when the block drifts, so this can gate
CI; verified it fails on a simulated added handler and passes once regenerated.
Corrections to the curated table, each verified against the code first:
write_ram params are `addr`,`val` -- not `addr`,`hex` -- and it writes
exactly one byte per call (handle_write_ram)
screenshot writes a PNG (psx_screenshot.png), not a 24-bit BMP
(debug_server.c:7666,7856)
wtrace_range ring holds 262144 entries (WRITE_TRACE_CAP, 1<<18), not 1024
-- the documented figure was 256x too small
sljit_async row removed; the command is not registered anywhere (the
sljit tier was removed 2026-07-15)
pause/continue/step/run_to_frame
documented as working. All four are registered but
unconditionally return an error (debug_server.c:7324-7342);
they were deliberately removed in favour of ring-buffer
queries. Marked REMOVED with the replacement each error
message names.
Also corrected the header: it presented DuckStation (port 4371) as one of the
two live servers, though DuckStation was retired as the oracle on 2026-05-05
and has no .gitmodules entry, while the page's own CLI examples already used
Beetle on 4380. The live pair is now Native 4370 / Beetle 4380, with a note
that the inventory's "D" column is historical, and the "add a handler" checklist
now points at beetle_debug_server.c and the generator instead of DuckStation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These are not stale-by-omission; each asserts something the code does not do,
and each was verified against the implementation before editing.
docs/BUILDING.md
- PSX_ENABLE_VULKAN was documented "off by default" in two places. It
defaults ON (runtime/runtime.cmake:778). Corrected both, and noted that
the build option being ON does not make Vulkan the runtime renderer.
- Removed the PSX_BUILD_COSIM row: no option() for it exists anywhere in
the tree. Replaced with PSX_NETPLAY, which is real (runtime.cmake:194).
- Step 2 (regenerate BIOS) was labelled "(Optional)". It is required before
the first runtime build: step 3 configures psx-runtime from
generated/OpenBIOS_full.c and fails at configure time without it
("Cannot find source file" / "No SOURCES given to target"). Regenerating
the *retail* backend remains genuinely optional; that distinction is now
explicit.
- "This repository has no required git submodules" contradicted .gitmodules
(recomp-ui, lib/recomp-net) and README's --recurse-submodules clone.
Replaced with a table of which submodule each build option needs.
docs/ARCHITECTURE.md
- Same Vulkan default correction.
- Removed the claim that BIOS and game configs merge with "game wins on
scalar keys". No such merge exists: load_bios_config is called only from
the recompiler front-ends (main_bios.cpp, and main_psx.cpp solely to build
the BiosAddressModel) and never from runtime/src/main.cpp.
docs/config_schema.md
- Same merge claim, stated in more detail. Replaced with the actual runtime
precedence: env > CLI > settings.toml > game.toml > compiled default.
docs/STRING_TRANSLATION.md
- Header said "SPEC (design only - no implementation yet)" above a shipped
feature: runtime/src/text_xlate.cpp (~970 lines), initialised from
main.cpp, configured via [localization]. Marked IMPLEMENTED and added the
public entry points.
docs/FEATURES.md
- Layer A (dynamic DLL overlay loading) was headed "SHELVED". It is the live
production dispatch path: overlay_loader.c is ~4,400 lines with 15
LoadLibrary sites, last modified 2026-07-26, and COMPILING_OVERLAYS.md
documents shipping the resulting .dll shards in releases. Added a status
banner and kept the original account as history, since the CRC-keying
dead ends it records are still worth reading.
docs/BIOS_SELECTION.md
- Claimed the identity gate checks "size, CRC32, SHA-256". It checks size
and CRC32 only (bios_backend_for_file, main.cpp:1088-1089); image_sha256
is recorded metadata and never enters the accept/reject decision.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
recompiler/CMakeLists.txt defined both `psxrecomp` (the user-facing CLI,
src/main_cli.cpp) and `PSXRecomp` (a legacy duplicate of psxrecomp-game, built
from the identical PSXRECOMP_GAME_SOURCES list). CMake treats those as distinct
targets, but Windows and macOS filesystems are case-insensitive, so both wrote
the same file and the last one linked won.
The CLI lost. After a clean build, psxrecomp.exe was the game recompiler,
answering `--config <game.toml>` instead of the documented
`build --disc ... --bios ... --output ...`. tools/build_cli.py packages that
binary into psxrecomp-cli-windows-x86_64.zip, and README's first instruction to
a new user is to download and run it, so the primary onboarding path handed
people a program matching no documentation. Invisible on Linux, which is why it
survived.
Remove the legacy target (psxrecomp-game already covers those sources) and add a
configure-time guard so it cannot recur: the guard compares OUTPUT_NAME
case-insensitively across a directory's targets and fails with an actionable
message. A rename alone would not have prevented recurrence.
The PSXRecomp.exe named in README's Release Package section is a different
artifact, the BIOS-only runtime named via OUTPUT_NAME in runtime/runtime.cmake,
and is unaffected.
Verified: clean build now yields psxrecomp.exe (791KB) printing the documented
`build --disc` interface; no case-colliding pair remains; guard confirmed to
fire by temporarily reintroducing a collision.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Preserve wider guest staging and composition regions while extending framebuffer draw bounds for native-wide margins. Regression-tested in 16:9 on Tomba and Mega Man X6 with bundled OpenBIOS.
Stretch explicitly configured backdrop X-store sites across the native-wide surface while leaving 4:3, FMV, menu, and unconfigured games unchanged.\n\nValidated by the contributor on Xenogears Battle Mountain and regression-tested on Tomba's Watch Tower flower backdrop with bundled OpenBIOS.
Two runtime-side changes the launcher's SYSTEM row needs.
The row was hidden whenever the linked BIOS was bundled, on the reasoning
that there was nothing to select. With OpenBIOS AND a retail image linked
that strands the player: choosing retail — and clearing back — is exactly
what the row is for. Visibility now asks the registry (is any linked
backend NOT the bundled one?) via psx_bios_has_selectable(), which is
also correct before selection, unlike psx_bios_image.
A BIOS named in settings.toml is likewise now honoured on every build.
It was previously ignored on bundled builds to stop a stale path pointing
at a foreign image, but that guard would now silently discard the only
in-app way to opt into retail. The identity gate is the real protection,
and it explains a mismatch rather than ignoring it. An EMPTY value stays
meaningful in the other direction: that is the launcher's Clear, "go back
to the bundled BIOS", so it is not adopted as a path.
Pairs with recomp-ui feat/bios-row-clear.
Not yet compile-verified against the launcher: building with
PSX_RECOMP_UI=ON on this branch fails in the recomp-ui netplay callback
ABI (main.cpp:4495), which traces to 87491cb and is inherited from this
branch's base predating master's matching integration — the same skew
that stops MMX4/MMX5 building on current master with their pinned
recomp-ui. Needs master merged in, which this branch requires before
merge regardless.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>