THE WARP FIELD: trek's scene recovered from the frame's compiled payloads
Walking the bin-page DMA chains enumerated 898 SEND payload programs per frame: 4 = the standard end-of-frame pipeline; ~860 = the scene's per-primitive coefficient blocks at 0x815f000+, far beyond the old dump window. Their geometry sits in plain screen-space IEEE floats (+0x14/+0x18 and +0x24/+0x28): 433 star positions + 389 streaks radiating from a convergence point = the Star Trek warp-speed starfield, reconstructed from micro-code compiled by the original firmware on the emulated i860. Readout §06 now renders the warp field from the embedded position/segment data. Tools: dumpcontent.py (payload-region dump), content_frame.py (chain walk + payload parse + reconstruction). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
"""THE TRUE FRAME, attempt 1: walk the bin-page chains (trek_wide30) region by region;
|
||||
for each content SEND (payloads at 0x815xxxx+, from trek_content30) parse the payload,
|
||||
extract float coefficients, reconstruct edge triples -> triangle vertices (pairwise
|
||||
line intersection), place by the region's TILE origin, plot everything."""
|
||||
import pickle, struct, collections
|
||||
S = r'C:\Users\cyd\AppData\Local\Temp\claude\c--VWE-TeslaRel410\4e848c76-6e89-4034-8047-d8d491cb32d8\scratchpad'
|
||||
wide = pickle.load(open(S + r'\trek_wide30.pkl', 'rb'))['mem']
|
||||
content = pickle.load(open(S + r'\trek_content30.pkl', 'rb'))['mem']
|
||||
|
||||
def asf(w):
|
||||
return struct.unpack('<f', struct.pack('<I', w & 0xffffffff))[0]
|
||||
def isf(w):
|
||||
e = (w >> 23) & 0xff
|
||||
return 1 < e < 254 and 1e-6 < abs(asf(w)) < 1e6
|
||||
|
||||
# 1) per-region chains from bin pages: collect (send_addr, size) + tile id per page
|
||||
OPS = {0x1: 'SEND', 0x9: 'SENDE', 0x2: 'TILE', 0x3: 'TXDN', 0x6: 'FLUSH',
|
||||
0x0: 'GOTO', 0xf: 'STOP', 0x8: 'WAIT'}
|
||||
regions = [] # (page, tile_id, [(addr,size,op)])
|
||||
for page_base in sorted(set(a & ~0xfff for a in wide if a >= 0x0801e000)):
|
||||
sends = []; tile = None
|
||||
a = page_base
|
||||
while a < page_base + 0x800:
|
||||
w0 = wide.get(a); w1 = wide.get(a + 4)
|
||||
if w0 is None or w1 is None:
|
||||
a += 8; continue
|
||||
code = (w1 >> 28) & 0xf
|
||||
name = OPS.get(code)
|
||||
if name in ('SEND', 'SENDE') and w0 >= 0x08000000:
|
||||
sends.append((w0, w1 & 0x7f, name))
|
||||
elif name == 'TILE':
|
||||
tile = w0
|
||||
a += 8
|
||||
if sends:
|
||||
regions.append((page_base, tile, sends))
|
||||
print("regions with sends: %d" % len(regions))
|
||||
|
||||
# 2) parse content payloads: floats per block
|
||||
def parse_payload(addr, size):
|
||||
floats = []
|
||||
for i in range(size):
|
||||
w = content.get(addr + i * 4)
|
||||
if w is None:
|
||||
w = wide.get(addr + i * 4)
|
||||
if w is None: continue
|
||||
if isf(w):
|
||||
floats.append((i, asf(w)))
|
||||
return floats
|
||||
|
||||
# 3) reconstruct: for each region, each content send -> triangle from edge triples
|
||||
def isect(e1, e2):
|
||||
(a1, b1, c1), (a2, b2, c2) = e1, e2
|
||||
d = a1 * b2 - a2 * b1
|
||||
if abs(d) < 1e-9: return None
|
||||
return ((b1 * c2 - b2 * c1) / d, (a2 * c1 - a1 * c2) / d)
|
||||
|
||||
tris = [] # (tile, [(x,y)..])
|
||||
stats = collections.Counter()
|
||||
sample_shown = 0
|
||||
for page, tile, sends in regions:
|
||||
for addr, size, op in sends:
|
||||
if 0x08014000 <= addr < 0x08018000:
|
||||
stats['std'] += 1; continue
|
||||
stats['content'] += 1
|
||||
fl = parse_payload(addr, size)
|
||||
if sample_shown < 3 and len(fl) >= 6:
|
||||
print("\nsample payload @%08x size=%d tile=%s floats:" % (addr, size, tile))
|
||||
for i, f in fl[:12]:
|
||||
print(" +%02x %.6g" % (i * 4, f))
|
||||
sample_shown += 1
|
||||
# edge-triple attempt: first 9 floats as 3x(A,B,C)
|
||||
vals = [f for _, f in fl]
|
||||
if len(vals) >= 9:
|
||||
e = [tuple(vals[k*3:k*3+3]) for k in range(3)]
|
||||
pts = [isect(e[0], e[1]), isect(e[1], e[2]), isect(e[2], e[0])]
|
||||
if all(p and -200 < p[0] < 200 and -200 < p[1] < 200 for p in pts):
|
||||
tris.append((tile, pts))
|
||||
print("\nstats:", dict(stats), " reconstructed tris:", len(tris))
|
||||
if tris:
|
||||
# place by tile origin: id=(row<<5)|col
|
||||
W, H = 832, 512
|
||||
canvas = [[0]*104 for _ in range(40)]
|
||||
for tile, pts in tris:
|
||||
ox = ((tile or 0) & 0x1f) * 64; oy = (((tile or 0) >> 5) & 0x1f) * 128
|
||||
for x, y in pts:
|
||||
gx = int((ox + x) / W * 103); gy = int((oy + y) / H * 39)
|
||||
if 0 <= gx < 104 and 0 <= gy < 40: canvas[gy][gx] += 1
|
||||
print("vertex scatter (tile-placed):")
|
||||
mx = max(max(r) for r in canvas) or 1
|
||||
for row in canvas:
|
||||
print(" " + "".join(" .:-=+*#%@"[min(9, int(c/mx*9.99))] if c else " " for c in row))
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Dump the payload region MEMORY (0x8014000-0x8018000) at draw ~30 and draw ~300 of
|
||||
trek — the full compiled scene at two depths, independent of when writes occurred.
|
||||
Saves trek_content30.pkl / trek_content300.pkl for frame assembly."""
|
||||
import sys, time, struct, pickle
|
||||
sys.path.insert(0, r'C:\VWE\TeslaRel410\emulator\firmware-decomp')
|
||||
sys.path.insert(0, r'C:\VWE\TeslaRel410\dpl3-revive\patha')
|
||||
import emu860, dis860, emu_main
|
||||
emu860.Mem.log = lambda self, *a, **k: None
|
||||
S = r'C:\Users\cyd\AppData\Local\Temp\claude\c--VWE-TeslaRel410\4e848c76-6e89-4034-8047-d8d491cb32d8\scratchpad'
|
||||
snap = pickle.load(open(S + r'\snap_trek0.pkl', 'rb'))
|
||||
r = emu_main.MainRunner(r'C:\VWE\TeslaRel410\dpl3-revive\patha\trek.raw.bin',
|
||||
fw='capfw7', max_cmds=30000)
|
||||
cpu = r.cpu
|
||||
cpu.mem.pages = {k: bytearray(v) for k, v in snap['pages'].items()}
|
||||
cpu.ctrl.clear(); cpu.ctrl.update(snap['ctrl'])
|
||||
cpu.r = list(snap['r']); cpu.f = list(snap['f']); cpu.cr = dict(snap['cr']); cpu.pc = snap['pc']
|
||||
cpu._apipe = list(snap['apipe']); cpu._mpipe = list(snap['mpipe']); cpu._fp_pipes()
|
||||
cpu._lpipe = list(snap['lpipe']); cpu._gpipe = list(snap['gpipe'])
|
||||
cpu._kr, cpu._ki, cpu._t = snap['kr'], snap['ki'], snap['t']
|
||||
cpu.lcc = snap['lcc']; r.qi = snap['qi']; r.heap = list(snap['heap'])
|
||||
|
||||
def dump(tag):
|
||||
words = {}
|
||||
for a in range(0x0815e000, 0x08180000, 4):
|
||||
w = cpu.mem.r32(a)
|
||||
if w:
|
||||
words[a] = w
|
||||
pickle.dump({'mem': words, 'qi': r.qi},
|
||||
open(S + r'\trek_content%s.pkl' % tag, 'wb'))
|
||||
print("dumped %d nonzero words at cmd %d -> trek_content%s.pkl" %
|
||||
(len(words), r.qi, tag), flush=True)
|
||||
|
||||
t0 = time.time(); startq = r.qi
|
||||
targets = [(startq + 60, '30')]
|
||||
ti = 0
|
||||
while time.time() - t0 < 800 and ti < len(targets):
|
||||
if r.qi >= targets[ti][0]:
|
||||
dump(targets[ti][1]); ti += 1
|
||||
continue
|
||||
h = r.hooks.get(cpu.pc)
|
||||
if h:
|
||||
if h(cpu) == 'done': break
|
||||
continue
|
||||
if not cpu.step(): break
|
||||
print("done at cmd %d in %.0fs" % (r.qi, time.time() - t0))
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user