started on profile/memory views

This commit is contained in:
Steven Hugg 2017-04-18 21:18:53 -04:00
parent 8a8638b295
commit 89c3209f09
14 changed files with 380 additions and 32 deletions

View File

@ -54,3 +54,9 @@ machines.
SWEET16 - Woz's tiny bytecode interpreter on the Apple ][ integer BASIC ROM.
Still emcumbered by Apple's copyright for the foreseeable future.
http://6502.org/source/interpreters/sweet16.htm
https://github.com/EtchedPixels/FUZIX/wiki
gcc6809 - need to check this out

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -75,6 +75,18 @@ div.emulator {
background-color: #666;
margin-top: 20px auto 0;
}
div.debugwindow {
position:absolute;
left:50%;
top:0;
width:50%;
background-color: #666;
color: #66ff66;
white-space: pre;
margin-top: 20px auto 0;
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
font-size: 10pt;
}
div.mem_info {
position: fixed;
left: 51%;
@ -83,7 +95,7 @@ div.mem_info {
color: #66ff66;
white-space: pre;
padding: 20px;
z-index: 2;
z-index: 12;
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
font-size: 12pt;
}
@ -226,7 +238,7 @@ div.bitmap_editor {
</span>
<select id="preset_select" name="">
</select>
<img id="compile_spinner" src="spinner.gif" height="20em" style="visibility:hidden;margin-left:8px;margin-right:8px">
<img id="compile_spinner" src="images/spinner.gif" height="20em" style="visibility:hidden;margin-left:8px;margin-right:8px">
<span class="debug_bar" id="debug_bar">
<button id="dbg_reset" type="submit" title="Reset and Break"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
<button id="dbg_pause" type="button" title="Pause"><span class="glyphicon glyphicon-pause" aria-hidden="true"></span></button>
@ -235,9 +247,13 @@ div.bitmap_editor {
<button id="dbg_toline" type="submit" title="Run To Line"><span class="glyphicon glyphicon-save" aria-hidden="true"></span></button>
<button id="dbg_stepout" type="submit" title="Step Out of Subroutine"><span class="glyphicon glyphicon-hand-up" aria-hidden="true"></span></button>
<button id="dbg_stepback" type="submit" title="Step Backwards"><span class="glyphicon glyphicon-step-backward" aria-hidden="true"></span></button>
<button id="dbg_timing" type="submit" title="See Timing" style="display:none"><span class="glyphicon glyphicon-time" aria-hidden="true"></span></button>
</span>
<button id="dbg_disasm" type="submit" title="Show Disassembly" style="display:none"><span class="glyphicon glyphicon-list" aria-hidden="true"></span></button>
<span class="extra_bar" id="extra_bar">
<button id="dbg_timing" type="submit" title="See Timing" style="display:none"><span class="glyphicon glyphicon-time" aria-hidden="true"></span></button>
<button id="dbg_disasm" type="submit" title="Show Disassembly" style="display:none"><span class="glyphicon glyphicon-list" aria-hidden="true"></span></button>
<button id="dbg_memory" type="submit" title="Show Memory" style="display:none"><span class="glyphicon glyphicon-sunglasses" aria-hidden="true"></span></button>
<button id="dbg_profile" type="submit" title="Show Profile" style="display:none"><span class="glyphicon glyphicon-stats" aria-hidden="true"></span></button>
</span>
</div>
<div id="notebook">
<div id="workspace">
@ -249,8 +265,12 @@ div.bitmap_editor {
<div class="emulator" id="emulator">
<div id="javatari-screen" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
<div id="javatari-console-panel" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
<div id="mem_info" class="mem_info" style="display:none">
</div>
</div>
<div id="mem_info" class="mem_info" style="display:none">
</div>
<div id="profileview" class="debugwindow" style="display:none;z-index:10">
</div>
<div id="memoryview" class="debugwindow" style="display:none;z-index:10">
</div>
</div>
<div class="twitbtn">
@ -314,6 +334,7 @@ Dest: $<span id="bitmap_editor_dest">0000/0</span>
<script src="FileSaver.js/FileSaver.min.js"></script>
<script src="octokat.js/dist/octokat.js"></script>
<script src="src/vlist.js"></script>
<script src="src/emu.js"></script>
<script src="src/audio.js"></script>
<script src="src/util.js"></script>

View File

@ -825,11 +825,26 @@ function AddressDecoder(table, options) {
}
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) {
var val = bus.read(a);
return val;
if (active) {
callback(a);
}
return bus.read(a);
}
this.write = function(a,v) {
return bus.write(a,v);
if (active) {
callback(a,v);
}
bus.write(a,v);
}
}

View File

@ -264,6 +264,9 @@ var GalaxianPlatform = function(mainElement, options) {
isContended: function() { return false; },
};
}
this.readMemory = function(a) {
return (a == 0x7000 || a == 0x7800) ? null : membus.read; // ignore watchdog
};
audio = new MasterAudio();
psg1 = new AY38910_Audio(audio);
psg2 = new AY38910_Audio(audio);
@ -368,7 +371,7 @@ var GalaxianPlatform = function(mainElement, options) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();

View File

@ -60,6 +60,7 @@ var Midway8080BWPlatform = function(mainElement) {
]),
isContended: function() { return false; },
};
this.readMemory = membus.read;
iobus = {
read: function(addr) {
addr &= 0x3;
@ -177,7 +178,7 @@ var Midway8080BWPlatform = function(mainElement) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();

View File

@ -122,7 +122,7 @@ var KonamiSoundPlatform = function(mainElement) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();

View File

@ -137,7 +137,7 @@ var WilliamsSoundPlatform = function(mainElement) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();

View File

@ -65,7 +65,7 @@ var VCSPlatform = function() {
return {x:xpos, y:ypos};
}
this.isRunning = function() { return Javatari.room.console.isRunning(); }
this.isRunning = function() { return Javatari.room && Javatari.room.console.isRunning(); }
this.pause = function() { Javatari.room.console.pause(); }
this.resume = function() { Javatari.room.console.go(); }
this.step = function() { Javatari.room.console.debugSingleStepCPUClock(); }

View File

@ -89,6 +89,7 @@ var AtariVectorPlatform = function(mainElement) {
], {gmask:0x7fff})
};
this.readMemory = membus.read;
cpu = self.newCPU(bus);
// create video/audio
video = new VectorVideo(mainElement,1024,1024);
@ -131,7 +132,7 @@ var AtariVectorPlatform = function(mainElement) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();
@ -275,7 +276,7 @@ var AtariColorVectorPlatform = function(mainElement) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();
@ -401,7 +402,7 @@ var Z80ColorVectorPlatform = function(mainElement, proto) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();

