There are no available options for this view.

Parent Directory Parent Directory | Revision Log Revision Log

Revision 1.7 - (show annotations) (download) (as text)
Mon Jan 30 03:00:10 2006 UTC (10 years, 11 months ago) by astronouth7303
Branch: MAIN
CVS Tags: HEAD
Changes since 1.6: +13 -4 lines
File MIME type: text/x-python
_Almost_ works (on the EDU bot, times out at EB0h)
1 """Parses the Intel Hex files. (Used a specification dated 1/6/88 for reference.)"""
2 from pyc.utils import listToLong_LE as _listToLong_LE, listToLong_BE as _listToLong_BE
3
4 enableEnhanced = False
5
6 class _bigList(list):
7 """An endless list. If you set an item and the list isn't big enough, None is inserted in-between."""
8 def __setitem__(self, key, value):
9 l = len(self)
10 if isinstance(key, slice):
11 vi = 0
12 ind = key.indices(max(len(self), len(value), key.stop)) ## Feed it the length of the theoretical final list
13 for si in range(ind[0], ind[1], ind[2]):
14 self.__setitem__(si, value[vi])
15 vi += 1
16 else:
17 if l <= key:
18 buf = ([None] * (key - l)) + [value]
19 super(_bigList, self).__setslice__(l, key, buf)
20 else:
21 super(_bigList, self).__setitem__(key, value)
22 def __setslice__(self, low, high, seq):
23 self.__setitem__(slice(low, high), seq)
24
25 class HexParseError(Exception, object):
26 """Failed to parse hex file."""
27 raiseMe = False ## Set this on a per-class basis to fine-tune errors thrown by the parser
28 hexFile = None
29 hexLine = None
30 def __init__(self, file=None, line=None):
31 self.hexFile = file
32 self.hexLine = line
33
34 class ChecksumMismatch(HexParseError):
35 """The checksum did not match the calculated one."""
36 raiseMe = True
37
38 class MisplacedEOF(HexParseError):
39 """There was an EOF record before the end of the file."""
40 raiseMe = False
41
42 class MissingEOF(HexParseError):
43 """No EOF record was found at the end of the hex file."""
44 raiseMe = False
45
46 class MalformedRecord(HexParseError):
47 """A meta-record was malformed."""
48 raiseMe = True
49
50 class UnknownRecordType(HexParseError):
51 """An unknown record type was encountered."""
52 raiseMe = True
53
54 class IncorrectRecordLength(HexParseError):
55 """The record length does not match the amount of data."""
56 raiseMe = True
57
58 _recordTypes = {}
59
60 class HexRecord(object):
61 _type = _address = _data = _checksum = _length = None
62 type = property( (lambda self: self._type), doc="The type of the record" )
63 address = property( (lambda self: self._address), doc="The address offset" )
64 data = property( (lambda self: self._data), doc="Sequence of data bytes" )
65 checksum = property( (lambda self: self._checksum), doc="The checksum of the data" )
66 length = property( (lambda self: len(self._data)), doc="The length of the record" )
67 baseAddress = 0
68
69 def __new__(cls, line, dbgInfo):
70 reclen = int(line[1:3], 16)
71 address = long(line[3:7], 16)
72 recordtype = int(line[7:9], 16)
73 data = line[9:-2]
74 checksum = int(line[-2:], 16)
75
76 ## Split data so it is a list of ints (not string of hex)
77 data = [int(data[i:i+2], 16) for i in range(0, len(data), 2)]
78
79 ## check the record length
80 if reclen != len(data) and IncorrectRecordLength.raiseMe: raise IncorrectRecordLength(**dbgInfo)
81
82 ## Calculate and check the checksum
83 calcsum = sum([int(line[i:i+2], 16) for i in range(1, len(line), 2)]) & 0xFF
84 if calcsum != 0 and ChecksumMismatch.raiseMe: raise ChecksumMismatch(**dbgInfo)
85
86 if recordtype in _recordTypeClasses: cls = _recordTypeClasses[recordtype]
87 elif UnknownRecordType.raiseMe: raise UnknownRecordType(**dbgInfo)
88
89 self = super(HexRecord, cls).__new__(cls)
90 self._length = reclen
91 self._address = address + cls.baseAddress
92 self._type = recordtype
93 self._data = tuple(data)
94 self._checksum = checksum
95 if hasattr(self, '__init__'): self.__init__(line, dbgInfo)
96 return self
97
98 class DataRecord(HexRecord):
99 """The basic data record."""
100 pass
101
102 class EOFRecord(HexRecord):
103 """The record that should be the last line."""
104 pass
105
106 class SegmentAddressRecord(HexRecord):
107 """This record sets the base address, begining with bit 4."""
108 def __init__(self, line, dbgInfo):
109 if (self.length != 2 or self._length != 2) and MalformedRecord.raiseMe: raise MalformedRecord(**dbgInfo)
110 addr = _listToLong_LE(self._data)
111 HexRecord.baseAddress = addr << 4
112
113 class LinearAddressRecord(HexRecord):
114 """A linear address record. Sets the base address bits 16 and up."""
115 def __init__(self, line, dbgInfo):
116 if (self.length != 2 or self._length != 2) and MalformedRecord.raiseMe: raise MalformedRecord(**dbgInfo)
117 addr = _listToLong_LE(self._data)
118 HexRecord.baseAddress = addr << 16
119
120 class StartLinearRecord(HexRecord):
121 """Sets the execution start location (EIP register). A hold-over from the '88 spec, methinks."""
122 _location = None
123 location = property( (lambda self: self._location), doc="The address to start execution at." )
124 EIP = property( (lambda self: self._location), doc="The value of the EIP register (same as location)." )
125 def __init__(self, line, dbgInfo):
126 if (self.length != 4 or self._length != 4) and MalformedRecord.raiseMe: raise MalformedRecord(**dbgInfo)
127 loc = _listToLong_BE(self._data)
128 self._location = loc
129
130 class StartSegmentRecord(HexRecord):
131 """Sets the execution start location (CS/IP registers). A hold-over from the '88 spec, methinks."""
132 _location = _cs = _ip = None
133 location = property( (lambda self: self._location), doc="The address to start execution at." )
134 CS = property( (lambda self: self._cs), doc="The value of the CS register." )
135 IP = property( (lambda self: self._ip), doc="The value of the IP register." )
136 def __init__(self, line, dbgInfo):
137 if (self.length != 4 or self._length != 4) and MalformedRecord.raiseMe: raise MalformedRecord(**dbgInfo)
138 loc = _listToLong_BE(self._data)
139 cs = _listToLong_BE(self._data[:2])
140 ip = _listToLong_BE(self._data[2:])
141 self._location = loc
142 self._cs = cs
143 self._ip = ip
144
145 _recordTypeClasses = {
146 0x00 : DataRecord,
147 0x01 : EOFRecord,
148 0x02 : SegmentAddressRecord,
149 0x03 : StartSegmentRecord,
150 0x04 : LinearAddressRecord,
151 0x05 : StartLinearRecord,
152 }
153
154 class HexFile(object):
155 """Loads and parses an Intel Hex file.
156
157 Parses are performed in two steps: first a by-record parse, then a record->memory parse."""
158 __file = None
159 __records = []
160 __memory = _bigList()
161 records = property( (lambda self: self.__records), doc="A sequence of the parsed records in the file." )
162 memory = property( (lambda self: self.__memory), doc="A sequence representing the memory set by the records. None is used where it isn't set." )
163 size = 0
164
165 def __init__(self, filename, parse=True):
166 """Initializes the HexFile object. parse is whether or not to parse it now."""
167 self.__file = filename
168 self.__records = []
169 self.__memory = _bigList()
170 ##if parse: self.parse()
171
172 def parseFile(self):
173 """Performs phase 1 of parsing. Reads the file and populates the records list."""
174 infile = file(self.__file, 'r')
175 linenum = 0 ## Debugging only
176 maxAddr = 0
177 for buf in (i.rstrip() for i in infile):
178 linenum += 1
179 newRec = HexRecord(buf, dbgInfo={'file':self.__file, 'line':linenum})
180 if newRec.address > maxAddr: maxAddr = newRec.address
181 self.__records += [ newRec ]
182 if len(self.__records) > 2 and self.__records[-2].type == 1 and MisplacedEOF.raiseMe:
183 raise MisplacedEOF(file=self.__file, line=linenum)
184 if self.__records[-1].type != 1 and MissingEOF.raiseMe:
185 raise MisplacedEOF(file=self.__file, line=linenum)
186 self.size = maxAddr
187
188 def loadRecords(self):
189 """Loads the records into memory."""
190 for rec in self.__records:
191 if rec.type != 0:
192 continue ## only do data records
193 #if rec.address == 0:
194 #continue ## Don't do anything if address is 0
195 self.__memory[ rec.address : rec.address + rec.length ] = rec.data
196
197 ## Returns a list containing the memory. Any values not loaded are None
198 #def load_hex(filen):
199 # infile = file(filen, 'r')
200 # memory = []
201 # linenum = 0 ## Debugging only
202 # for buf in (i.rstrip() for i in infile): ## remove trailing whitespace (eg, newlines)
203 # linenum += 1
204 # if buf[:1] != ":": continue ## If not a record, skip it
205 # reclen = int(buf[1:3], 16)
206 # address = long(buf[3:7], 16)
207 # recordtype = int(buf[7:9], 16)
208 # data = buf[9:-2]
209 # checksum = int(buf[-2:], 16)
210 #
211 # ## Split data so it is a list of ints (not string of hex)
212 # data = [int(data[i:i+2], 16) for i in range(0, len(data), 2)]
213 #
214 # ## Calculate and check the checksum
215 # calcsum = sum([int(buf[i:i+2], 16) for i in range(1, len(buf), 2)]) & 0xFF
216 # assert calcsum == 0, "Bad checksum: %u at line #%u" % (checksum, linenum)
217 #
218 # ## Not data record (00h) or address is 0000h, skip
219 # if recordtype != 0 or address == 0: continue
220 #
221 # ## Load the record into mem
222 # memory[address:address+reclen] = data
223 #
224 # return memory