8000 Move instruction decoding out of disassembler · wnienhaus/micropython-esp32-ulp@2e69c12 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2e69c12

Browse files
committed
Move instruction decoding out of disassembler
The disassembler is now mainly the command line tool, which deals with interpreting user input, formatting output, etc. This allows us to add decoding logic for new cpus (the S2) and the disassembler can then dynamically load the correct decoding module.
1 parent 4bf6389 commit 2e69c12

File tree

5 files changed

+154
-156
lines changed

5 files changed

+154
-156
lines changed

tests/00_unit_tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
set -e
66

7-
LIST=${1:-opcodes opcodes_s2 assemble link util preprocess definesdb disassemble}
7+
LIST=${1:-opcodes opcodes_s2 assemble link util preprocess definesdb decode}
88

99
for file in $LIST; do
1010
echo testing $file...

tests/03_disassembler_tests.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test_disassembling_a_file() {
2222
lst_file="${testname}.lst"
2323
lst_file_fixture=fixtures/${testname}${verbose}.lst
2424
echo -e "\tDisassembling $ulp_file using micropython-esp32-ulp disassembler"
25-
micropython tools/disassemble.py $verbose $ulp_file > $lst_file
25+
micropython -m tools.disassemble $verbose $ulp_file > $lst_file
2626

2727
if ! diff $lst_file_fixture $lst_file 1>/dev/null; then
2828
echo -e "\tDisassembled output differs from expected output!"
@@ -49,7 +49,7 @@ test_disassembling_a_manual_sequence() {
4949
lst_file="manual_bytes.lst"
5050
lst_file_fixture=fixtures/manual_bytes${verbose}.lst
5151
echo -e "\tDisassembling manual byte sequence using micropython-esp32-ulp disassembler"
52-
micropython tools/disassemble.py $verbose -m $sequence > $lst_file
52+
micropython -m tools.disassemble $verbose -m $sequence > $lst_file
5353

5454
if ! diff $lst_file_fixture $lst_file 1>/dev/null; then
5555
echo -e "\tDisassembled output differs from expected output!"

tests/disassemble.py renamed to tests/decode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from tools.disassemble import decode_instruction, get_instruction_fields
1+
from tools.decode import decode_instruction, get_instruction_fields
22
import esp32_ulp.opcodes as opcodes
33
import ubinascii
44

tools/decode.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import esp32_ulp.opcodes as opcodes
2+
3+
4+
alu_cnt_ops = ('STAGE_INC', 'STAGE_DEC', 'STAGE_RST')
5+
alu_ops = ('ADD', 'SUB', 'AND', 'OR', 'MOVE', 'LSH', 'RSH')
6+
jump_types = ('--', 'EQ', 'OV')
7+
cmp_ops = ('LT', 'GE', 'LE', 'EQ', 'GT')
8+
9+
lookup = {
10+
opcodes.OPCODE_ADC: ('ADC', opcodes._adc, lambda op: 'ADC r%s, %s, %s' % (op.dreg, op.mux, op.sar_sel)),
11+
opcodes.OPCODE_ALU: ('ALU', opcodes._alu_imm, {
12+
opcodes.SUB_OPCODE_ALU_CNT: (
13+
'ALU_CNT',
14+
opcodes._alu_cnt,
15+
lambda op: '%s%s' % (alu_cnt_ops[op.sel], '' if op.sel == opcodes.ALU_SEL_RST else ' %s' % op.imm)
16+
),
17+
opcodes.SUB_OPCODE_ALU_IMM: (
18+
'ALU_IMM',
19+
opcodes._alu_imm,
20+
lambda op: '%s r%s, %s' % (alu_ops[op.sel], op.dreg, op.imm) if op.sel == opcodes.ALU_SEL_MOV
21+
else '%s r%s, r%s, %s' % (alu_ops[op.sel], op.dreg, op.sreg, op.imm)
22+
),
23+
opcodes.SUB_OPCODE_ALU_REG: (
24+
'ALU_REG',
25+
opcodes._alu_reg,
26+
lambda op: '%s r%s, r%s' % (alu_ops[op.sel], op.dreg, op.sreg) if op.sel == opcodes.ALU_SEL_MOV
27+
else '%s r%s, r%s, r%s' % (alu_ops[op.sel], op.dreg, op.sreg 341A , op.treg)
28+
),
29+
}),
30+
opcodes.OPCODE_BRANCH: ('BRANCH', opcodes._bx, {
31+
opcodes.SUB_OPCODE_BX: (
32+
'BX',
33+
opcodes._bx,
34+
lambda op: 'JUMP %s%s' % (op.addr if op.reg == 0 else 'r%s' % op.dreg, ', %s' % jump_types[op.type]
35+
if op.type != 0 else '')
36+
),
37+
opcodes.SUB_OPCODE_BR: (
38+
'BR',
39+
opcodes._br,
40+
lambda op: 'JUMPR %s, %s, %s' % ('%s%s' % ('-' if op.sign == 1 else '', op.offset), op.imm, cmp_ops[op.cmp])
41+
),
42+
opcodes.SUB_OPCODE_BS: (
43+
'BS',
44+
opcodes._bs,
45+
lambda op: 'JUMPS %s, %s, %s' % ('%s%s' % ('-' if op.sign == 1 else '', op.offset), op.imm, cmp_ops[op.cmp])
46+
),
47+
}),
48+
opcodes.OPCODE_DELAY: (
49+
'DELAY',
50+
opcodes._delay,
51+
lambda op: 'NOP' if op.cycles == 0 else 'WAIT %s' % op.cycles
52+
),
53+
opcodes.OPCODE_END: ('END', opcodes._end, {
54+
opcodes.SUB_OPCODE_END: (
55+
'WAKE',
56+
opcodes._end
57+
),
58+
opcodes.SUB_OPCODE_SLEEP: (
59+
'SLEEP',
60+
opcodes._sleep,
61+
lambda op: 'SLEEP %s' % op.cycle_sel
62+
),
63+
}),
64+
opcodes.OPCODE_HALT: ('HALT', opcodes._halt),
65+
opcodes.OPCODE_I2C: (
66+
'I2C',
67+
opcodes._i2c,
68+
lambda op: 'I2C_%s %s, %s, %s, %s' % ('RD' if op.rw == 0 else 'WR', op.sub_addr, op.high, op.low, op.i2c_sel)
69+
),
70+
opcodes.OPCODE_LD: ('LD', opcodes._ld, lambda op: 'LD r%s, r%s, %s' % (op.dreg, op.sreg, op.offset)),
71+
opcodes.OPCODE_ST: ('ST', opcodes._st, lambda op: 'ST r%s, r%s, %s' % (op.sreg, op.dreg, op.offset)),
72+
opcodes.OPCODE_RD_REG: (
73+
'RD_REG',
74+
opcodes._rd_reg,
75+
lambda op: 'REG_RD 0x%x, %s, %s' % (op.periph_sel << 8 | op.addr, op.high, op.low)
76+
),
77+
opcodes.OPCODE_WR_REG: (
78+
'WR_REG',
79+
opcodes._wr_reg,
80+
lambda op: 'REG_WR 0x%x, %s, %s, %s' % (op.periph_sel << 8 | op.addr, op.high, op.low, op.data)
81+
),
82+
opcodes.OPCODE_TSENS: ('TSENS', opcodes._tsens, lambda op: 'TSENS r%s, %s' % (op.dreg, op.delay)),
83+
}
84+
85+
86+
def decode_instruction(i):
87+
if i == 0:
88+
raise Exception('<empty>')
89+
90+
ins = opcodes._end
91+
ins.all = i # abuse a struct to get opcode
92+
93+
params = lookup.get(ins.opcode, None)
94+
95+
if not params:
96+
raise Exception('Unknown instruction')
97+
98+
if len(params) == 3:
99+
name, ins, third = params
100+
ins.all = i
101+
102+
if callable(third):
103+
params = (third(ins), ins)
104+
else:
105+
params = third.get(ins.sub_opcode, ())
106+
107+
if len(params) == 3:
108+
name, ins, pretty = params
109+
ins.all = i
110+
name = pretty(ins)
111+
else:
112+
name, ins = params
113+
ins.all = i
114+
115+
return ins, name
116+
117+
118+
def get_instruction_fields(ins):
119+
possible_fields = (
120+
'addr', 'cmp', 'cycle_sel', 'cycles', 'data', 'delay', 'dreg',
121+
'high', 'i2c_sel', 'imm', 'low', 'mux', 'offset', 'opcode',
122+
'periph_sel', 'reg', 'rw', 'sar_sel', 'sel', 'sign', 'sreg',
123+
'sub_addr', 'sub_opcode', 'treg', 'type', 'unused', 'unused1',
124+
'unused2', 'wakeup'
125+
)
126+
field_details = []
127+
for field in possible_fields:
128+
extra = ''
129+
try:
130+
# eval is ugly but constrained to possible_fields and variable ins
131+
val = eval('i.%s' % field, {}, {'i': ins})
132+
if (val>9):
133+
extra = ' (0x%02x)' % val
134+
except KeyError:
135+
continue
136+
137+
if field == 'sel': # ALU
138+
if ins.sub_opcode == opcodes.SUB_OPCODE_ALU_CNT:
139+
extra = ' (%s)' % alu_cnt_ops[val]
140+
else:
141+
extra = ' (%s)' % alu_ops[val]
142+
elif field == 'type': # JUMP
143+
extra = ' (%s)' % jump_types[val]
144+
elif field == 'cmp': # JUMPR/JUMPS
145+
extra = ' (%s)' % cmp_ops[val]
146+
147+
field_details.append((field, val, extra))
148+
149+
return field_details

tools/disassemble.py

Lines changed: 1 addition & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,9 @@
11
from uctypes import struct, addressof, LITTLE_ENDIAN, UINT16, UINT32
2-
from esp32_ulp.opcodes import RD_REG_PERIPH_RTC_CNTL, RD_REG_PERIPH_RTC_IO, RD_REG_PERIPH_RTC_I2C, \
3-
RD_REG_PERIPH_SENS, DR_REG_MAX_DIRECT
4-
import esp32_ulp.opcodes as opcodes
5-
import esp32_ulp.soc as soc
2+
from .decode import decode_instruction, get_instruction_fields
63
import ubinascii
74
import sys
85

96

10-
alu_cnt_ops = ('STAGE_INC', 'STAGE_DEC', 'STAGE_RST')
11-
alu_ops = ('ADD', 'SUB', 'AND', 'OR', 'MOVE', 'LSH', 'RSH')
12-
jump_types = ('--', 'EQ', 'OV')
13-
cmp_ops = ('LT', 'GE', 'LE', 'EQ', 'GT')
14-
15-
lookup = {
16-
opcodes.OPCODE_ADC: ('ADC', opcodes._adc, lambda op: 'ADC r%s, %s, %s' % (op.dreg, op.mux, op.sar_sel)),
17-
opcodes.OPCODE_ALU: ('ALU', opcodes._alu_imm, {
18-
opcodes.SUB_OPCODE_ALU_CNT: (
19-
'ALU_CNT',
20-
opcodes._alu_cnt,
21-
lambda op: '%s%s' % (alu_cnt_ops[op.sel], '' if op.sel == opcodes.ALU_SEL_RST else ' %s' % op.imm)
22-
),
23-
opcodes.SUB_OPCODE_ALU_IMM: (
24-
'ALU_IMM',
25-
opcodes._alu_imm,
26-
lambda op: '%s r%s, %s' % (alu_ops[op.sel], op.dreg, op.imm) if op.sel == opcodes.ALU_SEL_MOV
27-
else '%s r%s, r%s, %s' % (alu_ops[op.sel], op.dreg, op.sreg, op.imm)
28-
),
29-
opcodes.SUB_OPCODE_ALU_REG: (
30-
'ALU_REG',
31-
opcodes._alu_reg,
32-
lambda op: '%s r%s, r%s' % (alu_ops[op.sel], op.dreg, op.sreg) if op.sel == opcodes.ALU_SEL_MOV
33-
else '%s r%s, r%s, r%s' % (alu_ops[op.sel], op.dreg, op.sreg, op.treg)
34-
),
35-
}),
36-
opcodes.OPCODE_BRANCH: ('BRANCH', opcodes._bx, {
37-
opcodes.SUB_OPCODE_BX: (
38-
'BX',
39-
opcodes._bx,
40-
lambda op: 'JUMP %s%s' % (op.addr if op.reg == 0 else 'r%s' % op.dreg, ', %s' % jump_types[op.type]
41-
if op.type != 0 else '')
42-
),
43-
opcodes.SUB_OPCODE_BR: (
44-
'BR',
45-
opcodes._br,
46-
lambda op: 'JUMPR %s, %s, %s' % ('%s%s' % ('-' if op.sign == 1 else '', op.offset), op.imm, cmp_ops[op.cmp])
47-
),
48-
opcodes.SUB_OPCODE_BS: (
49-
'BS',
50-
opcodes._bs,
51-
lambda op: 'JUMPS %s, %s, %s' % ('%s%s' % ('-' if op.sign == 1 else '', op.offset), op.imm, cmp_ops[op.cmp])
52-
),
53-
}),
54-
opcodes.OPCODE_DELAY: (
55-
'DELAY',
56-
opcodes._delay,
57-
lambda op: 'NOP' if op.cycles == 0 else 'WAIT %s' % op.cycles
58-
),
59-
opcodes.OPCODE_END: ('END', opcodes._end, {
60-
opcodes.SUB_OPCODE_END: (
61-
'WAKE',
62-
opcodes._end
63-
),
64-
opcodes.SUB_OPCODE_SLEEP: (
65-
'SLEEP',
66-
opcodes._sleep,
67-
lambda op: 'SLEEP %s' % op.cycle_sel
68-
),
69-
}),
70-
opcodes.OPCODE_HALT: ('HALT', opcodes._halt),
71-
opcodes.OPCODE_I2C: (
72-
'I2C',
73-
opcodes._i2c,
74-
lambda op: 'I2C_%s %s, %s, %s, %s' % ('RD' if op.rw == 0 else 'WR', op.sub_addr, op.high, op.low, op.i2c_sel)
75-
),
76-
opcodes.OPCODE_LD: ('LD', opcodes._ld, lambda op: 'LD r%s, r%s, %s' % (op.dreg, op.sreg, op.offset)),
77-
opcodes.OPCODE_ST: ('ST', opcodes._st, lambda op: 'ST r%s, r%s, %s' % (op.sreg, op.dreg, op.offset)),
78-
opcodes.OPCODE_RD_REG: (
79-
'RD_REG',
80-
opcodes._rd_reg,
81-
lambda op: 'REG_RD 0x%x, %s, %s' % (op.periph_sel << 8 | op.addr, op.high, op.low)
82-
),
83-
opcodes.OPCODE_WR_REG: (
84-
'WR_REG',
85-
opcodes._wr_reg,
86-
lambda op: 'REG_WR 0x%x, %s, %s, %s' % (op.periph_sel << 8 | op.addr, op.high, op.low, op.data)
87-
),
88-
opcodes.OPCODE_TSENS: ('TSENS', opcodes._tsens, lambda op: 'TSENS r%s, %s' % (op.dreg, op.delay)),
89-
}
90-
91-
92-
def decode_instruction(i):
93-
if i == 0:
94-
raise Exception('<empty>')
95-
96-
ins = opcodes._end
97-
ins.all = i # abuse a struct to get opcode
98-
99-
params = lookup.get(ins.opcode, None)
100-
101-
if not params:
102-
raise Exception('Unknown instruction')
103-
104-
if len(params) == 3:
105-
name, ins, third = params
106-
ins.all = i
107-
108-
if callable(third):
109-
params = (third(ins), ins)
110-
else:
111-
params = third.get(ins.sub_opcode, ())
112-
113-
if len(params) == 3:
114-
name, ins, pretty = params
115-
ins.all = i
116-
name = pretty(ins)
117-
else:
118-
name, ins = params
119-
ins.all = i
120-
121-
return ins, name
122-
123-
124-
def get_instruction_fields(ins):
125-
possible_fields = (
126-
'addr', 'cmp', 'cycle_sel', 'cycles', 'data', 'delay', 'dreg',
127-
'high', 'i2c_sel', 'imm', 'low', 'mux', 'offset', 'opcode',
128-
'periph_sel', 'reg', 'rw', 'sar_sel', 'sel', 'sign', 'sreg',
129-
'sub_addr', 'sub_opcode', 'treg', 'type', 'unused', 'unused1',
130-
'unused2', 'wakeup'
131-
)
132-
field_details = []
133-
for field in possible_fields:
134-
extra = ''
135-
try:
136-
# eval is ugly but constrained to possible_fields and variable ins
137-
val = eval('i.%s' % field, {}, {'i': ins})
138-
if (val>9):
139-
extra = ' (0x%02x)' % val
140-
except KeyError:
141-
continue
142-
143-
if field == 'sel': # ALU
144-
if ins.sub_opcode == opcodes.SUB_OPCODE_ALU_CNT:
145-
extra = ' (%s)' % alu_cnt_ops[val]
146-
else:
147-
extra = ' (%s)' % alu_ops[val]
148-
elif field == 'type': # JUMP
149-
extra = ' (%s)' % jump_types[val]
150-
elif field == 'cmp': # JUMPR/JUMPS
151-
extra = ' (%s)' % cmp_ops[val]
152-
153-
field_details.append((field, val, extra))
154-
155-
return field_details
156-
157-
1587
def chunk_into_words(code, bytes_per_word, byteorder):
1598
chunks = [
1609
ubinascii.hexlify(code[i:i + bytes_per_word])

0 commit comments

Comments
 (0)
0