ac6502 1.2.0 → 1.4.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 (182) hide show
  1. package/README.md +12 -12
  2. package/dist/components/CPU.d.ts +162 -0
  3. package/dist/components/Cart.d.ts +9 -0
  4. package/dist/components/IO/ACIA.d.ts +76 -0
  5. package/dist/components/IO/ACIA.js +282 -0
  6. package/dist/components/IO/ACIA.js.map +1 -0
  7. package/dist/components/IO/Attachments/Attachment.d.ts +112 -0
  8. package/dist/components/IO/Attachments/Attachment.js +71 -0
  9. package/dist/components/IO/Attachments/Attachment.js.map +1 -0
  10. package/dist/components/IO/Attachments/JoystickAttachment.d.ts +53 -0
  11. package/dist/components/IO/Attachments/JoystickAttachment.js +90 -0
  12. package/dist/components/IO/Attachments/JoystickAttachment.js.map +1 -0
  13. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +63 -0
  14. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +489 -0
  15. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -0
  16. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.d.ts +44 -0
  17. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js +274 -0
  18. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js.map +1 -0
  19. package/dist/components/IO/Attachments/KeypadAttachment.d.ts +47 -0
  20. package/dist/components/IO/Attachments/KeypadAttachment.js +141 -0
  21. package/dist/components/IO/Attachments/KeypadAttachment.js.map +1 -0
  22. package/dist/components/IO/Attachments/LCDAttachment.d.ts +110 -0
  23. package/dist/components/IO/Attachments/LCDAttachment.js +716 -0
  24. package/dist/components/IO/Attachments/LCDAttachment.js.map +1 -0
  25. package/dist/components/IO/Attachments/SNESAttachment.d.ts +85 -0
  26. package/dist/components/IO/Attachments/SNESAttachment.js +184 -0
  27. package/dist/components/IO/Attachments/SNESAttachment.js.map +1 -0
  28. package/dist/components/IO/DevOutputBoard.d.ts +19 -0
  29. package/dist/components/IO/DevOutputBoard.js +33 -0
  30. package/dist/components/IO/DevOutputBoard.js.map +1 -0
  31. package/dist/components/IO/Empty.d.ts +9 -0
  32. package/dist/components/IO/Empty.js +5 -7
  33. package/dist/components/IO/Empty.js.map +1 -1
  34. package/dist/components/IO/EmptyCard.d.ts +9 -0
  35. package/dist/components/IO/GPIOAttachments/GPIOAttachment.d.ts +112 -0
  36. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.d.ts +53 -0
  37. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.d.ts +63 -0
  38. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.d.ts +44 -0
  39. package/dist/components/IO/GPIOAttachments/GPIOKeypadAttachment.d.ts +47 -0
  40. package/dist/components/IO/GPIOAttachments/GPIOLCDAttachment.d.ts +110 -0
  41. package/dist/components/IO/GPIOCard.d.ts +105 -0
  42. package/dist/components/IO/GPIOCard.js.map +1 -1
  43. package/dist/components/IO/RAMBank.d.ts +37 -0
  44. package/dist/components/IO/RAMBank.js +63 -0
  45. package/dist/components/IO/RAMBank.js.map +1 -0
  46. package/dist/components/IO/RAMCard.d.ts +37 -0
  47. package/dist/components/IO/RTC.d.ts +107 -0
  48. package/dist/components/IO/RTC.js +483 -0
  49. package/dist/components/IO/RTC.js.map +1 -0
  50. package/dist/components/IO/RTCCard.d.ts +107 -0
  51. package/dist/components/IO/SerialCard.d.ts +76 -0
  52. package/dist/components/IO/Sound.d.ts +120 -0
  53. package/dist/components/IO/Sound.js +622 -0
  54. package/dist/components/IO/Sound.js.map +1 -0
  55. package/dist/components/IO/SoundCard.d.ts +120 -0
  56. package/dist/components/IO/Storage.d.ts +74 -0
  57. package/dist/components/IO/Storage.js +409 -0
  58. package/dist/components/IO/Storage.js.map +1 -0
  59. package/dist/components/IO/StorageCard.d.ts +74 -0
  60. package/dist/components/IO/Terminal.d.ts +19 -0
  61. package/dist/components/IO/Terminal.js +33 -0
  62. package/dist/components/IO/Terminal.js.map +1 -0
  63. package/dist/components/IO/VIA.d.ts +105 -0
  64. package/dist/components/IO/VIA.js +597 -0
  65. package/dist/components/IO/VIA.js.map +1 -0
  66. package/dist/components/IO/Video.d.ts +141 -0
  67. package/dist/components/IO/Video.js +630 -0
  68. package/dist/components/IO/Video.js.map +1 -0
  69. package/dist/components/IO/VideoCard.d.ts +141 -0
  70. package/dist/components/IO.d.ts +8 -0
  71. package/dist/components/Machine.d.ts +62 -0
  72. package/dist/components/Machine.js +260 -153
  73. package/dist/components/Machine.js.map +1 -1
  74. package/dist/components/RAM.d.ts +9 -0
  75. package/dist/components/ROM.d.ts +9 -0
  76. package/dist/index.d.ts +2 -0
  77. package/dist/index.js +61 -28
  78. package/dist/index.js.map +1 -1
  79. package/dist/lib.d.ts +22 -0
  80. package/dist/lib.js +47 -0
  81. package/dist/lib.js.map +1 -0
  82. package/dist/tests/CPU.test.d.ts +1 -0
  83. package/dist/tests/Cart.test.d.ts +1 -0
  84. package/dist/tests/IO/ACIA.test.d.ts +1 -0
  85. package/dist/tests/IO/ACIA.test.js +423 -0
  86. package/dist/tests/IO/ACIA.test.js.map +1 -0
  87. package/dist/tests/IO/Attachments/Attachment.test.d.ts +1 -0
  88. package/dist/tests/IO/Attachments/Attachment.test.js +339 -0
  89. package/dist/tests/IO/Attachments/Attachment.test.js.map +1 -0
  90. package/dist/tests/IO/Attachments/JoystickAttachment.test.d.ts +1 -0
  91. package/dist/tests/IO/Attachments/JoystickAttachment.test.js +126 -0
  92. package/dist/tests/IO/Attachments/JoystickAttachment.test.js.map +1 -0
  93. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.d.ts +1 -0
  94. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +779 -0
  95. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -0
  96. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.d.ts +1 -0
  97. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js +355 -0
  98. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js.map +1 -0
  99. package/dist/tests/IO/Attachments/KeypadAttachment.test.d.ts +1 -0
  100. package/dist/tests/IO/Attachments/KeypadAttachment.test.js +323 -0
  101. package/dist/tests/IO/Attachments/KeypadAttachment.test.js.map +1 -0
  102. package/dist/tests/IO/Attachments/LCDAttachment.test.d.ts +1 -0
  103. package/dist/tests/IO/Attachments/LCDAttachment.test.js +627 -0
  104. package/dist/tests/IO/Attachments/LCDAttachment.test.js.map +1 -0
  105. package/dist/tests/IO/Attachments/SNESAttachment.test.d.ts +1 -0
  106. package/dist/tests/IO/Attachments/SNESAttachment.test.js +331 -0
  107. package/dist/tests/IO/Attachments/SNESAttachment.test.js.map +1 -0
  108. package/dist/tests/IO/Empty.test.d.ts +1 -0
  109. package/dist/tests/IO/Empty.test.js +121 -0
  110. package/dist/tests/IO/Empty.test.js.map +1 -0
  111. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.d.ts +1 -0
  112. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.d.ts +1 -0
  113. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.d.ts +1 -0
  114. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.d.ts +1 -0
  115. package/dist/tests/IO/GPIOAttachments/GPIOKeypadAttachment.test.d.ts +1 -0
  116. package/dist/tests/IO/GPIOAttachments/GPIOLCDAttachment.test.d.ts +1 -0
  117. package/dist/tests/IO/GPIOCard.test.d.ts +1 -0
  118. package/dist/tests/IO/GPIOCard.test.js.map +1 -1
  119. package/dist/tests/IO/RAMBank.test.d.ts +1 -0
  120. package/dist/tests/IO/RAMBank.test.js +229 -0
  121. package/dist/tests/IO/RAMBank.test.js.map +1 -0
  122. package/dist/tests/IO/RAMCard.test.d.ts +1 -0
  123. package/dist/tests/IO/RTC.test.d.ts +1 -0
  124. package/dist/tests/IO/RTC.test.js +177 -0
  125. package/dist/tests/IO/RTC.test.js.map +1 -0
  126. package/dist/tests/IO/RTCCard.test.d.ts +1 -0
  127. package/dist/tests/IO/SerialCard.test.d.ts +1 -0
  128. package/dist/tests/IO/Sound.test.d.ts +1 -0
  129. package/dist/tests/IO/Sound.test.js +528 -0
  130. package/dist/tests/IO/Sound.test.js.map +1 -0
  131. package/dist/tests/IO/SoundCard.test.d.ts +1 -0
  132. package/dist/tests/IO/Storage.test.d.ts +1 -0
  133. package/dist/tests/IO/Storage.test.js +656 -0
  134. package/dist/tests/IO/Storage.test.js.map +1 -0
  135. package/dist/tests/IO/StorageCard.test.d.ts +1 -0
  136. package/dist/tests/IO/VIA.test.d.ts +1 -0
  137. package/dist/tests/IO/VIA.test.js +503 -0
  138. package/dist/tests/IO/VIA.test.js.map +1 -0
  139. package/dist/tests/IO/Video.test.d.ts +1 -0
  140. package/dist/tests/IO/Video.test.js +549 -0
  141. package/dist/tests/IO/Video.test.js.map +1 -0
  142. package/dist/tests/IO/VideoCard.test.d.ts +1 -0
  143. package/dist/tests/Machine.test.d.ts +1 -0
  144. package/dist/tests/Machine.test.js +27 -42
  145. package/dist/tests/Machine.test.js.map +1 -1
  146. package/dist/tests/RAM.test.d.ts +1 -0
  147. package/dist/tests/ROM.test.d.ts +1 -0
  148. package/package.json +5 -3
  149. package/src/components/IO/{SerialCard.ts → ACIA.ts} +2 -2
  150. package/src/components/IO/{GPIOAttachments/GPIOAttachment.ts → Attachments/Attachment.ts} +2 -2
  151. package/src/components/IO/{GPIOAttachments/GPIOJoystickAttachment.ts → Attachments/JoystickAttachment.ts} +3 -3
  152. package/src/components/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.ts → Attachments/KeyboardEncoderAttachment.ts} +3 -3
  153. package/src/components/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.ts → Attachments/KeyboardMatrixAttachment.ts} +5 -5
  154. package/src/components/IO/{GPIOAttachments/GPIOKeypadAttachment.ts → Attachments/KeypadAttachment.ts} +3 -3
  155. package/src/components/IO/{GPIOAttachments/GPIOLCDAttachment.ts → Attachments/LCDAttachment.ts} +7 -7
  156. package/src/components/IO/{EmptyCard.ts → Empty.ts} +1 -1
  157. package/src/components/IO/{RAMCard.ts → RAMBank.ts} +8 -8
  158. package/src/components/IO/{RTCCard.ts → RTC.ts} +1 -1
  159. package/src/components/IO/{SoundCard.ts → Sound.ts} +2 -2
  160. package/src/components/IO/{StorageCard.ts → Storage.ts} +70 -73
  161. package/src/components/IO/Terminal.ts +34 -0
  162. package/src/components/IO/{GPIOCard.ts → VIA.ts} +64 -64
  163. package/src/components/IO/{VideoCard.ts → Video.ts} +1 -1
  164. package/src/components/Machine.ts +286 -160
  165. package/src/index.ts +65 -35
  166. package/src/lib.ts +27 -0
  167. package/src/tests/IO/{SerialCard.test.ts → ACIA.test.ts} +5 -5
  168. package/src/tests/IO/{GPIOAttachments/GPIOAttachment.test.ts → Attachments/Attachment.test.ts} +12 -12
  169. package/src/tests/IO/{GPIOAttachments/GPIOJoystickAttachment.test.ts → Attachments/JoystickAttachment.test.ts} +23 -23
  170. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts → Attachments/KeyboardEncoderAttachment.test.ts} +4 -4
  171. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts → Attachments/KeyboardMatrixAttachment.test.ts} +5 -5
  172. package/src/tests/IO/{GPIOAttachments/GPIOKeypadAttachment.test.ts → Attachments/KeypadAttachment.test.ts} +38 -38
  173. package/src/tests/IO/{GPIOAttachments/GPIOLCDAttachment.test.ts → Attachments/LCDAttachment.test.ts} +12 -12
  174. package/src/tests/IO/Empty.test.ts +143 -0
  175. package/src/tests/IO/{RAMCard.test.ts → RAMBank.test.ts} +33 -33
  176. package/src/tests/IO/{RTCCard.test.ts → RTC.test.ts} +6 -6
  177. package/src/tests/IO/{SoundCard.test.ts → Sound.test.ts} +6 -6
  178. package/src/tests/IO/{StorageCard.test.ts → Storage.test.ts} +34 -25
  179. package/src/tests/IO/{GPIOCard.test.ts → VIA.test.ts} +7 -7
  180. package/src/tests/IO/{VideoCard.test.ts → Video.test.ts} +13 -13
  181. package/src/tests/Machine.test.ts +31 -38
  182. package/tsconfig.json +1 -0
