10 Commits
Author SHA1 Message Date
Joseph MattielloandClaude Sonnet 5.5 05e3abb07a feat(app): tool window layout fixes; docs(debug): app-debug-tools reference, JTRM RGB16 layout, release notes
Palette/Object list/Blitter log/Audio scope default sizes and column widths fixed from
real-core screenshots on yarc (clipped A1/A2 and list columns, scope meters cut off).
The Object list no longer flashes a 'waiting' line at 10 Hz while live.
docs/app-debug-tools.md filled in (every tool, the register-write policy and always-blocked
addresses, texture limits, hardcore, multi-instance, F9/F10, GDB stub coexistence, zero cost
detached); app/README Debug tools section; jtrm-object-processor RGB16 is R 11-15, B 6-10,
G 0-5 (JTRM v8 p.13), not 5-6-5; v3.8.1 notes and WHATSNEW.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-13 21:29:41 -04:00
Joseph MattielloandClaude Sonnet 5.5 29998fee28 docs: JTRM errata reference and verified corrections to the distilled docs (#820)
Add docs/jtrm-errata.md: where the manual is wrong/inconsistent/hardware
differs (A), where it is silent and the answer came from netlists, Verilator,
MiSTer or games (B), Atari's own bug lists with a modeled? column (C), and the
doc mistakes we already paid for (D). Point docs/agent/hardware.md and
CLAUDE.md at it.

Correct, against JTRM Rev 8 / Rev 10 / Software Reference v2.4 / Hardware Bugs
& Warnings, the distilled docs that CLAUDE.md tells agents to trust:
- jtrm-gpu-dsp.md: 27 wrong opcode numbers (14 instructions missing), SH direction, MULT/IMULT
  signedness, PACK/UNPACK, DSP-only opcodes (SAT32S 42, MIRROR 48), DSP flags
  and ctrl layout (int 5 at bits 16/17, INT_LAT5 at bit 16), wave table
  geometry, BUS_HOG/DMAEN gotchas
- jtrm-register-map.md / jtrm-blitter.md: LFU encodings, PITCH 3, window width
  encoding, SRCZ1/SRCZ2 and the Rev 2.2 footnote, MEMCON1/MEMCON2 bit
  placement, ZMODE, GOURZ, G_HIDATA, D_FLAGS, misattributed bug list
- jtrm-clocks-timing.md: video timing and pixel-divisor tables vs v10 p.6/p.8,
  CLK1/2/3 formulas, MEMCON register table
- jtrm-jerry.md, jtrm-object-processor.md: wavetable entry count, STOPOBJ,
  unsourced VC=$FFFF claim
- comments only (no behaviour change): stale PIT clock comment in jerry.c,
  blitter status value comment, bcompen_basic.s "differs from JTRM" claim,
  test_texdump.c LFU label, and several tests that cited our own source as the
  hardware spec

Code that contradicts the manual (DSP DIV_OFFSET bit, STOP object interrupt
gating, test_texdump LFU constant) is recorded as follow-ups, not changed.

Refs #820

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-07 04:19:11 -04:00
Joseph MattielloandClaude Opus 5 1ae49088e8 docs: fix jtrm-*.md provenance -- stop citing source code as JTRM (#522)
21 of 23 `Source:` lines in docs/jtrm-*.md cited source files (e.g.
`src/jerry/jerry.c`) rather than the manual, so CLAUDE.md's claim that
these files "supersede source comments" was circular for most sections:
a wrong constant in the source could get echoed back as the manual's word.

Phase 1 (mechanical): relabel every source-code citation as
`Derived from: <file> -- NOT verified against the JTRM`, matching the
style of the two lines that already cited the manual correctly. Update
the docs/jtrm-*.md pointer block in CLAUDE.md and docs/agent/hardware.md
to say these files are a mix of manual- and source-derived material, and
add a provenance caveat to jtrm-register-map.md (no per-line Source: tags
at all).

Phase 2 (bounded verification against the JTRM PDFs, read as rendered
images): clocks/timing and interrupt/register sections only, per the
issue's own risk ranking.

- Upgraded to page citations (verified correct): PIT timer formula
  (jtrm-clocks-timing.md), DSP timer-interrupt routing and JERRY-to-TOM
  INT1 routing (jtrm-jerry.md), against Jaguar Software Reference Manual
  v2.4 pp. 14, 69-71, 79.
- Two disagreements found and reported inline, NOT silently fixed:
  (1) the CLK1/CLK2/CLK3 "system_clock / (2*(N+1))" divider formula is
  wrong -- those are PLL "Do NOT Modify" registers with a different
  formula (PCLKDIV/VCLKDIV = (N+1)*CHRDIV); the doc's formula is actually
  JERRY's unrelated SCLK register. Not a live emulation bug: the source
  never computes anything from these registers, matching hardware's "for
  information only" status.
  (2) the wavetable ROM entry count ("256 x 16-bit samples" per waveform)
  is wrong per both the manual and this repo's own `wavetable.c` comment
  ("Each entry has 128 values") -- docs-only error, source code already
  correct.

No content deleted; no src/ changes.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-20 23:00:31 -04:00
Joseph MattielloandClaude Opus 5 f810ef70be fix(op): OB exposes the object phrase LSW-first, not straight big-endian (#354)
Val d'Isere Skiing rendered its foreground snow as a flat, mottled band
with no perspective -- the reporter's "looks like a static image".

The ground is generated by the GPU's IRQ3 handler, which dispatches on
OB before doing anything else:

    $F030D0: movei #$00F00014, r0
    $F030E4: load  (r0), r0
    $F030F6: cmpq  #0, r0          ; signed 5-bit imm: field 0 means 0
    $F030FA: jump  Z, ($F03150)    ; -> perspective floor renderer

$F03150 reads VC and the table at $001368E0, does the per-line
perspective maths, and writes the per-scanline scaled-bitmap objects
into the display list at [$F03094].

OPSetCurrentObject() stored the phrase straight big-endian, so $F00014
returned the phrase's LOW long -- which always carries TYPE in bits 2-0
and so can never read zero.  The compare never matched and every IRQ3
fell through to a do-nothing epilogue.

Root cause, from the original Flare/Atari TOM design source
(jag_sim netlists/tom/OB.NET:55-67, "the first phrase can be read as
four words"):

    Ob0rd[0-2]   := TS (dr[0-2],  type[0-2],      ob0r);
    Ob0rd[3-13]  := TS (dr[3-13], ypos[0-10],     ob0r);
    Ob0rd[14-15] := TS (dr[14-15],newheight[0-1], ob0r);
    Ob1rd[0-7]   := TS (dr[0-7],  newheight[2-9], ob1r);
    Ob1rd[8-15]  := TS (dr[8-15], link[0-7],      ob1r);
    Ob2rd[0-10]  := TS (dr[0-10], link[8-18],     ob2r);
    Ob2rd[11-15] := TS (dr[11-15],data[0-4],      ob2r);
    Ob3rd[0-15]  := TS (dr[0-15], data[5-20],     ob3r);

OB is four 16-bit registers holding the phrase LSW-first:

    OB0 $F00010 = phrase[15:0]     OB2 $F00014 = phrase[47:32]
    OB1 $F00012 = phrase[31:16]    OB3 $F00016 = phrase[63:48]

Three non-circular checks pin that reading:

  * IODEC.NET:85-88 decodes ob0r..ob3r at low nibble $0/$2/$4/$6; the
    same file places Hcr_ at $F00004, Vcr_ at $F00006 and Obfw_ at
    $F00026, all matching the JTRM register map.
  * dr[0] is D0, not D15: vc[0] is the VC counter's LSB (carry chain
    vco[0-9] feeds Vc[1-10]) and Vcd[0-11] puts it on dr[0], while the
    JTRM has VC at bits 0-10 of $F00006.
  * OB.NET:71-75's write-back drives type[0-2]->wd[0-2] ...
    newdata[0-20]->wd[43-63], exactly the JTRM phrase layout, so
    type[n] is phrase bit n.

This supersedes the earlier attempt on this branch, which flipped the
long order on the strength of the game's behaviour alone and was
reverted because it settled a manual-ambiguous point by editing tests to
match the code.  The netlist settles it independently of any game; Val
d'Isere is now corroboration rather than the argument.  It also retracts
this branch's "MAME corroboration" doc commit: MAME's OB_HH/OB_LL naming
is an enum label in a reimplementation, and the design source
contradicts it.

The four tests that asserted the old layout are rederived from OB.NET
rather than flipped, each citing it.  op_short_branch reads TYPE from
the WORD at $F00010; the two GPU-int-object tests read OB2:OB3 at
$F00014 and expect the marker word-swapped ($0BADF00D -> $F00D0BAD),
deliberately keeping an asymmetric marker so it discriminates the order.

Verified at the pixel level, not just the counter: same input script,
same frame 1900, same 046 MPH -- before, a flat mottled band; after, a
snow field with ski tracks converging toward the horizon.

Known divergence recorded in the doc: real OB returns latched control
fields mixed with live counters (data[0-20] is a UPCNT, newheight comes
from write-back).  We latch the verbatim phrase.  No corpus title can
observe it -- every commercial GPU-object phrase carries DATA == 0.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-12 22:32:43 -04:00
Joseph MattielloandClaude Fable 5 b4556e67bb docs(jtrm-op): confirm OB is straight big-endian (MAME corroboration)
The JTRM never states which phrase bits land at which OB address, so the
byte order was settled against an independent implementation instead.
MAME names the four registers in address order in
src/mame/atari/jaguar_v.cpp:

    OB_HH(0x10),  OB_HL(0x12),  OB_LH(0x14),  OB_LL(0x16)

high-high at $F00010 through low-low at $F00016 -- straight big-endian,
bit 63 at $F00010 and bit 0 at $F00017. That agrees with
OPSetCurrentObject() as it already stands, with all four in-tree tests
that assert the layout, and with the direction Rev 8's general
big-endian convention points. The OB ordering is therefore NOT the cause
of issue #354, and the doc now says so explicitly so the flip attempted
in PR #424 is not retried.

Reframes the remaining puzzle as an open question rather than a settled
root cause: Val d'Isere's IRQ3 handler gates its per-scanline ground
generator ($F03150, which writes the scaled-bitmap objects into the
display list at [$F03094]) on `load ($F00014)` / `cmpq #0`, but $F00014
returns the phrase's low long, which always carries TYPE in bits 2-0 and
so can never read zero. Measured $87A; the generator never runs. Records
the candidates worth checking next -- the latched phrase, whether the
object should fire on those lines at all given OBJECT_TYPE_GPU ignores
YPOS, and the OPProcessScaledBitmap vertical-stepping hypothesis from
the issue -- plus the note that $F03098 is a red herring guarding a
line-buffer capture, not the floor.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-12 22:00:39 -04:00
Joseph MattielloandClaude Fable 5 4db591f860 revert(op): back out the OB long-order flip; JTRM does not settle it
Reverts the OPSetCurrentObject() change and all three test edits from
ed2bf9b/d6e6e20, restoring straight big-endian. Keeps only the
documentation, rewritten to record the open question honestly.

The coordinator asked the right question: which address holds bit 0 of
the object phrase, $F00010 or $F00016? Rev 8 does not answer it. Its
complete set of statements about OB is the register-table row, "these
four registers allow the graphics processor to read the current object",
the GPU-object note that DATA is "memory mapped as the object code
registers OB0-3", and "the interrupt service routine should read the
Object Code registers". None of them says which phrase bits land at
which address, and the manual's sample GPU ISR does not read OB. Rev 10
repeats only the table row.

What Rev 8 does say generally (p.131) is that the document "adopts the
big-endian convention", that big-endian "will see the high word of
long-word at the low address", and that for phrase data the left-most
pixel "always includes bit 63... in byte address terms this is stored in
byte 0" -- which, applied to OB, gives the straight big-endian layout
already implemented. That passage is about operands in memory and pixels
within a phrase rather than about mapping a register group, so it is an
inference, not a quote -- but it is the only thing the manual offers, and
it points at the existing code, not at my change. Flipping the order and
then editing three tests to match would have been settling a
manual-ambiguous point by making the tests agree with the code, so I have
backed it out instead.

The finding underneath is still real and is recorded in the doc: the
ground is generated by the GPU IRQ3 handler at $F03150, which writes the
per-scanline scaled-bitmap objects into the display list at [$F03094]
(this reconciles with the measurement on issue #354 that the ground is
drawn by OPProcessScaledBitmap). That handler is gated on
`load ($F00014)` / `cmpq #0`, and under straight big-endian $F00014
returns the phrase's low long, which contains TYPE in bits 2-0 and so can
never be zero for a valid object -- measured at $87A, floor never drawn.
Resolving that needs hardware or an independent implementation, not a
vote between our own tests and one game.

Also corrected in the doc, each verbatim from Rev 8 and independent of
the byte-order question: GPUOBJ has no LINK field (bits 14-63 are DATA;
"execution continues with the object in the next phrase"), BRANCHOBJ CC 0
and 1 were swapped and CC 4 was missing, and the OB row is RO not WO.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-12 21:53:24 -04:00
Joseph MattielloandClaude Fable 5 d6e6e20d03 docs(jtrm-op): record the OB DATA window; fix GPUOBJ/BRANCHOBJ tables
Follow-up to the #354 OB fix. The synthesized OP doc had no record of
which OB window holds what, so the next reader of jtrm-object-processor.md
would find nothing to stop them reintroducing the straight-big-endian
layout. It also carried two field-table errors, both checked against
JTRM Rev 8 ("Technical Reference v8.pdf" -- the one with a text layer;
the Software Reference and Sample Programs PDFs are image-only).

GPUOBJ (type 2): the doc listed bits 14-42 as unused and 43-63 as LINK.
Rev 8 has no LINK field on this object at all -- bits 14-63 are DATA,
"memory mapped as the object code registers OB0-3", and execution
continues with the object in the next phrase. Corrected, and a new
section records that the OP latches the phrase low long at $F00010 and
high long at $F00014, so DATA reads back at $F00014, with the note that
Rev 8 does not disambiguate which half of DATA lands in which window.

BRANCHOBJ (type 3): CC 0 and 1 were swapped (Rev 8: 0 = "YPOS == VC or
YPOS == 7FF", 1 = "YPOS > VC") and CC 4 ("second half of display line,
HC10 = 1") was missing. Rev 8's own table prints CC as bits 14-15, which
cannot hold five values; noted, since OPProcessList() decodes three bits.

The OB register row said WO; it is RO.

test_op_gpu_object's ob_holds() hard-coded straight big-endian and so
failed after the op.c fix. Updated to the corrected layout with the same
reasoning inline. This is the emulator's semantics changing and the
assertion following it, not a threshold being relaxed -- the test now
checks both longs rather than assuming a byte order.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-12 21:23:52 -04:00
Joseph MattielloandClaude Fable 5 2d1625cd17 docs(op): correct REMAINDER bit range, pin HSCALE/VSCALE direction
docs/jtrm-object-processor.md listed the scaled-object REMAINDER field as bits
16-31. The Jaguar Technical Reference Manual Revision 8 ("Scaled Bit Mapped
Object", Software Reference p.20) says bits 23-16, eight bits — so op.c's
`(p2 >> 16) & 0xFF` was correct and the doc was wrong.

Also records, verbatim from the same table, the thing the doc never stated and
which is easy to invert: HSCALE/VSCALE are DESTINATION PER SOURCE — "the number
determines how many pixels are written into the line buffer for each source
pixel" — so a value below $20 SHRINKS. Worked example added (IWIDTH=40 phrases
@ 8bpp with HSCALE=$10 renders 160px, not 640px), plus the REMAINDER algorithm
that pins the vertical direction and, by analogy, the horizontal one.

This is not cosmetic. While investigating #354 the bad REMAINDER entry sent me
looking for a nonexistent 16-bit field, and the absent direction note
independently led a reviewer to propose an inverted-HSCALE "fix". Both readings
are falsified by the manual; neither would have been proposed had the doc been
right.

Adds test/tools/op_list_dump.c, the OP display-list decoder built for that
investigation: dumps TYPE/YPOS/HEIGHT/XPOS/IWIDTH/DWIDTH/DEPTH/PITCH/HSCALE/
VSCALE/REMAINDER/DATA, raw phrases, and BRANCH link/cc. It separates "the OP is
misbehaving" from "the OP is faithfully drawing bad data the game wrote" —
opposite fixes — and on #354 established the latter. Indexed in the vj-debug
skill under "Pixels wrong". Skip-listed in c89-lint.sh alongside every other
harness-based tool (they build against test/harness/, outside $INCLUDES).

No src/ change; emulation behaviour is unaffected.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-12 19:51:54 -04:00
b8d4c7e90e docs: correct the CRY pixel field layout in jtrm-object-processor.md (#214)
* docs: correct the CRY pixel field layout in jtrm-object-processor.md

The file described CRY as C[15:8] intensity / R[7:4] / Y[3:0]. It is the
other way round: Cyan[15:12], Red[11:8], intensitY[7:0] — the acronym
spells the field order. Verified against src/tom/tom.c (cyan >>12,
red >>8, intensity & 0x00FF) and src/tom/op.c.

Found while investigating Battle Sphere's menu text, where the wrong
layout would have sent the reader hunting a nonexistent CRY decode bug.
These docs are the LLM-facing hardware reference, so an inverted field
layout actively misleads future accuracy work.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
2026-07-29 23:49:18 -04:00
Joseph MattielloandClaude Opus 4.6 c4e8ac6b1a docs: add distilled JTRM hardware reference for emulation development
Synthesized reference docs from the Jaguar Technical Reference Manual,
reorganized for emulation developers and LLM consumption. Covers clocks,
registers, GPU/DSP ISA, blitter, object processor, and JERRY subsystem.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-05-04 00:50:15 -04:00