Add assertations and errors to Interpreter

This commit is contained in:
Cormac Shannon
2025-04-15 23:32:04 +01:00
parent 164d1069d0
commit e51a9b476c

View File

@@ -7,13 +7,20 @@ from colorama import Fore, Style, Back
from .util import hexdump from .util import hexdump
Operation: TypeAlias = str | int Operation: TypeAlias = int
Subroutine: TypeAlias = Callable Subroutine: TypeAlias = Callable
OPCODE_HEADER = "__interpreter__opcode" OPCODE_HEADER = "__interpreter__opcode"
OPCODE_ERROR_TYPE_CHECK = f"Opcodes must be of type f{int}."
OPCODE_ERROR_REUSE = "Opcode {opcode} has already been used."
OPCODE_ERROR_ILLEGAL = (
"Opcode {opcode} cannot be found, has it been registered correct?"
)
def opcode(opcode: Operation): def opcode(opcode: Operation):
assert type(opcode) == Operation, OPCODE_ERROR_TYPE_CHECK
def _helper(func: Callable): def _helper(func: Callable):
setattr(func, OPCODE_HEADER, opcode) setattr(func, OPCODE_HEADER, opcode)
return func return func
@@ -45,24 +52,36 @@ class Interpreter:
def __init__(self, bits=8, memsize=None) -> None: def __init__(self, bits=8, memsize=None) -> None:
self.__bits = bits self.__bits = bits
self.__max_cpu_instructions = 2**self.__bits
self.__memory_max_size = 2**self.__bits self.__memory_max_size = 2**self.__bits
self.__memory_size = min(memsize or 2**bits, self.__memory_max_size) self.__memory_size = min(memsize or 2**bits, self.__memory_max_size)
self._memory: Memory = Memory(self.__memory_size) self._memory: Memory = Memory(self.__memory_size)
self._registers: Registers = Registers() self._registers: Registers = Registers()
self.__operations: dict[Operation, Subroutine] = dict() self.__operations: list[Subroutine | None] = [
None for _ in range(self.__max_cpu_instructions)
]
self.__init_opcodes() self.__init_opcodes()
def __init_opcodes(self): def __init_opcodes(self):
for _, func in getmembers(self, lambda obj: hasattr(obj, OPCODE_HEADER)): for _, func in getmembers(self, lambda obj: hasattr(obj, OPCODE_HEADER)):
opcode = getattr(func, OPCODE_HEADER) opcode = getattr(func, OPCODE_HEADER)
assert self.__operations[opcode] is None, OPCODE_ERROR_REUSE.format(
opcode=opcode
)
self.__operations[opcode] = func self.__operations[opcode] = func
def run(self) -> None: def run(self) -> None:
try: try:
# Keep running until you reach a null byte # Keep running until you reach a null byte
while (opcode := self._memory[self._registers.program_counter]) != 0: while (opcode := self._memory[self._registers.program_counter]) != 0:
self.__operations[opcode]() subroutine = self.__operations[opcode]
assert subroutine is not None, OPCODE_ERROR_ILLEGAL.format(
opcode=opcode
)
subroutine()
self._registers.program_counter += 1 self._registers.program_counter += 1
except Exception as e: except Exception as e:
self.dump() self.dump()