Review of #561. Guest time now has two entry points (psx_cycles.h): the
CPU charge psx_cpu_charge() (instruction base, load fudge/region/completion,
i-cache refills, BIOS HLE costs), which applies [timing] guest_cycle_scale,
and the unscaled device advance psx_advance_cycles() (DMA, idle skips,
mul/div and GTE deadline waits). The emitters' block-mode charges emit
psx_cpu_charge; overlay DLLs accumulate it raw and publish through the
cpu_charge callback (was advance_cycles, same slot) into the same host
function, so nothing is scaled at the DLL boundary. The batched
psx_cyc_charge path, the dirty-RAM interpreter, psx_cyc_readmem, i-cache
refills and bios_hle.c all charge through it. At scale 1 it is exactly
psx_advance_cycles.
Gate in psxrecomp: [timing] guest_cycle_scale_gate, RAM predicates judged
at every VBlank edge (deterministic, works online, no mod needed), plus
psx_mod_set_guest_cycle_scale_gate() in mod_plugins.h for custom logic or
to tie the scale to a mod (guest_cycle_scale_gated = true; every mod
session reset shuts it). The carried fraction and both gate states are a
boot_state section (BS_SEC_GCS, written only when the scale is not 1), so
savestates and the netplay rollback snapshot re-simulate the same cycles.
psx_guest_cycle_scale_gate_test pins tier identity, the gates and rollback
re-simulation against the real psx_cycles.c.
OpenBIOS regenerated (emitter stamp); retail SCPH1001 is not.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes Ape Escape terrain seams: CPU-built subdivision midpoints and lwl/lwr-copied
grid corners drew native next to precise neighbours. Adds pgxp_tri_ring/pgxp_store_ring.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#479 guards the host_relay.h include, so offline title builds no longer need recomp-net's include dir; the tool had no other managed block.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
main.cpp includes recomp_net/host_relay.h unconditionally, so offline
(PSX_NETPLAY=OFF) game builds fail with 'No such file'. Add a managed
recomp-net-headers block to CMakeLists.txt.in and an idempotent
update_cmake tool (py core + .sh/.ps1 wrappers) to sync existing projects.
Reconciled with #470 (PGXP): gpu.c precise vertices take PGXP only under geometry correction and count it, then native-wide edge recovery; the PGXP triangle stat stays PGXP-only. Stubs and tests from both kept; stamps recomputed.
Closes#474. The win64 binary zip carries no license file; stage the notice beside tcc.exe and in licenses/, and make the zip verifier require both.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A renderer that keeps a screen X as SX << 16 tests the right edge against
a lui-held W << 16 and the left edge with bltz on the shifted value. No
explicit [widescreen.cull] kind fits either: bltz_sites compares the whole
register with -margin, and a lui at a screen_x site was a build error.
screen_x_sites now also takes
lui rt, W -> rt = (W + margin) << 16
bltz x, outside -> taken while SX < -margin (x = SX << 16)
Both are the identity at 4:3. The helpers are defined for native code and
for overlay shards; their math is in ws_cull_edge.h. The two prototypes are
written only for a config that lists screen_x_sites, so the generated code
of every other title is unchanged.
A BIOS module's glue.c includes overlay_dispatch_preamble.c.inc and psx_bios_module_glue.c.inc by name. The tcc path compiles against a BOM-stripped copy of the include directory, and that copy held only *.h, so every tcc module build stopped at "include file 'overlay_dispatch_preamble.c.inc' not found" and the runtime fell back to the bundled BIOS.
Copy .inc files into the tcc include copy as well. Game overlay shards were not affected: compile_overlays inlines the preamble text instead of including it.
An x86_64 macOS runtime under Rosetta compiled every overlay shard for
arm64. The runtime's shell and the bundled Python inherit x86_64, so
compile_overlays.py named the cache macos-x64, but it ran the compiler
without -arch and Xcode's clang is an arm64-only binary that defaults to
arm64. The arm64 shards landed in the macos-x64 cache, the runtime could
not load any of them, and every overlay ran interpreted. The BIOS module
builder had the same compile line.
- overlay_loader.h now defines PSX_OVERLAY_ARCH_ABI (moved from
overlay_loader.c), and autocompile.c exports it to every compile it
spawns, next to PSX_OVERLAY_FLAVOR. It is the running slice of a
universal binary.
- compile_overlays.py adopts it (env wins over the new --arch-abi), names
the cache with it, and passes -arch on macOS. Without a pin it builds
for its own interpreter, still with an explicit -arch.
- Shards are validated by loading them into the Python that built them,
so a Python that cannot run as the runtime's architecture now refuses
up front instead of writing shards nothing loads. The Python's
architecture is its process's: platform.machine() on macOS and Linux,
the interpreter's build (sysconfig.get_platform()) on Windows, where
platform.machine() reports the machine. So an x64 Python emulated on
Windows on ARM still builds the x64 runtime's shards.
- psx_bios_module.c passes --arch-abi to bios_module_build.py, which
applies the same rules.
- Windows and Linux compile lines are unchanged, and so are their cache
directories, except that the x64 build on Windows on ARM now uses the
runtime's win-x64 instead of the machine's win-arm64.
overlay_compile_arch_test (CTest) pins the handoff, the flags, the refusal,
the Windows process architecture and the unchanged Windows/Linux commands;
with a real cc it builds a shard for each slice the Mac can run and loads
it in a process of that architecture.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The v3.0 Japanese (SCPH-5500, 09/09/96) and US (SCPH-5501, 11/18/96)
retail BIOS images join SCPH-1001, SCPH-101 and SCPH-5552, so every v3.0
region (J, A, E) has a profile.
Both images match SCPH-1001 v2.2 byte for byte in the kernel,
ROM [0x80, 0x18000). SCPH-5501's reset vector is identical too; SCPH-5500's
differs in 24 bytes. The shell differs in both. So each profile is
SCPH1001.toml with its own identity (name, id, rom, sha256 pin, stem): the
copy windows and install slots are kernel properties and carry over
unchanged. The shell runs through the dirty-RAM interpreter, as for
SCPH5552.
Each image gets its own seed list, derived by the same rule as
phase2_ghidra_seeds_SCPH5552.json: the SCPH-1001 corpus filtered to seeds
whose 64-byte ROM window is identical in both images (534 kept, 569
dropped), plus the two architectural vectors and 6 A0/B0/C0 kernel-table
targets. The files carry only addresses, labels and rationales, plus the
SHA-256 of both ROMs.
- psx_bios_known_images.h: CRC-32 FF3EEB8C (SCPH-5500) and 8D8CB7E4
(SCPH-5501), both 512 KiB. A build that does not link these backends
builds one from the player's own dump as a loadable module, like
SCPH-5552, and the netplay lobby can name the image.
- main_cli.cpp: map SCPH5500 / SCPH5501 dumps to their profiles.
- build_cli.py: package both profiles with the CLI framework.
- docs/BIOS_SELECTION.md: list them among the module-built images.
A bundled release links recomp-ui (MIT) into the executable and copies its
fonts and images into assets/ (OpenMoji CC BY-SA 4.0; Noto Sans Symbols 2,
Lato and the Noto Color Emoji flag sheet, OFL-1.1), but
package_game_release.sh staged only psxrecomp's own notices. The setup-host
zips carried the whole recomp-ui tree, so the move to bundled releases
dropped recomp-ui/LICENSE and assets/common/{fonts,img}/NOTICE.md from every
title's release.
The packager now stages recomp-ui's LICENSE as licenses/recomp-ui-LICENSE
and its fonts/img NOTICE.md beside the files they cover, from the tree the
build linked (RECOMP_UI_ROOT in the build's CMakeCache, else
<root>/recomp-ui), unless the build turned PSX_RECOMP_UI off. A title
cannot do this itself with --runtime-file (the path would keep recomp-ui/,
which the kit gate rejects) or --doc (staged by basename, so LICENSE would
overwrite the title's own).
Not covered: the TinyCC LGPL-2.1 notice for the Windows overlay_toolchain/tcc
(THIRD_PARTY_ATTRIBUTION.md says it must ship with it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Preserve current segmented dispatch, active-plan mod hooks and software scale limits. Extend function filters through the active plan, retain the last-store pointer with overlay ABI 25, and keep default config fingerprints stable. Refresh BIOS emitter stamps after byte-identical regeneration and reconcile regression fixtures.
Retro Studio's bulk tab bumps the title's psxrecomp pin first so the script
it runs is the one the title will pin; the clean-tree gate must not refuse
that. Submodule-internal changes are still refused at bump time.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One pass per title: bump psxrecomp/recomp-ui to a ref that carries
docs/ci/templates/game-release.yml and the committed BIOS backends (refused
otherwise), un-ignore generated/, ensure [runtime] overlay_cache = true, emit
scripts/package_release.sh (retiring package_setup_release.sh) and
release.yml from the pinned template, rebuild emitters + regenerate the game
C from the title's disc, refresh framework_pins.txt, run check_boot_exe and
check_generated, commit; --push optional, --dry-run writes nothing.
The operation lives in project_studio/migrate_bundled.py; the tools/ script,
`git migrate-bundled` and `git bulk-migrate-bundled` (map_repos, per-repo
streaming) are its faces. Exercised end to end on a scratch copy of
Bomberman Party Edition (48 generated files committed, zip prefix kept,
generate_ci --check current).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The windows-2022 release job accepted the committed backends in the step
shell (Git Bash) and refused them at configure: runtime.cmake ran the bash
fingerprint through whichever bash find_program() located, and that
environment hashed a byte-identical tree differently. The fingerprint is now
tools/bios_emitter_fingerprint.cmake, run with the configuring cmake itself
on every platform; the shell script is a thin wrapper around it. Same digest
shape as before, so existing stamps stay valid (verified on an LF tree and
an autocrlf clone).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Windows release job reports the committed backends as stale while
Linux and macOS accept the same tree; the configure error names no input.
check_bios_stamps.sh compares each stem's stamp with the local fingerprint
and, on mismatch, prints every hashed input's digest and the tool versions,
so the next run attributes the difference to a file.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Git for Windows checks text files out with CRLF, so the windows-2022 release
job hashed a byte-identical tree to a different value and BIOS_STALE_FATAL
refused the committed backends there while Linux and macOS passed. Stripping
CR before hashing makes the fingerprint line-ending independent (verified:
an LF tree and a CRLF copy of it now agree). Stamps rewritten to the new
canonical value; the generated C is unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
--check only looked for template steps the installed file lacked, so a copy
that still ran the removed OpenBIOS emission (or the setup-host wipe) read as
current and failed on the runner.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Decision (Alex, 2026-09-30): the recompiled BIOS carries the same risk as
the recompiled game, which titles already commit, so the retail backend is
compiled in rather than built on the player's machine. The retail image is
still never shipped: the runtime needs its data at run time and accepts only
the exact image the backend was built from, so the player supplies their
own dump as before.
- generated/{OpenBIOS,SCPH1001}_{full,dispatch}.c and their .emitter.sha
stamps are tracked (.gitignore un-ignores exactly those); regenerated with
the current emitter.
- bios_emitter_fingerprint.sh no longer hashes the ROM image: the profile's
SHA-256 pin stands for it and the emitter refuses any other image, and CI
has no dump, so hashing it made every CI configure read "stale".
- runtime.cmake: the staleness check covers every LINKED stem and
-DPSXRECOMP_BIOS_STALE_FATAL=ON makes a stale stamp fail configure.
- game-release.yml: no CI-side OpenBIOS emission (generate_openbios.sh is
gone); configure with BIOS_STALE_FATAL and assert both backends linked.
check_generated.sh asserts the submodule carries both backends tracked
and no dump is tracked anywhere.
- The player-side module build stays for a known image a build does not
link (SCPH-5552).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>