diff options
Diffstat (limited to 'asm.py')
-rw-r--r-- | asm.py | 154 |
1 files changed, 154 insertions, 0 deletions
@@ -0,0 +1,154 @@ +mnems = """ +nop +rot +nrt +dup +swp +ovr +drp +tck +nip +equ +neq +add +sub +mul +div +neg +slt +ult +sle +ule +lod +sto +psh +pop +""".split() +mnems.extend(['???']*(64-len(mnems))) +def opws(kw): + for k in range(16): + mnems.append(kw + str(k)) +opws("mark") # who is mark +opws("loc") +opws("ret") +opws("lit") + +# 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(0) + case ['b', label] if label in local_labels: + emit_obranch(local_labels[label]) + case ['b', label]: + local_label_wants[label].add((pc, obranch_instr)) + case ['j', label] if label in local_labels: + emit_jump(local_labels[label]) + case ['j', label]: + local_label_wants[label].add((pc, jump_instr)) + + 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)) + 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): + output.extend(word.to_bytes(2)) + global pc + pc += 2 + +def put_at(addr, word): + output[addr:addr+1] = 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() |