1 # Copyright (c) 2005-2009, Alexander Belchenko
4 # Redistribution and use in source and binary forms,
5 # with or without modification, are permitted provided
6 # that the following conditions are met:
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.
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.
34 '''Intel HEX file format reader and converter.
36 @author Alexander Belchenko (bialix AT ukr net)
41 __docformat__ = "javadoc"
44 from array import array
45 from binascii import hexlify, unhexlify
46 from bisect import bisect_right
50 class IntelHex(object):
51 ''' Intel HEX file reader. '''
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.
57 @param source source for initialization
58 (file name of HEX file, file object, addr dict or
59 other IntelHex object)
64 self.start_addr = None
70 if source is not None:
71 if isinstance(source, basestring) or getattr(source, "read", None):
74 elif isinstance(source, dict):
76 elif isinstance(source, IntelHex):
77 self.padding = source.padding
79 self.start_addr = source.start_addr.copy()
80 self._buf = source._buf.copy()
82 raise ValueError("source: bad initializer type")
84 def _decode_record(self, s, line=0):
85 '''Decode one record of HEX file.
87 @param s line with HEX record.
88 @param line line number (for error messages).
90 @raise EndOfFile if EOF record encountered.
98 bin = array('B', unhexlify(s[1:]))
100 # this might be raised by unhexlify when odd hexascii digits
101 raise HexRecordError(line=line)
104 raise HexRecordError(line=line)
106 raise HexRecordError(line=line)
108 record_length = bin[0]
109 if length != (5 + record_length):
110 raise RecordLengthError(line=line)
112 addr = bin[1]*256 + bin[2]
115 if not (0 <= record_type <= 5):
116 raise RecordTypeError(line=line)
121 raise RecordChecksumError(line=line)
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)
134 elif record_type == 1:
136 if record_length != 0:
137 raise EOFRecordError(line=line)
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
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
152 elif record_type == 3:
153 # Start Segment Address Record
154 if record_length != 4 or addr != 0:
155 raise StartSegmentAddressRecordError(line=line)
157 raise DuplicateStartAddressRecordError(line=line)
158 self.start_addr = {'CS': bin[4]*256 + bin[5],
159 'IP': bin[6]*256 + bin[7],
162 elif record_type == 5:
163 # Start Linear Address Record
164 if record_length != 4 or addr != 0:
165 raise StartLinearAddressRecordError(line=line)
167 raise DuplicateStartAddressRecordError(line=line)
168 self.start_addr = {'EIP': (bin[4]*16777216 +
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.
179 @param fobj file name or file-like object
181 if getattr(fobj, "read", None) is None:
182 fobj = file(fobj, "r")
191 decode = self._decode_record
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.
207 @param fobj file name or file-like object
208 @param offset starting address offset
210 fread = getattr(fobj, "read", None)
219 for b in array('B', fread()):
220 self._buf[offset] = b
226 def loadfile(self, fobj, format):
227 """Load data file into internal buffer. Preferred wrapper over
230 @param fobj file name or file-like object
231 @param format file format ("hex" or "bin")
235 elif format == "bin":
238 raise ValueError('format should be either "hex" or "bin";'
239 ' got %r instead' % format)
241 # alias (to be consistent with method tofile)
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.
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.
256 start_addr = s.get('start_addr')
257 if s.has_key('start_addr'):
260 if type(k) not in (int, long) or k < 0:
261 raise ValueError('Source dictionary should have only int keys')
263 if start_addr is not None:
264 self.start_addr = start_addr
266 def _get_start_end(self, start=None, end=None):
267 """Return default values for start and end if they are None
270 start = min(self._buf.keys())
272 end = max(self._buf.keys())
274 start, end = end, start
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.
282 @param pad fill empty spaces with this value
283 (if None used self.padding).
284 @return array of unsigned char data.
294 start, end = self._get_start_end(start, end)
296 for i in xrange(start, end+1):
297 bin.append(self._buf.get(i, pad))
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.
305 @param pad fill empty spaces with this value
306 (if None used self.padding).
307 @return string of binary data.
309 return self.tobinarray(start, end, pad).tostring()
311 def tobinfile(self, fobj, start=None, end=None, pad=0xFF):
312 '''Convert to binary and write to file.
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.
317 @param pad fill empty spaces with this value
318 (if None used self.padding).
320 if getattr(fobj, "write", None) is None:
321 fobj = file(fobj, "wb")
326 fobj.write(self.tobinstr(start, end, pad))
332 '''Convert to python dictionary.
334 @return dict suitable for initializing another IntelHex object.
339 r['start_addr'] = self.start_addr
343 '''Returns all used addresses in sorted order.
344 @return list of occupied data addresses in sorted order.
346 aa = self._buf.keys()
351 '''Get minimal address of HEX content.
352 @return minimal address or None if no data
354 aa = self._buf.keys()
361 '''Get maximal address of HEX content.
362 @return maximal address or None if no data
364 aa = self._buf.keys()
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
379 raise TypeError('Address should be >= 0.')
380 return self._buf.get(addr, self.padding)
382 addresses = self._buf.keys()
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):
395 raise TypeError('Address has unsupported type: %s' % t)
397 def __setitem__(self, addr, byte):
398 """Set byte at address."""
402 raise TypeError('Address should be >= 0.')
403 self._buf[addr] = byte
405 addresses = self._buf.keys()
406 if not isinstance(byte, (list, tuple)):
407 raise ValueError('Slice operation expects sequence of bytes')
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 '
416 elif (start, stop) == (None, None):
417 raise TypeError('Unsupported address range')
419 start = stop - len(byte)
421 stop = start + len(byte)
423 raise TypeError('start address cannot be negative')
425 raise TypeError('stop address cannot be negative')
427 for i in xrange(start, stop, step):
428 self._buf[i] = byte[j]
431 raise TypeError('Address has unsupported type: %s' % t)
433 def __delitem__(self, addr):
434 """Delete byte at address."""
438 raise TypeError('Address should be >= 0.')
441 addresses = self._buf.keys()
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):
452 raise TypeError('Address has unsupported type: %s' % t)
455 """Return count of bytes with real values."""
456 return len(self._buf.keys())
458 def write_hex_file(self, f, write_start_addr=True):
459 """Write data to file f in HEX format.
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.
467 fwrite = getattr(f, "write", None)
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))
482 # start address record if any
483 if self.start_addr and write_start_addr:
484 keys = self.start_addr.keys()
486 bin = array('B', '\0'*9)
487 if keys == ['CS','IP']:
488 # Start Segment Address Record
490 bin[1] = 0 # offset msb
491 bin[2] = 0 # offset lsb
493 cs = self.start_addr['CS']
494 bin[4] = (cs >> 8) & 0x0FF
496 ip = self.start_addr['IP']
497 bin[6] = (ip >> 8) & 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
504 bin[1] = 0 # offset msb
505 bin[2] = 0 # offset lsb
507 eip = self.start_addr['EIP']
508 bin[4] = (eip >> 24) & 0x0FF
509 bin[5] = (eip >> 16) & 0x0FF
510 bin[6] = (eip >> 8) & 0x0FF
512 bin[8] = (-sum(bin)) & 0x0FF # chksum
513 fwrite(':' + hexlify(bin.tostring()).translate(table) + '\n')
517 raise InvalidStartAddressValueError(start_addr=self.start_addr)
520 addresses = self._buf.keys()
522 addr_len = len(addresses)
524 minaddr = addresses[0]
525 maxaddr = addresses[-1]
528 need_offset_record = True
530 need_offset_record = False
536 while cur_addr <= maxaddr:
537 if need_offset_record:
538 bin = array('B', '\0'*7)
540 bin[1] = 0 # offset msb
541 bin[2] = 0 # offset lsb
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')
552 low_addr = cur_addr & 0x0FFFF
554 chain_len = min(15, 65535-low_addr, maxaddr-cur_addr)
556 # search continuous chain
557 stop_addr = cur_addr + chain_len
559 ix = bisect_right(addresses, stop_addr,
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
568 chain_len = 1 # real chain_len
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
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]
579 # we catch a hole so we should shrink the chain
583 bin[4+chain_len] = (-sum(bin)) & 0x0FF # chksum
584 fwrite(':' + hexlify(bin.tostring()).translate(table) + '\n')
586 # adjust cur_addr/cur_ix
588 if cur_ix < addr_len:
589 cur_addr = addresses[cur_ix]
591 cur_addr = maxaddr + 1
593 high_addr = int(cur_addr/65536)
594 if high_addr > high_ofs:
598 fwrite(":00000001FF\n")
602 def tofile(self, fobj, format):
603 """Write data to hex or bin file. Preferred method over tobin or tohex.
605 @param fobj file name or file-like object
606 @param format file format ("hex" or "bin")
609 self.write_hex_file(fobj)
610 elif format == 'bin':
613 raise ValueError('format should be either "hex" or "bin";'
614 ' got %r instead' % format)
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)
622 for i in xrange(length):
623 a[i] = self._buf[addr+i]
625 raise NotEnoughDataError(address=addr, length=length)
628 def puts(self, addr, s):
629 """Put string of bytes at given address. Will overwrite any previous
633 for i in xrange(len(s)):
634 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.
643 if self._buf[addr+i] == 0:
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)
651 def putsz(self, addr, s):
652 """Put string in object at addr and append terminating zero at end."""
654 self._buf[addr+len(s)] = 0
656 def dump(self, tofile=None):
657 """Dump object content to specified file or to stdout if None. Format
658 is a hexdump with some header information at the beginning, addresses
659 on the left, and data on right.
661 @param tofile file-like object to dump to
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))
677 tofile.write('start_addr = %r\n' % start_addr)
679 addresses = self._buf.keys()
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
689 for i in xrange(startaddr, endaddr, 16):
690 tofile.write(templa % i)
694 x = self._buf.get(i+j)
696 tofile.write(' %02X' % x)
704 tofile.write(' |' + ''.join(s) + '|\n')
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.
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
722 if not isinstance(other, IntelHex):
723 raise TypeError('other should be IntelHex object')
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'")
731 other_buf = other._buf
734 if overlap == 'error':
735 raise AddressOverlapError(
736 'Data overlapped at address 0x%X' % i)
737 elif overlap == 'ignore':
739 this_buf[i] = other_buf[i]
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
747 if overlap == 'error':
748 raise AddressOverlapError(
749 'Starting addresses are different')
750 elif overlap == 'replace':
751 this.start_addr = other.start_addr
755 class IntelHex16bit(IntelHex):
756 """Access to data as 16-bit words."""
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
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.
770 if isinstance(source, IntelHex):
772 self.padding = source.padding
774 self._buf = source._buf
775 self._offset = source._offset
777 IntelHex.__init__(self, source)
779 if self.padding == 0x0FF:
780 self.padding = 0x0FFFF
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.
787 @param addr16 address of word (addr8 = 2 * addr16).
788 @return word if bytes exists in HEX file, or self.padding
793 byte1 = self._buf.get(addr1, None)
794 byte2 = self._buf.get(addr2, None)
796 if byte1 != None and byte2 != None:
797 return byte1 | (byte2 << 8) # low endian
799 if byte1 == None and byte2 == None:
802 raise BadAccess16bit(address=addr16)
804 def __setitem__(self, addr16, word):
805 """Sets the address at addr16 to word assuming Little Endian mode.
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]
813 '''Get minimal address of HEX content in 16-bit mode.
815 @return minimal address used in this object
817 aa = self._buf.keys()
824 '''Get maximal address of HEX content in 16-bit mode.
826 @return maximal address used in this object
828 aa = self._buf.keys()
834 #/class IntelHex16bit
837 def hex2bin(fin, fout, start=None, end=None, size=None, pad=0xFF):
838 """Hex-to-Bin convertor engine.
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 (optional)
845 @param size size of resulting file (in bytes) (optional)
846 @param pad padding byte (optional)
850 except HexReaderError, e:
851 print "ERROR: bad HEX file: %s" % str(e)
855 if size != None and size != 0:
859 end = start + size - 1
862 start = end + 1 - size
867 h.tobinfile(fout, start, end, pad)
869 print "ERROR: Could not write to file: %s: %s" % (fout, str(e))
876 def bin2hex(fin, fout, offset=0):
877 """Simple bin-to-hex convertor.
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
886 h.loadbin(fin, offset)
888 print 'ERROR: unable to load bin file:', str(e)
892 h.tofile(fout, format='hex')
894 print "ERROR: Could not write to file: %s: %s" % (fout, str(e))
901 class Record(object):
902 """Helper methods to build valid ihex records."""
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
909 @param bytes list of byte values so far to pack into record.
910 @return String representation of one HEX record
912 assert len(bytes) >= 4
914 s = (-sum(bytes)) & 0x0FF
915 bin = array('B', bytes + [s])
916 return ':' + hexlify(bin.tostring()).upper()
917 _from_bytes = staticmethod(_from_bytes)
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.
924 @param offset load offset of first byte.
925 @param bytes list of byte values to pack into record.
927 @return String representation of one HEX record
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)
936 """Return End of File record as a string.
937 @return String representation of Intel Hex EOF record
940 eof = staticmethod(eof)
942 def extended_segment_address(usba):
943 """Return Extended Segment Address Record.
944 @param usba Upper Segment Base Address.
946 @return String representation of Intel Hex USBA record.
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)
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.
957 @return String representation of Intel Hex SSA record.
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)
964 def extended_linear_address(ulba):
965 """Return Extended Linear Address Record.
966 @param ulba Upper Linear Base Address.
968 @return String representation of Intel Hex ELA record.
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)
974 def start_linear_address(eip):
975 """Return Start Linear Address Record.
976 @param eip 32-bit linear address for the EIP register.
978 @return String representation of Intel Hex SLA record.
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)
986 class _BadFileNotation(Exception):
987 """Special error class to use with _get_file_and_addr_range."""
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)
994 @raise _BadFileNotation when string cannot be safely split.
996 if _support_drive_letter is None:
997 _support_drive_letter = (os.name == 'nt')
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)]):
1003 parts = s.split(':')
1010 raise _BadFileNotation
1013 def ascii_hex_to_int(ascii):
1014 if ascii is not None:
1016 return int(ascii, 16)
1018 raise _BadFileNotation
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
1026 # IntelHex Errors Hierarchy:
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
1048 class IntelHexError(Exception):
1049 '''Base Exception class for IntelHex module'''
1051 _fmt = 'IntelHex base error' #: format string
1053 def __init__(self, msg=None, **kw):
1054 """Initialize the Exception with the given message.
1057 for key, value in kw.items():
1058 setattr(self, key, value)
1061 """Return the message in this Exception."""
1065 return self._fmt % self.__dict__
1066 except (NameError, ValueError, KeyError), e:
1067 return 'Unprintable exception %s: %s' \
1068 % (self.__class__.__name__, str(e))
1070 class _EndOfFile(IntelHexError):
1071 """Used for internal needs only."""
1072 _fmt = 'EOF record reached -- signal to stop read file'
1074 class HexReaderError(IntelHexError):
1075 _fmt = 'Hex reader base error'
1077 class AddressOverlapError(HexReaderError):
1078 _fmt = 'Hex file has data overlap at address 0x%(address)X on line %(line)d'
1080 # class NotAHexFileError was removed in trunk.revno.54 because it's not used
1083 class HexRecordError(HexReaderError):
1084 _fmt = 'Hex file contains invalid record at line %(line)d'
1087 class RecordLengthError(HexRecordError):
1088 _fmt = 'Record at line %(line)d has invalid length'
1090 class RecordTypeError(HexRecordError):
1091 _fmt = 'Record at line %(line)d has invalid record type'
1093 class RecordChecksumError(HexRecordError):
1094 _fmt = 'Record at line %(line)d has invalid checksum'
1096 class EOFRecordError(HexRecordError):
1097 _fmt = 'File has invalid End-of-File record'
1100 class ExtendedAddressRecordError(HexRecordError):
1101 _fmt = 'Base class for extended address exceptions'
1103 class ExtendedSegmentAddressRecordError(ExtendedAddressRecordError):
1104 _fmt = 'Invalid Extended Segment Address Record at line %(line)d'
1106 class ExtendedLinearAddressRecordError(ExtendedAddressRecordError):
1107 _fmt = 'Invalid Extended Linear Address Record at line %(line)d'
1110 class StartAddressRecordError(HexRecordError):
1111 _fmt = 'Base class for start address exceptions'
1113 class StartSegmentAddressRecordError(StartAddressRecordError):
1114 _fmt = 'Invalid Start Segment Address Record at line %(line)d'
1116 class StartLinearAddressRecordError(StartAddressRecordError):
1117 _fmt = 'Invalid Start Linear Address Record at line %(line)d'
1119 class DuplicateStartAddressRecordError(StartAddressRecordError):
1120 _fmt = 'Start Address Record appears twice at line %(line)d'
1122 class InvalidStartAddressValueError(StartAddressRecordError):
1123 _fmt = 'Invalid start address value: %(start_addr)s'
1126 class NotEnoughDataError(IntelHexError):
1127 _fmt = ('Bad access at 0x%(address)X: '
1128 'not enough data to read %(length)d contiguous bytes')
1130 class BadAccess16bit(NotEnoughDataError):
1131 _fmt = 'Bad access at 0x%(address)X: not enough data to read 16 bit value'