"""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('> 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))