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):
|
244
|
83 return 'Hexfile with {} regions'.format(len(self.regions))
|
|
84
|
|
85 def dump(self):
|
|
86 print(self)
|
233
|
87 for r in self.regions:
|
244
|
88 print(r)
|
|
89
|
|
90 def __eq__(self, other):
|
|
91 regions = self.regions
|
|
92 oregions = other.regions
|
|
93 if len(regions) != len(oregions):
|
|
94 return False
|
|
95 return all(rs == ro for rs, ro in zip(regions, oregions))
|
|
96
|
|
97 def addRegion(self, address, data):
|
|
98 r = HexFileRegion(address, data)
|
|
99 self.regions.append(r)
|
245
|
100 self.check()
|
|
101
|
|
102 def check(self):
|
244
|
103 self.regions.sort(key=lambda r: r.address)
|
245
|
104 if len(self.regions) > 1:
|
|
105 for r1, r2 in zip(self.regions[:-1], self.regions[1:]):
|
|
106 if r1.EndAddress > r2.address:
|
|
107 raise HexFileException('Overlapping regions')
|
|
108 change = True
|
|
109 while change and len(self.regions) > 1:
|
|
110 change = False
|
|
111 for r1, r2 in zip(self.regions[:-1], self.regions[1:]):
|
|
112 if r1.EndAddress == r2.address:
|
|
113 r1.addData(r2.data)
|
|
114 self.regions.remove(r2)
|
|
115 change = True
|
|
116
|
233
|
117
|
|
118 def save(self, f):
|
245
|
119 def emit(address, typ, data=bytes()):
|
|
120 print(makeHexLine(address, typ, data), file=f)
|
233
|
121 for r in self.regions:
|
245
|
122 ext = r.address & 0xFFFF0000
|
|
123 emit(0, EXTLINADR, struct.pack('>H', ext >> 16))
|
|
124 address = r.address - ext
|
244
|
125 for chunk in chunks(r.data):
|
245
|
126 if address >= 0x10000:
|
|
127 ext += 0x10000
|
|
128 emit(0, EXTLINADR, struct.pack('>H', ext >> 16))
|
|
129 address -= 0x10000
|
|
130 emit(address, DATA, chunk)
|
|
131 address += len(chunk)
|
|
132 emit(0, EOF)
|
233
|
133
|
104
|
134 class HexFileRegion:
|
244
|
135 def __init__(self, address, data = bytes()):
|
|
136 self.address = address
|
|
137 self.data = data
|
|
138
|
|
139 def __repr__(self):
|
245
|
140 return 'Region at 0x{:08X} of {} bytes'.format(self.address, len(self.data))
|
244
|
141
|
|
142 def __eq__(self, other):
|
|
143 return (self.address, self.data) == (other.address, other.data)
|
|
144
|
245
|
145 def addData(self, d):
|
|
146 self.data = self.data + d
|
|
147
|
244
|
148 @property
|
|
149 def EndAddress(self):
|
|
150 return self.address + len(self.data)
|
104
|
151
|
233
|
152
|