"""Minimal i860 XR/XP disassembler driven by the binutils opcode table. Usage: python i860dis.py [len_hex] Disassembles VREND.MNG code (loads at 0xf0400000; file header 0x1C). Rebuilt 2026-07-12 (the task-#55 original didn't survive); opcode truth = scratchpad/i860_opcodes.h (binutils include/opcode/i860.h, haiku legacy mirror). Operand syntax per the header comment. """ import re, struct, sys, os HERE = os.path.dirname(os.path.abspath(__file__)) MNG = os.path.join(HERE, '..', 'content', 'VREND.MNG') HDR = 0x1C CODEBASE = 0xf0400000 CODESZ = 0x3ac80 DATASZ = 0x1e9c0 def load_ops(): src = open(os.path.join(HERE, 'i860_opcodes.h')).read() ops = [] for m in re.finditer(r'\{\s*"([^"]+)",\s*(0x[0-9a-fA-F]+),\s*(0x[0-9a-fA-F]+),\s*"([^"]*)",\s*(\w+)\s*\}', src): name, match, lose, args, expand = m.groups() ops.append((name, int(match, 16), int(lose, 16), args)) return ops OPS = load_ops() def fields(w): return { 'src2': (w >> 21) & 0x1F, 'dest': (w >> 16) & 0x1F, 'src1': (w >> 11) & 0x1F, 'imm16': w & 0xFFFF, 'imm5': (w >> 11) & 0x1F, 'lbroff': w & 0x03FFFFFF, 'sbroff': ((w >> 5) & 0xF800) | (w & 0x07FF), # split 16-bit branch offset 'split16': ((w >> 5) & 0xF800) | (w & 0x07FF), # split 16-bit (st) immediate 'creg': (w >> 21) & 0x1F, } def sext(v, bits): return v - (1 << bits) if v & (1 << (bits - 1)) else v def decode(w, pc): for name, match, lose, args in OPS: if (w & match) == match and (w & lose) == 0: # binutils order guarantees no false positive only if we check # match as EXACT on the fixed bits: (w & ~varmask) == match. # Reconstruct: fixed bits = match | lose; variable = operands. f = fields(w) out = [] for a in args: if a in '#,()': continue if a == '1': out.append('r%d' % f['src1']) elif a == '2': out.append('r%d' % f['src2']) elif a == 'd': out.append('r%d' % f['dest']) elif a in 'iIJKLM': out.append('0x%x' % f['imm16']) elif a == '5': out.append('%d' % f['imm5']) elif a == 'l': tgt = pc + 4 + (sext(f['lbroff'], 26) << 2) out.append('0x%08x' % tgt) elif a == 'r': tgt = pc + 4 + (sext(f['sbroff'], 16) << 2) out.append('0x%08x' % tgt) elif a in 'sSTU': out.append('0x%x' % f['split16']) elif a == 'e': out.append('f%d' % f['src1']) elif a == 'f': out.append('f%d' % f['src2']) elif a == 'g': out.append('f%d' % f['dest']) elif a == 'c': out.append('creg%d' % f['creg']) else: out.append('?%s' % a) return name, out return None, None def candidates(w): """All table rows consistent with word w (the table has overlaps; the first (most specific) is printed, rest shown when ambiguous).""" res = [] for name, match, lose, args in OPS: if (w & match) == match and (w & lose) == 0: res.append(name) return res def main(): data = open(MNG, 'rb').read() code = data[HDR:HDR + CODESZ] va = int(sys.argv[1], 16) ln = int(sys.argv[2], 16) if len(sys.argv) > 2 else 0x100 off = va - CODEBASE for pc in range(va, va + ln, 4): w = struct.unpack_from('