__init__.py 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286
  1. # Copyright (c) 2005-2013, Alexander Belchenko
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms,
  5. # with or without modification, are permitted provided
  6. # that the following conditions are met:
  7. #
  8. # * Redistributions of source code must retain
  9. # the above copyright notice, this list of conditions
  10. # and the following disclaimer.
  11. # * Redistributions in binary form must reproduce
  12. # the above copyright notice, this list of conditions
  13. # and the following disclaimer in the documentation
  14. # and/or other materials provided with the distribution.
  15. # * Neither the name of the author nor the names
  16. # of its contributors may be used to endorse
  17. # or promote products derived from this software
  18. # without specific prior written permission.
  19. #
  20. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  22. # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  23. # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  24. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  25. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  26. # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  27. # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  28. # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  30. # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  31. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  32. # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. '''Intel HEX file format reader and converter.
  34. @author Alexander Belchenko (alexander dot belchenko at gmail dot com)
  35. @version 1.5
  36. '''
  37. __docformat__ = "javadoc"
  38. from array import array
  39. from binascii import hexlify, unhexlify
  40. from bisect import bisect_right
  41. import os
  42. import sys
  43. from compat import asbytes, asstr
  44. class _DeprecatedParam(object):
  45. pass
  46. _DEPRECATED = _DeprecatedParam()
  47. class IntelHex(object):
  48. ''' Intel HEX file reader. '''
  49. def __init__(self, source=None):
  50. ''' Constructor. If source specified, object will be initialized
  51. with the contents of source. Otherwise the object will be empty.
  52. @param source source for initialization
  53. (file name of HEX file, file object, addr dict or
  54. other IntelHex object)
  55. '''
  56. # public members
  57. self.padding = 0x0FF
  58. # Start Address
  59. self.start_addr = None
  60. # private members
  61. self._buf = {}
  62. self._offset = 0
  63. if source is not None:
  64. if isinstance(source, basestring) or getattr(source, "read", None):
  65. # load hex file
  66. self.loadhex(source)
  67. elif isinstance(source, dict):
  68. self.fromdict(source)
  69. elif isinstance(source, IntelHex):
  70. self.padding = source.padding
  71. if source.start_addr:
  72. self.start_addr = source.start_addr.copy()
  73. self._buf = source._buf.copy()
  74. else:
  75. raise ValueError("source: bad initializer type")
  76. def _decode_record(self, s, line=0):
  77. '''Decode one record of HEX file.
  78. @param s line with HEX record.
  79. @param line line number (for error messages).
  80. @raise EndOfFile if EOF record encountered.
  81. '''
  82. s = s.rstrip('\r\n')
  83. if not s:
  84. return # empty line
  85. if s[0] == ':':
  86. try:
  87. bin = array('B', unhexlify(asbytes(s[1:])))
  88. except (TypeError, ValueError):
  89. # this might be raised by unhexlify when odd hexascii digits
  90. raise HexRecordError(line=line)
  91. length = len(bin)
  92. if length < 5:
  93. raise HexRecordError(line=line)
  94. else:
  95. raise HexRecordError(line=line)
  96. record_length = bin[0]
  97. if length != (5 + record_length):
  98. raise RecordLengthError(line=line)
  99. addr = bin[1]*256 + bin[2]
  100. record_type = bin[3]
  101. if not (0 <= record_type <= 5):
  102. raise RecordTypeError(line=line)
  103. crc = sum(bin)
  104. crc &= 0x0FF
  105. if crc != 0:
  106. raise RecordChecksumError(line=line)
  107. if record_type == 0:
  108. # data record
  109. addr += self._offset
  110. for i in xrange(4, 4+record_length):
  111. if not self._buf.get(addr, None) is None:
  112. raise AddressOverlapError(address=addr, line=line)
  113. self._buf[addr] = bin[i]
  114. addr += 1 # FIXME: addr should be wrapped
  115. # BUT after 02 record (at 64K boundary)
  116. # and after 04 record (at 4G boundary)
  117. elif record_type == 1:
  118. # end of file record
  119. if record_length != 0:
  120. raise EOFRecordError(line=line)
  121. raise _EndOfFile
  122. elif record_type == 2:
  123. # Extended 8086 Segment Record
  124. if record_length != 2 or addr != 0:
  125. raise ExtendedSegmentAddressRecordError(line=line)
  126. self._offset = (bin[4]*256 + bin[5]) * 16
  127. elif record_type == 4:
  128. # Extended Linear Address Record
  129. if record_length != 2 or addr != 0:
  130. raise ExtendedLinearAddressRecordError(line=line)
  131. self._offset = (bin[4]*256 + bin[5]) * 65536
  132. elif record_type == 3:
  133. # Start Segment Address Record
  134. if record_length != 4 or addr != 0:
  135. raise StartSegmentAddressRecordError(line=line)
  136. if self.start_addr:
  137. raise DuplicateStartAddressRecordError(line=line)
  138. self.start_addr = {'CS': bin[4]*256 + bin[5],
  139. 'IP': bin[6]*256 + bin[7],
  140. }
  141. elif record_type == 5:
  142. # Start Linear Address Record
  143. if record_length != 4 or addr != 0:
  144. raise StartLinearAddressRecordError(line=line)
  145. if self.start_addr:
  146. raise DuplicateStartAddressRecordError(line=line)
  147. self.start_addr = {'EIP': (bin[4]*16777216 +
  148. bin[5]*65536 +
  149. bin[6]*256 +
  150. bin[7]),
  151. }
  152. def loadhex(self, fobj):
  153. """Load hex file into internal buffer. This is not necessary
  154. if object was initialized with source set. This will overwrite
  155. addresses if object was already initialized.
  156. @param fobj file name or file-like object
  157. """
  158. if getattr(fobj, "read", None) is None:
  159. fobj = open(fobj, "r")
  160. fclose = fobj.close
  161. else:
  162. fclose = None
  163. self._offset = 0
  164. line = 0
  165. try:
  166. decode = self._decode_record
  167. try:
  168. for s in fobj:
  169. line += 1
  170. decode(s, line)
  171. except _EndOfFile:
  172. pass
  173. finally:
  174. if fclose:
  175. fclose()
  176. def loadbin(self, fobj, offset=0):
  177. """Load bin file into internal buffer. Not needed if source set in
  178. constructor. This will overwrite addresses without warning
  179. if object was already initialized.
  180. @param fobj file name or file-like object
  181. @param offset starting address offset
  182. """
  183. fread = getattr(fobj, "read", None)
  184. if fread is None:
  185. f = open(fobj, "rb")
  186. fread = f.read
  187. fclose = f.close
  188. else:
  189. fclose = None
  190. try:
  191. self.frombytes(array('B', asbytes(fread())), offset=offset)
  192. finally:
  193. if fclose:
  194. fclose()
  195. def loadfile(self, fobj, format):
  196. """Load data file into internal buffer. Preferred wrapper over
  197. loadbin or loadhex.
  198. @param fobj file name or file-like object
  199. @param format file format ("hex" or "bin")
  200. """
  201. if format == "hex":
  202. self.loadhex(fobj)
  203. elif format == "bin":
  204. self.loadbin(fobj)
  205. else:
  206. raise ValueError('format should be either "hex" or "bin";'
  207. ' got %r instead' % format)
  208. # alias (to be consistent with method tofile)
  209. fromfile = loadfile
  210. def fromdict(self, dikt):
  211. """Load data from dictionary. Dictionary should contain int keys
  212. representing addresses. Values should be the data to be stored in
  213. those addresses in unsigned char form (i.e. not strings).
  214. The dictionary may contain the key, ``start_addr``
  215. to indicate the starting address of the data as described in README.
  216. The contents of the dict will be merged with this object and will
  217. overwrite any conflicts. This function is not necessary if the
  218. object was initialized with source specified.
  219. """
  220. s = dikt.copy()
  221. start_addr = s.get('start_addr')
  222. if start_addr is not None:
  223. del s['start_addr']
  224. for k in s.keys():
  225. if type(k) not in (int, long) or k < 0:
  226. raise ValueError('Source dictionary should have only int keys')
  227. self._buf.update(s)
  228. if start_addr is not None:
  229. self.start_addr = start_addr
  230. def frombytes(self, bytes, offset=0):
  231. """Load data from array or list of bytes.
  232. Similar to loadbin() method but works directly with iterable bytes.
  233. """
  234. for b in bytes:
  235. self._buf[offset] = b
  236. offset += 1
  237. def _get_start_end(self, start=None, end=None, size=None):
  238. """Return default values for start and end if they are None.
  239. If this IntelHex object is empty then it's error to
  240. invoke this method with both start and end as None.
  241. """
  242. if (start,end) == (None,None) and self._buf == {}:
  243. raise EmptyIntelHexError
  244. if size is not None:
  245. if None not in (start, end):
  246. raise ValueError("tobinarray: you can't use start,end and size"
  247. " arguments in the same time")
  248. if (start, end) == (None, None):
  249. start = self.minaddr()
  250. if start is not None:
  251. end = start + size - 1
  252. else:
  253. start = end - size + 1
  254. if start < 0:
  255. raise ValueError("tobinarray: invalid size (%d) "
  256. "for given end address (%d)" % (size,end))
  257. else:
  258. if start is None:
  259. start = self.minaddr()
  260. if end is None:
  261. end = self.maxaddr()
  262. if start > end:
  263. start, end = end, start
  264. return start, end
  265. def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None):
  266. ''' Convert this object to binary form as array. If start and end
  267. unspecified, they will be inferred from the data.
  268. @param start start address of output bytes.
  269. @param end end address of output bytes (inclusive).
  270. @param pad [DEPRECATED PARAMETER, please use self.padding instead]
  271. fill empty spaces with this value
  272. (if pad is None then this method uses self.padding).
  273. @param size size of the block, used with start or end parameter.
  274. @return array of unsigned char data.
  275. '''
  276. if not isinstance(pad, _DeprecatedParam):
  277. print "IntelHex.tobinarray: 'pad' parameter is deprecated."
  278. if pad is not None:
  279. print "Please, use IntelHex.padding attribute instead."
  280. else:
  281. print "Please, don't pass it explicitly."
  282. print "Use syntax like this: ih.tobinarray(start=xxx, end=yyy, size=zzz)"
  283. else:
  284. pad = None
  285. return self._tobinarray_really(start, end, pad, size)
  286. def _tobinarray_really(self, start, end, pad, size):
  287. if pad is None:
  288. pad = self.padding
  289. bin = array('B')
  290. if self._buf == {} and None in (start, end):
  291. return bin
  292. if size is not None and size <= 0:
  293. raise ValueError("tobinarray: wrong value for size")
  294. start, end = self._get_start_end(start, end, size)
  295. for i in xrange(start, end+1):
  296. bin.append(self._buf.get(i, pad))
  297. return bin
  298. def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None):
  299. ''' Convert to binary form and return as a string.
  300. @param start start address of output bytes.
  301. @param end end address of output bytes (inclusive).
  302. @param pad [DEPRECATED PARAMETER, please use self.padding instead]
  303. fill empty spaces with this value
  304. (if pad is None then this method uses self.padding).
  305. @param size size of the block, used with start or end parameter.
  306. @return string of binary data.
  307. '''
  308. if not isinstance(pad, _DeprecatedParam):
  309. print "IntelHex.tobinstr: 'pad' parameter is deprecated."
  310. if pad is not None:
  311. print "Please, use IntelHex.padding attribute instead."
  312. else:
  313. print "Please, don't pass it explicitly."
  314. print "Use syntax like this: ih.tobinstr(start=xxx, end=yyy, size=zzz)"
  315. else:
  316. pad = None
  317. return self._tobinstr_really(start, end, pad, size)
  318. def _tobinstr_really(self, start, end, pad, size):
  319. return asstr(self._tobinarray_really(start, end, pad, size).tostring())
  320. def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None):
  321. '''Convert to binary and write to file.
  322. @param fobj file name or file object for writing output bytes.
  323. @param start start address of output bytes.
  324. @param end end address of output bytes (inclusive).
  325. @param pad [DEPRECATED PARAMETER, please use self.padding instead]
  326. fill empty spaces with this value
  327. (if pad is None then this method uses self.padding).
  328. @param size size of the block, used with start or end parameter.
  329. '''
  330. if not isinstance(pad, _DeprecatedParam):
  331. print "IntelHex.tobinfile: 'pad' parameter is deprecated."
  332. if pad is not None:
  333. print "Please, use IntelHex.padding attribute instead."
  334. else:
  335. print "Please, don't pass it explicitly."
  336. print "Use syntax like this: ih.tobinfile(start=xxx, end=yyy, size=zzz)"
  337. else:
  338. pad = None
  339. if getattr(fobj, "write", None) is None:
  340. fobj = open(fobj, "wb")
  341. close_fd = True
  342. else:
  343. close_fd = False
  344. fobj.write(self._tobinstr_really(start, end, pad, size))
  345. if close_fd:
  346. fobj.close()
  347. def todict(self):
  348. '''Convert to python dictionary.
  349. @return dict suitable for initializing another IntelHex object.
  350. '''
  351. r = {}
  352. r.update(self._buf)
  353. if self.start_addr:
  354. r['start_addr'] = self.start_addr
  355. return r
  356. def addresses(self):
  357. '''Returns all used addresses in sorted order.
  358. @return list of occupied data addresses in sorted order.
  359. '''
  360. aa = self._buf.keys()
  361. aa.sort()
  362. return aa
  363. def minaddr(self):
  364. '''Get minimal address of HEX content.
  365. @return minimal address or None if no data
  366. '''
  367. aa = self._buf.keys()
  368. if aa == []:
  369. return None
  370. else:
  371. return min(aa)
  372. def maxaddr(self):
  373. '''Get maximal address of HEX content.
  374. @return maximal address or None if no data
  375. '''
  376. aa = self._buf.keys()
  377. if aa == []:
  378. return None
  379. else:
  380. return max(aa)
  381. def __getitem__(self, addr):
  382. ''' Get requested byte from address.
  383. @param addr address of byte.
  384. @return byte if address exists in HEX file, or self.padding
  385. if no data found.
  386. '''
  387. t = type(addr)
  388. if t in (int, long):
  389. if addr < 0:
  390. raise TypeError('Address should be >= 0.')
  391. return self._buf.get(addr, self.padding)
  392. elif t == slice:
  393. addresses = self._buf.keys()
  394. ih = IntelHex()
  395. if addresses:
  396. addresses.sort()
  397. start = addr.start or addresses[0]
  398. stop = addr.stop or (addresses[-1]+1)
  399. step = addr.step or 1
  400. for i in xrange(start, stop, step):
  401. x = self._buf.get(i)
  402. if x is not None:
  403. ih[i] = x
  404. return ih
  405. else:
  406. raise TypeError('Address has unsupported type: %s' % t)
  407. def __setitem__(self, addr, byte):
  408. """Set byte at address."""
  409. t = type(addr)
  410. if t in (int, long):
  411. if addr < 0:
  412. raise TypeError('Address should be >= 0.')
  413. self._buf[addr] = byte
  414. elif t == slice:
  415. if not isinstance(byte, (list, tuple)):
  416. raise ValueError('Slice operation expects sequence of bytes')
  417. start = addr.start
  418. stop = addr.stop
  419. step = addr.step or 1
  420. if None not in (start, stop):
  421. ra = range(start, stop, step)
  422. if len(ra) != len(byte):
  423. raise ValueError('Length of bytes sequence does not match '
  424. 'address range')
  425. elif (start, stop) == (None, None):
  426. raise TypeError('Unsupported address range')
  427. elif start is None:
  428. start = stop - len(byte)
  429. elif stop is None:
  430. stop = start + len(byte)
  431. if start < 0:
  432. raise TypeError('start address cannot be negative')
  433. if stop < 0:
  434. raise TypeError('stop address cannot be negative')
  435. j = 0
  436. for i in xrange(start, stop, step):
  437. self._buf[i] = byte[j]
  438. j += 1
  439. else:
  440. raise TypeError('Address has unsupported type: %s' % t)
  441. def __delitem__(self, addr):
  442. """Delete byte at address."""
  443. t = type(addr)
  444. if t in (int, long):
  445. if addr < 0:
  446. raise TypeError('Address should be >= 0.')
  447. del self._buf[addr]
  448. elif t == slice:
  449. addresses = self._buf.keys()
  450. if addresses:
  451. addresses.sort()
  452. start = addr.start or addresses[0]
  453. stop = addr.stop or (addresses[-1]+1)
  454. step = addr.step or 1
  455. for i in xrange(start, stop, step):
  456. x = self._buf.get(i)
  457. if x is not None:
  458. del self._buf[i]
  459. else:
  460. raise TypeError('Address has unsupported type: %s' % t)
  461. def __len__(self):
  462. """Return count of bytes with real values."""
  463. return len(self._buf.keys())
  464. def write_hex_file(self, f, write_start_addr=True):
  465. """Write data to file f in HEX format.
  466. @param f filename or file-like object for writing
  467. @param write_start_addr enable or disable writing start address
  468. record to file (enabled by default).
  469. If there is no start address in obj, nothing
  470. will be written regardless of this setting.
  471. """
  472. fwrite = getattr(f, "write", None)
  473. if fwrite:
  474. fobj = f
  475. fclose = None
  476. else:
  477. fobj = open(f, 'w')
  478. fwrite = fobj.write
  479. fclose = fobj.close
  480. # Translation table for uppercasing hex ascii string.
  481. # timeit shows that using hexstr.translate(table)
  482. # is faster than hexstr.upper():
  483. # 0.452ms vs. 0.652ms (translate vs. upper)
  484. if sys.version_info[0] >= 3:
  485. table = bytes(range(256)).upper()
  486. else:
  487. table = ''.join(chr(i).upper() for i in range(256))
  488. # start address record if any
  489. if self.start_addr and write_start_addr:
  490. keys = self.start_addr.keys()
  491. keys.sort()
  492. bin = array('B', asbytes('\0'*9))
  493. if keys == ['CS','IP']:
  494. # Start Segment Address Record
  495. bin[0] = 4 # reclen
  496. bin[1] = 0 # offset msb
  497. bin[2] = 0 # offset lsb
  498. bin[3] = 3 # rectyp
  499. cs = self.start_addr['CS']
  500. bin[4] = (cs >> 8) & 0x0FF
  501. bin[5] = cs & 0x0FF
  502. ip = self.start_addr['IP']
  503. bin[6] = (ip >> 8) & 0x0FF
  504. bin[7] = ip & 0x0FF
  505. bin[8] = (-sum(bin)) & 0x0FF # chksum
  506. fwrite(':' +
  507. asstr(hexlify(bin.tostring()).translate(table)) +
  508. '\n')
  509. elif keys == ['EIP']:
  510. # Start Linear Address Record
  511. bin[0] = 4 # reclen
  512. bin[1] = 0 # offset msb
  513. bin[2] = 0 # offset lsb
  514. bin[3] = 5 # rectyp
  515. eip = self.start_addr['EIP']
  516. bin[4] = (eip >> 24) & 0x0FF
  517. bin[5] = (eip >> 16) & 0x0FF
  518. bin[6] = (eip >> 8) & 0x0FF
  519. bin[7] = eip & 0x0FF
  520. bin[8] = (-sum(bin)) & 0x0FF # chksum
  521. fwrite(':' +
  522. asstr(hexlify(bin.tostring()).translate(table)) +
  523. '\n')
  524. else:
  525. if fclose:
  526. fclose()
  527. raise InvalidStartAddressValueError(start_addr=self.start_addr)
  528. # data
  529. addresses = self._buf.keys()
  530. addresses.sort()
  531. addr_len = len(addresses)
  532. if addr_len:
  533. minaddr = addresses[0]
  534. maxaddr = addresses[-1]
  535. if maxaddr > 65535:
  536. need_offset_record = True
  537. else:
  538. need_offset_record = False
  539. high_ofs = 0
  540. cur_addr = minaddr
  541. cur_ix = 0
  542. while cur_addr <= maxaddr:
  543. if need_offset_record:
  544. bin = array('B', asbytes('\0'*7))
  545. bin[0] = 2 # reclen
  546. bin[1] = 0 # offset msb
  547. bin[2] = 0 # offset lsb
  548. bin[3] = 4 # rectyp
  549. high_ofs = int(cur_addr>>16)
  550. b = divmod(high_ofs, 256)
  551. bin[4] = b[0] # msb of high_ofs
  552. bin[5] = b[1] # lsb of high_ofs
  553. bin[6] = (-sum(bin)) & 0x0FF # chksum
  554. fwrite(':' +
  555. asstr(hexlify(bin.tostring()).translate(table)) +
  556. '\n')
  557. while True:
  558. # produce one record
  559. low_addr = cur_addr & 0x0FFFF
  560. # chain_len off by 1
  561. chain_len = min(15, 65535-low_addr, maxaddr-cur_addr)
  562. # search continuous chain
  563. stop_addr = cur_addr + chain_len
  564. if chain_len:
  565. ix = bisect_right(addresses, stop_addr,
  566. cur_ix,
  567. min(cur_ix+chain_len+1, addr_len))
  568. chain_len = ix - cur_ix # real chain_len
  569. # there could be small holes in the chain
  570. # but we will catch them by try-except later
  571. # so for big continuous files we will work
  572. # at maximum possible speed
  573. else:
  574. chain_len = 1 # real chain_len
  575. bin = array('B', asbytes('\0'*(5+chain_len)))
  576. b = divmod(low_addr, 256)
  577. bin[1] = b[0] # msb of low_addr
  578. bin[2] = b[1] # lsb of low_addr
  579. bin[3] = 0 # rectype
  580. try: # if there is small holes we'll catch them
  581. for i in range(chain_len):
  582. bin[4+i] = self._buf[cur_addr+i]
  583. except KeyError:
  584. # we catch a hole so we should shrink the chain
  585. chain_len = i
  586. bin = bin[:5+i]
  587. bin[0] = chain_len
  588. bin[4+chain_len] = (-sum(bin)) & 0x0FF # chksum
  589. fwrite(':' +
  590. asstr(hexlify(bin.tostring()).translate(table)) +
  591. '\n')
  592. # adjust cur_addr/cur_ix
  593. cur_ix += chain_len
  594. if cur_ix < addr_len:
  595. cur_addr = addresses[cur_ix]
  596. else:
  597. cur_addr = maxaddr + 1
  598. break
  599. high_addr = int(cur_addr>>16)
  600. if high_addr > high_ofs:
  601. break
  602. # end-of-file record
  603. fwrite(":00000001FF\n")
  604. if fclose:
  605. fclose()
  606. def tofile(self, fobj, format):
  607. """Write data to hex or bin file. Preferred method over tobin or tohex.
  608. @param fobj file name or file-like object
  609. @param format file format ("hex" or "bin")
  610. """
  611. if format == 'hex':
  612. self.write_hex_file(fobj)
  613. elif format == 'bin':
  614. self.tobinfile(fobj)
  615. else:
  616. raise ValueError('format should be either "hex" or "bin";'
  617. ' got %r instead' % format)
  618. def gets(self, addr, length):
  619. """Get string of bytes from given address. If any entries are blank
  620. from addr through addr+length, a NotEnoughDataError exception will
  621. be raised. Padding is not used."""
  622. a = array('B', asbytes('\0'*length))
  623. try:
  624. for i in xrange(length):
  625. a[i] = self._buf[addr+i]
  626. except KeyError:
  627. raise NotEnoughDataError(address=addr, length=length)
  628. return asstr(a.tostring())
  629. def puts(self, addr, s):
  630. """Put string of bytes at given address. Will overwrite any previous
  631. entries.
  632. """
  633. a = array('B', asbytes(s))
  634. for i in xrange(len(a)):
  635. self._buf[addr+i] = a[i]
  636. def getsz(self, addr):
  637. """Get zero-terminated string from given address. Will raise
  638. NotEnoughDataError exception if a hole is encountered before a 0.
  639. """
  640. i = 0
  641. try:
  642. while True:
  643. if self._buf[addr+i] == 0:
  644. break
  645. i += 1
  646. except KeyError:
  647. raise NotEnoughDataError(msg=('Bad access at 0x%X: '
  648. 'not enough data to read zero-terminated string') % addr)
  649. return self.gets(addr, i)
  650. def putsz(self, addr, s):
  651. """Put string in object at addr and append terminating zero at end."""
  652. self.puts(addr, s)
  653. self._buf[addr+len(s)] = 0
  654. def dump(self, tofile=None):
  655. """Dump object content to specified file object or to stdout if None.
  656. Format is a hexdump with some header information at the beginning,
  657. addresses on the left, and data on right.
  658. @param tofile file-like object to dump to
  659. """
  660. if tofile is None:
  661. tofile = sys.stdout
  662. # start addr possibly
  663. if self.start_addr is not None:
  664. cs = self.start_addr.get('CS')
  665. ip = self.start_addr.get('IP')
  666. eip = self.start_addr.get('EIP')
  667. if eip is not None and cs is None and ip is None:
  668. tofile.write('EIP = 0x%08X\n' % eip)
  669. elif eip is None and cs is not None and ip is not None:
  670. tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip))
  671. else:
  672. tofile.write('start_addr = %r\n' % start_addr)
  673. # actual data
  674. addresses = self._buf.keys()
  675. if addresses:
  676. addresses.sort()
  677. minaddr = addresses[0]
  678. maxaddr = addresses[-1]
  679. startaddr = int(minaddr>>4)*16
  680. endaddr = int((maxaddr>>4)+1)*16
  681. maxdigits = max(len(str(endaddr)), 4)
  682. templa = '%%0%dX' % maxdigits
  683. range16 = range(16)
  684. for i in xrange(startaddr, endaddr, 16):
  685. tofile.write(templa % i)
  686. tofile.write(' ')
  687. s = []
  688. for j in range16:
  689. x = self._buf.get(i+j)
  690. if x is not None:
  691. tofile.write(' %02X' % x)
  692. if 32 <= x < 127: # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot
  693. s.append(chr(x))
  694. else:
  695. s.append('.')
  696. else:
  697. tofile.write(' --')
  698. s.append(' ')
  699. tofile.write(' |' + ''.join(s) + '|\n')
  700. def merge(self, other, overlap='error'):
  701. """Merge content of other IntelHex object into current object (self).
  702. @param other other IntelHex object.
  703. @param overlap action on overlap of data or starting addr:
  704. - error: raising OverlapError;
  705. - ignore: ignore other data and keep current data
  706. in overlapping region;
  707. - replace: replace data with other data
  708. in overlapping region.
  709. @raise TypeError if other is not instance of IntelHex
  710. @raise ValueError if other is the same object as self
  711. (it can't merge itself)
  712. @raise ValueError if overlap argument has incorrect value
  713. @raise AddressOverlapError on overlapped data
  714. """
  715. # check args
  716. if not isinstance(other, IntelHex):
  717. raise TypeError('other should be IntelHex object')
  718. if other is self:
  719. raise ValueError("Can't merge itself")
  720. if overlap not in ('error', 'ignore', 'replace'):
  721. raise ValueError("overlap argument should be either "
  722. "'error', 'ignore' or 'replace'")
  723. # merge data
  724. this_buf = self._buf
  725. other_buf = other._buf
  726. for i in other_buf:
  727. if i in this_buf:
  728. if overlap == 'error':
  729. raise AddressOverlapError(
  730. 'Data overlapped at address 0x%X' % i)
  731. elif overlap == 'ignore':
  732. continue
  733. this_buf[i] = other_buf[i]
  734. # merge start_addr
  735. if self.start_addr != other.start_addr:
  736. if self.start_addr is None: # set start addr from other
  737. self.start_addr = other.start_addr
  738. elif other.start_addr is None: # keep existing start addr
  739. pass
  740. else: # conflict
  741. if overlap == 'error':
  742. raise AddressOverlapError(
  743. 'Starting addresses are different')
  744. elif overlap == 'replace':
  745. self.start_addr = other.start_addr
  746. #/IntelHex
  747. class IntelHex16bit(IntelHex):
  748. """Access to data as 16-bit words. Intended to use with Microchip HEX files."""
  749. def __init__(self, source=None):
  750. """Construct class from HEX file
  751. or from instance of ordinary IntelHex class. If IntelHex object
  752. is passed as source, the original IntelHex object should not be used
  753. again because this class will alter it. This class leaves padding
  754. alone unless it was precisely 0xFF. In that instance it is sign
  755. extended to 0xFFFF.
  756. @param source file name of HEX file or file object
  757. or instance of ordinary IntelHex class.
  758. Will also accept dictionary from todict method.
  759. """
  760. if isinstance(source, IntelHex):
  761. # from ihex8
  762. self.padding = source.padding
  763. self.start_addr = source.start_addr
  764. # private members
  765. self._buf = source._buf
  766. self._offset = source._offset
  767. elif isinstance(source, dict):
  768. raise IntelHexError("IntelHex16bit does not support initialization from dictionary yet.\n"
  769. "Patches are welcome.")
  770. else:
  771. IntelHex.__init__(self, source)
  772. if self.padding == 0x0FF:
  773. self.padding = 0x0FFFF
  774. def __getitem__(self, addr16):
  775. """Get 16-bit word from address.
  776. Raise error if only one byte from the pair is set.
  777. We assume a Little Endian interpretation of the hex file.
  778. @param addr16 address of word (addr8 = 2 * addr16).
  779. @return word if bytes exists in HEX file, or self.padding
  780. if no data found.
  781. """
  782. addr1 = addr16 * 2
  783. addr2 = addr1 + 1
  784. byte1 = self._buf.get(addr1, None)
  785. byte2 = self._buf.get(addr2, None)
  786. if byte1 != None and byte2 != None:
  787. return byte1 | (byte2 << 8) # low endian
  788. if byte1 == None and byte2 == None:
  789. return self.padding
  790. raise BadAccess16bit(address=addr16)
  791. def __setitem__(self, addr16, word):
  792. """Sets the address at addr16 to word assuming Little Endian mode.
  793. """
  794. addr_byte = addr16 * 2
  795. b = divmod(word, 256)
  796. self._buf[addr_byte] = b[1]
  797. self._buf[addr_byte+1] = b[0]
  798. def minaddr(self):
  799. '''Get minimal address of HEX content in 16-bit mode.
  800. @return minimal address used in this object
  801. '''
  802. aa = self._buf.keys()
  803. if aa == []:
  804. return 0
  805. else:
  806. return min(aa)>>1
  807. def maxaddr(self):
  808. '''Get maximal address of HEX content in 16-bit mode.
  809. @return maximal address used in this object
  810. '''
  811. aa = self._buf.keys()
  812. if aa == []:
  813. return 0
  814. else:
  815. return max(aa)>>1
  816. def tobinarray(self, start=None, end=None, size=None):
  817. '''Convert this object to binary form as array (of 2-bytes word data).
  818. If start and end unspecified, they will be inferred from the data.
  819. @param start start address of output data.
  820. @param end end address of output data (inclusive).
  821. @param size size of the block (number of words),
  822. used with start or end parameter.
  823. @return array of unsigned short (uint16_t) data.
  824. '''
  825. bin = array('H')
  826. if self._buf == {} and None in (start, end):
  827. return bin
  828. if size is not None and size <= 0:
  829. raise ValueError("tobinarray: wrong value for size")
  830. start, end = self._get_start_end(start, end, size)
  831. for addr in xrange(start, end+1):
  832. bin.append(self[addr])
  833. return bin
  834. #/class IntelHex16bit
  835. def hex2bin(fin, fout, start=None, end=None, size=None, pad=None):
  836. """Hex-to-Bin convertor engine.
  837. @return 0 if all OK
  838. @param fin input hex file (filename or file-like object)
  839. @param fout output bin file (filename or file-like object)
  840. @param start start of address range (optional)
  841. @param end end of address range (inclusive; optional)
  842. @param size size of resulting file (in bytes) (optional)
  843. @param pad padding byte (optional)
  844. """
  845. try:
  846. h = IntelHex(fin)
  847. except HexReaderError, e:
  848. txt = "ERROR: bad HEX file: %s" % str(e)
  849. print(txt)
  850. return 1
  851. # start, end, size
  852. if size != None and size != 0:
  853. if end == None:
  854. if start == None:
  855. start = h.minaddr()
  856. end = start + size - 1
  857. else:
  858. if (end+1) >= size:
  859. start = end + 1 - size
  860. else:
  861. start = 0
  862. try:
  863. if pad is not None:
  864. # using .padding attribute rather than pad argument to function call
  865. h.padding = pad
  866. h.tobinfile(fout, start, end)
  867. except IOError, e:
  868. txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e))
  869. print(txt)
  870. return 1
  871. return 0
  872. #/def hex2bin
  873. def bin2hex(fin, fout, offset=0):
  874. """Simple bin-to-hex convertor.
  875. @return 0 if all OK
  876. @param fin input bin file (filename or file-like object)
  877. @param fout output hex file (filename or file-like object)
  878. @param offset starting address offset for loading bin
  879. """
  880. h = IntelHex()
  881. try:
  882. h.loadbin(fin, offset)
  883. except IOError, e:
  884. txt = 'ERROR: unable to load bin file:', str(e)
  885. print(txt)
  886. return 1
  887. try:
  888. h.tofile(fout, format='hex')
  889. except IOError, e:
  890. txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e))
  891. print(txt)
  892. return 1
  893. return 0
  894. #/def bin2hex
  895. def diff_dumps(ih1, ih2, tofile=None, name1="a", name2="b", n_context=3):
  896. """Diff 2 IntelHex objects and produce unified diff output for their
  897. hex dumps.
  898. @param ih1 first IntelHex object to compare
  899. @param ih2 second IntelHex object to compare
  900. @param tofile file-like object to write output
  901. @param name1 name of the first hex file to show in the diff header
  902. @param name2 name of the first hex file to show in the diff header
  903. @param n_context number of context lines in the unidiff output
  904. """
  905. def prepare_lines(ih):
  906. from cStringIO import StringIO
  907. sio = StringIO()
  908. ih.dump(sio)
  909. dump = sio.getvalue()
  910. lines = dump.splitlines()
  911. return lines
  912. a = prepare_lines(ih1)
  913. b = prepare_lines(ih2)
  914. import difflib
  915. result = list(difflib.unified_diff(a, b, fromfile=name1, tofile=name2, n=n_context, lineterm=''))
  916. if tofile is None:
  917. tofile = sys.stdout
  918. output = '\n'.join(result)+'\n'
  919. tofile.write(output)
  920. class Record(object):
  921. """Helper methods to build valid ihex records."""
  922. def _from_bytes(bytes):
  923. """Takes a list of bytes, computes the checksum, and outputs the entire
  924. record as a string. bytes should be the hex record without the colon
  925. or final checksum.
  926. @param bytes list of byte values so far to pack into record.
  927. @return String representation of one HEX record
  928. """
  929. assert len(bytes) >= 4
  930. # calculate checksum
  931. s = (-sum(bytes)) & 0x0FF
  932. bin = array('B', bytes + [s])
  933. return ':' + asstr(hexlify(bin.tostring())).upper()
  934. _from_bytes = staticmethod(_from_bytes)
  935. def data(offset, bytes):
  936. """Return Data record. This constructs the full record, including
  937. the length information, the record type (0x00), the
  938. checksum, and the offset.
  939. @param offset load offset of first byte.
  940. @param bytes list of byte values to pack into record.
  941. @return String representation of one HEX record
  942. """
  943. assert 0 <= offset < 65536
  944. assert 0 < len(bytes) < 256
  945. b = [len(bytes), (offset>>8)&0x0FF, offset&0x0FF, 0x00] + bytes
  946. return Record._from_bytes(b)
  947. data = staticmethod(data)
  948. def eof():
  949. """Return End of File record as a string.
  950. @return String representation of Intel Hex EOF record
  951. """
  952. return ':00000001FF'
  953. eof = staticmethod(eof)
  954. def extended_segment_address(usba):
  955. """Return Extended Segment Address Record.
  956. @param usba Upper Segment Base Address.
  957. @return String representation of Intel Hex USBA record.
  958. """
  959. b = [2, 0, 0, 0x02, (usba>>8)&0x0FF, usba&0x0FF]
  960. return Record._from_bytes(b)
  961. extended_segment_address = staticmethod(extended_segment_address)
  962. def start_segment_address(cs, ip):
  963. """Return Start Segment Address Record.
  964. @param cs 16-bit value for CS register.
  965. @param ip 16-bit value for IP register.
  966. @return String representation of Intel Hex SSA record.
  967. """
  968. b = [4, 0, 0, 0x03, (cs>>8)&0x0FF, cs&0x0FF,
  969. (ip>>8)&0x0FF, ip&0x0FF]
  970. return Record._from_bytes(b)
  971. start_segment_address = staticmethod(start_segment_address)
  972. def extended_linear_address(ulba):
  973. """Return Extended Linear Address Record.
  974. @param ulba Upper Linear Base Address.
  975. @return String representation of Intel Hex ELA record.
  976. """
  977. b = [2, 0, 0, 0x04, (ulba>>8)&0x0FF, ulba&0x0FF]
  978. return Record._from_bytes(b)
  979. extended_linear_address = staticmethod(extended_linear_address)
  980. def start_linear_address(eip):
  981. """Return Start Linear Address Record.
  982. @param eip 32-bit linear address for the EIP register.
  983. @return String representation of Intel Hex SLA record.
  984. """
  985. b = [4, 0, 0, 0x05, (eip>>24)&0x0FF, (eip>>16)&0x0FF,
  986. (eip>>8)&0x0FF, eip&0x0FF]
  987. return Record._from_bytes(b)
  988. start_linear_address = staticmethod(start_linear_address)
  989. class _BadFileNotation(Exception):
  990. """Special error class to use with _get_file_and_addr_range."""
  991. pass
  992. def _get_file_and_addr_range(s, _support_drive_letter=None):
  993. """Special method for hexmerge.py script to split file notation
  994. into 3 parts: (filename, start, end)
  995. @raise _BadFileNotation when string cannot be safely split.
  996. """
  997. if _support_drive_letter is None:
  998. _support_drive_letter = (os.name == 'nt')
  999. drive = ''
  1000. if _support_drive_letter:
  1001. if s[1:2] == ':' and s[0].upper() in ''.join([chr(i) for i in range(ord('A'), ord('Z')+1)]):
  1002. drive = s[:2]
  1003. s = s[2:]
  1004. parts = s.split(':')
  1005. n = len(parts)
  1006. if n == 1:
  1007. fname = parts[0]
  1008. fstart = None
  1009. fend = None
  1010. elif n != 3:
  1011. raise _BadFileNotation
  1012. else:
  1013. fname = parts[0]
  1014. def ascii_hex_to_int(ascii):
  1015. if ascii is not None:
  1016. try:
  1017. return int(ascii, 16)
  1018. except ValueError:
  1019. raise _BadFileNotation
  1020. return ascii
  1021. fstart = ascii_hex_to_int(parts[1] or None)
  1022. fend = ascii_hex_to_int(parts[2] or None)
  1023. return drive+fname, fstart, fend
  1024. ##
  1025. # IntelHex Errors Hierarchy:
  1026. #
  1027. # IntelHexError - basic error
  1028. # HexReaderError - general hex reader error
  1029. # AddressOverlapError - data for the same address overlap
  1030. # HexRecordError - hex record decoder base error
  1031. # RecordLengthError - record has invalid length
  1032. # RecordTypeError - record has invalid type (RECTYP)
  1033. # RecordChecksumError - record checksum mismatch
  1034. # EOFRecordError - invalid EOF record (type 01)
  1035. # ExtendedAddressRecordError - extended address record base error
  1036. # ExtendedSegmentAddressRecordError - invalid extended segment address record (type 02)
  1037. # ExtendedLinearAddressRecordError - invalid extended linear address record (type 04)
  1038. # StartAddressRecordError - start address record base error
  1039. # StartSegmentAddressRecordError - invalid start segment address record (type 03)
  1040. # StartLinearAddressRecordError - invalid start linear address record (type 05)
  1041. # DuplicateStartAddressRecordError - start address record appears twice
  1042. # InvalidStartAddressValueError - invalid value of start addr record
  1043. # _EndOfFile - it's not real error, used internally by hex reader as signal that EOF record found
  1044. # BadAccess16bit - not enough data to read 16 bit value (deprecated, see NotEnoughDataError)
  1045. # NotEnoughDataError - not enough data to read N contiguous bytes
  1046. # EmptyIntelHexError - requested operation cannot be performed with empty object
  1047. class IntelHexError(Exception):
  1048. '''Base Exception class for IntelHex module'''
  1049. _fmt = 'IntelHex base error' #: format string
  1050. def __init__(self, msg=None, **kw):
  1051. """Initialize the Exception with the given message.
  1052. """
  1053. self.msg = msg
  1054. for key, value in kw.items():
  1055. setattr(self, key, value)
  1056. def __str__(self):
  1057. """Return the message in this Exception."""
  1058. if self.msg:
  1059. return self.msg
  1060. try:
  1061. return self._fmt % self.__dict__
  1062. except (NameError, ValueError, KeyError), e:
  1063. return 'Unprintable exception %s: %s' \
  1064. % (repr(e), str(e))
  1065. class _EndOfFile(IntelHexError):
  1066. """Used for internal needs only."""
  1067. _fmt = 'EOF record reached -- signal to stop read file'
  1068. class HexReaderError(IntelHexError):
  1069. _fmt = 'Hex reader base error'
  1070. class AddressOverlapError(HexReaderError):
  1071. _fmt = 'Hex file has data overlap at address 0x%(address)X on line %(line)d'
  1072. # class NotAHexFileError was removed in trunk.revno.54 because it's not used
  1073. class HexRecordError(HexReaderError):
  1074. _fmt = 'Hex file contains invalid record at line %(line)d'
  1075. class RecordLengthError(HexRecordError):
  1076. _fmt = 'Record at line %(line)d has invalid length'
  1077. class RecordTypeError(HexRecordError):
  1078. _fmt = 'Record at line %(line)d has invalid record type'
  1079. class RecordChecksumError(HexRecordError):
  1080. _fmt = 'Record at line %(line)d has invalid checksum'
  1081. class EOFRecordError(HexRecordError):
  1082. _fmt = 'File has invalid End-of-File record'
  1083. class ExtendedAddressRecordError(HexRecordError):
  1084. _fmt = 'Base class for extended address exceptions'
  1085. class ExtendedSegmentAddressRecordError(ExtendedAddressRecordError):
  1086. _fmt = 'Invalid Extended Segment Address Record at line %(line)d'
  1087. class ExtendedLinearAddressRecordError(ExtendedAddressRecordError):
  1088. _fmt = 'Invalid Extended Linear Address Record at line %(line)d'
  1089. class StartAddressRecordError(HexRecordError):
  1090. _fmt = 'Base class for start address exceptions'
  1091. class StartSegmentAddressRecordError(StartAddressRecordError):
  1092. _fmt = 'Invalid Start Segment Address Record at line %(line)d'
  1093. class StartLinearAddressRecordError(StartAddressRecordError):
  1094. _fmt = 'Invalid Start Linear Address Record at line %(line)d'
  1095. class DuplicateStartAddressRecordError(StartAddressRecordError):
  1096. _fmt = 'Start Address Record appears twice at line %(line)d'
  1097. class InvalidStartAddressValueError(StartAddressRecordError):
  1098. _fmt = 'Invalid start address value: %(start_addr)s'
  1099. class NotEnoughDataError(IntelHexError):
  1100. _fmt = ('Bad access at 0x%(address)X: '
  1101. 'not enough data to read %(length)d contiguous bytes')
  1102. class BadAccess16bit(NotEnoughDataError):
  1103. _fmt = 'Bad access at 0x%(address)X: not enough data to read 16 bit value'
  1104. class EmptyIntelHexError(IntelHexError):
  1105. _fmt = "Requested operation cannot be executed with empty object"