DARKER Research archive

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.

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.