Skip to content

Commit 83b2374

Browse files
JoeMattclaude
andcommitted
Add CD emulation docs and update CLAUDE.md
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]>
1 parent c7e0186 commit 83b2374

4 files changed

Lines changed: 316 additions & 2 deletions

File tree

CLAUDE.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Output binary name varies by platform:
2020
- Linux: `virtualjaguar_libretro.so`
2121
- Windows: `virtualjaguar_libretro.dll`
2222

23-
There is no test suite. CI runs `make -j4` on Ubuntu (GCC) and macOS (Clang).
23+
CI runs `make -j4` on Ubuntu (GCC) and macOS (Clang), plus screenshot regression tests via `test/regression_test.sh`. See `docs/test-infrastructure.md` for the full test harness inventory.
2424

2525
## Architecture
2626

@@ -56,12 +56,30 @@ Core options defined in `libretro_core_options.h` control blitter mode, BIOS usa
5656
- `src/` — emulator core (hardware chips, CPU, I/O, BIOS ROMs as C arrays)
5757
- `src/m68000/` — UAE-derived 68K CPU emulation
5858
- `libretro-common/` — shared libretro utility library (string, file, VFS)
59-
- `docs/` — original Virtual Jaguar documentation, changelog, known issues
59+
- `docs/` — documentation: changelog, known issues, BUTCH register map, CD data flow, test infrastructure
60+
- `test/tools` - Test scripts and headless front-ends
61+
- `test/roms` - Test roms, `private` sub dir has test commercial roms and bioses
6062

6163
### Build System
6264

6365
`Makefile` handles 30+ platform targets with auto-detection. `Makefile.common` lists all source files. Platform is selected via `platform=` variable or auto-detected from `uname`. Key flags: `-D__LIBRETRO__`, `-DMSB_FIRST` for big-endian platforms.
6466

67+
### Jaguar CD Emulation
68+
69+
CD support is implemented across `src/cdrom.c` (BUTCH chip / FIFO / DSA commands), `src/cdintf.c` (disc image loading: CUE/BIN, CHD, CDI), and hooks in `src/jaguar.c` (BIOS auth bypass, boot stub injection).
70+
71+
Key docs:
72+
- `docs/butch-registers.md` — full BUTCH register map ($DFFF00-$DFFF2F) with bit definitions
73+
- `docs/cd-data-flow.md` — how CD data moves from disc to RAM (I2S -> FIFO -> GPU ISR -> RAM), BIOS code map, boot stub layout
74+
75+
### Testing
76+
77+
See `docs/test-infrastructure.md` for all test harnesses:
78+
- `test/headless.py` — Python headless runner via libretro.py (screenshots, frame control)
79+
- `test/regression_test.sh` — screenshot regression suite with baseline comparison
80+
- `test/test_cd_boot.c` — low-level C harness with dlsym access to 68K registers and RAM
81+
- `test/sram_test.sh` — SRAM interface round-trip testing
82+
6583
### Known Limitations
6684

6785
- Blitter not fully cycle-accurate (some games need fast blitter mode)

