1
0
mirror of https://github.com/mnaberez/py65.git synced 2025-04-12 00:37:06 +00:00

Reorganized utilities into separate modules.

This commit is contained in:
Mike Naberezny 2009-04-05 12:04:36 -07:00
parent 13f7b783f2
commit 90d189151b
13 changed files with 218 additions and 194 deletions

View File

@ -1,6 +1,6 @@
import re
from py65.disassembler import Disassembler
from py65.util import AddressParser
from py65.utils.addressing import AddressParser
class Assembler:
Statement = re.compile(r'^([A-z]{3}\s+'

View File

@ -9,7 +9,9 @@ import sys
from py65.mpu6502 import MPU
from py65.disassembler import Disassembler
from py65.assembler import Assembler
from py65.util import itoa, AddressParser, getch
from py65.utils.addressing import AddressParser
from py65.utils.console import getch
from py65.utils.conversions import itoa
from py65.memory import ObservableMemory
class Monitor(cmd.Cmd):

View File

@ -1,7 +1,7 @@
import unittest
import sys
from py65.assembler import Assembler
from py65.util import AddressParser
from py65.utils.addressing import AddressParser
class AssemblerTests(unittest.TestCase):
def test_assembles_00(self):

View File

@ -2,7 +2,7 @@ import unittest
import sys
from py65.mpu6502 import MPU
from py65.disassembler import Disassembler
from py65.util import AddressParser
from py65.utils.addressing import AddressParser
class DisassemblerTests(unittest.TestCase):
def test_disassembles_00(self):

View File

@ -0,0 +1 @@
# this is a package

View File

@ -1,44 +1,34 @@
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))
from py65.utils.addressing import AddressParser
class AddressParserTests(unittest.TestCase):
def test_number_hex_literal(self):
parser = util.AddressParser()
parser = AddressParser()
self.assertEqual(49152, parser.number('$c000'))
def test_number_dec_literal(self):
parser = util.AddressParser()
parser = AddressParser()
self.assertEqual(49152, parser.number('+49152'))
def test_number_bin_literal(self):
parser = util.AddressParser()
parser = AddressParser()
self.assertEqual(129, parser.number('%10000001'))
def test_number_default_radix(self):
parser = util.AddressParser()
parser = 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 = AddressParser()
parser.labels = {'foo': 0xC000}
self.assertEquals(0xC000, parser.number('foo'))
def test_number_bad_label(self):
parser = util.AddressParser()
parser = AddressParser()
try:
parser.number('bad_label')
self.fail()
@ -46,7 +36,7 @@ class AddressParserTests(unittest.TestCase):
self.assertEqual('Label not found: bad_label', why[0])
def test_number_label_hex_offset(self):
parser = util.AddressParser()
parser = AddressParser()
parser.labels = {'foo': 0xC000}
self.assertEquals(0xC003, parser.number('foo+$3'))
self.assertEquals(0xBFFD, parser.number('foo-$3'))
@ -54,7 +44,7 @@ class AddressParserTests(unittest.TestCase):
self.assertEquals(0xBFFD, parser.number('foo - $3'))
def test_number_label_dec_offset(self):
parser = util.AddressParser()
parser = AddressParser()
parser.labels = {'foo': 0xC000}
self.assertEquals(0xC003, parser.number('foo++3'))
self.assertEquals(0xBFFD, parser.number('foo-+3'))
@ -62,7 +52,7 @@ class AddressParserTests(unittest.TestCase):
self.assertEquals(0xBFFD, parser.number('foo - +3'))
def test_number_label_bin_offset(self):
parser = util.AddressParser()
parser = AddressParser()
parser.labels = {'foo': 0xC000}
self.assertEquals(0xC003, parser.number('foo+%00000011'))
self.assertEquals(0xBFFD, parser.number('foo-%00000011'))
@ -70,7 +60,7 @@ class AddressParserTests(unittest.TestCase):
self.assertEquals(0xBFFD, parser.number('foo - %00000011'))
def test_number_label_offset_default_radix(self):
parser = util.AddressParser()
parser = AddressParser()
parser.labels = {'foo': 0xC000}
parser.radix = 16
self.assertEquals(0xC010, parser.number('foo+10'))
@ -84,7 +74,7 @@ class AddressParserTests(unittest.TestCase):
self.assertEquals(0xBFF6, parser.number('foo - 10'))
def test_number_bad_label_with_offset(self):
parser = util.AddressParser()
parser = AddressParser()
try:
parser.number('bad_label+3')
self.fail()
@ -92,17 +82,18 @@ class AddressParserTests(unittest.TestCase):
self.assertEqual('Label not found: bad_label', why[0])
def test_label_for_returns_label(self):
parser = util.AddressParser(labels={'chrout':0xFFD2})
parser = AddressParser(labels={'chrout':0xFFD2})
self.assertEqual('chrout', parser.label_for(0xFFD2))
def test_label_for_returns_none_by_default(self):
parser = util.AddressParser(labels={})
parser = AddressParser(labels={})
self.assertEqual(None, parser.label_for(0xFFD2))
def test_label_for_returns_alternate_default(self):
parser = util.AddressParser(labels={})
parser = AddressParser(labels={})
self.assertEqual('foo', parser.label_for(0xFFD2, 'foo'))
def test_suite():
return unittest.findTestCases(sys.modules[__name__])

