disassemble complex match statements

This commit is contained in:
Alex Bates 2020-11-08 14:57:58 +00:00
parent 5d36960883
commit d11595684e
3 changed files with 77 additions and 39 deletions

View File

@ -239,6 +239,7 @@ class LabelAllocation(Visitor):
self.labels = []
def label_decl(self, tree):
# TODO: int labels
name = tree.children[0].children[0]
if name in self.labels:
raise CompileError(f"label `{name}' already declared", tree.meta)

View File

@ -16,8 +16,6 @@ def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"):
struct = midx.pop(0)
name = struct["name"]
print(name)
if name == "Script_Main": name = f"M(Main)"
#print(f"{offset:X} ({name}, start = {struct['start']:X}, len = {struct['length']:X})")
@ -35,6 +33,8 @@ def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"):
try:
out += disasm_script.ScriptDSLDisassembler(bytes, f"M({name})", symbol_map).disassemble()
except disasm_script.UnsupportedScript as e:
print(f"Unable to use DSL for {struct['name']}: {e}")
bytes.seek(pos)
out += disasm_script.ScriptDisassembler(bytes, f"M({name})", symbol_map).disassemble()
elif struct["type"] == "Padding":
@ -222,6 +222,8 @@ if __name__ == "__main__":
if filetype == "bin":
with open(f"{bin_dir}/{rom_addr:X}.bin", "rb") as bytes:
print(f"Disassembling {rom_addr:X}")
disasm = disassemble(bytes, rom_addr - rom_start, midx, symbol_map, map_name)
if len(disasm.strip()) > 0:

View File

