Skip to content

Commit 02e94ba

Browse files
Merge pull request #52 from wnienhaus/fixes-for-v1
More fixes towards v1.0
2 parents e647bcc + 78e9812 commit 02e94ba

File tree

6 files changed

+77
-28
lines changed

6 files changed

+77
-28
lines changed

Diff for: esp32_ulp/assemble.py

+15-19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
ESP32 ULP Co-Processor Assembler
33
"""
44

5+
import re
56
from . import opcodes
67
from .nocomment import remove_comments as do_remove_comments
78
from .util import garbage_collect
@@ -91,6 +92,12 @@ def __init__(self, symbols=None, bases=None, globals=None):
9192
self.symbols = SymbolTable(symbols or {}, bases or {}, globals or {})
9293
opcodes.symbols = self.symbols # XXX dirty hack
9394

95+
# regex for parsing assembly lines
96+
# format: [[whitespace]label:][whitespace][opcode[whitespace arg[,arg...]]]
97+
# where [] means optional
98+
# initialised here once, instead of compiling once per line
99+
self.line_regex = re.compile(r'^(\s*([a-zA-Z0-9_$.]+):)?\s*((\S*)\s*(.*))$')
100+
94101
def init(self, a_pass):
95102
self.a_pass = a_pass
96103
self.sections = dict(text=[], data=[])
@@ -108,25 +115,14 @@ def parse_line(self, line):
108115
"""
109116
if not line:
110117
return
111-
has_label = line[0] not in '\t .'
112-
if has_label:
113-
label_line = line.split(None, 1)
114-
if len(label_line) == 2:
115-
label, line = label_line
116-
else: # 1
117-
label, line = label_line[0], None
118-
label = label.rstrip(':')
119-
else:
120-
label, line = None, line.lstrip()
121-
if line is None:
122-
opcode, args = None, ()
123-
else:
124-
opcode_args = line.split(None, 1)
125-
if len(opcode_args) == 2:
126-
opcode, args = opcode_args
127-
args = tuple(arg.strip() for arg in args.split(','))
128-
else: # 1
129-
opcode, args = opcode_args[0], ()
118+
119+
matches = self.line_regex.match(line)
120+
label, opcode, args = matches.group(2), matches.group(4), matches.group(5)
121+
122+
label = label if label else None # force empty strings to None
123+
opcode = opcode if opcode else None # force empty strings to None
124+
args = tuple(arg.strip() for arg in args.split(',')) if args else ()
125+
130126
return label, opcode, args
131127

132128
def split_statements(self, lines):

Diff for: esp32_ulp/opcodes.py

+17-8
Original file line numberDiff line numberDiff line change
@@ -342,9 +342,9 @@ def get_rel(arg):
342342
if arg.type == IMM:
343343
if arg.value & 3 != 0: # bitwise version of: arg.value % 4 != 0
344344
raise ValueError('Relative offset must be a multiple of 4')
345-
return arg.value >> 2 # bitwise version of: arg.value // 4
345+
return IMM, arg.value >> 2 # bitwise version of: arg.value // 4
346346
if arg.type == SYM:
347-
return symbols.resolve_relative(arg.value)
347+
return SYM, symbols.resolve_relative(arg.value)
348348
raise TypeError('wanted: immediate, got: %s' % arg.raw)
349349

350350

@@ -449,7 +449,7 @@ def i_tsens(reg_dest, delay):
449449
return _tsens.all
450450

451451

