From 878c61c9bfbc539c3b575e451af38199d0eafa39 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Wed, 11 Jul 2018 10:17:37 -0500 Subject: [PATCH] moved base 6502/z80 platforms to .ts; mocha 5.2.0 --- index.html | 1 + package.json | 2 +- src/audio/z80worker.js | 2 +- src/baseplatform.ts | 657 ++++++++++++++++++++++++++++++++++++- src/emu.ts | 716 +---------------------------------------- src/ui.ts | 2 +- viz.html | 84 ----- 7 files changed, 657 insertions(+), 807 deletions(-) delete mode 100644 viz.html diff --git a/index.html b/index.html index 2516ad8f..d2e5cba9 100644 --- a/index.html +++ b/index.html @@ -245,6 +245,7 @@ function require(modname) { + diff --git a/package.json b/package.json index b6e3353d..40baa8ff 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "typescript": "^2.9.2", "@types/jquery": "^2.x", "@types/bootstrap": "^3.x", - "mocha": "^3.2.0", + "mocha": "^5.2.0", "mocha-phantomjs": "^4.1.0", "wavedrom-cli": "^0.5.0" }, diff --git a/src/audio/z80worker.js b/src/audio/z80worker.js index e9675dac..2101c710 100644 --- a/src/audio/z80worker.js +++ b/src/audio/z80worker.js @@ -26,7 +26,7 @@ var window = {}; -importScripts("../emu.js"); +importScripts("../../gen/emu.js"); importScripts("../cpu/z80.js"); window.buildZ80({ diff --git a/src/baseplatform.ts b/src/baseplatform.ts index 8cb5fd6e..d47b159f 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -1,9 +1,19 @@ export interface OpcodeMetadata { minCycles: number; - // TODO + maxCycles: number; + insnLength: number; } +interface CpuState { + PC:number, T?:number, o?:number/*opcode*/, + /* + A:number, X:number, Y:number, SP:number, R:boolean, + N,V,D,Z,C:boolean*/ +}; +interface EmuState {c:CpuState}; +type DisasmLine = {line:string, nbytes:number}; + export interface Platform { start() : void; reset() : void; @@ -16,7 +26,7 @@ export interface Platform { loadROM(title:string, rom:any); // TODO: Uint8Array inspect?(ident:string) : void; - disassemble?(addr:number, readfn:(addr:number)=>number) : any; // TODO + disassemble?(addr:number, readfn:(addr:number)=>number) : DisasmLine; readAddress?(addr:number) : number; setFrameRate?(fps:number) : void; getFrameRate?() : number; @@ -30,11 +40,11 @@ export interface Platform { runToPC?(pc:number) : void; runUntilReturn?() : void; stepBack?() : void; - //TODO runEval?(evalfn : (cpustate) => boolean) : void; - runEval?(evalfn : Function) : void; + runEval?(evalfunc/* : DebugEvalCondition*/) : void; getOpcodeMetadata?(opcode:number, offset:number) : OpcodeMetadata; //TODO - saveState?() : any; // TODO + loadState?(state : EmuState) : void; + saveState?() : EmuState; getDebugCallback?() : any; // TODO getSP?() : number; } @@ -46,3 +56,640 @@ export interface Preset { title? : string; } +export interface MemoryBus { + read : (address:number) => number; + write : (address:number, value:number) => void; +} + +type DebugCondition = () => boolean; +type DebugEvalCondition = (c:CpuState) => boolean; +type BreakpointCallback = (EmuState) => void; + +///// + +abstract class BaseDebugPlatform { + onBreakpointHit : BreakpointCallback; + debugCondition : DebugCondition; + debugSavedState : EmuState = null; + debugBreakState : EmuState = null; + debugTargetClock : number = 0; + debugClock : number = 0; + + abstract getCPUState() : CpuState; + abstract saveState() : EmuState; + abstract loadState?(state : EmuState) : void; + abstract pause() : void; + abstract resume() : void; + abstract readAddress?(addr:number) : number; + + getDebugCallback() : DebugCondition { + return this.debugCondition; + } + setupDebug(callback : BreakpointCallback) { + this.onBreakpointHit = callback; + } + clearDebug() { + this.debugSavedState = null; + this.debugBreakState = null; + this.debugTargetClock = 0; + this.debugClock = 0; + this.onBreakpointHit = null; + this.debugCondition = null; + } + setDebugCondition(debugCond : DebugCondition) { + if (this.debugSavedState) { + this.loadState(this.debugSavedState); + } else { + this.debugSavedState = this.saveState(); + } + this.debugClock = 0; + this.debugCondition = debugCond; + this.debugBreakState = null; + this.resume(); + } +} + +////// 6502 + +function getToolForFilename_6502(fn:string) : string { + if (fn.endsWith(".pla")) return "plasm"; + if (fn.endsWith(".c")) return "cc65"; + if (fn.endsWith(".s")) return "ca65"; + if (fn.endsWith(".acme")) return "acme"; + return "dasm"; // .a +} + +abstract class Base6502Platform extends BaseDebugPlatform { + + newCPU(membus : MemoryBus) { + var cpu = new jt.M6502(); + cpu.connectBus(membus); + return cpu; + } + + getOpcodeMetadata(opcode, offset) { + return Javatari.getOpcodeMetadata(opcode, offset); // TODO + } + + getOriginPC() : number { + return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff; + } + + restartDebugState() { + if (this.debugCondition && !this.debugBreakState) { + this.debugSavedState = this.saveState(); + this.debugTargetClock -= this.debugClock; + this.debugClock = 0; + } + } + breakpointHit(targetClock : number) { + this.debugTargetClock = targetClock; + this.debugBreakState = this.saveState(); + this.debugBreakState.c.PC = (this.debugBreakState.c.PC-1) & 0xffff; + console.log("Breakpoint at clk", this.debugClock, "PC", this.debugBreakState.c.PC.toString(16)); + this.pause(); + if (this.onBreakpointHit) { + this.onBreakpointHit(this.debugBreakState); + } + } + // TODO: lower bound of clock value + step() { + var previousPC = -1; + this.setDebugCondition( () => { + if (this.debugClock++ > this.debugTargetClock) { + var thisState = this.getCPUState(); + if (previousPC < 0) { + previousPC = thisState.PC; + } else { + if (thisState.PC != previousPC && thisState.T == 0) { + //console.log(previousPC.toString(16), thisPC.toString(16)); + this.breakpointHit(this.debugClock-1); + return true; + } + } + } + return false; + }); + } + stepBack() { + var prevState; + var prevClock; + this.setDebugCondition( () => { + if (this.debugClock++ >= this.debugTargetClock && prevState) { + this.loadState(prevState); + this.breakpointHit(prevClock-1); + return true; + } else if (this.debugClock > this.debugTargetClock-10 && this.debugClock < this.debugTargetClock) { + if (this.getCPUState().T == 0) { + prevState = this.saveState(); + prevClock = this.debugClock; + } + } + return false; + }); + } + runEval(evalfunc : DebugEvalCondition) { + this.setDebugCondition( () => { + if (this.debugClock++ > this.debugTargetClock) { + var cpuState = this.getCPUState(); + cpuState.PC = (cpuState.PC-1)&0xffff; + if (evalfunc(cpuState)) { + this.breakpointHit(this.debugClock-1); + return true; + } else { + return false; + } + } + }); + } + runUntilReturn() { + var depth = 1; + this.runEval( (c:CpuState) => { + if (depth <= 0 && c.T == 0) + return true; + if (c.o == 0x20) + depth++; + else if (c.o == 0x60 || c.o == 0x40) + --depth; + return false; + }); + } + disassemble(pc:number, read:(addr:number)=>number) : DisasmLine { + return disassemble6502(pc, read(pc), read(pc+1), read(pc+2)); + } + cpuStateToLongString(c:CpuState) : string { + return cpuStateToLongString_6502(c); + } + getToolForFilename = getToolForFilename_6502; + getDefaultExtension() { return ".a"; }; +} + +function cpuStateToLongString_6502(c) : string { + function decodeFlags(c) { + var s = ""; + s += c.N ? " N" : " -"; + s += c.V ? " V" : " -"; + s += c.D ? " D" : " -"; + s += c.Z ? " Z" : " -"; + s += c.C ? " C" : " -"; + // s += c.I ? " I" : " -"; + return s; + } + return "PC " + hex(c.PC,4) + " " + decodeFlags(c) + "\n" + + " A " + hex(c.A) + " " + (c.R ? "" : "BUSY") + "\n" + + " X " + hex(c.X) + "\n" + + " Y " + hex(c.Y) + " " + "SP " + hex(c.SP) + "\n"; +} + +////// Z80 + +function cpuStateToLongString_Z80(c) { + function decodeFlags(flags) { + var flagspec = "SZ-H-VNC"; + var s = ""; + for (var i=0; i<8; i++) + s += (flags & (128>>i)) ? flagspec.slice(i,i+1) : "-"; + return s; // TODO + } + return "PC " + hex(c.PC,4) + " " + decodeFlags(c.AF) + "\n" + + "SP " + hex(c.SP,4) + " IR " + hex(c.IR,4) + "\n" + + "IX " + hex(c.IX,4) + " IY " + hex(c.IY,4) + "\n" + + "AF " + hex(c.AF,4) + " BC " + hex(c.BC,4) + "\n" + + "DE " + hex(c.DE,4) + " HL " + hex(c.HL,4) + "\n" + ; +} + +function BusProbe(bus : MemoryBus) { + var active = false; + var callback; + this.activate = function(_callback) { + active = true; + callback = _callback; + } + this.deactivate = function() { + active = false; + callback = null; + } + this.read = function(a) { + if (active) { + callback(a); + } + return bus.read(a); + } + this.write = function(a,v) { + if (active) { + callback(a,v); + } + bus.write(a,v); + } +} + +abstract class BaseZ80Platform extends BaseDebugPlatform { + + _cpu; + probe; + + newCPU(membus, iobus) { + this.probe = new BusProbe(membus); + this._cpu = Z80_fast({ + display: {}, + memory: this.probe, + ioBus: iobus + }); + return this._cpu; + } + + getProbe() { return this.probe; } + getPC() { return this._cpu.getPC(); } + getSP() { return this._cpu.getSP(); } + + // TODO: refactor other parts into here + runCPU(cpu, cycles) { + this._cpu = cpu; // TODO? + if (this.wasBreakpointHit()) + return 0; + var debugCond = this.getDebugCallback(); + var targetTstates = cpu.getTstates() + cycles; + if (debugCond) { // || trace) { + while (cpu.getTstates() < targetTstates) { + //_trace(); // TODO + if (debugCond && debugCond()) { + debugCond = null; + break; + } + cpu.runFrame(cpu.getTstates() + 1); + } + } else { + cpu.runFrame(targetTstates); + } + return cpu.getTstates() - targetTstates; + } + restartDebugState() { + if (this.debugCondition && !this.debugBreakState) { + this.debugSavedState = this.saveState(); + if (this.debugTargetClock > 0) + this.debugTargetClock -= this.debugSavedState.c.T; + this.debugSavedState.c.T = 0; + this.loadState(this.debugSavedState); + } + } + breakpointHit(targetClock : number) { + this.debugTargetClock = targetClock; + this.debugBreakState = this.saveState(); + //this.debugBreakState.c.PC = (this.debugBreakState.c.PC-1) & 0xffff; + console.log("Breakpoint at clk", this.debugBreakState.c.T, "PC", this.debugBreakState.c.PC.toString(16)); + this.pause(); + if (this.onBreakpointHit) { + this.onBreakpointHit(this.debugBreakState); + } + } + wasBreakpointHit() : boolean { + return this.debugBreakState != null; + } + // TODO: lower bound of clock value + step() { + this.setDebugCondition( () => { + var cpuState = this.getCPUState(); + if (cpuState.T > this.debugTargetClock) { + this.breakpointHit(cpuState.T); + return true; + } + return false; + }); + } + stepBack() { + var prevState; + var prevClock; + this.setDebugCondition( () => { + var cpuState = this.getCPUState(); + var debugClock = cpuState.T; + if (debugClock >= this.debugTargetClock && prevState) { + this.loadState(prevState); + this.breakpointHit(prevClock); + return true; + } else if (debugClock > this.debugTargetClock-20 && debugClock < this.debugTargetClock) { + prevState = this.saveState(); + prevClock = debugClock; + } + return false; + }); + } + runEval(evalfunc : DebugEvalCondition) { + this.setDebugCondition( () => { + var cpuState = this.getCPUState(); + if (cpuState.T > this.debugTargetClock) { + if (evalfunc(cpuState)) { + this.breakpointHit(cpuState.T); + return true; + } + } + return false; + }); + } + runUntilReturn() { + var depth = 1; + this.runEval( (c) => { + if (depth <= 0) + return true; + var op = this.readAddress(c.PC); + if (op == 0xcd) // CALL + depth++; + else if (op == 0xc0 || op == 0xc8 || op == 0xc9 || op == 0xd0) // RET (TODO?) + --depth; + return false; + }); + } + cpuStateToLongString(c) { + return cpuStateToLongString_Z80(c); + } + getToolForFilename = getToolForFilename_z80; + getDefaultExtension() { return ".c"; }; + // TODO + //this.getOpcodeMetadata = function() { } +} + +function getToolForFilename_z80(fn) { + if (fn.endsWith(".c")) return "sdcc"; + if (fn.endsWith(".s")) return "sdasz80"; + if (fn.endsWith(".ns")) return "naken"; + if (fn.endsWith(".scc")) return "sccz80"; + return "z80asm"; +} + +/// MAME SUPPORT + +declare var FS, ENV, Module; // mame emscripten + +// TODO: make class +var BaseMAMEPlatform = function() { + + var self = this; + + 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 = {}; + } + + // http://docs.mamedev.org/techspecs/luaengine.html + this.luacall = function(s) { + 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 || ""); + } + + this.pause = function() { + if (loaded && running) { + this.luacall('emu.pause()'); + running = false; + } + } + + this.resume = function() { + if (loaded && !running) { // TODO + this.luacall('emu.unpause()'); + running = true; + } + } + + this.reset = function() { + this.luacall('manager:machine():soft_reset()'); + running = true; + initluavars = false; + } + + this.isRunning = function() { + return running; + } + + function 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); + } + } + } else { + console.log(s); + } + } + + this.startModule = function(mainElement, opts) { + romfn = opts.romfn; + if (opts.romdata) romdata = opts.romdata; + if (!romdata) romdata = new RAM(opts.romsize).mem; + // create canvas + video = new RasterVideo(mainElement, opts.width, opts.height); + video.create(); + $(video.canvas).attr('id','canvas'); + // load asm.js module + console.log("loading", opts.jsfile); + var modargs = [opts.driver, + '-debug', + '-debugger', 'none', + '-verbose', '-window', '-nokeepaspect', + '-resolution', video.canvas.width+'x'+video.canvas.height + ]; + if (romfn) modargs.push('-cart', romfn); + window['JSMESS'] = {}; + window['Module'] = { + arguments: modargs, + screenIsReadOnly: true, + print: bufferConsoleOutput, + canvas:video.canvas, + doNotCaptureKeyboard:true, + keyboardListeningElement:video.canvas, + preInit: function () { + console.log("loading FS"); + ENV.SDL_EMSCRIPTEN_KEYBOARD_ELEMENT = 'canvas'; + if (opts.cfgfile) { + FS.mkdir('/cfg'); + FS.writeFile('/cfg/' + opts.cfgfile, opts.cfgdata, {encoding:'utf8'}); + } + if (opts.biosfile) { + FS.mkdir('/roms'); + FS.mkdir('/roms/' + opts.driver); + FS.writeFile('/roms/' + opts.biosfile, opts.biosdata, {encoding:'binary'}); + } + FS.mkdir('/emulator'); + if (romfn) + FS.writeFile(romfn, romdata, {encoding:'binary'}); + //FS.writeFile('/debug.ini', 'debugger none\n', {encoding:'utf8'}); + if (opts.preInit) { + opts.preInit(self); + } + preinitted = true; + }, + preRun: [ + function() { + $(video.canvas).click(function(e) { + video.canvas.focus(); + }); + loaded = true; + console.log("about to run..."); + } + ] + }; + // preload files + // TODO: ensure loaded + var fetch_cfg, fetch_lua; + var fetch_bios = $.Deferred(); + var fetch_wasm = $.Deferred(); + // fetch config file + if (opts.cfgfile) { + fetch_cfg = $.get('mame/cfg/' + opts.cfgfile, function(data) { + opts.cfgdata = data; + console.log("loaded " + opts.cfgfile); + }, 'text'); + } + // fetch BIOS file + if (opts.biosfile) { + var oReq1 = new XMLHttpRequest(); + oReq1.open("GET", 'mame/roms/' + opts.biosfile, true); + oReq1.responseType = "arraybuffer"; + oReq1.onload = function(oEvent) { + opts.biosdata = new Uint8Array(oReq1.response); + console.log("loaded " + opts.biosfile); // + " (" + oEvent.total + " bytes)"); + fetch_bios.resolve(); + }; + oReq1.send(); + } else { + fetch_bios.resolve(); + } + // load debugger Lua script + fetch_lua = $.get('mame/debugger.lua', function(data) { + luadebugscript = data; + console.log("loaded debugger.lua"); + }, 'text'); + // load WASM + { + var oReq2 = new XMLHttpRequest(); + oReq2.open("GET", 'mame/' + opts.jsfile.replace('.js','.wasm'), true); + oReq2.responseType = "arraybuffer"; + oReq2.onload = function(oEvent) { + console.log("loaded WASM file"); + window['Module'].wasmBinary = new Uint8Array(oReq2.response); + fetch_wasm.resolve(); + }; + oReq2.send(); + } + // start loading script + $.when(fetch_lua, fetch_cfg, fetch_bios, fetch_wasm).done(function() { + var script = document.createElement('script'); + script.src = 'mame/' + opts.jsfile; + document.getElementsByTagName('head')[0].appendChild(script); + console.log("created script element"); + }); + } + + this.loadROMFile = function(data) { + romdata = data; + if (preinitted && romfn) { + FS.writeFile(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'); + 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]); + } + + // DEBUGGING SUPPORT + + var onBreakpointHit; + + this.clearDebug = function() { + onBreakpointHit = null; + } + this.getDebugCallback = function() { + return onBreakpointHit;// TODO? + } + this.setupDebug = function(callback) { + self.initlua(); + self.luareset(); + onBreakpointHit = callback; + } + this.runToPC = function(pc) { + self.luacall('mamedbg.runTo(' + pc + ')'); + self.resume(); + } + this.runToVsync = function() { + self.luacall('mamedbg.runToVsync()'); + self.resume(); + } + this.runUntilReturn = function() { + self.luacall('mamedbg.runUntilReturn()'); + self.resume(); + } + this.step = function() { + self.luacall('mamedbg.step()'); + self.resume(); + } + // TODO: other than z80 + this.cpuStateToLongString = function(c) { + if (c.HL) + return cpuStateToLongString_Z80(c); + else + return null; // TODO + } +} diff --git a/src/emu.ts b/src/emu.ts index 48a9d26a..ea893354 100644 --- a/src/emu.ts +++ b/src/emu.ts @@ -268,174 +268,7 @@ var AnimationTimer = function(frequencyHz:number, callback:() => void) { // -function cpuStateToLongString_6502(c) : string { - function decodeFlags(c) { - var s = ""; - s += c.N ? " N" : " -"; - s += c.V ? " V" : " -"; - s += c.D ? " D" : " -"; - s += c.Z ? " Z" : " -"; - s += c.C ? " C" : " -"; - // s += c.I ? " I" : " -"; - return s; - } - return "PC " + hex(c.PC,4) + " " + decodeFlags(c) + "\n" - + " A " + hex(c.A) + " " + (c.R ? "" : "BUSY") + "\n" - + " X " + hex(c.X) + "\n" - + " Y " + hex(c.Y) + " " + "SP " + hex(c.SP) + "\n"; -} - -////// 6502 - -var Base6502Platform = function() { - - this.newCPU = function(membus) { - var cpu = new jt.M6502(); - cpu.connectBus(membus); - return cpu; - } - - this.getOpcodeMetadata = function(opcode, offset) { - return Javatari.getOpcodeMetadata(opcode, offset); // TODO - } - - this.getOriginPC = function() { - return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff; - } - - var onBreakpointHit; - var debugCondition; - var debugSavedState = null; - var debugBreakState = null; - var debugTargetClock = 0; - var debugClock = 0; - - this.setDebugCondition = function(debugCond) { - if (debugSavedState) { - this.loadState(debugSavedState); - } else { - debugSavedState = this.saveState(); - } - debugClock = 0; - debugCondition = debugCond; - debugBreakState = null; - this.resume(); - } - this.restartDebugState = function() { - if (debugCondition && !debugBreakState) { - debugSavedState = this.saveState(); - debugTargetClock -= debugClock; - debugClock = 0; - } - } - this.getDebugCallback = function() { - return debugCondition; - } - this.setupDebug = function(callback) { - onBreakpointHit = callback; - } - this.clearDebug = function() { - debugSavedState = null; - debugBreakState = null; - debugTargetClock = 0; - debugClock = 0; - onBreakpointHit = null; - debugCondition = null; - } - this.breakpointHit = function(targetClock) { - debugTargetClock = targetClock; - debugBreakState = this.saveState(); - debugBreakState.c.PC = (debugBreakState.c.PC-1) & 0xffff; - console.log("Breakpoint at clk", debugClock, "PC", debugBreakState.c.PC.toString(16)); - this.pause(); - if (onBreakpointHit) { - onBreakpointHit(debugBreakState); - } - } - // TODO: lower bound of clock value - this.step = function() { - var self = this; - var previousPC = -1; - this.setDebugCondition(function() { - if (debugClock++ > debugTargetClock) { - var thisState = self.getCPUState(); - if (previousPC < 0) { - previousPC = thisState.PC; - } else { - if (thisState.PC != previousPC && thisState.T == 0) { - //console.log(previousPC.toString(16), thisPC.toString(16)); - self.breakpointHit(debugClock-1); - return true; - } - } - } - return false; - }); - } - this.stepBack = function() { - var self = this; - var prevState; - var prevClock; - this.setDebugCondition(function() { - if (debugClock++ >= debugTargetClock && prevState) { - self.loadState(prevState); - self.breakpointHit(prevClock-1); - return true; - } else if (debugClock > debugTargetClock-10 && debugClock < debugTargetClock) { - if (self.getCPUState().T == 0) { - prevState = self.saveState(); - prevClock = debugClock; - } - } - return false; - }); - } - this.runEval = function(evalfunc) { - var self = this; - this.setDebugCondition(function() { - if (debugClock++ > debugTargetClock) { - var cpuState = self.getCPUState(); - cpuState.PC = (cpuState.PC-1)&0xffff; - if (evalfunc(cpuState)) { - self.breakpointHit(debugClock-1); - return true; - } else { - return false; - } - } - }); - } - this.runUntilReturn = function() { - var depth = 1; - this.runEval(function(c) { - if (depth <= 0 && c.T == 0) - return true; - if (c.o == 0x20) - depth++; - else if (c.o == 0x60 || c.o == 0x40) - --depth; - return false; - }); - } - this.disassemble = function(pc, read) { - return disassemble6502(pc, read(pc), read(pc+1), read(pc+2)); - } - this.cpuStateToLongString = function(c) { - return cpuStateToLongString_6502(c); - } - this.getToolForFilename = getToolForFilename_6502; - this.getDefaultExtension = function() { return ".a"; }; -} - -function getToolForFilename_6502(fn) { - if (fn.endsWith(".pla")) return "plasm"; - if (fn.endsWith(".c")) return "cc65"; - if (fn.endsWith(".s")) return "ca65"; - if (fn.endsWith(".acme")) return "acme"; - return "dasm"; // .a -} - -function dumpRAM(ram, ramofs, ramlen) { +function dumpRAM(ram:number[], ramofs:number, ramlen:number) : string { var s = ""; // TODO: show scrollable RAM for other platforms for (var ofs=0; ofs>i)) ? flagspec.slice(i,i+1) : "-"; - return s; // TODO - } - return "PC " + hex(c.PC,4) + " " + decodeFlags(c.AF) + "\n" - + "SP " + hex(c.SP,4) + " IR " + hex(c.IR,4) + "\n" - + "IX " + hex(c.IX,4) + " IY " + hex(c.IY,4) + "\n" - + "AF " + hex(c.AF,4) + " BC " + hex(c.BC,4) + "\n" - + "DE " + hex(c.DE,4) + " HL " + hex(c.HL,4) + "\n" - ; -} - - -var BaseZ80Platform = function() { - - var _cpu; - var probe; - - this.newCPU = function(membus, iobus) { - probe = new BusProbe(membus); - _cpu = Z80_fast({ - display: {}, - memory: probe, - ioBus: iobus - }); - return _cpu; - } - - this.getProbe = function() { return probe; } - this.getPC = function() { return _cpu.getPC(); } - this.getSP = function() { return _cpu.getSP(); } - - // TODO: refactor other parts into here - this.runCPU = function(cpu, cycles) { - _cpu = cpu; // TODO? - if (this.wasBreakpointHit()) - return 0; - var debugCond = this.getDebugCallback(); - var targetTstates = cpu.getTstates() + cycles; - if (debugCond) { // || trace) { - while (cpu.getTstates() < targetTstates) { - //_trace(); // TODO - if (debugCond && debugCond()) { - debugCond = null; - break; - } - cpu.runFrame(cpu.getTstates() + 1); - } - } else { - cpu.runFrame(targetTstates); - } - return cpu.getTstates() - targetTstates; - } - - var onBreakpointHit; - var debugCondition; - var debugSavedState = null; - var debugBreakState = null; - var debugTargetClock = 0; - - this.setDebugCondition = function(debugCond) { - if (debugSavedState) { - this.loadState(debugSavedState); - } else { - debugSavedState = this.saveState(); - } - debugCondition = debugCond; - debugBreakState = null; - this.resume(); - } - this.restartDebugState = function() { - if (debugCondition && !debugBreakState) { - debugSavedState = this.saveState(); - if (debugTargetClock > 0) - debugTargetClock -= debugSavedState.c.T; - debugSavedState.c.T = 0; - this.loadState(debugSavedState); - } - } - this.getDebugCallback = function() { - return debugCondition; - } - this.setupDebug = function(callback) { - onBreakpointHit = callback; - } - this.clearDebug = function() { - debugSavedState = null; - debugBreakState = null; - debugTargetClock = 0; - onBreakpointHit = null; - debugCondition = null; - } - this.breakpointHit = function(targetClock) { - debugTargetClock = targetClock; - debugBreakState = this.saveState(); - //debugBreakState.c.PC = (debugBreakState.c.PC-1) & 0xffff; - console.log("Breakpoint at clk", debugBreakState.c.T, "PC", debugBreakState.c.PC.toString(16)); - this.pause(); - if (onBreakpointHit) { - onBreakpointHit(debugBreakState); - } - } - this.wasBreakpointHit = function() { - return debugBreakState != null; - } - // TODO: lower bound of clock value - this.step = function() { - var self = this; - this.setDebugCondition(function() { - var cpuState = self.getCPUState(); - if (cpuState.T > debugTargetClock) { - self.breakpointHit(cpuState.T); - return true; - } - return false; - }); - } - this.stepBack = function() { - var self = this; - var prevState; - var prevClock; - this.setDebugCondition(function() { - var cpuState = self.getCPUState(); - var debugClock = cpuState.T; - if (debugClock >= debugTargetClock && prevState) { - self.loadState(prevState); - self.breakpointHit(prevClock); - return true; - } else if (debugClock > debugTargetClock-20 && debugClock < debugTargetClock) { - prevState = self.saveState(); - prevClock = debugClock; - } - return false; - }); - } - this.runEval = function(evalfunc) { - var self = this; - this.setDebugCondition(function() { - var cpuState = self.getCPUState(); - if (cpuState.T > debugTargetClock) { - if (evalfunc(cpuState)) { - self.breakpointHit(cpuState.T); - return true; - } - } - return false; - }); - } - this.runUntilReturn = function() { - var self = this; - var depth = 1; - this.runEval(function(c) { - if (depth <= 0) - return true; - var op = self.readAddress(c.PC); - if (op == 0xcd) // CALL - depth++; - else if (op == 0xc0 || op == 0xc8 || op == 0xc9 || op == 0xd0) // RET (TODO?) - --depth; - return false; - }); - } - this.cpuStateToLongString = function(c) { - return cpuStateToLongString_Z80(c); - } - this.getToolForFilename = getToolForFilename_z80; - this.getDefaultExtension = function() { return ".c"; }; - // TODO - //this.getOpcodeMetadata = function() { } -} - -function getToolForFilename_z80(fn) { - if (fn.endsWith(".c")) return "sdcc"; - if (fn.endsWith(".s")) return "sdasz80"; - if (fn.endsWith(".ns")) return "naken"; - if (fn.endsWith(".scc")) return "sccz80"; - return "z80asm"; -} - -////// 6809 - -function cpuStateToLongString_6809(c) { - function decodeFlags(flags) { - var flagspec = "EFHINZVC"; - var s = ""; - for (var i=0; i<8; i++) - s += (flags & (128>>i)) ? flagspec.slice(i,i+1) : "-"; - return s; // TODO - } - return "PC " + hex(c.PC,4) + " " + decodeFlags(c.CC) + "\n" - + "SP " + hex(c.SP,4) + "\n" - + " A " + hex(c.A,2) + "\n" - + " B " + hex(c.B,2) + "\n" - + " X " + hex(c.X,4) + "\n" - + " Y " + hex(c.Y,4) + "\n" - + " U " + hex(c.U,4) + "\n" - ; -} - -var Base6809Platform = function() { - this.__proto__ = new BaseZ80Platform(); - - this.newCPU = function(membus) { - var cpu = new CPU6809(); - cpu.init(membus.write, membus.read, 0); - return cpu; - } - - this.runUntilReturn = function() { - var self = this; - var depth = 1; - self.runEval(function(c) { - if (depth <= 0) - return true; - var op = self.readAddress(c.PC); - // TODO: 6809 opcodes - if (op == 0x9d || op == 0xad || op == 0xbd) // CALL - depth++; - else if (op == 0x3b || op == 0x39) // RET - --depth; - return false; - }); - } - this.cpuStateToLongString = function(c) { - return cpuStateToLongString_6809(c); - } - this.getToolForFilename = function(fn) { - return "xasm6809"; - } - this.disassemble = function(pc, read) { - // TODO: don't create new CPU - return new CPU6809().disasm(read(pc), read(pc+1), read(pc+2), read(pc+3), read(pc+4), pc); - } - this.getDefaultExtension = function() { return ".asm"; }; - //this.getOpcodeMetadata = function() { } -} - -////////// - var Keys = { VK_ESCAPE: {c: 27, n: "Esc"}, VK_F1: {c: 112, n: "F1"}, @@ -869,305 +457,3 @@ function AddressDecoder(table, options) { return makeFunction(0x0, 0xffff).bind(self); } -var BusProbe = function(bus) { - var active = false; - var callback; - this.activate = function(_callback) { - active = true; - callback = _callback; - } - this.deactivate = function() { - active = false; - callback = null; - } - this.read = function(a) { - if (active) { - callback(a); - } - return bus.read(a); - } - this.write = function(a,v) { - if (active) { - callback(a,v); - } - bus.write(a,v); - } -} - -/// MAME SUPPORT - -declare var FS, ENV, Module; // mame emscripten - -var BaseMAMEPlatform = function() { - - var self = this; - - 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 = {}; - } - - // http://docs.mamedev.org/techspecs/luaengine.html - this.luacall = function(s) { - 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 || ""); - } - - this.pause = function() { - if (loaded && running) { - this.luacall('emu.pause()'); - running = false; - } - } - - this.resume = function() { - if (loaded && !running) { // TODO - this.luacall('emu.unpause()'); - running = true; - } - } - - this.reset = function() { - this.luacall('manager:machine():soft_reset()'); - running = true; - initluavars = false; - } - - this.isRunning = function() { - return running; - } - - function 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); - } - } - } else { - console.log(s); - } - } - - this.startModule = function(mainElement, opts) { - romfn = opts.romfn; - if (opts.romdata) romdata = opts.romdata; - if (!romdata) romdata = new RAM(opts.romsize).mem; - // create canvas - video = new RasterVideo(mainElement, opts.width, opts.height); - video.create(); - $(video.canvas).attr('id','canvas'); - // load asm.js module - console.log("loading", opts.jsfile); - var modargs = [opts.driver, - '-debug', - '-debugger', 'none', - '-verbose', '-window', '-nokeepaspect', - '-resolution', video.canvas.width+'x'+video.canvas.height - ]; - if (romfn) modargs.push('-cart', romfn); - window['JSMESS'] = {}; - window['Module'] = { - arguments: modargs, - screenIsReadOnly: true, - print: bufferConsoleOutput, - canvas:video.canvas, - doNotCaptureKeyboard:true, - keyboardListeningElement:video.canvas, - preInit: function () { - console.log("loading FS"); - ENV.SDL_EMSCRIPTEN_KEYBOARD_ELEMENT = 'canvas'; - if (opts.cfgfile) { - FS.mkdir('/cfg'); - FS.writeFile('/cfg/' + opts.cfgfile, opts.cfgdata, {encoding:'utf8'}); - } - if (opts.biosfile) { - FS.mkdir('/roms'); - FS.mkdir('/roms/' + opts.driver); - FS.writeFile('/roms/' + opts.biosfile, opts.biosdata, {encoding:'binary'}); - } - FS.mkdir('/emulator'); - if (romfn) - FS.writeFile(romfn, romdata, {encoding:'binary'}); - //FS.writeFile('/debug.ini', 'debugger none\n', {encoding:'utf8'}); - if (opts.preInit) { - opts.preInit(self); - } - preinitted = true; - }, - preRun: [ - function() { - $(video.canvas).click(function(e) { - video.canvas.focus(); - }); - loaded = true; - console.log("about to run..."); - } - ] - }; - // preload files - // TODO: ensure loaded - var fetch_cfg, fetch_lua; - var fetch_bios = $.Deferred(); - var fetch_wasm = $.Deferred(); - // fetch config file - if (opts.cfgfile) { - fetch_cfg = $.get('mame/cfg/' + opts.cfgfile, function(data) { - opts.cfgdata = data; - console.log("loaded " + opts.cfgfile); - }, 'text'); - } - // fetch BIOS file - if (opts.biosfile) { - var oReq1 = new XMLHttpRequest(); - oReq1.open("GET", 'mame/roms/' + opts.biosfile, true); - oReq1.responseType = "arraybuffer"; - oReq1.onload = function(oEvent) { - opts.biosdata = new Uint8Array(oReq1.response); - console.log("loaded " + opts.biosfile); // + " (" + oEvent.total + " bytes)"); - fetch_bios.resolve(); - }; - oReq1.send(); - } else { - fetch_bios.resolve(); - } - // load debugger Lua script - fetch_lua = $.get('mame/debugger.lua', function(data) { - luadebugscript = data; - console.log("loaded debugger.lua"); - }, 'text'); - // load WASM - { - var oReq2 = new XMLHttpRequest(); - oReq2.open("GET", 'mame/' + opts.jsfile.replace('.js','.wasm'), true); - oReq2.responseType = "arraybuffer"; - oReq2.onload = function(oEvent) { - console.log("loaded WASM file"); - window['Module'].wasmBinary = new Uint8Array(oReq2.response); - fetch_wasm.resolve(); - }; - oReq2.send(); - } - // start loading script - $.when(fetch_lua, fetch_cfg, fetch_bios, fetch_wasm).done(function() { - var script = document.createElement('script'); - script.src = 'mame/' + opts.jsfile; - document.getElementsByTagName('head')[0].appendChild(script); - console.log("created script element"); - }); - } - - this.loadROMFile = function(data) { - romdata = data; - if (preinitted && romfn) { - FS.writeFile(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'); - 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]); - } - - // DEBUGGING SUPPORT - - var onBreakpointHit; - - this.clearDebug = function() { - onBreakpointHit = null; - } - this.getDebugCallback = function() { - return onBreakpointHit; - } - this.setupDebug = function(callback) { - self.initlua(); - self.luareset(); - onBreakpointHit = callback; - } - this.runToPC = function(pc) { - self.luacall('mamedbg.runTo(' + pc + ')'); - self.resume(); - } - this.runToVsync = function() { - self.luacall('mamedbg.runToVsync()'); - self.resume(); - } - this.runUntilReturn = function() { - self.luacall('mamedbg.runUntilReturn()'); - self.resume(); - } - this.step = function() { - self.luacall('mamedbg.step()'); - self.resume(); - } - // TODO: other than z80 - this.cpuStateToLongString = function(c) { - if (c.HL) - return cpuStateToLongString_Z80(c); - else - return null; // TODO - } - -} diff --git a/src/ui.ts b/src/ui.ts index d36fbb6e..a0c9eaa1 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -537,7 +537,7 @@ function resetAndDebug() { } } -var lastBreakExpr = "c.PC = 0x6000"; +var lastBreakExpr = "c.PC == 0x6000"; function _breakExpression() { var exprs = window.prompt("Enter break expression", lastBreakExpr); if (exprs) { diff --git a/viz.html b/viz.html deleted file mode 100644 index 011dd466..00000000 --- a/viz.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - 8bitworkshop ~ Z80 Compiler Visualization - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
-
-
-
-
-
-
- -
- - - - - - - - - - - - - - - - -