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