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>
83 lines
3.2 KiB
Markdown
83 lines
3.2 KiB
Markdown
# 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
|
|
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:
|
|
|
|
1. `planes.py` first: *which head*, and is it missing or extra?
|
|
2. `BT_VIS_LOG`: which widgets exist on that head, at what position?
|
|
3. Only then read code, and only that widget's.
|
|
|
|
And re-stage before believing any measurement.
|