diff --git a/index.html b/index.html index cd40cebb..84e6b37d 100644 --- a/index.html +++ b/index.html @@ -141,7 +141,7 @@ if (window.location.host.endsWith('8bitworkshop.com')) { - PLATFORMS + PLATFORMS diff --git a/package-lock.json b/package-lock.json index b6d59499..ab343b17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -240,6 +240,12 @@ "graceful-readlink": ">= 1.0.0" } }, + "commandpost": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/commandpost/-/commandpost-1.4.0.tgz", + "integrity": "sha512-aE2Y4MTFJ870NuB/+2z1cXBhSBBzRydVVjzhFC4gtenEhpnj15yu0qptWGJsO9YGrcPZ3ezX8AWb1VA391MKpQ==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -324,6 +330,26 @@ "safer-buffer": "^2.1.0" } }, + "editorconfig": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", + "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", + "dev": true, + "requires": { + "commander": "^2.19.0", + "lru-cache": "^4.1.5", + "semver": "^5.6.0", + "sigmund": "^1.0.1" + }, + "dependencies": { + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + } + } + }, "encoding": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", @@ -669,6 +695,16 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, "lzg": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lzg/-/lzg-1.0.0.tgz", @@ -903,6 +939,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", @@ -1014,6 +1056,18 @@ "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=", "dev": true }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1120,6 +1174,16 @@ "integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==", "dev": true }, + "typescript-formatter": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/typescript-formatter/-/typescript-formatter-7.2.2.tgz", + "integrity": "sha512-V7vfI9XArVhriOTYHPzMU2WUnm5IMdu9X/CPxs8mIMGxmTBFpDABlbkBka64PZJ9/xgQeRpK8KzzAG4MPzxBDQ==", + "dev": true, + "requires": { + "commandpost": "^1.0.0", + "editorconfig": "^0.15.0" + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -1231,6 +1295,12 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-1.3.1.tgz", "integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==", "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true } } } diff --git a/src/baseplatform.ts b/src/baseplatform.ts index ee955b1e..5d205013 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -1091,6 +1091,7 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform { timer; audio; psg; + pixels : Uint32Array; inputs = new Uint8Array(16); mainElement : HTMLElement; @@ -1117,6 +1118,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(); + this.pixels = this.video.getFrameData(); setKeyboardFromMap(this.video, this.inputs, this.getKeyboardMap(), this.getKeyboardFunction()); this.timer = new AnimationTimer(60, this.nextFrame.bind(this)); } diff --git a/src/platform/mw8080bw.ts b/src/platform/mw8080bw.ts index f5025584..d2dc225d 100644 --- a/src/platform/mw8080bw.ts +++ b/src/platform/mw8080bw.ts @@ -1,200 +1,154 @@ "use strict"; -import { Platform, BaseZ80Platform } from "../baseplatform"; +import { Platform, BasicZ80ScanlinePlatform, BaseZ80Platform } from "../baseplatform"; import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap } from "../emu"; import { hex } from "../util"; // http://www.computerarcheology.com/Arcade/ const MW8080BW_PRESETS = [ - {id:'gfxtest.c', name:'Graphics Test'}, - {id:'shifter.c', name:'Sprite w/ Bit Shifter'}, - {id:'game2.c', name:'Cosmic Impalas'}, + { id: 'gfxtest.c', name: 'Graphics Test' }, + { id: 'shifter.c', name: 'Sprite w/ Bit Shifter' }, + { id: 'game2.c', name: 'Cosmic Impalas' }, ]; -const _Midway8080BWPlatform = function(mainElement) { +const SPACEINV_KEYCODE_MAP = makeKeycodeMap([ + [Keys.VK_SPACE, 1, 0x10], // P1 + [Keys.VK_LEFT, 1, 0x20], + [Keys.VK_RIGHT, 1, 0x40], + [Keys.VK_S, 2, 0x10], // P2 + [Keys.VK_A, 2, 0x20], + [Keys.VK_D, 2, 0x40], + [Keys.VK_5, 1, 0x1], + [Keys.VK_1, 1, 0x4], + [Keys.VK_2, 1, 0x2], +]); - var cpu, ram, membus, iobus, rom; - var probe; - var video, timer, pixels, displayPCs; - var inputs = [0xe,0x8,0x0]; - var bitshift_offset = 0; - var bitshift_register = 0; - var watchdog_counter; - const cpuFrequency = 1996800; - const cpuCyclesPerLine = cpuFrequency/(60*224); // TODO - const INITIAL_WATCHDOG = 256; - const PIXEL_ON = 0xffeeeeee; - const PIXEL_OFF = 0xff000000; +const INITIAL_WATCHDOG = 256; +const PIXEL_ON = 0xffeeeeee; +const PIXEL_OFF = 0xff000000; - const SPACEINV_KEYCODE_MAP = makeKeycodeMap([ - [Keys.VK_SPACE, 1, 0x10], // P1 - [Keys.VK_LEFT, 1, 0x20], - [Keys.VK_RIGHT, 1, 0x40], - [Keys.VK_S, 2, 0x10], // P2 - [Keys.VK_A, 2, 0x20], - [Keys.VK_D, 2, 0x40], - [Keys.VK_5, 1, 0x1], - [Keys.VK_1, 1, 0x4], - [Keys.VK_2, 1, 0x2], - ]); - - class Midway8080BWPlatform extends BaseZ80Platform implements Platform { - getPresets() { - return MW8080BW_PRESETS; - } +class Midway8080BWPlatform extends BasicZ80ScanlinePlatform implements Platform { + cpuFrequency = 1996800; // MHz + canvasWidth = 256; + numTotalScanlines = 262; + numVisibleScanlines = 224; + defaultROMSize = 0x2000; - start = function() { - ram = new RAM(0x2000); - //displayPCs = new Uint16Array(new ArrayBuffer(0x2000*2)); - membus = { + bitshift_offset = 0; + bitshift_register = 0; + watchdog_counter; + + getPresets() { return MW8080BW_PRESETS; } + getKeyboardMap() { return SPACEINV_KEYCODE_MAP; } + getVideoOptions() { return { rotate: -90 }; } + newRAM() { return new Uint8Array(0x2000); } + + newMembus() { + return { read: newAddressDecoder([ - [0x0000, 0x1fff, 0x1fff, function(a) { return rom ? rom[a] : 0; }], - [0x2000, 0x3fff, 0x1fff, function(a) { return ram.mem[a]; }], - ]), - write: newAddressDecoder([ - [0x2000, 0x23ff, 0x3ff, function(a,v) { ram.mem[a] = v; }], - [0x2400, 0x3fff, 0x1fff, function(a,v) { - ram.mem[a] = v; - var ofs = (a - 0x400)<<3; - for (var i=0; i<8; i++) - pixels[ofs+i] = (v & (1< { return this.rom ? this.rom[a] : 0; }], + [0x2000, 0x3fff, 0x1fff, (a) => { return this.ram[a]; }], + ]), + write: newAddressDecoder([ + [0x2000, 0x23ff, 0x3ff, (a, v) => { this.ram[a] = v; }], + [0x2400, 0x3fff, 0x1fff, (a, v) => { + this.ram[a] = v; + var ofs = (a - 0x400) << 3; + for (var i = 0; i < 8; i++) { + this.pixels[ofs + i] = (v & (1 << i)) ? PIXEL_ON : PIXEL_OFF; + } + //if (displayPCs) displayPCs[a] = cpu.getPC(); // save program counter + }], + ]), isContended: function() { return false; }, }; - iobus = { - read: function(addr) { - addr &= 0x3; + } + + newIOBus() { + return { + read: (addr) => { + addr &= 0x3; //console.log('IO read', hex(addr,4)); switch (addr) { case 0: case 1: case 2: - return inputs[addr]; + return this.inputs[addr]; case 3: - return (bitshift_register >> (8-bitshift_offset)) & 0xff; + return (this.bitshift_register >> (8 - this.bitshift_offset)) & 0xff; } return 0; - }, - write: function(addr, val) { - addr &= 0x7; - val &= 0xff; + }, + write: (addr, val) => { + addr &= 0x7; + val &= 0xff; //console.log('IO write', hex(addr,4), hex(val,2)); switch (addr) { case 2: - bitshift_offset = val & 0x7; + this.bitshift_offset = val & 0x7; break; case 3: case 5: // TODO: sound break; case 4: - bitshift_register = (bitshift_register >> 8) | (val << 8); + this.bitshift_register = (this.bitshift_register >> 8) | (val << 8); break; case 6: - watchdog_counter = INITIAL_WATCHDOG; + this.watchdog_counter = INITIAL_WATCHDOG; break; } - } + } }; - cpu = this.newCPU(membus, iobus); - video = new RasterVideo(mainElement,256,224,{rotate:-90}); - video.create(); + } + + startScanline(sl: number) { + } + + drawScanline(sl: number) { + // at end of scanline + if (sl == 95) + this.cpu.requestInterrupt(0x8); // RST $8 + else if (sl == 223) + this.cpu.requestInterrupt(0x10); // RST $10 + } + + advance(novideo: boolean) { + super.advance(novideo); + if (this.watchdog_counter-- <= 0) { + console.log("WATCHDOG FIRED"); // TODO: alert on video + this.reset(); + } + } + + loadState(state) { + super.loadState(state); + this.bitshift_register = state.bsr; + this.bitshift_offset = state.bso; + this.watchdog_counter = state.wdc; + } + saveState() { + var state: any = super.saveState(); + state.bsr = this.bitshift_register; + state.bso = this.bitshift_offset; + state.wdc = this.watchdog_counter; + return state; + } + reset() { + super.reset(); + this.watchdog_counter = INITIAL_WATCHDOG; + } +} + +/* $(video.canvas).click(function(e) { var x = Math.floor(e.offsetX * video.canvas.width / $(video.canvas).width()); var y = Math.floor(e.offsetY * video.canvas.height / $(video.canvas).height()); var addr = (x>>3) + (y*32) + 0x400; if (displayPCs) console.log(x, y, hex(addr,4), "PC", hex(displayPCs[addr],4)); }); - var idata = video.getFrameData(); - setKeyboardFromMap(video, inputs, SPACEINV_KEYCODE_MAP); - pixels = video.getFrameData(); - timer = new AnimationTimer(60, this.nextFrame.bind(this)); - } + */ - readAddress(addr) { - return membus.read(addr); - } - - advance(novideo : boolean) { - for (var sl=0; sl<224; sl++) { - this.runCPU(cpu, cpuCyclesPerLine); - if (sl == 95) - cpu.requestInterrupt(0x8); // RST $8 - else if (sl == 223) - cpu.requestInterrupt(0x10); // RST $10 - } - if (!novideo) { - video.updateFrame(); - } - if (watchdog_counter-- <= 0) { - console.log("WATCHDOG FIRED"); // TODO: alert on video - this.reset(); - } - } - - loadROM(title, data) { - rom = padBytes(data, 0x2000); - this.reset(); - } - - loadState(state) { - cpu.loadState(state.c); - ram.mem.set(state.b); - bitshift_register = state.bsr; - bitshift_offset = state.bso; - watchdog_counter = state.wdc; - inputs[0] = state.in0; - inputs[1] = state.in1; - inputs[2] = state.in2; - } - saveState() { - return { - c:this.getCPUState(), - b:ram.mem.slice(0), - bsr:bitshift_register, - bso:bitshift_offset, - wdc:watchdog_counter, - in0:inputs[0], - in1:inputs[1], - in2:inputs[2], - }; - } - loadControlsState(state) { - inputs[0] = state.in0; - inputs[1] = state.in1; - inputs[2] = state.in2; - } - saveControlsState() { - return { - in0:inputs[0], - in1:inputs[1], - in2:inputs[2], - }; - } - getCPUState() { - return cpu.saveState(); - } - - isRunning() { - return timer && timer.isRunning(); - } - pause() { - timer.stop(); - } - resume() { - timer.start(); - } - reset() { - cpu.reset(); - cpu.setTstates(0); - watchdog_counter = INITIAL_WATCHDOG; - } - } - return new Midway8080BWPlatform(); -} - -PLATFORMS['mw8080bw'] = _Midway8080BWPlatform; +PLATFORMS['mw8080bw'] = Midway8080BWPlatform; diff --git a/src/ui.ts b/src/ui.ts index 8d3b4b29..ff8e4fb9 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -1842,6 +1842,7 @@ export function startUI(loadplatform : boolean) { platform_id = qs['platform'] = "vcs"; } $("#item_platform_"+platform_id).addClass("dropdown-item-checked"); + $("#platform_name").text(platform_id); setupSplits(); // create store store_id = repo_id || getBasePlatform(platform_id);