From 9699cea11790b11cfeb5ed9cf2b1839eb2cdef5c Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 22 May 2019 18:02:00 -0400 Subject: [PATCH] refactored vicdual to use BasicZ80ScanlinePlatform, ran tsfmt --- doc/notes.txt | 1 + package.json | 1 + src/baseplatform.ts | 11 +- src/platform/vicdual.ts | 362 ++++++++++++++++++---------------------- tsfmt.json | 5 + 5 files changed, 171 insertions(+), 209 deletions(-) create mode 100644 tsfmt.json diff --git a/doc/notes.txt b/doc/notes.txt index 04ba1aaa..28fe4166 100644 --- a/doc/notes.txt +++ b/doc/notes.txt @@ -90,6 +90,7 @@ TODO: - global undo/redo at checkpoints (when rom changes) - pulldown shows wrong file if preset not present - landscape mode for arcade ports +- symmetric load/save state types - pixel editor - persist palette/tilemap selections - more tools for editing diff --git a/package.json b/package.json index 26dcbc26..8c775fc8 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "octokat": "^0.10.0", "pngjs": "^3.3.3", "typescript": "^3.3.3", + "typescript-formatter": "^7.2.2", "wavedrom-cli": "^0.5.x" }, "description": "8bitworkshop.com", diff --git a/src/baseplatform.ts b/src/baseplatform.ts index dd9556b3..ee955b1e 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -1097,11 +1097,12 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform { abstract newRAM() : Uint8Array; abstract newMembus() : MemoryBus; abstract newIOBus() : MemoryBus; - abstract getVideoOptions(); + abstract getVideoOptions() : {}; abstract getKeyboardMap(); - abstract startScanline(sl : number); - abstract drawScanline(sl : number); - getRasterScanline() { return this.currentScanline; } + abstract startScanline(sl : number) : void; + abstract drawScanline(sl : number) : void; + getRasterScanline() : number { return this.currentScanline; } + getKeyboardFunction() { return null; } constructor(mainElement : HTMLElement) { super(); @@ -1116,7 +1117,7 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform { this.cpu = this.newCPU(this.membus, this.iobus); this.video = new RasterVideo(this.mainElement, this.canvasWidth, this.numVisibleScanlines, this.getVideoOptions()); this.video.create(); - setKeyboardFromMap(this.video, this.inputs, this.getKeyboardMap()) + setKeyboardFromMap(this.video, this.inputs, this.getKeyboardMap(), this.getKeyboardFunction()); this.timer = new AnimationTimer(60, this.nextFrame.bind(this)); } diff --git a/src/platform/vicdual.ts b/src/platform/vicdual.ts index f76df8a9..4f420aab 100644 --- a/src/platform/vicdual.ts +++ b/src/platform/vicdual.ts @@ -1,80 +1,59 @@ "use strict"; -import { Platform, BaseZ80Platform } from "../baseplatform"; -import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap } from "../emu"; -import { hex } from "../util"; +import { Platform, BasicZ80ScanlinePlatform } from "../baseplatform"; +import { PLATFORMS, newAddressDecoder, padBytes, Keys, makeKeycodeMap } from "../emu"; import { MasterAudio, AY38910_Audio } from "../audio"; const VICDUAL_PRESETS = [ - {id:'minimal.c', name:'Minimal Example'}, - {id:'hello.c', name:'Hello World'}, - {id:'gfxtest.c', name:'Graphics Test'}, - {id:'soundtest.c', name:'Sound Test'}, - {id:'snake1.c', name:'Siege Game (Prototype)'}, - {id:'snake2.c', name:'Siege Game (Full)'}, - {id:'music.c', name:'Music Player'}, + { id: 'minimal.c', name: 'Minimal Example' }, + { id: 'hello.c', name: 'Hello World' }, + { id: 'gfxtest.c', name: 'Graphics Test' }, + { id: 'soundtest.c', name: 'Sound Test' }, + { id: 'snake1.c', name: 'Siege Game (Prototype)' }, + { id: 'snake2.c', name: 'Siege Game (Full)' }, + { id: 'music.c', name: 'Music Player' }, ]; -const _VicDualPlatform = function(mainElement) { +class VicDualDisplay { + palbank: number = 0; - var cpu, ram, membus, iobus, rom; - var video, audio, psg, timer, pixels; - var inputs = [0xff, 0xff, 0xff, 0xff^0x8]; // most things active low - var palbank = 0; + palette = [ + 0xff000000, // black + 0xff0000ff, // red + 0xff00ff00, // green + 0xff00ffff, // yellow + 0xffff0000, // blue + 0xffff00ff, // magenta + 0xffffff00, // cyan + 0xffffffff // white + ]; - const XTAL = 15468000.0; - const scanlinesPerFrame = 0x106; - const vblankStart = 0xe0; - const vsyncStart = 0xec; - const vsyncEnd = 0xf0; - const cpuFrequency = XTAL/8; - const hsyncFrequency = XTAL/3/scanlinesPerFrame; - const vsyncFrequency = hsyncFrequency/0x148; - const cpuCyclesPerLine = cpuFrequency/hsyncFrequency; - const timerFrequency = 500; // input 2 bit 0x8 - const cyclesPerTimerTick = cpuFrequency / (2 * timerFrequency); + // default PROM + colorprom = [ + 0xe0, 0x60, 0x20, 0x60, 0xc0, 0x60, 0x40, 0xc0, + 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x0e, + 0xe0, 0xe0, 0xe0, 0xe0, 0x60, 0x60, 0x60, 0x60, + 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, + ]; - var reset_disable = false; - var reset_disable_timer; - var framestats; - - const palette = [ - 0xff000000, // black - 0xff0000ff, // red - 0xff00ff00, // green - 0xff00ffff, // yellow - 0xffff0000, // blue - 0xffff00ff, // magenta - 0xffffff00, // cyan - 0xffffffff // white - ]; - - // default PROM - var colorprom = [ - 0xe0,0x60,0x20,0x60, 0xc0,0x60,0x40,0xc0, - 0x20,0x40,0x60,0x80, 0xa0,0xc0,0xe0,0x0e, - 0xe0,0xe0,0xe0,0xe0, 0x60,0x60,0x60,0x60, - 0xe0,0xe0,0xe0,0xe0, 0xe0,0xe0,0xe0,0xe0, - ]; - - // videoram 0xc000-0xc3ff - // RAM 0xc400-0xc7ff - // charram 0xc800-0xcfff - function drawScanline(pixels, sl) { - if (sl >= 224) return; - var pixofs = sl*256; - var outi = pixofs; // starting output pixel in frame buffer - var vramofs = (sl>>3)<<5; // offset in VRAM - var yy = sl & 7; // y offset within tile - for (var xx=0; xx<32; xx++) { - var code = ram.mem[vramofs+xx]; - var data = ram.mem[0x800 + (code<<3) + yy]; - var col = (code>>5) + (palbank<<3); - var color1 = palette[(colorprom[col] >> 1) & 7]; - var color2 = palette[(colorprom[col] >> 5) & 7]; - for (var i=0; i<8; i++) { - var bm = 128>>i; - pixels[outi] = (data&bm) ? color2 : color1; + // videoram 0xc000-0xc3ff + // RAM 0xc400-0xc7ff + // charram 0xc800-0xcfff + drawScanline(ram, pixels: Uint32Array, sl: number) { + if (sl >= 224) return; + var pixofs = sl * 256; + var outi = pixofs; // starting output pixel in frame buffer + var vramofs = (sl >> 3) << 5; // offset in VRAM + var yy = sl & 7; // y offset within tile + for (var xx = 0; xx < 32; xx++) { + var code = ram[vramofs + xx]; + var data = ram[0x800 + (code << 3) + yy]; + var col = (code >> 5) + (this.palbank << 3); + var color1 = this.palette[(this.colorprom[col] >> 1) & 7]; + var color2 = this.palette[(this.colorprom[col] >> 5) & 7]; + for (var i = 0; i < 8; i++) { + var bm = 128 >> i; + pixels[outi] = (data & bm) ? color2 : color1; /* TODO if (framestats) { framestats.layers.tiles[outi] = (data&bm) ? colorprom[col+8] : colorprom[col]; @@ -82,164 +61,139 @@ const _VicDualPlatform = function(mainElement) { */ outi++; } - } - } + } + } +} - const CARNIVAL_KEYCODE_MAP = makeKeycodeMap([ - [Keys.VK_SPACE, 2, -0x20], - [Keys.VK_SHIFT, 2, -0x40], - [Keys.VK_LEFT, 1, -0x10], - [Keys.VK_RIGHT, 1, -0x20], - [Keys.VK_UP, 1, -0x40], - [Keys.VK_DOWN, 1, -0x80], - [Keys.VK_1, 2, -0x10], - [Keys.VK_2, 3, -0x20], - [Keys.VK_5, 3, 0x8], - ]); +const CARNIVAL_KEYCODE_MAP = makeKeycodeMap([ + [Keys.VK_SPACE, 2, -0x20], + [Keys.VK_SHIFT, 2, -0x40], + [Keys.VK_LEFT, 1, -0x10], + [Keys.VK_RIGHT, 1, -0x20], + [Keys.VK_UP, 1, -0x40], + [Keys.VK_DOWN, 1, -0x80], + [Keys.VK_1, 2, -0x10], + [Keys.VK_2, 3, -0x20], + [Keys.VK_5, 3, 0x8], +]); - class VicDualPlatform extends BaseZ80Platform implements Platform { - - scanline : number = 0; +const XTAL = 15468000.0; +const scanlinesPerFrame = 0x106; +const vblankStart = 0xe0; +const vsyncStart = 0xec; +const vsyncEnd = 0xf0; +const cpuFrequency = XTAL / 8; +const hsyncFrequency = XTAL / 3 / scanlinesPerFrame; +const vsyncFrequency = hsyncFrequency / 0x148; +const cpuCyclesPerLine = cpuFrequency / hsyncFrequency; +const timerFrequency = 500; // input 2 bit 0x8 +const cyclesPerTimerTick = cpuFrequency / (2 * timerFrequency); - getPresets() { - return VICDUAL_PRESETS; +class VicDualPlatform extends BasicZ80ScanlinePlatform implements Platform { + + display: VicDualDisplay; + psg; + reset_disable = false; + reset_disable_timer; + + cpuFrequency = XTAL / 8; // MHz + canvasWidth = 256; + numTotalScanlines = 262; + numVisibleScanlines = 224; + defaultROMSize = 0x4040; + + getPresets() { return VICDUAL_PRESETS; } + + getKeyboardMap() { return CARNIVAL_KEYCODE_MAP; } + + getKeyboardFunction() { + return (o) => { + // reset when coin inserted + if (o.index == 3 && o.mask == 0x8 && !this.reset_disable) { + this.cpu.reset(); + console.log("coin inserted"); + console.log(this.inputs) + } + // don't allow repeated resets in short period of time + this.reset_disable = true; + clearTimeout(this.reset_disable_timer); + this.reset_disable_timer = setTimeout(() => { this.reset_disable = false; }, 1100); + } + }; + + getVideoOptions() { return { rotate: -90 }; } + + newRAM() { + return new Uint8Array(0x1000); + } + + newMembus() { + return { + read: newAddressDecoder([ + [0x0000, 0x7fff, 0x3fff, (a) => { return this.rom ? this.rom[a] : null; }], + [0x8000, 0xffff, 0x0fff, (a) => { return this.ram[a]; }], + ]), + write: newAddressDecoder([ + [0x8000, 0xffff, 0x0fff, (a, v) => { this.ram[a] = v; }], + ]), + isContended: () => { return false; }, + }; + } + + newIOBus() { + return { + read: (addr) => { + return this.inputs[addr & 3]; + }, + write: (addr, val) => { + if (addr & 0x1) { this.psg.selectRegister(val & 0xf); }; // audio 1 + if (addr & 0x2) { this.psg.setData(val); }; // audio 2 + if (addr & 0x8) { }; // TODO: assert coin status + if (addr & 0x40) { this.display.palbank = val & 3; }; // palette + } + }; } start() { - ram = new RAM(0x1000); - membus = { - read: newAddressDecoder([ - [0x0000, 0x7fff, 0x3fff, function(a) { return rom ? rom[a] : null; }], - [0x8000, 0xffff, 0x0fff, function(a) { return ram.mem[a]; }], - ]), - write: newAddressDecoder([ - [0x8000, 0xffff, 0x0fff, function(a,v) { ram.mem[a] = v; }], - ]), - isContended: function() { return false; }, - }; - iobus = { - read: function(addr) { - return inputs[addr&3]; - }, - write: function(addr, val) { - if (addr & 0x1) { psg.selectRegister(val & 0xf); }; // audio 1 - if (addr & 0x2) { psg.setData(val); }; // audio 2 - if (addr & 0x8) { }; // TODO: assert coin status - if (addr & 0x40) { palbank = val & 3; }; // palette - } - }; - cpu = this.newCPU(membus, iobus); - video = new RasterVideo(mainElement,256,224,{rotate:-90}); - audio = new MasterAudio(); - psg = new AY38910_Audio(audio); - //var speech = new VotraxSpeech(); - //audio.master.addChannel(speech); - video.create(); - var idata = video.getFrameData(); - setKeyboardFromMap(video, inputs, CARNIVAL_KEYCODE_MAP, function(o) { - // reset when coin inserted - if (o.index==3 && o.mask==0x8 && !reset_disable) cpu.reset(); - // don't allow repeated resets in short period of time - reset_disable = true; - clearTimeout(reset_disable_timer); - reset_disable_timer = setTimeout(function() { reset_disable = false; }, 1100); - }); - pixels = video.getFrameData(); - timer = new AnimationTimer(60, this.nextFrame.bind(this)); + super.start(); + this.inputs.set([0xff, 0xff, 0xff, 0xff ^ 0x8]); // most things active low + this.display = new VicDualDisplay(); + this.audio = new MasterAudio(); + this.psg = new AY38910_Audio(this.audio); } - readAddress(addr) { - return membus.read(addr); + reset() { + super.reset(); + this.psg.reset(); } - advance(novideo : boolean) { - var targetTstates = cpu.getTstates(); - for (var sl=0; sl= 0x4020 && (data[0x4000] || data[0x401f])) { - colorprom = data.slice(0x4000,0x4020); + this.display.colorprom = data.slice(0x4000, 0x4020); } - rom = padBytes(data, 0x4040); - this.reset(); } loadState(state) { - cpu.loadState(state.c); - ram.mem.set(state.b); - this.loadControlsState(state); - palbank = state.pb; + super.loadState(state); + this.display.palbank = state.pb; } saveState() { - return { - c:this.getCPUState(), - b:ram.mem.slice(0), - in0:inputs[0], - in1:inputs[1], - in2:inputs[2], - in3:inputs[3], - pb:palbank, - }; + var state = super.saveState(); + state['pb'] = this.display.palbank; + return state; } - loadControlsState(state) { - inputs[0] = state.in0; - inputs[1] = state.in1; - inputs[2] = state.in2; - inputs[3] = state.in3; - } - saveControlsState() { - return { - in0:inputs[0], - in1:inputs[1], - in2:inputs[2], - in3:inputs[3] - }; - } - getCPUState() { - return cpu.saveState(); - } - - isRunning() { - return timer && timer.isRunning(); - } - pause() { - timer.stop(); - audio.stop(); - } - resume() { - timer.start(); - audio.start(); - } - reset() { - cpu.reset(); - psg.reset(); - if (!this.getDebugCallback()) cpu.setTstates(0); // TODO? - } - setFrameStats(on) { - framestats = on ? { - palette: palette, - layers: {width:256, height:224, tiles:[]} - } : null; - } - } - return new VicDualPlatform(); } -PLATFORMS['vicdual'] = _VicDualPlatform; +PLATFORMS['vicdual'] = VicDualPlatform; diff --git a/tsfmt.json b/tsfmt.json new file mode 100644 index 00000000..006a03ad --- /dev/null +++ b/tsfmt.json @@ -0,0 +1,5 @@ +{ + "indentSize": 2, + "newLineCharacter": "\n", + "convertTabsToSpaces": true +} \ No newline at end of file