shithub: pokecrystal

Download patch

ref: eb6cb9812da6a75dc86f64be27d9ef419963790c
parent: 9a29def2503b00a971c7384e2071877aacf64eea
author: Bryan Bishop <[email protected]>
date: Sun Jan 27 14:10:42 EST 2013

dump_sections - tool to dump a skeleton asm file

--- /dev/null
+++ b/extras/dump_sections
@@ -1,0 +1,14 @@
+#!/bin/bash
+# This wraps dump_sections.py so that other python scripts can import the
+# functions. If dump_sections.py was instead called dump_sections, then other
+# python source code would be unable to use the functions via import
+# statements.
+
+# figure out the path to this script
+cwd="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+# construct the path to dump_sections.py
+secpath=$cwd/dump_sections.py
+
+# run dump_sections.py
+$secpath $1
--- /dev/null
+++ b/extras/dump_sections.py
@@ -1,0 +1,130 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+Use this tool to dump an asm file for a new source code or disassembly project.
+
+usage:
+
+    from dump_sections import dump_sections
+
+    output = dump_sections("../../butt.gbc")
+
+    file_handler = open("main.asm", "w")
+    file_handler.write(output)
+    file_handler.close()
+
+You can also use this script from the shell, where it will look for
+"baserom.gbc" in the current working path or whatever file path you pass in the
+first positional argument.
+"""
+
+import os
+import sys
+import argparse
+
+def upper_hex(input):
+    """
+    Converts the input to an uppercase hex string.
+    """
+    if input in [0, "0"]:
+        return "0"
+    elif input <= 0xF:
+        return ("%.x" % (input)).upper()
+    else:
+        return ("%.2x" % (input)).upper()
+
+def format_bank_number(address, bank_size=0x4000):
+    """
+    Returns a str of the hex number of the bank based on the address.
+    """
+    return upper_hex(address / bank_size)
+
+def calculate_bank_quantity(path, bank_size=0x4000):
+    """
+    Returns the number of 0x4000 banks in the file at path.
+    """
+    return float(os.lstat(path).st_size) / bank_size
+
+def dump_section(bank_number, separator="\n\n"):
+    """
+    Returns a str of a section header for the asm file.
+    """
+    output = "SECTION \""
+    if bank_number in [0, "0"]:
+        output += "bank0\",HOME"
+    else:
+        output += "bank"
+        output += bank_number
+        output += "\",DATA,BANK[$"
+        output += bank_number
+        output += "]"
+    output += separator
+    return output
+
+def dump_incbin_for_section(address, bank_size=0x4000, baserom="baserom.gbc", separator="\n\n"):
+    """
+    Returns a str for an INCBIN line for an entire section.
+    """
+    output = "INCBIN \""
+    output += baserom
+    output += "\",$"
+    output += upper_hex(address)
+    output += ",$"
+    output += upper_hex(bank_size)
+    output += separator
+    return output
+
+def dump_sections(path, bank_size=0x4000, initial_bank=0, last_bank=None, separator="\n\n"):
+    """
+    Returns a str of assembly source code. The source code delineates each
+    SECTION and includes bytes from the file specified by baserom.
+    """
+    if not last_bank:
+        last_bank = calculate_bank_quantity(path, bank_size=bank_size)
+
+    if last_bank < initial_bank:
+        raise Exception("last_bank must be greater than or equal to initial_bank")
+
+    baserom_name = os.path.basename(path)
+
+    output = ""
+
+    banks = range(initial_bank, last_bank)
+
+    for bank_number in banks:
+        address = bank_number * bank_size
+
+        # get a formatted hex number of the bank based on the address
+        formatted_bank_number = format_bank_number(address, bank_size=bank_size)
+
+        # SECTION
+        output += dump_section(formatted_bank_number, separator=separator)
+
+        # INCBIN a range of bytes from the ROM
+        output += dump_incbin_for_section(address, bank_size=bank_size, baserom=baserom_name, separator=separator)
+
+    # clean up newlines at the end of the output
+    if output[-2:] == "\n\n":
+        output = output[:-2]
+        output += "\n"
+
+    return output
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument("rompath", nargs="?", metavar="rompath", type=str)
+    args = parser.parse_args()
+
+    # default to "baserom.gbc" in the current working directory
+    baserom = "baserom.gbc"
+
+    # but let the user override the default
+    if args.rompath:
+        baserom = args.rompath
+
+    # generate some asm
+    output = dump_sections(baserom)
+
+    # dump everything to stdout
+    sys.stdout.write(output)
+
--- /dev/null
+++ b/extras/test_dump_sections.py
@@ -1,0 +1,74 @@
+# -*- coding: utf-8 -*-
+
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
+# check for things we need in unittest
+if not hasattr(unittest.TestCase, 'setUpClass'):
+    sys.stderr.write("The unittest2 module or Python 2.7 is required to run this script.")
+    sys.exit(1)
+
+from dump_sections import (
+    upper_hex,
+    format_bank_number,
+    calculate_bank_quantity,
+    dump_section,
+    dump_incbin_for_section,
+)
+
+class TestDumpSections(unittest.TestCase):
+    def test_upper_hex(self):
+        number = 0x52
+        self.assertEquals(number, int("0x" + upper_hex(number), 16))
+
+        number = 0x1
+        self.assertEquals(number, int("0x" + upper_hex(number), 16))
+
+        number = 0x0
+        self.assertEquals(number, int("0x" + upper_hex(number), 16))
+
+        number = 0xAA
+        self.assertEquals(number, int("0x" + upper_hex(number), 16))
+
+        number = 0xFFFFAAA0000
+        self.assertEquals(number, int("0x" + upper_hex(number), 16))
+
+    def test_format_bank_number(self):
+        address = 0x0
+        self.assertEquals("0", format_bank_number(address))
+
+        address = 0x4000
+        self.assertEquals("1", format_bank_number(address))
+
+        address = 0x1FC000
+        self.assertEquals("7F", format_bank_number(address))
+
+    def test_dump_section(self):
+        self.assertIn("SECTION", dump_section(str(0)))
+        self.assertIn("HOME", dump_section(str(0)))
+        self.assertNotIn("HOME", dump_section(str(1)))
+        self.assertIn("DATA", dump_section(str(2)))
+        self.assertIn("BANK", dump_section(str(40)))
+        self.assertNotIn("BANK", dump_section(str(0)))
+
+    def test_dump_incbin_for_section(self):
+        self.assertIn("INCBIN", dump_incbin_for_section(0))
+
+    def test_dump_incbin_for_section_separator(self):
+        separator = "\n\n"
+        self.assertIn(separator, dump_incbin_for_section(0, separator=separator))
+
+        separator = "\t\t" # dumb
+        self.assertIn(separator, dump_incbin_for_section(0, separator=separator))
+
+    def test_dump_incbin_for_section_default(self):
+        rom = "baserom.gbc"
+        self.assertIn(rom, dump_incbin_for_section(0))
+
+        rom = "baserom"
+        self.assertIn(rom, dump_incbin_for_section(0x4000))
+
+if __name__ == "__main__":
+    unittest.main()