View File

@ -15,7 +15,7 @@ var VicDualPlatform = function(mainElement) {
this.__proto__ = new BaseZ80Platform();
var cpu, ram, membus, iobus, rom;
var video, audio, psg, timer, pixels;
var video, audio, psg, timer, pixels, probe;
var inputs = [0xff, 0xff, 0xff, 0xff^0x8]; // most things active low
var palbank = 0;
@ -108,6 +108,7 @@ var VicDualPlatform = function(mainElement) {
]),
isContended: function() { return false; },
};
this.readMemory = membus.read;
iobus = {
read: function(addr) {
return inputs[addr&3];
@ -119,9 +120,10 @@ var VicDualPlatform = function(mainElement) {
if (addr & 0x40) { palbank = val & 3; }; // palette
}
};
probe = new BusProbe(membus);
cpu = window.Z80({
display: {},
memory: membus,
memory: probe,
ioBus: iobus
});
video = new RasterVideo(mainElement,256,224,{rotate:-90});
@ -199,7 +201,7 @@ var VicDualPlatform = function(mainElement) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();
@ -223,6 +225,7 @@ var VicDualPlatform = function(mainElement) {
layers: {width:256, height:224, tiles:[]}
} : null;
}
this.getProbe = function() { return probe; }
}
PLATFORMS['vicdual'] = VicDualPlatform;

