analysis.ts for 6502 cycle counting (vcs, nes)
This commit is contained in:
parent
9d3e658a7b
commit
662f8a057d
|
@ -116,7 +116,7 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
|
|||
<button id="dbg_fastest" type="submit" title="Faster"><span class="glyphicon glyphicon-fast-forward" aria-hidden="true"></span></button>
|
||||
</span>
|
||||
<span class="btn_group view_group" 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_timing" type="submit" title="Analyze CPU 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>
|
||||
|
@ -245,12 +245,13 @@ function require(modname) {
|
|||
<script src="tss/js/tss/AudioLooper.js"></script>
|
||||
<script src="tss/js/Log.js"></script>
|
||||
|
||||
<script src="gen/util.js"></script>
|
||||
<script src="gen/store.js"></script>
|
||||
<script src="src/vlist.js"></script>
|
||||
<script src="gen/emu.js"></script>
|
||||
<script src="gen/baseplatform.js"></script>
|
||||
<script src="gen/analysis.js"></script>
|
||||
<script src="gen/audio.js"></script>
|
||||
<script src="gen/util.js"></script>
|
||||
<script src="gen/cpu/disasm6502.js"></script>
|
||||
<script src="gen/workertypes.js"></script>
|
||||
<script src="gen/project.js"></script>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
|
||||
include "nesdefs.asm"
|
||||
|
||||
;;;;; VARIABLES
|
||||
;;;;; ZERO-PAGE VARIABLES
|
||||
|
||||
seg.u ZEROPAGE
|
||||
org $0
|
||||
|
||||
;;;;; OTHER VARIABLES
|
||||
|
||||
seg.u RAM
|
||||
org $300
|
||||
|
@ -34,7 +35,7 @@ Start:
|
|||
sta PPU_SCROLL ; scroll = $0000
|
||||
lda #CTRL_NMI
|
||||
sta PPU_CTRL ; enable NMI
|
||||
lda #MASK_BG
|
||||
lda #MASK_BG|MASK_SPR
|
||||
sta PPU_MASK ; enable rendering
|
||||
.endless
|
||||
jmp .endless ; endless loop
|
||||
|
@ -70,6 +71,16 @@ SetPalette: subroutine
|
|||
bne .loop
|
||||
rts
|
||||
|
||||
; set sprite 0
|
||||
SetSprite0: subroutine
|
||||
sta $200 ;y
|
||||
lda #1 ;code
|
||||
sta $201
|
||||
lda #0 ;flags
|
||||
sta $202
|
||||
lda #8 ;xpos
|
||||
sta $203
|
||||
rts
|
||||
|
||||
;;;;; COMMON SUBROUTINES
|
||||
|
||||
|
@ -79,6 +90,17 @@ SetPalette: subroutine
|
|||
|
||||
NMIHandler: subroutine
|
||||
SAVE_REGS
|
||||
lda #112
|
||||
jsr SetSprite0
|
||||
; load sprites
|
||||
lda #$02
|
||||
sta PPU_OAM_DMA
|
||||
; wait for sprite 0
|
||||
.wait0 bit PPU_STATUS
|
||||
bvs .wait0
|
||||
.wait1 bit PPU_STATUS
|
||||
bvc .wait1
|
||||
; alter horiz. scroll position for each scanline
|
||||
ldy #0
|
||||
.loop
|
||||
tya
|
||||
|
@ -91,10 +113,9 @@ NMIHandler: subroutine
|
|||
sta PPU_SCROLL ; horiz byte
|
||||
lda #0
|
||||
sta PPU_SCROLL ; vert byte
|
||||
ldx #15
|
||||
.delay
|
||||
dex
|
||||
bne .delay
|
||||
REPEAT 25
|
||||
bit $0000
|
||||
REPEND
|
||||
iny
|
||||
cpy #224
|
||||
bne .loop
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
|
||||
import { hex, byte2signed } from "./util";
|
||||
import { Platform } from "./baseplatform";
|
||||
|
||||
export interface CodeAnalyzer {
|
||||
showLoopTimingForPC(pc:number);
|
||||
pc2minclocks : {[key:number]:number};
|
||||
pc2maxclocks : {[key:number]:number};
|
||||
MAX_CLOCKS : number;
|
||||
}
|
||||
|
||||
/// VCS TIMING ANALYSIS
|
||||
|
||||
// [taken, not taken]
|
||||
const BRANCH_CONSTRAINTS = [
|
||||
[{N:0},{N:1}],
|
||||
[{N:1},{N:0}],
|
||||
[{V:0},{V:1}],
|
||||
[{V:1},{V:0}],
|
||||
[{C:0},{C:1}],
|
||||
[{C:1},{C:0}],
|
||||
[{Z:0},{Z:1}],
|
||||
[{Z:1},{Z:0}]
|
||||
];
|
||||
|
||||
function constraintEquals(a,b) {
|
||||
if (a == null || b == null)
|
||||
return null;
|
||||
for (var n in a) {
|
||||
if (b[n] !== 'undefined')
|
||||
return a[n] == b[n];
|
||||
}
|
||||
for (var n in b) {
|
||||
if (a[n] !== 'undefined')
|
||||
return a[n] == b[n];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract class CodeAnalyzer6502 implements CodeAnalyzer {
|
||||
pc2minclocks = {};
|
||||
pc2maxclocks = {};
|
||||
START_CLOCKS : number;
|
||||
MAX_CLOCKS : number;
|
||||
WRAP_CLOCKS : boolean;
|
||||
jsrresult = {};
|
||||
platform : Platform;
|
||||
|
||||
constructor(platform : Platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
getClockCountsAtPC(pc) {
|
||||
var opcode = this.platform.readAddress(pc);
|
||||
var meta = this.platform.getOpcodeMetadata(opcode, pc);
|
||||
return meta; // minCycles, maxCycles
|
||||
}
|
||||
|
||||
traceInstructions(pc:number, minclocks:number, maxclocks:number, subaddr:number, constraints) {
|
||||
//console.log("trace", hex(pc), minclocks, maxclocks);
|
||||
if (!minclocks) minclocks = 0;
|
||||
if (!maxclocks) maxclocks = 0;
|
||||
if (!constraints) constraints = {};
|
||||
var modified = true;
|
||||
var abort = false;
|
||||
for (var i=0; i<1000 && modified && !abort; i++) {
|
||||
modified = false;
|
||||
var meta = this.getClockCountsAtPC(pc);
|
||||
var lob = this.platform.readAddress(pc+1);
|
||||
var hib = this.platform.readAddress(pc+2);
|
||||
var addr = lob + (hib << 8);
|
||||
var pc0 = pc;
|
||||
if (!this.pc2minclocks[pc0] || minclocks < this.pc2minclocks[pc0]) {
|
||||
this.pc2minclocks[pc0] = minclocks;
|
||||
modified = true;
|
||||
}
|
||||
if (!this.pc2maxclocks[pc0] || maxclocks > this.pc2maxclocks[pc0]) {
|
||||
this.pc2maxclocks[pc0] = maxclocks;
|
||||
modified = true;
|
||||
}
|
||||
//console.log(hex(pc),minclocks,maxclocks,meta);
|
||||
if (!meta.insnlength) {
|
||||
console.log("Illegal instruction!", hex(pc), hex(meta.opcode), meta);
|
||||
break;
|
||||
}
|
||||
pc += meta.insnlength;
|
||||
var oldconstraints = constraints;
|
||||
constraints = null;
|
||||
// TODO: if jump to zero-page, maybe assume RTS?
|
||||
switch (meta.opcode) {
|
||||
/*
|
||||
case 0xb9: // TODO: hack for zero page,y
|
||||
if (addr < 0x100)
|
||||
meta.maxCycles -= 1;
|
||||
break;
|
||||
*/
|
||||
// TODO: don't do in NES
|
||||
case 0x85:
|
||||
if (lob == 0x2) { // STA WSYNC
|
||||
minclocks = maxclocks = 0;
|
||||
meta.minCycles = meta.maxCycles = 0;
|
||||
}
|
||||
break;
|
||||
case 0x20: // JSR
|
||||
this.traceInstructions(addr, minclocks, maxclocks, addr, constraints);
|
||||
var result = this.jsrresult[addr];
|
||||
if (result) {
|
||||
minclocks = result.minclocks;
|
||||
maxclocks = result.maxclocks;
|
||||
} else {
|
||||
console.log("No JSR result!", hex(pc), hex(addr));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x4c: // JMP
|
||||
pc = addr; // TODO: make sure in ROM space
|
||||
break;
|
||||
case 0x60: // RTS
|
||||
if (subaddr) { // TODO: 0 doesn't work
|
||||
// TODO: combine with previous result
|
||||
var result = this.jsrresult[subaddr];
|
||||
if (!result) {
|
||||
result = {minclocks:minclocks, maxclocks:maxclocks};
|
||||
} else {
|
||||
result = {
|
||||
minclocks:Math.min(minclocks,result.minclocks),
|
||||
maxclocks:Math.max(maxclocks,result.maxclocks)
|
||||
}
|
||||
}
|
||||
this.jsrresult[subaddr] = result;
|
||||
console.log("RTS", hex(pc), hex(subaddr), this.jsrresult[subaddr]);
|
||||
}
|
||||
return;
|
||||
case 0x10: case 0x30: // branch
|
||||
case 0x50: case 0x70:
|
||||
case 0x90: case 0xB0:
|
||||
case 0xD0: case 0xF0:
|
||||
var newpc = pc + byte2signed(lob);
|
||||
var crosspage = (pc>>8) != (newpc>>8);
|
||||
if (!crosspage) meta.maxCycles--;
|
||||
// TODO: other instructions might modify flags too
|
||||
var cons = BRANCH_CONSTRAINTS[Math.floor((meta.opcode-0x10)/0x20)];
|
||||
var cons0 = constraintEquals(oldconstraints, cons[0]);
|
||||
var cons1 = constraintEquals(oldconstraints, cons[1]);
|
||||
if (cons0 !== false) {
|
||||
this.traceInstructions(newpc, minclocks+meta.maxCycles, maxclocks+meta.maxCycles, subaddr, cons[0]);
|
||||
}
|
||||
if (cons1 === false) {
|
||||
console.log("abort", hex(pc), oldconstraints, cons[1]);
|
||||
abort = true;
|
||||
}
|
||||
constraints = cons[1]; // not taken
|
||||
meta.maxCycles = meta.minCycles; // branch not taken, no extra clock(s)
|
||||
break;
|
||||
case 0x6c:
|
||||
console.log("Instruction not supported!", hex(pc), hex(meta.opcode), meta); // TODO
|
||||
return;
|
||||
}
|
||||
minclocks = Math.min(this.MAX_CLOCKS, minclocks + meta.minCycles);
|
||||
maxclocks = Math.min(this.MAX_CLOCKS, maxclocks + meta.maxCycles);
|
||||
if (this.WRAP_CLOCKS && maxclocks >= this.MAX_CLOCKS) {
|
||||
minclocks = minclocks % this.MAX_CLOCKS;
|
||||
maxclocks = maxclocks % this.MAX_CLOCKS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showLoopTimingForPC(pc:number) {
|
||||
this.pc2minclocks = {};
|
||||
this.pc2maxclocks = {};
|
||||
this.jsrresult = {};
|
||||
// recurse through all traces
|
||||
this.traceInstructions(pc | this.platform.getOriginPC(), this.START_CLOCKS, this.MAX_CLOCKS, 0, {});
|
||||
}
|
||||
}
|
||||
|
||||
// 76 cycles * 2 (support two scanline kernels)
|
||||
export class CodeAnalyzer_vcs extends CodeAnalyzer6502 {
|
||||
constructor(platform : Platform) {
|
||||
super(platform);
|
||||
this.MAX_CLOCKS = this.START_CLOCKS = 76*2;
|
||||
this.WRAP_CLOCKS = false;
|
||||
}
|
||||
}
|
||||
|
||||
// https://wiki.nesdev.com/w/index.php/PPU_rendering#Line-by-line_timing
|
||||
// TODO: sprite 0 hit, CPU stalls
|
||||
export class CodeAnalyzer_nes extends CodeAnalyzer6502 {
|
||||
constructor(platform : Platform) {
|
||||
super(platform);
|
||||
this.MAX_CLOCKS = 114; // ~341/3
|
||||
this.START_CLOCKS = 0;
|
||||
this.WRAP_CLOCKS = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
import { RAM, RasterVideo, dumpRAM, dumpStackToString } from "./emu";
|
||||
import { hex } from "./util";
|
||||
import { CodeAnalyzer } from "./analysis";
|
||||
|
||||
declare var Z80_fast, jt, CPU6809;
|
||||
|
||||
|
@ -52,6 +53,7 @@ export interface Platform {
|
|||
getDebugCallback?() : any; // TODO
|
||||
getSP?() : number;
|
||||
getOriginPC?() : number;
|
||||
newCodeAnalyzer() : CodeAnalyzer;
|
||||
|
||||
getDebugCategories() : string[];
|
||||
getDebugInfo(category:string, state:EmuState) : string;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, cpuStateToLongString_6502, getToolForFilename_6502 } from "../baseplatform";
|
||||
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM, dumpStackToString } from "../emu";
|
||||
import { hex, lpad, lzgmini } from "../util";
|
||||
import { CodeAnalyzer_nes } from "../analysis";
|
||||
|
||||
declare var jsnes : any;
|
||||
|
||||
|
@ -131,7 +132,12 @@ var JSNESPlatform = function(mainElement) {
|
|||
nes.loadROM(romstr);
|
||||
frameindex = 0;
|
||||
}
|
||||
|
||||
this.newCodeAnalyzer = function() {
|
||||
return new CodeAnalyzer_nes(this);
|
||||
}
|
||||
this.getOriginPC = function() { // TODO: is actually NMI
|
||||
return (this.readAddress(0xfffa) | (this.readAddress(0xfffb) << 8)) & 0xffff;
|
||||
}
|
||||
this.getOpcodeMetadata = getOpcodeMetadata_6502;
|
||||
this.getDefaultExtension = function() { return ".c"; };
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import { Platform, cpuStateToLongString_6502, BaseMAMEPlatform } from "../baseplatform";
|
||||
import { PLATFORMS, RAM, newAddressDecoder, dumpRAM } from "../emu";
|
||||
import { hex, lpad, tobin, byte2signed } from "../util";
|
||||
import { CodeAnalyzer_vcs } from "../analysis";
|
||||
|
||||
declare var platform : Platform; // global platform object
|
||||
declare var Javatari : any;
|
||||
|
@ -99,6 +100,9 @@ var VCSPlatform = function() {
|
|||
this.getOriginPC = function() {
|
||||
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
|
||||
}
|
||||
this.newCodeAnalyzer = function() {
|
||||
return new CodeAnalyzer_vcs(this);
|
||||
}
|
||||
/*
|
||||
this.saveState = function() {
|
||||
return Javatari.room.console.saveState(); // TODO
|
||||
|
@ -184,175 +188,6 @@ function nonegstr(n) {
|
|||
return n < 0 ? "-" : n.toString();
|
||||
}
|
||||
|
||||
/// VCS TIMING ANALYSIS
|
||||
|
||||
var pc2minclocks = {};
|
||||
var pc2maxclocks = {};
|
||||
var jsrresult = {};
|
||||
var MAX_CLOCKS = 76*2;
|
||||
|
||||
// [taken, not taken]
|
||||
var BRANCH_CONSTRAINTS = [
|
||||
[{N:0},{N:1}],
|
||||
[{N:1},{N:0}],
|
||||
[{V:0},{V:1}],
|
||||
[{V:1},{V:0}],
|
||||
[{C:0},{C:1}],
|
||||
[{C:1},{C:0}],
|
||||
[{Z:0},{Z:1}],
|
||||
[{Z:1},{Z:0}]
|
||||
];
|
||||
|
||||
function constraintEquals(a,b) {
|
||||
if (a == null || b == null)
|
||||
return null;
|
||||
for (var n in a) {
|
||||
if (b[n] !== 'undefined')
|
||||
return a[n] == b[n];
|
||||
}
|
||||
for (var n in b) {
|
||||
if (a[n] !== 'undefined')
|
||||
return a[n] == b[n];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getClockCountsAtPC(pc) {
|
||||
var opcode = platform.readAddress(pc);
|
||||
var meta = platform.getOpcodeMetadata(opcode, pc);
|
||||
return meta; // minCycles, maxCycles
|
||||
}
|
||||
|
||||
function _traceInstructions(pc:number, minclocks:number, maxclocks:number, subaddr:number, constraints) {
|
||||
//console.log("trace", hex(pc), minclocks, maxclocks);
|
||||
if (!minclocks) minclocks = 0;
|
||||
if (!maxclocks) maxclocks = 0;
|
||||
if (!constraints) constraints = {};
|
||||
var modified = true;
|
||||
var abort = false;
|
||||
for (var i=0; i<1000 && modified && !abort; i++) {
|
||||
modified = false;
|
||||
var meta = getClockCountsAtPC(pc);
|
||||
var lob = platform.readAddress(pc+1);
|
||||
var hib = platform.readAddress(pc+2);
|
||||
var addr = lob + (hib << 8);
|
||||
var pc0 = pc;
|
||||
if (!pc2minclocks[pc0] || minclocks < pc2minclocks[pc0]) {
|
||||
pc2minclocks[pc0] = minclocks;
|
||||
modified = true;
|
||||
}
|
||||
if (!pc2maxclocks[pc0] || maxclocks > pc2maxclocks[pc0]) {
|
||||
pc2maxclocks[pc0] = maxclocks;
|
||||
modified = true;
|
||||
}
|
||||
//console.log(hex(pc),minclocks,maxclocks,meta);
|
||||
if (!meta.insnlength) {
|
||||
console.log("Illegal instruction!", hex(pc), hex(meta.opcode), meta);
|
||||
break;
|
||||
}
|
||||
pc += meta.insnlength;
|
||||
var oldconstraints = constraints;
|
||||
constraints = null;
|
||||
// TODO: if jump to zero-page, maybe assume RTS?
|
||||
switch (meta.opcode) {
|
||||
/*
|
||||
case 0xb9: // TODO: hack for zero page,y
|
||||
if (addr < 0x100)
|
||||
meta.maxCycles -= 1;
|
||||
break;
|
||||
*/
|
||||
case 0x85:
|
||||
if (lob == 0x2) { // STA WSYNC
|
||||
minclocks = maxclocks = 0;
|
||||
meta.minCycles = meta.maxCycles = 0;
|
||||
}
|
||||
break;
|
||||
case 0x20: // JSR
|
||||
_traceInstructions(addr, minclocks, maxclocks, addr, constraints);
|
||||
var result = jsrresult[addr];
|
||||
if (result) {
|
||||
minclocks = result.minclocks;
|
||||
maxclocks = result.maxclocks;
|
||||
} else {
|
||||
console.log("No JSR result!", hex(pc), hex(addr));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x4c: // JMP
|
||||
pc = addr; // TODO: make sure in ROM space
|
||||
break;
|
||||
case 0x60: // RTS
|
||||
if (subaddr) { // TODO: 0 doesn't work
|
||||
// TODO: combine with previous result
|
||||
var result = jsrresult[subaddr];
|
||||
if (!result) {
|
||||
result = {minclocks:minclocks, maxclocks:maxclocks};
|
||||
} else {
|
||||
result = {
|
||||
minclocks:Math.min(minclocks,result.minclocks),
|
||||
maxclocks:Math.max(maxclocks,result.maxclocks)
|
||||
}
|
||||
}
|
||||
jsrresult[subaddr] = result;
|
||||
console.log("RTS", hex(pc), hex(subaddr), jsrresult[subaddr]);
|
||||
}
|
||||
return;
|
||||
case 0x10: case 0x30: // branch
|
||||
case 0x50: case 0x70:
|
||||
case 0x90: case 0xB0:
|
||||
case 0xD0: case 0xF0:
|
||||
var newpc = pc + byte2signed(lob);
|
||||
var crosspage = (pc>>8) != (newpc>>8);
|
||||
if (!crosspage) meta.maxCycles--;
|
||||
// TODO: other instructions might modify flags too
|
||||
var cons = BRANCH_CONSTRAINTS[Math.floor((meta.opcode-0x10)/0x20)];
|
||||
var cons0 = constraintEquals(oldconstraints, cons[0]);
|
||||
var cons1 = constraintEquals(oldconstraints, cons[1]);
|
||||
if (cons0 !== false) {
|
||||
_traceInstructions(newpc, minclocks+meta.maxCycles, maxclocks+meta.maxCycles, subaddr, cons[0]);
|
||||
}
|
||||
if (cons1 === false) {
|
||||
console.log("abort", hex(pc), oldconstraints, cons[1]);
|
||||
abort = true;
|
||||
}
|
||||
constraints = cons[1]; // not taken
|
||||
meta.maxCycles = meta.minCycles; // branch not taken, no extra clock(s)
|
||||
break;
|
||||
case 0x6c:
|
||||
console.log("Instruction not supported!", hex(pc), hex(meta.opcode), meta); // TODO
|
||||
return;
|
||||
}
|
||||
// TODO: wraparound?
|
||||
minclocks = Math.min(MAX_CLOCKS, minclocks + meta.minCycles);
|
||||
maxclocks = Math.min(MAX_CLOCKS, maxclocks + meta.maxCycles);
|
||||
}
|
||||
}
|
||||
|
||||
function showLoopTimingForPC(pc, sourcefile, ed) {
|
||||
pc2minclocks = {};
|
||||
pc2maxclocks = {};
|
||||
jsrresult = {};
|
||||
// recurse through all traces
|
||||
_traceInstructions(pc | platform.getOriginPC(), MAX_CLOCKS, MAX_CLOCKS, 0, {});
|
||||
ed.editor.clearGutter("gutter-bytes");
|
||||
// show the lines
|
||||
for (var line in sourcefile.line2offset) {
|
||||
var pc = sourcefile.line2offset[line];
|
||||
var minclocks = pc2minclocks[pc];
|
||||
var maxclocks = pc2maxclocks[pc];
|
||||
if (minclocks>=0 && maxclocks>=0) {
|
||||
var s;
|
||||
if (maxclocks == minclocks)
|
||||
s = minclocks + "";
|
||||
else
|
||||
s = minclocks + "-" + maxclocks;
|
||||
if (maxclocks == MAX_CLOCKS)
|
||||
s += "+";
|
||||
ed.setGutterBytes(line, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////
|
||||
|
||||
var VCSMAMEPlatform = function(mainElement) {
|
||||
|
|
|
@ -68,7 +68,7 @@ localforage.defineDriver(OldFileStoreDriver);
|
|||
*/
|
||||
|
||||
// copy localStorage to new driver
|
||||
function copyFromOldStorageFormat(platformid:string, newstore, callback:()=>void) {
|
||||
function copyFromOldStorageFormat(platformid:string, newstore, conversioncallback:()=>void) {
|
||||
var alreadyMigratedKey = "__migrated_" + platformid;
|
||||
//localStorage.removeItem(alreadyMigratedKey);
|
||||
if (localStorage.getItem(alreadyMigratedKey))
|
||||
|
@ -97,8 +97,8 @@ function copyFromOldStorageFormat(platformid:string, newstore, callback:()=>void
|
|||
console.log("Migrated " + len + " local files to new data store");
|
||||
if (len) {
|
||||
localStorage.setItem(alreadyMigratedKey, 'true');
|
||||
if (callback)
|
||||
callback();
|
||||
if (conversioncallback)
|
||||
conversioncallback();
|
||||
else
|
||||
window.location.reload();
|
||||
}
|
||||
|
|
33
src/ui.ts
33
src/ui.ts
|
@ -1,6 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
import { getFilenameForPath, getFilenamePrefix, highlightDifferences, invertMap } from "./util";
|
||||
|
||||
// 8bitworkshop IDE user interface
|
||||
|
||||
|
@ -12,11 +11,11 @@ import { ProjectWindows } from "./windows";
|
|||
import { Platform, Preset } from "./baseplatform";
|
||||
import { PLATFORMS } from "./emu";
|
||||
import * as Views from "./views";
|
||||
import { createNewPersistentStore } from "./store";
|
||||
import { getFilenameForPath, getFilenamePrefix, highlightDifferences, invertMap } from "./util";
|
||||
|
||||
// external libs (TODO)
|
||||
declare var Octokat, ga, Tour, GIF, saveAs;
|
||||
declare function createNewPersistentStore(platform_id : string);
|
||||
declare function showLoopTimingForPC(pc:number, sourcefile:SourceFile, wnd:Views.ProjectView);
|
||||
|
||||
// make sure VCS doesn't start
|
||||
if (window['Javatari']) window['Javatari'].AUTO_START = false;
|
||||
|
@ -669,8 +668,10 @@ function _openBitmapEditor() {
|
|||
function traceTiming() {
|
||||
projectWindows.refresh(false);
|
||||
var wnd = projectWindows.getActive();
|
||||
if (wnd.getSourceFile && wnd.setGutterBytes) { // is editor active?
|
||||
showLoopTimingForPC(0, wnd.getSourceFile(), wnd);
|
||||
if (wnd.getSourceFile && wnd.setTimingResult) { // is editor active?
|
||||
var analyzer = platform.newCodeAnalyzer();
|
||||
analyzer.showLoopTimingForPC(0);
|
||||
wnd.setTimingResult(analyzer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -689,7 +690,7 @@ function setupDebugControls(){
|
|||
$("#dbg_tovsync").hide();
|
||||
if ((platform.runEval || platform.runToPC) && platform_id != 'verilog')
|
||||
$("#dbg_toline").click(runToCursor).show();
|
||||
else
|
||||
else
|
||||
$("#dbg_toline").hide();
|
||||
if (platform.runUntilReturn)
|
||||
$("#dbg_stepout").click(runUntilReturn).show();
|
||||
|
@ -700,7 +701,7 @@ function setupDebugControls(){
|
|||
else
|
||||
$("#dbg_stepback").hide();
|
||||
|
||||
if (window['showLoopTimingForPC']) { // VCS-only (TODO: put in platform)
|
||||
if (platform.newCodeAnalyzer) {
|
||||
$("#dbg_timing").click(traceTiming).show();
|
||||
}
|
||||
$("#disassembly").hide();
|
||||
|
@ -823,10 +824,6 @@ function gotoNewLocation() {
|
|||
window.location.href = "?" + $.param(qs);
|
||||
}
|
||||
|
||||
function initPlatform() {
|
||||
store = createNewPersistentStore(platform_id);
|
||||
}
|
||||
|
||||
function showBookLink() {
|
||||
if (platform_id == 'vcs')
|
||||
$("#booklink_vcs").show();
|
||||
|
@ -860,7 +857,6 @@ function addPageFocusHandlers() {
|
|||
}
|
||||
|
||||
function startPlatform() {
|
||||
initPlatform();
|
||||
if (!PLATFORMS[platform_id]) throw Error("Invalid platform '" + platform_id + "'.");
|
||||
platform = new PLATFORMS[platform_id]($("#emulator")[0]);
|
||||
PRESETS = platform.getPresets();
|
||||
|
@ -893,11 +889,12 @@ function loadSharedFile(sharekey : string) {
|
|||
var json = JSON.parse(val.description.slice(val.description.indexOf(' ')+1));
|
||||
console.log("Fetched " + newid, json);
|
||||
platform_id = json['platform'];
|
||||
initPlatform();
|
||||
current_project.updateFile(newid, val.files[filename].content);
|
||||
reloadPresetNamed(newid);
|
||||
delete qs['sharekey'];
|
||||
gotoNewLocation();
|
||||
store = createNewPersistentStore(platform_id, () => {
|
||||
current_project.updateFile(newid, val.files[filename].content);
|
||||
reloadPresetNamed(newid);
|
||||
delete qs['sharekey'];
|
||||
gotoNewLocation();
|
||||
});
|
||||
}).fail(function(err) {
|
||||
alert("Error loading share file: " + err.message);
|
||||
});
|
||||
|
@ -918,9 +915,9 @@ function startUI(loadplatform : boolean) {
|
|||
if (qs['sharekey']) {
|
||||
loadSharedFile(qs['sharekey']);
|
||||
} else {
|
||||
store = createNewPersistentStore(platform_id, null);
|
||||
// reset file?
|
||||
if (qs['file'] && qs['reset']) {
|
||||
initPlatform();
|
||||
store.removeItem(qs['fileview'] || qs['file']);
|
||||
qs['reset'] = '';
|
||||
gotoNewLocation();
|
||||
|
|
22
src/views.ts
22
src/views.ts
|
@ -5,6 +5,7 @@ import { CodeProject } from "./project";
|
|||
import { SourceFile, WorkerError } from "./workertypes";
|
||||
import { Platform } from "./baseplatform";
|
||||
import { hex } from "./util";
|
||||
import { CodeAnalyzer } from "./analysis";
|
||||
|
||||
export interface ProjectView {
|
||||
createDiv(parent:HTMLElement, text:string) : HTMLElement;
|
||||
|
@ -17,6 +18,7 @@ export interface ProjectView {
|
|||
openBitmapEditorAtCursor?() : void;
|
||||
markErrors?(errors:WorkerError[]) : void;
|
||||
clearErrors?() : void;
|
||||
setTimingResult?(result:CodeAnalyzer) : void;
|
||||
};
|
||||
|
||||
// TODO: move to different namespace
|
||||
|
@ -211,6 +213,26 @@ export class SourceEditor implements ProjectView {
|
|||
this.setGutter("gutter-bytes", line-1, s);
|
||||
}
|
||||
|
||||
setTimingResult(result:CodeAnalyzer) : void {
|
||||
this.editor.clearGutter("gutter-bytes");
|
||||
// show the lines
|
||||
for (const line in Object.keys(this.sourcefile.line2offset)) {
|
||||
var pc = this.sourcefile.line2offset[line];
|
||||
var minclocks = result.pc2minclocks[pc];
|
||||
var maxclocks = result.pc2maxclocks[pc];
|
||||
if (minclocks>=0 && maxclocks>=0) {
|
||||
var s;
|
||||
if (maxclocks == minclocks)
|
||||
s = minclocks + "";
|
||||
else
|
||||
s = minclocks + "-" + maxclocks;
|
||||
if (maxclocks == result.MAX_CLOCKS)
|
||||
s += "+";
|
||||
this.setGutterBytes(parseInt(line), s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentLine(line:number, moveCursor:boolean) {
|
||||
|
||||
var addCurrentMarker = (line:number) => {
|
||||
|
|
Loading…
Reference in New Issue