Handoff: green-light script, reconciled figures, a smaller first step for the decoder

Prepares session 4 for handoff. No new measurements; this reconciles the docs
with what session 4 changed and makes the next session's entry point cheaper.

- tools/bench/check.sh re-runs both display regression tests from the Blu-ray in
  ~40 s and prints ALL GREEN. Verified green cold, after wiping tmp/ and
  re-extracting. STATUS and README both open with it, because everything
  downstream assumes the display path is pixel-exact and nothing previously
  checked that in one command.

- Reconciled the figures session 4 invalidated. Session 3's 38.88 dB ceiling is
  struck through in STATUS with a pointer to 40.81; the "three facts the player
  must honour" table no longer quotes R20 = 0x0116, which was the 768-wide IPL
  timing and would have been copied into the player as if it were the shipping
  value. crtc_mode.lua is now named as the single source of truth for CRTC
  registers, in both STATUS and README.

  The 38.88 dB in the session-3 reproduce section is left alone and annotated
  instead: it is correct for that test, which still packs I = 1. The two numbers
  disagree for a reason and a reader should be able to see which is which.

- Next-step 2 now leads with something smaller than "write the decoder": a dumb
  full-frame RAM->GVRAM blit in 68000 code, timed. That number alone confirms or
  kills the 38% estimate, and needs no bitstream, codebooks, or DLX1 parsing.
  The reference image and its checker already exist.

- Parked the user's Cliff Hanger / Lupin III follow-on in STATUS so it is not
  lost and not mistaken for scheduled work. Cheaper than this project on every
  axis except media prep, which is where it would actually stall.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
