#!/usr/bin/env python3 from mnems import mnems # print(mnems) import sys import collections pc = 0 labels = {} local_labels = {} # map of labelname -> (dest, ifn) label_wants = collections.defaultdict(set) local_label_wants = collections.defaultdict(set) output = bytearray() class Wrong(Exception): def __init__(self, lineno, msg): self.lineno = lineno self.msg = msg def __str__(self): return f"{self.lineno}: {self.msg}" def assemble_line(args, lineno): match args: case ["c", label] if label in labels: emit_call(labels[label]) case ["c", label]: label_wants[label].add((pc,call_instr)) emit(0x8000) case ['b', label] if label in local_labels: emit_obranch(local_labels[label]) case ['b', label]: local_label_wants[label].add((pc, obranch_instr)) emit(0x5000) case ['j', label] if label in local_labels: emit_jump(local_labels[label]) case ['j', label]: local_label_wants[label].add((pc, jump_instr)) emit(0x4000) case ["i", i1]: assemble_line(["i", i1, "nop"], lineno) case ["i", i1, i2]: opc1 = mnems.index(i1) opc2 = mnems.index(i2) emit_instrs(opc1, opc2) case ["l", val]: emit_lit(int(val,16)) case [':', label]: if label in labels: raise Wrong(lineno, "label defined twice "+label) labels[label] = pc for (dest, ifn) in label_wants[label]: put_at(dest, ifn(pc, dest)) del label_wants[label] local_labels.clear() for u,v in local_label_wants.items(): raise Wrong(lineno, "unfulfilled local label "+u) local_label_wants.clear() case ['%', label]: if label in local_labels: raise Wrong(lineno, "local label defined twice "+label) local_labels[label] = pc for (dest, ifn) in local_label_wants[label]: put_at(dest, ifn(pc, dest)) del local_label_wants[label] case []: pass case _: raise Wrong(lineno, "unknown wordtype") def emit_call(addr): emit(call_instr(addr)) def emit_instrs(opc1, opc2): emit(instrs_instr(opc1, opc2)) def emit_lit(val): emit(lit_instr(val)) def emit_obranch(addr): emit(obranch_instr(addr, origin=pc)) def emit_jump(addr): emit(jump_instr(addr, origin=pc)) def emit(word): oldlen = len(output) output.extend(word.to_bytes(2)) global pc pc += 2 assert len(output) == 2 + oldlen, "beeite" def put_at(addr, word): assert addr % 2 == 0, "ooeu" output[addr:addr+2] = word.to_bytes(2) def lit_instr(val): return (val&0b1111111111111) | 0b0110000000000000 def instrs_instr(opc1, opc2): return (opc1 << 7) | opc2 def call_instr(addr,*_): return (addr>>1)|0b1000000000000000 def obranch_instr(target, origin): rel = target - origin + 2048 return (rel&0b111111111111)|0b0101000000000000 def jump_instr(target, origin): rel = target - origin + 2048 return (rel&0b111111111111)|0b0100000000000000 import sys def main(): for ix, line in enumerate(sys.stdin): line = line.strip() args = line.split() assemble_line(args, ix+1) for u,v in label_wants.items(): print("unfulfilled global label",u, file=sys.stderr) sys.stdout.buffer.write(output) if __name__ == "__main__": main()