ccmdm-re/analyze-asm.py

190 lines
6.4 KiB
Python
Executable File

#!/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<cc> 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[dreg], REGS[src])
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 <asm file> <hex file>" % 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))