@@ -2,21 +2,21 @@ import { CPU } from './CPU'
2
2
  import { RAM } from './RAM'
3
3
  import { ROM } from './ROM'
4
4
  import { Cart } from './Cart'
5
- import { GPIOCard } from './IO/GPIOCard'
6
- import { RAMCard } from './IO/RAMCard'
7
- import { RTCCard } from './IO/RTCCard'
8
- import { SerialCard } from './IO/SerialCard'
9
- import { SoundCard } from './IO/SoundCard'
10
- import { StorageCard } from './IO/StorageCard'
11
- import { VideoCard } from './IO/VideoCard'
12
- import { GPIOKeyboardMatrixAttachment } from './IO/GPIOAttachments/GPIOKeyboardMatrixAttachment'
13
- import { GPIOKeyboardEncoderAttachment } from './IO/GPIOAttachments/GPIOKeyboardEncoderAttachment'
14
- import { GPIOJoystickAttachment } from './IO/GPIOAttachments/GPIOJoystickAttachment'
15
- import { GPIOLCDAttachment } from './IO/GPIOAttachments/GPIOLCDAttachment'
16
- import { GPIOKeypadAttachment } from './IO/GPIOAttachments/GPIOKeypadAttachment'
17
- import { EmptyCard } from './IO/EmptyCard'
5
+ import { VIA } from './IO/VIA'
6
+ import { RAMBank } from './IO/RAMBank'
7
+ import { RTC } from './IO/RTC'
8
+ import { ACIA } from './IO/ACIA'
9
+ import { Sound } from './IO/Sound'
10
+ import { Storage } from './IO/Storage'
11
+ import { Video } from './IO/Video'
12
+ import { KeyboardMatrixAttachment } from './IO/Attachments/KeyboardMatrixAttachment'
13
+ import { KeyboardEncoderAttachment } from './IO/Attachments/KeyboardEncoderAttachment'
14
+ import { JoystickAttachment } from './IO/Attachments/JoystickAttachment'
15
+ import { LCDAttachment } from './IO/Attachments/LCDAttachment'
16
+ import { KeypadAttachment } from './IO/Attachments/KeypadAttachment'
17
+ import { Empty } from './IO/Empty'
18
+ import { Terminal } from './IO/Terminal'
18
19
  import { IO } from './IO'
