ac6502 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +261 -0
  3. package/dist/components/CPU.js +1170 -0
  4. package/dist/components/CPU.js.map +1 -0
  5. package/dist/components/Cart.js +23 -0
  6. package/dist/components/Cart.js.map +1 -0
  7. package/dist/components/IO/Empty.js +19 -0
  8. package/dist/components/IO/Empty.js.map +1 -0
  9. package/dist/components/IO/GPIOAttachments/GPIOAttachment.js +71 -0
  10. package/dist/components/IO/GPIOAttachments/GPIOAttachment.js.map +1 -0
  11. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js +90 -0
  12. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js.map +1 -0
  13. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js +489 -0
  14. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js.map +1 -0
  15. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js +274 -0
  16. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js.map +1 -0
  17. package/dist/components/IO/GPIOCard.js +597 -0
  18. package/dist/components/IO/GPIOCard.js.map +1 -0
  19. package/dist/components/IO/InputBoard.js +19 -0
  20. package/dist/components/IO/InputBoard.js.map +1 -0
  21. package/dist/components/IO/LCDCard.js +19 -0
  22. package/dist/components/IO/LCDCard.js.map +1 -0
  23. package/dist/components/IO/RAMCard.js +63 -0
  24. package/dist/components/IO/RAMCard.js.map +1 -0
  25. package/dist/components/IO/RTCCard.js +483 -0
  26. package/dist/components/IO/RTCCard.js.map +1 -0
  27. package/dist/components/IO/SerialCard.js +282 -0
  28. package/dist/components/IO/SerialCard.js.map +1 -0
  29. package/dist/components/IO/SoundCard.js +620 -0
  30. package/dist/components/IO/SoundCard.js.map +1 -0
  31. package/dist/components/IO/StorageCard.js +428 -0
  32. package/dist/components/IO/StorageCard.js.map +1 -0
  33. package/dist/components/IO/VGACard.js +9 -0
  34. package/dist/components/IO/VGACard.js.map +1 -0
  35. package/dist/components/IO/VideoCard.js +623 -0
  36. package/dist/components/IO/VideoCard.js.map +1 -0
  37. package/dist/components/IO.js +3 -0
  38. package/dist/components/IO.js.map +1 -0
  39. package/dist/components/Machine.js +310 -0
  40. package/dist/components/Machine.js.map +1 -0
  41. package/dist/components/RAM.js +24 -0
  42. package/dist/components/RAM.js.map +1 -0
  43. package/dist/components/ROM.js +23 -0
  44. package/dist/components/ROM.js.map +1 -0
  45. package/dist/index.js +441 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/tests/CPU.test.js +1626 -0
  48. package/dist/tests/CPU.test.js.map +1 -0
  49. package/dist/tests/Cart.test.js +119 -0
  50. package/dist/tests/Cart.test.js.map +1 -0
  51. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js +339 -0
  52. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js.map +1 -0
  53. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js +126 -0
  54. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js.map +1 -0
  55. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js +779 -0
  56. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js.map +1 -0
  57. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js +355 -0
  58. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js.map +1 -0
  59. package/dist/tests/IO/GPIOCard.test.js +503 -0
  60. package/dist/tests/IO/GPIOCard.test.js.map +1 -0
  61. package/dist/tests/IO/RAMCard.test.js +229 -0
  62. package/dist/tests/IO/RAMCard.test.js.map +1 -0
  63. package/dist/tests/IO/RTCCard.test.js +177 -0
  64. package/dist/tests/IO/RTCCard.test.js.map +1 -0
  65. package/dist/tests/IO/SerialCard.test.js +423 -0
  66. package/dist/tests/IO/SerialCard.test.js.map +1 -0
  67. package/dist/tests/IO/SoundCard.test.js +528 -0
  68. package/dist/tests/IO/SoundCard.test.js.map +1 -0
  69. package/dist/tests/IO/StorageCard.test.js +647 -0
  70. package/dist/tests/IO/StorageCard.test.js.map +1 -0
  71. package/dist/tests/IO/VideoCard.test.js +549 -0
  72. package/dist/tests/IO/VideoCard.test.js.map +1 -0
  73. package/dist/tests/Machine.test.js +383 -0
  74. package/dist/tests/Machine.test.js.map +1 -0
  75. package/dist/tests/RAM.test.js +160 -0
  76. package/dist/tests/RAM.test.js.map +1 -0
  77. package/dist/tests/ROM.test.js +123 -0
  78. package/dist/tests/ROM.test.js.map +1 -0
  79. package/jest.config.cjs +9 -0
  80. package/package.json +43 -0
  81. package/src/components/CPU.ts +1371 -0
  82. package/src/components/Cart.ts +20 -0
  83. package/src/components/IO/GPIOAttachments/GPIOAttachment.ts +189 -0
  84. package/src/components/IO/GPIOAttachments/GPIOJoystickAttachment.ts +99 -0
  85. package/src/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.ts +465 -0
  86. package/src/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.ts +287 -0
  87. package/src/components/IO/GPIOCard.ts +677 -0
  88. package/src/components/IO/RAMCard.ts +68 -0
  89. package/src/components/IO/RTCCard.ts +518 -0
  90. package/src/components/IO/SerialCard.ts +335 -0
  91. package/src/components/IO/SoundCard.ts +711 -0
  92. package/src/components/IO/StorageCard.ts +473 -0
  93. package/src/components/IO/VideoCard.ts +730 -0
  94. package/src/components/IO.ts +11 -0
  95. package/src/components/Machine.ts +364 -0
  96. package/src/components/RAM.ts +23 -0
  97. package/src/components/ROM.ts +19 -0
  98. package/src/index.ts +474 -0
  99. package/src/tests/CPU.test.ts +2045 -0
  100. package/src/tests/Cart.test.ts +149 -0
  101. package/src/tests/IO/GPIOAttachments/GPIOAttachment.test.ts +413 -0
  102. package/src/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.ts +147 -0
  103. package/src/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts +961 -0
  104. package/src/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts +449 -0
  105. package/src/tests/IO/GPIOCard.test.ts +644 -0
  106. package/src/tests/IO/RAMCard.test.ts +284 -0
  107. package/src/tests/IO/RTCCard.test.ts +222 -0
  108. package/src/tests/IO/SerialCard.test.ts +530 -0
  109. package/src/tests/IO/SoundCard.test.ts +659 -0
  110. package/src/tests/IO/StorageCard.test.ts +787 -0
  111. package/src/tests/IO/VideoCard.test.ts +668 -0
  112. package/src/tests/Machine.test.ts +437 -0
  113. package/src/tests/RAM.test.ts +196 -0
  114. package/src/tests/ROM.test.ts +154 -0
  115. package/tsconfig.json +12 -0
