// ConsoleDebugger.cpp // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../types.h" #include "../../fceu.h" #include "../../cheat.h" #include "../../debug.h" #include "../../driver.h" #include "../../version.h" #include "../../video.h" #include "../../movie.h" #include "../../palette.h" #include "../../fds.h" #include "../../cart.h" #include "../../ines.h" #include "../../asm.h" #include "../../ppu.h" #include "../../x6502.h" #include "common/configSys.h" #include "Qt/main.h" #include "Qt/dface.h" #include "Qt/config.h" #include "Qt/nes_shm.h" #include "Qt/fceuWrapper.h" #include "Qt/HexEditor.h" #include "Qt/ConsoleDebugger.h" #include "Qt/ConsoleUtilities.h" // Where are these defined? extern int vblankScanLines; extern int vblankPixel; debuggerBookmarkManager_t dbgBmMgr; static std::list dbgWinList; static void DeleteBreak(int sel); //---------------------------------------------------------------------------- ConsoleDebugger::ConsoleDebugger(QWidget *parent) : QDialog( parent, Qt::Window ) { QHBoxLayout *mainLayout; QVBoxLayout *vbox, *vbox1, *vbox2, *vbox3, *vbox4; QHBoxLayout *hbox, *hbox1, *hbox2, *hbox3; QGridLayout *grid; QPushButton *button; QFrame *frame; QLabel *lbl; QMenuBar *menuBar; QMenu *debugMenu; QAction *act; float fontCharWidth; QTreeWidgetItem * item; int opt, useNativeMenuBar; font.setFamily("Courier New"); font.setStyle( QFont::StyleNormal ); font.setStyleHint( QFont::Monospace ); QFontMetrics fm(font); fontCharWidth = 1.00 * fm.averageCharWidth(); setWindowTitle("6502 Debugger"); //resize( 512, 512 ); menuBar = new QMenuBar(this); // This is needed for menu bar to show up on MacOS g_config->getOption( "SDL.UseNativeMenuBar", &useNativeMenuBar ); menuBar->setNativeMenuBar( useNativeMenuBar ? true : false ); //----------------------------------------------------------------------- // Menu Start //----------------------------------------------------------------------- // Debug debugMenu = menuBar->addMenu(tr("Debug")); // Debug -> Run act = new QAction(tr("Run"), this); act->setShortcut(QKeySequence( tr("F5") ) ); act->setStatusTip(tr("Run")); connect( act, SIGNAL(triggered()), this, SLOT(debugRunCB(void)) ); debugMenu->addAction(act); // Debug -> Step Into act = new QAction(tr("Step Into"), this); act->setShortcut(QKeySequence( tr("F11") ) ); act->setStatusTip(tr("Step Into")); connect( act, SIGNAL(triggered()), this, SLOT(debugStepIntoCB(void)) ); debugMenu->addAction(act); // Debug -> Step Out act = new QAction(tr("Step Out"), this); act->setShortcut(QKeySequence( tr("Shift+F11") ) ); act->setStatusTip(tr("Step Out")); connect( act, SIGNAL(triggered()), this, SLOT(debugStepOutCB(void)) ); debugMenu->addAction(act); // Debug -> Step Over act = new QAction(tr("Step Over"), this); act->setShortcut(QKeySequence( tr("F10") ) ); act->setStatusTip(tr("Step Over")); connect( act, SIGNAL(triggered()), this, SLOT(debugStepOverCB(void)) ); debugMenu->addAction(act); // Debug -> Run Line act = new QAction(tr("Run Line"), this); act->setShortcut(QKeySequence( tr("F6") ) ); act->setStatusTip(tr("Run Line")); connect( act, SIGNAL(triggered()), this, SLOT(debugRunLineCB(void)) ); debugMenu->addAction(act); // Debug -> Run 128 Lines act = new QAction(tr("Run 128 Lines"), this); act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Run 128 Lines")); connect( act, SIGNAL(triggered()), this, SLOT(debugRunLine128CB(void)) ); debugMenu->addAction(act); //----------------------------------------------------------------------- // Menu End //----------------------------------------------------------------------- mainLayout = new QHBoxLayout(); mainLayout->setMenuBar( menuBar ); vbox4 = new QVBoxLayout(); grid = new QGridLayout(); asmView = new QAsmView(this); vbar = new QScrollBar( Qt::Vertical, this ); hbar = new QScrollBar( Qt::Horizontal, this ); asmLineSelLbl = new QLabel( tr("Line Select") ); emuStatLbl = new QLabel( tr("Emulator is Running") ); asmLineSelLbl->setWordWrap( true ); asmView->setScrollBars( hbar, vbar ); grid->addWidget( asmView, 0, 0 ); grid->addWidget( vbar , 0, 1 ); grid->addWidget( hbar , 1, 0 ); vbox1 = new QVBoxLayout(); vbox2 = new QVBoxLayout(); hbox1 = new QHBoxLayout(); hbar->setMinimum(0); hbar->setMaximum(100); vbar->setMinimum(0); vbar->setMaximum( 0x10000 ); vbox4->addLayout( grid, 100 ); vbox4->addWidget( asmLineSelLbl, 1 ); vbox4->addWidget( emuStatLbl , 1 ); //asmText->setFont(font); //asmText->setReadOnly(true); //asmText->setOverwriteMode(true); //asmText->setMinimumWidth( 20 * fontCharWidth ); //asmText->setLineWrapMode( QPlainTextEdit::NoWrap ); mainLayout->addLayout( vbox4, 10 ); mainLayout->addLayout( vbox1, 1 ); grid = new QGridLayout(); vbox1->addLayout( hbox1 ); hbox1->addLayout( vbox2 ); vbox2->addLayout( grid ); button = new QPushButton( tr("Run") ); grid->addWidget( button, 0, 0, Qt::AlignLeft ); connect( button, SIGNAL(clicked(void)), this, SLOT(debugRunCB(void)) ); button = new QPushButton( tr("Step Into") ); grid->addWidget( button, 0, 1, Qt::AlignLeft ); connect( button, SIGNAL(clicked(void)), this, SLOT(debugStepIntoCB(void)) ); button = new QPushButton( tr("Step Out") ); grid->addWidget( button, 1, 0, Qt::AlignLeft ); connect( button, SIGNAL(clicked(void)), this, SLOT(debugStepOutCB(void)) ); button = new QPushButton( tr("Step Over") ); grid->addWidget( button, 1, 1, Qt::AlignLeft ); connect( button, SIGNAL(clicked(void)), this, SLOT(debugStepOverCB(void)) ); button = new QPushButton( tr("Run Line") ); grid->addWidget( button, 2, 0, Qt::AlignLeft ); connect( button, SIGNAL(clicked(void)), this, SLOT(debugRunLineCB(void)) ); button = new QPushButton( tr("128 Lines") ); grid->addWidget( button, 2, 1, Qt::AlignLeft ); connect( button, SIGNAL(clicked(void)), this, SLOT(debugRunLine128CB(void)) ); button = new QPushButton( tr("Seek To:") ); grid->addWidget( button, 3, 0, Qt::AlignLeft ); connect( button, SIGNAL(clicked(void)), this, SLOT(seekToCB(void)) ); seekEntry = new QLineEdit(); seekEntry->setFont( font ); seekEntry->setText("0000"); seekEntry->setMaxLength( 4 ); seekEntry->setInputMask( ">HHHH;" ); seekEntry->setAlignment(Qt::AlignCenter); seekEntry->setMaximumWidth( 6 * fontCharWidth ); grid->addWidget( seekEntry, 3, 1, Qt::AlignLeft ); hbox = new QHBoxLayout(); lbl = new QLabel( tr("PC:") ); pcEntry = new QLineEdit(); pcEntry->setFont( font ); pcEntry->setMaxLength( 4 ); pcEntry->setInputMask( ">HHHH;" ); pcEntry->setAlignment(Qt::AlignCenter); pcEntry->setMaximumWidth( 6 * fontCharWidth ); hbox->addWidget( lbl ); hbox->addWidget( pcEntry, 1, Qt::AlignLeft ); grid->addLayout( hbox, 4, 0, Qt::AlignLeft ); button = new QPushButton( tr("Seek PC") ); grid->addWidget( button, 4, 1, Qt::AlignLeft ); connect( button, SIGNAL(clicked(void)), this, SLOT(seekPCCB(void)) ); hbox = new QHBoxLayout(); lbl = new QLabel( tr("A:") ); regAEntry = new QLineEdit(); regAEntry->setFont( font ); regAEntry->setMaxLength( 2 ); regAEntry->setInputMask( ">HH;" ); regAEntry->setAlignment(Qt::AlignCenter); regAEntry->setMaximumWidth( 4 * fontCharWidth ); hbox->addWidget( lbl ); hbox->addWidget( regAEntry, 1, Qt::AlignLeft ); lbl = new QLabel( tr("X:") ); regXEntry = new QLineEdit(); regXEntry->setFont( font ); regXEntry->setMaxLength( 2 ); regXEntry->setInputMask( ">HH;" ); regXEntry->setAlignment(Qt::AlignCenter); regXEntry->setMaximumWidth( 4 * fontCharWidth ); hbox->addWidget( lbl ); hbox->addWidget( regXEntry, 1, Qt::AlignLeft ); lbl = new QLabel( tr("Y:") ); regYEntry = new QLineEdit(); regYEntry->setFont( font ); regYEntry->setMaxLength( 2 ); regYEntry->setInputMask( ">HH;" ); regYEntry->setAlignment(Qt::AlignCenter); regYEntry->setMaximumWidth( 4 * fontCharWidth ); hbox->addWidget( lbl ); hbox->addWidget( regYEntry, 1, Qt::AlignLeft ); vbox2->addLayout( hbox ); stackFrame = new QGroupBox(tr("Stack $0100")); stackText = new DebuggerStackDisplay(this); hbox = new QHBoxLayout(); hbox->addWidget( stackText ); vbox2->addWidget( stackFrame ); stackFrame->setLayout( hbox ); stackText->setFont(font); stackText->setReadOnly(true); stackText->setWordWrapMode( QTextOption::NoWrap ); //stackText->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); //stackText->setMaximumWidth( 16 * fontCharWidth ); bpFrame = new QGroupBox(tr("Breakpoints")); vbox3 = new QVBoxLayout(); vbox = new QVBoxLayout(); hbox = new QHBoxLayout(); bpTree = new QTreeWidget(); bpTree->setColumnCount(2); bpTree->setSelectionMode( QAbstractItemView::SingleSelection ); item = new QTreeWidgetItem(); item->setFont( 0, font ); item->setFont( 1, font ); item->setFont( 2, font ); item->setFont( 3, font ); item->setText( 0, QString::fromStdString( "Addr" ) ); item->setText( 1, QString::fromStdString( "Flags" ) ); item->setText( 2, QString::fromStdString( "Cond" ) ); item->setText( 3, QString::fromStdString( "Desc" ) ); item->setTextAlignment( 0, Qt::AlignCenter); item->setTextAlignment( 1, Qt::AlignCenter); item->setTextAlignment( 2, Qt::AlignCenter); item->setTextAlignment( 3, 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") ); brkBadOpsCbox->setChecked( FCEUI_Debugger().badopbreak ); connect( brkBadOpsCbox, SIGNAL(stateChanged(int)), this, SLOT(breakOnBadOpcodeCB(int)) ); vbox->addWidget( bpTree ); vbox->addLayout( hbox ); vbox->addWidget( brkBadOpsCbox ); bpFrame->setLayout( vbox ); sfFrame = new QGroupBox(tr("Status Flags")); grid = new QGridLayout(); sfFrame->setLayout( grid ); N_chkbox = new QCheckBox( tr("N") ); V_chkbox = new QCheckBox( tr("V") ); U_chkbox = new QCheckBox( tr("U") ); B_chkbox = new QCheckBox( tr("B") ); D_chkbox = new QCheckBox( tr("D") ); I_chkbox = new QCheckBox( tr("I") ); Z_chkbox = new QCheckBox( tr("Z") ); C_chkbox = new QCheckBox( tr("C") ); grid->addWidget( N_chkbox, 0, 0, Qt::AlignCenter ); grid->addWidget( V_chkbox, 0, 1, Qt::AlignCenter ); grid->addWidget( U_chkbox, 0, 2, Qt::AlignCenter ); grid->addWidget( B_chkbox, 0, 3, Qt::AlignCenter ); grid->addWidget( D_chkbox, 1, 0, Qt::AlignCenter ); grid->addWidget( I_chkbox, 1, 1, Qt::AlignCenter ); grid->addWidget( Z_chkbox, 1, 2, Qt::AlignCenter ); grid->addWidget( C_chkbox, 1, 3, Qt::AlignCenter ); vbox3->addWidget( bpFrame); vbox3->addWidget( sfFrame); hbox1->addLayout( vbox3 ); hbox2 = new QHBoxLayout(); vbox = new QVBoxLayout(); frame = new QFrame(); ppuLbl = new QLabel( tr("PPU:") ); spriteLbl = new QLabel( tr("Sprite:") ); scanLineLbl = new QLabel( tr("Scanline:") ); pixLbl = new QLabel( tr("Pixel:") ); vbox->addWidget( ppuLbl ); vbox->addWidget( spriteLbl ); vbox->addWidget( scanLineLbl ); vbox->addWidget( pixLbl ); vbox1->addLayout( hbox2 ); hbox2->addWidget( frame ); frame->setLayout( vbox ); frame->setFrameShape( QFrame::Box ); vbox = new QVBoxLayout(); cpuCyclesLbl1 = new QLabel( tr("CPU Cycles:") ); cpuCyclesLbl2 = new QLabel( tr("(+0):") ); cpuInstrsLbl1 = new QLabel( tr("Instructions:") ); cpuInstrsLbl2 = new QLabel( tr("(+0):") ); brkCpuCycExd = new QCheckBox( tr("Break when Exceed") ); brkInstrsExd = new QCheckBox( tr("Break when Exceed") ); cpuCycExdVal = new QLineEdit( tr("0") ); instrExdVal = new QLineEdit( tr("0") ); hbox = new QHBoxLayout(); vbox->addLayout( hbox ); hbox->addWidget( cpuCyclesLbl1 ); hbox->addWidget( cpuCyclesLbl2 ); hbox = new QHBoxLayout(); vbox->addLayout( hbox ); hbox->addWidget( brkCpuCycExd ); hbox->addWidget( cpuCycExdVal, 1, Qt::AlignLeft ); hbox = new QHBoxLayout(); vbox->addLayout( hbox ); hbox->addWidget( cpuInstrsLbl1 ); hbox->addWidget( cpuInstrsLbl2 ); hbox = new QHBoxLayout(); vbox->addLayout( hbox ); hbox->addWidget( brkInstrsExd ); hbox->addWidget( instrExdVal, 1, Qt::AlignLeft ); hbox2->addLayout( vbox ); cpuCycExdVal->setFont( font ); cpuCycExdVal->setMaxLength( 16 ); cpuCycExdVal->setInputMask( ">9000000000000000;" ); cpuCycExdVal->setAlignment(Qt::AlignLeft); cpuCycExdVal->setMaximumWidth( 18 * fontCharWidth ); cpuCycExdVal->setCursorPosition(0); connect( cpuCycExdVal, SIGNAL(textEdited(const QString &)), this, SLOT(cpuCycleThresChanged(const QString &))); instrExdVal->setFont( font ); instrExdVal->setMaxLength( 16 ); instrExdVal->setInputMask( ">9000000000000000;" ); instrExdVal->setAlignment(Qt::AlignLeft); instrExdVal->setMaximumWidth( 18 * fontCharWidth ); instrExdVal->setCursorPosition(0); connect( instrExdVal, SIGNAL(textEdited(const QString &)), this, SLOT(instructionsThresChanged(const QString &))); brkCpuCycExd->setChecked( break_on_cycles ); connect( brkCpuCycExd, SIGNAL(stateChanged(int)), this, SLOT(breakOnCyclesCB(int)) ); brkInstrsExd->setChecked( break_on_instructions ); connect( brkInstrsExd, SIGNAL(stateChanged(int)), this, SLOT(breakOnInstructionsCB(int)) ); hbox3 = new QHBoxLayout(); hbox = new QHBoxLayout(); vbox = new QVBoxLayout(); bmFrame = new QGroupBox( tr("Address Bookmarks") ); bmTree = new QTreeWidget(); selBmAddr = new QLineEdit(); selBmAddrVal = 0; connect( selBmAddr, SIGNAL(textChanged(const QString &)), this, SLOT(selBmAddrChanged(const QString &))); bmTree->setColumnCount(2); item = new QTreeWidgetItem(); item->setFont( 0, font ); item->setFont( 1, font ); item->setText( 0, QString::fromStdString( "Addr" ) ); item->setText( 1, QString::fromStdString( "Name" ) ); item->setTextAlignment( 0, Qt::AlignCenter); item->setTextAlignment( 1, Qt::AlignCenter); bmTree->setHeaderItem( item ); bmTree->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); connect( bmTree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(bmItemClicked( QTreeWidgetItem*, int)) ); connect( bmTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(bmItemDoubleClicked( QTreeWidgetItem*, int)) ); vbox->addWidget( selBmAddr ); button = new QPushButton( tr("Add") ); vbox->addWidget( button ); connect( button, SIGNAL(clicked(void)), this, SLOT(add_BM_CB(void)) ); button = new QPushButton( tr("Delete") ); vbox->addWidget( button ); connect( button, SIGNAL(clicked(void)), this, SLOT(delete_BM_CB(void)) ); button = new QPushButton( tr("Name") ); vbox->addWidget( button ); connect( button, SIGNAL(clicked(void)), this, SLOT(edit_BM_CB(void)) ); hbox->addWidget( bmTree ); hbox->addLayout( vbox ); bmFrame->setLayout( hbox ); hbox3->addWidget( bmFrame ); vbox1->addLayout( hbox3 ); frame = new QFrame(); vbox = new QVBoxLayout(); button = new QPushButton( tr("Reset Counters") ); connect( button, SIGNAL(clicked(void)), this, SLOT(resetCountersCB(void)) ); vbox->addWidget( button ); vbox->addWidget( frame ); hbox3->addLayout( vbox ); vbox = new QVBoxLayout(); romOfsChkBox = new QCheckBox( tr("ROM Offsets") ); symDbgChkBox = new QCheckBox( tr("Symbolic Debug") ); regNamChkBox = new QCheckBox( tr("Register Names") ); vbox->addWidget( romOfsChkBox ); vbox->addWidget( symDbgChkBox ); vbox->addWidget( regNamChkBox ); symDbgChkBox->setChecked(true); regNamChkBox->setChecked(true); connect( romOfsChkBox, SIGNAL(stateChanged(int)), this, SLOT(displayROMoffsetCB(int)) ); connect( symDbgChkBox, SIGNAL(stateChanged(int)), this, SLOT(symbolDebugEnableCB(int)) ); connect( regNamChkBox, SIGNAL(stateChanged(int)), this, SLOT(registerNameEnableCB(int)) ); button = new QPushButton( tr("Reload Symbols") ); vbox->addWidget( button ); connect( button, SIGNAL(clicked(void)), this, SLOT(reloadSymbolsCB(void)) ); button = new QPushButton( tr("ROM Patcher") ); vbox->addWidget( button ); button->setEnabled(false); // TODO frame->setLayout( vbox ); frame->setFrameShape( QFrame::Box ); hbox = new QHBoxLayout(); vbox1->addLayout( hbox ); button = new QPushButton( tr("Default Window Size") ); autoOpenChkBox = new QCheckBox( tr("Auto-Open") ); debFileChkBox = new QCheckBox( tr("DEB Files") ); idaFontChkBox = new QCheckBox( tr("IDA Font") ); hbox->addWidget( button ); hbox->addWidget( autoOpenChkBox ); hbox->addWidget( debFileChkBox ); hbox->addWidget( idaFontChkBox ); g_config->getOption( "SDL.AutoOpenDebugger", &opt ); autoOpenChkBox->setChecked( opt ); g_config->getOption( "SDL.AutoLoadDebugFiles", &opt ); debFileChkBox->setChecked( opt ); connect( autoOpenChkBox, SIGNAL(stateChanged(int)), this, SLOT(autoOpenDebugCB(int)) ); connect( debFileChkBox , SIGNAL(stateChanged(int)), this, SLOT(debFileAutoLoadCB(int)) ); button->setEnabled(false); // TODO // IDA font is just a monospace font, we are forcing this anyway. It is just easier to read the assembly. // If a different font is desired, my thought is to open a QFontDialog and let the user pick a new font, // rather than use a checkbox that selects between two. But for the moment, I have more important things // to do. idaFontChkBox->setEnabled(false); idaFontChkBox->setChecked(true); setLayout( mainLayout ); windowUpdateReq = true; dbgWinList.push_back( this ); periodicTimer = new QTimer( this ); connect( periodicTimer, &QTimer::timeout, this, &ConsoleDebugger::updatePeriodic ); connect( hbar, SIGNAL(valueChanged(int)), this, SLOT(hbarChanged(int)) ); connect( vbar, SIGNAL(valueChanged(int)), this, SLOT(vbarChanged(int)) ); bpListUpdate( false ); periodicTimer->start( 100 ); // 10hz // If this is the first debug window to open, load breakpoints fresh if ( dbgWinList.size() == 1 ) { int autoLoadDebug; g_config->getOption( "SDL.AutoLoadDebugFiles", &autoLoadDebug ); if ( autoLoadDebug ) { loadGameDebugBreakpoints(); } } } //---------------------------------------------------------------------------- ConsoleDebugger::~ConsoleDebugger(void) { std::list ::iterator it; printf("Destroy Debugger Window\n"); periodicTimer->stop(); for (it = dbgWinList.begin(); it != dbgWinList.end(); it++) { if ( (*it) == this ) { dbgWinList.erase(it); printf("Removing Debugger Window\n"); break; } } if ( dbgWinList.size() == 0 ) { saveGameDebugBreakpoints(); debuggerClearAllBreakpoints(); } } //---------------------------------------------------------------------------- void ConsoleDebugger::closeEvent(QCloseEvent *event) { printf("Debugger Close Window Event\n"); done(0); deleteLater(); event->accept(); } //---------------------------------------------------------------------------- void ConsoleDebugger::closeWindow(void) { //printf("Close Window\n"); done(0); deleteLater(); } //---------------------------------------------------------------------------- void ConsoleDebugger::bpItemClicked( QTreeWidgetItem *item, int column) { int row = bpTree->indexOfTopLevelItem(item); //printf("Row: %i Column: %i \n", row, column ); if ( column == 0 ) { if ( (row >= 0) && (row < numWPs) ) { int isChecked = item->checkState( column ) != Qt::Unchecked; if ( isChecked ) { watchpoint[row].flags |= WP_E; } else { watchpoint[row].flags &= ~WP_E; } } } } //---------------------------------------------------------------------------- void ConsoleDebugger::bmItemClicked( QTreeWidgetItem *item, int column) { //int row = bmTree->indexOfTopLevelItem(item); //printf("Row: %i Column: %i \n", row, column ); } //---------------------------------------------------------------------------- void ConsoleDebugger::bmItemDoubleClicked( QTreeWidgetItem *item, int column) { int addr, line; //int row = bmTree->indexOfTopLevelItem(item); //printf("Row: %i Column: %i \n", row, column ); addr = strtol( item->text(0).toStdString().c_str(), NULL, 16 ); line = asmView->getAsmLineFromAddr( addr ); asmView->setLine( line ); } //---------------------------------------------------------------------------- void ConsoleDebugger::selBmAddrChanged(const QString &txt) { selBmAddrVal = strtol( txt.toStdString().c_str(), NULL, 16 ); //printf("selBmAddrVal = %04X\n", selBmAddrVal ); } //---------------------------------------------------------------------------- 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, *ebp; QGridLayout *grid; QFrame *frame; QGroupBox *gbox; QPushButton *okButton, *cancelButton; QRadioButton *cpu_radio, *ppu_radio, *sprite_radio; if ( editIdx >= 0 ) { dialog.setWindowTitle( tr("Edit Breakpoint") ); } else { 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") ); ebp = new QCheckBox( tr("Enable") ); 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->addWidget( ebp ); 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)) ); okButton->setDefault(true); 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 ) { ebp->setChecked(true); } if ( wp->condText ) { cond->setText( tr(wp->condText) ); } else { if ( editIdx < 0 ) { // If new breakpoint, suggest condition if in ROM Mapping area of memory. if ( wp->address >= 0x8000 ) { char str[64]; sprintf(str, "K==#%02X", getBank(wp->address)); cond->setText( tr(str) ); } } } 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; 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; } enable = ebp->isChecked(); 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::openDebugSymbolEditWindow( int addr ) { int ret, bank, charWidth; QDialog dialog(this); QHBoxLayout *hbox; QVBoxLayout *mainLayout; QLabel *lbl; QLineEdit *filepath, *addrEntry, *nameEntry, *commentEntry; QPushButton *okButton, *cancelButton; char stmp[512]; debugSymbol_t *sym; QFontMetrics fm(font); #if QT_VERSION > QT_VERSION_CHECK(5, 11, 0) charWidth = fm.horizontalAdvance(QLatin1Char('2')); #else charWidth = fm.width(QLatin1Char('2')); #endif if ( addr < 0x8000 ) { bank = -1; } else { bank = getBank( addr ); } sym = debugSymbolTable.getSymbolAtBankOffset( bank, addr ); generateNLFilenameForAddress( addr, stmp ); dialog.setWindowTitle( tr("Symbolic Debug Naming") ); hbox = new QHBoxLayout(); mainLayout = new QVBoxLayout(); lbl = new QLabel( tr("File") ); filepath = new QLineEdit(); filepath->setFont( font ); filepath->setText( tr(stmp) ); filepath->setReadOnly( true ); filepath->setMinimumWidth( charWidth * (filepath->text().size() + 4) ); hbox->addWidget( lbl ); hbox->addWidget( filepath ); mainLayout->addLayout( hbox ); sprintf( stmp, "%04X", addr ); hbox = new QHBoxLayout(); lbl = new QLabel( tr("Address") ); addrEntry = new QLineEdit(); addrEntry->setFont( font ); addrEntry->setText( tr(stmp) ); addrEntry->setReadOnly( true ); addrEntry->setAlignment(Qt::AlignCenter); addrEntry->setMaximumWidth( charWidth * 6 ); hbox->addWidget( lbl ); hbox->addWidget( addrEntry ); lbl = new QLabel( tr("Name") ); nameEntry = new QLineEdit(); hbox->addWidget( lbl ); hbox->addWidget( nameEntry ); mainLayout->addLayout( hbox ); hbox = new QHBoxLayout(); lbl = new QLabel( tr("Comment") ); commentEntry = new QLineEdit(); hbox->addWidget( lbl ); hbox->addWidget( commentEntry ); mainLayout->addLayout( hbox ); 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)) ); okButton->setDefault(true); if ( sym != NULL ) { nameEntry->setText( tr(sym->name.c_str()) ); commentEntry->setText( tr(sym->comment.c_str()) ); } dialog.setLayout( mainLayout ); ret = dialog.exec(); if ( ret == QDialog::Accepted ) { fceuWrapperLock(); if ( sym == NULL ) { sym = new debugSymbol_t(); sym->ofs = addr; sym->name = nameEntry->text().toStdString(); sym->comment = commentEntry->text().toStdString(); debugSymbolTable.addSymbolAtBankOffset( bank, addr, sym ); } else { sym->name = nameEntry->text().toStdString(); sym->comment = commentEntry->text().toStdString(); } sym->trimTrailingSpaces(); asmView->updateAssemblyView(); fceuWrapperUnLock(); } } //---------------------------------------------------------------------------- void ConsoleDebugger::bpListUpdate( bool reset ) { QTreeWidgetItem *item; char cond[128], desc[128], addrStr[32], flags[16], enable; if ( reset ) { bpTree->clear(); } for (int i=0; itopLevelItemCount() > i ) { item = bpTree->topLevelItem(i); } else { item = NULL; } 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; cond[0] = 0; desc[0] = 0; if (watchpoint[i].desc ) { strcat( desc, watchpoint[i].desc); } if (watchpoint[i].condText ) { strcat( cond, " ("); strcat( cond, watchpoint[i].condText); strcat( cond, ") "); } item->setCheckState( 0, enable ? Qt::Checked : Qt::Unchecked ); item->setFont( 0, font ); item->setFont( 1, font ); item->setFont( 2, font ); item->setFont( 3, font ); item->setText( 0, tr(addrStr)); item->setText( 1, tr(flags) ); item->setText( 2, tr(cond) ); item->setText( 3, tr(desc) ); item->setTextAlignment( 0, Qt::AlignLeft); item->setTextAlignment( 1, Qt::AlignLeft); item->setTextAlignment( 2, Qt::AlignLeft); item->setTextAlignment( 3, Qt::AlignLeft); } bpTree->viewport()->update(); } //---------------------------------------------------------------------------- void ConsoleDebugger::add_BM_CB(void) { dbgBmMgr.addBookmark( selBmAddrVal ); bmListUpdate(false); } //---------------------------------------------------------------------------- void ConsoleDebugger::edit_BM_CB(void) { int addr; std::string s; QTreeWidgetItem *item; item = bmTree->currentItem(); if ( item == NULL ) { printf( "No Item Selected\n"); return; } s = item->text(0).toStdString(); addr = strtol( s.c_str(), NULL, 16 ); edit_BM_name( addr ); } //---------------------------------------------------------------------------- void ConsoleDebugger::delete_BM_CB(void) { int addr; std::string s; QTreeWidgetItem *item; item = bmTree->currentItem(); if ( item == NULL ) { printf( "No Item Selected\n"); return; } s = item->text(0).toStdString(); addr = strtol( s.c_str(), NULL, 16 ); dbgBmMgr.deleteBookmark( addr ); bmListUpdate(true); } //---------------------------------------------------------------------------- void ConsoleDebugger::edit_BM_name( int addr ) { int ret; debuggerBookmark_t *bm; QInputDialog dialog(this); char stmp[128]; bm = dbgBmMgr.getAddr( addr ); sprintf( stmp, "Specify Bookmark Name for %04X", addr ); dialog.setWindowTitle( tr("Edit Bookmark") ); dialog.setLabelText( tr(stmp) ); dialog.setOkButtonText( tr("Edit") ); if ( bm != NULL ) { dialog.setTextValue( tr(bm->name.c_str()) ); } dialog.show(); ret = dialog.exec(); if ( QDialog::Accepted == ret ) { bm->name = dialog.textValue().toStdString(); bmListUpdate(false); } } //---------------------------------------------------------------------------- void ConsoleDebugger::bmListUpdate( bool reset ) { int i=0; QTreeWidgetItem *item; debuggerBookmark_t *bm; char addrStr[32]; if ( reset ) { bmTree->clear(); } bm = dbgBmMgr.begin(); while ( bm != NULL ) { if ( bmTree->topLevelItemCount() > i ) { item = bmTree->topLevelItem(i); } else { item = NULL; } if ( item == NULL ) { item = new QTreeWidgetItem(); bmTree->addTopLevelItem( item ); } sprintf( addrStr, "%04X", bm->addr ); //item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable ); item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren ); item->setFont( 0, font ); item->setFont( 1, font ); item->setText( 0, tr(addrStr)); item->setText( 1, tr(bm->name.c_str()) ); item->setTextAlignment( 0, Qt::AlignLeft); item->setTextAlignment( 1, Qt::AlignLeft); bm = dbgBmMgr.next(); i++; } bmTree->viewport()->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; fceuWrapperLock(); 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--; fceuWrapperUnLock(); } //---------------------------------------------------------------------------- void debuggerClearAllBreakpoints(void) { int i; fceuWrapperLock(); for (i=0; icurrentItem(); if ( item == NULL ) { printf( "No Item Selected\n"); return; } int row = bpTree->indexOfTopLevelItem(item); DeleteBreak( row ); delete item; //delete bpTree->takeTopLevelItem(row); //bpListUpdate( true ); } //---------------------------------------------------------------------------- void ConsoleDebugger::breakOnBadOpcodeCB(int value) { //printf("Value:%i\n", value); FCEUI_Debugger().badopbreak = (value != Qt::Unchecked); } //---------------------------------------------------------------------------- void ConsoleDebugger::breakOnCyclesCB( int value ) { std::string s; break_on_cycles = (value != Qt::Unchecked); s = cpuCycExdVal->text().toStdString(); //printf("'%s'\n", txt ); if ( s.size() > 0 ) { break_cycles_limit = strtoul( s.c_str(), NULL, 10 ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::cpuCycleThresChanged(const QString &txt) { std::string s; s = txt.toStdString(); //printf("Cycles: '%s'\n", s.c_str() ); if ( s.size() > 0 ) { break_cycles_limit = strtoul( s.c_str(), NULL, 10 ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::breakOnInstructionsCB( int value ) { std::string s; break_on_instructions = (value != Qt::Unchecked); s = instrExdVal->text().toStdString(); //printf("'%s'\n", txt ); if ( s.size() > 0 ) { break_instructions_limit = strtoul( s.c_str(), NULL, 10 ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::instructionsThresChanged(const QString &txt) { std::string s; s = txt.toStdString(); //printf("Instructions: '%s'\n", s.c_str() ); if ( s.size() > 0 ) { break_instructions_limit = strtoul( s.c_str(), NULL, 10 ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::displayROMoffsetCB( int value ) { asmView->setDisplayROMoffsets(value != Qt::Unchecked); } //---------------------------------------------------------------------------- void ConsoleDebugger::symbolDebugEnableCB( int value ) { asmView->setSymbolDebugEnable(value != Qt::Unchecked); } //---------------------------------------------------------------------------- void ConsoleDebugger::registerNameEnableCB( int value ) { asmView->setRegisterNameEnable(value != Qt::Unchecked); } //---------------------------------------------------------------------------- void ConsoleDebugger::autoOpenDebugCB( int value ) { g_config->setOption( "SDL.AutoOpenDebugger", value != Qt::Unchecked); } //---------------------------------------------------------------------------- void ConsoleDebugger::debFileAutoLoadCB( int value ) { int autoLoadDebug = value != Qt::Unchecked; g_config->setOption("SDL.AutoLoadDebugFiles", autoLoadDebug); if ( autoLoadDebug && (numWPs == 0) ) { loadGameDebugBreakpoints(); } } //---------------------------------------------------------------------------- void ConsoleDebugger::reloadSymbolsCB(void) { fceuWrapperLock(); debugSymbolTable.loadGameSymbols(); asmView->updateAssemblyView(); fceuWrapperUnLock(); } //---------------------------------------------------------------------------- void ConsoleDebugger::debugRunCB(void) { if (FCEUI_EmulationPaused()) { setRegsFromEntry(); FCEUI_ToggleEmulationPause(); //DebuggerWasUpdated = false done in above function; } } //---------------------------------------------------------------------------- void ConsoleDebugger::debugStepIntoCB(void) { if (FCEUI_EmulationPaused()) { setRegsFromEntry(); } FCEUI_Debugger().step = true; FCEUI_SetEmulationPaused(0); } //---------------------------------------------------------------------------- void ConsoleDebugger::debugStepOutCB(void) { if (FCEUI_EmulationPaused() > 0) { DebuggerState &dbgstate = FCEUI_Debugger(); setRegsFromEntry(); 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); } } //---------------------------------------------------------------------------- void ConsoleDebugger::debugStepOverCB(void) { if (FCEUI_EmulationPaused()) { setRegsFromEntry(); 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); } } //---------------------------------------------------------------------------- void ConsoleDebugger::debugRunLineCB(void) { if (FCEUI_EmulationPaused()) { setRegsFromEntry(); } 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); } //---------------------------------------------------------------------------- void ConsoleDebugger::debugRunLine128CB(void) { if (FCEUI_EmulationPaused()) { setRegsFromEntry(); } 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); } //---------------------------------------------------------------------------- void ConsoleDebugger::seekToCB (void) { std::string s; s = seekEntry->text().toStdString(); //printf("Seek To: '%s'\n", s.c_str() ); if ( s.size() > 0 ) { long int addr, line; addr = strtol( s.c_str(), NULL, 16 ); line = asmView->getAsmLineFromAddr(addr); asmView->setLine( line ); vbar->setValue( line ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::seekPCCB (void) { if (FCEUI_EmulationPaused()) { setRegsFromEntry(); //updateAllDebugWindows(); } windowUpdateReq = true; //asmView->scrollToPC(); } //---------------------------------------------------------------------------- void ConsoleDebugger::resetCountersCB (void) { ResetDebugStatisticsCounters(); updateRegisterView(); } //---------------------------------------------------------------------------- void ConsoleDebugger::asmViewCtxMenuAddBP(void) { watchpointinfo wp; wp.address = asmView->getCtxMenuAddr(); wp.endaddress = 0; wp.flags = WP_X | WP_E; wp.condText = 0; wp.desc = NULL; openBpEditWindow( -1, &wp ); } //---------------------------------------------------------------------------- void ConsoleDebugger::asmViewCtxMenuAddBM(void) { int addr = asmView->getCtxMenuAddr(); dbgBmMgr.addBookmark( addr ); edit_BM_name( addr ); bmListUpdate(false); } //---------------------------------------------------------------------------- void ConsoleDebugger::asmViewCtxMenuOpenHexEdit(void) { int romAddr = -1; int addr = asmView->getCtxMenuAddr(); if (addr >= 0x8000) { romAddr = GetNesFileAddress(addr); } if ( romAddr >= 0 ) { hexEditorOpenFromDebugger( QHexEdit::MODE_NES_ROM, romAddr ); } else { hexEditorOpenFromDebugger( QHexEdit::MODE_NES_RAM, addr ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::setBookmarkSelectedAddress( int addr ) { char stmp[32]; sprintf( stmp, "%04X", addr ); selBmAddr->setText( tr(stmp) ); selBmAddrVal = addr; } //---------------------------------------------------------------------------- void ConsoleDebugger::asmViewCtxMenuAddSym(void) { openDebugSymbolEditWindow( asmView->getCtxMenuAddr() ); } //---------------------------------------------------------------------------- int QAsmView::getAsmLineFromAddr(int addr) { int line = -1; int incr, nextLine; int run = 1; if ( asmEntry.size() <= 0 ) { return -1; } incr = asmEntry.size() / 2; if ( addr < asmEntry[0]->addr ) { return 0; } else if ( addr > asmEntry[ asmEntry.size() - 1 ]->addr ) { return asmEntry.size() - 1; } if ( incr < 1 ) incr = 1; nextLine = line = incr; // algorithm to efficiently find line from address. Starts in middle of list and // keeps dividing the list in 2 until it arrives at an answer. while ( run ) { //printf("incr:%i line:%i addr:%04X delta:%i\n", incr, line, asmEntry[line]->addr, addr - asmEntry[line]->addr); if ( incr == 1 ) { if ( asmEntry[line]->addr < addr ) { nextLine = line + 1; if ( asmEntry[line]->addr > nextLine ) { break; } line = nextLine; } else if ( asmEntry[line]->addr > addr ) { nextLine = line - 1; if ( asmEntry[line]->addr < nextLine ) { break; } line = nextLine; } else { nextLine = line - 1; if ( nextLine >= 0 ) { while ( asmEntry[nextLine]->addr == asmEntry[line]->addr ) { line = nextLine; nextLine--; if ( nextLine < 0 ) { break; } } } run = 0; break; } } else { incr = incr / 2; if ( incr < 1 ) incr = 1; if ( asmEntry[line]->addr < addr ) { nextLine = line + incr; } else if ( asmEntry[line]->addr > addr ) { nextLine = line - incr; } else { nextLine = line - 1; if ( nextLine >= 0 ) { while ( asmEntry[nextLine]->addr == asmEntry[line]->addr ) { line = nextLine; nextLine--; if ( nextLine < 0 ) { break; } } } run = 0; break; } line = nextLine; } } //for (size_t i=0; iaddr >= addr ) // { // line = i; break; // } //} return line; } //---------------------------------------------------------------------------- // 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 QAsmView::updateAssemblyView(void) { int starting_address, start_address_lp, addr, size; int instruction_addr, asmFlags = 0; std::string line; char chr[64]; uint8 opcode[3]; char asmTxt[256]; dbg_asm_entry_t *a, *d; //GtkTextIter iter, next_iter; char pc_found = 0; start_address_lp = starting_address = X.PC; for (int i=0; i < 0xFFFF; 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; } if ( starting_address < 0 ) { starting_address = start_address_lp; break; } start_address_lp = starting_address; } maxLineLen = 0; asmClear(); addr = starting_address; asmPC = NULL; if ( symbolicDebugEnable ) { asmFlags |= ASM_DEBUG_SYMS; if ( registerNameEnable ) { asmFlags |= ASM_DEBUG_REGS; } } //asmText->clear(); //gtk_text_buffer_get_start_iter( textbuf, &iter ); //textview_lines_allocated = gtk_text_buffer_get_line_count( textbuf ) - 1; //printf("Num Lines: %i\n", textview_lines_allocated ); for (int i=0; i < 0xFFFF; i++) { line.clear(); // PC pointer if (addr > 0xFFFF) break; a = new dbg_asm_entry_t; instruction_addr = addr; if ( !pc_found ) { if (addr > X.PC) { asmPC = a; line.assign(">"); pc_found = 1; } else if (addr == X.PC) { asmPC = a; line.assign(">"); pc_found = 1; } else { line.assign(" "); } } else { line.assign(" "); } a->addr = addr; if (addr >= 0x8000) { a->bank = getBank(addr); a->rom = GetNesFileAddress(addr); if (displayROMoffsets && (a->rom != -1) ) { sprintf(chr, " %06X: ", a->rom); } else { sprintf(chr, "%02X:%04X: ", a->bank, 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++; } DisassembleWithDebug(addr, opcode, asmFlags, asmTxt, &a->sym); line.append( asmTxt ); } for (int j=0; jopcode[j] = opcode[j]; } a->size = size; // special case: an RTS opcode if (GetMem(instruction_addr) == 0x60) { line.append("-------------------------"); } if ( symbolicDebugEnable ) { debugSymbol_t *dbgSym; dbgSym = debugSymbolTable.getSymbolAtBankOffset( a->bank, a->addr ); if ( dbgSym != NULL ) { int i,j; const char *c; char stmp[256]; //printf("Debug symbol Found at $%04X \n", dbgSym->ofs ); if ( dbgSym->name.size() > 0 ) { d = new dbg_asm_entry_t(); *d = *a; d->type = dbg_asm_entry_t::SYMBOL_NAME; d->text.assign( dbgSym->name ); d->text.append( ":"); d->line = asmEntry.size(); asmEntry.push_back(d); } i=0; j=0; c = dbgSym->comment.c_str(); while ( c[i] != 0 ) { if ( c[i] == '\n' ) { if ( j > 0 ) { stmp[j] = 0; d = new dbg_asm_entry_t(); *d = *a; d->type = dbg_asm_entry_t::SYMBOL_COMMENT; d->text.assign( stmp ); d->line = asmEntry.size(); asmEntry.push_back(d); } i++; j=0; } else { stmp[j] = c[i]; j++; i++; } } stmp[j] = 0; if ( j > 0 ) { d = new dbg_asm_entry_t(); *d = *a; d->type = dbg_asm_entry_t::SYMBOL_COMMENT; d->text.assign( stmp ); d->line = asmEntry.size(); asmEntry.push_back(d); } } } a->text.assign( line ); a->line = asmEntry.size(); if ( maxLineLen < line.size() ) { maxLineLen = line.size(); } line.append("\n"); asmEntry.push_back(a); } pxLineWidth = maxLineLen * pxCharWidth; setMinimumWidth( 50 * pxCharWidth ); vbar->setMaximum( asmEntry.size() ); } //---------------------------------------------------------------------------- void ConsoleDebugger::setRegsFromEntry(void) { std::string s; long int i; s = pcEntry->text().toStdString(); if ( s.size() > 0 ) { i = strtol( s.c_str(), NULL, 16 ); } else { i = 0; } X.PC = i; //printf("Set PC: '%s' %04X\n", s.c_str(), X.PC ); s = regAEntry->text().toStdString(); if ( s.size() > 0 ) { i = strtol( s.c_str(), NULL, 16 ); } else { i = 0; } X.A = i; //printf("Set A: '%s' %02X\n", s.c_str(), X.A ); s = regXEntry->text().toStdString(); if ( s.size() > 0 ) { i = strtol( s.c_str(), NULL, 16 ); } else { i = 0; } X.X = i; //printf("Set X: '%s' %02X\n", s.c_str(), X.X ); s = regYEntry->text().toStdString(); if ( s.size() > 0 ) { i = strtol( s.c_str(), NULL, 16 ); } else { i = 0; } X.Y = i; //printf("Set Y: '%s' %02X\n", s.c_str(), X.Y ); i=0; if ( N_chkbox->isChecked() ) { i |= N_FLAG; } if ( V_chkbox->isChecked() ) { i |= V_FLAG; } if ( U_chkbox->isChecked() ) { i |= U_FLAG; } if ( B_chkbox->isChecked() ) { i |= B_FLAG; } if ( D_chkbox->isChecked() ) { i |= D_FLAG; } if ( I_chkbox->isChecked() ) { i |= I_FLAG; } if ( Z_chkbox->isChecked() ) { i |= Z_FLAG; } if ( C_chkbox->isChecked() ) { i |= C_FLAG; } X.P = i; } //---------------------------------------------------------------------------- void ConsoleDebugger::updateRegisterView(void) { int stackPtr; char stmp[64]; char str[32], str2[32]; sprintf( stmp, "%04X", X.PC ); pcEntry->setText( tr(stmp) ); sprintf( stmp, "%02X", X.A ); regAEntry->setText( tr(stmp) ); sprintf( stmp, "%02X", X.X ); regXEntry->setText( tr(stmp) ); sprintf( stmp, "%02X", X.Y ); regYEntry->setText( tr(stmp) ); N_chkbox->setChecked( (X.P & N_FLAG) ? true : false ); V_chkbox->setChecked( (X.P & V_FLAG) ? true : false ); U_chkbox->setChecked( (X.P & U_FLAG) ? true : false ); B_chkbox->setChecked( (X.P & B_FLAG) ? true : false ); D_chkbox->setChecked( (X.P & D_FLAG) ? true : false ); I_chkbox->setChecked( (X.P & I_FLAG) ? true : false ); Z_chkbox->setChecked( (X.P & Z_FLAG) ? true : false ); C_chkbox->setChecked( (X.P & C_FLAG) ? true : false ); stackPtr = X.S | 0x0100; sprintf( stmp, "Stack: $%04X", stackPtr ); stackFrame->setTitle( tr(stmp) ); stackText->updateText(); // 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); cpuCyclesLbl1->setText( tr(stmp) ); counter_value = timestampbase + (uint64)timestamp - delta_cycles_base; if (counter_value < 0) // sanity check { ResetDebugStatisticsCounters(); counter_value = 0; } sprintf(stmp, "(+%llu)", counter_value); cpuCyclesLbl2->setText( tr(stmp) ); sprintf(stmp, "Instructions: %llu", total_instructions); cpuInstrsLbl1->setText( tr(stmp) ); sprintf(stmp, "(+%llu)", delta_instructions); cpuInstrsLbl2->setText( tr(stmp) ); // PPU Labels sprintf(stmp, "PPU: 0x%04X", (int)FCEUPPU_PeekAddress()); ppuLbl->setText( tr(stmp) ); sprintf(stmp, "Sprite: 0x%02X", PPU[3] ); spriteLbl->setText( tr(stmp) ); extern int linestartts; #define GETLASTPIXEL (PAL?((timestamp*48-linestartts)/15) : ((timestamp*48-linestartts)/16) ) int ppupixel = GETLASTPIXEL; if (ppupixel>341) //maximum number of pixels per scanline ppupixel = 0; //Currently pixel display is borked until Run 128 lines is clicked, this keeps garbage from displaying // If not in the 0-239 pixel range, make special cases for display if (scanline == 240 && vblankScanLines < (PAL?72:22)) { if (!vblankScanLines) { // Idle scanline (240) sprintf(str, "%d", scanline); // was "Idle %d" } else if (scanline + vblankScanLines == (PAL?311:261)) { // Pre-render sprintf(str, "-1"); // was "Prerender -1" } else { // Vblank lines (241-260/310) sprintf(str, "%d", scanline + vblankScanLines); // was "Vblank %d" } sprintf(str2, "%d", vblankPixel); } else { // Scanlines 0 - 239 sprintf(str, "%d", scanline); sprintf(str2, "%d", ppupixel); } if(newppu) { sprintf(str ,"%d",newppu_get_scanline()); sprintf(str2,"%d",newppu_get_dot()); } sprintf( stmp, "Scanline: %s", str ); scanLineLbl->setText( tr(stmp) ); sprintf( stmp, "Pixel: %s", str2 ); pixLbl->setText( tr(stmp) ); } //---------------------------------------------------------------------------- void ConsoleDebugger::updateWindowData(void) { asmView->updateAssemblyView(); asmView->scrollToPC(); updateRegisterView(); windowUpdateReq = false; } //---------------------------------------------------------------------------- void ConsoleDebugger::queueUpdate(void) { windowUpdateReq = true; } //---------------------------------------------------------------------------- void ConsoleDebugger::updatePeriodic(void) { //printf("Update Periodic\n"); if ( windowUpdateReq ) { fceuWrapperLock(); updateWindowData(); fceuWrapperUnLock(); } asmView->update(); if ( FCEUI_EmulationPaused() && !FCEUI_EmulationFrameStepped()) { emuStatLbl->setText( tr(" Emulator Stopped / Paused") ); emuStatLbl->setStyleSheet("background-color: red; color: white;"); } else { emuStatLbl->setText( tr(" Emulator is Running") ); emuStatLbl->setStyleSheet("background-color: green; color: white;"); } if ( bpTree->topLevelItemCount() != numWPs ) { printf("Breakpoint Tree Update\n"); bpListUpdate( true ); } if ( bmTree->topLevelItemCount() != dbgBmMgr.size() ) { printf("Bookmark Tree Update\n"); bmListUpdate( true ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::breakPointNotify( int bpNum ) { if ( bpNum >= 0 ) { // highlight bp_num item list if ( bpTree->topLevelItemCount() > 0 ) { QTreeWidgetItem * item; item = bpTree->currentItem(); if ( item != NULL ) { item->setSelected(false); } item = bpTree->topLevelItem( bpNum ); if ( item != NULL ) { item->setSelected(true); } bpTree->viewport()->update(); } } else { if (bpNum == BREAK_TYPE_CYCLES_EXCEED) { // TODO } else if (bpNum == BREAK_TYPE_INSTRUCTIONS_EXCEED) { // TODO } } windowUpdateReq = true; } //---------------------------------------------------------------------------- void ConsoleDebugger::hbarChanged(int value) { //printf("HBar Changed: %i\n", value); asmView->setXScroll( value ); } //---------------------------------------------------------------------------- void ConsoleDebugger::vbarChanged(int value) { //printf("VBar Changed: %i\n", value); asmView->setLine( value ); } //---------------------------------------------------------------------------- void FCEUD_DebugBreakpoint( int bpNum ) { std::list ::iterator it; if ( !nes_shm->runEmulator ) { return; } printf("Breakpoint Hit: %i \n", bpNum ); fceuWrapperUnLock(); for (it=dbgWinList.begin(); it!=dbgWinList.end(); it++) { (*it)->breakPointNotify( bpNum ); } while ( nes_shm->runEmulator && FCEUI_EmulationPaused() && !FCEUI_EmulationFrameStepped()) { // HACK: break when Frame Advance is pressed extern bool frameAdvanceRequested; extern int frameAdvance_Delay_count, frameAdvance_Delay; if (frameAdvanceRequested) { if ( (frameAdvance_Delay_count == 0) || (frameAdvance_Delay_count >= frameAdvance_Delay) ) { FCEUI_SetEmulationPaused(EMULATIONPAUSED_FA); } if (frameAdvance_Delay_count < frameAdvance_Delay) { frameAdvance_Delay_count++; } } usleep(16667); } // since we unfreezed emulation, reset delta_cycles counter ResetDebugStatisticsDeltaCounters(); fceuWrapperLock(); } //---------------------------------------------------------------------------- bool debuggerWindowIsOpen(void) { return (dbgWinList.size() > 0); } //---------------------------------------------------------------------------- void updateAllDebuggerWindows( void ) { std::list ::iterator it; for (it=dbgWinList.begin(); it!=dbgWinList.end(); it++) { (*it)->queueUpdate(); } } //---------------------------------------------------------------------------- static int getGameDebugBreakpointFileName(char *filepath) { int i,j; const char *romFile; romFile = getRomFile(); if ( romFile == NULL ) { return -1; } i=0; j = -1; while ( romFile[i] != 0 ) { if ( romFile[i] == '|' ) { filepath[i] = '.'; } else { if ( romFile[i] == '/' ) { j = -1; } else if ( romFile[i] == '.' ) { j = i; } filepath[i] = romFile[i]; } i++; } if ( j >= 0 ) { filepath[j] = 0; i=j; } filepath[i] = '.'; i++; filepath[i] = 'd'; i++; filepath[i] = 'b'; i++; filepath[i] = 'g'; i++; filepath[i] = 0; return 0; } //---------------------------------------------------------------------------- void saveGameDebugBreakpoints(void) { int i; FILE *fp; char stmp[512]; char flags[8]; debuggerBookmark_t *bm; // If no breakpoints are loaded, skip saving if ( (numWPs == 0) && (dbgBmMgr.size() == 0) ) { return; } if ( getGameDebugBreakpointFileName( stmp ) ) { return; } //printf("Debug Save File: '%s' \n", stmp ); fp = fopen( stmp, "w"); if ( fp == NULL ) { printf("Error: Failed to open file '%s' for writing\n", stmp ); return; } for (i=0; iaddr, bm->name.c_str() ); bm = dbgBmMgr.next(); } fclose(fp); return; } //---------------------------------------------------------------------------- static int getKeyValuePair( int i, const char *stmp, char *id, char *data ) { int j=0; char literal=0; id[0] = 0; data[0] = 0; if ( stmp[i] == 0 ) return i; while ( isspace(stmp[i]) ) i++; j=0; while ( isalnum(stmp[i]) ) { id[j] = stmp[i]; j++; i++; } id[j] = 0; if ( j == 0 ) { return i; } if ( stmp[i] != '=' ) { return i; } i++; j=0; if ( stmp[i] == '\"' ) { literal = 0; i++; while ( stmp[i] != 0 ) { if ( literal ) { data[j] = stmp[i]; i++; j++; literal = 0; } else { if ( stmp[i] == '\\' ) { literal = 1; i++; } else if ( stmp[i] == '\"' ) { i++; break; } else { data[j] = stmp[i]; j++; i++; } } } data[j] = 0; } else { j=0; while ( !isspace(stmp[i]) ) { data[j] = stmp[i]; j++; i++; } data[j] = 0; } return i; } //---------------------------------------------------------------------------- void loadGameDebugBreakpoints(void) { int i,j; FILE *fp; char stmp[512]; char id[64], data[128]; // If no debug windows are open, skip loading breakpoints if ( dbgWinList.size() == 0 ) { printf("No Debug Windows Open: Skipping loading of breakpoint data\n"); return; } if ( getGameDebugBreakpointFileName( stmp ) ) { return; } //printf("Debug Load File: '%s' \n", stmp ); fp = fopen( stmp, "r"); if ( fp == NULL ) { printf("Error: Failed to open file '%s' for writing\n", stmp ); return; } while ( fgets( stmp, sizeof(stmp), fp ) != 0 ) { i=0; j=0; while ( isspace(stmp[i]) ) i++; while ( isalnum(stmp[i]) ) { id[j] = stmp[i]; j++; i++; } id[j] = 0; while ( isspace(stmp[i]) ) i++; if ( stmp[i] != ':' ) { continue; } i++; while ( isspace(stmp[i]) ) i++; if ( strcmp( id, "BreakPoint" ) == 0 ) { int retval; int start_addr = -1, end_addr = -1, type = 0, enable = 0; char cond[256], desc[256]; cond[0] = 0; desc[0] = 0; while ( stmp[i] != 0 ) { i = getKeyValuePair( i, stmp, id, data ); //printf("ID:'%s' DATA:'%s' \n", id, data ); if ( strcmp( id, "startAddr" ) == 0 ) { start_addr = strtol( data, NULL, 16 ); } else if ( strcmp( id, "endAddr" ) == 0 ) { end_addr = strtol( data, NULL, 16 ); } else if ( strcmp( id, "flags" ) == 0 ) { type = 0; //enable = (data[0] == 'E'); // Always start with breakpoints disabled. if ( data[1] == 'P' ) { type |= BT_P; } else if ( data[1] == 'S' ) { type |= BT_S; } else { type |= BT_C; } type |= (data[2] == 'R') ? WP_R : 0; type |= (data[3] == 'W') ? WP_W : 0; type |= (data[4] == 'X') ? WP_X : 0; type |= (data[5] == 'F') ? WP_F : 0; } else if ( strcmp( id, "condition" ) == 0 ) { strcpy( cond, data ); } else if ( strcmp( id, "desc" ) == 0 ) { strcpy( desc, data ); } } if ( (start_addr >= 0) && (numWPs < 64) ) { retval = NewBreak( desc, start_addr, end_addr, type, cond, numWPs, enable); if ( (retval == 1) || (retval == 2) ) { printf("Breakpoint Add Failed\n"); } else { numWPs++; } } } else if ( strcmp( id, "Bookmark" ) == 0 ) { int addr = -1; char desc[256]; desc[0] = 0; while ( stmp[i] != 0 ) { i = getKeyValuePair( i, stmp, id, data ); //printf("ID:'%s' DATA:'%s' \n", id, data ); if ( strcmp( id, "addr" ) == 0 ) { addr = strtol( data, NULL, 16 ); } else if ( strcmp( id, "desc" ) == 0 ) { strcpy( desc, data ); } } if ( addr >= 0 ) { if ( dbgBmMgr.addBookmark( addr, desc ) ) { printf("Error:Failed to add debug bookmark: $%04X '%s' \n", addr, desc ); } } } } fclose(fp); return; } //---------------------------------------------------------------------------- QAsmView::QAsmView(QWidget *parent) : QWidget( parent ) { QPalette pal; QColor fg("black"), bg("white"); QColor c; useDarkTheme = false; font.setFamily("Courier New"); font.setStyle( QFont::StyleNormal ); font.setStyleHint( QFont::Monospace ); pal = this->palette(); //c = pal.color(QPalette::Base); //printf("Base: R:%i G:%i B:%i \n", c.red(), c.green(), c.blue() ); //c = pal.color(QPalette::Background); //printf("BackGround: R:%i G:%i B:%i \n", c.red(), c.green(), c.blue() ); // Figure out if we are using a light or dark theme by checking the // default window text grayscale color. If more white, then we will // use white text on black background, else we do the opposite. c = pal.color(QPalette::WindowText); if ( qGray( c.red(), c.green(), c.blue() ) > 128 ) { useDarkTheme = true; } //printf("WindowText: R:%i G:%i B:%i \n", c.red(), c.green(), c.blue() ); if ( useDarkTheme ) { pal.setColor(QPalette::Base , fg ); pal.setColor(QPalette::Background, fg ); pal.setColor(QPalette::WindowText, bg ); } else { pal.setColor(QPalette::Base , bg ); pal.setColor(QPalette::Background, bg ); pal.setColor(QPalette::WindowText, fg ); } this->parent = (ConsoleDebugger*)parent; this->setPalette(pal); this->setMouseTracking(true); calcFontData(); vbar = NULL; hbar = NULL; asmPC = NULL; displayROMoffsets = false; symbolicDebugEnable = true; registerNameEnable = true; maxLineLen = 0; pxLineWidth = 0; lineOffset = 0; maxLineOffset = 0; ctxMenuAddr = -1; mouseLeftBtnDown = false; txtHlgtStartChar = -1; txtHlgtStartLine = -1; txtHlgtEndChar = -1; txtHlgtEndLine = -1; selAddrLine = -1; selAddrChar = 0; selAddrWidth = 0; selAddrValue = -1; memset( selAddrText, 0, sizeof(selAddrText) ); wheelPixelCounter = 0; //setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding ); setFocusPolicy(Qt::StrongFocus); clipboard = QGuiApplication::clipboard(); //printf("clipboard->supportsSelection() : '%i' \n", clipboard->supportsSelection() ); //printf("clipboard->supportsFindBuffer(): '%i' \n", clipboard->supportsFindBuffer() ); } //---------------------------------------------------------------------------- QAsmView::~QAsmView(void) { asmClear(); } //---------------------------------------------------------------------------- void QAsmView::asmClear(void) { for (size_t i=0; i= pxLineWidth ) { pxLineXScroll = 0; } else { pxLineXScroll = (int)(0.010f * (float)value * (float)(pxLineWidth - viewWidth) ); } } //---------------------------------------------------------------------------- void QAsmView::scrollToPC(void) { if ( asmPC != NULL ) { lineOffset = asmPC->line; vbar->setValue( lineOffset ); } } //---------------------------------------------------------------------------- void QAsmView::setDisplayROMoffsets( bool value ) { if ( value != displayROMoffsets ) { displayROMoffsets = value; fceuWrapperLock(); updateAssemblyView(); fceuWrapperUnLock(); } } //---------------------------------------------------------------------------- void QAsmView::setSymbolDebugEnable( bool value ) { if ( value != symbolicDebugEnable ) { symbolicDebugEnable = value; fceuWrapperLock(); updateAssemblyView(); fceuWrapperUnLock(); } } //---------------------------------------------------------------------------- void QAsmView::setRegisterNameEnable( bool value ) { if ( value != registerNameEnable ) { registerNameEnable = value; fceuWrapperLock(); updateAssemblyView(); fceuWrapperUnLock(); } } //---------------------------------------------------------------------------- void QAsmView::calcFontData(void) { this->setFont(font); QFontMetrics metrics(font); #if QT_VERSION > QT_VERSION_CHECK(5, 11, 0) pxCharWidth = metrics.horizontalAdvance(QLatin1Char('2')); #else pxCharWidth = metrics.width(QLatin1Char('2')); #endif pxCharHeight = metrics.height(); pxLineSpacing = metrics.lineSpacing() * 1.25; pxLineLead = pxLineSpacing - pxCharHeight; pxCursorHeight = pxCharHeight; viewLines = (viewHeight / pxLineSpacing) + 1; } //---------------------------------------------------------------------------- void QAsmView::setScrollBars( QScrollBar *h, QScrollBar *v ) { hbar = h; vbar = v; } //---------------------------------------------------------------------------- void QAsmView::resizeEvent(QResizeEvent *event) { viewWidth = event->size().width(); viewHeight = event->size().height(); //printf("QAsmView Resize: %ix%i\n", viewWidth, viewHeight ); viewLines = (viewHeight / pxLineSpacing) + 1; maxLineOffset = 0; // mb.numLines() - viewLines + 1; if ( viewWidth >= pxLineWidth ) { pxLineXScroll = 0; } else { pxLineXScroll = (int)(0.010f * (float)hbar->value() * (float)(pxLineWidth - viewWidth) ); } } //---------------------------------------------------------------------------- void QAsmView::keyPressEvent(QKeyEvent *event) { //printf("Debug ASM Window Key Press: 0x%x \n", event->key() ); if (event->matches(QKeySequence::MoveToPreviousLine)) { lineOffset--; if ( lineOffset < 0 ) { lineOffset = 0; } vbar->setValue( lineOffset ); event->accept(); } else if (event->matches(QKeySequence::MoveToNextLine)) { lineOffset++; if ( lineOffset > maxLineOffset ) { lineOffset = maxLineOffset; } vbar->setValue( lineOffset ); event->accept(); } else if (event->matches(QKeySequence::MoveToNextPage)) { lineOffset += ( (3 * viewLines) / 4); if ( lineOffset >= maxLineOffset ) { lineOffset = maxLineOffset; } vbar->setValue( lineOffset ); event->accept(); } else if (event->matches(QKeySequence::MoveToPreviousPage)) { lineOffset -= ( (3 * viewLines) / 4); if ( lineOffset < 0 ) { lineOffset = 0; } vbar->setValue( lineOffset ); event->accept(); } else if ( selAddrValue >= 0 ) { ctxMenuAddr = selAddrValue; if ( event->key() == Qt::Key_B ) { parent->asmViewCtxMenuAddBP(); event->accept(); } else if ( event->key() == Qt::Key_S ) { parent->asmViewCtxMenuAddSym(); event->accept(); } else if ( event->key() == Qt::Key_M ) { parent->asmViewCtxMenuAddBM(); event->accept(); } else if ( event->key() == Qt::Key_H ) { parent->asmViewCtxMenuOpenHexEdit(); event->accept(); } } } //---------------------------------------------------------------------------- void QAsmView::keyReleaseEvent(QKeyEvent *event) { //printf("Debug ASM Window Key Release: 0x%x \n", event->key() ); } //---------------------------------------------------------------------------- QPoint QAsmView::convPixToCursor( QPoint p ) { QPoint c(0,0); if ( p.x() < 0 ) { c.setX(0); } else { float x = (float)p.x() / pxCharWidth; c.setX( (int)x ); } if ( p.y() < 0 ) { c.setY( 0 ); } else { float ly = ( (float)pxLineLead / (float)pxLineSpacing ); float py = ( (float)p.y() ) / (float)pxLineSpacing; float ry = fmod( py, 1.0 ); if ( ry < ly ) { c.setY( ((int)py) - 1 ); } else { c.setY( (int)py ); } } return c; } //---------------------------------------------------------------------------- bool QAsmView::textIsHighlighted(void) { bool set = false; if ( txtHlgtStartLine == txtHlgtEndLine ) { set = (txtHlgtStartChar != txtHlgtEndChar); } else { set = true; } return set; } //---------------------------------------------------------------------------- void QAsmView::setHighlightEndCoord( int x, int y ) { if ( txtHlgtAnchorLine < y ) { txtHlgtStartLine = txtHlgtAnchorLine; txtHlgtStartChar = txtHlgtAnchorChar; txtHlgtEndLine = y; txtHlgtEndChar = x; } else if ( txtHlgtAnchorLine > y ) { txtHlgtStartLine = y; txtHlgtStartChar = x; txtHlgtEndLine = txtHlgtAnchorLine; txtHlgtEndChar = txtHlgtAnchorChar; } else { txtHlgtStartLine = txtHlgtAnchorLine; txtHlgtEndLine = txtHlgtAnchorLine; if ( txtHlgtAnchorChar < x ) { txtHlgtStartChar = txtHlgtAnchorChar; txtHlgtEndChar = x; } else if ( txtHlgtAnchorChar > x ) { txtHlgtStartChar = x; txtHlgtEndChar = txtHlgtAnchorChar; } else { txtHlgtStartChar = txtHlgtAnchorChar; txtHlgtEndChar = txtHlgtAnchorChar; } } return; } //---------------------------------------------------------------------------- void QAsmView::mouseMoveEvent(QMouseEvent * event) { int line; char txt[256]; std::string s; QPoint c = convPixToCursor( event->pos() ); line = lineOffset + c.y(); if ( mouseLeftBtnDown ) { //printf("Left Button Move: (%i,%i)\n", c.x(), c.y() ); setHighlightEndCoord( c.x(), line ); } //printf("c (%i,%i) : Line %i : %04X \n", c.x(), c.y(), line, asmEntry[line]->addr ); if ( line < asmEntry.size() ) { int addr; addr = asmEntry[line]->addr; if (addr >= 0x8000) { int bank, romOfs; bank = getBank(addr); romOfs = GetNesFileAddress(addr); sprintf( txt, "CPU Address: %02X:%04X", bank, addr); s.assign( txt ); if (romOfs != -1) { const char *fileName; fileName = iNesShortFName(); if ( fileName == NULL ) { fileName = "..."; } sprintf( txt, "\nOffset 0x%06X in File \"%s\" (NL file: %X)", romOfs, fileName, bank); s.append( txt ); } } else { sprintf( txt, "CPU Address: %04X", addr); s.assign( txt ); } } if ( parent != NULL ) { parent->asmLineSelLbl->setText( tr(s.c_str()) ); } } //---------------------------------------------------------------------------- void QAsmView::loadClipboard( const char *txt ) { clipboard->setText( tr(txt), QClipboard::Clipboard ); if ( clipboard->supportsSelection() ) { clipboard->setText( tr(txt), QClipboard::Selection ); } } //---------------------------------------------------------------------------- void QAsmView::loadHighlightToClipboard(void) { if ( !textIsHighlighted() ) { return; } int l, row, nrow; std::string txt; nrow = (viewHeight / pxLineSpacing) + 1; if ( nrow < 1 ) nrow = 1; for (row=0; row < nrow; row++) { l = lineOffset + row; if ( (l >= txtHlgtStartLine) && (l <= txtHlgtEndLine) ) { int hlgtXs, hlgtXe, hlgtXd; std::string s; bool addNewLine; if ( l == txtHlgtStartLine ) { hlgtXs = txtHlgtStartChar; } else { hlgtXs = 0; } if ( l == txtHlgtEndLine ) { hlgtXe = txtHlgtEndChar; addNewLine = false; } else { hlgtXe = (viewWidth / pxCharWidth) + 1; addNewLine = true; } hlgtXd = (hlgtXe - hlgtXs); if ( hlgtXs < asmEntry[l]->text.size() ) { s = asmEntry[l]->text.substr( hlgtXs, hlgtXd ); } txt.append(s); if ( addNewLine ) { txt.append("\n"); } } } //printf("Load Text to Clipboard:\n%s\n", txt.c_str() ); loadClipboard( txt.c_str() ); } //---------------------------------------------------------------------------- void QAsmView::mouseReleaseEvent(QMouseEvent * event) { int line; QPoint c = convPixToCursor( event->pos() ); line = lineOffset + c.y(); if ( event->button() == Qt::LeftButton ) { //printf("Left Button Release: (%i,%i)\n", c.x(), c.y() ); mouseLeftBtnDown = false; setHighlightEndCoord( c.x(), line ); loadHighlightToClipboard(); } } //---------------------------------------------------------------------------- void QAsmView::mousePressEvent(QMouseEvent * event) { int line; QPoint c = convPixToCursor( event->pos() ); line = lineOffset + c.y(); //printf("Mouse Button Pressed: 0x%x (%i,%i)\n", event->button(), c.x(), c.y() ); if ( event->button() == Qt::LeftButton ) { //printf("Left Button Pressed: (%i,%i)\n", c.x(), c.y() ); mouseLeftBtnDown = true; txtHlgtAnchorChar = c.x(); txtHlgtAnchorLine = line; setHighlightEndCoord( c.x(), line ); } selAddrLine = -1; selAddrChar = 0; selAddrWidth = 0; selAddrValue = -1; selAddrText[0] = 0; if ( line < asmEntry.size() ) { int i,j, addr = -1, addrTextLoc = -1, selChar; int symTextStart = -1, symTextEnd = -1; char addrClicked = 0; char stmp[64]; selChar = c.x(); if ( asmEntry[line]->type == dbg_asm_entry_t::ASM_TEXT ) { if ( selChar < (int)asmEntry[line]->text.size() ) { i = selChar; if ( asmEntry[line]->sym.name.size() > 0 ) { size_t subStrLoc = asmEntry[line]->text.find( asmEntry[line]->sym.name, 22 ); if ( (subStrLoc != std::string::npos) && (subStrLoc > 22) ) { //printf("Line:%i asmEntry DB Sym: %zi '%s'\n", line, subStrLoc, asmEntry[line]->sym.name.c_str() ); symTextStart = subStrLoc; symTextEnd = subStrLoc + asmEntry[line]->sym.name.size(); } } if ( (i >= symTextStart) && (i < symTextEnd) ) { selAddrLine = line; selAddrChar = symTextStart; selAddrWidth = symTextEnd - symTextStart; selAddrValue = addr = asmEntry[line]->sym.ofs; if ( selAddrWidth >= (int)sizeof(selAddrText) ) { selAddrWidth = sizeof(selAddrText)-1; } strncpy( selAddrText, asmEntry[line]->sym.name.c_str(), selAddrWidth ); selAddrText[ selAddrWidth ] = 0; } else if ( isxdigit( asmEntry[line]->text[i] ) ) { addrClicked = 1; addrTextLoc = i; while ( isxdigit( asmEntry[line]->text[i] ) ) { addrTextLoc = i; i--; } if ( asmEntry[line]->text[i] == '$' || asmEntry[line]->text[i] == ':' ) { i--; } else { addrClicked = 0; } if ( asmEntry[line]->text[i] == '#' ) { addrClicked = 0; } if ( addrClicked ) { j=0; i = addrTextLoc; while ( isxdigit( asmEntry[line]->text[i] ) ) { stmp[j] = asmEntry[line]->text[i]; i++; j++; } stmp[j] = 0; //printf("Addr: '%s'\n", stmp ); addr = strtol( stmp, NULL, 16 ); selAddrLine = line; selAddrChar = addrTextLoc; selAddrWidth = j; selAddrValue = addr; strcpy( selAddrText, stmp ); } } } } else if ( asmEntry[line]->type == dbg_asm_entry_t::SYMBOL_NAME ) { selAddrLine = line; selAddrChar = 0; selAddrValue = addr = asmEntry[line]->addr; if ( asmEntry[line]->text.size() > 0 ) { selAddrWidth = asmEntry[line]->text.size()-1; } else { selAddrWidth = 0; } if ( selAddrWidth >= (int)sizeof(selAddrText) ) { selAddrWidth = sizeof(selAddrText)-1; } strncpy( selAddrText, asmEntry[line]->text.c_str(), selAddrWidth ); selAddrText[ selAddrWidth ] = 0; } if ( addr < 0 ) { addr = asmEntry[line]->addr; selAddrLine = line; selAddrChar = 4; selAddrWidth = 4; selAddrValue = addr; sprintf( selAddrText, "%04X", addr ); } //printf("Line: '%s'\n", asmEntry[line]->text.c_str() ); if ( addr >= 0 ) { parent->setBookmarkSelectedAddress( addr ); } if ( selAddrText[0] != 0 ) { loadClipboard( selAddrText ); } } } //---------------------------------------------------------------------------- void QAsmView::wheelEvent(QWheelEvent *event) { QPoint numPixels = event->pixelDelta(); QPoint numDegrees = event->angleDelta(); if (!numPixels.isNull()) { wheelPixelCounter -= numPixels.y(); //printf("numPixels: (%i,%i) \n", numPixels.x(), numPixels.y() ); } else if (!numDegrees.isNull()) { //QPoint numSteps = numDegrees / 15; //printf("numSteps: (%i,%i) \n", numSteps.x(), numSteps.y() ); //printf("numDegrees: (%i,%i) %i\n", numDegrees.x(), numDegrees.y(), pxLineSpacing ); wheelPixelCounter -= (pxLineSpacing * numDegrees.y()) / (15*8); } //printf("Wheel Event: %i\n", wheelPixelCounter); if ( wheelPixelCounter >= pxLineSpacing ) { lineOffset += (wheelPixelCounter / pxLineSpacing); if ( lineOffset > maxLineOffset ) { lineOffset = maxLineOffset; } vbar->setValue( lineOffset ); wheelPixelCounter = wheelPixelCounter % pxLineSpacing; } else if ( wheelPixelCounter <= -pxLineSpacing ) { lineOffset += (wheelPixelCounter / pxLineSpacing); if ( lineOffset < 0 ) { lineOffset = 0; } vbar->setValue( lineOffset ); wheelPixelCounter = wheelPixelCounter % pxLineSpacing; } event->accept(); } //---------------------------------------------------------------------------- void QAsmView::contextMenuEvent(QContextMenuEvent *event) { int line; QAction *act; QMenu menu(this); QPoint c = convPixToCursor( event->pos() ); line = lineOffset + c.y(); ctxMenuAddr = -1; if ( line < asmEntry.size() ) { int addr; if ( selAddrValue < 0 ) { ctxMenuAddr = addr = asmEntry[line]->addr; } else { ctxMenuAddr = addr = selAddrValue; } act = new QAction(tr("Add Breakpoint"), &menu); menu.addAction(act); act->setShortcut( QKeySequence(tr("B"))); connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuAddBP(void)) ); act = new QAction(tr("Add Symbolic Debug Marker"), &menu); menu.addAction(act); act->setShortcut( QKeySequence(tr("S"))); connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuAddSym(void)) ); act = new QAction(tr("Add Bookmark"), &menu); menu.addAction(act); act->setShortcut( QKeySequence(tr("M"))); connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuAddBM(void)) ); act = new QAction(tr("Open Hex Editor"), &menu); menu.addAction(act); act->setShortcut( QKeySequence(tr("H"))); connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuOpenHexEdit(void)) ); menu.exec(event->globalPos()); } } //---------------------------------------------------------------------------- void QAsmView::drawText( QPainter *painter, int x, int y, const char *txt ) { int i=0; char c[2]; c[0] = 0; c[1] = 0; while ( txt[i] != 0 ) { c[0] = txt[i]; painter->drawText( x, y, tr(c) ); i++; x += pxCharWidth; } } //---------------------------------------------------------------------------- void QAsmView::paintEvent(QPaintEvent *event) { int x,y,l, row, nrow, selAddr; QPainter painter(this); QColor white("white"), black("black"), blue("blue"); QColor hlgtFG("white"), hlgtBG("blue"); bool forceDarkColor = false; bool txtHlgtSet = false; painter.setFont(font); viewWidth = event->rect().width(); viewHeight = event->rect().height(); nrow = (viewHeight / pxLineSpacing) + 1; if ( nrow < 1 ) nrow = 1; viewLines = nrow; if ( cursorPosY >= viewLines ) { cursorPosY = viewLines-1; } maxLineOffset = asmEntry.size() - nrow + 1; if ( maxLineOffset < 0 ) { maxLineOffset = 0; } if ( lineOffset < 0 ) { lineOffset = 0; } if ( lineOffset > maxLineOffset ) { lineOffset = maxLineOffset; } selAddr = parent->getBookmarkSelectedAddress(); painter.fillRect( 0, 0, viewWidth, viewHeight, this->palette().color(QPalette::Background) ); y = pxLineSpacing; txtHlgtSet = textIsHighlighted(); for (row=0; row < nrow; row++) { x = -pxLineXScroll; l = lineOffset + row; forceDarkColor = false; if ( asmPC != NULL ) { if ( l == asmPC->line ) { painter.fillRect( 0, y - pxLineSpacing + pxLineLead, viewWidth, pxLineSpacing, QColor("pink") ); forceDarkColor = true; } } if ( l < asmEntry.size() ) { if ( asmEntry[l]->type != dbg_asm_entry_t::ASM_TEXT ) { painter.fillRect( 0, y - pxLineSpacing + pxLineLead, viewWidth, pxLineSpacing, QColor("light blue") ); forceDarkColor = true; } if ( forceDarkColor ) { painter.setPen( black ); } else { painter.setPen( this->palette().color(QPalette::WindowText)); } drawText( &painter, x, y, asmEntry[l]->text.c_str() ); if ( (selAddrLine == l) ) { // Highlight ASM line for selected address. if ( !txtHlgtSet && (selAddr == selAddrValue) && (asmEntry[l]->text.size() >= (selAddrChar + selAddrWidth) ) && ( asmEntry[l]->text.compare( selAddrChar, selAddrWidth, selAddrText ) == 0 ) ) { int ax; ax = x + selAddrChar*pxCharWidth; painter.fillRect( ax, y - pxLineSpacing + pxLineLead, selAddrWidth*pxCharWidth, pxLineSpacing, blue ); painter.setPen( white ); drawText( &painter, ax, y, selAddrText ); painter.setPen( this->palette().color(QPalette::WindowText)); } } } y += pxLineSpacing; } y = pxLineSpacing; painter.setPen( hlgtFG ); if ( txtHlgtSet ) { for (row=0; row < nrow; row++) { x = -pxLineXScroll; l = lineOffset + row; if ( (l >= txtHlgtStartLine) && (l <= txtHlgtEndLine) ) { int ax, hlgtXs, hlgtXe, hlgtXd; std::string s; if ( l == txtHlgtStartLine ) { hlgtXs = txtHlgtStartChar; } else { hlgtXs = 0; } if ( l == txtHlgtEndLine ) { hlgtXe = txtHlgtEndChar; } else { hlgtXe = (viewWidth / pxCharWidth) + 1; } hlgtXd = (hlgtXe - hlgtXs); if ( hlgtXs < asmEntry[l]->text.size() ) { s = asmEntry[l]->text.substr( hlgtXs, hlgtXd ); } ax = x + (hlgtXs * pxCharWidth); painter.fillRect( ax, y - pxLineSpacing + pxLineLead, hlgtXd * pxCharWidth, pxLineSpacing, hlgtBG ); drawText( &painter, ax, y, s.c_str() ); } y += pxLineSpacing; } } } //---------------------------------------------------------------------------- // Bookmark Manager Methods //---------------------------------------------------------------------------- debuggerBookmarkManager_t::debuggerBookmarkManager_t(void) { internal_iter = bmMap.begin(); } //---------------------------------------------------------------------------- debuggerBookmarkManager_t::~debuggerBookmarkManager_t(void) { this->clear(); } //---------------------------------------------------------------------------- void debuggerBookmarkManager_t::clear(void) { std::map ::iterator it; for (it=bmMap.begin(); it!=bmMap.end(); it++) { delete it->second; } bmMap.clear(); internal_iter = bmMap.begin(); } //---------------------------------------------------------------------------- int debuggerBookmarkManager_t::addBookmark( int addr, const char *name ) { int retval = -1; debuggerBookmark_t *bm = NULL; std::map ::iterator it; it = bmMap.find( addr ); if ( it == bmMap.end() ) { bm = new debuggerBookmark_t(); bm->addr = addr; if ( name != NULL ) { bm->name.assign( name ); } bmMap[ addr ] = bm; retval = 0; } return retval; } //---------------------------------------------------------------------------- int debuggerBookmarkManager_t::editBookmark( int addr, const char *name ) { int retval = -1; debuggerBookmark_t *bm = NULL; std::map ::iterator it; it = bmMap.find( addr ); if ( it != bmMap.end() ) { bm = it->second; if ( name != NULL ) { bm->name.assign( name ); } retval = 0; } return retval; } //---------------------------------------------------------------------------- int debuggerBookmarkManager_t::deleteBookmark( int addr ) { int retval = -1; std::map ::iterator it; it = bmMap.find( addr ); if ( it != bmMap.end() ) { bmMap.erase(it); retval = 0; } return retval; } //---------------------------------------------------------------------------- int debuggerBookmarkManager_t::size(void) { return bmMap.size(); } //---------------------------------------------------------------------------- debuggerBookmark_t *debuggerBookmarkManager_t::begin(void) { internal_iter = bmMap.begin(); if ( internal_iter == bmMap.end() ) { return NULL; } return internal_iter->second; } //---------------------------------------------------------------------------- debuggerBookmark_t *debuggerBookmarkManager_t::next(void) { if ( internal_iter == bmMap.end() ) { return NULL; } internal_iter++; if ( internal_iter == bmMap.end() ) { return NULL; } return internal_iter->second; } //---------------------------------------------------------------------------- debuggerBookmark_t *debuggerBookmarkManager_t::getAddr( int addr ) { std::map ::iterator it; it = bmMap.find( addr ); if ( it != bmMap.end() ) { return it->second; } return NULL; } //---------------------------------------------------------------------------- DebuggerStackDisplay::DebuggerStackDisplay(QWidget *parent) : QPlainTextEdit(parent) { stackBytesPerLine = 4; showAddrs = true; } //---------------------------------------------------------------------------- DebuggerStackDisplay::~DebuggerStackDisplay(void) { } //---------------------------------------------------------------------------- void DebuggerStackDisplay::keyPressEvent(QKeyEvent *event) { //printf("Debug Stack Window Key Press: 0x%x \n", event->key() ); if ( (event->key() >= Qt::Key_1) && ( (event->key() < Qt::Key_9) ) ) { stackBytesPerLine = event->key() - Qt::Key_0; updateText(); } else if ( event->key() == Qt::Key_A ) { showAddrs = !showAddrs; updateText(); } } //---------------------------------------------------------------------------- void DebuggerStackDisplay::contextMenuEvent(QContextMenuEvent *event) { QAction *act; QMenu menu(this); QMenu *subMenu; QActionGroup *group; QAction *bytesPerLineAct[4]; act = new QAction(tr("Show Addresses"), &menu); act->setCheckable(true); act->setChecked(showAddrs); connect( act, SIGNAL(triggered(void)), this, SLOT(toggleShowAddr(void)) ); menu.addAction( act ); subMenu = menu.addMenu(tr("Display Bytes Per Line")); group = new QActionGroup(&menu); group->setExclusive(true); for (int i=0; i<4; i++) { char stmp[8]; sprintf( stmp, "%i", i+1 ); bytesPerLineAct[i] = new QAction(tr(stmp), &menu); bytesPerLineAct[i]->setCheckable(true); group->addAction(bytesPerLineAct[i]); subMenu->addAction(bytesPerLineAct[i]); bytesPerLineAct[i]->setChecked( stackBytesPerLine == (i+1) ); } connect( bytesPerLineAct[0], SIGNAL(triggered(void)), this, SLOT(sel1BytePerLine(void)) ); connect( bytesPerLineAct[1], SIGNAL(triggered(void)), this, SLOT(sel2BytesPerLine(void)) ); connect( bytesPerLineAct[2], SIGNAL(triggered(void)), this, SLOT(sel3BytesPerLine(void)) ); connect( bytesPerLineAct[3], SIGNAL(triggered(void)), this, SLOT(sel4BytesPerLine(void)) ); menu.exec(event->globalPos()); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::toggleShowAddr(void) { showAddrs = !showAddrs; updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::sel1BytePerLine(void) { stackBytesPerLine = 1; updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::sel2BytesPerLine(void) { stackBytesPerLine = 2; updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::sel3BytesPerLine(void) { stackBytesPerLine = 3; updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::sel4BytesPerLine(void) { stackBytesPerLine = 4; updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::updateText(void) { char stmp[128]; int stackPtr = X.S | 0x0100; std::string stackLine; stackPtr++; if ( stackPtr <= 0x01FF ) { if ( showAddrs || (stackBytesPerLine <= 1) ) { sprintf( stmp, "%03X: %02X", stackPtr, GetMem(stackPtr) ); } else { sprintf( stmp, " %02X", GetMem(stackPtr) ); } stackLine.assign( stmp ); for (int i = 1; i < 128; i++) { //tmp = ((tmp+1)|0x0100)&0x01FF; //increment and fix pointer to $0100-$01FF range stackPtr++; if (stackPtr > 0x1FF) break; if ( stackBytesPerLine > 1 ) { if ((i % stackBytesPerLine) == 0) { if ( showAddrs ) { sprintf( stmp, "\n%03X: %02X", stackPtr, GetMem(stackPtr) ); } else { sprintf( stmp, "\n %02X", GetMem(stackPtr) ); } } else { sprintf( stmp, ",%02X", GetMem(stackPtr) ); } } else { sprintf( stmp, "\n%03X: %02X", stackPtr, GetMem(stackPtr) ); } stackLine.append( stmp ); //printf("Stack $%X: %s\n", stackPtr, stmp ); } } setPlainText( tr(stackLine.c_str()) ); } //----------------------------------------------------------------------------