aboutsummaryrefslogtreecommitdiffstats
path: root/asm
diff options
context:
space:
mode:
authorGravatar Peter McGoron 2023-02-07 17:46:44 +0000
committerGravatar Peter McGoron 2023-02-07 17:46:44 +0000
commitf6e8c62f9e00210930bf222c077abef6c6d7cb34 (patch)
treebc6b04e06d6338b3fed63ec4f1ad2d704ee6dd19 /asm
parentadd label test (diff)
move python
Diffstat (limited to 'asm')
-rw-r--r--asm/creole_asm.py165
-rw-r--r--asm/test.py42
2 files changed, 207 insertions, 0 deletions
diff --git a/asm/creole_asm.py b/asm/creole_asm.py
new file mode 100644
index 0000000..8e87b58
--- /dev/null
+++ b/asm/creole_asm.py
@@ -0,0 +1,165 @@
+#!/usr/bin/python3
+
+from enum import Enum
+
+class MalformedArgument(Exception):
+ pass
+
+class ArgType(Enum):
+ IMM = 1
+ REG = 2
+ VAL = 3
+ LAB = 4
+
+ def gettype(s):
+ if s.isnumeric():
+ return (ArgType.IMM, int(s))
+ elif s[0] == 'r' and s[1:].isnumeric():
+ return (ArgType.REG, int(s[1:]))
+ elif s[0] == 'l' and s[1:].isnumeric():
+ return (ArgType.LAB, int(s[1:]))
+ else:
+ raise MalformedArgument(s)
+
+ def typecheck(self, s):
+ t = ArgType.gettype(s)
+ if self == ArgType.VAL:
+ return t[0] == ArgType.REG or t[0] == ArgType.IMM
+ else:
+ return t[0] == self
+
+class OpcodeException(Exception):
+ pass
+class TypecheckLenException(Exception):
+ pass
+class TypecheckException(Exception):
+ pass
+class Instruction:
+ def __init__(self, opcode, argtypes):
+ if opcode > 0x7F or opcode < 0:
+ raise OpcodeException(opcode)
+
+ self.opcode = opcode
+ self.argtypes = argtypes
+ def typecheck(self, sargs):
+ rargs = []
+ if len(sargs) != len(self.argtypes):
+ raise TypecheckLenException(sargs, self.argtypes)
+ for i in range(0, len(sargs)):
+ if not self.argtypes[i].typecheck(sargs[i]):
+ raise TypecheckException(self.argtypes[i],
+ sargs[i])
+ rargs.append(ArgType.gettype(sargs[i]))
+ return rargs
+
+instructions = {
+"nop" : Instruction(0, []),
+"push" : Instruction(1, [ArgType.REG]),
+"pop" : Instruction(2, [ArgType.REG]),
+"add" : Instruction(3, [ArgType.VAL, ArgType.VAL, ArgType.VAL]),
+"mul" : Instruction(4, [ArgType.VAL, ArgType.VAL, ArgType.VAL]),
+"div" : Instruction(5, [ArgType.VAL, ArgType.VAL, ArgType.VAL]),
+"jl" : Instruction(6, [ArgType.LAB, ArgType.VAL, ArgType.VAL]),
+"clb" : Instruction(7, [ArgType.LAB]),
+"sys" : Instruction(8, [ArgType.VAL])
+}
+
+encoding_types = {
+# start mask A B
+ 2: (0x7F, 0xC0, 7),
+ 3: (0xFFF, 0xE0, 12),
+ 4: (0x1FFFF, 0xF0, 16),
+ 5: (0x3FFFFF, 0xF8, 21),
+ 6: (0x7FFFFFF, 0xFC, 26),
+ 7: (0xFFFFFFFF, 0xFE, 36),
+# A : number of bits in start byte
+# B : Total number of bits excluding high bits
+}
+
+class InvalidNumberException(Exception):
+ pass
+class InvalidLengthException(Exception):
+ pass
+def encode_pseudo_utf8(n, high_bits, to):
+ if n < 0:
+ raise InvalidNumberException(n)
+ if to is None or to < 0:
+ for k in sorted(encoding_types):
+ if n <= encoding_types[k][0]:
+ to = k
+ break
+ if to is None:
+ raise InvalidNumberException(n)
+ if to > 8 or to < 0:
+ raise InvalidLengthException(to)
+ elif to == 1:
+ if n < 0x80:
+ return bytes([n])
+ else:
+ raise InvalidNumberException(n,to)
+
+ (maxval, start_byte, n_tot) = encoding_types[to]
+ if n > maxval or high_bits > 15:
+ raise InvalidNumberException(n, high_bits)
+ n = n | (high_bits << n_tot)
+ all_bytes = []
+ for i in range(0, to - 1):
+ all_bytes.append(0x80 | (n & 0x3F))
+ n >>= 6
+ all_bytes.append(start_byte | n)
+ return bytes(reversed(all_bytes))
+
+class RangeCheckException(Exception):
+ pass
+class Line:
+ def __init__(self, opcode, args, labnum, regnum):
+ self.opcode = opcode
+ self.args = args
+ for a in args:
+ if a[0] == ArgType.REG:
+ if a[1] < 0 or a[1] >= regnum:
+ raise RangeCheckException(a[0],
+ a[1],
+ regnum)
+ elif a[0] == ArgType.LAB:
+ if a[1] < 0 or a[1] >= labnum:
+ raise RangeCheckException(a[0],
+ a[1],
+ regnum)
+
+ def __call__(self):
+ b = bytes([self.opcode])
+ for a in self.args:
+ l = 2 if a[1] < 0x80 else None
+ if a[0] == ArgType.REG:
+ b = b + encode_pseudo_utf8(a[1],1,l)
+ else:
+ b = b + encode_pseudo_utf8(a[1],0,l)
+ return b + bytes([0])
+
+class InstructionNotFoundException(Exception):
+ pass
+class Program:
+ def asm_push_line(self, ins, args):
+ self.asm.append(Line(ins, args, self.labnum, self.regnum))
+
+ def parse_asm_line(self, line):
+ line = line.split()
+ line[0] = line[0].casefold()
+ if line[0] not in instructions:
+ raise InstructionNotFoundException(line[0])
+ else:
+ ins = instructions[line[0]]
+ args_w_type = ins.typecheck(line[1:])
+ self.asm_push_line(ins.opcode, args_w_type)
+
+ def __call__(self):
+ b = bytes()
+ for line in self.asm:
+ b = b + line()
+ return b
+
+ def __init__(self, labnum=16, regnum=16):
+ self.asm = []
+ self.labnum = labnum
+ self.regnum = regnum
diff --git a/asm/test.py b/asm/test.py
new file mode 100644
index 0000000..e86b508
--- /dev/null
+++ b/asm/test.py
@@ -0,0 +1,42 @@
+from creole import *
+import unittest
+
+class ProgramTest(unittest.TestCase):
+ def test_oneline(self):
+ p = Program()
+ p.parse_asm_line("PUSH r0")
+ b = p()
+ self.assertEqual(b, b'\x01\xC2\x80\x00')
+ def test_large_reg(self):
+ p = Program(regnum=0x8000000)
+ p.parse_asm_line("PUSH r134217727")
+ b = p()
+ self.assertEqual(b, b'\x01\xFC\x87\xbf\xbf\xbf\xbf\x00')
+ def test_two(self):
+ p = Program()
+ p.parse_asm_line("PUSH r1")
+ p.parse_asm_line("ADD r1 5 6")
+ b = p()
+ self.assertEqual(b, b'\x01\xC2\x81\x00\x03\xC2\x81\xC0\x85\xC0\x86\x00')
+ def test_label(self):
+ p = Program()
+ b_ex = bytes()
+ p.parse_asm_line("CLB l0")
+ b_ex = b_ex + b'\x07\xC0\x80\x00'
+ p.parse_asm_line("pop r0")
+ b_ex = b_ex + b'\x02\xC2\x80\x00'
+ p.parse_asm_line("pop r1")
+ b_ex = b_ex + b'\x02\xC2\x81\x00'
+ p.parse_asm_line("mul r2 r0 r1")
+ b_ex = b_ex + b'\x04\xC2\x82\xC2\x80\xC2\x81\x00'
+ p.parse_asm_line("push r2")
+ b_ex = b_ex + b'\x01\xC2\x82\x00'
+ p.parse_asm_line("push r2")
+ b_ex = b_ex + b'\x01\xC2\x82\x00'
+ p.parse_asm_line("jl l0 r2 10")
+ b_ex = b_ex + b'\x06\xC0\x80\xC2\x82\xC0\x8A\x00'
+ b = p()
+ self.assertEqual(b, b_ex)
+
+if __name__ == "__main__":
+ unittest.main()