summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rwxr-xr-xutils/coverage.py70
-rw-r--r--utils/disasm_coverage.py102
-rw-r--r--utils/mapreader.py33
4 files changed, 94 insertions, 115 deletions
diff --git a/Makefile b/Makefile
index a3811b7..dad5b16 100644
--- a/Makefile
+++ b/Makefile
@@ -57,8 +57,8 @@ tidy:
# Visualize disassembly progress.
.PHONY: coverage
-coverage: $(ROM:.gb=.map) utils/disasm_coverage.py
- $(PYTHON) utils/disasm_coverage.py -m $< -b 0x40
+coverage: $(ROM:.gb=.map) utils/coverage.py
+ $(PYTHON) utils/coverage.py $<
%.map: %.gb
diff --git a/utils/coverage.py b/utils/coverage.py
new file mode 100755
index 0000000..3a3fca0
--- /dev/null
+++ b/utils/coverage.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+Usage: python3 coverage.py [pokegold-spaceworld.map] [coverage.png]
+
+Generate a PNG visualizing the space used by each bank in the ROM.
+"""
+
+import sys
+import png
+from colorsys import hls_to_rgb
+
+from mapreader import MapReader
+
+def main():
+ mapfile = sys.argv[1] if len(sys.argv) >= 2 else 'pokegold-spaceworld.map'
+ filename = sys.argv[2] if len(sys.argv) >= 3 else 'coverage.png'
+
+ num_banks = 0x40
+ bank_mask = 0x3FFF
+ bank_size = 0x4000 # bytes
+
+ bpp = 8 # bytes per pixel
+ height = 256 # pixels
+ assert bank_size % bpp == 0 and (bank_size // bpp) % height == 0
+
+ pixels_per_bank = bank_size // bpp # 2048 pixels
+ bank_width = pixels_per_bank // height # 8 pixels
+ width = bank_width * num_banks # 1024 pixels
+
+ r = MapReader()
+ with open(mapfile, 'r', encoding='utf-8') as f:
+ l = f.readlines()
+ r.read_map_data(l)
+
+ hit_data = []
+ default_bank_data = {'sections': [], 'used': 0, 'slack': bank_size}
+ for bank in range(num_banks):
+ hits = [0] * pixels_per_bank
+ data = r.bank_data['rom bank'].get(bank, default_bank_data)
+ for s in data['sections']:
+ if s['beg'] > s['end']:
+ continue
+ if s['beg'] == 0x0000 and s['end'] > 0xFFFF:
+ # https://github.com/rednex/rgbds/issues/515
+ continue
+ beg = s['beg'] & bank_mask
+ end = s['end'] & bank_mask
+ for i in range(beg, end + 1):
+ hits[i // bpp] += 1
+ hit_data.append(hits)
+
+ pixels = [[(0xFF, 0xFF, 0xFF)] * width for _ in range(height)]
+ for bank, hits in enumerate(hit_data):
+ hue = 0 if not bank else 210 if bank % 2 else 270
+ for i, h in enumerate(hits):
+ y = i // bank_width
+ x = i % bank_width + bank * bank_width
+ hls = (hue / 360.0, 1.0 - (h / bpp * (100 - 15)) / 100.0, 1.0)
+ rgb = tuple(int(c * 255) for c in hls_to_rgb(*hls))
+ pixels[y][x] = rgb
+
+ png_data = [tuple(c for pixel in row for c in pixel) for row in pixels]
+ with open(filename, 'wb') as f:
+ w = png.Writer(width, height)
+ w.write(f, png_data)
+
+if __name__ == '__main__':
+ main()
diff --git a/utils/disasm_coverage.py b/utils/disasm_coverage.py
deleted file mode 100644
index 6a68730..0000000
--- a/utils/disasm_coverage.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-
-from __future__ import division
-
-import os
-import sys
-import argparse
-import png
-from mapreader import MapReader
-from colorsys import hls_to_rgb
-
-if __name__ == '__main__':
- # argument parser
- ap = argparse.ArgumentParser()
- ap.add_argument('-r', dest='romname')
- ap.add_argument('-o', dest='filename', default='coverage.png')
- ap.add_argument('-m', dest='mapfile', required=True)
- ap.add_argument('-b', dest='num_banks', required=True, type=lambda x: int(x, 0))
- args = ap.parse_args()
-
- bank_mask = 0x3FFF
- bank_size = 0x4000 # bytes
- width = 256 # pixels per row
- bpp = 8 # bytes per pixel
-
- romname = args.romname
- rom_size = args.num_banks * bank_size # bytes
- height = (args.num_banks * bank_size + (width * bpp - 1)) // (width * bpp) # pixels
- rows_per_bank = bank_size // (width * bpp)
-
- r = MapReader()
- try:
- with open(args.mapfile, 'r') as f:
- l = f.readlines()
- except UnicodeDecodeError:
- # Python 3 seems to choke on the file's encoding, but the `encoding` keyword only works on Py3
- with open(args.mapfile, 'r', encoding= 'utf-8') as f:
- l = f.readlines()
- r.read_map_data(l)
-
- default_bank_data = {'sections': [], 'used': 0, 'slack': bank_size}
- filler = [0x00, 0xFF]
-
- if (romname is not None):
- with open(romname, 'rb') as f:
- for rb in range(0, args.num_banks):
- bank_data = r.bank_data['ROM0 bank' if rb == 0 else 'ROMX bank']
- data = bank_data.get(rb, default_bank_data)
- bank = f.read(bank_size)
- if (bank[bank_size - 1] in filler):
- fill = bank[bank_size - 1]
- for i in reversed(range(-1, bank_size - 1)):
- if (i < 0 or bank[i] != fill):
- break
- # i is now pointing to first different byte
- beg = i + 1 + (0 if rb == 0 else bank_size)
- end = bank_size + (0 if rb == 0 else bank_size)
- data['sections'].append({'beg': beg, 'end': end, 'name': 'Section_Trailing_Fill', 'symbols': []})
-
- hit_data = [[0] * width for _ in range(height)]
- for bank in range(args.num_banks):
- bank_data = r.bank_data['ROM0 bank' if bank == 0 else 'ROMX bank']
- data = bank_data.get(bank, default_bank_data)
- for s in data['sections']:
- beg = (s['beg'] & bank_mask) + bank * bank_size
- end = ((s['end'] -1) & bank_mask) + bank * bank_size # end is exclusive
- # skip zero-sized entries
- if (s['beg'] == s['end']):
- continue
- y_beg = beg // (width * bpp)
- x_beg = (beg % (width * bpp)) // bpp
- y_end = end // (width * bpp)
- x_end = (end % (width * bpp)) // bpp
- #print('beg {0} end {1}: {2}/{3} -- {4}/{5}'.format(beg, end, y_beg, x_beg, y_end, x_end))
- # special case y_beg/x_beg and y_end/x_end
- if (y_beg == y_end and x_beg == x_end):
- hit_data[y_beg][x_beg] += end - beg + 1
- else:
- hit_data[y_beg][x_beg] += bpp - ((beg % (width * bpp)) - x_beg * bpp)
- hit_data[y_end][x_end] += ((end % (width * bpp)) - x_end * bpp + 1)
- # regular case
- for y in range(y_beg, y_end + 1):
- x_line_beg = 0 if y_beg != y else x_beg + 1
- x_line_end = width - 1 if y_end != y else x_end - 1
- for x in range(x_line_beg, x_line_end + 1):
- hit_data[y][x] += bpp
-
- png_data = []
- for i, row in enumerate(hit_data):
- bank = i // rows_per_bank
- hue = 0 if bank % 2 else 120
- row_png_data = ()
- for col in row:
- hls = (hue/360.0, 1.0 - (col/bpp * (100 - 15))/100.0, 1.0)
- rgb = tuple(255 * x for x in hls_to_rgb(*hls))
- row_png_data += rgb
- png_data.append(row_png_data)
-
- with open(args.filename, 'wb') as f:
- w = png.Writer(width, height)
- w.write(f, png_data)
diff --git a/utils/mapreader.py b/utils/mapreader.py
index 1164cc4..0538d99 100644
--- a/utils/mapreader.py
+++ b/utils/mapreader.py
@@ -6,7 +6,7 @@ import re
class MapReader:
- # {'ROM Bank': { 0: { 'sections': [ { 'beg': 1234,
+ # {'rom bank': { 0: { 'sections': [ { 'beg': 1234,
# 'end': 5678,
# 'name': 'Section001',
# 'symbols': [ { 'symbol': 'Function1234',
@@ -19,7 +19,7 @@ class MapReader:
# 'slack': 4567,
# },
# },
- # 'OAM': { 'sections': [ { 'beg': 1234,
+ # 'oam': { 'sections': [ { 'beg': 1234,
# 'end': 5678,
# 'name': 'Section002',
# 'symbols': [ { 'symbol': 'Data1234',
@@ -36,13 +36,21 @@ class MapReader:
bank_data = {}
bank_types = {
- 'HRAM' : { 'size': 0x80, 'banked': False, },
- 'OAM' : { 'size': 0xA0, 'banked': False, },
- 'ROM0 bank': { 'size': 0x4000, 'banked': True, },
- 'ROMX bank': { 'size': 0x4000, 'banked': True, },
- 'SRAM bank': { 'size': 0x2000, 'banked': True, },
- 'VRAM bank': { 'size': 0x1000, 'banked': True, },
- 'WRAM bank': { 'size': 0x2000, 'banked': True, },
+ 'hram bank': { 'size': 0x80, 'banked': False, },
+ 'oam bank' : { 'size': 0xA0, 'banked': False, },
+ 'rom bank' : { 'size': 0x4000, 'banked': True, },
+ 'sram bank': { 'size': 0x2000, 'banked': True, },
+ 'vram bank': { 'size': 0x1000, 'banked': True, },
+ 'wram bank': { 'size': 0x2000, 'banked': True, },
+ }
+
+ bank_aliases = {
+ 'hram': 'hram bank',
+ 'oam': 'oam bank',
+ 'rom0 bank': 'rom bank',
+ 'romx bank': 'rom bank',
+ 'wram0 bank': 'wram bank',
+ 'wramx bank': 'wram bank',
}
# FSM states
@@ -63,8 +71,11 @@ class MapReader:
line = line.split(':', 1)[0]
parts = line.split(' #', 1)
- if (parts[0] in self.bank_types):
- self._cur_bank_name = parts[0]
+ bank_type = parts[0].lower()
+ bank_type = self.bank_aliases.get(bank_type, bank_type)
+
+ if (bank_type in self.bank_types):
+ self._cur_bank_name = bank_type
self._cur_bank_type = self.bank_types[self._cur_bank_name]
if (self._cur_bank_type['banked'] and len(parts) > 1):
parts[1] = parts[1].split(':', 1)[0]