-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathct64k_disasm.py
147 lines (134 loc) · 4.29 KB
/
ct64k_disasm.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import struct
u16 = lambda x: struct.unpack('<H', x)[0]
INSTRUCTION_NAMES = [
'MI',
'MV',
'MD',
'LD',
'ST',
'AD',
'SB',
'ND',
'OR',
'XR',
'SR',
'SL',
'SA',
'JG',
'JL',
'JQ',
]
# ARITH instructions are those that take two operands.
ARITH_INSTRUCTIONS = [
'MI', 'MV', 'MD', 'LD', 'ST', 'AD', 'SB',
'ND', 'OR', 'XR', 'SR', 'SL', 'SA',
]
# JUMP instructions are instructions take 3 operands
JUMP_INSTRUCTIONS = [
'JG', 'JL', 'JQ'
]
def format_hex(raw):
return ' '.join('{:02x}'.format(ord(c)) for c in raw)
class CT64Instruction(object):
def __init__(self, name, opcode, addr, rm=None, imm=None, mem=None, raw=None):
self.name = name
self.opcode = opcode
self.addr = addr
self.rm = rm
self.imm = imm
self.mem = mem
self.raw = raw
def __str__(self):
if self.name in ARITH_INSTRUCTIONS:
if self.name == 'MI':
return '({}) {} 0x{:04x}, 0x{:04x}'.format(format_hex(self.raw), self.name, self.rm, self.imm)
else:
return '({}) {} 0x{:04x}, 0x{:04x}'.format(format_hex(self.raw), self.name, self.rm, self.mem)
elif self.name in JUMP_INSTRUCTIONS:
return '({}) {} 0x{:04x}, 0x{:04x}, 0x{:04x}'.format( format_hex(self.raw), self.name, self.rm, self.mem, self.imm)
else:
return '{}'.format(self.name)
def redirects_control_flow(instr):
is_jump = instr.name in JUMP_INSTRUCTIONS
is_mi_and_modifies_ip = instr.name == 'MI' and instr.rm == 0
targets = None
if is_jump:
targets = [instr.imm]
elif is_mi_and_modifies_ip:
targets = [instr.imm]
return targets
def decode_arith(code, addr):
"""
decode an ARITH instruction at beginning of code
"""
if len(code) < 4:
raise ValueError("not enough bytes")
raw = code[0:4]
opcode = (u16(code[0:2]) & 0xF000) >> 12
name = INSTRUCTION_NAMES[opcode]
rm = u16(code[0:2]) & 0x0FFF
mem = u16(code[2:4])
imm = u16(code[2:4])
# MI has an imm, everything else doesn't
if name == 'MI':
return CT64Instruction(name=name, opcode=opcode, addr=addr, rm=rm, imm=imm, raw=raw)
else:
return CT64Instruction(name=name, opcode=opcode, addr=addr, rm=rm, mem=mem, raw=raw)
def decode_jump(code, addr):
"""
decode a JUMP instruction at beginning of code
"""
if len(code) < 6:
raise ValueError("not enough bytes")
raw = code[0:6]
opcode = (u16(code[0:2]) & 0xF000) >> 12
name = INSTRUCTION_NAMES[opcode]
rm = u16(code[0:2]) & 0x0FFF
mem = u16(code[2:4])
imm = u16(code[4:6])
# special handling for HF
if opcode == 0xF and rm == 0 and mem == 0 and imm == addr:
return CT64Instruction(name=name, opcode=opcode, addr=addr, rm=rm, mem=mem, imm=imm, raw=raw)
else:
return CT64Instruction(name=name, opcode=opcode, addr=addr, rm=rm, mem=mem, imm=imm, raw=raw)
def disassemble_at(code, addr):
"""
returns a (instr, width) tuple
"""
opcode = (u16(code[0:2]) & 0xF000) >> 12
mneumonic = INSTRUCTION_NAMES[opcode]
if mneumonic in ARITH_INSTRUCTIONS:
size = 2
instr = decode_arith(code, addr)
elif mneumonic in JUMP_INSTRUCTIONS:
size = 3
instr = decode_jump(code, addr)
else:
raise ValueError("Unknown opcode: {}, {}".format(mneumnoic, opcode))
return (instr, size)
def disasm(code):
"""
Return a dictionary of address -> CT64Instruction
"""
base_addr = 0x1000
curr_offset = 0x0
instrs = []
last_instr = None
try:
while not last_instr or last_instr.name != 'HF' and code[curr_offset:]:
instr, size = disassemble_at(code[curr_offset:], base_addr + curr_offset)
instrs.append(instr)
curr_offset += size*2 # double because size is in words, we need bytes
last_instr = instr
control_flow_targets = redirects_control_flow(instr)
print 'INSTR:', instr
if control_flow_targets:
print map(hex, control_flow_targets)
except ValueError:
pass # not enough bytes
return instrs
if __name__ == '__main__':
with open('./t') as f:
rom = f.read()
instrs = disasm(rom)
#print '\n'.join(str(x) for x in instrs)