@@ -544,7 +544,7 @@ def _get_eol_textfile(eolstyle, platform):
544544 raise ValueError ("wrong eolstyle %s" % repr (eolstyle ))
545545 _get_eol_textfile = staticmethod (_get_eol_textfile )
546546
547- def write_hex_file (self , f , write_start_addr = True , eolstyle = 'native' , byte_count = 16 ):
547+ def write_hex_file (self , f , write_start_addr = True , eolstyle = 'native' , byte_count = 16 , ext_addr_mode = 'linear' ):
548548 """Write data to file f in HEX format.
549549
550550 @param f filename or file-like object for writing
@@ -556,20 +556,55 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count
556556 for output file on different platforms.
557557 Supported eol styles: 'native', 'CRLF'.
558558 @param byte_count number of bytes in the data field
559+ @param ext_addr_mode used this to decide which record type to
560+ to write for Extended Address records:
561+ Linear (32-bit addressing) or Segment (20-bit addressing)
562+ or without Extended Address records (16-bit addressing)
563+ Supported modes: 'linear', 'segment', 'none', 'auto'.
559564 """
560565 if byte_count > 255 or byte_count < 1 :
561566 raise ValueError ("wrong byte_count value: %s" % byte_count )
562- fwrite = getattr (f , "write" , None )
563- if fwrite :
564- fobj = f
565- fclose = None
566- else :
567- fobj = open (f , 'w' )
568- fwrite = fobj .write
569- fclose = fobj .close
570567
571568 eol = IntelHex ._get_eol_textfile (eolstyle , sys .platform )
572569
570+ addresses = dict_keys (self ._buf )
571+ addresses .sort ()
572+ addr_len = len (addresses )
573+ minaddr = addresses [0 ] if addr_len else 0
574+ maxaddr = addresses [- 1 ] if addr_len else 0
575+
576+ # make parameter case-insensitive
577+ ext_addr_mode = ext_addr_mode .lower ()
578+ # resolve extended address type
579+ if ext_addr_mode == 'linear' :
580+ # enforces Extended Linear Address record type (default)
581+ extaddr_rectyp = 4
582+ elif ext_addr_mode == 'segment' :
583+ # enforces Extended Segment Address record type
584+ extaddr_rectyp = 2
585+ elif ext_addr_mode == 'none' :
586+ # enforces no Extended Address records format
587+ extaddr_rectyp = 0
588+ elif ext_addr_mode == 'auto' :
589+ # Extended Address record type is resolved by Max Address
590+ if maxaddr > 0x0FFFFF :
591+ extaddr_rectyp = 4
592+ else : # 1MB sapcing is max for Segement
593+ extaddr_rectyp = 2
594+ else :
595+ raise ValueError ('ext_addr_mode should be one of:'
596+ ' "linear", "segment", "none", "auto";'
597+ ' got %r instead' % ext_addr_mode )
598+
599+ # check max address with resolved format
600+ if addr_len :
601+ if extaddr_rectyp == 4 and maxaddr > 0xFFFFFFFF :
602+ raise LinearSpacingOverflowError (overflwd_len = (maxaddr - 0xFFFFFFFF ))
603+ elif extaddr_rectyp == 2 and maxaddr > 0x0FFFFF :
604+ raise SegmentSpacingOverflowError (overflwd_len = (maxaddr - 0x0FFFFF ))
605+ elif extaddr_rectyp == 0 and maxaddr > 0xFFFF :
606+ raise BasicSpacingOverflowError (overflwd_len = (maxaddr - 0xFFFF ))
607+
573608 # Translation table for uppercasing hex ascii string.
574609 # timeit shows that using hexstr.translate(table)
575610 # is faster than hexstr.upper():
@@ -581,6 +616,15 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count
581616 # Python 2
582617 table = '' .join (chr (i ).upper () for i in range_g (256 ))
583618
619+ fwrite = getattr (f , "write" , None )
620+ if fwrite :
621+ fobj = f
622+ fclose = None
623+ else :
624+ fobj = open (f , 'w' )
625+ fwrite = fobj .write
626+ fclose = fobj .close
627+
584628 # start address record if any
585629 if self .start_addr and write_start_addr :
586630 keys = dict_keys (self .start_addr )
@@ -623,19 +667,13 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count
623667 raise InvalidStartAddressValueError (start_addr = self .start_addr )
624668
625669 # data
626- addresses = dict_keys (self ._buf )
627- addresses .sort ()
628- addr_len = len (addresses )
629670 if addr_len :
630- minaddr = addresses [0 ]
631- maxaddr = addresses [- 1 ]
632-
633671 if maxaddr > 65535 :
634672 need_offset_record = True
635673 else :
636674 need_offset_record = False
637- high_ofs = 0
638675
676+ high_ofs = 0
639677 cur_addr = minaddr
640678 cur_ix = 0
641679
@@ -645,9 +683,15 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count
645683 bin [0 ] = 2 # reclen
646684 bin [1 ] = 0 # offset msb
647685 bin [2 ] = 0 # offset lsb
648- bin [3 ] = 4 # rectyp
649- high_ofs = int (cur_addr >> 16 )
686+ bin [3 ] = extaddr_rectyp # rectyp
687+
688+ if extaddr_rectyp == 4 :
689+ high_ofs = int (cur_addr >> 16 )
690+ else : # extaddr_rectyp == 2:
691+ # 0x00X0000 => 0xX000
692+ high_ofs = int ((cur_addr & 0x00F0000 ) >> 4 )
650693 b = divmod (high_ofs , 256 )
694+
651695 bin [4 ] = b [0 ] # msb of high_ofs
652696 bin [5 ] = b [1 ] # lsb of high_ofs
653697 bin [6 ] = (- sum (bin )) & 0x0FF # chksum
@@ -1094,7 +1138,7 @@ def bin2hex(fin, fout, offset=0):
10941138 return 1
10951139
10961140 try :
1097- h .tofile (fout , format = 'hex' )
1141+ h .write_hex_file (fout )
10981142 except IOError :
10991143 e = sys .exc_info ()[1 ] # current exception
11001144 txt = "ERROR: Could not write to file: %s: %s" % (fout , str (e ))
@@ -1279,6 +1323,10 @@ def ascii_hex_to_int(ascii):
12791323# BadAccess16bit - not enough data to read 16 bit value (deprecated, see NotEnoughDataError)
12801324# NotEnoughDataError - not enough data to read N contiguous bytes
12811325# EmptyIntelHexError - requested operation cannot be performed with empty object
1326+ # OverflowError - data overflow general error
1327+ # LinearSpacingOverflowError - 32-bit address spacing data overflow
1328+ # SegmentSpacingOverflowError - 20-bit address spacing data overflow
1329+ # BasicSpacingOverflowError - 16-bit address spacing data overflow
12821330
12831331class IntelHexError (Exception ):
12841332 '''Base Exception class for IntelHex module'''
@@ -1368,3 +1416,16 @@ class BadAccess16bit(NotEnoughDataError):
13681416
13691417class EmptyIntelHexError (IntelHexError ):
13701418 _fmt = "Requested operation cannot be executed with empty object"
1419+
1420+
1421+ class OverflowError (IntelHexError ):
1422+ _fmt = 'Base class for overflowed data'
1423+
1424+ class LinearSpacingOverflowError (OverflowError ):
1425+ _fmt = 'Data overflow Linear (32-bit) address spacing. Overflowed data: %(overflwd_len)d bytes'
1426+
1427+ class SegmentSpacingOverflowError (OverflowError ):
1428+ _fmt = 'Data overflow Segment (20-bit) address spacing. Overflowed data: %(overflwd_len)d bytes'
1429+
1430+ class BasicSpacingOverflowError (OverflowError ):
1431+ _fmt = 'Data overflow 16-bit address spacing. Overflowed data: %(overflwd_len)d bytes'
0 commit comments