Browse Source

Support addresses when converting assembly

master
Forest Belton 3 years ago
parent
commit
aa0d109c8a
1 changed files with 126 additions and 72 deletions
  1. +126
    -72
      gbsdk/tools/asmconvert.py

+ 126
- 72
gbsdk/tools/asmconvert.py View File

@ -5,6 +5,7 @@ import re
module = sys.argv[1] module = sys.argv[1]
class Token: class Token:
def __init__(self, kind, value, line_nr): def __init__(self, kind, value, line_nr):
self.kind = kind self.kind = kind
@ -19,22 +20,27 @@ class Token:
class Tokenizer: class Tokenizer:
TOKEN_REGEX = re.compile('|'.join('(?P<%s>%s)' % pair for pair in [
('HEX', r'0x[0-9A-Fa-f]+'),
('ASSIGN', r'='),
('ALABEL', r'\d+\$'),
('NUMBER', r'\d+(\.\d*)?'),
('COMMENT', r';[^\n]*'),
('LABEL', r':+'),
('EXPR', r'#'),
('STRING', '"[^"]*"'),
('DIRECTIVE', r'\.[A-Za-z_][A-Za-z0-9_\.]*'),
('ID', r'[A-Za-z_][A-Za-z0-9_\.]*'),
('OP', r'[+\-*/,\(\)<>]'),
('NEWLINE', r'\n'),
('SKIP', r'[ \t]+'),
('MISMATCH', r'.'),
]))
TOKEN_REGEX = re.compile(
"|".join(
"(?P<%s>%s)" % pair
for pair in [
("HEX", r"0x[0-9A-Fa-f]+"),
("ASSIGN", r"="),
("ALABEL", r"\d+\$"),
("NUMBER", r"\d+(\.\d*)?"),
("COMMENT", r";[^\n]*"),
("LABEL", r":+"),
("EXPR", r"#"),
("STRING", '"[^"]*"'),
("DIRECTIVE", r"\.[A-Za-z_][A-Za-z0-9_\.]*"),
("ID", r"[A-Za-z_][A-Za-z0-9_\.]*"),
("OP", r"[+\-*/,\(\)<>]"),
("NEWLINE", r"\n"),
("SKIP", r"[ \t]+"),
("MISMATCH", r"."),
]
)
)
def __init__(self, code): def __init__(self, code):
self.__tokens = [] self.__tokens = []
@ -42,21 +48,21 @@ class Tokenizer:
for mo in self.TOKEN_REGEX.finditer(code): for mo in self.TOKEN_REGEX.finditer(code):
kind = mo.lastgroup kind = mo.lastgroup
value = mo.group() value = mo.group()
if kind == 'MISMATCH':
print("ERR:", code.split("\n")[line_num-1])
if kind == "MISMATCH":
print("ERR:", code.split("\n")[line_num - 1])
raise RuntimeError("Syntax error on line: %d: %s\n%s", line_num, value) raise RuntimeError("Syntax error on line: %d: %s\n%s", line_num, value)
elif kind == 'SKIP':
elif kind == "SKIP":
pass pass
else: else:
if kind == 'HEX':
if kind == "HEX":
value = "$" + value[2:] value = "$" + value[2:]
if kind == 'ALABEL':
if kind == "ALABEL":
value = "._ANNO_" + value[:-1] value = "._ANNO_" + value[:-1]
kind = "ID" kind = "ID"
self.__tokens.append(Token(kind, value, line_num)) self.__tokens.append(Token(kind, value, line_num))
if kind == 'NEWLINE':
if kind == "NEWLINE":
line_num += 1 line_num += 1
self.__tokens.append(Token('NEWLINE', '\n', line_num))
self.__tokens.append(Token("NEWLINE", "\n", line_num))
def peek(self): def peek(self):
return self.__tokens[0] return self.__tokens[0]
@ -74,67 +80,107 @@ class Tokenizer:
def __bool__(self): def __bool__(self):
return bool(self.__tokens) return bool(self.__tokens)
tok = Tokenizer(sys.stdin.read()) tok = Tokenizer(sys.stdin.read())
def processExpression(): def processExpression():
while True: while True:
t = tok.peek() t = tok.peek()
if t.isA('EXPR'):
if t.isA("EXPR"):
tok.pop() tok.pop()
t = tok.peek() t = tok.peek()
if t.isA('OP', '<'):
sys.stdout.write('LOW')
if t.isA("OP", "<"):
sys.stdout.write("LOW")
tok.pop() tok.pop()
t = tok.peek() t = tok.peek()
if t.isA('OP', '>'):
sys.stdout.write('HIGH')
if t.isA("OP", ">"):
sys.stdout.write("HIGH")
tok.pop() tok.pop()
t = tok.peek() t = tok.peek()
if t.isA('OP', '('):
if t.isA("OP", "("):
tok.pop() tok.pop()
sys.stdout.write('(')
sys.stdout.write("(")
processExpression() processExpression()
t = tok.pop() t = tok.pop()
assert t.isA('OP', ')')
sys.stdout.write(')')
if t.isA('ID') and t.value.startswith("b_"):
assert t.isA("OP", ")")
sys.stdout.write(")")
if t.isA("ID") and t.value.startswith("b_"):
t.value = "BANK(%s)" % (t.value[1:]) t.value = "BANK(%s)" % (t.value[1:])
if t.isA('NEWLINE') or t.isA('OP', ')') or t.isA('OP', ','):
if t.isA("NEWLINE") or t.isA("OP", ")") or t.isA("OP", ","):
break break
sys.stdout.write(t.value) sys.stdout.write(t.value)
tok.pop() tok.pop()
def processParameter(): def processParameter():
t = tok.pop() t = tok.pop()
if t.isA('EXPR'):
if t.isA("EXPR"):
processExpression() processExpression()
elif t.isA('NEWLINE'):
elif t.isA("NEWLINE"):
return return
elif t.isA('ID') or t.isA('NUMBER') or t.isA('HEX'):
elif t.isA("ID") or t.isA("NUMBER") or t.isA("HEX"):
sys.stdout.write(t.value) sys.stdout.write(t.value)
elif t.isA('OP', '('):
elif t.isA("OP", "("):
sys.stdout.write("[") sys.stdout.write("[")
processExpression() processExpression()
t = tok.pop() t = tok.pop()
while not t.isA('OP', ')'):
while not t.isA("OP", ")"):
sys.stdout.write(t.value) sys.stdout.write(t.value)
t = tok.pop() t = tok.pop()
assert t.isA('OP', ')'), t
assert t.isA("OP", ")"), t
sys.stdout.write("]") sys.stdout.write("]")
else: else:
raise Exception(t) raise Exception(t)
class AnyStr:
def __eq__(self, other) -> bool:
return isinstance(other, str)
BYTE_ADDR_PATTERN = [
"(",
AnyStr(),
"+",
"0",
")",
",",
"(",
"(",
AnyStr(),
"+",
"0",
")",
">",
">",
"8",
")",
"\n",
]
ID_PATTERN_IDX1 = 1
ID_PATTERN_IDX2 = 8
def processByteAddr():
tokens = [tok.pop().value for _ in range(17)]
assert tokens == BYTE_ADDR_PATTERN
assert tokens[ID_PATTERN_IDX1] == tokens[ID_PATTERN_IDX2]
addr = tokens[ID_PATTERN_IDX1]
sys.stdout.write(f"LOW({addr}), HIGH({addr})")
while tok: while tok:
start = tok.pop() start = tok.pop()
if start.isA('NEWLINE'):
if start.isA("NEWLINE"):
pass pass
elif start.isA('COMMENT'):
elif start.isA("COMMENT"):
print(start.value) print(start.value)
elif start.isA('DIRECTIVE'):
if start.value in {'.module', '.optsdcc', '.globl'}:
while not tok.pop().isA('NEWLINE'):
elif start.isA("DIRECTIVE"):
if start.value in {".module", ".optsdcc", ".globl"}:
while not tok.pop().isA("NEWLINE"):
pass pass
elif start.value == '.area':
elif start.value == ".area":
area_name = tok.pop().value area_name = tok.pop().value
if area_name == "_DATA": if area_name == "_DATA":
print('SECTION "%s_%s", WRAM0' % (module, area_name)) print('SECTION "%s_%s", WRAM0' % (module, area_name))
@ -145,7 +191,10 @@ while tok:
elif area_name == "_CODE": elif area_name == "_CODE":
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name))
elif area_name.startswith("_CODE_"): elif area_name.startswith("_CODE_"):
print('SECTION FRAGMENT "%s_%s", ROMX, BANK[%d]' % (module, area_name, int(area_name[6:])))
print(
'SECTION FRAGMENT "%s_%s", ROMX, BANK[%d]'
% (module, area_name, int(area_name[6:]))
)
elif area_name == "_CABS": elif area_name == "_CABS":
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name))
elif area_name == "_GSINIT": elif area_name == "_GSINIT":
@ -156,72 +205,77 @@ while tok:
print('SECTION FRAGMENT "code_%s", ROMX' % (module)) print('SECTION FRAGMENT "code_%s", ROMX' % (module))
else: else:
raise Exception(area_name) raise Exception(area_name)
while not tok.pop().isA('NEWLINE'):
while not tok.pop().isA("NEWLINE"):
pass pass
elif start.value == '.ds':
elif start.value == ".ds":
sys.stdout.write("ds ") sys.stdout.write("ds ")
processExpression() processExpression()
sys.stdout.write("\n") sys.stdout.write("\n")
elif start.value == '.ascii':
elif start.value == ".ascii":
sys.stdout.write("db ") sys.stdout.write("db ")
sys.stdout.write(tok.pop().value) sys.stdout.write(tok.pop().value)
sys.stdout.write("\n") sys.stdout.write("\n")
elif start.value == '.db':
elif start.value == ".db":
sys.stdout.write("db ") sys.stdout.write("db ")
processParameter() processParameter()
while tok.peek().isA('OP', ','):
while tok.peek().isA("OP", ","):
sys.stdout.write(",") sys.stdout.write(",")
tok.pop() tok.pop()
processParameter() processParameter()
sys.stdout.write("\n") sys.stdout.write("\n")
elif start.value == '.dw':
elif start.value == ".byte":
sys.stdout.write("db ")
processByteAddr()
sys.stdout.write("\n")
elif start.value == ".dw":
sys.stdout.write("dw ") sys.stdout.write("dw ")
processParameter() processParameter()
while tok.peek().isA('OP', ','):
while tok.peek().isA("OP", ","):
sys.stdout.write(",") sys.stdout.write(",")
tok.pop() tok.pop()
processParameter() processParameter()
sys.stdout.write("\n") sys.stdout.write("\n")
elif start.value == '.incbin':
elif start.value == ".incbin":
sys.stdout.write("incbin ") sys.stdout.write("incbin ")
while not tok.peek().isA('NEWLINE'):
while not tok.peek().isA("NEWLINE"):
sys.stdout.write(tok.pop().value) sys.stdout.write(tok.pop().value)
sys.stdout.write("\n") sys.stdout.write("\n")
tok.pop() tok.pop()
else: else:
sys.stderr.write(f"{module}: error: could not parse\n")
raise Exception(start, tok.peek()) raise Exception(start, tok.peek())
elif start.isA('ID'):
if tok.peek().isA('ASSIGN'):
elif start.isA("ID"):
if tok.peek().isA("ASSIGN"):
tok.pop() tok.pop()
sys.stdout.write("%s = " % (start.value)) sys.stdout.write("%s = " % (start.value))
processExpression() processExpression()
sys.stdout.write("\n") sys.stdout.write("\n")
elif tok.peek().isA('LABEL'):
elif tok.peek().isA("LABEL"):
print("%s%s" % (start.value, tok.pop().value)) print("%s%s" % (start.value, tok.pop().value))
elif start.isA('ID', 'ldhl'):
tok.expect('ID', 'sp')
tok.expect('OP', ',')
elif start.isA("ID", "ldhl"):
tok.expect("ID", "sp")
tok.expect("OP", ",")
sys.stdout.write("ld hl, sp + ") sys.stdout.write("ld hl, sp + ")
processParameter() processParameter()
sys.stdout.write("\n") sys.stdout.write("\n")
elif start.isA('ID', 'lda'):
tok.expect('ID', 'hl')
tok.expect('OP', ',')
elif start.isA("ID", "lda"):
tok.expect("ID", "hl")
tok.expect("OP", ",")
t = tok.pop() t = tok.pop()
assert t.isA('NUMBER') or t.isA('HEX')
tok.expect('OP', '(')
tok.expect('ID', 'sp')
tok.expect('OP', ')')
assert t.isA("NUMBER") or t.isA("HEX")
tok.expect("OP", "(")
tok.expect("ID", "sp")
tok.expect("OP", ")")
sys.stdout.write("ld hl, sp + %s\n" % (t.value)) sys.stdout.write("ld hl, sp + %s\n" % (t.value))
else: else:
sys.stdout.write("%s " % (start.value)) sys.stdout.write("%s " % (start.value))
if not tok.peek().isA('NEWLINE'):
if not tok.peek().isA("NEWLINE"):
processParameter() processParameter()
if tok.peek().isA('OP', ','):
if tok.peek().isA("OP", ","):
tok.pop() tok.pop()
sys.stdout.write(", ") sys.stdout.write(", ")
processParameter() processParameter()
sys.stdout.write("\n") sys.stdout.write("\n")
tok.expect('NEWLINE')
tok.expect("NEWLINE")
else: else:
raise Exception(start) raise Exception(start)

Loading…
Cancel
Save