diff --git a/src/baseplatform.ts b/src/baseplatform.ts index 193a09e7..4a478340 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -50,7 +50,7 @@ export class DebugSymbols { this.symbolmap = symbolmap; this.addr2symbol = invertMap(symbolmap); //// TODO: shouldn't be necc. - if (!this.addr2symbol[0x0]) this.addr2symbol[0x0] = '__START__'; // needed for ... + if (!this.addr2symbol[0x0]) this.addr2symbol[0x0] = ' '; // needed for ... this.addr2symbol[0x10000] = '__END__'; // ... dump memory to work } } @@ -1089,7 +1089,6 @@ export abstract class Base6502MachinePlatform extends BaseMac default: return isDebuggable(this.machine) && this.machine.getDebugInfo(category, state); } } - } export abstract class BaseZ80MachinePlatform extends BaseMachinePlatform { diff --git a/src/devices.ts b/src/devices.ts index 64fdb3f7..c8ac3b63 100644 --- a/src/devices.ts +++ b/src/devices.ts @@ -351,6 +351,7 @@ export abstract class BasicMachine implements HasCPU, Bus, SampledAudioSource, A connectCPUIOBus(iobus:Bus) : void { this.cpu['connectIOBus'](this.probeIOBus(iobus)); } + // TODO: probe interrupts } export abstract class BasicScanlineMachine extends BasicMachine implements RasterFrameBased { diff --git a/src/platform/nes.ts b/src/platform/nes.ts index 59c6ccbc..b89ea33b 100644 --- a/src/platform/nes.ts +++ b/src/platform/nes.ts @@ -144,23 +144,6 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable { this.probe.logClocks(cycles); return cycles; } - var ppu = this.nes.ppu; - var old_endScanline = ppu.endScanline.bind(ppu); - var old_startFrame = ppu.startFrame.bind(ppu); - var old_writeMem = ppu.writeMem.bind(ppu); - ppu.endScanline = () => { - old_endScanline(); - this.probe.logNewScanline(); - } - ppu.startFrame = () => { - old_startFrame(); - this.probe.logNewFrame(); - } - ppu.writeMem = (a,v) => { - old_writeMem(a,v); - this.probe.logVRAMWrite(a,v); - } - this.timer = new AnimationTimer(60, this.nextFrame.bind(this)); // set keyboard map this.poller = setKeyboardFromMap(this.video, [], JSNES_KEYCODE_MAP, (o,key,code,flags) => { @@ -221,33 +204,55 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable { var romstr = byteArrayToString(data); this.nes.loadROM(romstr); this.frameindex = 0; + this.installIntercepts(); + } + installIntercepts() { // intercept bus calls, unless we did it already - if (!this.nes.mmap.haveProxied) { - var oldload = this.nes.mmap.load.bind(this.nes.mmap); - var oldwrite = this.nes.mmap.write.bind(this.nes.mmap); - //var oldregLoad = this.nes.mmap.regLoad.bind(this.nes.mmap); - //var oldregWrite = this.nes.mmap.regWrite.bind(this.nes.mmap); - this.nes.mmap.load = (addr) => { + var mmap = this.nes.mmap; + if (!mmap.haveProxied) { + var oldload = mmap.load.bind(mmap); + var oldwrite = mmap.write.bind(mmap); + //var oldregLoad = mmap.regLoad.bind(mmap); + //var oldregWrite = mmap.regWrite.bind(mmap); + mmap.load = (addr) => { var val = oldload(addr); this.probe.logRead(addr, val); return val; } - this.nes.mmap.write = (addr, val) => { + mmap.write = (addr, val) => { this.probe.logWrite(addr, val); oldwrite(addr, val); } /* - this.nes.mmap.regLoad = (addr) => { + mmap.regLoad = (addr) => { var val = oldregLoad(addr); this.probe.logIORead(addr, val); return val; } - this.nes.mmap.regWrite = (addr, val) => { + mmap.regWrite = (addr, val) => { this.probe.logIOWrite(addr, val); oldregWrite(addr, val); } */ - this.nes.mmap.haveProxied = true; + mmap.haveProxied = true; + } + var ppu = this.nes.ppu; + if (!ppu.haveProxied) { + var old_endScanline = ppu.endScanline.bind(ppu); + var old_startFrame = ppu.startFrame.bind(ppu); + var old_writeMem = ppu.writeMem.bind(ppu); + ppu.endScanline = () => { + old_endScanline(); + this.probe.logNewScanline(); + } + ppu.startFrame = () => { + old_startFrame(); + this.probe.logNewFrame(); + } + ppu.writeMem = (a,v) => { + old_writeMem(a,v); + this.probe.logVRAMWrite(a,v); + } } } newCodeAnalyzer() { @@ -261,6 +266,7 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable { reset() { //this.nes.cpu.reset(); // doesn't work right, crashes this.nes.cpu.requestIrq(this.nes.cpu.IRQ_RESET); + this.installIntercepts(); } isRunning() { return this.timer.isRunning(); @@ -312,6 +318,7 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable { this.nes.ppu.spriteMem = state.ppu.spriteMem.slice(0); this.loadControlsState(state.ctrl); //$.extend(this.nes, state); + this.installIntercepts(); } saveControlsState() { return { @@ -324,10 +331,10 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable { this.nes.controllers[2].state = state.c2; } readAddress(addr) { - return this.nes.cpu.mem[addr] & 0xff; + return this.nes.cpu.mem[addr]; } readVRAMAddress(addr : number) : number { - return this.nes.ppu.vramMem[addr & 0x7fff] & 0xff; + return this.nes.ppu.vramMem[addr]; } copy6502REGvars(c) { c.T = 0; diff --git a/src/recorder.ts b/src/recorder.ts index 6d505540..1d1bae25 100644 --- a/src/recorder.ts +++ b/src/recorder.ts @@ -215,7 +215,6 @@ export class ProbeRecorder implements ProbeAll { } start() { this.m.connectProbe(this); - this.reset(); } stop() { this.m.connectProbe(null); diff --git a/src/ui.ts b/src/ui.ts index ea84a5ee..3480e563 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -250,14 +250,11 @@ function refreshWindowList() { }); } if (platform.startProbing) { - addWindowItem("#eventprobe", "Event Probe", () => { - return new Views.EventProbeView(); - }); - addWindowItem("#memheatmap", "Memory Heatmap", () => { + addWindowItem("#memheatmap", "Memory Probe", () => { return new Views.AddressHeatMapView(); }); // TODO: only if raster - addWindowItem("#crtheatmap", "Screen Heatmap", () => { + addWindowItem("#crtheatmap", "CRT Probe", () => { return new Views.RasterPCHeatMapView(); }); } diff --git a/src/views.ts b/src/views.ts index f22320a7..cf948b58 100644 --- a/src/views.ts +++ b/src/views.ts @@ -1,5 +1,4 @@ -import $ = require("jquery"); //import CodeMirror = require("codemirror"); import { SourceFile, WorkerError, Segment, FileData } from "./workertypes"; import { Platform, EmuState, ProfilerOutput, lookupSymbol, BaseDebugPlatform } from "./baseplatform"; @@ -536,6 +535,7 @@ export class MemoryView implements ProjectView { maindiv : HTMLElement; static IGNORE_SYMS = {s__INITIALIZER:true, /* s__GSINIT:true, */ _color_prom:true}; recreateOnResize = true; + totalRows = 0x1400; createDiv(parent : HTMLElement) { var div = document.createElement('div'); @@ -550,7 +550,7 @@ export class MemoryView implements ProjectView { w: $(workspace).width(), h: $(workspace).height(), itemHeight: getVisibleEditorLineHeight(), - totalRows: 0x1400, + totalRows: this.totalRows, generatorFn: (row : number) => { var s = this.getMemoryLineAt(row); var linediv = document.createElement("div"); @@ -695,6 +695,7 @@ export class MemoryView implements ProjectView { } export class VRAMMemoryView extends MemoryView { + totalRows = 0x800; readAddress(n : number) { return platform.readVRAMAddress(n); } @@ -959,6 +960,15 @@ abstract class ProbeViewBase { return this.maindiv = div; } + addr2str(addr : number) : string { + var _addr2sym = (platform.debugSymbols && platform.debugSymbols.addr2symbol) || {}; + var sym = _addr2sym[addr]; + if (typeof sym === 'string') + return '$' + hex(addr) + ' (' + sym + ')'; + else + return '$' + hex(addr); + } + initCanvas() { } @@ -982,6 +992,7 @@ abstract class ProbeViewBase { setVisible(showing : boolean) : void { if (showing) { this.probe = platform.startProbing(); + this.tick(); } else { platform.stopProbing(); this.probe = null; @@ -989,14 +1000,6 @@ abstract class ProbeViewBase { } clear() { - var ctx = this.ctx; - //ctx.globalCompositeOperation = 'source-over'; - //ctx.globalAlpha = 1.0; - //ctx.fillStyle = '#000000'; - //ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); - ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - ctx.globalAlpha = 1.0; - ctx.globalCompositeOperation = 'lighter'; } redraw( eventfn:(op,addr,col,row) => void ) { @@ -1020,23 +1023,8 @@ abstract class ProbeViewBase { } tick() { - var ctx = this.ctx; this.clear(); this.redraw(this.drawEvent.bind(this)); - if (this.probe && !this.probe.singleFrame) this.probe.reset(); - } - - setContextForOp(op) { - var ctx = this.ctx; - switch (op) { - //case ProbeFlags.EXECUTE: ctx.fillStyle = "green"; break; - //case ProbeFlags.MEM_READ: ctx.fillStyle = "#7f7f7f"; break; - case ProbeFlags.MEM_WRITE: ctx.fillStyle = "red"; break; - case ProbeFlags.IO_READ: ctx.fillStyle = "green"; break; - case ProbeFlags.IO_WRITE: ctx.fillStyle = "magenta"; break; - case ProbeFlags.INTERRUPT: ctx.fillStyle = "yellow"; break; - default: ctx.fillStyle = "blue"; break; - } } abstract drawEvent(op, addr, col, row); @@ -1067,13 +1055,13 @@ abstract class ProbeBitmapViewBase extends ProbeViewBase { y = y|0; var s = ""; this.redraw( (op,addr,col,row) => { - if (col == x && row == y) { + if (y == row && x == col) { s += "\n" + this.opToString(op, addr); } } ); - return '(' + x + ',' + y + ')' + s; - } - opToString(op:number, addr:number) { + return 'X: ' + x + ' Y: ' + y + ' ' + s; + } + opToString(op:number, addr?:number) { var s = ""; switch (op) { case ProbeFlags.EXECUTE: s = "Exec"; break; @@ -1086,7 +1074,7 @@ abstract class ProbeBitmapViewBase extends ProbeViewBase { case ProbeFlags.INTERRUPT: s = "Interrupt"; break; default: s = ""; break; } - return s + " $" + hex(addr); + return typeof addr == 'number' ? s + " " + this.addr2str(addr) : s; } refresh() { @@ -1107,9 +1095,9 @@ abstract class ProbeBitmapViewBase extends ProbeViewBase { case ProbeFlags.MEM_WRITE: return 0x010180; case ProbeFlags.IO_READ: return 0x018080; case ProbeFlags.IO_WRITE: return 0xc00180; - case ProbeFlags.VRAM_READ: return 0x018080; - case ProbeFlags.VRAM_WRITE: return 0xc00180; - case ProbeFlags.INTERRUPT: return 0xc0c001; + case ProbeFlags.VRAM_READ: return 0x808001; + case ProbeFlags.VRAM_WRITE: return 0x4080c0; + case ProbeFlags.INTERRUPT: return 0xcfcfcf; default: return 0; } } @@ -1141,9 +1129,22 @@ export class AddressHeatMapView extends ProbeBitmapViewBase implements ProjectVi } getTooltipText(x:number, y:number) : string { - var addr = (x & 0xff) + (y << 8); - return '$'+hex(addr); - } + var a = (x & 0xff) + (y << 8); + var s = this.addr2str(a); + var pc = -1; + var already = {}; + this.redraw( (op,addr,col,row) => { + if (op == ProbeFlags.EXECUTE) { + pc = addr; + } + var key = op|pc; + if (addr == a && !already[key]) { + s += "\nPC " + this.addr2str(pc) + " " + this.opToString(op); + already[key] = 1; + } + } ); + return s; + } } /* @@ -1175,68 +1176,6 @@ export class RasterPCHeatMapView extends ProbeBitmapViewBase implements ProjectV } -export class EventProbeView extends ProbeViewBase implements ProjectView { - symcache : Map = new Map(); - xmax : number = 1; - ymax : number = 1; - lastsym : string; - xx : number; - yy : number; - - createDiv(parent : HTMLElement) { - return this.createCanvas( parent, $(parent).width(), $(parent).height() ); - } - - drawEvent(op, addr, col, row) { - var ctx = this.ctx; - this.xmax = Math.max(this.xmax, col); - this.ymax = Math.max(this.ymax, row); - var xscale = this.canvas.width / this.xmax; // TODO: pixels - var yscale = (this.canvas.height - 12) / this.ymax; // TODO: lines - var x = col * xscale; - var y = row * yscale; - x = this.xx; - y = this.yy = Math.max(this.yy, y); - var sym = this.getSymbol(addr); - if (!sym && op == ProbeFlags.IO_WRITE) sym = hex(addr,4); - //TODO if (!sym && op == ProbeFlags.IO_READ) sym = hex(addr,4); - if (sym && sym != this.lastsym) { - this.setContextForOp(op); - ctx.textAlign = 'left'; //ctx.textAlign = (x < this.canvas.width/2) ? 'left' : 'right'; - ctx.textBaseline = 'top'; //ctx.textBaseline = (y < this.canvas.height/2) ? 'top' : 'bottom'; - var mt = ctx.measureText(sym); - if (x + mt.width > this.canvas.width) { - x = 0; - y += 12; - } - ctx.fillText(sym, x, y); - this.xx = x + mt.width + 10; - this.yy = y; - this.lastsym = sym; - } - } - - getSymbol(addr:number) : string { - var sym = this.symcache[addr]; - if (!sym) { - sym = lookupSymbol(platform, addr, false); - this.symcache[addr] = sym; - } - return sym; - } - - tick() { - this.xx = this.yy = 0; - this.lastsym = ''; - super.tick(); - } - - refresh() { - this.tick(); - this.symcache.clear(); - } -} - /// export class AssetEditorView implements ProjectView, pixed.EditorContext { diff --git a/src/worker/workermain.ts b/src/worker/workermain.ts index 45d45489..e94d5537 100644 --- a/src/worker/workermain.ts +++ b/src/worker/workermain.ts @@ -974,6 +974,7 @@ function linkLD65(step:BuildStep) { } // build segment map var seg_re = /^__(\w+)_SIZE__$/; + // TODO: move to Platform class var segments = []; segments.push({name:'CPU Stack',start:0x100,size:0x100,type:'ram'}); segments.push({name:'CPU Vectors',start:0xfffa,size:0x6,type:'rom'});