Commit Graph
149 Commits
Author SHA1 Message Date
CydandClaude Opus 4.8 60d4c2da84 Liveness probe: per-draw polygon-site counts for any capture
probe_live.py boots a capture (with the required r.start()) and counts per-draw
hits at the known polygon sites (quadA/triB/emit-header/VSTRIP). Discriminates
real scene content from the background-only test bench. Findings so far: every
cap* capture + ravtest + dtest + sharksval = the SAME hardware test bench (one
9x5 patch; sharksval draws it 3x); real content lives in the extra-action
captures (sdemo/glblade/munga/trek/fxtest/klng*), which DO replay (fxtest ran
6K+ commands incl. act25/42 with no faults) but have much larger scene builds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:15:42 -05:00
CydandClaude Opus 4.8 cdc2c724db Capture triage: parse-only census of every wire capture
capture_triage.py parses each capture's first slice (no emulation) and reports
the embedded firmware build, action census, init config, and any actions outside
cap7's known-good set. Results: all captures share build 0x31440 + the same init
config; ravtest (RAV = the walled terrain map), dtest, cap6/cap8/capture/
sharksval are clean cap7-profile candidates; sdemo/duane/fishspls add morph;
bt3/munga7 add act31/43/45/list_remove. cap6's earlier "boot derail" was likely
just an under-budgeted run (87K cmds to parse), not a fault.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:29:27 -05:00
CydandClaude Opus 4.8 a408871390 Confirm cap7 is a single-object recording (not a mission with terrain)
payload_scan_mid.py scans a mid-mission frame (snapfull1, cmd 19,889): identical
9 coordinates as cmd 735 (65.5/72/181/236, all x[66,236]). So cap7 redraws the
same static surface patch every frame across its whole length -- no terrain, no
battle, ever. Corrects the earlier "mid-mission frames have battle scenes" guess:
a full battle scene lives in a different capture entirely. Readout last-mile
updated. The decode + array render fully account for cap7's content.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:22:58 -05:00
CydandClaude Opus 4.8 0019770bea Resolve the frame: this death-cam draw IS the object (no terrain)
payload_scan.py scans all writes into the payload region across the draw: 14,223
writes, every recoverable screen coordinate in x[66,236] = the object's range,
nothing near 0/400/832. So this death-cam frame carries no geometry beyond the
object -- the ~64 triangles account for essentially all payload writes; the rest
is a background clear. The "ground and sky still to render" was a wrong premise
for THIS frame: the array's object render IS this frame's geometry. Wider battle
scenes with terrain live in mid-mission frames (a separate capture). Readout §05
+ last-mile updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:16:22 -05:00
CydandClaude Opus 4.8 533c14b102 Negative result: payload coord-extraction does not rebuild frame geometry
frame_geometry.py snapshots each SEND payload (deduped by content) and pulls
fixed-point screen coords. Honest outcome: only 11 sparse coords, all in the
object's x-range, no terrain -- the payloads store compiled edge/plane
coefficients, not extractable vertex lists, so "grep coords and plot" is a dead
end. A real from-scratch render needs the full plane-role assembly (slopes +
constants -> lines -> triangles -> array), which stays unsolved. Documented in
MICROCODE-DECODE-NOTES.md so the shortcut isn't retried.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 17:01:43 -05:00
CydandClaude Opus 4.8 d1e83c3445 Map the full-frame micro-code structure: double-buffered primitives
frame_primitives.py decodes every coeff-copy word over the draw: 1051 SEND,
334 SENDE, 384 TILE, 327 GOTO, 231 STOP across 105 tiles -- but only 2 distinct
SEND payload addresses (0x08015xxx/0x08017xxx). That's double-buffering: the
firmware compiles each primitive into an alternating coeff block and SENDs it,
so the content changes over time while the addresses don't. A from-scratch full
frame must snapshot each primitive's payload at its SEND, then run all of them.
Readout §05 + notes updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:49:38 -05:00
CydandClaude Opus 4.8 0f9ff000c4 Decode plane constants: fixed-point screen coordinates
edge_decode.py: the edge payloads' non-float words carry the plane constant/
vertex terms as .8 fixed-point screen coordinates (0x0000ec00 = 60416 = 236.0,
a screen-x in the object's range), not IEEE floats -- which is why they weren't
in the float list. Locates the last missing piece for edge reconstruction:
A/B slope (float, verified 0.2%) + C (fixed-point coord). Readout §02 notes it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:39:25 -05:00
CydandClaude Opus 4.8 09ea5f9802 Verify the edge decode: payload coeff matches geometry to 0.2%
edge_verify.py: the edge SEND payload float 0.12527 matches the object's own
screen-space edge normal (0.12555, computed from the captured vertices) to
0.2%, and 0.1262 to 0.5%. Edges are pure screen geometry (no z-scale ambiguity),
so this is a hard confirmation that the compiled micro-code carries the real
coefficients. Readout §02 states the 0.2% match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:09:07 -05:00
CydandClaude Opus 4.8 b78b14b7f9 Array: read out the depth buffer; show it in the readout §05
igc_array.py gains readout_depth() -> the 24-bit Z stored in pixel memory as a
depth image (near=bright), i.e. the plane the decoded SENDE z-sweep interpolates.
Readout §05 now shows the depth buffer next to the tile footprint, ties the
z-decode to a visible array output, and reframes the "remaining work" as numeric
reconstruction across regions (the coefficients are already cross-validated).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:07:01 -05:00
CydandClaude Opus 4.8 943d6e1090 Decode the SENDE (z/color) micro-code instruction format
Full SENDE dump (ctrl_split.py) resolves into a regular 4-word instruction, one
per bit-plane: word0 = constant increment float, word1 = [length countdown,
source bit-addr] with op field 0x3a, word2 = the coefficient's place-value (the
x2 chain, -1.009..-0.00788), word3 = destination bit-plane (0x21..0x31 = a
17-bit fixed-point z/color). A MEMpluseqMEM bit-serial accumulation. SEND edge
payloads use the same value encoding with a different (header + 3-word group)
framing. Control-word split for the z/color path is now decoded; from-scratch
render still needs numeric reconstruction + plane mapping + the C term.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:48:22 -05:00
CydandClaude Opus 4.8 34e2155672 Decode the IGC coefficient value encoding: bit-serial x2 place values
The payload floats group into clean x2 doubling chains (0.0079 0.016 0.032 ...
1.009) = a coefficient stored as its binary place values C*2^k across the
bit-planes, exactly how a bit-serial adder holds a number. Recovered base
coefficients correlate with the object's own screen-space edge/z slopes
(decode_corr.py, chain_decode.py), so igc_array.py's inputs are cross-validated
against the compiled stream. Fixed-point scales from FOOTER.SS (Czscale=2^20,
Ctexscale=2^16). Readout §02 + decode notes updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:33:45 -05:00
CydandClaude Opus 4.8 90fd3497d1 Readout §02: real decoded DMA list + embedded-float payload finding
§02 now shows the actual per-region DMA command list captured from the emulator
(region 0x0801fa40: SEND/SENDE/TXDN/TILE/GOTO) and the real SENDE payload with
its embedded float coefficients (bit-serial MEMpluseqMEM sweep). §05 + the
last-mile list updated: the micro-code is partly decoded (lists clean, payloads
carry recoverable floats), remaining work is the control-word field split.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:24:03 -05:00
CydandClaude Opus 4.8 6c7e9cf1dc Decode the IGC DMA lists + find embedded float coeffs in SEND payloads
The coefficient-copy (0xf0411cd4) writes per-region DMA command lists; captured
from the cap7 death-cam they decode cleanly against DMAENGN.H ({addr,opcode}
pairs, SEND/SENDE/TXDN/TILE/GOTO/FLUSH). Every region references the same
tile-relative payloads and differs only in TILE(id) + the GOTO link.

