Update from feature/jaguar-cd-support#10
Draft
github-actions[bot] wants to merge 84 commits into
Draft
Conversation
Author
Regression:
|
| ROM | Status | Details | Diff |
|---|---|---|---|
| jagniccc | 🆕 NEW | no baseline | - |
| yarc | 🆕 NEW | no baseline | - |
| jagniccc (determinism) | ✅ PASS | identical across runs | - |
| yarc (determinism) | ✅ PASS | identical across runs | - |
| jagniccc (frameskip) | ✅ PASS | skip=0 matches skip=3 | - |
| yarc (frameskip) | ✅ PASS | skip=0 matches skip=3 | - |
| jagniccc (save state) | ✅ PASS | round-trip matches | - |
| yarc (save state) | ✅ PASS | round-trip matches | - |
| jagniccc (rewind) | ✅ PASS | rewind matches | - |
| yarc (rewind) | ✅ PASS | rewind matches | - |
Platform: Darwin arm64
Updated by CI at 2026-07-14T07:45:56.906Z
Author
Regression:
|
| ROM | Status | Details | Diff |
|---|---|---|---|
| jagniccc | 🆕 NEW | no baseline | - |
| yarc | 🆕 NEW | no baseline | - |
| jagniccc (determinism) | ✅ PASS | identical across runs | - |
| yarc (determinism) | ✅ PASS | identical across runs | - |
| jagniccc (frameskip) | ✅ PASS | skip=0 matches skip=3 | - |
| yarc (frameskip) | ✅ PASS | skip=0 matches skip=3 | - |
| jagniccc (save state) | ✅ PASS | round-trip matches | - |
| yarc (save state) | ✅ PASS | round-trip matches | - |
| jagniccc (rewind) | ✅ PASS | rewind matches | - |
| yarc (rewind) | ✅ PASS | rewind matches | - |
Platform: Linux x86_64
Updated by CI at 2026-07-14T07:46:45.477Z
Author
Regression:
|
| ROM | Status | Details | Diff |
|---|---|---|---|
| jagniccc | 🆕 NEW | no baseline | - |
| yarc | 🆕 NEW | no baseline | - |
| jagniccc (determinism) | ✅ PASS | identical across runs | - |
| yarc (determinism) | ✅ PASS | identical across runs | - |
| jagniccc (frameskip) | ✅ PASS | skip=0 matches skip=3 | - |
| yarc (frameskip) | ✅ PASS | skip=0 matches skip=3 | - |
| jagniccc (save state) | ✅ PASS | round-trip matches | - |
| yarc (save state) | ✅ PASS | round-trip matches | - |
| jagniccc (rewind) | ✅ PASS | rewind matches | - |
| yarc (rewind) | ✅ PASS | rewind matches | - |
Platform: Linux aarch64
Updated by CI at 2026-07-14T07:47:03.077Z
Bumps the actions-minor-patch group with 1 update: [cirrus-actions/rebase](https://github.com/cirrus-actions/rebase). Updates `cirrus-actions/rebase` from 1.4 to 1.8 - [Release notes](https://github.com/cirrus-actions/rebase/releases) - [Commits](cirrus-actions/rebase@1.4...1.8) --- updated-dependencies: - dependency-name: cirrus-actions/rebase dependency-version: '1.8' dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions-minor-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](actions/upload-artifact@v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/labeler](https://github.com/actions/labeler) from 5 to 6. - [Release notes](https://github.com/actions/labeler/releases) - [Commits](actions/labeler@v5...v6) --- updated-dependencies: - dependency-name: actions/labeler dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](actions/cache@v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5 to 6. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](codecov/codecov-action@v5...v6) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
…ns/develop/codecov/codecov-action-6 ci: Bump codecov/codecov-action from 5 to 6
…ns/develop/actions/cache-5 ci: Bump actions/cache from 4 to 5
…ns/develop/actions/labeler-6 ci: Bump actions/labeler from 5 to 6
…ns/develop/actions/upload-artifact-7 ci: Bump actions/upload-artifact from 4 to 7
…ns/develop/actions-minor-patch-4f0f6e5aaa ci: Bump cirrus-actions/rebase from 1.4 to 1.8 in the actions-minor-patch group
JoeMatt
force-pushed
the
feature/jaguar-cd-support
branch
from
May 7, 2026 02:19
806f321 to
77b0675
Compare
Signed-off-by: Joseph Mattiello <git@joemattiello.com>
PR libretro#170 (closed without merge) shipped a test_audio_clipping pass on Iron Soldier 2 that read "0 saturated samples / RMS=521" — which is silence, not fixed audio. The clipping test couldn't tell those apart: it only flags loud-broken output (saturation density, run length, sustained loud RMS), so a "fix" that drops RMS to zero sailed through. test_audio_presence is the missing counterpart. It runs N frames, captures the post-onset window, and asserts: - first_audio_frame is reached (some |s| > 32 seen) - window RMS lies in [floor, ceiling] -- per-ROM tunables via CLI - longest run of near-zero stereo frames < max_zero_run_pct of window Wired into `make test` against Iron Soldier 1 (boots straight to a music-on title, develop measures RMS ~1175). Floor 200 catches the silencing-regression class, ceiling 25000 catches the loud-broken class. Same skip-if-missing pattern as the rest of the audio tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR libretro#170 lesson: a clipping-only test missed the silencing regression where Iron Soldier 2 went from 17% saturated → RMS=521 (silent). Document `test_audio_clipping` + `test_audio_presence` as a pair, and add a "Audio / DSP work — required tests" section that spells out the required runs before declaring an audio change done. Also pin the rule into the sub-agent guidelines so worktree agents don't ship the same masked-failure pattern. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Brings in the CUE/BIN parser, BUTCH emulation, HLE/cart/bios boot strategies, and BootConfig settings glue from claude/add-jaguar-cd-support-hOzev (PR libretro#109), repointed to the new src/cd and src/core layout. The build now links the CD subsystem but no path activates it -- bootConfig.strategy is NULL and JaguarCDHLEActive() returns false. libretro.c CD detection and src/core/jaguar.c CD reset are deferred to Tier 2. C89 fixes applied to satisfy scripts/c89-lint.sh: hoisted for-loop counters (HLEHandleCDRead phase loop, HLEPopulateCartBuffer, ATRI sync block, cdrom.c HLETransferTick), moved mid-block decls to block tops in cdrom.c (BUTCHExec, NM93C14 opcode/addr decode, seek-redirect discTotal) and cdintf.c (CDIntfGetTrackInfo MSF locals), pulled HLEHandleCDRead's sentinelIsAscii/fallback*/phase_starts/wasRedirected to the function header, and converted designated initializers in cd_boot_strategy_{hle,cart,bios} to positional. VJSettings layout: kept hardwareTypeNTSC/useJaguarBIOS/useFastBlitter as the leading three fields so test/test_hle_bios.c (which redeclares the struct with just those three) still resolves them via dlsym at matching offsets. external_cd_bios[] / cd_bios_loaded_externally are emitted as weak fallback definitions in src/cd/jagcd_bios.c; libretro.c will provide the real definitions in Tier 2 and override them. JaguarInstallCDAuthBypass() is forward-declared in cdintf.c; it is defined in jagcd_bios.c so cdintf can arm the BNE-NOP patch when the BIOS reads into an inter-session gap. Files imported: - src/cd/cdintf.{c,h} -- CUE/BIN parser - src/cd/cdrom.{c,h} -- BUTCH emulation - src/cd/jagcd_{bios,cart,hle}.c, jagcd_{boot,hle}.h -- strategies - src/core/settings.{c,h} -- BootConfig + ResolveBootConfig - Makefile.common -- register the new objects Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Detects .cue/.cdi/.iso content in retro_load_game, scans the system directory for a CD BIOS image, opens the disc image via CDIntfOpenImage, calls ResolveBootConfig to pick the boot strategy, and dispatches via bootConfig.strategy->boot(info). Adds virtualjaguar_cd_bios_type and virtualjaguar_cd_boot_mode core options. Extends the EEPROM save buffer to include the CD EEPROM (cdrom_eeprom_ram[64]). The Tier 1 weak symbols for external_cd_bios[] and cd_bios_loaded_externally are now overridden by strong libretro.c definitions. Cart-only flow is unchanged when no CD content is loaded. Also propagates JaguarLoadFile failures out of cart_boot (so the 'unknown headerless BIN rejected' contract still holds) and folds the post-reset reload of RAM-loaded executables (ABS/COFF/JAGSERVER) into the cart strategy. Exposes cdrom_eeprom_ram so the save buffer can pack both EEPROMs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wires the M68KInstructionHook to dispatch JaguarCDHLEHook and the active
CDBootStrategy's instruction_hook, and invokes the strategy's reset()
from JaguarReset so HLE/BIOS per-run state (auth-bypass flag, boot-stub
flag, HLE active flag) clears alongside the rest of the machine.
The cart strategy keeps its existing path: cart_instruction_hook and
cart_reset are no-ops, so non-CD content is unaffected. CD HLE/BIOS
strategies (already imported by Tier 1) now actually fire.
Tier 1+2 already exposed everything else this needs:
- jagcd_hle.c defines JaguarCDHLEHook + cd_boot_strategy_hle
- jagcd_bios.c defines bios_instruction_hook + JaguarInstallCDAuthBypass
- jagcd_cart.c defines the cart no-op strategy
- cdrom.c handles BUTCH FIFO drain via HLETransferTick (no GPU-side
intercept needed; libretro#109's gpu.c HLE path was an alternate route)
- jerry.c already routes BUTCH I2S into the DAC (Tier 1 imported)
- libretro.c populates bootConfig.strategy via ResolveBootConfig
Hunks from libretro#109 not ported, with reasoning:
- src/m68000/m68kinterface.c MULL/DIVL: their version is buggier
(truncates 64-bit overflow detection, mishandles unsigned q sign,
drops Dh != Dl guard). Our existing code is correct; keep ours.
- src/tom/gpu.c +263 lines: 95% diagnostic tracing (gpuStartCount,
GPU_TRACE, gpu_isr_phase, gpu_ram_8 dumps). The functional CD
intercept (JaguarCDHLEGPUDataPhase) is reachable from cdrom.c's
HLE path; the GPU-side intercept is an alternate route we don't
need. Skipping to preserve our IRQ priority/dispatch (Test 9d).
- src/tom/tom.c: their tom.c has no CD-specific changes vs ours;
the diff was rename + cosmetic.
- src/jerry/eeprom.{c,h}: cdrom_eeprom_ram[] already lives in
cdrom.c with libretro.c handling save/restore. No header tweak
needed.
- src/core/file.c: ours has more code (raw-binary homebrew load
address inference); their version is older. Keep ours.
- src/core/log.h: identical content (just inline vs INLINE; ours
is more portable for MSVC).
No test changes (Tier 4) and no libretro.c changes (Tier 2 already
did those). test_hle_bios stays at 211/211, make test 0 failures.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Brings in libretro#109's test infrastructure for the CD subsystem. Per the Tier 5 triage report, redundant general-hardware suites were dropped; overlapping cherry-picks deferred to a follow-up. Imported and wired into make test (84 new passing asserts): test/test_butch_cd.c, test_bios_config.c, test_boot_config.c, test_audio_dac.c Built but not auto-run (private-ROM gated; surface real per-game boot regressions when discs are present): test/test_cd_boot.c, test_cd_hle_boot.c, test_cd_bios_boot.c, test_blitter.c Framework + helpers + tooling: test_framework.h, cd_assertions.h, mister_ground_truth.h, dump_pc.c, heap_search.c, headless.py, and test/tools/{analyze_cd_roms,bios_disasm,disasm_gpu_isr}.py. Dropped (redundant with our coverage in test_hle_bios, test_dsp_ops, test_gpu_ops, test_m68k_ops, test_blitter_simd): test_dsp_instructions, test_gpu_controlflow, test_gpu_instructions, test_m68k_instructions, test_timers, test_irq, test_gpu_ctrl, test_video_modes, test_memory_map, test_gpu_irq. Cherry-picks deferred to a follow-up: resolution-derivation cases from test_video_modes ->test_hle_bios, gpu_ctrl_bus_hog_readback / gpu_flags_int_enable_all_five from test_gpu_ctrl -> test_hle_bios, BUTCH cases from test_irq -> test_butch_cd, line_buffer cases from test_memory_map -> test_hle_bios. All imports pass scripts/c89-lint.sh (decls hoisted). test_hle_bios unchanged at 211; full suite OK: 0 test(s) failed.
…-ID shortcut Two bugs in jagcd_hle.c CD_read: 1. The "sentinel NOT found — read raw" fallback hard-coded scanLBA = lba (the original requested seek), which silently undid the streaming-continuation logic that advances startLBA past previously transferred sectors on repeated calls. Result: a game polling the same CD_read got the same 1 MB on loop forever. Now uses startLBA so continuation works on the fallback path too. 2. When D1's top 16 bits are zero (e.g. Space Ace passes D1=$00000001 as a transfer ID, not a sync pattern), the sentinel scan finds millions of false-positive `\0\0\0\x01` matches across the disc but never accepts a real sync block — then falls back to "read raw" anyway, after burning 4 M log lines and several seconds of CPU per call. Now short-circuits the scan when D1 is clearly a counter and streams raw from startLBA directly. Space Ace's HLE_LOG output drops from ~4.3 M lines to ~43 K (100x). Primal Rage's behaviour unchanged (it uses real ASCII sentinels — DDL9/DDL5 — which find proper sync blocks). Full HLE sweep still 5/9 pass; full make test green. The user-visible "screen goes black" failure on Space Ace is upstream of these fixes — the game now gets fresh sectors per call but quickly runs past end-of-disc into zero-fill. Tracking that as part of a broader CD-subsystem audit now that the CPU/GPU/DSP/IRQ baseline is solid.
load_external_cd_bios() previously only matched the .j64 variants of the World retail and developer CD BIOS plus the lowercase jaguarcd_bios variants. Real-world libretro frontends ship the BIOS under names like "Jaguar CD BIOS.rom" or with the libretro [BIOS] tag in .rom/.bin form. Add those filenames so the BIOS is picked up without renaming.
cdrom.c:593 was asserting GPUIRQ_DSP (=1, vector $F03010) but the CD BIOS installs its CD-data ISR at GPU IRQ0 ($F03000) - the EXT1/CPU line. The mismatch meant any BUTCH-triggered IRQ landed on the DSP vector with no handler. Fix to GPUIRQ_CPU. Identified by the CD-IRQ-chain audit (no behaviour change without the BUTCHExec scheduler tick - see follow-up commit).
BUTCHExec was fully written but never called from anywhere in the live engine, so BUTCH never asserted IRQs to the GPU on any path. The HLE shortcuts (jagcd_hle.c sentinel scan, cdrom.c:335 HLETransferTick) compensated by bypassing BUTCH entirely. Tick BUTCHExec once per halfline when bootConfig.isCDGame is true. Halfline cadence (~32 us) is much coarser than real BUTCH I2S timing but matches our existing event-queue resolution; the FIFO-fill state machine progresses one transition per call. Real-BIOS path can now fire its CD ISR. The HLE shortcuts remain load-bearing because of the documented PTRPOS divergence (cdrom.c:319-333) - that fix is separate. Adds test_butch_gpu_irq_line_mapping to test_hle_bios.c pinning the GPUSetIRQLine(line, ASSERT) -> gpu_control bit (6+line) mapping for both lines BUTCH could plausibly target, regression-locking the GPUIRQ_CPU vs GPUIRQ_DSP fix from the previous commit.
… dual-delivery
Two destructive paths surfaced when Path A wired BUTCHExec into the
scheduler:
1. The "after N seeks without HLE progress, fall back to native FIFO
IRQ" mechanism (HLE_FALLBACK_THRESHOLD) was harmless when BUTCH
was dormant but actively destructive once BUTCHExec ticks: the
fallback unleashed FIFO IRQs which run the BIOS GPU ISR's
fifo_read path, which writes to the wrong RAM address (PTRPOS
divergence, cdrom.c:319-333). Pin hleActive = (strategy==bios)
permanently. HLETransferTick remains the canonical CD-data path
for both strategies until the GPU-side PTRPOS bug is fixed.
2. The dual JERRY-EXT1 delivery — JERRYSetPendingIRQ() AND
m68k_set_irq(2) — fired the 68K IRQ2 vector during transfers when
the CD BIOS hadn't yet installed its EXT1 trampoline (Hover
Strike, Primal Rage stack-corruption to PC=$22xxxxxx). The CD
BIOS clears BUTCH bit 0 before issuing CD_read so the 68K side is
dormant during transfers; the GPU side (BUTCH -> JERRY EXT1
latch -> GPU IRQ0) is the path that matters. Drop the m68k_set_irq
call but keep the JERRYSetPendingIRQ pending bit so JINTCTRL reads
continue to see the latched source.
Sweep deltas vs pre-Path-A baseline (which passed 7/9 via fake
HLETransferTick path):
BIOS: 6/9 actually executing the BIOS code path (was 5/9 with
Path-A regression, was 7/9 fake before Path A).
HLE: 5/9 (unchanged).
Hover Strike: recovered to PASS.
Primal Rage: still fails but the wedge moved deeper into the BIOS
path (227 unique PCs, 16 KB RAM payload before the
$220DC09E garbage-PC). Tracking separately.
Adds per-disc visibility into actual CD subsystem activity. Previously the harness only sampled 68K PC, which can't distinguish "BIOS wedged in CD spin-loop" from "BIOS booted game code." Now the per-disc line includes GPU PC, GPU IRQ0 (CD ISR) count, GPU IRQ3 (OP IRQ) count, BUTCH diag counters (butchExec/fifoIRQs/dsaIRQs/fifoReads/seeks/ globalDisabled), and HLE transfer bytes. Implementation: - Adds gpu_irq0_count and gpu_irq3_count globals to gpu.c, declared in gpu.h. Incremented in GPUSetIRQLine on ASSERT_LINE; reset in GPUReset alongside other GPU state. Pure observability — no behavioural side effects. - Adds CDROMDiagGetCounters() accessor to cdrom.c/cdrom.h that exposes the existing static diag_* counters and hleTransferBytes to harnesses. Avoids dropping `static` on the whole set. - cd_assertions.h: new cd_diag_snapshot struct + cd_diag_capture() helper that reads the accessor via dlsym (alongside gpu_irq0_count, gpu_irq3_count, GPUGetPC). Both HLE and BIOS harnesses capture the snapshot before unload and emit a [DIAG] line per disc. C89/GNU89 clean (declarations at top of block). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Removed 6 patches in jagcd_bios.c (4 RAM stomps, BNE-NOP auth-bypass + helper, DSP completion-flag stomp), TryReadAuthRedirect in cdintf.c, and HLETransferTick + support machinery in cdrom.c. HLE 5/9 unchanged. BIOS 6/9 -> 7/9 (Primal Rage flips PASS, was wedging at gpu_pc=$22002200). make test green.
The CD BIOS GPU CD ISR reads DSCNTRL after handling a DSARX response and the inline comment in the embedded BIOS listing (cdrom.c:1643) documents this read as 'Clears DSA pending interrupt'. Real BUTCH hardware acks the DSA pending latch on a CPU/GPU read of DSCNTRL. Without this, dsaResponseReady stays sticky after the first seek and BUTCHExec re-asserts GPU IRQ0 every halfline indefinitely (Primal Rage trace shows 2.9 M GPU IRQ0 firings across 6 K frames, all DSARX, while the game's poll-loop at \$0050E2 waits for game state that never advances because the GPU is permanently re-entering its DSARX handler). The fix doesn't (yet) flip Primal Rage from harness wedge to harness PASS — the GPU still parks at gpu_pc=\$F03060 long enough for the user- visible boot to reach the publisher logo and stall there. That's a separate bug; this commit is the correct hardware semantics regardless.
The fast blitter's DCOMPEN+!CMPDST path inhibited writes only where the source pixel was zero. The hardware data comparator (DATACOMP) also inhibits where the source equals PATTERNDATA -- the colour-key the accurate gate-level path already implements (dcomp = patd ^ srcd, see MiSTer comp_ctrl.v). Blits keying transparency on a non-zero colour drew their transparent pixels as a solid colour instead of letting the background show through. Add the PATTERNDATA equality test alongside the existing source-zero test (inhibit on srcdata == 0 || srcdata == PATTERNDATA) in both data-movement directions. The source-zero test is KEPT, so the pixel-level transparency the accurate path was augmented to match (develop's COMP_CTRL zero-mask) is preserved -- no regression for the source-zero sprite-blit family. BCOMPEN keeps its single-bit "transparent on 0" test. The COMP_CTRL comment is updated to note the fast path now keys on both zero and PATD. Repro: Theme Park's grass tiles key transparency on colour 0x04; with the fast blitter those pixels were drawn as CLUT[4] instead of being transparent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-and-dcompen-key Fix/blitter a2 wrap and dcompen key
…ename develop's DSP cleanup renamed DSPHandleIRQs to DSPHandleIRQsNP and removed the old export (docs/emulation-bug-hunt-todos.md:126). test_framework.h (added by the CD branch's own Tier 4 commits) still declared and LOAD_SYM'd the old name, producing a harmless "dlsym failed" WARN in test_butch_cd / test_cd_hle_boot on every run. The pointer was never called anywhere in the CD test suite, so this is dead code stranded by the merge -- drop the struct field and the LOAD_SYM line. make test: still exits 0, all subsystems 0 failed, no more DSPHandleIRQs warnings.
test/tools/cd_boot_matrix.sh runs every local CD title through test_cd_hle_boot and test_cd_bios_boot (3000 frames, 120s wall-clock cap per title x mode), classifies each run into the boot-stage taxonomy (LOAD_FAIL -> BIOS_INTRO -> BOOT_STUB -> GAME_CODE -> MENU -> IN_GAME) from the harnesses' own structured output (final_pc bands, PC-SET, crash_detect watchdog lines), and writes docs/cd-boot-matrix.md. Rows append incrementally with a resume guard (already-recorded title x mode combos are skipped) and CD_MATRIX_MAX_RUNS chunking, so an interrupted sweep picks up where it left off. Baseline (21 rows, 9 CUE x 2 modes + baldies.cdi x 2 + 1 ISO doc row): HLE 9 GAME_CODE + 1 LOAD_FAIL; BIOS 4 BIOS_INTRO, 1 BOOT_STUB, 1 GAME_CODE, 2 ambiguous BIOS-service-band, 1 pc_escape, 1 HARNESS_HANG, 1 LOAD_FAIL, 1 ISO BIOS_INTRO (documented .iso limitation). No title reaches a verified MENU/IN_GAME headlessly; every future CD fix must move rows forward, never backward. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-up to 529ed38, three fixes: 1. docs "How to re-run" now documents ALL env knobs -- CD_MATRIX_MAX_RUNS (the chunking cap that makes long sweeps survivable), CD_MATRIX_LOGDIR, CD_MATRIX_OUT -- plus the recommended chunked invocation pattern and how to force re-runs of specific rows (delete them; resume guard refills). 2. Ambiguous BIOS-service-band ($0-$3FFF) classification no longer claims a check that never ran: the harness only prints [PC-SET] when unique_pcs <= 32, so absence of the line now yields "PC-SET suppressed, unique_pcs>32 -- game handoff undetermined" instead of the false checked-negative "no game-band PCs seen". Iron Soldier 2 bios row (unique_pcs=34) re-run and corrected accordingly; Highlander bios row re-run with the explicit checked-negative wording. 3. GAME_CODE calls made from PC-SET are now self-auditing: game-band entries ($090000-$1FFFFF) are quoted in the row's evidence column. BrainDead 13 bios row re-run and now carries its disambiguating "PC-SET game-band: $124342 $124346 $12434C" inline. Also: moved the dated notes section above the results table so the resume guard's append-at-end always lands inside the table (the three re-run rows had been appended after the notes), and added a matching "## Results" heading to fresh-file generation. Only the three affected title x mode combos were re-run (resume guard); the other 18 rows are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two mechanisms account for all four BIOS-mode failure classes, each cited with trace lines / register counters / disasm addresses: - Mechanism A (classes 1+3, Primal Rage/Highlander CONFIRMED, IS2 confirmed root): seek completes but the BIOS never sets I2CNTRL bit 2 (i2s_fifo_enabled) -- writes $0001/$0011 at PC $365E -- so BUTCHExec's bit-2 FIFO-fill gate never opens and the guest polls forever. Guest never issues the write (stored correctly), not a dropped write; no GPU/DSP ISR sets it either. - Mechanism B (classes 2+4, Battle Morph/Baldies): 68K exception vectors are left PRNG garbage in CD-BIOS mode (jaguar.c:857 vector-stub block gated !useJaguarBIOS), so an exception at frame 437 vectors through garbage and pc-escapes. Pre-CD-read (seeks=0) -> wrong-LBA/TOC hypothesis rejected. Also: IS2 (hle) LBAs are correct (MSF->LBA maps into track 12) -- the loop is a jagcd_hle repeated-read continuation bug, not wrong-LBA. Docs-only; temporary cdrom.c trace counters used for evidence were reverted.
Review fixes to the Task 5 diagnosis section (docs-only): - CRITICAL: Mechanism A verdict split -- the FIFO-never-fills deadlock remains CONFIRMED (airtight), but attribution is now UNCONFIRMED: the write counters prove no processor wrote I2CNTRL bit 2, not that the guest never intended to. The CD BIOS uploads a GPU IRQ0 CD-ISR (jaguar.c:762 asserts IRQ0 to drive it), and the observed signature (556k IRQ0 fires, gpu_run=1, DSA response never consumed, zero I2CNTRL writes) equally fits that guest ISR stalling under our GPU/IRQ emulation. Removed the "rules out a GPU/DSP ISR" over-claim; added Task-6 candidate 1: disassemble the uploaded GPU CD-ISR and resolve this against JTRM/MiSTer BEFORE any FIFO-gate loosening. - IMPORTANT: frame-437 trigger is statically knowable -- prev_pc $8020A2 is CD BIOS ROM; static disasm of the exception class is a stated Task-6 requirement before trusting RTE-stub vector population (correct for an expected interrupt, a mask for a genuine fault). Noted the unexplained same-prev_pc / different-target detail (Battle Morph $8CA88044 vs Baldies $A7E08FA0). - MINOR: named the reverted [T5-DIAG] counter placement (common CDROMWriteWord/Byte handlers -- the all-processor choke point).
New ROM-free register-level test (synthetic 16-sector CUE/BIN): mimic the CD BIOS setup (G_FLAGS=INT_ENA1 only, BUTCH master+RBUF enable, DSA seek $10/$11/$12), tick BUTCHExec past the seek delay, and require G_CTRL INT_LAT1 latched with dispatch to $F03010 — the vector where the CD BIOS installs its CD-data ISR (JTRM: vector = int# x 16, int 1 = DSP/JERRY). Fails on current code (IRQ0 latched, permanently masked); fixed by the follow-up commit. Exports _CDIntf* in the TEST_EXPORTS ABI only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…O transfer engine now runs The CD BIOS's uploaded GPU CD-data ISR was waiting for GPU IRQ 1: its entry stub is installed at $F03010 (the IRQ1 vector — JTRM vector = int# x 16, int 1 = DSP/JERRY source), it enables only G_FLAGS INT_ENA1 ($20), acks via INT_CLR1, and re-arms the JERRY external latch (J_INT=$0101) in its epilogue — BUTCH's eint enters through JERRY (MiSTer butch.v line 83). BUTCHExec asserted GPUIRQ_CPU (IRQ0, vector $F03000) instead, which the BIOS never enables, so the latch stayed permanently masked: instrumented Primal Rage run showed zero GPU IRQ dispatches ever (latch=$01 vs enable=$02), zero GPU BUTCH reads, zero I2CNTRL writes. The ISR's I2CNTRL bit-2 setter (BSET #2 on its BUTCH-bit-13 branch) never executed — the Mechanism-A FIFO-never-fills boot deadlock (Primal Rage / Highlander / Iron Soldier 2, bios mode). One-line fix: GPUSetIRQLine(GPUIRQ_DSP, ASSERT_LINE). Post-fix, the ISR consumes the DSA $0100 response on its first dispatch (dsaIRQs 556791 -> 1), sets I2CNTRL bit 2 (i2sEn=1), and the FIFO fill/IRQ/drain engine streams sectors (block 116618 -> 118131 in 1500 frames). Matrix: 3 Mechanism-A rows + 2 control rows re-run. Targets now show a live transfer engine (Primal Rage gpu_pc=$F03010 in-ISR, Highlander fifo_drains 0 -> 54); stage advance past BOOT_STUB is blocked by a separate downstream mechanism (boot-stub sentinel scan never matches) documented in docs/cd-boot-matrix.md. Controls unchanged. The cdrom.c bit-2 FIFO gate itself matches butch.v (i2s_fifo_enabled, bit 2) and is untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d commit Review follow-up for 73dbc18 (Approved, 1 Important + 2 Minor): - HLE rows re-run (Primal Rage, Iron Soldier 2, BrainDead 13): all unchanged vs baseline — BUTCHExec ticks in HLE mode too, so the IRQ0->IRQ1 reroute needed HLE coverage; no spurious $F03010 dispatches. Rows re-recorded via the matrix resume guard. - Provenance note: the reroute functionally reverts 3279b30 (2026-04-29), whose "$F03000 = EXT1" premise was unverified and self-declared no-behaviour-change without the BUTCHExec tick. - BrainDead 13 (bios) control-row gpu_pc drift ($F03270 -> $F03276, same poll band) called out explicitly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ssing IRQ vector Task 6B carried out the REQUIRED instruction-level disasm before trusting RTE-stub vector population, and it overturns Mechanism B's premise for both target titles. The fault is a SYNCHRONOUS CPU trap the loaded boot stub executes on its own error path, not an async interrupt with an uninstalled handler: - Baldies: op $4AFC (68000 ILLEGAL) at $004D54, reached via `jsr $4E18; tst.l d0; bpl; ILLEGAL`. $4E18 is a TOC scanner over $2C08 that returns d0=-1 because our injected $2C00 TOC (src/cd/jagcd_bios.c) puts a zero first longword at $2C08 (session marker byte at +4) — the scanner's first `tst.l d2` sees 0 and bails. Root cause = TOC format (project_cd_toc_format.md), not vectors. - Battle Morph: op $AFDE (Line-A) at $0507E2, which is inside a DATA region ($78xx run) — PC derailed into data; uncharacterized derailment. prev_pc=$8020A2 is a red herring (once-per-frame sampling of a benign BIOS copy loop). Different escape targets = different vectors (4 vs 10) plus frame-granular sampling landing mid-runaway. Populating vectors MASKS: vector 4 = plain RTE returns to $004D54 and re-loops ILLEGAL forever (pc_escape -> wedge) while a "vectors populated" test passes (PR#170 pattern). Real fix = the $2C00 TOC layout, a separate mechanism. Task 6B: BLOCKED with artifacts, no src change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per Task 6B review (Approved, one Important gap): the Mechanism B section named only defect (1) (marker slot's zero first longword at $2C08 ends the $4E18 scan). Add: - Defect (2): no injected entry carries the byte[4] key the scanner matches (jagcd_bios.c writes only bytes [0..3] = [track][min][sec][frm]) — so un-zeroing the marker alone still returns d0=-1. - The format-model question (per-entry session/type byte at [4] vs standalone marker slot), to be resolved against MiSTer ground truth. - The two-scanner requirement (Baldies $4E18 occurrence-count scan AND Primal Rage $0803E2 marker-then-NEXT-entry scan) and the still-unknown found-path payload. - Preempt the jagcd_hle.c:934-949 skip-2 ILLEGAL-handler alternative: it equally masks (stub continues past its deliberate halt with d0=-1). - Crisp frame-437 statement: BIOS reaches the $050176 injection hook at ~frame 437; each stub faults within its first frame after injection. - Correct the RTE analysis to cover Battle Morph: Line-A (vector 10) also stacks the faulting PC on a 68000, so plain RTE re-executes $AFDE = wedge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The boot-stub $2C00 TOC scan was failing because our injected table used a sequential layout with a standalone session-marker slot, incompatible with what the game boot stubs (and the real BIOS DSP TOC writer) expect. Baldies' $4E18 scanner hit the marker's zero first longword ($2C08), treated it as end-of-table, returned -1, and executed a deliberate ILLEGAL halt; no track entry ever carried the byte[4] session key it matches against, so the scan could not succeed past the marker either. Boot-mode determination = outcome (c): $2C00 is PRNG RAM-fill garbage at the injection point (getenv-gated dump, reverted), i.e. the real BIOS DSP/TOC writer never runs under our auth-bypass + boot-stub-injection boot, so the injected table must itself be correct. Reconstructed the authoritative layout from the BIOS DSP/TOC writer (disassembled at ROM $808BE8 in the retail CD BIOS): - entry for track N at $2C00 + N*8 ($808CB4 lsl.w #3; $808CBE store byte[0]) - byte[4] = 0-based session number ($808CC2 move.b d7,$4(a0,d5.w)) - byte[1..3] = start MSF (full-TOC words $62/$63/$64) - $2C00 is a header (byte[2]=minTrack, byte[3]=maxTrack); the two boot-stub scanners start at $2C08 and skip it. This layout satisfies BOTH documented scanners by construction: - Baldies $4E18: scans from $2C08, terminates on a zero first longword, matches byte[4] against session key 1 (0-based data session). - Primal Rage $0803E2 (disassembled from the injected stub this task): finds the first byte[4]==1 entry, advances to the NEXT entry, reads its MSF -> now the faithful firstDataTrack+1 (was one track early). Fixed in both injectors: the real-BIOS path (jagcd_bios.c) and the HLE path's own HLEPopulateTOC (jagcd_hle.c), which carried the identical defect. TDD: new run-gated test/test_cd_toc_contract.c reads the live $2C00 out of RAM after boot and runs both scanner algorithms in C, asserting each lands on the data-session track (Primal Rage on the exact firstDataTrack+1 with matching MSF). RED (pre-fix): Baldies scan returns -1; $2C08 holds the zero-longword marker. GREEN (post-fix): both scanners pass on Baldies and Primal Rage, bios and HLE modes. Matrix: Baldies (bios) advances from HARNESS_HANG (ILLEGAL halt) to PASS in the BIOS CD-read poll band with the CD stream live; Primal Rage and Battle Morph (bios) unchanged (their blockers are downstream/distinct mechanisms, not the TOC scan); controls (BrainDead 13, Dragon's Lair, Highlander bios) and HLE rows (Primal Rage, IS2) byte-identical. make TEST_EXPORTS=1 test exit 0 (56103/56103); c89-lint clean. No IRQ routing, vector-stub, HLE +3-LBA, or savestate changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-up to the $2C00 TOC fix (evidence-quality, no behavior change): 1. Writer provenance: the $808BE8 routine previously labeled "the BIOS DSP/TOC writer" is a 68K routine in the BIOS ROM (all disassembled instructions are 68K; it polls BUTCH DSA responses directly at $DFFF0A), not the DSP code the BIOS uploads to $F1B000. The prior project belief (DSP code queries the drive via BUTCH $14xx) most plausibly describes a different stage of the same pipeline (DSP = drive-level transport; this 68K routine consumes the $60-$64 DSA words and builds $2C00), but the disasm does not confirm the DSP stage either way — stated honestly in docs/cd-boot-matrix.md. The layout's real validation anchors are the two independently disassembled game boot-stub scanners plus the reviewer-reproduced GREEN on the real Baldies disc; track-indexed vs the old sequential placement is byte-identical for contiguous track numbering, so the load-bearing fixes are the removed zero-longword marker slot and the byte[4] session stamp. Source comments in jagcd_bios.c / jagcd_hle.c / test_cd_toc_contract.c corrected (repo policy: no known-wrong hardware provenance in comments). 2. Contract-test scope: noted in the test header, at the assertion site, and in the matrix doc that the Primal Rage MSF assertion compares the injected table against CDIntfGetTrackInfo — the injector's own source — proving copy fidelity into the scanner-visible layout, NOT seek-target/LBA correctness (H1-class pregap/offset bugs would still pass), and that Primal Rage's marker-then-NEXT-entry path is not yet exercised end-to-end in-game (title blocked downstream). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ristic CD_read ($303C) is a discrete, stateless read: D0 (packed MSF) fully specifies the source position, A0/A1 the destination range, D1 the sync marker locked onto after the seek. A re-issued byte-identical CD_read re-seeks to the SAME position and reproduces the SAME data -- there is no per-call continuation pointer. (Holds under both readings of the real service: stateless-idempotent, or a BIOS that streams internally; a repeat with an identical MSF is a restart from the same position either way.) The removed heuristic (hle_next_lba = scanLBA + s writeback + the "repeated read -- resuming from LBA" block, extended by 4844f6b into the raw-fallback and counter-ID paths) was a tuned guess, not a disassembled contract -- its comment admitted it "mimics the I2S stream real hardware would still be feeding." The +3/call Iron Soldier 2 exhibited is just s = ceil(0x14FE / 2352) = 3 sectors per transfer. Trace, IS2 (hle), identical D0=$00320001 D1=$4438410B dest=$00CB00 every call: BEFORE: read 1 finds the sync block at LBA 224857; each identical repeat resumes +3 (224860, 224863, ...), misses the sync block, and streams raw garbage into $00CB00 -- corrupting the good first read. AFTER: every identical repeat re-finds the sync block at LBA 224857 and transfers the same correct bytes; dest is stable. RED/GREEN: test/test_cd_hle_idempotent.c counts destination-region content changes after first population -- 27 (RED) -> 0 (GREEN). Matrix (current HEAD, before vs after at matched frame budgets): IS2 hle, Baldies hle, Primal Rage hle/bios, BrainDead 13 hle, Dragon's Lair hle, Baldies bios all unchanged. bios rows structurally unaffected (heuristic is HLE-path only). IS2 first read now stays correct/idempotent but IS2 does NOT boot past the loop -- a downstream game-logic re-read remains (separate defect); not re-tuned to force a boot. "Baldies shares the heuristic" hypothesis NOT supported by the trace. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ge log level; run idempotency test in suite Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is an automated pull request to update from branch feature/jaguar-cd-support