View File

@ -278,6 +278,7 @@ var WilliamsPlatform = function(mainElement, proto) {
read: memread_williams,
write: memwrite_williams,
};
this.readMemory = membus.read;
//var probebus = new BusProbe(membus);
var iobus = {
read: function(a) {return 0;},
@ -384,7 +385,7 @@ var WilliamsPlatform = function(mainElement, proto) {
}
this.isRunning = function() {
return timer.isRunning();
return timer && timer.isRunning();
}
this.pause = function() {
timer.stop();

158
src/ui.js
View File

@ -115,12 +115,10 @@ var current_preset_index = -1; // TODO: use URL
var current_preset_id = null;
var assemblyfile = null;
var sourcefile = null;
var pcvisits;
var trace_pending_at_pc;
var store;
var pendingWorkerMessages = 0;
var editor;
var disasmview = CodeMirror(document.getElementById('disassembly'), {
mode: 'z80',
theme: 'cobalt',
@ -129,6 +127,9 @@ var disasmview = CodeMirror(document.getElementById('disassembly'), {
styleActiveLine: true
});
var memoryview;
var profileview;
function newEditor(mode) {
var isAsm = (mode != 'text/x-csrc');
editor = CodeMirror(document.getElementById('editor'), {
@ -147,6 +148,11 @@ function newEditor(mode) {
setCode(editor.getValue());
}, 200);
});
editor.on('scroll', function(ed, changeobj) {
if (profileview) {
profileview.container.scrollTop = editor.getScrollInfo().top;
}
});
editor.setOption("mode", mode);
}
@ -400,7 +406,8 @@ function setCompileOutput(data) {
platform.loadROM(getCurrentPresetTitle(), rom);
resume();
current_output = rom;
pcvisits = {};
prof_reads = [];
prof_writes = [];
toolbar.removeClass("has-errors");
} catch (e) {
console.log(e); // TODO: show error
@ -447,7 +454,6 @@ function setCompileOutput(data) {
worker.onmessage = function(e) {
toolbar.removeClass("is-busy");
$('#compile_spinner').css('visibility', 'hidden');
// TODO: this doesn't completely work yet
if (pendingWorkerMessages > 1) {
pendingWorkerMessages = 0;
setCode(editor.getValue());
@ -523,7 +529,6 @@ function setupBreakpoint() {
console.log("BREAKPOINT", hex(PC));
// TODO: switch to disasm
}
pcvisits[PC] = pcvisits[PC] ? pcvisits[PC]+1 : 1;
showMemory(state);
updateDisassembly();
});
@ -872,6 +877,136 @@ function _breakExpression() {
}
}
function updateDebugWindows() {
if (platform.isRunning()) {
updateMemoryWindow();
updateProfileWindow();
}
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) {
var div = $(e);
var lineno = div.attr('data-index') | 0;
var newtext = getProfileLine(lineno+1);
if (newtext) {
var oldtext = div.text();
if (oldtext != newtext)
div.text(newtext);
}
});
}
}
function updateMemoryWindow() {
if (memoryview) {
$("#memoryview").find('[data-index]').each(function(i,e) {
var div = $(e);
var offset = div.attr('data-index') * 16;
var oldtext = div.text();
var newtext = getMemoryLineAtOffset(offset);
if (oldtext != newtext)
div.text(newtext);
});
}
}
function getMemoryLineAtOffset(offset) {
var s = hex(offset,4) + ' ';
for (var i=0; i<16; i++) {
var read = platform.readMemory(offset+i);
if (i==8) s += ' ';
s += ' ' + (read>=0?hex(read,2):'??');
}
return s;
}
function getEditorLineHeight() {
return $("#editor").find(".CodeMirror-line").first().height();
}
function showMemoryWindow() {
memoryview = new VirtualList({
w:$("#emulator").width(),
h:$("#emulator").height(),
itemHeight: getEditorLineHeight(),
totalRows: 0x1000,
generatorFn: function(row) {
var s = getMemoryLineAtOffset(row * 16);
var div = document.createElement("div");
div.appendChild(document.createTextNode(s));
return div;
}
});
$("#memoryview").empty().append(memoryview.container);
updateMemoryWindow();
memoryview.scrollToItem(0x800); // TODO
}
function toggleMemoryWindow() {
if ($("#profileview").is(':visible')) toggleProfileWindow();
if ($("#memoryview").is(':visible')) {
memoryview = null;
$("#emulator").show();
$("#memoryview").hide();
} else {
showMemoryWindow();
$("#emulator").hide();
$("#memoryview").show();
}
}
function createProfileWindow() {
profileview = new VirtualList({
w:$("#emulator").width(),
h:$("#emulator").height(),
itemHeight: getEditorLineHeight(),
totalRows: editor.lineCount(),
generatorFn: function(row) {
var div = document.createElement("div");
div.appendChild(document.createTextNode("."));
return div;
}
});
$("#profileview").empty().append(profileview.container);
updateProfileWindow();
}
var prof_reads, prof_writes;
function profileWindowCallback(a,v) {
if (v >= 0) {
prof_writes[a] = (prof_writes[a]|0)+1;
} else {
prof_reads[a] = (prof_reads[a]|0)+1;
}
}
function toggleProfileWindow() {
if ($("#memoryview").is(':visible')) toggleMemoryWindow();
if ($("#profileview").is(':visible')) {
profileview = null;
platform.getProbe().deactivate();
$("#emulator").show();
$("#profileview").hide();
} else {
createProfileWindow();
platform.getProbe().activate(profileWindowCallback);
$("#emulator").hide();
$("#profileview").show();
}
}
function setupDebugControls(){
$("#dbg_reset").click(resetAndDebug);
$("#dbg_pause").click(pause);
@ -883,6 +1018,12 @@ function setupDebugControls(){
if (platform_id == 'vcs') {
$("#dbg_timing").click(traceTiming).show();
}
else if (platform.readAddress) {
$("#dbg_memory").click(toggleMemoryWindow).show();
}
if (platform.getProbe) {
$("#dbg_profile").click(toggleProfileWindow).show();
}
if (platform.saveState) { // TODO: only show if listing or disasm available
$("#dbg_disasm").click(toggleDisassembly).show();
}
@ -893,6 +1034,7 @@ function setupDebugControls(){
$("#item_reset_file").click(_resetPreset);
$("#item_debug_expr").click(_breakExpression);
$("#item_download_rom").click(_downloadROMImage);
updateDebugWindows();
}
function showWelcomeMessage() {
@ -938,11 +1080,6 @@ function showWelcomeMessage() {
///////////////////////////////////////////////////
function setupBitmapEditor() {
}
///////////////////////////////////////////////////
var qs = (function (a) {
if (!a || a == "")
return {};
@ -975,7 +1112,6 @@ function startPlatform() {
// start platform and load file
preloadWorker(qs['file']);
setupDebugControls();
setupBitmapEditor();
platform.start();
loadPreset(qs['file']);
updateSelector();

161
src/vlist.js Normal file
View File

@ -0,0 +1,161 @@
/**
* The MIT License (MIT)
*
* Copyright (C) 2013 Sergi Mansilla
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the 'Software'), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
'use strict';
/**
* Creates a virtually-rendered scrollable list.
* @param {object} config
* @constructor
*/
function VirtualList(config) {
var width = (config && config.w + 'px') || '100%';
var height = (config && config.h + 'px') || '100%';
var itemHeight = this.itemHeight = config.itemHeight;
this.items = config.items;
this.generatorFn = config.generatorFn;
this.totalRows = config.totalRows || (config.items && config.items.length);
var scroller = VirtualList.createScroller(itemHeight * this.totalRows);
this.container = VirtualList.createContainer(width, height);
this.container.appendChild(scroller);
var screenItemsLen = Math.ceil(config.h / itemHeight);
// Cache 4 times the number of items that fit in the container viewport
this.cachedItemsLen = screenItemsLen * 3;
this._renderChunk(this.container, 0);
var self = this;
var lastRepaintY;
var maxBuffer = screenItemsLen * itemHeight;
var lastScrolled = 0;
// As soon as scrolling has stopped, this interval asynchronouslyremoves all
// the nodes that are not used anymore
this.rmNodeInterval = setInterval(function() {
if (Date.now() - lastScrolled > 100) {
var badNodes = document.querySelectorAll('[data-rm="1"]');
for (var i = 0, l = badNodes.length; i < l; i++) {
try {
self.container.removeChild(badNodes[i]);
} catch (e) {
//
}
}
}
}, 300);
function onScroll(e) {
var scrollTop = e.target.scrollTop; // Triggers reflow
if (!lastRepaintY || Math.abs(scrollTop - lastRepaintY) > maxBuffer) {
var first = parseInt(scrollTop / itemHeight) - screenItemsLen;
self._renderChunk(self.container, first < 0 ? 0 : first);
lastRepaintY = scrollTop;
}
lastScrolled = Date.now();
e.preventDefault && e.preventDefault();
}
this.container.addEventListener('scroll', onScroll);
}
VirtualList.prototype.createRow = function(i) {
var item;
if (this.generatorFn)
item = this.generatorFn(i);
else if (this.items) {
if (typeof this.items[i] === 'string') {
var itemText = document.createTextNode(this.items[i]);
item = document.createElement('div');
item.style.height = this.itemHeight + 'px';
item.appendChild(itemText);
} else {
item = this.items[i];
}
}
item.classList.add('vrow');
item.setAttribute('data-index', ''+i);
item.style.position = 'absolute';
item.style.top = (i * this.itemHeight) + 'px';
return item;
};
/**
* Renders a particular, consecutive chunk of the total rows in the list. To
* keep acceleration while scrolling, we mark the nodes that are candidate for
* deletion instead of deleting them right away, which would suddenly stop the
* acceleration. We delete them once scrolling has finished.
*
* @param {Node} node Parent node where we want to append the children chunk.
* @param {Number} from Starting position, i.e. first children index.
* @return {void}
*/
VirtualList.prototype._renderChunk = function(node, from) {
var finalItem = from + this.cachedItemsLen;
if (finalItem > this.totalRows)
finalItem = this.totalRows;
// Append all the new rows in a document fragment that we will later append to
// the parent node
var fragment = document.createDocumentFragment();
for (var i = from; i < finalItem; i++) {
fragment.appendChild(this.createRow(i));
}
// Hide and mark obsolete nodes for deletion.
for (var j = 1, l = node.childNodes.length; j < l; j++) {
node.childNodes[j].style.display = 'none';
node.childNodes[j].setAttribute('data-rm', '1');
}
node.appendChild(fragment);
};
VirtualList.createContainer = function(w, h) {
var c = document.createElement('div');
c.style.width = w;
c.style.height = h;
c.style.overflow = 'auto';
c.style.position = 'relative';
c.style.padding = 0;
c.style.border = '1px solid black';
return c;
};
VirtualList.createScroller = function(h) {
var scroller = document.createElement('div');
scroller.style.opacity = 0;
scroller.style.position = 'absolute';
scroller.style.top = 0;
scroller.style.left = 0;
scroller.style.width = '1px';
scroller.style.height = h + 'px';
return scroller;
};
VirtualList.prototype.scrollToItem = function(index) {
this.container.scrollTop = this.itemHeight * index;
};