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

Source Code for Package intelhex

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