creole/asm/ffi.py

183 lines
4.7 KiB
Python

# Copyright (c) 2023 Peter McGoron <code@mcgoron.com>
#
# 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