Dumping the SEND payloads shows they are NOT opaque: they interleave control
words with embedded IEEE floats = the edge/plane/colour coefficients, loaded as
a bit-serial MEMpluseqMEM sweep (regular 4-word instruction: increment float +
length/bit-address control + dest plane). So the micro-code decode is now
extraction + bit-serial execution, not blind ISA reversing -- the remaining
blocker is the control-word field split (igc_opco.h is not in the dump).

Full findings + next steps in MICROCODE-DECODE-NOTES.md; probes coefdump.py
(DMA lists) + payload_dump.py (payload floats), restore from snapv2.pkl.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:22:12 -05:00
CydandClaude Opus 4.8 7bd6af8c20 Readout: add the Tier-1 Pixel-Planes array section (§05)
Signal-chain Pixel-Planes stage flips to "simulated"; new section shows the
64x128 tile footprint the array lit for the object (18/50), the 26-byte
pixel-memory bit layout (Z24 + RGB), and states the validation + the honest
remaining gap (the compiled micro-code binary, still undecoded, that carries
the ground/sky).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:06:57 -05:00
CydandClaude Opus 4.8 9d8de701f6 Tier 1: a faithful PXPL5 IGC Pixel-Planes array simulator
igc_array.py implements the array model from PXPL5SUP/IGCOPS.C + IGCTYPES.H:
the screen is partitioned into 64x128 tiles; every pixel owns a 26-byte
bit-addressable memory + enable bit; all pixels evaluate the same linear
tree eval_ltree(x,y,A,B,C)=(int)(x*A+y*B+C) in lockstep. A triangle is drawn
exactly as the hardware does (PXPL5GEO tri_zb_rgb): three edge trees -> the
enable register, then z + r/g/b planes interpolated per pixel, z-buffered via
MEM2geMEM2, writes gated by enable, read back out of pixel memory.

Driven by the captured 9x5 surface it lights 18/50 tiles and produces pixels
that match shade_render.py to ~1% (edge anti-aliasing only) -- validating the
array against the reference rasteriser. This is the array's computational
model, not a decode of the compiled bit-serial micro-code (that binary
encoding is still undecoded); it produces the pixels that micro-code would.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:02:59 -05:00
CydandClaude Opus 4.8 e43cf24993 Decode the captured object: it's a complete 9x5 height-field surface
The 45 VSTRIP vertices captured off the i860 sort back into an exact 9x5
model-space grid (x,z in even 2-unit steps, y = height at every node; all
45 cells filled). cap7's death-camera views it nearly edge-on, which is why
the raw screen projection looks like a folded sliver. gridsurf.py rebuilds
the true grid connectivity (2 tris/quad) and shades it from the firmware's
own per-vertex normals -> a clean solid surface. render-readout.html now
leads with that true-3D reconstruction and shows the grazing projection +
wireframe as "how the death-camera saw it".

