123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643 |
- #!/usr/bin/python
- # Copyright (c) 2005-2007, Alexander Belchenko
- # All rights reserved.
- #
- # Redistribution and use in source and binary forms,
- # with or without modification, are permitted provided
- # that the following conditions are met:
- #
- # * Redistributions of source code must retain
- # the above copyright notice, this list of conditions
- # and the following disclaimer.
- # * Redistributions in binary form must reproduce
- # the above copyright notice, this list of conditions
- # and the following disclaimer in the documentation
- # and/or other materials provided with the distribution.
- # * Neither the name of the <Alexander Belchenko>
- # nor the names of its contributors may be used to endorse
- # or promote products derived from this software
- # without specific prior written permission.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
- # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
- # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- '''Intel HEX file format reader and converter.
- This script also may be used as hex2bin convertor utility.
- @author Alexander Belchenko (bialix AT ukr net)
- @version 0.8.6
- @date 2007/04/26
- '''
- __docformat__ = "javadoc"
- from array import array
- from binascii import hexlify, unhexlify
- class IntelHex:
- ''' Intel HEX file reader. '''
- def __init__(self, fname):
- ''' Constructor.
- @param fname file name of HEX file or file object.
- '''
- #public members
- self.Error = None
- self.AddrOverlap = None
- self.padding = 0x0FF
- # Start Address
- self.start_addr = None
- # private members
- self._fname = fname
- self._buf = {}
- self._readed = False
- self._eof = False
- self._offset = 0
- def readfile(self):
- ''' Read file into internal buffer.
- @return True if successful.
- '''
- if self._readed:
- return True
- if not hasattr(self._fname, "read"):
- f = file(self._fname, "rU")
- fclose = f.close
- else:
- f = self._fname
- fclose = None
- self._offset = 0
- self._eof = False
- result = True
- for s in f:
- if not self.decode_record(s):
- result = False
- break
- if self._eof:
- break
- if fclose:
- fclose()
- self._readed = result
- return result
- def decode_record(self, s):
- ''' Decode one record of HEX file.
- @param s line with HEX record.
- @return True if line decode OK, or this is not HEX line.
- False if this is invalid HEX line or checksum error.
- '''
- s = s.rstrip('\r\n')
- if not s:
- return True # empty line
- if s[0] == ':':
- try:
- bin = array('B', unhexlify(s[1:]))
- except TypeError:
- # this might be raised by unhexlify when odd hexascii digits
- self.Error = "Odd hexascii digits"
- return False
- length = len(bin)
- if length < 5:
- self.Error = "Too short line"
- return False
- else:
- return True # first char must be ':'
- record_length = bin[0]
- if length != (5 + record_length):
- self.Error = "Invalid line length"
- return False
- addr = bin[1]*256 + bin[2]
- record_type = bin[3]
- if not (0 <= record_type <= 5):
- self.Error = "Invalid type of record: %d" % record_type
- return False
- crc = sum(bin)
- crc &= 0x0FF
- if crc != 0:
- self.Error = "Invalid crc"
- return False
- if record_type == 0:
- # data record
- addr += self._offset
- for i in xrange(4, 4+record_length):
- if not self._buf.get(addr, None) is None:
- self.AddrOverlap = addr
- self._buf[addr] = bin[i]
- addr += 1 # FIXME: addr should be wrapped on 64K boundary
- elif record_type == 1:
- # end of file record
- if record_length != 0:
- self.Error = "Bad End-of-File Record"
- return False
- self._eof = True
- elif record_type == 2:
- # Extended 8086 Segment Record
- if record_length != 2 or addr != 0:
- self.Error = "Bad Extended 8086 Segment Record"
- return False
- self._offset = (bin[4]*256 + bin[5]) * 16
- elif record_type == 4:
- # Extended Linear Address Record
- if record_length != 2 or addr != 0:
- self.Error = "Bad Extended Linear Address Record"
- return False
- self._offset = (bin[4]*256 + bin[5]) * 65536
- elif record_type == 3:
- # Start Segment Address Record
- if record_length != 4 or addr != 0:
- self.Error = "Bad Start Segment Address Record"
- return False
- if self.start_addr:
- self.Error = "Start Address Record appears twice"
- return False
- self.start_addr = {'CS': bin[4]*256 + bin[5],
- 'IP': bin[6]*256 + bin[7],
- }
- elif record_type == 5:
- # Start Linear Address Record
- if record_length != 4 or addr != 0:
- self.Error = "Bad Start Linear Address Record"
- return False
- if self.start_addr:
- self.Error = "Start Address Record appears twice"
- return False
- self.start_addr = {'EIP': (bin[4]*16777216 +
- bin[5]*65536 +
- bin[6]*256 +
- bin[7]),
- }
- return True
- def _get_start_end(self, start=None, end=None):
- """Return default values for start and end if they are None
- """
- if start is None:
- start = min(self._buf.keys())
- if end is None:
- end = max(self._buf.keys())
- if start > end:
- start, end = end, start
- return start, end
- def tobinarray(self, start=None, end=None, pad=None):
- ''' Convert to binary form.
- @param start start address of output bytes.
- @param end end address of output bytes.
- @param pad fill empty spaces with this value
- (if None used self.padding).
- @return array of unsigned char data.
- '''
- if pad is None:
- pad = self.padding
- bin = array('B')
- if self._buf == {}:
- return bin
- start, end = self._get_start_end(start, end)
- for i in xrange(start, end+1):
- bin.append(self._buf.get(i, pad))
- return bin
- def tobinstr(self, start=None, end=None, pad=0xFF):
- ''' Convert to binary form.
- @param start start address of output bytes.
- @param end end address of output bytes.
- @param pad fill empty spaces with this value
- (if None used self.padding).
- @return string of binary data.
- '''
- return self.tobinarray(start, end, pad).tostring()
- def tobinfile(self, fobj, start=None, end=None, pad=0xFF):
- '''Convert to binary and write to file.
- @param fobj file name or file object for writing output bytes.
- @param start start address of output bytes.
- @param end end address of output bytes.
- @param pad fill empty spaces with this value
- (if None used self.padding).
- '''
- if not hasattr(fobj, "write"):
- fobj = file(fobj, "wb")
- fclose = fobj.close
- else:
- fclose = None
- fobj.write(self.tobinstr(start, end, pad))
- if fclose:
- fclose()
- def minaddr(self):
- ''' Get minimal address of HEX content. '''
- aa = self._buf.keys()
- if aa == []:
- return 0
- else:
- return min(aa)
- def maxaddr(self):
- ''' Get maximal address of HEX content. '''
- aa = self._buf.keys()
- if aa == []:
- return 0
- else:
- return max(aa)
- def __getitem__(self, addr):
- ''' Get byte from address.
- @param addr address of byte.
- @return byte if address exists in HEX file, or self.padding
- if no data found.
- '''
- return self._buf.get(addr, self.padding)
- def __setitem__(self, addr, byte):
- self._buf[addr] = byte
- def writefile(self, f, write_start_addr=True):
- """Write data to file f in HEX format.
- @param f filename or file-like object for writing
- @param write_start_addr enable or disable writing start address
- record to file (enabled by default).
- If there is no start address nothing
- will be written.
- @return True if successful.
- """
- fwrite = getattr(f, "write", None)
- if fwrite:
- fobj = f
- fclose = None
- else:
- fobj = file(f, 'w')
- fwrite = fobj.write
- fclose = fobj.close
- # start address record if any
- if self.start_addr and write_start_addr:
- keys = self.start_addr.keys()
- keys.sort()
- bin = array('B', '\0'*9)
- if keys == ['CS','IP']:
- # Start Segment Address Record
- bin[0] = 4 # reclen
- bin[1] = 0 # offset msb
- bin[2] = 0 # offset lsb
- bin[3] = 3 # rectyp
- cs = self.start_addr['CS']
- bin[4] = (cs >> 8) & 0x0FF
- bin[5] = cs & 0x0FF
- ip = self.start_addr['IP']
- bin[6] = (ip >> 8) & 0x0FF
- bin[7] = ip & 0x0FF
- bin[8] = (-sum(bin)) & 0x0FF # chksum
- fwrite(':')
- fwrite(hexlify(bin.tostring()).upper())
- fwrite('\n')
- elif keys == ['EIP']:
- # Start Linear Address Record
- bin[0] = 4 # reclen
- bin[1] = 0 # offset msb
- bin[2] = 0 # offset lsb
- bin[3] = 5 # rectyp
- eip = self.start_addr['EIP']
- bin[4] = (eip >> 24) & 0x0FF
- bin[5] = (eip >> 16) & 0x0FF
- bin[6] = (eip >> 8) & 0x0FF
- bin[7] = eip & 0x0FF
- bin[8] = (-sum(bin)) & 0x0FF # chksum
- fwrite(':')
- fwrite(hexlify(bin.tostring()).upper())
- fwrite('\n')
- else:
- self.Error = ('Invalid start address value: %r'
- % self.start_addr)
- return False
- # data
- minaddr = IntelHex.minaddr(self)
- maxaddr = IntelHex.maxaddr(self)
- if maxaddr > 65535:
- offset = (minaddr/65536)*65536
- else:
- offset = None
- while True:
- if offset != None:
- # emit 32-bit offset record
- high_ofs = offset / 65536
- offset_record = ":02000004%04X" % high_ofs
- bytes = divmod(high_ofs, 256)
- csum = 2 + 4 + bytes[0] + bytes[1]
- csum = (-csum) & 0x0FF
- offset_record += "%02X\n" % csum
- ofs = offset
- if (ofs + 65536) > maxaddr:
- rng = xrange(maxaddr - ofs + 1)
- else:
- rng = xrange(65536)
- else:
- ofs = 0
- offset_record = ''
- rng = xrange(maxaddr + 1)
- csum = 0
- k = 0
- record = ""
- for addr in rng:
- byte = self._buf.get(ofs+addr, None)
- if byte != None:
- if k == 0:
- # optionally offset record
- fobj.write(offset_record)
- offset_record = ''
- # start data record
- record += "%04X00" % addr
- bytes = divmod(addr, 256)
- csum = bytes[0] + bytes[1]
- k += 1
- # continue data in record
- record += "%02X" % byte
- csum += byte
- # check for length of record
- if k < 16:
- continue
- if k != 0:
- # close record
- csum += k
- csum = (-csum) & 0x0FF
- record += "%02X" % csum
- fobj.write(":%02X%s\n" % (k, record))
- # cleanup
- csum = 0
- k = 0
- record = ""
- else:
- if k != 0:
- # close record
- csum += k
- csum = (-csum) & 0x0FF
- record += "%02X" % csum
- fobj.write(":%02X%s\n" % (k, record))
- # advance offset
- if offset is None:
- break
- offset += 65536
- if offset > maxaddr:
- break
- # end-of-file record
- fobj.write(":00000001FF\n")
- if fclose:
- fclose()
- return True
- #/IntelHex
- class IntelHex16bit(IntelHex):
- """Access to data as 16-bit words."""
- def __init__(self, source):
- """Construct class from HEX file
- or from instance of ordinary IntelHex class.
- @param source file name of HEX file or file object
- or instance of ordinary IntelHex class
- """
- if isinstance(source, IntelHex):
- # from ihex8
- self.Error = source.Error
- self.AddrOverlap = source.AddrOverlap
- self.padding = source.padding
-
- # private members
- self._fname = source._fname
- self._buf = source._buf
- self._readed = source._readed
- self._eof = source._eof
- self._offset = source._offset
- else:
- IntelHex.__init__(self, source)
- if self.padding == 0x0FF:
- self.padding = 0x0FFFF
- def __getitem__(self, addr16):
- """Get 16-bit word from address.
- Raise error if found only one byte from pair.
- @param addr16 address of word (addr8 = 2 * addr16).
- @return word if bytes exists in HEX file, or self.padding
- if no data found.
- """
- addr1 = addr16 * 2
- addr2 = addr1 + 1
- byte1 = self._buf.get(addr1, None)
- byte2 = self._buf.get(addr2, None)
- if byte1 != None and byte2 != None:
- return byte1 | (byte2 << 8) # low endian
- if byte1 == None and byte2 == None:
- return self.padding
- raise Exception, 'Bad access in 16-bit mode (not enough data)'
- def __setitem__(self, addr16, word):
- addr_byte = addr16 * 2
- bytes = divmod(word, 256)
- self._buf[addr_byte] = bytes[1]
- self._buf[addr_byte+1] = bytes[0]
- def minaddr(self):
- '''Get minimal address of HEX content in 16-bit mode.'''
- aa = self._buf.keys()
- if aa == []:
- return 0
- else:
- return min(aa)/2
- def maxaddr(self):
- '''Get maximal address of HEX content in 16-bit mode.'''
- aa = self._buf.keys()
- if aa == []:
- return 0
- else:
- return max(aa)/2
- #/class IntelHex16bit
- def hex2bin(fin, fout, start=None, end=None, size=None, pad=0xFF):
- """Hex-to-Bin convertor engine.
- @return 0 if all OK
- @param fin input hex file (filename or file-like object)
- @param fout output bin file (filename or file-like object)
- @param start start of address range (optional)
- @param end end of address range (optional)
- @param size size of resulting file (in bytes) (optional)
- @param pad padding byte (optional)
- """
- h = IntelHex(fin)
- if not h.readfile():
- print "Bad HEX file"
- return 1
- # start, end, size
- if size != None and size != 0:
- if end == None:
- if start == None:
- start = h.minaddr()
- end = start + size - 1
- else:
- if (end+1) >= size:
- start = end + 1 - size
- else:
- start = 0
- try:
- h.tobinfile(fout, start, end, pad)
- except IOError:
- print "Could not write to file: %s" % fout
- return 1
- return 0
- #/def hex2bin
- if __name__ == '__main__':
- import getopt
- import os
- import sys
- usage = '''Hex2Bin python converting utility.
- Usage:
- python intelhex.py [options] file.hex [out.bin]
- Arguments:
- file.hex name of hex file to processing.
- out.bin name of output file.
- If omitted then output write to file.bin.
- Options:
- -h, --help this help message.
- -p, --pad=FF pad byte for empty spaces (ascii hex value).
- -r, --range=START:END specify address range for writing output
- (ascii hex value).
- Range can be in form 'START:' or ':END'.
- -l, --length=NNNN,
- -s, --size=NNNN size of output (decimal value).
- '''
- pad = 0xFF
- start = None
- end = None
- size = None
- try:
- opts, args = getopt.getopt(sys.argv[1:], "hp:r:l:s:",
- ["help", "pad=", "range=",
- "length=", "size="])
- for o, a in opts:
- if o in ("-h", "--help"):
- print usage
- sys.exit(0)
- elif o in ("-p", "--pad"):
- try:
- pad = int(a, 16) & 0x0FF
- except:
- raise getopt.GetoptError, 'Bad pad value'
- elif o in ("-r", "--range"):
- try:
- l = a.split(":")
- if l[0] != '':
- start = int(l[0], 16)
- if l[1] != '':
- end = int(l[1], 16)
- except:
- raise getopt.GetoptError, 'Bad range value(s)'
- elif o in ("-l", "--lenght", "-s", "--size"):
- try:
- size = int(a, 10)
- except:
- raise getopt.GetoptError, 'Bad size value'
- if start != None and end != None and size != None:
- raise getopt.GetoptError, 'Cannot specify START:END and SIZE simultaneously'
- if not args:
- raise getopt.GetoptError, 'Hex file is not specified'
- if len(args) > 2:
- raise getopt.GetoptError, 'Too many arguments'
- except getopt.GetoptError, msg:
- print msg
- print usage
- sys.exit(2)
- fin = args[0]
- if len(args) == 1:
- import os.path
- name, ext = os.path.splitext(fin)
- fout = name + ".bin"
- else:
- fout = args[1]
- if not os.path.isfile(fin):
- print "File not found"
- sys.exit(1)
- sys.exit(hex2bin(fin, fout, start, end, size, pad))
|