changeset 245:66912720d712

Added grinder
author Windel Bouwman
date Wed, 24 Jul 2013 22:40:29 +0200
parents 58155c7c4a8e
children f254b87258e6
files python/grind.py python/hexfile.py python/testhexfile.py
diffstat 3 files changed, 110 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- /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')
+
--- 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)
-
--- 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()