summaryrefslogtreecommitdiff
path: root/asm.py
diff options
context:
space:
mode:
Diffstat (limited to 'asm.py')
-rw-r--r--asm.py154
1 files changed, 154 insertions, 0 deletions
diff --git a/asm.py b/asm.py
new file mode 100644
index 0000000..7a20f8b
--- /dev/null
+++ b/asm.py
@@ -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()