From 0dd741f44610afe13d19c688894cb35e56c1311d Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Thu, 23 Aug 2018 08:49:14 -0400 Subject: [PATCH] made some platforms classy --- doc/notes.txt | 1 + src/baseplatform.ts | 211 ++++++++++++++++++++------------------- src/platform/apple2.ts | 169 ++++++++++++++++--------------- src/platform/atari8.ts | 4 +- src/platform/coleco.ts | 17 ++-- src/platform/galaxian.ts | 89 ++++++++++------- src/platform/mw8080bw.ts | 67 ++++++++----- src/platform/nes.ts | 119 ++++++++++++---------- src/platform/vcs.ts | 113 ++++++++++----------- src/platform/vector.ts | 24 +++++ src/platform/vicdual.ts | 92 ++++++++++------- src/platform/williams.ts | 10 +- src/ui.ts | 14 ++- 13 files changed, 513 insertions(+), 417 deletions(-) diff --git a/doc/notes.txt b/doc/notes.txt index d2226666..8044269b 100644 --- a/doc/notes.txt +++ b/doc/notes.txt @@ -55,6 +55,7 @@ TODO: - state buffer/replay - intro/help text for each platform - make sure controls work with replay feature (we'll have to save control state every frame) +- vscode/atom extension? FYI: Image links for the books on http://8bitworkshop.com/ are broken On the website the additional grey spacing next to the program line numbers is not dynamically resized when the web browser window size is changed. Intentional? diff --git a/src/baseplatform.ts b/src/baseplatform.ts index 21d15199..f07818f5 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -569,71 +569,75 @@ export abstract class Base6809Platform extends BaseZ80Platform { declare var FS, ENV, Module; // mame emscripten // TODO: make class -export function BaseMAMEPlatform() { +export abstract class BaseMAMEPlatform { - var self = this; + loaded = false; + preinitted = false; + romfn; + romdata; + video; + preload_files; + running = false; + console_vars : {[varname:string]:string[]} = {}; + console_varname; + initluavars = false; + luadebugscript; + js_lua_string; + onBreakpointHit; + mainElement; + + constructor(mainElement) { + this.mainElement = mainElement; + } - var loaded = false; - var preinitted = false; - var romfn; - var romdata; - var video; - var preload_files; - var running = false; - var console_vars : {[varname:string]:string[]} = {}; - var console_varname; - var initluavars = false; - var luadebugscript; - var js_lua_string; - - this.luareset = function() { - console_vars = {}; + luareset() { + this.console_vars = {}; } // http://docs.mamedev.org/techspecs/luaengine.html - this.luacall = function(s) { - console_varname = null; + luacall(s) { + this.console_varname = null; //Module.ccall('_Z13js_lua_stringPKc', 'void', ['string'], [s+""]); - if (!js_lua_string) js_lua_string = Module.cwrap('_Z13js_lua_stringPKc', 'void', ['string']); - js_lua_string(s || ""); + if (!this.js_lua_string) this.js_lua_string = Module.cwrap('_Z13js_lua_stringPKc', 'void', ['string']); + this.js_lua_string(s || ""); } - this.pause = function() { - if (loaded && running) { + pause() { + if (this.loaded && this.running) { this.luacall('emu.pause()'); - running = false; + this.running = false; } } - this.resume = function() { - if (loaded && !running) { // TODO + resume() { + if (this.loaded && !this.running) { // TODO this.luacall('emu.unpause()'); - running = true; + this.running = true; } } - this.reset = function() { + reset() { this.luacall('manager:machine():soft_reset()'); - running = true; - initluavars = false; + this.running = true; + this.initluavars = false; } - this.isRunning = function() { - return running; + isRunning() { + return this.running; } - function bufferConsoleOutput(s) { + bufferConsoleOutput(s) { if (!s) return; if (s.startsWith(">>>")) { - console_varname = s.length > 3 ? s.slice(3) : null; - if (console_varname) console_vars[console_varname] = []; - } else if (console_varname) { - console_vars[console_varname].push(s); - if (console_varname == 'debug_stopped') { - var debugSaveState = self.preserveState(); - self.pause(); - if (onBreakpointHit) { - onBreakpointHit(debugSaveState); + this.console_varname = s.length > 3 ? s.slice(3) : null; + if (this.console_varname) this.console_vars[this.console_varname] = []; + } else if (this.console_varname) { + this.console_vars[this.console_varname].push(s); + if (this.console_varname == 'debug_stopped') { + var debugSaveState = this.preserveState(); + this.pause(); + if (this.onBreakpointHit) { + this.onBreakpointHit(debugSaveState); } } } else { @@ -641,12 +645,11 @@ export function BaseMAMEPlatform() { } } - this.startModule = function(mainElement, opts) { - romfn = opts.romfn; - if (opts.romdata) romdata = opts.romdata; - if (!romdata) romdata = new RAM(opts.romsize).mem; + startModule(mainElement, opts) { + var romfn = this.romfn = this.romfn || opts.romfn; + var romdata = this.romdata = this.romdata || opts.romdata || new RAM(opts.romsize).mem; // create canvas - video = new RasterVideo(mainElement, opts.width, opts.height); + var video = this.video = new RasterVideo(this.mainElement, opts.width, opts.height); video.create(); $(video.canvas).attr('id','canvas'); // load asm.js module @@ -662,11 +665,11 @@ export function BaseMAMEPlatform() { window['Module'] = { arguments: modargs, screenIsReadOnly: true, - print: bufferConsoleOutput, + print: this.bufferConsoleOutput, canvas:video.canvas, doNotCaptureKeyboard:true, keyboardListeningElement:video.canvas, - preInit: function () { + preInit: () => { console.log("loading FS"); ENV.SDL_EMSCRIPTEN_KEYBOARD_ELEMENT = 'canvas'; if (opts.cfgfile) { @@ -685,14 +688,14 @@ export function BaseMAMEPlatform() { if (opts.preInit) { opts.preInit(self); } - preinitted = true; + this.preinitted = true; }, preRun: [ - function() { - $(video.canvas).click(function(e) { + () => { + $(video.canvas).click((e) =>{ video.canvas.focus(); }); - loaded = true; + this.loaded = true; console.log("about to run..."); } ] @@ -704,7 +707,7 @@ export function BaseMAMEPlatform() { var fetch_wasm = $.Deferred(); // fetch config file if (opts.cfgfile) { - fetch_cfg = $.get('mame/cfg/' + opts.cfgfile, function(data) { + fetch_cfg = $.get('mame/cfg/' + opts.cfgfile, (data) => { opts.cfgdata = data; console.log("loaded " + opts.cfgfile); }, 'text'); @@ -714,7 +717,7 @@ export function BaseMAMEPlatform() { var oReq1 = new XMLHttpRequest(); oReq1.open("GET", 'mame/roms/' + opts.biosfile, true); oReq1.responseType = "arraybuffer"; - oReq1.onload = function(oEvent) { + oReq1.onload = (oEvent) => { opts.biosdata = new Uint8Array(oReq1.response); console.log("loaded " + opts.biosfile); // + " (" + oEvent.total + " bytes)"); fetch_bios.resolve(); @@ -724,8 +727,8 @@ export function BaseMAMEPlatform() { fetch_bios.resolve(); } // load debugger Lua script - fetch_lua = $.get('mame/debugger.lua', function(data) { - luadebugscript = data; + fetch_lua = $.get('mame/debugger.lua', (data) => { + this.luadebugscript = data; console.log("loaded debugger.lua"); }, 'text'); // load WASM @@ -733,7 +736,7 @@ export function BaseMAMEPlatform() { var oReq2 = new XMLHttpRequest(); oReq2.open("GET", 'mame/' + opts.jsfile.replace('.js','.wasm'), true); oReq2.responseType = "arraybuffer"; - oReq2.onload = function(oEvent) { + oReq2.onload = (oEvent) => { console.log("loaded WASM file"); window['Module'].wasmBinary = new Uint8Array(oReq2.response); fetch_wasm.resolve(); @@ -741,7 +744,7 @@ export function BaseMAMEPlatform() { oReq2.send(); } // start loading script - $.when(fetch_lua, fetch_cfg, fetch_bios, fetch_wasm).done(function() { + $.when(fetch_lua, fetch_cfg, fetch_bios, fetch_wasm).done( () => { var script = document.createElement('script'); script.src = 'mame/' + opts.jsfile; document.getElementsByTagName('head')[0].appendChild(script); @@ -749,32 +752,32 @@ export function BaseMAMEPlatform() { }); } - this.loadROMFile = function(data) { - romdata = data; - if (preinitted && romfn) { - FS.writeFile(romfn, data, {encoding:'binary'}); + loadROMFile(data) { + this.romdata = data; + if (this.preinitted && this.romfn) { + FS.writeFile(this.romfn, data, {encoding:'binary'}); } } - this.loadRegion = function(region, data) { - if (loaded) { - //self.luacall('cart=manager:machine().images["cart"]\nprint(cart:filename())\ncart:load("' + romfn + '")\n'); + loadRegion(region, data) { + if (this.loaded) { + //this.luacall('cart=manager:machine().images["cart"]\nprint(cart:filename())\ncart:load("' + romfn + '")\n'); var s = 'rgn = manager:machine():memory().regions["' + region + '"]\n'; //s += 'print(rgn.size)\n'; for (var i=0; i>>v"); print(mem:read_u8(' + a + '))'); - return parseInt(console_vars.v[0]); + readAddress(a) { + this.initlua(); + this.luacall('print(">>>v"); print(mem:read_u8(' + a + '))'); + return parseInt(this.console_vars.v[0]); } // DEBUGGING SUPPORT - var onBreakpointHit; - - this.clearDebug = function() { - onBreakpointHit = null; + clearDebug() { + this.onBreakpointHit = null; } - this.getDebugCallback = function() { - return onBreakpointHit;// TODO? + getDebugCallback() { + return this.onBreakpointHit;// TODO? } - this.setupDebug = function(callback) { - self.initlua(); - self.luareset(); - onBreakpointHit = callback; + setupDebug(callback) { + this.initlua(); + this.luareset(); + this.onBreakpointHit = callback; } - this.runToPC = function(pc) { - self.luacall('mamedbg.runTo(' + pc + ')'); - self.resume(); + runToPC(pc) { + this.luacall('mamedbg.runTo(' + pc + ')'); + this.resume(); } - this.runToVsync = function() { - self.luacall('mamedbg.runToVsync()'); - self.resume(); + runToVsync() { + this.luacall('mamedbg.runToVsync()'); + this.resume(); } - this.runUntilReturn = function() { - self.luacall('mamedbg.runUntilReturn()'); - self.resume(); + runUntilReturn() { + this.luacall('mamedbg.runUntilReturn()'); + this.resume(); } - this.step = function() { - self.luacall('mamedbg.step()'); - self.resume(); + step() { + this.luacall('mamedbg.step()'); + this.resume(); } // TODO: other than z80 - this.cpuStateToLongString = function(c) { + cpuStateToLongString(c) { if (c.HL) return cpuStateToLongString_Z80(c); else diff --git a/src/platform/apple2.ts b/src/platform/apple2.ts index 08a46c24..2b2135af 100644 --- a/src/platform/apple2.ts +++ b/src/platform/apple2.ts @@ -7,7 +7,7 @@ import { SampleAudio } from "../audio"; declare var jt; // 6502 -var APPLE2_PRESETS = [ +const APPLE2_PRESETS = [ {id:'sieve.c', name:'Sieve'}, {id:'mandel.c', name:'Mandelbrot'}, {id:'tgidemo.c', name:'TGI Graphics Demo'}, @@ -19,15 +19,12 @@ var APPLE2_PRESETS = [ // {id:'tb_6502.s', name:'Tom Bombem (assembler game)'}, ]; -var GR_TXMODE = 1; -var GR_MIXMODE = 2; -var GR_PAGE1 = 4; -var GR_HIRES = 8; +const GR_TXMODE = 1; +const GR_MIXMODE = 2; +const GR_PAGE1 = 4; +const GR_HIRES = 8; -var Apple2Platform = function(mainElement) { - var self = this; - this.__proto__ = new (Base6502Platform as any)(); - +const _Apple2Platform = function(mainElement) { var cpuFrequency = 1023000; var cpuCyclesPerLine = 65; var cpu, ram, bus; @@ -50,11 +47,12 @@ var Apple2Platform = function(mainElement) { var bank2rdoffset=0, bank2wroffset=0; var grparams; - this.getPresets = function() { + class Apple2Platform extends Base6502Platform { + + getPresets() { return APPLE2_PRESETS; } - - this.start = function() { + start() { cpu = new jt.M6502(); ram = new RAM(0x13000); // 64K + 16K LC RAM - 4K hardware // ROM @@ -183,7 +181,7 @@ var Apple2Platform = function(mainElement) { timer = new AnimationTimer(60, this.advance.bind(this)); } - this.advance = function(novideo : boolean) { + advance(novideo : boolean) { // 262.5 scanlines per frame var clock = 0; var debugCond = this.getDebugCallback(); @@ -214,6 +212,70 @@ var Apple2Platform = function(mainElement) { this.restartDebugState(); // reset debug start state } + loadROM(title, data) { + pgmbin = data; + this.reset(); + } + + isRunning() { + return timer.isRunning(); + } + pause() { + timer.stop(); + audio.stop(); + } + resume() { + timer.start(); + audio.start(); + } + reset() { + cpu.reset(); + // execute until $c600 boot + for (var i=0; i<2000000; i++) { + cpu.clockPulse(); + if (this.getCPUState().PC == 0xc602) { + cpu.clockPulse(); + cpu.clockPulse(); + break; + } + } + } + readAddress(addr) { + return bus.read(addr); + } + + loadState(state) { + cpu.loadState(state.c); + ram.mem.set(state.b); + kbdlatch = state.kbd; + grswitch = state.gr; + auxRAMselected = state.lc.s; + auxRAMbank = state.lc.b; + writeinhibit = state.lc.w; + setupLanguageCardConstants(); + } + saveState() { + return { + c:cpu.saveState(), + b:ram.mem.slice(0), + kbd:kbdlatch, + gr:grswitch, + lc:{s:auxRAMselected,b:auxRAMbank,w:writeinhibit}, + }; + } + loadControlsState(state) { + kbdlatch = state.kbd; + } + saveControlsState() { + return { + kbd:kbdlatch, + }; + } + getCPUState() { + return cpu.saveState(); + } + } + function doLanguageCardIO(address, value) { switch (address & 0x0f) { @@ -284,68 +346,7 @@ var Apple2Platform = function(mainElement) { bank2wroffset = 0x3000; // map 0xd000-0xdfff -> 0x10000-0x10fff } - this.loadROM = function(title, data) { - pgmbin = data; - this.reset(); - } - - this.isRunning = function() { - return timer.isRunning(); - } - this.pause = function() { - timer.stop(); - audio.stop(); - } - this.resume = function() { - timer.start(); - audio.start(); - } - this.reset = function() { - cpu.reset(); - // execute until $c600 boot - for (var i=0; i<2000000; i++) { - cpu.clockPulse(); - if (this.getCPUState().PC == 0xc602) { - cpu.clockPulse(); - cpu.clockPulse(); - break; - } - } - } - this.readAddress = function(addr) { - return bus.read(addr); - } - - this.loadState = function(state) { - cpu.loadState(state.c); - ram.mem.set(state.b); - kbdlatch = state.kbd; - grswitch = state.gr; - auxRAMselected = state.lc.s; - auxRAMbank = state.lc.b; - writeinhibit = state.lc.w; - setupLanguageCardConstants(); - } - this.saveState = function() { - return { - c:cpu.saveState(), - b:ram.mem.slice(0), - kbd:kbdlatch, - gr:grswitch, - lc:{s:auxRAMselected,b:auxRAMbank,w:writeinhibit}, - }; - } - this.loadControlsState = function(state) { - kbdlatch = state.kbd; - } - this.saveControlsState = function() { - return { - kbd:kbdlatch, - }; - } - this.getCPUState = function() { - return cpu.saveState(); - } + return new Apple2Platform(); // return inner class from constructor }; var Apple2Display = function(pixels, apple) { @@ -1015,12 +1016,10 @@ var APPLEIIGO_LZG = [ /// MAME support -var Apple2MAMEPlatform = function(mainElement) { - var self = this; - this.__proto__ = new BaseMAMEPlatform(); +class Apple2MAMEPlatform extends BaseMAMEPlatform { - this.start = function() { - self.startModule(mainElement, { + start () { + this.startModule(this.mainElement, { jsfile:'mameapple2e.js', biosfile:['apple2e.zip'], //cfgfile:'nes.cfg', @@ -1035,16 +1034,16 @@ var Apple2MAMEPlatform = function(mainElement) { }); } - this.getOpcodeMetadata = getOpcodeMetadata_6502; - this.getDefaultExtension = function() { return ".c"; }; + getOpcodeMetadata = getOpcodeMetadata_6502; + getDefaultExtension () { return ".c"; }; - this.getPresets = function() { return APPLE2_PRESETS; } + getPresets () { return APPLE2_PRESETS; } - this.loadROM = function(title, data) { + loadROM (title, data) { this.loadROMFile(data); // TODO } } -PLATFORMS['apple2'] = Apple2Platform; +PLATFORMS['apple2'] = _Apple2Platform; PLATFORMS['apple2-e'] = Apple2MAMEPlatform; diff --git a/src/platform/atari8.ts b/src/platform/atari8.ts index de67af57..7bd91aba 100644 --- a/src/platform/atari8.ts +++ b/src/platform/atari8.ts @@ -12,7 +12,7 @@ var Atari8_PRESETS = [ var Atari8MAMEPlatform = function(mainElement) { var self = this; - this.__proto__ = new BaseMAMEPlatform(); + this.__proto__ = new (BaseMAMEPlatform as any)(); this.loadROM = function(title, data) { this.loadROMFile(data); @@ -47,7 +47,7 @@ var Atari800Platform = function(mainElement) { var Atari5200Platform = function(mainElement) { var self = this; - this.__proto__ = new Atari8MAMEPlatform(mainElement); + this.__proto__ = new (Atari8MAMEPlatform as any)(mainElement); this.start = function() { self.startModule(mainElement, { diff --git a/src/platform/coleco.ts b/src/platform/coleco.ts index c61a0cb2..658ea96c 100644 --- a/src/platform/coleco.ts +++ b/src/platform/coleco.ts @@ -33,13 +33,10 @@ var ColecoVision_PRESETS = [ /// MAME support -var ColecoVisionMAMEPlatform = function(mainElement) { - var self = this; - this.__proto__ = new BaseMAMEPlatform(); +class ColecoVisionMAMEPlatform extends BaseMAMEPlatform { -// - this.start = function() { - self.startModule(mainElement, { + start() { + this.startModule(this.mainElement, { jsfile:'mamecoleco.js', cfgfile:'coleco.cfg', biosfile:'coleco/313 10031-4005 73108a.u2', @@ -53,15 +50,15 @@ var ColecoVisionMAMEPlatform = function(mainElement) { }); } - this.loadROM = function(title, data) { + loadROM(title, data) { this.loadROMFile(data); this.loadRegion(":coleco_cart:rom", data); } - this.getPresets = function() { return ColecoVision_PRESETS; } + getPresets() { return ColecoVision_PRESETS; } - this.getToolForFilename = getToolForFilename_z80; - this.getDefaultExtension = function() { return ".c"; }; + getToolForFilename = getToolForFilename_z80; + getDefaultExtension() { return ".c"; }; } /// diff --git a/src/platform/galaxian.ts b/src/platform/galaxian.ts index 6232ada6..c7865bd0 100644 --- a/src/platform/galaxian.ts +++ b/src/platform/galaxian.ts @@ -5,12 +5,12 @@ import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, import { hex } from "../util"; import { MasterAudio, AY38910_Audio } from "../audio"; -var GALAXIAN_PRESETS = [ +const GALAXIAN_PRESETS = [ {id:'gfxtest.c', name:'Graphics Test'}, {id:'shoot2.c', name:'Solarian Game'}, ]; -var GALAXIAN_KEYCODE_MAP = makeKeycodeMap([ +const GALAXIAN_KEYCODE_MAP = makeKeycodeMap([ [Keys.VK_SPACE, 0, 0x10], // P1 [Keys.VK_LEFT, 0, 0x4], [Keys.VK_RIGHT, 0, 0x8], @@ -22,7 +22,7 @@ var GALAXIAN_KEYCODE_MAP = makeKeycodeMap([ [Keys.VK_2, 1, 0x2], ]); -var SCRAMBLE_KEYCODE_MAP = makeKeycodeMap([ +const SCRAMBLE_KEYCODE_MAP = makeKeycodeMap([ [Keys.VK_UP, 0, -0x1], // P1 [Keys.VK_SHIFT, 0, -0x2], // fire [Keys.VK_7, 0, -0x4], // credit @@ -38,10 +38,7 @@ var SCRAMBLE_KEYCODE_MAP = makeKeycodeMap([ ]); -var GalaxianPlatform = function(mainElement, options) { - var self = this; - this.__proto__ = new (BaseZ80Platform as any)(); - +const _GalaxianPlatform = function(mainElement, options) { options = options || {}; var romSize = options.romSize || 0x4000; var gfxBase = options.gfxBase || 0x2800; @@ -169,10 +166,6 @@ var GalaxianPlatform = function(mainElement, options) { } } - this.getPresets = function() { - return GALAXIAN_PRESETS; - } - var m_protection_state = 0; var m_protection_result = 0; function scramble_protection_w(addr,data) { @@ -200,8 +193,20 @@ var GalaxianPlatform = function(mainElement, options) { var bit = (m_protection_result >> 7) & 1; return (bit << 5) | ((bit^1) << 7); } + + const bitcolors = [ + 0x000021, 0x000047, 0x000097, // red + 0x002100, 0x004700, 0x009700, // green + 0x510000, 0xae0000 // blue + ]; - this.start = function() { + class GalaxianPlatform extends BaseZ80Platform { + + getPresets() { + return GALAXIAN_PRESETS; + } + + start() { ram = new RAM(0x800); vram = new RAM(0x400); oram = new RAM(0x100); @@ -271,9 +276,6 @@ var GalaxianPlatform = function(mainElement, options) { isContended: function() { return false; }, }; } - this.readAddress = function(a) { - return (a == 0x7000 || a == 0x7800) ? null : membus.read(a); // ignore watchdog - }; audio = new MasterAudio(); psg1 = new AY38910_Audio(audio); psg2 = new AY38910_Audio(audio); @@ -288,7 +290,7 @@ var GalaxianPlatform = function(mainElement, options) { if (addr & 0x8) { psg2.setData(val); }; } }; - cpu = self.newCPU(membus, iobus); + cpu = this.newCPU(membus, iobus); video = new RasterVideo(mainElement,264,264,{rotate:90}); video.create(); var idata = video.getFrameData(); @@ -297,7 +299,11 @@ var GalaxianPlatform = function(mainElement, options) { timer = new AnimationTimer(60, this.advance.bind(this)); } - this.advance = function(novideo : boolean) { + readAddress(a) { + return (a == 0x7000 || a == 0x7800) ? null : membus.read(a); // ignore watchdog + } + + advance(novideo : boolean) { var debugCond = this.getDebugCallback(); var targetTstates = cpu.getTstates(); for (var sl=0; slframe0; }); } - this.getCPUState = function() { + getCPUState() { var c = nes.cpu.toJSON(); this.copy6502REGvars(c); return c; } // TODO don't need to save ROM? - this.saveState = function() { + saveState() { //var s = $.extend(true, {}, nes); var s = nes.toJSON(); s.c = s.cpu; @@ -181,9 +181,10 @@ var JSNESPlatform = function(mainElement) { s.b = s.cpu.mem = s.cpu.mem.slice(0); s.ppu.vramMem = s.ppu.vramMem.slice(0); s.ppu.spriteMem = s.ppu.spriteMem.slice(0); + s.ctrl = this.saveControlsState(); return s; } - this.loadState = function(state) { + loadState(state) { nes.fromJSON(state); //nes.cpu.fromJSON(state.cpu); //nes.mmap.fromJSON(state.mmap); @@ -191,12 +192,23 @@ var JSNESPlatform = function(mainElement) { nes.cpu.mem = state.cpu.mem.slice(0); nes.ppu.vramMem = state.ppu.vramMem.slice(0); nes.ppu.spriteMem = state.ppu.spriteMem.slice(0); + this.loadControlsState(state.ctrl); //$.extend(nes, state); } - this.readAddress = function(addr) { + saveControlsState() { + return { + c1: nes.controllers[1].state.slice(0), + c2: nes.controllers[2].state.slice(0), + }; + } + loadControlsState(state) { + nes.controllers[1].state = state.c1; + nes.controllers[2].state = state.c2; + } + readAddress(addr) { return nes.cpu.mem[addr] & 0xff; } - this.copy6502REGvars = function(c) { + copy6502REGvars(c) { c.T = 0; c.PC = c.REG_PC; c.A = c.REG_ACC; @@ -214,10 +226,10 @@ var JSNESPlatform = function(mainElement) { return c; } - this.getDebugCategories = function() { + getDebugCategories() { return ['CPU','ZPRAM','Stack','PPU']; } - this.getDebugInfo = function(category, state) { + getDebugInfo(category, state) { switch (category) { case 'CPU': return cpuStateToLongString_6502(state.c); case 'ZPRAM': return dumpRAM(state.b, 0x0, 0x100); @@ -225,7 +237,7 @@ var JSNESPlatform = function(mainElement) { case 'PPU': return this.ppuStateToLongString(state.ppu, state.b); } } - this.ppuStateToLongString = function(ppu, mem) { + ppuStateToLongString(ppu, mem) { var s = ''; var PPUFLAGS = [ ["f_nmiOnVblank","NMI_ON_VBLANK"], @@ -283,54 +295,57 @@ var JSNESPlatform = function(mainElement) { */ return s; } + } + return new JSNESPlatform(); } /// MAME support -var NESMAMEPlatform = function(mainElement, lzgRom, romSize) { - var self = this; - this.__proto__ = new BaseMAMEPlatform(); +class NESMAMEPlatform extends BaseMAMEPlatform { +// = function(mainElement, lzgRom, romSize) { + lzgRom; + romSize; -// - this.start = function() { - self.startModule(mainElement, { + start() { + this.startModule(this.mainElement, { jsfile:'mamenes.js', //cfgfile:'nes.cfg', driver:'nes', width:256*2, height:240*2, romfn:'/emulator/cart.nes', - romsize:romSize, - romdata:new Uint8Array(new lzgmini().decode(lzgRom).slice(0, romSize)), + romsize:this.romSize, + romdata:new Uint8Array(new lzgmini().decode(this.lzgRom).slice(0, this.romSize)), preInit:function(_self) { }, }); } - this.getOpcodeMetadata = getOpcodeMetadata_6502; - this.getToolForFilename = getToolForFilename_6502; - this.getDefaultExtension = function() { return ".c"; }; + getOpcodeMetadata = getOpcodeMetadata_6502; + getToolForFilename = getToolForFilename_6502; + getDefaultExtension() { return ".c"; }; } -var NESConIOPlatform = function(mainElement) { - var self = this; - this.__proto__ = new NESMAMEPlatform(mainElement, NES_CONIO_ROM_LZG, 0xa010); +class NESConIOPlatform extends NESMAMEPlatform { + lzgRom = NES_CONIO_ROM_LZG; + romSize = 0xa010; - this.getPresets = function() { return NES_CONIO_PRESETS; } + getPresets() { return NES_CONIO_PRESETS; } - this.loadROM = function(title, data) { + loadROM(title, data) { this.loadROMFile(data); this.loadRegion(":nes_slot:cart:prg_rom", data.slice(0x10, 0x8010)); this.loadRegion(":nes_slot:cart:chr_rom", data.slice(0x8010, 0xa010)); } } -var NESLibPlatform = function(mainElement) { - var self = this; - this.__proto__ = new NESMAMEPlatform(mainElement, NES_NESLIB_ROM_LZG, 0x8010); - this.getPresets = function() { return NES_NESLIB_PRESETS; } +class NESLibPlatform extends NESMAMEPlatform { + lzgRom = NES_NESLIB_ROM_LZG; + romSize = 0x8010; - this.loadROM = function(title, data) { + getPresets() { return NES_NESLIB_PRESETS; } + + loadROM(title, data) { this.loadROMFile(data); this.loadRegion(":nes_slot:cart:prg_rom", data.slice(0x10, 0x8010)); } @@ -338,7 +353,7 @@ var NESLibPlatform = function(mainElement) { /// -PLATFORMS['nes'] = JSNESPlatform; +PLATFORMS['nes'] = _JSNESPlatform; PLATFORMS['nes-lib'] = NESLibPlatform; PLATFORMS['nes-conio'] = NESConIOPlatform; diff --git a/src/platform/vcs.ts b/src/platform/vcs.ts index 7bfc5df6..64c78b4d 100644 --- a/src/platform/vcs.ts +++ b/src/platform/vcs.ts @@ -9,7 +9,7 @@ declare var platform : Platform; // global platform object declare var Javatari : any; declare var jt : any; // 6502 -var VCS_PRESETS = [ +const VCS_PRESETS = [ {id:'examples/hello', chapter:4, name:'Hello 6502 and TIA'}, {id:'examples/vsync', chapter:5, name:'Painting on the CRT', title:'Color Bars'}, {id:'examples/playfield', chapter:6, name:'Playfield Graphics'}, @@ -47,13 +47,16 @@ Javatari.CARTRIDGE_CHANGE_DISABLED = true; Javatari.DEBUG_SCANLINE_OVERFLOW = false; // TODO: make a switch Javatari.AUDIO_BUFFER_SIZE = 256; -var VCSPlatform = function() { - var self = this; - this.paused = true; +class VCSPlatform { - this.getPresets = function() { return VCS_PRESETS; } + current_output; + recorder : EmuRecorder; + paused : boolean = true; - this.start = function() { + getPresets() { return VCS_PRESETS; } + + start() { + var self = this; $("#javatari-div").show(); Javatari.start(); // intercept clockPulse function @@ -65,16 +68,16 @@ var VCSPlatform = function() { this.paused = false; } - this.loadROM = function(title, data) { + loadROM(title, data) { Javatari.loadROM(title, data); this.current_output = data; // TODO: use bus } - this.getOpcodeMetadata = function(opcode, offset) { + getOpcodeMetadata(opcode, offset) { return Javatari.getOpcodeMetadata(opcode, offset); } - this.getRasterPosition = function() { + getRasterPosition() { var clkfs = Javatari.room.console.getClocksFromFrameStart() - 1; var row = Math.floor(clkfs/76); var col = clkfs - row*76; @@ -84,10 +87,10 @@ var VCSPlatform = function() { } // TODO: Clock changes this on event, so it may not be current - this.isRunning = function() { + isRunning() { return Javatari.room && Javatari.room.console.isRunning(); } - this.pause = function() { + pause() { //console.log('pause', this.paused, this.isRunning()); if (!this.paused) { this.paused = true; @@ -95,7 +98,7 @@ var VCSPlatform = function() { Javatari.room.speaker.mute(); } } - this.resume = function() { + resume() { //console.log('resume', this.paused, this.isRunning()); if (this.paused) { this.paused = false; @@ -103,51 +106,52 @@ var VCSPlatform = function() { Javatari.room.speaker.play(); } } - this.advance = function() { + advance() { Javatari.room.console.clockPulse(); } - this.step = function() { Javatari.room.console.debugSingleStepCPUClock(); } - this.stepBack = function() { Javatari.room.console.debugStepBackInstruction(); } - this.runEval = function(evalfunc) { Javatari.room.console.debugEval(evalfunc); } + step() { Javatari.room.console.debugSingleStepCPUClock(); } + stepBack() { Javatari.room.console.debugStepBackInstruction(); } + runEval(evalfunc) { Javatari.room.console.debugEval(evalfunc); } - this.setupDebug = function(callback) { - Javatari.room.console.onBreakpointHit = function(state) { - self.paused = true; + setupDebug(callback) { + Javatari.room.console.onBreakpointHit = (state) => { + this.paused = true; callback(state); } Javatari.room.speaker.mute(); } - this.clearDebug = function() { + clearDebug() { Javatari.room.console.disableDebug(); Javatari.room.console.onBreakpointHit = null; if (this.isRunning()) Javatari.room.speaker.play(); } - this.reset = function() { + reset() { Javatari.room.console.powerOff(); Javatari.room.console.resetDebug(); Javatari.room.console.powerOn(); Javatari.room.speaker.play(); } - this.getOriginPC = function() { + getOriginPC() { return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff; } - this.newCodeAnalyzer = function() { + newCodeAnalyzer() { return new CodeAnalyzer_vcs(this); } - this.saveState = function() { + saveState() { return Javatari.room.console.saveState(); } - this.loadState = function(state) { + loadState(state) { return Javatari.room.console.loadState(state); } - this.readAddress = function(addr) { + // TODO: load/save controls state + readAddress(addr) { return this.current_output[addr & 0xfff]; // TODO: use bus to read } - this.runUntilReturn = function() { + runUntilReturn() { var depth = 1; - self.runEval(function(c) { + this.runEval(function(c) { if (depth <= 0 && c.T == 0) return true; if (c.o == 0x20) @@ -157,38 +161,38 @@ var VCSPlatform = function() { return false; }); } - this.cpuStateToLongString = function(c) { + cpuStateToLongString(c) { return cpuStateToLongString_6502(c); } - this.getRAMForState = function(state) { + getRAMForState(state) { return jt.Util.byteStringToUInt8Array(atob(state.r.b)); } - this.ramStateToLongString = function(state) { - var ram = self.getRAMForState(state); + ramStateToLongString(state) { + var ram = this.getRAMForState(state); return "\n" + dumpRAM(ram, 0x80, 0x80); } - this.getToolForFilename = function(fn) { + getToolForFilename(fn) { return "dasm"; } - this.getDefaultExtension = function() { return ".a"; }; + getDefaultExtension() { return ".a"; }; - this.getDebugCategories = function() { + getDebugCategories() { return ['CPU','PIA','TIA']; } - this.getDebugInfo = function(category, state) { + getDebugInfo(category, state) { switch (category) { case 'CPU': return this.cpuStateToLongString(state.c); case 'PIA': return this.ramStateToLongString(state) + "\n" + this.piaStateToLongString(state.p); case 'TIA': return this.tiaStateToLongString(state.t); } } - this.piaStateToLongString = function(p) { + piaStateToLongString(p) { return "Timer " + p.t + "/" + p.c + "\n"; } - this.tiaStateToLongString = function(t) { + tiaStateToLongString(t) { var pos = this.getRasterPosition(); var s = ''; - s += "H" + lpad(pos.x,5) + " V" + lpad(pos.y,5) + " "; + s += "H" + lpad(pos.x.toString(),5) + " V" + lpad(pos.y.toString(),5) + " "; s += (t.vs?"VSYNC ":"- ") + (t.vb?"VBLANK ":"- ") + "\n"; s += "\n"; s += "Playfield " + t.f + "\n"; @@ -214,13 +218,13 @@ var VCSPlatform = function() { return s; } - this.setRecorder = function(recorder : EmuRecorder) : void { + setRecorder(recorder : EmuRecorder) : void { this.recorder = recorder; } - this.updateRecorder = function() { + updateRecorder() { // are we recording and do we need to save a frame? if (this.recorder && !this.paused && this.isRunning() && this.recorder.frameRequested()) { - this.recorder.recordFrame(this, this.saveState()); + this.recorder.recordFrame(this.saveState()); } } }; @@ -231,13 +235,12 @@ function nonegstr(n) { /////////////// -var VCSMAMEPlatform = function(mainElement) { - var self = this; - this.__proto__ = new BaseMAMEPlatform(); +class VCSMAMEPlatform extends BaseMAMEPlatform { // MCFG_SCREEN_RAW_PARAMS( MASTER_CLOCK_NTSC, 228, 26, 26 + 160 + 16, 262, 24 , 24 + 192 + 31 ) - this.start = function() { - self.startModule(mainElement, { + + start = function() { + this.startModule(this.mainElement, { jsfile:'mamea2600.js', driver:'a2600', width:176*2, @@ -247,27 +250,21 @@ var VCSMAMEPlatform = function(mainElement) { }); } - this.loadROM = function(title, data) { + loadROM = function(title, data) { this.loadROMFile(data); this.loadRegion(":cartslot:cart:rom", data); } - this.getPresets = function() { return VCS_PRESETS; } + getPresets = function() { return VCS_PRESETS; } - this.getToolForFilename = function(fn) { + getToolForFilename = function(fn) { return "dasm"; } - this.getDefaultExtension = function() { return ".a"; }; + getDefaultExtension = function() { return ".a"; }; - this.getOriginPC = function() { + getOriginPC = function() { return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff; } - /* - this.getOpcodeMetadata = function(opcode, offset) { - return Javatari.getOpcodeMetadata(opcode, offset); - } - */ - } //////////////// diff --git a/src/platform/vector.ts b/src/platform/vector.ts index c3f63195..ded29326 100644 --- a/src/platform/vector.ts +++ b/src/platform/vector.ts @@ -169,6 +169,14 @@ var AtariVectorPlatform = function(mainElement) { nmic:nmicount } } + this.loadControlsState = function(state) { + switches.set(state.sw); + } + this.saveControlsState = function() { + return { + sw:switches.slice(0), + } + } this.getCPUState = function() { return cpu.saveState(); } @@ -308,6 +316,14 @@ var AtariColorVectorPlatform = function(mainElement) { nmic:nmicount } } + this.loadControlsState = function(state) { + switches.set(state.sw); + } + this.saveControlsState = function() { + return { + sw:switches.slice(0), + } + } this.getCPUState = function() { return cpu.saveState(); } @@ -431,6 +447,14 @@ var Z80ColorVectorPlatform = function(mainElement, proto) { mr:mathram.slice(0), } } + this.loadControlsState = function(state) { + switches.set(state.sw); + } + this.saveControlsState = function() { + return { + sw:switches.slice(0), + } + } this.getCPUState = function() { return cpu.saveState(); } diff --git a/src/platform/vicdual.ts b/src/platform/vicdual.ts index d3622ef6..80e321ba 100644 --- a/src/platform/vicdual.ts +++ b/src/platform/vicdual.ts @@ -5,7 +5,7 @@ import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, import { hex } from "../util"; import { MasterAudio, AY38910_Audio } from "../audio"; -var VICDUAL_PRESETS = [ +const VICDUAL_PRESETS = [ {id:'minimal.c', name:'Minimal Example'}, {id:'hello.c', name:'Hello World'}, {id:'gfxtest.c', name:'Graphics Test'}, @@ -15,31 +15,30 @@ var VICDUAL_PRESETS = [ {id:'music.c', name:'Music Player'}, ]; -var VicDualPlatform = function(mainElement) { - var self = this; - this.__proto__ = new (BaseZ80Platform as any)(); +const _VicDualPlatform = function(mainElement) { 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; - var XTAL = 15468000.0; - var scanlinesPerFrame = 0x106; - var vblankStart = 0xe0; - var vsyncStart = 0xec; - var vsyncEnd = 0xf0; - var cpuFrequency = XTAL/8; - var hsyncFrequency = XTAL/3/scanlinesPerFrame; - var vsyncFrequency = hsyncFrequency/0x148; - var cpuCyclesPerLine = cpuFrequency/hsyncFrequency; - var timerFrequency = 500; // input 2 bit 0x8 - var cyclesPerTimerTick = cpuFrequency / (2 * timerFrequency); + 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); + var reset_disable = false; var reset_disable_timer; var framestats; - var palette = [ + const palette = [ 0xff000000, // black 0xff0000ff, // red 0xff00ff00, // green @@ -50,6 +49,7 @@ var VicDualPlatform = function(mainElement) { 0xffffffff // white ]; + // default PROM var colorprom = [ 0xe0,0x60,0x20,0x60, 0xc0,0x60,0x40,0xc0, 0x20,0x40,0x60,0x80, 0xa0,0xc0,0xe0,0x0e, @@ -85,7 +85,7 @@ var VicDualPlatform = function(mainElement) { } } - var CARNIVAL_KEYCODE_MAP = makeKeycodeMap([ + const CARNIVAL_KEYCODE_MAP = makeKeycodeMap([ [Keys.VK_SPACE, 2, -0x20], [Keys.VK_SHIFT, 2, -0x40], [Keys.VK_LEFT, 1, -0x10], @@ -96,12 +96,14 @@ var VicDualPlatform = function(mainElement) { [Keys.VK_2, 3, -0x20], [Keys.VK_5, 3, 0x8], ]); + + class VicDualPlatform extends BaseZ80Platform { - this.getPresets = function() { + getPresets() { return VICDUAL_PRESETS; } - this.start = function() { + start() { ram = new RAM(0x1000); membus = { read: newAddressDecoder([ @@ -113,7 +115,6 @@ var VicDualPlatform = function(mainElement) { ]), isContended: function() { return false; }, }; - this.readAddress = membus.read; iobus = { read: function(addr) { return inputs[addr&3]; @@ -145,7 +146,11 @@ var VicDualPlatform = function(mainElement) { timer = new AnimationTimer(60, this.advance.bind(this)); } - this.advance = function(novideo : boolean) { + readAddress(addr) { + return membus.read(addr); + } + + advance(novideo : boolean) { var targetTstates = cpu.getTstates(); for (var sl=0; sl= 0x4020 && (data[0x4000] || data[0x401f])) { colorprom = data.slice(0x4000,0x4020); } rom = padBytes(data, 0x4040); - self.reset(); + this.reset(); } - this.loadState = function(state) { + loadState(state) { cpu.loadState(state.c); ram.mem.set(state.b); - inputs[0] = state.in0; - inputs[1] = state.in1; - inputs[2] = state.in2; - inputs[3] = state.in3; + this.loadControlsState(state); palbank = state.pb; } - this.saveState = function() { + saveState() { return { - c:self.getCPUState(), + c:this.getCPUState(), b:ram.mem.slice(0), in0:inputs[0], in1:inputs[1], @@ -190,32 +192,48 @@ var VicDualPlatform = function(mainElement) { pb:palbank, }; } - this.getCPUState = function() { + 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(); } - this.isRunning = function() { + isRunning() { return timer && timer.isRunning(); } - this.pause = function() { + pause() { timer.stop(); audio.stop(); } - this.resume = function() { + resume() { timer.start(); audio.start(); } - this.reset = function() { + reset() { cpu.reset(); psg.reset(); if (!this.getDebugCallback()) cpu.setTstates(0); // TODO? } - this.setFrameStats = function(on) { + 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/src/platform/williams.ts b/src/platform/williams.ts index 7de5b274..f9cbc7c1 100644 --- a/src/platform/williams.ts +++ b/src/platform/williams.ts @@ -364,7 +364,7 @@ var WilliamsPlatform = function(mainElement, proto) { cpu.loadState(state.c); ram.mem.set(state.b); nvram.mem.set(state.nvram); - //pia6821.set(state.pia); + pia6821.set(state.pia); blitregs.set(state.blt); watchdog_counter = state.wdc; banksel = state.bs; @@ -382,6 +382,14 @@ var WilliamsPlatform = function(mainElement, proto) { ps:portsel, }; } + this.loadControlsState = function(state) { + pia6821.set(state.pia); + } + this.saveControlsState = function() { + return { + pia:pia6821.slice(0), + }; + } this.getCPUState = function() { return cpu.saveState(); } diff --git a/src/ui.ts b/src/ui.ts index 325b20a2..b713f4e5 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -951,6 +951,13 @@ function loadSharedFile(sharekey : string) { return true; } +function loadScript(scriptfn, onload) { + var script = document.createElement('script'); + script.onload = onload; + script.src = scriptfn; + document.getElementsByTagName('head')[0].appendChild(script); +} + // start function startUI(loadplatform : boolean) { installErrorHandler(); @@ -975,14 +982,11 @@ function startUI(loadplatform : boolean) { // load and start platform object if (loadplatform) { var scriptfn = 'gen/platform/' + platform_id.split(/[.-]/)[0] + '.js'; - var script = document.createElement('script'); - script.onload = function() { + loadScript(scriptfn, () => { console.log("loaded platform", platform_id); startPlatform(); showWelcomeMessage(); - }; - script.src = scriptfn; - document.getElementsByTagName('head')[0].appendChild(script); + }); } else { startPlatform(); showWelcomeMessage();