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>
65 lines
2.8 KiB
Python
65 lines
2.8 KiB
Python
"""Minimal i860 COFF (magic 0x014d) reader: dump file header, sections, and the
|
|
symbol table (function/global names + section + value). Deterministic -- no
|
|
instruction decoding -- so it is a trustworthy reference map of the firmware
|
|
objects. Validates the format by checking the symbol names look like the known
|
|
AS860/PGI source (e.g. _createBang, _PAZsfx)."""
|
|
import sys, struct, glob, os
|
|
|
|
def rd(b, off, fmt): return struct.unpack_from(fmt, b, off)
|
|
|
|
def parse(path):
|
|
d = open(path, 'rb').read()
|
|
magic, nscns, timdat, symptr, nsyms, opthdr, flags = rd(d, 0, '<HHIIIHH')
|
|
out = {'path': path, 'magic': magic, 'nscns': nscns, 'nsyms': nsyms,
|
|
'symptr': symptr, 'opthdr': opthdr, 'flags': flags,
|
|
'sections': [], 'syms': []}
|
|
# section headers
|
|
base = 20 + opthdr
|
|
for i in range(nscns):
|
|
o = base + i * 40
|
|
name = d[o:o+8].split(b'\0')[0].decode('latin1')
|
|
paddr, vaddr, size, scnptr, relptr, lnnoptr, nreloc, nlnno, sflags = \
|
|
rd(d, o + 8, '<IIIIIIHHI')
|
|
out['sections'].append((name, vaddr, size, scnptr, nreloc, sflags))
|
|
# symbol table (18-byte SYMENT) + string table right after
|
|
strbase = symptr + nsyms * 18
|
|
def symname(o):
|
|
z, offs = rd(d, o, '<II')
|
|
if z == 0: # name is an offset into the string table
|
|
e = d.index(b'\0', strbase + offs)
|
|
return d[strbase + offs:e].decode('latin1')
|
|
return d[o:o+8].split(b'\0')[0].decode('latin1')
|
|
i = 0
|
|
while i < nsyms:
|
|
o = symptr + i * 18
|
|
nm = symname(o)
|
|
value, scnum, typ, sclass, naux = rd(d, o + 8, '<iHHBB')
|
|
out['syms'].append((nm, value, scnum, typ, sclass, naux))
|
|
i += 1 + naux
|
|
return out
|
|
|
|
SCLASS = {2: 'EXT', 3: 'STAT', 6: 'LABEL', 100: '.bb', 101: '.file'}
|
|
|
|
def report(o, want_funcs=True):
|
|
print(f"\n=== {os.path.basename(o['path'])} magic={o['magic']:#06x} "
|
|
f"nscns={o['nscns']} nsyms={o['nsyms']} ===")
|
|
for (name, vaddr, size, scnptr, nreloc, sf) in o['sections']:
|
|
print(f" sect {name:<10} vaddr={vaddr:#010x} size={size:#08x} "
|
|
f"reloc={nreloc}")
|
|
# external/static functions (scnum>0, class EXT/STAT), text-ish
|
|
funcs = [(nm, v, sc) for (nm, v, sn, ty, sc, na) in o['syms']
|
|
if sn > 0 and sc in (2, 3) and not nm.startswith('.')]
|
|
print(f" -- {len(funcs)} defined EXT/STAT symbols:")
|
|
for nm, v, sc in sorted(funcs, key=lambda x: x[1]):
|
|
print(f" {v:#010x} {SCLASS.get(sc,sc):<5} {nm}")
|
|
|
|
if __name__ == '__main__':
|
|
pats = sys.argv[1:] or [r'C:\VWE\TeslaRel410\sda4\DPL3\VRENDER\*.O']
|
|
files = []
|
|
for p in pats: files += glob.glob(p)
|
|
for f in sorted(files):
|
|
try:
|
|
report(parse(f))
|
|
except Exception as e:
|
|
print(f"\n=== {f}: PARSE FAILED: {e} ===")
|