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