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,961 @@
1
+ import { GPIOKeyboardEncoderAttachment } from '../../../components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment'
2
+
3
+ describe('GPIOKeyboardEncoderAttachment', () => {
4
+ let encoder: GPIOKeyboardEncoderAttachment
5
+
6
+ beforeEach(() => {
7
+ encoder = new GPIOKeyboardEncoderAttachment(5)
8
+ })
9
+
10
+ describe('Initialization', () => {
11
+ it('should initialize with no data ready', () => {
12
+ expect(encoder.hasDataReadyA()).toBe(false)
13
+ expect(encoder.hasDataReadyB()).toBe(false)
14
+ })
15
+
16
+ it('should initialize with no interrupts pending', () => {
17
+ expect(encoder.hasCA1Interrupt()).toBe(false)
18
+ expect(encoder.hasCB1Interrupt()).toBe(false)
19
+ })
20
+
21
+ it('should be disabled by default (CA2/CB2 high)', () => {
22
+ encoder.updateControlLines(false, true, false, true)
23
+ encoder.updateKey(0x04, true) // 'a'
24
+ expect(encoder.hasCA1Interrupt()).toBe(false)
25
+ expect(encoder.hasCB1Interrupt()).toBe(false)
26
+ })
27
+
28
+ it('should have correct priority', () => {
29
+ expect(encoder.getPriority()).toBe(5)
30
+ })
31
+ })
32
+
33
+ describe('Reset', () => {
34
+ it('should clear all data and states', () => {
35
+ // Enable and generate some data
36
+ encoder.updateControlLines(false, false, false, false)
37
+ encoder.updateKey(0x04, true) // 'a'
38
+ expect(encoder.hasDataReadyA()).toBe(true)
39
+
40
+ // Reset
41
+ encoder.reset()
42
+ expect(encoder.hasDataReadyA()).toBe(false)
43
+ expect(encoder.hasDataReadyB()).toBe(false)
44
+ expect(encoder.hasCA1Interrupt()).toBe(false)
45
+ expect(encoder.hasCB1Interrupt()).toBe(false)
46
+ })
47
+ })
48
+
49
+ describe('Enable/Disable Control', () => {
50
+ it('should enable Port A when CA2 is LOW', () => {
51
+ encoder.updateControlLines(false, false, false, true)
52
+ encoder.updateKey(0x04, true) // 'a'
53
+ expect(encoder.hasCA1Interrupt()).toBe(true)
54
+ expect(encoder.hasCB1Interrupt()).toBe(false)
55
+ })
56
+
57
+ it('should enable Port B when CB2 is LOW', () => {
58
+ encoder.updateControlLines(false, true, false, false)
59
+ encoder.updateKey(0x04, true) // 'a'
60
+ expect(encoder.hasCA1Interrupt()).toBe(false)
61
+ expect(encoder.hasCB1Interrupt()).toBe(true)
62
+ })
63
+
64
+ it('should enable both ports when both CA2 and CB2 are LOW', () => {
65
+ encoder.updateControlLines(false, false, false, false)
66
+ encoder.updateKey(0x04, true) // 'a'
67
+ expect(encoder.hasCA1Interrupt()).toBe(true)
68
+ expect(encoder.hasCB1Interrupt()).toBe(true)
69
+ })
70
+
71
+ it('should disable Port A when CA2 is HIGH', () => {
72
+ encoder.updateControlLines(false, true, false, false)
73
+ encoder.updateKey(0x04, true) // 'a'
74
+ expect(encoder.hasCA1Interrupt()).toBe(false)
75
+ })
76
+
77
+ it('should disable Port B when CB2 is HIGH', () => {
78
+ encoder.updateControlLines(false, false, false, true)
79
+ encoder.updateKey(0x04, true) // 'a'
80
+ expect(encoder.hasCB1Interrupt()).toBe(false)
81
+ })
82
+ })
83
+
84
+ describe('Port Reading', () => {
85
+ it('should return 0xFF when no data ready on Port A', () => {
86
+ const value = encoder.readPortA(0xFF, 0x00)
87
+ expect(value).toBe(0xFF)
88
+ })
89
+
90
+ it('should return 0xFF when no data ready on Port B', () => {
91
+ const value = encoder.readPortB(0xFF, 0x00)
92
+ expect(value).toBe(0xFF)
93
+ })
94
+
95
+ it('should return ASCII data when data ready on Port A', () => {
96
+ encoder.updateControlLines(false, false, false, false)
97
+ encoder.updateKey(0x04, true) // 'a' = 0x61
98
+ const value = encoder.readPortA(0xFF, 0x00)
99
+ expect(value).toBe(0x61)
100
+ })
101
+
102
+ it('should return ASCII data when data ready on Port B', () => {
103
+ encoder.updateControlLines(false, false, false, false)
104
+ encoder.updateKey(0x04, true) // 'a' = 0x61
105
+ const value = encoder.readPortB(0xFF, 0x00)
106
+ expect(value).toBe(0x61)
107
+ })
108
+
109
+ it('should return 0xFF on disabled port even with data ready', () => {
110
+ encoder.updateControlLines(false, true, false, true) // Both disabled
111
+ encoder.updateKey(0x04, true) // 'a'
112
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFF)
113
+ expect(encoder.readPortB(0xFF, 0x00)).toBe(0xFF)
114
+ })
115
+
116
+ it('should provide same data on both ports', () => {
117
+ encoder.updateControlLines(false, false, false, false)
118
+ encoder.updateKey(0x04, true) // 'a'
119
+ const valueA = encoder.readPortA(0xFF, 0x00)
120
+ const valueB = encoder.readPortB(0xFF, 0x00)
121
+ expect(valueA).toBe(valueB)
122
+ expect(valueA).toBe(0x61)
123
+ })
124
+ })
125
+
126
+ describe('Interrupt Handling', () => {
127
+ it('should trigger CA1 interrupt when Port A enabled and key pressed', () => {
128
+ encoder.updateControlLines(false, false, false, true)
129
+ expect(encoder.hasCA1Interrupt()).toBe(false)
130
+ encoder.updateKey(0x04, true) // 'a'
131
+ expect(encoder.hasCA1Interrupt()).toBe(true)
132
+ })
133
+
134
+ it('should trigger CB1 interrupt when Port B enabled and key pressed', () => {
135
+ encoder.updateControlLines(false, true, false, false)
136
+ expect(encoder.hasCB1Interrupt()).toBe(false)
137
+ encoder.updateKey(0x04, true) // 'a'
138
+ expect(encoder.hasCB1Interrupt()).toBe(true)
139
+ })
140
+
141
+ it('should clear CA1 interrupt and data ready when cleared', () => {
142
+ encoder.updateControlLines(false, false, false, false)
143
+ encoder.updateKey(0x04, true) // 'a'
144
+ expect(encoder.hasCA1Interrupt()).toBe(true)
145
+ expect(encoder.hasDataReadyA()).toBe(true)
146
+
147
+ encoder.clearInterrupts(true, false, false, false)
148
+ expect(encoder.hasCA1Interrupt()).toBe(false)
149
+ expect(encoder.hasDataReadyA()).toBe(false)
150
+ })
151
+
152
+ it('should clear CB1 interrupt and data ready when cleared', () => {
153
+ encoder.updateControlLines(false, false, false, false)
154
+ encoder.updateKey(0x04, true) // 'a'
155
+ expect(encoder.hasCB1Interrupt()).toBe(true)
156
+ expect(encoder.hasDataReadyB()).toBe(true)
157
+
158
+ encoder.clearInterrupts(false, false, true, false)
159
+ expect(encoder.hasCB1Interrupt()).toBe(false)
160
+ expect(encoder.hasDataReadyB()).toBe(false)
161
+ })
162
+
163
+ it('should not trigger interrupt when port is disabled', () => {
164
+ encoder.updateControlLines(false, true, false, true) // Both disabled
165
+ encoder.updateKey(0x04, true) // 'a'
166
+ expect(encoder.hasCA1Interrupt()).toBe(false)
167
+ expect(encoder.hasCB1Interrupt()).toBe(false)
168
+ })
169
+ })
170
+
171
+ describe('Basic Key Mapping', () => {
172
+ beforeEach(() => {
173
+ encoder.updateControlLines(false, false, false, false)
174
+ })
175
+
176
+ it('should map lowercase letters correctly', () => {
177
+ encoder.updateKey(0x04, true) // 'a'
178
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x61)
179
+
180
+ encoder.clearInterrupts(true, false, true, false)
181
+ encoder.updateKey(0x1D, true) // 'z'
182
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x7A)
183
+ })
184
+
185
+ it('should map numbers correctly', () => {
186
+ encoder.updateKey(0x1E, true) // '1'
187
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x31)
188
+
189
+ encoder.clearInterrupts(true, false, true, false)
190
+ encoder.updateKey(0x27, true) // '0'
191
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x30)
192
+ })
193
+
194
+ it('should map special keys correctly', () => {
195
+ encoder.updateKey(0x28, true) // Enter
196
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x0D)
197
+
198
+ encoder.clearInterrupts(true, false, true, false)
199
+ encoder.updateKey(0x29, true) // Escape
200
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1B)
201
+
202
+ encoder.clearInterrupts(true, false, true, false)
203
+ encoder.updateKey(0x2C, true) // Space
204
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x20)
205
+ })
206
+
207
+ it('should ignore key releases', () => {
208
+ encoder.updateKey(0x04, true) // Press 'a'
209
+ expect(encoder.hasDataReadyA()).toBe(true)
210
+
211
+ encoder.clearInterrupts(true, false, true, false)
212
+ encoder.updateKey(0x04, false) // Release 'a'
213
+ expect(encoder.hasDataReadyA()).toBe(false) // No new data
214
+ })
215
+
216
+ it('should ignore unknown keycodes', () => {
217
+ encoder.updateKey(0xFF, true) // Invalid keycode
218
+ expect(encoder.hasDataReadyA()).toBe(false)
219
+ })
220
+ })
221
+
222
+ describe('Modifier Keys', () => {
223
+ beforeEach(() => {
224
+ encoder.updateControlLines(false, false, false, false)
225
+ })
226
+
227
+ it('should not generate output for modifier keys alone', () => {
228
+ encoder.updateKey(0xE0, true) // Left Ctrl
229
+ expect(encoder.hasDataReadyA()).toBe(false)
230
+
231
+ encoder.updateKey(0xE1, true) // Left Shift
232
+ expect(encoder.hasDataReadyA()).toBe(false)
233
+
234
+ encoder.updateKey(0xE2, true) // Left Alt
235
+ expect(encoder.hasDataReadyA()).toBe(false)
236
+ })
237
+
238
+ it('should track modifier key state across presses', () => {
239
+ // Press Shift
240
+ encoder.updateKey(0xE1, true)
241
+ encoder.updateKey(0x04, true) // 'a' -> 'A'
242
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41)
243
+
244
+ // Release Shift
245
+ encoder.clearInterrupts(true, false, true, false)
246
+ encoder.updateKey(0xE1, false)
247
+ encoder.updateKey(0x04, true) // 'a'
248
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x61)
249
+ })
250
+
251
+ it('should handle both left and right modifiers', () => {
252
+ // Left Ctrl
253
+ encoder.updateKey(0xE0, true)
254
+ encoder.updateKey(0x04, true) // Ctrl+a
255
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x01)
256
+
257
+ encoder.clearInterrupts(true, false, true, false)
258
+ encoder.updateKey(0xE0, false)
259
+
260
+ // Right Ctrl
261
+ encoder.updateKey(0xE4, true)
262
+ encoder.updateKey(0x04, true) // Ctrl+a
263
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x01)
264
+ })
265
+ })
266
+
267
+ describe('Shift Key Mapping', () => {
268
+ beforeEach(() => {
269
+ encoder.updateControlLines(false, false, false, false)
270
+ })
271
+
272
+ it('should map Shift+letter to uppercase', () => {
273
+ encoder.updateKey(0xE1, true) // Press Shift
274
+ encoder.updateKey(0x04, true) // 'a' -> 'A'
275
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41)
276
+ })
277
+
278
+ it('should map Shift+number to symbols', () => {
279
+ encoder.updateKey(0xE1, true) // Press Shift
280
+ encoder.updateKey(0x1E, true) // '1' -> '!'
281
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x21)
282
+
283
+ encoder.clearInterrupts(true, false, true, false)
284
+ encoder.updateKey(0x25, true) // '8' -> '*'
285
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x2A)
286
+ })
287
+
288
+ it('should map Shift+special keys to shifted symbols', () => {
289
+ encoder.updateKey(0xE1, true) // Press Shift
290
+
291
+ encoder.updateKey(0x2D, true) // '-' -> '_'
292
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x5F)
293
+
294
+ encoder.clearInterrupts(true, false, true, false)
295
+ encoder.updateKey(0x2E, true) // '=' -> '+'
296
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x2B)
297
+
298
+ encoder.clearInterrupts(true, false, true, false)
299
+ encoder.updateKey(0x2F, true) // '[' -> '{'
300
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x7B)
301
+ })
302
+ })
303
+
304
+ describe('Ctrl Key Mapping', () => {
305
+ beforeEach(() => {
306
+ encoder.updateControlLines(false, false, false, false)
307
+ })
308
+
309
+ it('should map Ctrl+letter to control codes', () => {
310
+ encoder.updateKey(0xE0, true) // Press Ctrl
311
+ encoder.updateKey(0x04, true) // Ctrl+a -> 0x01
312
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x01)
313
+
314
+ encoder.clearInterrupts(true, false, true, false)
315
+ encoder.updateKey(0x1D, true) // Ctrl+z -> 0x1A
316
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1A)
317
+ })
318
+
319
+ it('should map Ctrl+special keys to control codes', () => {
320
+ encoder.updateKey(0xE0, true) // Press Ctrl
321
+
322
+ encoder.updateKey(0x2F, true) // Ctrl+[ -> ESC (0x1B)
323
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1B)
324
+
325
+ encoder.clearInterrupts(true, false, true, false)
326
+ encoder.updateKey(0x31, true) // Ctrl+\ -> FS (0x1C)
327
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1C)
328
+
329
+ encoder.clearInterrupts(true, false, true, false)
330
+ encoder.updateKey(0x30, true) // Ctrl+] -> GS (0x1D)
331
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1D)
332
+ })
333
+
334
+ it('should map Ctrl+2 to NUL', () => {
335
+ encoder.updateKey(0xE0, true) // Press Ctrl
336
+ encoder.updateKey(0x1F, true) // Ctrl+2 -> 0x00
337
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x00)
338
+ })
339
+
340
+ it('should map Ctrl+6 to RS (UP arrow)', () => {
341
+ encoder.updateKey(0xE0, true) // Press Ctrl
342
+ encoder.updateKey(0x23, true) // Ctrl+6 -> 0x1E
343
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1E)
344
+ })
345
+
346
+ it('should map Ctrl+- to US (DOWN arrow)', () => {
347
+ encoder.updateKey(0xE0, true) // Press Ctrl
348
+ encoder.updateKey(0x2D, true) // Ctrl+- -> 0x1F
349
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1F)
350
+ })
351
+ })
352
+
353
+ describe('MENU Key Mapping', () => {
354
+ beforeEach(() => {
355
+ encoder.updateControlLines(false, false, false, false)
356
+ })
357
+
358
+ it('should map MENU key to 0x80', () => {
359
+ encoder.updateKey(0xE3, true) // Left GUI (MENU)
360
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x80)
361
+ })
362
+
363
+ it('should map Alt+MENU to 0x90', () => {
364
+ encoder.updateKey(0xE2, true) // Press Alt
365
+ encoder.updateKey(0xE3, true) // MENU
366
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x90)
367
+ })
368
+
369
+ it('should handle Right GUI as MENU', () => {
370
+ encoder.updateKey(0xE7, true) // Right GUI (MENU)
371
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x80)
372
+ })
373
+ })
374
+
375
+ describe('Function Key Mapping', () => {
376
+ beforeEach(() => {
377
+ encoder.updateControlLines(false, false, false, false)
378
+ })
379
+
380
+ it('should map F1-F12 to 0x81-0x8C', () => {
381
+ encoder.updateKey(0x3A, true) // F1
382
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x81)
383
+
384
+ encoder.clearInterrupts(true, false, true, false)
385
+ encoder.updateKey(0x3B, true) // F2
386
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x82)
387
+
388
+ encoder.clearInterrupts(true, false, true, false)
389
+ encoder.updateKey(0x45, true) // F12
390
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x8C)
391
+ })
392
+
393
+ it('should map F13-F15 to 0x8D-0x8F', () => {
394
+ encoder.updateKey(0x68, true) // F13
395
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x8D)
396
+
397
+ encoder.clearInterrupts(true, false, true, false)
398
+ encoder.updateKey(0x6A, true) // F15
399
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x8F)
400
+ })
401
+
402
+ it('should map Alt+F1-F12 to 0x91-0x9C', () => {
403
+ encoder.updateKey(0xE2, true) // Press Alt
404
+
405
+ encoder.updateKey(0x3A, true) // Alt+F1
406
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x91)
407
+
408
+ encoder.clearInterrupts(true, false, true, false)
409
+ encoder.updateKey(0x45, true) // Alt+F12
410
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x9C)
411
+ })
412
+
413
+ it('should map Alt+F13-F15 to 0x9D-0x9F', () => {
414
+ encoder.updateKey(0xE2, true) // Press Alt
415
+
416
+ encoder.updateKey(0x68, true) // Alt+F13
417
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x9D)
418
+
419
+ encoder.clearInterrupts(true, false, true, false)
420
+ encoder.updateKey(0x6A, true) // Alt+F15
421
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x9F)
422
+ })
423
+ })
424
+
425
+ describe('Alt Key Mapping', () => {
426
+ beforeEach(() => {
427
+ encoder.updateControlLines(false, false, false, false)
428
+ })
429
+
430
+ it('should map Alt+letter to extended character set', () => {
431
+ encoder.updateKey(0xE2, true) // Press Alt
432
+
433
+ encoder.updateKey(0x04, true) // Alt+a -> 0xE1
434
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xE1)
435
+
436
+ encoder.clearInterrupts(true, false, true, false)
437
+ encoder.updateKey(0x1D, true) // Alt+z -> 0xFA
438
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFA)
439
+ })
440
+
441
+ it('should map Alt+number to extended character set', () => {
442
+ encoder.updateKey(0xE2, true) // Press Alt
443
+
444
+ encoder.updateKey(0x1E, true) // Alt+1 -> 0xB1
445
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xB1)
446
+
447
+ encoder.clearInterrupts(true, false, true, false)
448
+ encoder.updateKey(0x27, true) // Alt+0 -> 0xB0
449
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xB0)
450
+ })
451
+
452
+ it('should map Alt+Space to 0xA0', () => {
453
+ encoder.updateKey(0xE2, true) // Press Alt
454
+ encoder.updateKey(0x2C, true) // Alt+Space -> 0xA0
455
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA0)
456
+ })
457
+
458
+ it('should map Alt+DEL to 0xFF', () => {
459
+ encoder.updateKey(0xE2, true) // Press Alt
460
+ encoder.updateKey(0x4C, true) // Alt+DEL -> 0xFF
461
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFF)
462
+ })
463
+ })
464
+
465
+ describe('Alt+Shift Key Mapping', () => {
466
+ beforeEach(() => {
467
+ encoder.updateControlLines(false, false, false, false)
468
+ })
469
+
470
+ it('should map Alt+Shift+letter to extended character set', () => {
471
+ encoder.updateKey(0xE2, true) // Press Alt
472
+ encoder.updateKey(0xE1, true) // Press Shift
473
+
474
+ encoder.updateKey(0x04, true) // Alt+Shift+a -> 0xC1
475
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC1)
476
+
477
+ encoder.clearInterrupts(true, false, true, false)
478
+ encoder.updateKey(0x1D, true) // Alt+Shift+z -> 0xDA
479
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xDA)
480
+ })
481
+
482
+ it('should map Alt+Shift+number to extended character set', () => {
483
+ encoder.updateKey(0xE2, true) // Press Alt
484
+ encoder.updateKey(0xE1, true) // Press Shift
485
+
486
+ encoder.updateKey(0x1E, true) // Alt+Shift+1 -> 0xA1
487
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA1)
488
+
489
+ encoder.clearInterrupts(true, false, true, false)
490
+ encoder.updateKey(0x1F, true) // Alt+Shift+2 -> 0xC0
491
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC0)
492
+ })
493
+
494
+ it('should map Alt+Shift+symbols to extended character set', () => {
495
+ encoder.updateKey(0xE2, true) // Press Alt
496
+ encoder.updateKey(0xE1, true) // Press Shift
497
+
498
+ encoder.updateKey(0x2D, true) // Alt+Shift+- -> 0xDF
499
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xDF)
500
+
501
+ encoder.clearInterrupts(true, false, true, false)
502
+ encoder.updateKey(0x2F, true) // Alt+Shift+[ -> 0xFB
503
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFB)
504
+ })
505
+ })
506
+
507
+ describe('Complex Key Combinations', () => {
508
+ beforeEach(() => {
509
+ encoder.updateControlLines(false, false, false, false)
510
+ })
511
+
512
+ it('should handle Ctrl+C combination', () => {
513
+ encoder.updateKey(0xE0, true) // Press Ctrl
514
+ encoder.updateKey(0x06, true) // c
515
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x03) // ETX
516
+ })
517
+
518
+ it('should prioritize Ctrl over Shift', () => {
519
+ encoder.updateKey(0xE0, true) // Press Ctrl
520
+ encoder.updateKey(0xE1, true) // Press Shift
521
+ encoder.updateKey(0x04, true) // a
522
+ // When both Ctrl and Shift are pressed, shift is ignored for Ctrl combinations
523
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x01) // Ctrl+a
524
+ })
525
+
526
+ it('should prioritize Alt+Shift over Alt alone', () => {
527
+ encoder.updateKey(0xE2, true) // Press Alt
528
+ encoder.updateKey(0xE1, true) // Press Shift
529
+ encoder.updateKey(0x04, true) // a
530
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC1) // Alt+Shift+a, not Alt+a (0xE1)
531
+ })
532
+
533
+ it('should apply Alt when both Ctrl and Alt are active', () => {
534
+ encoder.updateKey(0xE0, true) // Press Ctrl
535
+ encoder.updateKey(0xE2, true) // Press Alt
536
+ encoder.updateKey(0x04, true) // a
537
+ // Alt takes effect when both Ctrl and Alt are pressed (per C++ implementation)
538
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xE1) // Alt+a
539
+ })
540
+ })
541
+
542
+ describe('Sequential Key Presses', () => {
543
+ beforeEach(() => {
544
+ encoder.updateControlLines(false, false, false, false)
545
+ })
546
+
547
+ it('should handle multiple sequential key presses', () => {
548
+ encoder.updateKey(0x04, true) // 'a'
549
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x61)
550
+
551
+ encoder.clearInterrupts(true, false, true, false)
552
+ encoder.updateKey(0x05, true) // 'b'
553
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x62)
554
+
555
+ encoder.clearInterrupts(true, false, true, false)
556
+ encoder.updateKey(0x06, true) // 'c'
557
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x63)
558
+ })
559
+
560
+ it('should overwrite previous data with new key press', () => {
561
+ encoder.updateKey(0x04, true) // 'a'
562
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x61)
563
+
564
+ // New key press without clearing interrupts
565
+ encoder.updateKey(0x05, true) // 'b'
566
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x62) // Overwritten
567
+ })
568
+ })
569
+
570
+ describe('Edge Cases', () => {
571
+ beforeEach(() => {
572
+ encoder.updateControlLines(false, false, false, false)
573
+ })
574
+
575
+ it('should handle rapid modifier changes', () => {
576
+ encoder.updateKey(0xE1, true) // Press Shift
577
+ encoder.updateKey(0xE1, false) // Release Shift
578
+ encoder.updateKey(0xE1, true) // Press Shift again
579
+ encoder.updateKey(0x04, true) // 'a' -> 'A'
580
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41)
581
+ })
582
+
583
+ it('should handle all modifiers released', () => {
584
+ encoder.updateKey(0xE0, true) // Press Ctrl
585
+ encoder.updateKey(0xE1, true) // Press Shift
586
+ encoder.updateKey(0xE2, true) // Press Alt
587
+ encoder.updateKey(0xE0, false) // Release Ctrl
588
+ encoder.updateKey(0xE1, false) // Release Shift
589
+ encoder.updateKey(0xE2, false) // Release Alt
590
+ encoder.updateKey(0x04, true) // 'a'
591
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x61) // Plain 'a'
592
+ })
593
+
594
+ it('should handle port disabled mid-operation', () => {
595
+ encoder.updateKey(0x04, true) // 'a'
596
+ expect(encoder.hasCA1Interrupt()).toBe(true)
597
+
598
+ // Disable port
599
+ encoder.updateControlLines(false, true, false, true)
600
+ expect(encoder.hasCA1Interrupt()).toBe(false) // Interrupt not visible when disabled
601
+ })
602
+
603
+ it('should handle re-enabling port with data still present', () => {
604
+ encoder.updateKey(0x04, true) // 'a'
605
+ encoder.updateControlLines(false, true, false, true) // Disable
606
+
607
+ // Re-enable
608
+ encoder.updateControlLines(false, false, false, false)
609
+ // Data should still be there
610
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x61)
611
+ })
612
+ })
613
+
614
+ describe('Alt+Shift Symbol Mapping (Extended Character Set)', () => {
615
+ beforeEach(() => {
616
+ encoder.updateControlLines(false, false, false, false)
617
+ encoder.updateKey(0xE2, true) // Press Alt
618
+ encoder.updateKey(0xE1, true) // Press Shift
619
+ })
620
+
621
+ afterEach(() => {
622
+ encoder.clearInterrupts(true, false, true, false)
623
+ encoder.updateKey(0xE2, false) // Release Alt
624
+ encoder.updateKey(0xE1, false) // Release Shift
625
+ })
626
+
627
+ it('should map Alt+Shift+1 to ¡ (0xA1)', () => {
628
+ encoder.updateKey(0x1E, true) // '1'
629
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA1)
630
+ })
631
+
632
+ it('should map Alt+Shift+\' to ¢ (0xA2)', () => {
633
+ encoder.updateKey(0x34, true) // '\'' (apostrophe, USB HID 0x34)
634
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA2)
635
+ })
636
+
637
+ it('should map Alt+Shift+3 to £ (0xA3)', () => {
638
+ encoder.updateKey(0x20, true) // '3'
639
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA3)
640
+ })
641
+
642
+ it('should map Alt+Shift+4 to ¤ (0xA4)', () => {
643
+ encoder.updateKey(0x21, true) // '4'
644
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA4)
645
+ })
646
+
647
+ it('should map Alt+Shift+5 to ¥ (0xA5)', () => {
648
+ encoder.updateKey(0x22, true) // '5'
649
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA5)
650
+ })
651
+
652
+ it('should map Alt+Shift+7 to ¦ (0xA6)', () => {
653
+ encoder.updateKey(0x24, true) // '7'
654
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA6)
655
+ })
656
+
657
+ it('should map Alt+Shift+9 to ¨ (0xA8)', () => {
658
+ encoder.updateKey(0x26, true) // '9'
659
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA8)
660
+ })
661
+
662
+ it('should map Alt+Shift+0 to © (0xA9)', () => {
663
+ encoder.updateKey(0x27, true) // '0'
664
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA9)
665
+ })
666
+
667
+ it('should map Alt+Shift+8 to ª (0xAA)', () => {
668
+ encoder.updateKey(0x25, true) // '8'
669
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xAA)
670
+ })
671
+
672
+ it('should map Alt+Shift+= to « (0xAB)', () => {
673
+ encoder.updateKey(0x2E, true) // '='
674
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xAB)
675
+ })
676
+
677
+ it('should map Alt+Shift+; to º (0xBA)', () => {
678
+ encoder.updateKey(0x33, true) // ';'
679
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xBA)
680
+ })
681
+
682
+ it('should map Alt+Shift+, to ¼ (0xBC)', () => {
683
+ encoder.updateKey(0x36, true) // ','
684
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xBC)
685
+ })
686
+
687
+ it('should map Alt+Shift+. to ¾ (0xBE)', () => {
688
+ encoder.updateKey(0x37, true) // '.'
689
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xBE)
690
+ })
691
+
692
+ it('should map Alt+Shift+/ to ¿ (0xBF)', () => {
693
+ encoder.updateKey(0x38, true) // '/'
694
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xBF)
695
+ })
696
+
697
+ it('should map Alt+Shift+2 to À (0xC0)', () => {
698
+ encoder.updateKey(0x1F, true) // '2'
699
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC0)
700
+ })
701
+
702
+ it('should map Alt+Shift+b to  (0xC2)', () => {
703
+ encoder.updateKey(0x05, true) // 'b'
704
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC2)
705
+ })
706
+
707
+ it('should map Alt+Shift+c to à (0xC3)', () => {
708
+ encoder.updateKey(0x06, true) // 'c'
709
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC3)
710
+ })
711
+
712
+ it('should map Alt+Shift+d to Ä (0xC4)', () => {
713
+ encoder.updateKey(0x07, true) // 'd'
714
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC4)
715
+ })
716
+
717
+ it('should map Alt+Shift+e to Å (0xC5)', () => {
718
+ encoder.updateKey(0x08, true) // 'e'
719
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC5)
720
+ })
721
+
722
+ it('should map Alt+Shift+f to Æ (0xC6)', () => {
723
+ encoder.updateKey(0x09, true) // 'f'
724
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC6)
725
+ })
726
+
727
+ it('should map Alt+Shift+g to Ç (0xC7)', () => {
728
+ encoder.updateKey(0x0A, true) // 'g'
729
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC7)
730
+ })
731
+
732
+ it('should map Alt+Shift+h to È (0xC8)', () => {
733
+ encoder.updateKey(0x0B, true) // 'h'
734
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC8)
735
+ })
736
+
737
+ it('should map Alt+Shift+6 to Þ (0xDE)', () => {
738
+ encoder.updateKey(0x23, true) // '6'
739
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xDE)
740
+ })
741
+
742
+ it('should map Alt+Shift+- to ß (0xDF)', () => {
743
+ encoder.updateKey(0x2D, true) // '-'
744
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xDF)
745
+ })
746
+
747
+ it('should map Alt+Shift+[ to û (0xFB)', () => {
748
+ encoder.updateKey(0x2F, true) // '['
749
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFB)
750
+ })
751
+
752
+ it('should map Alt+Shift+\\ to ü (0xFC)', () => {
753
+ encoder.updateKey(0x31, true) // '\\'
754
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFC)
755
+ })
756
+
757
+ it('should map Alt+Shift+] to ý (0xFD)', () => {
758
+ encoder.updateKey(0x30, true) // ']'
759
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFD)
760
+ })
761
+
762
+ it('should map Alt+Shift+` to þ (0xFE)', () => {
763
+ encoder.updateKey(0x35, true) // '`'
764
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFE)
765
+ })
766
+ })
767
+
768
+ describe('Alt Symbol Mapping (Without Shift)', () => {
769
+ beforeEach(() => {
770
+ encoder.updateControlLines(false, false, false, false)
771
+ encoder.updateKey(0xE2, true) // Press Alt
772
+ })
773
+
774
+ afterEach(() => {
775
+ encoder.clearInterrupts(true, false, true, false)
776
+ encoder.updateKey(0xE2, false) // Release Alt
777
+ })
778
+
779
+ it('should map Alt+\' to § (0xA7)', () => {
780
+ encoder.updateKey(0x34, true) // '\''
781
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA7)
782
+ })
783
+
784
+ it('should map Alt+, to ¬ (0xAC)', () => {
785
+ encoder.updateKey(0x36, true) // ','
786
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xAC)
787
+ })
788
+
789
+ it('should map Alt+- to soft hyphen (0xAD)', () => {
790
+ encoder.updateKey(0x2D, true) // '-'
791
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xAD)
792
+ })
793
+
794
+ it('should map Alt+. to ® (0xAE)', () => {
795
+ encoder.updateKey(0x37, true) // '.'
796
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xAE)
797
+ })
798
+
799
+ it('should map Alt+/ to ¯ (0xAF)', () => {
800
+ encoder.updateKey(0x38, true) // '/'
801
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xAF)
802
+ })
803
+
804
+ it('should map Alt+; to » (0xBB)', () => {
805
+ encoder.updateKey(0x33, true) // ';'
806
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xBB)
807
+ })
808
+
809
+ it('should map Alt+= to ½ (0xBD)', () => {
810
+ encoder.updateKey(0x2E, true) // '='
811
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xBD)
812
+ })
813
+
814
+ it('should map Alt+[ to Û (0xDB)', () => {
815
+ encoder.updateKey(0x2F, true) // '['
816
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xDB)
817
+ })
818
+
819
+ it('should map Alt+\\ to Ü (0xDC)', () => {
820
+ encoder.updateKey(0x31, true) // '\\'
821
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xDC)
822
+ })
823
+
824
+ it('should map Alt+] to Ý (0xDD)', () => {
825
+ encoder.updateKey(0x30, true) // ']'
826
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xDD)
827
+ })
828
+
829
+ it('should map Alt+` to à (0xE0)', () => {
830
+ encoder.updateKey(0x35, true) // '`'
831
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0xE0)
832
+ })
833
+ })
834
+
835
+ describe('Ctrl Combinations Coverage', () => {
836
+ beforeEach(() => {
837
+ encoder.updateControlLines(false, false, false, false)
838
+ encoder.updateKey(0xE0, true) // Press Ctrl
839
+ })
840
+
841
+ afterEach(() => {
842
+ encoder.clearInterrupts(true, false, true, false)
843
+ encoder.updateKey(0xE0, false) // Release Ctrl
844
+ })
845
+
846
+ it('should map Ctrl+b to 0x02', () => {
847
+ encoder.updateKey(0x05, true) // 'b'
848
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x02)
849
+ })
850
+
851
+ it('should map Ctrl+d to 0x04', () => {
852
+ encoder.updateKey(0x07, true) // 'd'
853
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x04)
854
+ })
855
+
856
+ it('should map Ctrl+e to 0x05', () => {
857
+ encoder.updateKey(0x08, true) // 'e'
858
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x05)
859
+ })
860
+
861
+ it('should map Ctrl+f to 0x06', () => {
862
+ encoder.updateKey(0x09, true) // 'f'
863
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x06)
864
+ })
865
+
866
+ it('should map Ctrl+g to 0x07', () => {
867
+ encoder.updateKey(0x0A, true) // 'g'
868
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x07)
869
+ })
870
+
871
+ it('should map Ctrl+h to 0x08', () => {
872
+ encoder.updateKey(0x0B, true) // 'h'
873
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x08)
874
+ })
875
+
876
+ it('should map Ctrl+i to 0x09', () => {
877
+ encoder.updateKey(0x0C, true) // 'i'
878
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x09)
879
+ })
880
+
881
+ it('should map Ctrl+j to 0x0A', () => {
882
+ encoder.updateKey(0x0D, true) // 'j'
883
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x0A)
884
+ })
885
+
886
+ it('should map Ctrl+k to 0x0B', () => {
887
+ encoder.updateKey(0x0E, true) // 'k'
888
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x0B)
889
+ })
890
+
891
+ it('should map Ctrl+l to 0x0C', () => {
892
+ encoder.updateKey(0x0F, true) // 'l'
893
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x0C)
894
+ })
895
+
896
+ it('should map Ctrl+m to 0x0D', () => {
897
+ encoder.updateKey(0x10, true) // 'm'
898
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x0D)
899
+ })
900
+
901
+ it('should map Ctrl+n to 0x0E', () => {
902
+ encoder.updateKey(0x11, true) // 'n'
903
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x0E)
904
+ })
905
+
906
+ it('should map Ctrl+o to 0x0F', () => {
907
+ encoder.updateKey(0x12, true) // 'o'
908
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x0F)
909
+ })
910
+
911
+ it('should map Ctrl+p to 0x10', () => {
912
+ encoder.updateKey(0x13, true) // 'p'
913
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x10)
914
+ })
915
+
916
+ it('should map Ctrl+q to 0x11', () => {
917
+ encoder.updateKey(0x14, true) // 'q'
918
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x11)
919
+ })
920
+
921
+ it('should map Ctrl+r to 0x12', () => {
922
+ encoder.updateKey(0x15, true) // 'r'
923
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x12)
924
+ })
925
+
926
+ it('should map Ctrl+s to 0x13', () => {
927
+ encoder.updateKey(0x16, true) // 's'
928
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x13)
929
+ })
930
+
931
+ it('should map Ctrl+t to 0x14', () => {
932
+ encoder.updateKey(0x17, true) // 't'
933
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x14)
934
+ })
935
+
936
+ it('should map Ctrl+u to 0x15', () => {
937
+ encoder.updateKey(0x18, true) // 'u'
938
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x15)
939
+ })
940
+
941
+ it('should map Ctrl+v to 0x16', () => {
942
+ encoder.updateKey(0x19, true) // 'v'
943
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x16)
944
+ })
945
+
946
+ it('should map Ctrl+w to 0x17', () => {
947
+ encoder.updateKey(0x1A, true) // 'w'
948
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x17)
949
+ })
950
+
951
+ it('should map Ctrl+x to 0x18', () => {
952
+ encoder.updateKey(0x1B, true) // 'x'
953
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x18)
954
+ })
955
+
956
+ it('should map Ctrl+y to 0x19', () => {
957
+ encoder.updateKey(0x1C, true) // 'y'
958
+ expect(encoder.readPortA(0xFF, 0x00)).toBe(0x19)
959
+ })
960
+ })
961
+ })