docs/butch-registers.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# BUTCH Register Map ($DFFF00 - $DFFF2F)
2+
3+
Reference for the Jaguar CD BUTCH chip registers. Derived from MiSTer FPGA
4+
(`butch.v`, `butch_i2s.v`), MAME (`jaguar.cpp`), ChillyWilly JaguarLibs, and
5+
Atari Jaguar Technical Reference Manual.
6+
7+
## $DFFF00 - BUTCH (Interrupt Control Register, R/W)
8+
9+
### Write bits (longword)
10+
| Bit | Name | Description |
11+
|-----|------|-------------|
12+
| 0 | MASTER_EN | Master IRQ enable (must be 1 for any BUTCH interrupt) |
13+
| 1 | FIFO_EN | CD data FIFO half-full interrupt enable |
14+
| 2 | SUBFRAME_EN | CD subcode frame-time interrupt enable (~7ms at 2x) |
15+
| 3 | SUBMATCH_EN | Pre-set subcode time-match found interrupt enable |
16+
| 4 | TX_EN | CD module command TX buffer empty interrupt enable |
17+
| 5 | RX_EN | CD module command RX buffer full interrupt enable |
18+
| 6 | CIRC_EN | CIRC failure interrupt enable |
19+
| 17 | CD_RESET | CD reset |
20+
| 18 | BIOS_OVRD | CD BIOS override (BUTCH handles cart-space addresses) |
21+
| 19 | LID_RESET | CD open-lid reset |
22+
| 20 | CART_RESET | CD cartridge-pull reset |
23+
24+
### Read bits (longword)
25+
| Bit | Name | Description |
26+
|-----|------|-------------|
27+
| 9 | FIFO_HALF | CD data FIFO half-full (>= 8 entries) |
28+
| 10 | SUBCODE_PEND | Subcode frame pending |
29+
| 11 | FRAME_PEND | Frame pending (set if cdPlaying) |
30+
| 12 | TX_EMPTY | Command to CD drive pending (TX buffer empty if 1) |
31+
| 13 | RX_FULL | Response from CD drive pending (RX buffer full if 1) |
32+
| 14 | CD_ERROR | CD uncorrectable data error pending |
33+
34+
### Interrupt generation (from MiSTer butch.v)
35+
```
36+
eint = bit0 && (fifo_int || frame_int || sub_int || tbuf_int || rbuf_int)
37+
38+
fifo_int = bit9 && bit1 // FIFO half-full status AND enable
39+
frame_int = bit10 && bit2 // Frame status AND enable
40+
sub_int = bit11 && bit3 // Subcode status AND enable
41+
tbuf_int = bit12 && bit4 // TX empty status AND enable
42+
rbuf_int = bit13 && bit5 // RX full status AND enable
43+
```
44+
45+
## $DFFF04 - DSCNTRL (DSA Control Register, R/W)
46+
- Bit 16: Enable DSA bus
47+
- Reading clears bit 12 (TX buffer empty) in BUTCH status register
48+
49+
## $DFFF0A - DS_DATA (DSA TX/RX Data, R/W, 16-bit)
50+
51+
### DSA Commands (write)
52+
| Cmd | Description | Parameter |
53+
|-----|-------------|-----------|
54+
| $01nn | Play title | Track number (hex) |
55+
| $0200 | Stop | - |
56+
| $03nn | Read TOC | Session number |
57+
| $0400 | Pause | - |
58+
| $0500 | Pause release | - |
59+
| $10nn | Goto time (min) | Minutes (hex) |
60+
| $11nn | Goto time (sec) | Seconds (hex) |
61+
| $12nn | Goto time + start | Frames (hex, triggers seek) |
62+
| $14nn | Read long TOC | Session number |
63+
| $15nn | Set mode | Mode bits (bit 3 = CD-ROM mode) |
64+
| $18nn | Spin up | Session number |
65+
| $5000 | Get disc status | - |
66+
| $51nn | Set volume | Volume level |
67+
| $5400 | Get max session | - (returns session count) |
68+
| $70nn | Set DAC mode | Oversampling mode |
69+
70+
### DSA Responses (read)
71+
| Response | Description |
72+
|----------|-------------|
73+
| $0100 | Found (seek complete) |
74+
| $0200 | Stopped |
75+
| $03nn | Disc status |
76+
| $04nn | Error code |
77+
| $10nn | Current title (track number) |
78+
| $20nn-$24nn | TOC values: min track, max track, leadout M/S/F |
79+
80+
## $DFFF10 - I2CNTRL (I2S Bus Control Register, R/W)
81+
| Bit | Name | Description |
82+
|-----|------|-------------|
83+
| 0 | I2S_DRIVE | I2S drive enable (I2S output from BUTCH active) |
84+
| 1 | I2S_JERRY | I2S path to Jerry enabled |
85+
| 2 | FIFO_EN | FIFO enabled (gates samples into software-readable FIFO) |
86+
| 3 | MODE_16 | 16-bit mode (vs 32-bit I2S word format) |
87+
| 4 | FIFO_NE | FIFO not empty (read-only, `wptr != rptr`) |
88+
89+
Writing bit 2 high in CD-ROM mode triggers `splay` (playback start).
90+
91+
## $DFFF14 - SBCNTRL (Subcode Control, R/W)
92+
Reading clears pending subcode and frame interrupts.
93+
94+
## $DFFF18 - SUBDATA (Subcode Data A, R)
95+
## $DFFF1C - SUBDATB (Subcode Data B, R)
96+
Sub-Q channel data.
97+
98+
## $DFFF20 - SB_TIME (Subcode Time + Compare Enable, R/W)
99+
100+
## $DFFF24 - FIFO_DATA / I2SDAT1 (I2S FIFO Data, R)
101+
## $DFFF28 - I2SDAT2 (I2S FIFO Data, R)
102+
103+
Both addresses read from the **same 16-deep circular FIFO**. Each entry is a
104+
32-bit word (left+right 16-bit samples). The BIOS reads by alternating between
105+
$DFFF24 and $DFFF28 -- each read pops one 32-bit entry.
106+
107+
The BIOS reads 8 longwords per interrupt (16 word-reads = 32 bytes of data).
108+
109+
## $DFFF2C - EEPROM (NM93C14 EEPROM Interface, R/W)
110+
| Bit | Name | Description |
111+
|-----|------|-------------|
112+
| 0 | CS | Chip Select |
113+
| 1 | SK | Clock |
114+
| 2 | DO | Data Out (to EEPROM) |
115+
| 3 | DI | Data In / Busy (from EEPROM, read-only) |

