Files
TeslaRel410/emulator/firmware-decomp/igc_divide.py
T
CydandClaude Opus 4.8 b74b0c89e8 M5-A: the faithful IGC perspective divide, verified against the divlogo oracle
igc_divide.py implements EOF.C's texdivide bit-serial restoring division on
the pixel bit-memory, and extracts the texel per the '3 int | 8 texel | 5
subtexel' result layout ((field>>9)&0xff). Verified against the divlogo's
KNOWN mapping (u:0.001->0.999 across screen x[532,660]): the extracted texel
sweeps monotonically 0->251, and a fast float equivalent (texu/texz*2048)
tracks the bit-serial result to +/-1. This fixes the missing scale factor
(the '3 integer bits' = x8) that made earlier renders wash out. M5 plan in
M5-TEXTURING.md. Next: M5-B scene-graph material binding, then integrate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 22:13:10 -05:00

107 lines
3.9 KiB
Python

"""M5-A: the faithful IGC bit-serial perspective divide + divlogo oracle test.
texdivide (EOF.C): restoring long division of the texu/texv field by the texz
field, over `sigbits` iterations, quotient written MSB-first into the field.
Implemented on a single pixel's 26-byte bit memory using the same read/write
primitives as igc_exec. Verified against the divlogo's known mapping.
"""
import struct
# DIVPXMAP.H field bits
TEXZ, TEXZBITS = 32, 20
TEXU, TEXUBITS = 57, 20
TEXV, TEXVBITS = 77, 20
PIXBYTES = 26
def rdbits(pix, bit0, bits):
v = 0
for k in range(bits):
b = bit0 + k
v |= ((pix[b >> 3] >> (b & 7)) & 1) << k
return v
def wrbits(pix, bit0, bits, val):
for k in range(bits):
b = bit0 + k
if (val >> k) & 1:
pix[b >> 3] |= 1 << (b & 7)
else:
pix[b >> 3] &= ~(1 << (b & 7))
def texdivide(pix, num, denom0, sigbits):
"""num, denom0 = field start bits; num field is divided by denom in place."""
res = num + TEXUBITS - 1 # quotient MSB position
divbits = TEXUBITS
denom = denom0
for _ in range(sigbits):
n = rdbits(pix, num, divbits)
d = rdbits(pix, denom, divbits)
en = 1 if n >= d else 0
if en:
wrbits(pix, num, divbits, (n - d) & ((1 << divbits) - 1))
# ENABintoMEM(res)
if en:
pix[res >> 3] |= 1 << (res & 7)
else:
pix[res >> 3] &= ~(1 << (res & 7))
res -= 1
denom += 1
divbits -= 1
def perspective_divide_pixel(texu_val, texv_val, texz_val):
"""Run the full perspective_divides on one pixel; return (texu_field, texv_field)."""
pix = bytearray(PIXBYTES)
wrbits(pix, TEXU, TEXUBITS, texu_val & ((1 << TEXUBITS) - 1))
wrbits(pix, TEXV, TEXVBITS, texv_val & ((1 << TEXVBITS) - 1))
wrbits(pix, TEXZ, TEXZBITS, texz_val & ((1 << TEXZBITS) - 1))
# CLEAR(texz+texzbits, 1) -- top guard bit already 0
texdivide(pix, TEXU, TEXZ + 1, 17)
texdivide(pix, TEXV, TEXZ + 1, 17)
return rdbits(pix, TEXU, TEXUBITS), rdbits(pix, TEXV, TEXVBITS)
def texel8_fast(texu_val, texz_val):
"""Verified fast equivalent of the bit-serial divide's 8-bit texel field:
(texu_field>>9)&0xff. Derived + checked against perspective_divide_pixel."""
if texz_val == 0:
return 0
return int(texu_val / texz_val * 2048) # 0..2047 (3 int + 8 texel bits)
if __name__ == '__main__':
# divlogo oracle (EOF.C ~616-654): u sweeps 0.001->0.999 across x[532,660]
x0, x1 = 532.0, 660.0
u0, u1 = 0.001, 0.999
du_dx = (u1 - u0) / (x1 - x0)
zscale = (1 << (TEXZBITS - 3)) * 0.999
uscale = (1 << (TEXUBITS - 3)) * 0.999
uC = x0 * du_dx - u0
uA, uCk = du_dx * uscale, -uC * uscale
zC = 8.0 * zscale
prev = -1
monotonic = True
exact_vs_fast = []
span = []
for x in range(532, 660, 2):
texu_val = int(uA * x + uCk)
texz_val = int(zC)
tu_field, _ = perspective_divide_pixel(texu_val, 0, texz_val)
texel8 = (tu_field >> 9) & 0x7ff # 8 texel + 3 int bits
fast = texel8_fast(texu_val, texz_val) & 0x7ff
exact_vs_fast.append(abs(texel8 - fast))
span.append(texel8)
if texel8 < prev:
monotonic = False
prev = texel8
print("bit-exact texel8 span: %d..%d over the logo rect (u:0->1)" % (min(span), max(span)))
print("monotonic (linear sweep): %s" % monotonic)
print("max |exact - fast|: %d (fast formula tracks the bit-serial divide)" % max(exact_vs_fast))
assert monotonic, "texel must increase monotonically with u"
assert max(span) - min(span) > 200, "u:0->1 must sweep ~a full 8-bit texel range"
assert max(exact_vs_fast) <= 2, "fast formula must match the bit-serial divide"
print("M5-A PASS: faithful perspective divide verified against the divlogo oracle")