prog8/tinyvm/core.py
2018-03-10 20:32:34 +01:00

142 lines
4.7 KiB
Python

"""
Simplistic 8/16 bit Virtual Machine to execute a stack based instruction language.
Core data structures and definitions.
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
"""
import enum
import struct
import array
from typing import Callable, Union
from il65.codegen.shared import mflpt5_to_float, to_mflpt5
class DataType(enum.IntEnum):
BOOL = 1
BYTE = 2
SBYTE = 3
WORD = 4
SWORD = 5
FLOAT = 6
ARRAY_BYTE = 7
ARRAY_SBYTE = 8
ARRAY_WORD = 9
ARRAY_SWORD = 10
MATRIX_BYTE = 11
MATRIX_SBYTE = 12
@staticmethod
def guess_datatype_for(value: Union[bool, int, float, bytearray, array.array]) -> 'DataType':
if isinstance(value, int):
if 0 <= value <= 255:
return DataType.BYTE
if -128 <= value <= 127:
return DataType.SBYTE
if 0 <= value <= 65535:
return DataType.WORD
if -32768 <= value <= 32767:
return DataType.SWORD
raise OverflowError("integer value too large for byte or word", value)
if isinstance(value, bool):
return DataType.BOOL
if isinstance(value, float):
return DataType.FLOAT
if isinstance(value, bytearray):
return DataType.ARRAY_BYTE
if isinstance(value, array.array):
if value.typecode == "B":
return DataType.ARRAY_BYTE
if value.typecode == "H":
return DataType.ARRAY_WORD
if value.typecode == "b":
return DataType.ARRAY_SBYTE
if value.typecode == "h":
return DataType.ARRAY_SWORD
raise ValueError("invalid array typecode", value.typecode)
raise TypeError("invalid value type", value)
class ExecutionError(Exception):
pass
class TerminateExecution(SystemExit):
pass
class MemoryAccessError(Exception):
pass
class Memory:
def __init__(self):
self.mem = bytearray(65536)
self.readonly = bytearray(65536)
self.mmap_io_charout_addr = -1
self.mmap_io_charout_callback = None
self.mmap_io_charin_addr = -1
self.mmap_io_charin_callback = None
def mark_readonly(self, start: int, end: int) -> None:
self.readonly[start:end+1] = [1] * (end-start+1)
def memmapped_io_charout(self, address: int, callback: Callable) -> None:
self.mmap_io_charout_addr = address
self.mmap_io_charout_callback = callback
def memmapped_io_charin(self, address: int, callback: Callable) -> None:
self.mmap_io_charin_addr = address
self.mmap_io_charin_callback = callback
def get_byte(self, index: int) -> int:
if self.mmap_io_charin_addr == index:
self.mem[index] = self.mmap_io_charin_callback()
return self.mem[index]
def get_bytes(self, startindex: int, amount: int) -> bytearray:
return self.mem[startindex: startindex+amount]
def get_sbyte(self, index: int) -> int:
if self.mmap_io_charin_addr == index:
self.mem[index] = self.mmap_io_charin_callback()
return struct.unpack("b", self.mem[index:index+1])[0]
def get_word(self, index: int) -> int:
return self.mem[index] + 256 * self.mem[index+1]
def get_sword(self, index: int) -> int:
return struct.unpack("<h", self.mem[index:index+2])[0]
def get_float(self, index: int) -> float:
return mflpt5_to_float(self.mem[index: index+5])
def set_byte(self, index: int, value: int) -> None:
if self.readonly[index]:
raise MemoryAccessError("read-only", index)
self.mem[index] = value
if self.mmap_io_charout_addr == index:
self.mmap_io_charout_callback(value)
def set_sbyte(self, index: int, value: int) -> None:
if self.readonly[index]:
raise MemoryAccessError("read-only", index)
self.mem[index] = struct.pack("b", bytes([value]))[0]
if self.mmap_io_charout_addr == index:
self.mmap_io_charout_callback(self.mem[index])
def set_word(self, index: int, value: int) -> None:
if self.readonly[index] or self.readonly[index+1]:
raise MemoryAccessError("read-only", index)
self.mem[index], self.mem[index + 1] = struct.pack("<H", value)
def set_sword(self, index: int, value: int) -> None:
if self.readonly[index] or self.readonly[index+1]:
raise MemoryAccessError("read-only", index)
self.mem[index], self.mem[index + 1] = struct.pack("<h", value)
def set_float(self, index: int, value: float) -> None:
if any(self.readonly[index:index+5]):
raise MemoryAccessError("read-only", index)
self.mem[index: index+5] = to_mflpt5(value)