Skip to content

Commit ce82cbb

Browse files
committed
First crude version of a disassembler
Pass bytes from a hexdump in as command line arguments, eg: micropython -m tools.disassemble 401f 0040 (If the byte sequence is not quoted, all args are joined together into a single byte sequence. Spaces are allowed and will be ignored)
1 parent ba4e9ac commit ce82cbb

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

tools/disassemble.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import esp32_ulp.opcodes as opcodes
2+
import ubinascii
3+
import sys
4+
5+
6+
def decode_instruction(i):
7+
ins = opcodes._end
8+
ins.all = i # abuse a struct to get opcode and sub_opcode
9+
10+
print(ubinascii.hexlify(i.to_bytes(4, 'little')))
11+
12+
if ins.opcode == opcodes.OPCODE_ADC:
13+
print('OPCODE_ADC')
14+
opcodes._adc.all = i
15+
ins = opcodes._adc
16+
elif ins.opcode == opcodes.OPCODE_ALU and ins.sub_opcode == opcodes.SUB_OPCODE_ALU_CNT:
17+
print('OPCODE_ALU / SUB_OPCODE_ALU_CNT')
18+
opcodes._alu_cnt.all = i
19+
ins = opcodes._alu_cnt
20+
elif ins.opcode == opcodes.OPCODE_ALU and ins.sub_opcode == opcodes.SUB_OPCODE_ALU_IMM:
21+
print('OPCODE_ALU / SUB_OPCODE_ALU_IMM')
22+
opcodes._alu_imm.all = i
23+
ins = opcodes._alu_imm
24+
elif ins.opcode == opcodes.OPCODE_ALU and ins.sub_opcode == opcodes.SUB_OPCODE_ALU_REG:
25+
print('OPCODE_ALU / SUB_OPCODE_ALU_REG')
26+
opcodes._alu_reg.all = i
27+
ins = opcodes._alu_reg
28+
elif ins.opcode == opcodes.OPCODE_BRANCH and ins.sub_opcode == opcodes.SUB_OPCODE_BX:
29+
print('JUMP')
30+
opcodes._bx.all = i
31+
ins = opcodes._bx
32+
elif ins.opcode == opcodes.OPCODE_BRANCH and ins.sub_opcode == opcodes.SUB_OPCODE_BR:
33+
print('JUMPR')
34+
opcodes._br.all = i
35+
ins = opcodes._br
36+
elif ins.opcode == opcodes.OPCODE_BRANCH and ins.sub_opcode == opcodes.SUB_OPCODE_BS:
37+
print('JUMPS')
38+
opcodes._bs.all = i
39+
ins = opcodes._bs
40+
elif ins.opcode == opcodes.OPCODE_DELAY:
41+
print('OPCODE_DELAY')
42+
opcodes._delay.all = i
43+
ins = opcodes._delay
44+
elif ins.opcode == opcodes.OPCODE_END and ins.sub_opcode == opcodes.SUB_OPCODE_END:
45+
print('OPCODE_END')
46+
opcodes._end.all = i
47+
ins = opcodes._end
48+
elif ins.opcode == opcodes.OPCODE_END and ins.sub_opcode == opcodes.SUB_OPCODE_SLEEP:
49+
print('OPCODE_SLEEP')
50+
opcodes._sleep.all = i
51+
ins = opcodes._sleep
52+
elif ins.opcode == opcodes.OPCODE_HALT:
53+
print('OPCODE_HALT')
54+
opcodes._halt.all = i
55+
ins = opcodes._halt
56+
elif ins.opcode == opcodes.OPCODE_I2C:
57+
print('OPCODE_I2C')
58+
opcodes._i2c.all = i
59+
ins = opcodes._i2c
60+
elif ins.opcode == opcodes.OPCODE_LD:
61+
print('OPCODE_LD')
62+
opcodes._ld.all = i
63+
ins = opcodes._ld
64+
elif ins.opcode == opcodes.OPCODE_RD_REG:
65+
print('OPCODE_RD_REG')
66+
opcodes._rd_reg.all = i
67+
ins = opcodes._rd_reg
68+
elif ins.opcode == opcodes.OPCODE_ST:
69+
print('OPCODE_ST')
70+
opcodes._st.all = i
71+
ins = opcodes._st
72+
elif ins.opcode == opcodes.OPCODE_TSENS:
73+
print('OPCODE_TSENS')
74+
opcodes._tsens.all = i
75+
ins = opcodes._tsens
76+
elif ins.opcode == opcodes.OPCODE_WR_REG:
77+
print('OPCODE_WR_REG')
78+
opcodes._wr_reg.all = i
79+
ins = opcodes._wr_reg
80+
81+
possible_fields = (
82+
'addr', 'cmp', 'cycle_sel', 'cycles', 'data', 'delay', 'dreg',
83+
'high', 'i2c_sel', 'imm', 'low', 'mux', 'offset', 'opcode',
84+
'periph_sel', 'reg', 'rw', 'sar_sel', 'sel', 'sign', 'sreg',
85+
'sub_addr', 'sub_opcode', 'treg', 'type', 'unused', 'unused1',
86+
'unused2', 'wakeup'
87+
)
88+
for field in possible_fields:
89+
try:
90+
# eval is ugly but constrained to possible_fields and variable ins
91+
val = eval('i.%s' % field, {}, {'i': ins})
92+
except KeyError:
93+
continue
94+
extra = ''
95+
if field == 'sel':
96+
if ins.sub_opcode == opcodes.SUB_OPCODE_ALU_CNT:
97+
alu_ops = ('INC', 'DEC', 'RST')
98+
else:
99+
alu_ops = ('ADD', 'SUB', 'AND', 'OR', 'MOV', 'LSH', 'RSH')
100+
extra = ' (%s)' % alu_ops[val]
101+
elif field == 'cmp':
102+
cmp_ops = ('LT', 'GE', 'LE', 'EQ', 'GT')
103+
extra = ' (%s)' % cmp_ops[val]
104+
print(" {:10} = {:3}{}".format(field, val, extra))
105+
106+
107+
def disassemble_manually(byte_sequence_string):
108+
sequence = byte_sequence_string.strip().replace(' ','')
109+
chars_per_instruction = 8
110+
list = [
111+
sequence[i:i+chars_per_instruction]
112+
for i in range(0, len(sequence), chars_per_instruction)
113+
]
114+
115+
for instruction in list:
116+
byte_sequence = ubinascii.unhexlify(instruction.replace(' ',''))
117+
i = int.from_bytes(byte_sequence, 'little')
118+
decode_instruction(i)
119+
120+
121+
def handle_cmdline(params):
122+
byte_sequence = "".join(params)
123+
disassemble_manually(byte_sequence)
124+
125+
126+
if sys.argv: # if run from cmdline
127+
handle_cmdline(sys.argv[1:])

tools/esp32_ulp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../esp32_ulp

0 commit comments

Comments
 (0)