Add assertations and errors to Interpreter
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user