Esempio n. 1
0
 def puts(self, addr, s):
     """Put string of bytes at given address. Will overwrite any previous
     entries.
     """
     a = array("B", asbytes(s))
     for i in range_g(len(a)):
         self._buf[addr + i] = a[i]
Esempio n. 2
0
def _get_file_and_addr_range(s, _support_drive_letter=None):
    """Special method for hexmerge.py script to split file notation
    into 3 parts: (filename, start, end)

    @raise _BadFileNotation  when string cannot be safely split.
    """
    if _support_drive_letter is None:
        _support_drive_letter = (os.name == 'nt')
    drive = ''
    if _support_drive_letter:
        if s[1:2] == ':' and s[0].upper() in ''.join([chr(i) for i in range_g(ord('A'), ord('Z')+1)]):
            drive = s[:2]
            s = s[2:]
    parts = s.split(':')
    n = len(parts)
    if n == 1:
        fname = parts[0]
        fstart = None
        fend = None
    elif n != 3:
        raise _BadFileNotation
    else:
        fname = parts[0]
        def ascii_hex_to_int(ascii):
            if ascii is not None:
                try:
                    return int(ascii, 16)
                except ValueError:
                    raise _BadFileNotation
            return ascii
        fstart = ascii_hex_to_int(parts[1] or None)
        fend = ascii_hex_to_int(parts[2] or None)
    return drive+fname, fstart, fend
Esempio n. 3
0
 def __setitem__(self, addr, byte):
     """Set byte at address."""
     t = type(addr)
     if t in IntTypes:
         if addr < 0:
             raise TypeError('Address should be >= 0.')
         self._buf[addr] = byte
     elif t == slice:
         if not isinstance(byte, (list, tuple)):
             raise ValueError('Slice operation expects sequence of bytes')
         start = addr.start
         stop = addr.stop
         step = addr.step or 1
         if None not in (start, stop):
             ra = range_l(start, stop, step)
             if len(ra) != len(byte):
                 raise ValueError('Length of bytes sequence does not match '
                                  'address range')
         elif (start, stop) == (None, None):
             raise TypeError('Unsupported address range')
         elif start is None:
             start = stop - len(byte)
         elif stop is None:
             stop = start + len(byte)
         if start < 0:
             raise TypeError('start address cannot be negative')
         if stop < 0:
             raise TypeError('stop address cannot be negative')
         j = 0
         for i in range_g(start, stop, step):
             self._buf[i] = byte[j]
             j += 1
     else:
         raise TypeError('Address has unsupported type: %s' % t)
Esempio n. 4
0
def _get_file_and_addr_range(s, _support_drive_letter=None):
    """Special method for hexmerge.py script to split file notation
    into 3 parts: (filename, start, end)

    @raise _BadFileNotation  when string cannot be safely split.
    """
    if _support_drive_letter is None:
        _support_drive_letter = os.name == "nt"
    drive = ""
    if _support_drive_letter:
        if s[1:2] == ":" and s[0].upper() in "".join([chr(i) for i in range_g(ord("A"), ord("Z") + 1)]):
            drive = s[:2]
            s = s[2:]
    parts = s.split(":")
    n = len(parts)
    if n == 1:
        fname = parts[0]
        fstart = None
        fend = None
    elif n != 3:
        raise _BadFileNotation
    else:
        fname = parts[0]

        def ascii_hex_to_int(ascii):
            if ascii is not None:
                try:
                    return int(ascii, 16)
                except ValueError:
                    raise _BadFileNotation
            return ascii

        fstart = ascii_hex_to_int(parts[1] or None)
        fend = ascii_hex_to_int(parts[2] or None)
    return drive + fname, fstart, fend
