util.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. """
  2. # TOP2049 Open Source programming suite
  3. #
  4. # Utility functions
  5. #
  6. # Copyright (c) 2009-2011 Michael Buesch <m@bues.ch>
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License along
  19. # with this program; if not, write to the Free Software Foundation, Inc.,
  20. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. """
  22. import sys
  23. import re
  24. import math
  25. import random
  26. class TOPException(Exception): pass
  27. def byte2int(byte):
  28. if isinstance(byte, int): # It's already int
  29. assert 0 <= byte <= 0xFF
  30. return byte
  31. if isinstance(byte, str): # Compat for old code
  32. assert len(byte) == 1
  33. return ord(byte)
  34. assert isinstance(byte, (bytes, bytearray))
  35. assert len(byte) == 1
  36. return byte[0]
  37. def int2byte(integer):
  38. if isinstance(integer, (bytes, bytearray)): # It's already bytes
  39. return bytes(integer)
  40. assert isinstance(integer, int)
  41. assert 0 <= integer <= 0xFF
  42. return b"%c" % integer
  43. def hex2bin(hexdata):
  44. assert(len(hexdata) % 2 == 0)
  45. return b"".join(int2byte(int(hexdata[i:i+2], 16))
  46. for i in range(len(hexdata), 2))
  47. def byte2hex(byte):
  48. return "%02X" % byte2int(byte)
  49. def bytes2hex(bindata):
  50. if not bindata:
  51. return ""
  52. return "".join(byte2hex(b) for b in bindata)
  53. def byte2ascii(c):
  54. c = byte2int(c)
  55. if c >= 32 and c <= 126:
  56. return "%c" % c
  57. return "."
  58. def bytes2ascii(bindata):
  59. if not bindata:
  60. return ""
  61. return "".join(byte2ascii(b) for b in bindata)
  62. def str2bool(string):
  63. string = str(string).lower().strip()
  64. if string in ("false", "off", "no"):
  65. return False
  66. if string in ("true", "on", "yes"):
  67. return True
  68. try:
  69. return bool(int(string, 10))
  70. except (ValueError) as e:
  71. pass
  72. return None
  73. def genRandomBlob(size):
  74. return b"".join(int2byte(random.randint(0, 0xFF))
  75. for x in range(size))
  76. def bit(bitNr):
  77. return 1 << bitNr
  78. def nrBitsSet(integer):
  79. count = 0
  80. while integer:
  81. count += (integer & 1)
  82. integer >>= 1
  83. return count
  84. def roundup(x, y):
  85. x, y = int(x), int(y)
  86. return ((x + (y - 1)) // y) * y
  87. hexdump_re = re.compile(r"0x[0-9a-fA-F]+:\s+([0-9a-fA-F\s]+)\s*.*")
  88. def parseHexdump(dump):
  89. try:
  90. if isinstance(dump, (bytes, bytearray)):
  91. dump = dump.decode("ASCII")
  92. binData = []
  93. for line in dump.splitlines():
  94. line = line.strip()
  95. if not line:
  96. continue
  97. m = hexdump_re.match(line)
  98. if not m:
  99. raise TOPException("Invalid hexdump format (regex failure)")
  100. data = m.group(1)
  101. idx = data.find(" ")
  102. if idx >= 0:
  103. data = data[:idx] # Strip ascii section
  104. data = data.replace(" ", "")
  105. if len(data) % 2 != 0:
  106. raise TOPException("Invalid hexdump format (odd bytestring len)")
  107. for i in range(0, len(data), 2):
  108. byte = int(data[i:i+2], 16)
  109. binData.append(int2byte(byte))
  110. return b"".join(binData)
  111. except (ValueError, UnicodeError) as e:
  112. raise TOPException("Invalid hexdump format (Integer error)")
  113. def generateHexdump(mem):
  114. ret = ""
  115. asc = ""
  116. for i in range(0, len(mem)):
  117. if i % 16 == 0 and i != 0:
  118. ret += " " + asc + "\n"
  119. asc = ""
  120. if i % 16 == 0:
  121. ret += "0x%04X: " % i
  122. c = byte2int(mem[i])
  123. ret += "%02X" % c
  124. if (i % 2 != 0):
  125. ret += " "
  126. asc += byte2ascii(mem[i])
  127. ret += " " + asc + "\n\n"
  128. return ret
  129. def dumpMem(mem):
  130. sys.stdout.write(generateHexdump(mem))
  131. class IO_ihex(object):
  132. TYPE_DATA = 0
  133. TYPE_EOF = 1
  134. TYPE_ESAR = 2
  135. TYPE_SSAR = 3
  136. TYPE_ELAR = 4
  137. TYPE_SLAR = 5
  138. def autodetect(self, data):
  139. try:
  140. self.toBinary(data)
  141. except (TOPException) as e:
  142. return False
  143. return True
  144. def toBinary(self, ihexData, addressRange=None, defaultBytes=b"\xFF"):
  145. binData = []
  146. checksumWarned = False
  147. doublewriteWarned = False
  148. addrBias = addressRange.startAddress if addressRange else 0
  149. try:
  150. if isinstance(ihexData, (bytes, bytearray)):
  151. ihexData = ihexData.decode("ASCII")
  152. lines = ihexData.splitlines()
  153. hiAddr = 0
  154. segment = 0
  155. for line in lines:
  156. line = line.strip()
  157. if len(line) == 0:
  158. continue
  159. if len(line) < 11 or (len(line) - 1) % 2 != 0:
  160. raise TOPException("Invalid IHEX format (length error)")
  161. if line[0] != ':':
  162. raise TOPException("Invalid IHEX format (magic error)")
  163. count = int(line[1:3], 16)
  164. if len(line) != count * 2 + 11:
  165. raise TOPException("Invalid IHEX format (count error)")
  166. addr = (int(line[3:5], 16) << 8) | int(line[5:7], 16)
  167. addr |= hiAddr << 16
  168. addr += segment * 16
  169. if hiAddr and segment:
  170. print("WARNING: IHEX has ESAR and ELAR record")
  171. type = int(line[7:9], 16)
  172. checksum = 0
  173. for i in range(1, len(line), 2):
  174. byte = int(line[i:i+2], 16)
  175. checksum = (checksum + byte) & 0xFF
  176. checksum = checksum & 0xFF
  177. if checksum != 0 and not checksumWarned:
  178. checksumWarned = True
  179. print("WARNING: Invalid IHEX format (checksum error)")
  180. if type == self.TYPE_EOF:
  181. break
  182. if type == self.TYPE_ESAR:
  183. if count != 2:
  184. raise TOPException("Invalid IHEX format (inval ESAR)")
  185. segment = (int(line[9:11], 16) << 8) | int(line[11:13], 16)
  186. continue
  187. if type == self.TYPE_ELAR:
  188. if count != 2:
  189. raise TOPException("Invalid IHEX format (inval ELAR)")
  190. hiAddr = (int(line[9:11], 16) << 8) | int(line[11:13], 16)
  191. continue
  192. if addressRange and addr < addressRange.startAddress:
  193. continue
  194. if addressRange and addr > addressRange.endAddress:
  195. continue
  196. if type == self.TYPE_DATA:
  197. if len(binData) < addr - addrBias + count: # Reallocate
  198. bytesToAdd = addr - addrBias + count - len(binData)
  199. for i in range(bytesToAdd):
  200. defOffs = len(binData) % len(defaultBytes)
  201. binData += [ defaultBytes[defOffs], ]
  202. for i in range(9, 9 + count * 2, 2):
  203. byte = int2byte(int(line[i:i+2], 16))
  204. offset = (i - 9) // 2 + addr - addrBias
  205. if binData[offset] != defaultBytes[offset % len(defaultBytes)] and \
  206. not doublewriteWarned:
  207. doublewriteWarned = True
  208. print("Invalid IHEX format (Wrote twice to same location)")
  209. binData[offset] = byte
  210. continue
  211. raise TOPException("Invalid IHEX format (unsup type %d)" % type)
  212. except (ValueError, UnicodeError) as e:
  213. raise TOPException("Invalid IHEX format (digit format)")
  214. return b"".join(binData)
  215. def fromBinary(self, binData):
  216. ihex = []
  217. addr = 0
  218. for i in range(0, len(binData), 16):
  219. if addr > 0xFFFF:
  220. checksum = 0
  221. ihex.append(":%02X%04X%02X" % (2, 0, self.TYPE_ELAR))
  222. checksum += 2 + 0 + 0 + self.TYPE_ELAR
  223. a = (addr >> 16) & 0xFFFF
  224. ihex.append("%04X" % a)
  225. checksum += ((a >> 8) & 0xFF) + (a & 0xFF)
  226. checksum = ((checksum ^ 0xFF) + 1) & 0xFF
  227. ihex.append("%02X\n" % checksum)
  228. addr -= 0xFFFF
  229. checksum = 0
  230. size = min(len(binData) - i, 16)
  231. ihex.append(":%02X%04X%02X" % (size, addr, self.TYPE_DATA))
  232. checksum += size + ((addr >> 8) & 0xFF) + (addr & 0xFF) + self.TYPE_DATA
  233. for j in range(0, size):
  234. data = byte2int(binData[i + j])
  235. checksum = (checksum + data) & 0xFF
  236. ihex.append("%02X" % data)
  237. checksum = ((checksum ^ 0xFF) + 1) & 0xFF
  238. ihex.append("%02X\n" % checksum)
  239. addr += size
  240. ihex.append(":00000001FF\n")
  241. return "".join(ihex)
  242. class IO_ahex(object):
  243. def autodetect(self, data):
  244. try:
  245. self.toBinary(data)
  246. except (TOPException) as e:
  247. return False
  248. return True
  249. def toBinary(self, data, addressRange=None, defaultBytes=b"\xFF"):
  250. # defaultBytes is ignored
  251. binData = parseHexdump(data)
  252. if addressRange:
  253. binData = binData[addressRange.startAddress : addressRange.endAddress + 1]
  254. return binData
  255. def fromBinary(self, data):
  256. return generateHexdump(data)
  257. class IO_binary(object):
  258. def autodetect(self, data):
  259. return True
  260. def toBinary(self, data, addressRange=None, defaultBytes=b"\xFF"):
  261. # defaultBytes is ignored
  262. if addressRange:
  263. data = data[addressRange.startAddress : addressRange.endAddress + 1]
  264. return data
  265. def fromBinary(self, data):
  266. return data
  267. def IO_autodetect(data):
  268. "Returns an IO_... object for the data."
  269. if IO_ihex().autodetect(data):
  270. return IO_ihex
  271. elif IO_ahex().autodetect(data):
  272. return IO_ahex
  273. elif IO_binary().autodetect(data):
  274. return IO_binary
  275. assert(0) # Can't reach, because binary will always match.