moved profiler to separate class, profiler ticks when replay scroller used

This commit is contained in:
Steven Hugg 2019-06-09 11:13:25 -04:00
parent e8260a8e46
commit e9da7aac4a
12 changed files with 127 additions and 58 deletions

View File

@ -103,6 +103,8 @@ TODO:
- click on profiler to step to position - click on profiler to step to position
- breakpoints stop profiler from running - breakpoints stop profiler from running
- single-screen profiler - single-screen profiler
- hide labels that aren't available, like BIOS addrs
- show interrupts, other events
- https://remotestoragejs.readthedocs.io/en/latest/getting-started/how-to-add.html ? - https://remotestoragejs.readthedocs.io/en/latest/getting-started/how-to-add.html ?
- Verilog - Verilog
- larger scope range, better scrolling - larger scope range, better scrolling
@ -153,6 +155,8 @@ TODO:
- emulator needs reset shortcut for nes - emulator needs reset shortcut for nes
- switching platform of a repo? - switching platform of a repo?
- make sure to flatten subdirs - make sure to flatten subdirs
- astrocade
- EI should trigger interrupt if pending? (see stash, might need z80 option)
- ctrl+alt+l on ubuntu locks screen - ctrl+alt+l on ubuntu locks screen
WEB WORKER FORMAT WEB WORKER FORMAT

View File

@ -279,12 +279,12 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<span class="control-def"><span class="control-key small">Space</span> Trigger</span> <span class="control-def"><span class="control-key small">Space</span> Trigger</span>
<span class="control-def"><span class="control-key small">Mouse X</span> Knob</span> <span class="control-def"><span class="control-key small">Mouse X</span> Knob</span>
<span class="control-def"><span class="control-key small" style="display:inline-block;font-family:monospace"> <span class="control-def"><span class="control-key small" style="display:inline-block;font-family:monospace">
C A Z P<br> U I O P<br>
R S H /<br> J K L /<br>
7 8 9 X<br> 7 8 9 X<br>
4 5 6 -<br> 4 5 6 -<br>
1 2 3 ,<br> 1 2 3 ,<br>
E 0 . =</span> Keypad</span> \ 0 . =</span> Keypad</span>
</div> </div>
<!-- --> <!-- -->
<div id="emuoverlay" class="emuoverlay" style="display:none"> <div id="emuoverlay" class="emuoverlay" style="display:none">

View File

