Skip to content

Commit

Permalink
Fix Intel HEX addressing types.
Browse files Browse the repository at this point in the history
  • Loading branch information
eerimoq committed Oct 1, 2018
1 parent 5803c4a commit 054e112
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 36 deletions.
123 changes: 94 additions & 29 deletions bincopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@

DEFAULT_WORD_SIZE_BITS = 8

# Intel hex types.
IHEX_DATA = 0
IHEX_END_OF_FILE = 1
IHEX_EXTENDED_SEGMENT_ADDRESS = 2
IHEX_START_SEGMENT_ADDRESS = 3
IHEX_EXTENDED_LINEAR_ADDRESS = 4
IHEX_START_LINEAR_ADDRESS = 5


class Error(Exception):
"""Bincopy base exception.
Expand Down Expand Up @@ -690,23 +698,23 @@ def add_ihex(self, records, overwrite=False):
for record in StringIO(records):
type_, address, size, data = unpack_ihex(record.strip())

if type_ == 0:
if type_ == IHEX_DATA:
address = (address
+ extended_segment_address
+ extended_linear_address)
address *= self.word_size_bytes
self._segments.add(_Segment(address, address + size,
bytearray(data)),
overwrite)
elif type_ == 1:
elif type_ == IHEX_END_OF_FILE:
pass
elif type_ == 2:
elif type_ == IHEX_EXTENDED_SEGMENT_ADDRESS:
extended_segment_address = int(binascii.hexlify(data), 16)
extended_segment_address *= 16
elif type_ == 4:
elif type_ == IHEX_EXTENDED_LINEAR_ADDRESS:
extended_linear_address = int(binascii.hexlify(data), 16)
extended_linear_address <<= 16
elif type_ in [3, 5]:
elif type_ in [IHEX_START_SEGMENT_ADDRESS, IHEX_START_LINEAR_ADDRESS]:
self.execution_start_address = int(binascii.hexlify(data), 16)
else:
raise Error("expected type 1..5 in record {}, but got {}".format(
Expand Down Expand Up @@ -828,47 +836,104 @@ def as_ihex(self, number_of_data_bytes=32, address_length_bits=32):
"""

def i32hex(address, extended_linear_address, data_address):
if address > 0xffffffff:
raise Error(
'cannot address more than 4 GB in I32HEX files (32 '
'bits addresses)')

address_upper_16_bits = (address >> 16)
address &= 0xffff

# All segments are sorted by address. Update the
# extended linear address when required.
if address_upper_16_bits > extended_linear_address:
extended_linear_address = address_upper_16_bits
packed = pack_ihex(IHEX_EXTENDED_LINEAR_ADDRESS,
0,
2,
binascii.unhexlify('{:04X}'.format(
extended_linear_address)))
data_address.append(packed)

return address, extended_linear_address

def i16hex(address, extended_segment_address, data_address):
if address > 16 * 0xffff + 0xffff:
raise Error(
'cannot address more than 1 MB in I16HEX files (20 '
'bits addresses)')

address_lower = (address - 16 * extended_segment_address)

# All segments are sorted by address. Update the
# extended segment address when required.
if address_lower > 0xffff:
extended_segment_address = (4096 * (address >> 16))

if extended_segment_address > 0xffff:
extended_segment_address = 0xffff

address_lower = (address - 16 * extended_segment_address)
packed = pack_ihex(IHEX_EXTENDED_SEGMENT_ADDRESS,
0,
2,
binascii.unhexlify('{:04X}'.format(
extended_segment_address)))
data_address.append(packed)

return address_lower, extended_segment_address

def i8hex(address):
if address > 0xffff:
raise Error(
'cannot address more than 64 kB in I8HEX files (16 '
'bits addresses)')

data_address = []
extended_segment_address = 0
extended_linear_address = 0

for address, data in self._segments.chunks(number_of_data_bytes):
address //= self.word_size_bytes
address_upper_16_bits = (address >> 16)
address_lower_16_bits = (address & 0xffff)

if address_length_bits == 32:
# All segments are sorted by address. Update the
# extended linear address when required.
if address_upper_16_bits > extended_linear_address:
extended_linear_address = address_upper_16_bits
packed = pack_ihex(4,
0,
2,
binascii.unhexlify(
'{:04X}'.format(
extended_linear_address)))
data_address.append(packed)
address, extended_linear_address = i32hex(address,
extended_linear_address,
data_address)
elif address_length_bits == 24:
address, extended_segment_address = i16hex(address,
extended_segment_address,
data_address)
elif address_length_bits == 16:
i8hex(address)
else:
raise Error('expected address length 32, but got {}'.format(
address_length_bits))
raise Error(
'expected address length 16, 24 or 32, but got {}'.format(
address_length_bits))

data_address.append(pack_ihex(0,
address_lower_16_bits,
len(data), data))
data_address.append(pack_ihex(IHEX_DATA,
address,
len(data),
data))

footer = []

if self.execution_start_address is not None:
if address_length_bits == 16:
if address_length_bits == 24:
address = binascii.unhexlify(
'{:08X}'.format(self.execution_start_address))
footer.append(pack_ihex(3, 0, 4, address))
footer.append(pack_ihex(IHEX_START_SEGMENT_ADDRESS,
0,
4,
address))
elif address_length_bits == 32:
address = binascii.unhexlify(
'{:08X}'.format(self.execution_start_address))
footer.append(pack_ihex(5, 0, 4, address))
footer.append(pack_ihex(IHEX_START_LINEAR_ADDRESS,
0,
4,
address))

