104
|
1 import os
|
244
|
2 import struct
|
245
|
3 import binascii
|
|
4
|
|
5 DATA = 0
|
|
6 EOF = 1
|
|
7 EXTLINADR = 4
|
104
|
8
|
|
9 class HexFileException(Exception):
|
233
|
10 pass
|
104
|
11
|
|
12 def parseHexLine(line):
|
233
|
13 """ Parses a hexfile line into three parts """
|
|
14 line = line[1:] # Remove ':'
|
|
15 nums = bytes.fromhex(line)
|
|
16 bytecount = nums[0]
|
|
17 if len(nums) != bytecount + 5:
|
|
18 raise HexFileException('byte count field incorrect')
|
|
19 crc = sum(nums)
|
|
20 if (crc & 0xFF) != 0:
|
|
21 raise HexFileException('crc incorrect')
|
244
|
22 address = struct.unpack('>H', nums[1:3])[0]
|
233
|
23 typ = nums[3]
|
|
24 data = nums[4:-1]
|
|
25 return (address, typ, data)
|
|
26
|
244
|
27 def makeHexLine(address, typ, data=bytes()):
|
|
28 bytecount = len(data)
|
|
29 nums = bytearray()
|
|
30 nums.append(bytecount)
|
|
31 nums.extend(struct.pack('>H', address))
|
|
32 nums.append(typ)
|
|
33 nums.extend(data)
|
|
34 crc = sum(nums)
|
|
35 crc = ((~crc) + 1) & 0xFF
|
|
36 nums.append(crc)
|
245
|
37 line = ':' + binascii.hexlify(nums).decode('ascii')
|
244
|
38 return line
|
|
39
|
|
40 def chunks(data, csize=16):
|
|
41 idx = 0
|
|
42 while idx < len(data):
|
|
43 s = min(len(data) - idx, csize)
|
|
44 yield data[idx:idx+s]
|
|
45 idx += s
|
104
|
46
|
245
|
47 def hexfields(f):
|
|
48 for line in f:
|
|
49 line = line.strip() # Strip spaces and newlines
|
|
50 if not line:
|
|
51 continue # Skip empty lines
|
|
52 if line[0] != ':':
|
|
53 continue # Skip lines that do not start with a ':'
|
|
54 yield parseHexLine(line)
|
|
55
|
|
56
|
104
|
57 class HexFile:
|
233
|
58 """ Represents an intel hexfile """
|
240
|
59 def __init__(self):
|
|
60 self.regions = []
|
|
61 self.startAddress = 0
|
104
|
62
|
233
|
63 def load(self, f):
|
245
|
64 endOfFile = False
|
|
65 ext = 0
|
|
66 for address, typ, data in hexfields(f):
|
|
67 if endOfFile:
|
|
68 raise HexFileException('hexfile line after end of file record')
|
104
|
69 if typ == 0x0: # Data record
|
245
|
70 self.addRegion(address + ext, data)
|
|
71 elif typ == EXTLINADR: # Extended linear address record
|
|
72 ext = (struct.unpack('>H', data[0:2])[0]) << 16
|
|
73 elif typ == EOF: # End of file record
|
|
74 if len(data) != 0:
|
|
75 raise HexFileException('end of file not empty')
|
|
76 endOfFile = True
|
104
|
77 elif typ == 0x5: # Start address record (where IP goes after loading)
|
245
|
78 self.startAddress = struct.unpack('>I', data[0:4])[0]
|
104
|
79 else:
|
245
|
80 raise HexFileException('record type {0} not implemented'.format(typ))
|
233
|
81
|
|
82 def __repr__(self):
|
246
|
83 size = sum(len(r.data) for r in self.regions)
|
|
84 return 'Hexfile containing {} bytes'.format(size)
|
244
|
85
|
|
86 def dump(self):
|
|
87 print(self)
|
233
|
88 for r in self.regions:
|
244
|
89 print(r)
|
|
90
|
|
91 def __eq__(self, other):
|
|
92 regions = self.regions
|
|
93 oregions = other.regions
|
|
94 if len(regions) != len(oregions):
|
|
95 return False
|
|
96 return all(rs == ro for rs, ro in zip(regions, oregions))
|
|
97
|
|
98 def addRegion(self, address, data):
|
|
99 r = HexFileRegion(address, data)
|
|
100 self.regions.append(r)
|
245
|
101 self.check()
|
|
102
|
|
103 def check(self):
|
244
|
104 self.regions.sort(key=lambda r: r.address)
|
245
|
105 if len(self.regions) > 1:
|
|
106 for r1, r2 in zip(self.regions[:-1], self.regions[1:]):
|
|
107 if r1.EndAddress > r2.address:
|
|
108 raise HexFileException('Overlapping regions')
|
|
109 change = True
|
|
110 while change and len(self.regions) > 1:
|
|
111 change = False
|
|
112 for r1, r2 in zip(self.regions[:-1], self.regions[1:]):
|
|
113 if r1.EndAddress == r2.address:
|
|
114 r1.addData(r2.data)
|
|
115 self.regions.remove(r2)
|
|
116 change = True
|
|
117
|
246
|
118 def merge(self, other):
|
|
119 for r in other.regions:
|
|
120 self.addRegion(r.address, r.data)
|
233
|
121
|
|
122 def save(self, f):
|
245
|
123 def emit(address, typ, data=bytes()):
|
|
124 print(makeHexLine(address, typ, data), file=f)
|
233
|
125 for r in self.regions:
|
245
|
126 ext = r.address & 0xFFFF0000
|
|
127 emit(0, EXTLINADR, struct.pack('>H', ext >> 16))
|
|
128 address = r.address - ext
|
244
|
129 for chunk in chunks(r.data):
|
245
|
130 if address >= 0x10000:
|
|
131 ext += 0x10000
|
|
132 emit(0, EXTLINADR, struct.pack('>H', ext >> 16))
|
|
133 address -= 0x10000
|
|
134 emit(address, DATA, chunk)
|
|
135 address += len(chunk)
|
|
136 emit(0, EOF)
|
233
|
137
|
104
|
138 class HexFileRegion:
|
244
|
139 def __init__(self, address, data = bytes()):
|
|
140 self.address = address
|
|
141 self.data = data
|
|
142
|
|
143 def __repr__(self):
|
245
|
144 return 'Region at 0x{:08X} of {} bytes'.format(self.address, len(self.data))
|
244
|
145
|
|
146 def __eq__(self, other):
|
|
147 return (self.address, self.data) == (other.address, other.data)
|
|
148
|
245
|
149 def addData(self, d):
|
|
150 self.data = self.data + d
|
|
151
|
244
|
152 @property
|
|
153 def EndAddress(self):
|
|
154 return self.address + len(self.data)
|
104
|
155
|
233
|
156
|