6749 lines
161 KiB
C++
6749 lines
161 KiB
C++
/* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <string>
|
|
#include <list>
|
|
|
|
#include <SDL.h>
|
|
#include <QMenu>
|
|
#include <QAction>
|
|
#include <QPainter>
|
|
#include <QSpinBox>
|
|
#include <QHeaderView>
|
|
#include <QCloseEvent>
|
|
#include <QGridLayout>
|
|
#include <QRadioButton>
|
|
#include <QInputDialog>
|
|
#include <QFontDialog>
|
|
#include <QMessageBox>
|
|
#include <QMenuBar>
|
|
#include <QMenu>
|
|
#include <QAction>
|
|
#include <QActionGroup>
|
|
#include <QApplication>
|
|
#include <QGuiApplication>
|
|
#include <QSettings>
|
|
#include <QToolTip>
|
|
#include <QWindow>
|
|
#include <QScreen>
|
|
#include <QMimeData>
|
|
#include <QDrag>
|
|
|
|
#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 <ConsoleDebugger*>::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<tabList.size(); k++)
|
|
{
|
|
if ( tabList[k].size() > 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; k<tabView[i][j]->count(); 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<int> 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; i<numWPs; i++)
|
|
{
|
|
if ( bpTree->topLevelItemCount() > 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; i<numWPs; i++)
|
|
{
|
|
if (watchpoint[i].cond)
|
|
{
|
|
freeTree(watchpoint[i].cond);
|
|
}
|
|
if (watchpoint[i].condText)
|
|
{
|
|
free(watchpoint[i].condText);
|
|
}
|
|
if (watchpoint[i].desc)
|
|
{
|
|
free(watchpoint[i].desc);
|
|
}
|
|
|
|
watchpoint[i].address = 0;
|
|
watchpoint[i].endaddress = 0;
|
|
watchpoint[i].flags = 0;
|
|
watchpoint[i].cond = 0;
|
|
watchpoint[i].condText = 0;
|
|
watchpoint[i].desc = 0;
|
|
}
|
|
numWPs = 0;
|
|
|
|
fceuWrapperUnLock();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void ConsoleDebugger::delete_BP_CB(void)
|
|
{
|
|
QTreeWidgetItem *item;
|
|
|
|
item = bpTree->currentItem();
|
|
|
|
if ( item == NULL )
|
|
{
|
|
printf( "No Item Selected\n");
|
|
return;
|
|
}
|
|
|
|
int row = bpTree->indexOfTopLevelItem(item);
|
|
|
|
DeleteBreak( row );
|
|
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; k<tabView[i][j]->count(); 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 <int> 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 <int> 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 <int> 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<numWPs; i++)
|
|
{
|
|
if ( (watchpoint[i].flags & WP_X) == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
if ( (watchpoint[i].flags & (BT_P|BT_S) ) != 0 )
|
|
{
|
|
continue;
|
|
}
|
|
if ( watchpoint[i].endaddress )
|
|
{
|
|
if ( (addr >= 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; i<asmEntry.size(); i++)
|
|
{
|
|
asmEntry[i]->bpNum = 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; i<asmEntry.size(); i++)
|
|
//{
|
|
// if ( asmEntry[i]->addr >= 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; j<size; j++)
|
|
{
|
|
a->opcode[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 <ConsoleDebugger*>::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 <ConsoleDebugger*>::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; i<numWPs; i++)
|
|
{
|
|
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;
|
|
|
|
fprintf( fp, "BreakPoint: startAddr=%08X endAddr=%08X flags=%s condition=\"%s\" desc=\"%s\" \n",
|
|
watchpoint[i].address, watchpoint[i].endaddress, flags,
|
|
(watchpoint[i].condText != NULL) ? watchpoint[i].condText : "",
|
|
(watchpoint[i].desc != NULL) ? watchpoint[i].desc : "");
|
|
}
|
|
|
|
bm = dbgBmMgr.begin();
|
|
|
|
while ( bm != NULL )
|
|
{
|
|
fprintf( fp, "Bookmark: addr=%04X desc=\"%s\" \n", bm->addr, 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<asmEntry.size(); i++)
|
|
{
|
|
delete asmEntry[i];
|
|
}
|
|
asmEntry.clear();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QAsmView::calcLineOffsets(void)
|
|
{
|
|
pcLocLinePos = 4;
|
|
byteCodeLinePos = pcLocLinePos + 8; // 12;
|
|
opcodeLinePos = byteCodeLinePos + (showByteCodes ? 10 : 1); //22;
|
|
operandLinePos = opcodeLinePos + 3; // 25;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QAsmView::setLine(int lineNum)
|
|
{
|
|
lineOffset = lineNum;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QAsmView::setXScroll(int value)
|
|
{
|
|
if ( viewWidth >= 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<QHelpEvent *>(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 ( (i<pcLocLinePos) && (txt[i] != 0) )
|
|
{
|
|
c[0] = txt[i];
|
|
painter->drawText( x, y, tr(c) );
|
|
i++; x += pxCharWidth;
|
|
}
|
|
|
|
// Address Area
|
|
painter->setPen( this->palette().color(QPalette::WindowText));
|
|
|
|
while ( (i<byteCodeLinePos) && (txt[i] != 0) )
|
|
{
|
|
c[0] = txt[i];
|
|
painter->drawText( x, y, tr(c) );
|
|
i++; x += pxCharWidth;
|
|
}
|
|
|
|
// Byte Code Area
|
|
painter->setPen( this->palette().color(QPalette::WindowText));
|
|
|
|
while ( (i<opcodeLinePos) && (txt[i] != 0) )
|
|
{
|
|
c[0] = txt[i];
|
|
painter->drawText( x, y, tr(c) );
|
|
i++; x += pxCharWidth;
|
|
}
|
|
|
|
// Opcode Area
|
|
painter->setPen( opcodeColor );
|
|
|
|
while ( (i<operandLinePos) && (txt[i] != 0) )
|
|
{
|
|
c[0] = txt[i];
|
|
painter->drawText( 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 <int, debuggerBookmark_t*>::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 <int, debuggerBookmark_t*>::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 <int, debuggerBookmark_t*>::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 <int, debuggerBookmark_t*>::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 <int, debuggerBookmark_t*>::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<QHelpEvent *>(event);
|
|
//if ( popup == NULL )
|
|
//{
|
|
// printf("Tool Tip Show\n");
|
|
//}
|
|
popup = static_cast<ppuRegPopup*>(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<DebuggerTabBar*>(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<DebuggerTabBar*>(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<DebuggerTabWidget*>(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<DebuggerTabWidget*>(parent());
|
|
|
|
if ( p )
|
|
{
|
|
p->buildContextMenu(event);
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|