ac6502 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/components/CPU.js +1170 -0
- package/dist/components/CPU.js.map +1 -0
- package/dist/components/Cart.js +23 -0
- package/dist/components/Cart.js.map +1 -0
- package/dist/components/IO/Empty.js +19 -0
- package/dist/components/IO/Empty.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOAttachment.js +71 -0
- package/dist/components/IO/GPIOAttachments/GPIOAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js +90 -0
- package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js +489 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js +274 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js.map +1 -0
- package/dist/components/IO/GPIOCard.js +597 -0
- package/dist/components/IO/GPIOCard.js.map +1 -0
- package/dist/components/IO/InputBoard.js +19 -0
- package/dist/components/IO/InputBoard.js.map +1 -0
- package/dist/components/IO/LCDCard.js +19 -0
- package/dist/components/IO/LCDCard.js.map +1 -0
- package/dist/components/IO/RAMCard.js +63 -0
- package/dist/components/IO/RAMCard.js.map +1 -0
- package/dist/components/IO/RTCCard.js +483 -0
- package/dist/components/IO/RTCCard.js.map +1 -0
- package/dist/components/IO/SerialCard.js +282 -0
- package/dist/components/IO/SerialCard.js.map +1 -0
- package/dist/components/IO/SoundCard.js +620 -0
- package/dist/components/IO/SoundCard.js.map +1 -0
- package/dist/components/IO/StorageCard.js +428 -0
- package/dist/components/IO/StorageCard.js.map +1 -0
- package/dist/components/IO/VGACard.js +9 -0
- package/dist/components/IO/VGACard.js.map +1 -0
- package/dist/components/IO/VideoCard.js +623 -0
- package/dist/components/IO/VideoCard.js.map +1 -0
- package/dist/components/IO.js +3 -0
- package/dist/components/IO.js.map +1 -0
- package/dist/components/Machine.js +310 -0
- package/dist/components/Machine.js.map +1 -0
- package/dist/components/RAM.js +24 -0
- package/dist/components/RAM.js.map +1 -0
- package/dist/components/ROM.js +23 -0
- package/dist/components/ROM.js.map +1 -0
- package/dist/index.js +441 -0
- package/dist/index.js.map +1 -0
- package/dist/tests/CPU.test.js +1626 -0
- package/dist/tests/CPU.test.js.map +1 -0
- package/dist/tests/Cart.test.js +119 -0
- package/dist/tests/Cart.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js +339 -0
- package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js +126 -0
- package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js +779 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js +355 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOCard.test.js +503 -0
- package/dist/tests/IO/GPIOCard.test.js.map +1 -0
- package/dist/tests/IO/RAMCard.test.js +229 -0
- package/dist/tests/IO/RAMCard.test.js.map +1 -0
- package/dist/tests/IO/RTCCard.test.js +177 -0
- package/dist/tests/IO/RTCCard.test.js.map +1 -0
- package/dist/tests/IO/SerialCard.test.js +423 -0
- package/dist/tests/IO/SerialCard.test.js.map +1 -0
- package/dist/tests/IO/SoundCard.test.js +528 -0
- package/dist/tests/IO/SoundCard.test.js.map +1 -0
- package/dist/tests/IO/StorageCard.test.js +647 -0
- package/dist/tests/IO/StorageCard.test.js.map +1 -0
- package/dist/tests/IO/VideoCard.test.js +549 -0
- package/dist/tests/IO/VideoCard.test.js.map +1 -0
- package/dist/tests/Machine.test.js +383 -0
- package/dist/tests/Machine.test.js.map +1 -0
- package/dist/tests/RAM.test.js +160 -0
- package/dist/tests/RAM.test.js.map +1 -0
- package/dist/tests/ROM.test.js +123 -0
- package/dist/tests/ROM.test.js.map +1 -0
- package/jest.config.cjs +9 -0
- package/package.json +43 -0
- package/src/components/CPU.ts +1371 -0
- package/src/components/Cart.ts +20 -0
- package/src/components/IO/GPIOAttachments/GPIOAttachment.ts +189 -0
- package/src/components/IO/GPIOAttachments/GPIOJoystickAttachment.ts +99 -0
- package/src/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.ts +465 -0
- package/src/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.ts +287 -0
- package/src/components/IO/GPIOCard.ts +677 -0
- package/src/components/IO/RAMCard.ts +68 -0
- package/src/components/IO/RTCCard.ts +518 -0
- package/src/components/IO/SerialCard.ts +335 -0
- package/src/components/IO/SoundCard.ts +711 -0
- package/src/components/IO/StorageCard.ts +473 -0
- package/src/components/IO/VideoCard.ts +730 -0
- package/src/components/IO.ts +11 -0
- package/src/components/Machine.ts +364 -0
- package/src/components/RAM.ts +23 -0
- package/src/components/ROM.ts +19 -0
- package/src/index.ts +474 -0
- package/src/tests/CPU.test.ts +2045 -0
- package/src/tests/Cart.test.ts +149 -0
- package/src/tests/IO/GPIOAttachments/GPIOAttachment.test.ts +413 -0
- package/src/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.ts +147 -0
- package/src/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts +961 -0
- package/src/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts +449 -0
- package/src/tests/IO/GPIOCard.test.ts +644 -0
- package/src/tests/IO/RAMCard.test.ts +284 -0
- package/src/tests/IO/RTCCard.test.ts +222 -0
- package/src/tests/IO/SerialCard.test.ts +530 -0
- package/src/tests/IO/SoundCard.test.ts +659 -0
- package/src/tests/IO/StorageCard.test.ts +787 -0
- package/src/tests/IO/VideoCard.test.ts +668 -0
- package/src/tests/Machine.test.ts +437 -0
- package/src/tests/RAM.test.ts +196 -0
- package/src/tests/ROM.test.ts +154 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { CPU } from './CPU'
|
|
2
|
+
import { RAM } from './RAM'
|
|
3
|
+
import { ROM } from './ROM'
|
|
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 { readFile } from 'fs/promises'
|
|
16
|
+
|
|
17
|
+
export class Machine {
|
|
18
|
+
|
|
19
|
+
static MAX_FPS: number = 60
|
|
20
|
+
static FRAME_INTERVAL_MS: number = 1000 / Machine.MAX_FPS
|
|
21
|
+
|
|
22
|
+
private ioCycleAccumulator: number = 0
|
|
23
|
+
private ioTickInterval: number = 128 // adjust (64/128/256)
|
|
24
|
+
|
|
25
|
+
cpu: CPU
|
|
26
|
+
ram: RAM
|
|
27
|
+
rom: ROM
|
|
28
|
+
io1: RAMCard
|
|
29
|
+
io2: RAMCard
|
|
30
|
+
io3: RTCCard
|
|
31
|
+
io4: StorageCard
|
|
32
|
+
io5: SerialCard
|
|
33
|
+
io6: GPIOCard
|
|
34
|
+
io7: SoundCard
|
|
35
|
+
io8: VideoCard
|
|
36
|
+
|
|
37
|
+
cart?: Cart
|
|
38
|
+
|
|
39
|
+
// GPIO Attachments
|
|
40
|
+
keyboardMatrixAttachment: GPIOKeyboardMatrixAttachment
|
|
41
|
+
keyboardEncoderAttachment: GPIOKeyboardEncoderAttachment
|
|
42
|
+
joystickAttachment: GPIOJoystickAttachment
|
|
43
|
+
|
|
44
|
+
isAlive: boolean = false
|
|
45
|
+
isRunning: boolean = false
|
|
46
|
+
frequency: number = 2000000 // 2 MHz
|
|
47
|
+
scale: number = 2
|
|
48
|
+
frames: number = 0
|
|
49
|
+
frameDelay: number = 0
|
|
50
|
+
frameDelayCount: number = 0
|
|
51
|
+
startTime: number = Date.now()
|
|
52
|
+
previousTime: number = performance.now()
|
|
53
|
+
|
|
54
|
+
transmit?: (data: number) => void
|
|
55
|
+
render?: (buffer: Buffer<ArrayBufferLike>) => void
|
|
56
|
+
pushAudioSamples?: (samples: Float32Array) => void
|
|
57
|
+
|
|
58
|
+
//
|
|
59
|
+
// Initialization
|
|
60
|
+
//
|
|
61
|
+
|
|
62
|
+
constructor() {
|
|
63
|
+
this.cpu = new CPU(this.read.bind(this), this.write.bind(this))
|
|
64
|
+
this.ram = new RAM()
|
|
65
|
+
this.rom = new ROM()
|
|
66
|
+
this.io1 = new RAMCard()
|
|
67
|
+
this.io2 = new RAMCard()
|
|
68
|
+
this.io3 = new RTCCard()
|
|
69
|
+
this.io4 = new StorageCard()
|
|
70
|
+
this.io5 = new SerialCard()
|
|
71
|
+
this.io6 = new GPIOCard()
|
|
72
|
+
this.io7 = new SoundCard()
|
|
73
|
+
this.io8 = new VideoCard()
|
|
74
|
+
|
|
75
|
+
// Connect RTCCard IRQ/NMI to CPU
|
|
76
|
+
this.io3.raiseIRQ = () => this.cpu.irq()
|
|
77
|
+
this.io3.raiseNMI = () => this.cpu.nmi()
|
|
78
|
+
|
|
79
|
+
// Connect SerialCard IRQ/NMI to CPU
|
|
80
|
+
this.io5.raiseIRQ = () => this.cpu.irq()
|
|
81
|
+
this.io5.raiseNMI = () => this.cpu.nmi()
|
|
82
|
+
|
|
83
|
+
// Connect SerialCard transmit callback (use arrow function to look up this.transmit at call time)
|
|
84
|
+
this.io5.transmit = (data: number) => {
|
|
85
|
+
if (this.transmit) {
|
|
86
|
+
this.transmit(data)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Connect VideoCard IRQ/NMI to CPU
|
|
91
|
+
this.io8.raiseIRQ = () => this.cpu.irq()
|
|
92
|
+
this.io8.raiseNMI = () => this.cpu.nmi()
|
|
93
|
+
|
|
94
|
+
// Connect SoundCard pushSamples callback (use arrow function to look up this.pushAudioSamples at call time)
|
|
95
|
+
this.io7.pushSamples = (samples: Float32Array) => {
|
|
96
|
+
if (this.pushAudioSamples) {
|
|
97
|
+
this.pushAudioSamples(samples)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Create GPIO Attachments
|
|
102
|
+
// Keyboard matrix (manual scanning) - highest priority for Port A rows (priority 10)
|
|
103
|
+
this.keyboardMatrixAttachment = new GPIOKeyboardMatrixAttachment(10)
|
|
104
|
+
|
|
105
|
+
// Keyboard encoder (ASCII on both Port A and Port B) - medium priority (priority 20)
|
|
106
|
+
this.keyboardEncoderAttachment = new GPIOKeyboardEncoderAttachment(20)
|
|
107
|
+
|
|
108
|
+
// Joystick (Port B) - lowest priority, fallback (priority 100)
|
|
109
|
+
this.joystickAttachment = new GPIOJoystickAttachment(false, 100)
|
|
110
|
+
|
|
111
|
+
// Attach peripherals to GPIO Card
|
|
112
|
+
// Keyboard matrix supports both ports
|
|
113
|
+
this.io6.attachToPortA(this.keyboardMatrixAttachment)
|
|
114
|
+
this.io6.attachToPortB(this.keyboardMatrixAttachment)
|
|
115
|
+
|
|
116
|
+
// Keyboard encoder supports both ports
|
|
117
|
+
this.io6.attachToPortA(this.keyboardEncoderAttachment)
|
|
118
|
+
this.io6.attachToPortB(this.keyboardEncoderAttachment)
|
|
119
|
+
|
|
120
|
+
// Joystick attached to Port B only
|
|
121
|
+
this.io6.attachToPortB(this.joystickAttachment)
|
|
122
|
+
|
|
123
|
+
this.cpu.reset()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//
|
|
127
|
+
// Methods
|
|
128
|
+
//
|
|
129
|
+
|
|
130
|
+
loadROM = async (path: string) => {
|
|
131
|
+
try {
|
|
132
|
+
this.rom.load(Array.from(new Uint8Array(await readFile(path))))
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error('Error reading file:', error)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
loadCart = async (path: string) => {
|
|
139
|
+
try {
|
|
140
|
+
const data = Array.from(new Uint8Array(await readFile(path)))
|
|
141
|
+
const cart = new Cart()
|
|
142
|
+
cart.load(data)
|
|
143
|
+
this.cart = cart
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error('Error reading file:', error)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
start(): void {
|
|
150
|
+
this.startTime = Date.now()
|
|
151
|
+
this.isRunning = true
|
|
152
|
+
this.isAlive = true
|
|
153
|
+
this.loop()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
end(): void {
|
|
157
|
+
this.isRunning = false
|
|
158
|
+
this.isAlive = false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
run(): void {
|
|
162
|
+
this.isRunning = true
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
stop(): void {
|
|
166
|
+
this.isRunning = false
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
step(): void {
|
|
170
|
+
// Step through one complete instruction
|
|
171
|
+
const cyclesExecuted = this.cpu.step()
|
|
172
|
+
|
|
173
|
+
// Tick IO cards for each cycle of the instruction
|
|
174
|
+
for (let i = 0; i < cyclesExecuted; i++) {
|
|
175
|
+
// SerialCard must be cycle-accurate
|
|
176
|
+
this.io5.tick(this.frequency)
|
|
177
|
+
|
|
178
|
+
this.ioCycleAccumulator++
|
|
179
|
+
if (this.ioCycleAccumulator >= this.ioTickInterval) {
|
|
180
|
+
// Skip ticking RAMCard IO1 and IO2 since they have no timing behavior
|
|
181
|
+
this.io3.tick(this.frequency)
|
|
182
|
+
this.io4.tick(this.frequency)
|
|
183
|
+
this.io6.tick(this.frequency)
|
|
184
|
+
this.io7.tick(this.frequency)
|
|
185
|
+
this.io8.tick(this.frequency)
|
|
186
|
+
this.ioCycleAccumulator = 0
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
tick(): void {
|
|
192
|
+
// Execute one CPU clock cycle
|
|
193
|
+
this.cpu.tick()
|
|
194
|
+
|
|
195
|
+
// SerialCard must be cycle-accurate
|
|
196
|
+
this.io5.tick(this.frequency)
|
|
197
|
+
|
|
198
|
+
// Tick other IO cards at intervals
|
|
199
|
+
this.ioCycleAccumulator++
|
|
200
|
+
if (this.ioCycleAccumulator >= this.ioTickInterval) {
|
|
201
|
+
// Skip ticking RAMCard IO1 and IO2 since they have no timing behavior
|
|
202
|
+
this.io3.tick(this.frequency)
|
|
203
|
+
this.io4.tick(this.frequency)
|
|
204
|
+
this.io6.tick(this.frequency)
|
|
205
|
+
this.io7.tick(this.frequency)
|
|
206
|
+
this.io8.tick(this.frequency)
|
|
207
|
+
this.ioCycleAccumulator = 0
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
onReceive(data: number): void {
|
|
212
|
+
this.io5.onData(data) // Pass data to Serial card
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
onKeyDown(scancode: number): void {
|
|
216
|
+
// Route keyboard input to attachments
|
|
217
|
+
this.keyboardMatrixAttachment.updateKey(scancode, true) // Update keyboard matrix
|
|
218
|
+
this.keyboardEncoderAttachment.updateKey(scancode, true) // Update keyboard encoder
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
onKeyUp(scancode: number): void {
|
|
222
|
+
// Release key from keyboard matrix and encoder
|
|
223
|
+
this.keyboardMatrixAttachment.updateKey(scancode, false) // Update keyboard matrix
|
|
224
|
+
this.keyboardEncoderAttachment.updateKey(scancode, false) // Update keyboard encoder
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
onJoystick(buttons: number): void {
|
|
228
|
+
// Update joystick attachment with button states
|
|
229
|
+
this.joystickAttachment.updateJoystick(buttons)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
//
|
|
233
|
+
// Loop Operations
|
|
234
|
+
//
|
|
235
|
+
|
|
236
|
+
private loop(): void {
|
|
237
|
+
if (!this.isAlive) { return }
|
|
238
|
+
|
|
239
|
+
const now = performance.now()
|
|
240
|
+
const elapsedMs = now - this.previousTime
|
|
241
|
+
this.previousTime = now
|
|
242
|
+
|
|
243
|
+
if (this.isRunning) {
|
|
244
|
+
const ticksPerMs = this.frequency / 1000
|
|
245
|
+
let accumulator = (this as any)._accumulatorMs ?? 0
|
|
246
|
+
accumulator += elapsedMs
|
|
247
|
+
|
|
248
|
+
const maxCatchUpMs = 250
|
|
249
|
+
if (accumulator > maxCatchUpMs) accumulator = maxCatchUpMs
|
|
250
|
+
|
|
251
|
+
const ticksToRun = Math.floor(accumulator * ticksPerMs)
|
|
252
|
+
if (ticksToRun > 0) {
|
|
253
|
+
for (let i = 0; i < ticksToRun; i++) {
|
|
254
|
+
this.cpu.tick()
|
|
255
|
+
|
|
256
|
+
// SerialCard must be cycle-accurate
|
|
257
|
+
this.io5.tick(this.frequency)
|
|
258
|
+
|
|
259
|
+
this.ioCycleAccumulator++
|
|
260
|
+
if (this.ioCycleAccumulator >= this.ioTickInterval) {
|
|
261
|
+
// Skip ticking RAMCard IO1 and IO2 since they have no timing behavior
|
|
262
|
+
this.io3.tick(this.frequency)
|
|
263
|
+
this.io4.tick(this.frequency)
|
|
264
|
+
this.io6.tick(this.frequency)
|
|
265
|
+
this.io7.tick(this.frequency)
|
|
266
|
+
this.io8.tick(this.frequency)
|
|
267
|
+
this.ioCycleAccumulator = 0
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
accumulator -= ticksToRun / ticksPerMs
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
(this as any)._accumulatorMs = accumulator
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (this.render) {
|
|
277
|
+
this.render(this.io8.buffer)
|
|
278
|
+
this.frames += 1
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
setImmediate(() => this.loop())
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
//
|
|
285
|
+
// Bus Operations
|
|
286
|
+
//
|
|
287
|
+
|
|
288
|
+
reset(coldStart: boolean): void {
|
|
289
|
+
this.cpu.reset()
|
|
290
|
+
this.ram.reset(coldStart)
|
|
291
|
+
this.io1.reset(coldStart)
|
|
292
|
+
this.io2.reset(coldStart)
|
|
293
|
+
this.io3.reset(coldStart)
|
|
294
|
+
this.io4.reset(coldStart)
|
|
295
|
+
this.io5.reset(coldStart)
|
|
296
|
+
this.io6.reset(coldStart)
|
|
297
|
+
this.io7.reset(coldStart)
|
|
298
|
+
this.io8.reset(coldStart)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
read(address: number): number {
|
|
302
|
+
switch(true) {
|
|
303
|
+
case (this.cart && address >= Cart.CODE && address <= Cart.END):
|
|
304
|
+
return this.cart.read(address - Cart.START)
|
|
305
|
+
case (address >= ROM.CODE && address <= ROM.END):
|
|
306
|
+
return this.rom.read(address - ROM.START)
|
|
307
|
+
case (address >= RAM.START && address <= RAM.END):
|
|
308
|
+
return this.ram.read(address)
|
|
309
|
+
case (address >= 0x8000 && address <= 0x83FF):
|
|
310
|
+
return this.io1.read(address - 0x8000) || 0
|
|
311
|
+
case (address >= 0x8400 && address <= 0x87FF):
|
|
312
|
+
return this.io2.read(address - 0x8400) || 0
|
|
313
|
+
case (address >= 0x8800 && address <= 0x8BFF):
|
|
314
|
+
return this.io3.read(address - 0x8800) || 0
|
|
315
|
+
case (address >= 0x8C00 && address <= 0x8FFF):
|
|
316
|
+
return this.io4.read(address - 0x8C00) || 0
|
|
317
|
+
case (address >= 0x9000 && address <= 0x93FF):
|
|
318
|
+
return this.io5.read(address - 0x9000) || 0
|
|
319
|
+
case (address >= 0x9400 && address <= 0x97FF):
|
|
320
|
+
return this.io6.read(address - 0x9400) || 0
|
|
321
|
+
case (address >= 0x9800 && address <= 0x9BFF):
|
|
322
|
+
return this.io7.read(address - 0x9800) || 0
|
|
323
|
+
case (address >= 0x9C00 && address <= 0x9FFF):
|
|
324
|
+
return this.io8.read(address - 0x9C00) || 0
|
|
325
|
+
default:
|
|
326
|
+
return 0
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
write(address: number, data: number): void {
|
|
331
|
+
switch(true) {
|
|
332
|
+
case (address >= RAM.START && address <= RAM.END):
|
|
333
|
+
this.ram.write(address, data)
|
|
334
|
+
return
|
|
335
|
+
case (address >= 0x8000 && address <= 0x83FF):
|
|
336
|
+
this.io1.write(address - 0x8000, data)
|
|
337
|
+
return
|
|
338
|
+
case (address >= 0x8400 && address <= 0x87FF):
|
|
339
|
+
this.io2.write(address - 0x8400, data)
|
|
340
|
+
return
|
|
341
|
+
case (address >= 0x8800 && address <= 0x8BFF):
|
|
342
|
+
this.io3.write(address - 0x8800, data)
|
|
343
|
+
return
|
|
344
|
+
case (address >= 0x8C00 && address <= 0x8FFF):
|
|
345
|
+
this.io4.write(address - 0x8C00, data)
|
|
346
|
+
return
|
|
347
|
+
case (address >= 0x9000 && address <= 0x93FF):
|
|
348
|
+
this.io5.write(address - 0x9000, data)
|
|
349
|
+
return
|
|
350
|
+
case (address >= 0x9400 && address <= 0x97FF):
|
|
351
|
+
this.io6.write(address - 0x9400, data)
|
|
352
|
+
return
|
|
353
|
+
case (address >= 0x9800 && address <= 0x9BFF):
|
|
354
|
+
this.io7.write(address - 0x9800, data)
|
|
355
|
+
return
|
|
356
|
+
case (address >= 0x9C00 && address <= 0x9FFF):
|
|
357
|
+
this.io8.write(address - 0x9C00, data)
|
|
358
|
+
return
|
|
359
|
+
default:
|
|
360
|
+
return
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export class RAM {
|
|
2
|
+
|
|
3
|
+
static START: number = 0x0000
|
|
4
|
+
static END: number = 0x7FFF
|
|
5
|
+
static SIZE: number = RAM.END - RAM.START + 1
|
|
6
|
+
|
|
7
|
+
data: number[] = [...Array(RAM.SIZE)].fill(0x00)
|
|
8
|
+
|
|
9
|
+
read(address: number): number {
|
|
10
|
+
return this.data[address]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
write(address: number, data: number): void {
|
|
14
|
+
this.data[address] = data
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
reset(coldStart: boolean): void {
|
|
18
|
+
if (coldStart) {
|
|
19
|
+
this.data.fill(0x00)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class ROM {
|
|
2
|
+
|
|
3
|
+
static START: number = 0x8000
|
|
4
|
+
static END: number = 0xFFFF
|
|
5
|
+
static CODE: number = 0xA000
|
|
6
|
+
static SIZE: number = ROM.END - ROM.START + 1
|
|
7
|
+
|
|
8
|
+
data: number[] = [...Array(ROM.SIZE)].fill(0x00)
|
|
9
|
+
|
|
10
|
+
read(address: number): number {
|
|
11
|
+
return this.data[address]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
load(data: number[]): void {
|
|
15
|
+
if (data.length != ROM.SIZE) { return }
|
|
16
|
+
|
|
17
|
+
this.data = data
|
|
18
|
+
}
|
|
19
|
+
}
|