From 604a5a1a7414ace20a1b4ab32d523cbf8919b98c Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 21 Apr 2017 21:56:49 -0400 Subject: [PATCH] added symbols to memory window; BusProbe/newCPU/readAddress everywhere --- index.html | 4 + src/emu.js | 17 ++- src/platform/galaxian.js | 7 +- src/platform/mw8080bw.js | 12 +-- src/platform/sound_konami.js | 10 +- src/platform/sound_williams.js | 10 +- src/platform/vector.js | 13 +-- src/platform/vicdual.js | 24 +---- src/platform/williams.js | 14 +-- src/ui.js | 187 ++++++++++++++++++++++++++++----- src/worker/workermain.js | 17 +-- 11 files changed, 210 insertions(+), 105 deletions(-) diff --git a/index.html b/index.html index e33b65ba..182b6245 100644 --- a/index.html +++ b/index.html @@ -99,6 +99,10 @@ div.mem_info { font-family: "Andale Mono", "Menlo", "Lucida Console", monospace; font-size: 12pt; } +.seg_code { color: #ff9966; } +.seg_data { color: #66ff66; } +.seg_stack { color: #ffff66; } +.seg_unknown { color: #cccccc; } span.hilite { color: #ff66ff; } diff --git a/src/emu.js b/src/emu.js index 0522cbfc..4d02f2f6 100644 --- a/src/emu.js +++ b/src/emu.js @@ -441,21 +441,32 @@ function cpuStateToLongString_Z80(c) { var BaseZ80Platform = function() { + var _cpu; + var probe; + window.buildZ80({ applyContention: false // TODO??? }); - // TODO: refactor w/ platforms this.newCPU = function(membus, iobus) { - return window.Z80({ + probe = new BusProbe(membus); + _cpu = window.Z80({ display: {}, - memory: membus, + 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) { diff --git a/src/platform/galaxian.js b/src/platform/galaxian.js index cf98249b..9c826d93 100644 --- a/src/platform/galaxian.js +++ b/src/platform/galaxian.js @@ -264,8 +264,8 @@ var GalaxianPlatform = function(mainElement, options) { isContended: function() { return false; }, }; } - this.readMemory = function(a) { - return (a == 0x7000 || a == 0x7800) ? null : membus.read; // ignore watchdog + this.readAddress = function(a) { + return (a == 0x7000 || a == 0x7800) ? null : membus.read(a); // ignore watchdog }; audio = new MasterAudio(); psg1 = new AY38910_Audio(audio); @@ -387,9 +387,6 @@ var GalaxianPlatform = function(mainElement, options) { if (!this.getDebugCallback()) cpu.setTstates(0); // TODO? watchdog_counter = INITIAL_WATCHDOG; } - this.readAddress = function(addr) { - return membus.read(addr); // TODO? - } } var GalaxianScramblePlatform = function(mainElement) { diff --git a/src/platform/mw8080bw.js b/src/platform/mw8080bw.js index 3d6d1844..aa24eed7 100644 --- a/src/platform/mw8080bw.js +++ b/src/platform/mw8080bw.js @@ -13,6 +13,7 @@ var Midway8080BWPlatform = function(mainElement) { this.__proto__ = new BaseZ80Platform(); var cpu, ram, membus, iobus, rom; + var probe; var video, timer, pixels, displayPCs; var inputs = [0xe,0x8,0x0]; var bitshift_offset = 0; @@ -60,7 +61,7 @@ var Midway8080BWPlatform = function(mainElement) { ]), isContended: function() { return false; }, }; - this.readMemory = membus.read; + this.readAddress = membus.read; iobus = { read: function(addr) { addr &= 0x3; @@ -96,11 +97,7 @@ var Midway8080BWPlatform = function(mainElement) { } } }; - cpu = window.Z80({ - display: {}, - memory: membus, - ioBus: iobus - }); + cpu = this.newCPU(membus, iobus); video = new RasterVideo(mainElement,256,224,{rotate:-90}); video.create(); $(video.canvas).click(function(e) { @@ -191,9 +188,6 @@ var Midway8080BWPlatform = function(mainElement) { cpu.setTstates(0); watchdog_counter = INITIAL_WATCHDOG; } - this.readAddress = function(addr) { - return membus.read(addr); - } } PLATFORMS['mw8080bw'] = Midway8080BWPlatform; diff --git a/src/platform/sound_konami.js b/src/platform/sound_konami.js index c88d109d..00d063f5 100644 --- a/src/platform/sound_konami.js +++ b/src/platform/sound_konami.js @@ -56,11 +56,8 @@ var KonamiSoundPlatform = function(mainElement) { } } }; - cpu = window.Z80({ - display: {}, - memory: membus, - ioBus: iobus - }); + this.readAddress = membus.read; + cpu = this.newCPU(membus, iobus); psg = new PsgDeviceChannel(); master = new MasterChannel(); psg.setMode(PsgDeviceChannel.MODE_SIGNED); @@ -136,9 +133,6 @@ var KonamiSoundPlatform = function(mainElement) { cpu.reset(); if (!this.getDebugCallback()) cpu.setTstates(0); // TODO? } - this.readAddress = function(addr) { - return membus.read(addr); // TODO? - } } PLATFORMS['sound_konami'] = KonamiSoundPlatform; diff --git a/src/platform/sound_williams.js b/src/platform/sound_williams.js index 2f8d6fa6..60a9ae02 100644 --- a/src/platform/sound_williams.js +++ b/src/platform/sound_williams.js @@ -79,11 +79,8 @@ var WilliamsSoundPlatform = function(mainElement) { fillBuffer(); } }; - cpu = window.Z80({ - display: {}, - memory: membus, - ioBus: iobus - }); + this.readAddress = membus.read; + cpu = this.newCPU(membus, iobus); audio = new SampleAudio(cpuFrequency / cpuAudioFactor); audio.callback = function(lbuf) { if (self.isRunning()) { @@ -151,9 +148,6 @@ var WilliamsSoundPlatform = function(mainElement) { cpu.reset(); if (!this.getDebugCallback()) cpu.setTstates(0); // TODO? } - this.readAddress = function(addr) { - return membus.read(addr); // TODO? - } } PLATFORMS['sound_williams-z80'] = WilliamsSoundPlatform; diff --git a/src/platform/vector.js b/src/platform/vector.js index c134a557..577e92b6 100644 --- a/src/platform/vector.js +++ b/src/platform/vector.js @@ -90,7 +90,7 @@ var AtariVectorPlatform = function(mainElement) { ], {gmask:0x7fff}) }; - this.readMemory = membus.read; + this.readAddress = bus.read; cpu = self.newCPU(bus); // create video/audio video = new VectorVideo(mainElement,1024,1024); @@ -147,9 +147,6 @@ var AtariVectorPlatform = function(mainElement) { this.clearDebug(); cpu.reset(); } - this.readAddress = function(addr) { - return bus.read(addr); - } this.loadState = function(state) { cpu.loadState(state.c); @@ -240,6 +237,7 @@ var AtariColorVectorPlatform = function(mainElement) { ]) }; + this.readAddress = bus.read; cpu = self.newCPU(bus); // create video/audio video = new VectorVideo(mainElement,1024,1024); @@ -291,9 +289,6 @@ var AtariColorVectorPlatform = function(mainElement) { this.clearDebug(); cpu.reset(); } - this.readAddress = function(addr) { - return bus.read(addr); - } this.loadState = function(state) { cpu.loadState(state.c); @@ -376,6 +371,7 @@ var Z80ColorVectorPlatform = function(mainElement, proto) { ]) }; + this.readAddress = bus.read; cpu = self.newCPU(bus); // create video/audio video = new VectorVideo(mainElement,1024,1024); @@ -420,9 +416,6 @@ var Z80ColorVectorPlatform = function(mainElement, proto) { switches[0xe] = 16; cpu.reset(); } - this.readAddress = function(addr) { - return bus.read(addr); - } this.loadState = function(state) { cpu.loadState(state.c); diff --git a/src/platform/vicdual.js b/src/platform/vicdual.js index f360ffae..ebb9c9fe 100644 --- a/src/platform/vicdual.js +++ b/src/platform/vicdual.js @@ -15,7 +15,7 @@ var VicDualPlatform = function(mainElement) { this.__proto__ = new BaseZ80Platform(); var cpu, ram, membus, iobus, rom; - var video, audio, psg, timer, pixels, probe; + var video, audio, psg, timer, pixels; var inputs = [0xff, 0xff, 0xff, 0xff^0x8]; // most things active low var palbank = 0; @@ -108,7 +108,7 @@ var VicDualPlatform = function(mainElement) { ]), isContended: function() { return false; }, }; - this.readMemory = membus.read; + this.readAddress = membus.read; iobus = { read: function(addr) { return inputs[addr&3]; @@ -120,12 +120,7 @@ var VicDualPlatform = function(mainElement) { if (addr & 0x40) { palbank = val & 3; }; // palette } }; - probe = new BusProbe(membus); - cpu = window.Z80({ - display: {}, - memory: probe, - ioBus: iobus - }); + cpu = this.newCPU(membus, iobus); video = new RasterVideo(mainElement,256,224,{rotate:-90}); audio = new MasterAudio(); psg = new AY38910_Audio(audio); @@ -154,14 +149,7 @@ var VicDualPlatform = function(mainElement) { targetTstates += cpuCyclesPerLine; if (sl == vblankStart) inputs[1] |= 0x8; if (sl == vsyncEnd) inputs[1] &= ~0x8; - if (debugCond) { - while (cpu.getTstates() < targetTstates) { - if (debugCond && debugCond()) { debugCond = null; } - cpu.runFrame(cpu.getTstates() + 1); - } - } else { - cpu.runFrame(targetTstates); - } + self.runCPU(cpu, targetTstates - cpu.getTstates()); } video.updateFrame(); self.restartDebugState(); @@ -216,16 +204,12 @@ var VicDualPlatform = function(mainElement) { psg.reset(); if (!this.getDebugCallback()) cpu.setTstates(0); // TODO? } - this.readAddress = function(addr) { - return membus.read(addr & 0xffff); // TODO? - } this.setFrameStats = function(on) { framestats = on ? { palette: palette, layers: {width:256, height:224, tiles:[]} } : null; } - this.getProbe = function() { return probe; } } PLATFORMS['vicdual'] = VicDualPlatform; diff --git a/src/platform/williams.js b/src/platform/williams.js index dd4b4f8f..e4cc9f25 100644 --- a/src/platform/williams.js +++ b/src/platform/williams.js @@ -273,13 +273,12 @@ var WilliamsPlatform = function(mainElement, proto) { nvram = new RAM(0x400); // TODO: save in browser storage? //displayPCs = new Uint16Array(new ArrayBuffer(0x9800*2)); - rom = padBytes(new lzgmini().decode(ROBOTRON_ROM).slice(0), 0xc001); + //rom = padBytes(new lzgmini().decode(ROBOTRON_ROM).slice(0), 0xc001); membus = { read: memread_williams, write: memwrite_williams, }; - this.readMemory = membus.read; - //var probebus = new BusProbe(membus); + this.readAddress = membus.read; var iobus = { read: function(a) {return 0;}, write: function(a,v) {console.log(hex(a),hex(v));} @@ -317,13 +316,11 @@ var WilliamsPlatform = function(mainElement, proto) { cpu.requestInterrupt(); } } - if (!self.wasBreakpointHit()) - self.runCPU(cpu, cpuCyclesPerSection); + self.runCPU(cpu, cpuCyclesPerSection); if (sl < 256) video.updateFrame(0, 0, 256-4-sl, 0, 4, 304); } // last 6 lines - if (!self.wasBreakpointHit()) - self.runCPU(cpu, cpuCyclesPerSection*2); + self.runCPU(cpu, cpuCyclesPerSection*2); if (screenNeedsRefresh) { for (var i=0; i<0x9800; i++) drawDisplayByte(i, ram.mem[i]); @@ -400,9 +397,6 @@ var WilliamsPlatform = function(mainElement, proto) { watchdog_counter = INITIAL_WATCHDOG; banksel = 1; } - this.readAddress = function(addr) { - return membus.read(addr); - } this.scaleCPUFrequency = function(scale) { cpuScale = scale; cpuFrequency *= scale; diff --git a/src/ui.js b/src/ui.js index a352aaed..4edc755f 100644 --- a/src/ui.js +++ b/src/ui.js @@ -110,11 +110,14 @@ var TOOL_TO_SOURCE_STYLE = { } var worker = new Worker("./src/worker/workermain.js"); -var current_output = null; -var current_preset_index = -1; // TODO: use URL -var current_preset_id = null; -var assemblyfile = null; -var sourcefile = null; +var current_output; +var current_preset_index = -1; +var current_preset_id; +var assemblyfile; +var sourcefile; +var symbolmap; +var addr2symbol; +var compparams; var trace_pending_at_pc; var store; var pendingWorkerMessages = 0; @@ -367,11 +370,23 @@ function arrayCompare(a,b) { return true; } +function invertMap(m) { + var r = {}; + if (m) { + for (var k in m) r[m[k]] = k; + } + return r; +} + function setCompileOutput(data) { sourcefile = new SourceFile(data.lines); if (data.asmlines) { assemblyfile = new SourceFile(data.asmlines, data.intermediate.listing); } + symbolmap = data.symbolmap; + addr2symbol = invertMap(symbolmap); + addr2symbol[0x10000] = '__END__'; + compparams = data.params; // errors? function addErrorMarker(line, msg) { var div = document.createElement("div"); @@ -406,8 +421,7 @@ function setCompileOutput(data) { platform.loadROM(getCurrentPresetTitle(), rom); resume(); current_output = rom; - prof_reads = []; - prof_writes = []; + resetProfiler(); toolbar.removeClass("has-errors"); } catch (e) { console.log(e); // TODO: show error @@ -711,6 +725,16 @@ function _breakExpression() { } } +function getSymbolAtAddress(a) { + if (addr2symbol[a]) return addr2symbol[a]; + var i=0; + while (--a >= 0) { + i++; + if (addr2symbol[a]) return addr2symbol[a] + '+' + i; + } + return ''; +} + function updateDebugWindows() { if (platform.isRunning()) { updateMemoryWindow(); @@ -719,14 +743,6 @@ function updateDebugWindows() { setTimeout(updateDebugWindows, 200); } -function getProfileLine(line) { - var offset = sourcefile.line2offset[line]; - if (offset >= 0) { - if (prof_reads[offset] > 0) - return ""+prof_reads[offset]; - } -} - function updateProfileWindow() { if (profileview && sourcefile) { $("#profileview").find('[data-index]').each(function(i,e) { @@ -746,22 +762,41 @@ function updateMemoryWindow() { if (memoryview) { $("#memoryview").find('[data-index]').each(function(i,e) { var div = $(e); - var offset = div.attr('data-index') * 16; + var row = div.attr('data-index'); var oldtext = div.text(); - var newtext = getMemoryLineAtOffset(offset); + var newtext = getMemoryLineAt(row); if (oldtext != newtext) div.text(newtext); }); } } -function getMemoryLineAtOffset(offset) { +function getMemoryLineAt(row) { + var offset = row * 16; + var n1 = 0; + var n2 = 16; + var sym; + if (getDumpLines()) { + var dl = dumplines[row]; + if (dl) { + offset = dl.a & 0xfff0; + n1 = dl.a - offset; + n2 = n1 + dl.l; + sym = dl.s; + } else { + return '.'; + } + } var s = hex(offset,4) + ' '; - for (var i=0; i<16; i++) { - var read = platform.readMemory(offset+i); + for (var i=0; i 8) s += ' '; + for (var i=n1; i=0?hex(read,2):'??'); } + for (var i=n2; i<16; i++) s += ' '; + if (sym) s += ' ' + sym; return s; } @@ -769,6 +804,56 @@ function getEditorLineHeight() { return $("#editor").find(".CodeMirror-line").first().height(); } +function getDumpLineAt(line) { + var d = dumplines[line]; + if (d) { + return d.a + " " + d.s; + } +} + +var IGNORE_SYMS = {s__INITIALIZER:true, /* s__GSINIT:true, */ _color_prom:true}; + +function getDumpLines() { + if (!dumplines && addr2symbol) { + dumplines = []; + var ofs = 0; + var sym; + for (var nextofs in addr2symbol) { + nextofs |= 0; + var nextsym = addr2symbol[nextofs]; + if (sym) { + if (IGNORE_SYMS[sym]) { + ofs = nextofs; + } else { + while (ofs < nextofs) { + var ofs2 = (ofs + 16) & 0xffff0; + if (ofs2 > nextofs) ofs2 = nextofs; + //if (ofs < 1000) console.log(ofs, ofs2, nextofs, sym); + dumplines.push({a:ofs, l:ofs2-ofs, s:sym}); + ofs = ofs2; + } + } + } + sym = nextsym; + } + } + return dumplines; +} + +function getMemorySegment(a) { + if (!compparams) return 'unknown'; + if (a >= compparams.data_start && a < compparams.data_start+compparams.data_size) { + if (platform.getSP && a >= platform.getSP() - 15) + return 'stack'; + else + return 'data'; + } + else if (a >= compparams.code_start && a < compparams.code_start+compparams.code_size) + return 'code'; + else + return 'unknown'; +} + function showMemoryWindow() { memoryview = new VirtualList({ w:$("#emulator").width(), @@ -776,15 +861,19 @@ function showMemoryWindow() { itemHeight: getEditorLineHeight(), totalRows: 0x1000, generatorFn: function(row) { - var s = getMemoryLineAtOffset(row * 16); + var s = getMemoryLineAt(row); var div = document.createElement("div"); + if (dumplines) { + var dlr = dumplines[row]; + if (dlr) div.classList.add('seg_' + getMemorySegment(dumplines[row].a)); + } div.appendChild(document.createTextNode(s)); return div; } }); $("#memoryview").empty().append(memoryview.container); updateMemoryWindow(); - memoryview.scrollToItem(0x800); // TODO + memoryview.scrollToItem(0); // TODO } function toggleMemoryWindow() { @@ -816,13 +905,59 @@ function createProfileWindow() { updateProfileWindow(); } +var pcdata = {}; var prof_reads, prof_writes; +var dumplines; + +function resetProfiler() { + prof_reads = []; + prof_writes = []; + pcdata = []; + dumplines = null; +} function profileWindowCallback(a,v) { - if (v >= 0) { - prof_writes[a] = (prof_writes[a]|0)+1; + if (platform.getPC) { + var pc = platform.getPC(); + var pcd = pcdata[pc]; + if (!pcd) { + pcd = pcdata[pc] = {nv:1}; + } + pcd.nv++; + if (a != pc) { + if (v >= 0) { + pcd.lastwa = a; + pcd.lastwv = v; + } else { + pcd.lastra = a; + pcd.lastrv = platform.readAddress(a); + } + } } else { - prof_reads[a] = (prof_reads[a]|0)+1; + // TODO + if (v >= 0) { + prof_writes[a] = (prof_writes[a]|0)+1; + } else { + prof_reads[a] = (prof_reads[a]|0)+1; + } + } +} + +function getProfileLine(line) { + var offset = sourcefile.line2offset[line]; + if (offset >= 0) { + var pcd = pcdata[offset]; + if (pcd) { + var s = pcd.nv+""; + while (s.length < 8) { s = ' '+s; } + if (pcd.lastra >= 0) { + s += " read [" + hex(pcd.lastra,4) + "] == " + hex(pcd.lastrv,2); + } + if (pcd.lastwa >= 0) { + s += " write " + hex(pcd.lastwv,2) + " -> [" + hex(pcd.lastwa,4) + "]"; + } + return s; + } } } @@ -965,8 +1100,8 @@ function startPlatform() { showWelcomeMessage(); // start platform and load file preloadWorker(qs['file']); - setupDebugControls(); platform.start(); + setupDebugControls(); loadPreset(qs['file']); updateSelector(); return true; diff --git a/src/worker/workermain.js b/src/worker/workermain.js index 0253d3c9..842690a8 100644 --- a/src/worker/workermain.js +++ b/src/worker/workermain.js @@ -6,42 +6,49 @@ var PLATFORM_PARAMS = { code_size: 0x2000, data_start: 0x2000, data_size: 0x400, + stack_end: 0x2400, }, 'vicdual': { code_start: 0x0, code_size: 0x4020, data_start: 0xe400, data_size: 0x400, + stack_end: 0xe800, }, 'galaxian': { code_start: 0x0, code_size: 0x4000, data_start: 0x4000, data_size: 0x400, + stack_end: 0x4800, }, 'galaxian-scramble': { code_start: 0x0, code_size: 0x5020, data_start: 0x4000, data_size: 0x400, + stack_end: 0x4800, }, 'williams-z80': { code_start: 0x0, code_size: 0x9800, data_start: 0x9800, data_size: 0x2800, + stack_end: 0xc000, }, 'vector-z80color': { code_start: 0x0, code_size: 0x8000, data_start: 0xe000, data_size: 0x2000, + stack_end: 0x0, }, 'sound_williams-z80': { code_start: 0x0, code_size: 0x4000, data_start: 0x4000, data_size: 0x400, + stack_end: 0x8000, }, }; @@ -685,22 +692,19 @@ function assemblelinkSDASZ80(code, platform) { var asmlines = parseListing(lstout, /^\s*([0-9A-F]+)\s+([0-9A-F][0-9A-F r]*[0-9A-F])\s+\[([0-9 ]+)\]\s+(\d+) (.*)/i, 4, 1, 2, 5, 3); var srclines = parseSourceLines(lstout, /^\s+\d+ ;:(\d+):/i, /^\s*([0-9A-F]{4})/i); // parse symbol map - /* var symbolmap = {}; - console.log(mapout); for (var s of mapout.split("\n")) { var toks = s.split(" "); - if (s[0] == 'DEF') { - symbolmap[s[1]] = s[2]; + if (toks[0] == 'DEF' && !toks[1].startsWith("A$main$")) { + symbolmap[toks[1]] = parseInt(toks[2], 16); } } - */ return { output:parseIHX(hexout, params.code_start, params.code_size), lines:asmlines, srclines:srclines, errors:msvc_errors, // TODO? - symbolmap:mapout, + symbolmap:symbolmap, intermediate:{listing:rstout}, }; } @@ -897,6 +901,7 @@ onmessage = function(e) { var toolfn = TOOLS[e.data.tool]; if (!toolfn) throw "no tool named " + e.data.tool; var result = toolfn(code, platform); + result.params = PLATFORM_PARAMS[platform]; if (result) { postMessage(result); }