Esempio n. 5
0
 def __getitem__(self, addr):
     ''' Get requested byte from address.
     @param  addr    address of byte.
     @return         byte if address exists in HEX file, or self.padding
                     if no data found.
     '''
     t = type(addr)
     if t in IntTypes:
         if addr < 0:
             raise TypeError('Address should be >= 0.')
         return self._buf.get(addr, self.padding)
     elif t == slice:
         addresses = dict_keys(self._buf)
         ih = IntelHex()
         if addresses:
             addresses.sort()
             start = addr.start or addresses[0]
             stop = addr.stop or (addresses[-1] + 1)
             step = addr.step or 1
             for i in range_g(start, stop, step):
                 x = self._buf.get(i)
                 if x is not None:
                     ih[i] = x
         return ih
     else:
         raise TypeError('Address has unsupported type: %s' % t)
Esempio n. 6
0
 def puts(self, addr, s):
     """Put string of bytes at given address. Will overwrite any previous
     entries.
     """
     a = array('B', asbytes(s))
     for i in range_g(len(a)):
         self._buf[addr + i] = a[i]
Esempio n. 7
0
 def __setitem__(self, addr, byte):
     """Set byte at address."""
     t = type(addr)
     if t in IntTypes:
         if addr < 0:
             raise TypeError("Address should be >= 0.")
         self._buf[addr] = byte
     elif t == slice:
         if not isinstance(byte, (list, tuple)):
             raise ValueError("Slice operation expects sequence of bytes")
         start = addr.start
         stop = addr.stop
         step = addr.step or 1
         if None not in (start, stop):
             ra = range_l(start, stop, step)
             if len(ra) != len(byte):
                 raise ValueError("Length of bytes sequence does not match " "address range")
         elif (start, stop) == (None, None):
             raise TypeError("Unsupported address range")
         elif start is None:
             start = stop - len(byte)
         elif stop is None:
             stop = start + len(byte)
         if start < 0:
             raise TypeError("start address cannot be negative")
         if stop < 0:
             raise TypeError("stop address cannot be negative")
         j = 0
         for i in range_g(start, stop, step):
             self._buf[i] = byte[j]
             j += 1
     else:
         raise TypeError("Address has unsupported type: %s" % t)
Esempio n. 8
0
 def __getitem__(self, addr):
     """ Get requested byte from address.
     @param  addr    address of byte.
     @return         byte if address exists in HEX file, or self.padding
                     if no data found.
     """
     t = type(addr)
     if t in IntTypes:
         if addr < 0:
             raise TypeError("Address should be >= 0.")
         return self._buf.get(addr, self.padding)
     elif t == slice:
         addresses = dict_keys(self._buf)
         ih = IntelHex()
         if addresses:
             addresses.sort()
             start = addr.start or addresses[0]
             stop = addr.stop or (addresses[-1] + 1)
             step = addr.step or 1
             for i in range_g(start, stop, step):
                 x = self._buf.get(i)
                 if x is not None:
                     ih[i] = x
         return ih
     else:
         raise TypeError("Address has unsupported type: %s" % t)
Esempio n. 9
0
 def gets(self, addr, length):
     """Get string of bytes from given address. If any entries are blank
     from addr through addr+length, a NotEnoughDataError exception will
     be raised. Padding is not used."""
     a = array('B', asbytes('\0' * length))
     try:
         for i in range_g(length):
             a[i] = self._buf[addr + i]
     except KeyError:
         raise NotEnoughDataError(address=addr, length=length)
     return asstr(a.tostring())
Esempio n. 10
0
 def gets(self, addr, length):
     """Get string of bytes from given address. If any entries are blank
     from addr through addr+length, a NotEnoughDataError exception will
     be raised. Padding is not used."""
     a = array("B", asbytes("\0" * length))
     try:
         for i in range_g(length):
             a[i] = self._buf[addr + i]
     except KeyError:
         raise NotEnoughDataError(address=addr, length=length)
     return asstr(a.tostring())
