Skip to content

Commit

Permalink
Add free space utility from Crystal, build red-speedchoice rom
Browse files Browse the repository at this point in the history
  • Loading branch information
Dabomstew committed Jun 8, 2020
1 parent f435a80 commit cb93c35
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ pokered.ini

# rom patches
*.ips

# map file for red-speedchoice specifically, other map files can be data files
red-speedchoice.map
6 changes: 3 additions & 3 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
git clone https://github.com/Dabomstew/pokered-speedchoice
cd pokered-speedchoice

To build **pokered.gbc**:
To build **red-speedchoice.gbc**:

make red
make
Expand All @@ -31,7 +31,7 @@ Then in **Terminal**, run:
git clone https://github.com/Dabomstew/pokered-speedchoice
cd pokered-speedchoice

To build **pokered.gbc**:
To build **red-speedchoice.gbc**:

make red
make
Expand All @@ -56,7 +56,7 @@ In the **Cygwin terminal**, enter these commands:
git clone https://github.com/Dabomstew/pokered-speedchoice
cd pokered-speedchoice

To build **pokered.gbc**:
To build **red-speedchoice.gbc**:

make red
make
Expand Down
18 changes: 9 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
roms := pokered.gbc
roms := red-speedchoice.gbc

pokered_obj := audio_red.o main_red.o pics_red.o text_red.o wram_red.o
red-speedchoice_obj := audio_red.o main_red.o pics_red.o text_red.o wram_red.o


### Build tools
Expand All @@ -24,21 +24,21 @@ RGBLINK ?= $(RGBDS)rgblink
.PHONY: all red clean tidy compare tools config

all: $(roms) config
red: pokered.gbc
red: red-speedchoice.gbc

config: pokered.ini
config: red-speedchoice.ini

# For contributors to make sure a change didn't affect the contents of the rom.
compare: $(roms)
@$(MD5) roms.md5

clean:
rm -f $(roms) $(pokered_obj) $(roms:.gbc=.sym)
rm -f $(roms) $(red-speedchoice_obj) $(roms:.gbc=.sym)
find . \( -iname '*.1bpp' -o -iname '*.2bpp' -o -iname '*.pic' -o -iname '*.lz' \) -exec rm {} +
$(MAKE) clean -C tools/

tidy:
rm -f $(roms) $(pokered_obj) $(roms:.gbc=.sym)
rm -f $(roms) $(red-speedchoice_obj) $(roms:.gbc=.sym)
$(MAKE) clean -C tools/

tools:
Expand All @@ -57,13 +57,13 @@ $(shell echo "db \"-"$(shell git log -1 --format="%h")"\"" > git-revision.asm)
%.asm: ;

%_red.o: dep = $(shell tools/scan_includes $(@D)/$*.asm)
$(pokered_obj): %_red.o: %.asm $$(dep)
$(red-speedchoice_obj): %_red.o: %.asm $$(dep)
$(RGBASM) -D _RED -h -o $@ $*.asm

pokered_opt = -cjsv -k 01 -l 0x33 -m 0x13 -p 0 -r 03 -t "RED_SPDC" -i KAPC
red-speedchoice_opt = -cjsv -k 01 -l 0x33 -m 0x13 -p 0 -r 03 -t "RED_SPDC" -i KAPC

%.gbc: $$(%_obj)
$(RGBLINK) -n $*.sym -l pokered.link -o $@ $^
$(RGBLINK) -n $*.sym -l red-speedchoice.link -m red-speedchoice.map -o $@ $^
$(RGBFIX) $($*_opt) $@
sort $*.sym -o $*.sym