452-
def i_adc(reg_dest, adc_idx, mux):
452+
def i_adc(reg_dest, adc_idx, mux, _not_used=None):
453453
_adc.dreg = get_reg(reg_dest)
454454
_adc.mux = get_imm(mux)
455455
_adc.sar_sel = get_imm(adc_idx)
@@ -619,7 +619,8 @@ def i_jump(target, condition='--'):
619619
raise ValueError("invalid flags condition")
620620
if target.type == IMM or target.type == SYM:
621621
_bx.dreg = 0
622-
_bx.addr = get_abs(target)
622+
# we track label addresses in 32bit words, but immediate values are in bytes and need to get divided by 4.
623+
_bx.addr = get_abs(target) if target.type == SYM else get_abs(target) >> 2 # bitwise version of "// 4"
623624
_bx.unused = 0
624625
_bx.reg = 0
625626
_bx.type = jump_type
@@ -652,7 +653,7 @@ def _jump_relr(threshold, cond, offset):
652653

653654

654655
def i_jumpr(offset, threshold, condition):
655-
offset = get_rel(offset)
656+
offset_type, offset = get_rel(offset)
656657
threshold = get_imm(threshold)
657658
condition = get_cond(condition)
658659
if condition == 'lt':
@@ -669,7 +670,11 @@ def i_jumpr(offset, threshold, condition):
669670
# jump over next JUMPR
670671
skip_ins = _jump_relr(threshold + 1, BRCOND_GE, 2)
671672
# jump to target
672-
offset -= 1 # adjust for the additional JUMPR instruction
673+
if (offset_type == IMM and offset < 0) or offset_type == SYM:
674+
# adjust for the additional JUMPR instruction
675+
# for IMM offsets, the offset is relative to the 2nd instruction, so only backwards jumps need adjusting
676+
# for SYM offsets, label offsets already include the extra instruction, so both directions need adjusting
677+
offset -= 1
673678
jump_ins = _jump_relr(threshold, BRCOND_GE, offset)
674679
return (skip_ins, jump_ins)
675680
else:
@@ -691,7 +696,7 @@ def _jump_rels(threshold, cond, offset):
691696

692697

693698
def i_jumps(offset, threshold, condition):
694-
offset = get_rel(offset)
699+
offset_type, offset = get_rel(offset)
695700
threshold = get_imm(threshold)
696701
condition = get_cond(condition)
697702
if condition == 'lt':
@@ -711,7 +716,11 @@ def i_jumps(offset, threshold, condition):
711716
# jump over next JUMPS
712717
skip_ins = _jump_rels(threshold, skip_cond, 2)
713718
# jump to target
714-
offset -= 1 # adjust for the additional JUMPS instruction
719+
if (offset_type == IMM and offset < 0) or offset_type == SYM:
720+
# adjust for the additional JUMPS instruction
721+
# for IMM offsets, the offset is relative to the 2nd instruction, so only backwards jumps need adjusting
722+
# for SYM offsets, label offsets already include the extra instruction, so both directions need adjusting
723+
offset -= 1
715724
jump_ins = _jump_rels(threshold, jump_cond, offset)
716725

717726
return (skip_ins, jump_ins)

Diff for: tests/02_compat_rtc_tests.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ for src_file in ulptool/src/ulp_examples/*/*.s binutils-esp32ulp/gas/testsuite/g
6666
test_name="${src_name##*/}"
6767

6868
# for now, skip files that contain known bugs in esp32_ulp (essentially a todo list of what to fix)
69-
for I in esp32ulp_all esp32ulp_globals esp32ulp_jumpr esp32ulp_ranges test_reg; do
69+
for I in esp32ulp_jumpr esp32ulp_ranges; do
7070
if [ "${test_name}" = "$I" ]; then
7171
# these are old bugs, and not related to the RTC macro handling functionality
7272
# they will still be great to fix over time

Diff for: tests/assemble.py

+27
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,32 @@ def test_parse_line():
5353
assert a.parse_line(next(lines)) == (None, '.data', ()) # test left-aligned directive is not treated as label
5454

5555

