# HG changeset patch # User Windel Bouwman # Date 1374698429 -7200 # Node ID 66912720d7124478914ab42a827280a26e127fb5 # Parent 58155c7c4a8e2583ada0bf4c1d6bd7c4c7185cca Added grinder diff -r 58155c7c4a8e -r 66912720d712 python/grind.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/grind.py Wed Jul 24 22:40:29 2013 +0200 @@ -0,0 +1,11 @@ + +import cProfile +import unittest +import pstats + +if __name__ == '__main__': + suite = unittest.TestLoader().discover('.') + def runtests(): + unittest.TextTestRunner().run(suite) + s = cProfile.run('runtests()',sort='cumtime') + diff -r 58155c7c4a8e -r 66912720d712 python/hexfile.py --- a/python/hexfile.py Wed Jul 24 19:47:13 2013 +0200 +++ b/python/hexfile.py Wed Jul 24 22:40:29 2013 +0200 @@ -1,5 +1,10 @@ import os import struct +import binascii + +DATA = 0 +EOF = 1 +EXTLINADR = 4 class HexFileException(Exception): pass @@ -29,7 +34,7 @@ crc = sum(nums) crc = ((~crc) + 1) & 0xFF nums.append(crc) - line = ':' + ''.join(['{:02X}'.format(b) for b in nums]) + line = ':' + binascii.hexlify(nums).decode('ascii') return line def chunks(data, csize=16): @@ -39,6 +44,16 @@ yield data[idx:idx+s] idx += s +def hexfields(f): + for line in f: + line = line.strip() # Strip spaces and newlines + if not line: + continue # Skip empty lines + if line[0] != ':': + continue # Skip lines that do not start with a ':' + yield parseHexLine(line) + + class HexFile: """ Represents an intel hexfile """ def __init__(self): @@ -46,43 +61,23 @@ self.startAddress = 0 def load(self, f): - endOfFile = False - offset = 0 - startAddress = 0 - curAddress = 0 - curData = bytearray() - for line in f: - line = line.strip() # Strip spaces and newlines - if not line: continue # Skip empty lines - if line[0] != ':': continue # Skip lines that do not start with a ':' - if endOfFile: raise HexFileException('hexfile line after end of file record') - address, typ, data = parseHexLine(line) + endOfFile = False + ext = 0 + for address, typ, data in hexfields(f): + if endOfFile: + raise HexFileException('hexfile line after end of file record') if typ == 0x0: # Data record - address += offset # Fix address with offset - # Append data - if address == curAddress: - curData += data - curAddress += len(data) - else: - if curData: - self.regions.append(HexFileRegion(startAddress, bytes(curData))) - startAddress = address - curAddress = address + len(data) - curData = bytearray(data) - elif typ == 0x4: # Extended linear address record - offset = ((data[0] << 8) + data[1]) << 16 - elif typ == 0x1: # End of file record - if len(data) != 0: - raise HexFileException('end of file record must contain no data') - endOfFile = True + self.addRegion(address + ext, data) + elif typ == EXTLINADR: # Extended linear address record + ext = (struct.unpack('>H', data[0:2])[0]) << 16 + elif typ == EOF: # End of file record + if len(data) != 0: + raise HexFileException('end of file not empty') + endOfFile = True elif typ == 0x5: # Start address record (where IP goes after loading) - self.startAddress = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3] + self.startAddress = struct.unpack('>I', data[0:4])[0] else: - raise HexFileException('record type {0} not implemented'.format(typ)) - print(hex(address), typ, data) - # After all lines: - if curData: - self.regions.append(HexFileRegion(startAddress, bytes(curData))) + raise HexFileException('record type {0} not implemented'.format(typ)) def __repr__(self): return 'Hexfile with {} regions'.format(len(self.regions)) @@ -101,17 +96,40 @@ def addRegion(self, address, data): r = HexFileRegion(address, data) - # TODO: check touches self.regions.append(r) + self.check() + + def check(self): self.regions.sort(key=lambda r: r.address) + if len(self.regions) > 1: + for r1, r2 in zip(self.regions[:-1], self.regions[1:]): + if r1.EndAddress > r2.address: + raise HexFileException('Overlapping regions') + change = True + while change and len(self.regions) > 1: + change = False + for r1, r2 in zip(self.regions[:-1], self.regions[1:]): + if r1.EndAddress == r2.address: + r1.addData(r2.data) + self.regions.remove(r2) + change = True + def save(self, f): + def emit(address, typ, data=bytes()): + print(makeHexLine(address, typ, data), file=f) for r in self.regions: - offset = 0 + ext = r.address & 0xFFFF0000 + emit(0, EXTLINADR, struct.pack('>H', ext >> 16)) + address = r.address - ext for chunk in chunks(r.data): - print(makeHexLine(r.address + offset, 0x0, bytes(r.data[offset:offset+16])), file=f) - offset += len(chunk) - print(makeHexLine(0, 0x1, bytes()), file=f) + if address >= 0x10000: + ext += 0x10000 + emit(0, EXTLINADR, struct.pack('>H', ext >> 16)) + address -= 0x10000 + emit(address, DATA, chunk) + address += len(chunk) + emit(0, EOF) class HexFileRegion: def __init__(self, address, data = bytes()): @@ -119,22 +137,16 @@ self.data = data def __repr__(self): - return 'Region at 0x{0:X} of {1} bytes'.format(self.address, len(self.data)) + return 'Region at 0x{:08X} of {} bytes'.format(self.address, len(self.data)) def __eq__(self, other): return (self.address, self.data) == (other.address, other.data) + def addData(self, d): + self.data = self.data + d + @property def EndAddress(self): return self.address + len(self.data) -if __name__ == '__main__': - h = HexFile() - print(h) - """ Test hexfile implementation with some hexfile """ - h1 = HexFile() - with open('audio.hex', 'r') as f: - h1.load(f) - print(h1) - diff -r 58155c7c4a8e -r 66912720d712 python/testhexfile.py --- a/python/testhexfile.py Wed Jul 24 19:47:13 2013 +0200 +++ b/python/testhexfile.py Wed Jul 24 22:40:29 2013 +0200 @@ -10,12 +10,46 @@ hf2 = HexFile() hf2.load(io.StringIO(f.getvalue())) self.assertEqual(hf, hf2) - - def testSave(self): + + def testSave1(self): hf = HexFile() hf.addRegion(0x8000, bytes.fromhex('aabbcc')) self.saveload(hf) + def testSave2(self): + hf = HexFile() + hf.addRegion(0x8000, bytes.fromhex('aabbcc')) + hf.addRegion(0x118000, bytes.fromhex('aabbcc')) + self.saveload(hf) + + def testSave3(self): + hf = HexFile() + hf.addRegion(0x8000, bytes.fromhex('aabbcc')) + hf.addRegion(0xFFFE, bytes.fromhex('aabbcc')) + self.saveload(hf) + + def testSave4(self): + hf = HexFile() + hf.addRegion(0xF000, bytes.fromhex('ab')*0x20000) + self.saveload(hf) + + def testSave5(self): + hf = HexFile() + hf.addRegion(0xF003, bytes.fromhex('ab')*0x20000) + self.saveload(hf) + + def testMerge(self): + hf = HexFile() + hf.addRegion(0x10, bytes.fromhex('abcdab')) + hf.addRegion(0x13, bytes.fromhex('abcdab')) + self.assertEqual(1, len(hf.regions)) + + def testOverlapped(self): + hf = HexFile() + hf.addRegion(0x10, bytes.fromhex('abcdab')) + with self.assertRaisesRegex(HexFileException, 'verlap'): + hf.addRegion(0x12, bytes.fromhex('abcdab')) + def testEqual(self): hf1 = HexFile() hf2 = HexFile() @@ -29,7 +63,7 @@ hf1.addRegion(10, bytes.fromhex('aabbcc')) hf2.addRegion(10, bytes.fromhex('aabbdc')) self.assertNotEqual(hf1, hf2) - + def testNotEqual2(self): hf1 = HexFile() hf2 = HexFile()