1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
51 ''' Intel HEX file reader. '''
52
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
62 self.padding = 0x0FF
63
64 self.start_addr = None
65
66
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
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
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
95
96 if s[0] == ':':
97 try:
98 bin = array('B', unhexlify(s[1:]))
99 except TypeError:
100
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
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
131
132
133
134 elif record_type == 1:
135
136 if record_length != 0:
137 raise EOFRecordError(line=line)
138 raise _EndOfFile
139
140 elif record_type == 2:
141
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
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
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
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
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
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
242 fromfile = loadfile
243
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
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
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
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
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
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
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
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
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
475 """Return count of bytes with real values."""
476 return len(self._buf.keys())
477
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
497
498
499
500 table = ''.join(chr(i).upper() for i in range(256))
501
502
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
509 bin[0] = 4
510 bin[1] = 0
511 bin[2] = 0
512 bin[3] = 3
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
520 fwrite(':' + hexlify(bin.tostring()).translate(table) + '\n')
521 elif keys == ['EIP']:
522
523 bin[0] = 4
524 bin[1] = 0
525 bin[2] = 0
526 bin[3] = 5
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
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
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
560 bin[1] = 0
561 bin[2] = 0
562 bin[3] = 4
563 high_ofs = int(cur_addr/65536)
564 bytes = divmod(high_ofs, 256)
565 bin[4] = bytes[0]
566 bin[5] = bytes[1]
567 bin[6] = (-sum(bin)) & 0x0FF
568 fwrite(':' + hexlify(bin.tostring()).translate(table) + '\n')
569
570 while True:
571
572 low_addr = cur_addr & 0x0FFFF
573
574 chain_len = min(15, 65535-low_addr, maxaddr-cur_addr)
575
576
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
583
584
585
586
587 else:
588 chain_len = 1
589
590 bin = array('B', '\0'*(5+chain_len))
591 bytes = divmod(low_addr, 256)
592 bin[1] = bytes[0]
593 bin[2] = bytes[1]
594 bin[3] = 0
595 try:
596 for i in range(chain_len):
597 bin[4+i] = self._buf[cur_addr+i]
598 except KeyError:
599
600 chain_len = i
601 bin = bin[:5+i]
602 bin[0] = chain_len
603 bin[4+chain_len] = (-sum(bin)) & 0x0FF
604 fwrite(':' + hexlify(bin.tostring()).translate(table) + '\n')
605
606
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
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
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
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
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:
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
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
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
761 if this.start_addr != other.start_addr:
762 if this.start_addr is None:
763 this.start_addr = other.start_addr
764 elif other.start_addr is None:
765 pass
766 else:
767 if overlap == 'error':
768 raise AddressOverlapError(
769 'Starting addresses are different')
770 elif overlap == 'replace':
771 this.start_addr = other.start_addr
772
773
774
776 """Access to data as 16-bit words."""
777
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
792 self.padding = source.padding
793
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
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)
818
819 if byte1 == None and byte2 == None:
820 return self.padding
821
822 raise BadAccess16bit(address=addr16)
823
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
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
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
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
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
894
895
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
919
920
922 """Helper methods to build valid ihex records."""
923
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
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
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
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
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
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
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
1007 """Special error class to use with _get_file_and_addr_range."""
1008 pass
1009
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
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1069 '''Base Exception class for IntelHex module'''
1070
1071 _fmt = 'IntelHex base error'
1072
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
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
1091 """Used for internal needs only."""
1092 _fmt = 'EOF record reached -- signal to stop read file'
1093
1095 _fmt = 'Hex reader base error'
1096
1098 _fmt = 'Hex file has data overlap at address 0x%(address)X on line %(line)d'
1099
1100
1101
1102
1104 _fmt = 'Hex file contains invalid record at line %(line)d'
1105
1106
1108 _fmt = 'Record at line %(line)d has invalid length'
1109
1111 _fmt = 'Record at line %(line)d has invalid record type'
1112
1114 _fmt = 'Record at line %(line)d has invalid checksum'
1115
1117 _fmt = 'File has invalid End-of-File record'
1118
1119
1121 _fmt = 'Base class for extended address exceptions'
1122
1124 _fmt = 'Invalid Extended Segment Address Record at line %(line)d'
1125
1127 _fmt = 'Invalid Extended Linear Address Record at line %(line)d'
1128
1129
1131 _fmt = 'Base class for start address exceptions'
1132
1134 _fmt = 'Invalid Start Segment Address Record at line %(line)d'
1135
1137 _fmt = 'Invalid Start Linear Address Record at line %(line)d'
1138
1140 _fmt = 'Start Address Record appears twice at line %(line)d'
1141
1143 _fmt = 'Invalid start address value: %(start_addr)s'
1144
1145
1147 _fmt = ('Bad access at 0x%(address)X: '
1148 'not enough data to read %(length)d contiguous bytes')
1149
1151 _fmt = 'Bad access at 0x%(address)X: not enough data to read 16 bit value'
1152