56+
def test_parse_labels_correctly():
57+
"""
58+
description of what defines a label
59+
https://sourceware.org/binutils/docs/as/Statements.html
60+
https://sourceware.org/binutils/docs/as/Labels.html
61+
"""
62+
a = Assembler()
63+
assert a.parse_line('') is None
64+
assert a.parse_line('label: .set const, 42') == ('label', '.set', ('const', '42',))
65+
assert a.parse_line('label:.set const, 42') == ('label', '.set', ('const', '42',))
66+
assert a.parse_line('label:') == ('label', None, ())
67+
assert a.parse_line(' label:') == ('label', None, ())
68+
assert a.parse_line(' label: ') == ('label', None, ())
69+
assert a.parse_line('nop ') == (None, 'nop', ())
70+
assert a.parse_line('.set c, 1 ') == (None, '.set', ('c', '1',))
71+
assert a.parse_line('invalid : nop') == (None, 'invalid', (': nop',)) # no whitespace between label and colon
72+
assert a.parse_line('.string "hello world"') == (None, '.string', ('"hello world"',))
73+
assert a.parse_line('.string "hello : world"') == (None, '.string', ('"hello : world"',)) # colon in string
74+
assert a.parse_line('label::') == ('label', ':', ())
75+
assert a.parse_line('label: :') == ('label', ':', ())
76+
assert a.parse_line('a_label:') == ('a_label', None, ())
77+
assert a.parse_line('$label:') == ('$label', None, ())
78+
assert a.parse_line('.label:') == ('.label', None, ())
79+
assert a.parse_line('&label:') == (None, '&label:', ()) # & not a valid char in a label
80+
81+
5682
def test_parse():
5783
a = Assembler()
5884
lines = remove_comments(src)
@@ -260,6 +286,7 @@ def test_support_multiple_statements_per_line():
260286

261287

262288
test_parse_line()
289+
test_parse_labels_correctly()
263290
test_parse()
264291
test_assemble()
265292
test_assemble_bss()

Diff for: tests/compat/fixes.S

+5
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,9 @@ entry:
2828
# interpret ; as statement separator - this results in 2 NOP machine instructions
2929
nop; nop;
3030

31+
# adc supports an undocumented 4th argument, which should be entirely ignored
32+
# binutils-esp32ulp also ignores this argument, if present, see:
33+
# https://github.com/espressif/binutils-esp32ulp/blob/249ec34cc2c9574a86f3f86bbb175a863f988bcf/gas/config/esp32ulp-parse.y#L810
34+
adc r1, 0, 1, 100
35+
3136
halt

Diff for: tests/compat/jumps.S

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
entry:
66
nop
77

8+
# simple jumps
9+
jump entry
10+
jump later
11+
jump 0x120, EQ
12+
jump -288, EQ
13+
814
# jumps with labels
915
jumps entry, 42, lt
1016
jumps entry, 42, lt
@@ -20,12 +26,15 @@ entry:
2026

2127
# jumps with immediate offset (specified in bytes, but real instruction uses words)
2228
jumps 0, 42, lt
29+
jumps 0, 42, eq # dual-instruction condition
2330

2431
jumps 4, 42, lt
32+
jumps 4, 42, eq # dual-instruction condition
2533
jumps 8, 42, lt
2634
jumps 32, 42, lt
2735

2836
jumps -4, 42, lt
37+
jumps -4, 42, eq # dual-instruction condition
2938
jumps -8, 42, lt
3039
jumps -32, 42, lt
3140

@@ -46,12 +55,15 @@ entry:
4655

4756
# jumpr with immediate offset (specified in bytes, but real instruction uses words)
4857
jumpr 0, 42, lt
58+
jumpr 0, 42, eq # dual-instruction condition
4959

5060
jumpr 4, 42, lt
61+
jumpr 4, 42, eq # dual-instruction condition
5162
jumpr 8, 42, lt
5263
jumpr 32, 42, lt
5364

5465
jumpr -4, 42, lt
66+
jumpr -4, 42, eq # dual-instruction condition
5567
jumpr -8, 42, lt
5668
jumpr -32, 42, lt
5769

0 commit comments

Comments
 (0)