diff options
| author | 2023-02-07 17:46:44 +0000 | |
|---|---|---|
| committer | 2023-02-07 17:46:44 +0000 | |
| commit | f6e8c62f9e00210930bf222c077abef6c6d7cb34 (patch) | |
| tree | bc6b04e06d6338b3fed63ec4f1ad2d704ee6dd19 /asm | |
| parent | add label test (diff) | |
move python
Diffstat (limited to 'asm')
| -rw-r--r-- | asm/creole_asm.py | 165 | ||||
| -rw-r--r-- | asm/test.py | 42 |
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() |
