From 3f9f7eefb9d0836ec53a08e311219f7f497517c7 Mon Sep 17 00:00:00 2001 From: Jan Tuomi Date: Sat, 13 Apr 2024 00:25:18 +0300 Subject: Make text mode work --- atk16_emu/emu.py | 60 ++++-------------------------------------------- atk16_emu/memory.py | 56 ++++++++++++++++++++++++++++++++++++++++++++ atk16_emu/peripherals.py | 56 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 104 insertions(+), 68 deletions(-) create mode 100644 atk16_emu/memory.py (limited to 'atk16_emu') 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 -- cgit v1.3