ac6502 1.3.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +139 -32
  2. package/dist/components/IO/ACIA.d.ts +76 -0
  3. package/dist/components/IO/ACIA.js +282 -0
  4. package/dist/components/IO/ACIA.js.map +1 -0
  5. package/dist/components/IO/Attachments/Attachment.d.ts +112 -0
  6. package/dist/components/IO/Attachments/Attachment.js +71 -0
  7. package/dist/components/IO/Attachments/Attachment.js.map +1 -0
  8. package/dist/components/IO/Attachments/JoystickAttachment.d.ts +53 -0
  9. package/dist/components/IO/Attachments/JoystickAttachment.js +90 -0
  10. package/dist/components/IO/Attachments/JoystickAttachment.js.map +1 -0
  11. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +63 -0
  12. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +489 -0
  13. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -0
  14. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.d.ts +44 -0
  15. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js +274 -0
  16. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js.map +1 -0
  17. package/dist/components/IO/Attachments/KeypadAttachment.d.ts +47 -0
  18. package/dist/components/IO/Attachments/KeypadAttachment.js +141 -0
  19. package/dist/components/IO/Attachments/KeypadAttachment.js.map +1 -0
  20. package/dist/components/IO/Attachments/LCDAttachment.d.ts +110 -0
  21. package/dist/components/IO/Attachments/LCDAttachment.js +716 -0
  22. package/dist/components/IO/Attachments/LCDAttachment.js.map +1 -0
  23. package/dist/components/IO/Attachments/SNESAttachment.d.ts +85 -0
  24. package/dist/components/IO/Attachments/SNESAttachment.js +184 -0
  25. package/dist/components/IO/Attachments/SNESAttachment.js.map +1 -0
  26. package/dist/components/IO/Empty.d.ts +9 -0
  27. package/dist/components/IO/Empty.js +5 -7
  28. package/dist/components/IO/Empty.js.map +1 -1
  29. package/dist/components/IO/GPIOCard.d.ts +5 -5
  30. package/dist/components/IO/GPIOCard.js.map +1 -1
  31. package/dist/components/IO/RAMBank.d.ts +37 -0
  32. package/dist/components/IO/RAMBank.js +63 -0
  33. package/dist/components/IO/RAMBank.js.map +1 -0
  34. package/dist/components/IO/RTC.d.ts +107 -0
  35. package/dist/components/IO/RTC.js +483 -0
  36. package/dist/components/IO/RTC.js.map +1 -0
  37. package/dist/components/IO/Sound.d.ts +120 -0
  38. package/dist/components/IO/Sound.js +622 -0
  39. package/dist/components/IO/Sound.js.map +1 -0
  40. package/dist/components/IO/Storage.d.ts +74 -0
  41. package/dist/components/IO/Storage.js +409 -0
  42. package/dist/components/IO/Storage.js.map +1 -0
  43. package/dist/components/IO/Terminal.d.ts +19 -0
  44. package/dist/components/IO/Terminal.js +33 -0
  45. package/dist/components/IO/Terminal.js.map +1 -0
  46. package/dist/components/IO/VIA.d.ts +105 -0
  47. package/dist/components/IO/VIA.js +597 -0
  48. package/dist/components/IO/VIA.js.map +1 -0
  49. package/dist/components/IO/Video.d.ts +141 -0
  50. package/dist/components/IO/Video.js +630 -0
  51. package/dist/components/IO/Video.js.map +1 -0
  52. package/dist/components/Machine.d.ts +20 -24
  53. package/dist/components/Machine.js +249 -166
  54. package/dist/components/Machine.js.map +1 -1
  55. package/dist/index.js +28 -14
  56. package/dist/index.js.map +1 -1
  57. package/dist/lib.d.ts +16 -16
  58. package/dist/lib.js +32 -32
  59. package/dist/lib.js.map +1 -1
  60. package/dist/tests/IO/ACIA.test.d.ts +1 -0
  61. package/dist/tests/IO/ACIA.test.js +423 -0
  62. package/dist/tests/IO/ACIA.test.js.map +1 -0
  63. package/dist/tests/IO/Attachments/Attachment.test.d.ts +1 -0
  64. package/dist/tests/IO/Attachments/Attachment.test.js +339 -0
  65. package/dist/tests/IO/Attachments/Attachment.test.js.map +1 -0
  66. package/dist/tests/IO/Attachments/JoystickAttachment.test.d.ts +1 -0
  67. package/dist/tests/IO/Attachments/JoystickAttachment.test.js +126 -0
  68. package/dist/tests/IO/Attachments/JoystickAttachment.test.js.map +1 -0
  69. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.d.ts +1 -0
  70. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +779 -0
  71. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -0
  72. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.d.ts +1 -0
  73. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js +355 -0
  74. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js.map +1 -0
  75. package/dist/tests/IO/Attachments/KeypadAttachment.test.d.ts +1 -0
  76. package/dist/tests/IO/Attachments/KeypadAttachment.test.js +323 -0
  77. package/dist/tests/IO/Attachments/KeypadAttachment.test.js.map +1 -0
  78. package/dist/tests/IO/Attachments/LCDAttachment.test.d.ts +1 -0
  79. package/dist/tests/IO/Attachments/LCDAttachment.test.js +627 -0
  80. package/dist/tests/IO/Attachments/LCDAttachment.test.js.map +1 -0
  81. package/dist/tests/IO/Attachments/SNESAttachment.test.d.ts +1 -0
  82. package/dist/tests/IO/Attachments/SNESAttachment.test.js +331 -0
  83. package/dist/tests/IO/Attachments/SNESAttachment.test.js.map +1 -0
  84. package/dist/tests/IO/Empty.test.d.ts +1 -0
  85. package/dist/tests/IO/Empty.test.js +121 -0
  86. package/dist/tests/IO/Empty.test.js.map +1 -0
  87. package/dist/tests/IO/GPIOCard.test.js.map +1 -1
  88. package/dist/tests/IO/RAMBank.test.d.ts +1 -0
  89. package/dist/tests/IO/RAMBank.test.js +229 -0
  90. package/dist/tests/IO/RAMBank.test.js.map +1 -0
  91. package/dist/tests/IO/RTC.test.d.ts +1 -0
  92. package/dist/tests/IO/RTC.test.js +177 -0
  93. package/dist/tests/IO/RTC.test.js.map +1 -0
  94. package/dist/tests/IO/Sound.test.d.ts +1 -0
  95. package/dist/tests/IO/Sound.test.js +528 -0
  96. package/dist/tests/IO/Sound.test.js.map +1 -0
  97. package/dist/tests/IO/Storage.test.d.ts +1 -0
  98. package/dist/tests/IO/Storage.test.js +656 -0
  99. package/dist/tests/IO/Storage.test.js.map +1 -0
  100. package/dist/tests/IO/VIA.test.d.ts +1 -0
  101. package/dist/tests/IO/VIA.test.js +503 -0
  102. package/dist/tests/IO/VIA.test.js.map +1 -0
  103. package/dist/tests/IO/Video.test.d.ts +1 -0
  104. package/dist/tests/IO/Video.test.js +549 -0
  105. package/dist/tests/IO/Video.test.js.map +1 -0
  106. package/dist/tests/Machine.test.js +27 -42
  107. package/dist/tests/Machine.test.js.map +1 -1
  108. package/package.json +1 -1
  109. package/src/components/IO/{SerialCard.ts → ACIA.ts} +2 -2
  110. package/src/components/IO/{GPIOAttachments/GPIOAttachment.ts → Attachments/Attachment.ts} +2 -2
  111. package/src/components/IO/{GPIOAttachments/GPIOJoystickAttachment.ts → Attachments/JoystickAttachment.ts} +3 -3
  112. package/src/components/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.ts → Attachments/KeyboardEncoderAttachment.ts} +3 -3
  113. package/src/components/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.ts → Attachments/KeyboardMatrixAttachment.ts} +5 -5
  114. package/src/components/IO/{GPIOAttachments/GPIOKeypadAttachment.ts → Attachments/KeypadAttachment.ts} +3 -3
  115. package/src/components/IO/{GPIOAttachments/GPIOLCDAttachment.ts → Attachments/LCDAttachment.ts} +7 -7
  116. package/src/components/IO/{EmptyCard.ts → Empty.ts} +1 -1
  117. package/src/components/IO/{RAMCard.ts → RAMBank.ts} +8 -8
  118. package/src/components/IO/{RTCCard.ts → RTC.ts} +1 -1
  119. package/src/components/IO/{SoundCard.ts → Sound.ts} +2 -2
  120. package/src/components/IO/{StorageCard.ts → Storage.ts} +70 -73
  121. package/src/components/IO/{DevOutputBoard.ts → Terminal.ts} +2 -2
  122. package/src/components/IO/{GPIOCard.ts → VIA.ts} +64 -64
  123. package/src/components/IO/{VideoCard.ts → Video.ts} +1 -1
  124. package/src/components/Machine.ts +276 -176
  125. package/src/index.ts +34 -21
  126. package/src/lib.ts +16 -16
  127. package/src/tests/IO/{SerialCard.test.ts → ACIA.test.ts} +5 -5
  128. package/src/tests/IO/{GPIOAttachments/GPIOAttachment.test.ts → Attachments/Attachment.test.ts} +12 -12
  129. package/src/tests/IO/{GPIOAttachments/GPIOJoystickAttachment.test.ts → Attachments/JoystickAttachment.test.ts} +23 -23
  130. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts → Attachments/KeyboardEncoderAttachment.test.ts} +4 -4
  131. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts → Attachments/KeyboardMatrixAttachment.test.ts} +5 -5
  132. package/src/tests/IO/{GPIOAttachments/GPIOKeypadAttachment.test.ts → Attachments/KeypadAttachment.test.ts} +38 -38
  133. package/src/tests/IO/{GPIOAttachments/GPIOLCDAttachment.test.ts → Attachments/LCDAttachment.test.ts} +12 -12
  134. package/src/tests/IO/Empty.test.ts +143 -0
  135. package/src/tests/IO/{RAMCard.test.ts → RAMBank.test.ts} +33 -33
  136. package/src/tests/IO/{RTCCard.test.ts → RTC.test.ts} +6 -6
  137. package/src/tests/IO/{SoundCard.test.ts → Sound.test.ts} +6 -6
  138. package/src/tests/IO/{StorageCard.test.ts → Storage.test.ts} +34 -25
  139. package/src/tests/IO/{GPIOCard.test.ts → VIA.test.ts} +7 -7
  140. package/src/tests/IO/{VideoCard.test.ts → Video.test.ts} +13 -13
  141. package/src/tests/Machine.test.ts +31 -38
