diff --git a/src/drivers/Qt/ConsoleDebugger.cpp b/src/drivers/Qt/ConsoleDebugger.cpp index 28c6c9dc..43677586 100644 --- a/src/drivers/Qt/ConsoleDebugger.cpp +++ b/src/drivers/Qt/ConsoleDebugger.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "../../types.h" #include "../../fceu.h" @@ -32,6 +33,7 @@ #include "Qt/main.h" #include "Qt/dface.h" #include "Qt/config.h" +#include "Qt/nes_shm.h" #include "Qt/fceuWrapper.h" #include "Qt/ConsoleDebugger.h" @@ -52,6 +54,7 @@ ConsoleDebugger::ConsoleDebugger(QWidget *parent) QFrame *frame; QLabel *lbl; float fontCharWidth; + QTreeWidgetItem * item; font.setFamily("Courier New"); font.setStyle( QFont::StyleNormal ); @@ -191,6 +194,8 @@ ConsoleDebugger::ConsoleDebugger(QWidget *parent) stackFrame->setLayout( hbox ); stackText->setFont(font); stackText->setReadOnly(true); + stackText->setWordWrapMode( QTextOption::WordWrap ); + stackText->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); //stackText->setMaximumWidth( 16 * fontCharWidth ); bpFrame = new QGroupBox(tr("Breakpoints")); @@ -199,19 +204,40 @@ ConsoleDebugger::ConsoleDebugger(QWidget *parent) hbox = new QHBoxLayout(); bpTree = new QTreeWidget(); - bpTree->setColumnCount(1); + bpTree->setColumnCount(2); + + item = new QTreeWidgetItem(); + item->setFont( 0, font ); + item->setFont( 1, font ); + item->setFont( 2, font ); + item->setText( 0, QString::fromStdString( "Addr" ) ); + item->setText( 1, QString::fromStdString( "Flags" ) ); + item->setText( 2, QString::fromStdString( "Desc" ) ); + item->setTextAlignment( 0, Qt::AlignCenter); + item->setTextAlignment( 1, Qt::AlignCenter); + item->setTextAlignment( 2, Qt::AlignCenter); + + bpTree->setHeaderItem( item ); + + bpTree->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); + + connect( bpTree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), + this, SLOT(bpItemClicked( QTreeWidgetItem*, int)) ); hbox->addWidget( bpTree ); hbox = new QHBoxLayout(); button = new QPushButton( tr("Add") ); hbox->addWidget( button ); + connect( button, SIGNAL(clicked(void)), this, SLOT(add_BP_CB(void)) ); button = new QPushButton( tr("Delete") ); hbox->addWidget( button ); + connect( button, SIGNAL(clicked(void)), this, SLOT(delete_BP_CB(void)) ); button = new QPushButton( tr("Edit") ); hbox->addWidget( button ); + connect( button, SIGNAL(clicked(void)), this, SLOT(edit_BP_CB(void)) ); brkBadOpsCbox = new QCheckBox( tr("Break on Bad Opcodes") ); @@ -314,6 +340,8 @@ ConsoleDebugger::ConsoleDebugger(QWidget *parent) //connect( hbar, SIGNAL(valueChanged(int)), this, SLOT(hbarChanged(int)) ); connect( vbar, SIGNAL(valueChanged(int)), this, SLOT(vbarChanged(int)) ); + bpListUpdate( false ); + periodicTimer->start( 100 ); // 10hz } //---------------------------------------------------------------------------- @@ -338,6 +366,415 @@ void ConsoleDebugger::closeWindow(void) deleteLater(); } //---------------------------------------------------------------------------- +void ConsoleDebugger::bpItemClicked( QTreeWidgetItem *item, int column) +{ + int row = bpTree->indexOfTopLevelItem(item); + + printf("Row: %i Column: %i \n", row, column ); + +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp ) +{ + int ret; + QDialog dialog(this); + QHBoxLayout *hbox; + QVBoxLayout *mainLayout, *vbox; + QLabel *lbl; + QLineEdit *addr1, *addr2, *cond, *name; + QCheckBox *forbidChkBox, *rbp, *wbp, *xbp; + QGridLayout *grid; + QFrame *frame; + QGroupBox *gbox; + QPushButton *okButton, *cancelButton; + QRadioButton *cpu_radio, *ppu_radio, *sprite_radio; + + dialog.setWindowTitle( tr("Add Breakpoint") ); + + hbox = new QHBoxLayout(); + mainLayout = new QVBoxLayout(); + + mainLayout->addLayout( hbox ); + + lbl = new QLabel( tr("Address") ); + addr1 = new QLineEdit(); + + hbox->addWidget( lbl ); + hbox->addWidget( addr1 ); + + lbl = new QLabel( tr("-") ); + addr2 = new QLineEdit(); + hbox->addWidget( lbl ); + hbox->addWidget( addr2 ); + + forbidChkBox = new QCheckBox( tr("Forbid") ); + hbox->addWidget( forbidChkBox ); + + frame = new QFrame(); + vbox = new QVBoxLayout(); + hbox = new QHBoxLayout(); + gbox = new QGroupBox(); + + rbp = new QCheckBox( tr("Read") ); + wbp = new QCheckBox( tr("Write") ); + xbp = new QCheckBox( tr("Execute") ); + + gbox->setTitle( tr("Memory") ); + mainLayout->addWidget( frame ); + frame->setLayout( vbox ); + frame->setFrameShape( QFrame::Box ); + vbox->addLayout( hbox ); + vbox->addWidget( gbox ); + + hbox->addWidget( rbp ); + hbox->addWidget( wbp ); + hbox->addWidget( xbp ); + + hbox = new QHBoxLayout(); + cpu_radio = new QRadioButton( tr("CPU Mem") ); + ppu_radio = new QRadioButton( tr("PPU Mem") ); + sprite_radio = new QRadioButton( tr("Sprite Mem") ); + cpu_radio->setChecked(true); + + gbox->setLayout( hbox ); + hbox->addWidget( cpu_radio ); + hbox->addWidget( ppu_radio ); + hbox->addWidget( sprite_radio ); + + grid = new QGridLayout(); + + mainLayout->addLayout( grid ); + lbl = new QLabel( tr("Condition") ); + cond = new QLineEdit(); + + grid->addWidget( lbl, 0, 0 ); + grid->addWidget( cond, 0, 1 ); + + lbl = new QLabel( tr("Name") ); + name = new QLineEdit(); + + grid->addWidget( lbl, 1, 0 ); + grid->addWidget( name, 1, 1 ); + + hbox = new QHBoxLayout(); + okButton = new QPushButton( tr("OK") ); + cancelButton = new QPushButton( tr("Cancel") ); + + mainLayout->addLayout( hbox ); + hbox->addWidget( cancelButton ); + hbox->addWidget( okButton ); + + connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) ); + connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) ); + + if ( wp != NULL ) + { + char stmp[256]; + + if ( wp->flags & BT_P ) + { + ppu_radio->setChecked(true); + } + else if ( wp->flags & BT_S ) + { + sprite_radio->setChecked(true); + } + + sprintf( stmp, "%04X", wp->address ); + + addr1->setText( tr(stmp) ); + + if ( wp->endaddress > 0 ) + { + sprintf( stmp, "%04X", wp->endaddress ); + + addr2->setText( tr(stmp) ); + } + + if ( wp->flags & WP_R ) + { + rbp->setChecked(true); + } + if ( wp->flags & WP_W ) + { + wbp->setChecked(true); + } + if ( wp->flags & WP_X ) + { + xbp->setChecked(true); + } + if ( wp->flags & WP_F ) + { + forbidChkBox->setChecked(true); + } + if ( wp->flags & WP_E ) + { + //forbidChkBox->setChecked(true); + } + + if ( wp->condText ) + { + cond->setText( tr(wp->condText) ); + } + + if ( wp->desc ) + { + name->setText( tr(wp->desc) ); + } + } + + dialog.setLayout( mainLayout ); + + ret = dialog.exec(); + + if ( ret == QDialog::Accepted ) + { + int start_addr = -1, end_addr = -1, type = 0, enable = 1, slot; + std::string s; + printf("Accepted\n"); + + slot = (editIdx < 0) ? numWPs : editIdx; + + if ( cpu_radio->isChecked() ) + { + type |= BT_C; + } + else if ( ppu_radio->isChecked() ) + { + type |= BT_P; + } + else if ( sprite_radio->isChecked() ) + { + type |= BT_S; + } + + s = addr1->text().toStdString(); + + if ( s.size() > 0 ) + { + start_addr = offsetStringToInt( type, s.c_str() ); + } + + s = addr2->text().toStdString(); + + if ( s.size() > 0 ) + { + end_addr = offsetStringToInt( type, s.c_str() ); + } + + if ( rbp->isChecked() ) + { + type |= WP_R; + } + if ( wbp->isChecked() ) + { + type |= WP_W; + } + if ( xbp->isChecked() ) + { + type |= WP_X; + } + + if ( forbidChkBox->isChecked() ) + { + type |= WP_F; + } + + if ( (start_addr >= 0) && (numWPs < 64) ) + { + unsigned int retval; + std::string nameString, condString; + + nameString = name->text().toStdString(); + condString = cond->text().toStdString(); + + retval = NewBreak( nameString.c_str(), start_addr, end_addr, type, condString.c_str(), slot, enable); + + if ( (retval == 1) || (retval == 2) ) + { + printf("Breakpoint Add Failed\n"); + } + else + { + if (editIdx < 0) + { + numWPs++; + } + + bpListUpdate( false ); + } + } + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::bpListUpdate( bool reset ) +{ + QTreeWidgetItem *item; + char line[256], addrStr[32], flags[16], enable; + + if ( reset ) + { + bpTree->clear(); + } + + for (int i=0; itopLevelItem(i); + + if ( item == NULL ) + { + item = new QTreeWidgetItem(); + + bpTree->addTopLevelItem( item ); + } + + //item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable ); + item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren ); + + if ( watchpoint[i].endaddress > 0 ) + { + sprintf( addrStr, "$%04X-%04X:", watchpoint[i].address, watchpoint[i].endaddress ); + } + else + { + sprintf( addrStr, "$%04X:", watchpoint[i].address ); + } + + flags[0] = (watchpoint[i].flags & WP_E) ? 'E' : '-'; + + if ( watchpoint[i].flags & BT_P ) + { + flags[1] = 'P'; + } + else if ( watchpoint[i].flags & BT_S ) + { + flags[1] = 'S'; + } + else + { + flags[1] = 'C'; + } + + flags[2] = (watchpoint[i].flags & WP_R) ? 'R' : '-'; + flags[3] = (watchpoint[i].flags & WP_W) ? 'W' : '-'; + flags[4] = (watchpoint[i].flags & WP_X) ? 'X' : '-'; + flags[5] = (watchpoint[i].flags & WP_F) ? 'F' : '-'; + flags[6] = 0; + + enable = (watchpoint[i].flags & WP_E) ? 1 : 0; + + //strcpy( line, addrStr ); + //strcpy( line, flags ); + line[0] = 0; + + if (watchpoint[i].desc ) + { + strcat( line, watchpoint[i].desc); + } + + if (watchpoint[i].condText ) + { + strcat( line, " Condition:"); + strcat( line, watchpoint[i].condText); + strcat( line, " "); + } + + item->setCheckState( 0, enable ? Qt::Checked : Qt::Unchecked ); + + item->setFont( 0, font ); + item->setFont( 1, font ); + item->setFont( 2, font ); + + item->setText( 0, tr(addrStr)); + item->setText( 1, tr(flags) ); + item->setText( 2, tr(line) ); + + item->setTextAlignment( 0, Qt::AlignLeft); + item->setTextAlignment( 1, Qt::AlignLeft); + item->setTextAlignment( 2, Qt::AlignLeft); + } + + bpTree->update(); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::add_BP_CB(void) +{ + openBpEditWindow(-1); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::edit_BP_CB(void) +{ + QTreeWidgetItem *item; + + item = bpTree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + + int row = bpTree->indexOfTopLevelItem(item); + + openBpEditWindow( row, &watchpoint[row] ); +} +//---------------------------------------------------------------------------- +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--; +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::delete_BP_CB(void) +{ + QTreeWidgetItem *item; + + item = bpTree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + + int row = bpTree->indexOfTopLevelItem(item); + + DeleteBreak( row ); + bpListUpdate( true ); +} +//---------------------------------------------------------------------------- void ConsoleDebugger::debugRunCB(void) { if (FCEUI_EmulationPaused()) @@ -985,7 +1422,11 @@ void FCEUD_DebugBreakpoint( int addr ) { std::list ::iterator it; - printf("Breakpoint Hit: 0x%04X \n", addr ); + if ( !nes_shm->runEmulator ) + { + return; + } + //printf("Breakpoint Hit: 0x%04X \n", addr ); fceuWrapperUnLock(); @@ -994,7 +1435,7 @@ void FCEUD_DebugBreakpoint( int addr ) (*it)->breakPointNotify( addr ); } - while (FCEUI_EmulationPaused() && !FCEUI_EmulationFrameStepped()) + while ( nes_shm->runEmulator && FCEUI_EmulationPaused() && !FCEUI_EmulationFrameStepped()) { usleep(100000); } diff --git a/src/drivers/Qt/ConsoleDebugger.h b/src/drivers/Qt/ConsoleDebugger.h index ca1eeb15..d034cc25 100644 --- a/src/drivers/Qt/ConsoleDebugger.h +++ b/src/drivers/Qt/ConsoleDebugger.h @@ -18,12 +18,14 @@ #include #include #include +#include #include #include #include #include #include "Qt/main.h" +#include "../../debug.h" struct dbg_asm_entry_t { @@ -156,6 +158,8 @@ class ConsoleDebugger : public QDialog private: void setRegsFromEntry(void); + void openBpEditWindow(int editIdx = -1, watchpointinfo *wp = NULL ); + void bpListUpdate( bool reset = false ); public slots: void closeWindow(void); @@ -169,5 +173,9 @@ class ConsoleDebugger : public QDialog void debugRunLineCB(void); void debugRunLine128CB(void); void seekPCCB(void); + void add_BP_CB(void); + void edit_BP_CB(void); + void delete_BP_CB(void); + void bpItemClicked( QTreeWidgetItem *item, int column); }; diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index a220ce88..9e06722e 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -86,14 +86,14 @@ consoleWin_t::~consoleWin_t(void) closeGamePadConfWindow(); + //printf("Thread Finished: %i \n", gameThread->isFinished() ); + emulatorThread->quit(); + emulatorThread->wait( 1000 ); + fceuWrapperLock(); fceuWrapperClose(); fceuWrapperUnLock(); - //printf("Thread Finished: %i \n", gameThread->isFinished() ); - emulatorThread->quit(); - emulatorThread->wait(); - if ( viewport_GL != NULL ) { delete viewport_GL; viewport_GL = NULL; @@ -560,6 +560,9 @@ void consoleWin_t::closeApp(void) { nes_shm->runEmulator = 0; + emulatorThread->quit(); + emulatorThread->wait( 1000 ); + fceuWrapperLock(); fceuWrapperClose(); fceuWrapperUnLock(); @@ -568,7 +571,6 @@ void consoleWin_t::closeApp(void) // clear the NetworkIP field so this doesn't happen unintentionally g_config->setOption ("SDL.NetworkIP", ""); g_config->save (); - //SDL_Quit (); // Already called by fceuWrapperClose //qApp::quit(); qApp->quit();