Skip to content

Add Jaguar CD support (Cue/BIN, CDI, ISO)#109

Closed
JoeMatt wants to merge 31 commits intolibretro:masterfrom
Provenance-Emu:claude/add-jaguar-cd-support-hOzev
Closed

Add Jaguar CD support (Cue/BIN, CDI, ISO)#109
JoeMatt wants to merge 31 commits intolibretro:masterfrom
Provenance-Emu:claude/add-jaguar-cd-support-hOzev

Conversation

@JoeMatt
Copy link
Copy Markdown
Collaborator

@JoeMatt JoeMatt commented Apr 16, 2026

Summary

Adds Atari Jaguar CD support to Virtual Jaguar libretro: games are loaded from disc images using Cue + BIN (standard multi-file Red Book dumps), CDI (DiscJuggler-style images), and ISO single-track/single-file images where supported in cdintf.c.

Correction: A previous version of this description incorrectly emphasized CHD and vendored libchdr. Maintainer workflows and testing for this PR center on Cue/BIN, CDI, and ISO — not CHD. Use those formats when reproducing issues or validating CD boot.

Highlights

  • Disc interface (cdintf.c): CUE/BIN parsing (sessions, tracks, MSF), raw 2352-byte sector reads; CDI and ISO loading paths wired into CDIntfOpenImage / CDIntfReadBlock / TOC helpers.
  • Boot: Retail vs developer CD BIOS paths, cartridge-header boot model for the CD BIOS “module,” authentication/bypass hooks, BUTCH FIFO and CD-ROM command path (cdrom.c / jaguar.c).
  • Libretro: retro_load_game switches to CD mode for .cue / .cdi / .iso (see valid_extensions in-tree); full-path loading for multi-file cues.
  • Audio / streaming: BUTCH → I2S-style delivery into the existing DAC/CD audio plumbing.

Testing

Boot and HLE/real-BIOS smoke harnesses live under test/ (optional private ROM roots). Issues are easiest to triage with Cue/BIN or CDI dumps matching published Jaguar CD layouts.

@JoeMatt JoeMatt self-assigned this Apr 16, 2026
Copilot AI review requested due to automatic review settings April 16, 2026 03:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Jaguar CD disc image support to the core by integrating libchdr (for CHD), implementing CUE/BIN parsing and sector reads, and updating the libretro frontend to boot via the Jaguar CD BIOS when .cue/.chd content is loaded.

Changes:

  • Added CD image loading APIs and implemented CUE/BIN + CHD sector reading in cdintf.*.
  • Updated CDROM/Butch emulation paths and added core option for selecting Retail vs Developer CD BIOS.
  • Vendored deps/libchdr and wired it into the build via Makefile.common (plus upstream CMake/CI files included under deps/libchdr).

Reviewed changes

