You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
115 lines
3.6 KiB
115 lines
3.6 KiB
#!/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)
|
|
|