View File

@ -0,0 +1,12 @@
import unittest
from py65.utils.console import getch
class ConsoleTopLevelTests(unittest.TestCase):
pass
def test_suite():
return unittest.findTestCases(sys.modules[__name__])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View File

@ -0,0 +1,19 @@
import unittest
from py65.utils.conversions import itoa
class ConversionsTopLevelTests(unittest.TestCase):
def test_itoa_decimal_output(self):
self.assertEqual('10', itoa(10, base=10))
def test_itoa_hex_output(self):
self.assertEqual('a', itoa(10, base=16))
def test_itoa_bin_output(self):
self.assertEqual('1010', itoa(10, base=2))
def test_suite():
return unittest.findTestCases(sys.modules[__name__])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View File

@ -1,165 +0,0 @@
import re
import select
import os
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 label_for(self, address, default=None):
"""Given an address, return the corresponding label or a default.
"""
for label, label_address in self.labels.iteritems():
if label_address == address:
return label
return default
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):
""" 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:
num, last_digit = divmod(num, base)
digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[last_digit])
if negative:
digits.append('-')
digits.reverse()
return ''.join(digits)
def convert_to_bin(bcd):
return bcd2bin[bcd]
def convert_to_bcd(bin):
return bin2bcd[bin]
bcd2bin = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, # 0x00
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, # 0x10
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, # 0x20
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, # 0x30
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, # 0x40
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, # 0x50
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, # 0x60
70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, # 0x70
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, # 0x80
90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105, # 0x90
100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115, # 0xA0
110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, # 0xB0
120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135, # 0xC0
130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, # 0xD0
140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, # 0xE0
150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165 # 0xF0
]
bin2bcd = [
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99
]
def getch(stdin):
""" Performs a nonblocking read of one byte from stdin and returns
its ordinal value. If no byte is available, 0 is returned.
"""
import termios
import fcntl
fd = stdin.fileno()
oldterm = termios.tcgetattr(fd)
newattr = oldterm[:]
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
try:
byte = 0
r, w, e = select.select([fd], [], [], 0.1)
if r:
c = stdin.read(1)
byte = ord(c)
if byte == 0x0a:
byte = 0x0d
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
return byte

View File

@ -0,0 +1 @@
# this is a package

View File

@ -0,0 +1,78 @@
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 label_for(self, address, default=None):
"""Given an address, return the corresponding label or a default.
"""
for label, label_address in self.labels.iteritems():
if label_address == address:
return label
return default
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)

32
src/py65/utils/console.py Normal file
View File

@ -0,0 +1,32 @@
import select
import os
def getch(stdin):
""" Performs a nonblocking read of one byte from stdin and returns
its ordinal value. If no byte is available, 0 is returned.
"""
import termios
import fcntl
fd = stdin.fileno()
oldterm = termios.tcgetattr(fd)
newattr = oldterm[:]
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
try:
byte = 0
r, w, e = select.select([fd], [], [], 0.1)
if r:
c = stdin.read(1)
byte = ord(c)
if byte == 0x0a:
byte = 0x0d
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
return byte

View File

@ -0,0 +1,53 @@
def itoa(num, base=10):
""" 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:
num, last_digit = divmod(num, base)
digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[last_digit])
if negative:
digits.append('-')
digits.reverse()
return ''.join(digits)
def convert_to_bin(bcd):
return bcd2bin[bcd]
def convert_to_bcd(bin):
return bin2bcd[bin]
bcd2bin = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, # 0x00
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, # 0x10
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, # 0x20
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, # 0x30
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, # 0x40
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, # 0x50
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, # 0x60
70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, # 0x70
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, # 0x80
90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105, # 0x90
100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115, # 0xA0
110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, # 0xB0
120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135, # 0xC0
130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, # 0xD0
140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, # 0xE0
150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165 # 0xF0
]
bin2bcd = [
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99
]