Esempio n. 11
0
    def dump(self, tofile=None):
        """Dump object content to specified file object or to stdout if None.
        Format is a hexdump with some header information at the beginning,
        addresses on the left, and data on right.

        @param  tofile        file-like object to dump to
        """

        if tofile is None:
            tofile = sys.stdout
        # start addr possibly
        if self.start_addr is not None:
            cs = self.start_addr.get("CS")
            ip = self.start_addr.get("IP")
            eip = self.start_addr.get("EIP")
            if eip is not None and cs is None and ip is None:
                tofile.write("EIP = 0x%08X\n" % eip)
            elif eip is None and cs is not None and ip is not None:
                tofile.write("CS = 0x%04X, IP = 0x%04X\n" % (cs, ip))
            else:
                tofile.write("start_addr = %r\n" % start_addr)
        # actual data
        addresses = dict_keys(self._buf)
        if addresses:
            addresses.sort()
            minaddr = addresses[0]
            maxaddr = addresses[-1]
            startaddr = int(minaddr >> 4) * 16
            endaddr = int((maxaddr >> 4) + 1) * 16
            maxdigits = max(len(str(endaddr)), 4)
            templa = "%%0%dX" % maxdigits
            range16 = range_l(16)
            for i in range_g(startaddr, endaddr, 16):
                tofile.write(templa % i)
                tofile.write(" ")
                s = []
                for j in range16:
                    x = self._buf.get(i + j)
                    if x is not None:
                        tofile.write(" %02X" % x)
                        if 32 <= x < 127:  # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot
                            s.append(chr(x))
                        else:
                            s.append(".")
                    else:
                        tofile.write(" --")
                        s.append(" ")
                tofile.write("  |" + "".join(s) + "|\n")
Esempio n. 12
0
    def dump(self, tofile=None):
        """Dump object content to specified file object or to stdout if None.
        Format is a hexdump with some header information at the beginning,
        addresses on the left, and data on right.

        @param  tofile        file-like object to dump to
        """

        if tofile is None:
            tofile = sys.stdout
        # start addr possibly
        if self.start_addr is not None:
            cs = self.start_addr.get('CS')
            ip = self.start_addr.get('IP')
            eip = self.start_addr.get('EIP')
            if eip is not None and cs is None and ip is None:
                tofile.write('EIP = 0x%08X\n' % eip)
            elif eip is None and cs is not None and ip is not None:
                tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip))
            else:
                tofile.write('start_addr = %r\n' % start_addr)
        # actual data
        addresses = dict_keys(self._buf)
        if addresses:
            addresses.sort()
            minaddr = addresses[0]
            maxaddr = addresses[-1]
            startaddr = int(minaddr >> 4) * 16
            endaddr = int((maxaddr >> 4) + 1) * 16
            maxdigits = max(len(str(endaddr)), 4)
            templa = '%%0%dX' % maxdigits
            range16 = range_l(16)
            for i in range_g(startaddr, endaddr, 16):
                tofile.write(templa % i)
                tofile.write(' ')
                s = []
                for j in range16:
                    x = self._buf.get(i + j)
                    if x is not None:
                        tofile.write(' %02X' % x)
                        if 32 <= x < 127:  # GNU less does not like 0x7F (128 decimal) so we'd better show it as dot
                            s.append(chr(x))
                        else:
                            s.append('.')
                    else:
                        tofile.write(' --')
                        s.append(' ')
                tofile.write('  |' + ''.join(s) + '|\n')
Esempio n. 13
0
    def _tobinarray_really(self, start, end, pad, size):
        if pad is None:
            pad = self.padding

        bin = array('B')

        if self._buf == {} and None in (start, end):
            return bin

        if size is not None and size <= 0:
            raise ValueError("tobinarray: wrong value for size")

        start, end = self._get_start_end(start, end, size)

        for i in range_g(start, end + 1):
            bin.append(self._buf.get(i, pad))

        return bin
Esempio n. 14
0
    def _tobinarray_really(self, start, end, pad, size):
        if pad is None:
            pad = self.padding

        bin = array("B")

        if self._buf == {} and None in (start, end):
            return bin

        if size is not None and size <= 0:
            raise ValueError("tobinarray: wrong value for size")

        start, end = self._get_start_end(start, end, size)

        for i in range_g(start, end + 1):
            bin.append(self._buf.get(i, pad))

        return bin
