The gauge framebuffer is PLANE-PACKED: L4GAUGE.CFG gives each port a bit
mask, not a rectangle, so all six heads share the same x/y and are separated
only by bit -- the packing the VDB splits into the pod's physical screens.
Comparing RGB was therefore measuring nothing useful; the "magenta artifact"
just meant the wrong heads were lit at that pixel.
New instruments (emulator/render-bridge/gauge-ab/, with a README):
planes.py per-head scoreboard -- shipped vs ours, missing vs extra, per
port mask, recovering the 16-bit word from DOSBox's RGB565
BT_VIS_LOG a complete cockpit widget map from the single dispatch every
gauge widget is built through (MethodDescription::Execute),
printing name + port + authored position
ab.sh stages the fresh build over BTL4REC.EXE and kills any running
DOSBox before launching -- see below
The fix: the Comm page's pilot roster is the POD roster (the viewpoint mech's
ControlsMapper pilot array, mapper attributes 15/16), not the mission's
player list. It is zero in a solo mission, so the shipped page draws empty;
ours resolved the local player and painted its icon in colour 0xff. The gate
belongs in the row source, not PilotList::Execute -- Execute's empty branch
still has to run, because erasing is what the shipped page draws.
head shipped ours missing extra (was)
Eng1 17981 17981 0 0 (0 / 1030)
Eng2 17981 17981 0 0
Eng3 17981 17981 0 0
Comm 15656 13557 2099 0 (2099 / 1253)
Heat 21374 20913 566 105 (566 / 990)
Title-band magenta 404 -> 0. The 2099 Comm pixels we are missing were
missing before the gate, so it is a strict improvement.
Method note, which cost more than the bug: the rig's confs run BTL4REC.EXE
out of the mount while the build writes build410/btl4opt.exe, and nothing
connected the two. Three measurements ran an hour-old binary, so a change
that "did nothing" three times had never been tested -- and the correct first
hypothesis was discarded on that evidence. ab.sh now re-stages every launch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gauge-ab -- the cockpit A/B rig
Runs the reconstruction and the shipped 4.10 binary against the same mount
(same GAUGE/L4GAUGE.CFG, same egg, same mech) and compares what each one
paints into the gauge framebuffer.
With L4VIDEO=OFF and L4GAUGE=640x480x16, the DOSBox window is the packed
gauge framebuffer -- the exact buffer the pod's VDB splits into its physical
heads. So a screen grab is a lossless read of the cockpit.
Running
bash ab.sh rec # stage the fresh build + run the reconstruction
bash ab.sh shp # run the shipped binary
powershell -File grab.ps1 -Out ours.png
python3 planes.py shipped.png ours.png
Always launch through ab.sh. The confs run BTL4REC.EXE out of the
mount, not build410/btl4opt.exe. Forgetting to re-stage silently measures
the previous binary -- on 2026-07-27 that cost a full round of wrong
conclusions (a fix "did nothing" three times because the tested exe never
changed, and the correct first hypothesis was discarded on that evidence).
ab.sh re-stages every time and kills any running DOSBox first (two
instances share OUT.TXT and corrupt the trace).
The instruments
planes.py -- the per-head scoreboard (use this first)
The framebuffer is plane-packed: L4GAUGE.CFG gives each of the ten
logical ports a bit mask, not a rectangle --
configure(8,Heat, 0,0x4000,clut2,blue, NULL); # UL
configure(9,Comm, 0,0x8000,clut2,red, NULL); # UR
-- so all six heads occupy the same x/y and are separated only by bit. RGB
comparison is therefore meaningless: a "magenta artifact" is really the wrong
heads lit at that pixel. DOSBox presents the buffer as RGB565 with bit
replication, so the original 16-bit word is recovered exactly by
R>>3, G>>2, B>>3.
planes.py reports, per head, how many pixels the shipped binary lights, how
many we light, how many we're missing, how many are extra, and the
bounding box of the extras. Missing vs extra is the useful signal: extras are
our bugs, missing is unbuilt work.
score.py -- one whole-frame number
Coarse (the big MFD heads dominate it). Good for "did this regress", useless
for locating anything. Prefer planes.py.
BT_VIS_LOG -- the widget map
set BT_VIS_LOG=1 in the conf makes the build print every gauge widget the
interpreter constructs, with its port and authored position:
[w] pilotList port=9 at 0,0
[w] rankAndScore port=9 at 33,0
[gen] port=0 x=443 y=322 lampA='sgena.pcc' ... on=1 col0=3 col1=9
[cluster] port=4 x=0 y=240 title='qsensors.pcc' banner=1
The [w] line comes from the single dispatch every widget is built through
(MethodDescription::Execute, MUNGA/GAUGREND.CPP), so the map is complete.
This is what turns "a diff at framebuffer (67,3)" into "that head has no
drawing widget there, so look elsewhere".
Method
Source inspection has now produced five wrong suspects on this one artifact; each was settled -- or killed -- by one instrumented run. So:
planes.pyfirst: which head, and is it missing or extra?BT_VIS_LOG: which widgets exist on that head, at what position?- Only then read code, and only that widget's.
And re-stage before believing any measurement.