diff --git a/src/audio.ts b/src/audio.ts index 6d961e03..ceddf12d 100644 --- a/src/audio.ts +++ b/src/audio.ts @@ -480,15 +480,13 @@ export var SampleAudio = function(clockfreq) { } this.feedSample = function(value, count) { - while (count-- > 0) { - accum += value; - sfrac += sinc; - while (sfrac >= 1) { - sfrac -= 1; - value *= sfrac; - this.addSingleSample(accum - value); - accum = value; - } + accum += value * count; + sfrac += sinc * count; + while (sfrac >= 1) { + sfrac -= 1; + value *= sfrac; + this.addSingleSample(accum - value); + accum = value; } } diff --git a/src/cpu/ZilogZ80.ts b/src/cpu/ZilogZ80.ts index f6e37d06..df324678 100644 --- a/src/cpu/ZilogZ80.ts +++ b/src/cpu/ZilogZ80.ts @@ -3408,6 +3408,9 @@ export class Z80 implements CPU, InstructionBased, IOBusConnected, SavesState { return this.rom && this.rom[a]; }], + [0x4000, 0x7fff, 0x3ff, (a) => { return this.ram[a]; }] + ]); - var cpuFrequency = 18432000 / 6; // 3.072 MHz - var cpuCyclesPerFrame = cpuFrequency / 60; - var cpuAudioFactor = 32; - - function fillBuffer() { - var t = tstates / cpuAudioFactor; - while (last_tstate < t) { - current_buffer[last_tstate++] = dac_float; - } - } - - this.getPresets = function() { - return WILLIAMS_SOUND_PRESETS; - } - - this.start = function() { - ram = new RAM(0x400); - membus = { - read: newAddressDecoder([ - [0x0000, 0x3fff, 0x3fff, function(a) { return rom ? rom[a] : null; }], - [0x4000, 0x7fff, 0x3ff, function(a) { return ram.mem[a]; }] - ]), - write: newAddressDecoder([ - [0x4000, 0x7fff, 0x3ff, function(a, v) { ram.mem[a] = v; }], - ]), - }; - iobus = { - read: function(addr) { - return command & 0xff; + write = newAddressDecoder([ + [0x4000, 0x7fff, 0x3ff, (a, v) => { this.ram[a] = v; }], + ]); + + constructor() { + super(); + this.cpu = new Z80(); + this.connectCPUMemoryBus(this); + this.connectCPUIOBus({ + read: (addr) => { + return this.command & 0xff; }, - write: function(addr, val) { - dac = val & 0xff; - dac_float = ((dac & 0x80) ? -256 + dac : dac) / 128.0; - fillBuffer(); - } - }; - this.readAddress = membus.read; - cpu = this.newCPU(membus, iobus); - audio = new SampleAudio(cpuFrequency / cpuAudioFactor); - audio.callback = function(lbuf) { - if (self.isRunning()) { - current_buffer = lbuf; - last_tstate = 0; - tstates = 0; - while (tstates < lbuf.length * cpuAudioFactor) { - tstates += self.runCPU(cpu, 1); - } - fillBuffer(); - for (var i = 0; i < 256; i++) { - var y = Math.round((current_buffer[i] * 127) + 128); - pixels[i + y * 256] = 0xff33ff33; - } - } - }; - video = new RasterVideo(mainElement, 256, 256); - video.create(); - video.setKeyboardEvents(function(key, code, flags) { - var intr = (key - 49); - if (intr >= 0 && (flags & 1)) { - command = intr & 0xff; - cpu.reset(); - } - }); - pixels = video.getFrameData(); - timer = new AnimationTimer(30, function() { - if (self.isRunning()) { - video.updateFrame(); - pixels.fill(0); + write: (addr, val) => { + let dac = this.dac = val & 0xff; + this.dac_float = ((dac & 0x80) ? -256 + dac : dac) / 128.0; } }); } - - this.loadROM = function(title, data) { - rom = padBytes(data, 0x4000); - cpu.reset(); + + advanceFrame(maxCycles, trap) : number { + this.pixels && this.pixels.fill(0); // clear waveform + maxCycles = Math.min(this.cpuCyclesPerFrame, maxCycles); + var n = 0; + while (n < maxCycles) { + if (trap && trap()) { + break; + } + n += this.advanceCPU(); + } + return n; + } + + advanceCPU() { + var n = super.advanceCPU(); + this.audio && this.audio.feedSample(this.dac_float, n); + // draw waveform on screen + if (this.pixels && !this.cpu.isHalted()) { + this.pixels[((this.xpos >> 8) & 0xff) + ((255-this.dac) << 8)] = 0xff33ff33; + this.xpos = (this.xpos + n) & 0xffffff; + } + return n; } - this.loadState = function(state) { - cpu.loadState(state.c); - ram.mem.set(state.b); - } - this.saveState = function() { - return { - c: self.getCPUState(), - b: ram.mem.slice(0), - }; - } - this.getCPUState = function() { - return cpu.saveState(); - } - - this.isRunning = function() { - return timer && timer.isRunning(); - } - this.pause = function() { - timer.stop(); - audio.stop(); - } - this.resume = function() { - timer.start(); - audio.start(); - } - this.reset = function() { - cpu.reset(); + setKeyInput(key:number, code:number, flags:number) : void { + var intr = (key - 49); + if (intr >= 0 && (flags & 1)) { + this.command = intr & 0xff; + this.cpu.reset(); + } } } +export class WilliamsSoundPlatform extends BaseZ80MachinePlatform { + + newMachine() { return new WilliamsSound(); } + getPresets() { return WILLIAMS_SOUND_PRESETS; } + getDefaultExtension() { return ".c"; }; + readAddress(a) { return this.machine.read(a); } + +} + PLATFORMS['sound_williams-z80'] = WilliamsSoundPlatform; diff --git a/test/cli/testplatforms.js b/test/cli/testplatforms.js index a3b4900a..89ab64fd 100644 --- a/test/cli/testplatforms.js +++ b/test/cli/testplatforms.js @@ -262,7 +262,6 @@ describe('Platform Replay', () => { } }); }); -/* it('Should run sound_williams', () => { var platform = testPlatform('sound_williams-z80', 'swave.c.rom', 72, (platform, frameno) => { if (frameno == 60) { @@ -270,7 +269,6 @@ describe('Platform Replay', () => { } }); }); -*/ it('Should run astrocade', () => { var platform = testPlatform('astrocade', 'cosmic.c.rom', 92, (platform, frameno) => {