footer.append(pack_ihex(1, 0, 0, None))
footer.append(pack_ihex(IHEX_END_OF_FILE, 0, 0, None))

return '\n'.join(data_address + footer) + '\n'

Expand Down
73 changes: 66 additions & 7 deletions tests/test_bincopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,11 @@ def test_i8hex(self):

binfile.add_ihex(':0100000001FE\n'
':01FFFF0002FF\n'
':0400000300000000F9\n' # Will not be part of
# I8HEX output.
':00000001FF\n')

self.assertEqual(binfile.as_ihex(),
self.assertEqual(binfile.as_ihex(address_length_bits=16),
':0100000001FE\n'
':01FFFF0002FF\n'
':00000001FF\n')
Expand All @@ -148,6 +150,23 @@ def test_i8hex(self):
self.assertEqual(binfile[0], 1)
self.assertEqual(binfile[0xffff], 2)

def test_i8hex_address_above_64k(self):
binfile = bincopy.BinFile()

binfile.add_binary(b'\x00', 65536)

self.assertEqual(binfile.minimum_address, 0x10000)
self.assertEqual(binfile.maximum_address, 0x10001)
self.assertEqual(binfile[0x10000], 0)

with self.assertRaises(bincopy.Error) as cm:
binfile.as_ihex(address_length_bits=16)

self.assertEqual(
str(cm.exception),
'cannot address more than 64 kB in I8HEX files (16 bits '
'addresses)')

def test_i16hex(self):
"""I16HEX files use only record types 00 through 03 (20 bit
addresses).
Expand All @@ -163,15 +182,18 @@ def test_i16hex(self):
':01FFFF0005FC\n'
':020000021000EC\n'
':0100000003FC\n'
':0400000500000000F7\n' # Converted to 03 in
# I16HEX output.
':00000001FF\n')

self.assertEqual(binfile.as_ihex(),
self.assertEqual(binfile.as_ihex(address_length_bits=24),
':0100000001FE\n'
':02FFFF000203FB\n'
':02000004000FEB\n'
':02000002F0000C\n'
':01FFF000040C\n'
':020000040010EA\n'
':01FFEF00050C\n'
':02000002FFFFFE\n'
':01FFFF0005FC\n'
':0400000300000000F9\n'
':00000001FF\n')
self.assertEqual(binfile.minimum_address, 0)
self.assertEqual(binfile.maximum_address, 0x10fff0)
Expand All @@ -181,6 +203,23 @@ def test_i16hex(self):
self.assertEqual(binfile[0xffff0], 4)
self.assertEqual(binfile[0x10ffef], 5)

def test_i16hex_address_above_1meg(self):
binfile = bincopy.BinFile()

binfile.add_binary(b'\x00', 17 * 65535 + 1)

self.assertEqual(binfile.minimum_address, 0x10fff0)
self.assertEqual(binfile.maximum_address, 0x10fff1)
self.assertEqual(binfile[0x10fff0], 0)

with self.assertRaises(bincopy.Error) as cm:
binfile.as_ihex(address_length_bits=24)

self.assertEqual(
str(cm.exception),
'cannot address more than 1 MB in I16HEX files (20 bits '
'addresses)')

def test_i32hex(self):
"""I32HEX files use only record types 00, 01, 04, and 05 (32 bit
addresses).
Expand All @@ -196,6 +235,7 @@ def test_i32hex(self):
':01FFFF0005FC\n'
':020000040001F9\n'
':0100000003FC\n'
':0400000500000000F7\n'
':00000001FF\n')

self.assertEqual(binfile.as_ihex(),
Expand All @@ -204,16 +244,35 @@ def test_i32hex(self):
':02000004FFFFFC\n'
':0100000004FB\n'
':01FFFF0005FC\n'
':0400000500000000F7\n'
':00000001FF\n')
self.assertEqual(binfile.minimum_address, 0)
self.assertEqual(binfile.maximum_address, 0x100000000)
self.assertEqual(binfile.execution_start_address, 0)
self.assertEqual(binfile[0], 1)
self.assertEqual(binfile[0xffff], 2)
self.assertEqual(binfile[0x10000], 3)
self.assertEqual(binfile[0xffff0000], 4)
self.assertEqual(binfile[0xffff0002:0xffff0004], b'\xff\xff')
self.assertEqual(binfile[0xffffffff:0x100000000], b'\x05')

def test_i16hex_address_above_4gig(self):
binfile = bincopy.BinFile()

binfile.add_binary(b'\x00', 0x100000000)

self.assertEqual(binfile.minimum_address, 0x100000000)
self.assertEqual(binfile.maximum_address, 0x100000001)
self.assertEqual(binfile[0x100000000], 0)

with self.assertRaises(bincopy.Error) as cm:
binfile.as_ihex(address_length_bits=32)

self.assertEqual(
str(cm.exception),
'cannot address more than 4 GB in I32HEX files (32 bits '
'addresses)')

def test_binary(self):
# Add data to 0..2.
binfile = bincopy.BinFile()
Expand Down Expand Up @@ -784,10 +843,10 @@ def test_as_ihex_bad_address_length_bits(self):
binfile.add_binary(b'\x00')

with self.assertRaises(bincopy.Error) as cm:
binfile.as_ihex(address_length_bits=24)
binfile.as_ihex(address_length_bits=8)

self.assertEqual(str(cm.exception),
'expected address length 32, but got 24')
'expected address length 16, 24 or 32, but got 8')

def test_as_srec_bad_address_length(self):
binfile = bincopy.BinFile()
Expand Down

0 comments on commit 054e112

Please sign in to comment.