jsbeeb 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+
3
+ // MC6847 internal character ROM data (PAL square pixel variant, 8x12).
4
+ // Derived from the MAME project's MC6847 VDG emulation:
5
+ // https://github.com/mamedev/mame/blob/master/src/devices/video/mc6847.cpp
6
+ // Copyright Nathan Woods, licensed under BSD-3-Clause.
7
+ // See https://github.com/mamedev/mame/blob/master/LICENSE.md
8
+ export function makeCharsAtom() {
9
+ return new Uint8Array([
10
+ 0x00, 0x00, 0x00, 0x1c, 0x22, 0x02, 0x1a, 0x2a, 0x2a, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22,
11
+ 0x22, 0x3e, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x12, 0x12, 0x1c, 0x12, 0x12, 0x3c, 0x00, 0x00,
12
+ 0x00, 0x00, 0x00, 0x1c, 0x22, 0x20, 0x20, 0x20, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x12, 0x12,
13
+ 0x12, 0x12, 0x12, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x3e, 0x00, 0x00,
14
+ 0x00, 0x00, 0x00, 0x3e, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x20, 0x20,
15
+ 0x26, 0x22, 0x22, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22, 0x00, 0x00,
16
+ 0x00, 0x00, 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02,
17
+ 0x02, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x24, 0x28, 0x30, 0x28, 0x24, 0x22, 0x00, 0x00,
18
+ 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x36, 0x2a,
19
+ 0x2a, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x22, 0x22, 0x00, 0x00,
20
+ 0x00, 0x00, 0x00, 0x3e, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22,
21
+ 0x3c, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x2a, 0x24, 0x1a, 0x00, 0x00,
22
+ 0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x28, 0x24, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x10,
23
+ 0x08, 0x04, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
24
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22,
25
+ 0x14, 0x14, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x22, 0x00, 0x00,
26
+ 0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x14,
27
+ 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x04, 0x08, 0x10, 0x20, 0x3e, 0x00, 0x00,
28
+ 0x00, 0x00, 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x10,
29
+ 0x08, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e, 0x00, 0x00,
30
+ 0x00, 0x00, 0x00, 0x08, 0x1c, 0x2a, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10,
31
+ 0x3e, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x14, 0x14,
33
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x14, 0x36, 0x00, 0x36, 0x14, 0x14, 0x00, 0x00,
34
+ 0x00, 0x00, 0x00, 0x08, 0x1e, 0x20, 0x1c, 0x02, 0x3c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x32, 0x04,
35
+ 0x08, 0x10, 0x26, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x28, 0x28, 0x10, 0x2a, 0x24, 0x1a, 0x00, 0x00,
36
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x20,
37
+ 0x20, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, 0x00,
38
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x3e, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
39
+ 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x10, 0x20, 0x00, 0x00,
40
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x20, 0x00, 0x00,
42
+ 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x24, 0x24, 0x24, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x18, 0x08,
43
+ 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x02, 0x1c, 0x20, 0x20, 0x3e, 0x00, 0x00,
44
+ 0x00, 0x00, 0x00, 0x1c, 0x22, 0x02, 0x0c, 0x02, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x14,
45
+ 0x3e, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x20, 0x3c, 0x02, 0x02, 0x22, 0x1c, 0x00, 0x00,
46
+ 0x00, 0x00, 0x00, 0x1c, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x04,
47
+ 0x08, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c, 0x00, 0x00,
48
+ 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
49
+ 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00,
50
+ 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e,
51
+ 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00,
52
+ 0x00, 0x00, 0x00, 0x18, 0x24, 0x04, 0x08, 0x08, 0x00, 0x08, 0x00, 0x00,
53
+
54
+ // graphics
55
+
56
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57
+ 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0,
58
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f,
59
+ 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
60
+ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f,
61
+ 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00,
62
+ 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0,
63
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff,
64
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
65
+ 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0,
66
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00,
67
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f,
68
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00,
69
+ 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00,
70
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
71
+ 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff,
72
+ 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0,
73
+ 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
74
+ 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff,
75
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f,
76
+ 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff,
77
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78
+ 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00,
79
+ 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
80
+ 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f,
81
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0,
82
+ 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
83
+ 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f,
84
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
85
+ 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
86
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff,
87
+ 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
88
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
89
+ 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0,
90
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f,
91
+ 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
92
+ 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f,
93
+ 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00,
94
+ 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0,
95
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff,
96
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
97
+ 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0,
98
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
99
+
100
+ /* Lower case */
101
+ 0x00, 0x00, 0x00, 0x0c, 0x12, 0x10, 0x38, 0x10, 0x12, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
102
+ 0x02, 0x1e, 0x22, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x00, 0x00,
103
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x20, 0x20, 0x20, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x1e,
104
+ 0x22, 0x22, 0x22, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x3e, 0x20, 0x1c, 0x00, 0x00,
105
+ 0x00, 0x00, 0x00, 0x0c, 0x12, 0x10, 0x38, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
106
+ 0x22, 0x22, 0x22, 0x1e, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x22, 0x22, 0x00, 0x00,
107
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c,
108
+ 0x04, 0x04, 0x04, 0x04, 0x24, 0x18, 0x00, 0x00, 0x00, 0x20, 0x20, 0x24, 0x28, 0x38, 0x24, 0x22, 0x00, 0x00,
109
+ 0x00, 0x00, 0x00, 0x18, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
110
+ 0x2a, 0x2a, 0x2a, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x32, 0x22, 0x22, 0x22, 0x00, 0x00,
111
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
112
+ 0x22, 0x22, 0x22, 0x3c, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x1e, 0x02, 0x03,
113
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
114
+ 0x20, 0x1c, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x3c, 0x10, 0x10, 0x10, 0x12, 0x0c, 0x00, 0x00,
115
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x26, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22,
116
+ 0x22, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x2a, 0x2a, 0x1c, 0x14, 0x00, 0x00,
117
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22,
118
+ 0x22, 0x22, 0x22, 0x1e, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x04, 0x08, 0x10, 0x3e, 0x00, 0x00,
119
+ 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x20, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08,
120
+ 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, 0x00, 0x00,
121
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x2a, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x04, 0x3e,
122
+ 0x04, 0x08, 0x00, 0x00, 0x00, 0x00,
123
+ ]);
124
+ }
package/src/disc.js CHANGED
@@ -1,25 +1,7 @@
1
1
  // Translated from beebjit by Chris Evans.
