reload debug break state after breakpoint hit; debugPCDelta in load/saveState; renamed vrambuf_ functions

This commit is contained in:
Steven Hugg 2019-04-07 12:33:01 -04:00
parent 0a9fffee73
commit 78d83e0a20
14 changed files with 86 additions and 72 deletions

View File

@ -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

View File

@ -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<ROWS; y++) {
draw_floor_line(y);
// allow buffer to flush, delaying a frame
cflushnow();
vrambuf_flush();
}
}
@ -671,11 +671,11 @@ void type_message(const char* charptr) {
x = 2;
y++;
} else {
putbytes(getntaddr(x, y), &ch, 1);
vrambuf_put(getntaddr(x, y), &ch, 1);
x++;
}
// flush buffer and wait a few frames
cflushnow();
vrambuf_flush();
delay(5);
}
}
@ -705,7 +705,7 @@ void play_scene() {
while (actors[0].floor != MAX_FLOORS-1) {
//set_scroll_pixel_yy(scroll_pixel_yy+1);
cflushnow();
vrambuf_flush();
refresh_sprites();
move_player();
// move all the actors
@ -747,7 +747,7 @@ void setup_graphics() {
pal_all(PALETTE);
vram_adr(0x2000);
vram_fill(CH_BLANK, 0x1000);
cendbuf();
vrambuf_clear();
set_vram_update(updbuf);
ppu_on_all();
}

View File

@ -61,7 +61,7 @@ void update_nametable() {
// draw rest of building
memset(buf+PLAYROWS-bldg_height, bldg_char, bldg_height);
// draw vertical slice in name table
putbytes(addr ^ 0xc000, buf, sizeof(buf));
vrambuf_put(addr ^ 0xc000, buf, sizeof(buf));
// every 4 columns, update attribute table
if ((x & 3) == 1) {
// compute attribute table address
@ -71,7 +71,7 @@ void update_nametable() {
// put lower attribute block
addr += 8;
VRAMBUF_PUT(addr, bldg_attr, 0);
cendbuf();
vrambuf_end();
}
// generate new building?
if (--bldg_width == 0) {
@ -93,7 +93,7 @@ void scroll_demo() {
// manually force vram update
ppu_wait_nmi();
flush_vram_update(updbuf);
cclearbuf();
vrambuf_end();
// reset ppu address
vram_adr(0x0);
// set scroll register
@ -147,7 +147,7 @@ void main(void) {
ppu_mask(MASK_SPR|MASK_BG);
// clear vram buffer
cclearbuf();
vrambuf_clear();
// enable PPU rendering (turn on screen)
ppu_on_all();

View File

@ -142,8 +142,7 @@ const char TILESET[128*8*2] = {
#define BLANK 0
void clrscr() {
updptr = 0;
cendbuf();
vrambuf_clear();
ppu_off();
vram_adr(NAMETABLE_A);
vram_fill(BLANK, 32*28);
@ -165,7 +164,7 @@ void draw_bcd_word(byte col, byte row, word bcd) {
buf[j] = CHAR('0'+(bcd&0xf));
bcd >>= 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);
}

View File

@ -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<frames_per_move; i++) {
human_control(&players[0]);
cflushnow();
vrambuf_flush();
}
ai_control(&players[0]);
ai_control(&players[1]);
@ -224,12 +223,12 @@ void declare_winner(byte winner) {
clrscr();
for (i=0; i<ROWS/2-3; i++) {
draw_box(i,i,COLS-1-i,ROWS-1-i,BOX_CHARS);
cflushnow();
vrambuf_flush();
}
cputsxy(12,10,"WINNER:");
cputsxy(12,13,"PLAYER ");
cputcxy(12+7, 13, '1'+winner);
cflushnow();
vrambuf_flush();
delay(75);
gameover = 1;
}
@ -310,7 +309,7 @@ void play_game() {
void main() {
joy_install (joy_static_stddrv);
cendbuf();
vrambuf_clear();
set_vram_update(updbuf);
while (1) {
attract = 1;

View File

@ -7,34 +7,34 @@
byte updptr = 0;
// add EOF marker to buffer (but don't increment pointer)
void cendbuf(void) {
void vrambuf_end(void) {
VRAMBUF_SET(NT_UPD_EOF);
}
// clear vram buffer and place EOF marker
void cclearbuf(void) {
void vrambuf_clear(void) {
updptr = 0;
cendbuf();
vrambuf_end();
}
// wait for next frame, then clear buffer
// this assumes the NMI will call flush_vram_update()
void cflushnow(void) {
void vrambuf_flush(void) {
// make sure buffer has EOF marker
cendbuf();
vrambuf_end();
// wait for next frame to flush update buffer
// this will also set the scroll registers properly
ppu_wait_frame();
// clear the buffer
cclearbuf();
vrambuf_clear();
}
// add multiple characters to update buffer
// using horizontal increment
void putbytes(word addr, register const char* str, byte len) {
void vrambuf_put(word addr, register const char* str, byte len) {
// if bytes won't fit, wait for vsync and flush buffer
if (VBUFSIZE-4-len < updptr) {
cflushnow();
vrambuf_flush();
}
// add vram address
VRAMBUF_ADD((addr >> 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();
}

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -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();