1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
import pygame
import sys
import random
from .memory import RAM, ROM
class Peripherals:
def __init__(self):
pygame.init()
self.graphics = Graphics()
self.keyboard = Keyboard()
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.graphics.step()
class DummyPeripherals:
def __init__(self):
self.graphics = DummyGraphics()
self.keyboard = DummyKeyboard()
def step(self):
pass
class TPU:
def __init__(self):
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):
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
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.display.init()
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):
self.active_picture_unit = self.tpu
def activate_ppu(self):
raise NotImplementedError()
def deactivate(self):
self.active_picture_unit = None
def step(self):
if self.active_picture_unit:
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
def activate_tpu(self):
pass
def activate_ppu(self):
pass
def deactivate(self):
pass
def step(self):
pass
def write(self, addr: int, char: int):
pass
class Keyboard:
def __init__(self):
# mimic electronic behaviour my assigning a random value at start
self.latest_pressed: int = random.randrange(0, 2 ** 16)
def read(self) -> int:
return self.latest_pressed
class DummyKeyboard:
def __init__(self):
pass
def read(self) -> int:
print("warning: reading from dummy keyboard")
return 0xFFFF
|