creole/asm/ffi.py

183 lines
4.5 KiB
Python
Raw 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
LABEL_OVERFLOW = 7
TYPE_ERROR = 8
2023-02-09 11:16:41 -05:00
CLEARED_INSTRUCTION = 9
2023-02-11 09:28:43 -05:00
PROGRAM_OVERFLOW = 10
class RunRet(Enum):
CONTINUE = 0
SYSCALL = 1
STOP = 2
STACK_OVERFLOW = 3
STACK_UNDERFLOW = 4
LABEL_OVERFLOW = 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-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-07 23:15:54 -05:00
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
2023-02-09 11:16:41 -05:00
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)
]
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-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]
def pop(self):
if stk == 0:
return None
stk = stk - 1
return self.cenv.stk[stk]
2023-02-11 15:49:39 -05:00
def __init__(self, prog=None, reglen=32, lablen=32, stklen=4096, prglen=4096):
2023-02-09 11:16:41 -05:00
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
2023-02-11 15:49:39 -05:00
if prog is not None:
r = self.load(prog)
if r is not CompileRet.OK:
raise CompileError(r)
2023-02-09 11:16:41 -05:00
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)
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
2023-02-07 23:15:54 -05:00
class CParseLineException(Exception):
pass
def parse_line(line):
2023-02-09 11:16:41 -05:00
rd = make_reader(line)
2023-02-07 23:15:54 -05:00
ins = CIns()
ret = dll.creole_parse_line(byref(ins), byref(rd))
2023-02-09 11:16:41 -05:00
if ret != CompileRet.OK.value:
raise CParseLineException(CompileRet(ret).name)
2023-02-07 23:15:54 -05:00
return (ins.opcode, list(zip(ins.w_flags, ins.w)))