123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- """
- # TOP2049 Open Source programming suite
- #
- # Utility functions
- #
- # Copyright (c) 2009-2011 Michael Buesch <m@bues.ch>
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License along
- # with this program; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- """
- import sys
- import re
- import math
- import random
- class TOPException(Exception): pass
- def byte2int(byte):
- if isinstance(byte, int): # It's already int
- assert 0 <= byte <= 0xFF
- return byte
- if isinstance(byte, str): # Compat for old code
- assert len(byte) == 1
- return ord(byte)
- assert isinstance(byte, (bytes, bytearray))
- assert len(byte) == 1
- return byte[0]
- def int2byte(integer):
- if isinstance(integer, (bytes, bytearray)): # It's already bytes
- return bytes(integer)
- assert isinstance(integer, int)
- assert 0 <= integer <= 0xFF
- return b"%c" % integer
- def hex2bin(hexdata):
- assert(len(hexdata) % 2 == 0)
- return b"".join(int2byte(int(hexdata[i:i+2], 16))
- for i in range(len(hexdata), 2))
- def byte2hex(byte):
- return "%02X" % byte2int(byte)
- def bytes2hex(bindata):
- if not bindata:
- return ""
- return "".join(byte2hex(b) for b in bindata)
- def byte2ascii(c):
- c = byte2int(c)
- if c >= 32 and c <= 126:
- return "%c" % c
- return "."
- def bytes2ascii(bindata):
- if not bindata:
- return ""
- return "".join(byte2ascii(b) for b in bindata)
- def str2bool(string):
- string = str(string).lower().strip()
- if string in ("false", "off", "no"):
- return False
- if string in ("true", "on", "yes"):
- return True
- try:
- return bool(int(string, 10))
- except (ValueError) as e:
- pass
- return None
- def genRandomBlob(size):
- return b"".join(int2byte(random.randint(0, 0xFF))
- for x in range(size))
- def bit(bitNr):
- return 1 << bitNr
- def nrBitsSet(integer):
- count = 0
- while integer:
- count += (integer & 1)
- integer >>= 1
- return count
- def roundup(x, y):
- x, y = int(x), int(y)
- return ((x + (y - 1)) // y) * y
- hexdump_re = re.compile(r"0x[0-9a-fA-F]+:\s+([0-9a-fA-F\s]+)\s*.*")
- def parseHexdump(dump):
- try:
- if isinstance(dump, (bytes, bytearray)):
- dump = dump.decode("ASCII")
- binData = []
- for line in dump.splitlines():
- line = line.strip()
- if not line:
- continue
- m = hexdump_re.match(line)
- if not m:
- raise TOPException("Invalid hexdump format (regex failure)")
- data = m.group(1)
- idx = data.find(" ")
- if idx >= 0:
- data = data[:idx] # Strip ascii section
- data = data.replace(" ", "")
- if len(data) % 2 != 0:
- raise TOPException("Invalid hexdump format (odd bytestring len)")
- for i in range(0, len(data), 2):
- byte = int(data[i:i+2], 16)
- binData.append(int2byte(byte))
- return b"".join(binData)
- except (ValueError, UnicodeError) as e:
- raise TOPException("Invalid hexdump format (Integer error)")
- def generateHexdump(mem):
- ret = ""
- asc = ""
- for i in range(0, len(mem)):
- if i % 16 == 0 and i != 0:
- ret += " " + asc + "\n"
- asc = ""
- if i % 16 == 0:
- ret += "0x%04X: " % i
- c = byte2int(mem[i])
- ret += "%02X" % c
- if (i % 2 != 0):
- ret += " "
- asc += byte2ascii(mem[i])
- ret += " " + asc + "\n\n"
- return ret
- def dumpMem(mem):
- sys.stdout.write(generateHexdump(mem))
- class IO_ihex(object):
- TYPE_DATA = 0
- TYPE_EOF = 1
- TYPE_ESAR = 2
- TYPE_SSAR = 3
- TYPE_ELAR = 4
- TYPE_SLAR = 5
- def autodetect(self, data):
- try:
- self.toBinary(data)
- except (TOPException) as e:
- return False
- return True
- def toBinary(self, ihexData, addressRange=None, defaultBytes=b"\xFF"):
- binData = []
- checksumWarned = False
- doublewriteWarned = False
- addrBias = addressRange.startAddress if addressRange else 0
- try:
- if isinstance(ihexData, (bytes, bytearray)):
- ihexData = ihexData.decode("ASCII")
- lines = ihexData.splitlines()
- hiAddr = 0
- segment = 0
- for line in lines:
- line = line.strip()
- if len(line) == 0:
- continue
- if len(line) < 11 or (len(line) - 1) % 2 != 0:
- raise TOPException("Invalid IHEX format (length error)")
- if line[0] != ':':
- raise TOPException("Invalid IHEX format (magic error)")
- count = int(line[1:3], 16)
- if len(line) != count * 2 + 11:
- raise TOPException("Invalid IHEX format (count error)")
- addr = (int(line[3:5], 16) << 8) | int(line[5:7], 16)
- addr |= hiAddr << 16
- addr += segment * 16
- if hiAddr and segment:
- print("WARNING: IHEX has ESAR and ELAR record")
- type = int(line[7:9], 16)
- checksum = 0
- for i in range(1, len(line), 2):
- byte = int(line[i:i+2], 16)
- checksum = (checksum + byte) & 0xFF
- checksum = checksum & 0xFF
- if checksum != 0 and not checksumWarned:
- checksumWarned = True
- print("WARNING: Invalid IHEX format (checksum error)")
- if type == self.TYPE_EOF:
- break
- if type == self.TYPE_ESAR:
- if count != 2:
- raise TOPException("Invalid IHEX format (inval ESAR)")
- segment = (int(line[9:11], 16) << 8) | int(line[11:13], 16)
- continue
- if type == self.TYPE_ELAR:
- if count != 2:
- raise TOPException("Invalid IHEX format (inval ELAR)")
- hiAddr = (int(line[9:11], 16) << 8) | int(line[11:13], 16)
- continue
- if addressRange and addr < addressRange.startAddress:
- continue
- if addressRange and addr > addressRange.endAddress:
- continue
- if type == self.TYPE_DATA:
- if len(binData) < addr - addrBias + count: # Reallocate
- bytesToAdd = addr - addrBias + count - len(binData)
- for i in range(bytesToAdd):
- defOffs = len(binData) % len(defaultBytes)
- binData += [ defaultBytes[defOffs], ]
- for i in range(9, 9 + count * 2, 2):
- byte = int2byte(int(line[i:i+2], 16))
- offset = (i - 9) // 2 + addr - addrBias
- if binData[offset] != defaultBytes[offset % len(defaultBytes)] and \
- not doublewriteWarned:
- doublewriteWarned = True
- print("Invalid IHEX format (Wrote twice to same location)")
- binData[offset] = byte
- continue
- raise TOPException("Invalid IHEX format (unsup type %d)" % type)
- except (ValueError, UnicodeError) as e:
- raise TOPException("Invalid IHEX format (digit format)")
- return b"".join(binData)
- def fromBinary(self, binData):
- ihex = []
- addr = 0
- for i in range(0, len(binData), 16):
- if addr > 0xFFFF:
- checksum = 0
- ihex.append(":%02X%04X%02X" % (2, 0, self.TYPE_ELAR))
- checksum += 2 + 0 + 0 + self.TYPE_ELAR
- a = (addr >> 16) & 0xFFFF
- ihex.append("%04X" % a)
- checksum += ((a >> 8) & 0xFF) + (a & 0xFF)
- checksum = ((checksum ^ 0xFF) + 1) & 0xFF
- ihex.append("%02X\n" % checksum)
- addr -= 0xFFFF
- checksum = 0
- size = min(len(binData) - i, 16)
- ihex.append(":%02X%04X%02X" % (size, addr, self.TYPE_DATA))
- checksum += size + ((addr >> 8) & 0xFF) + (addr & 0xFF) + self.TYPE_DATA
- for j in range(0, size):
- data = byte2int(binData[i + j])
- checksum = (checksum + data) & 0xFF
- ihex.append("%02X" % data)
- checksum = ((checksum ^ 0xFF) + 1) & 0xFF
- ihex.append("%02X\n" % checksum)
- addr += size
- ihex.append(":00000001FF\n")
- return "".join(ihex)
- class IO_ahex(object):
- def autodetect(self, data):
- try:
- self.toBinary(data)
- except (TOPException) as e:
- return False
- return True
- def toBinary(self, data, addressRange=None, defaultBytes=b"\xFF"):
- # defaultBytes is ignored
- binData = parseHexdump(data)
- if addressRange:
- binData = binData[addressRange.startAddress : addressRange.endAddress + 1]
- return binData
- def fromBinary(self, data):
- return generateHexdump(data)
- class IO_binary(object):
- def autodetect(self, data):
- return True
- def toBinary(self, data, addressRange=None, defaultBytes=b"\xFF"):
- # defaultBytes is ignored
- if addressRange:
- data = data[addressRange.startAddress : addressRange.endAddress + 1]
- return data
- def fromBinary(self, data):
- return data
- def IO_autodetect(data):
- "Returns an IO_... object for the data."
- if IO_ihex().autodetect(data):
- return IO_ihex
- elif IO_ahex().autodetect(data):
- return IO_ahex
- elif IO_binary().autodetect(data):
- return IO_binary
- assert(0) # Can't reach, because binary will always match.
|