Procedural cockpit graphics: ring, attitude and enlarged contacts
Interactive cockpit · Extracted data · Browser checks
This pass adds the Skimma weapon ring, Caero attitude line and fixed vector
surround, and enlarged-radar contact symbols. The attitude line is in the
Caero (B91F = 19h) branch, not the Skimma branch. The controls supply drawing
inputs directly; they do not run a flight or targeting simulation.
Skimma weapon ring
5D47 draws the aiming outline at X=164. 5D56–5E0E selects weapon state and
radius; 5D83–5DDC places the individual masked sprites. Unlike the earlier
speculative description of a rotating ring, this inspected loop starts at a
fixed sine-table position on every call. It changes radius and remaining/spent
indicators, not a continuously advancing rotation phase.
| Weapon index | Sine byte increment (5E16) |
Ring positions / working capacity (5E19) |
|---|---|---|
| 0 (weapon 1) | 146 | 14 |
| 1 (weapon 2) | 255 | 8 |
| 2 (weapon 3, upgraded Skimma only) | 205 | 10 |
These are working shot counts, distinct from the reserve/reload capacities
5, 3 and 4 at 5E1C, and from field 454E's selected reserve display.
The loop reads signed high bytes of sine/cosine words. For raw radius r, its
unmasked destination origin is:
x = FCCD + ((sine_high * r) >> 8) - 2
y = baseline - ((cosine_high * r) >> 8)
The inspection uses FCCD = 160 and normal-play baseline 88. Nonzero 4540
selects baseline 118. Radius is consequently approximately half the raw input
in screen pixels. Mask 6010 comprises four (skip,width) rows:
(5,2), (4,4), (4,4), (5,2). The visible sprite extends beyond the unmasked
origin; it must not be recentered as a generic circle.
Before each sprite, the low count byte is decremented. A nonnegative result
selects source row 47 (remaining); a negative result selects row 52 (spent).
Destination X modulo four selects source X 8 + 5*alignment. The export keeps
all four alignment variants for both states and both Skimma sheets: 16 masked
RGBA PNGs, with original source coordinates in the JSON.
Reload and radius input
The native producer compares clock BF8 with deadline C51C using a signed
16-bit difference. When the difference is negative, it constructs radius from
that difference, suppresses drawing below raw radius 15, and supplies a zero
remaining count: the expanding reload ring uses spent indicators.
5E31–5E40 sets the deadline 1024 ticks ahead when refilling the working count.
Outside that interval, the producer uses (word[5E00] << 6) >> 8, preserving
16-bit wrap, clamps the raw radius to at least 15, and uses the selected working
count at 5E10 + index. Weapon enable bit 0 at 4543 + index gates both paths.
The later smoothing/update through 7CDB is not reproduced in this preview.
The browser's radius slider therefore remains an explicit drawing input.
Caero attitude and fixed vector graphics
5E6F–5ED3 computes a line from the sine-table offsets stored at 3E43 and
3E49. The main loop carries these as view-angle parameters. Multiplication
uses signed high-word products with scale 224, preserving asymmetric integer
rounding. The probe supplies the two offsets directly; mapping every camera
mode and player attitude into them remains separate work.
5F5D orders endpoints and enters the original line rasterizer at A77B.
The export executes that rasterizer for 289 combinations: pitch and roll
indices −128 through +128, step 16 (−45° through +45°, step 5.625°). Each frame
stores lossless horizontal pixel runs, not a browser-generated approximation.
The deliberately limited inspection grid avoids asserting unrestricted camera
angle/viewport behaviour.
5ED6–5F0D adds the fixed vector graphics using streams 5F67 and 6009.
Their pixels are also captured from native drawing code. The inspection uses
zero for the self-modified colour parameter at 5F01; live feedback is not
simulated. The line uses a fixed palette index 14 in the browser. Separately,
252 native cases check its actual endpoint and colour arithmetic, including
player pitch-high-byte inputs and both branches of 7ED2.
Coordinates are captured in a viewport with centre Y=84. The browser adds the established Caero eight-pixel cockpit offset, placing that centre at screen Y=92. Skimma ring sampling does not receive this Caero offset.
Enlarged-radar contacts
5BCA/5BE2 renders a 12-pixel rounded diamond, shared by the enlarged-radar
contact callers. The exported mono shape is relative to incoming BX/DI:
##
####
####
##
Its occupied coordinates are X=1..4, Y=1..4 within that local anchor. The native
routine loads colour through VGA latches; subsequent framebuffer writes carry
plane masks, not literal pixel colours. The capture records the selected
colour at DBB8 and reconstructs the planar coverage. Stereo duplication is
explicitly excluded.
The normal radar's object traversal and coverage predicate are reused. Enlarged
callbacks at 5C31 and 5C34 multiply the rotated fixed-point offsets by three
before reducing them to pixels. They reject squared distance >=3965. The
screen anchor is X=76 and a Y supplied through patched word 5C46; the original synthetic checks initially used Y=96. The assembled preview now
uses native local Y=100 plus the Caero eight-pixel row offset, giving screen
Y=108. Its contact checks now supply local Y=100 too. Each shape anchor is offset −3 on both axes.
Colour depends on distance and which callback is selected. For squared distance
d, it is 7 - ((d - 3965) >> (8 + shift)), reduced to a byte, with shift 0
for group A and 1 for group B. These group labels avoid assigning an unverified
allegiance. Coverage is assumed in the probes and controlled explicitly in the
browser, as for the existing small radar.
The browser now offers the assembled mono enlarged radar, as described below. Stereo placement and the complete navigation-contact traversal remain separate.
Evidence and reproduction
Native probes use the captured runtime image from
analysis/hidden-commands/ui/after-command-memory.bin.gz (image segment 01A2),
with synthetic display state. Drawing code and geometry execute in Unicorn;
coverage and selected hardware operations are intercepted as described above.
No original installation files are modified.
- 140 native ring-placement cases, including empty/full working counts and four radii, match an independent integer reference.
- 54 producer cases cover reload suppression/expansion, enable state and ordinary radius selection. Execution stops before the subsequent smoothing update; no claim is made about full target acquisition.
- 252 native attitude endpoint/colour cases match an integer reference; 289 rasterized angle frames are exported directly.
- 144 native enlarged-contact cases match the browser projection, including headings, boundary rejection and both colour groups.
- Browser checks compare ring placement and enlarged projection against those native results, exercise both craft families, enforce ordinary Skimma's two weapons, and check reset behaviour. Screenshots are retained alongside data.
PYTHONPATH=/tmp/darker-python python3 tools/extract_dynamic_hud.py
NODE_PATH=/tmp/riotfire-browser/node_modules node tools/verify_procedural_hud.js
The standalone tools/procedural_hud.py rebuilds its own JSON and ring PNGs;
use extract_dynamic_hud.py to update the assembled browser too.
Enlarged-radar surround and height-coded contact follow-up
Additional graphics data
is generated by tools/radar_graphics.py and incorporated by
tools/extract_dynamic_hud.py.
The original 59CB path now runs through its mono background and heading
rasterisation in the probe. The circle at A5D6 and planar span writer A712
produce the actual outline/coverage; no browser circle approximation is used.
The base capture contains 12,572 pixels including the centre marker and
number-panel clearing strip, stored as lossless horizontal runs. VGA plane
masks are combined with the selected colour latch rather than treating dummy
framebuffer-write values as palette indices. Stereo flags are disabled.
The original caller at 55B5 sets local Y to 100 + word[5458]. In the normal
Caero view that word is zero, and the row table adds eight pixels. The resulting
screen centre is (76,108). The previous centre (76,96) was explicitly an
inspection fixture, now superseded in the assembled display.
The heading line is captured at 256 heading states, matching the browser slider's 256-word increment. Native endpoints and rasterised pixels are retained per state. The real routine can consume finer headings; the export is a sampled inspection control, not a claim that all in-game headings quantise that way. The five large glyph copies are observed at local X=56,64,72,80,88 and Y=33; the Caero offset puts them at screen Y=41. They show row, separator, column using the existing extracted large digits. Font copying is intercepted in the native capture and assembled from those PNGs in the browser.
5B92 supplies the height-coded navigation glyph. It first restores a six-row
background mask at source X selected by destination alignment: 96,109,102,115.
Its source row is 8 in the actual sheets for all three cockpit resources. Twelve
masked PNGs preserve those backgrounds and alignments. It then draws the shared
12-pixel contact shape with a computed colour.
The colour uses a signed eight-bit difference between object height byte
+0D and reference byte 2E37, followed by arithmetic division by two. For a
negative result, the magnitude operation is bitwise complement, not absolute
value: -1 maps to magnitude zero. Magnitude saturates at ten. The two branches
start at palette indices 151 (nonnegative) and 183 (negative), then subtract
that magnitude. This behaviour is preserved, including wrapping differences;
no metre conversion is inferred.
144 native cases verify colour and background-source selection across
heights, reference values and all four horizontal alignments. The browser
compares its colour arithmetic with those fixtures and offers a fixed-position
sample on each craft. This is not yet the whole navigation radar: list selection,
projection/range clipping through 5B76, and the live reference-height producer
remain separate. Coverage/interference and live contact allegiance also remain
outside the assembled enlarged-radar preview.