@@ -0,0 +1,274 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KeyboardMatrixAttachment = void 0;
4
+ const Attachment_1 = require("./Attachment");
5
+ /**
6
+ * USB HID Keycode to ASCII mapping table
7
+ * Maps USB HID usage IDs to ASCII characters
8
+ */
9
+ const USB_HID_TO_ASCII = {
10
+ 0x04: 0x61, // a
11
+ 0x05: 0x62, // b
12
+ 0x06: 0x63, // c
13
+ 0x07: 0x64, // d
14
+ 0x08: 0x65, // e
15
+ 0x09: 0x66, // f
16
+ 0x0A: 0x67, // g
17
+ 0x0B: 0x68, // h
18
+ 0x0C: 0x69, // i
19
+ 0x0D: 0x6A, // j
20
+ 0x0E: 0x6B, // k
21
+ 0x0F: 0x6C, // l
22
+ 0x10: 0x6D, // m
23
+ 0x11: 0x6E, // n
24
+ 0x12: 0x6F, // o
25
+ 0x13: 0x70, // p
26
+ 0x14: 0x71, // q
27
+ 0x15: 0x72, // r
28
+ 0x16: 0x73, // s
29
+ 0x17: 0x74, // t
30
+ 0x18: 0x75, // u
31
+ 0x19: 0x76, // v
32
+ 0x1A: 0x77, // w
33
+ 0x1B: 0x78, // x
34
+ 0x1C: 0x79, // y
35
+ 0x1D: 0x7A, // z
36
+ 0x1E: 0x31, // 1
37
+ 0x1F: 0x32, // 2
38
+ 0x20: 0x33, // 3
39
+ 0x21: 0x34, // 4
40
+ 0x22: 0x35, // 5
41
+ 0x23: 0x36, // 6
42
+ 0x24: 0x37, // 7
43
+ 0x25: 0x38, // 8
44
+ 0x26: 0x39, // 9
45
+ 0x27: 0x30, // 0
46
+ 0x28: 0x0D, // Enter
47
+ 0x29: 0x1B, // Escape
48
+ 0x2A: 0x08, // Backspace
49
+ 0x2B: 0x09, // Tab
50
+ 0x2C: 0x20, // Space
51
+ 0x2D: 0x2D, // -
52
+ 0x2E: 0x3D, // =
53
+ 0x2F: 0x5B, // [
54
+ 0x30: 0x5D, // ]
55
+ 0x31: 0x5C, // backslash
56
+ 0x33: 0x3B, // ;
57
+ 0x34: 0x27, // '
58
+ 0x35: 0x60, // `
59
+ 0x36: 0x2C, // ,
60
+ 0x37: 0x2E, // .
61
+ 0x38: 0x2F, // /
62
+ 0x4C: 0x7F, // Delete
63
+ 0x4F: 0x1D, // Right Arrow
64
+ 0x50: 0x1C, // Left Arrow
65
+ 0x51: 0x1F, // Down Arrow
66
+ 0x52: 0x1E, // Up Arrow
67
+ 0x49: 0x1A, // Insert
68
+ };
69
+ /**
70
+ * KeyboardMatrixAttachment - Emulates a keyboard matrix connected to GPIO ports
71
+ *
72
+ * The keyboard matrix uses:
73
+ * - Port A (PA0-PA7): Rows (8 rows)
74
+ * - Port B (PB0-PB7): Columns (8 columns)
75
+ *
76
+ * Keys are active-low: when a key is pressed, the corresponding row/column intersection
77
+ * pulls the row line low when the column is selected (low).
78
+ */
79
+ class KeyboardMatrixAttachment extends Attachment_1.AttachmentBase {
80
+ constructor(priority = 0) {
81
+ super(priority, false, false, false, false);
82
+ // Keyboard matrix state - 8 rows, each storing which columns have keys pressed (bit mask)
83
+ this.keyboardMatrix = [0, 0, 0, 0, 0, 0, 0, 0];
84
+ // Currently selected columns from Port B write
85
+ this.selectedColumns = 0xFF;
86
+ this.reset();
87
+ }
88
+ reset() {
89
+ super.reset();
90
+ for (let i = 0; i < 8; i++) {
91
+ this.keyboardMatrix[i] = 0x00;
92
+ }
93
+ this.selectedColumns = 0xFF;
94
+ }
95
+ readPortA(ddr, or) {
96
+ let rowStates = 0xFF; // All rows high (not pressed) by default
97
+ // Check keyboard matrix based on selected columns
98
+ for (let row = 0; row < 8; row++) {
99
+ for (let col = 0; col < 8; col++) {
100
+ // If column is selected (low) and key is pressed in matrix
101
+ if (!(this.selectedColumns & (1 << col)) && (this.keyboardMatrix[row] & (1 << col))) {
102
+ rowStates &= ~(1 << row); // Pull row low (active-low)
103
+ }
104
+ }
105
+ }
106
+ return rowStates & 0xFF;
107
+ }
108
+ readPortB(ddr, or) {
109
+ return 0xFF; // Don't interfere with Port B reads
110
+ }
111
+ writePortB(value, ddr) {
112
+ // Store the column selection for matrix scanning
113
+ this.selectedColumns = value & 0xFF;
114
+ }
115
+ /**
116
+ * Update a key state based on USB HID keycode
117
+ * @param usbHidKeycode - USB HID usage ID for the key
118
+ * @param pressed - true if key is pressed, false if released
119
+ */
120
+ updateKey(usbHidKeycode, pressed) {
121
+ let targetRow = -1;
122
+ let targetCol = -1;
123
+ // Direct USB HID keycode to matrix position mapping for special/modifier keys
124
+ switch (usbHidKeycode) {
125
+ // Modifier keys
126
+ case 0xE0: // Left Ctrl
127
+ case 0xE4: // Right Ctrl
128
+ targetRow = 7;
129
+ targetCol = 0; // PA7, PB0 (CTRL)
130
+ break;
131
+ case 0xE1: // Left Shift
132
+ case 0xE5: // Right Shift
133
+ targetRow = 5;
134
+ targetCol = 4; // PA5, PB4 (SHIFT)
135
+ break;
136
+ case 0xE2: // Left Alt
137
+ case 0xE6: // Right Alt
138
+ targetRow = 7;
139
+ targetCol = 2; // PA7, PB2 (ALT)
140
+ break;
141
+ case 0xE3: // Left GUI (Windows/Command)
142
+ case 0xE7: // Right GUI
143
+ targetRow = 7;
144
+ targetCol = 1; // PA7, PB1 (META/GUI)
145
+ break;
146
+ // Special keys
147
+ case 0x39: // Caps Lock
148
+ targetRow = 3;
149
+ targetCol = 6; // PA3, PB6 (CAPS LOCK)
150
+ break;
151
+ // Function keys (F1-F10) - map to FN + number combination
152
+ case 0x3A: // F1
153
+ case 0x3B: // F2
154
+ case 0x3C: // F3
155
+ case 0x3D: // F4
156
+ case 0x3E: // F5
157
+ case 0x3F: // F6
158
+ case 0x40: // F7
159
+ case 0x41: // F8
160
+ case 0x42: // F9
161
+ case 0x43: // F10
162
+ {
163
+ // Always set the FN key (PA7, PB4)
164
+ if (pressed) {
165
+ this.keyboardMatrix[7] |= (1 << 4);
166
+ }
167
+ else {
168
+ this.keyboardMatrix[7] &= ~(1 << 4);
169
+ }
170
+ // Map to corresponding number key
171
+ let numberKey;
172
+ if (usbHidKeycode >= 0x3A && usbHidKeycode <= 0x42) {
173
+ // F1-F9 map to '1'-'9'
174
+ numberKey = 0x31 + (usbHidKeycode - 0x3A); // '1' = 0x31
175
+ }
176
+ else {
177
+ // F10 maps to '0'
178
+ numberKey = 0x30; // '0' = 0x30
179
+ }
180
+ // Find and set the number key in the matrix
181
+ for (let row = 0; row < 8; row++) {
182
+ for (let col = 0; col < 8; col++) {
183
+ if (KeyboardMatrixAttachment.KEYBOARD_LAYOUT[row][col] === numberKey) {
184
+ if (pressed) {
185
+ this.keyboardMatrix[row] |= (1 << col);
186
+ }
187
+ else {
188
+ this.keyboardMatrix[row] &= ~(1 << col);
189
+ }
190
+ return; // Done processing
191
+ }
192
+ }
193
+ }
194
+ return; // If number key not found, still return
195
+ }
196
+ default:
197
+ {
198
+ // For all other keys, try ASCII mapping
199
+ const asciiKey = USB_HID_TO_ASCII[usbHidKeycode];
200
+ // If no ASCII mapping, ignore the key
201
+ if (asciiKey === undefined || asciiKey === 0x00) {
202
+ return;
203
+ }
204
+ // Find the ASCII key in the keyboard layout
205
+ for (let row = 0; row < 8; row++) {
206
+ for (let col = 0; col < 8; col++) {
207
+ if (KeyboardMatrixAttachment.KEYBOARD_LAYOUT[row][col] === asciiKey) {
208
+ targetRow = row;
209
+ targetCol = col;
210
+ break;
211
+ }
212
+ }
213
+ if (targetRow !== -1)
214
+ break;
215
+ }
216
+ }
217
+ break;
218
+ }
219
+ // Update the keyboard matrix if we found a valid position
220
+ if (targetRow !== -1 && targetCol !== -1) {
221
+ if (pressed) {
222
+ this.keyboardMatrix[targetRow] |= (1 << targetCol); // Set bit (key pressed)
223
+ }
224
+ else {
225
+ this.keyboardMatrix[targetRow] &= ~(1 << targetCol); // Clear bit (key released)
226
+ }
227
+ }
228
+ }
229
+ /**
230
+ * Update a specific matrix position directly
231
+ * @param row - Row index (0-7)
232
+ * @param col - Column index (0-7)
233
+ * @param pressed - true if key is pressed, false if released
234
+ */
235
+ updateMatrixPosition(row, col, pressed) {
236
+ if (row < 8 && col < 8) {
237
+ if (pressed) {
238
+ this.keyboardMatrix[row] |= (1 << col);
239
+ }
240
+ else {
241
+ this.keyboardMatrix[row] &= ~(1 << col);
242
+ }
243
+ }
244
+ }
245
+ /**
246
+ * Get the current state of the keyboard matrix
247
+ * @returns Array of 8 bytes representing the matrix state
248
+ */
249
+ getMatrixState() {
250
+ return [...this.keyboardMatrix];
251
+ }
252
+ /**
253
+ * Get the currently selected columns
254
+ * @returns Byte representing selected columns
255
+ */
256
+ getSelectedColumns() {
257
+ return this.selectedColumns;
258
+ }
259
+ }
260
+ exports.KeyboardMatrixAttachment = KeyboardMatrixAttachment;
261
+ // Keyboard matrix layout mapping
262
+ // Rows are PA0-PA7, Columns are PB0-PB7
263
+ KeyboardMatrixAttachment.KEYBOARD_LAYOUT = [
264
+ // PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7
265
+ [0x60, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37], // PA0: ` 1 2 3 4 5 6 7
266
+ [0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x1B, 0x09], // PA1: 8 9 0 - = BS ESC TAB
267
+ [0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69], // PA2: q w e r t y u i
268
+ [0x6F, 0x70, 0x5B, 0x5D, 0x5C, 0x1A, 0x00, 0x61], // PA3: o p [ ] \ INS CAPS a
269
+ [0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C], // PA4: s d f g h j k l
270
+ [0x3B, 0x27, 0x0D, 0x7F, 0x00, 0x7A, 0x78, 0x63], // PA5: ; ' ENTER DEL SHIFT z x c
271
+ [0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x1E], // PA6: v b n m , . / UP
272
+ [0x00, 0x00, 0x00, 0x20, 0x00, 0x1C, 0x1F, 0x1D], // PA7: CTRL META ALT SPACE FN LEFT DOWN RIGHT
273
+ ];
274
+ //# sourceMappingURL=KeyboardMatrixAttachment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyboardMatrixAttachment.js","sourceRoot":"","sources":["../../../../src/components/IO/Attachments/KeyboardMatrixAttachment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { AttachmentBase } from './Attachment';
2
+ /**
3
+ * KeypadAttachment - Emulates a 4×6 matrix keypad with a built-in hardware encoder
4
+ *
5
+ * The encoder converts a key press into a 5-bit code (PA0–PA4) that appears on the GPIO
6
+ * port. Bits 5–7 are never driven by the keypad and always read as 0 when data is present.
7
+ *
8
+ * Behaviour mirrors a typical 74C922-style encoder:
9
+ * - On key press → the 5-bit keypad code is latched and a CA1/CB1 interrupt is asserted
10
+ * - On port read → the latched code is returned on bits 0–4 (bits 5–7 = 0)
11
+ * - clearInterrupts → clears the interrupt and the data-ready latch
12
+ * - Key releases → ignored (encoder only reports on the falling edge of a keypress)
13
+ *
14
+ * The attachment may be wired to either Port A or Port B via the constructor parameter.
15
+ * CA1/CB1 is the DA (Data Available) interrupt line from the 74C922.
16
+ * CA2/CB2 is connected to the 74C922 OE (Output Enable) pin; data is only driven onto the
17
+ * bus when OE is asserted LOW by the 6522.
18
+ */
19
+ export declare class KeypadAttachment extends AttachmentBase {
20
+ private keypadValue;
21
+ private dataReady;
22
+ private interruptPending;
23
+ private readonly attachedToPortA;
24
+ private oeState;
25
+ constructor(attachToPortA?: boolean, priority?: number);
26
+ reset(): void;
27
+ updateControlLines(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
28
+ readPortA(ddr: number, or: number): number;
29
+ readPortB(ddr: number, or: number): number;
30
+ hasCA1Interrupt(): boolean;
31
+ hasCB1Interrupt(): boolean;
32
+ clearInterrupts(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
33
+ /**
34
+ * Notify the attachment of a USB HID key event.
35
+ * Key releases are ignored; only presses generate output on the GPIO port.
36
+ *
37
+ * @param usbHidKeycode - USB HID usage ID for the key
38
+ * @param pressed - true for key-down, false for key-up
39
+ */
40
+ updateKey(usbHidKeycode: number, pressed: boolean): void;
41
+ /**
42
+ * Returns the current latched keypad code (bits 0–4) or 0xFF if no data is ready.
43
+ */
44
+ getCurrentKey(): number;
45
+ /** Returns true when a key has been pressed and the latch has not yet been cleared. */
46
+ hasDataReady(): boolean;
47
+ }
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KeypadAttachment = void 0;
4
+ const Attachment_1 = require("./Attachment");
5
+ /**
6
+ * USB HID keycode to keypad value mapping
7
+ * Maps USB HID usage IDs to the 5-bit keypad codes ($00-$17) per the 6502 Keypad Mapping table
8
+ *
9
+ * Keypad layout (4 columns × 6 rows = 24 keys):
10
+ * $00 = ◄ $01=1 $02=2 $03=3
11
+ * $04 = 4 $05=5 $06=6 $07=7
12
+ * $08 = 8 $09=9 $0A=0 $0B=►
13
+ * $0C = F $0D=E $0E=D $0F=C
14
+ * $10 = ESC $11=INS $12=PGUP $13=A
15
+ * $14 = ▲/Enter $15=DEL $16=PGDN $17=B
16
+ */
17
+ const USB_HID_TO_KEYPAD = {
18
+ 0x50: 0x00, // Left Arrow → ◄
19
+ 0x1E: 0x01, // 1
20
+ 0x1F: 0x02, // 2
21
+ 0x20: 0x03, // 3
22
+ 0x21: 0x04, // 4
23
+ 0x22: 0x05, // 5
24
+ 0x23: 0x06, // 6
25
+ 0x24: 0x07, // 7
26
+ 0x25: 0x08, // 8
27
+ 0x26: 0x09, // 9
28
+ 0x27: 0x0A, // 0
29
+ 0x4F: 0x0B, // Right Arrow → ►
30
+ 0x09: 0x0C, // f → F
31
+ 0x08: 0x0D, // e → E
32
+ 0x07: 0x0E, // d → D
33
+ 0x06: 0x0F, // c → C
34
+ 0x29: 0x10, // Escape → ESC
35
+ 0x49: 0x11, // Insert → INS
36
+ 0x4B: 0x12, // Page Up → PGUP
37
+ 0x04: 0x13, // a → A
38
+ 0x52: 0x14, // Up Arrow → ▲
39
+ 0x28: 0x14, // Enter → ▲
40
+ 0x4C: 0x15, // Delete → DEL
41
+ 0x4E: 0x16, // Page Down → PGDN
42
+ 0x05: 0x17, // b → B
43
+ };
44
+ /**
45
+ * KeypadAttachment - Emulates a 4×6 matrix keypad with a built-in hardware encoder
46
+ *
47
+ * The encoder converts a key press into a 5-bit code (PA0–PA4) that appears on the GPIO
48
+ * port. Bits 5–7 are never driven by the keypad and always read as 0 when data is present.
49
+ *
50
+ * Behaviour mirrors a typical 74C922-style encoder:
51
+ * - On key press → the 5-bit keypad code is latched and a CA1/CB1 interrupt is asserted
52
+ * - On port read → the latched code is returned on bits 0–4 (bits 5–7 = 0)
53
+ * - clearInterrupts → clears the interrupt and the data-ready latch
54
+ * - Key releases → ignored (encoder only reports on the falling edge of a keypress)
55
+ *
56
+ * The attachment may be wired to either Port A or Port B via the constructor parameter.
57
+ * CA1/CB1 is the DA (Data Available) interrupt line from the 74C922.
58
+ * CA2/CB2 is connected to the 74C922 OE (Output Enable) pin; data is only driven onto the
59
+ * bus when OE is asserted LOW by the 6522.
60
+ */
61
+ class KeypadAttachment extends Attachment_1.AttachmentBase {
62
+ constructor(attachToPortA = true, priority = 0) {
63
+ super(priority, false, false, false, false);
64
+ this.keypadValue = 0x00;
65
+ this.dataReady = false;
66
+ this.interruptPending = false;
67
+ // OE state: CA2 for Port A, CB2 for Port B. HIGH = output disabled (default).
68
+ this.oeState = true;
69
+ this.attachedToPortA = attachToPortA;
70
+ this.reset();
71
+ }
72
+ reset() {
73
+ super.reset();
74
+ this.keypadValue = 0x00;
75
+ this.dataReady = false;
76
+ this.interruptPending = false;
77
+ this.oeState = true; // OE disabled until explicitly asserted by the 6522
78
+ }
79
+ updateControlLines(ca1, ca2, cb1, cb2) {
80
+ // CA2 controls OE for Port A; CB2 controls OE for Port B.
81
+ // 74C922 OE is active-LOW, so a LOW signal enables the output.
82
+ this.oeState = this.attachedToPortA ? ca2 : cb2;
83
+ }
84
+ readPortA(ddr, or) {
85
+ // Only drive the bus when attached to Port A, OE is asserted (LOW), and data is latched
86
+ if (this.attachedToPortA && !this.oeState && this.dataReady) {
87
+ return this.keypadValue & 0x1F; // bits 0–4 only; bits 5–7 = 0
88
+ }
89
+ return 0xFF; // not driving the bus
90
+ }
91
+ readPortB(ddr, or) {
92
+ // Only drive the bus when attached to Port B, OE is asserted (LOW), and data is latched
93
+ if (!this.attachedToPortA && !this.oeState && this.dataReady) {
94
+ return this.keypadValue & 0x1F; // bits 0–4 only; bits 5–7 = 0
95
+ }
96
+ return 0xFF; // not driving the bus
97
+ }
98
+ hasCA1Interrupt() {
99
+ return this.attachedToPortA && this.interruptPending;
100
+ }
101
+ hasCB1Interrupt() {
102
+ return !this.attachedToPortA && this.interruptPending;
103
+ }
104
+ clearInterrupts(ca1, ca2, cb1, cb2) {
105
+ if ((this.attachedToPortA && ca1) || (!this.attachedToPortA && cb1)) {
106
+ this.interruptPending = false;
107
+ this.dataReady = false;
108
+ }
109
+ }
110
+ /**
111
+ * Notify the attachment of a USB HID key event.
112
+ * Key releases are ignored; only presses generate output on the GPIO port.
113
+ *
114
+ * @param usbHidKeycode - USB HID usage ID for the key
115
+ * @param pressed - true for key-down, false for key-up
116
+ */
117
+ updateKey(usbHidKeycode, pressed) {
118
+ if (!pressed) {
119
+ return;
120
+ }
121
+ const keypadCode = USB_HID_TO_KEYPAD[usbHidKeycode];
122
+ if (keypadCode === undefined) {
123
+ return; // key is not present on this keypad
124
+ }
125
+ this.keypadValue = keypadCode;
126
+ this.dataReady = true;
127
+ this.interruptPending = true;
128
+ }
129
+ /**
130
+ * Returns the current latched keypad code (bits 0–4) or 0xFF if no data is ready.
131
+ */
132
+ getCurrentKey() {
133
+ return this.dataReady ? (this.keypadValue & 0x1F) : 0xFF;
134
+ }
135
+ /** Returns true when a key has been pressed and the latch has not yet been cleared. */
136
+ hasDataReady() {
137
+ return this.dataReady;
138
+ }
139
+ }
140
+ exports.KeypadAttachment = KeypadAttachment;
141
+ //# sourceMappingURL=KeypadAttachment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeypadAttachment.js","sourceRoot":"","sources":["../../../../src/components/IO/Attachments/KeypadAttachment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { AttachmentBase } from './Attachment';
2
+ /**
3
+ * HD44780 LCD Controller Emulation — GPIO Attachment
4
+ *
5
+ * Emulates a 16×2 (or configurable) character LCD with HD44780 controller
6
+ * connected via 8-bit parallel interface on a 65C22 VIA.
7
+ *
8
+ * Pin mapping (accent on VIA ports):
9
+ * Port B (D0–D7): 8-bit data bus
10
+ * Port A bit 5: RS (Register Select — 0 = command, 1 = data)
11
+ * Port A bit 6: RW (Read/Write — 0 = write, 1 = read)
12
+ * Port A bit 7: E (Enable — active-high strobe, latched on falling edge)
13
+ *
14
+ * Display modes:
15
+ * Standard character display with 5×8 pixel characters
16
+ * Supports CGRAM for up to 8 user-defined characters
17
+ *
18
+ * Output: pixel buffer (cols*(5+1)-1) × (rows*(8+1)-1) with values:
19
+ * -1 = no pixel (inter-character gap)
20
+ * 0 = pixel off
21
+ * 1 = pixel on
22
+ *
23
+ * Reference: vrEmuLcd by Troy Schrapel
24
+ * https://github.com/visrealm/vrEmuLcd
25
+ */
26
+ export declare const LCD_CMD_CLEAR = 1;
27
+ export declare const LCD_CMD_HOME = 2;
28
+ export declare const LCD_CMD_ENTRY_MODE = 4;
29
+ export declare const LCD_CMD_ENTRY_MODE_INCREMENT = 2;
30
+ export declare const LCD_CMD_ENTRY_MODE_SHIFT = 1;
31
+ export declare const LCD_CMD_DISPLAY = 8;
32
+ export declare const LCD_CMD_DISPLAY_ON = 4;
33
+ export declare const LCD_CMD_DISPLAY_CURSOR = 2;
34
+ export declare const LCD_CMD_DISPLAY_CURSOR_BLINK = 1;
35
+ export declare const LCD_CMD_SHIFT = 16;
36
+ export declare const LCD_CMD_SHIFT_DISPLAY = 8;
37
+ export declare const LCD_CMD_SHIFT_RIGHT = 4;
38
+ export declare const LCD_CMD_FUNCTION = 32;
39
+ export declare const LCD_CMD_FUNCTION_LCD_2LINE = 8;
40
+ export declare const LCD_CMD_SET_CGRAM_ADDR = 64;
41
+ export declare const LCD_CMD_SET_DRAM_ADDR = 128;
42
+ export declare class LCDAttachment extends AttachmentBase {
43
+ readonly cols: number;
44
+ readonly rows: number;
45
+ private entryModeFlags;
46
+ private displayFlags;
47
+ private scrollOffset;
48
+ /** 128-byte Display Data RAM */
49
+ private ddRam;
50
+ /** Current DDRAM pointer offset */
51
+ private ddPtr;
52
+ private dataWidthCols;
53
+ /** Character Generator RAM — 16 characters × 8 rows, stored column-major
54
+ * as 16 × CHAR_WIDTH_PX bytes (matching vrEmuLcd cgRam layout) */
55
+ private cgRam;
56
+ /** Current CGRAM pointer (null when not in CGRAM mode) */
57
+ private cgPtr;
58
+ /** Pixel buffer: each byte is -1 (gap), 0 (off) or 1 (on) */
59
+ buffer: Int8Array;
60
+ readonly pixelsWidth: number;
61
+ readonly pixelsHeight: number;
62
+ private blinkAccumulator;
63
+ private blinkState;
64
+ private static readonly BLINK_PERIOD_MS;
65
+ private lastPortA;
66
+ private lastE;
67
+ constructor(cols?: number, rows?: number, priority?: number);
68
+ reset(): void;
69
+ tick(cpuFrequency: number): void;
70
+ /**
71
+ * Port A carries the control signals.
72
+ * We detect E falling edge to latch the bus.
73
+ */
74
+ writePortA(value: number, ddr: number): void;
75
+ readPortA(ddr: number, or: number): number;
76
+ readPortB(ddr: number, or: number): number;
77
+ private latchBus;
78
+ private lastPortBValue;
79
+ writePortB(value: number, ddr: number): void;
80
+ sendCommand(command: number): void;
81
+ writeByte(data: number): void;
82
+ private readByte;
83
+ readAddress(): number;
84
+ private incrementDdPtr;
85
+ private decrementDdPtr;
86
+ private doShift;
87
+ /**
88
+ * Get the 5-column font data for a character.
89
+ * Characters 0–15 come from CGRAM; 16–255 from ROM.
90
+ */
91
+ private charBits;
92
+ private getDataOffset;
93
+ updatePixels(): void;
94
+ /** Get the raw DDRAM contents */
95
+ getDDRam(): Uint8Array;
96
+ /** Get the current DDRAM address pointer */
97
+ getDDPtr(): number;
98
+ /** Get the display flags */
99
+ getDisplayFlags(): number;
100
+ /** Get the entry mode flags */
101
+ getEntryModeFlags(): number;
102
+ /** Get scroll offset */
103
+ getScrollOffset(): number;
104
+ /** Get CGRAM pointer (null if not in CGRAM mode) */
105
+ getCGPtr(): number | null;
106
+ /** Read the text content of a specific display row */
107
+ getRowText(row: number): string;
108
+ /** Pixel state at a given coordinate: -1 (gap), 0 (off), 1 (on) */
109
+ pixelState(x: number, y: number): number;
110
+ }