Also resolved a long-standing red herring: the (1,1,10,10) extent bounds
that earlier sessions chased as the "empty-bins bug" appear identically in
the working frame -- they're the per-frame marker rect, not the geometry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 14:44:49 -05:00
CydandClaude Opus 4.8 32ac5ca9b8 Shaded frame: Gouraud raster of the i860's projected geometry
Captures the object cap7's death-camera view draws (4 VSTRIP strips, 45
verts) with its per-vertex normals straight off the emulated i860, and
shades it in software with a barycentric z-buffered fill (shade_render.py).
This is our rasteriser showing the firmware's geometry lit by the firmware's
own normals -- not the board's bit-serial Pixel-Planes array (that stays the
Tier-1 build). cap7-geometry.json is the portable capture; render-readout.html
is the published readout with the shaded frame as its hero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 14:19:43 -05:00
CydandClaude Opus 4.8 6aaceaa312 firmware-decomp: capture_wireframe.py -- reconstruct the projected object as a
VSTRIP triangle-strip wireframe from the emulated i860's transform output

Vertex node layout (capfw7): model xyz @+0x00, normal @+0x10, next @+0x2c (linked
list = triangle strip), screen Y @+0x28, screen X @+0x40. Hook the transform loop
tail (0xf041614c), read node=r22-0x40 + f13/f16 = screen (x,y), group strips by
node-address gap. cap7 death-cam object = 4 strips / 45 verts -> a real wireframe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 14:03:45 -05:00
CydandClaude Opus 4.8 9f6cd44333 i860 emu: pfgt/pfle exact semantics (R bit selects gt/le, pipelined retire+advance); IGC consumption resets page write-index for reuse
- pfgt/pfle/pfeq are always pipelined: fdest <- A-pipe retire, push undefined;
  bit7 selects pfle (inverted CC sense) -- was misread as result precision.
  316 pipelined compares in the firmware previously desynced the A-pipe.
- h_igcwait now models full consumption: done-status nonzero AND write-index
  reset (+0x7f8=0), otherwise queue pages saturate at 0x7f0 across frames and
  enqueueing silently stops (observed: even the per-frame marker item stopped
  binning by cmd ~4300).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 21:05:08 -05:00
CydandClaude Opus 4.8 170b35d812 emu_main: IGC drain-wait completion is NONZERO status, not zero (per-page done flag; loop exits when [page]!=0)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:05:13 -05:00
CydandClaude Opus 4.8 051544fed6 i860 emu: pipelined pftrunc/pfix through the adder pipe (raw-bits pipe entries)
The frame-1 extent bug traced to four `pftrunc.sd fN,f0,fX` ops (P=1)
converting the region bounds through the A-pipe -- our fix/ftrunc was
scalar-only (MAME's core never implemented the pipelined form: "pipelined
not functional yet"). The drains then retired neighboring values (the
homogeneous w=1.0f as raw bits -> y1=0x3F800000>>7=8323072 -> ~8.3M phantom
tile rows = the 49K uniform bins / 4096 same-page descriptors).

Pipe stages now carry (value, rp, raw): pipelined ftrunc/fix push the
truncated integer BITS (64-bit pair for .sd results), and _retire() writes
raw entries back verbatim instead of re-encoding floats. Extent probe after
fix: x0=y0=x1=y1=0 (sane empty bound), single visit instead of a hot loop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 17:53:59 -05:00
CydandClaude Opus 4.8 6991b68685 emu_main: IGC drain-wait hook (0xf0421510): firmware polls [r16] until the rasterizer consumes the region queue; we consume instantly
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:21:01 -05:00
CydandClaude Opus 4.8 4f143e3a26 i860 emu: EXACT pipelined FP model (MAME-validated) -- N-R divide passes
unhooked; draw_scene #1 runs with 41K IGC board accesses

The functional pipe model is replaced with exact i860 semantics, validated
against MAME's i860 core (src_opers[] and insn_dualop/insn_fadd_sub verbatim):

- Precision bits: SOURCE = bit8, RESULT = bit7 (swapped from our old reading;
  .ds is invalid except famov). For DUAL ops: sp = multiplier source prec,
  rp = adder source prec AND all results.
- Dual ops (sub 0x00-0x1f): bit10 selects PFAM (fdest <- A-pipe retire) vs
  PFMAM (fdest <- M-pipe retire) -- NOT "pipelined". FLAGM operands (the
  A-pipe entries in the DPC table) read the M-pipe in the PFMAM family.
  T-loads on DPC 2,3,6,7,8,0xb,0xc; K-loads on DPC 1,3,5,7 (from fsrc1 at
  mul precision; T from the M-pipe last stage).
- fdest BYPASS: pipelined ops whose source register equals fdest read the
  retiring pipe value instead of the stale register (dual mul: op2 only;
  dual add + scalar pipelined fadd/fmul: both operands).
- Pipe stages carry (value, result-precision); push rounds to single when
  rp=0 (exact magic-constant float->int bit games); retire encodes with the
  pusher's precision. Adder = 3 stages; multiplier = 2 (double) / 3 (single);
  pfld = 3-stage load pipe; graphics fiadd/fisub = 1-stage pipe, 64-bit .dd.

