import sys
|
|
import re
|
|
|
|
## Need to convert asxxxx syntax to rgbds syntax
|
|
|
|
module = sys.argv[1]
|
|
|
|
|
|
class Token:
|
|
def __init__(self, kind, value, line_nr):
|
|
self.kind = kind
|
|
self.value = value
|
|
self.line_nr = line_nr
|
|
|
|
def isA(self, kind, value=None):
|
|
return self.kind == kind and (value is None or value == self.value)
|
|
|
|
def __repr__(self):
|
|
return "[%s:%s:%d]" % (self.kind, self.value, self.line_nr)
|
|
|
|
|
|
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"."),
|
|
]
|
|
)
|
|
)
|
|
|
|
def __init__(self, code):
|
|
self.__tokens = []
|
|
line_num = 1
|
|
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])
|
|
raise RuntimeError("Syntax error on line: %d: %s\n%s", line_num, value)
|
|
elif kind == "SKIP":
|
|
pass
|
|
else:
|
|
if kind == "HEX":
|
|
value = "$" + value[2:]
|
|
if kind == "ALABEL":
|
|
value = "._ANNO_" + value[:-1]
|
|
kind = "ID"
|
|
self.__tokens.append(Token(kind, value, line_num))
|
|
if kind == "NEWLINE":
|
|
line_num += 1
|
|
self.__tokens.append(Token("NEWLINE", "\n", line_num))
|
|
|
|
def peek(self):
|
|
return self.__tokens[0]
|
|
|
|
def pop(self):
|
|
return self.__tokens.pop(0)
|
|
|
|
def expect(self, kind, value=None):
|
|
pop = self.pop()
|
|
if not pop.isA(kind, value):
|
|
if value is not None:
|
|
raise SyntaxError("%s != %s:%s" % (pop, kind, value))
|
|
raise SyntaxError("%s != %s" % (pop, kind))
|
|
|
|
def __bool__(self):
|
|
return bool(self.__tokens)
|
|
|
|
|
|
tok = Tokenizer(sys.stdin.read())
|
|
global_names = set()
|
|
|
|
|
|
def processExpression():
|
|
while True:
|
|
t = tok.peek()
|
|
if t.isA("EXPR"):
|
|
tok.pop()
|
|
t = tok.peek()
|
|
if t.isA("OP", "<"):
|
|
sys.stdout.write("LOW")
|
|
tok.pop()
|
|
t = tok.peek()
|
|
if t.isA("OP", ">"):
|
|
sys.stdout.write("HIGH")
|
|
tok.pop()
|
|
t = tok.peek()
|
|
if t.isA("OP", "("):
|
|
tok.pop()
|
|
sys.stdout.write("(")
|
|
processExpression()
|
|
t = tok.pop()
|
|
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", ","):
|
|
break
|
|
sys.stdout.write(t.value)
|
|
tok.pop()
|
|
|
|
|
|
def processParameter():
|
|
t = tok.pop()
|
|
if t.isA("EXPR"):
|
|
processExpression()
|
|
elif t.isA("NEWLINE"):
|
|
return
|
|
elif t.isA("ID") or t.isA("NUMBER") or t.isA("HEX"):
|
|
sys.stdout.write(t.value)
|
|
elif t.isA("OP", "("):
|
|
sys.stdout.write("[")
|
|
processExpression()
|
|
t = tok.pop()
|
|
while not t.isA("OP", ")"):
|
|
sys.stdout.write(t.value)
|
|
t = tok.pop()
|
|
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"):
|
|
pass
|
|
elif start.isA("COMMENT"):
|
|
print(start.value)
|
|
elif start.isA("DIRECTIVE"):
|
|
if start.value in {".module", ".optsdcc"}:
|
|
while not tok.pop().isA("NEWLINE"):
|
|
pass
|
|
elif start.value == ".globl":
|
|
global_name = tok.pop().value
|
|
global_names.add(global_name)
|
|
assert tok.pop().isA("NEWLINE")
|
|
elif start.value == ".area":
|
|
area_name = tok.pop().value
|
|
if area_name == "_DATA":
|
|
print('SECTION "%s_%s", WRAM0' % (module, area_name))
|
|
elif area_name == "_DABS":
|
|
print('SECTION "%s_%s", SRAM' % (module, area_name))
|
|
elif area_name == "_HOME":
|
|
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name))
|
|
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:]))
|
|
)
|
|
elif area_name == "_CABS":
|
|
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name))
|
|
elif area_name == "_GSINIT":
|
|
print('SECTION FRAGMENT "GSINIT", ROMX, BANK[1]')
|
|
elif area_name == "_GSFINAL":
|
|
print('SECTION FRAGMENT "GSFINAL", ROMX, BANK[1]')
|
|
elif area_name == "_auto":
|
|
print('SECTION FRAGMENT "code_%s", ROMX' % (module))
|
|
else:
|
|
raise Exception(area_name)
|
|
while not tok.pop().isA("NEWLINE"):
|
|
pass
|
|
elif start.value == ".ds":
|
|
sys.stdout.write("ds ")
|
|
processExpression()
|
|
sys.stdout.write("\n")
|
|
elif start.value == ".ascii":
|
|
sys.stdout.write("db ")
|
|
sys.stdout.write(tok.pop().value)
|
|
sys.stdout.write("\n")
|
|
elif start.value == ".db":
|
|
sys.stdout.write("db ")
|
|
processParameter()
|
|
while tok.peek().isA("OP", ","):
|
|
sys.stdout.write(",")
|
|
tok.pop()
|
|
processParameter()
|
|
sys.stdout.write("\n")
|
|
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", ","):
|
|
sys.stdout.write(",")
|
|
tok.pop()
|
|
processParameter()
|
|
sys.stdout.write("\n")
|
|
elif start.value == ".incbin":
|
|
sys.stdout.write("incbin ")
|
|
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"):
|
|
tok.pop()
|
|
sys.stdout.write("%s = " % (start.value))
|
|
processExpression()
|
|
sys.stdout.write("\n")
|
|
elif tok.peek().isA("LABEL"):
|
|
label_suffix = tok.pop().value
|
|
if label_suffix == ":" and start.value in global_names:
|
|
label_suffix = "::"
|
|
print("%s%s" % (start.value, label_suffix))
|
|
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", ",")
|
|
t = tok.pop()
|
|
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"):
|
|
processParameter()
|
|
if tok.peek().isA("OP", ","):
|
|
tok.pop()
|
|
sys.stdout.write(", ")
|
|
processParameter()
|
|
sys.stdout.write("\n")
|
|
tok.expect("NEWLINE")
|
|
else:
|
|
raise Exception(start)
|