docs/cd-data-flow.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Jaguar CD Data Flow
2+
3+
How CD data gets from disc to main RAM. Derived from MiSTer FPGA core,
4+
MAME, and BIOS disassembly.
5+
6+
## Interrupt Path
7+
8+
```
9+
BUTCH eint --> Jerry external interrupt 0 --> 68K IRQ2 / GPU IRQ0 / DSP EXT0
10+
```
11+
12+
Jerry routes `eint` to both the 68K interrupt controller (via J_INTCTRL
13+
$F10020) and the DSP external interrupt inputs (via D_FLAGS $F1A100 EXT0ENA).
14+
15+
The BIOS typically configures a **GPU ISR** to handle CD data transfers. The
16+
68K sets G_DSPENA in G_FLAGS so the GPU receives the interrupt from Jerry.
17+
18+
## I2S Data Path: Disc -> FIFO -> RAM
19+
20+
1. **CD mechanism** sends audio/data frames to BUTCH over a serial bus
21+
2. **BUTCH transport** buffers 8-byte chunks in a 4-deep 64-bit FIFO,
22+
deserializes at 44.1kHz into 16-bit samples via the I2S serializer
23+
3. If I2CNTRL bit 2 is set, each sample pair is written into the
24+
**16-deep 32-bit software FIFO** (`i2s_fifo[0:15]`)
25+
4. When FIFO fill >= 8, bit 9 (FIFO_HALF) asserts in BUTCH status
26+
5. If bits 0+1 (master + FIFO IRQ enable) are set, `eint` asserts
27+
6. **Jerry external interrupt 0** fires -> **GPU ISR** activates
28+
7. GPU ISR reads 8 longwords alternating $DFFF28/$DFFF24 -> stores to RAM
29+
8. Each read pops one 32-bit entry; FIFO drops below half -> `eint` deasserts
30+
9. BUTCH continues filling; when half-full again, cycle repeats
31+
32+
## CD_read BIOS Function Sequence
33+
34+
### Phase 1: Setup (68K)
35+
1. Write I2CNTRL ($DFFF10) = $07 (I2S drive + Jerry path + FIFO enable)
36+
2. Write BUTCH ($DFFF00) = $03 (master IRQ + FIFO half-full IRQ enable)
37+
3. Configure Jerry I2S as slave via SMODE ($F1A154)
38+
4. Load GPU ISR into GPU RAM for FIFO drain
39+
5. Enable GPU with DSP interrupt input (G_DSPENA in G_FLAGS)
40+
41+
### Phase 2: Seek (68K -> DSA)
42+
6. Write DS_DATA: $10mm (goto minutes), $11ss (goto seconds), $12ff (goto frames)
43+
7. $12ff triggers the actual seek; BUTCH queues $0100 response when complete
44+
8. Optional: $15nn to set CD-ROM mode (bit 3)
45+
46+
### Phase 3: Playback (BUTCH internal)
47+
9. When I2CNTRL bit 2 transitions 0->1 in CD-ROM mode, BUTCH starts `splay`:
48+
pre-fills internal FIFO, enables I2S serializer, transport begins
49+
50+
### Phase 4: Data Transfer (continuous loop)
51+
10. BUTCH fills 16-deep FIFO at I2S rate (~22us per entry)
52+
11. FIFO fill >= 8 -> bit 9 set -> `eint` asserts
53+
12. GPU ISR fires, reads 8 longwords from $DFFF28/$DFFF24
54+
13. Stores to target RAM buffer, advances CD_ptr
55+
14. Repeats until requested byte count reached
56+
57+
### Phase 5: Completion
58+
15. 68K monitors CD_ptr to know when read is complete
59+
16. Game sends $0200 (STOP) through DS_DATA
60+
61+
## BIOS RAM Code Map
62+
63+
| ROM Range | RAM Range | Size | Purpose |
64+
|-----------|-----------|------|---------|
65+
| $802000-$8042A6 | $050000+ | 9KB | BIOS RAM-resident code |
66+
| $8084A6-$808E90 | $003000+ | 2.5KB | BIOS jump table |
67+
| $808E90-$81421C | $080000+ | 23KB | CD Player UI fallback |
68+
| $81421C-$82F1C8 | $192000+ | 110KB | BIOS service routines |
69+
70+
Entry: Cart populator at $802000 copies all of the above, then JMPs to $0500D6.
71+
BIOS runs auth, then `JSR $00080000` at PC=$050176 (boot stub or CD Player).
72+
73+
## BIOS Jump Table ($003000)
74+
75+
6-byte entries: BRA.W + NOP. Key entries:
76+
- Entry 13 ($304E -> $3610): CD_read -- the function games call to read CD data
77+
78+
## Boot Stub Layout (Session 2 Track, sector 0, after word-swap)
79+
80+
```
81+
+0x000-0x041: Sync preamble (0xD7 0x72 "ATRI"... repeated)
82+
+0x042-0x061: "ATARI APPROVED DATA HEADER ATRI " (32-byte magic)
83+
+0x062-0x065: Load address (big-endian, typically $00080000)
84+
+0x066-0x069: Length (big-endian)
85+
+0x06A onward: M68K boot loader code
86+
```
87+
88+
## References
89+
90+
- [MiSTer Jaguar CD_latest](https://github.com/MiSTer-devel/Jaguar_MiSTer/tree/CD_latest) - butch.v, butch_i2s.v
91+
- [MAME jaguar.cpp](https://github.com/mamedev/mame/blob/master/src/mame/atari/jaguar.cpp)
92+
- [Jaguar Technical Reference Manual](https://www.hillsoftware.com/files/atari/jaguar/jag_v8.pdf)
93+
- [AtariAge CD BIOS threads](https://forums.atariage.com/topic/254145-cd-bios-questions/)

docs/test-infrastructure.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Test Infrastructure
2+
3+
## headless.py - Python Libretro Test Harness
4+
5+
Primary headless test script using [libretro.py](https://github.com/JesseTG/libretro.py).
6+
7+
### Setup
8+
```bash
9+
python3.12 -m venv .venv-libretropy
10+
source .venv-libretropy/bin/activate
11+
pip install 'libretro.py[cli]'
12+
```
13+
14+
### Usage
15+
```bash
16+
python test/headless.py <content.cue|.j64> [--frames N] [--cd-bios retail|dev] [--screenshot output.ppm]
17+
```
18+
19+
### Capabilities
20+
- Runs core completely headless (no GUI)
21+
- Configurable frame count (default 600)
22+
- Screenshots as PPM files
23+
- Platform auto-detection (darwin/linux/win32)
24+
- Stderr/stdout capture for debug logging
25+
26+
## regression_test.sh - Screenshot Regression Testing
27+
28+
Uses [miniretro](https://github.com/davidgfnet/miniretro) for automated
29+
screenshot comparison against baselines.
30+
31+
### Usage
32+
```bash
33+
./test/regression_test.sh ./virtualjaguar_libretro.dylib
34+
```
35+
36+
### Features
37+
- ImageMagick `compare` for pixel-diff measurement
38+
- Baseline PNGs in `test/baselines/`
39+
- Visual diff generation on failures
40+
- Determinism verification (runs each ROM twice)
41+
- Frameskip invariance testing
42+
- Save state round-trip validation
43+
44+
## test_cd_boot.c - Low-Level C Harness
45+
46+
Direct libretro API testing with hardware-level diagnostics via dlsym access
47+
to internal functions.
48+
49+
### Build & Run
50+
```bash
51+
cc -o test/test_cd_boot test/test_cd_boot.c -ldl
52+
./test/test_cd_boot roms/private/game.cue 600
53+
```
54+
55+
### Capabilities
56+
- `m68k_get_reg()` -- read 68K registers (D0-D7, A0-A7, PC, SR, SP)
57+
- `TOMReadWord()` / `JERRYReadWord()` / `CDROMReadWord()` -- hardware registers
58+
- `GetRamPtr()` -- direct RAM access
59+
- Frame hashing, PC sampling, vector inspection
60+
61+
## sram_test.sh - SRAM Interface Testing
62+
63+
Tests libretro SRAM interface for save game handling.
64+
65+
```bash
66+
./test/sram_test.sh ./virtualjaguar_libretro.dylib
67+
```
68+
69+
## CI Integration
70+
71+
GitHub Actions workflow (`.github/workflows/regression-test.yml`) runs
72+
`regression_test.sh` and `sram_test.sh` on Linux x64, Linux ARM64, macOS ARM64.
73+
Uploads diff artifacts on failure and comments on PRs.
74+
75+
## Directory Layout
76+
77+
```
78+
test/
79+
headless.py # Python libretro.py harness
80+
regression_test.sh # Screenshot regression suite
81+
sram_test.sh # SRAM interface test
82+
test_cd_boot.c # CD boot diagnostics (C)
83+
test_blitter_simd.c # SIMD blitter test (C)
84+
baselines/ # Reference PNG screenshots
85+
roms/ # Test ROMs (private/ is git-ignored)
86+
tools/ # Test ROM generators, SRAM test harness
87+
cd_trace_*.log # Debug logs from CD boot tests
88+
```

0 commit comments

Comments
 (0)