view python/hexfile.py @ 244:58155c7c4a8e

Add hexutil
author Windel Bouwman
date Wed, 24 Jul 2013 19:47:13 +0200
parents 6259856841a0
children 66912720d712
line wrap: on
line source

import os
import struct

class HexFileException(Exception):
    pass

def parseHexLine(line):
    """ Parses a hexfile line into three parts """
    line = line[1:] # Remove ':'
    nums = bytes.fromhex(line)
    bytecount = nums[0]
    if len(nums) != bytecount + 5:
        raise HexFileException('byte count field incorrect')
    crc = sum(nums)
    if (crc & 0xFF) != 0:
        raise HexFileException('crc incorrect')
    address = struct.unpack('>H', nums[1:3])[0]
    typ = nums[3]
    data = nums[4:-1]
    return (address, typ, data)

def makeHexLine(address, typ, data=bytes()):
    bytecount = len(data)
    nums = bytearray()
    nums.append(bytecount)
    nums.extend(struct.pack('>H', address))
    nums.append(typ)
    nums.extend(data)
    crc = sum(nums)
    crc = ((~crc) + 1) & 0xFF
    nums.append(crc)
    line = ':' + ''.join(['{:02X}'.format(b) for b in nums])
    return line

def chunks(data, csize=16):
    idx = 0
    while idx < len(data):
        s = min(len(data) - idx, csize)
        yield data[idx:idx+s]
        idx += s

class HexFile:
    """ Represents an intel hexfile """
    def __init__(self):
        self.regions = []
        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)
            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
            elif typ == 0x5: # Start address record (where IP goes after loading)
               self.startAddress = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]
            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)))

    def __repr__(self):
        return 'Hexfile with {} regions'.format(len(self.regions))

    def dump(self):
        print(self)
        for r in self.regions:
            print(r)

    def __eq__(self, other):
        regions = self.regions
        oregions = other.regions
        if len(regions) != len(oregions):
            return False
        return all(rs == ro for rs, ro in zip(regions, oregions))

    def addRegion(self, address, data):
        r = HexFileRegion(address, data)
        # TODO: check touches
        self.regions.append(r)
        self.regions.sort(key=lambda r: r.address)

    def save(self, f):
        for r in self.regions:
            offset = 0
            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)

class HexFileRegion:
    def __init__(self, address, data = bytes()):
        self.address = address
        self.data = data

    def __repr__(self):
        return 'Region at 0x{0:X} of {1} bytes'.format(self.address, len(self.data))

    def __eq__(self, other):
        return (self.address, self.data) == (other.address, other.data)

    @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)