ac6502 1.9.3 → 1.11.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/dist/components/CPU.d.ts +4 -0
- package/dist/components/CPU.js +87 -30
- package/dist/components/CPU.js.map +1 -1
- package/dist/components/IO/ACIA.d.ts +13 -21
- package/dist/components/IO/ACIA.js +53 -151
- package/dist/components/IO/ACIA.js.map +1 -1
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +5 -10
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +43 -266
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -1
- package/dist/components/IO/Empty.d.ts +1 -3
- package/dist/components/IO/Empty.js +1 -5
- package/dist/components/IO/Empty.js.map +1 -1
- package/dist/components/IO/RAMBank.d.ts +3 -4
- package/dist/components/IO/RAMBank.js +4 -13
- package/dist/components/IO/RAMBank.js.map +1 -1
- package/dist/components/IO/RTC.d.ts +2 -3
- package/dist/components/IO/RTC.js +17 -7
- package/dist/components/IO/RTC.js.map +1 -1
- package/dist/components/IO/Sound.d.ts +1 -3
- package/dist/components/IO/Sound.js +13 -23
- package/dist/components/IO/Sound.js.map +1 -1
- package/dist/components/IO/Storage.d.ts +1 -3
- package/dist/components/IO/Storage.js +1 -3
- package/dist/components/IO/Storage.js.map +1 -1
- package/dist/components/IO/VIA.d.ts +1 -3
- package/dist/components/IO/VIA.js +6 -7
- package/dist/components/IO/VIA.js.map +1 -1
- package/dist/components/IO/Video.d.ts +1 -3
- package/dist/components/IO/Video.js +3 -5
- package/dist/components/IO/Video.js.map +1 -1
- package/dist/components/IO.d.ts +1 -3
- package/dist/components/Machine.d.ts +1 -2
- package/dist/components/Machine.js +21 -74
- package/dist/components/Machine.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tests/IO/ACIA.test.js +57 -108
- package/dist/tests/IO/ACIA.test.js.map +1 -1
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +334 -574
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -1
- package/dist/tests/IO/Empty.test.js +2 -14
- package/dist/tests/IO/Empty.test.js.map +1 -1
- package/dist/tests/IO/RAMBank.test.js +5 -12
- package/dist/tests/IO/RAMBank.test.js.map +1 -1
- package/dist/tests/IO/RTC.test.js +7 -16
- package/dist/tests/IO/RTC.test.js.map +1 -1
- package/dist/tests/IO/Sound.test.js +6 -8
- package/dist/tests/IO/Sound.test.js.map +1 -1
- package/dist/tests/IO/Storage.test.js +0 -6
- package/dist/tests/IO/Storage.test.js.map +1 -1
- package/dist/tests/IO/VIA.test.js +6 -10
- package/dist/tests/IO/VIA.test.js.map +1 -1
- package/dist/tests/IO/Video.test.js +7 -7
- package/dist/tests/IO/Video.test.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CPU.ts +94 -31
- package/src/components/IO/ACIA.ts +57 -176
- package/src/components/IO/Attachments/KeyboardEncoderAttachment.ts +45 -217
- package/src/components/IO/Empty.ts +1 -4
- package/src/components/IO/RAMBank.ts +4 -15
- package/src/components/IO/RTC.ts +18 -7
- package/src/components/IO/Sound.ts +14 -27
- package/src/components/IO/Storage.ts +2 -5
- package/src/components/IO/VIA.ts +6 -8
- package/src/components/IO/Video.ts +5 -7
- package/src/components/IO.ts +1 -4
- package/src/components/Machine.ts +22 -90
- package/src/index.ts +1 -1
- package/src/tests/IO/ACIA.test.ts +60 -122
- package/src/tests/IO/Attachments/KeyboardEncoderAttachment.test.ts +342 -676
- package/src/tests/IO/Empty.test.ts +2 -17
- package/src/tests/IO/RAMBank.test.ts +5 -14
- package/src/tests/IO/RTC.test.ts +7 -20
- package/src/tests/IO/Sound.test.ts +6 -8
- package/src/tests/IO/Storage.test.ts +0 -7
- package/src/tests/IO/VIA.test.ts +6 -12
- package/src/tests/IO/Video.test.ts +7 -8
|
@@ -5,9 +5,15 @@ describe('KeyboardEncoderAttachment', () => {
|
|
|
5
5
|
|
|
6
6
|
beforeEach(() => {
|
|
7
7
|
encoder = new KeyboardEncoderAttachment(5)
|
|
8
|
+
encoder.activePort = 'both' // Enable both ports for testing
|
|
8
9
|
})
|
|
9
10
|
|
|
10
11
|
describe('Initialization', () => {
|
|
12
|
+
it('should default to Port B only', () => {
|
|
13
|
+
const fresh = new KeyboardEncoderAttachment(5)
|
|
14
|
+
expect(fresh.activePort).toBe('B')
|
|
15
|
+
})
|
|
16
|
+
|
|
11
17
|
it('should initialize with no data ready', () => {
|
|
12
18
|
expect(encoder.hasDataReadyA()).toBe(false)
|
|
13
19
|
expect(encoder.hasDataReadyB()).toBe(false)
|
|
@@ -20,7 +26,7 @@ describe('KeyboardEncoderAttachment', () => {
|
|
|
20
26
|
|
|
21
27
|
it('should be disabled by default (CA2/CB2 high)', () => {
|
|
22
28
|
encoder.updateControlLines(false, true, false, true)
|
|
23
|
-
encoder.updateKey(0x04, true) // '
|
|
29
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
24
30
|
expect(encoder.hasCA1Interrupt()).toBe(false)
|
|
25
31
|
expect(encoder.hasCB1Interrupt()).toBe(false)
|
|
26
32
|
})
|
|
@@ -32,51 +38,60 @@ describe('KeyboardEncoderAttachment', () => {
|
|
|
32
38
|
|
|
33
39
|
describe('Reset', () => {
|
|
34
40
|
it('should clear all data and states', () => {
|
|
35
|
-
// Enable and generate some data
|
|
36
41
|
encoder.updateControlLines(false, false, false, false)
|
|
37
|
-
encoder.updateKey(0x04, true) // '
|
|
42
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
38
43
|
expect(encoder.hasDataReadyA()).toBe(true)
|
|
39
44
|
|
|
40
|
-
// Reset
|
|
41
45
|
encoder.reset()
|
|
42
46
|
expect(encoder.hasDataReadyA()).toBe(false)
|
|
43
47
|
expect(encoder.hasDataReadyB()).toBe(false)
|
|
44
48
|
expect(encoder.hasCA1Interrupt()).toBe(false)
|
|
45
49
|
expect(encoder.hasCB1Interrupt()).toBe(false)
|
|
46
50
|
})
|
|
51
|
+
|
|
52
|
+
it('should clear modifier states on reset', () => {
|
|
53
|
+
encoder.updateControlLines(false, false, false, false)
|
|
54
|
+
// Press Shift, then reset
|
|
55
|
+
encoder.updateKey(0xE1, true) // Left Shift down
|
|
56
|
+
encoder.reset()
|
|
57
|
+
encoder.updateControlLines(false, false, false, false)
|
|
58
|
+
// After reset, Shift should be released - numbers produce numbers, not symbols
|
|
59
|
+
encoder.updateKey(0x1E, true) // '1'
|
|
60
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x31) // '1', not '!'
|
|
61
|
+
})
|
|
47
62
|
})
|
|
48
63
|
|
|
49
64
|
describe('Enable/Disable Control', () => {
|
|
50
65
|
it('should enable Port A when CA2 is LOW', () => {
|
|
51
66
|
encoder.updateControlLines(false, false, false, true)
|
|
52
|
-
encoder.updateKey(0x04, true) // '
|
|
67
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
53
68
|
expect(encoder.hasCA1Interrupt()).toBe(true)
|
|
54
69
|
expect(encoder.hasCB1Interrupt()).toBe(false)
|
|
55
70
|
})
|
|
56
71
|
|
|
57
72
|
it('should enable Port B when CB2 is LOW', () => {
|
|
58
73
|
encoder.updateControlLines(false, true, false, false)
|
|
59
|
-
encoder.updateKey(0x04, true) // '
|
|
74
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
60
75
|
expect(encoder.hasCA1Interrupt()).toBe(false)
|
|
61
76
|
expect(encoder.hasCB1Interrupt()).toBe(true)
|
|
62
77
|
})
|
|
63
78
|
|
|
64
79
|
it('should enable both ports when both CA2 and CB2 are LOW', () => {
|
|
65
80
|
encoder.updateControlLines(false, false, false, false)
|
|
66
|
-
encoder.updateKey(0x04, true) // '
|
|
81
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
67
82
|
expect(encoder.hasCA1Interrupt()).toBe(true)
|
|
68
83
|
expect(encoder.hasCB1Interrupt()).toBe(true)
|
|
69
84
|
})
|
|
70
85
|
|
|
71
86
|
it('should disable Port A when CA2 is HIGH', () => {
|
|
72
87
|
encoder.updateControlLines(false, true, false, false)
|
|
73
|
-
encoder.updateKey(0x04, true) // '
|
|
88
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
74
89
|
expect(encoder.hasCA1Interrupt()).toBe(false)
|
|
75
90
|
})
|
|
76
91
|
|
|
77
92
|
it('should disable Port B when CB2 is HIGH', () => {
|
|
78
93
|
encoder.updateControlLines(false, false, false, true)
|
|
79
|
-
encoder.updateKey(0x04, true) // '
|
|
94
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
80
95
|
expect(encoder.hasCB1Interrupt()).toBe(false)
|
|
81
96
|
})
|
|
82
97
|
})
|
|
@@ -94,32 +109,32 @@ describe('KeyboardEncoderAttachment', () => {
|
|
|
94
109
|
|
|
95
110
|
it('should return ASCII data when data ready on Port A', () => {
|
|
96
111
|
encoder.updateControlLines(false, false, false, false)
|
|
97
|
-
encoder.updateKey(0x04, true) // '
|
|
112
|
+
encoder.updateKey(0x04, true) // 'A' = 0x41
|
|
98
113
|
const value = encoder.readPortA(0xFF, 0x00)
|
|
99
|
-
expect(value).toBe(
|
|
114
|
+
expect(value).toBe(0x41)
|
|
100
115
|
})
|
|
101
116
|
|
|
102
117
|
it('should return ASCII data when data ready on Port B', () => {
|
|
103
118
|
encoder.updateControlLines(false, false, false, false)
|
|
104
|
-
encoder.updateKey(0x04, true) // '
|
|
119
|
+
encoder.updateKey(0x04, true) // 'A' = 0x41
|
|
105
120
|
const value = encoder.readPortB(0xFF, 0x00)
|
|
106
|
-
expect(value).toBe(
|
|
121
|
+
expect(value).toBe(0x41)
|
|
107
122
|
})
|
|
108
123
|
|
|
109
124
|
it('should return 0xFF on disabled port even with data ready', () => {
|
|
110
125
|
encoder.updateControlLines(false, true, false, true) // Both disabled
|
|
111
|
-
encoder.updateKey(0x04, true) // '
|
|
126
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
112
127
|
expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFF)
|
|
113
128
|
expect(encoder.readPortB(0xFF, 0x00)).toBe(0xFF)
|
|
114
129
|
})
|
|
115
130
|
|
|
116
131
|
it('should provide same data on both ports', () => {
|
|
117
132
|
encoder.updateControlLines(false, false, false, false)
|
|
118
|
-
encoder.updateKey(0x04, true) // '
|
|
133
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
119
134
|
const valueA = encoder.readPortA(0xFF, 0x00)
|
|
120
135
|
const valueB = encoder.readPortB(0xFF, 0x00)
|
|
121
136
|
expect(valueA).toBe(valueB)
|
|
122
|
-
expect(valueA).toBe(
|
|
137
|
+
expect(valueA).toBe(0x41)
|
|
123
138
|
})
|
|
124
139
|
})
|
|
125
140
|
|
|
@@ -127,20 +142,20 @@ describe('KeyboardEncoderAttachment', () => {
|
|
|
127
142
|
it('should trigger CA1 interrupt when Port A enabled and key pressed', () => {
|
|
128
143
|
encoder.updateControlLines(false, false, false, true)
|
|
129
144
|
expect(encoder.hasCA1Interrupt()).toBe(false)
|
|
130
|
-
encoder.updateKey(0x04, true) // '
|
|
145
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
131
146
|
expect(encoder.hasCA1Interrupt()).toBe(true)
|
|
132
147
|
})
|
|
133
148
|
|
|
134
149
|
it('should trigger CB1 interrupt when Port B enabled and key pressed', () => {
|
|
135
150
|
encoder.updateControlLines(false, true, false, false)
|
|
136
151
|
expect(encoder.hasCB1Interrupt()).toBe(false)
|
|
137
|
-
encoder.updateKey(0x04, true) // '
|
|
152
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
138
153
|
expect(encoder.hasCB1Interrupt()).toBe(true)
|
|
139
154
|
})
|
|
140
155
|
|
|
141
156
|
it('should clear CA1 interrupt and data ready when cleared', () => {
|
|
142
157
|
encoder.updateControlLines(false, false, false, false)
|
|
143
|
-
encoder.updateKey(0x04, true) // '
|
|
158
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
144
159
|
expect(encoder.hasCA1Interrupt()).toBe(true)
|
|
145
160
|
expect(encoder.hasDataReadyA()).toBe(true)
|
|
146
161
|
|
|
@@ -151,7 +166,7 @@ describe('KeyboardEncoderAttachment', () => {
|
|
|
151
166
|
|
|
152
167
|
it('should clear CB1 interrupt and data ready when cleared', () => {
|
|
153
168
|
encoder.updateControlLines(false, false, false, false)
|
|
154
|
-
encoder.updateKey(0x04, true) // '
|
|
169
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
155
170
|
expect(encoder.hasCB1Interrupt()).toBe(true)
|
|
156
171
|
expect(encoder.hasDataReadyB()).toBe(true)
|
|
157
172
|
|
|
@@ -162,380 +177,356 @@ describe('KeyboardEncoderAttachment', () => {
|
|
|
162
177
|
|
|
163
178
|
it('should not trigger interrupt when port is disabled', () => {
|
|
164
179
|
encoder.updateControlLines(false, true, false, true) // Both disabled
|
|
165
|
-
encoder.updateKey(0x04, true) // '
|
|
180
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
166
181
|
expect(encoder.hasCA1Interrupt()).toBe(false)
|
|
167
182
|
expect(encoder.hasCB1Interrupt()).toBe(false)
|
|
168
183
|
})
|
|
169
184
|
})
|
|
170
185
|
|
|
171
|
-
describe('
|
|
186
|
+
describe('Letter Key Mapping', () => {
|
|
172
187
|
beforeEach(() => {
|
|
173
188
|
encoder.updateControlLines(false, false, false, false)
|
|
174
189
|
})
|
|
175
190
|
|
|
176
|
-
it('should
|
|
177
|
-
|
|
178
|
-
|
|
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)
|
|
191
|
+
it('should output uppercase letters A-Z', () => {
|
|
192
|
+
// 'A' (HID 0x04)
|
|
193
|
+
encoder.updateKey(0x04, true)
|
|
194
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41)
|
|
201
195
|
|
|
202
196
|
encoder.clearInterrupts(true, false, true, false)
|
|
203
|
-
|
|
204
|
-
|
|
197
|
+
// 'Z' (HID 0x1D)
|
|
198
|
+
encoder.updateKey(0x1D, true)
|
|
199
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x5A)
|
|
205
200
|
})
|
|
206
201
|
|
|
207
|
-
it('should
|
|
208
|
-
encoder.updateKey(
|
|
209
|
-
|
|
202
|
+
it('should output uppercase letters even with Shift held', () => {
|
|
203
|
+
encoder.updateKey(0xE1, true) // Left Shift down
|
|
204
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
205
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41) // Still uppercase A
|
|
210
206
|
|
|
211
207
|
encoder.clearInterrupts(true, false, true, false)
|
|
212
|
-
encoder.updateKey(
|
|
213
|
-
expect(encoder.
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
it('should
|
|
217
|
-
|
|
218
|
-
|
|
208
|
+
encoder.updateKey(0x10, true) // 'M'
|
|
209
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x4D) // Still uppercase M
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should map all letters correctly', () => {
|
|
213
|
+
const letterMap: [number, number][] = [
|
|
214
|
+
[0x04, 0x41], [0x05, 0x42], [0x06, 0x43], [0x07, 0x44],
|
|
215
|
+
[0x08, 0x45], [0x09, 0x46], [0x0A, 0x47], [0x0B, 0x48],
|
|
216
|
+
[0x0C, 0x49], [0x0D, 0x4A], [0x0E, 0x4B], [0x0F, 0x4C],
|
|
217
|
+
[0x10, 0x4D], [0x11, 0x4E], [0x12, 0x4F], [0x13, 0x50],
|
|
218
|
+
[0x14, 0x51], [0x15, 0x52], [0x16, 0x53], [0x17, 0x54],
|
|
219
|
+
[0x18, 0x55], [0x19, 0x56], [0x1A, 0x57], [0x1B, 0x58],
|
|
220
|
+
[0x1C, 0x59], [0x1D, 0x5A],
|
|
221
|
+
]
|
|
222
|
+
for (const [hid, ascii] of letterMap) {
|
|
223
|
+
encoder.clearInterrupts(true, false, true, false)
|
|
224
|
+
encoder.updateKey(hid, true)
|
|
225
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(ascii)
|
|
226
|
+
}
|
|
219
227
|
})
|
|
220
228
|
})
|
|
221
229
|
|
|
222
|
-
describe('
|
|
230
|
+
describe('Number Key Mapping', () => {
|
|
223
231
|
beforeEach(() => {
|
|
224
232
|
encoder.updateControlLines(false, false, false, false)
|
|
225
233
|
})
|
|
226
234
|
|
|
227
|
-
it('should
|
|
228
|
-
encoder.updateKey(
|
|
229
|
-
expect(encoder.
|
|
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)
|
|
235
|
+
it('should map numbers correctly', () => {
|
|
236
|
+
encoder.updateKey(0x1E, true) // '1'
|
|
237
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x31)
|
|
243
238
|
|
|
244
|
-
// Release Shift
|
|
245
239
|
encoder.clearInterrupts(true, false, true, false)
|
|
246
|
-
encoder.updateKey(
|
|
247
|
-
encoder.
|
|
248
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x61)
|
|
240
|
+
encoder.updateKey(0x27, true) // '0'
|
|
241
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x30)
|
|
249
242
|
})
|
|
250
243
|
|
|
251
|
-
it('should
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
encoder.updateKey(0x04, true) // Ctrl+a
|
|
263
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x01)
|
|
244
|
+
it('should map all number keys 0-9', () => {
|
|
245
|
+
const numberMap: [number, number][] = [
|
|
246
|
+
[0x1E, 0x31], [0x1F, 0x32], [0x20, 0x33], [0x21, 0x34],
|
|
247
|
+
[0x22, 0x35], [0x23, 0x36], [0x24, 0x37], [0x25, 0x38],
|
|
248
|
+
[0x26, 0x39], [0x27, 0x30],
|
|
249
|
+
]
|
|
250
|
+
for (const [hid, ascii] of numberMap) {
|
|
251
|
+
encoder.clearInterrupts(true, false, true, false)
|
|
252
|
+
encoder.updateKey(hid, true)
|
|
253
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(ascii)
|
|
254
|
+
}
|
|
264
255
|
})
|
|
265
256
|
})
|
|
266
257
|
|
|
267
|
-
describe('
|
|
258
|
+
describe('Special Key Mapping', () => {
|
|
268
259
|
beforeEach(() => {
|
|
269
260
|
encoder.updateControlLines(false, false, false, false)
|
|
270
261
|
})
|
|
271
262
|
|
|
272
|
-
it('should map
|
|
273
|
-
encoder.updateKey(
|
|
274
|
-
encoder.
|
|
275
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41)
|
|
263
|
+
it('should map Enter to CR (0x0D)', () => {
|
|
264
|
+
encoder.updateKey(0x28, true)
|
|
265
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x0D)
|
|
276
266
|
})
|
|
277
267
|
|
|
278
|
-
it('should map
|
|
279
|
-
encoder.updateKey(
|
|
280
|
-
encoder.
|
|
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)
|
|
268
|
+
it('should map Escape to ESC (0x1B)', () => {
|
|
269
|
+
encoder.updateKey(0x29, true)
|
|
270
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1B)
|
|
286
271
|
})
|
|
287
272
|
|
|
288
|
-
it('should map
|
|
289
|
-
encoder.updateKey(
|
|
290
|
-
|
|
291
|
-
|
|
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)
|
|
273
|
+
it('should map Backspace to BS (0x08)', () => {
|
|
274
|
+
encoder.updateKey(0x2A, true)
|
|
275
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x08)
|
|
276
|
+
})
|
|
297
277
|
|
|
298
|
-
|
|
299
|
-
encoder.updateKey(
|
|
300
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(
|
|
278
|
+
it('should map Tab to HT (0x09)', () => {
|
|
279
|
+
encoder.updateKey(0x2B, true)
|
|
280
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x09)
|
|
301
281
|
})
|
|
302
|
-
})
|
|
303
282
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
encoder.
|
|
283
|
+
it('should map Space to SP (0x20)', () => {
|
|
284
|
+
encoder.updateKey(0x2C, true)
|
|
285
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x20)
|
|
307
286
|
})
|
|
308
287
|
|
|
309
|
-
it('should map
|
|
310
|
-
encoder.updateKey(
|
|
311
|
-
encoder.
|
|
312
|
-
|
|
288
|
+
it('should map Delete to DEL (0x7F)', () => {
|
|
289
|
+
encoder.updateKey(0x4C, true)
|
|
290
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x7F)
|
|
291
|
+
})
|
|
313
292
|
|
|
314
|
-
|
|
315
|
-
encoder.updateKey(
|
|
293
|
+
it('should map Insert to SUB (0x1A)', () => {
|
|
294
|
+
encoder.updateKey(0x49, true)
|
|
316
295
|
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1A)
|
|
317
296
|
})
|
|
318
297
|
|
|
319
|
-
it('should map
|
|
320
|
-
encoder.updateKey(
|
|
321
|
-
|
|
322
|
-
encoder.updateKey(0x2F, true) // Ctrl+[ -> ESC (0x1B)
|
|
323
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1B)
|
|
298
|
+
it('should map arrow keys correctly', () => {
|
|
299
|
+
encoder.updateKey(0x4F, true) // Right Arrow
|
|
300
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1D)
|
|
324
301
|
|
|
325
302
|
encoder.clearInterrupts(true, false, true, false)
|
|
326
|
-
encoder.updateKey(
|
|
303
|
+
encoder.updateKey(0x50, true) // Left Arrow
|
|
327
304
|
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1C)
|
|
328
305
|
|
|
329
306
|
encoder.clearInterrupts(true, false, true, false)
|
|
330
|
-
encoder.updateKey(
|
|
331
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(
|
|
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
|
-
})
|
|
307
|
+
encoder.updateKey(0x51, true) // Down Arrow
|
|
308
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1F)
|
|
339
309
|
|
|
340
|
-
|
|
341
|
-
encoder.updateKey(
|
|
342
|
-
encoder.updateKey(0x23, true) // Ctrl+6 -> 0x1E
|
|
310
|
+
encoder.clearInterrupts(true, false, true, false)
|
|
311
|
+
encoder.updateKey(0x52, true) // Up Arrow
|
|
343
312
|
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1E)
|
|
344
313
|
})
|
|
345
314
|
|
|
346
|
-
it('should map
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
315
|
+
it('should map symbol keys correctly', () => {
|
|
316
|
+
const symbolMap: [number, number][] = [
|
|
317
|
+
[0x2D, 0x2D], // -
|
|
318
|
+
[0x2E, 0x3D], // =
|
|
319
|
+
[0x2F, 0x5B], // [
|
|
320
|
+
[0x30, 0x5D], // ]
|
|
321
|
+
[0x31, 0x5C], // backslash
|
|
322
|
+
[0x33, 0x3B], // ;
|
|
323
|
+
[0x34, 0x27], // '
|
|
324
|
+
[0x35, 0x60], // `
|
|
325
|
+
[0x36, 0x2C], // ,
|
|
326
|
+
[0x37, 0x2E], // .
|
|
327
|
+
[0x38, 0x2F], // /
|
|
328
|
+
]
|
|
329
|
+
for (const [hid, ascii] of symbolMap) {
|
|
330
|
+
encoder.clearInterrupts(true, false, true, false)
|
|
331
|
+
encoder.updateKey(hid, true)
|
|
332
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(ascii)
|
|
333
|
+
}
|
|
350
334
|
})
|
|
351
335
|
})
|
|
352
336
|
|
|
353
|
-
describe('
|
|
337
|
+
describe('Shift Key Mapping', () => {
|
|
354
338
|
beforeEach(() => {
|
|
355
339
|
encoder.updateControlLines(false, false, false, false)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
340
|
+
encoder.updateKey(0xE1, true) // Left Shift down
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
it('should produce shifted number symbols', () => {
|
|
344
|
+
const shiftNumberMap: [number, number][] = [
|
|
345
|
+
[0x1E, 0x21], // '1' -> '!'
|
|
346
|
+
[0x1F, 0x40], // '2' -> '@'
|
|
347
|
+
[0x20, 0x23], // '3' -> '#'
|
|
348
|
+
[0x21, 0x24], // '4' -> '$'
|
|
349
|
+
[0x22, 0x25], // '5' -> '%'
|
|
350
|
+
[0x23, 0x5E], // '6' -> '^'
|
|
351
|
+
[0x24, 0x26], // '7' -> '&'
|
|
352
|
+
[0x25, 0x2A], // '8' -> '*'
|
|
353
|
+
[0x26, 0x28], // '9' -> '('
|
|
354
|
+
[0x27, 0x29], // '0' -> ')'
|
|
355
|
+
]
|
|
356
|
+
for (const [hid, ascii] of shiftNumberMap) {
|
|
357
|
+
encoder.clearInterrupts(true, false, true, false)
|
|
358
|
+
encoder.updateKey(hid, true)
|
|
359
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(ascii)
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
it('should produce shifted symbol keys', () => {
|
|
364
|
+
const shiftSymbolMap: [number, number][] = [
|
|
365
|
+
[0x2D, 0x5F], // '-' -> '_'
|
|
366
|
+
[0x2E, 0x2B], // '=' -> '+'
|
|
367
|
+
[0x2F, 0x7B], // '[' -> '{'
|
|
368
|
+
[0x30, 0x7D], // ']' -> '}'
|
|
369
|
+
[0x31, 0x7C], // '\\' -> '|'
|
|
370
|
+
[0x33, 0x3A], // ';' -> ':'
|
|
371
|
+
[0x34, 0x22], // '\'' -> '"'
|
|
372
|
+
[0x36, 0x3C], // ',' -> '<'
|
|
373
|
+
[0x37, 0x3E], // '.' -> '>'
|
|
374
|
+
[0x38, 0x3F], // '/' -> '?'
|
|
375
|
+
[0x35, 0x7E], // '`' -> '~'
|
|
376
|
+
]
|
|
377
|
+
for (const [hid, ascii] of shiftSymbolMap) {
|
|
378
|
+
encoder.clearInterrupts(true, false, true, false)
|
|
379
|
+
encoder.updateKey(hid, true)
|
|
380
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(ascii)
|
|
381
|
+
}
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('should not change letter output when Shift is held (already uppercase)', () => {
|
|
385
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
386
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41) // Still 'A'
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('should track right Shift the same as left Shift', () => {
|
|
390
|
+
encoder.updateKey(0xE1, false) // Release left Shift
|
|
391
|
+
encoder.updateKey(0xE5, true) // Right Shift down
|
|
392
|
+
encoder.updateKey(0x1E, true) // '1'
|
|
393
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x21) // '!'
|
|
372
394
|
})
|
|
373
395
|
})
|
|
374
396
|
|
|
375
|
-
describe('
|
|
397
|
+
describe('Ctrl Key Mapping', () => {
|
|
376
398
|
beforeEach(() => {
|
|
377
399
|
encoder.updateControlLines(false, false, false, false)
|
|
400
|
+
encoder.updateKey(0xE0, true) // Left Ctrl down
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
it('should produce control codes for Ctrl+A through Ctrl+Z', () => {
|
|
404
|
+
const ctrlLetterMap: [number, number][] = [
|
|
405
|
+
[0x04, 0x01], // Ctrl+A
|
|
406
|
+
[0x05, 0x02], // Ctrl+B
|
|
407
|
+
[0x06, 0x03], // Ctrl+C
|
|
408
|
+
[0x07, 0x04], // Ctrl+D
|
|
409
|
+
[0x08, 0x05], // Ctrl+E
|
|
410
|
+
[0x09, 0x06], // Ctrl+F
|
|
411
|
+
[0x0A, 0x07], // Ctrl+G
|
|
412
|
+
[0x0B, 0x08], // Ctrl+H
|
|
413
|
+
[0x0C, 0x09], // Ctrl+I
|
|
414
|
+
[0x0D, 0x0A], // Ctrl+J
|
|
415
|
+
[0x0E, 0x0B], // Ctrl+K
|
|
416
|
+
[0x0F, 0x0C], // Ctrl+L
|
|
417
|
+
[0x10, 0x0D], // Ctrl+M
|
|
418
|
+
[0x11, 0x0E], // Ctrl+N
|
|
419
|
+
[0x12, 0x0F], // Ctrl+O
|
|
420
|
+
[0x13, 0x10], // Ctrl+P
|
|
421
|
+
[0x14, 0x11], // Ctrl+Q
|
|
422
|
+
[0x15, 0x12], // Ctrl+R
|
|
423
|
+
[0x16, 0x13], // Ctrl+S
|
|
424
|
+
[0x17, 0x14], // Ctrl+T
|
|
425
|
+
[0x18, 0x15], // Ctrl+U
|
|
426
|
+
[0x19, 0x16], // Ctrl+V
|
|
427
|
+
[0x1A, 0x17], // Ctrl+W
|
|
428
|
+
[0x1B, 0x18], // Ctrl+X
|
|
429
|
+
[0x1C, 0x19], // Ctrl+Y
|
|
430
|
+
[0x1D, 0x1A], // Ctrl+Z
|
|
431
|
+
]
|
|
432
|
+
for (const [hid, ascii] of ctrlLetterMap) {
|
|
433
|
+
encoder.clearInterrupts(true, false, true, false)
|
|
434
|
+
encoder.updateKey(hid, true)
|
|
435
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(ascii)
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
it('should produce Ctrl+C = 0x03 (BASIC break)', () => {
|
|
440
|
+
encoder.updateKey(0x06, true) // 'C'
|
|
441
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x03)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it('should produce Ctrl+2 = NUL (0x00)', () => {
|
|
445
|
+
encoder.updateKey(0x1F, true) // '2'
|
|
446
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x00)
|
|
378
447
|
})
|
|
379
448
|
|
|
380
|
-
it('should
|
|
381
|
-
encoder.updateKey(
|
|
382
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(
|
|
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)
|
|
449
|
+
it('should produce Ctrl+6 = RS (0x1E)', () => {
|
|
450
|
+
encoder.updateKey(0x23, true) // '6'
|
|
451
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1E)
|
|
422
452
|
})
|
|
423
|
-
})
|
|
424
453
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
encoder.
|
|
454
|
+
it('should produce Ctrl+- = US (0x1F)', () => {
|
|
455
|
+
encoder.updateKey(0x2D, true) // '-'
|
|
456
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1F)
|
|
428
457
|
})
|
|
429
458
|
|
|
430
|
-
it('should
|
|
431
|
-
encoder.updateKey(
|
|
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)
|
|
459
|
+
it('should produce Ctrl+[ = ESC (0x1B)', () => {
|
|
460
|
+
encoder.updateKey(0x2F, true) // '['
|
|
461
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1B)
|
|
439
462
|
})
|
|
440
463
|
|
|
441
|
-
it('should
|
|
442
|
-
encoder.updateKey(
|
|
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)
|
|
464
|
+
it('should produce Ctrl+\\ = FS (0x1C)', () => {
|
|
465
|
+
encoder.updateKey(0x31, true) // '\\'
|
|
466
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1C)
|
|
450
467
|
})
|
|
451
468
|
|
|
452
|
-
it('should
|
|
453
|
-
encoder.updateKey(
|
|
454
|
-
encoder.
|
|
455
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(0xA0)
|
|
469
|
+
it('should produce Ctrl+] = GS (0x1D)', () => {
|
|
470
|
+
encoder.updateKey(0x30, true) // ']'
|
|
471
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x1D)
|
|
456
472
|
})
|
|
457
473
|
|
|
458
|
-
it('should
|
|
459
|
-
encoder.updateKey(
|
|
460
|
-
encoder.updateKey(
|
|
461
|
-
|
|
474
|
+
it('should track right Ctrl the same as left Ctrl', () => {
|
|
475
|
+
encoder.updateKey(0xE0, false) // Release left Ctrl
|
|
476
|
+
encoder.updateKey(0xE4, true) // Right Ctrl down
|
|
477
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
478
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x01) // Ctrl+A
|
|
462
479
|
})
|
|
463
480
|
})
|
|
464
481
|
|
|
465
|
-
describe('
|
|
482
|
+
describe('Modifier Key Behavior', () => {
|
|
466
483
|
beforeEach(() => {
|
|
467
484
|
encoder.updateControlLines(false, false, false, false)
|
|
468
485
|
})
|
|
469
486
|
|
|
470
|
-
it('should
|
|
471
|
-
encoder.updateKey(
|
|
472
|
-
encoder.
|
|
473
|
-
|
|
474
|
-
encoder.
|
|
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)
|
|
487
|
+
it('should not generate output for Ctrl press/release alone', () => {
|
|
488
|
+
encoder.updateKey(0xE0, true) // Left Ctrl down
|
|
489
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
490
|
+
encoder.updateKey(0xE0, false) // Left Ctrl up
|
|
491
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
480
492
|
})
|
|
481
493
|
|
|
482
|
-
it('should
|
|
483
|
-
encoder.updateKey(
|
|
484
|
-
encoder.
|
|
485
|
-
|
|
486
|
-
encoder.
|
|
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)
|
|
494
|
+
it('should not generate output for Shift press/release alone', () => {
|
|
495
|
+
encoder.updateKey(0xE1, true) // Left Shift down
|
|
496
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
497
|
+
encoder.updateKey(0xE1, false) // Left Shift up
|
|
498
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
492
499
|
})
|
|
493
500
|
|
|
494
|
-
it('should
|
|
495
|
-
encoder.updateKey(
|
|
496
|
-
encoder.
|
|
497
|
-
|
|
498
|
-
encoder.updateKey(0x2D, true) // Alt+Shift+- -> 0xDF
|
|
499
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(0xDF)
|
|
500
|
-
|
|
501
|
+
it('should not generate output for key release events', () => {
|
|
502
|
+
encoder.updateKey(0x04, true) // 'A' pressed
|
|
503
|
+
expect(encoder.hasDataReadyA()).toBe(true)
|
|
501
504
|
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
505
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
encoder.updateControlLines(false, false, false, false)
|
|
506
|
+
encoder.updateKey(0x04, false) // 'A' released
|
|
507
|
+
expect(encoder.hasDataReadyA()).toBe(false) // No new data from release
|
|
510
508
|
})
|
|
511
509
|
|
|
512
|
-
it('should
|
|
513
|
-
encoder.updateKey(
|
|
514
|
-
encoder.
|
|
515
|
-
|
|
510
|
+
it('should ignore Alt key (no output, no modifier effect)', () => {
|
|
511
|
+
encoder.updateKey(0xE2, true) // Left Alt down - ignored
|
|
512
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
513
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
514
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41) // Normal 'A', no alt mapping
|
|
516
515
|
})
|
|
517
516
|
|
|
518
|
-
it('should
|
|
519
|
-
encoder.updateKey(
|
|
520
|
-
encoder.
|
|
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
|
|
517
|
+
it('should ignore Caps Lock key', () => {
|
|
518
|
+
encoder.updateKey(0x39, true) // Caps Lock - ignored
|
|
519
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
524
520
|
})
|
|
525
521
|
|
|
526
|
-
it('should
|
|
527
|
-
encoder.updateKey(
|
|
528
|
-
encoder.
|
|
529
|
-
encoder.updateKey(0x04, true) // a
|
|
530
|
-
expect(encoder.readPortA(0xFF, 0x00)).toBe(0xC1) // Alt+Shift+a, not Alt+a (0xE1)
|
|
522
|
+
it('should ignore GUI/MENU keys', () => {
|
|
523
|
+
encoder.updateKey(0xE3, true) // Left GUI - ignored (no mapping in table)
|
|
524
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
531
525
|
})
|
|
532
526
|
|
|
533
|
-
it('should
|
|
534
|
-
encoder.updateKey(
|
|
535
|
-
encoder.
|
|
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
|
|
527
|
+
it('should ignore unrecognized keycodes', () => {
|
|
528
|
+
encoder.updateKey(0x3A, true) // F1 - no mapping
|
|
529
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
539
530
|
})
|
|
540
531
|
})
|
|
541
532
|
|
|
@@ -544,418 +535,93 @@ describe('KeyboardEncoderAttachment', () => {
|
|
|
544
535
|
encoder.updateControlLines(false, false, false, false)
|
|
545
536
|
})
|
|
546
537
|
|
|
547
|
-
it('should
|
|
548
|
-
encoder.updateKey(0x04, true) // '
|
|
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'
|
|
538
|
+
it('should overwrite data with new key press', () => {
|
|
539
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
580
540
|
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
541
|
|
|
594
|
-
|
|
595
|
-
encoder.
|
|
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
|
|
542
|
+
encoder.updateKey(0x05, true) // 'B' overwrites without clearing
|
|
543
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x42)
|
|
601
544
|
})
|
|
602
545
|
|
|
603
|
-
it('should handle
|
|
604
|
-
encoder.updateKey(0x04, true) // '
|
|
605
|
-
encoder.
|
|
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(() => {
|
|
546
|
+
it('should handle read-clear-press cycle', () => {
|
|
547
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
548
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41)
|
|
622
549
|
encoder.clearInterrupts(true, false, true, false)
|
|
623
|
-
encoder.
|
|
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
|
-
})
|
|
550
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
751
551
|
|
|
752
|
-
|
|
753
|
-
encoder.
|
|
754
|
-
expect(encoder.
|
|
552
|
+
encoder.updateKey(0x05, true) // 'B'
|
|
553
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x42)
|
|
554
|
+
expect(encoder.hasDataReadyA()).toBe(true)
|
|
755
555
|
})
|
|
756
556
|
|
|
757
|
-
it('should
|
|
758
|
-
encoder.updateKey(
|
|
759
|
-
|
|
760
|
-
|
|
557
|
+
it('should handle modifier press then key press', () => {
|
|
558
|
+
encoder.updateKey(0xE0, true) // Ctrl down
|
|
559
|
+
encoder.updateKey(0x06, true) // 'C' -> Ctrl+C = 0x03
|
|
560
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x03)
|
|
761
561
|
|
|
762
|
-
|
|
763
|
-
encoder.updateKey(
|
|
764
|
-
|
|
562
|
+
encoder.clearInterrupts(true, false, true, false)
|
|
563
|
+
encoder.updateKey(0xE0, false) // Ctrl up
|
|
564
|
+
encoder.updateKey(0x06, true) // 'C' -> normal 'C' = 0x43
|
|
565
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x43)
|
|
765
566
|
})
|
|
766
567
|
})
|
|
767
568
|
|
|
768
|
-
describe('
|
|
569
|
+
describe('Active Port Selection', () => {
|
|
769
570
|
beforeEach(() => {
|
|
770
571
|
encoder.updateControlLines(false, false, false, false)
|
|
771
|
-
encoder.updateKey(0xE2, true) // Press Alt
|
|
772
572
|
})
|
|
773
573
|
|
|
774
|
-
|
|
775
|
-
encoder.
|
|
776
|
-
encoder.updateKey(
|
|
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)
|
|
574
|
+
it('should default to both ports active', () => {
|
|
575
|
+
expect(encoder.activePort).toBe('both')
|
|
576
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
577
|
+
expect(encoder.hasDataReadyA()).toBe(true)
|
|
578
|
+
expect(encoder.hasDataReadyB()).toBe(true)
|
|
822
579
|
})
|
|
823
580
|
|
|
824
|
-
it('should
|
|
825
|
-
encoder.
|
|
826
|
-
|
|
581
|
+
it('should only update Port A when activePort is A', () => {
|
|
582
|
+
encoder.activePort = 'A'
|
|
583
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
584
|
+
expect(encoder.hasDataReadyA()).toBe(true)
|
|
585
|
+
expect(encoder.hasDataReadyB()).toBe(false)
|
|
827
586
|
})
|
|
828
587
|
|
|
829
|
-
it('should
|
|
830
|
-
encoder.
|
|
831
|
-
|
|
588
|
+
it('should only update Port B when activePort is B', () => {
|
|
589
|
+
encoder.activePort = 'B'
|
|
590
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
591
|
+
expect(encoder.hasDataReadyA()).toBe(false)
|
|
592
|
+
expect(encoder.hasDataReadyB()).toBe(true)
|
|
832
593
|
})
|
|
833
594
|
})
|
|
834
595
|
|
|
835
|
-
describe('
|
|
596
|
+
describe('Edge Cases', () => {
|
|
836
597
|
beforeEach(() => {
|
|
837
598
|
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
599
|
})
|
|
910
600
|
|
|
911
|
-
it('should
|
|
912
|
-
encoder.updateKey(
|
|
913
|
-
expect(encoder.
|
|
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
|
-
})
|
|
601
|
+
it('should handle port disable mid-operation', () => {
|
|
602
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
603
|
+
expect(encoder.hasCA1Interrupt()).toBe(true)
|
|
945
604
|
|
|
946
|
-
|
|
947
|
-
encoder.
|
|
948
|
-
expect(encoder.
|
|
605
|
+
// Disable Port A
|
|
606
|
+
encoder.updateControlLines(false, true, false, false)
|
|
607
|
+
expect(encoder.hasCA1Interrupt()).toBe(false)
|
|
608
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0xFF) // Disabled, returns 0xFF
|
|
949
609
|
})
|
|
950
610
|
|
|
951
|
-
it('should
|
|
952
|
-
encoder.updateKey(
|
|
953
|
-
|
|
611
|
+
it('should allow re-enabling port and reading existing data', () => {
|
|
612
|
+
encoder.updateKey(0x04, true) // 'A'
|
|
613
|
+
encoder.updateControlLines(false, true, false, true) // Disable both
|
|
614
|
+
encoder.updateControlLines(false, false, false, false) // Re-enable both
|
|
615
|
+
// Data should still be readable (dataReady is still true, just interrupt was gated)
|
|
616
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x41)
|
|
954
617
|
})
|
|
955
618
|
|
|
956
|
-
it('should
|
|
957
|
-
encoder.updateKey(
|
|
958
|
-
|
|
619
|
+
it('should handle Ctrl+2 producing NUL (0x00) correctly', () => {
|
|
620
|
+
encoder.updateKey(0xE0, true) // Ctrl down
|
|
621
|
+
encoder.updateKey(0x1F, true) // '2'
|
|
622
|
+
// Should produce 0x00 and data should be ready
|
|
623
|
+
expect(encoder.hasDataReadyA()).toBe(true)
|
|
624
|
+
expect(encoder.readPortA(0xFF, 0x00)).toBe(0x00)
|
|
959
625
|
})
|
|
960
626
|
})
|
|
961
627
|
})
|