#!/usr/bin/env python3 import sys from typing import * class InsnRec(NamedTuple): addr: int opcode: int insn: str args: str # special insns: # * DBG_PRINT0: lli+jsr # * DBG_FUNC: output+input+btst+bne+outbset+rts def get_ops(file): def aftersplit(st, sep, ind): a = st.split(sep, maxsplit=ind) if len(a) == 1: return a # sep not in st return a[ind:] with open(file, 'r') as f: for l in f.readlines(): if len(l) == 0: continue nocomment = l.split(';', maxsplit=1)[0].strip() if len(nocomment) == 0: continue nolbl = ' '.join(x.strip() for x in aftersplit(nocomment, ':', 1)) if len(nolbl) == 0: continue #print(nolbl) #op = nolbl.split(maxsplit=1)[0]#.lower() #print(op) spl = nolbl.split(maxsplit=1) #print(spl) yield spl def parse_hex(file): with open(file, 'r') as f: for l in f.readlines(): if len(l) == 0: continue nocomment = l.split('#', maxsplit=1)[0].strip() if len(nocomment) == 0: continue for x in nocomment.split(): yield int(x, 16) def join_asm(ops, hdt): hoff = 0 for op in ops: if op[0] == '.ORG': continue # skip elif op[0] == 'DBG_PRINT0': yield InsnRec(hoff, hdt[hoff+0], "lli", op[1]) yield InsnRec(hoff, hdt[hoff+1], "jsr", "_DBG_PRINT") hoff += 2 elif op[0] == 'DBG_FUNC': yield InsnRec(hoff, hdt[hoff+0], "output", "r0, MCETRCCMD") yield InsnRec(hoff, hdt[hoff+1], "input", "MCETRCBUSY, r0") yield InsnRec(hoff, hdt[hoff+2], "btst", "0, r0") yield InsnRec(hoff, hdt[hoff+3], "bne", "_DBG_PRINT_WAIT") yield InsnRec(hoff, hdt[hoff+4], "outbset", "MCETRCSEND_SEND, MCETRCSEND") yield InsnRec(hoff, hdt[hoff+5], "rts", "") hoff += 6 else: yield InsnRec(hoff, hdt[hoff], op[0], "" if len(op)==1 else op[1]) hoff += 1 assert hoff == len(hdt) def get_baseops(recs): d = dict() for r in recs: if r.insn == '.DATA': continue tup = d.get(r.insn, (0, 0xffff)) d[r.insn] = (tup[0] | r.opcode, tup[1] & r.opcode) return d def decode(insn): REGS = [*["r%d"%x for x in range(16)], "pc", *["unk%d"%x for x in range(17,32)]] ALUOPS = ["or", "and", "xor?", "tst?", "mov", "add", "sub", "cmp"] assert insn == (insn & 0xffff), "insn %04x must be 16bit!"%insn op = insn >> 12 # often used dreg = insn & 0xf dstabs = insn & 0x3ff if op == 0 or op == 1: # ALU op aluop = (insn >> 10) & 7 src = (insn >> 4) & 0x1f mode = insn & (1 << 9) if mode == 0: # reg src return "%s %s, %s" % (ALUOPS[aluop], REGS[src], REGS[dreg]) else: # imm src return "%s %d, %s" % (ALUOPS[aluop], src, REGS[dreg]) elif op == 2: # bit op bitop = (insn >> 8) & 0xf src = (insn >> 4) & 0xf opstr = {0b0010: 'btst', 0b1010: 'bclr'}.get(bitop, "bitop_%x"%bitop) return "%s %d, %s" % (opstr, src, REGS[dreg]) elif op == 3: # shift op shiftop = (insn >> 9) & 7 src = (insn >> 4) & 0xf mode = insn & (1 << 8) opstr = {0: "sl0", 4: "sr0", 6: "srx"}.get(shiftop, "shiftop_%d"%shiftop) if mode == 0: # reg src return "%s %s, %s" % (opstr, REGS[src], REGS[dreg]) else: # imm src return "%s %d, %s" % (opstr, src, REGS[dreg]) elif op == 4 or op == 5: # b NEWCC? cc = (insn >> 10) & 7 ccstr = ["eq","ne","mi","pl", *["CC%d"%x for x in range(4,8)]][cc] return "b%s 0x%04x" % (ccstr, dstabs) elif op == 6: # jmpop + reg-indirect if (insn & 0x0c00) == 0x0c00: # reg-indirect typ = (insn >> 8) & 3 src = (insn >> 4) & 0xf if typ == 0: # jmp (reg) if src != 0: return "jmp_WUT %d, (%s)" % (src, REGS[dreg]) else: return "jmp (%s)" % REGS[dreg] elif typ == 1: # input return "input (%s), %s" % (REGS[src], REGS[dreg]) elif typ == 2: # output return "output %s, (%s)" % (REGS[src], REGS[dreg]) elif typ == 3: # lmd return "lmd (%s), %s" % (REGS[src], REGS[dreg]) else: assert False else: # jmpop typ = (insn >> 10) & 3 typstr = {0: "jmp", 1: "jsr", 2: "loop"}[typ] return "%s 0x%04x" % (typstr, dstabs) elif op == 7: # misc if (insn & 0x0800) != 0: # lmd src = (insn >> 4) & 0x7f unsure = (src & 0x70) != 0 return "lmd%s 0x%x, %s" % (("?" if unsure else ""), src, REGS[dreg]) else: # outclr/outset/rts/wait subop = (insn >> 8) & 7 dstabs &= 0xff if subop in (0,1): opstr = ["rts", "wait"][subop] return opstr if dstabs == 0 else ("%s? 0x%02x"%(opstr,dstabs)) elif subop in (2,3): opstr = ["outclr", "outset"][subop-2] return "%s 0x%02x" % (opstr, dstabs) else: return "misc_abs %d, 0x%02x" % (subop, dstabs) elif op >= 8 and op < 0xc: # I/O src = (insn >> 4) & 0xff opstr = ["input", "output", "outbclr", "outbset"][op-8] if op == 8: # input return "%s 0x%02x, %s" % (opstr, src, REGS[dreg]) else: return "%s %s, 0x%02x" % (opstr, (REGS[dreg] if op == 9 else str(dreg)), src) elif op >= 0xc and op <= 0xf: # lli src = (insn & 0x3ff0) >> 4 return "lli 0x%x, %s" % (src, REGS[dreg]) else: assert False, "Unknown opcode of insn %04x"%insn if __name__ == '__main__': assert len(sys.argv) > 2, ("Usage: %s " % sys.argv[0]) ops = get_ops(sys.argv[1]) #for x in ops: print(x[0]) hdt = list(parse_hex(sys.argv[2])) #for x in hdt: print(hex(x)) recs = join_asm(ops, hdt) #for x in recs: print("%04x:\t%04x\t%s\t%s"%x) #for k, v in get_baseops(recs).items(): # print("%s\t(%04x, %04x)"%(k,*v)) declen = 20 for x in recs: dec = "" if x.insn == ".DATA" else decode(x.opcode) dec += (" "*max(declen-len(dec),0)) print("%04x:\t%04x\t%s\t%s %s" % (x.addr, x.opcode, dec, x.insn, x.args))