mirror of
https://github.com/mnaberez/py65.git
synced 2025-01-04 16:30:42 +00:00
Added hexdump loader.
This commit is contained in:
parent
ddcffcdb08
commit
16db578570
134
src/py65/tests/utils/test_hexdump.py
Normal file
134
src/py65/tests/utils/test_hexdump.py
Normal file
@ -0,0 +1,134 @@
|
||||
import unittest
|
||||
import sys
|
||||
from py65.utils.hexdump import load, Loader
|
||||
|
||||
class TopLevelHexdumpTests(unittest.TestCase):
|
||||
def test_load(self):
|
||||
text = 'c000: aa bb'
|
||||
start, data = load(text)
|
||||
self.assertEqual(0xC000, start)
|
||||
self.assertEqual([0xAA, 0xBB], data)
|
||||
|
||||
class HexdumpLoaderTests(unittest.TestCase):
|
||||
def test_empty_string_does_nothing(self):
|
||||
text = ''
|
||||
loader = Loader(text)
|
||||
self.assertEqual(None, loader.start_address)
|
||||
self.assertEqual([], loader.data)
|
||||
|
||||
def test_all_whitespace_does_nothing(self):
|
||||
text = " \r\n \t \n"
|
||||
loader = Loader(text)
|
||||
self.assertEqual(None, loader.start_address)
|
||||
self.assertEqual([], loader.data)
|
||||
|
||||
def test_raises_when_start_address_not_found(self):
|
||||
text = 'aa bb cc'
|
||||
try:
|
||||
Loader(text)
|
||||
self.fail()
|
||||
except ValueError, e:
|
||||
msg = 'Start address was not found in data'
|
||||
self.assert_(e.message.startswith('Start address'))
|
||||
|
||||
def test_raises_when_start_address_is_invalid(self):
|
||||
text = 'oops: aa bb cc'
|
||||
try:
|
||||
Loader(text)
|
||||
self.fail()
|
||||
except ValueError, e:
|
||||
msg = 'Could not parse address: oops'
|
||||
self.assertEqual(msg, e.message)
|
||||
|
||||
def test_raises_when_start_address_is_too_short(self):
|
||||
text = '01: aa bb cc'
|
||||
try:
|
||||
Loader(text)
|
||||
self.fail()
|
||||
except ValueError, e:
|
||||
msg = 'Expected address to be 2 bytes, got 1'
|
||||
self.assertEqual(msg, e.message)
|
||||
|
||||
def test_raises_when_start_address_is_too_long(self):
|
||||
text = '010304: aa bb cc'
|
||||
try:
|
||||
Loader(text)
|
||||
self.fail()
|
||||
except ValueError, e:
|
||||
msg = 'Expected address to be 2 bytes, got 3'
|
||||
self.assertEqual(msg, e.message)
|
||||
|
||||
def test_raises_when_next_address_is_unexpected(self):
|
||||
text = "c000: aa\nc002: cc"
|
||||
try:
|
||||
Loader(text)
|
||||
self.fail()
|
||||
except ValueError, e:
|
||||
msg = 'Non-contigous block detected. Expected next ' \
|
||||
'address to be $c001, label was $c002'
|
||||
self.assertEqual(msg, e.message)
|
||||
|
||||
def test_raises_when_data_is_invalid(self):
|
||||
text = 'c000: foo'
|
||||
try:
|
||||
Loader(text)
|
||||
self.fail()
|
||||
except ValueError, e:
|
||||
msg = 'Could not parse data: foo'
|
||||
self.assertEqual(msg, e.message)
|
||||
|
||||
def test_loads_data_without_dollar_signs(self):
|
||||
text = 'c000: aa bb'
|
||||
load = Loader(text)
|
||||
self.assertEqual(0xC000, load.start_address)
|
||||
self.assertEqual([0xAA, 0xBB], load.data)
|
||||
|
||||
def test_loads_data_with_some_dollar_signs(self):
|
||||
text = '$c000: aa $bb'
|
||||
load = Loader(text)
|
||||
self.assertEqual(0xC000, load.start_address)
|
||||
self.assertEqual([0xAA, 0xBB], load.data)
|
||||
|
||||
def test_loads_multiline_data_with_unix_endings(self):
|
||||
text = '$c000: aa bb\n$c002: cc'
|
||||
load = Loader(text)
|
||||
self.assertEqual(0xC000, load.start_address)
|
||||
self.assertEqual([0xAA, 0xBB, 0xCC], load.data)
|
||||
|
||||
def test_loads_multiline_data_with_dos_endings(self):
|
||||
text = '$c000: aa bb\r\n$c002: cc'
|
||||
load = Loader(text)
|
||||
self.assertEqual(0xC000, load.start_address)
|
||||
self.assertEqual([0xAA, 0xBB, 0xCC], load.data)
|
||||
|
||||
def test_ignores_semicolon_comments(self):
|
||||
text = 'c000: aa bb ;comment'
|
||||
load = Loader(text)
|
||||
self.assertEqual(0xC000, load.start_address)
|
||||
self.assertEqual([0xAA, 0xBB], load.data)
|
||||
|
||||
def test_ignores_double_dash_comments(self):
|
||||
text = 'c000: aa bb -- comment'
|
||||
load = Loader(text)
|
||||
self.assertEqual(0xC000, load.start_address)
|
||||
self.assertEqual([0xAA, 0xBB], load.data)
|
||||
|
||||
def test_ignores_pound_comments(self):
|
||||
text = 'c000: aa bb # comment'
|
||||
load = Loader(text)
|
||||
self.assertEqual(0xC000, load.start_address)
|
||||
self.assertEqual([0xAA, 0xBB], load.data)
|
||||
|
||||
def test_ignores_pound_comments(self):
|
||||
text = 'c000: aa bb # comment'
|
||||
load = Loader(text)
|
||||
self.assertEqual(0xC000, load.start_address)
|
||||
self.assertEqual([0xAA, 0xBB], load.data)
|
||||
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.findTestCases(sys.modules[__name__])
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='test_suite')
|
80
src/py65/utils/hexdump.py
Normal file
80
src/py65/utils/hexdump.py
Normal file
@ -0,0 +1,80 @@
|
||||
from binascii import a2b_hex
|
||||
|
||||
def load(text):
|
||||
load = Loader(text)
|
||||
return (load.start_address, load.data)
|
||||
|
||||
class Loader:
|
||||
def __init__(self, text):
|
||||
self.load(text)
|
||||
|
||||
def load(self, text):
|
||||
self._reset()
|
||||
|
||||
for line in text.splitlines():
|
||||
self._parse_line(line)
|
||||
|
||||
def _reset(self):
|
||||
self.data = []
|
||||
self.start_address = None
|
||||
self.current_address = None
|
||||
|
||||
def _parse_line(self, line):
|
||||
line = self._remove_comments(line)
|
||||
pieces = line.strip().split()
|
||||
|
||||
for piece in pieces:
|
||||
if piece.startswith('$'):
|
||||
piece = piece[1:]
|
||||
|
||||
if piece.endswith(':'):
|
||||
self._parse_address(piece[:-1])
|
||||
|
||||
else:
|
||||
self._parse_bytes(piece)
|
||||
|
||||
def _remove_comments(self, line):
|
||||
for delimiter in (';', '--', '#'):
|
||||
pos = line.find(delimiter)
|
||||
if pos != -1:
|
||||
line = line[:pos]
|
||||
return line
|
||||
|
||||
def _parse_address(self, piece):
|
||||
try:
|
||||
addr_bytes = [ ord(c) for c in a2b_hex(piece) ]
|
||||
except (TypeError, ValueError):
|
||||
msg = "Could not parse address: %s" % piece
|
||||
raise ValueError, msg
|
||||
|
||||
if len(addr_bytes) != 2:
|
||||
msg = "Expected address to be 2 bytes, got %d" % (
|
||||
len(addr_bytes))
|
||||
raise ValueError, msg
|
||||
|
||||
address = (addr_bytes[0] << 8) + addr_bytes[1]
|
||||
|
||||
if self.start_address is None:
|
||||
self.start_address = address
|
||||
self.current_address = address
|
||||
|
||||
elif address != (self.current_address):
|
||||
msg = "Non-contigous block detected. Expected next address " \
|
||||
"to be $%04x, label was $%04x" % (self.current_address,
|
||||
address)
|
||||
raise ValueError, msg
|
||||
|
||||
def _parse_bytes(self, piece):
|
||||
if self.start_address is None:
|
||||
msg = "Start address was not found in data"
|
||||
raise ValueError, msg
|
||||
|
||||
else:
|
||||
try:
|
||||
bytes = [ ord(c) for c in a2b_hex(piece) ]
|
||||
except (TypeError, ValueError):
|
||||
msg = "Could not parse data: %s" % piece
|
||||
raise ValueError, msg
|
||||
|
||||
self.current_address += len(bytes)
|
||||
self.data.extend(bytes)
|
Loading…
Reference in New Issue
Block a user