Commit Graph

2225 Commits

Author SHA1 Message Date
Matthew Stanley 6534b3ddad Merge latest master for segment design; preserve landed IsC and segment notes 2026-09-30 15:02:17 -07:00
Matt Stanley fcc685ce1d Merge pull request #435 from RetroPortingToolKit/fix/isc-icache-invalidate
Apply cache-isolated (IsC) stores to the caches, as Beetle does
2026-09-30 15:00:19 -07:00
Matt Stanley befdfea2db Merge pull request #442 from RetroPortingToolKit/fix/windows-framework-validation-20260930
fix: unblock Windows framework regression builds and tests
2026-09-30 14:58:38 -07:00
Alex Vanderveen 9a5c6e0eab docs: BIOS selection in bundled builds, and the planned player-side backend build
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 17:55:18 -04:00
Alex Vanderveen 0b6baa49c4 launcher: never offer "Generate & rebuild" for a BIOS in a bundled build
A shipped bundled build links OpenBIOS only and carries no CLI, sources
or toolchain, yet the codegen host forced the BIOS row on and a retail
pick answered with needs_regen ("Generate & rebuild to switch"), a step
that cannot run there.

- The BIOS row is shown only when a choice can mean something: a retail
  backend is linked, nothing is linked yet (setup host), or the codegen
  host actually wired Generate (prepare_with_progress). main.cpp decides
  after host_apply instead of the host forcing has_bios.
- ae_bios_verify short-circuits a bundled-only build with a plain
  "runs its bundled OpenBIOS only" message before the identity checks,
  whose "this build expects SCPH-1001" wording names the pinned stem,
  not a linked backend, and sets needs_regen only when Generate exists.
- test_bios_selection_guards pins both.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 17:54:53 -04:00
Matthew Stanley 243cc855ba tests: use native Git Bash for bundled release fixtures on Windows 2026-09-30 14:37:57 -07:00
Matthew Stanley 962d949f1e Merge remote-tracking branch 'origin/master' into fix/framework-tests-followup-20260930
# Conflicts:
#	runtime/tests/test_packagers_never_format_cache_tag.py
2026-09-30 14:27:29 -07:00
Alex Vanderveen 74325e7446 package_game_release: ship the whole staged assets/ tree; allow BIOS profile TOMLs
The icon copy iterated a literal path nullglob does not cover and died on
the first real title (runtime.cmake stages the icon at assets/psxrecomp.png,
not beside the exe). And the dump gate rejected overlay_toolchain/bios/
SCPH1001.toml, a profile the toolchain needs; only image files are
forbidden.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 17:23:57 -04:00
Alex Vanderveen 61aa87f04c cli: prefer the project's build-recompiler emitters over the framework's build dir
A stale psxrecomp/recompiler/build/psxrecomp-game from August shadowed the
build-recompiler tree built from the pinned submodule, so generate emitted
a dispatch calling psx_ram_canon_code_addr, which the pinned runtime had
removed; the bundled release failed at link on every platform. The
project's emitter tree is what CI and the scaffold build, so it wins, and
the resolved paths are logged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 17:21:25 -04:00
Alex Vanderveen a7c0db2c63 tests: register test_bundled_release_layout with CTest
check_test_registration.cmake fails configure for an unregistered test
file, which broke every title's emitter build at the new pin.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 17:15:01 -04:00
Alex Vanderveen 874df9b5bb release: ship bundled, compiled games from committed generated/ C
Retire the setup-host release model (CI wiped generated/ and shipped
emitters + sources + a wizard so every player regenerated and rebuilt the
title). Titles now commit generated/ and CI ships the compiled game.

- docs/ci/templates/game-release.yml replaces setup-release.yml: gate on
  committed game C (tools/ci/check_generated.sh), emit the OpenBIOS backend
  in CI (tools/ci/generate_openbios.sh), configure with
  PSXRECOMP_REQUIRE_GAME_C=ON and assert the full path was taken, package
  with tools/package_game_release.sh, verify the zip is a game not a kit.
