asm_compiler.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. #!/usr/bin/python3
  2. import re
  3. import math
  4. import traceback
  5. from typing import Dict, List
  6. label_re = re.compile(r"^[\w$#@~.?]+$", re.IGNORECASE)
  7. hex_re = re.compile(r"^[0-9a-f]+$", re.IGNORECASE)
  8. bin_re = re.compile(r"^[0-1_]+$", re.IGNORECASE)
  9. oct_re = re.compile(r"^[0-8]+$", re.IGNORECASE)
  10. args_re = re.compile("(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)", re.IGNORECASE)
  11. func_re = re.compile("^([\w$#@~.?]+)\s*([|^<>+\-*/%@]{1,2})\s*([\w$#@~.?]+)$", re.IGNORECASE)
  12. func2_re = re.compile("^([\w$#@~.?]+)\s*\(\s*([\w$#@~.?]+)*\)$", re.IGNORECASE)
  13. brackets_re = re.compile(r"(\((?:\(??[^\(]*?\)))", re.IGNORECASE)
  14. secs_re = re.compile("^([\d]+)x([\d]+)x([\d]+)$", re.IGNORECASE)
  15. funcc_re = re.compile("^([\w$#@~.?]+)\(([\w,]+)\)(.*)", re.IGNORECASE)
  16. MAX_INT_BYTES = 12
  17. def args2operands(args):
  18. operands = ['"' + a[1] + '"' if a[0] == '"' else a[1] for a in args_re.findall(args or '') if a[1]]
  19. return operands
  20. def match(regex, s):
  21. return regex.match(s) is not None
  22. class CompilingError(Exception):
  23. def __init__(self, message):
  24. self.message = message
  25. class InstructionError(Exception):
  26. def __init__(self, message):
  27. self.message = message
  28. class Instruction:
  29. def __init__(self, name: str, opcode: str, operands=0, alias=None):
  30. name = name.strip().lower()
  31. if not name or not name.isalnum():
  32. raise InstructionError(f"Invalid instruction name '{name}'")
  33. self.name = name.strip()
  34. self.alias = alias or []
  35. self.reg_operands = 0
  36. opcode = opcode.replace('_', '')
  37. if len(opcode) == 8:
  38. if opcode[4:6] == '??':
  39. self.reg_operands += 1
  40. if opcode[6:8] == '??':
  41. self.reg_operands += 1
  42. else:
  43. raise CompilingError("Invalid opcode: " + opcode)
  44. self.opcode = int(opcode.replace('?', '0'), 2)
  45. self.imm_operands = operands
  46. self.compiler = None
  47. @property
  48. def length(self):
  49. return self.imm_operands + 1
  50. def __len__(self):
  51. return self.length
  52. def _gen_instr(self, regs):
  53. instr = self.opcode
  54. if len(regs) != self.reg_operands:
  55. raise CompilingError(f"Invalid number of registers: set {len(regs)}, required: {self.reg_operands}")
  56. if len(regs) == 2:
  57. if regs[1] is None:
  58. raise CompilingError(f"Unable to decode register name {regs[1]}")
  59. if regs[0] is None:
  60. raise CompilingError(f"Unable to decode register name {regs[0]}")
  61. instr |= regs[0] << 2 | regs[1]
  62. elif len(regs) == 1:
  63. if regs[0] is None:
  64. raise CompilingError(f"Unable to decode register name {regs[0]}")
  65. instr |= regs[0] << 2
  66. return instr.to_bytes(1, 'little') # Order does not matter with 1 byte
  67. def compile(self, operands, scope):
  68. regs = []
  69. for reg in operands[:self.reg_operands]:
  70. regs.append(self.compiler.decode_reg(reg))
  71. imm = self.compiler.decode_with_labels(operands[self.reg_operands:], scope)
  72. if len(imm) != self.imm_operands:
  73. raise CompilingError(f"Instruction {self.name} has invalid argument size {len(imm)} != {self.imm_operands},"
  74. f" supplied args: 0x{imm.hex()}")
  75. instr = self._gen_instr(regs)
  76. return instr + imm
  77. class Section:
  78. def __init__(self):
  79. self.instr = []
  80. self.data = b''
  81. self.count = 0
  82. self.width = 1
  83. self.length = 3
  84. self.options = {}
  85. self.depth = 2 ** 8
  86. @property
  87. def bin_width(self):
  88. if 'bin_width' in self.options and self.options['bin_width'].isdecimal():
  89. return int(self.options['bin_width'])
  90. return self.width * 8
  91. @property
  92. def fill_bits(self):
  93. if 'fill_bits' in self.options and self.options['fill_bits'].isdecimal():
  94. return int(self.options['fill_bits'])
  95. return self.depth * self.width * 8
  96. class Compiler:
  97. def __init__(self, address_size=2, byte_order='little'):
  98. self.instr_db: Dict[str, Instruction] = {}
  99. self.data = []
  100. self.labels = {}
  101. self.macros = {}
  102. self.order = byte_order
  103. self.regnames = {}
  104. self.address_size = address_size
  105. def decode_reg(self, s: str):
  106. s = s.strip()
  107. if s in self.regnames:
  108. return self.regnames[s]
  109. raise CompilingError(f"Unrecognised register name: {s}")
  110. def decode_bytes(self, s: str):
  111. s = s.strip()
  112. typ = ""
  113. # Decimal numbers
  114. if (s.startswith('+') or s.startswith('-')) and s[1:].isnumeric():
  115. typ = 'int'
  116. elif s.isnumeric():
  117. typ = 'uint'
  118. elif s.endswith('d') and s[:-1].isnumeric():
  119. s = s[:-1]
  120. typ = 'uint'
  121. elif s.startswith('0d') and s[2:].isnumeric():
  122. s = s[2:]
  123. typ = 'uint'
  124. # Hexadecimal numbers
  125. elif s.startswith('0') and s.endswith('h') and match(hex_re, s[1:-1]):
  126. s = s[1:-1]
  127. typ = 'hex'
  128. elif (s.startswith('$0') or s.startswith('0x') or s.startswith('$0')) and match(hex_re, s[2:]):
  129. s = s[2:]
  130. typ = 'hex'
  131. # Octal numbers
  132. elif (s.endswith('q') or s.endswith('o')) and match(oct_re, s[:-1]):
  133. s = s[:-1]
  134. typ = 'oct'
  135. elif (s.startswith('0q') or s.startswith('0o')) and match(oct_re, s[2:]):
  136. s = s[2:]
  137. typ = 'oct'
  138. # Binary number
  139. elif (s.endswith('b') or s.endswith('y')) and match(bin_re, s[:-1]):
  140. s = s[:-1].replace('_', '')
  141. typ = 'bin'
  142. elif (s.startswith('0b') or s.startswith('0y')) and match(bin_re, s[2:]):
  143. s = s[2:].replace('_', '')
  144. typ = 'bin'
  145. # ASCII
  146. elif s.startswith("'") and s.endswith("'") and len(s) == 3:
  147. s = ord(s[1:-1]).to_bytes(1, self.order)
  148. typ = 'ascii'
  149. elif (s.startswith("'") and s.endswith("'")) or (s.startswith('"') and s.endswith('"')):
  150. s = s[1:-1].encode('utf-8').decode("unicode_escape").encode('utf-8')
  151. typ = 'string'
  152. # Convert with limits
  153. if typ == 'uint':
  154. numb = int(s)
  155. for i in range(1, MAX_INT_BYTES + 1):
  156. if numb < 2 ** (i * 8):
  157. return numb.to_bytes(i, self.order)
  158. elif typ == 'int':
  159. numb = int(s)
  160. for i in range(1, MAX_INT_BYTES + 1):
  161. if -2 ** (i * 7) < numb < 2 ** (i * 7):
  162. return numb.to_bytes(i, self.order)
  163. elif typ == 'hex':
  164. numb = int(s, 16)
  165. return numb.to_bytes(int(len(s) / 2) + len(s) % 2, self.order)
  166. elif typ == 'oct':
  167. numb = int(s, 8)
  168. for i in range(1, 9):
  169. if -2 ** (i * 7) < i < 2 ** (i * 8):
  170. return numb.to_bytes(i, self.order)
  171. elif typ == 'bin':
  172. numb = int(s, 2)
  173. return numb.to_bytes(int(len(s) / 8) + len(s) % 8, self.order)
  174. else:
  175. return s
  176. def _decode_labels(self, arg, scope):
  177. immx = self.decode_bytes(arg)
  178. if isinstance(immx, str):
  179. if immx.startswith('.'):
  180. immx = scope + immx
  181. if immx in self.labels:
  182. return self.labels[immx]
  183. else:
  184. raise CompilingError(f"Unknown label: {immx}")
  185. elif isinstance(immx, bytes):
  186. return immx
  187. def decode_with_labels(self, args, scope):
  188. data = b''
  189. for arg in args:
  190. if isinstance(arg, str):
  191. funcm = func_re.match(arg)
  192. if funcm is not None:
  193. g = funcm.groups()
  194. left = self._decode_labels(g[0], scope)
  195. right = self._decode_labels(g[2], scope)
  196. data += self.proc_func(left, right, g[1])
  197. continue
  198. data += self._decode_labels(arg, scope)
  199. return data
  200. def add_reg(self, name, val):
  201. self.regnames[name] = val
  202. self.regnames['$' + name] = val
  203. def add_instr(self, instr: Instruction):
  204. instr.compiler = self
  205. operands = instr.reg_operands + instr.imm_operands
  206. if instr.name in self.instr_db:
  207. raise InstructionError(f"Instruction {instr.name} operands={operands} duplicate!")
  208. self.instr_db[instr.name] = instr
  209. for alias in instr.alias:
  210. if alias.lower() in self.instr_db:
  211. raise InstructionError(f"Instruction alias {alias} operands={operands} duplicate!")
  212. self.instr_db[alias.lower()] = instr
  213. def proc_func(self, left, right, op):
  214. leftInt = int.from_bytes(left, self.order)
  215. rightInt = int.from_bytes(right, self.order)
  216. if op == '|':
  217. result = leftInt | rightInt
  218. elif op == '^':
  219. result = leftInt ^ rightInt
  220. elif op == '&':
  221. result = leftInt & rightInt
  222. elif op == '<<':
  223. result = leftInt << rightInt
  224. elif op == '>>':
  225. result = leftInt >> rightInt
  226. elif op == '+':
  227. result = leftInt + rightInt
  228. elif op == '-':
  229. result = leftInt - rightInt
  230. elif op == '*':
  231. result = leftInt * rightInt
  232. elif op == '/' or op == '//':
  233. result = leftInt // rightInt
  234. elif op == '%' or op == '%%':
  235. result = leftInt % rightInt
  236. elif op == '@':
  237. return bytes([left[len(left) - rightInt - 1]])
  238. else:
  239. raise CompilingError(f"Invalid function operation {op}")
  240. return result.to_bytes(len(left), self.order)
  241. def __code_compiler(self, file, lnum, line_args, csect, scope, macro):
  242. builtin_cmds = {'db', 'dbe'}
  243. if line_args[0].endswith(':') and label_re.match(line_args[0][:-1]) is not None:
  244. # Must be label
  245. label = line_args[0][:-1]
  246. line_args = line_args[1:]
  247. if label.startswith('.'):
  248. if scope is None:
  249. raise CompilingError(f"No local scope for {label}!")
  250. label = scope + label
  251. elif not macro:
  252. scope = label
  253. if label in self.labels:
  254. raise CompilingError(f"Label {label} duplicate")
  255. self.labels[label] = csect.count.to_bytes(csect.length, self.order)
  256. if len(line_args) == 0:
  257. return scope
  258. elif len(line_args) == 1:
  259. args = None
  260. else:
  261. args = line_args[1]
  262. instr_name = line_args[0].lower()
  263. # Builtin instructions
  264. if instr_name == 'db':
  265. data = self.decode_with_labels(args2operands(args), scope)
  266. if len(data) % csect.width != 0:
  267. fill = csect.width - (len(data) % csect.width)
  268. data += b'\x00' * fill
  269. csect.instr.append(data)
  270. csect.count += len(data) // csect.width
  271. return scope
  272. if instr_name == 'dbe':
  273. try:
  274. fill = int(args[0])
  275. except ValueError:
  276. raise CompilingError(f"Instruction 'dbe' invalid argument, must be a number")
  277. except IndexError:
  278. raise CompilingError(f"Instruction 'dbe' invalid argument count! Must be 1")
  279. if fill % csect.width != 0:
  280. fill += csect.width - (fill % csect.width)
  281. data = b'\x00' * fill
  282. csect.instr.append(data)
  283. csect.count += len(data) // csect.width
  284. return scope
  285. if instr_name == '%def':
  286. ops = args2operands(args)
  287. if len(ops) != 2:
  288. raise CompilingError(f"Command '%def' hsa invalid argument count! Must be 2, found {len(ops)}")
  289. self.labels[scope + '.' + ops[0]] = ops[1].lower()
  290. return scope
  291. if instr_name in self.macros:
  292. argsp = args2operands(args)
  293. if len(argsp) != self.macros[instr_name][0]:
  294. raise CompilingError(f"Invalid macro argument count!")
  295. self.macros[instr_name][3] += 1 # How many time macro been used (used for macro labels)
  296. mlabel = f'{instr_name}.{self.macros[instr_name][3]}'
  297. for slnum, sline in enumerate(self.macros[instr_name][1]):
  298. slnum += 1
  299. mline = sline.copy()
  300. for i, mline0 in enumerate(mline):
  301. for j in range(len(argsp)):
  302. mline0 = mline0.replace(f'%{j + 1}', argsp[j])
  303. mline[i] = re.sub(r'(%{2})([\w$#~.?]+)', mlabel + r'.\2', mline0)
  304. try:
  305. scope = self.__code_compiler(file, lnum, mline, csect, scope, True)
  306. except CompilingError as e:
  307. print(f"ERROR {file}:{self.macros[instr_name][2] + slnum}: {e.message}")
  308. raise CompilingError(f"Previous error")
  309. return scope
  310. if scope + '.' + instr_name in self.labels:
  311. instr_name = self.labels[scope + '.' + instr_name] # replace with definition
  312. if instr_name not in self.instr_db:
  313. raise CompilingError(f"Instruction '{instr_name}' not recognised!")
  314. # replace args with %def
  315. ops = args2operands(args)
  316. for i, arg in enumerate(ops):
  317. if scope + '.' + arg in self.labels:
  318. ops[i] = self.labels[scope + '.' + arg]
  319. args = ','.join(ops)
  320. instr_obj = self.instr_db[instr_name.lower()]
  321. csect.instr.append((instr_obj, args, lnum, scope))
  322. csect.count += instr_obj.length
  323. return scope
  324. @staticmethod
  325. def __line_generator(code):
  326. for lnum, line in enumerate(code):
  327. lnum += 1
  328. line = line.split(';', 1)[0]
  329. line = re.sub(' +', ' ', line) # replace multiple spaces
  330. line = line.strip()
  331. line_args = [l.strip() for l in line.split(' ', 2)]
  332. # line_args = list(filter(lambda x: len(x) > 0, line_args))
  333. if len(line_args) == 0 or line_args[0] == '':
  334. continue
  335. yield lnum, line_args
  336. def compile_file(self, file):
  337. try:
  338. with open(file, 'r') as f:
  339. data = self.compile(file, f.readlines())
  340. return data
  341. except IOError:
  342. return None
  343. def compile(self, file, code):
  344. failure = False
  345. sections: Dict[str, Section] = {}
  346. csect = None
  347. scope = None
  348. macro = None
  349. for lnum, line_args in self.__line_generator(code):
  350. try:
  351. # Inside macro
  352. if macro is not None:
  353. if line_args[0].lower() == '%endmacro':
  354. macro = None
  355. continue
  356. self.macros[macro][1].append(line_args)
  357. continue
  358. # Section
  359. if line_args[0].lower() == 'section':
  360. if len(line_args) < 2:
  361. raise CompilingError(f"Invalid section arguments!")
  362. section_name = line_args[1].lower()
  363. if section_name not in sections:
  364. s = Section()
  365. options = {}
  366. if len(line_args) == 3:
  367. for sp in line_args[2].split(','):
  368. if '=' not in sp:
  369. continue
  370. sp2 = sp.split('=', 1)
  371. key = sp2[0].lower()
  372. val = sp2[1]
  373. s.options[key] = val
  374. if not val.isdecimal():
  375. continue
  376. if key == 'depth':
  377. s.depth = int(val)
  378. if key == 'width':
  379. s.width = int(val)
  380. # m = secs_re.match(line_args[2])
  381. # if m is not None:
  382. # g = m.groups()
  383. # s.width = int(g[0])
  384. # s.length = int(g[1])
  385. # s.size = int(g[2])
  386. # else:
  387. # raise CompilingError(f"Invalid section argument: {line_args[2]}")
  388. sections[section_name] = s
  389. csect = sections[section_name]
  390. continue
  391. # Macros
  392. elif line_args[0].lower() == '%define':
  393. if len(line_args) != 3:
  394. raise CompilingError(f"Invalid %define arguments!")
  395. self.labels[line_args[1]] = self.decode_bytes(line_args[2])
  396. continue
  397. elif line_args[0].lower() == '%macro':
  398. if len(line_args) != 3:
  399. raise CompilingError(f"Invalid %macro arguments!")
  400. if line_args[1] in self.macros:
  401. raise CompilingError(f"Macro '{line_args[1]}' already in use")
  402. if not line_args[2].isdigit():
  403. raise CompilingError(f"%macro argument 2 must be a number")
  404. macro = line_args[1].lower()
  405. self.macros[macro] = [int(line_args[2]), [], lnum, 0]
  406. continue
  407. elif line_args[0].lower() == '%include':
  408. if len(line_args) != 2:
  409. raise CompilingError(f"Invalid %include arguments!")
  410. raise CompilingError(f"%include is not implemented yet") # TODO: Complete
  411. continue
  412. elif line_args[0].lower() == '%ifdef':
  413. if len(line_args) != 1:
  414. raise CompilingError(f"Invalid %ifdef arguments!")
  415. raise CompilingError(f"%ifdef is not implemented yet") # TODO: Complete
  416. continue
  417. elif line_args[0].lower() == '%ifndef':
  418. if len(line_args) != 1:
  419. raise CompilingError(f"Invalid %ifndef arguments!")
  420. raise CompilingError(f"%ifndef is not implemented yet") # TODO: Complete
  421. continue
  422. elif line_args[0].lower() == '%else':
  423. if len(line_args) != 0:
  424. raise CompilingError(f"Invalid %else arguments!")
  425. raise CompilingError(f"%else is not implemented yet") # TODO: Complete
  426. continue
  427. elif line_args[0].lower() == '%endif':
  428. if len(line_args) != 0:
  429. raise CompilingError(f"Invalid %endif arguments!")
  430. raise CompilingError(f"%endif is not implemented yet") # TODO: Complete
  431. continue
  432. if csect is None:
  433. raise CompilingError(f"No section defined!")
  434. scope = self.__code_compiler(file, lnum, line_args, csect, scope, False)
  435. except CompilingError as e:
  436. failure = True
  437. print(f"ERROR {file}:{lnum}: {e.message}")
  438. for section in sections.values():
  439. for instr_tuple in section.instr:
  440. if isinstance(instr_tuple, bytes):
  441. section.data += instr_tuple
  442. continue
  443. instr, args, lnum, scope = instr_tuple
  444. try:
  445. operands = args2operands(args)
  446. section.data += instr.compile(operands, scope)
  447. except CompilingError as e:
  448. failure = True
  449. print(f"ERROR {file}:{lnum}: {e.message}")
  450. if failure:
  451. return None
  452. # return {k: (v.width, v.length, v.size, v.data) for k, v in sections.items()}
  453. return sections
  454. def decompile(self, binary):
  455. addr = 0
  456. res = []
  457. ibin = iter(binary)
  458. for data in ibin:
  459. norm0 = int(data)
  460. norm1 = norm0 & int('11110011', 2)
  461. norm2 = norm0 & int('11110000', 2)
  462. for instr in self.instr_db.values():
  463. if not ((instr.reg_operands == 0 and norm0 == instr.opcode) or
  464. (instr.reg_operands == 1 and norm1 == instr.opcode) or
  465. (instr.reg_operands == 2 and norm2 == instr.opcode)):
  466. continue
  467. asm = f'{addr:04x}: {instr.name.upper().ljust(6)}'
  468. args = []
  469. raw = format(norm0, '02x')
  470. if instr.reg_operands > 0:
  471. args.append(f'r{(norm0 & 12) >> 2}')
  472. if instr.reg_operands > 1:
  473. args.append(f'r{(norm0 & 3)}')
  474. if instr.imm_operands > 0:
  475. b = '0x'
  476. for i in range(instr.imm_operands):
  477. try:
  478. bi = format(int(next(ibin)), '02x')
  479. except StopIteration:
  480. break
  481. b += bi
  482. raw += bi
  483. addr += 1
  484. args.append(b)
  485. line = asm + ', '.join(args)
  486. tabs = ' ' * (27 - int(len(line)))
  487. res.append(f'{line}{tabs}[{raw}]')
  488. break
  489. addr += 1
  490. return '\n'.join(res)
  491. def main(asmc):
  492. import sys
  493. import argparse
  494. from os import path, mkdir
  495. from bitstring import BitArray
  496. parser = argparse.ArgumentParser(description='Assembly compiler', add_help=True)
  497. parser.add_argument('file', help='Files to compile')
  498. parser.add_argument('-o', '--output', help='Output directory')
  499. parser.add_argument('-f', '--force', action='store_true', help='Force override output file')
  500. parser.add_argument('-s', '--stdout', action='store_true', help='Print to stdout')
  501. parser.add_argument('-D', '--decompile', action='store_true', help='Print decompiled')
  502. args = parser.parse_args(sys.argv[1:])
  503. bname = path.basename(args.file).rsplit('.', 1)[0]
  504. if not path.isfile(args.file):
  505. print(f'No file {args.file}!')
  506. sys.exit(1)
  507. output_dir = args.output or path.dirname(args.file)
  508. if not path.exists(output_dir):
  509. mkdir(output_dir)
  510. data = asmc.compile_file(args.file)
  511. if data is not None:
  512. for sec_name in data:
  513. sec = data[sec_name]
  514. if sec_name == '.text' and args.decompile:
  515. print(asmc.decompile(sec.data))
  516. output = path.join(output_dir, f'{bname}.{sec_name.strip(".")}.o')
  517. if not args.stdout and not args.force:
  518. if path.isfile(output):
  519. print(f'Output file already exists {output}!')
  520. inval = ''
  521. while True:
  522. inval = input('Override? [y/n]: ')
  523. inval = inval.lower().strip()
  524. if inval != 'y' and inval != 'n':
  525. print('Please type y or n')
  526. continue
  527. break
  528. if inval == 'n':
  529. continue
  530. parity = 0
  531. if 'parity' in sec.options and sec.options['parity'].isdigit():
  532. parity = int(sec.options['parity'])
  533. content = sec.data
  534. used = -1
  535. # if fill_bits == 0 and len(sec.data) < sec.depth:
  536. # used = len(content)/sec.depth
  537. # content += (sec.depth*sec.width - len(content)) * bytearray(b'\x00')
  538. # converting to BitArray
  539. binary = BitArray()
  540. for i in range(sec.depth):
  541. if len(binary) >= sec.fill_bits: # FIXME: better solution
  542. break
  543. cell = content[i*sec.width:(i+1)*sec.width]
  544. cell_bytes = BitArray(cell)
  545. if len(cell_bytes) < sec.width: # we can assume content ends here
  546. if used == -1:
  547. used = len(binary)/sec.fill_bits
  548. f = '0' * (sec.bin_width - len(cell_bytes))
  549. cell_bytes.append(BitArray(bin=f))
  550. cell_bytes = cell_bytes[len(cell_bytes)-sec.bin_width:]
  551. binary.append(cell_bytes)
  552. if parity > 0 and i % parity == 1:
  553. parity_bit = str(binary[-(i*parity*sec.bin_width):].bin.count('1') % 2)
  554. binary.append(BitArray(bin=parity_bit))
  555. if used == -1: # this is bad. Content is bigger than memory itself
  556. used = len(binary)/sec.fill_bits
  557. # if fill_bits > 0 and len(binary) < fill_bits:
  558. # used = len(binary) / fill_bits
  559. # fill = (fill_bits - len(binary)) * BitArray(bin='0')
  560. # binary.append(fill)
  561. with open(output, 'wb') as f:
  562. f.write(binary.bytes)
  563. print(f'Saved {sec_name} to "{output}", used {used*100:.2f}% [{int(len(binary)/8 * used)}B of {len(binary)//8}B]')
  564. else:
  565. print(f'Failed to compile {args.file}!')
  566. sys.exit(1)
  567. sys.exit(0)