From 78d83e0a20b5673718153d448f085a7dc8b55a77 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 7 Apr 2019 12:33:01 -0400 Subject: [PATCH] reload debug break state after breakpoint hit; debugPCDelta in load/saveState; renamed vrambuf_ functions --- doc/notes.txt | 2 ++ presets/nes/climber.c | 14 ++++++------- presets/nes/horizmask.c | 8 ++++---- presets/nes/shoot2.c | 11 +++++----- presets/nes/siegegame.c | 19 +++++++++--------- presets/nes/vrambuf.c | 18 ++++++++--------- presets/nes/vrambuf.h | 8 ++++---- presets/nes/vrambuffer.c | 6 +++--- src/baseplatform.ts | 42 +++++++++++++++++++++++++-------------- src/platform/apple2.ts | 6 ++++-- src/platform/nes.ts | 3 +++ src/ui.ts | 5 ++++- src/views.ts | 14 +++---------- test/cli/testplatforms.js | 2 ++ 14 files changed, 86 insertions(+), 72 deletions(-) diff --git a/doc/notes.txt b/doc/notes.txt index 4a172e22..1ba9c063 100644 --- a/doc/notes.txt +++ b/doc/notes.txt @@ -114,6 +114,8 @@ TODO: - editing too fast refreshes kills the editor, also doesn't update palette - crt0.s compiled each time? - debug highlight doesn't go away when debugging -> running + - show breakpoint of PC or highest address on stack + - can we highlight line instead of select? - replay doesn't work for nes (force background tile redraw) - running profiler while replaying? grand unified replay? - click on profiler to step to position diff --git a/presets/nes/climber.c b/presets/nes/climber.c index 744490d0..de14496b 100644 --- a/presets/nes/climber.c +++ b/presets/nes/climber.c @@ -293,10 +293,10 @@ void draw_floor_line(byte screen_y) { else a = 0x00; memset(attrs, a, 8); - putbytes(nt2attraddr(addr), attrs, 8); + vrambuf_put(nt2attraddr(addr), attrs, 8); } // copy line to screen buffer - putbytes(addr, buf, COLS); + vrambuf_put(addr, buf, COLS); // create actors on this floor, if needed // TODO: maybe this happens too early? if (dy == 0 && (floor >= 2)) { @@ -314,7 +314,7 @@ void draw_entire_stage() { for (y=0; y>= 4; } - putbytes(NTADR_A(col, row), buf, 5); + vrambuf_put(NTADR_A(col, row), buf, 5); } // GAME CODE @@ -309,7 +308,7 @@ void draw_row(byte row) { } x += 3; } - putbytes(NTADR_A(0, y), buf, sizeof(buf)); + vrambuf_put(NTADR_A(0, y), buf, sizeof(buf)); } void draw_next_row() { @@ -721,7 +720,7 @@ void play_round() { if (!enemies_left) end_timer--; draw_next_row(); } - cflushnow(); + vrambuf_flush(); copy_sprites(); #ifdef DEBUG_FRAMERATE putchar(t0 & 31, 27, CHAR(' ')); @@ -773,7 +772,7 @@ void setup_graphics() { dest += 3*16; } // activate vram buffer - cendbuf(); + vrambuf_clear(); set_vram_update(updbuf); } diff --git a/presets/nes/siegegame.c b/presets/nes/siegegame.c index 47119f92..bca8ced7 100644 --- a/presets/nes/siegegame.c +++ b/presets/nes/siegegame.c @@ -37,16 +37,15 @@ byte getchar(byte x, byte y) { } void cputcxy(byte x, byte y, char ch) { - putbytes(NTADR_A(x,y), &ch, 1); + vrambuf_put(NTADR_A(x,y), &ch, 1); } void cputsxy(byte x, byte y, const char* str) { - putbytes(NTADR_A(x,y), str, strlen(str)); + vrambuf_put(NTADR_A(x,y), str, strlen(str)); } void clrscr() { - updptr = 0; - cendbuf(); + vrambuf_clear(); ppu_off(); vram_adr(0x2000); vram_fill(0, 32*28); @@ -198,8 +197,8 @@ void flash_colliders() { //cv_set_attenuation(CV_SOUNDCHANNEL_0, i/2); if (players[0].collided) players[0].head_attr ^= 0x80; if (players[1].collided) players[1].head_attr ^= 0x80; - cflushnow(); - cflushnow(); + vrambuf_flush(); + vrambuf_flush(); draw_player(&players[0]); draw_player(&players[1]); } @@ -210,7 +209,7 @@ void make_move() { byte i; for (i=0; i> 8) ^ NT_UPD_HORZ); @@ -45,5 +45,5 @@ void putbytes(word addr, register const char* str, byte len) { memcpy(updbuf+updptr, str, len); updptr += len; // place EOF mark - cendbuf(); + vrambuf_end(); } diff --git a/presets/nes/vrambuf.h b/presets/nes/vrambuf.h index 044178ff..ba761d8c 100644 --- a/presets/nes/vrambuf.h +++ b/presets/nes/vrambuf.h @@ -24,17 +24,17 @@ extern byte updptr; VRAMBUF_ADD(len); // add EOF marker to buffer (but don't increment pointer) -void cendbuf(void); +void vrambuf_end(void); // clear vram buffer and place EOF marker -void cclearbuf(void); +void vrambuf_clear(void); // wait for next frame, then clear buffer // this assumes the NMI will call flush_vram_update() -void cflushnow(void); +void vrambuf_flush(void); // add multiple characters to update buffer // using horizontal increment -void putbytes(word addr, const char* str, byte len); +void vrambuf_put(word addr, const char* str, byte len); #endif // vrambuf.h diff --git a/presets/nes/vrambuffer.c b/presets/nes/vrambuffer.c index 5396ffde..5b5de7fe 100644 --- a/presets/nes/vrambuffer.c +++ b/presets/nes/vrambuffer.c @@ -24,10 +24,10 @@ void scroll_demo() { // write message to string array sprintf(str, "%6x %6d", y, y); // write string array into VRAM buffer - putbytes(NTADR_A(2,y%30), str, 32); + vrambuf_put(NTADR_A(2,y%30), str, 32); // wait for next frame // and flush VRAM buffer - cflushnow(); + vrambuf_flush(); // set scroll (shadow) registers scroll(x, y); // update y variable @@ -46,7 +46,7 @@ void main(void) { pal_col(3,0x30); // clear vram buffer - cclearbuf(); + vrambuf_clear(); // set NMI handler set_vram_update(updbuf); diff --git a/src/baseplatform.ts b/src/baseplatform.ts index 401cbed2..343464c7 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -289,7 +289,11 @@ export function getToolForFilename_6502(fn:string) : string { // TODO: can merge w/ Z80? export abstract class Base6502Platform extends BaseDebugPlatform { + // some platforms store their PC one byte before or after the first opcode + // so we correct when saving and loading from state debugPCDelta = -1; + fixPC(c) { c.PC = (c.PC + this.debugPCDelta) & 0xffff; return c; } + unfixPC(c) { c.PC = (c.PC - this.debugPCDelta) & 0xffff; return c;} evalDebugCondition() { if (this.debugCallback && !this.debugBreakState) { @@ -297,18 +301,21 @@ export abstract class Base6502Platform extends BaseDebugPlatform { } } postFrame() { - // save state every frame and rewind debug clocks - var debugging = this.debugCallback && !this.debugBreakState; - if (debugging) { - this.debugSavedState = this.saveState(); - this.debugTargetClock -= this.debugClock; - this.debugClock = 0; + if (this.debugCallback) { + if (this.debugBreakState) { + // reload debug state at end of frame after breakpoint + this.loadState(this.debugBreakState); + } else { + // save state every frame and rewind debug clocks + this.debugSavedState = this.saveState(); + this.debugTargetClock -= this.debugClock; + this.debugClock = 0; + } } } breakpointHit(targetClock : number) { this.debugTargetClock = targetClock; this.debugBreakState = this.saveState(); - this.debugBreakState.c.PC = (this.debugBreakState.c.PC + this.debugPCDelta) & 0xffff; console.log("Breakpoint at clk", this.debugClock, "PC", this.debugBreakState.c.PC.toString(16)); this.pause(); if (this.onBreakpointHit) { @@ -319,7 +326,6 @@ export abstract class Base6502Platform extends BaseDebugPlatform { this.setDebugCondition( () => { if (this.debugClock++ > this.debugTargetClock) { var cpuState = this.getCPUState(); - cpuState.PC = (cpuState.PC + this.debugPCDelta) & 0xffff; if (evalfunc(cpuState)) { this.breakpointHit(this.debugClock-1); return true; @@ -366,7 +372,7 @@ export abstract class Base6502Platform extends BaseDebugPlatform { this.loadState(prevState); this.breakpointHit(prevClock-1); return true; - } else if (this.debugClock > this.debugTargetClock-10 && this.debugClock < this.debugTargetClock) { + } else if (this.debugClock > this.debugTargetClock-10 && this.debugClock <= this.debugTargetClock+this.debugPCDelta) { // TODO: why this works? if (this.getCPUState().T == 0) { prevState = this.saveState(); prevClock = this.debugClock; @@ -553,12 +559,18 @@ export abstract class BaseZ80Platform extends BaseDebugPlatform { cpu.requestInterrupt(data); } postFrame() { - if (this.debugCallback && !this.debugBreakState) { - this.debugSavedState = this.saveState(); - if (this.debugTargetClock > 0) - this.debugTargetClock -= this.debugSavedState.c.T; - this.debugSavedState.c.T = 0; - this.loadState(this.debugSavedState); + if (this.debugCallback) { + if (this.debugBreakState) { + // if breakpoint, reload debug state after frame + this.loadState(this.debugBreakState); + } else { + // reset debug target clocks + this.debugSavedState = this.saveState(); + if (this.debugTargetClock > 0) + this.debugTargetClock -= this.debugSavedState.c.T; + this.debugSavedState.c.T = 0; + this.loadState(this.debugSavedState); + } } } breakpointHit(targetClock : number) { diff --git a/src/platform/apple2.ts b/src/platform/apple2.ts index 9dd84fc4..ebd43c7c 100644 --- a/src/platform/apple2.ts +++ b/src/platform/apple2.ts @@ -251,7 +251,9 @@ const _Apple2Platform = function(mainElement) { } loadState(state) { + this.unfixPC(state.c); cpu.loadState(state.c); + this.fixPC(state.c); ram.mem.set(state.b); kbdlatch = state.kbd; grswitch = state.gr; @@ -263,7 +265,7 @@ const _Apple2Platform = function(mainElement) { } saveState() { return { - c:cpu.saveState(), + c:this.getCPUState(), b:ram.mem.slice(0), kbd:kbdlatch, gr:grswitch, @@ -279,7 +281,7 @@ const _Apple2Platform = function(mainElement) { }; } getCPUState() { - return cpu.saveState(); + return this.fixPC(cpu.saveState()); } } diff --git a/src/platform/nes.ts b/src/platform/nes.ts index 3ef9e37a..b25864b7 100644 --- a/src/platform/nes.ts +++ b/src/platform/nes.ts @@ -240,7 +240,9 @@ class JSNESPlatform extends Base6502Platform implements Platform { return s; } loadState(state) { + this.unfixPC(state.cpu); this.nes.fromJSON(state); + this.fixPC(state.cpu); //this.nes.cpu.fromJSON(state.cpu); //this.nes.mmap.fromJSON(state.mmap); //this.nes.ppu.fromJSON(state.ppu); @@ -266,6 +268,7 @@ class JSNESPlatform extends Base6502Platform implements Platform { copy6502REGvars(c) { c.T = 0; c.PC = c.REG_PC; + this.fixPC(c); c.A = c.REG_ACC; c.X = c.REG_X; c.Y = c.REG_Y; diff --git a/src/ui.ts b/src/ui.ts index 740376ba..b880f594 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -1380,9 +1380,10 @@ function loadSharedGist(gistkey : string) { }); } -function loadScript(scriptfn, onload) { +function loadScript(scriptfn, onload, onerror?) { var script = document.createElement('script'); script.onload = onload; + script.onerror = onerror; script.src = scriptfn; document.getElementsByTagName('head')[0].appendChild(script); } @@ -1442,6 +1443,8 @@ export function startUI(loadplatform : boolean) { startPlatform(); showWelcomeMessage(); document.title = document.title + " [" + platform_id + "] - " + main_file_id; + }, () => { + alert('Platform "' + platform_id + '" not supported.'); }); } else { startPlatform(); diff --git a/src/views.ts b/src/views.ts index 5ab9e67e..8cbac5dc 100644 --- a/src/views.ts +++ b/src/views.ts @@ -400,7 +400,7 @@ export class DisassemblerView implements ProjectView { // TODO: too many globals refresh(moveCursor: boolean) { - var state = lastDebugState || platform.saveState(); + var state = lastDebugState || platform.saveState(); // TODO? var pc = state.c ? state.c.PC : 0; var curline = 0; var selline = 0; @@ -501,7 +501,7 @@ export class ListingView extends DisassemblerView implements ProjectView { refresh(moveCursor: boolean) { this.refreshListing(); if (!this.assemblyfile) return; // TODO? - var state = lastDebugState || platform.saveState(); + var state = lastDebugState || platform.saveState(); // TODO? var pc = state.c ? (state.c.EPC || state.c.PC) : 0; var asmtext = this.assemblyfile.text; var disasmview = this.getDisasmView(); @@ -530,15 +530,6 @@ export class MemoryView implements ProjectView { maindiv : HTMLElement; static IGNORE_SYMS = {s__INITIALIZER:true, /* s__GSINIT:true, */ _color_prom:true}; recreateOnResize = true; - /* - read(addr:number) { - // TODO: b offset ? - if (lastDebugState && lastDebugState.b && addr < lastDebugState.b.length) - return lastDebugState.b[addr]; - else - return this.platform.readMemory(addr); - } - */ createDiv(parent : HTMLElement) { var div = document.createElement('div'); @@ -1207,6 +1198,7 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext { } // TODO: recreate editors when refreshing +// TODO: look for changes, not moveCursor refresh(moveCursor : boolean) { if (moveCursor) { this.maindiv.empty(); diff --git a/test/cli/testplatforms.js b/test/cli/testplatforms.js index 81a2b307..989d731f 100644 --- a/test/cli/testplatforms.js +++ b/test/cli/testplatforms.js @@ -129,6 +129,8 @@ function testPlatform(platid, romname, maxframes, callback) { maxframes = 120*60; assert.equal(maxframes, rec.numFrames()); var state1 = platform.saveState(); + platform.loadState(state1); + assert.deepEqual(state1, platform.saveState()); assert.equal(1, rec.loadFrame(1)); assert.equal(maxframes, rec.loadFrame(maxframes)); var state2 = platform.saveState();