ac6502 1.0.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.
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/components/CPU.js +1170 -0
- package/dist/components/CPU.js.map +1 -0
- package/dist/components/Cart.js +23 -0
- package/dist/components/Cart.js.map +1 -0
- package/dist/components/IO/Empty.js +19 -0
- package/dist/components/IO/Empty.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOAttachment.js +71 -0
- package/dist/components/IO/GPIOAttachments/GPIOAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js +90 -0
- package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js +489 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js +274 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js.map +1 -0
- package/dist/components/IO/GPIOCard.js +597 -0
- package/dist/components/IO/GPIOCard.js.map +1 -0
- package/dist/components/IO/InputBoard.js +19 -0
- package/dist/components/IO/InputBoard.js.map +1 -0
- package/dist/components/IO/LCDCard.js +19 -0
- package/dist/components/IO/LCDCard.js.map +1 -0
- package/dist/components/IO/RAMCard.js +63 -0
- package/dist/components/IO/RAMCard.js.map +1 -0
- package/dist/components/IO/RTCCard.js +483 -0
- package/dist/components/IO/RTCCard.js.map +1 -0
- package/dist/components/IO/SerialCard.js +282 -0
- package/dist/components/IO/SerialCard.js.map +1 -0
- package/dist/components/IO/SoundCard.js +620 -0
- package/dist/components/IO/SoundCard.js.map +1 -0
- package/dist/components/IO/StorageCard.js +428 -0
- package/dist/components/IO/StorageCard.js.map +1 -0
- package/dist/components/IO/VGACard.js +9 -0
- package/dist/components/IO/VGACard.js.map +1 -0
- package/dist/components/IO/VideoCard.js +623 -0
- package/dist/components/IO/VideoCard.js.map +1 -0
- package/dist/components/IO.js +3 -0
- package/dist/components/IO.js.map +1 -0
- package/dist/components/Machine.js +310 -0
- package/dist/components/Machine.js.map +1 -0
- package/dist/components/RAM.js +24 -0
- package/dist/components/RAM.js.map +1 -0
- package/dist/components/ROM.js +23 -0
- package/dist/components/ROM.js.map +1 -0
- package/dist/index.js +441 -0
- package/dist/index.js.map +1 -0
- package/dist/tests/CPU.test.js +1626 -0
- package/dist/tests/CPU.test.js.map +1 -0
- package/dist/tests/Cart.test.js +119 -0
- package/dist/tests/Cart.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js +339 -0
- package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js +126 -0
- package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js +779 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js +355 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOCard.test.js +503 -0
- package/dist/tests/IO/GPIOCard.test.js.map +1 -0
- package/dist/tests/IO/RAMCard.test.js +229 -0
- package/dist/tests/IO/RAMCard.test.js.map +1 -0
- package/dist/tests/IO/RTCCard.test.js +177 -0
- package/dist/tests/IO/RTCCard.test.js.map +1 -0
- package/dist/tests/IO/SerialCard.test.js +423 -0
- package/dist/tests/IO/SerialCard.test.js.map +1 -0
- package/dist/tests/IO/SoundCard.test.js +528 -0
- package/dist/tests/IO/SoundCard.test.js.map +1 -0
- package/dist/tests/IO/StorageCard.test.js +647 -0
- package/dist/tests/IO/StorageCard.test.js.map +1 -0
- package/dist/tests/IO/VideoCard.test.js +549 -0
- package/dist/tests/IO/VideoCard.test.js.map +1 -0
- package/dist/tests/Machine.test.js +383 -0
- package/dist/tests/Machine.test.js.map +1 -0
- package/dist/tests/RAM.test.js +160 -0
- package/dist/tests/RAM.test.js.map +1 -0
- package/dist/tests/ROM.test.js +123 -0
- package/dist/tests/ROM.test.js.map +1 -0
- package/jest.config.cjs +9 -0
- package/package.json +43 -0
- package/src/components/CPU.ts +1371 -0
- package/src/components/Cart.ts +20 -0
- package/src/components/IO/GPIOAttachments/GPIOAttachment.ts +189 -0
- package/src/components/IO/GPIOAttachments/GPIOJoystickAttachment.ts +99 -0
- package/src/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.ts +465 -0
- package/src/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.ts +287 -0
- package/src/components/IO/GPIOCard.ts +677 -0
- package/src/components/IO/RAMCard.ts +68 -0
- package/src/components/IO/RTCCard.ts +518 -0
- package/src/components/IO/SerialCard.ts +335 -0
- package/src/components/IO/SoundCard.ts +711 -0
- package/src/components/IO/StorageCard.ts +473 -0
- package/src/components/IO/VideoCard.ts +730 -0
- package/src/components/IO.ts +11 -0
- package/src/components/Machine.ts +364 -0
- package/src/components/RAM.ts +23 -0
- package/src/components/ROM.ts +19 -0
- package/src/index.ts +474 -0
- package/src/tests/CPU.test.ts +2045 -0
- package/src/tests/Cart.test.ts +149 -0
- package/src/tests/IO/GPIOAttachments/GPIOAttachment.test.ts +413 -0
- package/src/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.ts +147 -0
- package/src/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts +961 -0
- package/src/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts +449 -0
- package/src/tests/IO/GPIOCard.test.ts +644 -0
- package/src/tests/IO/RAMCard.test.ts +284 -0
- package/src/tests/IO/RTCCard.test.ts +222 -0
- package/src/tests/IO/SerialCard.test.ts +530 -0
- package/src/tests/IO/SoundCard.test.ts +659 -0
- package/src/tests/IO/StorageCard.test.ts +787 -0
- package/src/tests/IO/VideoCard.test.ts +668 -0
- package/src/tests/Machine.test.ts +437 -0
- package/src/tests/RAM.test.ts +196 -0
- package/src/tests/ROM.test.ts +154 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { GPIOKeyboardMatrixAttachment } from '../../../components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment'
|
|
2
|
+
|
|
3
|
+
describe('GPIOKeyboardMatrixAttachment', () => {
|
|
4
|
+
let keyboard: GPIOKeyboardMatrixAttachment
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
keyboard = new GPIOKeyboardMatrixAttachment(0)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
describe('Initialization', () => {
|
|
11
|
+
it('should initialize with empty matrix', () => {
|
|
12
|
+
const matrix = keyboard.getMatrixState()
|
|
13
|
+
expect(matrix).toEqual([0, 0, 0, 0, 0, 0, 0, 0])
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should initialize with all columns selected', () => {
|
|
17
|
+
expect(keyboard.getSelectedColumns()).toBe(0xFF)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('should be enabled by default', () => {
|
|
21
|
+
expect(keyboard.isEnabled()).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should have correct priority', () => {
|
|
25
|
+
const kb = new GPIOKeyboardMatrixAttachment(5)
|
|
26
|
+
expect(kb.getPriority()).toBe(5)
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('Reset', () => {
|
|
31
|
+
it('should clear all pressed keys', () => {
|
|
32
|
+
keyboard.updateKey(0x04, true) // Press 'a'
|
|
33
|
+
keyboard.reset()
|
|
34
|
+
const matrix = keyboard.getMatrixState()
|
|
35
|
+
expect(matrix).toEqual([0, 0, 0, 0, 0, 0, 0, 0])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should reset selected columns', () => {
|
|
39
|
+
keyboard.writePortB(0x00, 0xFF)
|
|
40
|
+
keyboard.reset()
|
|
41
|
+
expect(keyboard.getSelectedColumns()).toBe(0xFF)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('Port B Write (Column Selection)', () => {
|
|
46
|
+
it('should update selected columns', () => {
|
|
47
|
+
keyboard.writePortB(0xAA, 0xFF)
|
|
48
|
+
expect(keyboard.getSelectedColumns()).toBe(0xAA)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should select all columns with 0x00', () => {
|
|
52
|
+
keyboard.writePortB(0x00, 0xFF)
|
|
53
|
+
expect(keyboard.getSelectedColumns()).toBe(0x00)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should select individual columns', () => {
|
|
57
|
+
keyboard.writePortB(0xFE, 0xFF) // Select column 0 only
|
|
58
|
+
expect(keyboard.getSelectedColumns()).toBe(0xFE)
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('Port A Read (Row State)', () => {
|
|
63
|
+
it('should return all rows high when no keys pressed', () => {
|
|
64
|
+
keyboard.writePortB(0x00, 0xFF) // Select all columns
|
|
65
|
+
expect(keyboard.readPortA(0x00, 0xFF)).toBe(0xFF)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should return row low when key is pressed in selected column', () => {
|
|
69
|
+
// Press 'a' key which is at PA3, PB7
|
|
70
|
+
keyboard.updateKey(0x04, true) // 'a' USB HID code
|
|
71
|
+
keyboard.writePortB(0x7F, 0xFF) // Select column 7 only (bit 7 = 0)
|
|
72
|
+
|
|
73
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
74
|
+
expect(rowState & (1 << 3)).toBe(0) // Row 3 should be low
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should not affect row when key column is not selected', () => {
|
|
78
|
+
// Press 'a' at PA3, PB7
|
|
79
|
+
keyboard.updateKey(0x04, true)
|
|
80
|
+
keyboard.writePortB(0xFF, 0xFF) // No columns selected
|
|
81
|
+
|
|
82
|
+
expect(keyboard.readPortA(0x00, 0xFF)).toBe(0xFF) // All rows high
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('should handle multiple keys in same row', () => {
|
|
86
|
+
// Press 'q' (PA2, PB0) and 'w' (PA2, PB1)
|
|
87
|
+
keyboard.updateKey(0x14, true) // 'q'
|
|
88
|
+
keyboard.updateKey(0x1A, true) // 'w'
|
|
89
|
+
|
|
90
|
+
keyboard.writePortB(0xFC, 0xFF) // Select columns 0 and 1
|
|
91
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
92
|
+
|
|
93
|
+
expect(rowState & (1 << 2)).toBe(0) // Row 2 should be low
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should handle multiple keys in different rows', () => {
|
|
97
|
+
// Press '1' (PA0, PB1) and 'a' (PA3, PB7)
|
|
98
|
+
keyboard.updateKey(0x1E, true) // '1'
|
|
99
|
+
keyboard.updateKey(0x04, true) // 'a'
|
|
100
|
+
|
|
101
|
+
keyboard.writePortB(0x00, 0xFF) // Select all columns
|
|
102
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
103
|
+
|
|
104
|
+
expect(rowState & (1 << 0)).toBe(0) // Row 0 low
|
|
105
|
+
expect(rowState & (1 << 3)).toBe(0) // Row 3 low
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('Port B Read', () => {
|
|
110
|
+
it('should always return 0xFF (not interfere)', () => {
|
|
111
|
+
expect(keyboard.readPortB(0x00, 0x00)).toBe(0xFF)
|
|
112
|
+
expect(keyboard.readPortB(0xFF, 0xFF)).toBe(0xFF)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe('USB HID Key Mapping - Letters', () => {
|
|
117
|
+
it('should map letter keys correctly', () => {
|
|
118
|
+
keyboard.updateKey(0x04, true) // 'a'
|
|
119
|
+
keyboard.writePortB(0x7F, 0xFF) // Select column 7 (PB7)
|
|
120
|
+
|
|
121
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
122
|
+
expect(rowState & (1 << 3)).toBe(0) // PA3 should be low
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should map q key', () => {
|
|
126
|
+
keyboard.updateKey(0x14, true) // 'q'
|
|
127
|
+
keyboard.writePortB(0xFE, 0xFF) // Select column 0
|
|
128
|
+
|
|
129
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
130
|
+
expect(rowState & (1 << 2)).toBe(0) // PA2 should be low
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should map z key', () => {
|
|
134
|
+
keyboard.updateKey(0x1D, true) // 'z'
|
|
135
|
+
keyboard.writePortB(0xDF, 0xFF) // Select column 5 (PB5)
|
|
136
|
+
|
|
137
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
138
|
+
expect(rowState & (1 << 5)).toBe(0) // PA5 should be low
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe('USB HID Key Mapping - Numbers', () => {
|
|
143
|
+
it('should map number 1', () => {
|
|
144
|
+
keyboard.updateKey(0x1E, true) // '1'
|
|
145
|
+
keyboard.writePortB(0xFD, 0xFF) // Select column 1
|
|
146
|
+
|
|
147
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
148
|
+
expect(rowState & (1 << 0)).toBe(0) // PA0 should be low
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('should map number 0', () => {
|
|
152
|
+
keyboard.updateKey(0x27, true) // '0'
|
|
153
|
+
keyboard.writePortB(0xFB, 0xFF) // Select column 2 (PB2)
|
|
154
|
+
|
|
155
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
156
|
+
expect(rowState & (1 << 1)).toBe(0) // PA1 should be low
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
describe('USB HID Key Mapping - Special Keys', () => {
|
|
161
|
+
it('should map Enter key', () => {
|
|
162
|
+
keyboard.updateKey(0x28, true) // Enter
|
|
163
|
+
keyboard.writePortB(0xFB, 0xFF) // Select column 2 (PB2)
|
|
164
|
+
|
|
165
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
166
|
+
expect(rowState & (1 << 5)).toBe(0) // PA5 should be low
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should map Escape key', () => {
|
|
170
|
+
keyboard.updateKey(0x29, true) // Escape
|
|
171
|
+
keyboard.writePortB(0xBF, 0xFF) // Select column 6
|
|
172
|
+
|
|
173
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
174
|
+
expect(rowState & (1 << 1)).toBe(0) // PA1 should be low
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('should map Backspace key', () => {
|
|
178
|
+
keyboard.updateKey(0x2A, true) // Backspace
|
|
179
|
+
keyboard.writePortB(0xDF, 0xFF) // Select column 5
|
|
180
|
+
|
|
181
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
182
|
+
expect(rowState & (1 << 1)).toBe(0) // PA1 should be low
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('should map Tab key', () => {
|
|
186
|
+
keyboard.updateKey(0x2B, true) // Tab
|
|
187
|
+
keyboard.writePortB(0x7F, 0xFF) // Select column 7
|
|
188
|
+
|
|
189
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
190
|
+
expect(rowState & (1 << 1)).toBe(0) // PA1 should be low
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('should map Space key', () => {
|
|
194
|
+
keyboard.updateKey(0x2C, true) // Space
|
|
195
|
+
keyboard.writePortB(0xF7, 0xFF) // Select column 3 (PB3)
|
|
196
|
+
|
|
197
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
198
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
describe('USB HID Key Mapping - Modifier Keys', () => {
|
|
203
|
+
it('should map Left Ctrl', () => {
|
|
204
|
+
keyboard.updateKey(0xE0, true) // Left Ctrl
|
|
205
|
+
keyboard.writePortB(0xFE, 0xFF) // Select column 0
|
|
206
|
+
|
|
207
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
208
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('should map Right Ctrl', () => {
|
|
212
|
+
keyboard.updateKey(0xE4, true) // Right Ctrl
|
|
213
|
+
keyboard.writePortB(0xFE, 0xFF) // Select column 0
|
|
214
|
+
|
|
215
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
216
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('should map Left Shift', () => {
|
|
220
|
+
keyboard.updateKey(0xE1, true) // Left Shift
|
|
221
|
+
keyboard.writePortB(0xEF, 0xFF) // Select column 4
|
|
222
|
+
|
|
223
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
224
|
+
expect(rowState & (1 << 5)).toBe(0) // PA5 should be low
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('should map Right Shift', () => {
|
|
228
|
+
keyboard.updateKey(0xE5, true) // Right Shift
|
|
229
|
+
keyboard.writePortB(0xEF, 0xFF) // Select column 4
|
|
230
|
+
|
|
231
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
232
|
+
expect(rowState & (1 << 5)).toBe(0) // PA5 should be low
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('should map Left Alt', () => {
|
|
236
|
+
keyboard.updateKey(0xE2, true) // Left Alt
|
|
237
|
+
keyboard.writePortB(0xFB, 0xFF) // Select column 2
|
|
238
|
+
|
|
239
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
240
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('should map Left GUI (Windows/Command)', () => {
|
|
244
|
+
keyboard.updateKey(0xE3, true) // Left GUI
|
|
245
|
+
keyboard.writePortB(0xFD, 0xFF) // Select column 1
|
|
246
|
+
|
|
247
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
248
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
describe('USB HID Key Mapping - Function Keys', () => {
|
|
253
|
+
it('should map F1 as FN+1', () => {
|
|
254
|
+
keyboard.updateKey(0x3A, true) // F1
|
|
255
|
+
keyboard.writePortB(0xEF, 0xFF) // Select column 4 (FN key)
|
|
256
|
+
|
|
257
|
+
let rowState = keyboard.readPortA(0x00, 0xFF)
|
|
258
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low (FN)
|
|
259
|
+
|
|
260
|
+
keyboard.writePortB(0xFD, 0xFF) // Select column 1 ('1' key)
|
|
261
|
+
rowState = keyboard.readPortA(0x00, 0xFF)
|
|
262
|
+
expect(rowState & (1 << 0)).toBe(0) // PA0 should be low ('1')
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('should map F10 as FN+0', () => {
|
|
266
|
+
keyboard.updateKey(0x43, true) // F10
|
|
267
|
+
keyboard.writePortB(0xEF, 0xFF) // Select column 4 (FN key)
|
|
268
|
+
|
|
269
|
+
let rowState = keyboard.readPortA(0x00, 0xFF)
|
|
270
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low (FN)
|
|
271
|
+
|
|
272
|
+
keyboard.writePortB(0xFB, 0xFF) // Select column 2 ('0' key)
|
|
273
|
+
rowState = keyboard.readPortA(0x00, 0xFF)
|
|
274
|
+
expect(rowState & (1 << 1)).toBe(0) // PA1 should be low ('0')
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('should release F key properly', () => {
|
|
278
|
+
keyboard.updateKey(0x3A, true) // Press F1
|
|
279
|
+
keyboard.updateKey(0x3A, false) // Release F1
|
|
280
|
+
|
|
281
|
+
keyboard.writePortB(0x00, 0xFF) // Select all columns
|
|
282
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
283
|
+
expect(rowState).toBe(0xFF) // All rows should be high
|
|
284
|
+
})
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
describe('USB HID Key Mapping - Arrow Keys', () => {
|
|
288
|
+
it('should map Up Arrow', () => {
|
|
289
|
+
keyboard.updateKey(0x52, true) // Up
|
|
290
|
+
keyboard.writePortB(0x7F, 0xFF) // Select column 7
|
|
291
|
+
|
|
292
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
293
|
+
expect(rowState & (1 << 6)).toBe(0) // PA6 should be low
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it('should map Down Arrow', () => {
|
|
297
|
+
keyboard.updateKey(0x51, true) // Down
|
|
298
|
+
keyboard.writePortB(0xBF, 0xFF) // Select column 6
|
|
299
|
+
|
|
300
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
301
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
it('should map Left Arrow', () => {
|
|
305
|
+
keyboard.updateKey(0x50, true) // Left
|
|
306
|
+
keyboard.writePortB(0xDF, 0xFF) // Select column 5
|
|
307
|
+
|
|
308
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
309
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('should map Right Arrow', () => {
|
|
313
|
+
keyboard.updateKey(0x4F, true) // Right
|
|
314
|
+
keyboard.writePortB(0x7F, 0xFF) // Select column 7
|
|
315
|
+
|
|
316
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
317
|
+
expect(rowState & (1 << 7)).toBe(0) // PA7 should be low
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
describe('Key Press and Release', () => {
|
|
322
|
+
it('should press and release a key', () => {
|
|
323
|
+
keyboard.updateKey(0x04, true) // Press 'a'
|
|
324
|
+
keyboard.writePortB(0x7F, 0xFF)
|
|
325
|
+
|
|
326
|
+
let rowState = keyboard.readPortA(0x00, 0xFF)
|
|
327
|
+
expect(rowState & (1 << 3)).toBe(0) // Row should be low
|
|
328
|
+
|
|
329
|
+
keyboard.updateKey(0x04, false) // Release 'a'
|
|
330
|
+
rowState = keyboard.readPortA(0x00, 0xFF)
|
|
331
|
+
expect(rowState & (1 << 3)).not.toBe(0) // Row should be high
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
it('should handle simultaneous key presses', () => {
|
|
335
|
+
keyboard.updateKey(0x04, true) // 'a'
|
|
336
|
+
keyboard.updateKey(0x16, true) // 's'
|
|
337
|
+
keyboard.updateKey(0x07, true) // 'd'
|
|
338
|
+
|
|
339
|
+
keyboard.writePortB(0x00, 0xFF) // Select all columns
|
|
340
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
341
|
+
|
|
342
|
+
expect(rowState & (1 << 3)).toBe(0) // 'a' row
|
|
343
|
+
expect(rowState & (1 << 4)).toBe(0) // 's' and 'd' row
|
|
344
|
+
})
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
describe('Direct Matrix Position Update', () => {
|
|
348
|
+
it('should update matrix position directly', () => {
|
|
349
|
+
keyboard.updateMatrixPosition(0, 0, true)
|
|
350
|
+
keyboard.writePortB(0xFE, 0xFF) // Select column 0
|
|
351
|
+
|
|
352
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
353
|
+
expect(rowState & (1 << 0)).toBe(0)
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
it('should handle out of bounds positions', () => {
|
|
357
|
+
expect(() => {
|
|
358
|
+
keyboard.updateMatrixPosition(10, 10, true)
|
|
359
|
+
}).not.toThrow()
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
it('should release matrix position', () => {
|
|
363
|
+
keyboard.updateMatrixPosition(2, 3, true)
|
|
364
|
+
keyboard.updateMatrixPosition(2, 3, false)
|
|
365
|
+
|
|
366
|
+
keyboard.writePortB(0xF7, 0xFF) // Select column 3
|
|
367
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
368
|
+
expect(rowState & (1 << 2)).not.toBe(0) // Should be high
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
describe('Complex Scenarios', () => {
|
|
373
|
+
it('should handle Ctrl+C combination', () => {
|
|
374
|
+
keyboard.updateKey(0xE0, true) // Ctrl
|
|
375
|
+
keyboard.updateKey(0x06, true) // 'c'
|
|
376
|
+
|
|
377
|
+
keyboard.writePortB(0x00, 0xFF) // Select all columns
|
|
378
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
379
|
+
|
|
380
|
+
expect(rowState & (1 << 7)).toBe(0) // Ctrl row
|
|
381
|
+
expect(rowState & (1 << 5)).toBe(0) // 'c' row
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('should handle column scanning sequence', () => {
|
|
385
|
+
keyboard.updateKey(0x04, true) // 'a' at PA3, PB7
|
|
386
|
+
|
|
387
|
+
// Scan each column
|
|
388
|
+
for (let col = 0; col < 8; col++) {
|
|
389
|
+
keyboard.writePortB(~(1 << col) & 0xFF, 0xFF)
|
|
390
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
391
|
+
|
|
392
|
+
if (col === 7) {
|
|
393
|
+
expect(rowState & (1 << 3)).toBe(0) // Should detect 'a'
|
|
394
|
+
} else {
|
|
395
|
+
expect(rowState).toBe(0xFF) // No keys in other columns
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
it('should handle rapid key press/release', () => {
|
|
401
|
+
for (let i = 0; i < 10; i++) {
|
|
402
|
+
keyboard.updateKey(0x04, true)
|
|
403
|
+
keyboard.updateKey(0x04, false)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
keyboard.writePortB(0x00, 0xFF)
|
|
407
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
408
|
+
expect(rowState).toBe(0xFF) // Should be released
|
|
409
|
+
})
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
describe('Matrix State Inspection', () => {
|
|
413
|
+
it('should return current matrix state', () => {
|
|
414
|
+
keyboard.updateMatrixPosition(0, 0, true)
|
|
415
|
+
keyboard.updateMatrixPosition(1, 1, true)
|
|
416
|
+
|
|
417
|
+
const matrix = keyboard.getMatrixState()
|
|
418
|
+
expect(matrix[0] & (1 << 0)).not.toBe(0)
|
|
419
|
+
expect(matrix[1] & (1 << 1)).not.toBe(0)
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
it('should not modify internal state when getting matrix', () => {
|
|
423
|
+
keyboard.updateMatrixPosition(0, 0, true)
|
|
424
|
+
const matrix1 = keyboard.getMatrixState()
|
|
425
|
+
matrix1[0] = 0xFF // Modify returned array
|
|
426
|
+
|
|
427
|
+
const matrix2 = keyboard.getMatrixState()
|
|
428
|
+
expect(matrix2[0]).not.toBe(0xFF) // Internal state unchanged
|
|
429
|
+
})
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
describe('Invalid/Unknown Keys', () => {
|
|
433
|
+
it('should ignore unknown USB HID codes', () => {
|
|
434
|
+
keyboard.updateKey(0xFF, true) // Invalid code
|
|
435
|
+
keyboard.writePortB(0x00, 0xFF)
|
|
436
|
+
|
|
437
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
438
|
+
expect(rowState).toBe(0xFF) // No keys pressed
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
it('should ignore keys with no ASCII mapping', () => {
|
|
442
|
+
keyboard.updateKey(0x46, true) // Print Screen (no ASCII)
|
|
443
|
+
keyboard.writePortB(0x00, 0xFF)
|
|
444
|
+
|
|
445
|
+
const rowState = keyboard.readPortA(0x00, 0xFF)
|
|
446
|
+
expect(rowState).toBe(0xFF) // No keys pressed
|
|
447
|
+
})
|
|
448
|
+
})
|
|
449
|
+
})
|