aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_emu
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2024-04-13 23:41:59 +0300
committerJan Tuomi <jan@jantuomi.fi>2024-04-14 00:06:39 +0300
commitafe7b17b1de1a34d384e72f49d402ea5ee4591cd (patch)
tree03e51fc272d225df180d63a494d888655c39f54d /atk16_emu
parent915846b9898f74308509346ba94db0dd2ba1de45 (diff)
Implement keyboard interrupts
Diffstat (limited to 'atk16_emu')
-rw-r--r--atk16_emu/emu.py19
-rw-r--r--atk16_emu/peripherals.py30
2 files changed, 42 insertions, 7 deletions
diff --git a/atk16_emu/emu.py b/atk16_emu/emu.py
index 3f0fa41..43dfdf1 100644
--- a/atk16_emu/emu.py
+++ b/atk16_emu/emu.py
@@ -119,6 +119,7 @@ class Machine:
self.rh = Register(16)
self.pc = Counter(16)
+ self.ipc = Register(16)
self.fr = ALUFlags(
carry = False,
overflow = False,
@@ -128,10 +129,11 @@ class Machine:
self.steps_taken = 0
self.running = False
+ self.is_in_irq = False
self.peripherals_enabled = peripherals_enabled
if peripherals_enabled:
- self.peripherals = Peripherals()
+ self.peripherals = Peripherals(set_irq_line=self.set_irq_line)
else:
self.peripherals = DummyPeripherals()
@@ -176,6 +178,9 @@ class Machine:
new_machine.pc = Counter(self.pc.bits)
new_machine.pc.value = self.pc.value
+ new_machine.ipc = Register(self.ipc.bits)
+ new_machine.ipc.value = self.ipc.value
+
new_machine.fr = ALUFlags(
carry = self.fr.carry,
overflow = self.fr.overflow,
@@ -262,6 +267,14 @@ class Machine:
raise ValueError(f"Invalid flag number: {n}")
+ def set_irq_line(self, line: int):
+ if not self.is_in_irq:
+ self.ipc.value = self.pc.value
+ isr_addr_pointer = 0x10 + line # see bootstrap.atk16 vector table
+ isr_addr = self.mem_read(isr_addr_pointer)
+ self.pc.value = isr_addr
+ self.is_in_irq = True
+
def step(self):
if not self.running:
raise RuntimeError("Machine is not running")
@@ -345,6 +358,7 @@ class Machine:
case ISRP0():
# interrupt service routine, read store PC in IPC register, set PC to ISRA value
+
raise NotImplementedError()
case ISRP1():
@@ -353,7 +367,8 @@ class Machine:
case RTI():
# return from interrupt routine, read PC from IPC register
- raise NotImplementedError()
+ self.pc.value = self.ipc.value
+ self.is_in_irq = False
case HLT():
self.running = False
diff --git a/atk16_emu/peripherals.py b/atk16_emu/peripherals.py
index 54020f4..343f32d 100644
--- a/atk16_emu/peripherals.py
+++ b/atk16_emu/peripherals.py
@@ -2,21 +2,26 @@ import pygame
import sys
import random
from .memory import RAM, ROM
+from typing import Callable
+
+IRQ_LINE_KEYBOARD = 0
class Peripherals:
- def __init__(self):
+ def __init__(self, set_irq_line: Callable[[int], None]):
pygame.init()
self.graphics = Graphics()
self.keyboard = Keyboard()
self.terminal = Terminal()
+ self.set_irq_line = set_irq_line
+
def step(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit(0)
elif event.type == pygame.KEYDOWN:
- print(f"key pressed: {event.key}")
- self.keyboard.latest_pressed = event.key
+ self.keyboard.set_latest_pressed(event.key)
+ self.set_irq_line(IRQ_LINE_KEYBOARD)
self.graphics.step()
@@ -62,7 +67,7 @@ class TPU:
def write_char(self, addr: int, char: int):
# address: 5 bits y, 6 bits x
- self.text_mem.write(addr, char)
+ self.text_mem.write(addr, char & 0xFF)
class Graphics:
def __init__(self):
@@ -70,10 +75,14 @@ class Graphics:
surface_scale = 4
self.screen = pygame.display.set_mode((320 * surface_scale, 240 * surface_scale))
+ pygame.display.set_caption("ATK16 Emulator")
self.clock = pygame.time.Clock()
self.tpu = TPU()
self.active_picture_unit: TPU | None = None
+ self.step_counter = 0
+ self.draw_frame_every_n_steps = 500 # arbitrary, perf related
+
def activate_tpu(self):
self.active_picture_unit = self.tpu
@@ -84,13 +93,17 @@ class Graphics:
self.active_picture_unit = None
def step(self):
- if self.active_picture_unit:
+ if self.active_picture_unit and self.step_counter >= self.draw_frame_every_n_steps:
self.active_picture_unit.frame()
self.screen.blit(pygame.transform.scale(self.tpu.surface, self.screen.get_size()), (0, 0))
pygame.display.flip()
self.clock.tick(60)
+ self.step_counter = 0
+ else:
+ self.step_counter += 1
+
def write(self, addr: int, char: int):
# take only the lower 11 bits
self.tpu.write_char(addr & 0x7FF, char)
@@ -119,6 +132,13 @@ class Keyboard:
# mimic electronic behaviour my assigning a random value at start
self.latest_pressed: int = random.randrange(0, 2 ** 16)
+ def set_latest_pressed(self, i: int):
+ if i < 0 or i > 0xFFFF:
+ # sometimes the keycodes are out of range, don't know why
+ return
+
+ self.latest_pressed = i
+
def read(self) -> int:
return self.latest_pressed