# Copyright (c) 2023 Peter McGoron # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. from ctypes import * import creole from enum import Enum dll = CDLL("./libcreole.so") class CompileRet(Enum): OK = 0 OPCODE_READ_ERROR = 1 OPCODE_MALFORMED = 2 ARG_READ_ERROR = 3 ARG_MALFORMED = 4 LAST_READ_ERROR = 5 LAST_MALFORMED = 6 DATA_OVERFLOW = 7 TYPE_ERROR = 8 PROGRAM_OVERFLOW = 9 class RunRet(Enum): CONTINUE = 0 SYSCALL = 1 STOP = 2 STACK_OVERFLOW = 3 STACK_UNDERFLOW = 4 DECODE_ERROR = 5 REGISTER_OVERFLOW = 6 UNKNOWN_OPCODE = 7 DIVIDE_BY_ZERO = 8 HIGH_BIT_MALFORMED = 9 JUMP_OVERFLOW = 10 def is_halt(self): return not (self == RunRet.CONTINUE or self == RunRet.SYSCALL) class CWord(Structure): _fields_ = [("len", c_int), ("high_bits", c_int), ("word", c_uint)] class CReader(Structure): _fields_ = [("p", POINTER(c_ubyte)), ("left", c_size_t)] class Reader: def __init__(self, s): self.buf = (c_ubyte * len(s))() self.buf[:] = s[:] self.rd = CReader(self.buf, len(s)) class CEnv(Structure): _fields_ = [ ("dats", POINTER(POINTER(c_ubyte))), ("datlen", c_size_t), ("reg", POINTER(c_uint)), ("reglen", c_size_t), ("stk", POINTER(c_uint)), ("stkptr", c_size_t), ("stklen", c_size_t), ("r_current", CReader), ("r_start", CReader) ] class RegisterOverflowError(Exception): def __init__(self, reg): self.reg = reg class StackOverflowError(Exception): def __init__(self, pos): self.pos = pos class InvalidSyscallError(Exception): def __init__(self, sc): self.sc = sc class CompileError(Exception): def __init__(self, r): self.r = r class DataOverflowError(Exception): pass class Environment: def getreg(self, reg, signed=False): """ Get the value at the register. :param reg: Register number. :param signed: If the register value should be interpreted as signed. """ if reg >= self.cenv.reglen or reg < 0: raise RegisterOverflowError(r) if signed: return creole.from_2c(self.cenv.reg[reg]) else: return self.cenv.reg[reg] def getstk(self, stk, signed=False): """ Get the value at the stack position. :param reg: Register number. :param signed: If the stack value should be interpreted as signed. """ if stk >= self.cenv.stklen or stk < 0: raise StackOverflowError(r) if signed: return creole.from_2c(self.cenv.stk[stk]) else: return self.cenv.stk[stk] def getdat(self, n): if n >= self.cenv.datlen or n < 0: raise DataOverflowError(n) rdr = CReader() rdr.p = self.cenv.dats[n] # Python does not allow for direct pointer arithmetic rdr_p_v = addressof(rdr.p.contents) r_start_p_v = addressof(self.cenv.r_start.p) rdr.left = self.cenv.r_start.left - (rdr_p_v - r_start_p_v) l = [] w = CWord() while dll.creole_decode(byref(rdr), byref(w)) == 1: if w.word == 0 and w.len == 1: break l.append(w.word) return l def pop(self): if stk == 0: return None stk = stk - 1 return self.cenv.stk[stk] def reset(self): self.cenv.r_current = self.cenv.r_start def __init__(self, prog=None, reglen=32, datlen=32, stklen=4096, prglen=4096): cenv = CEnv() cenv.dats = (POINTER(c_ubyte) * datlen)() cenv.datlen = datlen cenv.reglen = reglen cenv.reg = (c_uint * reglen)() cenv.stklen = stklen cenv.stk = (c_uint * stklen)() cenv.stkptr = 0 self.cenv = cenv if prog is not None: if type(prog) is creole.Program: prog = prog() r = self.load(prog) if r is not CompileRet.OK: raise CompileError(r) def load(self, prog): self.reader = Reader(prog) self.cenv.r_current = self.reader.rd self.cenv.r_start = self.cenv.r_current ret = dll.creole_compile(byref(self.cenv), byref(self.cenv.r_current)) return CompileRet(ret) def syscall(self, sc): raise InvalidSyscallError(sc) def __call__(self, debug=False): sc = c_size_t() ret = RunRet.CONTINUE while not ret.is_halt(): ret = RunRet(dll.creole_step(byref(self.cenv), byref(sc))) if ret == RunRet.SYSCALL: self.syscall(sc) if debug: print(self.cenv.reg[0]) return ret