1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-05-28 08:41:30 +00:00

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 - editing too fast refreshes kills the editor, also doesn't update palette
- crt0.s compiled each time? - crt0.s compiled each time?
- debug highlight doesn't go away when debugging -> running - 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) - replay doesn't work for nes (force background tile redraw)
- running profiler while replaying? grand unified replay? - running profiler while replaying? grand unified replay?
- click on profiler to step to position - click on profiler to step to position

View File

@ -293,10 +293,10 @@ void draw_floor_line(byte screen_y) {
else else
a = 0x00; a = 0x00;
memset(attrs, a, 8); memset(attrs, a, 8);
putbytes(nt2attraddr(addr), attrs, 8); vrambuf_put(nt2attraddr(addr), attrs, 8);
} }
// copy line to screen buffer // copy line to screen buffer
putbytes(addr, buf, COLS); vrambuf_put(addr, buf, COLS);
// create actors on this floor, if needed // create actors on this floor, if needed
// TODO: maybe this happens too early? // TODO: maybe this happens too early?
if (dy == 0 && (floor >= 2)) { if (dy == 0 && (floor >= 2)) {
@ -314,7 +314,7 @@ void draw_entire_stage() {
for (y=0; y<ROWS; y++) { for (y=0; y<ROWS; y++) {
draw_floor_line(y); draw_floor_line(y);
// allow buffer to flush, delaying a frame // allow buffer to flush, delaying a frame
cflushnow(); vrambuf_flush();
} }
} }
@ -671,11 +671,11 @@ void type_message(const char* charptr) {
x = 2; x = 2;
y++; y++;
} else { } else {
putbytes(getntaddr(x, y), &ch, 1); vrambuf_put(getntaddr(x, y), &ch, 1);
x++; x++;
} }
// flush buffer and wait a few frames // flush buffer and wait a few frames
cflushnow(); vrambuf_flush();
delay(5); delay(5);
} }
} }
@ -705,7 +705,7 @@ void play_scene() {
while (actors[0].floor != MAX_FLOORS-1) { while (actors[0].floor != MAX_FLOORS-1) {
//set_scroll_pixel_yy(scroll_pixel_yy+1); //set_scroll_pixel_yy(scroll_pixel_yy+1);
cflushnow(); vrambuf_flush();
refresh_sprites(); refresh_sprites();
move_player(); move_player();
// move all the actors // move all the actors
@ -747,7 +747,7 @@ void setup_graphics() {
pal_all(PALETTE); pal_all(PALETTE);
vram_adr(0x2000); vram_adr(0x2000);
vram_fill(CH_BLANK, 0x1000); vram_fill(CH_BLANK, 0x1000);
cendbuf(); vrambuf_clear();
set_vram_update(updbuf); set_vram_update(updbuf);
ppu_on_all(); ppu_on_all();
} }

View File

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

View File

@ -142,8 +142,7 @@ const char TILESET[128*8*2] = {
#define BLANK 0 #define BLANK 0
void clrscr() { void clrscr() {
updptr = 0; vrambuf_clear();
cendbuf();
ppu_off(); ppu_off();
vram_adr(NAMETABLE_A); vram_adr(NAMETABLE_A);
vram_fill(BLANK, 32*28); 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)); buf[j] = CHAR('0'+(bcd&0xf));
bcd >>= 4; bcd >>= 4;
} }
putbytes(NTADR_A(col, row), buf, 5); vrambuf_put(NTADR_A(col, row), buf, 5);
} }
// GAME CODE // GAME CODE
@ -309,7 +308,7 @@ void draw_row(byte row) {
} }
x += 3; x += 3;
} }
putbytes(NTADR_A(0, y), buf, sizeof(buf)); vrambuf_put(NTADR_A(0, y), buf, sizeof(buf));
} }
void draw_next_row() { void draw_next_row() {
@ -721,7 +720,7 @@ void play_round() {
if (!enemies_left) end_timer--; if (!enemies_left) end_timer--;
draw_next_row(); draw_next_row();
} }
cflushnow(); vrambuf_flush();
copy_sprites(); copy_sprites();
#ifdef DEBUG_FRAMERATE #ifdef DEBUG_FRAMERATE
putchar(t0 & 31, 27, CHAR(' ')); putchar(t0 & 31, 27, CHAR(' '));
@ -773,7 +772,7 @@ void setup_graphics() {
dest += 3*16; dest += 3*16;
} }
// activate vram buffer // activate vram buffer
cendbuf(); vrambuf_clear();
set_vram_update(updbuf); set_vram_update(updbuf);
} }