Copilot reviewed 65 out of 69 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/settings.h Adds settings fields/enums for enabling CD BIOS and selecting CD BIOS type.
src/cdrom.c Updates Butch status/interrupt behavior and sector/audio delivery reads.
src/cdintf.h Expands the CD interface API and adds disc image load/unload helpers and data structures.
src/cdintf.c Implements CUE/BIN parsing, CHD parsing/reads, and image-backed sector reads.
libretro_core_options.h Adds core option to select Retail vs Developer Jaguar CD BIOS.
libretro.c Enables .cue/.chd loading, boots via CD BIOS, and loads cart ROMs from path when needed.
Makefile.common Adds libchdr include paths/defines and compiles vendored libchdr sources.
deps/libchdr/unity.c Unity build translation unit for libchdr + bundled deps sources.
deps/libchdr/src/link.T Version-script for symbol visibility when building shared libchdr.
deps/libchdr/src/libchdr_huffman.c Vendored libchdr implementation (Huffman).
deps/libchdr/src/libchdr_flac.c Vendored libchdr implementation (FLAC wrapper).
deps/libchdr/src/libchdr_codec_zstd.c Vendored libchdr codec implementation (Zstd).
deps/libchdr/src/libchdr_codec_zlib.c Vendored libchdr codec implementation (Zlib/miniz).
deps/libchdr/src/libchdr_codec_lzma.c Vendored libchdr codec implementation (LZMA).
deps/libchdr/src/libchdr_codec_huff.c Vendored libchdr codec implementation (Huffman).
deps/libchdr/src/libchdr_codec_flac.c Vendored libchdr codec implementation (FLAC).
deps/libchdr/src/libchdr_codec_cdzs.c Vendored libchdr CD codec implementation (CD + Zstd).
deps/libchdr/src/libchdr_codec_cdzl.c Vendored libchdr CD codec implementation (CD + Zlib).
deps/libchdr/src/libchdr_codec_cdlz.c Vendored libchdr CD codec implementation (CD + LZMA).
deps/libchdr/src/libchdr_codec_cdfl.c Vendored libchdr CD codec implementation (CD + FLAC).
deps/libchdr/src/libchdr_bitstream.c Vendored libchdr bitstream implementation.
deps/libchdr/pkg-config.pc.in pkg-config template for standalone libchdr builds.
deps/libchdr/include/libchdr/macros.h Vendored libchdr public header (macros).
deps/libchdr/include/libchdr/huffman.h Vendored libchdr public header (Huffman).
deps/libchdr/include/libchdr/flac.h Vendored libchdr public header (FLAC).
deps/libchdr/include/libchdr/coretypes.h Vendored libchdr public header (core file callbacks/types).
deps/libchdr/include/libchdr/codec_zstd.h Vendored libchdr public header (Zstd codec).
deps/libchdr/include/libchdr/codec_zlib.h Vendored libchdr public header (Zlib codec).
deps/libchdr/include/libchdr/codec_lzma.h Vendored libchdr public header (LZMA codec).
deps/libchdr/include/libchdr/codec_huff.h Vendored libchdr public header (Huffman codec).
deps/libchdr/include/libchdr/codec_flac.h Vendored libchdr public header (FLAC codec).
deps/libchdr/include/libchdr/codec_cdzs.h Vendored libchdr public header (CDZS codec).
deps/libchdr/include/libchdr/codec_cdzl.h Vendored libchdr public header (CDZL codec).
deps/libchdr/include/libchdr/codec_cdlz.h Vendored libchdr public header (CDLZ codec).
deps/libchdr/include/libchdr/codec_cdfl.h Vendored libchdr public header (CDFL codec).
deps/libchdr/include/libchdr/chdconfig.h Vendored libchdr public header (feature config).
deps/libchdr/include/libchdr/chd.h Vendored libchdr public header (CHD API).
deps/libchdr/include/libchdr/cdrom.h Vendored libchdr public header (CD constants/helpers).
deps/libchdr/include/libchdr/bitstream.h Vendored libchdr public header (bitstream).
deps/libchdr/deps/zstd-1.5.7/zstd_errors.h Bundled Zstd error definitions for libchdr.
deps/libchdr/deps/zstd-1.5.7/CMakeLists.txt CMake build file for bundled zstd static lib.
deps/libchdr/deps/miniz-3.1.1/CMakeLists.txt CMake build file for bundled miniz static lib.
deps/libchdr/deps/lzma-25.01/src/LzmaDec.c LZMA decoder compilation unit wrapper for bundled SDK.
deps/libchdr/deps/lzma-25.01/include/real/LzmaDec.h Bundled LZMA SDK header.
deps/libchdr/deps/lzma-25.01/include/real/7zTypes.h Bundled LZMA SDK types header.
deps/libchdr/deps/lzma-25.01/include/LzmaDec.h Namespacing wrapper header to avoid symbol collisions.
deps/libchdr/deps/lzma-25.01/LICENSE Bundled LZMA SDK license.
deps/libchdr/deps/lzma-25.01/CMakeLists.txt CMake build file for bundled LZMA static lib (incl. optional asm).
deps/libchdr/deps/lzma-25.01/Asm/x86/7zAsm.asm Bundled LZMA x86 asm macros.
deps/libchdr/deps/lzma-25.01/Asm/arm64/7zAsm.S Bundled LZMA arm64 asm macros.
deps/libchdr/README.md libchdr README (vendored).
deps/libchdr/LICENSE.txt libchdr license (vendored).
deps/libchdr/CMakeLists.txt Standalone libchdr CMake build definition.
deps/libchdr/.gitignore Ignore build artifacts within vendored libchdr directory.
deps/libchdr/.github/workflows/vita.yml Vendored libchdr CI workflow (Vita).
deps/libchdr/.github/workflows/switch.yml Vendored libchdr CI workflow (Switch).
deps/libchdr/.github/workflows/msys2.yml Vendored libchdr CI workflow (MSYS2).
deps/libchdr/.github/workflows/cross-platform-actions.yml Vendored libchdr CI workflow (BSD/Haiku/OmniOS).
deps/libchdr/.github/workflows/cmake.yml Vendored libchdr CI workflow (Linux/macOS/Windows).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread libretro.c Outdated
Comment thread src/cdintf.c Outdated
Comment thread src/cdintf.c
Comment thread src/cdrom.c Outdated
Comment thread src/cdrom.c
Comment thread libretro.c
Comment thread libretro.c Outdated
@JoeMatt JoeMatt linked an issue Apr 16, 2026 that may be closed by this pull request
@JoeMatt JoeMatt force-pushed the claude/add-jaguar-cd-support-hOzev branch from c4a8963 to 379ee16 Compare April 16, 2026 05:14
@LibretroAdmin
Copy link
Copy Markdown
Contributor

