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>
This commit is contained in:
@@ -155,3 +155,75 @@ case and read what it writes — the board-side fog disable (`_eof_doFOG`) and a
|
||||
DAC/colour-map change. That is the ground-truth answer to grayscale-squash+defog
|
||||
vs a palette. (Anchor utils seen: 0x2c330 = most-called util/log, 0x4408 =
|
||||
logger, 0xb120/0xb688 = effect helpers.)
|
||||
|
||||
## Tier 0 emulator: i860 interpreter (emu860.py) — BOOTS THE FIRMWARE
|
||||
|
||||
`emu860.py` executes the real `VREND.MNG` on an emulated i860 (reusing `dis860`'s
|
||||
decode). First run (`python emu860.py <VREND.MNG> --trace 30`): 5000 instructions,
|
||||
**zero unimplemented ops**. It runs the firmware's i860 boot sequence correctly —
|
||||
`psr`/`dirbase`/`fsr` setup, a board-register write at `0x8380_7000`, then it
|
||||
starts **polling the CCB at `0xFFFFF7xx`** (the `CM200IO.H` `INPUT_CCB` region),
|
||||
dispatching on `IO_BREAK`(1)/`IO_ACK`(2). I.e. the i860 is waiting on the
|
||||
**transputer** for work — exactly the T425↔i860 model.
|
||||
|
||||
Validated working: integer arith/logic/shift, loads/stores, control regs,
|
||||
branches + **delay slots**, basic single-precision FP.
|
||||
|
||||
**NEXT (Tier 0 cont.):**
|
||||
1. Model the **CCB handshake** (`0xFFFFxxxx`: `INPUT_CCB 0xF000`/`OUTPUT_CCB
|
||||
0xF300`, `CLEAR_INT`, the `IO_*` opcodes) so the firmware advances past the
|
||||
poll loop into its main command loop. Stub the transputer side: feed a
|
||||
captured wire in, ACK the CCB.
|
||||
2. Model the **board register region** (`0x8380_xxxx`) as it's touched.
|
||||
3. Add remaining instructions as the geometry path needs them (dual-instruction
|
||||
mode, the pipelined `pf*`/graphics FP ops).
|
||||
4. Capture the **IGC coefficient stream** the firmware emits (Tier 1 handoff).
|
||||
|
||||
Then Tier 1 = rasterize the captured coefficients; Tier 2 = the T425 + timing.
|
||||
|
||||
### Tier 0 update: full boot mapped → idle (waiting for transputer interrupt)
|
||||
|
||||
With `map_control()` feeding the board-config regs (`0xfffff720=2`,
|
||||
`0xfffff70c=1`), the firmware runs its whole low-level boot in ~73 instructions:
|
||||
clear the control-register block, write POST-style status codes, **enable the
|
||||
MMU** (`dirbase` ATE), then **idle-spin at `0xf0400590` (`bri r1`→self)**. That's
|
||||
init-complete — a self-spin breaks only on an **interrupt**, so the i860 is
|
||||
waiting for the transputer to hand it CCB commands. The whole application runs in
|
||||
the interrupt handler, not the boot path.
|
||||
|
||||
So the next phase is the **command-processing path**:
|
||||
1. i860 **interrupt/trap** (INT → PC←`0xFFFFFF00`) + the **MMU** (ATE is now on;
|
||||
try identity-map first).
|
||||
2. The **CCB** struct + a **transputer stub** that writes a command and asserts
|
||||
the interrupt.
|
||||
3. Inject a captured wire command → the handler dispatches (jump table @0x47cb8)
|
||||
→ runs → emits IGC coefficients (Tier-1 handoff).
|
||||
|
||||
emu860 debug aids added: `map_control()`, tail ring-buffer, `stopat`.
|
||||
|
||||
### Tier 0 update: call harness + real globals + FP core (draw_scene runs real code)
|
||||
|
||||
Autonomous session progress on emu860.py:
|
||||
- **Call harness** (`cpu.call(addr, args)`): invoke firmware functions directly
|
||||
(PGI conv: args r16.., ret r16, r1=return, r2=sp), bypassing the un-emulated
|
||||
transputer. Validated on `velocirender_statistics`.
|
||||
- **Data placement FIXED:** `.data`/`.bss` are linked LOW (code builds global
|
||||
addrs 0xebc4..0x22644). `DATA_BASE=0x0` (verified: globals now carry real data,
|
||||
e.g. `[0xebc4]` is ASCII, `[0x10]=0xffffffff`). Was the reason cold calls saw
|
||||
zero globals.
|
||||
- **FP unit rewritten with DOUBLE precision** (register pairs f[N]=low/f[N+1]=hi):
|
||||
fadd/fsub/fmul/famov/frcp/frsqr/fix/ftrunc/fgt/feq/fxfr/fiadd/fisub, precision
|
||||
from bits 7/8. (pf* pipelined ops treated non-pipelined for now.)
|
||||
- **Result:** `draw_scene` now executes real code (299 instrs incl. a double
|
||||
int→float conversion helper) until it needs SCENE STATE — cold-calling it on an
|
||||
empty scene follows uninitialised scene pointers and derails. Confirms a frame
|
||||
needs the full wire sequence replayed first.
|
||||
|
||||
**NEXT (the command-replay phase):** find the top-level command handler
|
||||
(`velocirender_input`/`remote_velocirender`; dispatcher head ~`0xf040eeb0`, jump
|
||||
table @file 0x47cb8), feed a **captured wire command sequence** (create/dcs/
|
||||
list_add → draw_scene) via a CCB struct at `0xffffe000`, so the scene builds and
|
||||
`draw_scene` renders → capture the IGC coefficient stream (Tier-1 handoff).
|
||||
Boot config regs: `map_control()` sets `0xfffff720=2`, `0xfffff70c=1`. Note the
|
||||
transputer set up the MMU/page-tables + trap vector, so the natural interrupt
|
||||
boot-flow needs the transputer OR the direct-call harness (used here).
|
||||
|
||||
Reference in New Issue
Block a user