From 8ff234fe9c2ab4323ec8305fef948719e1b0d8e7 Mon Sep 17 00:00:00 2001 From: Cyd Date: Fri, 17 Jul 2026 23:10:05 -0500 Subject: [PATCH] IGC decode: sweep-family 2-word format derived; doubling-chain myth busted The firmware's SENDE emitter (disassembled at 0xf041db90 via builder_trace) proves the captured 0x8x01213a words are op-0x21 instructions whose len-field (136-k)<<23 decrements through the IEEE exponent bits -- the 'x2 doubling chain floats' of the earlier decode were an artifact, not coefficients. The 69-word SENDE is a fixed bit-serial transform program (texz -> texu region); real per-primitive coefficients are in the SEND(33)/SEND(41) blocks (builders at 0xf04126xx, next disasm target). 2-word sweep format: {op|dst|(len1+116)<<23, src|dst<<8|len2<<16}, verified word-exact. Co-Authored-By: Claude Opus 4.8 --- .../IGC-ENCODING-DERIVATION.md | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/emulator/firmware-decomp/IGC-ENCODING-DERIVATION.md b/emulator/firmware-decomp/IGC-ENCODING-DERIVATION.md index 5751a27..6290bb7 100644 --- a/emulator/firmware-decomp/IGC-ENCODING-DERIVATION.md +++ b/emulator/firmware-decomp/IGC-ENCODING-DERIVATION.md @@ -121,6 +121,69 @@ PXPL5OPT.S (rev differs, both useful) + EOF.S. Grep pattern: `0x[0-9a-f]{4,6} \| parenthesized expressions — eval them with the known (op,addr,len) from the .SS source lines to fill the whole op table. +## The bit-serial SWEEP family (firmware's builder) — 2-word format DERIVED + +The firmware's own packet builder (and EOF.C's linterp/multuu_unc) emit 2-word +memory-op instructions. From _linterp compiled (EOF.S 663-860): + +``` +op-word = BASE | dstaddr | ((len1+116)&0x7f)<<23 (BASE e.g. 0x80006c00 = MEMpluseqMEM-family K) +operand = srcaddr | dstaddr<<8 | (len2&0x7f)<<16 +``` + +VERIFIED against capture: `{0xba01253a, 0x00143a21}` → op 0x25 dst=58, operand +src=0x21(33) dst=0x3a(58)✓ len=0x14(20). The 0x48-family builds as +`(len+116)<<23 | 0x4800 | addr` (linterp line 699-707, emitted right after +SETENABS). `104192 = 0x19700` = the 0x97-family base (bit16 set) — captured +`0x0001979d` = 0x19700|0x9d ✓; bare `0x0001008f` = bit16 operand words. +CPY 2-word pairs in the eof blocks: `{0xbb004cb3, 0x0001008f}` = +CPY(dst=179, src=143=texid) etc. Known family bases so far: 0x25??, 0x21 (with +decrementing len = the multuu cascade), 0x6c (K=0x80006c00), 0x4c (CPY), +0x48, 0x97(+bit16), 0x1f, 0xfa, 0x39, 0xec (last four TBD — likely +SHIFTL/TBLENTRY/OVFIX/SPLAT-family from IGCOPS.C). + +The 69-word SENDE block structure: SETENABS + 3-word lead-in + 16 pairs of +{0x25-op, operand}/{0x21-op, operand} with len1 decrementing 9..0 and src +walking 0x21..0x31 = a bit-serial multiply cascade (multuu_unc's exact shape: +`IGC_MEMpluseqMEM(res+j, Ub, Rlen-j, Ublen)`). + +Builder-trace tooling: scratchpad/builder_trace.py logs (pc, addr, val) for all +firmware writes into 0x08015000-0x08015800 during cap7 build → writer pc +histogram names the firmware's builder fns for disassembly. + +## FIRMWARE SWEEP EMITTER READ (builder trace + disasm @0xf041db90-dc50) — the +## "x2 doubling chain" was an ARTIFACT, not data + +builder_trace.py pinned the SENDE(69) emitter (writes at cmd 737 = draw#1): +loop constants r9 = 0x80012500|dst|(116<<23) (= 0xba01253a for dst=0x3a), +r10 = 0x80012100|dst, r8 = dst<<8. Per bit k (k=0..19, 4 words): + +``` +w0 = 0x80012100 | dst | (((136-k)&0x7f)<<23) ; op 0x21 aux 1, len-field decrementing +w1 = src+k ; bare operand word +w2 = 0x80012500 | dst | (116<<23) ; op 0x25 aux 1, len=0 (constant) +w3 = (src+k) | dst<<8 | ((20-k)<<16) ; operand: src | dst<<8 | len<<16 +``` + +**The len-field (136-k) walks straight through the IEEE exponent bits, so w0 +"decodes" as a float that halves each step — the earlier doubling-chain reading +(MICROCODE-DECODE-NOTES.md) was a coincidence artifact. These words are +INSTRUCTIONS, not coefficients.** The SENDE(69) is a fixed bit-serial transform +program: src bits 33..52 (texz) → dst bit 58 (texu region) — per-frame plumbing, +not per-triangle data. The real primitive coefficients live in the SEND(33)/ +SEND(41) blocks (builders = the 0xf04126xx cluster, 336-344 writes each) and the +SEND(4) edge block (0xf041fc64/fc84/fc9c; word1 0x3e013991 computed = a real +float, word2 0xec00 = fixed-point .8 coordinate 236.0). + +Lead-in confirmed: {0x100 SETENABS, 0x3a804834 = op 0x48 addr 52 (opacity) +len 1 = the 50% stipple enable}. + +NEXT for the decoder: (1) disassemble the 0xf04126xx builders (the 33/41-block +emitters) the same way — builder_trace.py already exists, just widen the range / +dump more words; (2) implement the sweep ops (0x21/0x25 pair = bit-serial +add/carry per IGCOPS.C MEMpluseqMEM semantics) + run the whole per-tile program +in igc_exec; (3) bars. + ## NEXT (the miner) Write `eofs_mine.py`: parse EOF.S per function; track `st.l`/`fst.l` stores through the coeff pointer; recover, in order, each emitted word (int literals