mirror of
https://github.com/irmen/prog8.git
synced 2024-08-18 23:29:32 +00:00
timer irq changes
This commit is contained in:
parent
8bfe21dd65
commit
12ae95bbbc
@ -1,7 +1,7 @@
|
|||||||
; source code for a tinyvm program for the timer
|
; source code for a tinyvm program for the timer
|
||||||
%block b1_timer
|
%block b1_timer
|
||||||
%vardefs
|
%vardefs
|
||||||
var byte teller_timer 0
|
var byte teller_timer 32
|
||||||
const word screenloc_timer 1028
|
const word screenloc_timer 1028
|
||||||
const byte one_timer 1
|
const byte one_timer 1
|
||||||
%end_vardefs
|
%end_vardefs
|
||||||
|
@ -5,7 +5,7 @@ var word teller 0
|
|||||||
var word numbertoprint 0
|
var word numbertoprint 0
|
||||||
const byte one 1
|
const byte one 1
|
||||||
const word thousand 1000
|
const word thousand 1000
|
||||||
const byte space_chr 10
|
const byte space_chr 32
|
||||||
const word screenstart 1024
|
const word screenstart 1024
|
||||||
%end_vardefs
|
%end_vardefs
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import enum
|
import enum
|
||||||
import array
|
import array
|
||||||
import operator
|
import operator
|
||||||
from typing import List, Dict, Optional, Union, Callable
|
from typing import List, Dict, Optional, Union, Callable, Any
|
||||||
|
|
||||||
|
|
||||||
class Opcode(enum.IntEnum):
|
class Opcode(enum.IntEnum):
|
||||||
@ -109,7 +109,9 @@ class Value:
|
|||||||
def __floordiv__(self, other: 'Value') -> 'Value':
|
def __floordiv__(self, other: 'Value') -> 'Value':
|
||||||
return self.number_arithmetic(self, operator.floordiv, other)
|
return self.number_arithmetic(self, operator.floordiv, other)
|
||||||
|
|
||||||
def __eq__(self, other: 'Value') -> bool:
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
if not isinstance(other, Value):
|
||||||
|
return False
|
||||||
return self.number_comparison(self, operator.eq, other)
|
return self.number_comparison(self, operator.eq, other)
|
||||||
|
|
||||||
def __lt__(self, other: 'Value') -> bool:
|
def __lt__(self, other: 'Value') -> bool:
|
||||||
@ -125,7 +127,6 @@ class Value:
|
|||||||
return self.number_comparison(self, operator.ge, other)
|
return self.number_comparison(self, operator.ge, other)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Variable:
|
class Variable:
|
||||||
__slots__ = ["name", "value", "dtype", "length", "height", "const"]
|
__slots__ = ["name", "value", "dtype", "length", "height", "const"]
|
||||||
|
|
||||||
|
73
tinyvm/vm.py
73
tinyvm/vm.py
@ -51,7 +51,7 @@
|
|||||||
# call function (arguments are on stack)
|
# call function (arguments are on stack)
|
||||||
# enter / exit (function call frame)
|
# enter / exit (function call frame)
|
||||||
#
|
#
|
||||||
# TIMER INTERRUPT: triggered around each 1/30th of a second.
|
# TIMER INTERRUPT: triggered around each 1/60th of a second.
|
||||||
# executes on a DIFFERENT stack and with a different PROGRAM LIST,
|
# executes on a DIFFERENT stack and with a different PROGRAM LIST,
|
||||||
# but with access to ALL THE SAME DYNAMIC VARIABLES.
|
# but with access to ALL THE SAME DYNAMIC VARIABLES.
|
||||||
# This suspends the main program until the timer program RETURNs!
|
# This suspends the main program until the timer program RETURNs!
|
||||||
@ -65,7 +65,7 @@ import threading
|
|||||||
import pprint
|
import pprint
|
||||||
import tkinter
|
import tkinter
|
||||||
import tkinter.font
|
import tkinter.font
|
||||||
from typing import Dict, List, Tuple, Union
|
from typing import Dict, List, Tuple, Union, no_type_check
|
||||||
from il65.emit import mflpt5_to_float, to_mflpt5
|
from il65.emit import mflpt5_to_float, to_mflpt5
|
||||||
from .program import Instruction, Variable, Block, Program, Opcode, Value, DataType
|
from .program import Instruction, Variable, Block, Program, Opcode, Value, DataType
|
||||||
|
|
||||||
@ -224,8 +224,6 @@ class VM:
|
|||||||
str_alt_encoding = "iso-8859-15"
|
str_alt_encoding = "iso-8859-15"
|
||||||
readonly_mem_ranges = [] # type: List[Tuple[int, int]]
|
readonly_mem_ranges = [] # type: List[Tuple[int, int]]
|
||||||
timer_irq_resolution = 1/30
|
timer_irq_resolution = 1/30
|
||||||
timer_irq_interlock = threading.Lock()
|
|
||||||
timer_irq_event = threading.Event()
|
|
||||||
|
|
||||||
def __init__(self, program: Program, timerprogram: Program=None) -> None:
|
def __init__(self, program: Program, timerprogram: Program=None) -> None:
|
||||||
opcode_names = [oc.name for oc in Opcode]
|
opcode_names = [oc.name for oc in Opcode]
|
||||||
@ -263,7 +261,6 @@ class VM:
|
|||||||
if i.opcode in (Opcode.CALL, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE)), "main: alt_nexts must be set"
|
if i.opcode in (Opcode.CALL, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE)), "main: alt_nexts must be set"
|
||||||
assert all(i.alt_next for i in self.timer_program
|
assert all(i.alt_next for i in self.timer_program
|
||||||
if i.opcode in (Opcode.CALL, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE)), "timer: alt_nexts must be set"
|
if i.opcode in (Opcode.CALL, Opcode.JUMP_IF_FALSE, Opcode.JUMP_IF_TRUE)), "timer: alt_nexts must be set"
|
||||||
threading.Thread(target=self.timer_irq, name="timer_irq", daemon=True).start()
|
|
||||||
print("[TinyVM starting up.]")
|
print("[TinyVM starting up.]")
|
||||||
|
|
||||||
def enable_charscreen(self, screen_address: int, width: int, height: int) -> None:
|
def enable_charscreen(self, screen_address: int, width: int, height: int) -> None:
|
||||||
@ -339,15 +336,24 @@ class VM:
|
|||||||
threading.Thread(target=ScreenViewer.create,
|
threading.Thread(target=ScreenViewer.create,
|
||||||
args=(self.memory, self.system, self.charscreen_address, self.charscreen_width, self.charscreen_height),
|
args=(self.memory, self.system, self.charscreen_address, self.charscreen_width, self.charscreen_height),
|
||||||
name="screenviewer", daemon=True).start()
|
name="screenviewer", daemon=True).start()
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
self.pc = self.program[0] # first instruction of the main program
|
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
|
self.stack.push(CallFrameMarker(None)) # enter the call frame so the timer program can end with a RETURN
|
||||||
try:
|
try:
|
||||||
|
counter = 0
|
||||||
|
previous_timer_irq = time.perf_counter()
|
||||||
while self.pc is not None:
|
while self.pc is not None:
|
||||||
with self.timer_irq_interlock:
|
|
||||||
next_pc = self.dispatch_table[self.pc.opcode](self, self.pc)
|
next_pc = self.dispatch_table[self.pc.opcode](self, self.pc)
|
||||||
if next_pc:
|
if next_pc:
|
||||||
self.pc = self.pc.next
|
self.pc = self.pc.next
|
||||||
|
counter += 1
|
||||||
|
if self.charscreen_address and counter % 1000 == 0:
|
||||||
|
time.sleep(0.001) # allow the tkinter window to update
|
||||||
|
time_since_irq = time.perf_counter() - previous_timer_irq
|
||||||
|
if time_since_irq > 1/60:
|
||||||
|
self.timer_irq()
|
||||||
|
previous_timer_irq = time.perf_counter()
|
||||||
except TerminateExecution as x:
|
except TerminateExecution as x:
|
||||||
why = str(x)
|
why = str(x)
|
||||||
print("[TinyVM execution terminated{:s}]\n".format(": "+why if why else "."))
|
print("[TinyVM execution terminated{:s}]\n".format(": "+why if why else "."))
|
||||||
@ -360,17 +366,9 @@ class VM:
|
|||||||
print("[TinyVM execution ended.]")
|
print("[TinyVM execution ended.]")
|
||||||
|
|
||||||
def timer_irq(self) -> None:
|
def timer_irq(self) -> None:
|
||||||
# This is the timer 'irq' handler. It runs the timer program at a certain interval.
|
# This is the timer 'irq' handler. It is called to run the timer program at a certain interval.
|
||||||
# NOTE: executing the timer program will LOCK the main program and vice versa!
|
# During the execution the main program is halted
|
||||||
# (because the VM is a strictly single threaded machine)
|
|
||||||
resolution = 1/30
|
|
||||||
wait_time = resolution
|
|
||||||
while True:
|
|
||||||
self.timer_irq_event.wait(wait_time)
|
|
||||||
self.timer_irq_event.clear()
|
|
||||||
start = time.perf_counter()
|
|
||||||
if self.timer_program:
|
if self.timer_program:
|
||||||
with self.timer_irq_interlock:
|
|
||||||
previous_pc = self.pc
|
previous_pc = self.pc
|
||||||
previous_program = self.program
|
previous_program = self.program
|
||||||
previous_stack = self.stack
|
previous_stack = self.stack
|
||||||
@ -385,9 +383,6 @@ class VM:
|
|||||||
self.pc = previous_pc
|
self.pc = previous_pc
|
||||||
self.program = previous_program
|
self.program = previous_program
|
||||||
self.stack = previous_stack
|
self.stack = previous_stack
|
||||||
current = time.perf_counter()
|
|
||||||
duration, previously = current - start, current
|
|
||||||
wait_time = max(0, resolution - duration)
|
|
||||||
|
|
||||||
def debug_stack(self, size: int=5) -> None:
|
def debug_stack(self, size: int=5) -> None:
|
||||||
stack = self.stack.debug_peek(size)
|
stack = self.stack.debug_peek(size)
|
||||||
@ -416,7 +411,7 @@ class VM:
|
|||||||
raise TerminateExecution()
|
raise TerminateExecution()
|
||||||
|
|
||||||
def opcode_PUSH(self, instruction: Instruction) -> bool:
|
def opcode_PUSH(self, instruction: Instruction) -> bool:
|
||||||
value = self.variables[instruction.args[0]].value
|
value = self.variables[instruction.args[0]].value # type: ignore
|
||||||
self.stack.push(value)
|
self.stack.push(value)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -435,12 +430,14 @@ class VM:
|
|||||||
self.stack.push2(value2, value1)
|
self.stack.push2(value2, value1)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_PUSH2(self, instruction: Instruction) -> bool:
|
def opcode_PUSH2(self, instruction: Instruction) -> bool:
|
||||||
value1 = self.variables[instruction.args[0]].value
|
value1 = self.variables[instruction.args[0]].value
|
||||||
value2 = self.variables[instruction.args[1]].value
|
value2 = self.variables[instruction.args[1]].value
|
||||||
self.stack.push2(value1, value2)
|
self.stack.push2(value1, value2)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_PUSH3(self, instruction: Instruction) -> bool:
|
def opcode_PUSH3(self, instruction: Instruction) -> bool:
|
||||||
value1 = self.variables[instruction.args[0]].value
|
value1 = self.variables[instruction.args[0]].value
|
||||||
value2 = self.variables[instruction.args[1]].value
|
value2 = self.variables[instruction.args[1]].value
|
||||||
@ -448,12 +445,14 @@ class VM:
|
|||||||
self.stack.push3(value1, value2, value3)
|
self.stack.push3(value1, value2, value3)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_POP(self, instruction: Instruction) -> bool:
|
def opcode_POP(self, instruction: Instruction) -> bool:
|
||||||
value = self.stack.pop()
|
value = self.stack.pop()
|
||||||
variable = self.variables[instruction.args[0]]
|
variable = self.variables[instruction.args[0]]
|
||||||
self.assign_variable(variable, value)
|
self.assign_variable(variable, value)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_POP2(self, instruction: Instruction) -> bool:
|
def opcode_POP2(self, instruction: Instruction) -> bool:
|
||||||
value1, value2 = self.stack.pop2()
|
value1, value2 = self.stack.pop2()
|
||||||
variable = self.variables[instruction.args[0]]
|
variable = self.variables[instruction.args[0]]
|
||||||
@ -462,6 +461,7 @@ class VM:
|
|||||||
self.assign_variable(variable, value2)
|
self.assign_variable(variable, value2)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_POP3(self, instruction: Instruction) -> bool:
|
def opcode_POP3(self, instruction: Instruction) -> bool:
|
||||||
value1, value2, value3 = self.stack.pop3()
|
value1, value2, value3 = self.stack.pop3()
|
||||||
variable = self.variables[instruction.args[0]]
|
variable = self.variables[instruction.args[0]]
|
||||||
@ -472,24 +472,28 @@ class VM:
|
|||||||
self.assign_variable(variable, value3)
|
self.assign_variable(variable, value3)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_ADD(self, instruction: Instruction) -> bool:
|
def opcode_ADD(self, instruction: Instruction) -> bool:
|
||||||
second, first = self.stack.pop2()
|
second, first = self.stack.pop2()
|
||||||
self.stack.push(first + second) # type: ignore
|
self.stack.push(first + second)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_SUB(self, instruction: Instruction) -> bool:
|
def opcode_SUB(self, instruction: Instruction) -> bool:
|
||||||
second, first = self.stack.pop2()
|
second, first = self.stack.pop2()
|
||||||
self.stack.push(first - second) # type: ignore
|
self.stack.push(first - second)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_MUL(self, instruction: Instruction) -> bool:
|
def opcode_MUL(self, instruction: Instruction) -> bool:
|
||||||
second, first = self.stack.pop2()
|
second, first = self.stack.pop2()
|
||||||
self.stack.push(first * second) # type: ignore
|
self.stack.push(first * second)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_DIV(self, instruction: Instruction) -> bool:
|
def opcode_DIV(self, instruction: Instruction) -> bool:
|
||||||
second, first = self.stack.pop2()
|
second, first = self.stack.pop2()
|
||||||
self.stack.push(first / second) # type: ignore
|
self.stack.push(first / second)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def opcode_AND(self, instruction: Instruction) -> bool:
|
def opcode_AND(self, instruction: Instruction) -> bool:
|
||||||
@ -522,21 +526,25 @@ class VM:
|
|||||||
self.stack.push(Value(DataType.BOOL, first == second))
|
self.stack.push(Value(DataType.BOOL, first == second))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_CMP_LT(self, instruction: Instruction) -> bool:
|
def opcode_CMP_LT(self, instruction: Instruction) -> bool:
|
||||||
second, first = self.stack.pop2()
|
second, first = self.stack.pop2()
|
||||||
self.stack.push(Value(DataType.BOOL, first < second))
|
self.stack.push(Value(DataType.BOOL, first < second))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_CMP_GT(self, instruction: Instruction) -> bool:
|
def opcode_CMP_GT(self, instruction: Instruction) -> bool:
|
||||||
second, first = self.stack.pop2()
|
second, first = self.stack.pop2()
|
||||||
self.stack.push(Value(DataType.BOOL, first > second))
|
self.stack.push(Value(DataType.BOOL, first > second))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_CMP_LTE(self, instruction: Instruction) -> bool:
|
def opcode_CMP_LTE(self, instruction: Instruction) -> bool:
|
||||||
second, first = self.stack.pop2()
|
second, first = self.stack.pop2()
|
||||||
self.stack.push(Value(DataType.BOOL, first <= second))
|
self.stack.push(Value(DataType.BOOL, first <= second))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@no_type_check
|
||||||
def opcode_CMP_GTE(self, instruction: Instruction) -> bool:
|
def opcode_CMP_GTE(self, instruction: Instruction) -> bool:
|
||||||
second, first = self.stack.pop2()
|
second, first = self.stack.pop2()
|
||||||
self.stack.push(Value(DataType.BOOL, first >= second))
|
self.stack.push(Value(DataType.BOOL, first >= second))
|
||||||
@ -632,7 +640,7 @@ class System:
|
|||||||
value = self.vm.stack.pop()
|
value = self.vm.stack.pop()
|
||||||
assert isinstance(value, Value)
|
assert isinstance(value, Value)
|
||||||
if value.dtype == DataType.ARRAY_BYTE:
|
if value.dtype == DataType.ARRAY_BYTE:
|
||||||
print(self.decodestr(value.value), end="@") # type: ignore
|
print(self.decodestr(value.value), end="") # type: ignore
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
raise TypeError("printstr expects bytearray", value)
|
raise TypeError("printstr expects bytearray", value)
|
||||||
@ -656,6 +664,7 @@ class System:
|
|||||||
|
|
||||||
def syscall_decimalstr_signed(self) -> bool:
|
def syscall_decimalstr_signed(self) -> bool:
|
||||||
value = self.vm.stack.pop()
|
value = self.vm.stack.pop()
|
||||||
|
assert isinstance(value, Value)
|
||||||
if value.dtype in (DataType.SBYTE, DataType.SWORD):
|
if value.dtype in (DataType.SBYTE, DataType.SWORD):
|
||||||
self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(str(value.value))))
|
self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(str(value.value))))
|
||||||
return True
|
return True
|
||||||
@ -664,6 +673,7 @@ class System:
|
|||||||
|
|
||||||
def syscall_decimalstr_unsigned(self) -> bool:
|
def syscall_decimalstr_unsigned(self) -> bool:
|
||||||
value = self.vm.stack.pop()
|
value = self.vm.stack.pop()
|
||||||
|
assert isinstance(value, Value)
|
||||||
if value.dtype in (DataType.BYTE, DataType.WORD):
|
if value.dtype in (DataType.BYTE, DataType.WORD):
|
||||||
self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(str(value.value))))
|
self.vm.stack.push(Value(DataType.ARRAY_BYTE, self.encodestr(str(value.value))))
|
||||||
return True
|
return True
|
||||||
@ -737,7 +747,8 @@ class System:
|
|||||||
class ScreenViewer(tkinter.Tk):
|
class ScreenViewer(tkinter.Tk):
|
||||||
def __init__(self, memory: Memory, system: System, screen_addr: int, screen_width: int, screen_height: int) -> None:
|
def __init__(self, memory: Memory, system: System, screen_addr: int, screen_width: int, screen_height: int) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.fontsize = 14
|
self.title("IL65 tinyvm")
|
||||||
|
self.fontsize = 16
|
||||||
self.memory = memory
|
self.memory = memory
|
||||||
self.system = system
|
self.system = system
|
||||||
self.address = screen_addr
|
self.address = screen_addr
|
||||||
@ -751,11 +762,13 @@ class ScreenViewer(tkinter.Tk):
|
|||||||
|
|
||||||
def update_screen(self):
|
def update_screen(self):
|
||||||
self.canvas.delete(tkinter.ALL)
|
self.canvas.delete(tkinter.ALL)
|
||||||
|
lines = []
|
||||||
for y in range(self.height):
|
for y in range(self.height):
|
||||||
line = self.memory.get_bytes(self.address+y*self.width, self.width)
|
line = self.system.decodestr(self.memory.get_bytes(self.address+y*self.width, self.width))
|
||||||
text = self.system.decodestr(line)
|
lines.append("".join(c if c.isprintable() else " " for c in line))
|
||||||
self.canvas.create_text(4, self.fontsize*y, text=text, fill="white", font=self.monospace, anchor=tkinter.NW)
|
for y, line in enumerate(lines):
|
||||||
self.after(10, self.update_screen)
|
self.canvas.create_text(4, self.fontsize*y, text=line, fill="white", font=self.monospace, anchor=tkinter.NW)
|
||||||
|
self.after(30, self.update_screen)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, memory: Memory, system: System, screen_addr: int, screen_width: int, screen_height: int) -> None:
|
def create(cls, memory: Memory, system: System, screen_addr: int, screen_width: int, screen_height: int) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user