From aa0d109c8aaf990a6b3ce25ba312ea1281284e08 Mon Sep 17 00:00:00 2001 From: Forest Belton Date: Wed, 29 Sep 2021 14:52:06 -0400 Subject: [PATCH] Support addresses when converting assembly --- gbsdk/tools/asmconvert.py | 198 ++++++++++++++++++++++++-------------- 1 file changed, 126 insertions(+), 72 deletions(-) diff --git a/gbsdk/tools/asmconvert.py b/gbsdk/tools/asmconvert.py index 90c8b41..4c3d332 100644 --- a/gbsdk/tools/asmconvert.py +++ b/gbsdk/tools/asmconvert.py @@ -5,6 +5,7 @@ import re module = sys.argv[1] + class Token: def __init__(self, kind, value, line_nr): self.kind = kind @@ -19,22 +20,27 @@ class Token: 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): self.__tokens = [] @@ -42,21 +48,21 @@ class Tokenizer: for mo in self.TOKEN_REGEX.finditer(code): kind = mo.lastgroup 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) - elif kind == 'SKIP': + elif kind == "SKIP": pass else: - if kind == 'HEX': + if kind == "HEX": value = "$" + value[2:] - if kind == 'ALABEL': + if kind == "ALABEL": value = "._ANNO_" + value[:-1] kind = "ID" self.__tokens.append(Token(kind, value, line_num)) - if kind == 'NEWLINE': + if kind == "NEWLINE": line_num += 1 - self.__tokens.append(Token('NEWLINE', '\n', line_num)) + self.__tokens.append(Token("NEWLINE", "\n", line_num)) def peek(self): return self.__tokens[0] @@ -74,67 +80,107 @@ class Tokenizer: def __bool__(self): return bool(self.__tokens) + tok = Tokenizer(sys.stdin.read()) + def processExpression(): while True: t = tok.peek() - if t.isA('EXPR'): + if t.isA("EXPR"): tok.pop() t = tok.peek() - if t.isA('OP', '<'): - sys.stdout.write('LOW') + if t.isA("OP", "<"): + sys.stdout.write("LOW") tok.pop() t = tok.peek() - if t.isA('OP', '>'): - sys.stdout.write('HIGH') + if t.isA("OP", ">"): + sys.stdout.write("HIGH") tok.pop() t = tok.peek() - if t.isA('OP', '('): + if t.isA("OP", "("): tok.pop() - sys.stdout.write('(') + sys.stdout.write("(") processExpression() 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:]) - 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 sys.stdout.write(t.value) tok.pop() + def processParameter(): t = tok.pop() - if t.isA('EXPR'): + if t.isA("EXPR"): processExpression() - elif t.isA('NEWLINE'): + elif t.isA("NEWLINE"): 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) - elif t.isA('OP', '('): + elif t.isA("OP", "("): sys.stdout.write("[") processExpression() t = tok.pop() - while not t.isA('OP', ')'): + while not t.isA("OP", ")"): sys.stdout.write(t.value) t = tok.pop() - assert t.isA('OP', ')'), t + assert t.isA("OP", ")"), t sys.stdout.write("]") else: 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: start = tok.pop() - if start.isA('NEWLINE'): + if start.isA("NEWLINE"): pass - elif start.isA('COMMENT'): + elif start.isA("COMMENT"): 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 - elif start.value == '.area': + elif start.value == ".area": area_name = tok.pop().value if area_name == "_DATA": print('SECTION "%s_%s", WRAM0' % (module, area_name)) @@ -145,7 +191,10 @@ while tok: elif area_name == "_CODE": print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) 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": print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) elif area_name == "_GSINIT": @@ -156,72 +205,77 @@ while tok: print('SECTION FRAGMENT "code_%s", ROMX' % (module)) else: raise Exception(area_name) - while not tok.pop().isA('NEWLINE'): + while not tok.pop().isA("NEWLINE"): pass - elif start.value == '.ds': + elif start.value == ".ds": sys.stdout.write("ds ") processExpression() sys.stdout.write("\n") - elif start.value == '.ascii': + elif start.value == ".ascii": sys.stdout.write("db ") sys.stdout.write(tok.pop().value) sys.stdout.write("\n") - elif start.value == '.db': + elif start.value == ".db": sys.stdout.write("db ") processParameter() - while tok.peek().isA('OP', ','): + while tok.peek().isA("OP", ","): sys.stdout.write(",") tok.pop() processParameter() 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 ") processParameter() - while tok.peek().isA('OP', ','): + while tok.peek().isA("OP", ","): sys.stdout.write(",") tok.pop() processParameter() sys.stdout.write("\n") - elif start.value == '.incbin': + elif start.value == ".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("\n") tok.pop() else: + sys.stderr.write(f"{module}: error: could not parse\n") 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() sys.stdout.write("%s = " % (start.value)) processExpression() sys.stdout.write("\n") - elif tok.peek().isA('LABEL'): + elif tok.peek().isA("LABEL"): 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 + ") processParameter() 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() - 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)) else: sys.stdout.write("%s " % (start.value)) - if not tok.peek().isA('NEWLINE'): + if not tok.peek().isA("NEWLINE"): processParameter() - if tok.peek().isA('OP', ','): + if tok.peek().isA("OP", ","): tok.pop() sys.stdout.write(", ") processParameter() sys.stdout.write("\n") - tok.expect('NEWLINE') + tok.expect("NEWLINE") else: raise Exception(start)