ac6502 1.4.1 → 1.5.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.
- package/README.md +1 -1
- package/dist/components/CPU.js +6 -4
- package/dist/components/CPU.js.map +1 -1
- package/dist/components/IO/ACIA.d.ts +5 -0
- package/dist/components/IO/ACIA.js +39 -16
- package/dist/components/IO/ACIA.js.map +1 -1
- package/dist/index.js +70 -35
- package/dist/index.js.map +1 -1
- package/dist/tests/IO/ACIA.test.js +8 -8
- package/dist/tests/IO/ACIA.test.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CPU.ts +8 -4
- package/src/components/IO/ACIA.ts +43 -18
- package/src/index.ts +46 -14
- package/src/tests/IO/ACIA.test.ts +8 -8
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ import sdl from '@kmamal/sdl'
|
|
|
12
12
|
import { readFile, writeFile } from 'fs/promises'
|
|
13
13
|
import { existsSync } from 'fs'
|
|
14
14
|
|
|
15
|
-
const VERSION = '1.
|
|
15
|
+
const VERSION = '1.5.1'
|
|
16
16
|
const WIDTH = 320
|
|
17
17
|
const HEIGHT = 240
|
|
18
18
|
|
|
@@ -72,10 +72,11 @@ class Emulator {
|
|
|
72
72
|
await this.loadBinaries()
|
|
73
73
|
this.configureFrequency()
|
|
74
74
|
this.configureScale()
|
|
75
|
-
this.setupSerialPort()
|
|
75
|
+
await this.setupSerialPort()
|
|
76
76
|
this.setupAudio()
|
|
77
77
|
this.setupWindow()
|
|
78
78
|
this.setupControllers()
|
|
79
|
+
this.machine.reset(true)
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
private validateOptions(): void {
|
|
@@ -155,7 +156,7 @@ class Emulator {
|
|
|
155
156
|
}
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
private setupSerialPort(): void {
|
|
159
|
+
private async setupSerialPort(): Promise<void> {
|
|
159
160
|
if (!this.options.port) {
|
|
160
161
|
return
|
|
161
162
|
}
|
|
@@ -170,11 +171,8 @@ class Emulator {
|
|
|
170
171
|
baudRate: baudRate,
|
|
171
172
|
parity: parity,
|
|
172
173
|
dataBits: dataBits,
|
|
173
|
-
stopBits: stopBits
|
|
174
|
-
|
|
175
|
-
if (err) {
|
|
176
|
-
console.log('Error: ', err.message)
|
|
177
|
-
}
|
|
174
|
+
stopBits: stopBits,
|
|
175
|
+
autoOpen: false
|
|
178
176
|
})
|
|
179
177
|
|
|
180
178
|
this.serialPort.on('data', (data: Buffer<ArrayBuffer>) => {
|
|
@@ -192,10 +190,22 @@ class Emulator {
|
|
|
192
190
|
})
|
|
193
191
|
}
|
|
194
192
|
}
|
|
193
|
+
|
|
194
|
+
await new Promise<void>((resolve, reject) => {
|
|
195
|
+
this.serialPort!.open((err) => {
|
|
196
|
+
if (err) {
|
|
197
|
+
console.log('Error opening serial port: ', err.message)
|
|
198
|
+
reject(err)
|
|
199
|
+
} else {
|
|
200
|
+
console.log(`Serial port opened: ${this.options.port}`)
|
|
201
|
+
resolve()
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
})
|
|
195
205
|
}
|
|
196
206
|
|
|
197
207
|
private setupAudio(): void {
|
|
198
|
-
if (this.options.target === 'kim'
|
|
208
|
+
if (this.options.target === 'kim') return
|
|
199
209
|
try {
|
|
200
210
|
this.audioDevice = sdl.audio.openDevice({ type: 'playback' }, {
|
|
201
211
|
channels: AUDIO_CHANNELS as 1,
|
|
@@ -204,8 +214,10 @@ class Emulator {
|
|
|
204
214
|
buffered: AUDIO_BUFFERED,
|
|
205
215
|
})
|
|
206
216
|
|
|
207
|
-
// Configure Sound sample rate to match audio device
|
|
208
|
-
|
|
217
|
+
// Configure Sound sample rate to match audio device (not needed for dev target)
|
|
218
|
+
if (this.options.target !== 'dev') {
|
|
219
|
+
;(this.machine.io7 as Sound).sampleRate = this.audioDevice.frequency
|
|
220
|
+
}
|
|
209
221
|
|
|
210
222
|
// Connect the Machine's audio callback to the SDL audio device
|
|
211
223
|
this.machine.play = (samples: Float32Array) => {
|
|
@@ -347,12 +359,32 @@ class Emulator {
|
|
|
347
359
|
for (let i = 0; i < WIDTH * HEIGHT; i++) {
|
|
348
360
|
const v = src[i]
|
|
349
361
|
const off = i * 4
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
362
|
+
const r3 = (v >> 5) & 0x07
|
|
363
|
+
const g3 = (v >> 2) & 0x07
|
|
364
|
+
const b2 = v & 0x03
|
|
365
|
+
rgbaBuffer[off] = (r3 << 5) | (r3 << 2) | (r3 >> 1)
|
|
366
|
+
rgbaBuffer[off + 1] = (g3 << 5) | (g3 << 2) | (g3 >> 1)
|
|
367
|
+
rgbaBuffer[off + 2] = (b2 << 6) | (b2 << 4) | (b2 << 2) | b2
|
|
353
368
|
rgbaBuffer[off + 3] = 0xFF
|
|
354
369
|
}
|
|
355
370
|
this.window.render(WIDTH, HEIGHT, WIDTH * 4, 'rgba32', rgbaBuffer)
|
|
371
|
+
|
|
372
|
+
// Synthesize and play any queued VTAC bell tones
|
|
373
|
+
if (this.machine.play && this.audioDevice) {
|
|
374
|
+
const sampleRate = this.audioDevice.frequency
|
|
375
|
+
while (devBoard.vtac.hasQueuedBells()) {
|
|
376
|
+
const bell = devBoard.vtac.getNextBell()
|
|
377
|
+
if (bell) {
|
|
378
|
+
const numSamples = Math.floor((bell.duration / 60) * sampleRate)
|
|
379
|
+
const samples = new Float32Array(numSamples)
|
|
380
|
+
const twoPiF = 2 * Math.PI * bell.frequency
|
|
381
|
+
for (let i = 0; i < numSamples; i++) {
|
|
382
|
+
samples[i] = Math.sin(twoPiF * i / sampleRate)
|
|
383
|
+
}
|
|
384
|
+
this.machine.play(samples)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
356
388
|
}
|
|
357
389
|
}
|
|
358
390
|
|
|
@@ -137,18 +137,18 @@ describe('ACIA (6551 ACIA)', () => {
|
|
|
137
137
|
expect(mockIRQ).toHaveBeenCalled()
|
|
138
138
|
})
|
|
139
139
|
|
|
140
|
-
it('should disable receive IRQ when bit
|
|
140
|
+
it('should disable receive IRQ when RIIE bit (bit 1) is set', () => {
|
|
141
141
|
const mockIRQ = jest.fn()
|
|
142
142
|
serialCard.raiseIRQ = mockIRQ
|
|
143
143
|
|
|
144
|
-
serialCard.write(0x02,
|
|
144
|
+
serialCard.write(0x02, 0x02) // RIIE=1: receive IRQ disabled (W65C51N: bit1=1 disables)
|
|
145
145
|
serialCard.onData(0x42)
|
|
146
146
|
|
|
147
147
|
expect(mockIRQ).not.toHaveBeenCalled()
|
|
148
148
|
})
|
|
149
149
|
|
|
150
|
-
it('should enable echo mode when bit
|
|
151
|
-
serialCard.write(0x02,
|
|
150
|
+
it('should enable echo mode when REM bit (bit 4) is set', () => {
|
|
151
|
+
serialCard.write(0x02, 0x10) // REM=1: echo mode enabled (bit 4 per 6551 spec)
|
|
152
152
|
serialCard.onData(0x42)
|
|
153
153
|
|
|
154
154
|
// In echo mode, received data is added to transmit buffer
|
|
@@ -276,7 +276,7 @@ describe('ACIA (6551 ACIA)', () => {
|
|
|
276
276
|
serialCard.transmit = mockTransmit
|
|
277
277
|
|
|
278
278
|
serialCard.write(0x03, 0x00) // Set control register
|
|
279
|
-
serialCard.write(0x02,
|
|
279
|
+
serialCard.write(0x02, 0x04) // TIC=01: transmit IRQ enabled with /RTS low (bits 3-2 = 01)
|
|
280
280
|
serialCard.write(0x00, 0x42)
|
|
281
281
|
|
|
282
282
|
for (let i = 0; i < 100; i++) {
|
|
@@ -291,7 +291,7 @@ describe('ACIA (6551 ACIA)', () => {
|
|
|
291
291
|
const mockIRQ = jest.fn()
|
|
292
292
|
serialCard.raiseIRQ = mockIRQ
|
|
293
293
|
|
|
294
|
-
serialCard.write(0x02,
|
|
294
|
+
serialCard.write(0x02, 0x02) // RIIE=1: receive IRQ disabled (W65C51N: bit1=1 disables)
|
|
295
295
|
serialCard.onData(0x42)
|
|
296
296
|
|
|
297
297
|
expect(mockIRQ).not.toHaveBeenCalled()
|
|
@@ -343,7 +343,7 @@ describe('ACIA (6551 ACIA)', () => {
|
|
|
343
343
|
const mockTransmit = jest.fn()
|
|
344
344
|
serialCard.transmit = mockTransmit
|
|
345
345
|
|
|
346
|
-
serialCard.write(0x02,
|
|
346
|
+
serialCard.write(0x02, 0x10) // REM=1: echo mode enabled (bit 4 per 6551 spec)
|
|
347
347
|
serialCard.onData(0x42)
|
|
348
348
|
|
|
349
349
|
// Tick to allow transmission
|
|
@@ -369,7 +369,7 @@ describe('ACIA (6551 ACIA)', () => {
|
|
|
369
369
|
})
|
|
370
370
|
|
|
371
371
|
it('should clear TDRE flag in echo mode', () => {
|
|
372
|
-
serialCard.write(0x02,
|
|
372
|
+
serialCard.write(0x02, 0x10) // REM=1: echo mode enabled (bit 4 per 6551 spec)
|
|
373
373
|
serialCard.onData(0x42)
|
|
374
374
|
|
|
375
375
|
const status = serialCard.read(0x01)
|