diff options
author | PikalaxALT <pikalaxalt@gmail.com> | 2018-06-04 11:27:51 -0400 |
---|---|---|
committer | PikalaxALT <pikalaxalt@gmail.com> | 2018-06-04 11:27:51 -0400 |
commit | cf061ed92956e0e661ec3336e6c999cda1c3f2ce (patch) | |
tree | 73b612ed81712c53e4581292b87861eef7c24340 /tools/map2link.py | |
parent | 500a40705e426fcfc874c0cf3605c192e9862c1c (diff) |
Create linkerscript and add Python 3.6 script for linkerscript generation
Diffstat (limited to 'tools/map2link.py')
-rw-r--r-- | tools/map2link.py | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/tools/map2link.py b/tools/map2link.py new file mode 100644 index 0000000..b24c82f --- /dev/null +++ b/tools/map2link.py @@ -0,0 +1,183 @@ +#!/bin/python3.6 + +import argparse +import re +import sys +from collections import OrderedDict + + +class MapFile: + LINETYPE_BLANK = -1 + LINETYPE_BANK = 0 + LINETYPE_SECTION = 1 + LINETYPE_SYMBOL = 2 + LINETYPE_SLACK = 3 + LINETYPE_EMPTY = 4 + + bank_types = ('ROM', 'VRAM', 'SRAM', 'WRAM', 'OAM', 'HRAM') + bank_starts = (0x4000, 0x8000, 0xa000, 0xd000, 0xfe00, 0xff80) + + class MapFileLine: + def __init__(self, linestr): + self.raw = linestr + + def __str__(self): + return self.raw + + class BlankLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + + class BankLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + match = re.search('#(\d+)', linestr, re.I) + if match is None: + self.bankno = 0 + else: + self.bankno = int(match.group(1)) + try: + self.banktype = MapFile.bank_types.index(linestr.split()[0].rstrip(':')) + except ValueError as e: + raise ValueError(f'Unrecognized bank type: {linestr.split()[0]}') from e + + @property + def name(self): + if self.banktype == 0: # ROM + if self.bankno == 0: + return 'ROM0' + else: + return f'ROMX ${self.bankno:02x}' + elif self.banktype == 3: # WRAM + if self.bankno == 0: + return 'WRAM0' + else: + return f'WRAMX ${self.bankno:02x}' + elif self.banktype < 3: + return f'{MapFile.bank_types[self.banktype]} {self.bankno:d}' + else: + return f'{MapFile.bank_types[self.banktype]}' + + @property + def start(self): + if self.bankno == 0: + if self.banktype == 0: + return 0x0000 + elif self.banktype == 3: + return 0xc000 + return MapFile.bank_starts[self.banktype] + + def __hash__(self): + return hash(self.name) + + class SectionLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + match = re.search(r'\$([0-9A-F]{4}) \(\$([0-9A-F]+) bytes\) \["(.+)"\]', linestr, re.I) + start, size, self.name = match.groups() + self.start = int(start, 16) + self.size = int(size, 16) + self.end = self.start + self.size + self.symbols = [] + + class SymbolLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + + class SlackLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + match = re.search(r'\$([0-9A-F]{4}) bytes', linestr, re.I) + self.slack = int(match.group(1), 16) + + class EmptyLine(MapFileLine): + def __init__(self, linestr): + super().__init__(linestr) + + line_classes = { + LINETYPE_BLANK: BlankLine, + LINETYPE_BANK: BankLine, + LINETYPE_SECTION: SectionLine, + LINETYPE_SYMBOL: SymbolLine, + LINETYPE_SLACK: SlackLine, + LINETYPE_EMPTY: EmptyLine + } + + def __init__(self, fname, *fpargs, **fpkwargs): + self.fname = fname + self.fp = None + self.fpargs = fpargs + self.fpkwargs = fpkwargs + + def open(self): + if self.fp is None or self.fp.closed: + self.fp = open(self.fname, *self.fpargs, **self.fpkwargs) + + def close(self): + if not self.fp.closed: + self.fp.close() + self.fp = None + + def __enter__(self): + self.open() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.fp.__exit__(exc_type, exc_val, exc_tb) + self.fp = None + + def __iter__(self): + if self.fp is None or self.fp.closed: + print('Warning: Cowardly refusing to read closed file', file=sys.stderr) + raise StopIteration + for line in self.fp: + linestr = line.strip('\n') + if linestr == '': + line_type = self.LINETYPE_BLANK + elif 'SECTION:' in linestr: + line_type = self.LINETYPE_SECTION + elif 'SLACK:' in linestr: + line_type = self.LINETYPE_SLACK + elif linestr == ' EMPTY': + line_type = self.LINETYPE_EMPTY + elif ' = ' in linestr: + line_type = self.LINETYPE_SYMBOL + else: + line_type = self.LINETYPE_BANK + yield self.line_classes[line_type](linestr) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('mapfile', type=MapFile) + parser.add_argument('linkfile', type=argparse.FileType('w')) + args = parser.parse_args() + + rom_map = OrderedDict() + cur_bank = None + + print('; Automatically generated by map2link.py', file=args.linkfile) + + with args.mapfile: + for line in args.mapfile: + if isinstance(line, MapFile.BankLine): + cur_bank = line + rom_map[cur_bank] = [] + elif isinstance(line, MapFile.SectionLine): + rom_map[cur_bank].append(line) + + for bank, sections in rom_map.items(): + if len(sections) == 0: + continue + sections.sort(key=lambda s: s.start) + print(bank.name, file=args.linkfile) + start = bank.start + for section in sections: + if section.start > start: + print(f'\torg ${section.start:04x}', file=args.linkfile) + print(f'\t"{section.name}" ; size: ${section.size:04x}', file=args.linkfile) + start = section.end + + +if __name__ == '__main__': + main() |