added symbols to memory window; BusProbe/newCPU/readAddress everywhere

This commit is contained in:
Steven Hugg 2017-04-21 21:56:49 -04:00
parent 340f23b568
commit 604a5a1a74
11 changed files with 210 additions and 105 deletions

View File

@ -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;
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

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, 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;

View File

@ -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;

187
src/ui.js
View File

@ -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<n1; i++) s += ' ';
if (n1 > 8) s += ' ';
for (var i=n1; i<n2; i++) {
var read = platform.readAddress(offset+i);
if (i==8) s += ' ';
s += ' ' + (read>=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;

View File

@ -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+ ;<stdin>:(\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);
}