mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-29 14:51:17 +00:00
fixed callbackGetRemote(); started on profiler
This commit is contained in:
parent
ce019b5632
commit
ab1500ccb6
@ -98,6 +98,9 @@ export interface Platform {
|
|||||||
showHelp?(tool:string, ident?:string) : void;
|
showHelp?(tool:string, ident?:string) : void;
|
||||||
resize?() : void;
|
resize?() : void;
|
||||||
|
|
||||||
|
startProfiling?() : ProfilerOutput;
|
||||||
|
getRasterScanline?() : number;
|
||||||
|
|
||||||
debugSymbols? : DebugSymbols;
|
debugSymbols? : DebugSymbols;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +125,17 @@ export interface EmuRecorder {
|
|||||||
recordFrame(state : EmuState);
|
recordFrame(state : EmuState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProfilerScanline {
|
||||||
|
start,end : number; // start/end frameindex
|
||||||
|
}
|
||||||
|
export interface ProfilerFrame {
|
||||||
|
iptab : Uint32Array; // array of IPs
|
||||||
|
lines : ProfilerScanline[];
|
||||||
|
}
|
||||||
|
export interface ProfilerOutput {
|
||||||
|
frame : ProfilerFrame;
|
||||||
|
}
|
||||||
|
|
||||||
/////
|
/////
|
||||||
|
|
||||||
export abstract class BasePlatform {
|
export abstract class BasePlatform {
|
||||||
@ -194,10 +208,31 @@ export abstract class BaseDebugPlatform extends BasePlatform {
|
|||||||
this.advance(novideo);
|
this.advance(novideo);
|
||||||
this.postFrame();
|
this.postFrame();
|
||||||
}
|
}
|
||||||
|
startProfiling() : ProfilerOutput {
|
||||||
|
var frame = null;
|
||||||
|
var output = {frame:null};
|
||||||
|
var i = 0;
|
||||||
|
var lastsl = 9999;
|
||||||
|
var start = 0;
|
||||||
|
(this as any).runEval((c:CpuState) => {
|
||||||
|
var sl = (this as any).getRasterScanline();
|
||||||
|
if (sl != lastsl) {
|
||||||
|
if (frame) frame.lines.push({start:start,end:i});
|
||||||
|
if (sl < lastsl) {
|
||||||
|
output.frame = frame;
|
||||||
|
frame = {iptab:new Uint32Array(14672), lines:[]};
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
start = i+1;
|
||||||
|
lastsl = sl;
|
||||||
|
}
|
||||||
|
frame.iptab[i++] = c.EPC || c.PC;
|
||||||
|
return false; // profile forever
|
||||||
|
});
|
||||||
|
return output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////
|
|
||||||
|
|
||||||
////// 6502
|
////// 6502
|
||||||
|
|
||||||
export function getToolForFilename_6502(fn:string) : string {
|
export function getToolForFilename_6502(fn:string) : string {
|
||||||
@ -933,7 +968,7 @@ export function dumpStackToString(platform:Platform, mem:Uint8Array|number[], st
|
|||||||
var opcode = read(addr + jsrofs); // might be out of bounds
|
var opcode = read(addr + jsrofs); // might be out of bounds
|
||||||
if (opcode == jsrop) { // JSR
|
if (opcode == jsrop) { // JSR
|
||||||
s += "\n$" + hex(sp) + ": ";
|
s += "\n$" + hex(sp) + ": ";
|
||||||
s += hex(addr,4) + " " + lookupSymbol(platform, addr);
|
s += hex(addr,4) + " " + lookupSymbol(platform, addr, true);
|
||||||
sp++;
|
sp++;
|
||||||
nraw = 0;
|
nraw = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -946,20 +981,18 @@ export function dumpStackToString(platform:Platform, mem:Uint8Array|number[], st
|
|||||||
return s+"\n";
|
return s+"\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function lookupSymbol(platform:Platform, addr:number) {
|
export function lookupSymbol(platform:Platform, addr:number, extra:boolean) {
|
||||||
var start = addr;
|
var start = addr;
|
||||||
var foundsym;
|
|
||||||
var addr2symbol = platform.debugSymbols && platform.debugSymbols.addr2symbol;
|
var addr2symbol = platform.debugSymbols && platform.debugSymbols.addr2symbol;
|
||||||
while (addr2symbol && addr >= 0) {
|
while (addr2symbol && addr >= 0) {
|
||||||
var sym = addr2symbol[addr];
|
var sym = addr2symbol[addr];
|
||||||
if (sym && sym.startsWith('_')) { // return first C symbol we find
|
if (sym) { // return first symbol we find
|
||||||
return addr2symbol[addr] + " + " + (start-addr);
|
var sym = addr2symbol[addr];
|
||||||
} else if (sym && !foundsym) { // cache first non-C symbol found
|
return extra ? (sym + " + " + (start-addr)) : sym;
|
||||||
foundsym = sym;
|
|
||||||
}
|
}
|
||||||
addr--;
|
addr--;
|
||||||
}
|
}
|
||||||
return foundsym || "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Basic Platforms
|
///// Basic Platforms
|
||||||
@ -1074,4 +1107,3 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform {
|
|||||||
this.cpu.setTstates(0);
|
this.cpu.setTstates(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, cpuStateToLongString_6502, getToolForFilename_6502, dumpStackToString } from "../baseplatform";
|
import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, cpuStateToLongString_6502, getToolForFilename_6502, dumpStackToString, ProfilerOutput } from "../baseplatform";
|
||||||
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM, KeyFlags } from "../emu";
|
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM, KeyFlags } from "../emu";
|
||||||
import { hex, lpad, lzgmini } from "../util";
|
import { hex, lpad, lzgmini } from "../util";
|
||||||
import { CodeAnalyzer_nes } from "../analysis";
|
import { CodeAnalyzer_nes } from "../analysis";
|
||||||
@ -131,9 +131,7 @@ const _JSNESPlatform = function(mainElement) {
|
|||||||
nes.cpu._emulate = nes.cpu.emulate;
|
nes.cpu._emulate = nes.cpu.emulate;
|
||||||
nes.cpu.emulate = () => {
|
nes.cpu.emulate = () => {
|
||||||
var cycles = nes.cpu._emulate();
|
var cycles = nes.cpu._emulate();
|
||||||
//if (self.debugCondition && !self.debugBreakState && self.debugClock < 100) console.log(self.debugClock, nes.cpu.REG_PC);
|
|
||||||
this.evalDebugCondition();
|
this.evalDebugCondition();
|
||||||
// TODO: doesn't stop on breakpoint
|
|
||||||
return cycles;
|
return cycles;
|
||||||
}
|
}
|
||||||
timer = new AnimationTimer(60, this.nextFrame.bind(this));
|
timer = new AnimationTimer(60, this.nextFrame.bind(this));
|
||||||
@ -227,7 +225,35 @@ const _JSNESPlatform = function(mainElement) {
|
|||||||
|
|
||||||
runToVsync() {
|
runToVsync() {
|
||||||
var frame0 = frameindex;
|
var frame0 = frameindex;
|
||||||
this.runEval(function(c) { return frameindex>frame0; });
|
this.runEval((c) => { return frameindex>frame0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
getRasterScanline() : number {
|
||||||
|
return nes.ppu.scanline;
|
||||||
|
}
|
||||||
|
startProfiling() : ProfilerOutput {
|
||||||
|
var frame0 = frameindex;
|
||||||
|
var frame = null;
|
||||||
|
var output = {frame:null};
|
||||||
|
var i = 0;
|
||||||
|
var lastsl = 9999;
|
||||||
|
var start = 0;
|
||||||
|
this.runEval((c) => {
|
||||||
|
var sl = this.getRasterScanline();
|
||||||
|
if (sl != lastsl) {
|
||||||
|
if (frame) frame.lines.push({start:start,end:i});
|
||||||
|
if (sl < lastsl) {
|
||||||
|
output.frame = frame;
|
||||||
|
frame = {iptab:new Uint32Array(14672), lines:[]};
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
start = i+1;
|
||||||
|
lastsl = sl;
|
||||||
|
}
|
||||||
|
frame.iptab[i++] = c.EPC || c.PC;
|
||||||
|
return false; //frameindex>frame0; // TODO
|
||||||
|
});
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCPUState() {
|
getCPUState() {
|
||||||
|
@ -112,6 +112,9 @@ class VCSPlatform extends BasePlatform {
|
|||||||
var ypos = row-39;
|
var ypos = row-39;
|
||||||
return {x:xpos, y:ypos};
|
return {x:xpos, y:ypos};
|
||||||
}
|
}
|
||||||
|
getRasterScanline() : number {
|
||||||
|
return this.getRasterPosition().y;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Clock changes this on event, so it may not be current
|
// TODO: Clock changes this on event, so it may not be current
|
||||||
isRunning() {
|
isRunning() {
|
||||||
|
@ -209,20 +209,18 @@ export class CodeProject {
|
|||||||
webpath += ".a"; // legacy stuff
|
webpath += ".a"; // legacy stuff
|
||||||
// try to GET file, use file ext to determine text/binary
|
// try to GET file, use file ext to determine text/binary
|
||||||
this.callbackGetRemote( webpath, (data:FileData) => {
|
this.callbackGetRemote( webpath, (data:FileData) => {
|
||||||
|
if (data == null) {
|
||||||
|
console.log("Could not load preset file", path);
|
||||||
|
this.filedata[path] = null; // mark cache entry as invalid
|
||||||
|
} else {
|
||||||
if (data instanceof ArrayBuffer)
|
if (data instanceof ArrayBuffer)
|
||||||
data = new Uint8Array(data); // convert to typed array
|
data = new Uint8Array(data); // convert to typed array
|
||||||
console.log("GET",webpath,data.length,'bytes');
|
console.log("GET",webpath,data.length,'bytes');
|
||||||
this.filedata[path] = data; // do not update store, just cache
|
this.filedata[path] = data; // do not update store, just cache
|
||||||
addResult(path, data);
|
addResult(path, data);
|
||||||
|
}
|
||||||
loadNext();
|
loadNext();
|
||||||
}, isProbablyBinary(path) ? 'arraybuffer' : 'text')
|
}, isProbablyBinary(path) ? 'arraybuffer' : 'text');
|
||||||
.fail( (err:XMLHttpRequest) => {
|
|
||||||
console.log("Could not load preset file", path, err.status);
|
|
||||||
// only cache result if status is 404 (not found)
|
|
||||||
if (err.status && err.status == 404)
|
|
||||||
this.filedata[path] = null; // mark cache entry as invalid
|
|
||||||
loadNext();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// not gonna find it, keep going
|
// not gonna find it, keep going
|
||||||
loadNext();
|
loadNext();
|
||||||
|
10
src/ui.ts
10
src/ui.ts
@ -100,7 +100,12 @@ function getWithBinary(url:string, success:(text:FileData)=>void, datatype:'text
|
|||||||
oReq.open("GET", url, true);
|
oReq.open("GET", url, true);
|
||||||
oReq.responseType = datatype;
|
oReq.responseType = datatype;
|
||||||
oReq.onload = function (oEvent) {
|
oReq.onload = function (oEvent) {
|
||||||
|
if (oReq.status == 200)
|
||||||
success(oReq.response);
|
success(oReq.response);
|
||||||
|
else if (oReq.status == 404)
|
||||||
|
success(null);
|
||||||
|
else
|
||||||
|
throw "Error " + oReq.status + " loading " + url;
|
||||||
}
|
}
|
||||||
oReq.send(null);
|
oReq.send(null);
|
||||||
}
|
}
|
||||||
@ -210,6 +215,11 @@ function refreshWindowList() {
|
|||||||
return new Views.MemoryMapView();
|
return new Views.MemoryMapView();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (platform.startProfiling && platform.runEval && platform.getRasterScanline) {
|
||||||
|
addWindowItem("#profiler", "Profiler", function() {
|
||||||
|
return new Views.ProfileView();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// can pass integer or string id
|
// can pass integer or string id
|
||||||
|
81
src/views.ts
81
src/views.ts
@ -4,7 +4,7 @@ import $ = require("jquery");
|
|||||||
//import CodeMirror = require("codemirror");
|
//import CodeMirror = require("codemirror");
|
||||||
import { CodeProject } from "./project";
|
import { CodeProject } from "./project";
|
||||||
import { SourceFile, WorkerError, Segment } from "./workertypes";
|
import { SourceFile, WorkerError, Segment } from "./workertypes";
|
||||||
import { Platform, EmuState } from "./baseplatform";
|
import { Platform, EmuState, ProfilerOutput, lookupSymbol } from "./baseplatform";
|
||||||
import { hex, lpad, rpad } from "./util";
|
import { hex, lpad, rpad } from "./util";
|
||||||
import { CodeAnalyzer } from "./analysis";
|
import { CodeAnalyzer } from "./analysis";
|
||||||
import { platform, platform_id, compparams, current_project, lastDebugState, projectWindows } from "./ui";
|
import { platform, platform_id, compparams, current_project, lastDebugState, projectWindows } from "./ui";
|
||||||
@ -833,3 +833,82 @@ export class MemoryMapView implements ProjectView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
export class ProfileView implements ProjectView {
|
||||||
|
profilelist;
|
||||||
|
prof : ProfilerOutput;
|
||||||
|
maindiv : HTMLElement;
|
||||||
|
symcache : {};
|
||||||
|
|
||||||
|
createDiv(parent : HTMLElement) {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.setAttribute("class", "memdump");
|
||||||
|
parent.appendChild(div);
|
||||||
|
this.showMemoryWindow(parent, div);
|
||||||
|
return this.maindiv = div;
|
||||||
|
}
|
||||||
|
|
||||||
|
showMemoryWindow(workspace:HTMLElement, parent:HTMLElement) {
|
||||||
|
this.profilelist = new VirtualList({
|
||||||
|
w: $(workspace).width(),
|
||||||
|
h: $(workspace).height(),
|
||||||
|
itemHeight: getVisibleEditorLineHeight(),
|
||||||
|
totalRows: 262,
|
||||||
|
generatorFn: (row : number) => {
|
||||||
|
var s = this.getProfileLineAt(row);
|
||||||
|
var linediv = document.createElement("div");
|
||||||
|
linediv.appendChild(document.createTextNode(s));
|
||||||
|
return linediv;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$(parent).append(this.profilelist.container);
|
||||||
|
this.symcache = {};
|
||||||
|
this.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
getProfileLineAt(row : number) : string {
|
||||||
|
var s = lpad(row+': ',5);
|
||||||
|
if (!this.prof) return s;
|
||||||
|
var f = this.prof.frame;
|
||||||
|
if (!f) return s;
|
||||||
|
var l = f.lines[row];
|
||||||
|
if (!l) return s;
|
||||||
|
var lastsym = '';
|
||||||
|
for (var i=l.start; i<=l.end; i++) {
|
||||||
|
var pc = f.iptab[i];
|
||||||
|
var sym = this.symcache[pc];
|
||||||
|
if (!sym) {
|
||||||
|
sym = lookupSymbol(platform, pc, false);
|
||||||
|
this.symcache[pc] = sym;
|
||||||
|
}
|
||||||
|
if (sym != lastsym) {
|
||||||
|
s += sym + ' ';
|
||||||
|
lastsym = sym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
tick() {
|
||||||
|
if (this.profilelist) {
|
||||||
|
$(this.maindiv).find('[data-index]').each( (i,e) => {
|
||||||
|
var div = $(e);
|
||||||
|
var row = parseInt(div.attr('data-index'));
|
||||||
|
var oldtext = div.text();
|
||||||
|
var newtext = this.getProfileLineAt(row);
|
||||||
|
if (oldtext != newtext)
|
||||||
|
div.text(newtext);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// TODO: better way to keep it profiling? also, it clears the buffer
|
||||||
|
if (platform.isRunning()) {
|
||||||
|
this.prof = platform.startProfiling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -934,10 +934,12 @@ function linkLD65(step:BuildStep) {
|
|||||||
var toks = s.split(" ");
|
var toks = s.split(" ");
|
||||||
if (toks[0] == 'al') {
|
if (toks[0] == 'al') {
|
||||||
let ident = toks[2].substr(1);
|
let ident = toks[2].substr(1);
|
||||||
|
if (ident.length != 5 || !ident.startsWith('L')) { // no line numbers
|
||||||
let ofs = parseInt(toks[1], 16);
|
let ofs = parseInt(toks[1], 16);
|
||||||
symbolmap[ident] = ofs;
|
symbolmap[ident] = ofs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// build segment map
|
// build segment map
|
||||||
var seg_re = /^__(\w+)_SIZE__$/;
|
var seg_re = /^__(\w+)_SIZE__$/;
|
||||||
var segments = [].concat(params.extra_segments||[]);
|
var segments = [].concat(params.extra_segments||[]);
|
||||||
@ -1226,7 +1228,7 @@ function linkSDLDZ80(step:BuildStep)
|
|||||||
var symbolmap = {};
|
var symbolmap = {};
|
||||||
for (var s of noiout.split("\n")) {
|
for (var s of noiout.split("\n")) {
|
||||||
var toks = s.split(" ");
|
var toks = s.split(" ");
|
||||||
if (toks[0] == 'DEF' && !toks[1].startsWith("A$main$")) {
|
if (toks[0] == 'DEF' && !toks[1].startsWith("A$")) {
|
||||||
symbolmap[toks[1]] = parseInt(toks[2], 16);
|
symbolmap[toks[1]] = parseInt(toks[2], 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,10 @@ describe('Store', function() {
|
|||||||
var platform = {};
|
var platform = {};
|
||||||
var project = new prj.CodeProject(worker, test_platform_id, platform, store);
|
var project = new prj.CodeProject(worker, test_platform_id, platform, store);
|
||||||
var remote = [];
|
var remote = [];
|
||||||
project.callbackGetRemote = function(path) { remote.push(path); return {fail:function(failfn){failfn({status:404})}} };
|
project.callbackGetRemote = function(path, success, datatype) {
|
||||||
|
remote.push(path);
|
||||||
|
success();
|
||||||
|
};
|
||||||
project.loadFiles(['local/test','test'], function(err, result) {
|
project.loadFiles(['local/test','test'], function(err, result) {
|
||||||
assert.equal(null, err);
|
assert.equal(null, err);
|
||||||
assert.deepEqual(["presets/_TEST/test"], remote);
|
assert.deepEqual(["presets/_TEST/test"], remote);
|
||||||
|
Loading…
Reference in New Issue
Block a user