mirror of
https://github.com/irmen/prog8.git
synced 2025-02-18 05:30:34 +00:00
memory mapped i/o (charin, charout)
This commit is contained in:
parent
7a5813d3be
commit
bcc1a6b91f
60
tests/test_vmcore.py
Normal file
60
tests/test_vmcore.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import pytest
|
||||||
|
from tinyvm.core import Memory
|
||||||
|
|
||||||
|
|
||||||
|
def test_memory_unsigned():
|
||||||
|
m = Memory()
|
||||||
|
m.set_byte(1000, 1)
|
||||||
|
m.set_byte(1001, 2)
|
||||||
|
m.set_byte(1002, 3)
|
||||||
|
m.set_byte(1003, 4)
|
||||||
|
m.set_byte(2000, 252)
|
||||||
|
m.set_byte(2001, 253)
|
||||||
|
m.set_byte(2002, 254)
|
||||||
|
m.set_byte(2003, 255)
|
||||||
|
assert 1 == m.get_byte(1000)
|
||||||
|
assert 2 == m.get_byte(1001)
|
||||||
|
assert 3 == m.get_byte(1002)
|
||||||
|
assert 4 == m.get_byte(1003)
|
||||||
|
assert 252 == m.get_byte(2000)
|
||||||
|
assert 253 == m.get_byte(2001)
|
||||||
|
assert 254 == m.get_byte(2002)
|
||||||
|
assert 255 == m.get_byte(2003)
|
||||||
|
assert b"\x01\x02\x03\x04" == m.get_bytes(1000, 4)
|
||||||
|
assert 0x0201 == m.get_word(1000)
|
||||||
|
assert 0xfffe == m.get_word(2002)
|
||||||
|
m.set_word(2002, 40000)
|
||||||
|
assert 40000 == m.get_word(2002)
|
||||||
|
assert 0x40 == m.get_byte(2002)
|
||||||
|
assert 0x9c == m.get_byte(2003)
|
||||||
|
|
||||||
|
|
||||||
|
def test_memory_signed():
|
||||||
|
m = Memory()
|
||||||
|
m.set_byte(1000, 1)
|
||||||
|
m.set_byte(1001, 2)
|
||||||
|
m.set_byte(1002, 3)
|
||||||
|
m.set_byte(1003, 4)
|
||||||
|
m.set_byte(2000, 252)
|
||||||
|
m.set_byte(2001, 253)
|
||||||
|
m.set_byte(2002, 254)
|
||||||
|
m.set_byte(2003, 255)
|
||||||
|
assert 1 == m.get_sbyte(1000)
|
||||||
|
assert 2 == m.get_sbyte(1001)
|
||||||
|
assert 3 == m.get_sbyte(1002)
|
||||||
|
assert 4 == m.get_sbyte(1003)
|
||||||
|
assert -4 == m.get_sbyte(2000)
|
||||||
|
assert -3 == m.get_sbyte(2001)
|
||||||
|
assert -2 == m.get_sbyte(2002)
|
||||||
|
assert -1 == m.get_sbyte(2003)
|
||||||
|
assert 0x0201 == m.get_sword(1000)
|
||||||
|
assert -2 == m.get_sword(2002)
|
||||||
|
m.set_sword(2002, 30000)
|
||||||
|
assert 30000 == m.get_sword(2002)
|
||||||
|
assert 0x30 == m.get_sbyte(2002)
|
||||||
|
assert 0x75 == m.get_sbyte(2003)
|
||||||
|
m.set_sword(2002, -30000)
|
||||||
|
assert -30000 == m.get_sword(2002)
|
||||||
|
assert 0x8ad0 == m.get_word(2002)
|
||||||
|
assert 0xd0 == m.get_byte(2002)
|
||||||
|
assert 0x8a == m.get_byte(2003)
|
110
tinyvm/core.py
Normal file
110
tinyvm/core.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
"""
|
||||||
|
Simplistic 8/16 bit Virtual Machine to execute a stack based instruction language.
|
||||||
|
Core data structures and definitions.
|
||||||
|
|
||||||
|
Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
import enum
|
||||||
|
import struct
|
||||||
|
from typing import Callable
|
||||||
|
from il65.emit import mflpt5_to_float, to_mflpt5
|
||||||
|
|
||||||
|
|
||||||
|
class DataType(enum.IntEnum):
|
||||||
|
BOOL = 1
|
||||||
|
BYTE = 2
|
||||||
|
SBYTE = 3
|
||||||
|
WORD = 4
|
||||||
|
SWORD = 5
|
||||||
|
FLOAT = 6
|
||||||
|
ARRAY_BYTE = 7
|
||||||
|
ARRAY_SBYTE = 8
|
||||||
|
ARRAY_WORD = 9
|
||||||
|
ARRAY_SWORD = 10
|
||||||
|
MATRIX_BYTE = 11
|
||||||
|
MATRIX_SBYTE = 12
|
||||||
|
|
||||||
|
|
||||||
|
class ExecutionError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TerminateExecution(SystemExit):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryAccessError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Memory:
|
||||||
|
def __init__(self):
|
||||||
|
self.mem = bytearray(65536)
|
||||||
|
self.readonly = bytearray(65536)
|
||||||
|
self.mmap_io_charout_addr = -1
|
||||||
|
self.mmap_io_charout_callback = None
|
||||||
|
self.mmap_io_charin_addr = -1
|
||||||
|
self.mmap_io_charin_callback = None
|
||||||
|
|
||||||
|
def mark_readonly(self, start: int, end: int) -> None:
|
||||||
|
self.readonly[start:end+1] = [1] * (end-start+1)
|
||||||
|
|
||||||
|
def memmapped_io_charout(self, address: int, callback: Callable) -> None:
|
||||||
|
self.mmap_io_charout_addr = address
|
||||||
|
self.mmap_io_charout_callback = callback
|
||||||
|
|
||||||
|
def memmapped_io_charin(self, address: int, callback: Callable) -> None:
|
||||||
|
self.mmap_io_charin_addr = address
|
||||||
|
self.mmap_io_charin_callback = callback
|
||||||
|
|
||||||
|
def get_byte(self, index: int) -> int:
|
||||||
|
if self.mmap_io_charin_addr == index:
|
||||||
|
self.mem[index] = self.mmap_io_charin_callback()
|
||||||
|
return self.mem[index]
|
||||||
|
|
||||||
|
def get_bytes(self, startindex: int, amount: int) -> bytearray:
|
||||||
|
return self.mem[startindex: startindex+amount]
|
||||||
|
|
||||||
|
def get_sbyte(self, index: int) -> int:
|
||||||
|
if self.mmap_io_charin_addr == index:
|
||||||
|
self.mem[index] = self.mmap_io_charin_callback()
|
||||||
|
return struct.unpack("b", self.mem[index:index+1])[0]
|
||||||
|
|
||||||
|
def get_word(self, index: int) -> int:
|
||||||
|
return self.mem[index] + 256 * self.mem[index+1]
|
||||||
|
|
||||||
|
def get_sword(self, index: int) -> int:
|
||||||
|
return struct.unpack("<h", self.mem[index:index+2])[0]
|
||||||
|
|
||||||
|
def get_float(self, index: int) -> float:
|
||||||
|
return mflpt5_to_float(self.mem[index: index+5])
|
||||||
|
|
||||||
|
def set_byte(self, index: int, value: int) -> None:
|
||||||
|
if self.readonly[index]:
|
||||||
|
raise MemoryAccessError("read-only", index)
|
||||||
|
self.mem[index] = value
|
||||||
|
if self.mmap_io_charout_addr == index:
|
||||||
|
self.mmap_io_charout_callback(value)
|
||||||
|
|
||||||
|
def set_sbyte(self, index: int, value: int) -> None:
|
||||||
|
if self.readonly[index]:
|
||||||
|
raise MemoryAccessError("read-only", index)
|
||||||
|
self.mem[index] = struct.pack("b", bytes([value]))[0]
|
||||||
|
if self.mmap_io_charout_addr == index:
|
||||||
|
self.mmap_io_charout_callback(self.mem[index])
|
||||||
|
|
||||||
|
def set_word(self, index: int, value: int) -> None:
|
||||||
|
if self.readonly[index] or self.readonly[index+1]:
|
||||||
|
raise MemoryAccessError("read-only", index)
|
||||||
|
self.mem[index], self.mem[index + 1] = struct.pack("<H", value)
|
||||||
|
|
||||||
|
def set_sword(self, index: int, value: int) -> None:
|
||||||
|
if self.readonly[index] or self.readonly[index+1]:
|
||||||
|
raise MemoryAccessError("read-only", index)
|
||||||
|
self.mem[index], self.mem[index + 1] = struct.pack("<h", value)
|
||||||
|
|
||||||
|
def set_float(self, index: int, value: float) -> None:
|
||||||
|
if any(self.readonly[index:index+5]):
|
||||||
|
raise MemoryAccessError("read-only", index)
|
||||||
|
self.mem[index: index+5] = to_mflpt5(value)
|
26
tinyvm/examples/printiovm.txt
Normal file
26
tinyvm/examples/printiovm.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
; source code for a tinyvm program; memory mapped I/O
|
||||||
|
%block b1
|
||||||
|
%vardefs
|
||||||
|
const byte chr_i 105
|
||||||
|
const byte chr_r 114
|
||||||
|
const byte chr_m 109
|
||||||
|
const byte chr_e 101
|
||||||
|
const byte chr_n 110
|
||||||
|
const byte chr_EOL 10
|
||||||
|
const word chrout 53248
|
||||||
|
const word chrin 53249
|
||||||
|
%end_vardefs
|
||||||
|
%instructions
|
||||||
|
loop:
|
||||||
|
push chrin
|
||||||
|
syscall memread_byte
|
||||||
|
push chrout
|
||||||
|
swap
|
||||||
|
syscall memwrite_byte
|
||||||
|
push chrout
|
||||||
|
push chr_EOL
|
||||||
|
syscall memwrite_byte
|
||||||
|
syscall delay
|
||||||
|
jump loop
|
||||||
|
%end_instructions
|
||||||
|
%end_block ;b1
|
@ -7,8 +7,9 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|||||||
|
|
||||||
import array
|
import array
|
||||||
from typing import Optional, List, Tuple, Dict, Any
|
from typing import Optional, List, Tuple, Dict, Any
|
||||||
from .program import DataType, Opcode, Program, Block, Variable, Instruction, Value
|
from .program import Opcode, Program, Block, Variable, Instruction, Value
|
||||||
from .vm import StackValueType
|
from .vm import StackValueType
|
||||||
|
from .core import DataType
|
||||||
|
|
||||||
|
|
||||||
class ParseError(Exception):
|
class ParseError(Exception):
|
||||||
|
@ -9,6 +9,7 @@ import enum
|
|||||||
import array
|
import array
|
||||||
import operator
|
import operator
|
||||||
from typing import List, Dict, Optional, Union, Callable, Any
|
from typing import List, Dict, Optional, Union, Callable, Any
|
||||||
|
from .core import DataType
|
||||||
|
|
||||||
|
|
||||||
class Opcode(enum.IntEnum):
|
class Opcode(enum.IntEnum):
|
||||||
@ -45,21 +46,6 @@ class Opcode(enum.IntEnum):
|
|||||||
SYSCALL = 205
|
SYSCALL = 205
|
||||||
|
|
||||||
|
|
||||||
class DataType(enum.IntEnum):
|
|
||||||
BOOL = 1
|
|
||||||
BYTE = 2
|
|
||||||
SBYTE = 3
|
|
||||||
WORD = 4
|
|
||||||
SWORD = 5
|
|
||||||
FLOAT = 6
|
|
||||||
ARRAY_BYTE = 7
|
|
||||||
ARRAY_SBYTE = 8
|
|
||||||
ARRAY_WORD = 9
|
|
||||||
ARRAY_SWORD = 10
|
|
||||||
MATRIX_BYTE = 11
|
|
||||||
MATRIX_SBYTE = 12
|
|
||||||
|
|
||||||
|
|
||||||
class Value:
|
class Value:
|
||||||
__slots__ = ["dtype", "value", "length", "height"]
|
__slots__ = ["dtype", "value", "length", "height"]
|
||||||
|
|
||||||
|
131
tinyvm/vm.py
131
tinyvm/vm.py
@ -27,7 +27,7 @@ Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
|||||||
# read [byte/bytearray from keyboard],
|
# read [byte/bytearray from keyboard],
|
||||||
# wait [till any input comes available], @todo
|
# wait [till any input comes available], @todo
|
||||||
# check [if input is available) @todo
|
# check [if input is available) @todo
|
||||||
# or via memory-mapped I/O (text screen matrix, keyboard scan register @todo)
|
# or via memory-mapped I/O (text screen matrix, keyboard scan register)
|
||||||
#
|
#
|
||||||
# CPU: stack based execution, no registers.
|
# CPU: stack based execution, no registers.
|
||||||
# unlimited dynamic variables (v0, v1, ...) that have a value and a type.
|
# unlimited dynamic variables (v0, v1, ...) that have a value and a type.
|
||||||
@ -73,76 +73,8 @@ import pprint
|
|||||||
import tkinter
|
import tkinter
|
||||||
import tkinter.font
|
import tkinter.font
|
||||||
from typing import Dict, List, Tuple, Union, no_type_check
|
from typing import Dict, List, Tuple, Union, no_type_check
|
||||||
from il65.emit import mflpt5_to_float, to_mflpt5
|
from .program import Instruction, Variable, Block, Program, Opcode, Value
|
||||||
from .program import Instruction, Variable, Block, Program, Opcode, Value, DataType
|
from .core import Memory, DataType, TerminateExecution
|
||||||
|
|
||||||
|
|
||||||
class ExecutionError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TerminateExecution(SystemExit):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MemoryAccessError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Memory:
|
|
||||||
def __init__(self):
|
|
||||||
self.mem = bytearray(65536)
|
|
||||||
self.readonly = bytearray(65536)
|
|
||||||
|
|
||||||
def mark_readonly(self, start: int, end: int) -> None:
|
|
||||||
self.readonly[start:end+1] = [1] * (end-start+1)
|
|
||||||
|
|
||||||
def get_byte(self, index: int) -> int:
|
|
||||||
return self.mem[index]
|
|
||||||
|
|
||||||
def get_bytes(self, startindex: int, amount: int) -> int:
|
|
||||||
return self.mem[startindex: startindex+amount]
|
|
||||||
|
|
||||||
def get_sbyte(self, index: int) -> int:
|
|
||||||
return 256 - self.mem[index]
|
|
||||||
|
|
||||||
def get_word(self, index: int) -> int:
|
|
||||||
return self.mem[index] + 256 * self.mem[index+1]
|
|
||||||
|
|
||||||
def get_sword(self, index: int) -> int:
|
|
||||||
return 65536 - (self.mem[index] + 256 * self.mem[index+1])
|
|
||||||
|
|
||||||
def get_float(self, index: int) -> float:
|
|
||||||
return mflpt5_to_float(self.mem[index: index+5])
|
|
||||||
|
|
||||||
def set_byte(self, index: int, value: int) -> None:
|
|
||||||
if self.readonly[index]:
|
|
||||||
raise MemoryAccessError("read-only", index)
|
|
||||||
self.mem[index] = value
|
|
||||||
|
|
||||||
def set_sbyte(self, index: int, value: int) -> None:
|
|
||||||
if self.readonly[index]:
|
|
||||||
raise MemoryAccessError("read-only", index)
|
|
||||||
self.mem[index] = value + 256
|
|
||||||
|
|
||||||
def set_word(self, index: int, value: int) -> None:
|
|
||||||
if self.readonly[index] or self.readonly[index+1]:
|
|
||||||
raise MemoryAccessError("read-only", index)
|
|
||||||
hi, lo = divmod(value, 256)
|
|
||||||
self.mem[index] = lo
|
|
||||||
self.mem[index+1] = hi
|
|
||||||
|
|
||||||
def set_sword(self, index: int, value: int) -> None:
|
|
||||||
if self.readonly[index] or self.readonly[index+1]:
|
|
||||||
raise MemoryAccessError("read-only", index)
|
|
||||||
hi, lo = divmod(value + 65536, 256)
|
|
||||||
self.mem[index] = lo
|
|
||||||
self.mem[index+1] = hi
|
|
||||||
|
|
||||||
def set_float(self, index: int, value: float) -> None:
|
|
||||||
if any(self.readonly[index:index+5]):
|
|
||||||
raise MemoryAccessError("read-only", index)
|
|
||||||
self.mem[index: index+5] = to_mflpt5(value)
|
|
||||||
|
|
||||||
|
|
||||||
class CallFrameMarker:
|
class CallFrameMarker:
|
||||||
@ -231,6 +163,8 @@ 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
|
||||||
|
charout_address = 0xd000
|
||||||
|
charin_address = 0xd001
|
||||||
|
|
||||||
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]
|
||||||
@ -246,6 +180,8 @@ class VM:
|
|||||||
if oc not in self.dispatch_table:
|
if oc not in self.dispatch_table:
|
||||||
raise NotImplementedError("no dispatch entry in table for " + oc.name)
|
raise NotImplementedError("no dispatch entry in table for " + oc.name)
|
||||||
self.memory = Memory()
|
self.memory = Memory()
|
||||||
|
self.memory.memmapped_io_charout(self.charout_address, self.memmapped_charout)
|
||||||
|
self.memory.memmapped_io_charin(self.charin_address, self.memmapped_charin)
|
||||||
for start, end in self.readonly_mem_ranges:
|
for start, end in self.readonly_mem_ranges:
|
||||||
self.memory.mark_readonly(start, end)
|
self.memory.mark_readonly(start, end)
|
||||||
self.main_stack = Stack()
|
self.main_stack = Stack()
|
||||||
@ -259,6 +195,7 @@ class VM:
|
|||||||
self.charscreen_address = 0
|
self.charscreen_address = 0
|
||||||
self.charscreen_width = 0
|
self.charscreen_width = 0
|
||||||
self.charscreen_height = 0
|
self.charscreen_height = 0
|
||||||
|
self.keyboard_scancode = 0
|
||||||
self.system = System(self)
|
self.system = System(self)
|
||||||
assert all(i.next for i in self.main_program
|
assert all(i.next for i in self.main_program
|
||||||
if i.opcode != Opcode.TERMINATE), "main: all instrs next must be set"
|
if i.opcode != Opcode.TERMINATE), "main: all instrs next must be set"
|
||||||
@ -341,7 +278,7 @@ class VM:
|
|||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
if self.charscreen_address:
|
if self.charscreen_address:
|
||||||
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, self.charscreen_address, self.charscreen_width, self.charscreen_height),
|
||||||
name="screenviewer", daemon=True).start()
|
name="screenviewer", daemon=True).start()
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
|
|
||||||
@ -405,6 +342,13 @@ class VM:
|
|||||||
if self.pc is not None:
|
if self.pc is not None:
|
||||||
print("* instruction:", self.pc)
|
print("* instruction:", self.pc)
|
||||||
|
|
||||||
|
def memmapped_charout(self, value: int) -> None:
|
||||||
|
string = self.system.decodestr(bytearray([value]))
|
||||||
|
print(string, end="")
|
||||||
|
|
||||||
|
def memmapped_charin(self) -> int:
|
||||||
|
return self.keyboard_scancode
|
||||||
|
|
||||||
def assign_variable(self, variable: Variable, value: Value) -> None:
|
def assign_variable(self, variable: Variable, value: Value) -> None:
|
||||||
assert not variable.const, "cannot modify a const"
|
assert not variable.const, "cannot modify a const"
|
||||||
assert isinstance(value, Value)
|
assert isinstance(value, Value)
|
||||||
@ -742,6 +686,13 @@ class System:
|
|||||||
self.vm.memory.set_byte(address+i, b) # type: ignore
|
self.vm.memory.set_byte(address+i, b) # type: ignore
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def syscall_memread_byte(self) -> bool:
|
||||||
|
address = self.vm.stack.pop()
|
||||||
|
assert isinstance(address, Value)
|
||||||
|
assert address.dtype == DataType.WORD
|
||||||
|
self.vm.stack.push(Value(DataType.BYTE, self.vm.memory.get_byte(address.value))) # type: ignore
|
||||||
|
return True
|
||||||
|
|
||||||
def syscall_smalldelay(self) -> bool:
|
def syscall_smalldelay(self) -> bool:
|
||||||
time.sleep(1/100)
|
time.sleep(1/100)
|
||||||
return True
|
return True
|
||||||
@ -752,12 +703,11 @@ 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, vm: VM, screen_addr: int, screen_width: int, screen_height: int) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.title("IL65 tinyvm")
|
self.title("IL65 tinyvm")
|
||||||
self.fontsize = 16
|
self.fontsize = 16
|
||||||
self.memory = memory
|
self.vm = vm
|
||||||
self.system = system
|
|
||||||
self.address = screen_addr
|
self.address = screen_addr
|
||||||
self.width = screen_width
|
self.width = screen_width
|
||||||
self.height = screen_height
|
self.height = screen_height
|
||||||
@ -765,19 +715,42 @@ class ScreenViewer(tkinter.Tk):
|
|||||||
cw = self.monospace.measure("x")*self.width+8
|
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 = tkinter.Canvas(self, width=cw, height=self.fontsize*self.height+8, bg="blue")
|
||||||
self.canvas.pack()
|
self.canvas.pack()
|
||||||
|
self.bind("<KeyPress>", self.keypress)
|
||||||
|
self.bind("<KeyRelease>", self.keyrelease)
|
||||||
self.after(10, self.update_screen)
|
self.after(10, self.update_screen)
|
||||||
|
|
||||||
def update_screen(self):
|
def keypress(self, e) -> None:
|
||||||
|
key = e.char or e.keysym
|
||||||
|
if len(key) == 1:
|
||||||
|
self.vm.keyboard_scancode = self.vm.system.encodestr(key)[0]
|
||||||
|
elif len(key) > 1:
|
||||||
|
code = 0
|
||||||
|
if key == "Up":
|
||||||
|
code = ord("w")
|
||||||
|
elif key == "Down":
|
||||||
|
code = ord("s")
|
||||||
|
elif key == "Left":
|
||||||
|
code = ord("a")
|
||||||
|
elif key == "Right":
|
||||||
|
code = ord("d")
|
||||||
|
self.vm.keyboard_scancode = code
|
||||||
|
else:
|
||||||
|
self.vm.keyboard_scancode = 0
|
||||||
|
|
||||||
|
def keyrelease(self, e) -> None:
|
||||||
|
self.vm.keyboard_scancode = 0
|
||||||
|
|
||||||
|
def update_screen(self) -> None:
|
||||||
self.canvas.delete(tkinter.ALL)
|
self.canvas.delete(tkinter.ALL)
|
||||||
lines = []
|
lines = []
|
||||||
for y in range(self.height):
|
for y in range(self.height):
|
||||||
line = self.system.decodestr(self.memory.get_bytes(self.address+y*self.width, self.width))
|
line = self.vm.system.decodestr(self.vm.memory.get_bytes(self.address+y*self.width, self.width))
|
||||||
lines.append("".join(c if c.isprintable() else " " for c in line))
|
lines.append("".join(c if c.isprintable() else " " for c in line))
|
||||||
for y, line in enumerate(lines):
|
for y, line in enumerate(lines):
|
||||||
self.canvas.create_text(4, self.fontsize*y, text=line, fill="white", font=self.monospace, anchor=tkinter.NW)
|
self.canvas.create_text(4, self.fontsize*y, text=line, fill="white", font=self.monospace, anchor=tkinter.NW)
|
||||||
self.after(30, self.update_screen)
|
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, vm: VM, screen_addr: int, screen_width: int, screen_height: int) -> None:
|
||||||
viewer = cls(memory, system, screen_addr, screen_width, screen_height)
|
viewer = cls(vm, screen_addr, screen_width, screen_height)
|
||||||
viewer.mainloop()
|
viewer.mainloop()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user