Fix off-by-one errors with 'make coverage' and reformat output (horizontal banks)

This commit is contained in:
Rangi 2020-09-12 21:42:28 -04:00
parent 462b7521b2
commit 371ce4da5c
4 changed files with 94 additions and 115 deletions

View File

@ -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

70
utils/coverage.py Executable file
View File

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

View File

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

View File

@ -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]