Expand Down
File renamed without changes.
73 changes: 73 additions & 0 deletions tools/free_space.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Usage: python3 free_space.py [BANK=none] [red-speedchoice.map]
Calculate the free space in the ROM or its individual banks.
The BANK argument allows printing free space in one, all, or none of the ROM's banks.
Valid arguments are numbers (in decimal "42" or hexadecimal "0x2A"), "all" or "none".
If not specified, defaults to "none".
"""

import sys

from mapreader import MapReader

def main():
print_bank = 'none'
mapfile = 'red-speedchoice.map'

if len(sys.argv) >= 2 and sys.argv[1].startswith('BANK='):
print_bank = sys.argv[1].split('=', 1)[-1]
if len(sys.argv) >= 3:
mapfile = sys.argv[2]
elif len(sys.argv) >= 3 and sys.argv[2].startswith('BANK='):
print_bank = sys.argv[2].split('=', 1)[-1]
mapfile = sys.argv[1]

if print_bank not in {'all', 'none'}:
try:
if print_bank.startswith('0x') or print_bank.startswith('0X'):
print_bank = int(print_bank[2:], 16)
else:
print_bank = int(print_bank)
except ValueError:
error = 'Error: invalid BANK: %s' % print_bank
if print_bank.isalnum():
error += ' (did you mean: 0x%s?)' % print_bank
print(error, file=sys.stderr)
sys.exit(1)

num_banks = 0x40
bank_size = 0x4000 # bytes
total_size = num_banks * bank_size

r = MapReader()
with open(mapfile, 'r', encoding='utf-8') as f:
l = f.readlines()
r.read_map_data(l)

free_space = 0
per_bank = []
default_bank_data = {'sections': [], 'used': 0, 'slack': bank_size}
for bank in range(num_banks):
bank_data = r.bank_data['ROM0 bank'] if bank == 0 else r.bank_data['ROMX bank']
data = bank_data.get(bank, default_bank_data)
used = data['used']
slack = data['slack']
per_bank.append((used, slack))
free_space += slack

print('Free space: %d/%d (%.2f%%)' % (free_space, total_size, free_space * 100.0 / total_size))
if print_bank != 'none':
print()
print('bank, used, free')
for bank in range(num_banks):
used, slack = per_bank[bank]
if print_bank in {'all', bank}:
print('$%02X, %d, %d' % (bank, used, slack))

if __name__ == '__main__':
main()
173 changes: 173 additions & 0 deletions tools/mapreader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-

# A library for parsing the pokecrystal.map file output by rgbds.

import re

class MapReader:

# {'ROM Bank': { 0: { 'sections': [ { 'beg': 1234,
# 'end': 5678,
# 'name': 'Section001',
# 'symbols': [ { 'symbol': 'Function1234',
# 'address: 1234,
# },
# ]
# },
# ],
# 'used': 1234,
# 'slack': 4567,
# },
# },
# 'OAM': { 'sections': [ { 'beg': 1234,
# 'end': 5678,
# 'name': 'Section002',
# 'symbols': [ { 'symbol': 'Data1234',
# 'address: 1234,
# },
# ]
# },
# ],
# 'used': 1234,
# 'slack': 4567,
# },
# }
#
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, },
}

# FSM states
INIT, BANK, SECTION = range(3)

# $506D-$519A ($012E bytes) ["Type Matchups"]
section_header_regex = re.compile('\$([0-9A-Fa-f]{4})-\$([0-9A-Fa-f]{4}) \(.*\) \["(.*)"\]')
# $506D = TypeMatchups
section_data_regex = re.compile('\$([0-9A-Fa-f]{4}) = (.*)')
# $3ED2 bytes
slack_regex = re.compile('\$([0-9A-Fa-f]{4}) bytes?')

def __init__(self, *args, **kwargs):
self.__dict__.update(kwargs)

def _parse_init(self, line):

line = line.split(':', 1)[0]
parts = line.split(' #', 1)

if (parts[0] in self.bank_types):
self._cur_bank_name = parts[0]
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]
parts[1] = parts[1].split(' ', 1)[0]
self._cur_bank = int(parts[1], 10)
if self._cur_bank_name not in self.bank_data:
self.bank_data[self._cur_bank_name] = {}
if self._cur_bank_type['banked']:
if self._cur_bank not in self.bank_data[self._cur_bank_name]:
self.bank_data[self._cur_bank_name][self._cur_bank] = {}
self._cur_data = self.bank_data[self._cur_bank_name][self._cur_bank]
else:
self._cur_data = self.bank_data[self._cur_bank_name]

if ({} == self._cur_data):
self._cur_data['sections'] = []
self._cur_data['used'] = 0
self._cur_data['slack'] = self._cur_bank_type['size']
return True

return False

def _parse_section_header(self, header):

section_data = self.section_header_regex.match(header)
if section_data is not None:
beg = int(section_data.group(1), 16)
end = int(section_data.group(2), 16)
name = section_data.group(3)
self._cur_section = {'beg': beg, 'end': end, 'name': name, 'symbols': []}
self._cur_data['sections'].append(self._cur_section)
return True
return False

def _parse_slack(self, data):

slack_data = self.slack_regex.match(data)
slack_bytes = int(slack_data.group(1), 16)
self._cur_data['slack'] = slack_bytes

used_bytes = 0

for s in self._cur_data['sections']:
used_bytes += s['end'] - s['beg'] + 1

self._cur_data['used'] = used_bytes

def read_map_data(self, map):

if type(map) is str:
map = map.split('\n')

self._state = MapReader.INIT
self._cur_bank_name = ''
self._cur_bank_type = {}
self._cur_bank = 0
self._cur_data = {}

for line in map:

line = line.rstrip()
if (MapReader.INIT == self._state):

if (self._parse_init(line)):
self._state = MapReader.BANK

elif (MapReader.BANK == self._state or MapReader.SECTION == self._state):

if ('' == line):
self._state = MapReader.INIT
else:

line = line.lstrip()
parts = line.split(': ', 1)

if (MapReader.SECTION == self._state):
section_data = self.section_data_regex.match(parts[0])
if section_data is not None:
address = int(section_data.group(1), 16)
name = section_data.group(2)
self._cur_section['symbols'].append({'name': name, 'address': address})
continue

if ('SECTION' == parts[0]):
if (self._parse_section_header(parts[1])):
self._state = MapReader.SECTION
elif ('SLACK' == parts[0]):
self._parse_slack(parts[1])
self._state = MapReader.INIT
elif ('EMPTY' == parts[0]):
self._cur_data = {'sections': [], 'used': 0, 'slack': self._cur_bank_type['size']}
self._state = MapReader.INIT

else:
pass

for k, v in self.bank_data.items():
if (self.bank_types[k]['banked']):
for _, vv in v.items():
vv['sections'].sort(key=lambda x: x['beg'])
for vvv in vv['sections']:
vvv['symbols'].sort(key=lambda x: x['address'])
else:
v['sections'].sort(key=lambda x: x['beg'])
for vv in v['sections']:
vv['symbols'].sort(key=lambda x: x['address'])

0 comments on commit cb93c35

Please sign in to comment.