#!/usr/bin/python3 from enum import Enum class ArgType(Enum): TYPE_IMM = 1 TYPE_REG = 2 TYPE_VAL = 3 TYPE_LAB = 4 def gettype(s): if s.isnumeric(): return (TYPE_IMM, int(s)) elif s[0] == 'r' and s[1:].isnumeric(): return (TYPE_REG, int(s[1:])) elif s[0] == 'l' and s[1:].isnumeric(): return (TYPE_LAB, int(s[1:])) else: return None def typecheck(self, s): t = ArgType.gettype(s) if t is None: return None if self == TYPE_VAL: return t[0] == TYPE_REG or t[0] == TYPE_IMM else: return t[0] == self class Instruction: def __init__(self, opcode, argtypes): self.opcode = opcode assert self.opcode < 0x80 and self.opcode >= 0 self.argtypes = argtypes def typecheck(self, sargs): rargs = [] if len(sargs) != len(self.argtypes): return None for i in range(0, len(sargs)): if not self.argtypes[i].typecheck(sargs[i]): return None rargs.append(ArgType.gettype(sargs[i])) return rargs instructions = { "NOP" : Instruction(0, []), "PUSH" : Instruction(1, [ArgType.TYPE_REG]), "POP" : Instruction(2, [ArgType.TYPE_REG]), "ADD" : Instruction(3, [ArgType.TYPE_VAL, ArgType.TYPE_VAL, ArgType.TYPE_VAL]), "MUL" : Instruction(4, [ArgType.TYPE_VAL, ArgType.TYPE_VAL, ArgType.TYPE_VAL]), "DIV" : Instruction(5, [ArgType.TYPE_VAL, ArgType.TYPE_VAL, ArgType.TYPE_VAL]), "JL" : Instruction(6, [ArgType.TYPE_LAB, ArgType.TYPE_VAL, ArgType.TYPE_VAL]), "CLB" : Instruction(7, [ArgType.TYPE_LAB]), "SYS" : Instruction(8, [ArgType.TYPE_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 } def encode_pseudo_utf8(n, high_bits, to): if to > 8 or to < 0: return None elif to == 1: if n < 0x80: return bytes([n]) else: return None (maxval, start_byte, n_tot) = encoding_types[to] if n > maxval or high_bits > 15: return None 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 Line: def __init__(self, opcode, args): self.opcode = opcode self.args = args def __call__(self): b = bytes([self.opcode]) for a in args: if a[0] == TYPE_REG: b = b + encode_pseudo_utf8(a[1],1,None) else: b = b + encode_pseudo_utf8(a[1],0,None) return b + bytes([0]) class Program: def asm_push_line(self, ins, args): self.asm.append(Line(ins, args)) def parse_asm_line(self, line): line = line.split() if line[0] not in instructions: raise Exception else: ins = instructions[line[0]] args_w_type = ins.typecheck(line[1:]) if r is None: raise Exception 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): self.asm = []