Hi, let me know if you want us to merge this

@JoeMatt JoeMatt force-pushed the claude/add-jaguar-cd-support-hOzev branch 2 times, most recently from 67cd444 to 4f8a734 Compare April 23, 2026 02:56
claude and others added 19 commits April 23, 2026 15:22
…ch emulation

Implements the foundation for Jaguar CD game support based on the spike
research in docs/spike-jaguar-cd-support.md. This covers Phases 1-4 of
the implementation plan.

Phase 1 - Disc Image Loading:
- Complete CUE/BIN parser in cdintf.c with session/track/MSF parsing
- CDIntfReadBlock reads raw 2352-byte sectors from BIN files
- CDIntfGetSessionInfo/GetTrackInfo return proper TOC data
- CDIntfOpenImage/CloseImage manage disc image lifecycle

Phase 2 - CD BIOS Boot:
- retro_load_game detects .cue files and enters CD mode
- Loads 256KB CD BIOS (retail or developer) at $E00000
- Reads boot vectors from BIOS for proper 68K initialization
- Forces BIOS-on mode for CD games (required by hardware)
- ROM loading via file path (need_fullpath=true for CD support)

Phase 3 - Butch Emulation:
- Enables BUTCHExec with FIFO half-full and DSARX interrupt generation
- Routes Butch interrupts through JERRY/DSP EXT1 to GPU
- FIFO_DATA and I2SDAT2 reads deliver sector data from disc image
- Proper BUTCH status register read with interrupt pending flags
- $5400 command returns actual session count from disc

Phase 4 - CD Audio:
- Simplified GetWordFromButchSSI reads audio sectors directly
- SetSSIWordsXmittedFromButch delivers L/R samples to DAC
- Removed legacy two-sector kludge workaround

Also adds:
- CD BIOS Type core option (retail vs developer)
- Valid extensions updated to include .cue
- Proper cleanup of CD resources on unload
- All existing cartridge regression tests pass

