1
0
mirror of https://github.com/mnaberez/py65.git synced 2025-01-04 01:30:18 +00:00

Extracted address parsing from Monitor into util.AddressParser.

This commit is contained in:
Mike Naberezny 2008-09-10 03:54:36 +00:00
parent e008b993e7
commit 94cb64320a
4 changed files with 202 additions and 154 deletions

View File

@ -6,7 +6,7 @@ import re
import shlex
import asyncore
from py65.mpu6502 import MPU
from py65.util import itoa
from py65.util import itoa, AddressParser
from py65.memory import ObservableMemory
class Monitor(cmd.Cmd):
@ -16,8 +16,7 @@ class Monitor(cmd.Cmd):
self._mpu = MPU()
self._install_mpu_observers()
self._update_prompt()
self._radix = 16
self._labels = {}
self._address_parser = AddressParser()
cmd.Cmd.__init__(self, completekey, stdin, stdout)
def onecmd(self, line):
@ -115,7 +114,7 @@ class Monitor(cmd.Cmd):
self._run(stopcodes=returns)
def do_goto(self, args):
self._mpu.pc = self._parsenum(args)
self._mpu.pc = self._address_parser.number(args)
brks = [0x00] # BRK
self._run(stopcodes=brks)
@ -144,13 +143,13 @@ class Monitor(cmd.Cmd):
changed = False
for name, radix in radixes.iteritems():
if name[0].lower() == new:
self._radix = radix
self._address_parser.radix = radix
changed = True
if not changed:
self._output("Illegal radix: %s" % args)
for name, radix in radixes.iteritems():
if self._radix == radix:
if self._address_parser.radix == radix:
self._output("Default radix is %s" % name)
def help_tilde(self):
@ -159,7 +158,7 @@ class Monitor(cmd.Cmd):
def do_tilde(self, args):
try:
num = self._parsenum(args)
num = self._address_parser.number(args)
except ValueError:
self._output("Syntax error: %s" % args)
return
@ -187,7 +186,7 @@ class Monitor(cmd.Cmd):
self._output("Invalid register: %s" % register)
else:
try:
intval = self._parsenum(value) & 0xFFFF
intval = self._address_parser.number(value) & 0xFFFF
if len(register) == 1:
intval &= 0xFF
setattr(self._mpu, register, intval)
@ -226,7 +225,7 @@ class Monitor(cmd.Cmd):
filename = split[0]
if len(split) == 2:
start = self._parsenum(split[1])
start = self._address_parser.number(split[1])
else:
start = self._mpu.pc
@ -253,8 +252,8 @@ class Monitor(cmd.Cmd):
self._output("Syntax error: %s" % args)
return
start, end = self._parserange(split[0])
filler = map(self._parsenum, split[1:])
start, end = self._address_parser.range(split[0])
filler = map(self._address_parser.number, split[1:])
self._fill(start, end, filler)
@ -283,7 +282,7 @@ class Monitor(cmd.Cmd):
self._output("Display the contents of memory.")
def do_mem(self, args):
start, end = self._parserange(args)
start, end = self._address_parser.range(args)
out = itoa(start, 16).zfill(4) + ": "
for byte in self._mpu.memory[start:end+1]:
@ -296,74 +295,26 @@ class Monitor(cmd.Cmd):
self._output("Syntax error: %s" % args)
return
address = self._parsenum(split[0])
address = self._address_parser.number(split[0])
label = split[1]
self._labels[label] = address
self._address_parser.labels[label] = address
def do_show_labels(self, args):
byaddress = zip(self._labels.values(), self._labels.keys())
values = self._address_parser.labels.values()
keys = self._address_parser.labels.keys()
byaddress = zip(values, keys)
byaddress.sort()
for address, label in byaddress:
self._output("%04x: %s" % (address, label))
def do_delete_label(self, args):
try:
del self._labels[args]
del self._address_parser.labels[args]
except KeyError:
pass
def _parsenum(self, num):
if num.startswith('$'):
return int(num[1:], 16)
elif num.startswith('+'):
return int(num[1:], 10)
elif num.startswith('%'):
return int(num[1:], 2)
elif num in self._labels:
return self._labels[num]
else:
matches = re.match('^([^\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._parsenum(offset)
if sign == '+':
address = base + offset
else:
address = base - offset
if address < 0:
address = 0
if address > 0xFFFF:
address = 0xFFFF
return address
else:
try:
return int(num, self._radix)
except ValueError:
raise KeyError("Label not found: %s" % num)
def _parserange(self, addresses):
matches = re.match('^([^:,]+)\s*[:,]+\s*([^:,]+)$', addresses)
if matches:
start, end = map(self._parsenum, matches.groups(0))
else:
start = end = self._parsenum(addresses)
if start > end:
start, end = end, start
return (start, end)
def main(args=None, options=None):

View File

@ -4,83 +4,7 @@ import re
from py65.monitor import Monitor
class MonitorTests(unittest.TestCase):
def test__parsenum_hex_literal(self):
mon = Monitor()
self.assertEqual(49152, mon._parsenum('$c000'))
def test__parsenum_dec_literal(self):
mon = Monitor()
self.assertEqual(49152, mon._parsenum('+49152'))
def test__parsenum_bin_literal(self):
mon = Monitor()
self.assertEqual(129, mon._parsenum('%10000001'))
def test__parsenum_default_radix(self):
mon = Monitor()
mon._radix = 10
self.assertEqual(10, mon._parsenum('10'))
mon._radix = 16
self.assertEqual(16, mon._parsenum('10'))
def test__parsenum_label(self):
mon = Monitor()
mon._labels = {'foo': 0xC000}
self.assertEquals(0xC000, mon._parsenum('foo'))
def test__parsenum_bad_label(self):
mon = Monitor()
try:
mon._parsenum('bad_label')
self.fail()
except KeyError, why:
self.assertEqual('Label not found: bad_label', why[0])
def test__parsenum_label_hex_offset(self):
mon = Monitor()
mon._labels = {'foo': 0xC000}
self.assertEquals(0xC003, mon._parsenum('foo+$3'))
self.assertEquals(0xBFFD, mon._parsenum('foo-$3'))
self.assertEquals(0xC003, mon._parsenum('foo + $3'))
self.assertEquals(0xBFFD, mon._parsenum('foo - $3'))
def test__parsenum_label_dec_offset(self):
mon = Monitor()
mon._labels = {'foo': 0xC000}
self.assertEquals(0xC003, mon._parsenum('foo++3'))
self.assertEquals(0xBFFD, mon._parsenum('foo-+3'))
self.assertEquals(0xC003, mon._parsenum('foo + +3'))
self.assertEquals(0xBFFD, mon._parsenum('foo - +3'))
def test__parsenum_label_bin_offset(self):
mon = Monitor()
mon._labels = {'foo': 0xC000}
self.assertEquals(0xC003, mon._parsenum('foo+%00000011'))
self.assertEquals(0xBFFD, mon._parsenum('foo-%00000011'))
self.assertEquals(0xC003, mon._parsenum('foo + %00000011'))
self.assertEquals(0xBFFD, mon._parsenum('foo - %00000011'))
def test__parsenum_label_offset_default_radix(self):
mon = Monitor()
mon._labels = {'foo': 0xC000}
mon._radix = 16
self.assertEquals(0xC010, mon._parsenum('foo+10'))
self.assertEquals(0xBFF0, mon._parsenum('foo-10'))
self.assertEquals(0xC010, mon._parsenum('foo + 10'))
self.assertEquals(0xBFF0, mon._parsenum('foo - 10'))
mon._radix = 10
self.assertEquals(0xC00A, mon._parsenum('foo+10'))
self.assertEquals(0xBFF6, mon._parsenum('foo-10'))
self.assertEquals(0xC00A, mon._parsenum('foo + 10'))
self.assertEquals(0xBFF6, mon._parsenum('foo - 10'))
def test__parsenum_bad_label_with_offset(self):
mon = Monitor()
try:
mon._parsenum('bad_label+3')
self.fail()
except KeyError, why:
self.assertEqual('Label not found: bad_label', why[0])
pass
def test_suite():
return unittest.findTestCases(sys.modules[__name__])

View File

@ -0,0 +1,99 @@
import unittest
import sys
from py65 import util
class UtilTopLevelTests(unittest.TestCase):
def test_itoa_decimal_output(self):
self.assertEqual('10', util.itoa(10, base=10))
def test_itoa_hex_output(self):
self.assertEqual('a', util.itoa(10, base=16))
def test_itoa_bin_output(self):
self.assertEqual('1010', util.itoa(10, base=2))
class AddressParserTests(unittest.TestCase):
def test_number_hex_literal(self):
parser = util.AddressParser()
self.assertEqual(49152, parser.number('$c000'))
def test_number_dec_literal(self):
parser = util.AddressParser()
self.assertEqual(49152, parser.number('+49152'))
def test_number_bin_literal(self):
parser = util.AddressParser()
self.assertEqual(129, parser.number('%10000001'))
def test_number_default_radix(self):
parser = util.AddressParser()
parser.radix = 10
self.assertEqual(10, parser.number('10'))
parser.radix = 16
self.assertEqual(16, parser.number('10'))
def test_number_label(self):
parser = util.AddressParser()
parser.labels = {'foo': 0xC000}
self.assertEquals(0xC000, parser.number('foo'))
def test_number_bad_label(self):
parser = util.AddressParser()
try:
parser.number('bad_label')
self.fail()
except KeyError, why:
self.assertEqual('Label not found: bad_label', why[0])
def test_number_label_hex_offset(self):
parser = util.AddressParser()
parser.labels = {'foo': 0xC000}
self.assertEquals(0xC003, parser.number('foo+$3'))
self.assertEquals(0xBFFD, parser.number('foo-$3'))
self.assertEquals(0xC003, parser.number('foo + $3'))
self.assertEquals(0xBFFD, parser.number('foo - $3'))
def test_number_label_dec_offset(self):
parser = util.AddressParser()
parser.labels = {'foo': 0xC000}
self.assertEquals(0xC003, parser.number('foo++3'))
self.assertEquals(0xBFFD, parser.number('foo-+3'))
self.assertEquals(0xC003, parser.number('foo + +3'))
self.assertEquals(0xBFFD, parser.number('foo - +3'))
def test_number_label_bin_offset(self):
parser = util.AddressParser()
parser.labels = {'foo': 0xC000}
self.assertEquals(0xC003, parser.number('foo+%00000011'))
self.assertEquals(0xBFFD, parser.number('foo-%00000011'))
self.assertEquals(0xC003, parser.number('foo + %00000011'))
self.assertEquals(0xBFFD, parser.number('foo - %00000011'))
def test_number_label_offset_default_radix(self):
parser = util.AddressParser()
parser.labels = {'foo': 0xC000}
parser.radix = 16
self.assertEquals(0xC010, parser.number('foo+10'))
self.assertEquals(0xBFF0, parser.number('foo-10'))
self.assertEquals(0xC010, parser.number('foo + 10'))
self.assertEquals(0xBFF0, parser.number('foo - 10'))
parser.radix = 10
self.assertEquals(0xC00A, parser.number('foo+10'))
self.assertEquals(0xBFF6, parser.number('foo-10'))
self.assertEquals(0xC00A, parser.number('foo + 10'))
self.assertEquals(0xBFF6, parser.number('foo - 10'))
def test_number_bad_label_with_offset(self):
parser = util.AddressParser()
try:
parser.number('bad_label+3')
self.fail()
except KeyError, why:
self.assertEqual('Label not found: bad_label', why[0])
def test_suite():
return unittest.findTestCases(sys.modules[__name__])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View File

@ -1,22 +1,96 @@
import re
class AddressParser:
"""Parse user input into addresses or ranges of addresses.
"""
def __init__(self, radix=16, labels={}):
"""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.labels = labels
def number(self, num):
"""Parse a string containing a label or number into an address.
"""
if num.startswith('$'):
return int(num[1:], 16)
elif num.startswith('+'):
return int(num[1:], 10)
elif num.startswith('%'):
return int(num[1:], 2)
elif num in self.labels:
return self.labels[num]
else:
matches = re.match('^([^\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
if address < 0:
address = 0
if address > 0xFFFF:
address = 0xFFFF
return address
else:
try:
return 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('^([^:,]+)\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 itoa(num, base=10):
negative = num < 0
if negative:
""" Convert a decimal number to its equivalent in another base.
This is essentially the inverse of int(num, base).
"""
negative = num < 0
if negative:
num = -num
digits = []
while num > 0:
digits = []
while num > 0:
num, last_digit = divmod(num, base)
digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[last_digit])
if negative:
if negative:
digits.append('-')
digits.reverse()
return ''.join(digits)
digits.reverse()
return ''.join(digits)
def convert_to_bin(bcd):
return bcd2bin[bcd]
return bcd2bin[bcd]
def convert_to_bcd(bin):
return bin2bcd[bin]
return bin2bcd[bin]
bcd2bin = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, # 0x00