80 lines
2.2 KiB
Python
80 lines
2.2 KiB
Python
from collections.abc import Callable
|
|
from inspect import getmembers
|
|
from typing import TypeAlias
|
|
from traceback import print_exception
|
|
from pprint import pprint
|
|
|
|
from .util import hexdump
|
|
|
|
Operation: TypeAlias = str | int
|
|
Subroutine: TypeAlias = Callable
|
|
|
|
OPCODE_HEADER = "__interpreter__opcode"
|
|
|
|
|
|
def opcode(opcode: Operation):
|
|
def _helper(func: Callable):
|
|
setattr(func, OPCODE_HEADER, opcode)
|
|
return func
|
|
|
|
return _helper
|
|
|
|
|
|
class Registers:
|
|
def __init__(self) -> None:
|
|
self.program_counter: int = 0
|
|
|
|
|
|
class Memory:
|
|
def __init__(self, size: int) -> None:
|
|
self.__memory_size: int = size
|
|
self.__memory: bytearray = bytearray(self.__memory_size)
|
|
|
|
def __getitem__(self, idx) -> int:
|
|
return self.__memory[idx]
|
|
|
|
def __setitem__(self, idx, value) -> None:
|
|
self.__memory[idx] = value
|
|
|
|
def dump(self):
|
|
return self.__memory
|
|
|
|
|
|
class Interpreter:
|
|
def __init__(self, bits=8, memsize=None) -> None:
|
|
self.__bits = bits
|
|
|
|
self.__memory_max_size = 2**self.__bits
|
|
self.__memory_size = min(memsize or 2**bits, self.__memory_max_size)
|
|
self._memory: Memory = Memory(self.__memory_size)
|
|
self._registers: Registers = Registers()
|
|
|
|
self.__operations: dict[Operation, Subroutine] = dict()
|
|
self.__init_opcodes()
|
|
|
|
def __init_opcodes(self):
|
|
for _, func in getmembers(self, lambda obj: hasattr(obj, OPCODE_HEADER)):
|
|
opcode = getattr(func, OPCODE_HEADER)
|
|
self.__operations[opcode] = func
|
|
|
|
def run(self) -> None:
|
|
try:
|
|
# Keep running until you reach a null byte
|
|
while (opcode := self._memory[self._registers.program_counter]) != 0:
|
|
self.__operations[opcode]()
|
|
self._registers.program_counter += 1
|
|
except Exception as e:
|
|
self.dump()
|
|
print_exception(e)
|
|
|
|
def dump(self):
|
|
hexdump(
|
|
data=self._memory.dump(),
|
|
program_counter=self._registers.program_counter,
|
|
memory_pointer=self._registers.memory_pointer
|
|
)
|
|
pprint(self.__operations)
|
|
|
|
def load_program(self, program: bytearray) -> None:
|
|
self._memory[: len(program)] = program[:]
|