def __init__(self, memory, address): super().__init__(memory, address) while True: if address >= 0x8000 or memory[address]: break try: instr = Instruction(self.memory, address) except InstructionDecodeError: print("Encountered invalid instruction [%02x] in code at: %02x:%04x" % (memory.byte(address), memory.bankNumber, address)) break if not self.resize(len(self) + instr.size, allow_fail=True): print("Odd instruction overlap at: %02x:%04x" % (memory.bankNumber, address)) break for n in range(1, instr.size): memory.ensureNoLabel(address + n) address += instr.size target = instr.jumpTarget() if target != None and target < 0x8000: if memory.bankNumber > 0: active_bank = memory else: active_bank = memory.activeRomBankAt(address - instr.size) if active_bank is not None: active_bank = RomInfo.romBank(active_bank) other_memory = RomInfo.memoryAt(target, active_bank) if other_memory: other_block = other_memory[target] if other_block == None: other_block = CodeBlock(other_memory, target) elif isinstance(other_block, CodeBlock): if instr.type in (CALL, RST): other_block.onCall(self.memory, address - instr.size, address) else: other_block.onJump(self.memory, address - instr.size, address) other_block.addAutoLabel(target, address, instr.type) elif isinstance(instr.p0, Ref) and isinstance(instr.p0.target, int) and instr.p0.target >= 0x8000: mem = RomInfo.memoryAt(instr.p0.target, memory) if mem: mem.addAutoLabel(instr.p0.target, address, "data") elif isinstance(instr.p1, Ref) and isinstance(instr.p1.target, int): mem = RomInfo.memoryAt(instr.p1.target, memory) if mem: mem.addAutoLabel(instr.p1.target, address, "data") elif instr.p0 in (BC, DE, HL) and isinstance(instr.p1, int): if 0x4000 <= instr.p1 < 0x8000 and memory.bankNumber > 0: # Banked ROM RomInfo.memoryAt(instr.p1, memory).addAutoLabel(instr.p1, address, "data") elif 0xC000 <= instr.p1 < 0xE000: # WRAM RomInfo.memoryAt(instr.p1).addAutoLabel(instr.p1, address, "data") elif 0xFF80 <= instr.p1 < 0xFFFF: # HRAM RomInfo.memoryAt(instr.p1).addAutoLabel(instr.p1, address, "data") if not instr.hasNext(): break
def export(self, file): try: target_memory = RomInfo.memoryAt(self.__target_address, RomInfo.romBank(self.__bank)) except IndexError: file.dataLine(4) return label = target_memory.getLabel(self.__target_address) if not label: file.dataLine(4) return file.asmLine(4, "dw", str(label), "BANK(%s)" % (label), is_data=True)
def __init__(self, memory, address): super().__init__(memory, address, size=4) self.__target_address = memory.word(address) self.__bank = memory.byte(address + 2) unknown = memory.byte(address + 3) print("SDCC farcall to: %02x:%04x" % (self.__bank, self.__target_address)) try: target_memory = RomInfo.memoryAt(self.__target_address, RomInfo.romBank(self.__bank)) except IndexError: return target_block = target_memory[self.__target_address] if target_block is None: target_block = CodeBlock(target_memory, self.__target_address) target_block.addAutoLabel(self.__target_address, address, "call")
def export(self, path): for bank in RomInfo.getRomBanks(): AutoLabelLocalizer(bank) if not os.path.exists(path): shutil.copytree( os.path.join(os.path.dirname(__file__), "template"), path) open(os.path.join(path, "rom.gb.md5"), "wt").write("%s rom.gb\n" % (self.__rom.md5sum())) objfiles = [] for bank in RomInfo.getRomBanks(): print("Processing bank: %d" % (bank.bankNumber)) self.__exportRomBank( AssemblyFile( os.path.join(path, "src", "bank%02X.asm" % (bank.bankNumber)), bank), bank) f = AssemblyFile(os.path.join(path, "src", "memory.asm")) self.__exportRam(f, RomInfo.getWRam()) self.__exportRam(f, RomInfo.getHRam()) macro_file = open(os.path.join(path, "src", "include", "macros.inc"), "wt") for macro, contents in sorted(RomInfo.macros.items()): macro_file.write("%s: MACRO\n" % (macro)) for line in contents.rstrip().split("\n"): macro_file.write(" %s\n" % (line.rstrip())) macro_file.write("ENDM\n") charmap_file = open( os.path.join(path, "src", "include", "charmaps.inc"), "wt") for name, data in sorted(RomInfo.charmap.items()): if name == "main": charmap_file.write("SETCHARMAP %s\n" % (name)) else: charmap_file.write("NEWCHARMAP %s\n" % (name)) for key, value in sorted(data.items()): charmap_file.write("CHARMAP \"%s\", %d\n" % (value, key))
def processRom(self): # First process all the annotations for bank in RomInfo.getRomBanks(): for addr, comments in bank.getAllComments(): for comment in comments: if comment.startswith("@"): callAnnotation(bank, addr, comment[1:]) for addr, comment in bank.getAllInlineComments(): if comment.startswith("@"): callAnnotation(bank, addr, comment[1:]) # Next process our normal entry point, header info and interrupts. ROMHeader(RomInfo.romBank(0)) if RomInfo.romBank(0)[0x0100] is None: CodeBlock(RomInfo.romBank(0), 0x0100).addLabel(0x0100, "entry") for addr, name in [(0x0040, "isrVBlank"), (0x0048, "isrLCDC"), (0x0050, "isrTimer"), (0x0058, "isrSerial"), (0x0060, "isrJoypad")]: if RomInfo.memoryAt(addr).byte(addr) not in ( 0x00, 0xff) and RomInfo.memoryAt(addr)[addr] == None: CodeBlock(RomInfo.romBank(0), addr).addLabel(addr, name) # Finally, for any data that has no blocks on it, see if we have marks from instrumentation that can decode it for bank in RomInfo.getRomBanks(): for addr in range(bank.base_address, bank.base_address + len(bank)): if not bank[addr]: if bank.hasMark(addr, "CODE"): CodeBlock(bank, addr) elif bank.hasMark(addr, "GFX_LOW") and bank.hasMark( addr + 1, "GFX_HIGH"): size = 2 while size < 16 and bank.hasMark( addr + size, "GFX_LOW") and bank.hasMark( addr + size + 1, "GFX_HIGH"): size += 2 GfxBlock(bank, addr, bpp=2, size=size // 2) elif bank.hasMark(addr, "GFX_HIGH"): size = 1 while size < 8 and bank.hasMark( addr + size, "GFX_HIGH"): size += 1 GfxBlock(bank, addr, bpp=1, size=size) elif bank.hasMark(addr, "PTR_LOW") and bank.hasMark( addr + 1, "PTR_HIGH"): DataBlock(bank, addr, format="p", amount=1) elif bank.hasMark(addr, "WORD_LOW") and bank.hasMark( addr + 1, "WORD_HIGH"): DataBlock(bank, addr, format="w", amount=1)
def processInstrumentation(filename): f = open(filename, "rb") while True: data = f.read(16) if not data: break source, used_as = struct.unpack("<QQ", data) if (source & ID_MASK) == ID_ROM: addr = source & 0x3FFF bank = (source >> 14) & 0x3FF if bank > 0: addr |= 0x4000 memory = RomInfo.romBank(bank) if used_as & MARK_INSTR: memory.mark(addr, "CODE") if used_as & MARK_DATA: memory.mark(addr, "DATA") used_as_id = (used_as & ID_MASK) used_as_addr = (used_as & 0xFFFF) if used_as_id == ID_VRAM: if used_as_addr < 0x1800: if (used_as_addr & 1) == 0: memory.mark(addr, "GFX_LOW") else: memory.mark(addr, "GFX_HIGH") else: memory.mark(addr, "TILE") elif used_as_id == ID_OAM: pass elif used_as_id == ID_IO: pass elif used_as_id == ID_ROM: if used_as_addr & 0xF000 == 0x2000: memory.mark(addr, "BANK") if used_as & MARK_PTR_LOW: memory.mark(addr, "PTR_LOW") if used_as & MARK_PTR_HIGH: memory.mark(addr, "PTR_HIGH") if used_as & MARK_WORD_LOW: memory.mark(addr, "WORD_LOW") if used_as & MARK_WORD_HIGH: memory.mark(addr, "WORD_HIGH") if bank == 0 and (used_as & MARK_BANK_MASK) != 0 and (used_as & MARK_BANK_MASK) >> MARK_BANK_SHIFT: memory.setActiveRomBankAt(addr, (used_as & MARK_BANK_MASK) >> MARK_BANK_SHIFT)
def __init__(self, rom): self.__rom = rom RomInfo.init(rom)
def formatAsAddressOrLabel(self, target, source_addr): label = RomInfo.getLabelAt(target, RomInfo.romBank(self.memory.activeRomBankAt(source_addr))) if label: return label return "$%04x" % (target)