thingamajig/thingamajig_disasm.py

113 lines
3.5 KiB
Python

#!/usr/bin/env python
from collections import namedtuple
import enum
class rfield(enum.Enum):
none, reg, imm_flag = range(3)
Opcode = namedtuple('Opcode', ('mnemonic', 'rx', 'ry', 'addr'))
opcodes = [
Opcode('halt', rx=rfield.none, ry=rfield.none, addr=False),
Opcode('ret', rx=rfield.none, ry=rfield.none, addr=False),
Opcode('shl', rx=rfield.reg, ry=rfield.none, addr=False),
Opcode('shr', rx=rfield.reg, ry=rfield.none, addr=False),
Opcode('rol', rx=rfield.reg, ry=rfield.none, addr=False),
Opcode('ror', rx=rfield.reg, ry=rfield.none, addr=False),
Opcode('nand', rx=rfield.reg, ry=rfield.reg, addr=False),
Opcode('and', rx=rfield.reg, ry=rfield.reg, addr=False),
Opcode('or', rx=rfield.reg, ry=rfield.reg, addr=False),
Opcode('xor', rx=rfield.reg, ry=rfield.reg, addr=False),
Opcode('load', rx=rfield.reg, ry=rfield.imm_flag, addr=True),
Opcode('store', rx=rfield.none, ry=rfield.reg, addr=True),
Opcode('breq', rx=rfield.reg, ry=rfield.reg, addr=True),
Opcode('brneq', rx=rfield.reg, ry=rfield.reg, addr=True),
Opcode('cleq', rx=rfield.reg, ry=rfield.reg, addr=True),
Opcode('clneq', rx=rfield.reg, ry=rfield.reg, addr=True),
]
Instruction = namedtuple('Instruction', ['opcode', 'rx', 'ry', 'addr', 'immediate'])
Data = namedtuple('Data', ['byte'])
Statement = namedtuple('Statement', ['addr', 'raw', 'contents'])
def segment(binary, origin):
statements = []
ip = origin
while ip < len(binary):
byte = binary[ip]
opcode = byte >> 4
rx = (byte >> 2) & 3
ry = byte & 3
immediate = opcodes[opcode].ry == rfield.imm_flag and ry == 3
valid = True
if opcodes[opcode].rx == rfield.none and rx != 0: valid = False
if opcodes[opcode].ry == rfield.none and ry != 0: valid = False
if opcodes[opcode].ry == rfield.imm_flag and ry not in (0, 3): valid = False
if opcodes[opcode].addr and not immediate and ip + 2 >= len(binary): valid = False
if immediate and ip + 1 >= len(binary): valid = False
if not valid:
raw = binary[ip:ip + 1]
statements.append(Statement(ip, raw, Data(byte)))
ip += 1
elif immediate:
raw = binary[ip:ip + 2]
instruction = Instruction(opcode, rx, ry, None, binary[ip + 1])
statements.append(Statement(ip, raw, instruction))
ip += 2
elif opcodes[opcode].addr:
raw = binary[ip:ip + 3]
addr = (binary[ip + 1] << 8) + binary[ip + 2]
instruction = Instruction(opcode, rx, ry, addr, None)
statements.append(Statement(ip, raw, instruction))
ip += 3
else:
raw = binary[ip:ip + 1]
instruction = Instruction(opcode, rx, ry, None, None)
statements.append(Statement(ip, raw, instruction))
ip += 1
return statements
def disasm(binary, origin = 0):
for addr, raw, contents in segment(binary, origin):
if type(contents) == Data:
statement = f'data {contents.byte:02x}'
else:
mnemonic = opcodes[contents.opcode].mnemonic
fields = []
if opcodes[contents.opcode].rx == rfield.reg:
fields.append(f'r{contents.rx}')
if opcodes[contents.opcode].ry == rfield.reg:
fields.append(f'r{contents.ry}')
if contents.immediate is not None:
fields.append(f'#{contents.immediate:02x}')
elif opcodes[contents.opcode].addr:
fields.append(f'{contents.addr:04x}')
if mnemonic == 'store':
fields = ', '.join(reversed(fields))
else:
fields = ', '.join(fields)
if len(fields) != 0:
statement = f'{mnemonic} {fields}'
else:
statement = mnemonic
print(f'{addr:04x} {raw.hex():6} {statement}')
if __name__ == '__main__':
import sys
with open(sys.argv[1], 'rb') as f:
binary = f.read()
disasm(binary)