1
0
mirror of https://github.com/mnaberez/py65.git synced 2024-06-01 03:41:31 +00:00
py65/py65/utils/addressing.py
2024-04-12 12:21:46 -07:00

106 lines
3.3 KiB
Python

import re
class AddressParser(object):
"""Parse user input into addresses or ranges of addresses.
"""
def __init__(self, maxwidth=16, radix=16, labels={}):
"""Maxwidth is the maximum width of an address in bits.
Radix is the default radix to use when one is not specified
as a prefix of any input. Labels are a dictionary of label
names that can be substituted for addresses.
"""
self.radix = radix
self.maxwidth = maxwidth
self.labels = {}
for k, v in labels.items():
self.labels[k] = self._constrain(v)
def _get_maxwidth(self):
return self._maxwidth
def _set_maxwidth(self, width):
self._maxwidth = width
self._maxaddr = pow(2, width) - 1
maxwidth = property(_get_maxwidth, _set_maxwidth)
def address_for(self, label, default=None):
"""Given a label, return the corresponding address or a default.
"""
return self.labels.get(label, default)
def label_for(self, address, default=None):
"""Given an address, return the corresponding label or a default.
"""
for label, label_address in self.labels.items():
if label_address == address:
return label
return default
def number(self, num):
"""Parse a string containing a label or number into an address.
"""
try:
if num.startswith('$'):
# hexadecimal
return self._constrain(int(num[1:], 16))
elif num.startswith('+'):
# decimal
return self._constrain(int(num[1:], 10))
elif num.startswith('%'):
# binary
return self._constrain(int(num[1:], 2))
elif num in self.labels:
# label name
return self.labels[num]
else:
matches = re.match(r'^([^\s+-]+)\s*([+\-])\s*([$+%]?\d+)$', num)
if matches:
label, sign, offset = matches.groups()
if label not in self.labels:
raise KeyError("Label not found: %s" % label)
base = self.labels[label]
offset = self.number(offset)
if sign == '+':
address = base + offset
else:
address = base - offset
return self._constrain(address)
else:
return self._constrain(int(num, self.radix))
except ValueError:
raise KeyError("Label not found: %s" % num)
def range(self, addresses):
"""Parse a string containing an address or a range of addresses
into a tuple of (start address, end address)
"""
matches = re.match(r'^([^:,]+)\s*[:,]+\s*([^:,]+)$', addresses)
if matches:
start, end = map(self.number, matches.groups(0))
else:
start = end = self.number(addresses)
if start > end:
start, end = end, start
return (start, end)
def _constrain(self, address):
'''Raises OverflowError if the address is illegal'''
if address < 0 or address > self._maxaddr:
raise OverflowError(address)
return address