@@ -0,0 +1,287 @@
1
+ import { GPIOAttachmentBase } from './GPIOAttachment'
2
+
3
+ /**
4
+ * USB HID Keycode to ASCII mapping table
5
+ * Maps USB HID usage IDs to ASCII characters
6
+ */
7
+ const USB_HID_TO_ASCII: { [key: number]: number } = {
8
+ 0x04: 0x61, // a
9
+ 0x05: 0x62, // b
10
+ 0x06: 0x63, // c
11
+ 0x07: 0x64, // d
12
+ 0x08: 0x65, // e
13
+ 0x09: 0x66, // f
14
+ 0x0A: 0x67, // g
15
+ 0x0B: 0x68, // h
16
+ 0x0C: 0x69, // i
17
+ 0x0D: 0x6A, // j
18
+ 0x0E: 0x6B, // k
19
+ 0x0F: 0x6C, // l
20
+ 0x10: 0x6D, // m
21
+ 0x11: 0x6E, // n
22
+ 0x12: 0x6F, // o
23
+ 0x13: 0x70, // p
24
+ 0x14: 0x71, // q
25
+ 0x15: 0x72, // r
26
+ 0x16: 0x73, // s
27
+ 0x17: 0x74, // t
28
+ 0x18: 0x75, // u
29
+ 0x19: 0x76, // v
30
+ 0x1A: 0x77, // w
31
+ 0x1B: 0x78, // x
32
+ 0x1C: 0x79, // y
33
+ 0x1D: 0x7A, // z
34
+ 0x1E: 0x31, // 1
35
+ 0x1F: 0x32, // 2
36
+ 0x20: 0x33, // 3
37
+ 0x21: 0x34, // 4
38
+ 0x22: 0x35, // 5
39
+ 0x23: 0x36, // 6
40
+ 0x24: 0x37, // 7
41
+ 0x25: 0x38, // 8
42
+ 0x26: 0x39, // 9
43
+ 0x27: 0x30, // 0
44
+ 0x28: 0x0D, // Enter
45
+ 0x29: 0x1B, // Escape
46
+ 0x2A: 0x08, // Backspace
47
+ 0x2B: 0x09, // Tab
48
+ 0x2C: 0x20, // Space
49
+ 0x2D: 0x2D, // -
50
+ 0x2E: 0x3D, // =
51
+ 0x2F: 0x5B, // [
52
+ 0x30: 0x5D, // ]
53
+ 0x31: 0x5C, // backslash
54
+ 0x33: 0x3B, // ;
55
+ 0x34: 0x27, // '
56
+ 0x35: 0x60, // `
57
+ 0x36: 0x2C, // ,
58
+ 0x37: 0x2E, // .
59
+ 0x38: 0x2F, // /
60
+ 0x4C: 0x7F, // Delete
61
+ 0x4F: 0x1D, // Right Arrow
62
+ 0x50: 0x1C, // Left Arrow
63
+ 0x51: 0x1F, // Down Arrow
64
+ 0x52: 0x1E, // Up Arrow
65
+ 0x49: 0x1A, // Insert
66
+ }
67
+
68
+ /**
69
+ * GPIOKeyboardMatrixAttachment - Emulates a keyboard matrix connected to GPIO ports
70
+ *
71
+ * The keyboard matrix uses:
72
+ * - Port A (PA0-PA7): Rows (8 rows)
73
+ * - Port B (PB0-PB7): Columns (8 columns)
74
+ *
75
+ * Keys are active-low: when a key is pressed, the corresponding row/column intersection
76
+ * pulls the row line low when the column is selected (low).
77
+ */
78
+ export class GPIOKeyboardMatrixAttachment extends GPIOAttachmentBase {
79
+ // Keyboard matrix layout mapping
80
+ // Rows are PA0-PA7, Columns are PB0-PB7
81
+ private static readonly KEYBOARD_LAYOUT: number[][] = [
82
+ // PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7
83
+ [0x60, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37], // PA0: ` 1 2 3 4 5 6 7
84
+ [0x38, 0x39, 0x30, 0x2D, 0x3D, 0x08, 0x1B, 0x09], // PA1: 8 9 0 - = BS ESC TAB
85
+ [0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69], // PA2: q w e r t y u i
86
+ [0x6F, 0x70, 0x5B, 0x5D, 0x5C, 0x1A, 0x00, 0x61], // PA3: o p [ ] \ INS CAPS a
87
+ [0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C], // PA4: s d f g h j k l
88
+ [0x3B, 0x27, 0x0D, 0x7F, 0x00, 0x7A, 0x78, 0x63], // PA5: ; ' ENTER DEL SHIFT z x c
89
+ [0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F, 0x1E], // PA6: v b n m , . / UP
90
+ [0x00, 0x00, 0x00, 0x20, 0x00, 0x1C, 0x1F, 0x1D], // PA7: CTRL META ALT SPACE FN LEFT DOWN RIGHT
91
+ ]
92
+
93
+ // Keyboard matrix state - 8 rows, each storing which columns have keys pressed (bit mask)
94
+ private keyboardMatrix: number[] = [0, 0, 0, 0, 0, 0, 0, 0]
95
+
96
+ // Currently selected columns from Port B write
97
+ private selectedColumns: number = 0xFF
98
+
99
+ constructor(priority: number = 0) {
100
+ super(priority, false, false, false, false)
101
+ this.reset()
102
+ }
103
+
104
+ reset(): void {
105
+ super.reset()
106
+ for (let i = 0; i < 8; i++) {
107
+ this.keyboardMatrix[i] = 0x00
108
+ }
109
+ this.selectedColumns = 0xFF
110
+ }
111
+
112
+ readPortA(ddr: number, or: number): number {
113
+ let rowStates = 0xFF // All rows high (not pressed) by default
114
+
115
+ // Check keyboard matrix based on selected columns
116
+ for (let row = 0; row < 8; row++) {
117
+ for (let col = 0; col < 8; col++) {
118
+ // If column is selected (low) and key is pressed in matrix
119
+ if (!(this.selectedColumns & (1 << col)) && (this.keyboardMatrix[row] & (1 << col))) {
120
+ rowStates &= ~(1 << row) // Pull row low (active-low)
121
+ }
122
+ }
123
+ }
124
+
125
+ return rowStates & 0xFF
126
+ }
127
+
128
+ readPortB(ddr: number, or: number): number {
129
+ return 0xFF // Don't interfere with Port B reads
130
+ }
131
+
132
+ writePortB(value: number, ddr: number): void {
133
+ // Store the column selection for matrix scanning
134
+ this.selectedColumns = value & 0xFF
135
+ }
136
+
137
+ /**
138
+ * Update a key state based on USB HID keycode
139
+ * @param usbHidKeycode - USB HID usage ID for the key
140
+ * @param pressed - true if key is pressed, false if released
141
+ */
142
+ updateKey(usbHidKeycode: number, pressed: boolean): void {
143
+ let targetRow = -1
144
+ let targetCol = -1
145
+
146
+ // Direct USB HID keycode to matrix position mapping for special/modifier keys
147
+ switch (usbHidKeycode) {
148
+ // Modifier keys
149
+ case 0xE0: // Left Ctrl
150
+ case 0xE4: // Right Ctrl
151
+ targetRow = 7
152
+ targetCol = 0 // PA7, PB0 (CTRL)
153
+ break
154
+ case 0xE1: // Left Shift
155
+ case 0xE5: // Right Shift
156
+ targetRow = 5
157
+ targetCol = 4 // PA5, PB4 (SHIFT)
158
+ break
159
+ case 0xE2: // Left Alt
160
+ case 0xE6: // Right Alt
161
+ targetRow = 7
162
+ targetCol = 2 // PA7, PB2 (ALT)
163
+ break
164
+ case 0xE3: // Left GUI (Windows/Command)
165
+ case 0xE7: // Right GUI
166
+ targetRow = 7
167
+ targetCol = 1 // PA7, PB1 (META/GUI)
168
+ break
169
+
170
+ // Special keys
171
+ case 0x39: // Caps Lock
172
+ targetRow = 3
173
+ targetCol = 6 // PA3, PB6 (CAPS LOCK)
174
+ break
175
+
176
+ // Function keys (F1-F10) - map to FN + number combination
177
+ case 0x3A: // F1
178
+ case 0x3B: // F2
179
+ case 0x3C: // F3
180
+ case 0x3D: // F4
181
+ case 0x3E: // F5
182
+ case 0x3F: // F6
183
+ case 0x40: // F7
184
+ case 0x41: // F8
185
+ case 0x42: // F9
186
+ case 0x43: // F10
187
+ {
188
+ // Always set the FN key (PA7, PB4)
189
+ if (pressed) {
190
+ this.keyboardMatrix[7] |= (1 << 4)
191
+ } else {
192
+ this.keyboardMatrix[7] &= ~(1 << 4)
193
+ }
194
+
195
+ // Map to corresponding number key
196
+ let numberKey: number
197
+ if (usbHidKeycode >= 0x3A && usbHidKeycode <= 0x42) {
198
+ // F1-F9 map to '1'-'9'
199
+ numberKey = 0x31 + (usbHidKeycode - 0x3A) // '1' = 0x31
200
+ } else {
201
+ // F10 maps to '0'
202
+ numberKey = 0x30 // '0' = 0x30
203
+ }
204
+
205
+ // Find and set the number key in the matrix
206
+ for (let row = 0; row < 8; row++) {
207
+ for (let col = 0; col < 8; col++) {
208
+ if (GPIOKeyboardMatrixAttachment.KEYBOARD_LAYOUT[row][col] === numberKey) {
209
+ if (pressed) {
210
+ this.keyboardMatrix[row] |= (1 << col)
211
+ } else {
212
+ this.keyboardMatrix[row] &= ~(1 << col)
213
+ }
214
+ return // Done processing
215
+ }
216
+ }
217
+ }
218
+ return // If number key not found, still return
219
+ }
220
+
221
+ default:
222
+ {
223
+ // For all other keys, try ASCII mapping
224
+ const asciiKey = USB_HID_TO_ASCII[usbHidKeycode]
225
+
226
+ // If no ASCII mapping, ignore the key
227
+ if (asciiKey === undefined || asciiKey === 0x00) {
228
+ return
229
+ }
230
+
231
+ // Find the ASCII key in the keyboard layout
232
+ for (let row = 0; row < 8; row++) {
233
+ for (let col = 0; col < 8; col++) {
234
+ if (GPIOKeyboardMatrixAttachment.KEYBOARD_LAYOUT[row][col] === asciiKey) {
235
+ targetRow = row
236
+ targetCol = col
237
+ break
238
+ }
239
+ }
240
+ if (targetRow !== -1) break
241
+ }
242
+ }
243
+ break
244
+ }
245
+
246
+ // Update the keyboard matrix if we found a valid position
247
+ if (targetRow !== -1 && targetCol !== -1) {
248
+ if (pressed) {
249
+ this.keyboardMatrix[targetRow] |= (1 << targetCol) // Set bit (key pressed)
250
+ } else {
251
+ this.keyboardMatrix[targetRow] &= ~(1 << targetCol) // Clear bit (key released)
252
+ }
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Update a specific matrix position directly
258
+ * @param row - Row index (0-7)
259
+ * @param col - Column index (0-7)
260
+ * @param pressed - true if key is pressed, false if released
261
+ */
262
+ updateMatrixPosition(row: number, col: number, pressed: boolean): void {
263
+ if (row < 8 && col < 8) {
264
+ if (pressed) {
265
+ this.keyboardMatrix[row] |= (1 << col)
266
+ } else {
267
+ this.keyboardMatrix[row] &= ~(1 << col)
268
+ }
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Get the current state of the keyboard matrix
274
+ * @returns Array of 8 bytes representing the matrix state
275
+ */
276
+ getMatrixState(): number[] {
277
+ return [...this.keyboardMatrix]
278
+ }
279
+
280
+ /**
281
+ * Get the currently selected columns
282
+ * @returns Byte representing selected columns
283
+ */
284
+ getSelectedColumns(): number {
285
+ return this.selectedColumns
286
+ }
287
+ }