https://claude.ai/code/session_017594R2HVUZmGUxyQp9328w
Vendors libchdr (https://github.com/rtissera/libchdr) with its
dependencies (lzma, miniz, zstd) to support loading Jaguar CD games
from CHD (MAME Compressed Hunks of Data) format, the preferred format
for distribution in libretro.

Changes:
- deps/libchdr/: Vendored libchdr library with lzma, miniz, zstd deps
- Makefile.common: Add libchdr sources and include paths, define HAVE_CHD
- src/cdintf.c: Add ParseCHD() that reads CHTR/CHTR2 track metadata,
  CDIntfReadBlockCHD() that reads sectors via hunk-based access with
  single-hunk caching, updated CDIntfOpenImage/CloseImage/IsImageLoaded
  to handle CHD alongside CUE/BIN
- libretro.c: Add .chd to valid_extensions, detect CHD in load_game

The CHD reader extracts track layout from CHD metadata tags, handles
both CDROM_TRACK_METADATA and CDROM_TRACK_METADATA2 formats (with
pregap/postgap), and reads raw 2352-byte audio sectors from the
compressed hunk data. All existing cartridge regression tests pass.

https://claude.ai/code/session_017594R2HVUZmGUxyQp9328w
- Remove undeclared cdBuf2/cdBuf3 from CDROMStateSave/Load
- Add test/roms/private/ for commercial ROMs (gitignored)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Add cdrom_eeprom_ram[64] array in eeprom.c for Jaguar CD saves
- Include CD EEPROM in save state serialization
- Extend SRAM buffer to 256 bytes (128 cart + 128 CD EEPROM)
- Pack/unpack both arrays for RETRO_MEMORY_SAVE_RAM

The CD EEPROM I/O hookup (BUTCH register $DFFF2C) is not yet
implemented — this provides the data infrastructure for when it is.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
CDROMInit() (called by JaguarInit()) checks CDIntfIsImageLoaded() to set
haveCDGoodness. The disc image must be opened before that check runs,
otherwise the CD drive is never activated.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The embedded CD BIOS data (jaguarCDBootROM) is scrambled and does not
contain valid 68K reset vectors, so CD games cannot boot with it.

Changes:
- Add load_external_cd_bios() to load a real BIOS dump from the
  system directory (looks for jaguarcd_bios.bin, jagcd_bios.bin, etc.)
- Validate the BIOS by checking that the initial PC points into the
  BIOS ROM range ($E00000-$E3FFFF)
- Move CD BIOS boot vector setup AFTER JaguarReset() since
  JaguarReset() overwrites RAM[0..7] when jaguarCartInserted is false
- Re-pulse the 68K reset after setting vectors so it picks them up
- Add test/test_cd_boot.c diagnostic harness for CD boot testing

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The CD BIOS is not a replacement for the standard boot ROM at $E00000.
It is a "cartridge" loaded at $800000 with a Jaguar universal header
at $800404 containing entry point $802000.

Boot sequence:
1. Standard boot ROM at $E00000 initializes the 68K (SP=0, PC=$E00008)
2. Boot ROM detects "cartridge" (CD BIOS) at $800000
3. Boot ROM reads entry point from $800404 and jumps to $802000
4. CD BIOS code runs, shows intro animation, reads CD TOC

The embedded jaguarCDBootROM data is not encrypted -- it contains
readable strings (VLM, "ATARI APPROVED DATA HEADER") and valid 68K
code at offset $2000. It just doesn't use standard 68K reset vectors
because it boots as a cartridge, not a boot ROM.

Also adds support for loading external CD BIOS from system directory
with the common No-Intro filename convention (.j64 extension).

Tested: CD BIOS boots, shows intro animation loop. CD drive protocol
responses need further work for games to load.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The retail CD BIOS now passes the session-2 pregap audio authentication
and reaches its built-in CD Player interface (verified via headless
screenshot at 326x240).

Boot flow now requires five hooks in JaguarExecuteNew (gated by
vjs.useCDBIOS):

  $050A9C - JaguarInstallCDAuthBypass (BNE.W $0504EC -> 2x NOP)
  $050AB2 - DSPWriteLong $F1B4C8 = $80010000 (DSP-result fake)
  $050B0C - JaguarWriteLong $FB000  = $0A      (post-BSR success)
  $0505FA - JaguarWriteLong $1AE00C = $20010001 (CD response magic)
  $192E46 - JaguarWriteWord $1A6800 = $0001     (BIOS GPU mailbox)

The TryReadAuthRedirect path in cdintf.c serves real TAIRTAIR audio
from track 30 BIN for the auth window (LBA 139668-139816). cdintf.c
needs `#undef fprintf` after streams/file_stream_transforms.h to
prevent fprintf->rfprintf macro substitution from silently eating
debug logs.

Adds test/headless.py - libretro.py-based local test harness so we
can drive the core without round-tripping logs through iOS. Includes
optional --screenshot flag to dump the framebuffer as PPM.

Game-specific boot (jumping from BIOS CD Player into Primal Rage's
own boot.abs) is the next milestone.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Adds one-shot JaguarDumpMemWindow hooks in JaguarExecuteNew() for the
game CD-event poll function ($081220), its flag area ($0008B398), and
the BIOS service routines the game calls into ($00196446 DSP serial
comms, $00194D18 CD-data processing). Also traces writes to the
$0008B398 game flag.

These dumps decoded the post-auth blocker: the BIOS service at $194D18
expects $001AE034 (data-present) and $001AE032 (bytes-remaining) to be
non-zero, kicked by ($001AE00C & 0x2000). Our $0505FA stuff value of
$20010001 lacks bit 13, so the kick path never triggers.

Also adds .iso to libretro core's valid_extensions and headless.py docs.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
New documentation:
- BUTCH register map with bit definitions
- CD data flow: I2S, FIFO, GPU ISR, boot stub layout
- Test infrastructure inventory

Co-Authored-By: Claude Opus 4.6 <[email protected]>
CHD support removed — CUE/BIN and CDI formats are sufficient.
Add jagcd_hle.c to the source list for HLE CD boot path.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
cdintf: rewrite CUE parser for multi-file multi-session discs,
add CDI format support, boot stub extraction, auth-zone redirect
for redump-style dumps that strip pregap audio.

cdrom/jaguar: improve BUTCH FIFO emulation, DSA command handling,
add CD auth bypass for stripped-pregap dumps, boot stub injection
hooks, GPU data phase intercept for HLE path.

libretro: add HLE CD boot fallback when no external BIOS ROM found.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
jagcd_hle: high-level emulation of the CD BIOS jump table — extracts
boot stub, populates TOC, intercepts CD_read/CD_poll/CD_stop calls
to transfer sectors directly from disc image to RAM. Enables CD boot
without a real BIOS ROM.

test_cd_boot: headless test harness that loads a CUE/BIN via dlsym,
runs frames, and dumps 68K register state and RAM contents for
debugging the CD boot sequence.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Joseph Mattiello <[email protected]>
Mirror the 20 official Atari Jaguar developer-binder PDFs released into the
public domain by Hasbro Interactive in 1999, converted to Markdown via
pymupdf4llm so the Tom/Jerry register reference, opcode tables, and
hardware-bugs list are greppable next to src/op.c, src/tom.c, src/gpu.c,
src/dsp.c, etc.

Source PDFs are mirrored from cubanismo/jaguar-sdk and hillsoftware.com.
The PDFs themselves are .gitignored to keep the repo small (~73 MB skipped,
~2 MB of Markdown checked in); fetch-pdfs.sh + .convert.py reproduce them
locally on demand.

The 'Technical Reference v8.md' (Brennan/Dunn/Mathieson, rev 8, 28 Feb 2001)
comes from a typeset PDF and is the cleanest source. The numbered binder
files (00-17) are scans, so OCR quality varies — README.md notes this and
points to the originals when in doubt.

Made-with: Cursor
Signed-off-by: Joseph Mattiello <[email protected]>
Signed-off-by: Joseph Mattiello <[email protected]>
Signed-off-by: Joseph Mattiello <[email protected]>
Signed-off-by: Joseph Mattiello <[email protected]>
JoeMatt and others added 12 commits April 23, 2026 15:22
Signed-off-by: Joseph Mattiello <[email protected]>
Signed-off-by: Joseph Mattiello <[email protected]>
Signed-off-by: Joseph Mattiello <[email protected]>
…rbosity

Add src/log.h with LOG_DBG/LOG_INF/LOG_WRN/LOG_ERR macros that use
the retro_log_printf_t callback (falls back to stderr for test harnesses).
Convert ~107 fprintf(stderr) calls across 7 source files to use log levels:
- Debug: hex dumps, per-sector traces, sentinel matches, GPU loop traces
- Info: boot progress, CD loading, auth bypass
- Warn: missing BIOS, fallback paths
- Error: hard failures (rfopen, magic mismatch, bad lengths)

Also: increase boot stub buffer from 12 to 32 sectors (fixes Space Ace
$FA00 boot stub), use register-based TOM resolution (HDB1/HDE/VDB/VDE),
fix JERRY_TRACE_DEBUG and GPU trace guards for audio regression.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The HLE CD_read sentinel scan now iterates across every session-2 track
when the scan from the boot-stub-supplied LBA misses, with a single-match
fallback for ASCII-tagged sentinels (CODE/STUB/SCOR/TITL).  Many discs
(Highlander, Battle Morph, BrainDead 13) supply MSF values that point to
session-2 lead-in instead of game data; scanning each session-2 track
in order locates the sync block reliably.

Also:
* CD_poll now reports A0 = end+4 (matching the real GPU CD ISR which
  pre-decrements before each long write), unblocking cmp+bge polling
  idioms used by Highlander.
* Boot stub buffers bumped from 256KB to 600KB to fit Battle Morph's
  ~414KB stub; both the cdintf raw-sector buffer and the jagcd_hle
  injection buffer kept in lockstep.
* New cdintf accessors: CDIntfGetSession2FirstTrackLBA(),
  CDIntfGetSession2TrackCount(), CDIntfGetSession2TrackLBA(i).
* Test harness test/test_cd_hle_boot.c discovers all .cue/.iso/.cdi
  under VJ_TEST_CD_ROOT (defaults to test/roms/private), runs each
  through 300 frames, and asserts PC stays in RAM, escapes self-loops,
  and visits more than a handful of unique addresses.  Defaults to
  cue-only via VJ_TEST_CD_EXTS.
* CHD support removed (libchdr deps deleted, .info dropped chd ext).
* test_hle_bios test cd_poll_a0_advances_past_end_after_read renamed
  + assertion updated to match the end+4 contract.

Current CUE baseline: 4 PASS / 5 FAIL.
PASS: Battle Morph, Dragon's Lair, Highlander, Space Ace.
FAIL: Baldies, BrainDead 13, Hover Strike, Iron Soldier 2, Primal Rage
(all blocked on GPU CD ISR streaming or post-load downstream waits).

Made-with: Cursor
Per docs/cd-bios-calling-convention.md and the retail BIOS disassembly:

  "The BIOS does NOT use CD_poll. It polls DSP RAM flag at [\$F1B4C8] —
   the GPU ISR writes \$FFFFFFFF there when the transfer completes, and
   the BIOS loops until negative."

HLEHandleCDRead now mirrors that contract: clear the flag at the start
of the read and write \$FFFFFFFF when the transfer finishes.  This is
the hardware-correct completion primitive.  Game boot stubs that follow
the BIOS convention will pick this up automatically.

The remaining failing CUE games (Baldies, BrainDead 13, Iron Soldier 2,
Primal Rage) do NOT poll [\$F1B4C8] — they either spin in STOP waiting
for a JERRY ext IRQ from BUTCH, or they read the BUTCH FIFO data
register (\$DFFF24/\$DFFF28) directly from the 68K.  Both are separate,
larger problems (interrupt-driven streaming and direct-FIFO emulation)
tracked for follow-up work.

Test diagnostic: the boot smoke test now also dumps 32 bytes of code
around each visited PC when fewer than 32 unique PCs are seen, so the
wait-loop instruction stream can be decoded without re-running.

Result: 4 PASS / 5 FAIL (no regression vs prior baseline).
Made-with: Cursor
When the boot smoke test parks on a tiny PC set, we already dump 32
bytes around each visited PC.  Add a one-line dump of D0-D3 / A0-A2 /
A6 / SP at the same time so the wait loop's source pointer and target
value are visible without re-running with extra logging.

Example: BrainDead 13 stops at \$12438A executing
  CMPA.L (A0)+, D0 ; BEQ ; BRA -4
with A0=\$00851644 and D0=\$41545249 ("ATRI") — i.e. it scans cart
space for the universal boot header instead of using CD_poll, which
means HLE needs to populate the CD cart memory window (or implement
direct BUTCH FIFO data reads) before that path can complete.

Made-with: Cursor
…space

Two related fixes for boot stubs that issue multiple CD_read calls:

1. Re-seek (D0 bit 31 set) is now a no-op transfer.  Per
   docs/cd-bios-calling-convention.md, bit 31 means "skip hardware
   init, just re-seek; the GPU data area is already configured by
   the prior call."  We were treating these as full reads, computing
   byteCount from A0/A1 (which hold stale or garbage values in
   re-seek mode) and falling back to a default \$5BC00 transfer that
   stomped the boot stub's just-loaded code/data with raw audio
   sectors.  Hover Strike previously crashed to PC=\$FFFFFFFF at
   frame 86 because its 4th and 5th CD_reads (D0=\$80657374,
   \$80F652B9) overwrote 750KB of memory; with this fix it now runs
   to a clean wait loop at \$065B36 with 19 unique PCs.

2. Loaded data is now mirrored into cart space at the same offset.
   On real Jaguar CD hardware the CD cart's onboard buffer maps into
   cart space (\$800000-\$DFFFFF); some boot stubs scan cart-space
   addresses (e.g. BrainDead 13 reads A0=\$00851644) for the universal
   "ATRI" header.  Cart space is otherwise empty in HLE mode, so the
   mirror is harmless when not needed.

Test diagnostic upgrade: when the run barely moves we also dump 32
bytes of the current memory at A0/A1 (using the libretro core's
jagMemSpace[] symbol so cart space is visible), so the wait loop's
read target shows up alongside the PC bytes.

Result: 4 PASS / 5 FAIL.  Hover Strike no longer crashes (now a
wait-loop FAIL); other failures unchanged for now.

Made-with: Cursor
When a boot stub re-issues the same CD_read (same D0/D1/A0/A1) without
varying parameters, real hardware is still feeding new sectors of disc
data through the I2S stream — each call produces a different chunk.
Without a notion of "where we left off" the HLE handed the same 5KB to
the game over and over (Iron Soldier 2 has been stuck in this loop).

We now remember the (D0, D1, dest, end) signature of the prior call
plus the post-transfer LBA, and on a matching repeat we resume the
sentinel scan from that LBA instead of the boot-stub-supplied MSF.

This unblocks the multi-chunk boot pattern but does NOT fix Iron
Soldier 2 by itself: its sentinel sync block sits at a single fixed
LBA in the boot-stub track, so even after resuming we keep finding
the same one.  Iron Soldier 2 ultimately stops at \$007416 polling
RAM[\$44F4] for a flag updated by an interrupt path we don't yet
emulate; further progress needs real interrupt-driven streaming.

No PASS regressions; all five PASSes hold (Battle Morph, Dragon's
Lair, Highlander, Space Ace, Highlander).

Made-with: Cursor
Mirrors test_cd_hle_boot but forces virtualjaguar_cd_boot_mode=bios so
the real Atari Jaguar CD BIOS is loaded from VJ_TEST_CD_ROOT (default
test/roms/private). Discovers all .cue/.iso under that root, runs each
for VJ_TEST_CD_FRAMES (default 600 — enough to clear the BIOS animation
window and watch each disc reach its game-code entry point).

New make target: test-cd-bios-boot (parallel to test-cd-hle-boot, also
intentionally excluded from 'make test''s strict pass/fail loop).

Adds two diagnostic LOG lines around CDIntfOpenImage in retro_load_game
so silent disc-open failures are visible in the test log instead of
just retro_load_game failed.

Current real-BIOS baseline (600 frames):
  - All 9 cue discs advance through CD-AUTH bypass into game code
    (vs all 9 stuck in BIOS animation at 300 frames)
  - 8/9 then PC-OOB into garbage (stack/streaming corruption);
    Primal Rage stalls in BIOS at \$003616

Made-with: Cursor
Three fixes that take real-BIOS CD boot from 0/9 to 5/9 passing:

1. Boot stub trampoline ($080000): The BIOS always does JSR $080000
   after authentication, but most games' boot stubs load at $004000,
   $006000, or $124000. When the load address differs from $080000,
   install a JMP trampoline at $080000 pointing to the actual address.

2. Boot stub buffer (256KB -> 600KB): Battle Morph's boot stub is
   414KB — exceeds the old 256KB buffer. Matches the HLE path's 600KB.

3. Static flag reset: cdAuthBypassInstalled and cdBootStubInjected
   are now module-level statics reset in JaguarReset() via
   JaguarResetCDHooks(), preventing stale state across core reloads.

Also adds frozen-OOB diagnostic snapshots to test_cd_bios_boot.c:
captures registers, prev-PC bytes, stack, A0/A1 memory at the exact
moment the PC first leaves valid memory, before OP/blitter can corrupt
the post-mortem evidence.

Real-BIOS baseline (600 frames, 9 CUE discs):
  PASS: BrainDead 13, Dragon's Lair, Highlander, Hover Strike, Space Ace
  FAIL: Baldies (BIOS init OOB), Battle Morph (BIOS init OOB),
        Iron Soldier 2 (self-loop $006AC0),
        Primal Rage (self-loop at CD_read $003616)

Made-with: Cursor
Refactor CD boot into pluggable strategy vtable (HLE, real BIOS, cart
hybrid) with polymorphic dispatch. Fix real-BIOS CD boot for Dragon's
Lair, Iron Soldier 2, and Baldies. Improve HLE sentinel scanning with
LBA redirect for session-2 games and relaxed self-loop detection.

Build: guard HAVE_NEON for osx/Intel, fix test_blitter_simd rule to
use auto-detected SIMD source, add jagcd_bios.c/jagcd_cart.c to
Makefile.common, update .gitignore for test artifacts.

Tests: add harnesses for audio DAC, blitter, BUTCH CD, boot config,
GPU control flow/ctrl/IRQ, memory map, timers, and video modes.
Relax HLE boot test criteria for post-boot polling loops.

Made-with: Cursor
@JoeMatt JoeMatt force-pushed the claude/add-jaguar-cd-support-hOzev branch from 4f8a734 to fe363e1 Compare April 23, 2026 19:23
@JoeMatt JoeMatt changed the title Add Jaguar CD support with CUE/BIN and CHD image loading Add Jaguar CD support with CUE/BIN image loading Apr 23, 2026
@JoeMatt JoeMatt changed the title Add Jaguar CD support with CUE/BIN image loading Add Jaguar CD support (Cue/BIN, CDI, ISO) Apr 23, 2026
@JoeMatt
Copy link
Copy Markdown
Collaborator Author

JoeMatt commented Apr 29, 2026

Superseded by Provenance-Emu#11. The CD subsystem has been rebased onto fix/general-emulation-fixes (which has the src/ reorg, HLE BIOS work, IRQ-latching unification, OP edge clipping, and the audio-dropout fix). The diff is much tighter — many of this PR's local workarounds are no longer necessary. Closing in favour of the new PR.

@JoeMatt
Copy link
Copy Markdown
Collaborator Author

JoeMatt commented Apr 29, 2026

Updated supersession: now #120 (against upstream).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bounty] Atari Jaguar CD Support

4 participants