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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +261 -0
  3. package/dist/components/CPU.js +1170 -0
  4. package/dist/components/CPU.js.map +1 -0
  5. package/dist/components/Cart.js +23 -0
  6. package/dist/components/Cart.js.map +1 -0
  7. package/dist/components/IO/Empty.js +19 -0
  8. package/dist/components/IO/Empty.js.map +1 -0
  9. package/dist/components/IO/GPIOAttachments/GPIOAttachment.js +71 -0
  10. package/dist/components/IO/GPIOAttachments/GPIOAttachment.js.map +1 -0
  11. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js +90 -0
  12. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js.map +1 -0
  13. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js +489 -0
  14. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js.map +1 -0
  15. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js +274 -0
  16. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js.map +1 -0
  17. package/dist/components/IO/GPIOCard.js +597 -0
  18. package/dist/components/IO/GPIOCard.js.map +1 -0
  19. package/dist/components/IO/InputBoard.js +19 -0
  20. package/dist/components/IO/InputBoard.js.map +1 -0
  21. package/dist/components/IO/LCDCard.js +19 -0
  22. package/dist/components/IO/LCDCard.js.map +1 -0
  23. package/dist/components/IO/RAMCard.js +63 -0
  24. package/dist/components/IO/RAMCard.js.map +1 -0
  25. package/dist/components/IO/RTCCard.js +483 -0
  26. package/dist/components/IO/RTCCard.js.map +1 -0
  27. package/dist/components/IO/SerialCard.js +282 -0
  28. package/dist/components/IO/SerialCard.js.map +1 -0
  29. package/dist/components/IO/SoundCard.js +620 -0
  30. package/dist/components/IO/SoundCard.js.map +1 -0
  31. package/dist/components/IO/StorageCard.js +428 -0
  32. package/dist/components/IO/StorageCard.js.map +1 -0
  33. package/dist/components/IO/VGACard.js +9 -0
  34. package/dist/components/IO/VGACard.js.map +1 -0
  35. package/dist/components/IO/VideoCard.js +623 -0
  36. package/dist/components/IO/VideoCard.js.map +1 -0
  37. package/dist/components/IO.js +3 -0
  38. package/dist/components/IO.js.map +1 -0
  39. package/dist/components/Machine.js +310 -0
  40. package/dist/components/Machine.js.map +1 -0
  41. package/dist/components/RAM.js +24 -0
  42. package/dist/components/RAM.js.map +1 -0
  43. package/dist/components/ROM.js +23 -0
  44. package/dist/components/ROM.js.map +1 -0
  45. package/dist/index.js +441 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/tests/CPU.test.js +1626 -0
  48. package/dist/tests/CPU.test.js.map +1 -0
  49. package/dist/tests/Cart.test.js +119 -0
  50. package/dist/tests/Cart.test.js.map +1 -0
  51. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js +339 -0
  52. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js.map +1 -0
  53. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js +126 -0
  54. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js.map +1 -0
  55. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js +779 -0
  56. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js.map +1 -0
  57. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js +355 -0
  58. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js.map +1 -0
  59. package/dist/tests/IO/GPIOCard.test.js +503 -0
  60. package/dist/tests/IO/GPIOCard.test.js.map +1 -0
  61. package/dist/tests/IO/RAMCard.test.js +229 -0
  62. package/dist/tests/IO/RAMCard.test.js.map +1 -0
  63. package/dist/tests/IO/RTCCard.test.js +177 -0
  64. package/dist/tests/IO/RTCCard.test.js.map +1 -0
  65. package/dist/tests/IO/SerialCard.test.js +423 -0
  66. package/dist/tests/IO/SerialCard.test.js.map +1 -0
  67. package/dist/tests/IO/SoundCard.test.js +528 -0
  68. package/dist/tests/IO/SoundCard.test.js.map +1 -0
  69. package/dist/tests/IO/StorageCard.test.js +647 -0
  70. package/dist/tests/IO/StorageCard.test.js.map +1 -0
  71. package/dist/tests/IO/VideoCard.test.js +549 -0
  72. package/dist/tests/IO/VideoCard.test.js.map +1 -0
  73. package/dist/tests/Machine.test.js +383 -0
  74. package/dist/tests/Machine.test.js.map +1 -0
  75. package/dist/tests/RAM.test.js +160 -0
  76. package/dist/tests/RAM.test.js.map +1 -0
  77. package/dist/tests/ROM.test.js +123 -0
  78. package/dist/tests/ROM.test.js.map +1 -0
  79. package/jest.config.cjs +9 -0
  80. package/package.json +43 -0
  81. package/src/components/CPU.ts +1371 -0
  82. package/src/components/Cart.ts +20 -0
  83. package/src/components/IO/GPIOAttachments/GPIOAttachment.ts +189 -0
  84. package/src/components/IO/GPIOAttachments/GPIOJoystickAttachment.ts +99 -0
  85. package/src/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.ts +465 -0
  86. package/src/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.ts +287 -0
  87. package/src/components/IO/GPIOCard.ts +677 -0
  88. package/src/components/IO/RAMCard.ts +68 -0
  89. package/src/components/IO/RTCCard.ts +518 -0
  90. package/src/components/IO/SerialCard.ts +335 -0
  91. package/src/components/IO/SoundCard.ts +711 -0
  92. package/src/components/IO/StorageCard.ts +473 -0
  93. package/src/components/IO/VideoCard.ts +730 -0
  94. package/src/components/IO.ts +11 -0
  95. package/src/components/Machine.ts +364 -0
  96. package/src/components/RAM.ts +23 -0
  97. package/src/components/ROM.ts +19 -0
  98. package/src/index.ts +474 -0
  99. package/src/tests/CPU.test.ts +2045 -0
  100. package/src/tests/Cart.test.ts +149 -0
  101. package/src/tests/IO/GPIOAttachments/GPIOAttachment.test.ts +413 -0
  102. package/src/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.ts +147 -0
  103. package/src/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts +961 -0
  104. package/src/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts +449 -0
  105. package/src/tests/IO/GPIOCard.test.ts +644 -0
  106. package/src/tests/IO/RAMCard.test.ts +284 -0
  107. package/src/tests/IO/RTCCard.test.ts +222 -0
  108. package/src/tests/IO/SerialCard.test.ts +530 -0
  109. package/src/tests/IO/SoundCard.test.ts +659 -0
  110. package/src/tests/IO/StorageCard.test.ts +787 -0
  111. package/src/tests/IO/VideoCard.test.ts +668 -0
  112. package/src/tests/Machine.test.ts +437 -0
  113. package/src/tests/RAM.test.ts +196 -0
  114. package/src/tests/ROM.test.ts +154 -0
  115. 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
+ })