Package intelhex
[hide private]
[frames] | no frames]

Source Code for Package intelhex

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