Files
TeslaRel410/emulator/firmware-decomp/derive860.py
T
CydandClaude Fable 5 1323397a50 pvision solved (texture-value ramp) + i860/hardware reverse-engineering
Predator/IR vision: reverse-engineered from the original firmware and
confirmed by the build team -- it is the Division board's TEXTURE-VALUE RAMP
mode (a "check your texture maps" diagnostic the devs hijacked), NOT a
grayscale squash or a false-colour palette. Located in VREND.MNG (effect
handler @0xe6c0, wire action 0x1b, type -1 ON / -2 OFF); ramp colours from
VR_DRAW.C. Renderer reworked to match: vrview_gl now does the 4-ramp
lerp(color0,color1,luminance(texel)) in the mesh pass (grayscale+defog
removed). Live-rendered on a new night-clear arena egg; crew A/B verdict
pending.

Firmware-decomp toolchain (emulator/firmware-decomp/), all built from the
project's own artifacts and validated:
- coff860.py    i860 COFF reader (symbols/sections), names match AS860 source
- derive860.py  derives the i860 opcode map from matched .S<->.O pairs
- dis860.py     i860 disassembler (98% on clean ground truth; proven on
                VREND.MNG -- velocirender_statistics decodes correctly)
- sigmatch860.py reloc-invariant signature matcher onto the stripped image
- i860-encoding.md / FIRMWARE-SYMBOLS.txt / README.md

PVISION-IMPLEMENTATION-GUIDE.md: self-contained hand-off for the BT411 team.

HARDWARE-ARCHITECTURE.md + hardware-photos/ (15 board shots): the Division
VelociRender card is a 2-board stack driving a 3-processor pipeline --
INMOS IMS T425-J25S (comms/control, runs vrendmon.btl) + Intel i860 XP-50 (FP
geometry, runs vrender.mng) + Division PXPL IGC 5.2 ASIC with ~48x PXPL EMC
5.1 (UNC Pixel-Planes-5 SIMD array; "EMC" = the firmware's configEMCs) +
Analog Devices ADV7150 RAMDAC + NTSC. Plus the VWE Video Distribution Board
(P/N 1404: AMD MACH130 + 3x Brooktree Bt477) for the 3-VGA-head cockpit split.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 22:29:53 -05:00

101 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Empirically derive the i860 opcode table from matched .S (PGI/AS860 assembly)
and .O (COFF machine code) pairs. Byte-accounting is proven correct by
cross-checking every .S label against its .O symbol offset; only then is the
op6->mnemonic harvest trusted."""
import sys, struct, re, glob, os
sys.path.insert(0, r'C:\VWE\TeslaRel410\emulator\firmware-decomp')
from coff860 import parse
DATASZ = {'.long':4, '.word':4, '.int':4, '.float':4, '.single':4,
'.double':8, '.short':2, '.half':2, '.byte':1, '.string':None}
def parse_S(path):
"""Return (labels{name:off}, instrs[(off,mnem,line)]) for the .text section."""
labels, instrs = {}, []
off = 0; sect = '.text'; in_text = True
for raw in open(path, encoding='latin1'):
line = raw.split('//', 1)[0].rstrip()
s = line.strip()
if not s:
continue
# label(s): possibly "name:" / "name::" possibly followed by an instr
while True:
m = re.match(r'^([A-Za-z_.$][\w.$]*)::?\s*(.*)$', s)
if m and (m.group(2) == '' or not m.group(2).startswith('.')) and ':' in s[:m.end(1)+2]:
# confirm it's really a label (colon right after the name)
after = s[m.end(1):]
if after.startswith('::') or after.startswith(':'):
if in_text: labels[m.group(1)] = off
s = after.lstrip(':').strip()
if not s: break
continue
break
if not s:
continue
if s.startswith('.'):
d = s.split()[0].lower()
if d in ('.text',): sect='.text'; in_text=True
elif d in ('.data','.bss','.section'): sect=d; in_text=False
elif d == '.align' and in_text:
n = int(s.split()[1]); off = (off + n - 1) & ~(n - 1)
elif d in DATASZ and in_text:
sz = DATASZ[d]
if sz is None: # .string -> count bytes incl NUL
txt = s[s.index('"')+1:s.rindex('"')]
off += len(txt.encode('latin1').decode('unicode_escape')) + 1
else:
vals = s[len(d):].split(',')
off += sz * len([v for v in vals if v.strip()])
# other directives: no size
continue
if in_text:
mnem = s.split()[0]
instrs.append((off, mnem, s))
off += 4
return labels, instrs
def load_text(opath):
o = parse(opath); d = open(opath, 'rb').read()
text = syms = None
for (name, vaddr, size, scnptr, nreloc, sf) in o['sections']:
if name == '.text': text = d[scnptr:scnptr+size]
syms = {nm: v for (nm, v, sn, ty, sc, na) in o['syms'] if sn == 1 and sc in (2,3)}
return text, syms
def derive(spath, opath, table):
labels, instrs = parse_S(spath)
text, syms = load_text(opath)
# cross-check label offsets vs symbol table
common = [k for k in labels if k in syms]
good = [k for k in common if labels[k] == syms[k]]
bad = [(k, labels[k], syms[k]) for k in common if labels[k] != syms[k]]
tag = os.path.basename(opath)
print(f"[{tag}] instrs={len(instrs)} textwords={len(text)//4} "
f"labels_ok={len(good)}/{len(common)}", end='')
if bad:
print(f" MISALIGNED e.g. {bad[:3]}")
return False
print(" ALIGNED")
for (off, mnem, line) in instrs:
if off + 4 > len(text): break
w = struct.unpack_from('<I', text, off)[0]
op6 = (w >> 26) & 0x3f
table.setdefault(op6, {}).setdefault(mnem, 0)
table[op6][mnem] += 1
return True
if __name__ == '__main__':
base = r'C:\VWE\TeslaRel410\sda4\DPL3\VRENDER\AS860'
pairs = []
for s in glob.glob(os.path.join(base, '*.S')):
o = s[:-2] + '.O'
if os.path.exists(o): pairs.append((s, o))
table = {}
for s, o in sorted(pairs):
try: derive(s, o, table)
except Exception as e: print(f"[{os.path.basename(o)}] ERROR {e}")
print("\n=== derived op6 -> mnemonic(count) ===")
for op6 in sorted(table):
items = sorted(table[op6].items(), key=lambda x: -x[1])
print(f" {op6:#04x} ({op6:06b}): " + ", ".join(f"{m}×{c}" for m,c in items))