View File

@ -37,16 +37,15 @@ byte getchar(byte x, byte y) {
} }
void cputcxy(byte x, byte y, char ch) { 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) { 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() { void clrscr() {
updptr = 0; vrambuf_clear();
cendbuf();
ppu_off(); ppu_off();
vram_adr(0x2000); vram_adr(0x2000);
vram_fill(0, 32*28); vram_fill(0, 32*28);
@ -198,8 +197,8 @@ void flash_colliders() {
//cv_set_attenuation(CV_SOUNDCHANNEL_0, i/2); //cv_set_attenuation(CV_SOUNDCHANNEL_0, i/2);
if (players[0].collided) players[0].head_attr ^= 0x80; if (players[0].collided) players[0].head_attr ^= 0x80;
if (players[1].collided) players[1].head_attr ^= 0x80; if (players[1].collided) players[1].head_attr ^= 0x80;
cflushnow(); vrambuf_flush();
cflushnow(); vrambuf_flush();
draw_player(&players[0]); draw_player(&players[0]);
draw_player(&players[1]); draw_player(&players[1]);
} }
@ -210,7 +209,7 @@ void make_move() {
byte i; byte i;
for (i=0; i<frames_per_move; i++) { for (i=0; i<frames_per_move; i++) {
human_control(&players[0]); human_control(&players[0]);
cflushnow(); vrambuf_flush();
} }
ai_control(&players[0]); ai_control(&players[0]);
ai_control(&players[1]); ai_control(&players[1]);
@ -224,12 +223,12 @@ void declare_winner(byte winner) {
clrscr(); clrscr();
for (i=0; i<ROWS/2-3; i++) { for (i=0; i<ROWS/2-3; i++) {
draw_box(i,i,COLS-1-i,ROWS-1-i,BOX_CHARS); draw_box(i,i,COLS-1-i,ROWS-1-i,BOX_CHARS);
cflushnow(); vrambuf_flush();
} }
cputsxy(12,10,"WINNER:"); cputsxy(12,10,"WINNER:");
cputsxy(12,13,"PLAYER "); cputsxy(12,13,"PLAYER ");
cputcxy(12+7, 13, '1'+winner); cputcxy(12+7, 13, '1'+winner);
cflushnow(); vrambuf_flush();
delay(75); delay(75);
gameover = 1; gameover = 1;
} }
@ -310,7 +309,7 @@ void play_game() {
void main() { void main() {
joy_install (joy_static_stddrv); joy_install (joy_static_stddrv);
cendbuf(); vrambuf_clear();
set_vram_update(updbuf); set_vram_update(updbuf);
while (1) { while (1) {
attract = 1; attract = 1;

View File

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

View File

@ -24,17 +24,17 @@ extern byte updptr;
VRAMBUF_ADD(len); VRAMBUF_ADD(len);
// add EOF marker to buffer (but don't increment pointer) // add EOF marker to buffer (but don't increment pointer)
void cendbuf(void); void vrambuf_end(void);
// clear vram buffer and place EOF marker // clear vram buffer and place EOF marker
void cclearbuf(void); void vrambuf_clear(void);
// wait for next frame, then clear buffer // wait for next frame, then clear buffer
// this assumes the NMI will call flush_vram_update() // this assumes the NMI will call flush_vram_update()
void cflushnow(void); void vrambuf_flush(void);
// add multiple characters to update buffer // add multiple characters to update buffer
// using horizontal increment // 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 #endif // vrambuf.h

View File

@ -24,10 +24,10 @@ void scroll_demo() {
// write message to string array // write message to string array
sprintf(str, "%6x %6d", y, y); sprintf(str, "%6x %6d", y, y);
// write string array into VRAM buffer // 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 // wait for next frame
// and flush VRAM buffer // and flush VRAM buffer
cflushnow(); vrambuf_flush();
// set scroll (shadow) registers // set scroll (shadow) registers
scroll(x, y); scroll(x, y);
// update y variable // update y variable
@ -46,7 +46,7 @@ void main(void) {
pal_col(3,0x30); pal_col(3,0x30);
// clear vram buffer // clear vram buffer
cclearbuf(); vrambuf_clear();
// set NMI handler // set NMI handler
set_vram_update(updbuf); set_vram_update(updbuf);

View File

@ -289,7 +289,11 @@ export function getToolForFilename_6502(fn:string) : string {
// TODO: can merge w/ Z80? // TODO: can merge w/ Z80?
export abstract class Base6502Platform extends BaseDebugPlatform { 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; 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() { evalDebugCondition() {
if (this.debugCallback && !this.debugBreakState) { if (this.debugCallback && !this.debugBreakState) {
@ -297,18 +301,21 @@ export abstract class Base6502Platform extends BaseDebugPlatform {
} }
} }
postFrame() { postFrame() {
// save state every frame and rewind debug clocks if (this.debugCallback) {
var debugging = this.debugCallback && !this.debugBreakState; if (this.debugBreakState) {
if (debugging) { // reload debug state at end of frame after breakpoint
this.debugSavedState = this.saveState(); this.loadState(this.debugBreakState);
this.debugTargetClock -= this.debugClock; } else {
this.debugClock = 0; // save state every frame and rewind debug clocks
this.debugSavedState = this.saveState();
this.debugTargetClock -= this.debugClock;
this.debugClock = 0;
}
} }
} }
breakpointHit(targetClock : number) { breakpointHit(targetClock : number) {
this.debugTargetClock = targetClock; this.debugTargetClock = targetClock;
this.debugBreakState = this.saveState(); 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)); console.log("Breakpoint at clk", this.debugClock, "PC", this.debugBreakState.c.PC.toString(16));
this.pause(); this.pause();
if (this.onBreakpointHit) { if (this.onBreakpointHit) {
@ -319,7 +326,6 @@ export abstract class Base6502Platform extends BaseDebugPlatform {
this.setDebugCondition( () => { this.setDebugCondition( () => {
if (this.debugClock++ > this.debugTargetClock) { if (this.debugClock++ > this.debugTargetClock) {
var cpuState = this.getCPUState(); var cpuState = this.getCPUState();
cpuState.PC = (cpuState.PC + this.debugPCDelta) & 0xffff;
if (evalfunc(cpuState)) { if (evalfunc(cpuState)) {
this.breakpointHit(this.debugClock-1); this.breakpointHit(this.debugClock-1);
return true; return true;
@ -366,7 +372,7 @@ export abstract class Base6502Platform extends BaseDebugPlatform {
this.loadState(prevState); this.loadState(prevState);
this.breakpointHit(prevClock-1); this.breakpointHit(prevClock-1);
return true; 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) { if (this.getCPUState().T == 0) {
prevState = this.saveState(); prevState = this.saveState();
prevClock = this.debugClock; prevClock = this.debugClock;
@ -553,12 +559,18 @@ export abstract class BaseZ80Platform extends BaseDebugPlatform {
cpu.requestInterrupt(data); cpu.requestInterrupt(data);
} }
postFrame() { postFrame() {
if (this.debugCallback && !this.debugBreakState) { if (this.debugCallback) {
this.debugSavedState = this.saveState(); if (this.debugBreakState) {
if (this.debugTargetClock > 0) // if breakpoint, reload debug state after frame
this.debugTargetClock -= this.debugSavedState.c.T; this.loadState(this.debugBreakState);
this.debugSavedState.c.T = 0; } else {
this.loadState(this.debugSavedState); // 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) { breakpointHit(targetClock : number) {

View File

@ -251,7 +251,9 @@ const _Apple2Platform = function(mainElement) {
} }
loadState(state) { loadState(state) {
this.unfixPC(state.c);
cpu.loadState(state.c); cpu.loadState(state.c);
this.fixPC(state.c);
ram.mem.set(state.b); ram.mem.set(state.b);
kbdlatch = state.kbd; kbdlatch = state.kbd;
grswitch = state.gr; grswitch = state.gr;
@ -263,7 +265,7 @@ const _Apple2Platform = function(mainElement) {
} }
saveState() { saveState() {
return { return {
c:cpu.saveState(), c:this.getCPUState(),
b:ram.mem.slice(0), b:ram.mem.slice(0),
kbd:kbdlatch, kbd:kbdlatch,
gr:grswitch, gr:grswitch,
@ -279,7 +281,7 @@ const _Apple2Platform = function(mainElement) {
}; };
} }
getCPUState() { getCPUState() {
return cpu.saveState(); return this.fixPC(cpu.saveState());
} }
} }

View File

@ -240,7 +240,9 @@ class JSNESPlatform extends Base6502Platform implements Platform {
return s; return s;
} }
loadState(state) { loadState(state) {
this.unfixPC(state.cpu);
this.nes.fromJSON(state); this.nes.fromJSON(state);
this.fixPC(state.cpu);
//this.nes.cpu.fromJSON(state.cpu); //this.nes.cpu.fromJSON(state.cpu);
//this.nes.mmap.fromJSON(state.mmap); //this.nes.mmap.fromJSON(state.mmap);
//this.nes.ppu.fromJSON(state.ppu); //this.nes.ppu.fromJSON(state.ppu);
@ -266,6 +268,7 @@ class JSNESPlatform extends Base6502Platform implements Platform {
copy6502REGvars(c) { copy6502REGvars(c) {
c.T = 0; c.T = 0;
c.PC = c.REG_PC; c.PC = c.REG_PC;
this.fixPC(c);
c.A = c.REG_ACC; c.A = c.REG_ACC;
c.X = c.REG_X; c.X = c.REG_X;
c.Y = c.REG_Y; 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'); var script = document.createElement('script');
script.onload = onload; script.onload = onload;
script.onerror = onerror;
script.src = scriptfn; script.src = scriptfn;
document.getElementsByTagName('head')[0].appendChild(script); document.getElementsByTagName('head')[0].appendChild(script);
} }
@ -1442,6 +1443,8 @@ export function startUI(loadplatform : boolean) {
startPlatform(); startPlatform();
showWelcomeMessage(); showWelcomeMessage();
document.title = document.title + " [" + platform_id + "] - " + main_file_id; document.title = document.title + " [" + platform_id + "] - " + main_file_id;
}, () => {
alert('Platform "' + platform_id + '" not supported.');
}); });
} else { } else {
startPlatform(); startPlatform();

View File

@ -400,7 +400,7 @@ export class DisassemblerView implements ProjectView {
// TODO: too many globals // TODO: too many globals
refresh(moveCursor: boolean) { refresh(moveCursor: boolean) {
var state = lastDebugState || platform.saveState(); var state = lastDebugState || platform.saveState(); // TODO?
var pc = state.c ? state.c.PC : 0; var pc = state.c ? state.c.PC : 0;
var curline = 0; var curline = 0;
var selline = 0; var selline = 0;
@ -501,7 +501,7 @@ export class ListingView extends DisassemblerView implements ProjectView {
refresh(moveCursor: boolean) { refresh(moveCursor: boolean) {
this.refreshListing(); this.refreshListing();
if (!this.assemblyfile) return; // TODO? 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 pc = state.c ? (state.c.EPC || state.c.PC) : 0;
var asmtext = this.assemblyfile.text; var asmtext = this.assemblyfile.text;
var disasmview = this.getDisasmView(); var disasmview = this.getDisasmView();
@ -530,15 +530,6 @@ export class MemoryView implements ProjectView {
maindiv : HTMLElement; maindiv : HTMLElement;
static IGNORE_SYMS = {s__INITIALIZER:true, /* s__GSINIT:true, */ _color_prom:true}; static IGNORE_SYMS = {s__INITIALIZER:true, /* s__GSINIT:true, */ _color_prom:true};
recreateOnResize = 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) { createDiv(parent : HTMLElement) {
var div = document.createElement('div'); var div = document.createElement('div');
@ -1207,6 +1198,7 @@ export class AssetEditorView implements ProjectView, pixed.EditorContext {
} }
// TODO: recreate editors when refreshing // TODO: recreate editors when refreshing
// TODO: look for changes, not moveCursor
refresh(moveCursor : boolean) { refresh(moveCursor : boolean) {
if (moveCursor) { if (moveCursor) {
this.maindiv.empty(); this.maindiv.empty();

View File

@ -129,6 +129,8 @@ function testPlatform(platid, romname, maxframes, callback) {
maxframes = 120*60; maxframes = 120*60;
assert.equal(maxframes, rec.numFrames()); assert.equal(maxframes, rec.numFrames());
var state1 = platform.saveState(); var state1 = platform.saveState();
platform.loadState(state1);
assert.deepEqual(state1, platform.saveState());
assert.equal(1, rec.loadFrame(1)); assert.equal(1, rec.loadFrame(1));
assert.equal(maxframes, rec.loadFrame(maxframes)); assert.equal(maxframes, rec.loadFrame(maxframes));
var state2 = platform.saveState(); var state2 = platform.saveState();