ACCEPTANCE: (1) the firmware's own Newton-Raphson integer divide now computes
16/16 = 1 through the pipes -- the intdiv hook is retired (authentic
execution); (2) draw_scene #1 from the snapshot executes real render phases:
coefficient fld.d bursts, 0x800-byte queue-page initialization, tile enqueue,
and the first region flush -- 40,959 IGC/board MMIO accesses (previously 0).
Frame still running at the profiling time budget (~240M steps in).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:40:29 -05:00
CydandClaude Opus 4.8 58b9dcda2f emu_main: fix __init__ split by the intdiv-hook edit (self.heap was unreachable)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 14:01:41 -05:00
CydandClaude Opus 4.8 abd908a4e5 emu_main: hook the runtime integer-divide (0xf042ee00, r22=r22/r23) with exact host division -- the compiled Newton-Raphson needs exact dual-op pipe timing; geometry stream now consumes frames correctly (replay passes cmd 151 blocker, 387+ commands)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 14:00:44 -05:00
CydandClaude Opus 4.8 d9cfd74070 i860 emu: pipelined FP unit + dual-ops, adds CC sign rule, flush op;
version-matched capfw7 replay BUILDS THE SCENE

- Pipelined FP model: 3-stage adder / 2-stage(double) multiplier pipes with
  KR/KI/T registers; pfadd/pfsub/pfmul honor the P-bit; PFAM(0x00-0x0f)/
  PFSM(0x10-0x1f) dual-operations implemented from the validated mnemonic
  grammar (r2pt, ra1p2, m12apm, ...: M-unit/A-unit routing + K/T loading).
  Survives the capture build's Newton-Raphson boot math.
- adds CC = result-negative (sign rule); addu stays carry. Fixes printf's
  pad-to-width loops and the exit-walk's empty-table branch.
- op 0x0d = flush (cache-line flush, store-format split offset, bit0
  auto-increment): boot cache-flush loops.
- fix/ftrunc: NaN/inf/out-of-range -> IEEE indefinite instead of raising.
- emu_main: per-build address maps (sda4 + capfw7); capfw7 map fully derived
  (main 0xf0403f80, remote_velocirender 0xf040cee0, dN_receive 0xf04024c0,
  velocirender_init 0xf040c5d8, do_init 0xf0409440, reply 0xf0409320,
  alloc core 0xf042c628, v2p 0xf042c300, locks 0xf0423360/0xf04233b8).
  The 860-boot preamble is skipped for capfw7 (the monitor's job -- the app
  build rejects those actions); the capture build dispatches 42 actions,
  explaining the unknown actions 29/42 in cap7.
- run_to_draw: version-matched marathon from the TRUE ENTRY 0xf0400000 with
  the boot handshake ([0xfffff728]) satisfied -- crt0/pools/main are all the
  firmware's own startup, eliminating the seed-slot guesswork.

STATE: authentic boot + init (reply=4) + scene build running: 152 commands
(31 create, 59 flush, dcs_link, list_add, texmaps) processed and replied
before the time cap. First draw_scene sits ~1400 commands ahead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 13:20:35 -05:00
CydandClaude Opus 4.8 753564aef2 Capture-embedded firmware extracted: cap7 carries the EXACT build the game ran
VR_COMMS.C boot_860() shows the host boots the render board by streaming the
whole .MNG over the wire: args860 ("i860 50MHz and kicking"), a 40-byte
code860 packet holding the 7-word header {csize,dsize,bsize,cstart,dstart,
bstart,entry}, then the code/data/bss segments. cap7's preamble is therefore
a complete firmware image -- and a DIFFERENT BUILD than sda4/RPLIVE/VREND.MNG
(text 0x31440 vs 0x39ec0), which explains the residual global-address
mismatches (version skew) when replaying cap7 against the sda4 build.

The wire header also confirms the link layout from the protocol itself:
cstart=0xf0400000, dstart=0x00001000, bstart=0x0001f4a0, entry=0xf0400000.
Note the streamed "bss" content is NOT zeros (10685/14272 nonzero bytes).

extract_capfw.py reassembles the image (capfw7.mng + capfw7.bss, committed);
it boots in emu860 to 0xf040062c then needs op 0x0d (new ctrl-reg variant).
run_to_draw.py = marathon runner with board-stream dump.

Next: re-derive the hook addresses (main/dN_receive/allocator/...) for the
capture build and replay cap7 against its own firmware, version-matched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:43:02 -05:00
CydandClaude Opus 4.8 a688cc1c30 i860 emu: ISA round 2 (ground-truth encodings) + authentic-main runner; firmware
processes the full wire boot incl. the downloaded PAZ/sfx module

ISA fixes, all derived from the toolchain's own .S<->.O pairs (AS860.ZIP:
OPTFLOAT/TRISTRIP/ZBUF32, plus DNC.O) and the firmware's linked COFF header:

- DATA_BASE = 0x1000 DEFINITIVE: VREND.MNG carries its original COFF header in
  the file tail (.data vaddr 0x1000, .bss 0x1f940, entry 0xf0400000).
- Integer loads: even opcodes are register-indexed (EA = src2 + src1); op 4/5
  size flag = instr bit0 (0 = ld.s 16-bit, 1 = ld.l 32-bit); ld.b/ld.s
  sign-extend.
