summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/progress.py225
-rw-r--r--tools/script_extractor.py284
-rw-r--r--tools/tcgdisasm.py30
3 files changed, 536 insertions, 3 deletions
diff --git a/tools/progress.py b/tools/progress.py
new file mode 100644
index 0000000..b1e1160
--- /dev/null
+++ b/tools/progress.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python3#
+import subprocess
+import math
+import argparse
+
+# TODO -
+# -- reportINCROMs --
+ # add in percentage
+# -- reportUnnamedSymbols --
+
+# global vals
+banks = 0x40
+
+def main():
+ parser = argparse.ArgumentParser(description='Progress checker for poketcg')
+ parser.add_argument('-i', '--incrom', action='store_true', help="Turns on incrom report")
+ parser.add_argument('-d', '--directory', default=".", help="Override incrom search directory. Ignores if incrom report is off")
+ parser.add_argument('-s', '--symfile', default=None, type=argparse.FileType('r'), help="Turns on Unnamed Symbol report using given sym file")
+ parser.add_argument('-f', '--function_source', action='store_true', help='Shows a breakdown of what bank each unnamed function comes from. Ignores if symfile report is off')
+ parser.add_argument('-o', '--other_unnamed', action='store_true', help='Shows all other unnamed symbols and a count of how many there are. Ignores if symfile report is off')
+ parser.add_argument('--list_funcs', nargs="+", default=None, help="Lists every unnamed function in the given banks. WILL BE LONG. ignores if symfile report is off")
+
+ args = parser.parse_args()
+
+ if args.incrom:
+ reportINCROMs(args.directory)
+ print("\n")
+
+ if args.symfile != None:
+ # parse the list command
+ listBankSet = set([])
+ if args.list_funcs != None:
+ listBankSet = parseBankList(args.list_funcs)
+ reportUnnamedSymbols(args.symfile,listBankSet, args.function_source, args.other_unnamed)
+
+def reportINCROMs(incromDir):
+ grepProc = subprocess.Popen(['grep', '-r', 'INCROM', incromDir], stdout=subprocess.PIPE)
+ targetLines = grepProc.communicate()[0].decode().split('\n')
+ incromBytes = [0]*banks
+ incromByteTotal = 0
+ for line in targetLines:
+ line = line.lower() # ignore case
+
+ # ignore the actual definition of the macro
+ if 'macro' in line:
+ continue
+
+ # ignore anything in tools
+ if '/tools/' in line:
+ continue
+
+ # ignore binary files in case swp's exist
+ if 'binary file' in line:
+ continue
+
+ # find the last two hex location values
+ splitLine = line.split("$")
+
+ # not sure what the line is, but it's not working so skip
+ if len(splitLine) < 3:
+ continue
+
+ incEnd = int(splitLine[-1],16)
+ incStart = int(splitLine[-2].split(",",1)[0],16)
+ incBank = math.floor(incStart / 0x4000)
+ diff = incEnd - incStart
+ incromBytes[incBank] += diff
+ incromByteTotal += diff
+ print("Total: " + str(incromByteTotal) + " bytes")
+ print("Made up of the following: ")
+ for i in range(0,banks):
+ if incromBytes[i] == 0:
+ continue
+
+ bankName= "bank" + format(i,"02x") + ": "
+ if i == 0:
+ bankName = "home: "
+ bytesString = str(incromBytes[i])
+ formattingStrings = " "*(8-len(bytesString))
+ print(bankName + bytesString + formattingStrings + "bytes")
+
+
+# reads sym files and looks for instances of tcgdisasm's automatic symbols
+def reportUnnamedSymbols(symfile, listBankSet, showFunctionBanks, showOtherUnnamed):
+ data = symfile.read().split("\n")
+
+ # format [ [ "type" : number ], ... ]
+ typeCounts = []
+
+ # to cut back on for loops I'll manually list the super common ones, such as Func
+ funcCounts = [0]*banks
+ funcCount = 1
+ branchCount = 0
+ wramCount = 0
+ sramCount = 0
+ hramCount = 0
+
+ labelTotal = 0
+ localLabelTotal = 0
+ unnamedLocalLabelTotal = 0
+ unnamedLabelTotal = 0
+
+ # expecting all lines to be formated as `bank:addr name`
+ for line in data:
+
+ splitline = line.split(":")
+
+ # line not formatted as expected
+ if len(splitline) < 2:
+ continue
+
+ # at this point it's probably some form of label
+ if "." in line:
+ localLabelTotal += 1
+ else:
+ labelTotal += 1
+
+ bank = int(splitline[0], 16)
+ splitline = splitline[1].split(" ")
+
+ # line not formatted as expected
+ if len(splitline) < 2:
+ continue
+
+ localAddr = int(splitline[0], 16)
+ name = splitline[1]
+
+ globalAddr = bank*0x4000 + localAddr
+ if bank > 0:
+ globalAddr -= 0x4000
+
+ globalAddrString = format(globalAddr,"04x")
+ if name.endswith(globalAddrString):
+
+ # don't pay as much attention to local labels
+ if "." in line:
+ unnamedLocalLabelTotal += 1
+ continue
+ else:
+ unnamedLabelTotal += 1
+
+ labelType = name[0:len(globalAddrString)*-1]
+
+ # take care of the common ones before looping
+ if labelType == "Func_":
+ if bank in listBankSet:
+ print("bank " + format(bank,'02x') + ":" + name)
+ funcCounts[bank] += 1
+ funcCount += 1
+ continue
+ elif labelType == "Branch_":
+ branchCount += 1
+ continue
+ elif labelType == "w":
+ wramCount += 1
+ continue
+ elif labelType in ["s0","s1","s2","s3"]: # all that are listed in sram.asm
+ sramCount += 1
+ continue
+ elif labelType == "h":
+ hramCount += 1
+ continue
+
+ foundType = False
+ for tc in typeCounts:
+ if tc[0] == labelType:
+ tc[1] += 1
+ foundType = True
+
+ if not foundType:
+ typeCounts.append([labelType,1])
+
+
+ # there are so many that I did them manually, but they're a misc type
+ typeCounts.append(["Branch_", branchCount])
+
+ # do some sorting.
+ typeCounts = sorted(typeCounts, key = lambda x: x[1], reverse = True)
+
+ namedLabelTotal = labelTotal - unnamedLabelTotal
+ namedLabelPercent = round((namedLabelTotal / labelTotal)*100, 3)
+ namedLocalLabelTotal = localLabelTotal - unnamedLocalLabelTotal
+ namedLocalLabelPercent = round((namedLocalLabelTotal / localLabelTotal)*100, 3)
+
+ print("Named Labels: " + str(namedLabelTotal) + "/" + str(labelTotal) + " (" + str(namedLabelPercent) + "%)")
+ print("Named Local Labels: " + str(namedLocalLabelTotal) + "/" + str(localLabelTotal) + " (" + str(namedLocalLabelPercent) + "%)")
+ print()
+ print("func count: " + str(funcCount))
+ if showFunctionBanks:
+ for i in range(0,banks):
+ if funcCounts[i] == 0:
+ continue
+ bank = "bank" + format(i,"02x") + ":"
+ if i == 0:
+ bank = "home: "
+ print("\t" + bank + " " + str(funcCounts[i]))
+
+ print("wram count: " + str(wramCount))
+ print("sram count: " + str(sramCount))
+ print("hram count: " + str(hramCount))
+ if showOtherUnnamed:
+ print()
+ print("Additional types:")
+
+ for tc in typeCounts:
+ spaces = " " * (30 - len(tc[0]))
+ if tc[1] == 1:
+ print(tc[0])
+ continue
+ print(tc[0] + spaces + "x" + format(tc[1],"02"))
+
+def parseBankList(strList):
+ retSet = set([])
+ for bankName in strList:
+ if bankName == "home":
+ retSet.add(0)
+ elif bankName.startswith("bank"):
+ retSet.add(int(bankName[4:],16))
+ else:
+ retSet.add(int(bankName,0))
+ return retSet
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/script_extractor.py b/tools/script_extractor.py
new file mode 100644
index 0000000..f043010
--- /dev/null
+++ b/tools/script_extractor.py
@@ -0,0 +1,284 @@
+#!/usr/bin/env python3
+###############################################################################
+###### Use: python3 tools/script_extractor --noauto --error location ######
+###### --noauto turns off automatic script parsing (enter to continue) ######
+###### --error stops execution if an error occurs ######
+###### location can be local to bank or global. This program assumes ######
+###### every script is in bank 3, which seems to be the case. ######
+###### ######
+###### Script list is a work in progress. The following arguments are ######
+###### accepted and accounted for. ######
+###### b - byte w - word j - jump (within script) t - text (tx) ######
+###### f - flag d - direction i - decimal byte m - npc move ptr ######
+###### q - Used when the script's arguments have not been determined yet ######
+###############################################################################
+import argparse
+
+# Quit Types
+DO_NOT_QUIT = 0
+QUIT_CONTINUE_CODE = 1
+QUIT_JUMP = 2
+QUIT_SPECIAL = 3
+QUIT_DEBUG = -1
+
+dir_list = ["NORTH","EAST","SOUTH","WEST"]
+
+def printHeader(loc, prefix):
+ ls = format(loc,"04x")
+ lsa = format(loc-0x8000,"04x")
+ print(prefix + ls + ": ; " + ls + " (3:" + lsa + ")" )
+
+def extractMovement(game_data, loc, errQuit):
+ printHeader(loc, "NPCMovement_")
+ loc -= 1 # so we can continue without breaking things
+ while game_data[loc+1] != 0xff:
+ loc += 1
+ dirLow = game_data[loc] & 0x0f
+ if dirLow > 3:
+ print("ERROR: [" + format(loc,"04x") + "] was not a valid direction. Got: " + format(game_data[loc],"02x"))
+ if errQuit:
+ return QUIT_DEBUG
+ continue
+ lineStr = "\tdb " + dir_list[dirLow]
+ dirHigh = game_data[loc] & 0xf0
+ if dirHigh == 0x80:
+ lineStr += " | NO_MOVE"
+ elif dirHigh != 0x00:
+ print("ERROR: [" + format(loc,"04x") + "] was not a valid direction. Got: " + format(game_data[loc],"02x"))
+ if errQuit:
+ return QUIT_DEBUG
+ continue
+ print(lineStr)
+ print("\tdb $ff")
+ print("; " + format(loc+2,"04x"))
+ return DO_NOT_QUIT
+
+def decodeLine(scriptList, game_data, loc, ignore_broken, locList):
+ currLine = scriptList[game_data[loc]]
+ ret = "\trun_command " + currLine[0] + "\n"
+ loc+=1
+ quit = currLine[2]
+ for c in currLine[1]:
+ if c == "b":
+ ret += "\tdb $" + format(game_data[loc],"02x") + "\n"
+ loc += 1
+ elif c == "i":
+ ret += "\tdb " + str(game_data[loc]) + "\n"
+ loc += 1
+ elif c == "w":
+ ret += "\tdw $" + format((game_data[loc] + (game_data[loc+1]<<8)),"04x") + "\n"
+ loc += 2
+ elif c == "j":
+ wordLoc = (game_data[loc] + (game_data[loc+1]<<8))
+ if wordLoc == 0000:
+ ret += "\tdw NO_JUMP\n"
+ else:
+ ret += "\tdw .ows_" + format(wordLoc+0x8000,"04x") + "\n"
+ locList.append(wordLoc)
+ loc += 2
+ elif c == "t":
+ addr = (game_data[loc] + (game_data[loc+1]<<8))
+ if addr == 0:
+ ret += "\tdw $0000\n"
+ else:
+ ret += "\ttx Text" + format(addr,"04x") + "\n"
+ loc += 2
+ elif c == "f":
+ ret += "\tdb EVENT_FLAG_" + format(game_data[loc],"02X") + "\n"
+ loc += 1
+ elif c == "d":
+ ret += "\tdb " + dir_list[game_data[loc]] + "\n"
+ loc += 1
+ elif c == "m":
+ wordLoc = (game_data[loc] + (game_data[loc+1]<<8))
+ ret += "\tdw NPCMovement_" + format(wordLoc + 0x8000, "04x") + "\n"
+ loc += 2
+ elif c == "q":
+ print("haven't updated data for this yet")
+ if not ignore_broken:
+ quit = QUIT_DEBUG
+ else:
+ print("UNACCEPTED CHARACTER: " + c)
+ return (loc, ret, quit)
+
+def main():
+ scriptList = createList()
+ locList = []
+
+ parser = argparse.ArgumentParser(description='Pokemon TCG Script Extractor')
+ parser.add_argument('--noauto', action='store_true', help='turns off automatic script parsing')
+ parser.add_argument('--error', action='store_true', help='stops execution if an error occurs')
+ parser.add_argument('-m', '--movement', action='store_true', help='interprets bytes as a movement sequence rather than a Script')
+ parser.add_argument('-r', '--rom', default="baserom.gbc", help='rom file to extract script from')
+ parser.add_argument('locations', nargs="+", help='locations to extract from. May be local to bank or global.')
+ args = parser.parse_args()
+ for locStr in args.locations:
+ loc = int(locStr,0)
+ if loc > 0x7fff:
+ # Must be a global location
+ loc -= 0x8000
+ locList.append(loc)
+
+ # this is a list of every start location we've read to avoid infinite loops
+ exploredList = []
+
+ with open(args.rom, "rb") as file:
+ game_data = file.read()
+
+ auto = not args.noauto
+ end = DO_NOT_QUIT
+ ignore_broken = not args.error
+ while (len(locList) > 0 and end != QUIT_DEBUG):
+ locList.sort() # export parts in order somewhat
+ loc = locList.pop(0) + 0x8000
+ if args.movement:
+ end = extractMovement(game_data,loc, args.error)
+ else:
+ end = printScript(game_data, loc, auto, ignore_broken, scriptList,\
+ locList, exploredList)
+
+def printScript(game_data, loc, auto, ignore_broken, scriptList, \
+ locList, exploredList):
+ if loc in exploredList:
+ return
+ exploredList.append(loc)
+ script = ""
+ end = DO_NOT_QUIT
+ if game_data[loc] != 0xe7:
+ #print("Error: first byte was not start_script")
+ print(".ows_" + format(loc,"04x"))
+ else:
+
+ # TODO this is hacky please don't do this
+ printHeader(loc, "Script_")
+ loc += 1
+ print("\tstart_script")
+ while end == DO_NOT_QUIT:
+ loc, outstr, end = decodeLine(scriptList,game_data,loc,ignore_broken,locList)
+ outstr = outstr[:-1] # [:-1] strips the newline at the end
+ if auto:
+ print(outstr)
+ else:
+ input(outstr)
+ warning = ""
+ if end == QUIT_CONTINUE_CODE:
+ warning = " WARNING: There is probably regular assembly here"
+
+ print("; " + hex(loc) + warning)
+
+ # if the next byte is a ret, print it for the continue_code case
+ if game_data[loc] == 0xc9:
+ print("\tret")
+
+ return end
+
+def createList(): # this is a func just so all this can go at the bottom
+ # name, arg list, is an ender
+ return [
+ ("ScriptCommand_EndScriptLoop1", "", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_CloseAdvancedTextBox", "", DO_NOT_QUIT),
+ ("ScriptCommand_PrintTextString", "t", DO_NOT_QUIT),
+ ("Func_ccdc", "t", DO_NOT_QUIT),
+ ("ScriptCommand_AskQuestionJump", "tj", DO_NOT_QUIT), # more complex behavior too (jumping)
+ ("ScriptCommand_StartBattle", "bbb", DO_NOT_QUIT),
+ ("ScriptCommand_PrintVariableText", "tt", DO_NOT_QUIT),
+ ("Func_cda8", "bbbb", DO_NOT_QUIT),
+ ("ScriptCommand_PrintTextQuitFully", "t", QUIT_SPECIAL),
+ ("Func_cdcb", "", DO_NOT_QUIT),
+ ("ScriptCommand_MoveActiveNPCByDirection", "w", DO_NOT_QUIT),
+ ("ScriptCommand_CloseTextBox", "", DO_NOT_QUIT),
+ ("ScriptCommand_GiveBoosterPacks", "bbb", DO_NOT_QUIT),
+ ("Func_cf0c", "bj", DO_NOT_QUIT), # more complex behavior too (jumping)
+ ("Func_cf12", "bj", DO_NOT_QUIT),
+ ("ScriptCommand_GiveCard", "b", DO_NOT_QUIT),
+ ("ScriptCommand_TakeCard", "b", DO_NOT_QUIT),
+ ("Func_cf53", "w", DO_NOT_QUIT), # more complex behavior too (jumping)
+ ("Func_cf7b", "", DO_NOT_QUIT),
+ ("Func_cf2d", "bbbb", DO_NOT_QUIT), # more complex behavior too (jumping + ??)
+ ("Func_cf96", "w", DO_NOT_QUIT), # only jumps? still needs args to do that though
+ ("Func_cfc6", "b", DO_NOT_QUIT),
+ ("Func_cfd4", "", DO_NOT_QUIT),
+ ("Func_d00b", "", DO_NOT_QUIT), # includes something with random and extra data
+ ("Func_d025", "w", DO_NOT_QUIT), # possibly only jumps, still needs args
+ ("Func_d032", "w", DO_NOT_QUIT), # see above
+ ("Func_d03f", "", DO_NOT_QUIT),
+ ("ScriptCommand_Jump", "j", QUIT_JUMP), # jumps to d
+ ("ScriptCommand_TryGiveMedalPCPacks", "", DO_NOT_QUIT),
+ ("ScriptCommand_SetPlayerDirection", "d", DO_NOT_QUIT),
+ ("ScriptCommand_MovePlayer", "db", DO_NOT_QUIT),
+ ("ScriptCommand_ShowCardReceivedScreen", "b", DO_NOT_QUIT),
+ ("ScriptCommand_SetDialogName", "b", DO_NOT_QUIT),
+ ("ScriptCommand_SetNextNPCandScript", "bj", DO_NOT_QUIT),
+ ("Func_d095", "bbb", DO_NOT_QUIT),
+ ("Func_d0be", "bb", DO_NOT_QUIT),
+ ("ScriptCommand_DoFrames", "i", DO_NOT_QUIT),
+ ("Func_d0d9", "bbw", DO_NOT_QUIT), # jumps but still needs args
+ ("ScriptCommand_JumpIfPlayerCoordMatches", "iij", DO_NOT_QUIT), # jumps but still needs args
+ ("ScriptCommand_MoveActiveNPC", "m", DO_NOT_QUIT),
+ ("ScriptCommand_GiveOneOfEachTrainerBooster", "", DO_NOT_QUIT),
+ ("Func_d103", "q", DO_NOT_QUIT),
+ ("Func_d125", "b", DO_NOT_QUIT),
+ ("Func_d135", "b", DO_NOT_QUIT),
+ ("Func_d16b", "b", DO_NOT_QUIT),
+ ("Func_cd4f", "bbb", DO_NOT_QUIT),
+ ("Func_cd94", "q", DO_NOT_QUIT),
+ ("ScriptCommand_MoveWramNPC", "m", DO_NOT_QUIT),
+ ("Func_cdd8", "", DO_NOT_QUIT),
+ ("Func_cdf5", "bb", DO_NOT_QUIT),
+ ("Func_d195", "", DO_NOT_QUIT),
+ ("Func_d1ad", "", DO_NOT_QUIT),
+ ("Func_d1b3", "", DO_NOT_QUIT),
+ ("ScriptCommand_QuitScriptFully", "", QUIT_SPECIAL),
+ ("Func_d244", "q", DO_NOT_QUIT),
+ ("Func_d24c", "q", DO_NOT_QUIT),
+ ("ScriptCommand_OpenDeckMachine", "b", DO_NOT_QUIT),
+ ("Func_d271", "q", DO_NOT_QUIT),
+ ("ScriptCommand_EnterMap", "bbood", DO_NOT_QUIT),
+ ("ScriptCommand_MoveArbitraryNPC", "bm", DO_NOT_QUIT),
+ ("Func_d209", "", DO_NOT_QUIT),
+ ("Func_d38f", "b", DO_NOT_QUIT),
+ ("Func_d396", "b", DO_NOT_QUIT),
+ ("Func_cd76", "", DO_NOT_QUIT),
+ ("Func_d39d", "b", DO_NOT_QUIT),
+ ("Func_d3b9", "", DO_NOT_QUIT),
+ ("ScriptCommand_TryGivePCPack", "b", DO_NOT_QUIT),
+ ("ScriptCommand_nop", "", DO_NOT_QUIT),
+ ("Func_d3d4", "q", DO_NOT_QUIT),
+ ("Func_d3e0", "", DO_NOT_QUIT),
+ ("Func_d3fe", "q", DO_NOT_QUIT),
+ ("Func_d408", "b", DO_NOT_QUIT),
+ ("Func_d40f", "q", DO_NOT_QUIT),
+ ("ScriptCommand_PlaySFX", "b", DO_NOT_QUIT),
+ ("ScriptCommand_PauseSong", "q", DO_NOT_QUIT),
+ ("ScriptCommand_ResumeSong", "q", DO_NOT_QUIT),
+ ("Func_d41d", "", DO_NOT_QUIT),
+ ("ScriptCommand_WaitForSongToFinish", "q", DO_NOT_QUIT),
+ ("Func_d435", "b", DO_NOT_QUIT),
+ ("ScriptCommand_AskQuestionJumpDefaultYes", "tj", DO_NOT_QUIT),
+ ("Func_d2f6", "q", DO_NOT_QUIT),
+ ("Func_d317", "", DO_NOT_QUIT),
+ ("Func_d43d", "", DO_NOT_QUIT),
+ ("ScriptCommand_EndScriptLoop2", "q", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_EndScriptLoop3", "q", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_EndScriptLoop4", "q", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_EndScriptLoop5", "q", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_EndScriptLoop6", "q", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_SetFlagValue", "fb", DO_NOT_QUIT),
+ ("ScriptCommand_JumpIfFlagZero1", "fj", DO_NOT_QUIT),
+ ("ScriptCommand_JumpIfFlagNonzero1", "q", DO_NOT_QUIT),
+ ("ScriptCommand_JumpIfFlagEqual", "fbj", DO_NOT_QUIT), # also capable of jumping
+ ("ScriptCommand_JumpIfFlagNotEqual", "fbj", DO_NOT_QUIT), # jumps
+ ("ScriptCommand_JumpIfFlagNotLessThan", "fbj", DO_NOT_QUIT),
+ ("ScriptCommand_JumpIfFlagLessThan", "fbj", DO_NOT_QUIT),
+ ("ScriptCommand_MaxOutFlagValue", "f", DO_NOT_QUIT),
+ ("ScriptCommand_ZeroOutFlagValue", "f", DO_NOT_QUIT),
+ ("ScriptCommand_JumpIfFlagNonzero2", "fj", DO_NOT_QUIT),
+ ("ScriptCommand_JumpIfFlagZero2", "fj", DO_NOT_QUIT),
+ ("ScriptCommand_IncrementFlagValue", "f", DO_NOT_QUIT),
+ ("ScriptCommand_EndScriptLoop7", "q", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_EndScriptLoop8", "q", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_EndScriptLoop9", "q", QUIT_CONTINUE_CODE),
+ ("ScriptCommand_EndScriptLoop10", "q", QUIT_CONTINUE_CODE)
+ ]
+
+main()
diff --git a/tools/tcgdisasm.py b/tools/tcgdisasm.py
index d6731d0..579d577 100644
--- a/tools/tcgdisasm.py
+++ b/tools/tcgdisasm.py
@@ -304,12 +304,14 @@ bit_ops_table = [
"set 7, b", "set 7, c", "set 7, d", "set 7, e", "set 7, h", "set 7, l", "set 7, [hl]", "set 7, a" # $f8 - $ff
]
-unconditional_returns = [0xc9, 0xd9]
+unconditional_returns = [0xc9, 0xd9, 0xe7] # e7 begins a script, which is not handled by tcgdisasm
absolute_jumps = [0xc3, 0xc2, 0xca, 0xd2, 0xda]
call_commands = [0xcd, 0xc4, 0xcc, 0xd4, 0xdc, 0xdf, 0xef]
relative_jumps = [0x18, 0x20, 0x28, 0x30, 0x38]
unconditional_jumps = [0xc3, 0x18]
+# the flag macros found in bank 3. They db a byte after calling so need to be treated specially
+flag_macros = [(0xca8f,"set_flag_value {}"),(0xcacd,"zero_flag_value {}"),(0xca84,"zero_flag_value2 {}"), (0xcac2,"max_flag_value {}"), (0xca69,"get_flag_value {}")]
def asm_label(address):
"""
@@ -740,11 +742,17 @@ class Disassembler(object):
opcode_output_str = bit_ops_table[opcode_arg_1]
elif opcode_nargs == 2:
+
+ # define opcode_output_str as None so we can substitute our own if a macro appears
+ opcode_output_str = None
+
# opcodes with a pointer as an argument
# format the two arguments into a little endian 16-bit pointer
local_target_offset = opcode_arg_2 << 8 | opcode_arg_1
+
# get the global offset of the pointer
target_offset = get_global_address(local_target_offset, bank_id)
+
# attempt to look for a matching label
if opcode_byte == 0xdf:
# bank1call
@@ -753,7 +761,22 @@ class Disassembler(object):
# regular call or jump instructions
target_label = self.find_label(local_target_offset, bank_id)
- if opcode_byte in call_commands + absolute_jumps:
+ # handle the special flag macros
+ found_flag_macro = False
+ for flag_macro in flag_macros:
+ if flag_macro[0] == target_offset:
+ found_flag_macro = True
+ current_flag_macro = flag_macro
+ event_flag = "EVENT_FLAG_" + format(opcode_arg_3, "02X")
+ opcode_output_str = flag_macro[1].format(event_flag)
+
+ # we need to skip a byte since this macro takes one extra
+ opcode_nargs+=1
+ break
+
+
+ if not found_flag_macro and opcode_byte in call_commands + absolute_jumps:
+
if target_label is None:
# if this is a call or jump opcode and the target label is not defined, create an undocumented label descriptor
target_label = "Func_%x" % target_offset
@@ -793,7 +816,8 @@ class Disassembler(object):
data_tables[local_target_offset]["definition"] = False
# format the label that was created into the opcode string
- opcode_output_str = opcode_str.format(target_label)
+ if opcode_output_str is None:
+ opcode_output_str = opcode_str.format(target_label)
elif opcode_nargs == 3:
# macros with bank and pointer as an argument