luckytyphlosion

Very hacky job to make gbz80disasm.py to work

Oct 25th, 2015
103
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # coding: utf-8
  2. """
  3. RGBDS BSS section and constant parsing.
  4. """
  5.  
  6. import os
  7.  
  8.  
  9. def separate_comment(line):
  10.     if ';' in line:
  11.         i = line.find(';')
  12.         return line[:i], line[i:]
  13.     return line, None
  14.  
  15.  
  16. def rgbasm_to_py(text):
  17.     return text.replace('$', '0x').replace('%', '0b').replace('@', '0xd8a3')
  18.  
  19. def rgbasm_to_py2(text):
  20.     return text.replace('$', '0x').replace('%', '%').replace('@', '0xd8a3')
  21.    
  22. def make_wram_labels(wram_sections):
  23.     wram_labels = {}
  24.     for section in wram_sections:
  25.         for label in section['labels']:
  26.             if label['address'] not in wram_labels.keys():
  27.                 wram_labels[label['address']] = []
  28.             wram_labels[label['address']] += [label['label']]
  29.     return wram_labels
  30.  
  31. def bracket_value(string, i=0):
  32.     return string.split('[')[1 + i*2].split(']')[0]
  33.  
  34. saved_value = ""
  35. class BSSReader:
  36.     """
  37.     Read rgbasm BSS/WRAM sections and associate labels with addresses.
  38.     Also reads constants/variables, even in macros.
  39.     """
  40.     sections  = []
  41.     section   = None
  42.     address   = None
  43.     macros  = {}
  44.     constants = {}
  45.  
  46.     section_types = {
  47.         'VRAM':  0x8000,
  48.         'SRAM':  0xa000,
  49.         'WRAM0': 0xc000,
  50.         'WRAMX': 0xd000,
  51.         'HRAM':  0xff80,
  52.     }
  53.  
  54.     def __init__(self, *args, **kwargs):
  55.         self.__dict__.update(kwargs)
  56.  
  57.     def read_bss_line(self, l):
  58.         parts = l.strip().split(' ')
  59.         token = parts[0].strip()
  60.         params = ' '.join(parts[1:]).split(',')
  61.         if token in ['ds', 'db', 'dw']:
  62.             if any(params):
  63.                 length = eval(rgbasm_to_py(params[0]), self.constants.copy())
  64.             else:
  65.                 length = {'ds': 1, 'db': 1, 'dw': 2}[token]
  66.             self.address += length
  67.             # assume adjacent labels to use the same space
  68.             for label in self.section['labels'][::-1]:
  69.                 if label['length'] == 0:
  70.                     label['length'] = length
  71.                 else:
  72.                     break
  73.  
  74.         elif token in self.macros.keys():
  75.             macro_text = '\n'.join(self.macros[token]) + '\n'
  76.             for i, p in enumerate(params):
  77.                 macro_text = macro_text.replace('\\'+str(i+1),p)
  78.             macro_text = macro_text.split('\n')
  79.             macro_reader = BSSReader(
  80.                 sections  = list(self.sections),
  81.                 section   = dict(self.section),
  82.                 address   = self.address,
  83.                 constants = self.constants,
  84.             )
  85.             macro_sections = macro_reader.read_bss_sections(macro_text)
  86.             self.section = macro_sections[-1]
  87.             if self.section['labels']:
  88.                 self.address = self.section['labels'][-1]['address'] + self.section['labels'][-1]['length']
  89.  
  90.  
  91.     def read_bss_sections(self, bss):
  92.  
  93.         if self.section is None:
  94.             self.section = {
  95.                 "labels": [],
  96.             }
  97.  
  98.         if type(bss) is str:
  99.             bss = bss.split('\n')
  100.  
  101.         macro = False
  102.         macro_name = None
  103.         for line in bss:
  104.             line = line.lstrip()
  105.             line, comment = separate_comment(line)
  106.             line = line.strip()
  107.             split_line = line.split()
  108.             split_line_upper = map(str.upper, split_line)
  109.  
  110.             if not line:
  111.                 pass
  112.  
  113.             elif line[-4:].upper() == 'ENDM':
  114.                 macro = False
  115.                 macro_name = None
  116.  
  117.             elif macro:
  118.                 self.macros[macro_name] += [line]
  119.  
  120.             elif line[-5:].upper() == 'MACRO':
  121.                 macro_name = line.split(':')[0]
  122.                 macro = True
  123.                 self.macros[macro_name] = []
  124.  
  125.             elif 'INCLUDE' == line[:7].upper():
  126.                 filename = line.split('"')[1]
  127.                 self.read_bss_sections(open(filename).readlines())
  128.  
  129.             elif 'SECTION' == line[:7].upper():
  130.                 if self.section: # previous
  131.                     self.sections += [self.section]
  132.  
  133.                 section_def = line.split(',')
  134.                 name  = section_def[0].split('"')[1]
  135.                 type_ = section_def[1].strip()
  136.                 if len(section_def) > 2:
  137.                     bank = bracket_value(section_def[2])
  138.                 else:
  139.                     bank = None
  140.  
  141.                 if '[' in type_:
  142.                     self.address = int(rgbasm_to_py(bracket_value(type_)), 16)
  143.                 else:
  144.                     if self.address == None or bank != self.section['bank'] or self.section['type'] != type_:
  145.                         self.address = self.section_types.get(type_, self.address)
  146.                     # else: keep going from this address
  147.  
  148.                 self.section = {
  149.                     'name': name,
  150.                     'type': type_,
  151.                     'bank': bank,
  152.                     'start': self.address,
  153.                     'labels': [],
  154.                 }
  155.  
  156.             elif ':' in line:
  157.                 # rgbasm allows labels without :, but prefer convention
  158.                 label = line[:line.find(':')]
  159.                 if '\\' in label:
  160.                     raise Exception, line + ' ' + label
  161.                 if ';' not in label:
  162.                     section_label = {
  163.                         'label': label,
  164.                         'address': self.address,
  165.                         'length': 0,
  166.                     }
  167.                     self.section['labels'] += [section_label]
  168.                     self.read_bss_line(line.split(':')[-1])
  169.             elif any(x in split_line_upper for x in ['EQU', '=', 'SET']): # TODO: refactor
  170.                 for x in ['EQU', '=', 'SET']:
  171.                     if x in split_line_upper:
  172.                         index = split_line_upper.index(x)
  173.                         real = split_line[index]
  174.                         name, value = map(' '.join, [split_line[:index], split_line[index+1:]])
  175.                         saved_value = value
  176.                         try:
  177.                             value = rgbasm_to_py(value)
  178.                             self.constants[name] = eval(value, self.constants.copy())
  179.                         except SyntaxError:
  180.                             value = saved_value
  181.                             value = rgbasm_to_py2(value)
  182.                             self.constants[name] = eval(value, self.constants.copy())
  183.             else:
  184.                 try:
  185.                     self.read_bss_line(line)
  186.                 except NameError:
  187.                     pass
  188.         self.sections += [self.section]
  189.         return self.sections
  190.  
  191. def read_bss_sections(bss):
  192.     reader = BSSReader()
  193.     return reader.read_bss_sections(bss)
  194.  
  195.  
  196. def constants_to_dict(constants):
  197.     """Deprecated. Use BSSReader."""
  198.     return dict((eval(rgbasm_to_py(constant[constant.find('EQU')+3:constant.find(';')])), constant[:constant.find('EQU')].strip()) for constant in constants)
  199.  
  200. def scrape_constants(text):
  201.     if type(text) is not list:
  202.         text = text.split('\n')
  203.     bss = BSSReader()
  204.     bss.read_bss_sections(text)
  205.     constants = bss.constants
  206.     return {v: k for k, v in constants.items()}
  207.  
  208. def read_constants(filepath):
  209.     """
  210.     Load lines from a file and grab any constants using BSSReader.
  211.     """
  212.     lines = []
  213.     if os.path.exists(filepath):
  214.         with open(filepath, "r") as file_handler:
  215.             lines = file_handler.readlines()
  216.     return scrape_constants(lines)
  217.  
  218. class WRAMProcessor(object):
  219.     """
  220.     RGBDS BSS section and constant parsing.
  221.     """
  222.  
  223.     def __init__(self, config):
  224.         """
  225.         Setup for WRAM parsing.
  226.         """
  227.         self.config = config
  228.  
  229.         self.paths = {}
  230.  
  231.         if hasattr(self.config, "wram"):
  232.             self.paths["wram"] = self.config.wram
  233.         else:
  234.             self.paths["wram"] = os.path.join(self.config.path, "wram.asm")
  235.  
  236.         if hasattr(self.config, "hram"):
  237.             self.paths["hram"] = self.config.hram
  238.         else:
  239.             self.paths["hram"] = os.path.join(self.config.path, "hram.asm")
  240.  
  241.         if hasattr(self.config, "gbhw"):
  242.             self.paths["gbhw"] = self.config.gbhw
  243.         else:
  244.             self.paths["gbhw"] = os.path.join(self.config.path, "gbhw.asm")
  245.  
  246.     def initialize(self):
  247.         """
  248.         Read constants.
  249.         """
  250.         self.setup_wram_sections()
  251.         self.setup_wram_labels()
  252.         self.setup_hram_constants()
  253.         self.setup_gbhw_constants()
  254.  
  255.         self.reformat_wram_labels()
  256.  
  257.     def read_wram_sections(self):
  258.         """
  259.         Opens the wram file and calls read_bss_sections.
  260.         """
  261.         wram_content = None
  262.         wram_file_path = self.paths["wram"]
  263.  
  264.         with open(wram_file_path, "r") as wram:
  265.             wram_content = wram.readlines()
  266.  
  267.         wram_sections = read_bss_sections(wram_content)
  268.         return wram_sections
  269.  
  270.     def setup_wram_sections(self):
  271.         """
  272.         Call read_wram_sections and set a variable.
  273.         """
  274.         self.wram_sections = self.read_wram_sections()
  275.         return self.wram_sections
  276.  
  277.     def setup_wram_labels(self):
  278.         """
  279.         Make wram labels based on self.wram_sections as input.
  280.         """
  281.         self.wram_labels = make_wram_labels(self.wram_sections)
  282.         return self.wram_labels
  283.  
  284.     def read_hram_constants(self):
  285.         """
  286.         Read constants from hram.asm using read_constants.
  287.         """
  288.         hram_constants = read_constants(self.paths["hram"])
  289.         return hram_constants
  290.  
  291.     def setup_hram_constants(self):
  292.         """
  293.         Call read_hram_constants and set a variable.
  294.         """
  295.         self.hram_constants = self.read_hram_constants()
  296.         return self.hram_constants
  297.  
  298.     def read_gbhw_constants(self):
  299.         """
  300.         Read constants from gbhw.asm using read_constants.
  301.         """
  302.         gbhw_constants = read_constants(self.paths["gbhw"])
  303.         return gbhw_constants
  304.  
  305.     def setup_gbhw_constants(self):
  306.         """
  307.         Call read_gbhw_constants and set a variable.
  308.         """
  309.         self.gbhw_constants = self.read_gbhw_constants()
  310.         return self.gbhw_constants
  311.  
  312.     def reformat_wram_labels(self):
  313.         """
  314.         Flips the wram_labels dictionary the other way around to access
  315.         addresses by label.
  316.         """
  317.         self.wram = {}
  318.  
  319.         for (address, labels) in self.wram_labels.iteritems():
  320.             for label in labels:
  321.                 self.wram[label] = address
RAW Paste Data