Files
Joseph MattielloandClaude Fable 5 72470bb22b fix(acid): un-wedge pit_countdown_rate and unaligned_word
Both ROMs assembled and booted but never wrote a result signature, so
the acid suite reported them as NOT-RUN-YET on every build. Each was
violating a documented hardware contract; the emulator was right in both
cases, so the fix is in the test ROMs.

pit_countdown_rate: the IRQ handler acked TOM's INT1 copy but never
cleared JERRY's own pending latch. JERRY drives TOM INT1 bit 4 for as
long as an enabled source has an uncleared latch (tom.c:TOMPendingMask),
and m68k_execute re-presents that level-2 request at every instruction
boundary, so the 68K re-entered the handler forever: ~2200 IRQs/frame
with the busy-loop counter d2 frozen, i.e. 100% of the frame's 227k
cycles spent in exception entry plus handler. JINTCTRL's low byte is the
enable mask and its high byte is write-1-to-clear, so writing the enable
bit alone cleared nothing. Handler now writes IRQ2_TIMER1_CLR too.

The PIT period itself was never wrong: with the storm gone the test
measures 395 IRQs/sec against a [385, 426] window. Do not widen that
window if it drifts -- this test is the only acid coverage of the
dram_timing model, and it has already been churned once (209e5b2).

unaligned_word: address error is a group 0 exception and stacks 14
bytes, not 6. The handler treated SP+2 as the return PC (it is really
the fault address), bumped it, then RTE'd off the long frame -- popping
the special status word as SR and the fault address as PC, running away
into zeroed RAM. The 14-byte frame is deliberate (cpuextra.c:Exception,
issue #138 / Pitfall), so "fixing the emulator" would mean reverting a
real bug fix. Handler now discards the 8 extra bytes before RTE, and
additionally asserts the frame layout in place so a regression back to a
short frame reports a clean FAIL rather than a silent runaway --
verified by temporarily forcing a 6-byte frame, which yields
detail=0x2 observed=0x0080202a expected=0x00080001.

Also fixed, both of which let this class of failure hide:

- test/acid/Makefile: `make baseline` extracted the ROM path with
  awk '{print $1, $3}'. That works for the padded `[PASS       ]` and
  `[FAIL       ]` lines but not `[NOT-RUN-YET]`, which is unpadded and
  carries a trailing message -- field 3 there is part of the message, so
  the row was written without a .jag path, failed check-baseline.py's
  regex, and vanished from BASELINE.txt. The test then read as
  "NEW (OK)" forever, so a ROM that stopped booting looked green.
  Extract by pattern instead of field index.

  The recipe's `sort` is also now pinned to LC_ALL=C. It previously
  inherited the caller's locale while the committed BASELINE.txt was
  collated differently, so `make baseline` churned unrelated lines and
  buried the real change. BASELINE.txt is re-sorted once here to match;
  that hunk is ordering-only (verified set-identical), and the recipe
  now reproduces the file byte-for-byte.

- docs/jtrm-jerry.md: both JINTCTRL bit tables were off by one and
  omitted J_DSPENA/J_DSPCLR, so J_TIM1CLR read as bit 9 ($0200) when it
  is bit 10 ($0400). Since these docs supersede source comments, that
  would have reproduced the storm above. Added the clear-bit values and
  the handler-must-clear rule.

- test/acid/scripts/gen-jaguar-regs.py: emit IRQ2_*_CLR constants
  (enable << 8) so tests name the clear bit instead of hard-coding it.

BASELINE.txt's statuses are unchanged: it already recorded both as PASS,
and they pass again. Note the suite still has one unrelated,
deterministic regression, tests/op/op_gpu_int_object.jag (detail=1
observed=0x2800 expected=0x200), which predates this branch and is left
flagged rather than baselined away; it is fixed separately on
fix/acid-op-gpu-int-halted.

Verified: make test exits 0; both ROMs PASS on the production ABI and
the TEST_EXPORTS ABI; acid lint clean; no C sources touched.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-03 03:37:27 -04:00
..