190 lines
6.4 KiB
Python
Executable File
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))
|
|
|