fceux/src/drivers/Qt/ConsoleDebugger.cpp

4087 lines
94 KiB
C++

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