- Integer stores: st.s/st.l selected by offset bit0, same split-offset rule.
- FP loads/stores: FP register lives in the DEST field for both fld and fst
  (fst does NOT use the integer split-store encoding); flag bits: bit0 =
  auto-increment (base <- EA), bit1 1=.l/0=.d, bit2 = .q; .d/.q span register
  pairs/quads. ~450 fld.d + ~300 fst.d were previously read/written 32-bit.
- bla (op 0x2d, was misdecoded as shrd): branch-on-LCC-and-add with the
  sign-dependent LCC rule (src1<0 -> signed sum >= 0), so spent countdown
  loops terminate. 335 bla instructions in the firmware.
- CORE ESCAPE (op 0x13): sub-op 1 = lock, 2 = calli, 7 = unlock. Previously
  everything decoded as calli, so every spinlock acquire jumped to address 0 --
  this was the phantom "exit stub" behind most earlier derails.
- f2b: IEEE overflow -> +/-inf instead of raising.

emu_main.py (new): runs the firmware's OWN main() (0xf0403f10) and feeds real
wire captures through a hooked dN_receive, so init/do_init/dispatch/handlers
all execute authentically. Provides the transputer-monitor environment
(processor id, DRAM region descriptors in the shared control block, sbrk/
shared-block seed slots) and hooks only the link primitives (bla busy-wait,
dN_mynode/dN_nodes/dN_receive/dN_send, putchar path, spinlocks, page allocator
+ virt->phys translator pending Tier-2 VRENDMON).

KEY DISCOVERY: the capture's args860/code860/data860/bss860 preamble is the
host DOWNLOADING an additional i860 module (the PAZ/sfx renderer layer, banner
"i860 50MHz") which installs the runtime handler tables and system objects.
Feeding it through the firmware's own handlers, the module loads and makes the
first IGC board-register writes. State: 834+ wire commands processed (module
download + init + create); first draw_scene sits at command 1568.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:26:10 -05:00
CydandClaude Opus 4.8 f5e9ae987d i860 emu: add EMU_DATA_BASE / EMU_INDEXED experiment hooks (default off)
Env-gated hooks to continue the create()/jump-table investigation without
changing default behavior:
- EMU_DATA_BASE (default 0): overrides I860.DATA_BASE for the .data link base.
- EMU_INDEXED=1 (default off): makes EVEN integer load opcodes register-indexed
  (EA = base + index), per the ground-truth `ld.l r30(r31),r31` in VR_REMOT.S.

Sweep finding (EMU_INDEXED=1): create() RETURNS at DATA_BASE=0xff0 making real
subroutine calls, confirming register-indexed loads + a ~0x1000 data base are
correct. Residual: do_init derails to (DATA_BASE+0x30) for base >= 0xfe0 while
create needs >= ~0xfe0 -- a single flat base can't satisfy both yet, pointing
to a segment/relocation nuance (or a separate do_init jump-table derail).
Details + repro in the tier-0 memory. Default behavior unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:40:33 -05:00
CydandClaude Opus 4.8 ef4a0b67ac i860 emu: fix 6 core interpreter bugs; boot+velocirender_init+do_init now run clean
Worked the full-render marathon by tracing faults in the real VREND.MNG
firmware and matching them to the ground-truth AS860 assembly (VR_REMOT.S)
and C source (VR_REMOT.C). Six interpreter bugs found and fixed:

1. subs/subu direction: computed src2-src1; must be src1-src2 with
   CC=(src1<src2). Corrupted every subtraction, compare and bounds-check.
2. logical CC: all i860 logicals (and/andnot/or/xor + .h) set CC=(result==0).
   Had wrongly limited it to the AND family, so the `xor 0x0,rN,r0; bnc`
   zero-test idiom spun forever.
3. STORE encoding (the big one): i860 stores encode the SOURCE register in
   bits 15:11 (src1), not the dest field, and SPLIT the 16-bit offset across
   bits 20:16 (high) + bits 10:0 (low). The old decode saved the wrong
   register to the wrong offset, so function prologues never stored r1 and
   every `bri r1` return jumped to 0.
4. ld.b/st.b: op 0x00/0x01 = ld.b, op 0x03 = st.b (byte). Proven by byte-scan
   loops (r5 advanced by 1) and a save/restore pair at 0xf042b418 -> b430.
5. Mem page-span: r16/r32/w16/w32 crashed on 64KB page boundaries; now fall
   back to byte access across the boundary.
6. fmlow.dd (FP subop 0x21): the i860 has no integer multiply, so ints are
   ixfr'd into FP regs, multiplied via fmlow.dd, and fxfr'd back. Implemented
   as a 32x32 -> 64 multiply across the destination register pair.

emu_replay.py now runs the source-accurate init sequence: velocirender_init
on the init(0) command, then do_init before the first real command. Result:
boot idles at 0xf0400590; velocirender_init returns; do_init runs to
completion (~9200 steps of real allocator / name-table / scene-root setup).