2
2
  // https://github.com/scarybeasts/beebjit
3
3
 
4
- import * as utils from "./utils.js";
5
-
6
- /**
7
- * Compute CRC32 of a Uint8Array.
8
- * @param {Uint8Array} data
9
- * @returns {number} CRC32 as a signed 32-bit integer
10
- */
11
- export function crc32(data) {
12
- let crc = 0xffffffff;
13
- for (let i = 0; i < data.length; ++i) {
14
- crc ^= data[i];
15
- for (let j = 0; j < 8; ++j) {
16
- const doEor = crc & 1;
17
- crc = crc >>> 1;
18
- if (doEor) crc ^= 0xedb88320;
19
- }
20
- }
21
- return ~crc;
22
- }
4
+ import { hexbyte, crc32 } from "./utils.js";
23
5
  class TrackBuilder {
24
6
  /**
25
7
  * @param {Track} track
@@ -482,7 +464,7 @@ class Track {
482
464
  }
483
465
  break;
484
466
  default:
485
- console.log(`Unknown marker byte ${utils.hexbyte(dataByte)} ${this.description}`);
467
+ console.log(`Unknown marker byte ${hexbyte(dataByte)} ${this.description}`);
486
468
  }
487
469
  }
488
470
  return sectors;
package/src/fake6502.js CHANGED
@@ -6,7 +6,7 @@ import { FakeSoundChip } from "./soundchip.js";
6
6
  import { findModel, TEST_6502, TEST_65C02, TEST_65C12 } from "./models.js";
7
7
  import { FakeDdNoise } from "./ddnoise.js";
8
8
  import { FakeRelayNoise } from "./relaynoise.js";
9
- import { Cpu6502 } from "./6502.js";
9
+ import { Cpu6502, AtomCpu6502 } from "./6502.js";
10
10
  import { Cmos } from "./cmos.js";
11
11
  import { FakeMusic5000 } from "./music5000.js";
12
12
 
@@ -20,7 +20,8 @@ export function fake6502(model, opts) {
20
20
  opts = opts || {};
21
21
  model = model || TEST_6502;
22
22
  if (opts.tube) model.tube = findModel("Tube65c02");
23
- return new Cpu6502(model, {
23
+ const CpuClass = model.isAtom ? AtomCpu6502 : Cpu6502;
24
+ return new CpuClass(model, {
24
25
  dbgr,
25
26
  video: opts.video || fakeVideo,
26
27
  soundChip: opts.soundChip || soundChip,
package/src/jsbeeb.css CHANGED
@@ -188,6 +188,29 @@ div.led {
188
188
  background-image: url(/images/yellow-on-16.png);
189
189
  }
190
190
 
191
+ .tape-btn {
192
+ background: #333;
193
+ color: #ccc;
194
+ border: 1px solid #666;
195
+ border-radius: 3px;
196
+ font-size: 10px;
197
+ width: 20px;
198
+ height: 18px;
199
+ padding: 0;
200
+ line-height: 18px;
201
+ cursor: pointer;
202
+ text-align: center;
203
+ }
204
+
205
+ .tape-btn:hover {
206
+ background: #555;
207
+ color: #fff;
208
+ }
209
+
210
+ .tape-btn.playing {
211
+ color: #4c4;
212
+ }
213
+
191
214
  #pages {
192
215
  margin-top: 10px;
193
216
  }
package/src/keyboard.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  import * as utils from "./utils.js";
3
+ import { ATOM } from "./utils_atom.js";
3
4
 
4
5
  const isMac = typeof window !== "undefined" && /^Mac/i.test(window.navigator?.platform || "");
5
6
 
@@ -28,6 +29,14 @@ export class Keyboard extends EventTarget {
28
29
  this.inputEnabledFunction = inputEnabledFunction;
29
30
  this.dbgr = dbgr;
30
31
 
32
+ // Key interface: routes key events to SysVia (BBC) or PPIA (Atom).
33
+ // Both provide keyDown, keyUp, keyToggleRaw, setKeyLayout,
34
+ // clearKeys, disableKeyboard, enableKeyboard.
35
+ this.keyInterface = processor.model.isAtom ? processor.atomppia : processor.sysvia;
36
+ // The SHIFT key constant used by stringToMachineKeys in the paste key array.
37
+ // Compared by reference in _deliverPasteKey to avoid toggling shift off.
38
+ this._shiftKey = processor.model.isAtom ? ATOM.SHIFT : utils.BBC.SHIFT;
39
+
31
40
  // State
32
41
  this.emuKeyHandlers = {};
33
42
  this.running = false;
@@ -133,7 +142,7 @@ export class Keyboard extends EventTarget {
133
142
  */
134
143
  setKeyLayout(layout) {
135
144
  this.keyLayout = layout;
136
- this.processor.sysvia.setKeyLayout(layout);
145
+ this.keyInterface.setKeyLayout(layout);
137
146
  }
138
147
 
139
148
  /**
@@ -225,7 +234,7 @@ export class Keyboard extends EventTarget {
225
234
  const isSpecialHandled = this._handleSpecialKeys(code);
226
235
  if (isSpecialHandled) return;
227
236
 
228
- // Check for registered handlers first; if one fires, don't also send to the BBC.
237
+ // Check for registered handlers first; if one fires, don't pass to the emulator.
229
238
  // This lets Alt+key and Ctrl+key handlers cleanly own their keys without the
230
239
  // underlying key leaking through to the emulated machine.
231
240
  const handler = this._findKeyHandler(code, evt.altKey, evt.ctrlKey);
@@ -234,8 +243,8 @@ export class Keyboard extends EventTarget {
234
243
  return;
235
244
  }
236
245
 
237
- // No handler claimed the key pass it to the BBC Micro.
238
- this.processor.sysvia.keyDown(code, evt.shiftKey);
246
+ // No handler claimed the key; pass it to the emulated machine.
247
+ this.keyInterface.keyDown(code, evt.shiftKey);
239
248
  }
240
249
 
241
250
  /**
@@ -268,7 +277,7 @@ export class Keyboard extends EventTarget {
268
277
 
269
278
  // Always let the key ups come through to avoid sticky keys in the debugger
270
279
  const code = this.keyCode(evt);
271
- this.processor.sysvia.keyUp(code);
280
+ this.keyInterface.keyUp(code);
272
281
 
273
282
  // No further special handling needed if not running
274
283
  if (!this.running) return;
@@ -301,10 +310,10 @@ export class Keyboard extends EventTarget {
301
310
 
302
311
  // Mac browsers seem to model caps lock as a physical key that's down when capslock is on, and up when it's off.
303
312
  // No event is generated when it is physically released on the keyboard. So, we simulate a "tap" here.
304
- this.processor.sysvia.keyDown(utils.keyCodes.CAPSLOCK);
313
+ this.keyInterface.keyDown(utils.keyCodes.CAPSLOCK);
305
314
 
306
315
  // Simulate a key release after a short delay
307
- setTimeout(() => this.processor.sysvia.keyUp(utils.keyCodes.CAPSLOCK), CAPS_LOCK_DELAY);
316
+ setTimeout(() => this.keyInterface.keyUp(utils.keyCodes.CAPSLOCK), CAPS_LOCK_DELAY);
308
317
 
309
318
  if (isMac && window.localStorage && !window.localStorage.getItem("warnedAboutRubbishMacs")) {
310
319
  this.dispatchEvent(
@@ -324,20 +333,21 @@ export class Keyboard extends EventTarget {
324
333
  }
325
334
 
326
335
  /**
327
- * Send raw keyboard input to the BBC
328
- * @param {Array} keysToSend - Array of keys to send
336
+ * Send raw keyboard input to the emulated machine (for paste/autotype).
337
+ * @param {Array} keysToSend - Array of machine-specific key codes to send
329
338
  * @param {boolean} checkCapsAndShiftLocks - Whether to check caps and shift locks
330
339
  */
331
- sendRawKeyboardToBBC(keysToSend, checkCapsAndShiftLocks) {
340
+ sendRawKeyboard(keysToSend, checkCapsAndShiftLocks) {
332
341
  if (this.isPasting) this.cancelPaste();
333
342
 
334
- this.processor.sysvia.disableKeyboard();
335
- this._pasteClocksPerMs = Math.floor(this.processor.cpuMultiplier * 2000000) / 1000;
343
+ this.keyInterface.disableKeyboard();
344
+ this._pasteClocksPerMs =
345
+ Math.floor(this.processor.cpuMultiplier * this.processor.peripheralCyclesPerSecond) / 1000;
336
346
 
337
347
  if (checkCapsAndShiftLocks) {
338
348
  let toggleKey = null;
339
- if (!this.processor.sysvia.capsLockLight) toggleKey = utils.BBC.CAPSLOCK;
340
- else if (this.processor.sysvia.shiftLockLight) toggleKey = utils.BBC.SHIFTLOCK;
349
+ if (!this.keyInterface.capsLockLight) toggleKey = utils.BBC.CAPSLOCK;
350
+ else if (this.keyInterface.shiftLockLight) toggleKey = utils.BBC.SHIFTLOCK;
341
351
  if (toggleKey) {
342
352
  keysToSend.unshift(toggleKey);
343
353
  keysToSend.push(toggleKey);
@@ -356,13 +366,22 @@ export class Keyboard extends EventTarget {
356
366
  * @private
357
367
  */
358
368
  _deliverPasteKey() {
359
- if (this._pasteLastChar && this._pasteLastChar !== utils.BBC.SHIFT) {
360
- this.processor.sysvia.keyToggleRaw(this._pasteLastChar);
369
+ if (this._pasteLastChar && this._pasteLastChar !== this._shiftKey) {
370
+ this.keyInterface.keyToggleRaw(this._pasteLastChar);
361
371
  }
362
372
 
363
373
  if (this._pasteKeys.length === 0) {
364
374
  this._pasteLastChar = undefined;
365
- this.processor.sysvia.enableKeyboard();
375
+ this.keyInterface.enableKeyboard();
376
+ return;
377
+ }
378
+
379
+ // Atom's PPIA keyboard is polled, not interrupt-driven like the BBC's
380
+ // SysVIA. Insert a debounce gap after every key release so the ROM
381
+ // sees the key-up before the next key-down arrives.
382
+ if (this._pasteLastChar && this._pasteLastChar !== this._shiftKey && this.processor.model.isAtom) {
383
+ this._pasteLastChar = undefined;
384
+ this._pasteTask.schedule(30 * this._pasteClocksPerMs);
366
385
  return;
367
386
  }
368
387
 
@@ -375,12 +394,15 @@ export class Keyboard extends EventTarget {
375
394
  return;
376
395
  }
377
396
 
378
- let delayMs = 50;
397
+ // Atom ROM polls the keyboard once per VSync (~16ms at 60 Hz).
398
+ // 80ms gives the ROM ~5 scan cycles to detect, debounce, and
399
+ // process each keypress.
400
+ let delayMs = this.processor.model.isAtom ? 80 : 50;
379
401
  if (typeof this._pasteLastChar === "number") {
380
402
  delayMs = this._pasteLastChar;
381
403
  this._pasteLastChar = undefined;
382
404
  } else {
383
- this.processor.sysvia.keyToggleRaw(this._pasteLastChar);
405
+ this.keyInterface.keyToggleRaw(this._pasteLastChar);
384
406
  }
385
407
 
386
408
  this._pasteKeys.shift();
@@ -393,12 +415,12 @@ export class Keyboard extends EventTarget {
393
415
  cancelPaste() {
394
416
  if (!this.isPasting) return;
395
417
  this._pasteTask.cancel();
396
- if (this._pasteLastChar && this._pasteLastChar !== utils.BBC.SHIFT) {
397
- this.processor.sysvia.keyToggleRaw(this._pasteLastChar);
418
+ if (this._pasteLastChar && this._pasteLastChar !== this._shiftKey) {
419
+ this.keyInterface.keyToggleRaw(this._pasteLastChar);
398
420
  }
399
421
  this._pasteLastChar = undefined;
400
422
  this._pasteKeys = [];
401
- this.processor.sysvia.enableKeyboard();
423
+ this.keyInterface.enableKeyboard();
402
424
  }
403
425
 
404
426
  /**
@@ -413,7 +435,7 @@ export class Keyboard extends EventTarget {
413
435
  * Clears all pressed keys
414
436
  */
415
437
  clearKeys() {
416
- this.processor.sysvia.clearKeys();
438
+ this.keyInterface.clearKeys();
417
439
  }
418
440
 
419
441
  /**
@@ -9,7 +9,7 @@ import { readFileSync } from "fs";
9
9
  import { fileURLToPath } from "url";
10
10
  import path from "path";
11
11
  import { TestMachine } from "../tests/test-machine.js";
12
- import { InstrumentedSoundChip } from "./soundchip.js";
12
+ import { InstrumentedSoundChip, FakeSoundChip } from "./soundchip.js";
13
13
 
14
14
  // Resolve the jsbeeb package root from our own location (src/machine-session.js
15
15
  // → go up one level). Passed to setNodeBasePath() so the ROM loader resolves
@@ -46,22 +46,31 @@ export class MachineSession {
46
46
 
47
47
  // Create a real Video instance so we get pixel output
48
48
  const modelObj = findModel(modelName);
49
- this._video = new Video(modelObj.isMaster, this._fb32, (minx, miny, maxx, maxy) => {
50
- this._lastPaint = { minx, miny, maxx, maxy };
51
- this._frameDirty = true;
52
- // Snapshot the complete frame now, before clearPaintBuffer() wipes _fb32.
53
- // This mirrors what the browser does: paint_ext fires → canvas updated → fb32 cleared.
54
- this._completeFb8.set(this._fb8);
55
- });
56
-
57
- // Use a real (instrumented) sound chip so we can read registers and capture writes
58
- this._soundChip = new InstrumentedSoundChip();
49
+ this._isAtom = modelObj.isAtom;
50
+ this._video = new Video(
51
+ modelObj.isMaster,
52
+ this._fb32,
53
+ (minx, miny, maxx, maxy) => {
54
+ this._lastPaint = { minx, miny, maxx, maxy };
55
+ this._frameDirty = true;
56
+ // Snapshot the complete frame now, before clearPaintBuffer() wipes _fb32.
57
+ // This mirrors what the browser does: paint_ext fires canvas updated fb32 cleared.
58
+ this._completeFb8.set(this._fb8);
59
+ },
60
+ { isAtom: modelObj.isAtom },
61
+ );
62
+
63
+ // Use a real (instrumented) sound chip so we can read registers and capture writes.
64
+ // Atom models use AtomSoundChip which has a different interface (speakerGenerator,
65
+ // toneGenerator); FakeSoundChip provides compatible no-op stubs for headless mode.
66
+ this._soundChip = modelObj.isAtom ? new FakeSoundChip() : new InstrumentedSoundChip();
59
67
 
60
68
  // TestMachine forwards opts.video and opts.soundChip to fake6502
61
69
  this._machine = new TestMachine(modelName, { video: this._video, soundChip: this._soundChip });
62
70
 
63
71
  // Accumulated VDU text output — drained by callers
64
72
  this._pendingOutput = [];
73
+ this._flushCapture = () => {};
65
74
 
66
75
  // Breakpoint management — persistent hooks that survive across run calls
67
76
  this._breakpoints = new Map(); // id → { hook, type, address, hit }
@@ -90,8 +99,9 @@ export class MachineSession {
90
99
  /**
91
100
  * Install the VDU character-output capture hook.
92
101
  *
93
- * WRCHV discovery: RAM at 0x20E/0x20F initialises to 0xFFFF before the
94
- * OS runs. We read directly from cpu.ramRomOs (two array lookups no
102
+ * WRCHV discovery: RAM at the OS write-character vector (0x20E on BBC,
103
+ * 0x208 on Atom) initialises to 0x0000 before the OS runs. We read
104
+ * directly from cpu.ramRomOs (two array lookups — no
95
105
  * readmem() dispatch overhead) on every instruction, waiting for the
96
106
  * value to change from its initial 0xFFFF. Once the OS installs a real
97
107
  * handler we use that address for the lifetime of the session. Programs
@@ -105,7 +115,9 @@ export class MachineSession {
105
115
  _installCaptureHook() {
106
116
  const cpu = this._machine.processor;
107
117
  const ram = cpu.ramRomOs; // direct Uint8Array — no dispatch overhead
108
- const initialWrchv = ram[0x20e] | (ram[0x20f] << 8); // 0xFFFF pre-boot
118
+ // WRCHV vector: BBC at $020E, Atom at $0208.
119
+ const wrchvAddr = this._isAtom ? 0x208 : 0x20e;
120
+ const initialWrchv = ram[wrchvAddr] | (ram[wrchvAddr + 1] << 8); // 0xFFFF pre-boot
109
121
 
110
122
  const attributes = { x: 0, y: 0, text: "", foreground: 7, background: 0, mode: 7 };
111
123
  let currentText = "";
@@ -124,6 +136,12 @@ export class MachineSession {
124
136
  currentText = "";
125
137
  }
126
138
 
139
+ // Expose flush so drainOutput can capture trailing text
140
+ // that hasn't been terminated by a control character.
141
+ this._flushCapture = flush;
142
+
143
+ const isAtom = this._isAtom;
144
+
127
145
  function onChar(c) {
128
146
  if (nextN) {
129
147
  params.push(c);
@@ -135,9 +153,6 @@ export class MachineSession {
135
153
  return;
136
154
  }
137
155
  switch (c) {
138
- case 1: // next char to printer only
139
- nextN = 1;
140
- break;
141
156
  case 10: // LF
142
157
  flush();
143
158
  attributes.y++;
@@ -151,48 +166,58 @@ export class MachineSession {
151
166
  flush();
152
167
  attributes.x = 0;
153
168
  break;
154
- case 17: // COLOUR n
155
- nextN = 1;
156
- vduProc = (p) => {
157
- if (p[0] & 0x80) attributes.background = p[0] & 0xf;
158
- else attributes.foreground = p[0] & 0xf;
159
- };
160
- break;
161
- case 18: // GCOL
162
- nextN = 2;
163
- break;
164
- case 19: // define logical colour
165
- nextN = 5;
166
- break;
167
- case 22: // MODE n
168
- nextN = 1;
169
- vduProc = (p) => {
170
- flush();
171
- attributes.mode = p[0];
172
- attributes.x = 0;
173
- attributes.y = 0;
174
- attributes.foreground = 7;
175
- attributes.background = 0;
176
- };
177
- break;
178
- case 25: // PLOT
179
- nextN = 5;
180
- break;
181
- case 28: // define text window
182
- nextN = 4;
183
- break;
184
- case 29: // define graphics origin
185
- nextN = 4;
186
- break;
187
- case 31: // TAB(x,y)
188
- nextN = 2;
189
- vduProc = (p) => {
190
- flush();
191
- attributes.x = p[0];
192
- attributes.y = p[1];
193
- };
194
- break;
195
169
  default:
170
+ // BBC VDU control codes (multi-byte sequences).
171
+ // The Atom doesn't use these; skip them to avoid
172
+ // swallowing printable characters.
173
+ if (!isAtom) {
174
+ switch (c) {
175
+ case 1: // next char to printer only
176
+ nextN = 1;
177
+ return;
178
+ case 17: // COLOUR n
179
+ nextN = 1;
180
+ vduProc = (p) => {
181
+ if (p[0] & 0x80) attributes.background = p[0] & 0xf;
182
+ else attributes.foreground = p[0] & 0xf;
183
+ };
184
+ return;
185
+ case 18: // GCOL
186
+ nextN = 2;
187
+ return;
188
+ case 19: // define logical colour
189
+ nextN = 5;
190
+ return;
191
+ case 22: // MODE n
192
+ nextN = 1;
193
+ vduProc = (p) => {
194
+ flush();
195
+ attributes.mode = p[0];
196
+ attributes.x = 0;
197
+ attributes.y = 0;
198
+ attributes.foreground = 7;
199
+ attributes.background = 0;
200
+ };
201
+ return;
202
+ case 25: // PLOT
203
+ nextN = 5;
204
+ return;
205
+ case 28: // define text window
206
+ nextN = 4;
207
+ return;
208
+ case 29: // define graphics origin
209
+ nextN = 4;
210
+ return;
211
+ case 31: // TAB(x,y)
212
+ nextN = 2;
213
+ vduProc = (p) => {
214
+ flush();
215
+ attributes.x = p[0];
216
+ attributes.y = p[1];
217
+ };
218
+ return;
219
+ }
220
+ }
196
221
  if (c >= 32 && c < 0x7f) {
197
222
  currentText += String.fromCharCode(c);
198
223
  } else {
@@ -207,7 +232,7 @@ export class MachineSession {
207
232
  // Once the OS sets WRCHV (it changes from 0xFFFF), we start
208
233
  // capturing. Programs that install a custom VDU driver mid-run
209
234
  // are handled transparently because we re-read on every call.
210
- const wrchv = ram[0x20e] | (ram[0x20f] << 8);
235
+ const wrchv = ram[wrchvAddr] | (ram[wrchvAddr + 1] << 8);
211
236
  if (wrchv !== initialWrchv && addr === wrchv) {
212
237
  onChar(cpu.a);
213
238
  }
@@ -391,6 +416,7 @@ export class MachineSession {
391
416
  * Also includes a flat `screenText` reconstruction.
392
417
  */
393
418
  drainOutput({ clear = true } = {}) {
419
+ this._flushCapture();
394
420
  const elements = clear ? this._pendingOutput.splice(0) : [...this._pendingOutput];
395
421
  return {
396
422
  elements,