This commit is contained in:
Irmen de Jong 2018-03-04 15:11:45 +01:00
parent ab84b6012b
commit 8bfe21dd65
6 changed files with 212 additions and 91 deletions

View File

@ -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)

View File

@ -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 "<LiteralValue value={!r} at {}>".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:

View File

@ -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

View File

@ -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")

View File

@ -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 "<Value dtype={} val={}>".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 "<Var name={} value={} const? {}>".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 "<Instruction {} args: {}>".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 "<Block '{}' in '{}'>".format(self.name, self.parent.name)
return "<Block '{}'>".format(self.name)

View File

@ -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 "<CallFrameMarker returninstruction={:s}>".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()