frontend: add Intel HEX support
Add Intel HEX support with the help of the IHex library. IHex needed some modifications to make it work with Python 3. Intel HEX files are autodetected, based on file extension (.hex, .ihx or .ihex).
This commit is contained in:
parent
f452ca226e
commit
63327e3498
@ -25,6 +25,7 @@ import argparse
|
||||
import stcgal
|
||||
from stcgal.utils import Utils, BaudType
|
||||
from stcgal.protocols import *
|
||||
from stcgal.ihex import IHex
|
||||
|
||||
class StcGal:
|
||||
"""STC ISP flash tool frontend"""
|
||||
@ -55,11 +56,31 @@ class StcGal:
|
||||
except ValueError as e:
|
||||
raise NameError("invalid option '%s' (%s)" % (kv[0], e))
|
||||
|
||||
def load_file_auto(self, fileobj):
|
||||
"""Load file with Intel Hex autodetection."""
|
||||
|
||||
fname = fileobj.name.lower()
|
||||
if (fname.endswith(".hex") or fname.endswith(".ihx") or
|
||||
fname.endswith(".ihex")):
|
||||
try:
|
||||
hexfile = IHex.read(fileobj)
|
||||
binary = hexfile.extract_data()
|
||||
print("%d bytes (Intel HEX)" %len(binary))
|
||||
return binary
|
||||
except ValueError as e:
|
||||
raise IOError("invalid Intel HEX file (%s)" %e)
|
||||
else:
|
||||
binary = fileobj.read()
|
||||
print("%d bytes (Binary)" %len(binary))
|
||||
return binary
|
||||
|
||||
def program_mcu(self):
|
||||
code_size = self.protocol.model.code
|
||||
ee_size = self.protocol.model.eeprom
|
||||
|
||||
bindata = self.opts.code_binary.read()
|
||||
print("Loading flash: ", end="")
|
||||
sys.stdout.flush()
|
||||
bindata = self.load_file_auto(self.opts.code_binary)
|
||||
|
||||
# warn if it overflows
|
||||
if len(bindata) > code_size:
|
||||
@ -70,7 +91,9 @@ class StcGal:
|
||||
|
||||
# add eeprom data if supplied
|
||||
if self.opts.eeprom_binary:
|
||||
eedata = self.opts.eeprom_binary.read()
|
||||
print("Loading EEPROM: ", end="")
|
||||
sys.stdout.flush()
|
||||
eedata = self.load_file_auto(self.opts.eeprom_binary)
|
||||
if len(eedata) > ee_size:
|
||||
print("WARNING: eeprom_binary truncated!", file=sys.stderr)
|
||||
eedata = eedata[0:ee_size]
|
||||
@ -116,6 +139,11 @@ class StcGal:
|
||||
else:
|
||||
self.protocol.disconnect()
|
||||
return 0
|
||||
except IOError as e:
|
||||
sys.stdout.flush();
|
||||
print("I/O error: %s" % e, file=sys.stderr)
|
||||
self.protocol.disconnect()
|
||||
return 1
|
||||
except NameError as e:
|
||||
sys.stdout.flush();
|
||||
print("Option error: %s" % e, file=sys.stderr)
|
||||
|
202
stcgal/ihex.py
Normal file
202
stcgal/ihex.py
Normal file
@ -0,0 +1,202 @@
|
||||
# IHex by Kier Davis, modified for Python 3
|
||||
# Public Domain
|
||||
# https://github.com/kierdavis/IHex
|
||||
|
||||
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
|
||||
|
||||
t, a, d = ihex.parse_line(line)
|
||||
if t == 0x00:
|
||||
ihex.insert_data(segbase + a, d)
|
||||
|
||||
elif t == 0x01:
|
||||
break # Should we check for garbage after this?
|
||||
|
||||
elif t == 0x02:
|
||||
ihex.set_mode(16)
|
||||
segbase = struct.unpack(">H", d[0:2])[0] << 4
|
||||
|
||||
elif t == 0x03:
|
||||
ihex.set_mode(16)
|
||||
|
||||
cs, ip = struct.unpack(">2H", d[0:2])
|
||||
ihex.set_start((cs, ip))
|
||||
|
||||
elif t == 0x04:
|
||||
ihex.set_mode(32)
|
||||
segbase = struct.unpack(">H", d[0:2])[0] << 16
|
||||
|
||||
elif t == 0x05:
|
||||
ihex.set_mode(32)
|
||||
ihex.set_start(struct.unpack(">I", d[0:4])[0])
|
||||
|
||||
else:
|
||||
raise ValueError("Invalid type byte")
|
||||
|
||||
return ihex
|
||||
|
||||
@classmethod
|
||||
def read_file(cls, fname):
|
||||
f = open(fname, "rb")
|
||||
ihex = cls.read(f)
|
||||
f.close()
|
||||
return ihex
|
||||
|
||||
def __init__(self):
|
||||
self.areas = {}
|
||||
self.start = None
|
||||
self.mode = 8
|
||||
self.row_bytes = 16
|
||||
|
||||
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:
|
||||
end = 0
|
||||
result = bytes()
|
||||
|
||||
for addr, data in self.areas.items():
|
||||
if addr >= start:
|
||||
end = max(end, addr + len(data))
|
||||
result = result[:start] + data[start-addr:end-addr] + result[end:]
|
||||
|
||||
return result
|
||||
|
||||
else:
|
||||
result = bytes()
|
||||
|
||||
for addr, data in self.areas.items():
|
||||
if addr >= start and addr < end:
|
||||
result = result[:start] + data[start-addr:end-addr] + result[end:]
|
||||
|
||||
return result
|
||||
|
||||
def set_start(self, start=None):
|
||||
self.start = start
|
||||
|
||||
def set_mode(self, mode):
|
||||
self.mode = mode
|
||||
|
||||
def get_area(self, addr):
|
||||
for start, data in self.areas.items():
|
||||
end = start + len(data)
|
||||
if addr >= start and addr <= end:
|
||||
return start
|
||||
|
||||
return None
|
||||
|
||||
def insert_data(self, istart, idata):
|
||||
iend = istart + len(idata)
|
||||
|
||||
area = self.get_area(istart)
|
||||
if area is None:
|
||||
self.areas[istart] = idata
|
||||
|
||||
else:
|
||||
data = self.areas[area]
|
||||
# istart - iend + len(idata) + len(data)
|
||||
self.areas[area] = data[:istart-area] + idata + data[iend-area:]
|
||||
|
||||
def calc_checksum(self, bytes):
|
||||
total = sum(bytes)
|
||||
return (-total) & 0xFF
|
||||
|
||||
def parse_line(self, rawline):
|
||||
if rawline[0:1] != b":":
|
||||
raise ValueError("Invalid line start character (%r)" % rawline[0])
|
||||
|
||||
try:
|
||||
#line = rawline[1:].decode("hex")
|
||||
line = codecs.decode(rawline[1:], "hex_codec")
|
||||
except:
|
||||
raise ValueError("Invalid hex data")
|
||||
|
||||
length, addr, type = struct.unpack(">BHB", line[:4])
|
||||
|
||||
dataend = length + 4
|
||||
data = line[4:dataend]
|
||||
|
||||
#~ print line[dataend:dataend + 2], repr(line)
|
||||
cs1 = line[dataend]
|
||||
cs2 = self.calc_checksum(line[:dataend])
|
||||
|
||||
if cs1 != cs2:
|
||||
raise ValueError("Checksums do not match")
|
||||
|
||||
return (type, addr, data)
|
||||
|
||||
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"
|
||||
|
||||
def write(self):
|
||||
output = ""
|
||||
|
||||
for start, data in sorted(self.areas.items()):
|
||||
i = 0
|
||||
segbase = 0
|
||||
|
||||
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):
|
||||
f = open(fname, "w")
|
||||
f.write(self.write())
|
||||
f.close()
|
Loading…
Reference in New Issue
Block a user