creole/asm/ffi.py

170 lines
3.7 KiB
Python

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
LABEL_OVERFLOW = 7
TYPE_ERROR = 8
CLEARED_INSTRUCTION = 9
PROGRAM_OVERFLOW = 10
class RunRet(Enum):
CONTINUE = 0
SYSCALL = 1
STOP = 2
STACK_OVERFLOW = 3
STACK_UNDERFLOW = 4
LABEL_OVERFLOW = 5
REGISTER_OVERFLOW = 6
UNKNOWN_OPCODE = 7
DIVIDE_BY_ZERO = 8
HIGH_BIT_MALFORMED = 9
def is_halt(self):
return not (self == RunRet.CONTINUE or self == RunRet.SYSCALL)
class CIns(Structure):
_fields_ = [("opcode", c_int),
("w_flags", c_ubyte * 3),
("w", c_uint * 3)]
class CReader(Structure):
_fields_ = [("p", POINTER(c_ubyte)),
("left", c_size_t)]
def make_uchar_buf(s):
buf = (c_ubyte * len(s))()
buf[:] = s[:]
return buf
def make_reader(s):
buf = make_uchar_buf(s)
return CReader(buf, len(s))
class CEnv(Structure):
_fields_ = [
("reg", POINTER(c_uint)),
("reglen", c_size_t),
("lab", POINTER(c_size_t)),
("lablen", c_size_t),
("stk", POINTER(c_uint)),
("stkptr", c_size_t),
("stklen", c_size_t),
("prg", POINTER(CIns)),
("prgptr", c_size_t),
("prgend", c_size_t),
("prglen", c_size_t)
]
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 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 pop(self):
if stk == 0:
return None
stk = stk - 1
return self.cenv.stk[stk]
def __init__(self, prog=None, reglen=32, lablen=32, stklen=4096, prglen=4096):
cenv = CEnv()
cenv.reglen = reglen
cenv.reg = (c_uint * reglen)()
cenv.lablen = lablen
cenv.lab = (c_size_t * lablen)()
cenv.stklen = stklen
cenv.stk = (c_uint * stklen)()
cenv.stkptr = 0
cenv.prglen = prglen
cenv.prg = (CIns * prglen)()
cenv.prgptr = 0
cenv.prgend = 0
self.cenv = cenv
if prog is not None:
r = self.load(prog)
if r is not CompileRet.OK:
raise CompileError(r)
def restart(self):
self.cenv.stkptr = 0
self.cenv.prgptr = 0
self.cenv.prgend = 0
def load(self, prog):
rd = make_reader(prog)
self.restart()
ret = dll.creole_compile(byref(self.cenv), byref(rd))
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
class CParseLineException(Exception):
pass
def parse_line(line):
rd = make_reader(line)
ins = CIns()
ret = dll.creole_parse_line(byref(ins), byref(rd))
if ret != CompileRet.OK.value:
raise CParseLineException(CompileRet(ret).name)
return (ins.opcode, list(zip(ins.w_flags, ins.w)))