mirror of
https://github.com/irmen/prog8.git
synced 2025-04-14 02:37:09 +00:00
typing
This commit is contained in:
parent
ab84b6012b
commit
8bfe21dd65
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
137
tinyvm/vm.py
137
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 "<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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user