mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
tooling: PSX_FORCE_INTERP — measure the dirty-RAM interp's cycle model
Adds a force-interp knob (dirty_ram_is_dirty reports all RAM above the kernel window dirty when PSX_FORCE_INTERP=1) so the dispatcher routes clean compiled game text through the dirty-RAM interpreter — the SAME path overlays take — with no emitter/dispatch change. This is the interp-path Δ-ruler enabler: it lets the cyctest isolation loops be measured native-INTERP vs Beetle. RESULT — the dirty-RAM interpreter == Beetle EXACT on all 12 ruler#2 components (baseline/alu/load/load2/load_use/div/div_spaced/mult/ gte_rtps/gte_nclip/gte_read_use/ld_div). The interpreter's per-instruction load-delay + GTE/muldiv interlock is now MEASURED equal to the oracle and to the compiled backend, not merely shared-by-construction. Process notes (cost real time, now documented in the cyctest README): - launch psx-cyctest via PowerShell Start-Process; a bash '&' launch fails to boot (pc=0). - sample cyc_watch / freeze_check at STEADY STATE: an early query (before the BIOS boots to the EXE entry) reports dirty_ram_insns=0 / warm-up values. This premature-sampling artifact was the whole "dispatch paradox" — the interp engages only after the EXE entry is reached. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -106,8 +106,22 @@ void dirty_ram_mark_executable_range(uint32_t phys, uint32_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Force-interp mode (tooling): PSX_FORCE_INTERP=1 makes ALL RAM above the kernel
|
||||
* window report dirty, so every dispatch into game/overlay text routes to the
|
||||
* dirty-RAM interpreter (the SAME path overlays take) instead of the compiled
|
||||
* image. Interp-path Δ-ruler enabler: lets the cyctest isolation loops be measured
|
||||
* native-INTERP vs Beetle. Consulted by every routing site that already calls
|
||||
* dirty_ram_is_dirty (top dispatch, dirty_ram_dispatch_inner gates, the
|
||||
* psx_dispatch_game_compiled gates) — no emitter/dispatch change needed. */
|
||||
static int dirty_ram_force_interp(void) {
|
||||
static int s = -1;
|
||||
if (s < 0) { const char* e = getenv("PSX_FORCE_INTERP"); s = (e && e[0] && e[0] != '0'); }
|
||||
return s;
|
||||
}
|
||||
|
||||
int dirty_ram_is_dirty(uint32_t phys) {
|
||||
if (phys >= RAM_SIZE) return 0;
|
||||
if (dirty_ram_force_interp() && phys >= DIRTY_RAM_KERNEL_TRACK_BYTES) return 1;
|
||||
uint32_t page = phys >> DIRTY_RAM_PAGE_SHIFT;
|
||||
return (dirty_ram_bitmap[page >> 5] >> (page & 31u)) & 1u;
|
||||
}
|
||||
|
||||
@@ -73,13 +73,42 @@ tracked, so each developer reproduces the disc from a disc they own.
|
||||
## Measure
|
||||
|
||||
```bash
|
||||
# both backends boot the same disc/cyctest.cue:
|
||||
# both backends boot the same disc/cyctest.cue (psx-cyctest = the native test-runtime, port 4600):
|
||||
# beetle: psx-beetle <bios> --disc disc/cyctest.cue --port 4382
|
||||
# native: <test-runtime> ... --disc disc/cyctest.cue (TODO: test-runtime)
|
||||
# native: psx-cyctest --no-launcher --game game.toml --bios <bios> --disc disc/cyctest.cue
|
||||
python measure.py --port 4382 # Beetle oracle: per-iter + per-component delta
|
||||
python measure.py --port 4500 # native cost model (once the test-runtime exists)
|
||||
python measure.py --port 4600 # native cost model (compiled backend)
|
||||
```
|
||||
|
||||
IMPORTANT: launch psx-cyctest via PowerShell `Start-Process` (a bash `&` launch
|
||||
fails to boot — pc=0). cyc_watch / freeze_check sampling must be done AT STEADY
|
||||
STATE: query AFTER the BIOS has booted to the EXE and the loops are running
|
||||
(`measure.py`'s own wait handles this) — an early sample reports warm-up values
|
||||
(Beetle's boot window) or, for the interp path, dirty_ram_insns=0 because the
|
||||
EXE entry hasn't been reached yet.
|
||||
|
||||
## Interp-path measurement (PSX_FORCE_INTERP)
|
||||
|
||||
Both backends share one cost model (`psx_cyc_*`, psx_cyc.h), but `measure.py
|
||||
--port 4600` above measures the COMPILED backend. To measure the dirty-RAM
|
||||
INTERPRETER on the same loops (isolated interp-vs-Beetle Δ — not by-construction),
|
||||
launch psx-cyctest with `PSX_FORCE_INTERP=1`:
|
||||
|
||||
```powershell
|
||||
$env:PSX_FORCE_INTERP='1'; Start-Process psx-cyctest.exe -ArgumentList ...
|
||||
```
|
||||
|
||||
`PSX_FORCE_INTERP=1` makes `dirty_ram_is_dirty()` (runtime/src/memory.c) report all
|
||||
RAM above the kernel window as dirty, so the dispatcher routes the test ROM through
|
||||
the dirty-RAM interpreter (the SAME path overlays take) instead of the compiled
|
||||
image — no emitter/dispatch change. Confirm it engaged via freeze_check
|
||||
`dirty_ram_insns` climbing (hundreds of millions during a measure run).
|
||||
|
||||
**Interp == Beetle EXACT on all 12 components (2026-06-27):** baseline/alu/load/
|
||||
load2/load_use/div/div_spaced/mult/gte_rtps/gte_nclip/gte_read_use/ld_div all match
|
||||
the oracle, so the interpreter's per-instruction interlock model is MEASURED equal
|
||||
to the compiled backend and to Beetle, not merely shared-by-construction.
|
||||
|
||||
## Beetle ORACLE results (2026-06-26 — the HW cost targets)
|
||||
|
||||
Per-iteration cycle delta, and component cost (minus baseline=3):
|
||||
|
||||
Reference in New Issue
Block a user