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:
parent
e008b993e7
commit
94cb64320a
@ -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):
|
||||
|
@ -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__])
|
||||
|
99
src/py65/tests/test_util.py
Normal file
99
src/py65/tests/test_util.py
Normal 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')
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user