@ -91,6 +91,15 @@ void end_music(void);
// QUICK MACROS // QUICK MACROS
#define SYS_ACTINT()\
__asm__("rst 0x38");\
__asm__(".db 0x0f");\
#define SYS_PAWS(frames)\
__asm__("rst 0x38");\
__asm__(".db 0x51");\
__asm__(".db "#frames);\
#define SYS_SETOUT(verbl,horcb,inmod)\ #define SYS_SETOUT(verbl,horcb,inmod)\
__asm__("rst 0x38");\ __asm__("rst 0x38");\
__asm__(".db 0x17");\ __asm__(".db 0x17");\
@ -112,6 +121,26 @@ void end_music(void);
__asm__(".dw "#count);\ __asm__(".dw "#count);\
__asm__(".dw "#src);\ __asm__(".dw "#src);\
#define SYS_RECTAN(x,y,width,height,color)\
__asm__("rst 0x38");\
__asm__(".db 0x1d");\
__asm__(".db "#x);\
__asm__(".db "#y);\
__asm__(".db "#width);\
__asm__(".db "#height);\
__asm__(".db "#color);\
#define SYS_BMUSIC(stack,voices,musicdata)\
__asm__("rst 0x38");\
__asm__(".db 0x13");\
__asm__(".dw "#stack);\
__asm__(".db "#voices);\
__asm__(".dw "#musicdata);\
#define SYS_EMUSIC()\
__asm__("rst 0x38");\
__asm__(".db 0x15");\
#define RESET_TIMEOUT() \ #define RESET_TIMEOUT() \
__asm__("ld a,#0xff");\ __asm__("ld a,#0xff");\
__asm__("ld (0x4FEC),a"); __asm__("ld (0x4FEC),a");

View File

@ -146,8 +146,3 @@ _begin_music:
SYSTEM BMUSIC SYSTEM BMUSIC
ret ret
; EMUSIC
.globl _end_music
_end_music:
SYSTEM EMUSIC
ret

View File

@ -64,6 +64,8 @@ const byte palette[8] = {
#define MAXLIVES 5 #define MAXLIVES 5
#define PLYRHEIGHT 9 #define PLYRHEIGHT 9
#define ENEMY_WIDTH 8
#define ENEMY_HEIGHT 8
#define ENEMY_SPACING_X 14 #define ENEMY_SPACING_X 14
#define ENEMY_SPACING_Y 11 #define ENEMY_SPACING_Y 11
#define ENEMY_WIDTH 8 #define ENEMY_WIDTH 8
@ -208,6 +210,7 @@ void delete_enemy(Enemy* e) {
} }
memmove(e, e+1, sizeof(Enemy)*(enemies-e+MAX_ENEMIES-1)); memmove(e, e+1, sizeof(Enemy)*(enemies-e+MAX_ENEMIES-1));
num_enemies--; // update_next_enemy() will check enemy_index num_enemies--; // update_next_enemy() will check enemy_index
// TODO: enemy_index might skip updating an enemy
} }
void update_next_enemy() { void update_next_enemy() {
@ -255,7 +258,7 @@ Enemy* find_enemy_at(byte x, byte y) {
byte i; byte i;
for (i=0; i<num_enemies; i++) { for (i=0; i<num_enemies; i++) {
Enemy* e = &enemies[i]; Enemy* e = &enemies[i];
if (in_rect(e, x, y, 4, 5)) { if (in_rect(e, x, y, 4, 1)) {
return e; return e;
} }
} }
@ -303,11 +306,11 @@ void drop_bomb() {
byte i = rand() % num_enemies; byte i = rand() % num_enemies;
Enemy* e = &enemies[i]; Enemy* e = &enemies[i];
// don't drop on someone else! // don't drop on someone else!
if (find_enemy_at(e->x, e->y+ENEMY_SPACING_Y*2)) { if (find_enemy_at(e->x, e->y+ENEMY_HEIGHT+ENEMY_SPACING_Y+1)) {
return; return;
} }
bomb_x = e->x + ENEMY_WIDTH/4; bomb_x = e->x + ENEMY_WIDTH/4;
bomb_y = e->y + ENEMY_SPACING_Y; bomb_y = e->y + ENEMY_HEIGHT;
xor_bomb(); xor_bomb();
} }
@ -321,7 +324,7 @@ void move_bomb() {
xor_bomb(); xor_bomb();
if (hw_intst & 0xf0) { // any pixels leftover? if (hw_intst & 0xf0) { // any pixels leftover?
erase_sprite(bomb_bitmap, bomb_x, bomb_y); erase_sprite(bomb_bitmap, bomb_x, bomb_y);
if (bomb_y >= player_y) { if (bomb_y >= player_y-2) {
// player was hit (probably) // player was hit (probably)
destroy_player(); destroy_player();
} }

View File

@ -51,8 +51,9 @@ void main(void) {
// set screen height // set screen height
// set horizontal color split (position / 4) // set horizontal color split (position / 4)
// set interrupt status // set interrupt status
// use SYS_SETOUT macro
SYS_SETOUT(89*2, 23, 0); SYS_SETOUT(89*2, 23, 0);
// clear screen // clear screen w/ SYS_FILL macro
SYS_FILL(0x4000, 89*40, 0); SYS_FILL(0x4000, 89*40, 0);
// display standard characters // display standard characters
display_string(2, 2, OPT_ON(1), "HELLO, WORLD!\xb1\xb2\xb3\xb4\xb5"); display_string(2, 2, OPT_ON(1), "HELLO, WORLD!\xb1\xb2\xb3\xb4\xb5");
@ -69,7 +70,8 @@ void main(void) {
display_string(4, 80, OPT_ON(1), "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9"); display_string(4, 80, OPT_ON(1), "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9");
// paint a rectangle with a pattern mask (0xa5) // paint a rectangle with a pattern mask (0xa5)
paint_rectangle(4, 72, 100, 5, 0x55); paint_rectangle(4, 72, 100, 5, 0x55);
paint_rectangle(6, 74, 100, 4, 0xaa); // more compact macro
SYS_RECTAN(6, 74, 100, 4, 0xaa);
// write from pattern block // write from pattern block
write_relative(50, 80, M_XPAND, BALL); write_relative(50, 80, M_XPAND, BALL);
write_relative(60, 80, M_XPAND, BALL); write_relative(60, 80, M_XPAND, BALL);
@ -83,6 +85,7 @@ void main(void) {
// make sure screen doesn't black out // make sure screen doesn't black out
RESET_TIMEOUT(); RESET_TIMEOUT();
// play music // play music
//SYS_BMUSIC(_music_stack, 0b11111100, _MUSICDATA);
//begin_music(music_stack, 0b11111100, MUSICDATA); //begin_music(music_stack, 0b11111100, MUSICDATA);
while (1) { while (1) {
// wait for SENTRY result // wait for SENTRY result

View File

@ -104,6 +104,9 @@ export interface Platform {
startProfiling?() : ProfilerOutput; startProfiling?() : ProfilerOutput;
stopProfiling?() : void; stopProfiling?() : void;
getRasterScanline?() : number; getRasterScanline?() : number;
setBreakpoint?(id : string, cond : DebugCondition);
clearBreakpoint?(id : string);
getCPUState?() : CpuState;
debugSymbols? : DebugSymbols; debugSymbols? : DebugSymbols;
} }
@ -160,6 +163,10 @@ export interface ProfilerFrame {
export interface ProfilerOutput { export interface ProfilerOutput {
frame : ProfilerFrame; frame : ProfilerFrame;
} }
export interface EmuProfiler {
start() : ProfilerOutput;
stop();
}
///// /////
@ -207,35 +214,6 @@ export abstract class BaseDebugPlatform extends BasePlatform {
clearBreakpoint(id : string) { clearBreakpoint(id : string) {
delete this.breakpoints.id2bp[id]; delete this.breakpoints.id2bp[id];
} }
startProfiling() : ProfilerOutput {
var frame = null;
var output = {frame:null};
var i = 0;
var lastsl = 9999;
var start = 0;
this.setBreakpoint('profile', () => {
var sl = (this as any).getRasterScanline();
if (sl != lastsl) {
if (frame) {
frame.lines.push({start:start,end:i-1});
}
if (sl < lastsl) {
output.frame = frame;
frame = {iptab:new Uint32Array(0x8000), lines:[]}; // TODO: const
i = 0;
}
start = i;
lastsl = sl;
}
var c = this.getCPUState();
frame.iptab[i++] = c.EPC || c.PC;
return false; // profile forever
});
return output;
}
stopProfiling() {
this.clearBreakpoint('profile');
}
getDebugCallback() : DebugCondition { getDebugCallback() : DebugCondition {
return this.breakpoints.getDebugCondition(); return this.breakpoints.getDebugCondition();
} }

View File

@ -435,7 +435,7 @@ export class TextDataNode extends CodeProjectDataNode {
} }
updateLeft() { updateLeft() {
if (this.right.words.length != this.words.length) if (this.right.words.length != this.words.length)
throw "Expected " + this.words.length + " bytes; image has " + this.right.words.length; throw "Expected " + this.right.words.length + " bytes; image has " + this.words.length;
this.words = this.right.words; this.words = this.right.words;
// TODO: reload editors? // TODO: reload editors?
var datastr = this.text.substring(this.start, this.end); var datastr = this.text.substring(this.start, this.end);

View File

@ -36,6 +36,12 @@ const ASTROCADE_KEYCODE_MAP = makeKeycodeMap([
[Keys.LEFT, 0x10, 0x4], [Keys.LEFT, 0x10, 0x4],
[Keys.RIGHT, 0x10, 0x8], [Keys.RIGHT, 0x10, 0x8],
[Keys.A, 0x10, 0x10], [Keys.A, 0x10, 0x10],
// player 2
[Keys.P2_UP, 0x11, 0x1],
[Keys.P2_DOWN, 0x11, 0x2],
[Keys.P2_LEFT, 0x11, 0x4],
[Keys.P2_RIGHT, 0x11, 0x8],
[Keys.P2_A, 0x11, 0x10],
// keypad $14 // keypad $14
[Keys.VK_P, 0x14, 0x1], [Keys.VK_P, 0x14, 0x1],
[Keys.VK_SLASH, 0x14, 0x2], [Keys.VK_SLASH, 0x14, 0x2],
@ -44,26 +50,26 @@ const ASTROCADE_KEYCODE_MAP = makeKeycodeMap([
[Keys.VK_COMMA, 0x14, 0x10], [Keys.VK_COMMA, 0x14, 0x10],
[Keys.VK_EQUALS,0x14, 0x20], [Keys.VK_EQUALS,0x14, 0x20],
// keypad $15 // keypad $15
[Keys.VK_Z, 0x15, 0x1], [Keys.VK_O, 0x15, 0x1],
[Keys.VK_H, 0x15, 0x2], [Keys.VK_L, 0x15, 0x2],
[Keys.VK_9, 0x15, 0x4], [Keys.VK_9, 0x15, 0x4],
[Keys.VK_6, 0x15, 0x8], [Keys.VK_6, 0x15, 0x8],
[Keys.VK_3, 0x15, 0x10], [Keys.VK_3, 0x15, 0x10],
[Keys.VK_PERIOD,0x15, 0x20], [Keys.VK_PERIOD,0x15, 0x20],
// keypad $16 // keypad $16
[Keys.VK_A, 0x16, 0x1], [Keys.VK_I, 0x16, 0x1],
[Keys.VK_S, 0x16, 0x2], [Keys.VK_K, 0x16, 0x2],
[Keys.VK_8, 0x16, 0x4], [Keys.VK_8, 0x16, 0x4],
[Keys.VK_5, 0x16, 0x8], [Keys.VK_5, 0x16, 0x8],
[Keys.VK_2, 0x16, 0x10], [Keys.VK_2, 0x16, 0x10],
[Keys.VK_0, 0x16, 0x20], [Keys.VK_0, 0x16, 0x20],
// keypad $17 // keypad $17
[Keys.VK_C, 0x17, 0x1], [Keys.VK_U, 0x17, 0x1],
[Keys.VK_R, 0x17, 0x2], [Keys.VK_J, 0x17, 0x2],
[Keys.VK_7, 0x17, 0x4], [Keys.VK_7, 0x17, 0x4],
[Keys.VK_4, 0x17, 0x8], [Keys.VK_4, 0x17, 0x8],
[Keys.VK_1, 0x17, 0x10], [Keys.VK_1, 0x17, 0x10],
[Keys.VK_E, 0x17, 0x20], [Keys.VK_BACK_SLASH, 0x17, 0x20],
]); ]);
const _BallyAstrocadePlatform = function(mainElement, arcade) { const _BallyAstrocadePlatform = function(mainElement, arcade) {

View File

@ -1,7 +1,10 @@
import { Platform, EmuState, EmuControlsState, EmuRecorder } from "./baseplatform"; import { Platform, EmuState, EmuControlsState, EmuRecorder } from "./baseplatform";
import { BaseDebugPlatform, EmuProfiler, ProfilerOutput } from "./baseplatform";
import { getNoiseSeed, setNoiseSeed } from "./emu"; import { getNoiseSeed, setNoiseSeed } from "./emu";
// RECORDER
type FrameRec = {controls:EmuControlsState, seed:number}; type FrameRec = {controls:EmuControlsState, seed:number};
export class StateRecorderImpl implements EmuRecorder { export class StateRecorderImpl implements EmuRecorder {
@ -111,3 +114,44 @@ export class StateRecorderImpl implements EmuRecorder {
return this.checkpoints.length && this.checkpoints[this.checkpoints.length-1]; return this.checkpoints.length && this.checkpoints[this.checkpoints.length-1];
} }
} }
// PROFILER
export class EmuProfilerImpl implements EmuProfiler {
platform : Platform;
constructor(platform : Platform) {
this.platform = platform;
}
start() : ProfilerOutput {
var frame = null;
var output = {frame:null};
var i = 0;
var lastsl = 9999;
var start = 0;
this.platform.setBreakpoint('profile', () => {
var sl = this.platform.getRasterScanline();
if (sl != lastsl) {
if (frame) {
frame.lines.push({start:start,end:i-1});
}
if (sl < lastsl) {
output.frame = frame;
frame = {iptab:new Uint32Array(0x8000), lines:[]}; // TODO: const
i = 0;
}
start = i;
lastsl = sl;
}
var c = this.platform.getCPUState();
frame.iptab[i++] = c.EPC || c.PC;
return false; // profile forever
});
return output;
}
stop() {
this.platform.clearBreakpoint('profile');
}
}

View File

@ -235,7 +235,7 @@ function refreshWindowList() {
return new Views.MemoryMapView(); return new Views.MemoryMapView();
}); });
} }
if (platform.startProfiling && platform.runEval && platform.getRasterScanline) { if (platform.getRasterScanline && platform.setBreakpoint && platform.getCPUState) { // TODO: use profiler class to determine compat
addWindowItem("#profiler", "Profiler", () => { addWindowItem("#profiler", "Profiler", () => {
return new Views.ProfileView(); return new Views.ProfileView();
}); });
@ -1506,6 +1506,7 @@ function setupReplaySlider() {
var frame = (<any>e.target).value; var frame = (<any>e.target).value;
if (stateRecorder.loadFrame(frame)) { if (stateRecorder.loadFrame(frame)) {
updateFrameNo(frame); updateFrameNo(frame);
projectWindows.tick();
} }
}; };
var setFrameTo = (frame:number) => { var setFrameTo = (frame:number) => {
@ -1513,6 +1514,7 @@ function setupReplaySlider() {
if (stateRecorder.loadFrame(frame)) { if (stateRecorder.loadFrame(frame)) {
replayslider.val(frame); replayslider.val(frame);
updateFrameNo(frame); updateFrameNo(frame);
projectWindows.tick();
console.log('seek to frame',frame); console.log('seek to frame',frame);
} }
}; };

View File

@ -3,10 +3,11 @@
import $ = require("jquery"); import $ = require("jquery");
//import CodeMirror = require("codemirror"); //import CodeMirror = require("codemirror");
import { SourceFile, WorkerError, Segment, FileData } from "./workertypes"; import { SourceFile, WorkerError, Segment, FileData } from "./workertypes";
import { Platform, EmuState, ProfilerOutput, lookupSymbol } from "./baseplatform"; import { Platform, EmuState, ProfilerOutput, lookupSymbol, BaseDebugPlatform } from "./baseplatform";
import { hex, lpad, rpad, safeident, rgb2bgr } from "./util"; import { hex, lpad, rpad, safeident, rgb2bgr } 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";
import { EmuProfilerImpl } from "./recorder";
import * as pixed from "./pixed/pixeleditor"; import * as pixed from "./pixed/pixeleditor";
declare var Mousetrap; declare var Mousetrap;
@ -819,8 +820,9 @@ export class MemoryMapView implements ProjectView {
/// ///
export class ProfileView implements ProjectView { export class ProfileView implements ProjectView {
prof : EmuProfilerImpl;
profilelist; profilelist;
prof : ProfilerOutput; out : ProfilerOutput;
maindiv : HTMLElement; maindiv : HTMLElement;
symcache : {}; symcache : {};
recreateOnResize = true; recreateOnResize = true;
@ -852,8 +854,8 @@ export class ProfileView implements ProjectView {
addProfileLine(div : HTMLElement, row : number) : void { addProfileLine(div : HTMLElement, row : number) : void {
div.appendChild(createTextSpan(lpad(row+':',4), "profiler-lineno")); div.appendChild(createTextSpan(lpad(row+':',4), "profiler-lineno"));
if (!this.prof) return; if (!this.out) return;
var f = this.prof.frame; var f = this.out.frame;
if (!f) return; if (!f) return;
var l = f.lines[row]; var l = f.lines[row];
if (!l) return; if (!l) return;
@ -899,10 +901,13 @@ export class ProfileView implements ProjectView {
} }
setVisible(showing : boolean) : void { setVisible(showing : boolean) : void {
if (!this.prof) {
this.prof = new EmuProfilerImpl(platform);
}
if (showing) if (showing)
this.prof = platform.startProfiling(); this.out = this.prof.start();
else else
platform.stopProfiling(); this.prof.stop();
} }
} }