Clean up Intel HEX utilities
No functional change intended.
This commit is contained in:
		
							
								
								
									
										327
									
								
								stcgal/ihex.py
									
									
									
									
									
								
							
							
						
						
									
										327
									
								
								stcgal/ihex.py
									
									
									
									
									
								
							@ -5,201 +5,214 @@
 | 
			
		||||
import struct
 | 
			
		||||
import codecs
 | 
			
		||||
 | 
			
		||||
class IHex(object):
 | 
			
		||||
  @classmethod
 | 
			
		||||
  def read(cls, lines):
 | 
			
		||||
    ihex = cls()
 | 
			
		||||
 | 
			
		||||
    segbase = 0
 | 
			
		||||
    for line in lines:
 | 
			
		||||
      line = line.strip()
 | 
			
		||||
      if not line: continue
 | 
			
		||||
class IHex:
 | 
			
		||||
    """Intel HEX parser and writer"""
 | 
			
		||||
 | 
			
		||||
      t, a, d = ihex.parse_line(line)
 | 
			
		||||
      if t == 0x00:
 | 
			
		||||
        ihex.insert_data(segbase + a, d)
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def read(cls, lines):
 | 
			
		||||
        """Read Intel HEX data from string or lines"""
 | 
			
		||||
        ihex = cls()
 | 
			
		||||
 | 
			
		||||
      elif t == 0x01:
 | 
			
		||||
        break # Should we check for garbage after this?
 | 
			
		||||
        segbase = 0
 | 
			
		||||
        for line in lines:
 | 
			
		||||
            line = line.strip()
 | 
			
		||||
            if not line:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
      elif t == 0x02:
 | 
			
		||||
        ihex.set_mode(16)
 | 
			
		||||
        segbase = struct.unpack(">H", d[0:2])[0] << 4
 | 
			
		||||
            t, a, d = ihex.parse_line(line)
 | 
			
		||||
            if t == 0x00:
 | 
			
		||||
                ihex.insert_data(segbase + a, d)
 | 
			
		||||
 | 
			
		||||
      elif t == 0x03:
 | 
			
		||||
        ihex.set_mode(16)
 | 
			
		||||
            elif t == 0x01:
 | 
			
		||||
                break  # Should we check for garbage after this?
 | 
			
		||||
 | 
			
		||||
        cs, ip = struct.unpack(">2H", d[0:2])
 | 
			
		||||
        ihex.set_start((cs, ip))
 | 
			
		||||
            elif t == 0x02:
 | 
			
		||||
                ihex.set_mode(16)
 | 
			
		||||
                segbase = struct.unpack(">H", d[0:2])[0] << 4
 | 
			
		||||
 | 
			
		||||
      elif t == 0x04:
 | 
			
		||||
        ihex.set_mode(32)
 | 
			
		||||
        segbase = struct.unpack(">H", d[0:2])[0] << 16
 | 
			
		||||
            elif t == 0x03:
 | 
			
		||||
                ihex.set_mode(16)
 | 
			
		||||
 | 
			
		||||
      elif t == 0x05:
 | 
			
		||||
        ihex.set_mode(32)
 | 
			
		||||
        ihex.set_start(struct.unpack(">I", d[0:4])[0])
 | 
			
		||||
                cs, ip = struct.unpack(">2H", d[0:2])
 | 
			
		||||
                ihex.set_start((cs, ip))
 | 
			
		||||
 | 
			
		||||
      else:
 | 
			
		||||
        raise ValueError("Invalid type byte")
 | 
			
		||||
            elif t == 0x04:
 | 
			
		||||
                ihex.set_mode(32)
 | 
			
		||||
                segbase = struct.unpack(">H", d[0:2])[0] << 16
 | 
			
		||||
 | 
			
		||||
    return ihex
 | 
			
		||||
            elif t == 0x05:
 | 
			
		||||
                ihex.set_mode(32)
 | 
			
		||||
                ihex.set_start(struct.unpack(">I", d[0:4])[0])
 | 
			
		||||
 | 
			
		||||
  @classmethod
 | 
			
		||||
  def read_file(cls, fname):
 | 
			
		||||
    f = open(fname, "rb")
 | 
			
		||||
    ihex = cls.read(f)
 | 
			
		||||
    f.close()
 | 
			
		||||
    return ihex
 | 
			
		||||
            else:
 | 
			
		||||
                raise ValueError("Invalid type byte")
 | 
			
		||||
 | 
			
		||||
  def __init__(self):
 | 
			
		||||
    self.areas = {}
 | 
			
		||||
    self.start = None
 | 
			
		||||
    self.mode = 8
 | 
			
		||||
    self.row_bytes = 16
 | 
			
		||||
        return ihex
 | 
			
		||||
 | 
			
		||||
  def set_row_bytes(self, row_bytes):
 | 
			
		||||
      """Set output hex file row width (bytes represented per row)."""
 | 
			
		||||
      if row_bytes < 1 or row_bytes > 0xff:
 | 
			
		||||
          raise ValueError("Value out of range: (%r)" % row_bytes)
 | 
			
		||||
      self.row_bytes = row_bytes
 | 
			
		||||
  
 | 
			
		||||
  def extract_data(self, start=None, end=None):
 | 
			
		||||
    if start is None:
 | 
			
		||||
      start = 0
 | 
			
		||||
    
 | 
			
		||||
    if end is None:
 | 
			
		||||
      result = bytearray()
 | 
			
		||||
      
 | 
			
		||||
      for addr, data in self.areas.items():
 | 
			
		||||
        if addr >= start:
 | 
			
		||||
          if len(result) < (addr - start):
 | 
			
		||||
            result[len(result):addr-start] = bytes(addr-start-len(result))
 | 
			
		||||
          result[addr-start:addr-start+len(data)] = data
 | 
			
		||||
      
 | 
			
		||||
      return bytes(result)
 | 
			
		||||
    
 | 
			
		||||
    else:
 | 
			
		||||
      result = bytearray()
 | 
			
		||||
      
 | 
			
		||||
      for addr, data in self.areas.items():
 | 
			
		||||
        if addr >= start and addr < end:
 | 
			
		||||
          data = data[:end-addr]
 | 
			
		||||
          if len(result) < (addr - start):
 | 
			
		||||
            result[len(result):addr-start] = bytes(addr-start-len(result))
 | 
			
		||||
          result[addr-start:addr-start+len(data)] = data
 | 
			
		||||
      
 | 
			
		||||
      return bytes(result)
 | 
			
		||||
  
 | 
			
		||||
  def set_start(self, start=None):
 | 
			
		||||
    self.start = start
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def read_file(cls, fname):
 | 
			
		||||
        """Read Intel HEX data from file"""
 | 
			
		||||
        f = open(fname, "rb")
 | 
			
		||||
        ihex = cls.read(f)
 | 
			
		||||
        f.close()
 | 
			
		||||
        return ihex
 | 
			
		||||
 | 
			
		||||
  def set_mode(self, mode):
 | 
			
		||||
    self.mode = mode
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.areas = {}
 | 
			
		||||
        self.start = None
 | 
			
		||||
        self.mode = 8
 | 
			
		||||
        self.row_bytes = 16
 | 
			
		||||
 | 
			
		||||
  def get_area(self, addr):
 | 
			
		||||
    for start, data in self.areas.items():
 | 
			
		||||
      end = start + len(data)
 | 
			
		||||
      if addr >= start and addr <= end:
 | 
			
		||||
        return start
 | 
			
		||||
    def set_row_bytes(self, row_bytes):
 | 
			
		||||
        """Set output hex file row width (bytes represented per row)."""
 | 
			
		||||
        if row_bytes < 1 or row_bytes > 0xff:
 | 
			
		||||
            raise ValueError("Value out of range: (%r)" % row_bytes)
 | 
			
		||||
        self.row_bytes = row_bytes
 | 
			
		||||
 | 
			
		||||
    return None
 | 
			
		||||
    def extract_data(self, start=None, end=None):
 | 
			
		||||
        """Extract binary data"""
 | 
			
		||||
        if start is None:
 | 
			
		||||
            start = 0
 | 
			
		||||
 | 
			
		||||
  def insert_data(self, istart, idata):
 | 
			
		||||
    iend = istart + len(idata)
 | 
			
		||||
        if end is None:
 | 
			
		||||
            result = bytearray()
 | 
			
		||||
 | 
			
		||||
    area = self.get_area(istart)
 | 
			
		||||
    if area is None:
 | 
			
		||||
      self.areas[istart] = idata
 | 
			
		||||
            for addr, data in self.areas.items():
 | 
			
		||||
                if addr >= start:
 | 
			
		||||
                    if len(result) < (addr - start):
 | 
			
		||||
                        result[len(result):addr - start] = bytes(
 | 
			
		||||
                            addr - start - len(result))
 | 
			
		||||
                    result[addr - start:addr - start + len(data)] = data
 | 
			
		||||
 | 
			
		||||
    else:
 | 
			
		||||
      data = self.areas[area]
 | 
			
		||||
      # istart - iend + len(idata) + len(data)
 | 
			
		||||
      self.areas[area] = data[:istart-area] + idata + data[iend-area:]
 | 
			
		||||
            return bytes(result)
 | 
			
		||||
 | 
			
		||||
  def calc_checksum(self, bytes):
 | 
			
		||||
    total = sum(bytes)
 | 
			
		||||
    return (-total) & 0xFF
 | 
			
		||||
        else:
 | 
			
		||||
            result = bytearray()
 | 
			
		||||
 | 
			
		||||
  def parse_line(self, rawline):
 | 
			
		||||
    if rawline[0:1] != b":":
 | 
			
		||||
      raise ValueError("Invalid line start character (%r)" % rawline[0])
 | 
			
		||||
            for addr, data in self.areas.items():
 | 
			
		||||
                if addr >= start and addr < end:
 | 
			
		||||
                    data = data[:end - addr]
 | 
			
		||||
                    if len(result) < (addr - start):
 | 
			
		||||
                        result[len(result):addr - start] = bytes(
 | 
			
		||||
                            addr - start - len(result))
 | 
			
		||||
                    result[addr - start:addr - start + len(data)] = data
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
      #line = rawline[1:].decode("hex")
 | 
			
		||||
      line = codecs.decode(rawline[1:], "hex_codec")
 | 
			
		||||
    except:
 | 
			
		||||
      raise ValueError("Invalid hex data")
 | 
			
		||||
            return bytes(result)
 | 
			
		||||
 | 
			
		||||
    length, addr, type = struct.unpack(">BHB", line[:4])
 | 
			
		||||
    def set_start(self, start=None):
 | 
			
		||||
        self.start = start
 | 
			
		||||
 | 
			
		||||
    dataend = length + 4
 | 
			
		||||
    data = line[4:dataend]
 | 
			
		||||
    def set_mode(self, mode):
 | 
			
		||||
        self.mode = mode
 | 
			
		||||
 | 
			
		||||
    #~ print line[dataend:dataend + 2], repr(line)
 | 
			
		||||
    cs1 = line[dataend]
 | 
			
		||||
    cs2 = self.calc_checksum(line[:dataend])
 | 
			
		||||
    def get_area(self, addr):
 | 
			
		||||
        for start, data in self.areas.items():
 | 
			
		||||
            end = start + len(data)
 | 
			
		||||
            if addr >= start and addr <= end:
 | 
			
		||||
                return start
 | 
			
		||||
 | 
			
		||||
    if cs1 != cs2:
 | 
			
		||||
      raise ValueError("Checksums do not match")
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    return (type, addr, data)
 | 
			
		||||
    def insert_data(self, istart, idata):
 | 
			
		||||
        iend = istart + len(idata)
 | 
			
		||||
 | 
			
		||||
  def make_line(self, type, addr, data):
 | 
			
		||||
    line = struct.pack(">BHB", len(data), addr, type)
 | 
			
		||||
    line += data
 | 
			
		||||
    line += chr(self.calc_checksum(line))
 | 
			
		||||
    #~ return ":" + line.encode("hex")
 | 
			
		||||
    return ":" + line.encode("hex").upper() + "\r\n"
 | 
			
		||||
        area = self.get_area(istart)
 | 
			
		||||
        if area is None:
 | 
			
		||||
            self.areas[istart] = idata
 | 
			
		||||
 | 
			
		||||
  def write(self):
 | 
			
		||||
    output = ""
 | 
			
		||||
    
 | 
			
		||||
    for start, data in sorted(self.areas.items()):
 | 
			
		||||
      i = 0
 | 
			
		||||
      segbase = 0
 | 
			
		||||
        else:
 | 
			
		||||
            data = self.areas[area]
 | 
			
		||||
            # istart - iend + len(idata) + len(data)
 | 
			
		||||
            self.areas[area] = data[
 | 
			
		||||
                :istart - area] + idata + data[iend - area:]
 | 
			
		||||
 | 
			
		||||
      while i < len(data):
 | 
			
		||||
        chunk = data[i:i + self.row_bytes]
 | 
			
		||||
    def calc_checksum(self, data):
 | 
			
		||||
        total = sum(data)
 | 
			
		||||
        return (-total) & 0xFF
 | 
			
		||||
 | 
			
		||||
        addr = start
 | 
			
		||||
        newsegbase = segbase
 | 
			
		||||
    def parse_line(self, rawline):
 | 
			
		||||
        if rawline[0:1] != b":":
 | 
			
		||||
            raise ValueError("Invalid line start character (%r)" % rawline[0])
 | 
			
		||||
 | 
			
		||||
        if self.mode == 8:
 | 
			
		||||
          addr = addr & 0xFFFF
 | 
			
		||||
        try:
 | 
			
		||||
            line = codecs.decode(rawline[1:], "hex_codec")
 | 
			
		||||
        except:
 | 
			
		||||
            raise ValueError("Invalid hex data")
 | 
			
		||||
 | 
			
		||||
        elif self.mode == 16:
 | 
			
		||||
          t = addr & 0xFFFF
 | 
			
		||||
          newsegbase = (addr - t) >> 4
 | 
			
		||||
          addr = t
 | 
			
		||||
        length, addr, line_type = struct.unpack(">BHB", line[:4])
 | 
			
		||||
 | 
			
		||||
          if newsegbase != segbase:
 | 
			
		||||
            output += self.make_line(0x02, 0, struct.pack(">H", newsegbase))
 | 
			
		||||
            segbase = newsegbase
 | 
			
		||||
        dataend = length + 4
 | 
			
		||||
        data = line[4:dataend]
 | 
			
		||||
 | 
			
		||||
        elif self.mode == 32:
 | 
			
		||||
          newsegbase = addr >> 16
 | 
			
		||||
          addr = addr & 0xFFFF
 | 
			
		||||
        cs1 = line[dataend]
 | 
			
		||||
        cs2 = self.calc_checksum(line[:dataend])
 | 
			
		||||
 | 
			
		||||
          if newsegbase != segbase:
 | 
			
		||||
            output += self.make_line(0x04, 0, struct.pack(">H", newsegbase))
 | 
			
		||||
            segbase = newsegbase
 | 
			
		||||
        if cs1 != cs2:
 | 
			
		||||
            raise ValueError("Checksums do not match")
 | 
			
		||||
 | 
			
		||||
        output += self.make_line(0x00, addr, chunk)
 | 
			
		||||
        return (line_type, addr, data)
 | 
			
		||||
 | 
			
		||||
        i += self.row_bytes
 | 
			
		||||
        start += self.row_bytes
 | 
			
		||||
    def make_line(self, line_type, addr, data):
 | 
			
		||||
        line = struct.pack(">BHB", len(data), addr, line_type)
 | 
			
		||||
        line += data
 | 
			
		||||
        line += chr(self.calc_checksum(line))
 | 
			
		||||
        return ":" + line.encode("hex").upper() + "\r\n"
 | 
			
		||||
 | 
			
		||||
    if self.start is not None:
 | 
			
		||||
      if self.mode == 16:
 | 
			
		||||
        output += self.make_line(0x03, 0, struct.pack(">2H", self.start[0], self.start[1]))
 | 
			
		||||
      elif self.mode == 32:
 | 
			
		||||
        output += self.make_line(0x05, 0, struct.pack(">I", self.start))
 | 
			
		||||
    def write(self):
 | 
			
		||||
        """Write Intel HEX data to string"""
 | 
			
		||||
        output = ""
 | 
			
		||||
 | 
			
		||||
    output += self.make_line(0x01, 0, "")
 | 
			
		||||
    return output
 | 
			
		||||
        for start, data in sorted(self.areas.items()):
 | 
			
		||||
            i = 0
 | 
			
		||||
            segbase = 0
 | 
			
		||||
 | 
			
		||||
  def write_file(self, fname):
 | 
			
		||||
    f = open(fname, "w")
 | 
			
		||||
    f.write(self.write())
 | 
			
		||||
    f.close()
 | 
			
		||||
            while i < len(data):
 | 
			
		||||
                chunk = data[i:i + self.row_bytes]
 | 
			
		||||
 | 
			
		||||
                addr = start
 | 
			
		||||
                newsegbase = segbase
 | 
			
		||||
 | 
			
		||||
                if self.mode == 8:
 | 
			
		||||
                    addr = addr & 0xFFFF
 | 
			
		||||
 | 
			
		||||
                elif self.mode == 16:
 | 
			
		||||
                    t = addr & 0xFFFF
 | 
			
		||||
                    newsegbase = (addr - t) >> 4
 | 
			
		||||
                    addr = t
 | 
			
		||||
 | 
			
		||||
                    if newsegbase != segbase:
 | 
			
		||||
                        output += self.make_line(
 | 
			
		||||
                            0x02, 0, struct.pack(">H", newsegbase))
 | 
			
		||||
                        segbase = newsegbase
 | 
			
		||||
 | 
			
		||||
                elif self.mode == 32:
 | 
			
		||||
                    newsegbase = addr >> 16
 | 
			
		||||
                    addr = addr & 0xFFFF
 | 
			
		||||
 | 
			
		||||
                    if newsegbase != segbase:
 | 
			
		||||
                        output += self.make_line(
 | 
			
		||||
                            0x04, 0, struct.pack(">H", newsegbase))
 | 
			
		||||
                        segbase = newsegbase
 | 
			
		||||
 | 
			
		||||
                output += self.make_line(0x00, addr, chunk)
 | 
			
		||||
 | 
			
		||||
                i += self.row_bytes
 | 
			
		||||
                start += self.row_bytes
 | 
			
		||||
 | 
			
		||||
        if self.start is not None:
 | 
			
		||||
            if self.mode == 16:
 | 
			
		||||
                output += self.make_line(
 | 
			
		||||
                    0x03, 0, struct.pack(">2H", self.start[0], self.start[1]))
 | 
			
		||||
            elif self.mode == 32:
 | 
			
		||||
                output += self.make_line(
 | 
			
		||||
                    0x05, 0, struct.pack(">I", self.start))
 | 
			
		||||
 | 
			
		||||
        output += self.make_line(0x01, 0, "")
 | 
			
		||||
        return output
 | 
			
		||||
 | 
			
		||||
    def write_file(self, fname):
 | 
			
		||||
        """Write Intel HEX data to file"""
 | 
			
		||||
        f = open(fname, "w")
 | 
			
		||||
        f.write(self.write())
 | 
			
		||||
        f.close()
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user