diff --git a/emulator/firmware-decomp/render-readout.html b/emulator/firmware-decomp/render-readout.html index fb8faa2..a440546 100644 --- a/emulator/firmware-decomp/render-readout.html +++ b/emulator/firmware-decomp/render-readout.html @@ -53,6 +53,30 @@ letter-spacing:.05em;color:var(--mute);line-height:1.5;border-top:1px solid var(--line)} .duo figcaption b{color:var(--phos)} + .arr{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:18px;align-items:start} + @media(max-width:640px){.arr{grid-template-columns:1fr}} + .arr figure{margin:0;border:1px solid var(--line2);border-radius:10px;overflow:hidden; + background:linear-gradient(180deg,#05090d,#070d13)} + .arr canvas{display:block;width:100%;height:auto} + .arr figcaption{padding:10px 13px;font-family:var(--mono);font-size:11px;color:var(--mute); + line-height:1.5;border-top:1px solid var(--line)} + .arr figcaption b{color:var(--phos)} + .arrinfo{display:flex;flex-direction:column;gap:14px;padding-top:4px} + .pmlab{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase; + color:var(--faint)} + .pmbar{display:flex;gap:2px;height:34px;font-family:var(--mono);font-size:10px;font-weight:600} + .pmbar span{display:flex;align-items:center;justify-content:center;border-radius:3px; + color:#04070a;letter-spacing:.02em;white-space:nowrap;overflow:hidden} + .pmbar .z{flex:24;background:var(--cyan)} + .pmbar .r{flex:8;background:#ff6b6b} + .pmbar .g{flex:8;background:var(--phos)} + .pmbar .b{flex:8;background:#6ba8ff} + .pmbar .free{flex:160;background:#12202a;color:var(--faint)} + .mini{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:8px} + .mini li{position:relative;padding-left:18px;font-size:13.5px;color:var(--mute);line-height:1.45} + .mini li::before{content:"›";position:absolute;left:2px;color:var(--cyan);font-weight:700} + .mini li b{color:var(--ink)} + .hero-render .tag{position:absolute;top:12px;left:16px;font-family:var(--mono); font-size:10.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--faint); border:1px solid var(--line2);border-radius:5px;padding:4px 9px;z-index:2} @@ -94,9 +118,11 @@ .stage .d{font-size:12.5px;color:var(--mute)} .stage.on .v{color:var(--phos)} .stage.wait .v{color:var(--amber)} + .stage.sim .v{color:var(--cyan)} .stage .dot{position:absolute;top:14px;right:14px;width:7px;height:7px;border-radius:50%} .stage.on .dot{background:var(--phos);box-shadow:0 0 8px var(--phos)} .stage.wait .dot{background:var(--amber);box-shadow:0 0 8px var(--amber)} + .stage.sim .dot{background:var(--cyan);box-shadow:0 0 8px var(--cyan)} /* two-column data */ .cols{display:grid;grid-template-columns:1.05fr .95fr;gap:20px} @@ -195,7 +221,7 @@
cpu
i860
transform · clip · classify
geom
Binitize
per-region 64×128 bins
out
IGC DMA
SEND / TILE / FLUSH stream
-
px
Pixel-Planes
bit-serial array — next build
+
px
Pixel-Planes
array model simulated · §05
@@ -274,7 +300,45 @@
-

05 What it took to get here

+

05 The Pixel-Planes array, simulated

+

This is the final stage in silicon — and it now runs in software. The + PXPL5 IGC is a region rasteriser: the screen splits into + 64 × 128 tiles; every pixel carries a 26-byte bit-memory and an enable bit; all + pixels evaluate one linear tree in lockstep — + eval_ltree(x,y,A,B,C) = (int)(x·A + y·B + C). A triangle becomes + three edge trees into the enable register, then Z and R/G/B planes + interpolated per pixel and z-buffered in pixel memory (IGCOPS.C). + Fed the captured surface, the model lights the tiles below and renders pixels that match our + reference rasteriser to within edge anti-aliasing — the array reproduced faithfully.

+
+
+ +
the object's footprint across the array — 18 of 50 tiles lit, + each a 64×128 region of pixel processors
+
+
+
pixel memory · 26 bytes = 208 bits
+
+ Z · 24bR·8G·8 + B·8enable + working bits +
+
    +
  • three edge trees → the enable register (the inside test)
  • +
  • Z / R / G / B are planes, evaluated per pixel by the linear tree
  • +
  • z-buffer via MEM2geMEM2; every write gated by enable
  • +
  • output read back out of pixel memory → framebuffer
  • +
  • validated pixel-faithful to the reference — ~1%, at edges only
  • +
+
+
+

What this is not, yet: a decode of the compiled + bit-serial micro-code the DMA actually ships (§02, right). That binary encoding is still + undecoded, and it is how the ground and sky — which carry no stored vertices — reach the array. + This is the array's computational model: the pixels that micro-code would produce.

+
+ +
+

06 What it took to get here

The i860 core was corrected against the toolchain's own assembler output and MAME's validated i860 model. A selection of the load-bearing fixes:

@@ -299,15 +363,16 @@
  • Real PXPL5 IGC opcode stream emitted and decoded (SEND / TILE / FLUSH)
  • Geometry binned into 191 screen regions
  • Object recovered and shaded — a 9×5 height-field surface, lit by the firmware's own normals (above)
  • +
  • Pixel-Planes array simulated (§05) — 64×128 tiles, per-pixel memory, edge trees + z-buffer, validated pixel-faithful
  • The last mile to pixels

    @@ -315,7 +380,7 @@ @@ -556,5 +621,36 @@ bx.fillRect(px,py,CELL,CELL); } } + + /* ---- IGC tile footprint: which 64x128 tiles the array lit for the object ---- */ + var TILES={ntx:10,nty:5,touched:[[0,2],[1,1],[1,2],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3],[4,1],[4,2],[4,3],[5,1],[5,2],[6,1],[6,2],[7,1],[8,1]]}; + (function(){ + var tc=document.getElementById('tiles'), tx=tc.getContext('2d'); + var W=tc.width, H=tc.height, NX=TILES.ntx, NY=TILES.nty; + var lit={}; TILES.touched.forEach(function(t){lit[t[0]+'_'+t[1]]=1;}); + var gap=3; + var cw=(H-2*gap)/(NY*2); // enforce 64:128 -> cellH = 2*cellW + var ch=cw*2; + var gw=NX*cw, gh=NY*ch; + var ox=(W-gw)/2, oy=(H-gh)/2; + tx.clearRect(0,0,W,H); + for(var ty2=0;ty28?oy-4:10); + })(); })();