19
- import { readFile } from 'fs/promises'
20
20
 
21
21
  export class Machine {
22
22
 
@@ -25,184 +25,304 @@ export class Machine {
25
25
 
26
26
  private ioCycleAccumulator: number = 0
27
27
  private ioTickInterval: number = 128 // adjust (64/128/256)
28
+ private loopHandle?: ReturnType<typeof setImmediate> | ReturnType<typeof setTimeout>
28
29
 
29
30
  cpu: CPU
30
31
  ram: RAM
31
32
  rom: ROM
33
+ cart?: Cart
34
+
32
35
  io1: IO
33
36
  io2: IO
34
37
  io3: IO
35
38
  io4: IO
36
- io5: SerialCard
39
+ io5: IO
37
40
  io6: IO
38
41
  io7: IO
39
42
  io8: IO
40
43
 
41
- cart?: Cart
42
- kim: boolean
43
-
44
- // GPIO Attachments
45
- keyboardMatrixAttachment: GPIOKeyboardMatrixAttachment
46
- keyboardEncoderAttachment: GPIOKeyboardEncoderAttachment
47
- joystickAttachmentA: GPIOJoystickAttachment
48
- joystickAttachmentB: GPIOJoystickAttachment
44
+ // VIA Attachments
45
+ keyboardMatrixAttachment?: KeyboardMatrixAttachment
46
+ keyboardEncoderAttachment?: KeyboardEncoderAttachment
47
+ joystickAttachmentA?: JoystickAttachment
48
+ joystickAttachmentB?: JoystickAttachment
49
49
 
50
50
  // KIM mode attachments
51
- lcdAttachment?: GPIOLCDAttachment
52
- keypadAttachment?: GPIOKeypadAttachment
51
+ lcdAttachment?: LCDAttachment
52
+ keypadAttachment?: KeypadAttachment
53
53
 
54
- isAlive: boolean = false
54
+ target: string
55
55
  isRunning: boolean = false
56
56
  frequency: number = 2000000 // 2 MHz
57
57
  scale: number = 2
58
58
  frames: number = 0
59
- frameDelay: number = 0
60
- frameDelayCount: number = 0
61
59
  startTime: number = Date.now()
62
60
  previousTime: number = performance.now()
63
61
 
64
62
  transmit?: (data: number) => void
65
63
  render?: () => void
66
- pushAudioSamples?: (samples: Float32Array) => void
64
+ play?: (samples: Float32Array) => void
67
65
 
68
66
  //
69
67
  // Initialization
70
68
  //
71
69
 
72
- constructor(kim: boolean = false) {
73
- this.kim = kim
70
+ constructor(target: string) {
71
+ this.target = target
74
72
  this.cpu = new CPU(this.read.bind(this), this.write.bind(this))
75
73
  this.ram = new RAM()
76
74
  this.rom = new ROM()
77
75
 
78
- this.io5 = new SerialCard()
76
+ this.io1 = new Empty()
77
+ this.io2 = new Empty()
78
+ this.io3 = new Empty()
79
+ this.io4 = new Empty()
80
+ this.io5 = new Empty()
81
+ this.io6 = new Empty()
82
+ this.io7 = new Empty()
83
+ this.io8 = new Empty()
79
84
 
80
- // Connect SerialCard IRQ/NMI to CPU
81
- this.io5.raiseIRQ = () => this.cpu.irq()
82
- this.io5.raiseNMI = () => this.cpu.nmi()
85
+ this.configureTarget(target)
86
+
87
+ this.startTime = Date.now()
88
+ this.cpu.reset()
89
+ }
83
90
 
84
- // Connect SerialCard transmit callback
85
- this.io5.transmit = (data: number) => {
86
- if (this.transmit) {
87
- this.transmit(data)
88
- }
89
- }
91
+ configureTarget(target: string): void {
92
+ if (target === 'kim') {
93
+ const acia = new ACIA()
94
+ this.io5 = acia
90
95
 
91
- // Always create standard GPIO attachments (for type stability)
92
- this.keyboardMatrixAttachment = new GPIOKeyboardMatrixAttachment(10)
93
- this.keyboardEncoderAttachment = new GPIOKeyboardEncoderAttachment(20)
94
- this.joystickAttachmentA = new GPIOJoystickAttachment(false, 100)
95
- this.joystickAttachmentB = new GPIOJoystickAttachment(false, 100)
96
+ // Connect ACIA IRQ/NMI to CPU
97
+ acia.raiseIRQ = () => this.cpu.irq()
98
+ acia.raiseNMI = () => this.cpu.nmi()
96
99
 
97
- if (kim) {
98
- this.io1 = new EmptyCard()
99
- this.io2 = new EmptyCard()
100
- this.io3 = new EmptyCard()
101
- this.io4 = new EmptyCard()
102
- this.io6 = new EmptyCard()
103
- this.io7 = new EmptyCard()
100
+ // Connect ACIA transmit callback
101
+ acia.transmit = (data: number) => {
102
+ if (this.transmit) {
103
+ this.transmit(data)
104
+ }
105
+ }
106
+
107
+ this.io1 = new Empty()
108
+ this.io2 = new Empty()
109
+ this.io3 = new Empty()
110
+ this.io4 = new Empty()
111
+ this.io6 = new Empty()
112
+ this.io7 = new Empty()
104
113
 
105
- const gpioCard = new GPIOCard()
106
- this.io8 = gpioCard
114
+ const via = new VIA()
115
+ this.io8 = via
107
116
 
108
- // Connect GPIOCard IRQ/NMI to CPU
109
- gpioCard.raiseIRQ = () => this.cpu.irq()
110
- gpioCard.raiseNMI = () => this.cpu.nmi()
117
+ // Connect VIA IRQ/NMI to CPU
118
+ via.raiseIRQ = () => this.cpu.irq()
119
+ via.raiseNMI = () => this.cpu.nmi()
111
120
 
112
121
  // Create KIM GPIO Attachments
113
- this.lcdAttachment = new GPIOLCDAttachment(16, 2, 10)
114
- this.keypadAttachment = new GPIOKeypadAttachment(true, 20)
122
+ this.lcdAttachment = new LCDAttachment(16, 2, 10)
123
+ this.keypadAttachment = new KeypadAttachment(true, 20)
115
124
 
116
125
  // Attach LCD to Port A (control: RS/RW/E on bits 5-7) and Port B (data bus)
117
- gpioCard.attachToPortA(this.lcdAttachment)
118
- gpioCard.attachToPortB(this.lcdAttachment)
126
+ via.attachToPortA(this.lcdAttachment)
127
+ via.attachToPortB(this.lcdAttachment)
119
128
 
120
129
  // Attach keypad to Port A (bits 0-4)
121
- gpioCard.attachToPortA(this.keypadAttachment)
122
- } else {
123
- const rtcCard = new RTCCard()
124
- const storageCard = new StorageCard()
125
- const gpioCard = new GPIOCard()
126
- const soundCard = new SoundCard()
127
- const videoCard = new VideoCard()
128
-
129
- this.io1 = new RAMCard()
130
- this.io2 = new RAMCard()
131
- this.io3 = rtcCard
132
- this.io4 = storageCard
133
- this.io6 = gpioCard
134
- this.io7 = soundCard
135
- this.io8 = videoCard
136
-
137
- // Connect RTCCard IRQ/NMI to CPU
138
- rtcCard.raiseIRQ = () => this.cpu.irq()
139
- rtcCard.raiseNMI = () => this.cpu.nmi()
140
-
141
- // Connect VideoCard IRQ/NMI to CPU
142
- videoCard.raiseIRQ = () => this.cpu.irq()
143
- videoCard.raiseNMI = () => this.cpu.nmi()
144
-
145
- // Connect SoundCard pushSamples callback
146
- soundCard.pushSamples = (samples: Float32Array) => {
147
- if (this.pushAudioSamples) {
148
- this.pushAudioSamples(samples)
130
+ via.attachToPortA(this.keypadAttachment)
131
+ } else if (target === 'dev') {
132
+ const acia = new ACIA()
133
+ this.io5 = acia
134
+
135
+ // Connect ACIA IRQ/NMI to CPU
136
+ acia.raiseIRQ = () => this.cpu.irq()
137
+ acia.raiseNMI = () => this.cpu.nmi()
138
+
139
+ // Connect ACIA transmit callback
140
+ acia.transmit = (data: number) => {
141
+ if (this.transmit) {
142
+ this.transmit(data)
149
143
  }
150
144
  }
151
145
 
146
+ const rtc = new RTC()
147
+ const storage = new Storage()
148
+ const via = new VIA()
149
+
150
+ this.io1 = new RAMBank()
151
+ this.io2 = new RAMBank()
152
+ this.io3 = rtc
153
+ this.io4 = storage
154
+ this.io6 = via
155
+ this.io7 = new Empty()
156
+ this.io8 = new Terminal()
157
+
158
+ // Connect RTC IRQ/NMI to CPU
159
+ rtc.raiseIRQ = () => this.cpu.irq()
160
+ rtc.raiseNMI = () => this.cpu.nmi()
161
+
162
+ // Create standard GPIO attachments
163
+ this.keyboardMatrixAttachment = new KeyboardMatrixAttachment(10)
164
+ this.keyboardEncoderAttachment = new KeyboardEncoderAttachment(20)
165
+ this.joystickAttachmentA = new JoystickAttachment(false, 100)
166
+ this.joystickAttachmentB = new JoystickAttachment(false, 100)
167
+
152
168
  // Attach peripherals to GPIO Card
153
- gpioCard.attachToPortA(this.keyboardMatrixAttachment)
154
- gpioCard.attachToPortB(this.keyboardMatrixAttachment)
155
- gpioCard.attachToPortA(this.keyboardEncoderAttachment)
156
- gpioCard.attachToPortB(this.keyboardEncoderAttachment)
157
- gpioCard.attachToPortA(this.joystickAttachmentA)
158
- gpioCard.attachToPortB(this.joystickAttachmentB)
159
- }
169
+ via.attachToPortA(this.keyboardMatrixAttachment)
170
+ via.attachToPortB(this.keyboardMatrixAttachment)
171
+ via.attachToPortA(this.keyboardEncoderAttachment)
172
+ via.attachToPortB(this.keyboardEncoderAttachment)
173
+ via.attachToPortA(this.joystickAttachmentA)
174
+ via.attachToPortB(this.joystickAttachmentB)
175
+ } else if (target === 'vcs') {
176
+ this.io5 = new Empty()
177
+
178
+ const via = new VIA()
179
+ const sound = new Sound()
180
+ const video = new Video()
181
+
182
+ this.io1 = new Empty()
183
+ this.io2 = new Empty()
184
+ this.io3 = new Empty()
185
+ this.io4 = new Empty()
186
+ this.io6 = via
187
+ this.io7 = sound
188
+ this.io8 = video
189
+
190
+ // Connect Video IRQ/NMI to CPU
191
+ video.raiseIRQ = () => this.cpu.irq()
192
+ video.raiseNMI = () => this.cpu.nmi()
193
+
194
+ // Connect Sound pushSamples callback
195
+ sound.pushSamples = (samples: Float32Array) => {
196
+ if (this.play) {
197
+ this.play(samples)
198
+ }
199
+ }
160
200
 
161
- this.cpu.reset()
201
+ // Create standard GPIO attachments
202
+ this.keyboardMatrixAttachment = new KeyboardMatrixAttachment(10)
203
+ this.keyboardEncoderAttachment = new KeyboardEncoderAttachment(20)
204
+ this.joystickAttachmentA = new JoystickAttachment(false, 100)
205
+ this.joystickAttachmentB = new JoystickAttachment(false, 100)
206
+
207
+ // Attach peripherals to GPIO Card
208
+ via.attachToPortA(this.keyboardMatrixAttachment)
209
+ via.attachToPortB(this.keyboardMatrixAttachment)
210
+ via.attachToPortA(this.keyboardEncoderAttachment)
211
+ via.attachToPortB(this.keyboardEncoderAttachment)
212
+ via.attachToPortA(this.joystickAttachmentA)
213
+ via.attachToPortB(this.joystickAttachmentB)
214
+ } else if (target === 'cob') {
215
+ const acia = new ACIA()
216
+ this.io5 = acia
217
+
218
+ // Connect ACIA IRQ/NMI to CPU
219
+ acia.raiseIRQ = () => this.cpu.irq()
220
+ acia.raiseNMI = () => this.cpu.nmi()
221
+
222
+ // Connect ACIA transmit callback
223
+ acia.transmit = (data: number) => {
224
+ if (this.transmit) {
225
+ this.transmit(data)
226
+ }
227
+ }
228
+
229
+ const rtc = new RTC()
230
+ const storage = new Storage()
231
+ const gpio = new VIA()
232
+ const sound = new Sound()
233
+ const video = new Video()
234
+
235
+ this.io1 = new RAMBank()
236
+ this.io2 = new RAMBank()
237
+ this.io3 = rtc
238
+ this.io4 = storage
239
+ this.io6 = gpio
240
+ this.io7 = sound
241
+ this.io8 = video
242
+
243
+ // Connect RTC IRQ/NMI to CPU
244
+ rtc.raiseIRQ = () => this.cpu.irq()
245
+ rtc.raiseNMI = () => this.cpu.nmi()
246
+
247
+ // Connect Video IRQ/NMI to CPU
248
+ video.raiseIRQ = () => this.cpu.irq()
249
+ video.raiseNMI = () => this.cpu.nmi()
250
+
251
+ // Connect Sound pushSamples callback
252
+ sound.pushSamples = (samples: Float32Array) => {
253
+ if (this.play) {
254
+ this.play(samples)
255
+ }
256
+ }
257
+
258
+ // Create standard GPIO attachments
259
+ this.keyboardMatrixAttachment = new KeyboardMatrixAttachment(10)
260
+ this.keyboardEncoderAttachment = new KeyboardEncoderAttachment(20)
261
+ this.joystickAttachmentA = new JoystickAttachment(false, 100)
262
+ this.joystickAttachmentB = new JoystickAttachment(false, 100)
263
+
264
+ // Attach peripherals to GPIO Card
265
+ gpio.attachToPortA(this.keyboardMatrixAttachment)
266
+ gpio.attachToPortB(this.keyboardMatrixAttachment)
267
+ gpio.attachToPortA(this.keyboardEncoderAttachment)
268
+ gpio.attachToPortB(this.keyboardEncoderAttachment)
269
+ gpio.attachToPortA(this.joystickAttachmentA)
270
+ gpio.attachToPortB(this.joystickAttachmentB)
271
+ } else {
272
+ this.io1 = new Empty()
273
+ this.io2 = new Empty()
274
+ this.io3 = new Empty()
275
+ this.io4 = new Empty()
276
+ this.io5 = new Empty()
277
+ this.io6 = new Empty()
278
+ this.io7 = new Empty()
279
+ this.io8 = new Empty()
280
+ }
162
281
  }
163
282
 
164
283
  //
165
284
  // Methods
166
285
  //
167
286
 
168
- loadROM = async (path: string) => {
169
- try {
170
- this.rom.load(Array.from(new Uint8Array(await readFile(path))))
171
- } catch (error) {
172
- console.error('Error reading file:', error)
287
+ loadROM = (data: Uint8Array | number[] | ArrayBuffer) => {
288
+ if (data instanceof ArrayBuffer) {
289
+ this.rom.load(Array.from(new Uint8Array(data)))
290
+ } else if (data instanceof Uint8Array) {
291
+ this.rom.load(Array.from(data))
292
+ } else {
293
+ this.rom.load(data)
173
294
  }
174
295
  }
175
296
 
176
- loadCart = async (path: string) => {
177
- try {
178
- const data = Array.from(new Uint8Array(await readFile(path)))
179
- const cart = new Cart()
180
- cart.load(data)
181
- this.cart = cart
182
- } catch (error) {
183
- console.error('Error reading file:', error)
297
+ loadCart = (data: Uint8Array | number[] | ArrayBuffer) => {
298
+ let dataArray: number[]
299
+ if (data instanceof ArrayBuffer) {
300
+ dataArray = Array.from(new Uint8Array(data))
301
+ } else if (data instanceof Uint8Array) {
302
+ dataArray = Array.from(data)
303
+ } else {
304
+ dataArray = data
184
305
  }
185
- }
186
-
187
- start(): void {
188
- this.cpu.reset()
189
- this.startTime = Date.now()
190
- this.isRunning = true
191
- this.isAlive = true
192
- this.loop()
193
- }
194
-
195
- end(): void {
196
- this.isRunning = false
197
- this.isAlive = false
306
+ const cart = new Cart()
307
+ cart.load(dataArray)
308
+ this.cart = cart
198
309
  }
199
310
 
200
311
  run(): void {
201
312
  this.isRunning = true
313
+ this.loop()
202
314
  }
203
315
 
204
316
  stop(): void {
205
317
  this.isRunning = false
318
+ if (this.loopHandle) {
319
+ if (typeof clearImmediate !== 'undefined') {
320
+ clearImmediate(this.loopHandle as any)
321
+ } else {
322
+ clearTimeout(this.loopHandle as any)
323
+ }
324
+ this.loopHandle = undefined
325
+ }
206
326
  }
207
327
 
208
328
  step(): void {
@@ -211,12 +331,12 @@ export class Machine {
211
331
 
212
332
  // Tick IO cards for each cycle of the instruction
213
333
  for (let i = 0; i < cyclesExecuted; i++) {
214
- // SerialCard must be cycle-accurate
334
+ // ACIA must be cycle-accurate
215
335
  this.io5.tick(this.frequency)
216
336
 
217
337
  this.ioCycleAccumulator++
218
338
  if (this.ioCycleAccumulator >= this.ioTickInterval) {
219
- // Skip ticking RAMCard IO1 and IO2 since they have no timing behavior
339
+ // Skip ticking RAMBank IO1 and IO2 since they have no timing behavior
220
340
  this.io3.tick(this.frequency)
221
341
  this.io4.tick(this.frequency)
222
342
  this.io6.tick(this.frequency)
@@ -227,17 +347,30 @@ export class Machine {
227
347
  }
228
348
  }
229
349
 
350
+ reset(coldStart: boolean): void {
351
+ this.cpu.reset()
352
+ this.ram.reset(coldStart)
353
+ this.io1.reset(coldStart)
354
+ this.io2.reset(coldStart)
355
+ this.io3.reset(coldStart)
356
+ this.io4.reset(coldStart)
357
+ this.io5.reset(coldStart)
358
+ this.io6.reset(coldStart)
359
+ this.io7.reset(coldStart)
360
+ this.io8.reset(coldStart)
361
+ }
362
+
230
363
  tick(): void {
231
364
  // Execute one CPU clock cycle
232
365
  this.cpu.tick()
233
366
 
234
- // SerialCard must be cycle-accurate
367
+ // ACIA must be cycle-accurate
235
368
  this.io5.tick(this.frequency)
236
369
 
237
370
  // Tick other IO cards at intervals
238
371
  this.ioCycleAccumulator++
239
372
  if (this.ioCycleAccumulator >= this.ioTickInterval) {
240
- // Skip ticking RAMCard IO1 and IO2 since they have no timing behavior
373
+ // Skip ticking RAMBank IO1 and IO2 since they have no timing behavior
241
374
  this.io3.tick(this.frequency)
242
375
  this.io4.tick(this.frequency)
243
376
  this.io6.tick(this.frequency)
@@ -248,22 +381,24 @@ export class Machine {
248
381
  }
249
382
 
250
383
  onReceive(data: number): void {
251
- this.io5.onData(data) // Pass data to Serial card
384
+ if (this.target !== 'vcs') {
385
+ (this.io5 as ACIA).onData(data) // Pass data to Serial card
386
+ }
252
387
  }
253
388
 
254
389
  onKeyDown(scancode: number): void {
255
- if (this.kim) {
390
+ if (this.target === 'kim') {
256
391
  this.keypadAttachment?.updateKey(scancode, true)
257
392
  } else {
258
- this.keyboardMatrixAttachment.updateKey(scancode, true)
259
- this.keyboardEncoderAttachment.updateKey(scancode, true)
393
+ this.keyboardMatrixAttachment?.updateKey(scancode, true)
394
+ this.keyboardEncoderAttachment?.updateKey(scancode, true)
260
395
  }
261
396
  }
262
397
 
263
398
  onKeyUp(scancode: number): void {
264
- if (!this.kim) {
265
- this.keyboardMatrixAttachment.updateKey(scancode, false)
266
- this.keyboardEncoderAttachment.updateKey(scancode, false)
399
+ if (this.target !== 'kim') {
400
+ this.keyboardMatrixAttachment?.updateKey(scancode, false)
401
+ this.keyboardEncoderAttachment?.updateKey(scancode, false)
267
402
  }
268
403
  }
269
404
 
@@ -280,8 +415,6 @@ export class Machine {
280
415
  //
281
416
 
282
417
  private loop(): void {
283
- if (!this.isAlive) { return }
284
-
285
418
  const now = performance.now()
286
419
  const elapsedMs = now - this.previousTime
287
420
  this.previousTime = now
@@ -299,12 +432,12 @@ export class Machine {
299
432
  for (let i = 0; i < ticksToRun; i++) {
300
433
  this.cpu.tick()
301
434
 
302
- // SerialCard must be cycle-accurate
435
+ // ACIA must be cycle-accurate
303
436
  this.io5.tick(this.frequency)
304
437
 
305
438
  this.ioCycleAccumulator++
306
439
  if (this.ioCycleAccumulator >= this.ioTickInterval) {
307
- // Skip ticking RAMCard IO1 and IO2 since they have no timing behavior
440
+ // Skip ticking RAMBank IO1 and IO2 since they have no timing behavior
308
441
  this.io3.tick(this.frequency)
309
442
  this.io4.tick(this.frequency)
310
443
  this.io6.tick(this.frequency)
@@ -319,38 +452,31 @@ export class Machine {
319
452
  (this as any)._accumulatorMs = accumulator
320
453
  }
321
454
 
322
- if (this.render && !this.kim) {
323
- const videoCard = this.io8 as VideoCard
324
- if (videoCard.frameReady) {
325
- videoCard.frameReady = false
455
+ if (this.render && (this.target === 'kim' || this.target === 'dev')) {
456
+ this.render()
457
+ this.frames += 1
458
+ } else if (this.render && (this.target === 'cob' || this.target === 'vcs')) {
459
+ const Video = this.io8 as Video
460
+ if (Video.frameReady) {
461
+ Video.frameReady = false
326
462
  this.render()
327
463
  this.frames += 1
328
464
  }
329
- } else if (this.render && this.kim) {
330
- this.render()
331
- this.frames += 1
332
465
  }
333
466
 
334
- setImmediate(() => this.loop())
467
+ if (this.isRunning) {
468
+ if (typeof setImmediate !== 'undefined') {
469
+ this.loopHandle = setImmediate(() => this.loop())
470
+ } else {
471
+ this.loopHandle = setTimeout(() => this.loop(), 0)
472
+ }
473
+ }
335
474
  }
336
475
 
337
476
  //
338
477
  // Bus Operations
339
478
  //
340
479
 
341
- reset(coldStart: boolean): void {
342
- this.cpu.reset()
343
- this.ram.reset(coldStart)
344
- this.io1.reset(coldStart)
345
- this.io2.reset(coldStart)
346
- this.io3.reset(coldStart)
347
- this.io4.reset(coldStart)
348
- this.io5.reset(coldStart)
349
- this.io6.reset(coldStart)
350
- this.io7.reset(coldStart)
351
- this.io8.reset(coldStart)
352
- }
353
-
354
480
  read(address: number): number {
355
481
  switch(true) {
356
482
  case (this.cart && address >= Cart.CODE && address <= Cart.END):