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>
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>