prosolis
2026-08-23 13:31:11 -07:00
parent 34f9ee341d
commit 7ba979a236
3 changed files with 95 additions and 11 deletions
+7 -1
View File
@@ -6,6 +6,9 @@ This is fundamentally a **video codec problem**, not a game-logic problem: the
game logic is a scene table with branching input windows; the difficulty is game logic is a scene table with branching input windows; the difficulty is
pushing ~22 minutes of Don Bluth animation through a 10MHz 68000. pushing ~22 minutes of Don Bluth animation through a 10MHz 68000.
**Green-light check:** `./tools/bench/check.sh` (~40 s, needs the Blu-ray
mounted) re-runs both display regression tests and prints `ALL GREEN`.
## Read first ## Read first
- **`docs/FINDINGS.md`** — measured hardware facts, content statistics, codec - **`docs/FINDINGS.md`** — measured hardware facts, content statistics, codec
decision, and a section on measurement traps that produced three separate decision, and a section on measurement traps that produced three separate
@@ -23,7 +26,10 @@ docs/ findings, status, hardware reference
tools/analysis/ measurement scripts, numbered in the order they were written tools/analysis/ measurement scripts, numbered in the order they were written
(01/02 marked BROKEN deliberately, kept as regression refs). (01/02 marked BROKEN deliberately, kept as regression refs).
Run from the repo root — they import from tools/encoder/. Run from the repo root — they import from tools/encoder/.
tools/bench/ MAME Lua injection harness + 68000 benchmark sources tools/bench/ MAME Lua injection harness + 68000 benchmark sources.
`check.sh` re-runs both display regression tests (~40 s).
`crtc_mode.lua` is the single source of truth for CRTC R00-R08
and R20 — do not write CRTC values anywhere else.
tools/vasm/ vasm m68k assembler (built from source) tools/vasm/ vasm m68k assembler (built from source)
tools/encoder/ hybrid VQ encoder + DLX1 container writer (working) tools/encoder/ hybrid VQ encoder + DLX1 container writer (working)
src/player/ 68000 player (not yet written) src/player/ 68000 player (not yet written)
+58 -10
View File
@@ -1,4 +1,14 @@
# Status & next-session handoff — session 4 in progress (2026-08-23) # Status & next-session handoff — end of session 4 (2026-08-23)
## Start here: is the tree still green?
```
./tools/bench/check.sh
```
~40 s, needs the Blu-ray mounted. Re-runs both display regression tests from
source media and prints `ALL GREEN`. Verified green at end of session 4.
If it fails, fix that before doing anything else — everything downstream assumes
the display path is pixel-exact.
## Decisions locked ## Decisions locked
@@ -65,14 +75,14 @@ rate-distortion curve, not two codecs.
MAME 0.277 source**, not folklore: the palette word format, the 1024-byte MAME 0.277 source**, not folklore: the palette word format, the 1024-byte
GVRAM line stride, and the 256-colour page aliasing in `HARDWARE.md`. All GVRAM line stride, and the 256-colour page aliasing in `HARDWARE.md`. All
three were already written down correctly; they are now cited. three were already written down correctly; they are now cited.
4. **A new quality ceiling was measured** — the 15-bit+I palette alone costs 4. ~~**A new quality ceiling was measured** — the 15-bit+I palette alone costs
38.88 dB, the same order as the `scsi` profile's own codec error. FINDINGS 38.88 dB.~~ **Superseded by session 4:** that figure assumed the shared LSB
22.4. This bounds how much further `scsi` is worth raising. `I` is always 1. Chosen per entry, the ceiling is **40.81 dB**. FINDINGS 23.3.
5. **Two shell traps that wedged session 2's background jobs** are documented in 5. **Two shell traps that wedged session 2's background jobs** are documented in
the working-setup section below. They cost ~1.5 h of wall clock and a wedged the working-setup section below. They cost ~1.5 h of wall clock and a wedged
CPU core, and one of them was hit again this session. CPU core, and one of them was hit again this session.
## What session 4 settled (in progress) ## What session 4 settled
1. **A real 256x256 CRTC mode exists and is verified.** `crtc_mode.lua`, derived 1. **A real 256x256 CRTC mode exists and is verified.** `crtc_mode.lua`, derived
from `x68k_crtc.cpp`'s divisor ladder rather than recalled — the derivation is from `x68k_crtc.cpp`'s divisor ladder rather than recalled — the derivation is
@@ -220,9 +230,14 @@ Three facts the player MUST honour, none of which were guessable:
| what | where | value | | what | where | value |
|---|---|---| |---|---|---|
| **Un-hide the graphics layer** | CRTC R20 `$E80028` | clear bit 11 ("G-VRAM set to buffer"); IPL leaves `0x0B16` | | **Un-hide the graphics layer** | CRTC R20 `$E80028` | clear bit 11 ("G-VRAM set to buffer"); IPL leaves `0x0B16` |
| Colour setup (256c) | CRTC R20 bits 9-8 | `0x0100` -> `R20 = 0x0116` | | Colour setup (256c) | CRTC R20 bits 9-8 | `0x0100` |
| **Monitor contrast** | `$E8E001` bits 3-0 | IPL leaves **14**; write **15** or everything renders 7% dark | | **Monitor contrast** | `$E8E001` bits 3-0 | IPL leaves **14**; write **15** or everything renders 7% dark |
The `R20 = 0x0116` value quoted here in session 3 is the **768-wide IPL timing**
with the gate cleared. The shipping value is **`R20 = 0x0110`** — see the mode
table in `tools/bench/crtc_mode.lua`, which is now the single source of truth
for all of R00-R08 and R20.
Bit 11 is the one that cost the most time: GVRAM writes land and read back Bit 11 is the one that cost the most time: GVRAM writes land and read back
correctly while the layer is invisible, so the video controller looks guilty and correctly while the layer is invisible, so the video controller looks guilty and
is not. Contrast `0` blanks the screen — free fade-to-black for transitions. is not. Contrast `0` blanks the screen — free fade-to-black for transitions.
@@ -231,10 +246,11 @@ Palette format is now **confirmed from MAME source**, not assumed:
`GGGGGRRRRRBBBBBI` (G 15:11, R 10:6, B 5:1, shared LSB I), expanded as `GGGGGRRRRRBBBBBI` (G 15:11, R 10:6, B 5:1, shared LSB I), expanded as
`pal6bit((field<<1)|I)`. With contrast at 15 the render is **pixel-exact**. `pal6bit((field<<1)|I)`. With contrast at 15 the render is **pixel-exact**.
New ceiling: the 15-bit+I palette alone costs **38.88 dB** against the 24-bit Ceiling: the 15-bit+I palette costs **40.81 dB** against the 24-bit palettised
palettised source — the same order as the `scsi` profile's own codec error source, once `I` is chosen per entry (FINDINGS 23.3 — session 3's 38.88 dB
(39.4 dB). `scsi` is close to display-transparent on real hardware. See assumed `I = 1`). Still the same order as the `scsi` profile's own codec error
FINDINGS 22.4 before considering raising quality further. (39.4 dB), so `scsi` remains near display-transparent, with ~2 dB more headroom
than session 3 thought.
Snapshot recipe that works (`-video none` CANNOT snapshot): Snapshot recipe that works (`-video none` CANNOT snapshot):
``` ```
@@ -261,6 +277,20 @@ SDL_VIDEODRIVER=dummy mame x68000 -bios ipl10 -video soft -window \
validates the 38% full-frame blit estimate the whole CPU budget rests on. validates the 38% full-frame blit estimate the whole CPU budget rests on.
**This is now the top priority** — it is the only remaining unknown that can **This is now the top priority** — it is the only remaining unknown that can
still invalidate the design. still invalidate the design.
**Concrete first step, deliberately smaller than "write the decoder":** do
not start by parsing `DLX1`. Start by making 68000 code do the dumbest
possible full-frame blit — copy 256x192 bytes from RAM to GVRAM through the
mode set by `crtc_mode.lua` — and time it with the existing Lua harness.
That single number either confirms or kills the 38% estimate, and it needs
no bitstream, no codebooks, and no container parsing. `show_frame256.lua`
already produces the exact reference image to diff the result against, and
`verify_frame256.py` already knows how to check it. Only once that number is
in hand is it worth writing the mode dispatch.
Assembler: `tools/vasm/vasmm68k_mot -Fbin -o out.bin in.s`. The harness
pattern for loading and running 68000 code is in `tools/bench/one.lua` and
`tools/bench/bench.lua` (working, from session 1).
2a. ~~CRTC mode table for 256x192-in-256x256.~~ **DONE, session 4.** Derived from 2a. ~~CRTC mode table for 256x192-in-256x256.~~ **DONE, session 4.** Derived from
the CRTC divisor ladder (not recalled), verified by snapshot, pixel-exact. the CRTC divisor ladder (not recalled), verified by snapshot, pixel-exact.
`tools/bench/crtc_mode.lua`; write-up in FINDINGS 23; regression test `tools/bench/crtc_mode.lua`; write-up in FINDINGS 23; regression test
@@ -323,6 +353,8 @@ mkdir -p tmp/snap_verify && cd tmp && SDL_VIDEODRIVER=dummy mame x68000 -bios ip
cd .. && python3 tools/bench/verify_frame.py cd .. && python3 tools/bench/verify_frame.py
``` ```
Verified cold from the Blu-ray at end of session 3: exact match, 38.88 dB. Verified cold from the Blu-ray at end of session 3: exact match, 38.88 dB.
(That 38.88 is correct *for this test*: `show_frame.lua` still packs `I = 1`.
The 40.81 dB ceiling comes from `show_frame256.lua`, which picks `I` per entry.)
`tmp/` is gitignored scratch. The frames are NOT in the repo — regenerate them `tmp/` is gitignored scratch. The frames are NOT in the repo — regenerate them
with `extract.py`; the earlier ones lived in `/tmp` and do not survive a reboot. with `extract.py`; the earlier ones lived in `/tmp` and do not survive a reboot.
@@ -336,3 +368,19 @@ with `extract.py`; the earlier ones lived in `/tmp` and do not survive a reboot.
six register-poking attempts failed against a gate that one grep found. six register-poking attempts failed against a gate that one grep found.
- Blu-ray mounted at `/media/reala-misaki/BDROM` via - Blu-ray mounted at `/media/reala-misaki/BDROM` via
`udisksctl loop-setup -r -f DRAGONS_LAIR.iso`. `udisksctl loop-setup -r -f DRAGONS_LAIR.iso`.
---
## Parked ideas (not scheduled, not abandoned)
- **Cliff Hanger, retitled as Lupin III** (user, session 4). Stern's 1983
laserdisc game was cut from *Castle of Cagliostro* and *Mystery of Mamo* with
the Lupin branding stripped; a port would restore it. Technically **cheaper
than this project**: same content class (cel animation, flat colour, hard
cuts), ~13 min of footage vs Dragon's Lair's ~22, and flatter linework than
Bluth's, so fewer blocks should escape to V4/RAW. The codec, the display path,
and `crtc_mode.lua` would all drop straight in.
**The real cost is media prep, not code:** there is no clean master cut to
Stern's scene boundaries the way `DRAGONS_LAIR.iso` is, so the footage would
have to be sourced and cut to match. Not to be started until the CPU path is
proven — it changes nothing about whether this design works.
+30
View File
@@ -0,0 +1,30 @@
#!/bin/bash
# Green-light check: re-runs both display regression tests from the Blu-ray.
# ~40 s. Run from the repo root. Any non-zero exit means something drifted.
set -e
cd "$(dirname "$0")/../.."
[ -d /media/reala-misaki/BDROM ] || {
echo "Blu-ray not mounted. udisksctl loop-setup -r -f DRAGONS_LAIR.iso"; exit 2; }
python3 tools/encoder/extract.py 00020 tmp/fr_00020 12 crop
mkdir -p tmp/snap_verify tmp/snap256
run() { # run <script> <snapdir>
rm -f "tmp/$2/x68000"/*.png
( cd tmp && SDL_VIDEODRIVER=dummy timeout -k 5 120 mame x68000 -bios ipl10 \
-video soft -window -sound none -nothrottle -plugins \
-autoboot_script "../tools/bench/$1" \
-snapshot_directory "./$2" -snapview native -seconds_to_run 6 >"$2.log" 2>&1 )
}
echo "--- session 3: 768-wide IPL timing (FINDINGS 22) ---"
python3 tools/bench/prep_frame.py tmp/fr_00020 tmp/frame.bin 0
run show_frame.lua snap_verify
python3 tools/bench/verify_frame.py
echo "--- session 4: real 256x256 mode (FINDINGS 23) ---"
python3 tools/bench/prep_frame.py tmp/fr_00020 tmp/frame256.bin 0 --reserve-black
run show_frame256.lua snap256
python3 tools/bench/verify_frame256.py
echo "ALL GREEN"