Esempio n. 15
0
 def __delitem__(self, addr):
     """Delete byte at address."""
     t = type(addr)
     if t in IntTypes:
         if addr < 0:
             raise TypeError('Address should be >= 0.')
         del self._buf[addr]
     elif t == slice:
         addresses = dict_keys(self._buf)
         if addresses:
             addresses.sort()
             start = addr.start or addresses[0]
             stop = addr.stop or (addresses[-1] + 1)
             step = addr.step or 1
             for i in range_g(start, stop, step):
                 x = self._buf.get(i)
                 if x is not None:
                     del self._buf[i]
     else:
         raise TypeError('Address has unsupported type: %s' % t)
Esempio n. 16
0
 def __delitem__(self, addr):
     """Delete byte at address."""
     t = type(addr)
     if t in IntTypes:
         if addr < 0:
             raise TypeError("Address should be >= 0.")
         del self._buf[addr]
     elif t == slice:
         addresses = dict_keys(self._buf)
         if addresses:
             addresses.sort()
             start = addr.start or addresses[0]
             stop = addr.stop or (addresses[-1] + 1)
             step = addr.step or 1
             for i in range_g(start, stop, step):
                 x = self._buf.get(i)
                 if x is not None:
                     del self._buf[i]
     else:
         raise TypeError("Address has unsupported type: %s" % t)
Esempio n. 17
0
    def tobinarray(self, start=None, end=None, size=None):
        """Convert this object to binary form as array (of 2-bytes word data).
        If start and end unspecified, they will be inferred from the data.
        @param  start   start address of output data.
        @param  end     end address of output data (inclusive).
        @param  size    size of the block (number of words),
                        used with start or end parameter.
        @return         array of unsigned short (uint16_t) data.
        """
        bin = array("H")

        if self._buf == {} and None in (start, end):
            return bin

        if size is not None and size <= 0:
            raise ValueError("tobinarray: wrong value for size")

        start, end = self._get_start_end(start, end, size)

        for addr in range_g(start, end + 1):
            bin.append(self[addr])

        return bin
Esempio n. 18
0
    def tobinarray(self, start=None, end=None, size=None):
        '''Convert this object to binary form as array (of 2-bytes word data).
        If start and end unspecified, they will be inferred from the data.
        @param  start   start address of output data.
        @param  end     end address of output data (inclusive).
        @param  size    size of the block (number of words),
                        used with start or end parameter.
        @return         array of unsigned short (uint16_t) data.
        '''
        bin = array('H')

        if self._buf == {} and None in (start, end):
            return bin

        if size is not None and size <= 0:
            raise ValueError("tobinarray: wrong value for size")

        start, end = self._get_start_end(start, end, size)

        for addr in range_g(start, end + 1):
            bin.append(self[addr])

        return bin
Esempio n. 19
0
    def _decode_record(self, s, line=0):
        """Decode one record of HEX file.

        @param  s       line with HEX record.
        @param  line    line number (for error messages).

        @raise  EndOfFile   if EOF record encountered.
        """
        s = s.rstrip("\r\n")
        if not s:
            return  # empty line

        if s[0] == ":":
            try:
                bin = array("B", unhexlify(asbytes(s[1:])))
            except (TypeError, ValueError):
                # this might be raised by unhexlify when odd hexascii digits
                raise HexRecordError(line=line)
            length = len(bin)
            if length < 5:
                raise HexRecordError(line=line)
        else:
            raise HexRecordError(line=line)

        record_length = bin[0]
        if length != (5 + record_length):
            raise RecordLengthError(line=line)

        addr = bin[1] * 256 + bin[2]

        record_type = bin[3]
        if not (0 <= record_type <= 5):
            raise RecordTypeError(line=line)

        crc = sum(bin)
        crc &= 0x0FF
        if crc != 0:
            raise RecordChecksumError(line=line)

        if record_type == 0:
            # data record
            addr += self._offset
            for i in range_g(4, 4 + record_length):
                if not self._buf.get(addr, None) is None:
                    raise AddressOverlapError(address=addr, line=line)
                self._buf[addr] = bin[i]
                addr += 1  # FIXME: addr should be wrapped
                # BUT after 02 record (at 64K boundary)
                # and after 04 record (at 4G boundary)

        elif record_type == 1:
            # end of file record
            if record_length != 0:
                raise EOFRecordError(line=line)
            raise _EndOfFile

        elif record_type == 2:
            # Extended 8086 Segment Record
            if record_length != 2 or addr != 0:
                raise ExtendedSegmentAddressRecordError(line=line)
            self._offset = (bin[4] * 256 + bin[5]) * 16

        elif record_type == 4:
            # Extended Linear Address Record
            if record_length != 2 or addr != 0:
                raise ExtendedLinearAddressRecordError(line=line)
            self._offset = (bin[4] * 256 + bin[5]) * 65536

        elif record_type == 3:
            # Start Segment Address Record
            if record_length != 4 or addr != 0:
                raise StartSegmentAddressRecordError(line=line)
            if self.start_addr:
                raise DuplicateStartAddressRecordError(line=line)
            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:
                raise StartLinearAddressRecordError(line=line)
            if self.start_addr:
                raise DuplicateStartAddressRecordError(line=line)
            self.start_addr = {"EIP": (bin[4] * 16777216 + bin[5] * 65536 + bin[6] * 256 + bin[7])}
