#!/usr/bin/env python from collections import namedtuple import enum class rfield(enum.Enum): none, reg, imm_flag, shift = range(4) 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.shift, addr=False), Opcode('shr', rx=rfield.reg, ry=rfield.shift, addr=False), Opcode('rol', rx=rfield.reg, ry=rfield.shift, addr=False), Opcode('ror', rx=rfield.reg, ry=rfield.shift, 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}') elif opcodes[contents.opcode].ry == rfield.shift: shift = contents.ry if contents.ry != 0 else 4 fields.append(f'{shift}') 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)