creole/asm/ffi.py

178 lines
4.4 KiB
Python
Raw Permalink Normal View History

2023-02-12 15:54:21 -05:00
# 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.
2023-02-07 23:15:54 -05:00
from ctypes import *
2023-02-11 15:49:39 -05:00
import creole
2023-02-08 08:56:39 -05:00
from enum import Enum
2023-02-07 23:15:54 -05:00
dll = CDLL("./libcreole.so")
2023-02-08 08:56:39 -05:00
class CompileRet(Enum):
2023-02-09 11:16:41 -05:00
OK = 0
2023-02-08 08:56:39 -05:00
OPCODE_READ_ERROR = 1
OPCODE_MALFORMED = 2
ARG_READ_ERROR = 3
ARG_MALFORMED = 4
LAST_READ_ERROR = 5
LAST_MALFORMED = 6
2023-02-18 11:05:09 -05:00
DATA_OVERFLOW = 7
2023-02-08 08:56:39 -05:00
TYPE_ERROR = 8
2023-02-18 11:05:09 -05:00
PROGRAM_OVERFLOW = 9
2023-02-11 09:28:43 -05:00
class RunRet(Enum):
CONTINUE = 0
SYSCALL = 1
STOP = 2
STACK_OVERFLOW = 3
STACK_UNDERFLOW = 4
2023-02-18 11:05:09 -05:00
DECODE_ERROR = 5
2023-02-11 09:28:43 -05:00
REGISTER_OVERFLOW = 6
UNKNOWN_OPCODE = 7
2023-02-11 15:49:39 -05:00
DIVIDE_BY_ZERO = 8
HIGH_BIT_MALFORMED = 9
2023-02-18 11:05:09 -05:00
JUMP_OVERFLOW = 10
2023-02-11 09:28:43 -05:00
def is_halt(self):
return not (self == RunRet.CONTINUE or self == RunRet.SYSCALL)
2023-02-08 08:56:39 -05:00
2023-02-25 16:01:03 -05:00
class CWord(Structure):
_fields_ = [("len", c_int),
("high_bits", c_int),
("word", c_uint)]
2023-02-07 23:15:54 -05:00
class CReader(Structure):
_fields_ = [("p", POINTER(c_ubyte)),
("left", c_size_t)]
2023-02-18 11:05:09 -05:00
class Reader:
def __init__(self, s):
self.buf = (c_ubyte * len(s))()
self.buf[:] = s[:]
self.rd = CReader(self.buf, len(s))
2023-02-09 11:16:41 -05:00
class CEnv(Structure):
_fields_ = [
2023-04-07 15:49:53 -04:00
("dats", POINTER(CReader)),
2023-02-18 11:05:09 -05:00
("datlen", c_size_t),
2023-02-09 11:16:41 -05:00
("reg", POINTER(c_uint)),
("reglen", c_size_t),
("stk", POINTER(c_uint)),
("stkptr", c_size_t),
("stklen", c_size_t),
2023-02-18 11:05:09 -05:00
("r_current", CReader),
2023-04-07 15:49:53 -04:00
("r_start", CReader),
("fd", c_int)
2023-02-09 11:16:41 -05:00
]
2023-02-11 15:49:39 -05:00
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
2023-02-25 16:01:03 -05:00
class DataOverflowError(Exception):
pass
2023-02-11 15:49:39 -05:00
2023-02-09 15:42:36 -05:00
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.
"""
2023-02-11 15:49:39 -05:00
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]
2023-02-12 15:37:45 -05:00
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.
"""
2023-02-11 15:49:39 -05:00
if stk >= self.cenv.stklen or stk < 0:
raise StackOverflowError(r)
2023-02-12 15:37:45 -05:00
if signed:
return creole.from_2c(self.cenv.stk[stk])
else:
return self.cenv.stk[stk]
2023-02-25 16:01:03 -05:00
def getdat(self, n):
if n >= self.cenv.datlen or n < 0:
raise DataOverflowError(n)
2023-04-07 15:49:53 -04:00
rdr = self.cenv.dats[n]
2023-02-25 16:01:03 -05:00
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
2023-02-12 15:37:45 -05:00
def pop(self):
if stk == 0:
return None
stk = stk - 1
return self.cenv.stk[stk]
2023-02-11 15:49:39 -05:00
2023-02-18 11:05:09 -05:00
def reset(self):
self.cenv.r_current = self.cenv.r_start
def __init__(self, prog=None, reglen=32, datlen=32, stklen=4096, prglen=4096):
2023-02-09 11:16:41 -05:00
cenv = CEnv()
2023-04-07 15:49:53 -04:00
cenv.dats = (CReader * datlen)()
2023-02-18 11:05:09 -05:00
cenv.datlen = datlen
2023-02-09 11:16:41 -05:00
cenv.reglen = reglen
cenv.reg = (c_uint * reglen)()
cenv.stklen = stklen
cenv.stk = (c_uint * stklen)()
cenv.stkptr = 0
self.cenv = cenv
2023-02-11 15:49:39 -05:00
if prog is not None:
2023-02-18 11:05:09 -05:00
if type(prog) is creole.Program:
prog = prog()
2023-02-11 15:49:39 -05:00
r = self.load(prog)
if r is not CompileRet.OK:
raise CompileError(r)
2023-02-09 11:16:41 -05:00
def load(self, prog):
2023-02-18 11:05:09 -05:00
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))
2023-02-09 11:16:41 -05:00
return CompileRet(ret)
2023-02-07 23:15:54 -05:00
2023-02-11 09:28:43 -05:00
def syscall(self, sc):
2023-02-11 15:49:39 -05:00
raise InvalidSyscallError(sc)
2023-02-11 16:24:48 -05:00
def __call__(self, debug=False):
2023-02-11 09:28:43 -05:00
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)
2023-02-11 16:24:48 -05:00
if debug:
print(self.cenv.reg[0])
2023-02-11 09:28:43 -05:00
return ret