From 8bfe21dd657ca482310a1dde3240b082f697b7f7 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 4 Mar 2018 15:11:45 +0100 Subject: [PATCH] typing --- il65/compile.py | 2 +- il65/plyparse.py | 37 +++++++------ testvm.txt | 9 ++- tinyvm/parse.py | 18 +++--- tinyvm/program.py | 100 ++++++++++++++++++++++++++++++--- tinyvm/vm.py | 137 ++++++++++++++++++++++++++++------------------ 6 files changed, 212 insertions(+), 91 deletions(-) diff --git a/il65/compile.py b/il65/compile.py index 527912936..c1fd7bd4c 100644 --- a/il65/compile.py +++ b/il65/compile.py @@ -142,7 +142,7 @@ class PlyParser: continue else: raise ParseError("block without name must have address", node.sourceref) - parentname = (node.parent.name + ".") if node.parent else "" + parentname = (node.parent.name + ".") if node.parent else "" # type: ignore blockname = parentname + node.name if blockname in encountered_block_names: raise CompileError("block names not unique:", blockname) diff --git a/il65/plyparse.py b/il65/plyparse.py index d7c7a9eba..38a5a73a1 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -10,7 +10,7 @@ import builtins import inspect import enum from collections import defaultdict -from typing import Union, Generator, Tuple, List, Optional, Dict, Any, no_type_check +from typing import Union, Generator, Tuple, List, Optional, Dict, Any, no_type_check, Callable import attr from ply.yacc import yacc from .plylex import SourceRef, tokens, lexer, find_tok_column, print_warning @@ -70,12 +70,14 @@ class UndefinedSymbolError(LookupError): start = "start" -@attr.s(cmp=False, slots=True, frozen=False, repr=False) class AstNode: # all ast nodes have: sourceref, parent, and nodes (=list of zero or more sub-nodes) - sourceref = attr.ib(type=SourceRef) - parent = attr.ib(init=False, default=None) # will be hooked up later - nodes = attr.ib(type=list, init=False, default=attr.Factory(list)) # type: List['AstNode'] + __slots__ = ["sourceref", "parent", "nodes"] + + def __init__(self, sourceref: SourceRef) -> None: + self.sourceref = sourceref + self.parent = None # type: AstNode # will be hooked up later + self.nodes = [] # type: List[AstNode] @property def lineref(self) -> str: @@ -129,7 +131,7 @@ class AstNode: @attr.s(cmp=False) class Directive(AstNode): - name = attr.ib(type=str) + name = attr.ib(type=str, default=None) args = attr.ib(type=list, default=attr.Factory(list)) # no subnodes. @@ -162,7 +164,7 @@ class Scope(AstNode): parent_scope = self.parent while parent_scope and not isinstance(parent_scope, Scope): parent_scope = parent_scope.parent - return parent_scope + return parent_scope # type: ignore def __attrs_post_init__(self): # populate the symbol table for this scope for fast lookups via scope.lookup("name") or scope.lookup("dotted.name") @@ -208,7 +210,7 @@ class Scope(AstNode): if '.' in name: # look up the dotted name starting from the topmost scope scope = self - node = self + node = self # type: AstNode while node.parent: if isinstance(node.parent, Scope): scope = node.parent @@ -230,6 +232,7 @@ class Scope(AstNode): while parent_scope and not isinstance(parent_scope, Scope): parent_scope = parent_scope.parent if parent_scope: + assert isinstance(parent_scope, Scope) return parent_scope.lookup(name) raise UndefinedSymbolError("undefined symbol: " + name) @@ -314,7 +317,7 @@ class Block(AstNode): @attr.s(cmp=False, repr=False) class Module(AstNode): # has one subnode: the Scope. - name = attr.ib(type=str) # filename + name = attr.ib(type=str, default=None) # filename subroutine_usage = attr.ib(type=defaultdict, init=False, default=attr.Factory(lambda: defaultdict(set))) # will be populated later format = attr.ib(type=ProgramFormat, init=False, default=ProgramFormat.PRG) # can be set via directive address = attr.ib(type=int, init=False, default=0xc000, validator=validate_address) # can be set via directive @@ -344,7 +347,7 @@ class Module(AstNode): @attr.s(cmp=False) class Label(AstNode): - name = attr.ib(type=str) + name = attr.ib(type=str, default=None) # no subnodes. @@ -363,7 +366,7 @@ class Expression(AstNode): @attr.s(cmp=False, slots=True) class Register(Expression): - name = attr.ib(type=str, validator=attr.validators.in_(REGISTER_SYMBOLS)) + name = attr.ib(type=str, validator=attr.validators.in_(REGISTER_SYMBOLS), default="???") datatype = attr.ib(type=DataType, init=False) # no subnodes. @@ -434,7 +437,7 @@ class BuiltinFunction(AstNode): # to represent all supported built-in functions or math-functions. # No child nodes. name = attr.ib(type=str) - func = attr.ib(type=callable) + func = attr.ib(type=Callable) @attr.s(cmp=False, repr=False) @@ -462,7 +465,7 @@ class Subroutine(AstNode): @attr.s(cmp=True, slots=True, repr=False) class LiteralValue(Expression): # no subnodes. - value = attr.ib() + value = attr.ib(default=None) def __repr__(self) -> str: return "".format(self.value, self.sourceref) @@ -477,7 +480,7 @@ class LiteralValue(Expression): @attr.s(cmp=False) class AddressOf(Expression): # no subnodes. - name = attr.ib(type=str, validator=attr.validators._InstanceOfValidator(type=str)) + name = attr.ib(type=str, validator=attr.validators._InstanceOfValidator(type=str), default="???") # type: ignore def is_compiletime_const(self) -> bool: # address-of can be a compile time constant if the operand is a memory mapped variable or ZP variable @@ -499,7 +502,7 @@ class AddressOf(Expression): @attr.s(cmp=True, slots=True) class SymbolName(Expression): # no subnodes. - name = attr.ib(type=str) + name = attr.ib(type=str, default="???") def is_compiletime_const(self) -> bool: symdef = self.my_scope().lookup(self.name) @@ -515,7 +518,7 @@ class SymbolName(Expression): @attr.s(cmp=False) class Dereference(Expression): # one subnode: operand (SymbolName, integer LiteralValue or Register (pair) ) - datatype = attr.ib() + datatype = attr.ib(type=DataType, default=None) size = attr.ib(type=int, default=None) @property @@ -577,7 +580,7 @@ class IncrDecr(AstNode): @attr.s(cmp=False, slots=True, repr=False) class ExpressionWithOperator(Expression): # 2 nodes: left (Expression), right (not present if unary, Expression if not unary) - operator = attr.ib(type=str) + operator = attr.ib(type=str, default="???") @property def unary(self) -> bool: diff --git a/testvm.txt b/testvm.txt index 1fe8b91a3..7931757b6 100644 --- a/testvm.txt +++ b/testvm.txt @@ -1,10 +1,10 @@ ; source code for a tinyvm program %block b1 %vardefs -var byte teller 0 -var byte numbertoprint 0 +var word teller 0 +var word numbertoprint 0 const byte one 1 -const byte thousand 1000 +const word thousand 1000 const byte space_chr 10 const word screenstart 1024 %end_vardefs @@ -22,13 +22,12 @@ back: syscall memwrite_word pop teller call 1 printnumber - syscall delay push thousand cmp_lt jump_if_true back return 0 printnumber: - syscall decimalstr_signed + syscall decimalstr_unsigned syscall printstr push space_chr syscall printchr diff --git a/tinyvm/parse.py b/tinyvm/parse.py index 205558614..f7ad545d1 100644 --- a/tinyvm/parse.py +++ b/tinyvm/parse.py @@ -1,6 +1,6 @@ import array from typing import Optional, List, Tuple, Dict, Any -from .program import DataType, Opcode, Program, Block, Variable, Instruction +from .program import DataType, Opcode, Program, Block, Variable, Instruction, Value from .vm import StackValueType @@ -70,20 +70,20 @@ class Parser: length = height = 0 value = None # type: StackValueType if dtype in (DataType.BYTE, DataType.WORD, DataType.SBYTE, DataType.SWORD): - value = int(argstr) + value = Value(dtype, int(argstr)) elif dtype == DataType.FLOAT: - value = float(argstr) + value = Value(dtype, float(argstr)) elif dtype == DataType.BOOL: - value = argstr.lower() not in ("0", "false") + value = Value(dtype, argstr.lower() not in ("0", "false")) elif dtype in (DataType.ARRAY_BYTE, DataType.ARRAY_SBYTE, DataType.ARRAY_WORD, DataType.ARRAY_SWORD): args = argstr.split(maxsplit=1) length = int(args[0]) valuestr = args[1] typecode = self.get_array_type(dtype) if valuestr[0] == '[' and valuestr[-1] == ']': - value = array.array(typecode, [int(v) for v in valuestr[1:-1].split()]) + value = Value(dtype, array.array(typecode, [int(v) for v in valuestr[1:-1].split()])) else: - value = array.array(typecode, [int(valuestr)]) * length + value = Value(dtype, array.array(typecode, [int(valuestr)]) * length) elif dtype in (DataType.MATRIX_BYTE, DataType.MATRIX_SBYTE): args = argstr.split(maxsplit=2) length = int(args[0]) @@ -91,12 +91,12 @@ class Parser: valuestr = args[2] typecode = self.get_array_type(dtype) if valuestr[0] == '[' and valuestr[-1] == ']': - value = array.array(typecode, [int(v) for v in valuestr[1:-1].split()]) + value = Value(dtype, array.array(typecode, [int(v) for v in valuestr[1:-1].split()])) else: - value = array.array(typecode, [int(valuestr)] * length * height) + value = Value(dtype, array.array(typecode, [int(valuestr)] * length * height)) else: raise TypeError("weird dtype", dtype) - variables.append(Variable(name, dtype, value, length, height, vartype == "const")) + variables.append(Variable(name, dtype, value, vartype == "const")) self.lineno += 1 self.skip_empty() assert self.source[self.lineno].startswith("%end_vardefs") diff --git a/tinyvm/program.py b/tinyvm/program.py index 718720849..246910753 100644 --- a/tinyvm/program.py +++ b/tinyvm/program.py @@ -1,5 +1,7 @@ import enum -from typing import Any, List, Dict, Optional +import array +import operator +from typing import List, Dict, Optional, Union, Callable class Opcode(enum.IntEnum): @@ -51,28 +53,109 @@ class DataType(enum.IntEnum): MATRIX_SBYTE = 12 +class Value: + __slots__ = ["dtype", "value", "length", "height"] + + def __init__(self, dtype: DataType, value: Union[int, float, bytearray, array.array], length: int=0, height: int=0) -> None: + self.dtype = dtype + self.value = value + self.length = length + self.height = height + + def __str__(self): + return repr(self) + + def __repr__(self): + return "".format(self.dtype.name, self.value) + + def number_arithmetic(self, v1: 'Value', oper: Callable, v2: 'Value') -> 'Value': + if v1.dtype != DataType.FLOAT and v2.dtype == DataType.FLOAT: + raise TypeError("cannot use a float in arithmetic operation on an integer", v1, oper.__name__, v2) + if v1.dtype == DataType.BYTE: + return Value(DataType.BYTE, oper(v1.value, v2.value) & 255) + if v1.dtype == DataType.SBYTE: + result = oper(v1.value, v2.value) + if result < -128 or result > 127: + raise OverflowError("sbyte", result) + return Value(DataType.SBYTE, result) + if v1.dtype == DataType.WORD: + return Value(DataType.WORD, oper(v1.value, v2.value) & 65535) + if v1.dtype == DataType.SWORD: + result = oper(v1.value, v2.value) + if result < -32768 or result > 32767: + raise OverflowError("sword", result) + return Value(DataType.SWORD, result) + if v1.dtype == DataType.FLOAT: + return Value(DataType.FLOAT, oper(v1.value, v2.value)) + raise TypeError("cannot {} {}, {}".format(oper.__name__, v1, v2)) + + def number_comparison(self, v1: 'Value', oper: Callable, v2: 'Value') -> bool: + if v1.dtype != DataType.FLOAT and v2.dtype == DataType.FLOAT: + raise TypeError("cannot use a float in logical operation on an integer", v1, oper.__name__, v2) + return oper(v1.value, v2.value) + + def __add__(self, other: 'Value') -> 'Value': + return self.number_arithmetic(self, operator.add, other) + + def __sub__(self, other: 'Value') -> 'Value': + return self.number_arithmetic(self, operator.sub, other) + + def __mul__(self, other: 'Value') -> 'Value': + return self.number_arithmetic(self, operator.sub, other) + + def __truediv__(self, other: 'Value') -> 'Value': + return self.number_arithmetic(self, operator.truediv, other) + + def __floordiv__(self, other: 'Value') -> 'Value': + return self.number_arithmetic(self, operator.floordiv, other) + + def __eq__(self, other: 'Value') -> bool: + return self.number_comparison(self, operator.eq, other) + + def __lt__(self, other: 'Value') -> bool: + return self.number_comparison(self, operator.lt, other) + + def __le__(self, other: 'Value') -> bool: + return self.number_comparison(self, operator.le, other) + + def __gt__(self, other: 'Value') -> bool: + return self.number_comparison(self, operator.gt, other) + + def __ge__(self, other: 'Value') -> bool: + return self.number_comparison(self, operator.ge, other) + + + class Variable: __slots__ = ["name", "value", "dtype", "length", "height", "const"] - def __init__(self, name: str, dtype: DataType, value: Any, length: int=0, height: int=0, const: bool=False) -> None: + def __init__(self, name: str, dtype: DataType, value: Value, const: bool=False) -> None: self.name = name self.value = value self.dtype = dtype self.const = const - self.length = length - self.height = height + + def __str__(self): + return repr(self) + + def __repr__(self): + return "".format(self.name, self.value, self.const) class Instruction: __slots__ = ["opcode", "args", "next", "alt_next"] - def __init__(self, opcode: Opcode, args: List[Any], nxt: Optional['Instruction']=None, alt_next: Optional['Instruction']=None) -> None: + def __init__(self, opcode: Opcode, args: List[Union[Value, int, str]], + nxt: Optional['Instruction']=None, alt_next: Optional['Instruction']=None) -> None: self.opcode = opcode self.args = args self.next = nxt # regular next statement, None=end self.alt_next = alt_next # alternate next statement (for condition nodes, and return instruction for call nodes) - def __str__(self) -> str: + def __str__(self): + return repr(self) + + def __repr__(self): return "".format(self.opcode.name, self.args) @@ -89,7 +172,10 @@ class Block: self.instructions = instructions or [] self.labels = labels or {} - def __str__(self) -> str: + def __str__(self): + return repr(self) + + def __repr__(self): if self.parent: return "".format(self.name, self.parent.name) return "".format(self.name) diff --git a/tinyvm/vm.py b/tinyvm/vm.py index cf6665ed3..ad01e7e4f 100644 --- a/tinyvm/vm.py +++ b/tinyvm/vm.py @@ -67,7 +67,7 @@ import tkinter import tkinter.font from typing import Dict, List, Tuple, Union from il65.emit import mflpt5_to_float, to_mflpt5 -from .program import Instruction, Variable, Block, Program, Opcode +from .program import Instruction, Variable, Block, Program, Opcode, Value, DataType class ExecutionError(Exception): @@ -151,7 +151,7 @@ class CallFrameMarker: return "".format(str(self.returninstruction)) -StackValueType = Union[bool, int, float, bytearray, array.array, CallFrameMarker] +StackValueType = Union[Value, CallFrameMarker] class Stack: @@ -214,8 +214,8 @@ class Stack: self.stack[-2] = x def _typecheck(self, value: StackValueType): - if type(value) not in (bool, int, float, bytearray, array.array, CallFrameMarker): - raise TypeError("invalid item type pushed") + if not isinstance(value, (Value, CallFrameMarker)): + raise TypeError("invalid item type pushed", value) # noinspection PyPep8Naming,PyUnusedLocal,PyMethodMayBeStatic @@ -251,7 +251,9 @@ class VM: self.program = self.main_program self.stack = self.main_stack self.pc = None # type: Instruction - self.charscreen = None + self.charscreen_address = 0 + self.charscreen_width = 0 + self.charscreen_height = 0 self.system = System(self) assert all(i.next for i in self.main_program if i.opcode != Opcode.TERMINATE), "main: all instrs next must be set" @@ -265,7 +267,8 @@ class VM: print("[TinyVM starting up.]") def enable_charscreen(self, screen_address: int, width: int, height: int) -> None: - self.charscreen = (screen_address, width, height) + self.charscreen_address = screen_address + self.charscreen_width, self.charscreen_height = width, height def flatten_programs(self, main: Program, timer: Program) \ -> Tuple[List[Instruction], List[Instruction], Dict[str, Variable], Dict[str, Instruction]]: @@ -294,12 +297,12 @@ class VM: if ins.opcode == Opcode.SYSCALL: continue if ins.args: - newargs = [] + newargs = [] # type: List[Union[str, int, Value]] for a in ins.args: - if type(a) is str: + if isinstance(a, str): newargs.append(prefix + "." + a) else: - newargs.append(a) + newargs.append(a) # type: ignore ins.args = newargs for vardef in block.variables: vname = prefix + "." + vardef.name @@ -332,8 +335,10 @@ class VM: i.next = nexti def run(self) -> None: - if self.charscreen: - threading.Thread(target=ScreenViewer.create, args=(self.memory, self.system, self.charscreen), name="screenviewer", daemon=True).start() + if self.charscreen_address: + threading.Thread(target=ScreenViewer.create, + args=(self.memory, self.system, self.charscreen_address, self.charscreen_width, self.charscreen_height), + name="screenviewer", daemon=True).start() self.pc = self.program[0] # first instruction of the main program self.stack.push(CallFrameMarker(None)) # enter the call frame so the timer program can end with a RETURN @@ -363,7 +368,6 @@ class VM: while True: self.timer_irq_event.wait(wait_time) self.timer_irq_event.clear() - print("....timer irq", wait_time, time.time()) # XXX start = time.perf_counter() if self.timer_program: with self.timer_irq_interlock: @@ -399,8 +403,9 @@ class VM: if self.pc is not None: print("* instruction:", self.pc) - def assign_variable(self, variable: Variable, value: StackValueType) -> None: + def assign_variable(self, variable: Variable, value: Value) -> None: assert not variable.const, "cannot modify a const" + assert isinstance(value, Value) variable.value = value def opcode_NOP(self, instruction: Instruction) -> bool: @@ -501,49 +506,53 @@ class VM: second, first = self.stack.pop2() ifirst = 1 if first else 0 isecond = 1 if second else 0 - self.stack.push(bool(ifirst ^ isecond)) + self.stack.push(Value(DataType.BOOL, bool(ifirst ^ isecond))) return True def opcode_NOT(self, instruction: Instruction) -> bool: - self.stack.push(not self.stack.pop()) + self.stack.push(Value(DataType.BOOL, not self.stack.pop())) return True def opcode_TEST(self, instruction: Instruction) -> bool: - self.stack.push(bool(self.stack.pop())) + self.stack.push(Value(DataType.BOOL, bool(self.stack.pop()))) return True def opcode_CMP_EQ(self, instruction: Instruction) -> bool: second, first = self.stack.pop2() - self.stack.push(first == second) + self.stack.push(Value(DataType.BOOL, first == second)) return True def opcode_CMP_LT(self, instruction: Instruction) -> bool: second, first = self.stack.pop2() - self.stack.push(first < second) # type: ignore + self.stack.push(Value(DataType.BOOL, first < second)) return True def opcode_CMP_GT(self, instruction: Instruction) -> bool: second, first = self.stack.pop2() - self.stack.push(first > second) # type: ignore + self.stack.push(Value(DataType.BOOL, first > second)) return True def opcode_CMP_LTE(self, instruction: Instruction) -> bool: second, first = self.stack.pop2() - self.stack.push(first <= second) # type: ignore + self.stack.push(Value(DataType.BOOL, first <= second)) return True def opcode_CMP_GTE(self, instruction: Instruction) -> bool: second, first = self.stack.pop2() - self.stack.push(first >= second) # type: ignore + self.stack.push(Value(DataType.BOOL, first >= second)) return True def opcode_CALL(self, instruction: Instruction) -> bool: # arguments are already on the stack - self.stack.push_under(instruction.args[0], CallFrameMarker(instruction.alt_next)) + num_args = instruction.args[0] + assert isinstance(num_args, int) + self.stack.push_under(num_args, CallFrameMarker(instruction.alt_next)) return True def opcode_RETURN(self, instruction: Instruction) -> bool: - callframe = self.stack.pop_under(instruction.args[0]) + num_returnvalues = instruction.args[0] + assert isinstance(num_returnvalues, int) + callframe = self.stack.pop_under(num_returnvalues) assert isinstance(callframe, CallFrameMarker), callframe self.pc = callframe.returninstruction return False @@ -566,11 +575,13 @@ class VM: return False def opcode_SYSCALL(self, instruction: Instruction) -> bool: - call = getattr(self.system, "syscall_" + instruction.args[0], None) + syscall = instruction.args[0] + assert isinstance(syscall, str) + call = getattr(self.system, "syscall_" + syscall, None) if call: return call() else: - raise RuntimeError("no syscall method for " + instruction.args[0]) + raise RuntimeError("no syscall method for " + syscall) dispatch_table = { Opcode.TERMINATE: opcode_TERMINATE, @@ -619,35 +630,45 @@ class System: def syscall_printstr(self) -> bool: value = self.vm.stack.pop() - if isinstance(value, (bytearray, array.array)): - print(self.decodestr(value), end="") + assert isinstance(value, Value) + if value.dtype == DataType.ARRAY_BYTE: + print(self.decodestr(value.value), end="@") # type: ignore return True else: raise TypeError("printstr expects bytearray", value) def syscall_printchr(self) -> bool: - character = self.vm.stack.pop() - if isinstance(character, int): - print(self.decodestr(bytearray([character])), end="") + charactervalue = self.vm.stack.pop() + assert isinstance(charactervalue, Value) + if charactervalue.dtype == DataType.BYTE: + print(self.decodestr(bytearray([charactervalue.value])), end="") # type: ignore return True else: - raise TypeError("printchr expects integer (1 char)", character) + raise TypeError("printchr expects BYTE", charactervalue) def syscall_input(self) -> bool: - self.vm.stack.push(self.encodestr(input())) + self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(input()))) return True def syscall_getchr(self) -> bool: - self.vm.stack.push(self.encodestr(input() + '\n')[0]) + self.vm.stack.push(Value(DataType.BYTE, self.encodestr(input() + '\n')[0])) return True def syscall_decimalstr_signed(self) -> bool: value = self.vm.stack.pop() - if type(value) is int: - self.vm.stack.push(self.encodestr(str(value))) + if value.dtype in (DataType.SBYTE, DataType.SWORD): + self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(str(value.value)))) return True else: - raise TypeError("decimalstr expects int", value) + raise TypeError("decimalstr_signed expects signed int", value) + + def syscall_decimalstr_unsigned(self) -> bool: + value = self.vm.stack.pop() + if value.dtype in (DataType.BYTE, DataType.WORD): + self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(str(value.value)))) + return True + else: + raise TypeError("decimalstr_signed expects unsigned int", value) def syscall_hexstr_signed(self) -> bool: value = self.vm.stack.pop() @@ -656,40 +677,52 @@ class System: strvalue = "${:x}".format(value) else: strvalue = "-${:x}".format(-value) # type: ignore - self.vm.stack.push(self.encodestr(strvalue)) + self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(strvalue))) return True else: raise TypeError("hexstr expects int", value) def syscall_memwrite_byte(self) -> bool: value, address = self.vm.stack.pop2() - self.vm.memory.set_byte(address, value) # type: ignore + assert isinstance(value, Value) and isinstance(address, Value) + assert value.dtype == DataType.BYTE and address.dtype == DataType.WORD + self.vm.memory.set_byte(address.value, value.value) # type: ignore return True def syscall_memwrite_sbyte(self) -> bool: value, address = self.vm.stack.pop2() - self.vm.memory.set_sbyte(address, value) # type: ignore + assert isinstance(value, Value) and isinstance(address, Value) + assert value.dtype == DataType.SBYTE and address.dtype == DataType.WORD + self.vm.memory.set_sbyte(address.value, value.value) # type: ignore return True def syscall_memwrite_word(self) -> bool: value, address = self.vm.stack.pop2() - self.vm.memory.set_word(address, value) # type: ignore + assert isinstance(value, Value) and isinstance(address, Value) + assert value.dtype in (DataType.WORD, DataType.BYTE) and address.dtype == DataType.WORD + self.vm.memory.set_word(address.value, value.value) # type: ignore return True def syscall_memwrite_sword(self) -> bool: value, address = self.vm.stack.pop2() - self.vm.memory.set_sword(address, value) # type: ignore + assert isinstance(value, Value) and isinstance(address, Value) + assert value.dtype in (DataType.SWORD, DataType.SBYTE, DataType.BYTE) and address.dtype == DataType.WORD + self.vm.memory.set_sword(address.value, value.value) # type: ignore return True def syscall_memwrite_float(self) -> bool: value, address = self.vm.stack.pop2() - self.vm.memory.set_float(address, value) # type: ignore + assert isinstance(value, Value) and isinstance(address, Value) + assert value.dtype == DataType.FLOAT and address.dtype == DataType.WORD + self.vm.memory.set_float(address.value, value.value) # type: ignore return True def syscall_memwrite_str(self) -> bool: strbytes, address = self.vm.stack.pop2() - for i, b in enumerate(strbytes): # type: ignore - self.vm.memory.set_byte(address+i, b) # type: ignore + assert isinstance(strbytes, Value) and isinstance(address, Value) + assert strbytes.dtype == DataType.ARRAY_BYTE and address.dtype == DataType.WORD + for i, b in enumerate(strbytes.value): # type: ignore + self.vm.memory.set_byte(address+i, b) # type: ignore return True def syscall_smalldelay(self) -> bool: @@ -697,20 +730,20 @@ class System: return True def syscall_delay(self) -> bool: - time.sleep(0.5) + time.sleep(0.1) return True class ScreenViewer(tkinter.Tk): - def __init__(self, memory: Memory, system: System, screenconfig: Tuple[int, int, int]) -> None: + def __init__(self, memory: Memory, system: System, screen_addr: int, screen_width: int, screen_height: int) -> None: super().__init__() self.fontsize = 14 self.memory = memory self.system = system - self.address = screenconfig[0] - self.width = screenconfig[1] - self.height = screenconfig[2] - self.monospace = tkinter.font.Font(self, family="Courier", weight="bold", size=self.fontsize) + self.address = screen_addr + self.width = screen_width + self.height = screen_height + self.monospace = tkinter.font.Font(self, family="Courier", weight="bold", size=self.fontsize) # type: ignore cw = self.monospace.measure("x")*self.width+8 self.canvas = tkinter.Canvas(self, width=cw, height=self.fontsize*self.height+8, bg="blue") self.canvas.pack() @@ -725,6 +758,6 @@ class ScreenViewer(tkinter.Tk): self.after(10, self.update_screen) @classmethod - def create(cls, memory: Memory, system: System, screenconfig: Tuple[int, int, int]) -> None: - viewer = cls(memory, system, screenconfig) + def create(cls, memory: Memory, system: System, screen_addr: int, screen_width: int, screen_height: int) -> None: + viewer = cls(memory, system, screen_addr, screen_width, screen_height) viewer.mainloop()