/* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: * Copyright (C) 2020 mjbudd77 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // ConsoleDebugger.cpp // #include #include #include #include #include #include #include #include #include #include #include #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/os_utils.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" #include "Qt/ColorMenu.h" // Where are these defined? extern int vblankScanLines; extern int vblankPixel; debuggerBookmarkManager_t dbgBmMgr; static ConsoleDebugger* dbgWin = NULL; static void DeleteBreak(int sel); static bool waitingAtBp = false; static bool bpDebugEnable = true; static int lastBpIdx = 0; //---------------------------------------------------------------------------- ConsoleDebugger::ConsoleDebugger(QWidget *parent) : QDialog( parent, Qt::Window ) { QMenuBar *menuBar; QSettings settings; QFont cpuFont; std::string fontString; g_config->getOption("SDL.DebuggerCpuStatusFont", &fontString); if ( fontString.size() > 0 ) { cpuFont.fromString( QString::fromStdString( fontString ) ); } else { cpuFont.setFamily("Courier New"); cpuFont.setStyle( QFont::StyleNormal ); cpuFont.setStyleHint( QFont::Monospace ); } font.setFamily("Courier New"); font.setStyle( QFont::StyleNormal ); font.setStyleHint( QFont::Monospace ); QFontMetrics fm(font); setWindowTitle("6502 Debugger"); menuBar = buildMenuBar(); toolBar = buildToolBar(); mainLayoutv = new QVBoxLayout(); mainLayouth = new QSplitter( Qt::Horizontal ); mainLayouth->setOpaqueResize(true); mainLayoutv->setMenuBar( menuBar ); mainLayoutv->addWidget( toolBar ); mainLayoutv->addWidget( mainLayouth ); for (int i=0; i<2; i++) { vsplitter[i] = new QSplitter( Qt::Vertical ); for (int j=0; j<4; j++) { char stmp[64]; tabView[i][j] = new DebuggerTabWidget(i,j); sprintf( stmp, "debuggerTabView%i%i\n", i+1, j+1 ); tabView[i][j]->setObjectName( tr(stmp) ); vsplitter[i]->addWidget( tabView[i][j] ); } } buildAsmViewDisplay(); buildCpuListDisplay(); buildPpuListDisplay(); buildBpListDisplay(); buildBmListDisplay(); mainLayouth->addWidget( asmViewContainerWidget ); mainLayouth->addWidget( vsplitter[0] ); mainLayouth->addWidget( vsplitter[1] ); setLayout( mainLayoutv ); loadDisplayViews(); windowUpdateReq = true; dbgWin = 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 { int autoLoadDebug; g_config->getOption( "SDL.AutoLoadDebugFiles", &autoLoadDebug ); if ( autoLoadDebug ) { loadGameDebugBreakpoints(); } } //restoreGeometry(settings.value("debugger/geometry").toByteArray()); setCpuStatusFont( cpuFont ); opcodeColorAct->connectColor( &asmView->opcodeColor ); addressColorAct->connectColor( &asmView->addressColor ); immediateColorAct->connectColor( &asmView->immediateColor ); labelColorAct->connectColor( &asmView->labelColor ); commentColorAct->connectColor( &asmView->commentColor); pcColorAct->connectColor( &asmView->pcBgColor); } //---------------------------------------------------------------------------- ConsoleDebugger::~ConsoleDebugger(void) { std::list ::iterator it; //printf("Destroy Debugger Window\n"); periodicTimer->stop(); saveDisplayViews(); if ( dbgWin == this ) { dbgWin = NULL; } if ( dbgWin == NULL ) { saveGameDebugBreakpoints(); debuggerClearAllBreakpoints(); debuggerClearAllBookmarks(); if ( waitingAtBp ) { FCEUI_SetEmulationPaused(0); } } } //---------------------------------------------------------------------------- void ConsoleDebugger::closeEvent(QCloseEvent *event) { //printf("Debugger Close Window Event\n"); saveDisplayViews(); done(0); deleteLater(); event->accept(); } //---------------------------------------------------------------------------- void ConsoleDebugger::closeWindow(void) { saveDisplayViews(); //printf("Close Window\n"); done(0); deleteLater(); } //---------------------------------------------------------------------------- QMenuBar *ConsoleDebugger::buildMenuBar(void) { QMenu *fileMenu, *viewMenu, *debugMenu, *optMenu, *symMenu, *subMenu, *visMenu; QActionGroup *actGroup; QAction *act; int opt, useNativeMenuBar=0; QMenuBar *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 //----------------------------------------------------------------------- // File fileMenu = menuBar->addMenu(tr("&File")); // File -> Close act = new QAction(tr("&Close"), this); act->setShortcut(QKeySequence::Close); act->setStatusTip(tr("Close Window")); connect(act, SIGNAL(triggered()), this, SLOT(closeWindow(void)) ); fileMenu->addAction(act); // View viewMenu = menuBar->addMenu(tr("&View")); // View -> Go to Address act = new QAction(tr("&Go to Address"), this); act->setShortcut( QKeySequence(tr("Ctrl+A") )); act->setStatusTip(tr("&Go to Address")); //act->setIcon( QIcon(":icons/find.png") ); act->setIcon( QIcon(":icons/JumpTarget.png") ); connect(act, SIGNAL(triggered()), this, SLOT(openGotoAddrDialog(void)) ); viewMenu->addAction(act); // View -> Go to PC act = new QAction(tr("Go to &PC"), this); act->setShortcut( QKeySequence(tr("Ctrl+G") )); act->setStatusTip(tr("Go to &PC")); //act->setIcon( QIcon(":icons/JumpTarget.png") ); connect(act, SIGNAL(triggered()), this, SLOT(seekPCCB(void)) ); viewMenu->addAction(act); // View -> Change PC act = new QAction(tr("&Change PC"), this); act->setShortcut( QKeySequence(tr("Ctrl+Shift+G") )); act->setStatusTip(tr("&Change PC")); //act->setIcon( QIcon(":icons/JumpTarget.png") ); connect(act, SIGNAL(triggered()), this, SLOT(openChangePcDialog(void)) ); viewMenu->addAction(act); viewMenu->addSeparator(); // View -> Navigate Back act = new QAction(tr("Navigate &Back"), this); act->setShortcut( QKeySequence(tr("Ctrl+Left") )); act->setStatusTip(tr("Navigate Back")); //act->setIcon( QIcon(":icons/JumpTarget.png") ); connect(act, SIGNAL(triggered()), this, SLOT(navHistBackCB(void)) ); viewMenu->addAction(act); // View -> Navigate Forward act = new QAction(tr("Navigate &Forward"), this); act->setShortcut( QKeySequence(tr("Ctrl+Right") )); act->setStatusTip(tr("Navigate Forward")); //act->setIcon( QIcon(":icons/JumpTarget.png") ); connect(act, SIGNAL(triggered()), this, SLOT(navHistForwardCB(void)) ); viewMenu->addAction(act); viewMenu->addSeparator(); // View -> Layout visMenu = viewMenu->addMenu( tr("&Layout Presets") ); g_config->getOption( "SDL.DebuggerLayoutOpt", &opt ); actGroup = new QActionGroup(this); actGroup->setExclusive(true); // View -> Layout -> Compact act = new QAction(tr("&Compact"), this); //act->setStatusTip(tr("Compact")); connect( act, &QAction::triggered, [this]{ setLayoutOption(1); } ); actGroup->addAction(act); visMenu->addAction(act); // View -> Layout -> Compact Split act = new QAction(tr("Compact &Split"), this); //act->setStatusTip(tr("1 Tabbed Vertical Column with 2 Sections")); connect( act, &QAction::triggered, [this]{ setLayoutOption(2); } ); actGroup->addAction(act); visMenu->addAction(act); // View -> Layout -> Wide act = new QAction(tr("&Wide"), this); //act->setStatusTip(tr("2 Tabbed Vertical Columns with 3 Sections")); connect( act, &QAction::triggered, [this]{ setLayoutOption(3); } ); actGroup->addAction(act); visMenu->addAction(act); // View -> Layout -> Wide Quad act = new QAction(tr("Wide &Quad"), this); //act->setStatusTip(tr("2 Tabbed Vertical Columns with 4 Sections")); connect( act, &QAction::triggered, [this]{ setLayoutOption(4); } ); actGroup->addAction(act); visMenu->addAction(act); // View -> Font Selection subMenu = viewMenu->addMenu(tr("&Font Selection")); // Options -> Font Selection -> Assembly View act = new QAction(tr("&Assembly View"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Set Assembly View Font")); connect( act, SIGNAL(triggered(void)), this, SLOT(changeAsmFontCB(void)) ); subMenu->addAction(act); // View -> Font Selection -> Stack View act = new QAction(tr("&Stack View"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Set Stack View Font")); connect( act, SIGNAL(triggered(void)), this, SLOT(changeStackFontCB(void)) ); subMenu->addAction(act); // View -> Font Selection -> CPU Data View act = new QAction(tr("&CPU Data View"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Set CPU View Font")); connect( act, SIGNAL(triggered(void)), this, SLOT(changeCpuFontCB(void)) ); subMenu->addAction(act); // View -> Color Selection subMenu = viewMenu->addMenu(tr("&Color Selection")); // View -> Color Selection -> Opcodes opcodeColorAct = new ColorMenuItem( tr("&Opcodes"), "SDL.AsmSyntaxColorOpcode", this); subMenu->addAction(opcodeColorAct); // View -> Color Selection -> Address Values addressColorAct = new ColorMenuItem( tr("&Address Values"), "SDL.AsmSyntaxColorAddress", this); subMenu->addAction(addressColorAct); // View -> Color Selection -> Immediate Values immediateColorAct = new ColorMenuItem( tr("&Immediate Values"), "SDL.AsmSyntaxColorImmediate", this); subMenu->addAction(immediateColorAct); // View -> Color Selection -> Labels labelColorAct = new ColorMenuItem( tr("&Labels"), "SDL.AsmSyntaxColorLabel", this); subMenu->addAction(labelColorAct); // View -> Color Selection -> Comments commentColorAct = new ColorMenuItem( tr("&Comments"), "SDL.AsmSyntaxColorComment", this); subMenu->addAction(commentColorAct); subMenu->addSeparator(); // View -> Color Selection -> (PC) Active Statement pcColorAct = new ColorMenuItem( tr("(&PC) Active Statement BG"), "SDL.AsmSyntaxColorPC", this); subMenu->addAction(pcColorAct); viewMenu->addSeparator(); // View -> PC Position subMenu = viewMenu->addMenu(tr("&PC Line Positioning")); actGroup = new QActionGroup(this); actGroup->setExclusive(true); g_config->getOption( "SDL.DebuggerPCPlacement", &opt ); // View -> PC Position -> Top Line act = new QAction(tr("&Top Line"), this); act->setStatusTip(tr("Top Line")); act->setCheckable(true); act->setChecked( opt == 0 ); connect( act, SIGNAL(triggered()), this, SLOT(pcSetPlaceTop(void)) ); actGroup->addAction(act); subMenu->addAction(act); // View -> PC Position -> Upper Mid-Line act = new QAction(tr("&Upper Mid-Line"), this); act->setStatusTip(tr("Upper Mid-Line")); act->setCheckable(true); act->setChecked( opt == 1 ); connect( act, SIGNAL(triggered()), this, SLOT(pcSetPlaceUpperMid(void)) ); actGroup->addAction(act); subMenu->addAction(act); // View -> PC Position -> Center Line act = new QAction(tr("&Center Line"), this); act->setStatusTip(tr("Center Line")); act->setCheckable(true); act->setChecked( opt == 2 ); connect( act, SIGNAL(triggered()), this, SLOT(pcSetPlaceCenter(void)) ); actGroup->addAction(act); subMenu->addAction(act); // View -> PC Position -> Lower Mid-Line act = new QAction(tr("&Lower Mid-Line"), this); act->setStatusTip(tr("Lower Mid-Line")); act->setCheckable(true); act->setChecked( opt == 3 ); connect( act, SIGNAL(triggered()), this, SLOT(pcSetPlaceLowerMid(void)) ); actGroup->addAction(act); subMenu->addAction(act); // View -> PC Position -> Bottom act = new QAction(tr("&Bottom Line"), this); act->setStatusTip(tr("Bottom Line")); act->setCheckable(true); act->setChecked( opt == 4 ); connect( act, SIGNAL(triggered()), this, SLOT(pcSetPlaceBottom(void)) ); actGroup->addAction(act); subMenu->addAction(act); // View -> PC Position -> Custom Line act = new QAction(tr("Custom Line &Offset"), this); act->setStatusTip(tr("Custom Line Offset")); act->setChecked( opt == 5 ); act->setCheckable(true); connect( act, SIGNAL(triggered()), this, SLOT(pcSetPlaceCustom(void)) ); actGroup->addAction(act); subMenu->addAction(act); // View -> Show Byte Codes act = new QAction(tr("Show &Byte Codes"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Show &Byte Codes")); act->setCheckable(true); act->setChecked(false); connect( act, SIGNAL(triggered(bool)), this, SLOT(displayByteCodesCB(bool)) ); viewMenu->addAction(act); // View -> Display ROM Offsets act = new QAction(tr("Show ROM &Offsets"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Show ROM &Offsets")); act->setCheckable(true); act->setChecked(false); connect( act, SIGNAL(triggered(bool)), this, SLOT(displayROMoffsetCB(bool)) ); viewMenu->addAction(act); // Debug debugMenu = menuBar->addMenu(tr("&Debug")); // Debug -> Run act = new QAction(tr("&Run"), this); act->setShortcut(QKeySequence( tr("F5") ) ); act->setStatusTip(tr("Run")); //act->setIcon( style()->standardIcon( QStyle::SP_MediaPlay ) ); act->setIcon( QIcon(":icons/debug-run.png") ); 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")); act->setIcon( QIcon(":icons/StepInto.png") ); 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")); act->setIcon( QIcon(":icons/StepOut.png") ); 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")); act->setIcon( QIcon(":icons/StepOver.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugStepOverCB(void)) ); debugMenu->addAction(act); // Debug -> Run to Selected Line act = new QAction(tr("Run to S&elected Line"), this); act->setShortcut(QKeySequence( tr("F1") ) ); act->setStatusTip(tr("Run to Selected Line")); act->setIcon( QIcon(":icons/arrow-cursor.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugRunToCursorCB(void)) ); debugMenu->addAction(act); // Debug -> Run Line act = new QAction(tr("Run &Line"), this); act->setShortcut(QKeySequence( tr("F6") ) ); act->setStatusTip(tr("Run Line")); act->setIcon( QIcon(":icons/RunPpuScanline.png") ); 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")); act->setIcon( QIcon(":icons/RunPpuFrame.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugRunLine128CB(void)) ); debugMenu->addAction(act); debugMenu->addSeparator(); // Debug -> Break on Bad Opcodes act = new QAction(tr("Break on Bad &Opcodes"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Break on Bad Opcodes")); act->setCheckable(true); act->setChecked( FCEUI_Debugger().badopbreak ); connect( act, SIGNAL(triggered(bool)), this, SLOT(breakOnBadOpcodeCB(bool)) ); debugMenu->addAction(act); // Debug -> Break on Unlogged Code act = new QAction(tr("Break on Unlogged &Code"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Break on Unlogged Code")); act->setCheckable(true); act->setChecked( break_on_unlogged_code ); connect( act, SIGNAL(triggered(bool)), this, SLOT(breakOnNewCodeCB(bool)) ); debugMenu->addAction(act); // Debug -> Break on Unlogged Data act = new QAction(tr("Break on Unlogged &Data"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Break on Unlogged Data")); act->setCheckable(true); act->setChecked( break_on_unlogged_data ); connect( act, SIGNAL(triggered(bool)), this, SLOT(breakOnNewDataCB(bool)) ); debugMenu->addAction(act); // Debug -> Reset Counters act = new QAction(tr("Reset &Counters"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Reset Counters")); act->setCheckable(false); act->setIcon( style()->standardIcon( QStyle::SP_BrowserReload ) ); connect( act, SIGNAL(triggered(void)), this, SLOT(resetCountersCB(void)) ); debugMenu->addAction(act); // Options optMenu = menuBar->addMenu(tr("&Options")); // Options -> Open Debugger on ROM Load g_config->getOption( "SDL.AutoOpenDebugger", &opt ); act = new QAction(tr("&Open Debugger on ROM Load"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("&Reload")); act->setCheckable(true); act->setChecked( opt ? true : false ); connect( act, SIGNAL(triggered(bool)), this, SLOT(autoOpenDebugCB(bool)) ); optMenu->addAction(act); // Options -> Load .DEB g_config->getOption( "SDL.AutoLoadDebugFiles", &opt ); act = new QAction(tr("&Load .DEB on ROM Load"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("&Load .DEB on ROM Load")); act->setCheckable(true); act->setChecked( opt ? true : false ); connect( act, SIGNAL(triggered(bool)), this, SLOT(debFileAutoLoadCB(bool)) ); optMenu->addAction(act); optMenu->addSeparator(); // Symbols symMenu = menuBar->addMenu(tr("&Symbols")); // Symbols -> Reload act = new QAction(tr("&Reload"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("&Reload")); //act->setCheckable(true); //act->setChecked( break_on_unlogged_data ); connect( act, SIGNAL(triggered(void)), this, SLOT(reloadSymbolsCB(void)) ); symMenu->addAction(act); symMenu->addSeparator(); // Symbols -> Symbolic Debug act = new QAction(tr("&Symbolic Debug"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("&Symbolic Debug")); act->setCheckable(true); act->setChecked(true); connect( act, SIGNAL(triggered(bool)), this, SLOT(symbolDebugEnableCB(bool)) ); symMenu->addAction(act); // Symbols -> Symbolic Debug act = new QAction(tr("&Register Names"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("&Register Names")); act->setCheckable(true); act->setChecked(true); connect( act, SIGNAL(triggered(bool)), this, SLOT(registerNameEnableCB(bool)) ); symMenu->addAction(act); //----------------------------------------------------------------------- // Menu End //----------------------------------------------------------------------- return menuBar; } //---------------------------------------------------------------------------- QToolBar *ConsoleDebugger::buildToolBar(void) { QAction *act; QToolBar *toolBar = new QToolBar(this); //----------------------------------------------------------------------- // Tool Bar Setup Start //----------------------------------------------------------------------- // File -> Go to Address act = new QAction(tr("&Go to Address"), this); //act->setShortcut( QKeySequence(tr("Ctrl+A") )); act->setStatusTip(tr("&Go to Address")); //act->setIcon( QIcon(":icons/find.png") ); act->setIcon( QIcon(":icons/JumpTarget.png") ); connect(act, SIGNAL(triggered()), this, SLOT(openGotoAddrDialog(void)) ); toolBar->addAction(act); toolBar->addSeparator(); // Debug -> Run act = new QAction(tr("&Run"), this); //act->setShortcut(QKeySequence( tr("F5") ) ); act->setStatusTip(tr("Run")); //act->setIcon( style()->standardIcon( QStyle::SP_MediaPlay ) ); act->setIcon( QIcon(":icons/debug-run.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugRunCB(void)) ); toolBar->addAction(act); // Debug -> Step Into act = new QAction(tr("Step &Into"), this); //act->setShortcut(QKeySequence( tr("F11") ) ); act->setStatusTip(tr("Step Into")); act->setIcon( QIcon(":icons/StepInto.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugStepIntoCB(void)) ); toolBar->addAction(act); // Debug -> Step Out act = new QAction(tr("&Step Out"), this); //act->setShortcut(QKeySequence( tr("Shift+F11") ) ); act->setStatusTip(tr("Step Out")); act->setIcon( QIcon(":icons/StepOut.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugStepOutCB(void)) ); toolBar->addAction(act); // Debug -> Step Over act = new QAction(tr("Step &Over"), this); //act->setShortcut(QKeySequence( tr("F10") ) ); act->setStatusTip(tr("Step Over")); act->setIcon( QIcon(":icons/StepOver.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugStepOverCB(void)) ); toolBar->addAction(act); toolBar->addSeparator(); // Debug -> Run Line act = new QAction(tr("Run &Line"), this); //act->setShortcut(QKeySequence( tr("F6") ) ); act->setStatusTip(tr("Run Line")); act->setIcon( QIcon(":icons/RunPpuScanline.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugRunLineCB(void)) ); toolBar->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")); act->setIcon( QIcon(":icons/RunPpuFrame.png") ); connect( act, SIGNAL(triggered()), this, SLOT(debugRunLine128CB(void)) ); toolBar->addAction(act); toolBar->addSeparator(); // Debug -> Reset Counters act = new QAction(tr("Reset &Counters"), this); //act->setShortcut(QKeySequence( tr("F7") ) ); act->setStatusTip(tr("Reset Counters")); act->setIcon( style()->standardIcon( QStyle::SP_BrowserReload ) ); connect( act, SIGNAL(triggered(void)), this, SLOT(resetCountersCB(void)) ); toolBar->addAction(act); toolBar->addSeparator(); //----------------------------------------------------------------------- // Tool Bar Setup End //----------------------------------------------------------------------- return toolBar; } //---------------------------------------------------------------------------- void ConsoleDebugger::buildAsmViewDisplay(void) { QGridLayout *grid; 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 ); asmDpyVbox = new QVBoxLayout(); hbar->setMinimum(0); hbar->setMaximum(100); vbar->setMinimum(0); vbar->setMaximum( 0x10000 ); asmDpyVbox->addLayout( grid, 100 ); asmDpyVbox->addWidget( asmLineSelLbl, 1 ); asmDpyVbox->addWidget( emuStatLbl , 1 ); asmViewContainerWidget = new QWidget(); asmViewContainerWidget->setLayout( asmDpyVbox ); } //---------------------------------------------------------------------------- void ConsoleDebugger::buildCpuListDisplay(void) { QVBoxLayout *vbox, *vbox1; QHBoxLayout *hbox, *hbox1; QGridLayout *grid; QLabel *lbl; QFont cpuFont; std::string fontString; int fontCharWidth; g_config->getOption("SDL.DebuggerCpuStatusFont", &fontString); if ( fontString.size() > 0 ) { cpuFont.fromString( QString::fromStdString( fontString ) ); } else { cpuFont.setFamily("Courier New"); cpuFont.setStyle( QFont::StyleNormal ); cpuFont.setStyleHint( QFont::Monospace ); } QFontMetrics fm(cpuFont); fontCharWidth = fm.averageCharWidth(); vbox = new QVBoxLayout(); vbox1 = new QVBoxLayout(); hbox1 = new QHBoxLayout(); cpuFrame = new QFrame(); grid = new QGridLayout(); cpuFrame->setObjectName( tr("debuggerStatusCPU") ); cpuFrame->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ); hbox1->addLayout( vbox, 1 ); vbox->addLayout( grid ); vbox1->addLayout( hbox1, 1 ); vbox1->addStretch( 10 ); cpuFrame->setLayout( vbox1 ); hbox = new QHBoxLayout(); lbl = new QLabel( tr("PC:") ); lbl->setToolTip( tr("Program Counter Register") ); pcEntry = new QLineEdit(); pcEntry->setFont( cpuFont ); pcEntry->setMaxLength( 4 ); pcEntry->setInputMask( ">HHHH;0" ); pcEntry->setAlignment(Qt::AlignCenter); pcEntry->setMinimumWidth( 6 * fontCharWidth ); pcEntry->setMaximumWidth( 6 * fontCharWidth ); pcEntry->setToolTip( tr("Program Counter Register Hex Value") ); hbox->addWidget( lbl ); hbox->addWidget( pcEntry, 1, Qt::AlignLeft ); grid->addLayout( hbox, 0, 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:") ); lbl->setToolTip( tr("Accumulator Register") ); regAEntry = new QLineEdit(); regAEntry->setFont( cpuFont ); regAEntry->setMaxLength( 2 ); regAEntry->setInputMask( ">HH;0" ); regAEntry->setAlignment(Qt::AlignCenter); regAEntry->setMinimumWidth( 4 * fontCharWidth ); regAEntry->setMaximumWidth( 4 * fontCharWidth ); regAEntry->setToolTip( tr("Accumulator Register Hex Value") ); hbox->addWidget( lbl ); hbox->addWidget( regAEntry, 1, Qt::AlignLeft ); grid->addLayout( hbox, 0, 1 ); hbox = new QHBoxLayout(); lbl = new QLabel( tr("X:") ); lbl->setToolTip( tr("X Index Register") ); regXEntry = new QLineEdit(); regXEntry->setFont( cpuFont ); regXEntry->setMaxLength( 2 ); regXEntry->setInputMask( ">HH;0" ); regXEntry->setAlignment(Qt::AlignCenter); regXEntry->setMinimumWidth( 4 * fontCharWidth ); regXEntry->setMaximumWidth( 4 * fontCharWidth ); regXEntry->setToolTip( tr("X Index Register Hex Value") ); hbox->addWidget( lbl ); hbox->addWidget( regXEntry, 1, Qt::AlignLeft ); grid->addLayout( hbox, 0, 2 ); hbox = new QHBoxLayout(); lbl = new QLabel( tr("Y:") ); lbl->setToolTip( tr("Y Index Register") ); regYEntry = new QLineEdit(); regYEntry->setFont( cpuFont ); regYEntry->setMaxLength( 2 ); regYEntry->setInputMask( ">HH;0" ); regYEntry->setAlignment(Qt::AlignCenter); regYEntry->setMinimumWidth( 4 * fontCharWidth ); regYEntry->setMaximumWidth( 4 * fontCharWidth ); regYEntry->setToolTip( tr("Y Index Register Hex Value") ); hbox->addWidget( lbl ); hbox->addWidget( regYEntry, 1, Qt::AlignLeft ); grid->addLayout( hbox, 0, 3 ); QHBoxLayout *regPHbox = new QHBoxLayout(); lbl = new QLabel( tr("P:") ); lbl->setToolTip( tr("Status Register") ); regPEntry = new QLineEdit(); regPEntry->setFont( cpuFont ); regPEntry->setMaxLength( 2 ); regPEntry->setInputMask( ">HH;0" ); regPEntry->setAlignment(Qt::AlignCenter); regPEntry->setMinimumWidth( 4 * fontCharWidth ); regPEntry->setMaximumWidth( 4 * fontCharWidth ); regPEntry->setToolTip( tr("Status Register Hex Value") ); regPHbox->addWidget( lbl ); regPHbox->addWidget( regPEntry, 1, Qt::AlignLeft ); //grid->addLayout( regPHbox, 0, 4 ); cpuCyclesLbl1 = new QLabel( tr("CPU Cycles:") ); //cpuCyclesLbl2 = new QLabel( tr("(+0):") ); cpuCyclesVal = new QLineEdit( tr("(+0):") ); cpuInstrsLbl1 = new QLabel( tr("Instructions:") ); //cpuInstrsLbl2 = new QLabel( tr("(+0):") ); cpuInstrsVal = new QLineEdit( 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") ); cpuCyclesVal->setFont(cpuFont); cpuCyclesVal->setReadOnly(true); cpuCyclesVal->setMinimumWidth( 24 * fontCharWidth ); cpuInstrsVal->setFont(cpuFont); cpuInstrsVal->setReadOnly(true); cpuInstrsVal->setMinimumWidth( 24 * fontCharWidth ); //hbox = new QHBoxLayout(); //hbox->addWidget( cpuCyclesLbl1, 1 ); //hbox->addWidget( cpuCyclesVal , 4 ); //grid->addLayout( hbox, 1, 0, 1, 4 ); //hbox = new QHBoxLayout(); //hbox->addWidget( cpuInstrsLbl1, 1 ); //hbox->addWidget( cpuInstrsVal , 4 ); //grid->addLayout( hbox, 2, 0, 1, 4 ); grid->addWidget( cpuCyclesLbl1, 1, 0, 1, 1 ); grid->addWidget( cpuCyclesVal , 1, 1, 1, 3 ); grid->addWidget( cpuInstrsLbl1, 2, 0, 1, 1 ); grid->addWidget( cpuInstrsVal , 2, 1, 1, 3 ); stackFrame = new QGroupBox(tr("Stack $0100")); stackText = new DebuggerStackDisplay(this); hbox = new QHBoxLayout(); hbox->addWidget( stackText ); //vbox2->addWidget( stackFrame ); hbox1->addWidget( stackFrame, 10 ); stackFrame->setLayout( hbox ); //stackText->setFont(font); // Stack font is now set in constructor stackText->setReadOnly(true); stackText->setWordWrapMode( QTextOption::NoWrap ); //stackText->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); //stackText->setMaximumWidth( 16 * fontCharWidth ); stackFrame->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ); sfFrame = new QGroupBox(tr("Status Flags")); grid = new QGridLayout(); sfFrame->setLayout( grid ); sfFrame->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ); 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") ); N_chkbox->setToolTip( tr("Negative" ) ); V_chkbox->setToolTip( tr("Overflow" ) ); U_chkbox->setToolTip( tr("Unused" ) ); B_chkbox->setToolTip( tr("Break" ) ); D_chkbox->setToolTip( tr("Decimal" ) ); I_chkbox->setToolTip( tr("Interrupt") ); Z_chkbox->setToolTip( tr("Zero" ) ); C_chkbox->setToolTip( tr("Carry" ) ); grid->addLayout( regPHbox, 0, 0, 2, 1); grid->addWidget( N_chkbox, 0, 1, Qt::AlignLeft ); grid->addWidget( V_chkbox, 0, 2, Qt::AlignLeft ); grid->addWidget( U_chkbox, 0, 3, Qt::AlignLeft ); grid->addWidget( B_chkbox, 0, 4, Qt::AlignLeft ); grid->addWidget( D_chkbox, 1, 1, Qt::AlignLeft ); grid->addWidget( I_chkbox, 1, 2, Qt::AlignLeft ); grid->addWidget( Z_chkbox, 1, 3, Qt::AlignLeft ); grid->addWidget( C_chkbox, 1, 4, Qt::AlignLeft ); vbox->addWidget( sfFrame); } //---------------------------------------------------------------------------- void ConsoleDebugger::buildPpuListDisplay(void) { QVBoxLayout *vbox, *vbox1; QHBoxLayout /**hbox,*/ *hbox1; QGridLayout *grid, *grid1; QGroupBox *ctlFrame; QLabel *bgAddrLbl, *sprAddrLbl; QFont lblFont; ppuStatContainerWidget = new QWidget(this); ppuStatContainerWidget->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ); vbox = new QVBoxLayout(); hbox1 = new QHBoxLayout(); ppuFrame = new QFrame(); ppuLbl = new QLabel( tr("PPU:") ); spriteLbl = new QLabel( tr("Sprite:") ); scanLineLbl = new QLabel( tr("Scanline:") ); pixLbl = new QLabel( tr("Pixel:") ); ppuFrame->setObjectName( tr("debuggerStatusPPU") ); hbox1->addLayout( vbox ); vbox->addWidget( ppuLbl ); vbox->addWidget( spriteLbl ); vbox->addWidget( scanLineLbl ); vbox->addWidget( pixLbl ); bgAddrLbl = new QLabel( tr("BG Addr") ); sprAddrLbl = new QLabel( tr("Spr Addr") ); ctlFrame = new QGroupBox( tr("Control / Mask") ); grid1 = new QGridLayout(); grid = new QGridLayout(); hbox1->addWidget( ctlFrame ); ctlFrame->setLayout( grid1 ); //ppuBgAddr = new QLineEdit(); ppuBgAddr = new ppuCtrlRegDpy(); ppuSprAddr = new QLineEdit(); grid->addWidget( bgAddrLbl, 0, 0 ); grid->addWidget( sprAddrLbl, 1, 0 ); grid->addWidget( ppuBgAddr, 0, 1 ); grid->addWidget( ppuSprAddr, 1, 1 ); grid1->addLayout( grid, 0, 0, 3, 1 ); bgEnabled_cbox = new QCheckBox( tr("BG Enabled") ); sprites_cbox = new QCheckBox( tr("Sprites Enabled") ); drawLeftBg_cbox = new QCheckBox( tr("Draw Left BG (8px)") ); drawLeftFg_cbox = new QCheckBox( tr("Draw Left Sprites (8px)") ); vwrite_cbox = new QCheckBox( tr("Vertical Write") ); nmiBlank_cbox = new QCheckBox( tr("NMI on vBlank") ); sprite8x16_cbox = new QCheckBox( tr("8x16 Sprites") ); grayscale_cbox = new QCheckBox( tr("Grayscale") ); iRed_cbox = new QCheckBox( tr("Intensify Red") ); iGrn_cbox = new QCheckBox( tr("Intensify Green") ); iBlu_cbox = new QCheckBox( tr("Intensify Blue") ); grid1->addWidget( bgEnabled_cbox , 3, 0 ); grid1->addWidget( sprites_cbox , 4, 0 ); grid1->addWidget( drawLeftBg_cbox, 5, 0 ); grid1->addWidget( drawLeftFg_cbox, 6, 0 ); grid1->addWidget( vwrite_cbox , 0, 1 ); grid1->addWidget( nmiBlank_cbox , 1, 1 ); grid1->addWidget( sprite8x16_cbox, 2, 1 ); grid1->addWidget( grayscale_cbox , 3, 1 ); grid1->addWidget( iRed_cbox , 4, 1 ); grid1->addWidget( iGrn_cbox , 5, 1 ); grid1->addWidget( iBlu_cbox , 6, 1 ); ppuStatContainerWidget->setLayout( hbox1 ); vbox1 = new QVBoxLayout(); vbox1->addWidget( ppuStatContainerWidget, 1 ); vbox1->addStretch( 10 ); ppuFrame->setLayout( vbox1 ); lblFont = bgAddrLbl->font(); printf("Point Size: %i \n", lblFont.pointSize() ); lblFont.setPointSize(9); bgAddrLbl->setFont( lblFont ); sprAddrLbl->setFont( lblFont ); bgEnabled_cbox->setFont( lblFont ); sprites_cbox->setFont( lblFont ); drawLeftBg_cbox->setFont( lblFont ); drawLeftFg_cbox->setFont( lblFont ); vwrite_cbox->setFont( lblFont ); nmiBlank_cbox->setFont( lblFont ); sprite8x16_cbox->setFont( lblFont ); grayscale_cbox->setFont( lblFont ); iRed_cbox->setFont( lblFont ); iGrn_cbox->setFont( lblFont ); iBlu_cbox->setFont( lblFont ); //printf("Grid vspc:%i \n", grid1->verticalSpacing() ); grid1->setVerticalSpacing( grid1->verticalSpacing() / 2 ); } //---------------------------------------------------------------------------- void ConsoleDebugger::buildBpListDisplay(void) { QVBoxLayout *vbox; QHBoxLayout *hbox; QPushButton *button; QTreeWidgetItem *item; QFontMetrics fm(font); bpFrame = new QFrame(); vbox = new QVBoxLayout(); bpTree = new QTreeWidget(); bpFrame->setObjectName( tr("debuggerBreakpointList") ); bpTree->setColumnCount(2); bpTree->setSelectionMode( QAbstractItemView::SingleSelection ); bpTree->setMinimumHeight( 3 * fm.lineSpacing() ); bpTree->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Ignored ); 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 = new QHBoxLayout(); bpAddBtn = button = new QPushButton( tr("Add") ); hbox->addWidget( button ); connect( button, SIGNAL(clicked(void)), this, SLOT(add_BP_CB(void)) ); bpEditBtn = button = new QPushButton( tr("Edit") ); hbox->addWidget( button ); connect( button, SIGNAL(clicked(void)), this, SLOT(edit_BP_CB(void)) ); bpDelBtn = button = new QPushButton( tr("Delete") ); hbox->addWidget( button ); connect( button, SIGNAL(clicked(void)), this, SLOT(delete_BP_CB(void)) ); vbox->addWidget( bpTree ); vbox->addLayout( hbox ); bpTreeContainerWidget = new QWidget(this); bpTreeContainerWidget->setLayout( vbox ); vbox = new QVBoxLayout(); vbox->addWidget( bpTreeContainerWidget ); bpFrame->setLayout( vbox ); } //---------------------------------------------------------------------------- void ConsoleDebugger::buildBmListDisplay(void) { QVBoxLayout *vbox; QHBoxLayout *hbox; QPushButton *button; QTreeWidgetItem *item; QFontMetrics fm(font); bmTreeContainerWidget = new QWidget(this); hbox = new QHBoxLayout(); vbox = new QVBoxLayout(); bmFrame = new QFrame(); bmTree = new QTreeWidget(); selBmAddr = new QLineEdit(); selBmAddrVal = 0; connect( selBmAddr, SIGNAL(textChanged(const QString &)), this, SLOT(selBmAddrChanged(const QString &))); bmFrame->setObjectName( tr("debuggerBookmarkList") ); bmTree->setColumnCount(2); bmTree->setMinimumHeight( 3 * fm.lineSpacing() ); bmTree->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Ignored ); 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, 1 ); button = new QPushButton( tr("Add") ); vbox->addWidget( button, 1 ); connect( button, SIGNAL(clicked(void)), this, SLOT(add_BM_CB(void)) ); button = new QPushButton( tr("Delete") ); vbox->addWidget( button, 1 ); connect( button, SIGNAL(clicked(void)), this, SLOT(delete_BM_CB(void)) ); button = new QPushButton( tr("Name") ); vbox->addWidget( button, 1 ); connect( button, SIGNAL(clicked(void)), this, SLOT(edit_BM_CB(void)) ); vbox->addStretch( 10 ); hbox->addWidget( bmTree, 10 ); hbox->addLayout( vbox , 1 ); bmTreeContainerWidget->setLayout( hbox ); bmTreeContainerWidget->setVisible(true); vbox = new QVBoxLayout(); vbox->addWidget( bmTreeContainerWidget ); bmFrame->setLayout( vbox ); } //---------------------------------------------------------------------------- void ConsoleDebugger::loadDisplayViews(void) { char key[128]; QSettings settings; bool prevStateSaved; prevStateSaved = settings.value("debugger/prevStateSaved", false).toBool(); if ( !prevStateSaved ) { setLayoutOption(2); } for (int i=0; i<2; i++) { for (int j=0; j<4; j++) { QString tabListVal; sprintf( key, "debugger/tabView%i%i", i+1, j+1 ); tabListVal = settings.value(key).toString(); QStringList tabList = tabListVal.split(','); for (int k=0; k 0 ) { //printf(" %i: %s\n", k, tabList[k].toStdString().c_str() ); if ( tabList[k].compare( cpuFrame->objectName() ) == 0 ) { tabView[i][j]->addTab( cpuFrame, tr("CPU") ); } else if ( tabList[k].compare( ppuFrame->objectName() ) == 0 ) { tabView[i][j]->addTab( ppuFrame, tr("PPU") ); } else if ( tabList[k].compare( bpFrame->objectName() ) == 0 ) { tabView[i][j]->addTab( bpFrame, tr("Breakpoints") ); } else if ( tabList[k].compare( bmFrame->objectName() ) == 0 ) { tabView[i][j]->addTab( bmFrame, tr("Bookmarks") ); } } } } } if ( cpuFrame->parent() == nullptr ) { tabView[0][0]->addTab( cpuFrame, tr("CPU") ); } if ( ppuFrame->parent() == nullptr ) { tabView[0][0]->addTab( ppuFrame, tr("PPU") ); } if ( bpFrame->parent() == nullptr ) { tabView[0][0]->addTab( bpFrame, tr("Breakpoints") ); } if ( bmFrame->parent() == nullptr ) { tabView[0][0]->addTab( bmFrame, tr("Bookmarks") ); } // Restore Window Geometry restoreGeometry(settings.value("debugger/geometry").toByteArray()); // Restore Horizontal Panel State mainLayouth->restoreState( settings.value("debugger/hPanelState").toByteArray() ); // Save Vertical Panel State for (int i=0; i<2; i++) { sprintf( key, "debugger/vPanelState%i", i+1); vsplitter[i]->restoreState( settings.value(key).toByteArray() ); } updateTabVisibility(); } //---------------------------------------------------------------------------- void ConsoleDebugger::saveDisplayViews(void) { char key[128]; QSettings settings; // Save Tab Placement for (int i=0; i<2; i++) { for (int j=0; j<4; j++) { QString tabListVal; sprintf( key, "debugger/tabView%i%i", i+1, j+1 ); for (int k=0; kcount(); k++) { QWidget *w = tabView[i][j]->widget(k); //printf("(%i,%i,%i) %s\n", i, j, k, w->objectName().toStdString().c_str() ); tabListVal += w->objectName() + ","; } //printf("(%i,%i) %s\n", i, j, tabListVal.toStdString().c_str() ); settings.setValue( key, tabListVal ); } } // Save Horizontal Panel State settings.setValue("debugger/hPanelState", mainLayouth->saveState()); // Save Vertical Panel State for (int i=0; i<2; i++) { sprintf( key, "debugger/vPanelState%i", i+1); settings.setValue( key, vsplitter[i]->saveState()); } // Save Window Geometry settings.setValue("debugger/geometry", saveGeometry()); // Set Window settings.setValue("debugger/prevStateSaved", true); } //---------------------------------------------------------------------------- void ConsoleDebugger::updateTabVisibility(void) { for (int i=0; i<2; i++) { for (int j=0; j<4; j++) { if ( tabView[i][j]->count() > 0 ) { if ( !tabView[i][j]->isVisible() ) { QList s = vsplitter[i]->sizes(); s[j] = tabView[i][j]->minimumSizeHint().height(); vsplitter[i]->setSizes(s); } tabView[i][j]->setVisible( true ); } else { tabView[i][j]->setVisible( false ); } } } } //---------------------------------------------------------------------------- void ConsoleDebugger::moveTab( QWidget *w, int row, int column) { int idx = -1; DebuggerTabWidget *p = NULL; for (int i=0; i<2; i++) { for (int j=0; j<4; j++) { idx = tabView[i][j]->indexOf(w); if ( idx >= 0 ) { p = tabView[i][j]; break; } } if (p) break; } if ( p ) { QString txt = p->tabBar()->tabText( idx ); p->removeTab( idx ); tabView[column][row]->addTab(w, txt); //printf("Move Widget %p to (%i,%i) %s\n", w, row, column, txt.toStdString().c_str() ); } updateTabVisibility(); } //---------------------------------------------------------------------------- void ConsoleDebugger::setCpuStatusFont( const QFont &font ) { QFontMetrics fm(font); int fontCharWidth; fontCharWidth = fm.averageCharWidth(); pcEntry->setFont( font ); pcEntry->setMinimumWidth( 6 * fontCharWidth ); pcEntry->setMaximumWidth( 6 * fontCharWidth ); regAEntry->setFont( font ); regAEntry->setMinimumWidth( 4 * fontCharWidth ); regAEntry->setMaximumWidth( 4 * fontCharWidth ); regXEntry->setFont( font ); regXEntry->setMinimumWidth( 4 * fontCharWidth ); regXEntry->setMaximumWidth( 4 * fontCharWidth ); regYEntry->setFont( font ); regYEntry->setMinimumWidth( 4 * fontCharWidth ); regYEntry->setMaximumWidth( 4 * fontCharWidth ); regPEntry->setFont( font ); regPEntry->setMinimumWidth( 4 * fontCharWidth ); regPEntry->setMaximumWidth( 4 * fontCharWidth ); cpuCyclesVal->setFont(font); cpuCyclesVal->setMinimumWidth( 24 * fontCharWidth ); cpuInstrsVal->setFont(font); cpuInstrsVal->setMinimumWidth( 24 * fontCharWidth ); } //---------------------------------------------------------------------------- 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, bool forceAccept ) { 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, *oam_radio, *rom_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") ); ppu_radio = new QRadioButton( tr("PPU") ); oam_radio = new QRadioButton( tr("OAM") ); rom_radio = new QRadioButton( tr("ROM") ); cpu_radio->setChecked(true); gbox->setLayout( hbox ); hbox->addWidget( cpu_radio ); hbox->addWidget( ppu_radio ); hbox->addWidget( oam_radio ); hbox->addWidget( rom_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 ) { oam_radio->setChecked(true); } else if ( wp->flags & BT_R ) { rom_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 ) { int romAddr = GetNesFileAddress(wp->address); if ( romAddr >= 0 ) { wp->address = romAddr; sprintf( stmp, "%X", wp->address ); addr1->setText( tr(stmp) ); rom_radio->setChecked(true); } else { 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 ); if ( forceAccept ) { ret = QDialog::Accepted; } else { 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 ( oam_radio->isChecked() ) { type |= BT_S; } else if ( rom_radio->isChecked() ) { type |= BT_R; } 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; debugSymbol_t *sym; SymbolEditWindow win(this); if ( addr < 0x8000 ) { bank = -1; } else { bank = getBank( addr ); } sym = debugSymbolTable.getSymbolAtBankOffset( bank, addr ); win.setAddr( addr ); win.setSym( sym ); ret = win.exec(); if ( ret == QDialog::Accepted ) { fceuWrapperLock(); 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 if ( watchpoint[i].flags & BT_R ) { flags[1] = 'R'; } 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); saveGameDebugBreakpoints(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()) ); } 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::resizeToMinimumSizeHint(void) { resize( minimumSizeHint() ); } //---------------------------------------------------------------------------- void ConsoleDebugger::add_BP_CB(void) { openBpEditWindow(-1); asmView->determineLineBreakpoints(); } //---------------------------------------------------------------------------- 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] ); asmView->determineLineBreakpoints(); } //---------------------------------------------------------------------------- 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 debuggerClearAllBookmarks(void) { dbgBmMgr.clear(); } //---------------------------------------------------------------------------- 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 ); saveGameDebugBreakpoints(true); asmView->determineLineBreakpoints(); } //---------------------------------------------------------------------------- void ConsoleDebugger::breakOnBadOpcodeCB(bool value) { //printf("Value:%i\n", value); FCEUI_Debugger().badopbreak = value; } //---------------------------------------------------------------------------- void ConsoleDebugger::breakOnNewCodeCB(bool value) { //printf("Code Value:%i\n", value); break_on_unlogged_code = value; } //---------------------------------------------------------------------------- void ConsoleDebugger::breakOnNewDataCB(bool value) { //printf("Data Value:%i\n", value); break_on_unlogged_data = value; } //---------------------------------------------------------------------------- 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::displayByteCodesCB( bool value ) { asmView->setDisplayByteCodes(value); } //---------------------------------------------------------------------------- void ConsoleDebugger::displayROMoffsetCB( bool value ) { asmView->setDisplayROMoffsets(value); } //---------------------------------------------------------------------------- void ConsoleDebugger::symbolDebugEnableCB( bool value ) { asmView->setSymbolDebugEnable(value); } //---------------------------------------------------------------------------- void ConsoleDebugger::registerNameEnableCB( bool value ) { asmView->setRegisterNameEnable(value); } //---------------------------------------------------------------------------- void ConsoleDebugger::autoOpenDebugCB( bool value ) { g_config->setOption( "SDL.AutoOpenDebugger", value); } //---------------------------------------------------------------------------- void ConsoleDebugger::debFileAutoLoadCB( bool value ) { int autoLoadDebug = value; g_config->setOption("SDL.AutoLoadDebugFiles", autoLoadDebug); if ( autoLoadDebug && (numWPs == 0) ) { loadGameDebugBreakpoints(); } } //---------------------------------------------------------------------------- void ConsoleDebugger::changeAsmFontCB(void) { bool ok = false; QFont selFont = QFontDialog::getFont( &ok, asmView->QWidget::font(), this, tr("Select Font"), QFontDialog::MonospacedFonts ); if ( ok ) { asmView->setFont( selFont ); asmView->updateAssemblyView(); //printf("Font Changed to: '%s'\n", font.toString().toStdString().c_str() ); g_config->setOption("SDL.DebuggerAsmFont", selFont.toString().toStdString().c_str() ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::changeStackFontCB(void) { bool ok = false; QFont selFont = QFontDialog::getFont( &ok, stackText->QWidget::font(), this, tr("Select Font"), QFontDialog::MonospacedFonts ); if ( ok ) { stackText->setFont( selFont ); //printf("Font Changed to: '%s'\n", font.toString().toStdString().c_str() ); g_config->setOption("SDL.DebuggerStackFont", selFont.toString().toStdString().c_str() ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::changeCpuFontCB(void) { bool ok = false; QFont selFont = QFontDialog::getFont( &ok, pcEntry->QWidget::font(), this, tr("Select Font"), QFontDialog::MonospacedFonts ); if ( ok ) { setCpuStatusFont( selFont ); //printf("Font Changed to: '%s'\n", font.toString().toStdString().c_str() ); g_config->setOption("SDL.DebuggerCpuStatusFont", selFont.toString().toStdString().c_str() ); } } //---------------------------------------------------------------------------- void ConsoleDebugger::reloadSymbolsCB(void) { fceuWrapperLock(); debugSymbolTable.loadGameSymbols(); asmView->updateAssemblyView(); fceuWrapperUnLock(); } //---------------------------------------------------------------------------- void ConsoleDebugger::pcSetPlaceTop(void) { asmView->setPC_placement( 0 ); } //---------------------------------------------------------------------------- void ConsoleDebugger::pcSetPlaceUpperMid(void) { asmView->setPC_placement( 1 ); } //---------------------------------------------------------------------------- void ConsoleDebugger::pcSetPlaceCenter(void) { asmView->setPC_placement( 2 ); } //---------------------------------------------------------------------------- void ConsoleDebugger::pcSetPlaceLowerMid(void) { asmView->setPC_placement( 3 ); } //---------------------------------------------------------------------------- void ConsoleDebugger::pcSetPlaceBottom(void) { asmView->setPC_placement( 4 ); } //---------------------------------------------------------------------------- void ConsoleDebugger::pcSetPlaceCustom(void) { int ret, ofs; QInputDialog dialog(this); g_config->getOption("SDL.DebuggerPCLineOffset" , &ofs ); dialog.setWindowTitle( tr("PC Line Offset") ); dialog.setLabelText( tr("Enter a line offset from 0 to 100.") ); dialog.setOkButtonText( tr("Ok") ); dialog.setInputMode( QInputDialog::IntInput ); dialog.setIntRange( 0, 100 ); dialog.setIntValue( ofs ); ret = dialog.exec(); if ( QDialog::Accepted == ret ) { ofs = dialog.intValue(); asmView->setPC_placement( 5, ofs ); } } //---------------------------------------------------------------------------- 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) { int ret; QMessageBox msgBox(QMessageBox::Question, tr("Step Out Already Active"), tr("Step Out is currently in process. Cancel it and setup a new Step Out watch?"), QMessageBox::No | QMessageBox::Yes, this); ret = msgBox.exec(); if ( ret != QMessageBox::Yes ) { //printf("Step out cancelled\n"); return; } //printf("Step out reset\n"); } 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::debugRunToCursorCB(void) { asmView->setBreakpointAtSelectedLine(); } //---------------------------------------------------------------------------- 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->displayText().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::navHistBackCB (void) { asmView->navHistBack(); } //---------------------------------------------------------------------------- void ConsoleDebugger::navHistForwardCB (void) { asmView->navHistForward(); } //---------------------------------------------------------------------------- void ConsoleDebugger::setLayoutOption( int opt ) { for (int i=0; i<2; i++) { for (int j=0; j<4; j++) { for (int k=0; kcount(); k++) { tabView[i][j]->removeTab(k); } } } switch ( opt ) { default: case 1: { tabView[0][0]->addTab( cpuFrame, tr("CPU") ); tabView[0][0]->addTab( ppuFrame, tr("PPU") ); tabView[0][0]->addTab( bpFrame, tr("Breakpoints") ); tabView[0][0]->addTab( bmFrame, tr("BookMarks") ); tabView[0][0]->setVisible(true); } break; case 2: { tabView[0][0]->addTab( cpuFrame, tr("CPU") ); tabView[0][0]->addTab( ppuFrame, tr("PPU") ); tabView[0][1]->addTab( bpFrame, tr("Breakpoints") ); tabView[0][1]->addTab( bmFrame, tr("BookMarks") ); tabView[0][0]->setVisible(true); tabView[0][1]->setVisible(true); QList s = vsplitter[0]->sizes(); s[0] = tabView[0][0]->minimumSizeHint().height(); s[1] = vsplitter[0]->height() - s[0]; s[2] = 0; s[3] = 0; vsplitter[0]->setSizes(s); } break; case 3: { tabView[0][0]->addTab( cpuFrame, tr("CPU") ); tabView[0][1]->addTab( ppuFrame, tr("PPU") ); tabView[1][0]->addTab( bpFrame, tr("Breakpoints") ); tabView[1][0]->addTab( bmFrame, tr("BookMarks") ); tabView[0][0]->setVisible(true); tabView[0][1]->setVisible(true); tabView[1][0]->setVisible(true); QList s = vsplitter[0]->sizes(); s[0] = vsplitter[0]->height() / 2; s[1] = s[0]; s[2] = 0; s[3] = 0; vsplitter[0]->setSizes(s); s = mainLayouth->sizes(); if ( s[2] == 0 ) { s[0] = s[1] = s[2] = mainLayouth->width() / 3; mainLayouth->setSizes(s); } } break; case 4: { tabView[0][0]->addTab( cpuFrame, tr("CPU") ); tabView[0][1]->addTab( ppuFrame, tr("PPU") ); tabView[1][0]->addTab( bpFrame, tr("Breakpoints") ); tabView[1][1]->addTab( bmFrame, tr("BookMarks") ); tabView[0][0]->setVisible(true); tabView[0][1]->setVisible(true); tabView[1][0]->setVisible(true); tabView[1][1]->setVisible(true); QList s = vsplitter[0]->sizes(); s[0] = vsplitter[0]->height() / 2; s[1] = s[0]; s[2] = 0; s[3] = 0; vsplitter[0]->setSizes(s); vsplitter[1]->setSizes(s); s = mainLayouth->sizes(); if ( s[2] == 0 ) { s[0] = s[1] = s[2] = mainLayouth->width() / 3; mainLayouth->setSizes(s); } } break; } updateTabVisibility(); } //---------------------------------------------------------------------------- void ConsoleDebugger::seekPCCB (void) { if (FCEUI_EmulationPaused()) { setRegsFromEntry(); //updateAllDebugWindows(); } windowUpdateReq = true; //asmView->scrollToPC(); } //---------------------------------------------------------------------------- void ConsoleDebugger::openChangePcDialog(void) { int ret; QDialog dialog(this); QLabel *lbl; QSpinBox *sbox; QVBoxLayout *vbox; QHBoxLayout *hbox; QPushButton *okButton, *cancelButton; vbox = new QVBoxLayout(); hbox = new QHBoxLayout(); lbl = new QLabel( tr("Specify Address [ 0x0000 -> 0xFFFF ]") ); okButton = new QPushButton( tr("Go") ); cancelButton = new QPushButton( tr("Cancel") ); okButton->setIcon( style()->standardIcon( QStyle::SP_DialogApplyButton ) ); cancelButton->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) ); connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) ); connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) ); sbox = new QSpinBox(); sbox->setRange(0x0000, 0xFFFF); sbox->setDisplayIntegerBase(16); sbox->setValue( X.PC ); QFont font = sbox->font(); font.setCapitalization(QFont::AllUppercase); sbox->setFont(font); hbox->addWidget( cancelButton ); hbox->addWidget( okButton ); vbox->addWidget( lbl ); vbox->addWidget( sbox ); vbox->addLayout( hbox ); dialog.setLayout( vbox ); dialog.setWindowTitle( tr("Change Program Counter") ); okButton->setDefault(true); ret = dialog.exec(); if ( QDialog::Accepted == ret ) { X.PC = sbox->value(); windowUpdateReq = true; } } //---------------------------------------------------------------------------- void ConsoleDebugger::openGotoAddrDialog(void) { int ret; QDialog dialog(this); QLabel *lbl; QSpinBox *sbox; QVBoxLayout *vbox; QHBoxLayout *hbox; QPushButton *okButton, *cancelButton; vbox = new QVBoxLayout(); hbox = new QHBoxLayout(); lbl = new QLabel( tr("Specify Address [ 0x0000 -> 0xFFFF ]") ); okButton = new QPushButton( tr("Go") ); cancelButton = new QPushButton( tr("Cancel") ); okButton->setIcon( style()->standardIcon( QStyle::SP_DialogApplyButton ) ); cancelButton->setIcon( style()->standardIcon( QStyle::SP_DialogCancelButton ) ); connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) ); connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) ); sbox = new QSpinBox(); sbox->setRange(0x0000, 0xFFFF); sbox->setDisplayIntegerBase(16); sbox->setValue( X.PC ); QFont font = sbox->font(); font.setCapitalization(QFont::AllUppercase); sbox->setFont(font); hbox->addWidget( cancelButton ); hbox->addWidget( okButton ); vbox->addWidget( lbl ); vbox->addWidget( sbox ); vbox->addLayout( hbox ); dialog.setLayout( vbox ); dialog.setWindowTitle( tr("Goto Address") ); okButton->setDefault(true); ret = dialog.exec(); if ( QDialog::Accepted == ret ) { int addr; addr = sbox->value(); asmView->gotoAddr(addr); } } //---------------------------------------------------------------------------- void ConsoleDebugger::resetCountersCB (void) { ResetDebugStatisticsCounters(); updateRegisterView(); } //---------------------------------------------------------------------------- void ConsoleDebugger::asmViewCtxMenuRunToCursor(void) { fceuWrapperLock(); watchpoint[64].address = asmView->getCtxMenuAddr(); watchpoint[64].flags = WP_E|WP_X; FCEUI_SetEmulationPaused(0); fceuWrapperUnLock(); } //---------------------------------------------------------------------------- void ConsoleDebugger::asmViewCtxMenuGoTo(void) { asmView->gotoAddr( asmView->getCtxMenuAddr() ); } //---------------------------------------------------------------------------- void ConsoleDebugger::asmViewCtxMenuAddBP(void) { int bpNum; watchpointinfo wp; wp.address = asmView->getCtxMenuAddr(); wp.endaddress = 0; wp.flags = WP_X | WP_E; wp.condText = 0; wp.desc = NULL; bpNum = asmView->isBreakpointAtAddr( wp.address ); if ( bpNum >= 0 ) { openBpEditWindow( bpNum, &watchpoint[bpNum] ); } else { openBpEditWindow( -1, &wp ); } asmView->determineLineBreakpoints(); } //---------------------------------------------------------------------------- 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() ); } //---------------------------------------------------------------------------- void QAsmView::setPC_placement( int mode, int ofs ) { pcLinePlacement = mode; if ( mode == 5 ) { pcLineOffset = ofs; } g_config->setOption("SDL.DebuggerPCPlacement" , pcLinePlacement); g_config->setOption("SDL.DebuggerPCLineOffset" , pcLineOffset ); g_config->save(); } //---------------------------------------------------------------------------- void QAsmView::toggleBreakpoint(int line) { if ( line < asmEntry.size() ) { int bpNum = isBreakpointAtLine(line); if ( bpNum >= 0 ) { DeleteBreak( bpNum ); } else { watchpointinfo wp; wp.address = asmEntry[line]->addr; wp.endaddress = 0; wp.flags = WP_X | WP_E; wp.condText = 0; wp.desc = NULL; dbgWin->openBpEditWindow( -1, &wp, true ); } asmEntry[line]->bpNum = isBreakpointAtLine(line); } } //---------------------------------------------------------------------------- int QAsmView::isBreakpointAtAddr( int addr ) { for (int i=0; i= watchpoint[i].address) && addr < watchpoint[i].endaddress ) { return i; } } else { if (addr == watchpoint[i].address) { return i; } } } return -1; } //---------------------------------------------------------------------------- int QAsmView::isBreakpointAtLine( int l ) { if ( l < asmEntry.size() ) { if ( asmEntry[l]->type == dbg_asm_entry_t::ASM_TEXT ) { return isBreakpointAtAddr( asmEntry[l]->addr ); } } return -1; } //---------------------------------------------------------------------------- void QAsmView::determineLineBreakpoints(void) { for (size_t i=0; ibpNum = isBreakpointAtLine(i); } } //---------------------------------------------------------------------------- void QAsmView::setBreakpointAtSelectedLine(void) { int addr = -1; if ( (selAddrLine >= 0) && (selAddrLine < asmEntry.size()) ) { if ( selAddrValue == asmEntry[ selAddrLine ]->addr ) { addr = selAddrValue; } } if ( addr >= 0 ) { fceuWrapperLock(); watchpoint[64].address = addr; watchpoint[64].flags = WP_E|WP_X; FCEUI_SetEmulationPaused(0); fceuWrapperUnLock(); } } //---------------------------------------------------------------------------- int QAsmView::getAsmAddrFromLine(int line) { if ( (line >= 0) && (line < asmEntry.size()) ) { return asmEntry[line]->addr; } return -1; } //---------------------------------------------------------------------------- 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; } } // Don't stop on an symbol name or comment line, search for next assembly line while ( (line < asmEntry.size()) && (asmEntry[line]->type != dbg_asm_entry_t::ASM_TEXT) ) { line++; } //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; 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 | ASM_DEBUG_REPLACE; if ( registerNameEnable ) { asmFlags |= ASM_DEBUG_REGS; } } for (int i=0; i < 0xFFFF; i++) { line.clear(); // PC pointer if (addr > 0xFFFF) break; a = new dbg_asm_entry_t; if (cdloggerdataSize) { uint8_t cdl_data; instruction_addr = GetNesFileAddress(addr) - 16; if ( (instruction_addr >= 0) && (instruction_addr < cdloggerdataSize) ) { cdl_data = cdloggerdata[instruction_addr] & 3; if (cdl_data == 3) { line.append("cd "); // both Code and Data } else if (cdl_data == 2) { line.append(" d "); // Data } else if (cdl_data == 1) { line.append("c "); // Code } else { line.append(" "); // not logged } } else { line.append(" "); // cannot be logged } } instruction_addr = addr; if ( !pc_found ) { if (addr > X.PC) { asmPC = a; line.append(">"); pc_found = 1; } else if (addr == X.PC) { asmPC = a; line.append(">"); pc_found = 1; } else { line.append(" "); } } else { line.append(" "); } 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); a->size = 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); } delete a; break; } for (int j = 0; j < size; j++) { sprintf(chr, "%02X ", opcode[j] = GetMem(addr++)); if ( showByteCodes ) line.append(chr); } while (size < 3) { if ( showByteCodes ) 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]; } // 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 { if ( j == 0 ) { while ( j < 3 ) { stmp[j] = ' '; j++; } stmp[j] = ';'; j++; stmp[j] = ' '; j++; } 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+1) * pxCharWidth; if ( viewWidth >= pxLineWidth ) { hbar->hide(); } else { hbar->setPageStep( viewWidth ); hbar->setMaximum( pxLineWidth - viewWidth ); hbar->show(); } //setMaximumWidth( pxLineWidth ); vbar->setPageStep( (3*viewLines)/4 ); vbar->setMaximum( asmEntry.size() ); determineLineBreakpoints(); } //---------------------------------------------------------------------------- void ConsoleDebugger::setRegsFromEntry(void) { std::string s; long int i; s = pcEntry->displayText().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->displayText().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->displayText().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->displayText().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) ); sprintf( stmp, "%02X", X.P ); regPEntry->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_value1 = timestampbase + (uint64)timestamp - total_cycles_base; int64 counter_value2 = timestampbase + (uint64)timestamp - delta_cycles_base; if (counter_value1 < 0) // sanity check { ResetDebugStatisticsCounters(); counter_value1 = 0; } if (counter_value2 < 0) // sanity check { ResetDebugStatisticsCounters(); counter_value2 = 0; } sprintf(stmp, "%10llu (+%llu)", counter_value1, counter_value2); cpuCyclesVal->setText( tr(stmp) ); sprintf(stmp, "%10llu (+%llu)", total_instructions, delta_instructions); cpuInstrsVal->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) { bool buttonEnable; QTreeWidgetItem *item; //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 ( waitingAtBp && (lastBpIdx == BREAK_TYPE_CYCLES_EXCEED) ) { cpuCyclesLbl1->setStyleSheet("background-color: blue; color: white;"); } else { cpuCyclesLbl1->setStyleSheet(NULL); } if ( waitingAtBp && (lastBpIdx == BREAK_TYPE_INSTRUCTIONS_EXCEED) ) { cpuInstrsLbl1->setStyleSheet("background-color: blue; color: white;"); } else { cpuInstrsLbl1->setStyleSheet(NULL); } if ( bpTree->topLevelItemCount() != numWPs ) { //printf("Breakpoint Tree Update\n"); bpListUpdate( true ); } if ( bmTree->topLevelItemCount() != dbgBmMgr.size() ) { //printf("Bookmark Tree Update\n"); bmListUpdate( true ); } item = bpTree->currentItem(); if ( item == NULL ) { buttonEnable = false; } else { buttonEnable = true; } bpAddBtn->setEnabled(true); bpEditBtn->setEnabled(buttonEnable); bpDelBtn->setEnabled(buttonEnable); } //---------------------------------------------------------------------------- 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->setCurrentItem( item ); } bpTree->viewport()->update(); } } else { if (bpNum == BREAK_TYPE_CYCLES_EXCEED) { // Label Coloring done in periodic update } else if (bpNum == BREAK_TYPE_INSTRUCTIONS_EXCEED) { // Label Coloring done in periodic update } } windowUpdateReq = true; } //---------------------------------------------------------------------------- void ConsoleDebugger::hbarChanged(int value) { //printf("HBar Changed: %i\n", value); asmView->setXScroll( value ); asmView->update(); } //---------------------------------------------------------------------------- void ConsoleDebugger::vbarChanged(int value) { //printf("VBar Changed: %i\n", value); asmView->setLine( value ); asmView->update(); } //---------------------------------------------------------------------------- void bpDebugSetEnable(bool val) { bpDebugEnable = val; } //---------------------------------------------------------------------------- void FCEUD_DebugBreakpoint( int bpNum ) { std::list ::iterator it; if ( !nes_shm->runEmulator || !bpDebugEnable ) { return; } lastBpIdx = bpNum; waitingAtBp = true; printf("Breakpoint Hit: %i \n", bpNum ); fceuWrapperUnLock(); if ( dbgWin ) { dbgWin->breakPointNotify( bpNum ); } while ( nes_shm->runEmulator && bpDebugEnable && 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++; } } msleep(16); } // since we unfreezed emulation, reset delta_cycles counter ResetDebugStatisticsDeltaCounters(); fceuWrapperLock(); waitingAtBp = false; } //---------------------------------------------------------------------------- bool debuggerWindowIsOpen(void) { return dbgWin != NULL; } //---------------------------------------------------------------------------- void debuggerWindowSetFocus(bool val) { if ( dbgWin ) { dbgWin->activateWindow(); dbgWin->raise(); dbgWin->setFocus(); } } //---------------------------------------------------------------------------- bool debuggerWaitingAtBreakpoint(void) { return waitingAtBp; } //---------------------------------------------------------------------------- void updateAllDebuggerWindows( void ) { std::list ::iterator it; if ( dbgWin ) { dbgWin->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( bool force ) { int i; FILE *fp; char stmp[512]; char flags[8]; debuggerBookmark_t *bm; // If no breakpoints are loaded, skip saving if ( !force && (numWPs == 0) && (dbgBmMgr.size() == 0) ) { return; } if ( getGameDebugBreakpointFileName( stmp ) ) { printf("Error: Failed to get save file name for debug\n"); 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 ( dbgWin == NULL ) { printf("No Debug Windows Open: Skipping loading of breakpoint data\n"); return; } if ( getGameDebugBreakpointFileName( stmp ) ) { printf("Error: Failed to get load file name for debug\n"); 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'); if ( data[1] == 'P' ) { type |= BT_P; } else if ( data[1] == 'S' ) { type |= BT_S; } else if ( data[1] == 'R' ) { type |= BT_R; } 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; std::string fontString; useDarkTheme = false; pcBgColor.setRgb( 255, 255, 0 ); opcodeColor.setRgb( 46, 139, 87 ); labelColor.setRgb( 165, 42, 42 ); commentColor.setRgb( 0, 0, 255 ); addressColor.setRgb( 106, 90, 205 ); immediateColor.setRgb( 255, 1, 255 ); fceuLoadConfigColor( "SDL.AsmSyntaxColorOpcode" , &opcodeColor ); fceuLoadConfigColor( "SDL.AsmSyntaxColorAddress" , &addressColor ); fceuLoadConfigColor( "SDL.AsmSyntaxColorImmediate", &immediateColor ); fceuLoadConfigColor( "SDL.AsmSyntaxColorLabel" , &labelColor ); fceuLoadConfigColor( "SDL.AsmSyntaxColorComment" , &commentColor ); fceuLoadConfigColor( "SDL.AsmSyntaxColorPC" , &pcBgColor ); g_config->getOption("SDL.DebuggerAsmFont", &fontString); if ( fontString.size() > 0 ) { font.fromString( QString::fromStdString( fontString ) ); } else { 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::Window); //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::Window , fg ); pal.setColor(QPalette::WindowText, bg ); } else { pal.setColor(QPalette::Base , bg ); pal.setColor(QPalette::Window , bg ); pal.setColor(QPalette::WindowText, fg ); } this->parent = (ConsoleDebugger*)parent; this->setPalette(pal); this->setMouseTracking(true); showByteCodes = false; displayROMoffsets = false; symbolicDebugEnable = true; registerNameEnable = true; calcFontData(); vbar = NULL; hbar = NULL; asmPC = NULL; maxLineLen = 0; pxLineWidth = 0; lineOffset = 0; maxLineOffset = 0; ctxMenuAddr = -1; cursorPosX = 0; cursorPosY = 0; mouseLeftBtnDown = false; txtHlgtAnchorLine = -1; txtHlgtAnchorChar = -1; txtHlgtStartChar = -1; txtHlgtStartLine = -1; txtHlgtEndChar = -1; txtHlgtEndLine = -1; pcLinePlacement = 0; pcLineOffset = 0; g_config->getOption( "SDL.DebuggerPCPlacement" , &pcLinePlacement ); g_config->getOption( "SDL.DebuggerPCLineOffset", &pcLineOffset ); selAddrLine = -1; selAddrChar = 0; selAddrWidth = 0; selAddrValue = -1; memset( selAddrText, 0, sizeof(selAddrText) ); cursorLineAddr = -1; 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() ); calcLineOffsets(); } //---------------------------------------------------------------------------- QAsmView::~QAsmView(void) { asmClear(); } //---------------------------------------------------------------------------- void QAsmView::asmClear(void) { for (size_t i=0; i= pxLineWidth ) { pxLineXScroll = 0; } else { pxLineXScroll = value; } } //---------------------------------------------------------------------------- void QAsmView::scrollToPC(void) { if ( asmPC != NULL ) { curNavLoc.addr = asmPC->addr; scrollToLine( asmPC->line ); } } //---------------------------------------------------------------------------- void QAsmView::scrollToAddr( int addr ) { int line = getAsmLineFromAddr(addr); curNavLoc.addr = asmPC->addr; scrollToLine( line ); } //---------------------------------------------------------------------------- void QAsmView::scrollToLine( int line ) { int ofs = 0; int maxOfs = (viewLines-3); if ( maxOfs < 0 ) { maxOfs = 0; } switch ( pcLinePlacement ) { default: case 0: ofs = 0; break; case 1: ofs = (viewLines / 4); break; case 2: ofs = (viewLines / 2); break; case 3: ofs = (viewLines*3) / 4; break; case 4: ofs = maxOfs; break; case 5: ofs = pcLineOffset; if ( ofs < 0 ) { ofs = 0; } else if ( ofs > maxOfs ) { ofs = maxOfs; } break; } lineOffset = line - ofs; if ( lineOffset < 0 ) { lineOffset = 0; } vbar->setValue( lineOffset ); } //---------------------------------------------------------------------------- void QAsmView::gotoPC(void) { if ( asmPC != NULL ) { gotoLine( asmPC->line ); } } //---------------------------------------------------------------------------- void QAsmView::gotoAddr( int addr ) { int line = getAsmLineFromAddr(addr); gotoLine( line ); } //---------------------------------------------------------------------------- void QAsmView::gotoLine( int line ) { if ( (line >= 0) && (line < asmEntry.size()) ) { if ( curNavLoc.addr != asmEntry[line]->addr ) { // Don't push back to back duplicates into the navigation history pushAddrHist(); } curNavLoc.addr = asmEntry[line]->addr; scrollToLine( line ); setSelAddrToLine(line); } } //---------------------------------------------------------------------------- void QAsmView::pushAddrHist(void) { if ( navBckHist.size() > 0) { if ( navBckHist.back().addr == curNavLoc.addr ) { //printf("Address is Same\n"); return; } } navBckHist.push_back(curNavLoc); navFwdHist.clear(); //printf("Push Address: $%04X %zi\n", curNavLoc.addr, navBckHist.size() ); } //---------------------------------------------------------------------------- void QAsmView::navHistBack(void) { if ( navBckHist.size() > 0) { navFwdHist.push_back(curNavLoc); curNavLoc = navBckHist.back(); navBckHist.pop_back(); int line = getAsmLineFromAddr( curNavLoc.addr ); scrollToLine( line ); setSelAddrToLine( line ); } } //---------------------------------------------------------------------------- void QAsmView::navHistForward(void) { if ( navFwdHist.size() > 0 ) { navBckHist.push_back(curNavLoc); curNavLoc = navFwdHist.back(); navFwdHist.pop_back(); int line = getAsmLineFromAddr( curNavLoc.addr ); scrollToLine( line ); setSelAddrToLine( line ); } } //---------------------------------------------------------------------------- void QAsmView::setSelAddrToLine( int line ) { if ( (line >= 0) && (line < asmEntry.size()) ) { int addr = asmEntry[line]->addr; selAddrLine = line; selAddrChar = pcLocLinePos + 3; selAddrWidth = 4; selAddrValue = addr; sprintf( selAddrText, "%04X", addr ); parent->setBookmarkSelectedAddress( addr ); //printf("Selected ADDR: $%04X '%s' '%s'\n", addr, selAddrText, asmEntry[line]->text.c_str() ); txtHlgtAnchorLine = -1; txtHlgtAnchorChar = -1; txtHlgtStartChar = -1; txtHlgtStartLine = -1; txtHlgtEndChar = -1; txtHlgtEndLine = -1; } } //---------------------------------------------------------------------------- void QAsmView::setDisplayByteCodes( bool value ) { if ( value != showByteCodes ) { showByteCodes = value; calcLineOffsets(); calcMinimumWidth(); fceuWrapperLock(); updateAssemblyView(); fceuWrapperUnLock(); } } //---------------------------------------------------------------------------- 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::setFont( const QFont &newFont ) { font = newFont; QWidget::setFont(newFont); calcFontData(); } //---------------------------------------------------------------------------- void QAsmView::calcFontData(void) { QWidget::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.capHeight(); pxLineSpacing = metrics.lineSpacing() * 1.25; pxLineLead = pxLineSpacing - metrics.height(); pxCursorHeight = metrics.height(); printf("W:%i H:%i LS:%i \n", pxCharWidth, pxCharHeight, pxLineSpacing ); viewLines = (viewHeight / pxLineSpacing) + 1; calcMinimumWidth(); } //---------------------------------------------------------------------------- void QAsmView::calcMinimumWidth(void) { if ( showByteCodes ) { setMinimumWidth( 50 * pxCharWidth ); } else { setMinimumWidth( 41 * pxCharWidth ); } } //---------------------------------------------------------------------------- void QAsmView::setScrollBars( QScrollBar *h, QScrollBar *v ) { hbar = h; vbar = v; } //---------------------------------------------------------------------------- bool QAsmView::event(QEvent *event) { if (event->type() == QEvent::ToolTip) { int i,j, line, addr = 0; QHelpEvent *helpEvent = static_cast(event); bool opcodeValid = false, showOpcodeDesc = false, showSymHexDecode = false; bool showAddrDesc = false, showOperandAddrDesc = false; char stmp[256]; printf("QEvent::ToolTip\n"); QPoint c = convPixToCursor(helpEvent->pos()); line = lineOffset + c.y(); opcodeValid = (line < asmEntry.size()) && (asmEntry[line]->size > 0) && (asmEntry[line]->type == dbg_asm_entry_t::ASM_TEXT); showOpcodeDesc = (c.x() >= opcodeLinePos) && (c.x() < operandLinePos) && opcodeValid; showAddrDesc = (c.x() >= pcLocLinePos) && (c.x() < byteCodeLinePos) && opcodeValid; if ( (c.x() > operandLinePos) && opcodeValid && (asmEntry[line]->sym.name.size() > 0) ) { size_t subStrLoc = asmEntry[line]->text.find( asmEntry[line]->sym.name, operandLinePos ); if ( (subStrLoc != std::string::npos) && (subStrLoc > operandLinePos) ) { //printf("Line:%i asmEntry DB Sym: %zi '%s'\n", line, subStrLoc, asmEntry[line]->sym.name.c_str() ); int symTextStart = subStrLoc; int symTextEnd = subStrLoc + asmEntry[line]->sym.name.size(); if ( (c.x() >= symTextStart) && (c.x() < symTextEnd) ) { showSymHexDecode = true; } } } if ( opcodeValid && (c.x() > operandLinePos) && (c.x() < asmEntry[line]->text.size()) ) { i = c.x(); if ( isxdigit( asmEntry[line]->text[i] ) ) { int addrClicked = 1; int 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 ); showOperandAddrDesc = true; } } } if ( showOpcodeDesc ) { QString qs = fceuGetOpcodeToolTip(asmEntry[line]->opcode, asmEntry[line]->size ); //QToolTip::setFont(font); QToolTip::showText(helpEvent->globalPos(), qs, this ); } else if ( showSymHexDecode ) { sprintf( stmp, "$%04X", asmEntry[line]->sym.ofs ); QToolTip::showText(helpEvent->globalPos(), tr(stmp), this ); } else if ( showAddrDesc ) { if ( asmEntry[line]->bank < 0 ) { sprintf( stmp, "ADDR:\t$%04X", asmEntry[line]->addr ); } else { sprintf( stmp, "ADDR:\t$%04X\nBANK:\t$%02X\nROM:\t$%06X", asmEntry[line]->addr, asmEntry[line]->bank, asmEntry[line]->rom ); } QToolTip::showText(helpEvent->globalPos(), tr(stmp), this ); } else if ( showOperandAddrDesc ) { int bank = -1, romOfs = -1; if (addr >= 0x8000) { bank = getBank(addr); romOfs = GetNesFileAddress(addr); } if ( bank < 0 ) { sprintf( stmp, "ADDR:\t$%04X", addr ); } else { sprintf( stmp, "ADDR:\t$%04X\nBANK:\t$%02X\nROM:\t$%06X", addr, bank, romOfs ); } QToolTip::showText(helpEvent->globalPos(), tr(stmp), this ); } else { //printf("Tool Tip Hide\n"); QToolTip::hideText(); event->ignore(); } return true; } //else if (event->type() == QEvent::Leave) //{ // printf("QEvent::Leave\n"); //} return QWidget::event(event); } //---------------------------------------------------------------------------- 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; hbar->hide(); } else { hbar->setPageStep( viewWidth ); hbar->setMaximum( pxLineWidth - viewWidth ); hbar->show(); pxLineXScroll = hbar->value(); } vbar->setPageStep( (3*viewLines)/4 ); } //---------------------------------------------------------------------------- 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() + pxLineXScroll) / 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; cursorLineAddr = 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() ); if ( mouseLeftBtnDown ) { mouseLeftBtnDown = false; setHighlightEndCoord( c.x(), line ); loadHighlightToClipboard(); } } } //---------------------------------------------------------------------------- void QAsmView::mouseDoubleClickEvent(QMouseEvent * event) { //printf("Double Click\n"); if ( selAddrValue >= 0 ) { gotoAddr( selAddrValue ); mouseLeftBtnDown = false; txtHlgtAnchorLine = -1; txtHlgtAnchorChar = -1; txtHlgtStartChar = -1; txtHlgtStartLine = -1; txtHlgtEndChar = -1; txtHlgtEndLine = -1; } } //---------------------------------------------------------------------------- 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 ); if ( (c.x() >= 2) && (c.x() <= 3) ) { //printf("Toggle BP!\n"); toggleBreakpoint(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, operandLinePos ); if ( (subStrLoc != std::string::npos) && (subStrLoc > operandLinePos) ) { //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 = 3; selAddrValue = addr = asmEntry[line]->addr; i=selAddrChar; j=0; while ( asmEntry[line]->text[i] != 0 ) { if ( j >= (int)sizeof(selAddrText) ) { j=sizeof(selAddrText)-1; break; } selAddrText[j] = asmEntry[line]->text[i]; i++; j++; } selAddrText[j] = 0; selAddrWidth = j-1; selAddrText[ selAddrWidth ] = 0; } if ( addr < 0 ) { addr = asmEntry[line]->addr; selAddrLine = line; selAddrChar = pcLocLinePos+3; 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() ); bool enableRunToCursor = false; char stmp[128]; line = lineOffset + c.y(); ctxMenuAddr = -1; if ( line < asmEntry.size() ) { int addr; if ( selAddrValue < 0 ) { ctxMenuAddr = addr = asmEntry[line]->addr; enableRunToCursor = true; } else { ctxMenuAddr = addr = selAddrValue; enableRunToCursor = (selAddrValue == asmEntry[line]->addr); } if ( enableRunToCursor ) { act = new QAction(tr("Run To &Cursor"), &menu); menu.addAction(act); //act->setShortcut( QKeySequence(tr("Ctrl+F10"))); connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuRunToCursor(void)) ); } sprintf( stmp, "Go to $%04X\tDouble+Click", ctxMenuAddr ); act = new QAction(tr(stmp), &menu); menu.addAction(act); //act->setShortcut( QKeySequence(tr("Ctrl+F10"))); connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuGoTo(void)) ); if ( isBreakpointAtAddr( addr ) >= 0 ) { act = new QAction(tr("Edit &Breakpoint"), &menu); } else { 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 Book&mark"), &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::drawPointerPC( QPainter *painter, int xl, int yl ) { int x, y, w, h, b, b2; QPoint p[3]; b = 2; b2 = 2*b; w = pxCharWidth; h = (pxCharHeight / 4); if ( h < 1 ) { h = 1; } x = xl + (pxCharWidth*2); y = yl - pxCharHeight + (pxCharHeight - h - b2)/2; painter->fillRect( x, y, w, h+b2, Qt::black ); x = xl + (pxCharWidth*3); y = yl; p[0] = QPoint( x, y ); p[1] = QPoint( x, y-pxCharHeight ); p[2] = QPoint( x+pxCharWidth-1, y-(pxCharHeight/2) ); painter->setBrush(Qt::red); painter->drawPolygon( p, 3 ); x = xl + (pxCharWidth*2); y = yl - pxCharHeight + (pxCharHeight - h)/2; painter->fillRect( x+b, y, w, h, Qt::red ); } //---------------------------------------------------------------------------- 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::drawAsmLine( QPainter *painter, int x, int y, const char *txt ) { int i=0; char c[2]; c[0] = 0; c[1] = 0; // CD Log Area painter->setPen( this->palette().color(QPalette::WindowText)); while ( (idrawText( x, y, tr(c) ); i++; x += pxCharWidth; } // Address Area painter->setPen( this->palette().color(QPalette::WindowText)); while ( (idrawText( x, y, tr(c) ); i++; x += pxCharWidth; } // Byte Code Area painter->setPen( this->palette().color(QPalette::WindowText)); while ( (idrawText( x, y, tr(c) ); i++; x += pxCharWidth; } // Opcode Area painter->setPen( opcodeColor ); while ( (idrawText( x, y, tr(c) ); i++; x += pxCharWidth; } // Operand Area painter->setPen( this->palette().color(QPalette::WindowText)); while ( (txt[i] != 0) ) { if ( (txt[i] == '$') && isxdigit( txt[i+1] ) ) { painter->setPen( addressColor ); c[0] = txt[i]; painter->drawText( x, y, tr(c) ); i++; x += pxCharWidth; while ( isxdigit( txt[i] ) ) { c[0] = txt[i]; painter->drawText( x, y, tr(c) ); i++; x += pxCharWidth; } } else if ( (txt[i] == '#') && (txt[i+1] == '$') && isxdigit( txt[i+2] ) ) { painter->setPen( immediateColor ); c[0] = txt[i]; painter->drawText( x, y, tr(c) ); i++; x += pxCharWidth; c[0] = txt[i]; painter->drawText( x, y, tr(c) ); i++; x += pxCharWidth; while ( isxdigit( txt[i] ) ) { c[0] = txt[i]; painter->drawText( x, y, tr(c) ); i++; x += pxCharWidth; } } else { painter->setPen( this->palette().color(QPalette::WindowText)); c[0] = txt[i]; painter->drawText( x, y, tr(c) ); i++; x += pxCharWidth; } } } //---------------------------------------------------------------------------- void QAsmView::drawLabelLine( QPainter *painter, int x, int y, const char *txt ) { int i=0; char c[2]; c[0] = 0; c[1] = 0; // Label Text painter->setPen( this->palette().color(QPalette::WindowText)); while ( (txt[i] != 0) ) { if ( isalnum(txt[i]) || (txt[i] == '_') ) { painter->setPen( labelColor ); } else { painter->setPen( this->palette().color(QPalette::WindowText)); } c[0] = txt[i]; painter->drawText( x, y, tr(c) ); i++; x += pxCharWidth; } } //---------------------------------------------------------------------------- void QAsmView::drawCommentLine( QPainter *painter, int x, int y, const char *txt ) { int i=0; char c[2]; c[0] = 0; c[1] = 0; // Comment Text painter->setPen( commentColor ); 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; int cd_boundary, asm_start_boundary; int pxCharWidth2, vOffset, vlineOffset; QPainter painter(this); QColor white("white"), black("black"), blue("blue"); QColor hlgtFG("white"), hlgtBG("blue"); bool forceDarkColor = false; bool txtHlgtSet = false; bool lineIsPC = false; QPen pen; 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(); pxCharWidth2 = (pxCharWidth/2); cd_boundary = (int)(2.5*pxCharWidth) - pxLineXScroll; asm_start_boundary = cd_boundary + (10*pxCharWidth); //asm_stop_boundary = asm_start_boundary + (9*pxCharWidth); vlineOffset = -pxLineSpacing + (pxLineSpacing - pxCharHeight)/2; painter.fillRect( 0, 0, viewWidth, viewHeight, this->palette().color(QPalette::Window) ); painter.fillRect( 0, 0, cd_boundary, viewHeight, this->palette().color(QPalette::Mid) ); if ( showByteCodes ) { painter.fillRect( asm_start_boundary, 0, (9*pxCharWidth), viewHeight, this->palette().color(QPalette::AlternateBase) ); } 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( cd_boundary, y + vlineOffset, viewWidth, pxLineSpacing, pcBgColor ); forceDarkColor = true; lineIsPC = true; } else { lineIsPC = false; } } else { lineIsPC = false; } if ( l < asmEntry.size() ) { //if ( asmEntry[l]->type != dbg_asm_entry_t::ASM_TEXT ) //{ // painter.fillRect( cd_boundary, y - pxLineSpacing + pxLineLead, viewWidth, pxLineSpacing, QColor("light blue") ); // forceDarkColor = true; //} if ( forceDarkColor ) { painter.setPen( black ); } else { painter.setPen( this->palette().color(QPalette::WindowText)); } if ( asmEntry[l]->type == dbg_asm_entry_t::ASM_TEXT ) { drawAsmLine( &painter, x, y, asmEntry[l]->text.c_str() ); } else if ( asmEntry[l]->type == dbg_asm_entry_t::SYMBOL_NAME ) { drawLabelLine( &painter, x, y, asmEntry[l]->text.c_str() ); } else { drawCommentLine( &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 + vlineOffset, 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 + vlineOffset, hlgtXd * pxCharWidth, pxLineSpacing, hlgtBG ); drawText( &painter, ax, y, s.c_str() ); } y += pxLineSpacing; } } pen = painter.pen(); pen.setWidth(3); pen.setColor( this->palette().color(QPalette::WindowText)); painter.setPen( pen ); painter.drawLine( cd_boundary, 0, cd_boundary, viewHeight ); y = pxLineSpacing; pen.setWidth(2); painter.setPen( pen ); painter.setBrush(Qt::red); vOffset = pxLineLead + (pxLineSpacing - pxLineLead - pxCharWidth)/2; for (row=0; row < nrow; row++) { x = -pxLineXScroll; l = lineOffset + row; if ( l < asmEntry.size() ) { if ( asmPC != NULL ) { if ( l == asmPC->line ) { lineIsPC = true; } else { lineIsPC = false; } } else { lineIsPC = false; } if ( lineIsPC ) { drawPointerPC( &painter, x, y ); } if ( asmEntry[l]->bpNum >= 0 ) { painter.drawEllipse( cd_boundary - pxCharWidth2, y + vlineOffset + vOffset, pxCharWidth, pxCharWidth ); } } 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) { QFont font; std::string fontString; stackBytesPerLine = 4; showAddrs = true; recalcCharsPerLine(); g_config->getOption("SDL.DebuggerStackFont", &fontString); if ( fontString.size() > 0 ) { font.fromString( QString::fromStdString( fontString ) ); } else { font.setFamily("Courier New"); font.setStyle( QFont::StyleNormal ); font.setStyleHint( QFont::Monospace ); } setFont( font ); QFontMetrics fm(font); #if QT_VERSION > QT_VERSION_CHECK(5, 11, 0) pxCharWidth = fm.horizontalAdvance(QLatin1Char('2')); #else pxCharWidth = fm.width(QLatin1Char('2')); #endif pxLineSpacing = fm.lineSpacing(); setMinimumWidth( pxCharWidth * charsPerLine ); setMinimumHeight( pxLineSpacing * 5 ); setMaximumHeight( pxLineSpacing * 20 ); } //---------------------------------------------------------------------------- DebuggerStackDisplay::~DebuggerStackDisplay(void) { } //---------------------------------------------------------------------------- void DebuggerStackDisplay::setFont( const QFont &font ) { QFontMetrics fm(font); QPlainTextEdit::setFont(font); #if QT_VERSION > QT_VERSION_CHECK(5, 11, 0) pxCharWidth = fm.horizontalAdvance(QLatin1Char('2')); #else pxCharWidth = fm.width(QLatin1Char('2')); #endif pxLineSpacing = fm.lineSpacing(); setMinimumWidth( pxCharWidth * charsPerLine ); setMinimumHeight( pxLineSpacing * 5 ); setMaximumHeight( pxLineSpacing * 20 ); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::recalcCharsPerLine(void) { charsPerLine = (showAddrs ? 5 : 1) + (stackBytesPerLine*3); } //---------------------------------------------------------------------------- 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; recalcCharsPerLine(); updateText(); } else if ( event->key() == Qt::Key_A ) { showAddrs = !showAddrs; recalcCharsPerLine(); 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; recalcCharsPerLine(); updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::sel1BytePerLine(void) { stackBytesPerLine = 1; recalcCharsPerLine(); updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::sel2BytesPerLine(void) { stackBytesPerLine = 1; recalcCharsPerLine(); updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::sel3BytesPerLine(void) { stackBytesPerLine = 3; recalcCharsPerLine(); updateText(); } //---------------------------------------------------------------------------- void DebuggerStackDisplay::sel4BytesPerLine(void) { stackBytesPerLine = 4; recalcCharsPerLine(); 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()) ); setMinimumWidth( pxCharWidth * charsPerLine ); setMinimumHeight( pxLineSpacing * 5 ); } //---------------------------------------------------------------------------- //-- PPU Control Register Widget //---------------------------------------------------------------------------- ppuCtrlRegDpy::ppuCtrlRegDpy( QWidget *parent ) : QLineEdit( parent ) { popup = NULL; setReadOnly(true); } //---------------------------------------------------------------------------- ppuCtrlRegDpy::~ppuCtrlRegDpy( void ) { //if ( popup != NULL ) //{ // popup->close(); //} } //---------------------------------------------------------------------------- bool ppuCtrlRegDpy::event(QEvent *event) { if (event->type() == QEvent::ToolTip) { QHelpEvent *helpEvent = static_cast(event); //if ( popup == NULL ) //{ // printf("Tool Tip Show\n"); //} popup = static_cast(fceuCustomToolTipShow( helpEvent, new ppuRegPopup(this) )); QToolTip::hideText(); event->ignore(); return true; } return QWidget::event(event); } //---------------------------------------------------------------------------- //-- PPU Register Tool Tip Popup //---------------------------------------------------------------------------- ppuRegPopup::ppuRegPopup( QWidget *parent ) : fceuCustomToolTip( parent ) { QVBoxLayout *vbox, *vbox1; QGridLayout *grid, *grid1; QGroupBox *ctlFrame; QFrame *winFrame; QLabel *bgAddrLbl, *sprAddrLbl; QLineEdit *ppuBgAddr; QLineEdit *ppuSprAddr; QCheckBox *bgEnabled_cbox; QCheckBox *sprites_cbox; QCheckBox *drawLeftBg_cbox; QCheckBox *drawLeftFg_cbox; QCheckBox *vwrite_cbox; QCheckBox *nmiBlank_cbox; QCheckBox *sprite8x16_cbox; QCheckBox *grayscale_cbox; QCheckBox *iRed_cbox; QCheckBox *iGrn_cbox; QCheckBox *iBlu_cbox; char stmp[32]; //QPalette pal = this->palette(); //pal.setColor( QPalette::Window , pal.color(QPalette::ToolTipBase) ); //pal.setColor( QPalette::WindowText, pal.color(QPalette::ToolTipText) ); //setPalette(pal); vbox1 = new QVBoxLayout(); vbox = new QVBoxLayout(); winFrame = new QFrame(); ctlFrame = new QGroupBox( tr("PPU Control / Mask") ); grid1 = new QGridLayout(); grid = new QGridLayout(); winFrame->setFrameShape(QFrame::Box); vbox1->addWidget( winFrame ); winFrame->setLayout( vbox ); vbox->addWidget( ctlFrame ); ctlFrame->setLayout( grid1 ); bgAddrLbl = new QLabel( tr("BG Addr") ); sprAddrLbl = new QLabel( tr("Spr Addr") ); ppuBgAddr = new QLineEdit(); ppuSprAddr = new QLineEdit(); grid->addWidget( bgAddrLbl, 0, 0 ); grid->addWidget( sprAddrLbl, 1, 0 ); grid->addWidget( ppuBgAddr, 0, 1 ); grid->addWidget( ppuSprAddr, 1, 1 ); grid1->addLayout( grid, 0, 0, 3, 1 ); bgEnabled_cbox = new QCheckBox( tr("BG Enabled") ); sprites_cbox = new QCheckBox( tr("Sprites Enabled") ); drawLeftBg_cbox = new QCheckBox( tr("Draw Left BG (8px)") ); drawLeftFg_cbox = new QCheckBox( tr("Draw Left Sprites (8px)") ); vwrite_cbox = new QCheckBox( tr("Vertical Write") ); nmiBlank_cbox = new QCheckBox( tr("NMI on vBlank") ); sprite8x16_cbox = new QCheckBox( tr("8x16 Sprites") ); grayscale_cbox = new QCheckBox( tr("Grayscale") ); iRed_cbox = new QCheckBox( tr("Intensify Red") ); iGrn_cbox = new QCheckBox( tr("Intensify Green") ); iBlu_cbox = new QCheckBox( tr("Intensify Blue") ); sprintf( stmp, "$%04X", 0x2000 + (0x400*(PPU[0] & 0x03))); ppuBgAddr->setText( tr(stmp) ); sprintf( stmp, "$%04X", (PPU[0] & 0x08) ? 0x1000 : 0x0000 ); ppuSprAddr->setText( tr(stmp) ); nmiBlank_cbox->setChecked( PPU[0] & 0x80 ); sprite8x16_cbox->setChecked( PPU[0] & 0x20 ); grayscale_cbox->setChecked( PPU[1] & 0x01 ); drawLeftBg_cbox->setChecked( PPU[1] & 0x02 ); drawLeftFg_cbox->setChecked( PPU[1] & 0x04 ); bgEnabled_cbox->setChecked( PPU[1] & 0x08 ); sprites_cbox->setChecked( PPU[1] & 0x10 ); iRed_cbox->setChecked( PPU[1] & 0x20 ); iGrn_cbox->setChecked( PPU[1] & 0x40 ); iBlu_cbox->setChecked( PPU[1] & 0x80 ); grid1->addWidget( bgEnabled_cbox , 3, 0 ); grid1->addWidget( sprites_cbox , 4, 0 ); grid1->addWidget( drawLeftBg_cbox, 5, 0 ); grid1->addWidget( drawLeftFg_cbox, 6, 0 ); grid1->addWidget( vwrite_cbox , 0, 1 ); grid1->addWidget( nmiBlank_cbox , 1, 1 ); grid1->addWidget( sprite8x16_cbox, 2, 1 ); grid1->addWidget( grayscale_cbox , 3, 1 ); grid1->addWidget( iRed_cbox , 4, 1 ); grid1->addWidget( iGrn_cbox , 5, 1 ); grid1->addWidget( iBlu_cbox , 6, 1 ); setLayout( vbox1 ); } //---------------------------------------------------------------------------- ppuRegPopup::~ppuRegPopup( void ) { //printf("Popup Deleted\n"); } //---------------------------------------------------------------------------- //--- Debugger Tabbed Data Display //---------------------------------------------------------------------------- DebuggerTabWidget::DebuggerTabWidget( int row, int col, QWidget *parent ) : QTabWidget(parent) { DebuggerTabBar *bar = new DebuggerTabBar(this); setTabBar( bar ); bar->setAcceptDrops(true); bar->setChangeCurrentOnDrag(true); setMouseTracking(true); setAcceptDrops(true); _row = row; _col = col; setMovable(true); setUsesScrollButtons(true); connect( bar, &DebuggerTabBar::beginDragOut, this,[this,bar](int index) { if (!this->indexValid(index)) { return; } QWidget *drag_tab=this->widget(index); //Fixed tab will not be dragged out if (!drag_tab /*||fixedPage.contains(drag_tab)*/) { return; } //Drag and drop the current page as a snapshot //The size adds the title bar and border QPixmap pixmap(drag_tab->size()+QSize(2,31)); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); if (painter.isActive()) { //I want to make the title bar pasted on the content //But you can't get the image of the default title bar, just draw a rectangular box //If the external theme color is set, you need to change it QRect title_rect{0,0,pixmap.width(),30}; painter.fillRect(title_rect,Qt::white); painter.drawText(title_rect,Qt::AlignLeft|Qt::AlignVCenter," "+drag_tab->windowTitle()); painter.drawRect(pixmap.rect().adjusted(0,0,-1,-1)); } painter.end(); drag_tab->render(&pixmap,QPoint(1,30)); QMimeData *mime=new QMimeData; QDrag *drag=new QDrag(bar); drag->setMimeData(mime); drag->setPixmap(pixmap); drag->setHotSpot(QPoint(10,0)); //Drag is released after the mouse bounces up, at this time to judge whether it is dragged to the outside connect(drag,&QDrag::destroyed,this,[=]{ QPoint bar_point=bar->mapFromGlobal(QCursor::pos()); //Out of range, drag out if (!bar->contentsRect().contains(bar_point)) { popPage(drag_tab); } }); drag->exec(Qt::MoveAction); }); } //---------------------------------------------------------------------------- DebuggerTabWidget::~DebuggerTabWidget(void) { } //---------------------------------------------------------------------------- bool DebuggerTabWidget::indexValid(int idx) { return ( (idx >= 0) && (idx < count()) ); } //---------------------------------------------------------------------------- void DebuggerTabWidget::dragEnterEvent(QDragEnterEvent *event) { DebuggerTabBar *w = qobject_cast(event->source()); //printf("Tab Widget Drag Enter Event: %p\n", w); if ( (w != NULL) && (event->dropAction() == Qt::MoveAction) ) { //printf("Drag Action Accepted\n"); event->acceptProposedAction(); } } //---------------------------------------------------------------------------- void DebuggerTabWidget::dropEvent(QDropEvent *event) { DebuggerTabBar *bar = qobject_cast(event->source()); //printf("Tab Widget Drop Event: %p\n", bar); if ( (bar != NULL) && (event->dropAction() == Qt::MoveAction) ) { int idx = bar->currentIndex(); DebuggerTabWidget *p = qobject_cast(bar->parent()); if ( p == NULL ) { return; } QWidget *w = p->widget(idx); if ( w ) { QString txt = bar->tabText(idx); //printf("Removing Widget from Parent:%p %p %p %i\n", bar, p, w, idx); p->removeTab( idx ); addTab(w, txt); dbgWin->updateTabVisibility(); } } } //---------------------------------------------------------------------------- void DebuggerTabWidget::mouseMoveEvent(QMouseEvent * e) { //printf("TabWidget: (%i,%i) \n", e->pos().x(), e->pos().y() );; } //---------------------------------------------------------------------------- void DebuggerTabWidget::contextMenuEvent(QContextMenuEvent *event) { buildContextMenu(event); } //---------------------------------------------------------------------------- void DebuggerTabWidget::buildContextMenu(QContextMenuEvent *event) { int i,j; QAction *act; QActionGroup *group; QMenu menu(this); QMenu *moveMenu, *subMenu; //printf("TabWidget Context\n"); moveMenu = menu.addMenu( tr("Move Tab To...") ); group = new QActionGroup(moveMenu); group->setExclusive(true); for (j=0; j<4; j++) { const char *vertText; switch (j) { default: case 0: vertText = "Upper"; break; case 1: vertText = "Mid-Upper"; break; case 2: vertText = "Mid-Lower"; break; case 3: vertText = "Lower"; break; } subMenu = moveMenu->addMenu( tr(vertText) ); for (i=0; i<2; i++) { const char *horzText = i ? "Right" : "Left"; act = new QAction(tr(horzText), &menu); subMenu->addAction(act); group->addAction(act); QWidget *w = widget( currentIndex() ); connect( act, &QAction::triggered, [w, j, i]{ dbgWin->ConsoleDebugger::moveTab( w, j, i ); } ); } } menu.exec(event->globalPos()); } //---------------------------------------------------------------------------- void DebuggerTabWidget::popPage(QWidget *page) { printf("Pop Page: %p\n", page); } //---------------------------------------------------------------------------- //--- Debugger Tabbed Data Display //---------------------------------------------------------------------------- DebuggerTabBar::DebuggerTabBar( QWidget *parent ) : QTabBar(parent) { setMouseTracking(true); setAcceptDrops(true); theDragPress = false; theDragOut = false; } //---------------------------------------------------------------------------- DebuggerTabBar::~DebuggerTabBar(void) { } //---------------------------------------------------------------------------- void DebuggerTabBar::mouseMoveEvent( QMouseEvent *event) { //printf("TabBar Mouse Move: (%i,%i) \n", event->pos().x(), event->pos().y() );; QTabBar::mouseMoveEvent(event); if ( theDragPress && event->buttons()) { //Is it out of the scope of tabbar if ( !theDragOut && !contentsRect().contains(event->pos()) ) { theDragOut=true; emit beginDragOut(this->currentIndex()); //The release will not be triggered after QDrag.exec, manually trigger it yourself //But he still seems to animate after the mouse is up, to be resolved QMouseEvent *e=new QMouseEvent(QEvent::MouseButtonRelease, this->mapFromGlobal(QCursor::pos()), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); //mouseReleaseEvent(event); QApplication::postEvent(this,e); } } } //---------------------------------------------------------------------------- void DebuggerTabBar::mousePressEvent( QMouseEvent *event) { printf("TabBar Mouse Press: (%i,%i) \n", event->pos().x(), event->pos().y() );; QTabBar::mousePressEvent(event); if ( (event->button() == Qt::LeftButton) && (currentIndex() >= 0) ) { //Save state theDragPress = true; } } //---------------------------------------------------------------------------- void DebuggerTabBar::mouseReleaseEvent( QMouseEvent *event) { //printf("TabBar Mouse Release: (%i,%i) \n", event->pos().x(), event->pos().y() );; QTabBar::mouseReleaseEvent(event); theDragPress = false; theDragOut = false; } //---------------------------------------------------------------------------- void DebuggerTabBar::contextMenuEvent(QContextMenuEvent *event) { int idx; idx = tabAt(event->pos()); if ( idx < 0 ) { idx = currentIndex(); } if ( idx != currentIndex() ) { setCurrentIndex(idx); } DebuggerTabWidget *p = qobject_cast(parent()); if ( p ) { p->buildContextMenu(event); } } //----------------------------------------------------------------------------