Esempio n. 20
0
    def _decode_record(self, s, line=0):
        '''Decode one record of HEX file.

        @param  s       line with HEX record.
        @param  line    line number (for error messages).

        @raise  EndOfFile   if EOF record encountered.
        '''
        s = s.rstrip('\r\n')
        if not s:
            return  # empty line

        if s[0] == ':':
            try:
                bin = array('B', unhexlify(asbytes(s[1:])))
            except (TypeError, ValueError):
                # this might be raised by unhexlify when odd hexascii digits
                raise HexRecordError(line=line)
            length = len(bin)
            if length < 5:
                raise HexRecordError(line=line)
        else:
            raise HexRecordError(line=line)

        record_length = bin[0]
        if length != (5 + record_length):
            raise RecordLengthError(line=line)

        addr = bin[1] * 256 + bin[2]

        record_type = bin[3]
        if not (0 <= record_type <= 5):
            raise RecordTypeError(line=line)

        crc = sum(bin)
        crc &= 0x0FF
        if crc != 0:
            raise RecordChecksumError(line=line)

        if record_type == 0:
            # data record
            addr += self._offset
            for i in range_g(4, 4 + record_length):
                if not self._buf.get(addr, None) is None:
                    raise AddressOverlapError(address=addr, line=line)
                self._buf[addr] = bin[i]
                addr += 1  # FIXME: addr should be wrapped
                # BUT after 02 record (at 64K boundary)
                # and after 04 record (at 4G boundary)

        elif record_type == 1:
            # end of file record
            if record_length != 0:
                raise EOFRecordError(line=line)
            raise _EndOfFile

        elif record_type == 2:
            # Extended 8086 Segment Record
            if record_length != 2 or addr != 0:
                raise ExtendedSegmentAddressRecordError(line=line)
            self._offset = (bin[4] * 256 + bin[5]) * 16

        elif record_type == 4:
            # Extended Linear Address Record
            if record_length != 2 or addr != 0:
                raise ExtendedLinearAddressRecordError(line=line)
            self._offset = (bin[4] * 256 + bin[5]) * 65536

        elif record_type == 3:
            # Start Segment Address Record
            if record_length != 4 or addr != 0:
                raise StartSegmentAddressRecordError(line=line)
            if self.start_addr:
                raise DuplicateStartAddressRecordError(line=line)
            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:
                raise StartLinearAddressRecordError(line=line)
            if self.start_addr:
                raise DuplicateStartAddressRecordError(line=line)
            self.start_addr = {
                'EIP':
                (bin[4] * 16777216 + bin[5] * 65536 + bin[6] * 256 + bin[7]),
            }
