aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_emu
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2024-04-13 00:25:18 +0300
committerJan Tuomi <jan@jantuomi.fi>2024-04-13 00:25:18 +0300
commit3f9f7eefb9d0836ec53a08e311219f7f497517c7 (patch)
tree5b013223d710f179ec8fb31c159c62b5de665dc5 /atk16_emu
parentb6ac63225524f1b34ffc4f780726ed981ba7e6a2 (diff)
Make text mode work
Diffstat (limited to 'atk16_emu')
-rw-r--r--atk16_emu/emu.py60
-rw-r--r--atk16_emu/memory.py56
-rw-r--r--atk16_emu/peripherals.py56
3 files changed, 104 insertions, 68 deletions
diff --git a/atk16_emu/emu.py b/atk16_emu/emu.py
index 1a08103..90b588d 100644
--- a/atk16_emu/emu.py
+++ b/atk16_emu/emu.py
@@ -1,68 +1,14 @@
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
-import random
+
import sys
from dataclasses import dataclass
from .opcodes import *
+from .memory import *
from .colors import C
from .peripherals import Graphics, DummyGraphics
-class Register:
- def __init__(self, bits: int):
- self.bits = bits
- self.value = random.randint(0, 2 ** bits)
-
- def set_value(self, value: int):
- if value < 0 or value >= 2 ** self.bits:
- raise ValueError(f"Value {value} is out of range for {self.bits}-bit register")
-
- self.value = value
-
-class Counter:
- def __init__(self, bits: int):
- self.bits = bits
- self.value = random.randint(0, 2 ** bits)
-
- def step(self):
- self.value = (self.value + 1) % (2 ** self.bits)
-
- def reset(self):
- self.value = 0
-
-class ROM:
- def __init__(self, addr_bits: int, data_bits: int):
- self.addr_bits = addr_bits
- self.data_bits = data_bits
- self.memory = [random.randint(0, 2 ** data_bits) for _ in range(2 ** addr_bits)]
-
- def read(self, addr: int) -> int:
- if addr < 0 or addr >= 2 ** self.addr_bits:
- raise ValueError(f"Address {addr} is out of range for {self.addr_bits}-bit ROM")
-
- return self.memory[addr]
-
-class RAM:
- def __init__(self, addr_bits: int, data_bits: int):
- self.addr_bits = addr_bits
- self.data_bits = data_bits
- self.memory = [random.randint(0, 2 ** data_bits) for _ in range(2 ** addr_bits)]
-
- def read(self, addr: int) -> int:
- if addr < 0 or addr >= 2 ** self.addr_bits:
- raise ValueError(f"Address {addr} is out of range for {self.addr_bits}-bit RAM")
-
- return self.memory[addr]
-
- def write(self, addr: int, value: int):
- if addr < 0 or addr >= 2 ** self.addr_bits:
- raise ValueError(f"Address {addr} is out of range for {self.addr_bits}-bit RAM")
-
- if value < 0 or value >= 2 ** self.data_bits:
- raise ValueError(f"Value {value} is out of range for {self.data_bits}-bit RAM")
-
- self.memory[addr] = value
-
@dataclass
class ALUFlags:
carry: bool
@@ -259,6 +205,8 @@ class Machine:
self.graphics.activate_tpu()
elif addr == 0xE002 and value == 0b10:
self.graphics.activate_ppu()
+ elif 0xF800 < addr <= 0xFFFF:
+ self.graphics.write(addr, value)
else:
self.ram.write(addr & 0x7FFF, value)
diff --git a/atk16_emu/memory.py b/atk16_emu/memory.py
new file mode 100644
index 0000000..2ce9c85
--- /dev/null
+++ b/atk16_emu/memory.py
@@ -0,0 +1,56 @@
+import random
+
+class Register:
+ def __init__(self, bits: int):
+ self.bits = bits
+ self.value = random.randrange(0, 2 ** bits)
+
+ def set_value(self, value: int):
+ if value < 0 or value >= 2 ** self.bits:
+ raise ValueError(f"Value {value} is out of range for {self.bits}-bit register")
+
+ self.value = value
+
+class Counter:
+ def __init__(self, bits: int):
+ self.bits = bits
+ self.value = random.randrange(0, 2 ** bits)
+
+ def step(self):
+ self.value = (self.value + 1) % (2 ** self.bits)
+
+ def reset(self):
+ self.value = 0
+
+class ROM:
+ def __init__(self, addr_bits: int, data_bits: int):
+ self.addr_bits = addr_bits
+ self.data_bits = data_bits
+ self.memory = [random.randrange(0, 2 ** data_bits) for _ in range(2 ** addr_bits)]
+
+ def read(self, addr: int) -> int:
+ if addr < 0 or addr >= 2 ** self.addr_bits:
+ raise ValueError(f"Address {addr} is out of range for {self.addr_bits}-bit ROM")
+
+ return self.memory[addr]
+
+class RAM:
+ def __init__(self, addr_bits: int, data_bits: int):
+ self.addr_bits = addr_bits
+ self.data_bits = data_bits
+ self.memory = [random.randrange(0, 2 ** data_bits) for _ in range(2 ** addr_bits)]
+
+ def read(self, addr: int) -> int:
+ if addr < 0 or addr >= 2 ** self.addr_bits:
+ raise ValueError(f"Address {addr} is out of range for {self.addr_bits}-bit RAM")
+
+ return self.memory[addr]
+
+ def write(self, addr: int, value: int):
+ if addr < 0 or addr >= 2 ** self.addr_bits:
+ raise ValueError(f"Address {addr} is out of range for {self.addr_bits}-bit RAM")
+
+ if value < 0 or value >= 2 ** self.data_bits:
+ raise ValueError(f"Value {value} is out of range for {self.data_bits}-bit RAM")
+
+ self.memory[addr] = value \ No newline at end of file
diff --git a/atk16_emu/peripherals.py b/atk16_emu/peripherals.py
index dc35e3e..c11bb55 100644
--- a/atk16_emu/peripherals.py
+++ b/atk16_emu/peripherals.py
@@ -1,47 +1,79 @@
import pygame
+import sys
+from .memory import RAM, ROM
class TPU:
def __init__(self):
- self.surface: pygame.Surface | None = None
- self.text_mem = []
+ self.surface = pygame.Surface((320, 240))
+ self.surface.fill((255, 0, 0))
+ self.text_mem = RAM(11, 8)
+ self.char_mem = ROM(11, 8)
+
+ with open("out/charmem.bin", "rb") as f:
+ charmem = f.read()
+ for i in range(len(charmem)):
+ self.char_mem.memory[i] = int(charmem[i]) & 0xFF
def frame(self):
- # TODO
- pass
+ if not self.surface:
+ return
+
+ for cy in range(2 ** 3):
+ for ty in range(2 ** 5):
+ for tx in range(2 ** 6):
+ text_addr = (ty << 6) + tx
+ char_s = self.text_mem.read(text_addr)
+ char_addr = (char_s << 3) + cy
+ char_d = self.char_mem.read(char_addr)
+
+ for cx in range(2 ** 3):
+ sx = (tx << 3) + cx
+ sy = (ty << 3) + cy
+ #print("cy", cy, "ty", ty, "cx", cx, "tx", tx, "text_addr", text_addr, "char_s", char_s, "char_addr", char_addr)
+ pixel_value_bit = (char_d >> cx) & 1
+
+ self.surface.set_at((sx, sy), (255 * pixel_value_bit, 255 * pixel_value_bit, 255))
+
+ def write_char(self, addr: int, char: int):
+ # address: 5 bits y, 6 bits x
+ self.text_mem.write(addr, char)
class Graphics:
def __init__(self):
pygame.init()
pygame.display.init()
- self.surface = None
+ surface_scale = 4
+ self.screen = pygame.display.set_mode((320 * surface_scale, 240 * surface_scale))
self.clock = pygame.time.Clock()
self.tpu = TPU()
self.active_picture_unit: TPU | None = None
def activate_tpu(self):
- if self.active_picture_unit:
- pygame.display.quit()
-
- pygame.display.init()
- self.surface = pygame.display.set_mode((320, 240))
self.active_picture_unit = self.tpu
- self.active_picture_unit.surface = self.surface
def activate_ppu(self):
raise NotImplementedError()
def deactivate(self):
self.active_picture_unit = None
- pygame.display.quit()
def step(self):
if self.active_picture_unit:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ sys.exit(0)
+
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)
+ def write(self, addr: int, char: int):
+ # take only the lower 11 bits
+ self.tpu.write_char(addr & 0x7FF, char)
+
class DummyGraphics:
def __init__(self):
pass