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,335 @@
1
+ import { IO } from '../IO'
2
+
3
+ /**
4
+ * SerialCard - Emulates a 6551 ACIA (Asynchronous Communications Interface Adapter)
5
+ *
6
+ * Register Map:
7
+ * $00: Data Register (read/write)
8
+ * $01: Status Register (read) / Programmed Reset (write)
9
+ * $02: Command Register (write)
10
+ * $03: Control Register (write)
11
+ */
12
+ export class SerialCard implements IO {
13
+
14
+ raiseIRQ = () => {}
15
+ raiseNMI = () => {}
16
+ transmit?: (data: number) => void
17
+
18
+ // Registers
19
+ private dataRegister: number = 0
20
+ private statusRegister: number = 0x10 // Transmit Data Register Empty
21
+ private commandRegister: number = 0
22
+ private controlRegister: number = 0
23
+
24
+ // Buffers
25
+ private transmitBuffer: number[] = []
26
+ private receiveBuffer: number[] = []
27
+
28
+ // Status flags
29
+ private parityError: boolean = false
30
+ private framingError: boolean = false
31
+ private overrun: boolean = false
32
+ private irqFlag: boolean = false
33
+ private echoMode: boolean = false
34
+
35
+ // Timing
36
+ private cycleCounter: number = 0
37
+ private baudRate: number = 115200
38
+
39
+ /**
40
+ * Read from ACIA register
41
+ */
42
+ read(address: number): number {
43
+ const register = address & 0x03
44
+
45
+ switch (register) {
46
+ case 0x00: // Data Register
47
+ return this.readData()
48
+
49
+ case 0x01: // Status Register
50
+ return this.readStatus()
51
+
52
+ case 0x02: // Command Register (write-only, returns 0)
53
+ return 0
54
+
55
+ case 0x03: // Control Register (write-only, returns 0)
56
+ return 0
57
+
58
+ default:
59
+ return 0
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Write to ACIA register
65
+ */
66
+ write(address: number, data: number): void {
67
+ const register = address & 0x03
68
+
69
+ switch (register) {
70
+ case 0x00: // Data Register
71
+ this.writeData(data)
72
+ break
73
+
74
+ case 0x01: // Programmed Reset
75
+ this.programmedReset()
76
+ break
77
+
78
+ case 0x02: // Command Register
79
+ this.writeCommand(data)
80
+ break
81
+
82
+ case 0x03: // Control Register
83
+ this.writeControl(data)
84
+ break
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Read data from receive buffer
90
+ */
91
+ private readData(): number {
92
+ if (this.receiveBuffer.length > 0) {
93
+ const data = this.receiveBuffer.shift()!
94
+ this.dataRegister = data
95
+
96
+ // Update status: clear Receive Data Register Full
97
+ this.statusRegister &= ~0x08
98
+
99
+ // Check for overrun if more data arrives
100
+ if (this.receiveBuffer.length === 0) {
101
+ this.overrun = false
102
+ this.statusRegister &= ~0x04
103
+ }
104
+
105
+ // Clear IRQ if receive IRQ was enabled
106
+ if ((this.commandRegister & 0x04) && this.receiveBuffer.length === 0) {
107
+ this.irqFlag = false
108
+ this.statusRegister &= ~0x80
109
+ }
110
+
111
+ return data
112
+ }
113
+
114
+ return this.dataRegister
115
+ }
116
+
117
+ /**
118
+ * Write data to transmit buffer
119
+ */
120
+ private writeData(data: number): void {
121
+ this.transmitBuffer.push(data & 0xFF)
122
+
123
+ // Clear Transmit Data Register Empty flag
124
+ this.statusRegister &= ~0x10
125
+ }
126
+
127
+ /**
128
+ * Read status register
129
+ */
130
+ private readStatus(): number {
131
+ let status = 0
132
+
133
+ // Bit 0: Parity Error
134
+ if (this.parityError) status |= 0x01
135
+
136
+ // Bit 1: Framing Error
137
+ if (this.framingError) status |= 0x02
138
+
139
+ // Bit 2: Overrun
140
+ if (this.overrun) status |= 0x04
141
+
142
+ // Bit 3: Receive Data Register Full
143
+ if (this.receiveBuffer.length > 0) status |= 0x08
144
+
145
+ // Bit 4: Transmit Data Register Empty
146
+ if (this.transmitBuffer.length === 0) status |= 0x10
147
+
148
+ // Bit 5: Data Carrier Detect (DCD) - always clear (connected)
149
+ status &= ~0x20
150
+
151
+ // Bit 6: Data Set Ready (DSR) - always set (ready)
152
+ status |= 0x40
153
+
154
+ // Bit 7: Interrupt (IRQ)
155
+ if (this.irqFlag) status |= 0x80
156
+
157
+ this.statusRegister = status
158
+ return status
159
+ }
160
+
161
+ /**
162
+ * Write to command register
163
+ */
164
+ private writeCommand(data: number): void {
165
+ this.commandRegister = data & 0xFF
166
+
167
+ // Bits 0-1: DTR control
168
+ // const dtrControl = data & 0x03
169
+
170
+ // Bit 2: Receive IRQ Enable
171
+ const receiveIRQEnabled = (data & 0x04) !== 0
172
+
173
+ // Bits 3-4: Transmit control and IRQ enable
174
+ // const transmitControl = (data >> 3) & 0x03
175
+
176
+ // Bit 5: Echo mode
177
+ this.echoMode = (data & 0x20) !== 0
178
+
179
+ // Bits 6-7: Parity control
180
+ // const parityControl = (data >> 6) & 0x03
181
+
182
+ // Handle receive IRQ
183
+ if (receiveIRQEnabled && this.receiveBuffer.length > 0) {
184
+ this.irqFlag = true
185
+ this.statusRegister |= 0x80
186
+ this.raiseIRQ()
187
+ } else if (!receiveIRQEnabled) {
188
+ this.irqFlag = false
189
+ this.statusRegister &= ~0x80
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Write to control register
195
+ */
196
+ private writeControl(data: number): void {
197
+ this.controlRegister = data & 0xFF
198
+
199
+ // Bits 0-3: Baud rate
200
+ const baudRateCode = data & 0x0F
201
+ this.baudRate = this.getBaudRate(baudRateCode)
202
+
203
+ // Bit 4: Receiver clock source (internal/external)
204
+ const receiverClockSource = (data & 0x10) !== 0
205
+
206
+ // Bits 5-6: Word length (5, 6, 7, or 8 bits)
207
+ const wordLength = ((data >> 5) & 0x03) + 5
208
+
209
+ // Bit 7: Stop bits (1 or 2)
210
+ const stopBits = (data & 0x80) ? 2 : 1
211
+ }
212
+
213
+ /**
214
+ * Get baud rate from control register code
215
+ */
216
+ private getBaudRate(code: number): number {
217
+ const baudRates = [
218
+ 115200, // 0000 (actually 16x external clock, using 115200 as default)
219
+ 50, // 0001
220
+ 75, // 0010
221
+ 110, // 0011
222
+ 135, // 0100
223
+ 150, // 0101
224
+ 300, // 0110
225
+ 600, // 0111
226
+ 1200, // 1000
227
+ 1800, // 1001
228
+ 2400, // 1010
229
+ 3600, // 1011
230
+ 4800, // 1100
231
+ 7200, // 1101
232
+ 9600, // 1110
233
+ 19200 // 1111
234
+ ]
235
+ return baudRates[code] || 115200
236
+ }
237
+
238
+ /**
239
+ * Programmed reset
240
+ */
241
+ private programmedReset(): void {
242
+ this.statusRegister = 0x10 // Transmit Data Register Empty
243
+ this.parityError = false
244
+ this.framingError = false
245
+ this.overrun = false
246
+ this.irqFlag = false
247
+ }
248
+
249
+ /**
250
+ * Tick - emulate ACIA timing
251
+ */
252
+ tick(frequency: number): void {
253
+ this.cycleCounter++
254
+
255
+ // Calculate cycles per byte: (CPU_CLOCK / baud_rate) * bits_per_frame
256
+ // Assuming 10 bits per frame (1 start + 8 data + 1 stop)
257
+ const cyclesPerByte = Math.floor((frequency / this.baudRate) * 10)
258
+
259
+ // Simulate transmission based on actual baud rate
260
+ if (this.cycleCounter >= cyclesPerByte && this.transmitBuffer.length > 0) {
261
+ this.cycleCounter = 0
262
+
263
+ // Transmit one byte
264
+ const byte = this.transmitBuffer.shift()
265
+
266
+ if (byte !== undefined && this.transmit) {
267
+ this.transmit(byte)
268
+ }
269
+
270
+ // Set Transmit Data Register Empty if buffer is empty
271
+ if (this.transmitBuffer.length === 0) {
272
+ this.statusRegister |= 0x10
273
+
274
+ // Trigger transmit complete IRQ if enabled
275
+ if ((this.commandRegister & 0x18) === 0x08) {
276
+ this.irqFlag = true
277
+ this.statusRegister |= 0x80
278
+ this.raiseIRQ()
279
+ }
280
+ }
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Reset the ACIA
286
+ */
287
+ reset(coldStart: boolean): void {
288
+ this.dataRegister = 0
289
+ this.statusRegister = 0x10 // Transmit Data Register Empty
290
+ this.commandRegister = 0
291
+ this.controlRegister = 0
292
+
293
+ this.transmitBuffer = []
294
+ this.receiveBuffer = []
295
+
296
+ this.parityError = false
297
+ this.framingError = false
298
+ this.overrun = false
299
+ this.irqFlag = false
300
+ this.echoMode = false
301
+
302
+ this.cycleCounter = 0
303
+ this.baudRate = 115200
304
+ }
305
+
306
+ /**
307
+ * Receive data from external source
308
+ */
309
+ onData(data: number): void {
310
+ if (this.receiveBuffer.length > 0 && (this.statusRegister & 0x08)) {
311
+ // Overrun condition: data arrives before previous data was read
312
+ this.overrun = true
313
+ this.statusRegister |= 0x04
314
+ }
315
+
316
+ this.receiveBuffer.push(data & 0xFF)
317
+
318
+ // Set Receive Data Register Full flag
319
+ this.statusRegister |= 0x08
320
+
321
+ // Trigger receive IRQ if enabled
322
+ if (this.commandRegister & 0x04) {
323
+ this.irqFlag = true
324
+ this.statusRegister |= 0x80
325
+ this.raiseIRQ()
326
+ }
327
+
328
+ // Echo mode: automatically transmit received data
329
+ if (this.echoMode) {
330
+ this.transmitBuffer.push(data & 0xFF)
331
+ // Clear Transmit Data Register Empty flag
332
+ this.statusRegister &= ~0x10
333
+ }
334
+ }
335
+ }