ref: 88d32fe42cc9944209fa3e1bbfa0597ae4ecc175
parent: e7941cef61556d0b87c7384acd6c762c5c17841e
author: Remy Oukaour <remy.oukaour@gmail.com>
date: Wed Jan 3 09:41:17 EST 2018
python3 sort_map.py pokecrystal.map sorted.sym
--- /dev/null
+++ b/tools/sort_map.py
@@ -1,0 +1,103 @@
+#!/usr/bin/env python3
+
+import sys
+import re
+
+encoding = 'utf-8'
+
+def total_bank_size(type):
+ # used to output the size of EMPTY banks
+ sizes = {
+ 'ROM0': 0x4000, # 0000–3FFF
+ 'ROMX': 0x4000, # 4000–7FFF
+ 'VRAM': 0x2000, # 8000–9FFF
+ 'SRAM': 0x2000, # A000-BFFF
+ 'WRAM0': 0x1000, # C000-CFFF
+ 'WRAMX': 0x1000, # D000-DFFF
+ # E000-FDFF: echo RAM
+ 'OAM': 0xA0, # FE00-FE9F
+ # FEA0-FEFF: unusable
+ # FF00-FF7F: hardware I/O registers
+ 'HRAM': 0x80, # FF80-FFFF
+ }
+ return sizes[type]
+
+def sorted_mapfile(input):
+ bank_rx = re.compile(r'^([A-Z]+) Bank #([0-9]+)')
+ section_rx = re.compile(r' +SECTION: \$([0-9A-F]+)(?:-\$([0-9A-F]+))? \(\$([0-9A-F]+) bytes\) \["(.+)"\]')
+ label_rx = re.compile(r' +\$([0-9A-F]+) = (.+)')
+ slack_rx = re.compile(r' +SLACK: \$([0-9A-F]+) bytes')
+
+ bank_type = None
+ bank_number = None
+ bank_size = 0
+ bank_queue = []
+ section_queue = []
+
+ for line in input:
+
+ if line.startswith(' EMPTY'):
+ # empty banks have their entire capacity as slack
+ line = ' SLACK: $%04X bytes\n' % total_bank_size(bank_type)
+
+ x = re.match(bank_rx, line)
+ if x:
+ # start a new bank
+ bank_type = x.group(1)
+ bank_number = '%02X' % int(x.group(2))
+ if bank_type == 'ROM':
+ bank_type = 'ROM0' if bank_number == '00' else 'ROMX'
+ if bank_type == 'WRAM':
+ bank_type = 'WRAM0' if bank_number == '00' else 'WRAMX'
+ bank_size = 0
+ bank_queue.clear()
+ section_queue.clear()
+ continue
+
+ x = re.match(section_rx, line)
+ if x:
+ # finish current section
+ bank_queue.extend(sorted(section_queue))
+ # start a new section
+ start = x.group(1)
+ end = x.group(2) or start
+ size = x.group(3).zfill(4)
+ name = x.group(4)
+ bank_size += int(size, 16)
+ bank_queue.append('; %s:%s-%s ($%s) %s\n' % (bank_number, start, end, size, name))
+ section_queue.clear()
+ continue
+
+ x = re.match(label_rx, line)
+ if x:
+ # add label to section
+ address = x.group(1)
+ label = x.group(2)
+ section_queue.append('%s:%s %s\n' % (bank_number, address, label))
+ continue
+
+ x = re.match(slack_rx, line)
+ if x:
+ # finish current section
+ bank_queue.extend(sorted(section_queue))
+ # finish current bank
+ slack = int(x.group(1), 16)
+ yield '; %s $%s ($%s) ($%04X free)\n' % (bank_type, bank_number, bank_size, slack)
+ yield from bank_queue
+ continue
+
+def main():
+ if len(sys.argv) < 3:
+ print('Usage: %s pokecrystal.map sorted.sym' % sys.argv[0], file=sys.stderr)
+ sys.exit(1)
+ input_filename = sys.argv[1]
+ output_filename = sys.argv[2]
+ with open(input_filename, 'r', encoding=encoding) as infile:
+ input = infile.readlines()
+ output = sorted_mapfile(input)
+ with open(output_filename, 'w', encoding=encoding) as outfile:
+ for line in output:
+ outfile.write(line)
+
+if __name__ == '__main__':
+ main()