- tools/package_game_release.sh: exe, assets, bundled OpenBIOS, configs, mod
  catalog + overlay toolchain via release_stage.py, notices; refuses sources,
  emitters, generated C, BIOS dumps. clear_generated.sh removed;
  package_setup_host.sh marked retired.
- Scaffold: gitignore templates track generated/, package_release.sh.in
  wrapper, setup_project.{sh,ps1} commit generated/ after Generate.
- Project Studio: audit flags ignored/untracked generated/, retail BIOS C
  and setup-host workflows; merge_gitignore un-ignores generated/; ops emit
  the bundled packager + workflow; generate_ci uses the new template.
- Docs: docs/ci/BUNDLED_RELEASES.md replaces HOST_ONLY_RELEASES.md.
- Tests: tools/tests/test_bundled_release_layout.py; fix pre-existing
  false positives in the packager-surface and overlay-static tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 17:11:14 -04:00
Matthew Stanley 50e292748b tests: keep synthetic DLL loader failures noninteractive on Windows 2026-09-30 14:07:37 -07:00
Alex Vanderveen 2b3f8c66c5 Merge pull request #445 from Alexbeav/up/netplay-exit-reason
netplay: say why a match ended, and end a boot mismatch in 3 s
2026-09-30 16:58:02 -04:00
Alex Vanderveen d8b644bf6d Merge pull request #444 from Alexbeav/up/netplay-bios-identity
netplay: settle the lobby BIOS by image, not by "any retail"
2026-09-30 16:52:27 -04:00
Alex Vanderveen 4ea620deb0 Merge pull request #243 from Alexbeav/pr/scph5552-profile
bios: replace the stale SCPH5552 profile
2026-09-30 16:05:24 -04:00
Matthew Stanley 7450dee7dd fix: unblock Windows runtime builds and current framework tests 2026-09-30 12:55:56 -07:00
Matt Stanley c604cea4d5 Merge pull request #432 from Cyanoxide/feat/configurable-depth24-trailing-margin
Make FMV depth24 trailing margin configurable per title
2026-09-30 11:09:04 -07:00
Alex Vanderveen f8b4773cd4 Merge pull request #440 from Alexbeav/up/netplay-libjuice-offline
build: vendor libjuice so netplay builds configure offline
2026-09-30 09:59:16 -04:00
Alex Vanderveen 451e3d1828 Merge pull request #441 from Alexbeav/up/lobby-chat-ring-test
tests: pass mid and account to chat_push in the lobby chat fixture
2026-09-30 09:58:58 -04:00
Alexandros Mandravillis c2d96c9903 tests: pass mid and account to chat_push in the lobby chat fixture
0f1fd021a gave chat_push message-id and account parameters. The fixture
still called the four-argument form, so lobby_chat_ring_test stopped
compiling in any tree with the netplay submodules checked out. Pass NULL
for both; the ring behavior under test does not depend on them.
2026-09-30 16:10:59 +03:00
Alexandros Mandravillis 5c7a37146c netplay: say why a match ended, and end a boot mismatch in 3 s
A netplay match that ends early returns both players to the lobby. The
runtime knew why (netplay_soft_exit's origin) but only printed it to
stdout, so players saw the lobby again with no reason. A boot-digest
mismatch (peers booted different BIOS images or boot settings) first
held a black window for the full 20 s admit-stall watchdog.

netplay_soft_exit now records a sentence for its origin, and the soft
return to the lobby puts it on the launcher's status line through
last_error (the room and browser views already show it). The dig0 gate
timestamps a mismatch; once the same pair of digests has held for 3 s,
the watchdog ends the match with its own reason. A peer that gets the
other side's BYE while it sees the same mismatch reports the mismatch,
not "the other player left". A CLI match prints the sentence to stderr.

The origin-to-text map and the grace decision live in
netplay_exit_reason.c, covered by netplay_exit_reason_test. Builds on
the previous commit's psx_lobby_set_last_error.
2026-09-30 15:59:58 +03:00
Alexandros Mandravillis 5fbd11921a netplay: settle the lobby BIOS by image, not by "any retail"
The lobby's BIOS identity was a two-value token, openbios or scph1001,
and scph1001 meant "some retail dump". An SCPH-5552 peer and an
SCPH-1001 peer both offered it, the host settled it, each peer booted
its own image, and the rollback boot digest never matched: about 20 s
of black window, then both players back in the lobby with no reason
(reported in #243).

Each offer now carries the CRC-32 of the retail image that peer would
boot (online bios_offer.retail_crc; LAN JOIN line 6). The host settles
retail only when every seat offers the same CRC, else OpenBIOS when
every seat links it, else refuses to start and puts the reason on the
launcher's status line ("Players use different BIOS images (SCPH-5552
and SCPH-1001) ..."). The settled CRC ships in match_caps
(session_bios_crc) and on MOTK1 START line 7, and each peer boots only a
dump with that CRC. Rooms where every peer has the same image keep
working, including SCPH-5552-only builds with no OpenBIOS. Old peers
send no CRC, so a mixed old/new room falls back to OpenBIOS or refuses;
a session from an old host still accepts any retail dump. The lobby
server relays bios_offer and match_caps as opaque objects, so it needs
no change.

The rule lives in netplay_bios_settle.c, shared by the online and LAN
paths and covered by netplay_bios_settle_test. last_error grows to fit
one sentence, with psx_lobby_set_last_error to set it.
2026-09-30 15:58:52 +03:00
Alexandros Mandravillis 328d879512 build: vendor libjuice so netplay builds configure offline
With PSX_NETPLAY=ON, recomp-net FetchContents libjuice v1.7.2 from GitHub
at configure time. That download is outside third_party/deps.manifest, so
PSX_DEPS_OFFLINE does not cover it, and a build with no network (a sealed
CI job, a player behind a firewall) fails inside the FetchContent subbuild
with a curl error.

Commit the same archive recomp-net pins (MPL-2.0, 108 KB) and declare it
before add_subdirectory(recomp-net). FetchContent keeps the first
declaration of a name, so recomp-net's own FetchContent_MakeAvailable
builds the vendored copy with its own options. The configure stops if
recomp-net ever pins a different libjuice than the manifest.

Ship the MPL-2.0 notice and source location in runtime/licenses/ and
THIRD_PARTY_ATTRIBUTION.md. Two ctest cases configure a stand-in
recomp-net with the network sealed: the matching pin must configure,
a different pin must stop with the mismatch message.
2026-09-30 15:57:32 +03:00
Shokunin 4630a0d9f7 docs: record PR A (#429) as landed in the segment-aware design
#429 charges psx_icache_fetch before every instruction whose runtime PC is
uncached, in both emitters, and makes the BIOS emitter's cache-line start
test use the runtime PC. The OpenBIOS check finds no uncharged KSEG1 site,
so segment_aware_codegen passes with 10 known gaps; bios-kseg1-fetch-charge
has not been in KNOWN_GAPS since the ledger's first commit in this series.
The check stays as a regression guard: an uncharged site reports the id as
a new gap.

docs/SEGMENT_AWARE_CODE.md:
- §5.6 describes the charge as landed, including the runtime-PC line-start
  fix. The game emitter already has the predicate, but emit_pre_icache
  tests the compile address: PR B must route both its psx_fetch_uncached()
  argument and the fetch tag through runtime_pc(), and D adds the KSEG1
  variants they reach.
- §7.1 lists ten open ids and records how A's id left the ledger.
- §8 marks A merged, with its Beetle parity (shell entry -126 on OpenBIOS,
  -454 on SCPH-1001; the residuals are the IsC gap and SCPH-1001's KSEG1
  kernel entry). D now only needs the KSEG1 variants; kseg1-fetch-charge
  stays open until D. The macOS oracle recipe is docs/beetle-macos.md (#431).
- §1, §3.5 and the header say the BIOS undercharge is fixed. §10 is
  unchanged.
- §3.1, §3.5 and §5.2 cite the emitter lines as they are after #429
  (code_generator.cpp +6, full_function_emitter.cpp +10).

FAITHFUL_TIMING_PLAN §5 gets a log entry for the rebase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:20:52 -07:00
Shokunin 591cf8ff73 docs: segment-aware code after the #417/#418/#420 merges and the first Beetle runs
- Rebased onto master: #417 (the old base), #418 and #420 merged on
  2026-09-29. The status line says so, and every cited source line is
  refreshed for current master.
- PR A is #429. §7.2 records its passed LLE land gate against live Beetle
  (shell entry -126 OpenBIOS, -454 SCPH-1001; ruler #1 64/64, ruler #2
  14/14). The residue is the IsC gap (§9, now with per-flush numbers and the
  prototype result) and SCPH-1001's KSEG1 kernel entry 0xA0000500 (§3.3, -9),
  which PR D closes (§8).
- §7.2 records the synthetic EXE's first Beetle runs (macOS oracle recipe:
  #431). Disc boot works on both BIOSes; sideloading does not, because
  Beetle's loader forces a KSEG0 start. With the probe fixes, Beetle reads
  56/56/82 cycles for KUSEG/KSEG0/KSEG1, a 26-cycle surcharge equal to the
  fetch model's.
- Ledger values follow the new probe layout (probe_run 0x000100F0; 30 fetch
  tags; leaf 22 vs Beetle 11). Still 10 ids.
- ACCURACY_BURNDOWN points the KSEG1 fetch item at #429; FAITHFUL_TIMING_PLAN
  gets a dated log entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:20:52 -07:00
Shokunin 1c56a8b78c segment_testrom: line-align the probe call sites so the T2 deltas compare
With the delay-slot fix the Beetle deltas were sane but not comparable: the
three unrolled call sequences sat at different I-cache line offsets, and
probe_run shared a line with seeded, so the KUSEG run found its first line
already filled. Beetle read 59/59/89 (KUSEG/KSEG0/KSEG1), where the fetch
model predicts a 23-cycle KSEG1 surcharge.

Pad each call sequence so its first T2 read starts a line, and start
probe_run on its own line. Beetle (disc boot, OpenBIOS and SCPH-1001 alike)
now reads 56/56/82: KUSEG equals KSEG0, and KSEG1 exceeds them by 26 cycles,
exactly the Beetle fetch-model difference for the 15 KSEG1 fetches (60 vs a
cold cached 34).

probe_run moves to 0x000100F0. Ledger ids are unchanged; fetch-tag-segment
counts 30 tags and alias-fetch-coherence reads 22 vs Beetle 11, because leaf
now straddles two lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:20:52 -07:00
Shokunin ba18f992a5 segment_testrom: read T2 after the load delay slot and mask the delta to 16 bits
The Timer 2 subtraction sat in the second lw's load delay slot, so it read
the stale register, and the difference was not masked to T2's 16 bits. The
Beetle disc-boot run of the probe returned 0xFFFFFFF3-style values for
every delta. Add a nop after the load and an andi 0xFFFF.

The layout shifts by six words: probe_run moves from 0x000100C0 to
0x000100D8. The ledger ids are unchanged; fetch-tag-segment now counts 27
tags and alias-fetch-coherence reads 10 vs Beetle 5 for the moved leaf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:20:52 -07:00
Shokunin 3afa4a0b41 docs: add the GP0 source key to the store-PC re-key list; PR C closes eight ids
memory.c:1292 also reads g_debug_last_store_pc in every build: a GP0 write
from 0xBFC38B1C gets a RAM source key for the widescreen prim matching. That
key lies in SCPH1001's Shell window (runtime 0x80050B1C), so it has to be
re-keyed with the other six when the BIOS stamp moves to runtime_pc().
List it in §3.1, §5.2, §9 and the FAITHFUL_TIMING_PLAN entry, and give each
relocated key its runtime PC.

§8: PR C closes eight ledger ids (§5.3's seven plus segment-miss), so
C and D close all ten; A (#429) closed the eleventh gap,
bios-kseg1-fetch-charge, on master. B's store-PC hole ships with the re-key of
all seven keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:20:52 -07:00
Shokunin d51a34a766 docs: record the segment-aware decisions and the store-PC correction
The owner settled §10's four questions on 2026-09-29, all as recommended:
- I-cache tags follow Beetle; the hardware bit-31 difference is recorded
  in ACCURACY_BURNDOWN axis 4.
- Segment misses in static game code interpret loudly until the title is
  regenerated.
- Extra segments are requested through segment-qualified seeds.
- The overlay cache uses per-segment subdirectories.

The doc called g_debug_last_store_pc a debug/identity hook. It is not:
memory.c's RAM 0x0-0xF store filters compare it with exact PCs in every
build, the interpreter stamps the full PC, and #420 (ABI v24) forwards
overlay stamps to the host. §3.1 now lists it as a baked PC, and §5.2
routes it through runtime_pc(). Consequences:
- Option A, re-measured with the 1,261 store-PC stamps in the three R4
  shards: +3.8 % / +27.6 % __TEXT (was +3.2 % / +27.2 %).
- New ledger id store-pc-segment (10 KSEG0 stamps in the synthetic EXE's
  home bodies), closed by §5.3. The ledger now has 10 known gaps.
- §9: several filter keys are ROM addresses of BIOS code that runs
  relocated in SCPH1001. The BIOS emitter stamps ROM addresses and the
  interpreter stamps runtime PCs, so the keys must be re-keyed when the
  BIOS stamp moves to runtime_pc().

§8 notes that rollout PR A (fix/uncached-fetch-per-insn, based on master)
is being prepared. A has since landed as #429, so the ledger does not
list bios-kseg1-fetch-charge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:20:52 -07:00
Shokunin 9f8ee72f4d docs: design segment-aware code, with a KUSEG-linked acceptance ledger
Compiled code bakes KSEG0 into link values, CPS/IRQ resume PCs and I-cache
tags. KUSEG-linked EXEs run KUSEG PCs through those bodies (and their KUSEG
seeds are dropped), and overlay code executed at KUSEG/KSEG1 stays
interpreted after #417. Until #429, both emitters also charged the uncached
KSEG1 fetch only at cache-line leaders, so compiled BIOS ROM code was 4
cycles short on 5,477 of OpenBIOS's 9,592 KSEG1 instruction sites (Beetle
and the interp charge every fetch).

docs/SEGMENT_AWARE_CODE.md recommends per-segment compiled variants (code
identity = full VA, emitter segment as a per-compile constant, exact-segment
dispatch, per-instruction fetch in uncached bodies, segment-aware capture
and cache keys) over segment-relative emission, which measures +3.2 % /
+27.2 % __TEXT on R4 shards.

tools/segment_testrom/gen_segment_exe.py emits a synthetic KUSEG-linked
PS-X EXE that also works as an oracle probe. The new ctest
segment_aware_codegen runs the real recompilers on it and on OpenBIOS and
checks emitted PCs, the emitted dispatch lookup and fetch cycles (through
runtime/src/psx_icache.c) against a Beetle ReadInstruction transcription.
It passes only while the observed gaps equal its ledger (9 today).

#429 (rollout A) fixed the BIOS undercharge on master before this landed,
so KNOWN_GAPS does not list bios-kseg1-fetch-charge: the OpenBIOS check
finds no uncharged KSEG1 site and stays as a regression guard that reports
the id as a new gap. The last commit of this series brings the doc up to
date with #429.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:20:52 -07:00
Shokunin 825a89a9c5 runtime: keep host stores out of IsC, as DMA is
Only CPU stores are cache-isolated. Mods, the FMV skip, debug pokes, the
bg2d ring fill and the exception sentinel write guest memory from the host;
they now go through psx_host_write_*, which bypasses isc_store the way DMA
does, so a host store inside a FlushCache window reaches memory instead of
rewriting an I-cache tag. The render-pass checkpoint restores the new depth
counter like the DMA depth. isc_store_test covers it (a lane-0 host store in
tag-test mode would otherwise mark a cold line valid).

Docs: the SCPH-1001 residual starts at kernel init 0x598, and without #429
the fix removes 113 of the 126 OpenBIOS flush cycles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:55:26 -07:00
Shokunin 6de75122a3 docs: record the Beetle gate for IsC stores and tick the burndown item
FAITHFUL_TIMING_PLAN §5 (2026-09-29): with #429 on master, the LLE boot
now matches Beetle at every anchor to the shell on OpenBIOS (was -126)
and sits at -9 on SCPH-1001 (was -454), the KSEG1 kernel entry PR D
closes. Also measured without #429, the rulers (unchanged), R4's game
entry (+342 default boot, +18 LLE boot, 0 dispatch misses) and the
unchanged codegen hash.

ACCURACY_BURNDOWN axis 4: IsC item ticked; the SWL/SWR corner cases are
a new open item; the P2 scratchpad-under-IsC note points at Beetle's
rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:33:35 -07:00
Shokunin 621a92d49b runtime: apply cache-isolated (IsC) stores to the caches, as Beetle does
memory.c dropped every store made while SR.IsC was set. Beetle's
PS_CPU::WriteMemory sends them to the caches instead
(mednafen/psx/cpu.cpp:482-512), before any address decode:

- I-cache enabled (BIU bit 11) with a tag-test, invalidate or lock mode
  bit: the store rewrites the tag and valid bits of its line. This is how
  FlushCache (A 44h) and the boot cache init invalidate the I-cache.
- D-cache enabled with lock mode clear ((BIU & 0x81) == 0x80): the store
  lands in the scratchpad at addr & 0x3FF.
- Nothing reaches the bus, the BIU register at 0xFFFE0130 included.

psx_icache_isc_store transcribes the tag write; memory.c's three store
chokepoints call it (and the scratchpad write) first, so compiled code,
overlay shards and both interpreters share it. DMA writes are not CPU
stores and now bypass IsC, as in Beetle. A render pass applies the same
effect (its checkpoint restores tags and scratchpad), and the overlay
shadow record fails closed on an isolated store, which its replay
cannot redo.

isc_store_test drives the real memory.c and psx_icache.c: 15 of its
checks fail on master.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:12:55 -07:00
Matt Stanley 1ec24e9d49 Merge pull request #434 from RetroPortingToolKit/fix/aot-discovery-project-root
AOT extract: resolve discovery's BIOS profile from the framework, fail closed
2026-09-29 16:44:22 -07:00
Matthew Stanley 8f3b6778a0 AOT extract: resolve discovery's BIOS profile from the framework, fail closed
Normal-mode discovery ran psxrecomp-game with no --project-root and ignored its exit code, so from a cwd without bios/ every producer silently lost its discovered entries (Tomba 2 shipped 54 native pairs instead of 81).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 16:43:58 -07:00
Matt Stanley 0ffab1ceb9 Merge pull request #429 from RetroPortingToolKit/fix/uncached-fetch-per-insn
Charge the uncached (KSEG1) instruction fetch on every instruction
2026-09-29 16:31:20 -07:00
Matt Stanley 9a8737d579 Merge pull request #431 from RetroPortingToolKit/docs/beetle-macos
docs: build the psx-beetle oracle on macOS (plus the missing Beetle hook patches)
2026-09-29 16:31:07 -07:00
Matt Stanley acc718db4a Merge pull request #433 from RetroPortingToolKit/fix/gl-scale-fixture-args
Test: pass interp_capture's origin and redrawn arguments in the GL fixture
2026-09-29 16:30:54 -07:00
Shokunin 2dc4e2d591 Test: pass interp_capture's origin and redrawn arguments in the GL fixture
The merge of #427 (58bdf987) kept the fixture's 8-argument
interp_capture(...) call, but #424 had already grown the function to 11
parameters (origin_x, origin_y, redrawn). The fixture is compiled when the
test runs, so CMake configures and builds cleanly and
gl_scale_invariance_test then fails in about 0.2 s with "too few arguments
to function call, expected 11, have 8".

Pass 0, 0, 1 (origin at the capture rectangle, frame redrawn), as the
pre-merge integration of #424 and #427 did. With it the test passes on
macOS arm64 (Metal GL 4.1): lines, masks, captures and render passes at
1x-18x, the window mode and both clamps, 0 failures.

Test-only; no runtime change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 14:04:53 -07:00
Shokunin 1caf4a6897 docs: record the land-gate re-check after the rebase onto 470f03b7
The branch was rebased onto master 470f03b7, which now carries #418, so
the gate was re-checked on that tree without local merges. The
regenerated OpenBIOS and SCPH-1001 C is byte-identical to the gated
build's. Every boot anchor gives the same cycle (native - Beetle at the
shell entry: -126 on OpenBIOS, -454 on SCPH-1001). Ruler #1 matches Beetle
on all 64 passes, and ruler #2 on all 14 components on an HLE and an LLE
boot. uncached_fetch_charge and uncached_fetch_codegen_test pass. The
recompiler suite passes apart from the three environmental failures
master also shows on this Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:36:13 -07:00
Shokunin 035115392b docs: make the land-gate method reproducible from the tree
The land-gate entry said cyc_watch was armed from power-on on both
backends, but did not say how, and cited a local branch for the psx-beetle
build. The arming is PSX_CYC_WATCH / PSX_CYC_WATCH_N on psx-runtime and
psx-beetle (plus PSX_BEETLE_UNPACED=1 for a headless Beetle), documented in
docs/beetle-macos.md step 7 with the psx-beetle build; the entry now cites
both.

It also listed the shell entry as 0x80030000 on both sides. Both BIOS
profiles declare the shell window dispatch_key = "rom", so native keys it
at its ROM copy: the native anchor was 0xBFC0A500 (OpenBIOS) and
0xBFC18000 (SCPH-1001), and a native watch on 0x80030000 records nothing.
The numbers are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:27:58 -07:00
Shokunin 0cf03770d4 docs: record the Beetle land gate for the uncached fetch fix
SEGMENT_AWARE_CODE.md §7.2 gates this change on an LLE boot with per-anchor
cycle parity against psx-beetle to the shell, plus ruler #1. Both ran on
macOS, each twice: with OpenBIOS (SHA-1 95419841b5104d552b14810b1ecbe6c1358bcdf1)
and with the owner's own SCPH-1001 dump (SHA-1
10155d8d6e6e832d6ea66db9bc098321fb5e8ebf), the same image on both sides. Native
master and this branch were built with #418 merged locally, so the mult/div
flush could not mask the result.

At the shell entry, native minus Beetle goes from -2,165,591 to -126 cycles on
OpenBIOS and from -7,049,462 to -454 on SCPH-1001. The ROM boot path is exact
on both. Two known gaps explain what is left:
- IsC stores do not reach the I-cache model, so a FlushCache leaves native
  kernel lines valid that Beetle refills. A local prototype of Beetle's IsC
  tag write takes OpenBIOS to 0 and SCPH-1001 to -9. It is recorded as an
  axis-4 item here, not part of this change.
- SCPH-1001 enters its copied kernel through the KSEG1 alias 0xA0000500. The
  four trampoline words run uncached in Beetle and cached natively (-9). That
  is the KSEG1-alias gap in SEGMENT_AWARE_CODE.md §3.3.

Ruler #1 on SCPH-1001 matches Beetle on all 64 passes, on master and this
branch. Ruler #2 stays exact on all 15 loops. Beetle also confirms the memcpy
cost the branch now charges: 39 cycles per byte during boot, and 42 per byte,
860 and 3,884 cycles per call in R4.

The land gate item is ticked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:27:58 -07:00
Shokunin b3d902f80b docs: record the Beetle land gate and in-game cost of the uncached fetch fix
The 2026-09-29 log entry marked the live-Beetle validation OPEN inside a
done item. SEGMENT_AWARE_CODE.md §7.2 makes it a precondition for landing:
an LLE boot with per-anchor cycle parity against psx-beetle to the shell,
plus ruler #1. ACCURACY_BURNDOWN now carries it as its own unchecked gate
item, and the timing-plan entry says the PR stays draft until it runs on an
oracle machine.

The entry also framed the change as boot-only. OpenBIOS runs its A0/B0/C0
services in place, and 148 of its 192 A0 entries point into ROM, so the
per-instruction charge also applies during gameplay. Measured in an R4
race: memcpy's byte loop goes from 25 to 42 cycles per byte, and R4's two
memcpy calls per game frame go from 2,824 to 4,744 cycles (about 0.17% of
the frame). An LLE boot of R4 reaches the game entry 2,934,910 cycles
(1.10%) later, identical on two boots of each build, with 0 dispatch misses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:27:57 -07:00
Shokunin c981b3a2d7 bios emitter: test cache-line starts on the runtime PC
The cached line-leader rule charges a fetch at every 16-byte line start of
the runtime PC. The BIOS emitter tested bits[3:0] of the ROM address instead
and assumed every copy window preserves them. OpenBIOS copies its kernel from
ROM 0x1FC1E4D4 to RAM 0x500, which shifts them by 4. Each kernel line
crossing was charged one instruction early, where it is a hit, and the real
crossing went uncharged. 1,152 kernel instruction sites differed from the
interpreter.

The test now uses the runtime PC that relocate_ra already computes. In the
OpenBIOS kernel, 823 misplaced fetch sites go and 773 are added at runtime
line starts. Retail profiles are unaffected: SCPH-1001's copy windows are
16-byte aligned and SCPH-101/5552 declare none. The uncached_fetch_charge
ctest now also replays every cached OpenBIOS run from a cold cache against
the interpreter's fetch path and the Beetle transcription.

R4 (OpenBIOS, recompiled kernel, HLE boot) reaches the game entry 6,013
guest cycles later than with the uncached fix alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:27:57 -07:00
Shokunin 7342e6bcd1 emitters: charge an uncached (KSEG1) fetch before every instruction
Beetle's ReadInstruction never fills an I-cache line for a fetch at
0xA0000000 or above: every such fetch costs +4 and clears the pending load
give-back. The dirty-RAM interpreter charges that by fetching at every PC.
Both static emitters emitted psx_icache_fetch only at cache-line leaders,
which is exact for cached code only. In OpenBIOS, 5,477 of the 9,592
in-place ROM (KSEG1) instruction sites were uncharged: 21,908 cycles short
per pass through that code, each also missing its give-back clear.

Both emitters now charge a fetch before every instruction whose runtime PC
is uncached. The predicate is psx_fetch_uncached in psx_instr_cost.h, which
psx_icache.c now uses too. The A0/B0/C0 call-vector native stubs charge one
fetch per executed word, so a KSEG1 call pays like the interpreter. Cached
code keeps the leader rule. R4's regenerated game C is byte-identical, and
the OpenBIOS diff is insertions only. psx_instr_cost.h joins the codegen
hash list, since the emitters fold it into emitted code.

Tests:
- ctest uncached_fetch_charge runs psxrecomp-bios on OpenBIOS and compares
  per-instruction fetch charges three ways: the compiled fetches, the
  interpreter's fetch path (psx_icache_fetch_interp) at every PC, and a
  Beetle transcription.
- uncached_fetch_codegen_test checks the game emitter at KSEG1 and KSEG0.

Both tests fail on master. This closes bios-kseg1-fetch-charge in the
segment_aware_codegen ledger (#419).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:27:57 -07:00
Shokunin 8915131ced psx-beetle: say 4382 in the port comments
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>
2026-09-29 12:26:45 -07:00
Shokunin 7118734d04 docs: correct the ruler #2 icache_miss value on the 15-loop ROM
The 2026-06-27 MMIO entry in FAITHFUL_TIMING_PLAN.md says the 13 prior
loops were unchanged when mmio_timer and mmio_spu were added. icache_miss
was not: the two new loops sit before it and move its loop top from
0x80010144 to 0x80010170, so each of its two misses costs one cycle more
and it reads +16 on both backends (re-measured against live psx-beetle; the
cycle_testrom README already explains the shift). The entry now carries a
dated correction, and the beetle-macos.md intro no longer points readers
at +14 as the current value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 12:26:45 -07:00
Shokunin 216eb31ef2 cyc_watch: arm boot anchors from power-on on both backends
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>
2026-09-29 12:26:16 -07:00
Cyanoxide 4aa72128fa Make the depth24 trailing margin configurable per title 2026-09-29 20:00:35 +01:00
Matt Stanley 470f03b782 Merge pull request #430 from RetroPortingToolKit/feat/fp-identity-tool
tools: generic A/B guest-identity check (fp_identity.py)
2026-09-29 11:24:04 -07:00