Remaining blocker (documented in the tier-0 memory): create()'s switch needs
register-indexed integer loads (VR_REMOT.S: `ld.l r30(r31),r31`) together
with the correct .data link base (~0x1000, not 0) -- the two errors currently
cancel for the immediate paths but break the indexed jump-table dispatch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 09:35:44 -05:00
CydandClaude Fable 5 20dbd90026 Tier 0 emulator: wire-command replay harness (emu_replay.py)
Feeds the real captured VelociRender wire (dpl3-revive/patha/*.raw.bin, from the
soft-renderer work) to the firmware's own command handlers in emu860 -- the path
to building a scene and rendering a frame with the board's own code.

- Parses captures via vrboard's Assembler (skips the .BTL boot, frames
  action+payload). cap7 = a clean small scene (17 zones / 22 instances / 24
  geometry / 1 view).
- Calls each command's handler directly with the payload pointer in r16 --
  verified valid: create's prologue reads [r16+0]=type, [r16+4]=handle, i.e. the
  raw wire layout (no CCB wrapping needed). Bypasses the un-emulated transputer.
- Confirmed WIRE-action -> handler map by function identity (create/flush/
  dcs_link/list_add/draw_scene/statistics/set_texmap_texels).

Two blockers identified for a full frame (documented in the memory + README):
  1. The dispatch jump table is indexed by an INTERNAL command code, not the
     wire action (velocirender_input remaps wire->internal first, e.g. draw=9
     ->12, flush=3->30); the high-frequency per-frame commands (0x09, 0x1d
     artics, 0x2a) still need that remap reversed.
  2. init(0) must run first to set up the allocator/name-table/scene-root --
     cold create() faults without it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 07:01:52 -05:00
CydandClaude Fable 5 e28232e409 Tier 0 emulator: i860 interpreter boots VREND.MNG + runs real render code
emu860.py -- an i860 interpreter (reuses the dis860 decoder) that executes the
real Division firmware. This is the foundation of the preservation-grade
emulator (run the board's own code, vs the GL bridge which interprets the wire).

- Boots VREND.MNG cleanly: i860 init (psr/dirbase/fsr), enables the MMU, then
  idle-spins at 0xf0400590 waiting for a transputer interrupt (init complete).
  Board-config regs modelled (0xfffff720=2, 0xfffff70c=1) via map_control().
- Call harness (cpu.call): invoke firmware functions directly, bypassing the
  (un-emulated) transputer that normally drives the CCB.
- Correct memory map: .data/.bss linked LOW (DATA_BASE=0), so globals resolve.
- FP unit with double precision (register pairs): fadd/fsub/fmul/famov/frcp/
  frsqr/fix/ftrunc/fgt/feq/fxfr/fiadd/fisub.
- Delay slots, control regs, sparse MMIO memory with trap/logging, tail-trace
  and stopat debug aids.

draw_scene now runs real code (incl. a double int->float conversion) until it
needs accumulated scene state -- next step is replaying a captured wire command
sequence through the command dispatcher (jump table @0x47cb8) so the scene
builds and draw_scene emits the IGC coefficient stream (Tier-1 handoff).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 23:38:25 -05:00
CydandClaude Fable 5 1323397a50 pvision solved (texture-value ramp) + i860/hardware reverse-engineering
Predator/IR vision: reverse-engineered from the original firmware and
confirmed by the build team -- it is the Division board's TEXTURE-VALUE RAMP
mode (a "check your texture maps" diagnostic the devs hijacked), NOT a
grayscale squash or a false-colour palette. Located in VREND.MNG (effect
handler @0xe6c0, wire action 0x1b, type -1 ON / -2 OFF); ramp colours from
VR_DRAW.C. Renderer reworked to match: vrview_gl now does the 4-ramp
lerp(color0,color1,luminance(texel)) in the mesh pass (grayscale+defog
removed). Live-rendered on a new night-clear arena egg; crew A/B verdict
pending.

Firmware-decomp toolchain (emulator/firmware-decomp/), all built from the
project's own artifacts and validated:
- coff860.py    i860 COFF reader (symbols/sections), names match AS860 source
- derive860.py  derives the i860 opcode map from matched .S<->.O pairs
- dis860.py     i860 disassembler (98% on clean ground truth; proven on
                VREND.MNG -- velocirender_statistics decodes correctly)
- sigmatch860.py reloc-invariant signature matcher onto the stripped image
- i860-encoding.md / FIRMWARE-SYMBOLS.txt / README.md

PVISION-IMPLEMENTATION-GUIDE.md: self-contained hand-off for the BT411 team.

HARDWARE-ARCHITECTURE.md + hardware-photos/ (15 board shots): the Division
VelociRender card is a 2-board stack driving a 3-processor pipeline --
INMOS IMS T425-J25S (comms/control, runs vrendmon.btl) + Intel i860 XP-50 (FP
geometry, runs vrender.mng) + Division PXPL IGC 5.2 ASIC with ~48x PXPL EMC
5.1 (UNC Pixel-Planes-5 SIMD array; "EMC" = the firmware's configEMCs) +
Analog Devices ADV7150 RAMDAC + NTSC. Plus the VWE Video Distribution Board
(P/N 1404: AMD MACH130 + 3x Brooktree Bt477) for the 3-VGA-head cockpit split.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 22:29:53 -05:00
CydandClaude Opus 4.8 5593d57d48 Renderer: IR/thermal palette, bilinear default, searchlight (fog) + night egg
- IR / predator-vision thermal: the cockpit IR button toggles it via the
  wire (dpl_Effect on action 0x1b, mode -1 ON / -2 OFF -> board.pvision).
  vrview_gl present pass remaps scene luminance to a thermal palette
  (default heat/Predator; mono|green|amber via VRVIEW_PVISION_PALETTE;
  VRVIEW_PVISION / bridge 'v' key force it on). Exact palette + HUD-exempt
  pending crew review.
- Texture filter default flipped to bilinear (operator preference); the
  i860 board itself point-sampled, so VRVIEW_FILTER=nearest reverts. (A CRT
  present-pass bleed/scanline prototype was built and rejected; removed.)
- Searchlight: no code needed -- night A/B proved it is a VIEW-FOG push-back
  (fog near 5->60, far 400->500 on), already rendered by the existing view
  fog path. Not a light cone. Added TESTNITE.EGG (arena1/night/fog) +
  gauge_arena_night_pipe.conf for the test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 13:49:34 -05:00
CydandClaude Opus 4.8 aadda7f9a5 Cockpit camera complete + namedpipe transport + 832x512 native res
Render bridge (live_bridge.py, vrview_gl.py):
- Hat glances render (left/right frame the canopy, hat-down = clean rear).
  Root bug was a stale _ckpt['fix'] key -> KeyError every glance frame ->
  render aborted (screen froze on hold, snapped back on release). The glance
  itself is the authentic eye-DCS action-0x1f reflush fp_cam already applies.
- Torso twist turret-true (root-axis yaw, zero parallax); lasers follow torso.
- Rear glance drops the canopy shell for a clean view (original-hardware
  behavior); mission-fade shroud 9fd hidden.
- Wireframe debug mode (VRVIEW_WIREFRAME / 'w' key), scene-pass scoped.
- Renderer output = 832x512, the dPL3 board's native framebuffer res.

DOSBox-X fork: namedpipe serial backend (serialnamedpipe.cpp/.h) for
vRIO/vPLASMA, replacing com0com; overlapped non-blocking I/O; typed frames
(0x00 data / 0x01 DTR+RTS). Tracked copies + apply steps in vpx-device.

Docs: COCKPIT-CAGE-NOTES (full glance/twist/rear forensics), XP-PORT-PLAN
(back-burnered), RIO-NOTES (namedpipe + keypad), pipe/egg conf variants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:08:15 -05:00
CydandClaude Fable 5 873882b4be Deploy: mark the packaged archive INSTALL-VERIFIED (2026-07-11)
Fresh extract of the final TeslaPod410.zip (renderer bundled) to C:\Games,
configured via configure.ps1, launched end-to-end on the co-located rig --
user-verified working as expected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 16:35:14 -05:00
CydandClaude Fable 5 8cecb6e5ca Deploy: pod-launch explains an unconfigured install instead of suggesting --conf
A fresh extract has net_*.conf.tmpl but no rendered confs until postinstall/
configure.ps1 runs; the old "pass --conf" error led straight to renaming the
template by hand (which then breaks configure: the tokens never substitute
and the tmpl is no longer found). Detect the sitting .tmpl and point at the
real fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 16:15:28 -05:00
CydandClaude Fable 5 198b73eb96 Deploy: freeze the render bridge to renderer.exe -- archive is now complete
render-bridge/freeze.ps1: PyInstaller onefile of live_bridge.py + Dave's
vrboard/vrview/vrview_gl (+glcontext collected for moderngl) -> dist\
renderer.exe (~33 MB). GL backend live-verified from a pure-dist launch
(no --root/--renderer overrides) on 2026-07-10. package.ps1 bundles the
frozen exe automatically when present; DEPLOYMENT-PLAN's renderer OPEN
item is resolved. Ignore the build-artifact dirs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:57:14 -05:00
CydandClaude Fable 5 33a192a1fc Deploy: Npcap is a one-time MANUAL install per cockpit (operator consensus)
Retire the bundled-silent-installer requirement: postinstall now detects an
existing Npcap service and moves on, warns clearly when absent, and still
honors a bundled deploy\npcap.exe if one is shipped. package.ps1 no longer
flags the missing bundle as a warning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:49:36 -05:00
CydandClaude Fable 5 4adfaf740a Deploy: configure.ps1 hardening + co-located smoke-test caveats in the plan
configure.ps1: bay-IP pick now filters to Up physical adapters and fails
loudly on multiple candidates instead of coin-tossing by InterfaceMetric;
-BayIp forces the choice (and permits binding a virtual adapter on a dev
rig); -ConsoleIp sets WATTCP gateway/nameserver (default: the bay IP --
needs only to be a live on-subnet host, the console DIALS the pod);
-Root is validated up front.

DEPLOYMENT-PLAN: record the four first-smoke-test findings (2026-07-10,
end-to-end egg->mission achieved): SendToRxAdapters on the pod NIC, host
must not hold the game IP, NIC checksum offload silently kills co-located
console->pod IP (ARP works, TCP silent -- disable offload+LSO), and bt/rp
require sound + a packaged renderer (closes the renderer OPEN item: ship one).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:47:24 -05:00
CydandClaude Fable 5 7710be1a07 Deploy: pod-launch warns that --no-sound freezes bt/rp
--no-sound strips VWE_AWE32, the FAST (28Hz SOS) clock never ticks, and
BTL4OPT hangs forever in the RIO-reset busy-wait (L4RIO.cpp SetDTR loop on
a frozen Now()) -- looks exactly like a vRIO/serial failure but isn't.
Root-caused during the 2026-07-10 dist smoke test. Only safe with a
SLOW-clock conf (setenv arg2=s). Warn at launch + document in usage/README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:47:24 -05:00
CydandClaude Fable 5 a8e463e784 Emulator: kernel BPF RX filter for the pcap NIC + NE2000 accept/drop diagnostics
Real-NE2000 semantics: accept only own-MAC unicast + broadcast (multicast
excluded -- the guest stack is unicast+ARP only), and drop npcap's loopback
of our own injected frames. Without it, a live-LAN host download floods the
emulated NIC at wire rate (65k+ frames/min) and wedges netnub before boot
("discarding..."). Filter failure is non-fatal: logs and runs unfiltered.
Also log the first 12 unicast accepts/drops with the live PAR registers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:47:05 -05:00
CydandClaude Opus 4.8 131f58b4a0 Deploy: full console argument reference in pod-launch/README
Complete arg surface of the single installed entry-point -- mode selector
(bt/rp + recognized-unsupported), feature toggles, window placement, path
overrides, diagnostics, and the exit codes the console reads back (game
ExitCodeID passed through; 2=bad args; 3=mode not yet wired).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 14:34:51 -05:00
CydandClaude Opus 4.8 e65ff3f8d7 Deploy: RP loops (no single-shot) + confirm DOSBox needs no bundled DLLs
- net-boot/loop_rp.bat: GO.BAT-style netnub loop for RP (mirror of loop.bat,
  retargeted to rpl4opt). deploy/net_rp.conf.tmpl now calls it instead of the
  single-shot netnub + DOS pause -- the deployable runs in a loop until the
  supervisor kills DOSBox (per operator: no single-shot in the product).
- DLL closure resolved: dosbox-x.exe is a 182MB STATIC build -- import table is
  only Windows system DLLs, delay-import table empty, libpng/SDL/zlib embedded.
  Nothing to bundle; wpcap.dll comes from the Npcap install. package.ps1's "no
  DLLs" case is now informational, and the plan/README note the static build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:06:49 -05:00
CydandClaude Opus 4.8 9df87f2c01 Deploy: pod install scaffold -- supervisor entry-point, postinstall, packaging
Network-agnostic, air-gapped pod install for the two DOSBox titles (BT/RP 4.10),
fitting the existing TeslaConsole/TeslaLauncher pod-bay architecture.

emulator/DEPLOYMENT-PLAN.md
  Full design: bridge on the one NIC (pods form a source-proven P2P TCP mesh, so
  NAT/slirp is out); launcher keeps the bay IP, the DOSBox guest bridges at
  bayIP+100 (egg/mesh/game endpoint); two-edit +100 convention (postinstall +
  console DOSBox flag); static, air-gapped, <=32 pods.

emulator/pod-launch/  (C# net8 supervising entry-point)
  Creates a Job Object (KILL_ON_JOB_CLOSE), launches DOSBox-X + the render bridge
  into it and blocks -- kill this process and both die (kernel-enforced, even on
  a hard TerminateProcess of a hung session; verified). Mode dispatch: bt/rp
  wired (also serve camera + live mission-review via egg hostType); review +
  diagnostics recognized but fail clean until their DOS launch args are known.

emulator/deploy/  (install side)
  postinstall.bat (thin elevated wrapper) + configure.ps1 (NIC detect, realnic
  bind as a contiguous letter-leading GUID fragment, game IP = bayIP+100, stable
  MAC, render net_*.conf templates, stamp WATTCP.CFG my_ip) + tokenized conf
  templates + package.ps1 (assemble the zip). Render/bind/stamp + packaging
  verified against a scratch tree.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:36:30 -05:00
CydandClaude Fable 5 d1635d4694 Deploy: drop -SwapMFD switch -- edit the RECTS x-coords to swap MFD heads (simpler)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 19:55:06 -05:00
CydandClaude Fable 5 2d5f1562bd Deploy: -SwapMFD flag to trade the two MFD head positions (win3<->win4)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 19:53:46 -05:00
CydandClaude Fable 5 125d835a05 Deploy: pod_deploy.ps1 cockpit window layout + focus_dosbox.ps1 (renderer topmost, DOSBox focused)
Real-pod deployment launcher for the original VDB + octopus 4-head wiring:
VPX_COCKPIT borderless head windows (radar/win0 800,0; 3-color MFD/win4 1440,0;
2-color MFD/win3 2080,0; all 640x480) + the GL bridge as the main out-the-window
view (0,0, 800x600 via new BRIDGE_W/BRIDGE_H in live_bridge.py). Editable RECTS
at the top of the script for other rigs.

focus_dosbox.ps1 restores the deployment window state: renderer -> always-on-top
(WS_EX_TOPMOST, game view never covered) + DOSBox-X -> 10,10 with foreground
focus (keeps the emu thread at foreground priority so the RIO doesn't starve).
Coexist cleanly: topmost renderer stays above the focused-but-non-topmost DOSBox.
pod_deploy calls it last; re-run anytime the stack is disturbed. LAUNCH.md
documents both.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 19:44:45 -05:00
CydandClaude Fable 5 97028fc983 collab: RP runs full pod; open renderer item = RP track-decal z-fighting + vrview_gl merge points for David
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 19:31:46 -05:00
CydandClaude Fable 5 e1b63775e6 VDB-NOTES: RP heads fully verified -- all MFDs + radar correct (RP VDB DONE)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 19:24:00 -05:00