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()