diff --git a/intelhex/__init__.py b/intelhex/__init__.py index 34c28c6..20705dc 100644 --- a/intelhex/__init__.py +++ b/intelhex/__init__.py @@ -144,7 +144,7 @@ def _decode_record(self, s, line=0): 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 + addr += 1 # FIXME: addr should be wrapped # BUT after 02 record (at 64K boundary) # and after 04 record (at 4G boundary) @@ -286,10 +286,10 @@ def frombytes(self, bytes, offset=0): self._buf[offset] = b offset += 1 - def _get_start_end(self, start=None, end=None, size=None): + def _get_start_end(self, start=None, end=None, size=None, filter=False): """Return default values for start and end if they are None. If this IntelHex object is empty then it's error to - invoke this method with both start and end as None. + invoke this method with both start and end as None. """ if (start,end) == (None,None) and self._buf == {}: raise EmptyIntelHexError @@ -311,12 +311,14 @@ def _get_start_end(self, start=None, end=None, size=None): start = self.minaddr() if end is None: end = self.maxaddr() + elif filter: + end = self.maxaddr_in(start, end) if start > end: start, end = end, start return start, end - def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None): - ''' Convert this object to binary form as array. If start and end + def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None, filter=False): + ''' Convert this object to binary form as array. If start and end unspecified, they will be inferred from the data. @param start start address of output bytes. @param end end address of output bytes (inclusive). @@ -324,9 +326,10 @@ def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None): fill empty spaces with this value (if pad is None then this method uses self.padding). @param size size of the block, used with start or end parameter. + @param filter whether to filter the input range to avoid padding @return array of unsigned char data. ''' - if not isinstance(pad, _DeprecatedParam): + if not (isinstance(pad, _DeprecatedParam) or pad is None): print ("IntelHex.tobinarray: 'pad' parameter is deprecated.") if pad is not None: print ("Please, use IntelHex.padding attribute instead.") @@ -335,9 +338,9 @@ def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None): print ("Use syntax like this: ih.tobinarray(start=xxx, end=yyy, size=zzz)") else: pad = None - return self._tobinarray_really(start, end, pad, size) + return self._tobinarray_really(start, end, pad, size, filter) - def _tobinarray_really(self, start, end, pad, size): + def _tobinarray_really(self, start, end, pad, size, filter): """Return binary array.""" if pad is None: pad = self.padding @@ -346,12 +349,12 @@ def _tobinarray_really(self, start, end, pad, size): 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) + start, end = self._get_start_end(start, end, size, filter) for i in range_g(start, end+1): bin.append(self._buf.get(i, pad)) return bin - def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None): + def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None, filter=False): ''' Convert to binary form and return as binary string. @param start start address of output bytes. @param end end address of output bytes (inclusive). @@ -359,9 +362,10 @@ def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None): fill empty spaces with this value (if pad is None then this method uses self.padding). @param size size of the block, used with start or end parameter. + @param filter whether to filter the input range to avoid padding @return bytes string of binary data. ''' - if not isinstance(pad, _DeprecatedParam): + if not (isinstance(pad, _DeprecatedParam) or pad is None): print ("IntelHex.tobinstr: 'pad' parameter is deprecated.") if pad is not None: print ("Please, use IntelHex.padding attribute instead.") @@ -370,12 +374,12 @@ def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None): print ("Use syntax like this: ih.tobinstr(start=xxx, end=yyy, size=zzz)") else: pad = None - return self._tobinstr_really(start, end, pad, size) + return self._tobinstr_really(start, end, pad, size, filter) - def _tobinstr_really(self, start, end, pad, size): - return array_tobytes(self._tobinarray_really(start, end, pad, size)) + def _tobinstr_really(self, start, end, pad, size, filter): + return array_tobytes(self._tobinarray_really(start, end, pad, size, filter)) - def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None): + def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None, filter=False): '''Convert to binary and write to file. @param fobj file name or file object for writing output bytes. @@ -385,8 +389,9 @@ def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None): fill empty spaces with this value (if pad is None then this method uses self.padding). @param size size of the block, used with start or end parameter. + @param filter whether to filter the input range to avoid padding ''' - if not isinstance(pad, _DeprecatedParam): + if not (isinstance(pad, _DeprecatedParam) or pad is None): print ("IntelHex.tobinfile: 'pad' parameter is deprecated.") if pad is not None: print ("Please, use IntelHex.padding attribute instead.") @@ -401,7 +406,7 @@ def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None): else: close_fd = False - fobj.write(self._tobinstr_really(start, end, pad, size)) + fobj.write(self._tobinstr_really(start, end, pad, size, filter)) if close_fd: fobj.close() @@ -419,12 +424,21 @@ def todict(self): def addresses(self): '''Returns all used addresses in sorted order. - @return list of occupied data addresses in sorted order. + @return list of occupied data addresses in sorted order. ''' aa = dict_keys(self._buf) aa.sort() return aa + def addresses_in(self, start, end): + '''Returns all used addresses in a given range in sorted order. + @param start the start address of the range + @param end the end address of the range + @return list of occupied data addresses in sorted order. + ''' + # TODO: end is inclusive, correct? + return [a for a in self.addresses() if a >= start and a <= end] + def minaddr(self): '''Get minimal address of HEX content. @return minimal address or None if no data @@ -435,6 +449,18 @@ def minaddr(self): else: return min(aa) + def minaddr_in(self, start, end): + '''Get minimal address of HEX content within the specified range. + @param start the start address of the range + @param end the end address of the range + @return minimal address or None if no data + ''' + aa = self.addresses_in(start, end) + if aa == []: + return None + else: + return min(aa) + def maxaddr(self): '''Get maximal address of HEX content. @return maximal address or None if no data @@ -445,6 +471,18 @@ def maxaddr(self): else: return max(aa) + def maxaddr_in(self, start, end): + '''Get maximal address of HEX content within the specified range. + @param start the start address of the range + @param end the end address of the range + @return maximal address or None if no data + ''' + aa = self.addresses_in(start, end) + if aa == []: + return None + else: + return max(aa) + def __getitem__(self, addr): ''' Get requested byte from address. @param addr address of byte. @@ -749,7 +787,7 @@ def puts(self, addr, s): self._buf[addr+i] = a[i] def getsz(self, addr): - """Get zero-terminated bytes string from given address. Will raise + """Get zero-terminated bytes string from given address. Will raise NotEnoughDataError exception if a hole is encountered before a 0. """ i = 0 @@ -771,7 +809,7 @@ def putsz(self, addr, s): def find(self, sub, start=None, end=None): """Return the lowest index in self[start:end] where subsection sub is found. Optional arguments start and end are interpreted as in slice notation. - + @param sub bytes-like subsection to find @param start start of section to search within (optional) @param end end of section to search within (optional) @@ -801,7 +839,7 @@ def dump(self, tofile=None, width=16, withpadding=False): width = int(width) if tofile is None: tofile = sys.stdout - + # start addr possibly if self.start_addr is not None: cs = self.start_addr.get('CS') @@ -856,7 +894,7 @@ def merge(self, other, overlap='error'): in overlapping region. @raise TypeError if other is not instance of IntelHex - @raise ValueError if other is the same object as self + @raise ValueError if other is the same object as self (it can't merge itself) @raise ValueError if overlap argument has incorrect value @raise AddressOverlapError on overlapped data @@ -911,7 +949,7 @@ def segments(self, min_gap=1): beginnings = [addresses[b+1] for b in breaks] beginnings.insert(0, addresses[0]) return [(a, b+1) for (a, b) in zip(beginnings, endings)] - + def get_memory_size(self): """Returns the approximate memory footprint for data.""" n = sys.getsizeof(self) @@ -999,7 +1037,7 @@ def minaddr(self): def maxaddr(self): '''Get maximal address of HEX content in 16-bit mode. - @return maximal address used in this object + @return maximal address used in this object ''' aa = dict_keys(self._buf) if aa == []: @@ -1035,7 +1073,7 @@ def tobinarray(self, start=None, end=None, size=None): #/class IntelHex16bit -def hex2bin(fin, fout, start=None, end=None, size=None, pad=None): +def hex2bin(fin, fout, start=None, end=None, size=None, pad=None, filter=False): """Hex-to-Bin convertor engine. @return 0 if all OK @@ -1045,6 +1083,7 @@ def hex2bin(fin, fout, start=None, end=None, size=None, pad=None): @param end end of address range (inclusive; optional) @param size size of resulting file (in bytes) (optional) @param pad padding byte (optional) + @param filter whether to filter the input range to avoid padding (optional) """ try: h = IntelHex(fin) @@ -1070,7 +1109,7 @@ def hex2bin(fin, fout, start=None, end=None, size=None, pad=None): if pad is not None: # using .padding attribute rather than pad argument to function call h.padding = pad - h.tobinfile(fout, start, end) + h.tobinfile(fout, start, end, filter=filter) except IOError: e = sys.exc_info()[1] # current exception txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e)) @@ -1173,7 +1212,7 @@ def data(offset, bytes): def eof(): """Return End of File record as a string. - @return String representation of Intel Hex EOF record + @return String representation of Intel Hex EOF record """ return ':00000001FF' eof = staticmethod(eof) diff --git a/intelhex/scripts/hex2bin.py b/intelhex/scripts/hex2bin.py index 9533837..b520fbf 100755 --- a/intelhex/scripts/hex2bin.py +++ b/intelhex/scripts/hex2bin.py @@ -37,6 +37,15 @@ VERSION = '2.3.0' +def split_range(a): + l = a.split(":") + if l[0] != '': + start = int(l[0], 16) + if l[1] != '': + end = int(l[1], 16) + return start, end + + def main(): import getopt import os @@ -58,6 +67,9 @@ def main(): -r, --range=START:END specify address range for writing output (ascii hex value). Range can be in form 'START:' or ':END'. + -f, --filter=START:END specify address range for filtering input + (ascii hex value). + Filter range can be in form 'START:' or ':END'. -l, --length=NNNN, -s, --size=NNNN size of output (decimal value). ''' @@ -66,11 +78,12 @@ def main(): start = None end = None size = None + filter = False try: - opts, args = getopt.getopt(sys.argv[1:], "hvp:r:l:s:", + opts, args = getopt.getopt(sys.argv[1:], "hvp:r:f:l:s:", ["help", "version", "pad=", "range=", - "length=", "size="]) + "filter", "length=", "size="]) for o, a in opts: if o in ("-h", "--help"): @@ -86,13 +99,15 @@ def main(): 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) + start, end = split_range(a) except: raise getopt.GetoptError('Bad range value(s)') + elif o in ("-f", "--filter"): + filter = True + try: + start, end = split_range(a) + except: + raise getopt.GetoptError('Bad filter range value(s)') elif o in ("-l", "--lenght", "-s", "--size"): try: size = int(a, 10) @@ -129,7 +144,7 @@ def main(): fout = compat.get_binary_stdout() from intelhex import hex2bin - sys.exit(hex2bin(fin, fout, start, end, size, pad)) + sys.exit(hex2bin(fin, fout, start, end, size, pad, filter)) if __name__ == '__main__': main()