Esempio n. 21
0
    def write_hex_file(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 in obj, nothing
                                    will be written regardless of this setting.
        """
        fwrite = getattr(f, "write", None)
        if fwrite:
            fobj = f
            fclose = None
        else:
            fobj = open(f, "w")
            fwrite = fobj.write
            fclose = fobj.close

        # Translation table for uppercasing hex ascii string.
        # timeit shows that using hexstr.translate(table)
        # is faster than hexstr.upper():
        # 0.452ms vs. 0.652ms (translate vs. upper)
        if sys.version_info[0] >= 3:
            # Python 3
            table = bytes(range_l(256)).upper()
        else:
            # Python 2
            table = "".join(chr(i).upper() for i in range_g(256))

        # start address record if any
        if self.start_addr and write_start_addr:
            keys = dict_keys(self.start_addr)
            keys.sort()
            bin = array("B", asbytes("\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(":" + asstr(hexlify(bin.tostring()).translate(table)) + "\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(":" + asstr(hexlify(bin.tostring()).translate(table)) + "\n")
            else:
                if fclose:
                    fclose()
                raise InvalidStartAddressValueError(start_addr=self.start_addr)

        # data
        addresses = dict_keys(self._buf)
        addresses.sort()
        addr_len = len(addresses)
        if addr_len:
            minaddr = addresses[0]
            maxaddr = addresses[-1]

            if maxaddr > 65535:
                need_offset_record = True
            else:
                need_offset_record = False
            high_ofs = 0

            cur_addr = minaddr
            cur_ix = 0

            while cur_addr <= maxaddr:
                if need_offset_record:
                    bin = array("B", asbytes("\0" * 7))
                    bin[0] = 2  # reclen
                    bin[1] = 0  # offset msb
                    bin[2] = 0  # offset lsb
                    bin[3] = 4  # rectyp
                    high_ofs = int(cur_addr >> 16)
                    b = divmod(high_ofs, 256)
                    bin[4] = b[0]  # msb of high_ofs
                    bin[5] = b[1]  # lsb of high_ofs
                    bin[6] = (-sum(bin)) & 0x0FF  # chksum
                    fwrite(":" + asstr(hexlify(bin.tostring()).translate(table)) + "\n")

                while True:
                    # produce one record
                    low_addr = cur_addr & 0x0FFFF
                    # chain_len off by 1
                    chain_len = min(15, 65535 - low_addr, maxaddr - cur_addr)

                    # search continuous chain
                    stop_addr = cur_addr + chain_len
                    if chain_len:
                        ix = bisect_right(addresses, stop_addr, cur_ix, min(cur_ix + chain_len + 1, addr_len))
                        chain_len = ix - cur_ix  # real chain_len
                        # there could be small holes in the chain
                        # but we will catch them by try-except later
                        # so for big continuous files we will work
                        # at maximum possible speed
                    else:
                        chain_len = 1  # real chain_len

                    bin = array("B", asbytes("\0" * (5 + chain_len)))
                    b = divmod(low_addr, 256)
                    bin[1] = b[0]  # msb of low_addr
                    bin[2] = b[1]  # lsb of low_addr
                    bin[3] = 0  # rectype
                    try:  # if there is small holes we'll catch them
                        for i in range_g(chain_len):
                            bin[4 + i] = self._buf[cur_addr + i]
                    except KeyError:
                        # we catch a hole so we should shrink the chain
                        chain_len = i
                        bin = bin[: 5 + i]
                    bin[0] = chain_len
                    bin[4 + chain_len] = (-sum(bin)) & 0x0FF  # chksum
                    fwrite(":" + asstr(hexlify(bin.tostring()).translate(table)) + "\n")

                    # adjust cur_addr/cur_ix
                    cur_ix += chain_len
                    if cur_ix < addr_len:
                        cur_addr = addresses[cur_ix]
                    else:
                        cur_addr = maxaddr + 1
                        break
                    high_addr = int(cur_addr >> 16)
                    if high_addr > high_ofs:
                        break

        # end-of-file record
        fwrite(":00000001FF\n")
        if fclose:
            fclose()
Esempio n. 22
0
    def write_hex_file(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 in obj, nothing
                                    will be written regardless of this setting.
        """
        fwrite = getattr(f, "write", None)
        if fwrite:
            fobj = f
            fclose = None
        else:
            fobj = open(f, 'w')
            fwrite = fobj.write
            fclose = fobj.close

        # Translation table for uppercasing hex ascii string.
        # timeit shows that using hexstr.translate(table)
        # is faster than hexstr.upper():
        # 0.452ms vs. 0.652ms (translate vs. upper)
        if sys.version_info[0] >= 3:
            # Python 3
            table = bytes(range_l(256)).upper()
        else:
            # Python 2
            table = ''.join(chr(i).upper() for i in range_g(256))

        # start address record if any
        if self.start_addr and write_start_addr:
            keys = dict_keys(self.start_addr)
            keys.sort()
            bin = array('B', asbytes('\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(':' + asstr(hexlify(bin.tostring()).translate(table)) +
                       '\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(':' + asstr(hexlify(bin.tostring()).translate(table)) +
                       '\n')
            else:
                if fclose:
                    fclose()
                raise InvalidStartAddressValueError(start_addr=self.start_addr)

        # data
        addresses = dict_keys(self._buf)
        addresses.sort()
        addr_len = len(addresses)
        if addr_len:
            minaddr = addresses[0]
            maxaddr = addresses[-1]

            if maxaddr > 65535:
                need_offset_record = True
            else:
                need_offset_record = False
            high_ofs = 0

            cur_addr = minaddr
            cur_ix = 0

            while cur_addr <= maxaddr:
                if need_offset_record:
                    bin = array('B', asbytes('\0' * 7))
                    bin[0] = 2  # reclen
                    bin[1] = 0  # offset msb
                    bin[2] = 0  # offset lsb
                    bin[3] = 4  # rectyp
                    high_ofs = int(cur_addr >> 16)
                    b = divmod(high_ofs, 256)
                    bin[4] = b[0]  # msb of high_ofs
                    bin[5] = b[1]  # lsb of high_ofs
                    bin[6] = (-sum(bin)) & 0x0FF  # chksum
                    fwrite(':' +
                           asstr(hexlify(bin.tostring()).translate(table)) +
                           '\n')

                while True:
                    # produce one record
                    low_addr = cur_addr & 0x0FFFF
                    # chain_len off by 1
                    chain_len = min(15, 65535 - low_addr, maxaddr - cur_addr)

                    # search continuous chain
                    stop_addr = cur_addr + chain_len
                    if chain_len:
                        ix = bisect_right(
                            addresses, stop_addr, cur_ix,
                            min(cur_ix + chain_len + 1, addr_len))
                        chain_len = ix - cur_ix  # real chain_len
                        # there could be small holes in the chain
                        # but we will catch them by try-except later
                        # so for big continuous files we will work
                        # at maximum possible speed
                    else:
                        chain_len = 1  # real chain_len

                    bin = array('B', asbytes('\0' * (5 + chain_len)))
                    b = divmod(low_addr, 256)
                    bin[1] = b[0]  # msb of low_addr
                    bin[2] = b[1]  # lsb of low_addr
                    bin[3] = 0  # rectype
                    try:  # if there is small holes we'll catch them
                        for i in range_g(chain_len):
                            bin[4 + i] = self._buf[cur_addr + i]
                    except KeyError:
                        # we catch a hole so we should shrink the chain
                        chain_len = i
                        bin = bin[:5 + i]
                    bin[0] = chain_len
                    bin[4 + chain_len] = (-sum(bin)) & 0x0FF  # chksum
                    fwrite(':' +
                           asstr(hexlify(bin.tostring()).translate(table)) +
                           '\n')

                    # adjust cur_addr/cur_ix
                    cur_ix += chain_len
                    if cur_ix < addr_len:
                        cur_addr = addresses[cur_ix]
                    else:
                        cur_addr = maxaddr + 1
                        break
                    high_addr = int(cur_addr >> 16)
                    if high_addr > high_ofs:
                        break

        # end-of-file record
        fwrite(":00000001FF\n")
        if fclose:
            fclose()