From 9f009a18507232d8f9d8eddb3e3d75f6593db051 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Sun, 24 May 2020 21:10:10 -0400 Subject: [PATCH] Successful test of basic debugger functions. --- SConstruct | 2 +- src/debug.cpp | 4 +- src/drivers/sdl/debugger.cpp | 527 +++++++++++++++++++++++++++++++++-- src/ppu.cpp | 6 + 4 files changed, 512 insertions(+), 27 deletions(-) diff --git a/SConstruct b/SConstruct index 86133ba5..a5e5ec1c 100644 --- a/SConstruct +++ b/SConstruct @@ -186,7 +186,7 @@ else: if env['OPENGL'] and conf.CheckLibWithHeader('GL', 'GL/gl.h', 'c', autoadd=1): conf.env.Append(CCFLAGS = "-DOPENGL") - conf.env.Append(CPPDEFINES = ['PSS_STYLE=1']) + conf.env.Append(CPPDEFINES = ['PSS_STYLE=1',"FCEUDEF_DEBUGGER"]) env = conf.Finish() diff --git a/src/debug.cpp b/src/debug.cpp index 04a7cf73..c88e581a 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -565,9 +565,9 @@ void BreakHit(int bp_num) { FCEUI_SetEmulationPaused(EMULATIONPAUSED_PAUSED); //mbg merge 7/19/06 changed to use EmulationPaused() -#ifdef WIN32 +//#ifdef WIN32 FCEUD_DebugBreakpoint(bp_num); -#endif +//#endif } int StackAddrBackup; diff --git a/src/drivers/sdl/debugger.cpp b/src/drivers/sdl/debugger.cpp index 1bc7529e..d43e7c80 100644 --- a/src/drivers/sdl/debugger.cpp +++ b/src/drivers/sdl/debugger.cpp @@ -25,6 +25,7 @@ #include "../../fds.h" #include "../../cart.h" #include "../../ines.h" +#include "../../asm.h" #include "../../x6502.h" #include "../common/configSys.h" @@ -88,6 +89,8 @@ struct debuggerWin_t GtkWidget *sprite_radio_btn; int dialog_op; + int bpEditIdx; + char displayROMoffsets; debuggerWin_t(void) { @@ -128,6 +131,8 @@ struct debuggerWin_t P_Z_chkbox = NULL; P_C_chkbox = NULL; dialog_op = 0; + bpEditIdx = -1; + displayROMoffsets = 0; } ~debuggerWin_t(void) @@ -138,10 +143,55 @@ struct debuggerWin_t void bpListUpdate(void); void updateViewPort(void); void updateRegisterView(void); + void updateAssemblyView(void); + int get_bpList_selrow(void); }; static std::list debuggerWinList; +int debuggerWin_t::get_bpList_selrow(void) +{ + int retval = -1, numListRows; + GList *selListRows, *tmpList; + GtkTreeModel *model = NULL; + GtkTreeSelection *treeSel; + + treeSel = + gtk_tree_view_get_selection (GTK_TREE_VIEW(bp_tree) ); + + numListRows = gtk_tree_selection_count_selected_rows (treeSel); + + if (numListRows == 0) + { + return retval; + } + //printf("Number of Rows Selected: %i\n", numListRows ); + + selListRows = gtk_tree_selection_get_selected_rows (treeSel, &model); + + tmpList = selListRows; + + while (tmpList) + { + int depth; + int *indexArray; + GtkTreePath *path = (GtkTreePath *) tmpList->data; + + depth = gtk_tree_path_get_depth (path); + indexArray = gtk_tree_path_get_indices (path); + + if (depth > 0) + { + retval = indexArray[0]; + break; + } + tmpList = tmpList->next; + } + + g_list_free_full (selListRows, (GDestroyNotify) gtk_tree_path_free); + + return retval; +} void debuggerWin_t::bpListUpdate(void) { @@ -266,11 +316,191 @@ void debuggerWin_t::updateRegisterView(void) gtk_text_buffer_set_text( stackTextBuf, stackLine.c_str(), -1 ) ; + // update counters + int64 counter_value = timestampbase + (uint64)timestamp - total_cycles_base; + if (counter_value < 0) // sanity check + { + ResetDebugStatisticsCounters(); + counter_value = 0; + } + sprintf( stmp, "CPU Cycles: %llu", counter_value); + + gtk_label_set_text( GTK_LABEL(cpu_label1), stmp ); + + //SetDlgItemText(hDebug, IDC_DEBUGGER_VAL_CYCLES_COUNT, str); + counter_value = timestampbase + (uint64)timestamp - delta_cycles_base; + if (counter_value < 0) // sanity check + { + ResetDebugStatisticsCounters(); + counter_value = 0; + } + sprintf(stmp, "(+%llu)", counter_value); + + gtk_label_set_text( GTK_LABEL(cpu_label2), stmp ); + + sprintf(stmp, "Instructions: %llu", total_instructions); + gtk_label_set_text( GTK_LABEL(instr_label1), stmp ); + + sprintf(stmp, "(+%llu)", delta_instructions); + gtk_label_set_text( GTK_LABEL(instr_label2), stmp ); + +} + +// This function is for "smart" scrolling... +// it attempts to scroll up one line by a whole instruction +static int InstructionUp(int from) +{ + int i = std::min(16, from), j; + + while (i > 0) + { + j = i; + while (j > 0) + { + if (GetMem(from - j) == 0x00) + break; // BRK usually signifies data + if (opsize[GetMem(from - j)] == 0) + break; // invalid instruction! + if (opsize[GetMem(from - j)] > j) + break; // instruction is too long! + if (opsize[GetMem(from - j)] == j) + return (from - j); // instruction is just right! :D + j -= opsize[GetMem(from - j)]; + } + i--; + } + + // if we get here, no suitable instruction was found + if ((from >= 2) && (GetMem(from - 2) == 0x00)) + return (from - 2); // if a BRK instruction is possible, use that + if (from) + return (from - 1); // else, scroll up one byte + return 0; // of course, if we can't scroll up, just return 0! +} +static int InstructionDown(int from) +{ + int tmp = opsize[GetMem(from)]; + if ((tmp)) + return from + tmp; + else + return from + 1; // this is data or undefined instruction +} + +void debuggerWin_t::updateAssemblyView(void) +{ + int starting_address, start_address_lp, addr, size; + int instruction_addr; + std::string line, block; + char chr[64]; + uint8 opcode[3]; + const char *disassemblyText = NULL; + + start_address_lp = starting_address = X.PC; + + for (int i=0; i < 32; i++) + { + //printf("%i: Start Address: 0x%04X \n", i, start_address_lp ); + + starting_address = InstructionUp( start_address_lp ); + + if ( starting_address == start_address_lp ) + { + break; + } + start_address_lp = starting_address; + } + + addr = starting_address; + + for (int i=0; i < 64; i++) + { + line.clear(); + + // PC pointer + if (addr > 0xFFFF) break; + + instruction_addr = addr; + + if (addr == X.PC) + { + line.assign(">"); + } + else + { + line.assign(" "); + } + + if (addr >= 0x8000) + { + if (displayROMoffsets && (GetNesFileAddress(addr) != -1) ) + { + sprintf(chr, " %06X: ", GetNesFileAddress(addr)); + } + else + { + sprintf(chr, "%02X:%04X: ", getBank(addr), addr); + } + } else + { + sprintf(chr, " :%04X: ", addr); + } + line.append(chr); + + size = opsize[GetMem(addr)]; + if (size == 0) + { + sprintf(chr, "%02X UNDEFINED", GetMem(addr++)); + line.append(chr); + } else + { + if ((addr + size) > 0xFFFF) + { + while (addr < 0xFFFF) + { + sprintf(chr, "%02X OVERFLOW\n", GetMem(addr++)); + line.append(chr); + } + break; + } + for (int j = 0; j < size; j++) + { + sprintf(chr, "%02X ", opcode[j] = GetMem(addr++)); + line.append(chr); + } + while (size < 3) + { + line.append(" "); //pad output to align ASM + size++; + } + + disassemblyText = Disassemble(addr, opcode); + + if ( disassemblyText ) + { + line.append( disassemblyText ); + } + } + + // special case: an RTS opcode + if (GetMem(instruction_addr) == 0x60) + { + line.append("-------------------------"); + } + line.append("\n"); + + //printf("%s", line.c_str() ); + + block.append( line ); + } + + gtk_text_buffer_set_text( textbuf, block.c_str(), -1 ); + } void debuggerWin_t::updateViewPort(void) { updateRegisterView(); + updateAssemblyView(); } @@ -286,24 +516,13 @@ static void handleDialogResponse (GtkWidget * w, gint response_id, debuggerWin_t switch ( dw->dialog_op ) { case 1: // Breakpoint Add + case 2: // Breakpoint Edit { - int start_addr = -1, end_addr = -1, type = 0, enable = 1;; + int start_addr = -1, end_addr = -1, type = 0, enable = 1, slot; const char *name; const char *cond; - txt = gtk_entry_get_text( GTK_ENTRY( dw->bp_start_entry ) ); - - if ( txt[0] != 0 ) - { - start_addr = strtol( txt, NULL, 16 ); - } - - txt = gtk_entry_get_text( GTK_ENTRY( dw->bp_end_entry ) ); - - if ( txt[0] != 0 ) - { - end_addr = strtol( txt, NULL, 16 ); - } + slot = (dw->dialog_op == 1) ? numWPs : dw->bpEditIdx; if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( dw->cpu_radio_btn ) ) ) { @@ -318,6 +537,20 @@ static void handleDialogResponse (GtkWidget * w, gint response_id, debuggerWin_t type |= BT_S; } + txt = gtk_entry_get_text( GTK_ENTRY( dw->bp_start_entry ) ); + + if ( txt[0] != 0 ) + { + start_addr = offsetStringToInt( type, txt ); + } + + txt = gtk_entry_get_text( GTK_ENTRY( dw->bp_end_entry ) ); + + if ( txt[0] != 0 ) + { + end_addr = offsetStringToInt( type, txt ); + } + if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( dw->bp_read_chkbox ) ) ) { type |= WP_R; @@ -348,7 +581,7 @@ static void handleDialogResponse (GtkWidget * w, gint response_id, debuggerWin_t { unsigned int retval; - retval = NewBreak( name, start_addr, end_addr, type, cond, numWPs, enable); + retval = NewBreak( name, start_addr, end_addr, type, cond, slot, enable); if ( (retval == 1) || (retval == 2) ) { @@ -356,7 +589,10 @@ static void handleDialogResponse (GtkWidget * w, gint response_id, debuggerWin_t } else { - numWPs++; + if (dw->dialog_op == 1) + { + numWPs++; + } dw->bpListUpdate(); } @@ -430,6 +666,8 @@ static void create_breakpoint_dialog( int index, debuggerWin_t * dw ) (GTK_DIALOG_DESTROY_WITH_PARENT), "_Cancel", GTK_RESPONSE_CANCEL, "_Edit", GTK_RESPONSE_OK, NULL); + + dw->bpEditIdx = index; } gtk_dialog_set_default_response( GTK_DIALOG(win), GTK_RESPONSE_OK ); @@ -529,6 +767,25 @@ static void create_breakpoint_dialog( int index, debuggerWin_t * dw ) g_signal_connect (win, "response", G_CALLBACK (handleDialogResponse), dw); + if ( index >= 0 ) + { + if ( watchpoint[index].flags & BT_P ) + { + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( dw->ppu_radio_btn ), TRUE ); + } + else if ( watchpoint[index].flags & BT_S ) + { + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( dw->sprite_radio_btn ), TRUE ); + } + else + { + gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( dw->cpu_radio_btn ), TRUE ); + } + } + else + { + + } } static void addBreakpointCB (GtkButton * button, debuggerWin_t * dw) @@ -538,17 +795,214 @@ static void addBreakpointCB (GtkButton * button, debuggerWin_t * dw) create_breakpoint_dialog( -1, dw ); } +static void editBreakpointCB (GtkButton * button, debuggerWin_t * dw) +{ + int selRow; + + selRow = dw->get_bpList_selrow(); + + if ( selRow >= 0 ) + { + dw->dialog_op = 2; + + create_breakpoint_dialog( selRow, dw ); + } +} + +static void DeleteBreak(int sel) +{ + if(sel<0) return; + if(sel>=numWPs) return; + if (watchpoint[sel].cond) + { + freeTree(watchpoint[sel].cond); + } + if (watchpoint[sel].condText) + { + free(watchpoint[sel].condText); + } + if (watchpoint[sel].desc) + { + free(watchpoint[sel].desc); + } + // move all BP items up in the list + for (int i = sel; i < numWPs; i++) + { + watchpoint[i].address = watchpoint[i+1].address; + watchpoint[i].endaddress = watchpoint[i+1].endaddress; + watchpoint[i].flags = watchpoint[i+1].flags; +// ################################## Start of SP CODE ########################### + watchpoint[i].cond = watchpoint[i+1].cond; + watchpoint[i].condText = watchpoint[i+1].condText; + watchpoint[i].desc = watchpoint[i+1].desc; +// ################################## End of SP CODE ########################### + } + // erase last BP item + watchpoint[numWPs].address = 0; + watchpoint[numWPs].endaddress = 0; + watchpoint[numWPs].flags = 0; + watchpoint[numWPs].cond = 0; + watchpoint[numWPs].condText = 0; + watchpoint[numWPs].desc = 0; + numWPs--; +} + +static void deleteBreakpointCB (GtkButton * button, debuggerWin_t * dw) +{ + int selRow; + + selRow = dw->get_bpList_selrow(); + + if ( (selRow >= 0) && (selRow < numWPs) ) + { + DeleteBreak( selRow ); + dw->bpListUpdate(); + } +} + +static void debugRunCB (GtkButton * button, debuggerWin_t * dw) +{ + if (FCEUI_EmulationPaused()) + { + //UpdateRegs(hwndDlg); + FCEUI_ToggleEmulationPause(); + //DebuggerWasUpdated = false done in above function; + } +} + +static void debugStepIntoCB (GtkButton * button, debuggerWin_t * dw) +{ + FCEUI_Debugger().step = true; + FCEUI_SetEmulationPaused(0); +} + +static void debugStepOutCB (GtkButton * button, debuggerWin_t * dw) +{ + if (FCEUI_EmulationPaused() > 0) + { + DebuggerState &dbgstate = FCEUI_Debugger(); + //UpdateRegs(hwndDlg); + if (dbgstate.stepout) + { + printf("Step Out is currently in process.\n"); + return; + } + if (GetMem(X.PC) == 0x20) + { + dbgstate.jsrcount = 1; + } + else + { + dbgstate.jsrcount = 0; + } + dbgstate.stepout = 1; + FCEUI_SetEmulationPaused(0); + } +} + +static void debugStepOverCB (GtkButton * button, debuggerWin_t * dw) +{ + if (FCEUI_EmulationPaused()) + { + //UpdateRegs(hwndDlg); + int tmp=X.PC; + uint8 opcode = GetMem(X.PC); + bool jsr = opcode==0x20; + bool call = jsr; + #ifdef BRK_3BYTE_HACK + //with this hack, treat BRK similar to JSR + if(opcode == 0x00) + { + call = true; + } + #endif + if (call) + { + if (watchpoint[64].flags) + { + printf("Step Over is currently in process.\n"); + return; + } + watchpoint[64].address = (tmp+3); + watchpoint[64].flags = WP_E|WP_X; + } + else + { + FCEUI_Debugger().step = true; + } + FCEUI_SetEmulationPaused(0); + } +} +static void debugRunLineCB (GtkButton * button, debuggerWin_t * dw) +{ + uint64 ts=timestampbase; + ts+=timestamp; + ts+=341/3; + //if (scanline == 240) vblankScanLines++; + //else vblankScanLines = 0; + FCEUI_Debugger().runline = true; + FCEUI_Debugger().runline_end_time=ts; + FCEUI_SetEmulationPaused(0); +} + +static void debugRunLine128CB (GtkButton * button, debuggerWin_t * dw) +{ + //if (FCEUI_EmulationPaused()) + //{ + // UpdateRegs(hwndDlg); + //} + FCEUI_Debugger().runline = true; + { + uint64 ts=timestampbase; + ts+=timestamp; + ts+=128*341/3; + FCEUI_Debugger().runline_end_time=ts; + //if (scanline+128 >= 240 && scanline+128 <= 257) vblankScanLines = (scanline+128)-240; + //else vblankScanLines = 0; + } + FCEUI_SetEmulationPaused(0); +} +static void romOffsetToggleCB( GtkToggleButton *togglebutton, debuggerWin_t * dw) +{ + dw->displayROMoffsets = gtk_toggle_button_get_active( togglebutton ); + + dw->updateViewPort(); + + //printf("Toggle ROM Offset: %i \n", dw->displayROMoffsets ); +} + +static void updateAllDebugWindows(void) +{ + std::list < debuggerWin_t * >::iterator it; + + for (it = debuggerWinList.begin (); it != debuggerWinList.end (); it++) + { + (*it)->updateViewPort(); + } +} + +static void winDebuggerLoopStep(void) +{ + FCEUD_UpdateInput(); + + while(gtk_events_pending()) + { + gtk_main_iteration_do(FALSE); + } +} + //this code enters the debugger when a breakpoint was hit void FCEUD_DebugBreakpoint(int bp_num) { + //printf("Breakpoint Hit: %i \n", bp_num); + + updateAllDebugWindows(); - //std::list < debuggerWin_t * >::iterator it; - - //for (it = debuggerWinList.begin (); it != debuggerWinList.end (); it++) - //{ - - //} - + while(FCEUI_EmulationPaused() && !FCEUI_EmulationFrameStepped()) + { + usleep(50000); + winDebuggerLoopStep(); + } } static void closeDebuggerWindow (GtkWidget * w, GdkEvent * e, debuggerWin_t * dw) @@ -559,7 +1013,6 @@ static void closeDebuggerWindow (GtkWidget * w, GdkEvent * e, debuggerWin_t * dw { if (dw == *it) { - //printf("Removing MemView Window %p from List\n", cw); debuggerWinList.erase (it); break; } @@ -660,28 +1113,46 @@ void openDebuggerWindow (void) // Row 1 button = gtk_button_new_with_label ("Run"); + g_signal_connect (button, "clicked", + G_CALLBACK (debugRunCB), (gpointer) dw); + gtk_grid_attach( GTK_GRID(grid), button, 0, 0, 1, 1 ); button = gtk_button_new_with_label ("Step Into"); + g_signal_connect (button, "clicked", + G_CALLBACK (debugStepIntoCB), (gpointer) dw); + gtk_grid_attach( GTK_GRID(grid), button, 1, 0, 1, 1 ); // Row 2 button = gtk_button_new_with_label ("Step Out"); + g_signal_connect (button, "clicked", + G_CALLBACK (debugStepOutCB), (gpointer) dw); + gtk_grid_attach( GTK_GRID(grid), button, 0, 1, 1, 1 ); button = gtk_button_new_with_label ("Step Over"); + g_signal_connect (button, "clicked", + G_CALLBACK (debugStepOverCB), (gpointer) dw); + gtk_grid_attach( GTK_GRID(grid), button, 1, 1, 1, 1 ); // Row 3 button = gtk_button_new_with_label ("Run Line"); + g_signal_connect (button, "clicked", + G_CALLBACK (debugRunLineCB), (gpointer) dw); + gtk_grid_attach( GTK_GRID(grid), button, 0, 2, 1, 1 ); button = gtk_button_new_with_label ("128 Lines"); + g_signal_connect (button, "clicked", + G_CALLBACK (debugRunLine128CB), (gpointer) dw); + gtk_grid_attach( GTK_GRID(grid), button, 1, 2, 1, 1 ); // Row 4 @@ -838,10 +1309,16 @@ void openDebuggerWindow (void) button = gtk_button_new_with_label ("Delete"); + g_signal_connect (button, "clicked", + G_CALLBACK (deleteBreakpointCB), (gpointer) dw); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 2); button = gtk_button_new_with_label ("Edit"); + g_signal_connect (button, "clicked", + G_CALLBACK (editBreakpointCB), (gpointer) dw); + gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 2); button = gtk_check_button_new_with_label("Break on Bad Opcodes"); @@ -1004,6 +1481,8 @@ void openDebuggerWindow (void) button = gtk_button_new_with_label ("Reset Counters"); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2); button = gtk_check_button_new_with_label("ROM Offsets"); + g_signal_connect (button, "toggled", + G_CALLBACK (romOffsetToggleCB), dw); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2); button = gtk_check_button_new_with_label("Symbolic Debug"); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 2); diff --git a/src/ppu.cpp b/src/ppu.cpp index 56466383..ca2ee0e7 100644 --- a/src/ppu.cpp +++ b/src/ppu.cpp @@ -1375,7 +1375,9 @@ static void DoLine(void) { GameHBIRQHook(); } +#ifdef WIN32 DEBUG(FCEUD_UpdateNTView(scanline, 0)); +#endif if (SpriteON) RefreshSprites(); @@ -1852,7 +1854,9 @@ int FCEUPPU_Loop(int skip) { for (scanline = 0; scanline < totalscanlines; ) { //scanline is incremented in DoLine. Evil. :/ deempcnt[deemp]++; if (scanline < 240) +#ifdef WIN32 DEBUG(FCEUD_UpdatePPUView(scanline, 1)); +#endif DoLine(); if (scanline < normalscanlines || scanline == totalscanlines) @@ -2145,8 +2149,10 @@ int FCEUX_PPU_Loop(int skip) { ppuphase = PPUPHASE_BG; if (sl != 0 && sl < 241) { // ignore the invisible +#ifdef WIN32 DEBUG(FCEUD_UpdatePPUView(scanline = yp, 1)); DEBUG(FCEUD_UpdateNTView(scanline = yp, 1)); +#endif } //hack to fix SDF ship intro screen with split. is it right?