@ -78,12 +78,15 @@ class ScriptDisassembler:
if self.done:
return self.prefix + self.out
def write_line(self, line):
def write(self, line):
if self.indent < 0: self.indent = 0
if self.indent > 1: self.indent_used = True
self.out += " " * self.indent
self.out += line
def write_line(self, line):
self.write(line)
self.out += "\n"
def prefix_line(self, line):
@ -329,7 +332,17 @@ class ScriptDSLDisassembler(ScriptDisassembler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.in_case = False
# False: not in case
# True: in case block
# CASES: in condition, but { not written yet
# MULTI: in ',' condition(s), but { not written yet
self.case_stack = []
self.was_multi_case = False
@property
def in_case(self):
return self.case_stack[-1] if self.case_stack else False
def var(self, arg):
if arg in self.symbol_map:
@ -366,6 +379,27 @@ class ScriptDSLDisassembler(ScriptDisassembler):
return var
def disassemble_command(self, opcode, argc, argv):
# write case block braces
if self.in_case == "CASES" or self.in_case == "MULTI":
if opcode == 0x1D: # multi case
pass
elif 0x16 <= opcode <= 0x21: # standard case conditions
# open and close empty case
self.case_stack.pop()
self.case_stack.append(False)
else:
# open case
self.out += " {\n"
self.was_multi_case = self.in_case == "MULTI"
self.case_stack.pop()
self.case_stack.append(True)
elif self.in_case is True and 0x16 <= opcode <= 0x21: # new case
self.case_stack.pop()
self.indent -= 1
self.write_line("}")
self.indent += 1
if opcode == 0x01:
if self.out.endswith("return\n"):
# implicit return; break
@ -380,8 +414,8 @@ class ScriptDSLDisassembler(ScriptDisassembler):
self.done = True
elif opcode == 0x02: self.write_line(f"return")
elif opcode == 0x03: self.write_line(f"lbl{self.var(argv[0])}:")
elif opcode == 0x04: self.write_line(f"goto lbl{self.var(argv[0])}")
elif opcode == 0x03: self.write_line(f"{self.var(argv[0])}:")
elif opcode == 0x04: self.write_line(f"goto {self.var(argv[0])}")
elif opcode == 0x05:
if argv[0] == 0:
self.write_line("loop {")
@ -412,6 +446,9 @@ class ScriptDSLDisassembler(ScriptDisassembler):
elif opcode == 0x0F:
self.write_line(f"if {self.var(argv[0])} >= {self.var(argv[1])} {{")
self.indent += 1
elif opcode == 0x10:
self.write_line(f"if {self.var(argv[0])} ? {self.var(argv[1])} {{")
self.indent += 1
elif opcode == 0x12:
self.indent -= 1
self.write_line("} else {")
@ -422,65 +459,61 @@ class ScriptDSLDisassembler(ScriptDisassembler):
elif opcode == 0x14:
self.write_line(f"match {self.var(argv[0])} {{")
self.indent += 2
self.in_case = False
self.case_stack.append(False)
# elif opcode == 0x15:
# self.write_line(f"SI_SWITCH_CONST(0x{argv[0]:X}),")
# self.indent += 2
elif opcode == 0x16:
self.indent -= 1
if self.in_case: self.write_line("}")
self.in_case = True
self.write_line(f"{self.var(argv[0])} {{")
self.case_stack.append("CASES")
self.write(f"== {self.var(argv[0])}")
self.indent += 1
elif opcode == 0x17:
self.indent -= 1
if self.in_case: self.write_line("}")
self.in_case = True
self.write_line(f"!= {self.var(argv[0])} {{")
self.case_stack.append("CASES")
self.write(f"!= {self.var(argv[0])}")
self.indent += 1
elif opcode == 0x18:
self.indent -= 1
if self.in_case: self.write_line("}")
self.in_case = True
self.write_line(f"< {self.var(argv[0])} {{")
self.case_stack.append("CASES")
self.write(f"< {self.var(argv[0])}")
self.indent += 1
elif opcode == 0x19:
self.indent -= 1
if self.in_case: self.write_line("}")
self.in_case = True
self.write_line(f"> {self.var(argv[0])} {{")
self.case_stack.append("CASES")
self.write(f"> {self.var(argv[0])}")
self.indent += 1
elif opcode == 0x1A:
self.indent -= 1
if self.in_case: self.write_line("}")
self.in_case = True
self.write_line(f"<= {self.var(argv[0])} {{")
self.case_stack.append("CASES")
self.write(f"<= {self.var(argv[0])}")
self.indent += 1
elif opcode == 0x1B:
self.indent -= 1
if self.in_case: self.write_line("}")
self.in_case = True
self.write_line(f">= {self.var(argv[0])} {{")
self.case_stack.append("CASES")
self.write(f">= {self.var(argv[0])}")
self.indent += 1
elif opcode == 0x1C:
self.indent -= 1
if self.in_case: self.write_line("}")
self.in_case = True
self.write_line(f"else {{")
self.case_stack.append("CASES")
self.write(f"else")
self.indent += 1
elif opcode == 0x1D:
self.indent -= 1
if self.in_case == "CASES" or self.in_case == "MULTI":
self.out += f", {self.var(argv[0])}"
else:
self.write(f"{self.var(argv[0])}")
self.case_stack.append("MULTI")
self.indent += 1
# elif opcode == 0x1D:
# self.indent -= 1
# self.write_line(f"SI_CASE_OR_EQ({self.var(argv[0])}),")
# self.indent += 1
# opcode 0x1E?
elif opcode == 0x1F:
self.indent -= 1
self.write_line(f"? {self.var(argv[0])}")
self.indent += 1
# elif opcode == 0x20:
# self.indent -= 1
# self.write_line(f"SI_END_MULTI_CASE(),")
# self.indent += 1
elif opcode == 0x20 and self.was_multi_case:
self.indent -= 1
self.indent += 1
elif opcode == 0x21:
self.indent -= 1
self.write_line(f"{self.var(argv[0])}..{self.var(argv[1])} {{")
@ -488,8 +521,10 @@ class ScriptDSLDisassembler(ScriptDisassembler):
elif opcode == 0x22: self.write_line("break")
elif opcode == 0x23:
self.indent -= 1
if self.in_case: self.write_line("}")
self.in_case = False
if self.in_case:
self.write_line("}")
self.case_stack.pop()
self.case_stack.pop()
self.indent -= 1
self.write_line("}")
elif opcode == 0x24: self.write_line(f"{self.var(argv[0])} = {self.var(argv[1])}")
@ -542,7 +577,7 @@ class ScriptDSLDisassembler(ScriptDisassembler):
self.indent -= 1
self.write_line("}")
else:
raise UnsupportedScript(f"DSL does not support script opcode {opcode:X}")
raise UnsupportedScript(f"DSL does not support script opcode 0x{opcode:X}")
if __name__ == "__main__":
if len(sys.argv) <= 1: