diff --git a/README b/README index e75719a6..59cfcc13 100644 --- a/README +++ b/README @@ -56,7 +56,7 @@ To compile faster with multiple processes in parallel: After a sucessful compilation, the fceux binary will be generated to ./build/src/fceux . You can install fceux to your system with the following command: - make install + sudo make install You can optionally define a install prefix when running cmake from the previous step: diff --git a/TODO-SDL b/TODO-SDL index 90a21899..83f91b5c 100644 --- a/TODO-SDL +++ b/TODO-SDL @@ -42,12 +42,12 @@ RAM Search Window | NO | NO RAM Watch Window | NO | YES | Memory Watch Window | NO | NO | TAS Editor | NO | NO | -6502 Debugger Window | NO | YES | +6502 Debugger Window | YES | YES | PPU Viewer | NO | NO | Name Table Viewer | NO | NO | -Memory Hex Editor | NO | YES | -Trace Logger | NO | NO | -Code/Data Logger | NO | NO | +Memory Hex Editor | YES | YES | +Trace Logger | YES | NO | +Code/Data Logger | YES | NO | Game Genie Encoder/Decoder | NO | NO | iNES Header Editor | NO | NO | Built in help pages | NO | NO | diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ef224bb..41036c60 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -432,9 +432,13 @@ set(SRC_DRIVERS_SDL ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/LuaControl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/CheatsConf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/HexEditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/CodeDataLogger.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/SymbolicDebug.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleDebugger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleUtilities.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleVideoConf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ConsoleSoundConf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/TraceLogger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/AboutWindow.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/fceuWrapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/config.cpp diff --git a/src/debug.cpp b/src/debug.cpp index 47e873ed..3c9e8703 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -421,12 +421,12 @@ int condition(watchpointinfo* wp) //--------------------- -volatile int codecount, datacount, undefinedcount; -unsigned char *cdloggerdata; +volatile int codecount = 0, datacount = 0, undefinedcount = 0; +unsigned char *cdloggerdata = NULL; unsigned int cdloggerdataSize = 0; -static int indirectnext; +static int indirectnext = 0; -int debug_loggingCD; +int debug_loggingCD = 0; //called by the cpu to perform logging if CDLogging is enabled void LogCDVectors(int which){ @@ -865,10 +865,5 @@ void DebugCycle() if(debug_loggingCD) LogCDData(opcode, A, size); -#ifdef WIN32 - //This needs to be windows only or else the linux build system will fail since logging is declared in a - //windows source file FCEUD_TraceInstruction(opcode, size); -#endif - } diff --git a/src/debug.h b/src/debug.h index ef21f4c1..a3772f32 100644 --- a/src/debug.h +++ b/src/debug.h @@ -59,6 +59,7 @@ typedef struct { //mbg merge 7/18/06 had to make this extern extern watchpointinfo watchpoint[65]; //64 watchpoints, + 1 reserved for step over +extern unsigned int debuggerPageSize; int getBank(int offs); int GetNesFileAddress(int A); int GetPRGAddress(int A); diff --git a/src/drivers/Qt/CheatsConf.cpp b/src/drivers/Qt/CheatsConf.cpp index 1f53e556..18af6258 100644 --- a/src/drivers/Qt/CheatsConf.cpp +++ b/src/drivers/Qt/CheatsConf.cpp @@ -45,6 +45,7 @@ GuiCheatsDialog_t::GuiCheatsDialog_t(QWidget *parent) //printf("Pix Ratio: %f \n", devPixRatio ); } + font.setFamily("Courier New"); font.setStyle( QFont::StyleNormal ); font.setStyleHint( QFont::Monospace ); diff --git a/src/drivers/Qt/CodeDataLogger.cpp b/src/drivers/Qt/CodeDataLogger.cpp new file mode 100644 index 00000000..3f209fb4 --- /dev/null +++ b/src/drivers/Qt/CodeDataLogger.cpp @@ -0,0 +1,840 @@ +// CodeDataLogger.cpp +// +#include +#include +#include +#include + +#include "../../types.h" +#include "../../fceu.h" +#include "../../cart.h" +#include "../../x6502.h" +#include "../../debug.h" +#include "../../ppu.h" +#include "../../ines.h" +#include "../../nsf.h" + +#include "Qt/ConsoleUtilities.h" +#include "Qt/CodeDataLogger.h" +#include "Qt/main.h" +#include "Qt/dface.h" +#include "Qt/input.h" +#include "Qt/config.h" +#include "Qt/fceuWrapper.h" + +static int autoSaveCDL = true; +static int autoLoadCDL = true; +static int autoResumeCDL = false; +static char loadedcdfile[512] = {0}; + +static int getDefaultCDLFile( char *filepath ); +//---------------------------------------------------- +CodeDataLoggerDialog_t::CodeDataLoggerDialog_t(QWidget *parent) + : QDialog( parent ) +{ + QVBoxLayout *mainLayout, *vbox1, *vbox; + QHBoxLayout *hbox; + QGridLayout *grid; + QGroupBox *frame, *subframe; + QPushButton *btn; + + updateTimer = new QTimer( this ); + + connect( updateTimer, &QTimer::timeout, this, &CodeDataLoggerDialog_t::updatePeriodic ); + + setWindowTitle( tr("Code Data Logger") ); + + mainLayout = new QVBoxLayout(); + vbox1 = new QVBoxLayout(); + hbox = new QHBoxLayout(); + grid = new QGridLayout(); + statLabel = new QLabel( tr(" Logger is Paused: Press Start to Run ") ); + cdlFileLabel = new QLabel( tr("CDL File:") ); + + vbox1->addLayout( grid ); + vbox1->addLayout( hbox ); + vbox1->addWidget( cdlFileLabel ); + + hbox->addWidget( statLabel, 0, Qt::AlignHCenter ); + + frame = new QGroupBox(tr("Code/Data Log Status")); + frame->setLayout( vbox1 ); + + prgLoggedCodeLabel = new QLabel( tr("0x000000 0.00%") ); + prgLoggedDataLabel = new QLabel( tr("0x000000 0.00%") ); + prgUnloggedLabel = new QLabel( tr("0x000000 0.00%") ); + chrLoggedCodeLabel = new QLabel( tr("0x000000 0.00%") ); + chrLoggedDataLabel = new QLabel( tr("0x000000 0.00%") ); + chrUnloggedLabel = new QLabel( tr("0x000000 0.00%") ); + autoSaveCdlCbox = new QCheckBox( tr("Auto-save .CDL when closing ROMs") ); + autoLoadCdlCbox = new QCheckBox( tr("Auto-load .CDL when opening this window") ); + autoResumeLogCbox = new QCheckBox( tr("Auto-resume logging when loading ROMs") ); + + g_config->getOption("SDL.AutoSaveCDL", &autoSaveCDL); + g_config->getOption("SDL.AutoLoadCDL", &autoLoadCDL); + g_config->getOption("SDL.AutoResumeCDL", &autoResumeCDL); + + autoSaveCdlCbox->setChecked( autoSaveCDL ); + autoLoadCdlCbox->setChecked( autoLoadCDL ); + autoResumeLogCbox->setChecked( autoResumeCDL ); + + connect(autoSaveCdlCbox , SIGNAL(stateChanged(int)), this, SLOT(autoSaveCdlStateChange(int)) ); + connect(autoLoadCdlCbox , SIGNAL(stateChanged(int)), this, SLOT(autoLoadCdlStateChange(int)) ); + connect(autoResumeLogCbox, SIGNAL(stateChanged(int)), this, SLOT(autoResumeCdlStateChange(int)) ); + + subframe = new QGroupBox(tr("PRG Logged as Code")); + vbox = new QVBoxLayout(); + vbox->addWidget( prgLoggedCodeLabel ); + subframe->setLayout( vbox ); + + grid->addWidget( subframe, 0, 0, Qt::AlignCenter ); + + subframe = new QGroupBox(tr("PRG Logged as Data")); + vbox = new QVBoxLayout(); + vbox->addWidget( prgLoggedDataLabel ); + subframe->setLayout( vbox ); + + grid->addWidget( subframe, 0, 1, Qt::AlignCenter ); + + subframe = new QGroupBox(tr("PRG not Logged")); + vbox = new QVBoxLayout(); + vbox->addWidget( prgUnloggedLabel ); + subframe->setLayout( vbox ); + + grid->addWidget( subframe, 0, 2, Qt::AlignCenter ); + + subframe = new QGroupBox(tr("CHR Logged as Code")); + vbox = new QVBoxLayout(); + vbox->addWidget( chrLoggedCodeLabel ); + subframe->setLayout( vbox ); + + grid->addWidget( subframe, 1, 0, Qt::AlignCenter ); + + subframe = new QGroupBox(tr("CHR Logged as Data")); + vbox = new QVBoxLayout(); + vbox->addWidget( chrLoggedDataLabel ); + subframe->setLayout( vbox ); + + grid->addWidget( subframe, 1, 1, Qt::AlignCenter ); + + subframe = new QGroupBox(tr("CHR not Logged")); + vbox = new QVBoxLayout(); + vbox->addWidget( chrUnloggedLabel ); + subframe->setLayout( vbox ); + + grid->addWidget( subframe, 1, 2, Qt::AlignCenter ); + + grid = new QGridLayout(); + vbox1->addLayout( grid ); + btn = new QPushButton( tr("Reset Log") ); + grid->addWidget( btn, 0, 0, Qt::AlignCenter ); + connect( btn, SIGNAL(clicked(void)), this, SLOT(ResetCDLogClicked(void))); + + startPauseButton = new QPushButton( tr("Start") ); + grid->addWidget( startPauseButton, 0, 1, Qt::AlignCenter ); + connect( startPauseButton, SIGNAL(clicked(void)), this, SLOT(StartPauseCDLogClicked(void))); + + btn = new QPushButton( tr("Save") ); + grid->addWidget( btn, 0, 2, Qt::AlignCenter ); + connect( btn, SIGNAL(clicked(void)), this, SLOT(saveCdlFile(void))); + + btn = new QPushButton( tr("Load") ); + grid->addWidget( btn, 1, 0, Qt::AlignCenter ); + connect( btn, SIGNAL(clicked(void)), this, SLOT(loadCdlFile(void))); + + btn = new QPushButton( tr("Save As") ); + grid->addWidget( btn, 1, 2, Qt::AlignCenter ); + connect( btn, SIGNAL(clicked(void)), this, SLOT(saveCdlFileAs(void))); + + hbox = new QHBoxLayout(); + vbox1->addLayout( hbox ); + + subframe = new QGroupBox(tr("Logging Workflow Options")); + vbox = new QVBoxLayout(); + vbox->addWidget( autoSaveCdlCbox ); + vbox->addWidget( autoLoadCdlCbox ); + vbox->addWidget( autoResumeLogCbox ); + subframe->setLayout( vbox ); + hbox->addWidget( subframe ); + + subframe = new QGroupBox(tr("Generate ROM")); + vbox = new QVBoxLayout(); + + btn = new QPushButton( tr("Save Stripped Data") ); + vbox->addWidget( btn ); + connect( btn, SIGNAL(clicked(void)), this, SLOT(SaveStrippedROMClicked(void))); + btn = new QPushButton( tr("Save Unused Data") ); + vbox->addWidget( btn ); + connect( btn, SIGNAL(clicked(void)), this, SLOT(SaveUnusedROMClicked(void))); + subframe->setLayout( vbox ); + hbox->addWidget( subframe ); + + mainLayout->addWidget( frame ); + + setLayout( mainLayout ); + + updateTimer->start( 200 ); // 5hz + + if (autoLoadCDL) + { + char nameo[2048]; + getDefaultCDLFile( nameo ); + LoadCDLog(nameo); + } +} +//---------------------------------------------------- +CodeDataLoggerDialog_t::~CodeDataLoggerDialog_t(void) +{ + updateTimer->stop(); + + printf("Code Data Logger Window Deleted\n"); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::closeEvent(QCloseEvent *event) +{ + printf("Code Data Logger Close Window Event\n"); + done(0); + deleteLater(); + event->accept(); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::closeWindow(void) +{ + printf("Code Data Logger Close Window\n"); + done(0); + deleteLater(); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::autoSaveCdlStateChange(int state) +{ + autoSaveCDL = state != Qt::Unchecked; + + g_config->setOption("SDL.AutoSaveCDL", autoSaveCDL); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::autoLoadCdlStateChange(int state) +{ + autoLoadCDL = state != Qt::Unchecked; + + g_config->setOption("SDL.AutoLoadCDL", autoLoadCDL); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::autoResumeCdlStateChange(int state) +{ + autoResumeCDL = state != Qt::Unchecked; + + g_config->setOption("SDL.AutoResumeCDL", autoResumeCDL); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::updatePeriodic(void) +{ + char str[768]; + float fcodecount = codecount; + float fdatacount = datacount; + float frendercount = rendercount; + float fvromreadcount = vromreadcount; + float fundefinedcount = undefinedcount; + float fundefinedvromcount = undefinedvromcount; + float fromsize = cdloggerdataSize; + float fvromsize = (cdloggerVideoDataSize != 0) ? cdloggerVideoDataSize : 1; + + if ( FCEUI_GetLoggingCD() ) + { + startPauseButton->setText( tr("Pause") ); + statLabel->setText( tr(" Logger is Running: Press Pause to Stop ") ); + statLabel->setStyleSheet("background-color: green; color: white;"); + } + else + { + startPauseButton->setText( tr("Start") ); + statLabel->setText( tr(" Logger is Paused: Press Start to Run ") ); + statLabel->setStyleSheet("background-color: red; color: white;"); + } + + if ( cdloggerdataSize > 0 ) + { + sprintf(str,"0x%06x %.2f%%", codecount, (fcodecount / fromsize) * 100); + prgLoggedCodeLabel->setText( tr(str) ); + + sprintf(str,"0x%06x %.2f%%", datacount,(fdatacount / fromsize) * 100); + prgLoggedDataLabel->setText( tr(str) ); + + sprintf(str,"0x%06x %.2f%%", undefinedcount, (fundefinedcount / fromsize) * 100); + prgUnloggedLabel->setText( tr(str) ); + + sprintf(str,"0x%06x %.2f%%", rendercount, (frendercount / fvromsize) * 100); + chrLoggedCodeLabel->setText( tr(str) ); + + sprintf(str,"0x%06x %.2f%%", vromreadcount, (fvromreadcount / fvromsize) * 100); + chrLoggedDataLabel->setText( tr(str) ); + + sprintf(str,"0x%06x %.2f%%", undefinedvromcount, (fundefinedvromcount / fvromsize) * 100); + chrUnloggedLabel->setText( tr(str) ); + } + else + { + prgLoggedCodeLabel->setText( tr("------") ); + prgLoggedDataLabel->setText( tr("------") ); + prgUnloggedLabel->setText( tr("------") ); + chrLoggedCodeLabel->setText( tr("------") ); + chrLoggedDataLabel->setText( tr("------") ); + chrUnloggedLabel->setText( tr("------") ); + } + + sprintf( str, "CDL File: %s", loadedcdfile ); + + cdlFileLabel->setText( tr(str) ); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::ResetCDLogClicked(void) +{ + ::ResetCDLog(); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::StartPauseCDLogClicked(void) +{ + if ( FCEUI_GetLoggingCD() ) + { + //printf("CD Logging Paused\n"); + PauseCDLogging(); + startPauseButton->setText( tr("Start") ); + } + else + { + //printf("CD Logging Started\n"); + StartCDLogging(); + startPauseButton->setText( tr("Pause") ); + } +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::saveCdlFile(void) +{ + SaveCDLogFile(); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::saveCdlFileAs(void) +{ + int ret, useNativeFileDialogVal; + QString filename; + const char *romFile; + QFileDialog dialog(this, tr("Save CDL To File") ); + + dialog.setFileMode(QFileDialog::AnyFile); + + dialog.setNameFilter(tr("CDL Files (*.cdl *.CDL) ;; All files (*)")); + + dialog.setViewMode(QFileDialog::List); + dialog.setFilter( QDir::AllEntries | QDir::Hidden ); + dialog.setLabelText( QFileDialog::Accept, tr("Save") ); + dialog.setDefaultSuffix( tr(".cdl") ); + + romFile = getRomFile(); + + if ( romFile != NULL ) + { + char dir[512], base[256]; + + parseFilepath( romFile, dir, base ); + + strcat( base, ".cdl"); + + dialog.setDirectory( tr(dir) ); + + dialog.selectFile( tr(base) ); + } + + // Check config option to use native file dialog or not + g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal); + + dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal); + + dialog.show(); + ret = dialog.exec(); + + if ( ret ) + { + QStringList fileList; + fileList = dialog.selectedFiles(); + + if ( fileList.size() > 0 ) + { + filename = fileList[0]; + } + } + + if ( filename.isNull() ) + { + return; + } + //qDebug() << "selected file path : " << filename.toUtf8(); + + fceuWrapperLock(); + strcpy( loadedcdfile, filename.toStdString().c_str() ); + SaveCDLogFile(); + fceuWrapperUnLock(); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::loadCdlFile(void) +{ + int ret, useNativeFileDialogVal; + QString filename; + char dir[512]; + const char *romFile; + QFileDialog dialog(this, tr("Load CDL File") ); + + dialog.setFileMode(QFileDialog::ExistingFile); + + dialog.setNameFilter(tr("CDL files (*.cdl *.CDL) ;; All files (*)")); + + dialog.setViewMode(QFileDialog::List); + dialog.setFilter( QDir::AllEntries | QDir::Hidden ); + dialog.setLabelText( QFileDialog::Accept, tr("Load") ); + + romFile = getRomFile(); + + if ( romFile ) + { + getDirFromFile( romFile, dir ); + + dialog.setDirectory( tr(dir) ); + } + + // Check config option to use native file dialog or not + g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal); + + dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal); + + dialog.show(); + ret = dialog.exec(); + + if ( ret ) + { + QStringList fileList; + fileList = dialog.selectedFiles(); + + if ( fileList.size() > 0 ) + { + filename = fileList[0]; + } + } + + if ( filename.isNull() ) + { + return; + } + //qDebug() << "selected file path : " << filename.toUtf8(); + + fceuWrapperLock(); + LoadCDLog ( filename.toStdString().c_str() ); + fceuWrapperUnLock(); + + return; +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::SaveStrippedROM(int invert) +{ + + //this is based off of iNesSave() + //todo: make this support NSF + // + if (!GameInfo) + return; + + if (GameInfo->type==GIT_NSF) + { + printf("Sorry, you're not allowed to save optimized NSFs yet. Please don't optimize individual banks, as there are still some issues with several NSFs to be fixed, and it is easier to fix those issues with as much of the bank data intact as possible."); + return; + } + + if (codecount == 0) + { + printf("Unable to Generate Stripped ROM. Get Something Logged and try again."); + return; + } + + int i, ret, useNativeFileDialogVal; + QString filename; + const char *romFile; + QFileDialog dialog(this, tr("Save Stripped File As...") ); + + dialog.setFileMode(QFileDialog::AnyFile); + + if (GameInfo->type==GIT_NSF) + { + dialog.setNameFilter(tr("NSF Files (*.nsf *.NSF) ;; All files (*)")); + dialog.setDefaultSuffix( tr(".nsf") ); + } + else + { + dialog.setNameFilter(tr("NES Files (*.nes *.NES) ;; All files (*)")); + dialog.setDefaultSuffix( tr(".nes") ); + } + dialog.setViewMode(QFileDialog::List); + dialog.setFilter( QDir::AllEntries | QDir::Hidden ); + dialog.setLabelText( QFileDialog::Accept, tr("Save") ); + + romFile = getRomFile(); + + if ( romFile != NULL ) + { + char dir[512], base[256]; + + parseFilepath( romFile, dir, base ); + + dialog.setDirectory( tr(dir) ); + } + + // Check config option to use native file dialog or not + g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal); + + dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal); + + dialog.show(); + ret = dialog.exec(); + + if ( ret ) + { + QStringList fileList; + fileList = dialog.selectedFiles(); + + if ( fileList.size() > 0 ) + { + filename = fileList[0]; + } + } + + if ( filename.isNull() ) + { + return; + } + //qDebug() << "selected file path : " << filename.toUtf8(); + + FILE *fp = fopen( filename.toStdString().c_str(),"wb"); + if (!fp) + { + FCEUD_PrintError("Error opening target stripped rom file!"); + return; + } + + if (GameInfo->type==GIT_NSF) + { + uint8 NSFLoadLow; + uint8 NSFLoadHigh; + //Not used because if bankswitching, the addresses involved + //could still end up being used through writes + //static uint16 LoadAddr; + //LoadAddr=NSFHeader.LoadAddressLow; + //LoadAddr|=(NSFHeader.LoadAddressHigh&0x7F)<<8; + + //Simple store/restore for writing a working NSF header + NSFLoadLow = NSFHeader.LoadAddressLow; + NSFLoadHigh = NSFHeader.LoadAddressHigh; + NSFHeader.LoadAddressLow=0; + NSFHeader.LoadAddressHigh&=0xF0; + fwrite(&NSFHeader,1,0x8,fp); + NSFHeader.LoadAddressLow = NSFLoadLow; + NSFHeader.LoadAddressHigh = NSFLoadHigh; + + fseek(fp,0x8,SEEK_SET); + for (i = 0;i < ((NSFMaxBank+1)*4096);i++){ + unsigned char pchar; + if (cdloggerdata[i] & 3) + { + pchar = invert?0:NSFDATA[i]; + } + else + { + pchar = invert?NSFDATA[i]:0; + } + fputc(pchar, fp); + } + + } + else + { + iNES_HEADER cdlhead; + + cdlhead.ID[0] = 'N'; + cdlhead.ID[1] = 'E'; + cdlhead.ID[2] = 'S'; + cdlhead.ID[3] = 0x1A; + + cdlhead.ROM_size = cdloggerdataSize >> 14; + cdlhead.VROM_size = cdloggerVideoDataSize >> 13; + + fwrite(&cdlhead,1,16,fp); + + for (i = 0; i < (int)cdloggerdataSize; i++){ + unsigned char pchar; + if (cdloggerdata[i] & 3) + { + pchar = invert?0:PRGptr[0][i]; + } + else + { + pchar = invert?PRGptr[0][i]:0; + } + fputc(pchar, fp); + } + + if (cdloggerVideoDataSize != 0) + { + // since the OldPPU at least logs the $2007 read accesses, we should save the data anyway + for (i = 0; i < (int)cdloggerVideoDataSize; i++) { + unsigned char vchar; + if (cdloggervdata[i] & 3) + { + vchar = invert?0:CHRptr[0][i]; + } + else + { + vchar = invert?CHRptr[0][i]:0; + } + fputc(vchar, fp); + } + } + } + fclose(fp); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::SaveStrippedROMClicked(void) +{ + SaveStrippedROM(0); +} +//---------------------------------------------------- +void CodeDataLoggerDialog_t::SaveUnusedROMClicked(void) +{ + SaveStrippedROM(1); +} +//---------------------------------------------------- +static int getDefaultCDLFile( char *filepath ) +{ + const char *romFile; + char dir[512], baseFile[256]; + + filepath[0] = 0; + + romFile = getRomFile(); + + if ( romFile == NULL ) + { + return -1; + } + + parseFilepath( romFile, dir, baseFile ); + + sprintf( filepath, "%s/%s.cdl", dir, baseFile ); + + //printf("%s\n", filepath ); + + return 0; +} +//---------------------------------------------------- +void FreeCDLog(void) +{ + fceuWrapperLock(); + if (cdloggerdata) + { + free(cdloggerdata); + cdloggerdata = NULL; + cdloggerdataSize = 0; + } + if (cdloggervdata) + { + free(cdloggervdata); + cdloggervdata = NULL; + cdloggerVideoDataSize = 0; + } + fceuWrapperUnLock(); +} +//---------------------------------------------------- +void InitCDLog(void) +{ + fceuWrapperLock(); + cdloggerdataSize = PRGsize[0]; + cdloggerdata = (unsigned char*)malloc(cdloggerdataSize); + if (!CHRram[0] || (CHRptr[0] == PRGptr[0])) { // Some kind of workaround for my OneBus VRAM hack, will remove it if I find another solution for that + cdloggerVideoDataSize = CHRsize[0]; + cdloggervdata = (unsigned char*)malloc(cdloggerVideoDataSize); + } else { + if (GameInfo->type != GIT_NSF) { + cdloggerVideoDataSize = 0; + cdloggervdata = (unsigned char*)malloc(8192); + } + } + fceuWrapperUnLock(); +} +//---------------------------------------------------- +void ResetCDLog(void) +{ + if ( GameInfo == NULL ) + { + return; + } + fceuWrapperLock(); + + codecount = datacount = rendercount = vromreadcount = 0; + undefinedcount = cdloggerdataSize; + + if ( cdloggerdata != NULL ) + { + memset(cdloggerdata, 0, cdloggerdataSize); + } + if (cdloggerVideoDataSize != 0) + { + undefinedvromcount = cdloggerVideoDataSize; + + if ( cdloggervdata != NULL ) + { + memset(cdloggervdata, 0, cdloggerVideoDataSize); + } + } + else + { + if (GameInfo->type != GIT_NSF) + { + undefinedvromcount = 8192; + memset(cdloggervdata, 0, 8192); + } + } + fceuWrapperUnLock(); +} +//---------------------------------------------------- +bool LoadCDLog(const char* nameo) +{ + FILE *FP; + int i,j; + + FP = fopen(nameo, "rb"); + if (FP == NULL) + { + return false; + } + + for(i = 0;i < (int)cdloggerdataSize;i++) + { + j = fgetc(FP); + if (j == EOF) + break; + if ((j & 1) && !(cdloggerdata[i] & 1)) + codecount++; //if the new byte has something logged and + if ((j & 2) && !(cdloggerdata[i] & 2)) + datacount++; //and the old one doesn't. Then increment + if ((j & 3) && !(cdloggerdata[i] & 3)) + undefinedcount--; //the appropriate counter. + cdloggerdata[i] |= j; + } + + if(cdloggerVideoDataSize != 0) + { + for(i = 0;i < (int)cdloggerVideoDataSize;i++) + { + j = fgetc(FP); + if(j == EOF)break; + if((j & 1) && !(cdloggervdata[i] & 1))rendercount++; //if the new byte has something logged and + if((j & 2) && !(cdloggervdata[i] & 2))vromreadcount++; //if the new byte has something logged and + if((j & 3) && !(cdloggervdata[i] & 3))undefinedvromcount--; //the appropriate counter. + cdloggervdata[i] |= j; + } + } + + fclose(FP); + RenameCDLog(nameo); + + return true; +} +//---------------------------------------------------- +void StartCDLogging(void) +{ + fceuWrapperLock(); + FCEUI_SetLoggingCD(1); + //EnableTracerMenuItems(); + //SetDlgItemText(hCDLogger, BTN_CDLOGGER_START_PAUSE, "Pause"); + fceuWrapperUnLock(); +} +//---------------------------------------------------- +bool PauseCDLogging(void) +{ + // can't pause while Trace Logger is using + //if ((logging) && (logging_options & LOG_NEW_INSTRUCTIONS)) + //{ + // MessageBox(hCDLogger, "The Trace Logger is currently using this for some of its features.\nPlease turn the Trace Logger off and try again.","Unable to Pause Code/Data Logger", MB_OK); + // return false; + //} + fceuWrapperLock(); + FCEUI_SetLoggingCD(0); + //EnableTracerMenuItems(); + //SetDlgItemText(hCDLogger, BTN_CDLOGGER_START_PAUSE, "Start"); + fceuWrapperUnLock(); + return true; +} +//---------------------------------------------------- +void CDLoggerROMClosed(void) +{ + PauseCDLogging(); + if (autoSaveCDL) + { + SaveCDLogFile(); + } +} +//---------------------------------------------------- +void CDLoggerROMChanged(void) +{ + FreeCDLog(); + InitCDLog(); + ResetCDLog(); + RenameCDLog(""); + + if (!autoResumeCDL) + return; + + // try to load respective CDL file + char nameo[1024]; + getDefaultCDLFile( nameo ); + + FILE *FP; + FP = fopen(nameo, "rb"); + if (FP != NULL) + { + // .cdl file with this ROM name exists + fclose(FP); + //if (!hCDLogger) + //{ + // DoCDLogger(); + //} + if (LoadCDLog(nameo)) + { + StartCDLogging(); + } + } +} +//---------------------------------------------------- +void RenameCDLog(const char* newName) +{ + strcpy(loadedcdfile, newName); +} +//---------------------------------------------------- +void SaveCDLogFile(void) +{ + if (loadedcdfile[0] == 0) + { + char nameo[1024]; + getDefaultCDLFile( nameo ); + RenameCDLog(nameo); + } + + FILE *FP; + FP = fopen(loadedcdfile, "wb"); + if (FP == NULL) + { + FCEUD_PrintError("Error Saving File"); + return; + } + fwrite(cdloggerdata, cdloggerdataSize, 1, FP); + if (cdloggerVideoDataSize != 0) + { + fwrite(cdloggervdata, cdloggerVideoDataSize, 1, FP); + } + fclose(FP); +} +//---------------------------------------------------- diff --git a/src/drivers/Qt/CodeDataLogger.h b/src/drivers/Qt/CodeDataLogger.h new file mode 100644 index 00000000..ff8241b6 --- /dev/null +++ b/src/drivers/Qt/CodeDataLogger.h @@ -0,0 +1,73 @@ +// CodeDataLogger.h +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CodeDataLoggerDialog_t : public QDialog +{ + Q_OBJECT + + public: + CodeDataLoggerDialog_t(QWidget *parent = 0); + ~CodeDataLoggerDialog_t(void); + + protected: + QTimer *updateTimer; + QLabel *prgLoggedCodeLabel; + QLabel *prgLoggedDataLabel; + QLabel *prgUnloggedLabel; + QLabel *chrLoggedCodeLabel; + QLabel *chrLoggedDataLabel; + QLabel *chrUnloggedLabel; + QLabel *cdlFileLabel; + QLabel *statLabel; + QCheckBox *autoSaveCdlCbox; + QCheckBox *autoLoadCdlCbox; + QCheckBox *autoResumeLogCbox; + QPushButton *startPauseButton; + void closeEvent(QCloseEvent *bar); + + void SaveStrippedROM(int invert); + + private: + + public slots: + void closeWindow(void); + private slots: + void loadCdlFile(void); + void saveCdlFile(void); + void saveCdlFileAs(void); + void updatePeriodic(void); + void ResetCDLogClicked(void); + void StartPauseCDLogClicked(void); + void autoSaveCdlStateChange(int state); + void autoLoadCdlStateChange(int state); + void autoResumeCdlStateChange(int state); + void SaveStrippedROMClicked(void); + void SaveUnusedROMClicked(void); +}; + +void InitCDLog(void); +void ResetCDLog(void); +void FreeCDLog(void); +void StartCDLogging(void); +bool PauseCDLogging(void); +bool LoadCDLog(const char* nameo); +void RenameCDLog(const char* newName); +void CDLoggerROMClosed(void); +void CDLoggerROMChanged(void); +void SaveCDLogFile(void); + diff --git a/src/drivers/Qt/ConsoleDebugger.cpp b/src/drivers/Qt/ConsoleDebugger.cpp new file mode 100644 index 00000000..9462f4bf --- /dev/null +++ b/src/drivers/Qt/ConsoleDebugger.cpp @@ -0,0 +1,3276 @@ +// ConsoleDebugger.cpp +// +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../types.h" +#include "../../fceu.h" +#include "../../cheat.h" +#include "../../debug.h" +#include "../../driver.h" +#include "../../version.h" +#include "../../video.h" +#include "../../movie.h" +#include "../../palette.h" +#include "../../fds.h" +#include "../../cart.h" +#include "../../ines.h" +#include "../../asm.h" +#include "../../ppu.h" +#include "../../x6502.h" +#include "common/configSys.h" + +#include "Qt/main.h" +#include "Qt/dface.h" +#include "Qt/config.h" +#include "Qt/nes_shm.h" +#include "Qt/fceuWrapper.h" +#include "Qt/HexEditor.h" +#include "Qt/ConsoleDebugger.h" +#include "Qt/ConsoleUtilities.h" + +// Where are these defined? +extern int vblankScanLines; +extern int vblankPixel; + +debuggerBookmarkManager_t dbgBmMgr; +static std::list dbgWinList; + +static void DeleteBreak(int sel); +//---------------------------------------------------------------------------- +ConsoleDebugger::ConsoleDebugger(QWidget *parent) + : QDialog( parent ) +{ + QHBoxLayout *mainLayout; + QVBoxLayout *vbox, *vbox1, *vbox2, *vbox3, *vbox4; + QHBoxLayout *hbox, *hbox1, *hbox2, *hbox3; + QGridLayout *grid; + QPushButton *button; + QFrame *frame; + QLabel *lbl; + float fontCharWidth; + QTreeWidgetItem * item; + int opt; + + 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 ); + + mainLayout = new QHBoxLayout(); + + 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") ); + + 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 QPlainTextEdit(this); + hbox = new QHBoxLayout(); + hbox->addWidget( stackText ); + vbox2->addWidget( stackFrame ); + stackFrame->setLayout( hbox ); + stackText->setFont(font); + stackText->setReadOnly(true); + stackText->setWordWrapMode( QTextOption::WordWrap ); + stackText->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + //stackText->setMaximumWidth( 16 * fontCharWidth ); + + bpFrame = new QGroupBox(tr("Breakpoints")); + 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 ); + 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 ); + 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); + regNamChkBox->setEnabled(false); // TODO + + connect( romOfsChkBox, SIGNAL(stateChanged(int)), this, SLOT(displayROMoffsetCB(int)) ); + connect( symDbgChkBox, SIGNAL(stateChanged(int)), this, SLOT(symbolDebugEnableCB(int)) ); + connect( regNamChkBox, SIGNAL(stateChanged(int)), this, SLOT(registerNameEnableCB(int)) ); + + + button = new QPushButton( tr("Reload Symbols") ); + vbox->addWidget( button ); + connect( button, SIGNAL(clicked(void)), this, SLOT(reloadSymbolsCB(void)) ); + + button = new QPushButton( tr("ROM Patcher") ); + vbox->addWidget( button ); + button->setEnabled(false); // TODO + + frame->setLayout( vbox ); + frame->setFrameShape( QFrame::Box ); + + hbox = new QHBoxLayout(); + vbox1->addLayout( hbox ); + + button = new QPushButton( tr("Default Window Size") ); + autoOpenChkBox = new QCheckBox( tr("Auto-Open") ); + debFileChkBox = new QCheckBox( tr("DEB Files") ); + idaFontChkBox = new QCheckBox( tr("IDA Font") ); + hbox->addWidget( button ); + hbox->addWidget( autoOpenChkBox ); + hbox->addWidget( debFileChkBox ); + hbox->addWidget( idaFontChkBox ); + + g_config->getOption( "SDL.AutoOpenDebugger", &opt ); + autoOpenChkBox->setChecked( opt ); + + g_config->getOption( "SDL.AutoLoadDebugFiles", &opt ); + debFileChkBox->setChecked( opt ); + + connect( autoOpenChkBox, SIGNAL(stateChanged(int)), this, SLOT(autoOpenDebugCB(int)) ); + connect( debFileChkBox , SIGNAL(stateChanged(int)), this, SLOT(debFileAutoLoadCB(int)) ); + + button->setEnabled(false); // TODO + + // IDA font is just a monospace font, we are forcing this anyway. It is just easier to read the assembly. + // If a different font is desired, my thought is to open a QFontDialog and let the user pick a new font, + // rather than use a checkbox that selects between two. But for the moment, I have more important things + // to do. + idaFontChkBox->setEnabled(false); + idaFontChkBox->setChecked(true); + + setLayout( mainLayout ); + + windowUpdateReq = true; + + dbgWinList.push_back( this ); + + periodicTimer = new QTimer( this ); + + connect( periodicTimer, &QTimer::timeout, this, &ConsoleDebugger::updatePeriodic ); + connect( hbar, SIGNAL(valueChanged(int)), this, SLOT(hbarChanged(int)) ); + connect( vbar, SIGNAL(valueChanged(int)), this, SLOT(vbarChanged(int)) ); + + bpListUpdate( false ); + + periodicTimer->start( 100 ); // 10hz + + // If this is the first debug window to open, load breakpoints fresh + if ( dbgWinList.size() == 1 ) + { + int autoLoadDebug; + + g_config->getOption( "SDL.AutoLoadDebugFiles", &autoLoadDebug ); + + if ( autoLoadDebug ) + { + loadGameDebugBreakpoints(); + } + } +} +//---------------------------------------------------------------------------- +ConsoleDebugger::~ConsoleDebugger(void) +{ + std::list ::iterator it; + + printf("Destroy Debugger Window\n"); + periodicTimer->stop(); + + for (it = dbgWinList.begin(); it != dbgWinList.end(); it++) + { + if ( (*it) == this ) + { + dbgWinList.erase(it); + printf("Removing Debugger Window\n"); + break; + } + } + + if ( dbgWinList.size() == 0 ) + { + saveGameDebugBreakpoints(); + debuggerClearAllBreakpoints(); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::closeEvent(QCloseEvent *event) +{ + printf("Debugger Close Window Event\n"); + done(0); + deleteLater(); + event->accept(); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::closeWindow(void) +{ + //printf("Close Window\n"); + done(0); + deleteLater(); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::bpItemClicked( QTreeWidgetItem *item, int column) +{ + int row = bpTree->indexOfTopLevelItem(item); + + //printf("Row: %i Column: %i \n", row, column ); + + if ( column == 0 ) + { + if ( (row >= 0) && (row < numWPs) ) + { + int isChecked = item->checkState( column ) != Qt::Unchecked; + + if ( isChecked ) + { + watchpoint[row].flags |= WP_E; + } + else + { + watchpoint[row].flags &= ~WP_E; + } + } + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::bmItemClicked( QTreeWidgetItem *item, int column) +{ + //int row = bmTree->indexOfTopLevelItem(item); + + //printf("Row: %i Column: %i \n", row, column ); + +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::bmItemDoubleClicked( QTreeWidgetItem *item, int column) +{ + int addr, line; + //int row = bmTree->indexOfTopLevelItem(item); + + //printf("Row: %i Column: %i \n", row, column ); + + addr = strtol( item->text(0).toStdString().c_str(), NULL, 16 ); + + line = asmView->getAsmLineFromAddr( addr ); + + asmView->setLine( line ); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::selBmAddrChanged(const QString &txt) +{ + selBmAddrVal = strtol( txt.toStdString().c_str(), NULL, 16 ); + + //printf("selBmAddrVal = %04X\n", selBmAddrVal ); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::openBpEditWindow( int editIdx, watchpointinfo *wp ) +{ + int ret; + QDialog dialog(this); + QHBoxLayout *hbox; + QVBoxLayout *mainLayout, *vbox; + QLabel *lbl; + QLineEdit *addr1, *addr2, *cond, *name; + QCheckBox *forbidChkBox, *rbp, *wbp, *xbp, *ebp; + QGridLayout *grid; + QFrame *frame; + QGroupBox *gbox; + QPushButton *okButton, *cancelButton; + QRadioButton *cpu_radio, *ppu_radio, *sprite_radio; + + if ( editIdx >= 0 ) + { + dialog.setWindowTitle( tr("Edit Breakpoint") ); + } + else + { + dialog.setWindowTitle( tr("Add Breakpoint") ); + } + + hbox = new QHBoxLayout(); + mainLayout = new QVBoxLayout(); + + mainLayout->addLayout( hbox ); + + lbl = new QLabel( tr("Address") ); + addr1 = new QLineEdit(); + + hbox->addWidget( lbl ); + hbox->addWidget( addr1 ); + + lbl = new QLabel( tr("-") ); + addr2 = new QLineEdit(); + hbox->addWidget( lbl ); + hbox->addWidget( addr2 ); + + forbidChkBox = new QCheckBox( tr("Forbid") ); + hbox->addWidget( forbidChkBox ); + + frame = new QFrame(); + vbox = new QVBoxLayout(); + hbox = new QHBoxLayout(); + gbox = new QGroupBox(); + + rbp = new QCheckBox( tr("Read") ); + wbp = new QCheckBox( tr("Write") ); + xbp = new QCheckBox( tr("Execute") ); + ebp = new QCheckBox( tr("Enable") ); + + gbox->setTitle( tr("Memory") ); + mainLayout->addWidget( frame ); + frame->setLayout( vbox ); + frame->setFrameShape( QFrame::Box ); + vbox->addLayout( hbox ); + vbox->addWidget( gbox ); + + hbox->addWidget( rbp ); + hbox->addWidget( wbp ); + hbox->addWidget( xbp ); + hbox->addWidget( ebp ); + + hbox = new QHBoxLayout(); + cpu_radio = new QRadioButton( tr("CPU Mem") ); + ppu_radio = new QRadioButton( tr("PPU Mem") ); + sprite_radio = new QRadioButton( tr("Sprite Mem") ); + cpu_radio->setChecked(true); + + gbox->setLayout( hbox ); + hbox->addWidget( cpu_radio ); + hbox->addWidget( ppu_radio ); + hbox->addWidget( sprite_radio ); + + grid = new QGridLayout(); + + mainLayout->addLayout( grid ); + lbl = new QLabel( tr("Condition") ); + cond = new QLineEdit(); + + grid->addWidget( lbl, 0, 0 ); + grid->addWidget( cond, 0, 1 ); + + lbl = new QLabel( tr("Name") ); + name = new QLineEdit(); + + grid->addWidget( lbl, 1, 0 ); + grid->addWidget( name, 1, 1 ); + + hbox = new QHBoxLayout(); + okButton = new QPushButton( tr("OK") ); + cancelButton = new QPushButton( tr("Cancel") ); + + mainLayout->addLayout( hbox ); + hbox->addWidget( cancelButton ); + hbox->addWidget( okButton ); + + connect( okButton, SIGNAL(clicked(void)), &dialog, SLOT(accept(void)) ); + connect( cancelButton, SIGNAL(clicked(void)), &dialog, SLOT(reject(void)) ); + + 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)) ); + + 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 ) + { + 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(); + } + fceuWrapperLock(); + asmView->updateAssemblyView(); + fceuWrapperUnLock(); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::bpListUpdate( bool reset ) +{ + QTreeWidgetItem *item; + char cond[128], desc[128], addrStr[32], flags[16], enable; + + if ( reset ) + { + bpTree->clear(); + } + + for (int i=0; itopLevelItemCount() > i ) + { + item = bpTree->topLevelItem(i); + } + else + { + item = NULL; + } + + if ( item == NULL ) + { + item = new QTreeWidgetItem(); + + bpTree->addTopLevelItem( item ); + } + + //item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable ); + item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren ); + + if ( watchpoint[i].endaddress > 0 ) + { + sprintf( addrStr, "$%04X-%04X:", watchpoint[i].address, watchpoint[i].endaddress ); + } + else + { + sprintf( addrStr, "$%04X:", watchpoint[i].address ); + } + + flags[0] = (watchpoint[i].flags & WP_E) ? 'E' : '-'; + + if ( watchpoint[i].flags & BT_P ) + { + flags[1] = 'P'; + } + else if ( watchpoint[i].flags & BT_S ) + { + flags[1] = 'S'; + } + else + { + flags[1] = 'C'; + } + + flags[2] = (watchpoint[i].flags & WP_R) ? 'R' : '-'; + flags[3] = (watchpoint[i].flags & WP_W) ? 'W' : '-'; + flags[4] = (watchpoint[i].flags & WP_X) ? 'X' : '-'; + flags[5] = (watchpoint[i].flags & WP_F) ? 'F' : '-'; + flags[6] = 0; + + enable = (watchpoint[i].flags & WP_E) ? 1 : 0; + + cond[0] = 0; + desc[0] = 0; + + if (watchpoint[i].desc ) + { + strcat( desc, watchpoint[i].desc); + } + + if (watchpoint[i].condText ) + { + strcat( cond, " ("); + strcat( cond, watchpoint[i].condText); + strcat( cond, ") "); + } + + item->setCheckState( 0, enable ? Qt::Checked : Qt::Unchecked ); + + item->setFont( 0, font ); + item->setFont( 1, font ); + item->setFont( 2, font ); + item->setFont( 3, font ); + + item->setText( 0, tr(addrStr)); + item->setText( 1, tr(flags) ); + item->setText( 2, tr(cond) ); + item->setText( 3, tr(desc) ); + + item->setTextAlignment( 0, Qt::AlignLeft); + item->setTextAlignment( 1, Qt::AlignLeft); + item->setTextAlignment( 2, Qt::AlignLeft); + item->setTextAlignment( 3, Qt::AlignLeft); + } + + bpTree->viewport()->update(); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::add_BM_CB(void) +{ + dbgBmMgr.addBookmark( selBmAddrVal ); + + bmListUpdate(false); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::edit_BM_CB(void) +{ + int addr; + std::string s; + QTreeWidgetItem *item; + + item = bmTree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + s = item->text(0).toStdString(); + + addr = strtol( s.c_str(), NULL, 16 ); + + edit_BM_name( addr ); + +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::delete_BM_CB(void) +{ + int addr; + std::string s; + QTreeWidgetItem *item; + + item = bmTree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + s = item->text(0).toStdString(); + + addr = strtol( s.c_str(), NULL, 16 ); + + dbgBmMgr.deleteBookmark( addr ); + + bmListUpdate(true); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::edit_BM_name( int addr ) +{ + int ret; + debuggerBookmark_t *bm; + QInputDialog dialog(this); + char stmp[128]; + + bm = dbgBmMgr.getAddr( addr ); + + sprintf( stmp, "Specify Bookmark Name for %04X", addr ); + + dialog.setWindowTitle( tr("Edit Bookmark") ); + dialog.setLabelText( tr(stmp) ); + dialog.setOkButtonText( tr("Edit") ); + + if ( bm != NULL ) + { + dialog.setTextValue( tr(bm->name.c_str()) ); + } + + dialog.show(); + ret = dialog.exec(); + + if ( QDialog::Accepted == ret ) + { + bm->name = dialog.textValue().toStdString(); + bmListUpdate(false); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::bmListUpdate( bool reset ) +{ + int i=0; + QTreeWidgetItem *item; + debuggerBookmark_t *bm; + char addrStr[32]; + + if ( reset ) + { + bmTree->clear(); + } + + bm = dbgBmMgr.begin(); + + while ( bm != NULL ) + { + if ( bmTree->topLevelItemCount() > i ) + { + item = bmTree->topLevelItem(i); + } + else + { + item = NULL; + } + + if ( item == NULL ) + { + item = new QTreeWidgetItem(); + + bmTree->addTopLevelItem( item ); + } + + sprintf( addrStr, "%04X", bm->addr ); + + //item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable ); + item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren ); + + item->setFont( 0, font ); + item->setFont( 1, font ); + + item->setText( 0, tr(addrStr)); + item->setText( 1, tr(bm->name.c_str()) ); + + item->setTextAlignment( 0, Qt::AlignLeft); + item->setTextAlignment( 1, Qt::AlignLeft); + + bm = dbgBmMgr.next(); i++; + } + + bmTree->viewport()->update(); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::add_BP_CB(void) +{ + openBpEditWindow(-1); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::edit_BP_CB(void) +{ + QTreeWidgetItem *item; + + item = bpTree->currentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + + int row = bpTree->indexOfTopLevelItem(item); + + openBpEditWindow( row, &watchpoint[row] ); +} +//---------------------------------------------------------------------------- +static void DeleteBreak(int sel) +{ + if(sel<0) return; + if(sel>=numWPs) return; + + fceuWrapperLock(); + + if (watchpoint[sel].cond) + { + freeTree(watchpoint[sel].cond); + } + if (watchpoint[sel].condText) + { + free(watchpoint[sel].condText); + } + if (watchpoint[sel].desc) + { + free(watchpoint[sel].desc); + } + // move all BP items up in the list + for (int i = sel; i < numWPs; i++) + { + watchpoint[i].address = watchpoint[i+1].address; + watchpoint[i].endaddress = watchpoint[i+1].endaddress; + watchpoint[i].flags = watchpoint[i+1].flags; +// ################################## Start of SP CODE ########################### + watchpoint[i].cond = watchpoint[i+1].cond; + watchpoint[i].condText = watchpoint[i+1].condText; + watchpoint[i].desc = watchpoint[i+1].desc; +// ################################## End of SP CODE ########################### + } + // erase last BP item + watchpoint[numWPs].address = 0; + watchpoint[numWPs].endaddress = 0; + watchpoint[numWPs].flags = 0; + watchpoint[numWPs].cond = 0; + watchpoint[numWPs].condText = 0; + watchpoint[numWPs].desc = 0; + numWPs--; + + fceuWrapperUnLock(); +} +//---------------------------------------------------------------------------- +void debuggerClearAllBreakpoints(void) +{ + int i; + + fceuWrapperLock(); + + for (i=0; icurrentItem(); + + if ( item == NULL ) + { + printf( "No Item Selected\n"); + return; + } + + int row = bpTree->indexOfTopLevelItem(item); + + DeleteBreak( row ); + delete item; + //delete bpTree->takeTopLevelItem(row); + //bpListUpdate( true ); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::breakOnBadOpcodeCB(int value) +{ + //printf("Value:%i\n", value); + FCEUI_Debugger().badopbreak = (value != Qt::Unchecked); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::breakOnCyclesCB( int value ) +{ + std::string s; + + break_on_cycles = (value != Qt::Unchecked); + + s = cpuCycExdVal->text().toStdString(); + + //printf("'%s'\n", txt ); + + if ( s.size() > 0 ) + { + break_cycles_limit = strtoul( s.c_str(), NULL, 10 ); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::cpuCycleThresChanged(const QString &txt) +{ + std::string s; + + s = txt.toStdString(); + + //printf("Cycles: '%s'\n", s.c_str() ); + + if ( s.size() > 0 ) + { + break_cycles_limit = strtoul( s.c_str(), NULL, 10 ); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::breakOnInstructionsCB( int value ) +{ + std::string s; + + break_on_instructions = (value != Qt::Unchecked); + + s = instrExdVal->text().toStdString(); + + //printf("'%s'\n", txt ); + + if ( s.size() > 0 ) + { + break_instructions_limit = strtoul( s.c_str(), NULL, 10 ); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::instructionsThresChanged(const QString &txt) +{ + std::string s; + + s = txt.toStdString(); + + //printf("Instructions: '%s'\n", s.c_str() ); + + if ( s.size() > 0 ) + { + break_instructions_limit = strtoul( s.c_str(), NULL, 10 ); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::displayROMoffsetCB( int value ) +{ + asmView->setDisplayROMoffsets(value != Qt::Unchecked); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::symbolDebugEnableCB( int value ) +{ + asmView->setSymbolDebugEnable(value != Qt::Unchecked); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::registerNameEnableCB( int value ) +{ + asmView->setRegisterNameEnable(value != Qt::Unchecked); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::autoOpenDebugCB( int value ) +{ + g_config->setOption( "SDL.AutoOpenDebugger", value != Qt::Unchecked); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::debFileAutoLoadCB( int value ) +{ + int autoLoadDebug = value != Qt::Unchecked; + + g_config->setOption("SDL.AutoLoadDebugFiles", autoLoadDebug); + + if ( autoLoadDebug && (numWPs == 0) ) + { + loadGameDebugBreakpoints(); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::reloadSymbolsCB(void) +{ + debugSymbolTable.loadGameSymbols(); + + asmView->updateAssemblyView(); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::debugRunCB(void) +{ + if (FCEUI_EmulationPaused()) + { + setRegsFromEntry(); + FCEUI_ToggleEmulationPause(); + //DebuggerWasUpdated = false done in above function; + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::debugStepIntoCB(void) +{ + if (FCEUI_EmulationPaused()) + { + setRegsFromEntry(); + } + FCEUI_Debugger().step = true; + FCEUI_SetEmulationPaused(0); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::debugStepOutCB(void) +{ + if (FCEUI_EmulationPaused() > 0) + { + DebuggerState &dbgstate = FCEUI_Debugger(); + setRegsFromEntry(); + if (dbgstate.stepout) + { + printf("Step Out is currently in process.\n"); + return; + } + if (GetMem(X.PC) == 0x20) + { + dbgstate.jsrcount = 1; + } + else + { + dbgstate.jsrcount = 0; + } + dbgstate.stepout = 1; + FCEUI_SetEmulationPaused(0); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::debugStepOverCB(void) +{ + if (FCEUI_EmulationPaused()) + { + setRegsFromEntry(); + int tmp=X.PC; + uint8 opcode = GetMem(X.PC); + bool jsr = opcode==0x20; + bool call = jsr; + #ifdef BRK_3BYTE_HACK + //with this hack, treat BRK similar to JSR + if(opcode == 0x00) + { + call = true; + } + #endif + if (call) + { + if (watchpoint[64].flags) + { + printf("Step Over is currently in process.\n"); + return; + } + watchpoint[64].address = (tmp+3); + watchpoint[64].flags = WP_E|WP_X; + } + else + { + FCEUI_Debugger().step = true; + } + FCEUI_SetEmulationPaused(0); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::debugRunLineCB(void) +{ + if (FCEUI_EmulationPaused()) + { + setRegsFromEntry(); + } + uint64 ts=timestampbase; + ts+=timestamp; + ts+=341/3; + //if (scanline == 240) vblankScanLines++; + //else vblankScanLines = 0; + FCEUI_Debugger().runline = true; + FCEUI_Debugger().runline_end_time=ts; + FCEUI_SetEmulationPaused(0); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::debugRunLine128CB(void) +{ + if (FCEUI_EmulationPaused()) + { + setRegsFromEntry(); + } + FCEUI_Debugger().runline = true; + { + uint64 ts=timestampbase; + ts+=timestamp; + ts+=128*341/3; + FCEUI_Debugger().runline_end_time=ts; + //if (scanline+128 >= 240 && scanline+128 <= 257) vblankScanLines = (scanline+128)-240; + //else vblankScanLines = 0; + } + FCEUI_SetEmulationPaused(0); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::seekToCB (void) +{ + std::string s; + + s = seekEntry->text().toStdString(); + + //printf("Seek To: '%s'\n", s.c_str() ); + + if ( s.size() > 0 ) + { + long int addr, line; + + addr = strtol( s.c_str(), NULL, 16 ); + + line = asmView->getAsmLineFromAddr(addr); + + asmView->setLine( line ); + vbar->setValue( line ); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::seekPCCB (void) +{ + if (FCEUI_EmulationPaused()) + { + setRegsFromEntry(); + //updateAllDebugWindows(); + } + windowUpdateReq = true; + //asmView->scrollToPC(); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::resetCountersCB (void) +{ + ResetDebugStatisticsCounters(); + + updateRegisterView(); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::asmViewCtxMenuAddBP(void) +{ + watchpointinfo wp; + + wp.address = asmView->getCtxMenuAddr(); + wp.endaddress = 0; + wp.flags = WP_X | WP_E; + wp.condText = 0; + wp.desc = NULL; + + openBpEditWindow( -1, &wp ); + +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::asmViewCtxMenuAddBM(void) +{ + int addr = asmView->getCtxMenuAddr(); + + dbgBmMgr.addBookmark( addr ); + + edit_BM_name( addr ); + + bmListUpdate(false); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::asmViewCtxMenuOpenHexEdit(void) +{ + int romAddr = -1; + int addr = asmView->getCtxMenuAddr(); + + if (addr >= 0x8000) + { + romAddr = GetNesFileAddress(addr); + } + + if ( romAddr >= 0 ) + { + hexEditorOpenFromDebugger( QHexEdit::MODE_NES_ROM, romAddr ); + } + else + { + hexEditorOpenFromDebugger( QHexEdit::MODE_NES_RAM, addr ); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::setBookmarkSelectedAddress( int addr ) +{ + char stmp[32]; + + sprintf( stmp, "%04X", addr ); + + selBmAddr->setText( tr(stmp) ); + + selBmAddrVal = addr; +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::asmViewCtxMenuAddSym(void) +{ + openDebugSymbolEditWindow( asmView->getCtxMenuAddr() ); +} +//---------------------------------------------------------------------------- +int QAsmView::getAsmLineFromAddr(int addr) +{ + int line = -1; + int incr, nextLine; + int run = 1; + + if ( asmEntry.size() <= 0 ) + { + return -1; + } + incr = asmEntry.size() / 2; + + if ( addr < asmEntry[0]->addr ) + { + return 0; + } + else if ( addr > asmEntry[ asmEntry.size() - 1 ]->addr ) + { + return asmEntry.size() - 1; + } + + if ( incr < 1 ) incr = 1; + + nextLine = line = incr; + + // algorithm to efficiently find line from address. Starts in middle of list and + // keeps dividing the list in 2 until it arrives at an answer. + while ( run ) + { + //printf("incr:%i line:%i addr:%04X delta:%i\n", incr, line, asmEntry[line]->addr, addr - asmEntry[line]->addr); + + if ( incr == 1 ) + { + if ( asmEntry[line]->addr < addr ) + { + nextLine = line + 1; + if ( asmEntry[line]->addr > nextLine ) + { + break; + } + line = nextLine; + } + else if ( asmEntry[line]->addr > addr ) + { + nextLine = line - 1; + if ( asmEntry[line]->addr < nextLine ) + { + break; + } + line = nextLine; + } + else + { + nextLine = line - 1; + + if ( nextLine >= 0 ) + { + while ( asmEntry[nextLine]->addr == asmEntry[line]->addr ) + { + line = nextLine; + nextLine--; + if ( nextLine < 0 ) + { + break; + } + } + } + run = 0; break; + } + } + else + { + incr = incr / 2; + if ( incr < 1 ) incr = 1; + + if ( asmEntry[line]->addr < addr ) + { + nextLine = line + incr; + } + else if ( asmEntry[line]->addr > addr ) + { + nextLine = line - incr; + } + else + { + nextLine = line - 1; + + if ( nextLine >= 0 ) + { + while ( asmEntry[nextLine]->addr == asmEntry[line]->addr ) + { + line = nextLine; + nextLine--; + if ( nextLine < 0 ) + { + break; + } + } + } + run = 0; break; + } + line = nextLine; + } + } + + //for (size_t i=0; iaddr >= addr ) + // { + // line = i; break; + // } + //} + + return line; +} +//---------------------------------------------------------------------------- +// This function is for "smart" scrolling... +// it attempts to scroll up one line by a whole instruction +static int InstructionUp(int from) +{ + int i = std::min(16, from), j; + + while (i > 0) + { + j = i; + while (j > 0) + { + if (GetMem(from - j) == 0x00) + break; // BRK usually signifies data + if (opsize[GetMem(from - j)] == 0) + break; // invalid instruction! + if (opsize[GetMem(from - j)] > j) + break; // instruction is too long! + if (opsize[GetMem(from - j)] == j) + return (from - j); // instruction is just right! :D + j -= opsize[GetMem(from - j)]; + } + i--; + } + + // if we get here, no suitable instruction was found + if ((from >= 2) && (GetMem(from - 2) == 0x00)) + return (from - 2); // if a BRK instruction is possible, use that + if (from) + return (from - 1); // else, scroll up one byte + return 0; // of course, if we can't scroll up, just return 0! +} +//static int InstructionDown(int from) +//{ +// int tmp = opsize[GetMem(from)]; +// if ((tmp)) +// return from + tmp; +// else +// return from + 1; // this is data or undefined instruction +//} +//---------------------------------------------------------------------------- +void QAsmView::updateAssemblyView(void) +{ + int starting_address, start_address_lp, addr, size; + int instruction_addr; + std::string line; + char chr[64]; + uint8 opcode[3]; + const char *disassemblyText = NULL; + 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; + + //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++; + } + + disassemblyText = Disassemble(addr, opcode); + + if ( disassemblyText ) + { + line.append( disassemblyText ); + } + } + for (int j=0; jopcode[j] = opcode[j]; + } + a->size = size; + + // special case: an RTS opcode + if (GetMem(instruction_addr) == 0x60) + { + line.append("-------------------------"); + } + + if ( symbolicDebugEnable ) + { + debugSymbol_t *dbgSym; + + dbgSym = debugSymbolTable.getSymbolAtBankOffset( a->bank, a->addr ); + + if ( dbgSym != NULL ) + { + int i,j; + const char *c; + char stmp[256]; + printf("Debug symbol Found at $%04X \n", dbgSym->ofs ); + d = new dbg_asm_entry_t(); + + *d = *a; + d->type = dbg_asm_entry_t::SYMBOL_NAME; + d->text.assign( dbgSym->name ); + 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( pxLineWidth ); + + 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]; + std::string stackLine; + + 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) ); + + stackPtr++; + + if ( stackPtr <= 0x01FF ) + { + 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 ((i & 7) == 0) + sprintf( stmp, ",\r\n%02X", GetMem(stackPtr) ); + else + sprintf( stmp, ",%02X", GetMem(stackPtr) ); + + stackLine.append( stmp ); + } + } + + stackText->setPlainText( tr(stackLine.c_str()) ); + + // update counters + int64 counter_value = timestampbase + (uint64)timestamp - total_cycles_base; + if (counter_value < 0) // sanity check + { + ResetDebugStatisticsCounters(); + counter_value = 0; + } + sprintf( stmp, "CPU Cycles: %llu", counter_value); + + cpuCyclesLbl1->setText( tr(stmp) ); + + counter_value = timestampbase + (uint64)timestamp - delta_cycles_base; + if (counter_value < 0) // sanity check + { + ResetDebugStatisticsCounters(); + counter_value = 0; + } + sprintf(stmp, "(+%llu)", counter_value); + + cpuCyclesLbl2->setText( tr(stmp) ); + + sprintf(stmp, "Instructions: %llu", total_instructions); + cpuInstrsLbl1->setText( tr(stmp) ); + + sprintf(stmp, "(+%llu)", delta_instructions); + cpuInstrsLbl2->setText( tr(stmp) ); + + // PPU Labels + sprintf(stmp, "PPU: 0x%04X", (int)FCEUPPU_PeekAddress()); + ppuLbl->setText( tr(stmp) ); + + sprintf(stmp, "Sprite: 0x%02X", PPU[3] ); + spriteLbl->setText( tr(stmp) ); + + extern int linestartts; + #define GETLASTPIXEL (PAL?((timestamp*48-linestartts)/15) : ((timestamp*48-linestartts)/16) ) + + int ppupixel = GETLASTPIXEL; + + if (ppupixel>341) //maximum number of pixels per scanline + ppupixel = 0; //Currently pixel display is borked until Run 128 lines is clicked, this keeps garbage from displaying + + // If not in the 0-239 pixel range, make special cases for display + if (scanline == 240 && vblankScanLines < (PAL?72:22)) + { + if (!vblankScanLines) + { + // Idle scanline (240) + sprintf(str, "%d", scanline); // was "Idle %d" + } else if (scanline + vblankScanLines == (PAL?311:261)) + { + // Pre-render + sprintf(str, "-1"); // was "Prerender -1" + } else + { + // Vblank lines (241-260/310) + sprintf(str, "%d", scanline + vblankScanLines); // was "Vblank %d" + } + sprintf(str2, "%d", vblankPixel); + } else + { + // Scanlines 0 - 239 + sprintf(str, "%d", scanline); + sprintf(str2, "%d", ppupixel); + } + + if(newppu) + { + sprintf(str ,"%d",newppu_get_scanline()); + sprintf(str2,"%d",newppu_get_dot()); + } + + sprintf( stmp, "Scanline: %s", str ); + scanLineLbl->setText( tr(stmp) ); + + sprintf( stmp, "Pixel: %s", str2 ); + pixLbl->setText( tr(stmp) ); + +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::updateWindowData(void) +{ + asmView->updateAssemblyView(); + + asmView->scrollToPC(); + + updateRegisterView(); + + windowUpdateReq = false; +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::queueUpdate(void) +{ + windowUpdateReq = true; +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::updatePeriodic(void) +{ + //printf("Update Periodic\n"); + + if ( windowUpdateReq ) + { + fceuWrapperLock(); + updateWindowData(); + fceuWrapperUnLock(); + } + asmView->update(); + + if ( FCEUI_EmulationPaused() && !FCEUI_EmulationFrameStepped()) + { + emuStatLbl->setText( tr(" Emulator Stopped / Paused") ); + emuStatLbl->setStyleSheet("background-color: red; color: white;"); + } + else + { + emuStatLbl->setText( tr(" Emulator is Running") ); + emuStatLbl->setStyleSheet("background-color: green; color: white;"); + } + + if ( bpTree->topLevelItemCount() != numWPs ) + { + printf("Breakpoint Tree Update\n"); + bpListUpdate( true ); + } + + if ( bmTree->topLevelItemCount() != dbgBmMgr.size() ) + { + printf("Bookmark Tree Update\n"); + bmListUpdate( true ); + } +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::breakPointNotify( int bpNum ) +{ + if ( bpNum >= 0 ) + { + // highlight bp_num item list + + if ( bpTree->topLevelItemCount() > 0 ) + { + QTreeWidgetItem * item; + + item = bpTree->currentItem(); + + if ( item != NULL ) + { + item->setSelected(false); + } + + item = bpTree->topLevelItem( bpNum ); + + if ( item != NULL ) + { + item->setSelected(true); + } + bpTree->viewport()->update(); + } + } + else + { + if (bpNum == BREAK_TYPE_CYCLES_EXCEED) + { + // TODO + } + else if (bpNum == BREAK_TYPE_INSTRUCTIONS_EXCEED) + { + // TODO + } + } + + windowUpdateReq = true; +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::hbarChanged(int value) +{ + //printf("HBar Changed: %i\n", value); + asmView->setXScroll( value ); +} +//---------------------------------------------------------------------------- +void ConsoleDebugger::vbarChanged(int value) +{ + //printf("VBar Changed: %i\n", value); + asmView->setLine( value ); +} +//---------------------------------------------------------------------------- +void FCEUD_DebugBreakpoint( int bpNum ) +{ + std::list ::iterator it; + + if ( !nes_shm->runEmulator ) + { + return; + } + printf("Breakpoint Hit: %i \n", bpNum ); + + fceuWrapperUnLock(); + + for (it=dbgWinList.begin(); it!=dbgWinList.end(); it++) + { + (*it)->breakPointNotify( bpNum ); + } + + while ( nes_shm->runEmulator && FCEUI_EmulationPaused() && !FCEUI_EmulationFrameStepped()) + { + usleep(100000); + } + // since we unfreezed emulation, reset delta_cycles counter + ResetDebugStatisticsDeltaCounters(); + + fceuWrapperLock(); +} +//---------------------------------------------------------------------------- +bool debuggerWindowIsOpen(void) +{ + return (dbgWinList.size() > 0); +} +//---------------------------------------------------------------------------- +void updateAllDebuggerWindows( void ) +{ + std::list ::iterator it; + + for (it=dbgWinList.begin(); it!=dbgWinList.end(); it++) + { + (*it)->queueUpdate(); + } +} +//---------------------------------------------------------------------------- +static int getGameDebugBreakpointFileName(char *filepath) +{ + int i,j; + const char *romFile; + + romFile = getRomFile(); + + if ( romFile == NULL ) + { + return -1; + } + i=0; j = -1; + while ( romFile[i] != 0 ) + { + + if ( romFile[i] == '|' ) + { + filepath[i] = '.'; + } + else + { + if ( romFile[i] == '/' ) + { + j = -1; + } + else if ( romFile[i] == '.' ) + { + j = i; + } + filepath[i] = romFile[i]; + } + i++; + } + if ( j >= 0 ) + { + filepath[j] = 0; i=j; + } + + filepath[i] = '.'; i++; + filepath[i] = 'd'; i++; + filepath[i] = 'b'; i++; + filepath[i] = 'g'; i++; + filepath[i] = 0; + + return 0; +} +//---------------------------------------------------------------------------- +void saveGameDebugBreakpoints(void) +{ + int i; + FILE *fp; + char stmp[512]; + char flags[8]; + debuggerBookmark_t *bm; + + // If no breakpoints are loaded, skip saving + if ( numWPs == 0 ) + { + return; + } + getGameDebugBreakpointFileName( stmp ); + + //printf("Debug Save File: '%s' \n", stmp ); + + fp = fopen( stmp, "w"); + + if ( fp == NULL ) + { + printf("Error: Failed to open file '%s' for writing\n", stmp ); + return; + } + + for (i=0; iaddr, bm->name.c_str() ); + + bm = dbgBmMgr.next(); + } + + fclose(fp); + + return; +} +//---------------------------------------------------------------------------- +static int getKeyValuePair( int i, const char *stmp, char *id, char *data ) +{ + int j=0; + char literal=0; + + id[0] = 0; data[0] = 0; + + if ( stmp[i] == 0 ) return i; + + while ( isspace(stmp[i]) ) i++; + + j=0; + while ( isalnum(stmp[i]) ) + { + id[j] = stmp[i]; j++; i++; + } + id[j] = 0; + + if ( j == 0 ) + { + return i; + } + if ( stmp[i] != '=' ) + { + return i; + } + i++; j=0; + if ( stmp[i] == '\"' ) + { + literal = 0; + i++; + while ( stmp[i] != 0 ) + { + if ( literal ) + { + data[j] = stmp[i]; i++; j++; + literal = 0; + } + else + { + if ( stmp[i] == '\\' ) + { + literal = 1; i++; + } + else if ( stmp[i] == '\"' ) + { + i++; break; + } + else + { + data[j] = stmp[i]; j++; i++; + } + } + } + data[j] = 0; + } + else + { + j=0; + while ( !isspace(stmp[i]) ) + { + data[j] = stmp[i]; j++; i++; + } + data[j] = 0; + } + return i; +} +//---------------------------------------------------------------------------- +void loadGameDebugBreakpoints(void) +{ + int i,j; + FILE *fp; + char stmp[512]; + char id[64], data[128]; + + // If no debug windows are open, skip loading breakpoints + if ( dbgWinList.size() == 0 ) + { + printf("No Debug Windows Open: Skipping loading of breakpoint data\n"); + return; + } + getGameDebugBreakpointFileName( stmp ); + + //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"); + + font.setFamily("Courier New"); + font.setStyle( QFont::StyleNormal ); + font.setStyleHint( QFont::Monospace ); + + pal = this->palette(); + 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; + + //setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Expanding ); +} +//---------------------------------------------------------------------------- +QAsmView::~QAsmView(void) +{ + asmClear(); +} +//---------------------------------------------------------------------------- +void QAsmView::asmClear(void) +{ + for (size_t i=0; i= pxLineWidth ) + { + pxLineXScroll = 0; + } + else + { + pxLineXScroll = (int)(0.010f * (float)value * (float)(pxLineWidth - viewWidth) ); + } +} +//---------------------------------------------------------------------------- +void QAsmView::scrollToPC(void) +{ + if ( asmPC != NULL ) + { + lineOffset = asmPC->line; + vbar->setValue( lineOffset ); + } +} +//---------------------------------------------------------------------------- +void QAsmView::setDisplayROMoffsets( bool value ) +{ + if ( value != displayROMoffsets ) + { + displayROMoffsets = value; + + updateAssemblyView(); + } +} +//---------------------------------------------------------------------------- +void QAsmView::setSymbolDebugEnable( bool value ) +{ + if ( value != symbolicDebugEnable ) + { + symbolicDebugEnable = value; + + updateAssemblyView(); + } +} +//---------------------------------------------------------------------------- +void QAsmView::setRegisterNameEnable( bool value ) +{ + if ( value != registerNameEnable ) + { + registerNameEnable = value; + + updateAssemblyView(); + } +} +//---------------------------------------------------------------------------- +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() ); + +} +//---------------------------------------------------------------------------- +void QAsmView::keyReleaseEvent(QKeyEvent *event) +{ + printf("Debug ASM Window Key Release: 0x%x \n", event->key() ); + //assignHotkey( event ); +} +//---------------------------------------------------------------------------- +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; +} +//---------------------------------------------------------------------------- +void QAsmView::mouseMoveEvent(QMouseEvent * event) +{ + int line; + QPoint c = convPixToCursor( event->pos() ); + char txt[256]; + std::string s; + + line = lineOffset + c.y(); + + //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, ", Offset 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::mousePressEvent(QMouseEvent * event) +{ + int line; + QPoint c = convPixToCursor( event->pos() ); + + line = lineOffset + c.y(); + + if ( line < asmEntry.size() ) + { + int addr; + + addr = asmEntry[line]->addr; + + parent->setBookmarkSelectedAddress( addr ); + } +} +//---------------------------------------------------------------------------- +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; + + ctxMenuAddr = addr = asmEntry[line]->addr; + + act = new QAction(tr("Add Breakpoint"), this); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuAddBP(void)) ); + + act = new QAction(tr("Add Symbolic Debug Marker"), this); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuAddSym(void)) ); + + act = new QAction(tr("Add Bookmark"), this); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuAddBM(void)) ); + + act = new QAction(tr("Open Hex Editor"), this); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), parent, SLOT(asmViewCtxMenuOpenHexEdit(void)) ); + + menu.exec(event->globalPos()); + } +} +//---------------------------------------------------------------------------- +void QAsmView::paintEvent(QPaintEvent *event) +{ + int x,y,l, row, nrow, selAddr; + QPainter painter(this); + + 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; + + for (row=0; row < nrow; row++) + { + x = -pxLineXScroll; + l = lineOffset + row; + painter.setPen( this->palette().color(QPalette::WindowText)); + + if ( asmPC != NULL ) + { + if ( l == asmPC->line ) + { + painter.fillRect( 0, y - pxLineSpacing + pxLineLead, viewWidth, pxLineSpacing, QColor("pink") ); + } + } + + 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") ); + } + painter.drawText( x, y, tr(asmEntry[l]->text.c_str()) ); + + if ( selAddr == asmEntry[l]->addr ) + { // Highlight ASM line for selected address. + if ( !displayROMoffsets && (asmEntry[l]->type == dbg_asm_entry_t::ASM_TEXT) ) + { + int ax; + char addrString[16]; + + ax = 4*pxCharWidth; + + painter.fillRect( ax, y - pxLineSpacing + pxLineLead, 4*pxCharWidth, pxLineSpacing, QColor("blue") ); + + sprintf( addrString, "%04X", selAddr ); + + painter.setPen( this->palette().color(QPalette::Background)); + + painter.drawText( ax, y, tr(addrString) ); + + painter.setPen( this->palette().color(QPalette::WindowText)); + } + } + } + y += pxLineSpacing; + } +} +//---------------------------------------------------------------------------- +// Bookmark Manager Methods +//---------------------------------------------------------------------------- +debuggerBookmarkManager_t::debuggerBookmarkManager_t(void) +{ + internal_iter = bmMap.begin(); + +} +//---------------------------------------------------------------------------- +debuggerBookmarkManager_t::~debuggerBookmarkManager_t(void) +{ + this->clear(); +} +//---------------------------------------------------------------------------- +void debuggerBookmarkManager_t::clear(void) +{ + std::map ::iterator it; + + for (it=bmMap.begin(); it!=bmMap.end(); it++) + { + delete it->second; + } + bmMap.clear(); + + internal_iter = bmMap.begin(); +} +//---------------------------------------------------------------------------- +int debuggerBookmarkManager_t::addBookmark( int addr, const char *name ) +{ + int retval = -1; + debuggerBookmark_t *bm = NULL; + std::map ::iterator it; + + it = bmMap.find( addr ); + + if ( it == bmMap.end() ) + { + bm = new debuggerBookmark_t(); + bm->addr = addr; + + if ( name != NULL ) + { + bm->name.assign( name ); + } + bmMap[ addr ] = bm; + + retval = 0; + } + + return retval; +} +//---------------------------------------------------------------------------- +int debuggerBookmarkManager_t::editBookmark( int addr, const char *name ) +{ + int retval = -1; + debuggerBookmark_t *bm = NULL; + std::map ::iterator it; + + it = bmMap.find( addr ); + + if ( it != bmMap.end() ) + { + bm = it->second; + + if ( name != NULL ) + { + bm->name.assign( name ); + } + retval = 0; + } + + return retval; +} +//---------------------------------------------------------------------------- +int debuggerBookmarkManager_t::deleteBookmark( int addr ) +{ + int retval = -1; + std::map ::iterator it; + + it = bmMap.find( addr ); + + if ( it != bmMap.end() ) + { + bmMap.erase(it); + + retval = 0; + } + return retval; +} +//---------------------------------------------------------------------------- +int debuggerBookmarkManager_t::size(void) +{ + return bmMap.size(); +} +//---------------------------------------------------------------------------- +debuggerBookmark_t *debuggerBookmarkManager_t::begin(void) +{ + internal_iter = bmMap.begin(); + + if ( internal_iter == bmMap.end() ) + { + return NULL; + } + return internal_iter->second; +} +//---------------------------------------------------------------------------- +debuggerBookmark_t *debuggerBookmarkManager_t::next(void) +{ + if ( internal_iter == bmMap.end() ) + { + return NULL; + } + internal_iter++; + + if ( internal_iter == bmMap.end() ) + { + return NULL; + } + return internal_iter->second; +} +//---------------------------------------------------------------------------- +debuggerBookmark_t *debuggerBookmarkManager_t::getAddr( int addr ) +{ + std::map ::iterator it; + + it = bmMap.find( addr ); + + if ( it != bmMap.end() ) + { + return it->second; + } + return NULL; +} +//---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/ConsoleDebugger.h b/src/drivers/Qt/ConsoleDebugger.h new file mode 100644 index 00000000..51b6d40c --- /dev/null +++ b/src/drivers/Qt/ConsoleDebugger.h @@ -0,0 +1,285 @@ +// ConsoleDebugger.h +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Qt/main.h" +#include "Qt/SymbolicDebug.h" +#include "../../debug.h" + +struct dbg_asm_entry_t +{ + int addr; + int bank; + int rom; + int size; + int line; + uint8 opcode[3]; + std::string text; + + enum + { + ASM_TEXT = 0, + SYMBOL_NAME, + SYMBOL_COMMENT + } type; + + dbg_asm_entry_t(void) + { + addr = 0; bank = -1; rom = -1; + size = 0; line = 0; type = ASM_TEXT; + + for (int i=0; i<3; i++) + { + opcode[i] = 0; + } + } +}; + +class debuggerBookmark_t +{ + public: + int addr; + std::string name; + + debuggerBookmark_t(void) + { + addr = 0; + } +}; + +class debuggerBookmarkManager_t +{ + public: + debuggerBookmarkManager_t(void); + ~debuggerBookmarkManager_t(void); + + int addBookmark( int addr, const char *name = NULL ); + int editBookmark( int addr, const char *name ); + int deleteBookmark( int addr ); + + int size(void); + void clear(void); + debuggerBookmark_t *begin(void); + debuggerBookmark_t *next(void); + debuggerBookmark_t *getAddr( int addr ); + private: + std::map bmMap; + std::map ::iterator internal_iter; +}; + +class ConsoleDebugger; + +class QAsmView : public QWidget +{ + Q_OBJECT + + public: + QAsmView(QWidget *parent = 0); + ~QAsmView(void); + + void setScrollBars( QScrollBar *h, QScrollBar *v ); + void updateAssemblyView(void); + void asmClear(void); + int getAsmLineFromAddr(int addr); + void setLine(int lineNum); + void setXScroll(int value); + void scrollToPC(void); + void setDisplayROMoffsets( bool value ); + void setSymbolDebugEnable( bool value ); + void setRegisterNameEnable( bool value ); + int getCtxMenuAddr(void){ return ctxMenuAddr; }; + protected: + void paintEvent(QPaintEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + void mousePressEvent(QMouseEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void resizeEvent(QResizeEvent *event); + void contextMenuEvent(QContextMenuEvent *event); + + void calcFontData(void); + QPoint convPixToCursor( QPoint p ); + + private: + ConsoleDebugger *parent; + QFont font; + QScrollBar *vbar; + QScrollBar *hbar; + + int ctxMenuAddr; + int maxLineLen; + int pxCharWidth; + int pxCharHeight; + int pxCursorHeight; + int pxLineSpacing; + int pxLineLead; + int viewLines; + int viewWidth; + int viewHeight; + int lineOffset; + int maxLineOffset; + int pxLineWidth; + int pxLineXScroll; + int cursorPosX; + int cursorPosY; + + dbg_asm_entry_t *asmPC; + std::vector asmEntry; + + bool displayROMoffsets; + bool symbolicDebugEnable; + bool registerNameEnable; +}; + +class ConsoleDebugger : public QDialog +{ + Q_OBJECT + + public: + ConsoleDebugger(QWidget *parent = 0); + ~ConsoleDebugger(void); + + void updateWindowData(void); + void updateRegisterView(void); + void breakPointNotify(int bpNum); + void openBpEditWindow(int editIdx = -1, watchpointinfo *wp = NULL ); + void openDebugSymbolEditWindow( int addr ); + void setBookmarkSelectedAddress( int addr ); + int getBookmarkSelectedAddress(void){ return selBmAddrVal; }; + void edit_BM_name( int addr ); + void queueUpdate(void); + + QLabel *asmLineSelLbl; + protected: + void closeEvent(QCloseEvent *event); + //void keyPressEvent(QKeyEvent *event); + //void keyReleaseEvent(QKeyEvent *event); + + //QTreeWidget *tree; + QScrollBar *vbar; + QScrollBar *hbar; + QAsmView *asmView; + QPlainTextEdit *stackText; + QLineEdit *seekEntry; + QLineEdit *pcEntry; + QLineEdit *regAEntry; + QLineEdit *regXEntry; + QLineEdit *regYEntry; + QLineEdit *cpuCycExdVal; + QLineEdit *instrExdVal; + QLineEdit *selBmAddr; + QGroupBox *stackFrame; + QGroupBox *bpFrame; + QGroupBox *sfFrame; + QGroupBox *bmFrame; + QTreeWidget *bpTree; + QTreeWidget *bmTree; + QCheckBox *brkBadOpsCbox; + QCheckBox *N_chkbox; + QCheckBox *V_chkbox; + QCheckBox *U_chkbox; + QCheckBox *B_chkbox; + QCheckBox *D_chkbox; + QCheckBox *I_chkbox; + QCheckBox *Z_chkbox; + QCheckBox *C_chkbox; + QCheckBox *brkCpuCycExd; + QCheckBox *brkInstrsExd; + QCheckBox *romOfsChkBox; + QCheckBox *symDbgChkBox; + QCheckBox *regNamChkBox; + QCheckBox *autoOpenChkBox; + QCheckBox *debFileChkBox; + QCheckBox *idaFontChkBox; + QLabel *emuStatLbl; + QLabel *ppuLbl; + QLabel *spriteLbl; + QLabel *scanLineLbl; + QLabel *pixLbl; + QLabel *cpuCyclesLbl1; + QLabel *cpuCyclesLbl2; + QLabel *cpuInstrsLbl1; + QLabel *cpuInstrsLbl2; + QTimer *periodicTimer; + QFont font; + + int selBmAddrVal; + bool windowUpdateReq; + + private: + void setRegsFromEntry(void); + void bpListUpdate( bool reset = false ); + void bmListUpdate( bool reset = false ); + + public slots: + void closeWindow(void); + void asmViewCtxMenuAddBP(void); + void asmViewCtxMenuAddBM(void); + void asmViewCtxMenuAddSym(void); + void asmViewCtxMenuOpenHexEdit(void); + private slots: + void updatePeriodic(void); + void hbarChanged(int value); + void vbarChanged(int value); + void debugRunCB(void); + void debugStepIntoCB(void); + void debugStepOutCB(void); + void debugStepOverCB(void); + void debugRunLineCB(void); + void debugRunLine128CB(void); + void seekToCB(void); + void seekPCCB(void); + void add_BP_CB(void); + void edit_BP_CB(void); + void delete_BP_CB(void); + void add_BM_CB(void); + void edit_BM_CB(void); + void delete_BM_CB(void); + void resetCountersCB (void); + void reloadSymbolsCB(void); + void displayROMoffsetCB(int value); + void symbolDebugEnableCB(int value); + void registerNameEnableCB(int value); + void autoOpenDebugCB( int value ); + void debFileAutoLoadCB( int value ); + void breakOnBadOpcodeCB(int value); + void breakOnCyclesCB( int value ); + void breakOnInstructionsCB( int value ); + void bpItemClicked( QTreeWidgetItem *item, int column); + void bmItemClicked( QTreeWidgetItem *item, int column); + void bmItemDoubleClicked( QTreeWidgetItem *item, int column); + void cpuCycleThresChanged(const QString &txt); + void instructionsThresChanged(const QString &txt); + void selBmAddrChanged(const QString &txt); + +}; + +bool debuggerWindowIsOpen(void); +void saveGameDebugBreakpoints(void); +void loadGameDebugBreakpoints(void); +void debuggerClearAllBreakpoints(void); +void updateAllDebuggerWindows(void); + +extern debuggerBookmarkManager_t dbgBmMgr; diff --git a/src/drivers/Qt/ConsoleUtilities.cpp b/src/drivers/Qt/ConsoleUtilities.cpp index d118dd7d..9bedc75f 100644 --- a/src/drivers/Qt/ConsoleUtilities.cpp +++ b/src/drivers/Qt/ConsoleUtilities.cpp @@ -83,3 +83,65 @@ int getFileBaseName( const char *filepath, char *base ) return end; } //--------------------------------------------------------------------------- +int parseFilepath( const char *filepath, char *dir, char *base, char *suffix ) +{ + int i=0,j=0,end=0; + if ( filepath == NULL ) + { + if ( dir ) dir[0] = 0; + if ( base ) base[0] = 0; + if ( suffix) suffix[0] = 0; + return 0; + } + i=0; j=0; + while ( filepath[i] != 0 ) + { + if ( (filepath[i] == '/') || (filepath[i] == '\\') ) + { + j = i+1; + } + if ( dir ) + { + dir[i] = filepath[i]; + } + i++; + } + if ( dir ) + { + dir[j] = 0; + } + i = j; + + if ( base == NULL ) + { + return end; + } + + j=0; + while ( filepath[i] != 0 ) + { + base[j] = filepath[i]; i++; j++; + } + base[j] = 0; end=j; + + if ( suffix ) + { + suffix[0] = 0; + } + + while ( j > 1 ) + { + j--; + if ( base[j] == '.' ) + { + if ( suffix ) + { + strcpy( suffix, &base[j] ); + } + end=j; base[j] = 0; + break; + } + } + return end; +} +//--------------------------------------------------------------------------- diff --git a/src/drivers/Qt/ConsoleUtilities.h b/src/drivers/Qt/ConsoleUtilities.h index e55e0361..12838ddc 100644 --- a/src/drivers/Qt/ConsoleUtilities.h +++ b/src/drivers/Qt/ConsoleUtilities.h @@ -5,3 +5,5 @@ int getDirFromFile( const char *path, char *dir ); const char *getRomFile( void ); int getFileBaseName( const char *filepath, char *base ); + +int parseFilepath( const char *filepath, char *dir, char *base, char *suffix = NULL ); diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 57102e8e..d4f7e723 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -25,6 +25,9 @@ #include "Qt/LuaControl.h" #include "Qt/CheatsConf.h" #include "Qt/HexEditor.h" +#include "Qt/TraceLogger.h" +#include "Qt/CodeDataLogger.h" +#include "Qt/ConsoleDebugger.h" #include "Qt/ConsoleUtilities.h" #include "Qt/ConsoleSoundConf.h" #include "Qt/ConsoleVideoConf.h" @@ -85,14 +88,14 @@ consoleWin_t::~consoleWin_t(void) closeGamePadConfWindow(); + //printf("Thread Finished: %i \n", gameThread->isFinished() ); + emulatorThread->quit(); + emulatorThread->wait( 1000 ); + fceuWrapperLock(); fceuWrapperClose(); fceuWrapperUnLock(); - //printf("Thread Finished: %i \n", gameThread->isFinished() ); - emulatorThread->quit(); - emulatorThread->wait(); - if ( viewport_GL != NULL ) { delete viewport_GL; viewport_GL = NULL; @@ -482,6 +485,14 @@ void consoleWin_t::createMainMenu(void) // Debug debugMenu = menuBar()->addMenu(tr("Debug")); + // Debug -> Debugger + debuggerAct = new QAction(tr("Debugger..."), this); + //debuggerAct->setShortcut( QKeySequence(tr("Shift+F7"))); + debuggerAct->setStatusTip(tr("Open 6502 Debugger")); + connect(debuggerAct, SIGNAL(triggered()), this, SLOT(openDebugWindow(void)) ); + + debugMenu->addAction(debuggerAct); + // Debug -> Hex Editor hexEditAct = new QAction(tr("Hex Editor..."), this); //hexEditAct->setShortcut( QKeySequence(tr("Shift+F7"))); @@ -490,6 +501,22 @@ void consoleWin_t::createMainMenu(void) debugMenu->addAction(hexEditAct); + // Debug -> Trace Logger + traceLogAct = new QAction(tr("Trace Logger..."), this); + //traceLogAct->setShortcut( QKeySequence(tr("Shift+F7"))); + traceLogAct->setStatusTip(tr("Open Trace Logger")); + connect(traceLogAct, SIGNAL(triggered()), this, SLOT(openTraceLogger(void)) ); + + debugMenu->addAction(traceLogAct); + + // Debug -> Code/Data Logger + codeDataLogAct = new QAction(tr("Code/Data Logger..."), this); + //codeDataLogAct->setShortcut( QKeySequence(tr("Shift+F7"))); + codeDataLogAct->setStatusTip(tr("Open Code Data Logger")); + connect(codeDataLogAct, SIGNAL(triggered()), this, SLOT(openCodeDataLogger(void)) ); + + debugMenu->addAction(codeDataLogAct); + //----------------------------------------------------------------------- // Movie movieMenu = menuBar()->addMenu(tr("Movie")); @@ -551,6 +578,9 @@ void consoleWin_t::closeApp(void) { nes_shm->runEmulator = 0; + emulatorThread->quit(); + emulatorThread->wait( 1000 ); + fceuWrapperLock(); fceuWrapperClose(); fceuWrapperUnLock(); @@ -559,7 +589,6 @@ void consoleWin_t::closeApp(void) // clear the NetworkIP field so this doesn't happen unintentionally g_config->setOption ("SDL.NetworkIP", ""); g_config->save (); - //SDL_Quit (); // Already called by fceuWrapperClose //qApp::quit(); qApp->quit(); @@ -970,6 +999,17 @@ void consoleWin_t::openCheats(void) cheatWin->show(); } +void consoleWin_t::openDebugWindow(void) +{ + ConsoleDebugger *debugWin; + + //printf("Open GUI 6502 Debugger Window\n"); + + debugWin = new ConsoleDebugger(this); + + debugWin->show(); +} + void consoleWin_t::openHexEditor(void) { HexEditorDialog_t *hexEditWin; @@ -981,6 +1021,22 @@ void consoleWin_t::openHexEditor(void) hexEditWin->show(); } +void consoleWin_t::openCodeDataLogger(void) +{ + CodeDataLoggerDialog_t *cdlWin; + + //printf("Open Code Data Logger Window\n"); + + cdlWin = new CodeDataLoggerDialog_t(this); + + cdlWin->show(); +} + +void consoleWin_t::openTraceLogger(void) +{ + openTraceLoggerWindow(this); +} + void consoleWin_t::toggleAutoResume(void) { //printf("Auto Resume: %i\n", autoResume->isChecked() ); diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index eb5fdddb..a8d74264 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -89,6 +89,9 @@ class consoleWin_t : public QMainWindow QAction *fdsEjectAct; QAction *fdsLoadBiosAct; QAction *cheatsAct; + QAction *debuggerAct; + QAction *codeDataLogAct; + QAction *traceLogAct; QAction *hexEditAct; QAction *openMovAct; QAction *stopMovAct; @@ -112,6 +115,9 @@ class consoleWin_t : public QMainWindow private: void createMainMenu(void); + public slots: + void openDebugWindow(void); + void openHexEditor(void); private slots: void closeApp(void); void openROMFile(void); @@ -129,6 +135,8 @@ class consoleWin_t : public QMainWindow void openHotkeyConfWin(void); void openPaletteConfWin(void); void openGuiConfWin(void); + void openCodeDataLogger(void); + void openTraceLogger(void); void toggleAutoResume(void); void toggleFullscreen(void); void updatePeriodic(void); @@ -155,7 +163,6 @@ class consoleWin_t : public QMainWindow void fdsEjectDisk(void); void fdsLoadBiosFile(void); void openCheats(void); - void openHexEditor(void); void openMovie(void); void stopMovie(void); void recordMovie(void); diff --git a/src/drivers/Qt/HexEditor.cpp b/src/drivers/Qt/HexEditor.cpp index f822b40c..e5dd46a1 100644 --- a/src/drivers/Qt/HexEditor.cpp +++ b/src/drivers/Qt/HexEditor.cpp @@ -24,6 +24,7 @@ #include "../../movie.h" #include "../../palette.h" #include "../../fds.h" +#include "../../ppu.h" #include "../../cart.h" #include "../../ines.h" #include "../common/configSys.h" @@ -35,7 +36,10 @@ #include "Qt/keyscan.h" #include "Qt/fceuWrapper.h" #include "Qt/HexEditor.h" +#include "Qt/SymbolicDebug.h" +#include "Qt/ConsoleDebugger.h" #include "Qt/ConsoleUtilities.h" +#include "Qt/ConsoleWindow.h" static HexBookMarkManager_t hbm; static std::list winList; @@ -537,12 +541,11 @@ HexEditorDialog_t::HexEditorDialog_t(QWidget *parent) QMenuBar *menuBar; QMenu *fileMenu, *viewMenu, *colorMenu; QAction *saveROM, *closeAct; - QAction *viewRAM, *viewPPU, *viewOAM, *viewROM; QAction *actHlgt, *actHlgtRV, *actColorFG, *actColorBG; QActionGroup *group; int useNativeMenuBar; - setWindowTitle("Hex Editor"); + QDialog::setWindowTitle( tr("Hex Editor") ); resize( 512, 512 ); @@ -737,6 +740,19 @@ HexEditorDialog_t::~HexEditorDialog_t(void) } } //---------------------------------------------------------------------------- +void HexEditorDialog_t::setWindowTitle(void) +{ + const char *modeString; + char stmp[128]; + + modeString = memViewNames[ editor->getMode() ]; + + sprintf( stmp, "Hex Editor - %s: 0x%04X", modeString, editor->getAddr() ); + + QDialog::setWindowTitle( tr(stmp) ); + +} +//---------------------------------------------------------------------------- void HexEditorDialog_t::removeAllBookmarks(void) { int ret; @@ -947,6 +963,134 @@ void HexEditorDialog_t::actvHighlightRVCB(bool enable) editor->setHighlightReverseVideo( enable ); } //---------------------------------------------------------------------------- +void HexEditorDialog_t::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; + QFont font; + font.setFamily("Courier New"); + font.setStyle( QFont::StyleNormal ); + font.setStyleHint( QFont::Monospace ); + + 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)) ); + + 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 ) + { + 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(); + } + //fceuWrapperLock(); + updateAllDebuggerWindows(); + //fceuWrapperUnLock(); + } +} +//---------------------------------------------------------------------------- void HexEditorDialog_t::updatePeriodic(void) { //printf("Update Periodic\n"); @@ -956,6 +1100,36 @@ void HexEditorDialog_t::updatePeriodic(void) editor->memModeUpdate(); editor->update(); + + setWindowTitle(); + + switch ( editor->getMode() ) + { + case QHexEdit::MODE_NES_RAM: + if ( !viewRAM->isChecked() ) + { + viewRAM->setChecked(true); + } + break; + case QHexEdit::MODE_NES_PPU: + if ( !viewPPU->isChecked() ) + { + viewPPU->setChecked(true); + } + break; + case QHexEdit::MODE_NES_OAM: + if ( !viewOAM->isChecked() ) + { + viewOAM->setChecked(true); + } + break; + case QHexEdit::MODE_NES_ROM: + if ( !viewROM->isChecked() ) + { + viewROM->setChecked(true); + } + break; + } } //---------------------------------------------------------------------------- QHexEdit::QHexEdit(QWidget *parent) @@ -992,6 +1166,7 @@ QHexEdit::QHexEdit(QWidget *parent) lineOffset = 0; cursorPosX = 0; cursorPosY = 0; + cursorAddr = 0; cursorBlink = true; cursorBlinkCount = 0; maxLineOffset = 0; @@ -1531,20 +1706,24 @@ void QHexEdit::contextMenuEvent(QContextMenuEvent *event) { case MODE_NES_RAM: { - act = new QAction(tr("TODO Add Symbolic Debug Name"), this); + act = new QAction(tr("Add Symbolic Debug Name"), this); menu.addAction(act); + connect( act, SIGNAL(triggered(void)), this, SLOT(addDebugSym(void)) ); - sprintf( stmp, "TODO Add Read Breakpoint for Address $%04X", addr ); + sprintf( stmp, "Add Read Breakpoint for Address $%04X", addr ); act = new QAction(tr(stmp), this); - menu.addAction(act); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), this, SLOT(addRamReadBP(void)) ); - sprintf( stmp, "TODO Add Write Breakpoint for Address $%04X", addr ); + sprintf( stmp, "Add Write Breakpoint for Address $%04X", addr ); act = new QAction(tr(stmp), this); - menu.addAction(act); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), this, SLOT(addRamWriteBP(void)) ); - sprintf( stmp, "TODO Add Execute Breakpoint for Address $%04X", addr ); + sprintf( stmp, "Add Execute Breakpoint for Address $%04X", addr ); act = new QAction(tr(stmp), this); - menu.addAction(act); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), this, SLOT(addRamExecuteBP(void)) ); if ( addr > 0x6000 ) { @@ -1567,6 +1746,16 @@ void QHexEdit::contextMenuEvent(QContextMenuEvent *event) break; case MODE_NES_PPU: { + sprintf( stmp, "Add Read Breakpoint for Address $%04X", addr ); + act = new QAction(tr(stmp), this); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), this, SLOT(addPpuReadBP(void)) ); + + sprintf( stmp, "Add Write Breakpoint for Address $%04X", addr ); + act = new QAction(tr(stmp), this); + menu.addAction(act); + connect( act, SIGNAL(triggered(void)), this, SLOT(addPpuWriteBP(void)) ); + act = new QAction(tr("Add Bookmark"), this); menu.addAction(act); connect( act, SIGNAL(triggered(void)), this, SLOT(addBookMarkCB(void)) ); @@ -1630,6 +1819,136 @@ void QHexEdit::addBookMarkCB(void) } } //---------------------------------------------------------------------------- +void QHexEdit::addDebugSym(void) +{ + parent->openDebugSymbolEditWindow( ctxAddr ); +} +//---------------------------------------------------------------------------- +void QHexEdit::addRamReadBP(void) +{ + int retval, type; + char cond[64], name[64]; + + type = BT_C | WP_R; + + cond[0] = 0; + name[0] = 0; + + if ( ctxAddr >= 0x8000 ) + { + sprintf(cond, "K==#%02X", getBank(ctxAddr)); + } + + retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true); + + if ( (retval == 1) || (retval == 2) ) + { + printf("Breakpoint Add Failed\n"); + } + else + { + numWPs++; + } +} +//---------------------------------------------------------------------------- +void QHexEdit::addRamWriteBP(void) +{ + int retval, type; + char cond[64], name[64]; + + type = BT_C | WP_W; + + cond[0] = 0; + name[0] = 0; + + if ( ctxAddr >= 0x8000 ) + { + sprintf(cond, "K==#%02X", getBank(ctxAddr)); + } + + retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true); + + if ( (retval == 1) || (retval == 2) ) + { + printf("Breakpoint Add Failed\n"); + } + else + { + numWPs++; + } +} +//---------------------------------------------------------------------------- +void QHexEdit::addRamExecuteBP(void) +{ + int retval, type; + char cond[64], name[64]; + + type = BT_C | WP_X; + + cond[0] = 0; + name[0] = 0; + + if ( ctxAddr >= 0x8000 ) + { + sprintf(cond, "K==#%02X", getBank(ctxAddr)); + } + + retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true); + + if ( (retval == 1) || (retval == 2) ) + { + printf("Breakpoint Add Failed\n"); + } + else + { + numWPs++; + } +} +//---------------------------------------------------------------------------- +void QHexEdit::addPpuReadBP(void) +{ + int retval, type; + char cond[64], name[64]; + + type = BT_P | WP_R; + + cond[0] = 0; + name[0] = 0; + + retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true); + + if ( (retval == 1) || (retval == 2) ) + { + printf("Breakpoint Add Failed\n"); + } + else + { + numWPs++; + } +} +//---------------------------------------------------------------------------- +void QHexEdit::addPpuWriteBP(void) +{ + int retval, type; + char cond[64], name[64]; + + type = BT_P | WP_W; + + cond[0] = 0; + name[0] = 0; + + retval = NewBreak( name, ctxAddr, -1, type, cond, numWPs, true); + + if ( (retval == 1) || (retval == 2) ) + { + printf("Breakpoint Add Failed\n"); + } + else + { + numWPs++; + } +} +//---------------------------------------------------------------------------- void QHexEdit::jumpToROM(void) { setMode( MODE_NES_ROM ); @@ -1681,6 +2000,106 @@ int QHexEdit::checkMemActivity(void) return 0; } //---------------------------------------------------------------------------- +int QHexEdit::getRomAddrColor( int addr, QColor &fg, QColor &bg ) +{ + int temp_offset; + QColor color, oppColor; + + fg = this->palette().color(QPalette::WindowText); + bg = this->palette().color(QPalette::Background); + + if ( reverseVideo ) + { + color = this->palette().color(QPalette::Background); + oppColor = this->palette().color(QPalette::WindowText); + } + else + { + color = this->palette().color(QPalette::WindowText); + oppColor = this->palette().color(QPalette::Background); + } + + if ( viewMode != MODE_NES_ROM ) + { + return -1; + } + if (cdloggerdataSize == 0) + { + return -1; + } + temp_offset = addr - 16; + + if (temp_offset >= 0) + { + if ((unsigned int)temp_offset < cdloggerdataSize) + { + // PRG + if ((cdloggerdata[temp_offset] & 3) == 3) + { + // the byte is both Code and Data - green + color.setRgb(0, 190, 0); + } + else if ((cdloggerdata[temp_offset] & 3) == 1) + { + // the byte is Code - dark-yellow + color.setRgb(160, 140, 0); + oppColor.setRgb( 0, 0, 0 ); + } + else if ((cdloggerdata[temp_offset] & 3) == 2) + { + // the byte is Data - blue/cyan + if (cdloggerdata[temp_offset] & 0x40) + { + // PCM data - cyan + color.setRgb(0, 130, 160); + } + else + { + // non-PCM data - blue + color.setRgb(0, 0, 210); + } + } + } + else + { + temp_offset -= cdloggerdataSize; + if (((unsigned int)temp_offset < cdloggerVideoDataSize)) + { + // CHR + if ((cdloggervdata[temp_offset] & 3) == 3) + { + // the byte was both rendered and read programmatically - light-green + color.setRgb(5, 255, 5); + } + else if ((cdloggervdata[temp_offset] & 3) == 1) + { + // the byte was rendered - yellow + color.setRgb(210, 190, 0); + oppColor.setRgb( 0, 0, 0 ); + } + else if ((cdloggervdata[temp_offset] & 3) == 2) + { + // the byte was read programmatically - light-blue + color.setRgb(15, 15, 255); + } + } + } + } + + if ( reverseVideo ) + { + bg = color; + fg = oppColor; + } + else + { + fg = color; + bg = oppColor; + } + + return 0; +} +//---------------------------------------------------------------------------- void QHexEdit::memModeUpdate(void) { int memSize; @@ -1732,6 +2151,9 @@ void QHexEdit::memModeUpdate(void) printf("Error: Failed to allocate memview buffer size\n"); return; } + maxLineOffset = mb.numLines() - viewLines + 1; + + vbar->setMaximum( memSize / 16 ); } } //---------------------------------------------------------------------------- @@ -1764,6 +2186,15 @@ void QHexEdit::paintEvent(QPaintEvent *event) // maxLineOffset = mb.numLines() - nrow + 1; + if ( maxLineOffset < 0 ) + { + maxLineOffset = 0; + } + + if ( lineOffset < 0 ) + { + lineOffset = 0; + } if ( lineOffset > maxLineOffset ) { lineOffset = maxLineOffset; @@ -1798,6 +2229,7 @@ void QHexEdit::paintEvent(QPaintEvent *event) ca = 16*(lineOffset + cursorPosY) + a; } + cursorAddr = ca; if ( cursorBlink ) { @@ -1847,7 +2279,24 @@ void QHexEdit::paintEvent(QPaintEvent *event) } else { - if ( actvHighlightEnable && (mb.buf[addr].actv > 0) ) + if ( viewMode == MODE_NES_ROM ) + { + QColor romBgColor, romFgColor; + + getRomAddrColor( addr, romFgColor, romBgColor ); + + if ( reverseVideo ) + { + painter.setPen( romFgColor ); + painter.fillRect( x - (0.5*pxCharWidth) , y-pxLineSpacing+pxLineLead, 3*pxCharWidth, pxLineSpacing, romBgColor ); + painter.fillRect( pxHexAscii + (col*pxCharWidth) - pxLineXScroll, y-pxLineSpacing+pxLineLead, pxCharWidth, pxLineSpacing, romBgColor ); + } + else + { + painter.setPen( romFgColor ); + } + } + else if ( actvHighlightEnable && (mb.buf[addr].actv > 0) ) { if ( reverseVideo ) { @@ -1919,3 +2368,30 @@ void hexEditorSaveBookmarks(void) } } //---------------------------------------------------------------------------- +int hexEditorNumWindows(void) +{ + return winList.size(); +} +//---------------------------------------------------------------------------- +int hexEditorOpenFromDebugger( int mode, int addr ) +{ + HexEditorDialog_t *win = NULL; + + if ( winList.size() > 0 ) + { + win = winList.front(); + } + + if ( win == NULL ) + { + win = new HexEditorDialog_t(consoleWindow); + + win->show(); + } + + win->editor->setMode( mode ); + win->editor->setAddr( addr ); + + return 0; +} +//---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/HexEditor.h b/src/drivers/Qt/HexEditor.h index 6c2623a0..a7f5a8b1 100644 --- a/src/drivers/Qt/HexEditor.h +++ b/src/drivers/Qt/HexEditor.h @@ -114,6 +114,7 @@ class QHexEdit : public QWidget void setBackGroundColor( QColor bg ); void memModeUpdate(void); int checkMemActivity(void); + int getAddr(void){ return cursorAddr; }; enum { MODE_NES_RAM = 0, @@ -137,6 +138,8 @@ class QHexEdit : public QWidget QFont font; + int getRomAddrColor( int addr, QColor &fg, QColor &bg ); + memBlock_t mb; int (*memAccessFunc)( unsigned int offset); @@ -164,6 +167,7 @@ class QHexEdit : public QWidget int pxHexAscii; int cursorPosX; int cursorPosY; + int cursorAddr; int cursorBlinkCount; int viewLines; int viewWidth; @@ -182,6 +186,12 @@ class QHexEdit : public QWidget private slots: void jumpToROM(void); void addBookMarkCB(void); + void addDebugSym(void); + void addRamReadBP(void); + void addRamWriteBP(void); + void addRamExecuteBP(void); + void addPpuReadBP(void); + void addPpuWriteBP(void); }; @@ -195,15 +205,22 @@ class HexEditorDialog_t : public QDialog void gotoAddress(int newAddr); void populateBookmarkMenu(void); + void setWindowTitle(void); + void openDebugSymbolEditWindow( int addr ); + + QHexEdit *editor; protected: void closeEvent(QCloseEvent *bar); QScrollBar *vbar; QScrollBar *hbar; - QHexEdit *editor; QTimer *periodicTimer; QMenu *bookmarkMenu; + QAction *viewRAM; + QAction *viewPPU; + QAction *viewOAM; + QAction *viewROM; private: @@ -227,5 +244,7 @@ class HexEditorDialog_t : public QDialog void removeAllBookmarks(void); }; +int hexEditorNumWindows(void); void hexEditorLoadBookmarks(void); void hexEditorSaveBookmarks(void); +int hexEditorOpenFromDebugger( int mode, int addr ); diff --git a/src/drivers/Qt/HotKeyConf.h b/src/drivers/Qt/HotKeyConf.h index bf9c8a8f..b8db1909 100644 --- a/src/drivers/Qt/HotKeyConf.h +++ b/src/drivers/Qt/HotKeyConf.h @@ -1,4 +1,4 @@ -// GamePadConf.h +// HotKeyConf.h // #pragma once diff --git a/src/drivers/Qt/SymbolicDebug.cpp b/src/drivers/Qt/SymbolicDebug.cpp new file mode 100644 index 00000000..43925416 --- /dev/null +++ b/src/drivers/Qt/SymbolicDebug.cpp @@ -0,0 +1,568 @@ +// SymbolicDebug.cpp +#include +#include +#include + +#include "../../types.h" +#include "../../fceu.h" +#include "../../debug.h" +#include "../../driver.h" +#include "../../cart.h" +#include "../../ines.h" + +#include "Qt/SymbolicDebug.h" +#include "Qt/ConsoleUtilities.h" + +debugSymbolTable_t debugSymbolTable; + +//-------------------------------------------------------------- +// debugSymbolPage_t +//-------------------------------------------------------------- +debugSymbolPage_t::debugSymbolPage_t(void) +{ + pageNum = -1; + +} +//-------------------------------------------------------------- +debugSymbolPage_t::~debugSymbolPage_t(void) +{ + std::map ::iterator it; + + for (it=symMap.begin(); it!=symMap.end(); it++) + { + delete it->second; + } + symMap.clear(); +} +//-------------------------------------------------------------- +int debugSymbolPage_t::addSymbol( debugSymbol_t*sym ) +{ + std::map ::iterator it; + + it = symMap.find( sym->ofs ); + + if ( it != symMap.end() ) + { + return -1; + } + symMap[ sym->ofs ] = sym; + + return 0; +} +//-------------------------------------------------------------- +debugSymbol_t *debugSymbolPage_t::getSymbolAtOffset( int ofs ) +{ + debugSymbol_t*sym = NULL; + std::map ::iterator it; + + it = symMap.find( ofs ); + + if ( it != symMap.end() ) + { + sym = it->second; + } + return sym; +} +//-------------------------------------------------------------- +int debugSymbolPage_t::save(void) +{ + FILE *fp; + debugSymbol_t *sym; + std::map ::iterator it; + const char *romFile; + char stmp[512]; + int i,j; + + romFile = getRomFile(); + + if ( romFile == NULL ) + { + return -1; + } + i=0; + while ( romFile[i] != 0 ) + { + + if ( romFile[i] == '|' ) + { + stmp[i] = '.'; + } + else + { + stmp[i] = romFile[i]; + } + i++; + } + stmp[i] = 0; + + if ( pageNum < 0 ) + { + strcat( stmp, ".ram.nl" ); + } + else + { + char suffix[32]; + + sprintf( suffix, ".%X.nl", pageNum ); + + strcat( stmp, suffix ); + } + + fp = fopen( stmp, "w" ); + + if ( fp == NULL ) + { + printf("Error: Could not open file '%s' for writing\n", stmp ); + return -1; + } + + for (it=symMap.begin(); it!=symMap.end(); it++) + { + const char *c; + + sym = it->second; + + i=0; j=0; c = sym->comment.c_str(); + + while ( c[i] != 0 ) + { + if ( c[i] == '\n' ) + { + i++; break; + } + else + { + stmp[j] = c[i]; j++; i++; + } + } + stmp[j] = 0; + + fprintf( fp, "$%04X#%s#%s\n", sym->ofs, sym->name.c_str(), stmp ); + + j=0; + while ( c[i] != 0 ) + { + if ( c[i] == '\n' ) + { + i++; stmp[j] = 0; + + if ( j > 0 ) + { + fprintf( fp, "\\%s\n", stmp ); + } + j=0; + } + else + { + stmp[j] = c[i]; j++; i++; + } + } + } + + fclose(fp); + + return 0; +} +//-------------------------------------------------------------- +void debugSymbolPage_t::print(void) +{ + FILE *fp; + debugSymbol_t *sym; + std::map ::iterator it; + + fp = stdout; + + fprintf( fp, "Page: %X \n", pageNum ); + + for (it=symMap.begin(); it!=symMap.end(); it++) + { + sym = it->second; + + fprintf( fp, " Sym: $%04X '%s' \n", sym->ofs, sym->name.c_str() ); + } +} +//-------------------------------------------------------------- +// debugSymbolTable_t +//-------------------------------------------------------------- +debugSymbolTable_t::debugSymbolTable_t(void) +{ + +} +//-------------------------------------------------------------- +debugSymbolTable_t::~debugSymbolTable_t(void) +{ + this->clear(); +} +//-------------------------------------------------------------- +void debugSymbolTable_t::clear(void) +{ + std::map ::iterator it; + + for (it=pageMap.begin(); it!=pageMap.end(); it++) + { + delete it->second; + } + pageMap.clear(); +} +//-------------------------------------------------------------- +int generateNLFilenameForAddress(int address, char *NLfilename) +{ + int bank; + + if (address < 0x8000) + { + bank = -1; + } + else + { + bank = getBank(address); + #ifdef DW3_NL_0F_1F_HACK + if(bank == 0x0F) + bank = 0x1F; + #endif + } + return generateNLFilenameForBank( bank, NLfilename ); +} +//-------------------------------------------------------------- +int generateNLFilenameForBank(int bank, char *NLfilename) +{ + int i; + const char *romFile; + + romFile = getRomFile(); + + if ( romFile == NULL ) + { + return -1; + } + i=0; + while ( romFile[i] != 0 ) + { + + if ( romFile[i] == '|' ) + { + NLfilename[i] = '.'; + } + else + { + NLfilename[i] = romFile[i]; + } + i++; + } + NLfilename[i] = 0; + + if (bank < 0) + { + // The NL file for the RAM addresses has the name nesrom.nes.ram.nl + strcat(NLfilename, ".ram.nl"); + } + else + { + char stmp[64]; + #ifdef DW3_NL_0F_1F_HACK + if(bank == 0x0F) + bank = 0x1F; + #endif + sprintf( stmp, ".%X.nl", bank); + strcat(NLfilename, stmp ); + } + return 0; +} +//-------------------------------------------------------------- +int debugSymbolTable_t::loadFileNL( int bank ) +{ + FILE *fp; + int i, j, ofs, lineNum = 0, literal = 0; + char fileName[512], line[512]; + char stmp[512]; + debugSymbolPage_t *page = NULL; + debugSymbol_t *sym = NULL; + + //printf("Looking to Load Debug Bank: $%X \n", bank ); + + if ( generateNLFilenameForBank( bank, fileName ) ) + { + return -1; + } + //printf("Loading NL File: %s\n", fileName ); + + fp = ::fopen( fileName, "r" ); + + if ( fp == NULL ) + { + return -1; + } + page = new debugSymbolPage_t; + + page->pageNum = bank; + + pageMap[ page->pageNum ] = page; + + while ( fgets( line, sizeof(line), fp ) != 0 ) + { + i=0; lineNum++; + printf("%4i:%s", lineNum, line ); + + if ( line[i] == '\\' ) + { + // Line is a comment continuation line. + i++; + + j=0; + stmp[j] = '\n'; j++; + + while ( line[i] != 0 ) + { + stmp[j] = line[i]; j++; i++; + } + stmp[j] = 0; + + j--; + while ( j >= 0 ) + { + if ( isspace( stmp[j] ) ) + { + stmp[j] = 0; + } + else + { + break; + } + j--; + } + if ( sym != NULL ) + { + sym->comment.append( stmp ); + } + } + else if ( line[i] == '$' ) + { + // Line is a new debug offset + j=0; i++; + if ( !isxdigit( line[i] ) ) + { + printf("Error: Invalid Offset on Line %i of File %s\n", lineNum, fileName ); + } + while ( isxdigit( line[i] ) ) + { + stmp[j] = line[i]; i++; j++; + } + stmp[j] = 0; + + ofs = strtol( stmp, NULL, 16 ); + + if ( line[i] != '#' ) + { + printf("Error: Missing field delimiter following offset $%X on Line %i of File %s\n", ofs, lineNum, fileName ); + continue; + } + i++; + + while ( isspace(line[i]) ) i++; + + j = 0; + while ( line[i] != 0 ) + { + if ( line[i] == '\\' ) + { + if ( literal ) + { + switch ( line[i] ) + { + case 'r': + stmp[j] = '\r'; + break; + case 'n': + stmp[j] = '\n'; + break; + case 't': + stmp[j] = '\t'; + break; + default: + stmp[j] = line[i]; + break; + } + j++; i++; + literal = 0; + } + else + { + i++; + literal = !literal; + } + } + else if ( line[i] == '#' ) + { + break; + } + else + { + stmp[j] = line[i]; j++; i++; + } + } + stmp[j] = 0; + + j--; + while ( j >= 0 ) + { + if ( isspace( stmp[j] ) ) + { + stmp[j] = 0; + } + else + { + break; + } + j--; + } + + if ( line[i] != '#' ) + { + printf("Error: Missing field delimiter following name '%s' on Line %i of File %s\n", stmp, lineNum, fileName ); + continue; + } + i++; + + sym = new debugSymbol_t(); + + if ( sym == NULL ) + { + printf("Error: Failed to allocate memory for offset $%04X Name '%s' on Line %i of File %s\n", ofs, stmp, lineNum, fileName ); + continue; + } + sym->ofs = ofs; + sym->name.assign( stmp ); + + while ( isspace( line[i] ) ) i++; + + j=0; + while ( line[i] != 0 ) + { + stmp[j] = line[i]; j++; i++; + } + stmp[j] = 0; + + j--; + while ( j >= 0 ) + { + if ( isspace( stmp[j] ) ) + { + stmp[j] = 0; + } + else + { + break; + } + j--; + } + + sym->comment.assign( stmp ); + + if ( page->addSymbol( sym ) ) + { + printf("Error: Failed to add symbol for offset $%04X Name '%s' on Line %i of File %s\n", ofs, stmp, lineNum, fileName ); + delete sym; sym = NULL; // Failed to add symbol + } + } + } + + ::fclose(fp); + + return 0; +} +//-------------------------------------------------------------- +int debugSymbolTable_t::loadGameSymbols(void) +{ + int nPages, pageSize, romSize = 0x10000; + + this->save(); + this->clear(); + + if ( GameInfo != NULL ) + { + romSize = 16 + CHRsize[0] + PRGsize[0]; + } + + loadFileNL( -1 ); + + pageSize = (1<::iterator it; + + it = pageMap.find( bank ); + + if ( it == pageMap.end() ) + { + page = new debugSymbolPage_t(); + page->pageNum = bank; + pageMap[ bank ] = page; + } + else + { + page = it->second; + } + page->addSymbol( sym ); + + return 0; +} +//-------------------------------------------------------------- +debugSymbol_t *debugSymbolTable_t::getSymbolAtBankOffset( int bank, int ofs ) +{ + debugSymbol_t*sym = NULL; + std::map ::iterator it; + + it = pageMap.find( bank ); + + if ( it != pageMap.end() ) + { + sym = (it->second)->getSymbolAtOffset( ofs ); + } + return sym; +} +//-------------------------------------------------------------- +void debugSymbolTable_t::save(void) +{ + debugSymbolPage_t *page; + std::map ::iterator it; + + for (it=pageMap.begin(); it!=pageMap.end(); it++) + { + page = it->second; + + page->save(); + } +} +//-------------------------------------------------------------- +void debugSymbolTable_t::print(void) +{ + debugSymbolPage_t *page; + std::map ::iterator it; + + for (it=pageMap.begin(); it!=pageMap.end(); it++) + { + page = it->second; + + page->print(); + } +} +//-------------------------------------------------------------- diff --git a/src/drivers/Qt/SymbolicDebug.h b/src/drivers/Qt/SymbolicDebug.h new file mode 100644 index 00000000..7cce83b8 --- /dev/null +++ b/src/drivers/Qt/SymbolicDebug.h @@ -0,0 +1,76 @@ +// SymbolicDebug.h +// +#ifndef __SYMBOLIC_DEBUG_H__ +#define __SYMBOLIC_DEBUG_H__ + +#include +#include +#include + +struct debugSymbol_t +{ + int ofs; + std::string name; + std::string comment; + + debugSymbol_t(void) + { + ofs = 0; + }; +}; + +struct debugSymbolPage_t +{ + int pageNum; + + debugSymbolPage_t(void); + ~debugSymbolPage_t(void); + + int save(void); + void print(void); + int size(void){ return symMap.size(); } + + int addSymbol( debugSymbol_t *sym ); + + debugSymbol_t *getSymbolAtOffset( int ofs ); + + std::map symMap; +}; + +class debugSymbolTable_t +{ + + public: + debugSymbolTable_t(void); + ~debugSymbolTable_t(void); + + int loadFileNL( int addr ); + int loadGameSymbols(void); + int numPages(void){ return pageMap.size(); } + + void save(void); + void clear(void); + void print(void); + + debugSymbol_t *getSymbolAtBankOffset( int bank, int ofs ); + + int addSymbolAtBankOffset( int bank, int ofs, debugSymbol_t *sym ); + + private: + std::map pageMap; + +}; + +extern debugSymbolTable_t debugSymbolTable; + +//struct MemoryMappedRegister +//{ +// char* offset; +// char* name; +//}; + + +int generateNLFilenameForBank(int bank, char *NLfilename); +int generateNLFilenameForAddress(int address, char *NLfilename); + +#endif diff --git a/src/drivers/Qt/TraceLogger.cpp b/src/drivers/Qt/TraceLogger.cpp new file mode 100644 index 00000000..d8e098ff --- /dev/null +++ b/src/drivers/Qt/TraceLogger.cpp @@ -0,0 +1,1208 @@ +// TraceLogger.cpp +// +#include + +#include +#include +#include +#include +#include + +#include "../../types.h" +#include "../../fceu.h" +#include "../../cart.h" +#include "../../x6502.h" +#include "../../debug.h" +#include "../../asm.h" +#include "../../ppu.h" +#include "../../ines.h" +#include "../../nsf.h" +#include "../../movie.h" + +#include "Qt/ConsoleUtilities.h" +#include "Qt/TraceLogger.h" +#include "Qt/main.h" +#include "Qt/dface.h" +#include "Qt/input.h" +#include "Qt/config.h" +#include "Qt/fceuWrapper.h" + +#define LOG_REGISTERS 0x00000001 +#define LOG_PROCESSOR_STATUS 0x00000002 +#define LOG_NEW_INSTRUCTIONS 0x00000004 +#define LOG_NEW_DATA 0x00000008 +#define LOG_TO_THE_LEFT 0x00000010 +#define LOG_FRAMES_COUNT 0x00000020 +#define LOG_MESSAGES 0x00000040 +#define LOG_BREAKPOINTS 0x00000080 +#define LOG_SYMBOLIC 0x00000100 +#define LOG_CODE_TABBING 0x00000200 +#define LOG_CYCLES_COUNT 0x00000400 +#define LOG_INSTRUCTIONS_COUNT 0x00000800 +#define LOG_BANK_NUMBER 0x00001000 + +#define LOG_LINE_MAX_LEN 160 +// Frames count - 1+6+1 symbols +// Cycles count - 1+11+1 symbols +// Instructions count - 1+11+1 symbols +// AXYS state - 20 +// Processor status - 11 +// Tabs - 31 +// Address - 6 +// Data - 10 +// Disassembly - 45 +// EOL (/0) - 1 +// ------------------------ +// 148 symbols total +#define LOG_AXYSTATE_MAX_LEN 21 +#define LOG_PROCSTATUS_MAX_LEN 12 +#define LOG_TABS_MASK 31 +#define LOG_ADDRESS_MAX_LEN 13 +#define LOG_DATA_MAX_LEN 11 +#define LOG_DISASSEMBLY_MAX_LEN 46 +#define NL_MAX_MULTILINE_COMMENT_LEN 1000 + +static int logging = 0; +static int logging_options = LOG_REGISTERS | LOG_PROCESSOR_STATUS | LOG_TO_THE_LEFT | LOG_MESSAGES | LOG_BREAKPOINTS | LOG_CODE_TABBING; +static int oldcodecount = 0, olddatacount = 0; + +static traceRecord_t *recBuf = NULL; +static int recBufMax = 0; +static int recBufHead = 0; +static int recBufTail = 0; +static FILE *logFile = NULL; +static TraceLoggerDialog_t *traceLogWindow = NULL; +static void pushMsgToLogBuffer( const char *msg ); +//---------------------------------------------------- +TraceLoggerDialog_t::TraceLoggerDialog_t(QWidget *parent) + : QDialog( parent ) +{ + QVBoxLayout *mainLayout; + QHBoxLayout *hbox; + QGridLayout *grid; + QGroupBox *frame; + QLabel *lbl; + + if ( recBufMax == 0 ) + { + initTraceLogBuffer( 1000000 ); + } + + setWindowTitle( tr("Trace Logger") ); + + mainLayout = new QVBoxLayout(); + grid = new QGridLayout(); + mainLayout->addLayout( grid, 100 ); + + traceView = new QTraceLogView(this); + vbar = new QScrollBar( Qt::Vertical, this ); + hbar = new QScrollBar( Qt::Horizontal, this ); + + connect( hbar, SIGNAL(valueChanged(int)), this, SLOT(hbarChanged(int)) ); + connect( vbar, SIGNAL(valueChanged(int)), this, SLOT(vbarChanged(int)) ); + + traceView->setScrollBars( hbar, vbar ); + hbar->setMinimum(0); + hbar->setMaximum(100); + vbar->setMinimum(0); + vbar->setMaximum(recBufMax); + vbar->setValue(recBufMax); + + grid->addWidget( traceView, 0, 0); + grid->addWidget( vbar , 0, 1 ); + grid->addWidget( hbar , 1, 0 ); + + grid = new QGridLayout(); + mainLayout->addLayout( grid, 1 ); + + lbl = new QLabel( tr("Lines") ); + logLastCbox = new QCheckBox( tr("Log Last") ); + logMaxLinesComboBox = new QComboBox(); + + logLastCbox->setChecked(true); + logMaxLinesComboBox->addItem( tr("3,000,000"), 3000000 ); + logMaxLinesComboBox->addItem( tr("1,000,000"), 1000000 ); + logMaxLinesComboBox->addItem( tr("300,000") , 300000 ); + logMaxLinesComboBox->addItem( tr("100,000") , 100000 ); + logMaxLinesComboBox->addItem( tr("30,000") , 30000 ); + logMaxLinesComboBox->addItem( tr("10,000") , 10000 ); + logMaxLinesComboBox->addItem( tr("3,000") , 3000 ); + logMaxLinesComboBox->addItem( tr("1,000") , 1000 ); + + for (int i=0; icount(); i++) + { + if ( logMaxLinesComboBox->itemData(i).toInt() == recBufMax ) + { + logMaxLinesComboBox->setCurrentIndex( i ); + } + } + connect( logMaxLinesComboBox, SIGNAL(activated(int)), this, SLOT(logMaxLinesChanged(int)) ); + + logFileCbox = new QCheckBox( tr("Log to File") ); + selLogFileButton = new QPushButton( tr("Browse...") ); + startStopButton = new QPushButton( tr("Start Logging") ); + autoUpdateCbox = new QCheckBox( tr("Automatically update this window while logging") ); + + autoUpdateCbox->setChecked(true); + + if ( logging ) + { + startStopButton->setText( tr("Stop Logging") ); + } + connect( startStopButton, SIGNAL(clicked(void)), this, SLOT(toggleLoggingOnOff(void)) ); + connect( selLogFileButton, SIGNAL(clicked(void)), this, SLOT(openLogFile(void)) ); + + hbox = new QHBoxLayout(); + hbox->addWidget( logLastCbox ); + hbox->addWidget( logMaxLinesComboBox ); + hbox->addWidget( lbl ); + + grid->addLayout( hbox, 0, 0, Qt::AlignLeft ); + grid->addWidget( startStopButton, 0, 1, Qt::AlignCenter ); + + hbox = new QHBoxLayout(); + hbox->addWidget( logFileCbox ); + hbox->addWidget( selLogFileButton ); + + grid->addLayout( hbox, 1, 0, Qt::AlignLeft ); + grid->addWidget( autoUpdateCbox, 1, 1, Qt::AlignCenter ); + + grid = new QGridLayout(); + frame = new QGroupBox(tr("Log Options")); + frame->setLayout( grid ); + + logRegCbox = new QCheckBox( tr("Log State of Registers") ); + logFrameCbox = new QCheckBox( tr("Log Frames Count") ); + logEmuMsgCbox = new QCheckBox( tr("Log Emulator Messages") ); + symTraceEnaCbox = new QCheckBox( tr("Symbolic Trace") ); + logProcStatFlagCbox = new QCheckBox( tr("Log Processor Status Flags") ); + logCyclesCountCbox = new QCheckBox( tr("Log Cycles Count") ); + logBreakpointCbox = new QCheckBox( tr("Log Breakpoint Hits") ); + useStackPointerCbox = new QCheckBox( tr("Use Stack Pointer for Code Tabbing (Nesting Visualization)") ); + toLeftDisassemblyCbox = new QCheckBox( tr("To the Left from Disassembly") ); + logInstrCountCbox = new QCheckBox( tr("Log Instructions Count") ); + logBankNumCbox = new QCheckBox( tr("Log Bank Number") ); + + logRegCbox->setChecked( (logging_options & LOG_REGISTERS) ? true : false ); + logFrameCbox->setChecked( (logging_options & LOG_FRAMES_COUNT) ? true : false ); + logEmuMsgCbox->setChecked( (logging_options & LOG_MESSAGES) ? true : false ); + symTraceEnaCbox->setChecked( (logging_options & LOG_SYMBOLIC) ? true : false ); + logProcStatFlagCbox->setChecked( (logging_options & LOG_PROCESSOR_STATUS) ? true : false ); + logCyclesCountCbox->setChecked( (logging_options & LOG_CYCLES_COUNT) ? true : false ); + logBreakpointCbox->setChecked( (logging_options & LOG_BREAKPOINTS) ? true : false ); + useStackPointerCbox->setChecked( (logging_options & LOG_CODE_TABBING) ? true : false ); + toLeftDisassemblyCbox->setChecked( (logging_options & LOG_TO_THE_LEFT) ? true : false ); + logInstrCountCbox->setChecked( (logging_options & LOG_INSTRUCTIONS_COUNT) ? true : false ); + logBankNumCbox->setChecked( (logging_options & LOG_BANK_NUMBER) ? true : false ); + + connect( logRegCbox, SIGNAL(stateChanged(int)), this, SLOT(logRegStateChanged(int)) ); + connect( logFrameCbox, SIGNAL(stateChanged(int)), this, SLOT(logFrameStateChanged(int)) ); + connect( logEmuMsgCbox, SIGNAL(stateChanged(int)), this, SLOT(logEmuMsgStateChanged(int)) ); + connect( symTraceEnaCbox, SIGNAL(stateChanged(int)), this, SLOT(symTraceEnaStateChanged(int)) ); + connect( logProcStatFlagCbox, SIGNAL(stateChanged(int)), this, SLOT(logProcStatFlagStateChanged(int)) ); + connect( logCyclesCountCbox, SIGNAL(stateChanged(int)), this, SLOT(logCyclesCountStateChanged(int)) ); + connect( logBreakpointCbox, SIGNAL(stateChanged(int)), this, SLOT(logBreakpointStateChanged(int)) ); + connect( useStackPointerCbox, SIGNAL(stateChanged(int)), this, SLOT(useStackPointerStateChanged(int)) ); + connect( toLeftDisassemblyCbox, SIGNAL(stateChanged(int)), this, SLOT(toLeftDisassemblyStateChanged(int)) ); + connect( logInstrCountCbox, SIGNAL(stateChanged(int)), this, SLOT(logInstrCountStateChanged(int)) ); + connect( logBankNumCbox, SIGNAL(stateChanged(int)), this, SLOT(logBankNumStateChanged(int)) ); + + grid->addWidget( logRegCbox , 0, 0, Qt::AlignLeft ); + grid->addWidget( logFrameCbox , 1, 0, Qt::AlignLeft ); + grid->addWidget( logEmuMsgCbox , 2, 0, Qt::AlignLeft ); + grid->addWidget( symTraceEnaCbox, 3, 0, Qt::AlignLeft ); + grid->addWidget( logProcStatFlagCbox, 0, 1, Qt::AlignLeft ); + grid->addWidget( logCyclesCountCbox , 1, 1, Qt::AlignLeft ); + grid->addWidget( logBreakpointCbox , 2, 1, Qt::AlignLeft ); + grid->addWidget( useStackPointerCbox, 3, 1, 1, 2, Qt::AlignLeft ); + grid->addWidget( toLeftDisassemblyCbox, 0, 2, Qt::AlignLeft ); + grid->addWidget( logInstrCountCbox , 1, 2, Qt::AlignLeft ); + grid->addWidget( logBankNumCbox , 2, 2, Qt::AlignLeft ); + + mainLayout->addWidget( frame, 1 ); + + grid = new QGridLayout(); + frame = new QGroupBox(tr("Extra Log Options that work with the Code/Data Logger")); + frame->setLayout( grid ); + + logNewMapCodeCbox = new QCheckBox( tr("Only Log Newly Mapped Code") ); + logNewMapDataCbox = new QCheckBox( tr("Only Log that Accesses Newly Mapped Data") ); + + logNewMapCodeCbox->setChecked( (logging_options & LOG_NEW_INSTRUCTIONS) ? true : false ); + logNewMapDataCbox->setChecked( (logging_options & LOG_NEW_DATA) ? true : false ); + + connect( logNewMapCodeCbox, SIGNAL(stateChanged(int)), this, SLOT(logNewMapCodeChanged(int)) ); + connect( logNewMapDataCbox, SIGNAL(stateChanged(int)), this, SLOT(logNewMapDataChanged(int)) ); + + grid->addWidget( logNewMapCodeCbox, 0, 0, Qt::AlignLeft ); + grid->addWidget( logNewMapDataCbox, 0, 1, Qt::AlignLeft ); + + mainLayout->addWidget( frame, 1 ); + + setLayout( mainLayout ); + + traceViewCounter = 0; + + updateTimer = new QTimer( this ); + + connect( updateTimer, &QTimer::timeout, this, &TraceLoggerDialog_t::updatePeriodic ); + + updateTimer->start( 10 ); // 100hz +} +//---------------------------------------------------- +TraceLoggerDialog_t::~TraceLoggerDialog_t(void) +{ + updateTimer->stop(); + + traceLogWindow = NULL; + logging = 0; + + if ( logFile ) + { + fclose( logFile ); logFile = NULL; + } + printf("Trace Logger Window Deleted\n"); +} +//---------------------------------------------------- +void TraceLoggerDialog_t::closeEvent(QCloseEvent *event) +{ + printf("Trace Logger Close Window Event\n"); + done(0); + deleteLater(); + event->accept(); +} +//---------------------------------------------------- +void TraceLoggerDialog_t::closeWindow(void) +{ + printf("Trace Logger Close Window\n"); + done(0); + deleteLater(); +} +//---------------------------------------------------- +void TraceLoggerDialog_t::updatePeriodic(void) +{ + char traceViewDrawEnable; + + if ( logLastCbox->isChecked() ) + { + if ( FCEUI_EmulationPaused() ) + { + traceViewDrawEnable = 1; + } + else + { + traceViewDrawEnable = autoUpdateCbox->isChecked(); + } + } + else + { + traceViewDrawEnable = 0; + } + + if ( logFile && logFileCbox->isChecked() ) + { + char line[256]; + + while ( recBufHead != recBufTail ) + { + recBuf[ recBufTail ].convToText( line ); + + fprintf( logFile, "%s\n", line ); + + recBufTail = (recBufTail + 1) % recBufMax; + } + } + else + { + recBufTail = recBufHead; + } + + if ( traceViewCounter > 20 ) + { + if ( traceViewDrawEnable ) + { + traceView->update(); + } + traceViewCounter = 0; + } + traceViewCounter++; +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logMaxLinesChanged(int index) +{ + int logPrev; + int maxLines = logMaxLinesComboBox->itemData(index).toInt(); + + logPrev = logging; + logging = 0; + + usleep(1000); + + initTraceLogBuffer( maxLines ); + + vbar->setMaximum(recBufMax); + vbar->setValue(recBufMax); + + logging = logPrev; +} +//---------------------------------------------------- +void TraceLoggerDialog_t::toggleLoggingOnOff(void) +{ + if ( logging ) + { + logging = 0; + usleep( 1000 ); + pushMsgToLogBuffer("Logging Finished"); + startStopButton->setText( tr("Start Logging") ); + + if ( logFile ) + { + fclose( logFile ); logFile = NULL; + } + } + else + { + if ( logFileCbox->isChecked() ) + { + openLogFile(); + } + pushMsgToLogBuffer("Log Start"); + startStopButton->setText( tr("Stop Logging") ); + logging = 1; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::openLogFile(void) +{ + const char *romFile; + int ret, useNativeFileDialogVal; + QString filename; + QFileDialog dialog(this, tr("Select Log File") ); + + printf("Log File Select\n"); + + dialog.setFileMode(QFileDialog::AnyFile); + + dialog.setNameFilter(tr("LOG files (*.log *.LOG) ;; All files (*)")); + + dialog.setViewMode(QFileDialog::List); + dialog.setFilter( QDir::AllEntries | QDir::Hidden ); + dialog.setLabelText( QFileDialog::Accept, tr("Open") ); + dialog.setDefaultSuffix( tr(".log") ); + + romFile = getRomFile(); + + if ( romFile != NULL ) + { + char dir[512]; + getDirFromFile( romFile, dir ); + dialog.setDirectory( tr(dir) ); + } + + // Check config option to use native file dialog or not + g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal); + + dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal); + + dialog.show(); + ret = dialog.exec(); + + if ( ret ) + { + QStringList fileList; + fileList = dialog.selectedFiles(); + + if ( fileList.size() > 0 ) + { + filename = fileList[0]; + } + } + + if ( filename.isNull() ) + { + return; + } + //qDebug() << "selected file path : " << filename.toUtf8(); + + if ( logFile ) + { + fclose( logFile ); logFile = NULL; + } + logFile = fopen( filename.toStdString().c_str(), "w"); + + return; +} +//---------------------------------------------------- +void TraceLoggerDialog_t::hbarChanged(int val) +{ + traceView->update(); +} +//---------------------------------------------------- +void TraceLoggerDialog_t::vbarChanged(int val) +{ + traceView->update(); +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logRegStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_REGISTERS; + } + else + { + logging_options |= LOG_REGISTERS; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logFrameStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_FRAMES_COUNT; + } + else + { + logging_options |= LOG_FRAMES_COUNT; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logEmuMsgStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_MESSAGES; + } + else + { + logging_options |= LOG_MESSAGES; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::symTraceEnaStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_SYMBOLIC; + } + else + { + logging_options |= LOG_SYMBOLIC; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logProcStatFlagStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_PROCESSOR_STATUS; + } + else + { + logging_options |= LOG_PROCESSOR_STATUS; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logCyclesCountStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_CYCLES_COUNT; + } + else + { + logging_options |= LOG_CYCLES_COUNT; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logBreakpointStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_BREAKPOINTS; + } + else + { + logging_options |= LOG_BREAKPOINTS; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::useStackPointerStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_CODE_TABBING; + } + else + { + logging_options |= LOG_CODE_TABBING; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::toLeftDisassemblyStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_TO_THE_LEFT; + } + else + { + logging_options |= LOG_TO_THE_LEFT; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logInstrCountStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_INSTRUCTIONS_COUNT; + } + else + { + logging_options |= LOG_INSTRUCTIONS_COUNT; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logBankNumStateChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_BANK_NUMBER; + } + else + { + logging_options |= LOG_BANK_NUMBER; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logNewMapCodeChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_NEW_INSTRUCTIONS; + } + else + { + logging_options |= LOG_NEW_INSTRUCTIONS; + } +} +//---------------------------------------------------- +void TraceLoggerDialog_t::logNewMapDataChanged(int state) +{ + if ( state == Qt::Unchecked ) + { + logging_options &= ~LOG_NEW_DATA; + } + else + { + logging_options |= LOG_NEW_DATA; + } +} +//---------------------------------------------------- +traceRecord_t::traceRecord_t(void) +{ + cpu.PC = 0; + cpu.A = 0; + cpu.X = 0; + cpu.Y = 0; + cpu.S = 0; + cpu.P = 0; + + opCode[0] = 0; + opCode[1] = 0; + opCode[2] = 0; + opSize = 0; + asmTxtSize = 0; + asmTxt[0] = 0; + + cycleCount = 0; + instrCount = 0; + flags = 0; + + callAddr = -1; + romAddr = -1; + bank = -1; + skippedLines = 0; +} +//---------------------------------------------------- +int traceRecord_t::appendAsmText( const char *txt ) +{ + int i=0; + + while ( txt[i] != 0 ) + { + asmTxt[ asmTxtSize ] = txt[i]; i++; asmTxtSize++; + } + asmTxt[ asmTxtSize ] = 0; + + return 0; +} +//---------------------------------------------------- +static int convToXchar( int i ) +{ + int c = 0; + + if ( (i >= 0) && (i < 10) ) + { + c = i + '0'; + } + else if ( i < 16 ) + { + c = (i - 10) + 'A'; + } + return c; +} +//---------------------------------------------------- +int traceRecord_t::convToText( char *txt ) +{ + int i=0, j=0; + char stmp[128]; + char str_axystate[32], str_procstatus[32]; + + txt[0] = 0; + if ( opSize == 0 ) + { + j=0; + while ( asmTxt[j] != 0 ) + { + txt[i] = asmTxt[j]; i++; j++; + } + txt[i] = 0; + + return -1; + } + + if ( skippedLines > 0 ) + { + sprintf(stmp, "(%d lines skipped) ", skippedLines); + + j=0; + while ( stmp[j] != 0 ) + { + txt[i] = stmp[j]; i++; j++; + } + } + + // Start filling the str_temp line: Frame count, Cycles count, Instructions count, AXYS state, Processor status, Tabs, Address, Data, Disassembly + if (logging_options & LOG_FRAMES_COUNT) + { + sprintf(stmp, "f%-6llu ", (long long unsigned int)frameCount); + + j=0; + while ( stmp[j] != 0 ) + { + txt[i] = stmp[j]; i++; j++; + } + } + + if (logging_options & LOG_CYCLES_COUNT) + { + sprintf(stmp, "c%-11llu ", (long long unsigned int)cycleCount); + + j=0; + while ( stmp[j] != 0 ) + { + txt[i] = stmp[j]; i++; j++; + } + } + + if (logging_options & LOG_INSTRUCTIONS_COUNT) + { + sprintf(stmp, "i%-11llu ", (long long unsigned int)instrCount); + + j=0; + while ( stmp[j] != 0 ) + { + txt[i] = stmp[j]; i++; j++; + } + } + + if (logging_options & LOG_REGISTERS) + { + sprintf(str_axystate,"A:%02X X:%02X Y:%02X S:%02X ",(cpu.A),(cpu.X),(cpu.Y),(cpu.S)); + } + + if (logging_options & LOG_PROCESSOR_STATUS) + { + int tmp = cpu.P^0xFF; + sprintf(str_procstatus,"P:%c%c%c%c%c%c%c%c ", + 'N'|(tmp&0x80)>>2, + 'V'|(tmp&0x40)>>1, + 'U'|(tmp&0x20), + 'B'|(tmp&0x10)<<1, + 'D'|(tmp&0x08)<<2, + 'I'|(tmp&0x04)<<3, + 'Z'|(tmp&0x02)<<4, + 'C'|(tmp&0x01)<<5 + ); + } + + if (logging_options & LOG_TO_THE_LEFT) + { + if (logging_options & LOG_REGISTERS) + { + j=0; + while ( str_axystate[j] != 0 ) + { + txt[i] = str_axystate[j]; i++; j++; + } + } + if (logging_options & LOG_PROCESSOR_STATUS) + { + j=0; + while ( str_procstatus[j] != 0 ) + { + txt[i] = str_procstatus[j]; i++; j++; + } + } + } + + if (logging_options & LOG_CODE_TABBING) + { + // add spaces at the beginning of the line according to stack pointer + int spaces = (0xFF - cpu.S) & LOG_TABS_MASK; + + while ( spaces > 0 ) + { + txt[i] = ' '; i++; spaces--; + } + } + else if (logging_options & LOG_TO_THE_LEFT) + { + txt[i] = ' '; i++; + } + + if (logging_options & LOG_BANK_NUMBER) + { + if (cpu.PC >= 0x8000) + { + sprintf(stmp, "$%02X:%04X: ", bank, cpu.PC); + } + else + { + sprintf(stmp, " $%04X: ", cpu.PC); + } + } + else + { + sprintf(stmp, "$%04X: ", cpu.PC); + } + j=0; + while ( stmp[j] != 0 ) + { + txt[i] = stmp[j]; i++; j++; + } + + for (j=0; j> 4) & 0x0F ); i++; + txt[i] = convToXchar( opCode[j] & 0x0F ); i++; + txt[i] = ' '; i++; + } + while ( j < 3 ) + { + txt[i] = ' '; i++; + txt[i] = ' '; i++; + txt[i] = ' '; i++; + j++; + } + j=0; + while ( asmTxt[j] != 0 ) + { + txt[i] = asmTxt[j]; i++; j++; + } + if ( callAddr >= 0 ) + { + sprintf(stmp, " (from $%04X)", callAddr); + + j=0; + while ( stmp[j] != 0 ) + { + txt[i] = stmp[j]; i++; j++; + } + } + + if (!(logging_options & LOG_TO_THE_LEFT)) + { + if (logging_options & LOG_REGISTERS) + { + j=0; + while ( str_axystate[j] != 0 ) + { + txt[i] = str_axystate[j]; i++; j++; + } + } + if (logging_options & LOG_PROCESSOR_STATUS) + { + j=0; + while ( str_procstatus[j] != 0 ) + { + txt[i] = str_procstatus[j]; i++; j++; + } + } + } + + txt[i] = 0; + + return 0; +} +//---------------------------------------------------- +int initTraceLogBuffer( int maxRecs ) +{ + if ( maxRecs != recBufMax ) + { + size_t size; + + size = maxRecs * sizeof(traceRecord_t); + + recBuf = (traceRecord_t*)malloc( size ); + + if ( recBuf ) + { + memset( (void*)recBuf, 0, size ); + recBufMax = maxRecs; + } + else + { + recBufMax = 0; + } + } + return recBuf == NULL; +} +//---------------------------------------------------- +void openTraceLoggerWindow( QWidget *parent ) +{ + // Only allow one trace logger window to be open + if ( traceLogWindow != NULL ) + { + return; + } + //printf("Open Trace Logger Window\n"); + + traceLogWindow = new TraceLoggerDialog_t(parent); + + traceLogWindow->show(); +} +//---------------------------------------------------- +static void pushToLogBuffer( traceRecord_t &rec ) +{ + recBuf[ recBufHead ] = rec; + recBufHead = (recBufHead + 1) % recBufMax; + + if ( recBufHead == recBufTail ) + { + printf("Trace Log Overrun!!!\n"); + } +} +//---------------------------------------------------- +static void pushMsgToLogBuffer( const char *msg ) +{ + traceRecord_t rec; + + strncpy( rec.asmTxt, msg, sizeof(rec.asmTxt) ); + + rec.asmTxt[ sizeof(rec.asmTxt)-1 ] = 0; + + pushToLogBuffer( rec ); +} +//---------------------------------------------------- +//todo: really speed this up +void FCEUD_TraceInstruction(uint8 *opcode, int size) +{ + if (!logging) + return; + + traceRecord_t rec; + + unsigned int addr = X.PC; + static int unloggedlines = 0; + + rec.cpu.PC = X.PC; + rec.cpu.A = X.A; + rec.cpu.X = X.X; + rec.cpu.Y = X.Y; + rec.cpu.S = X.S; + rec.cpu.P = X.P; + + for (int i=0; i 0) + { + //sprintf(str_result, "(%d lines skipped)", unloggedlines); + rec.skippedLines = unloggedlines; + unloggedlines = 0; + } + } + else + { + if ((logging_options & LOG_NEW_INSTRUCTIONS) || + (logging_options & LOG_NEW_DATA)) + { + if (FCEUI_GetLoggingCD()) + { + unloggedlines++; + } + return; + } + } + } + + if ((addr + size) > 0xFFFF) + { + //sprintf(str_data, "%02X ", opcode[0]); + //sprintf(str_disassembly, "OVERFLOW"); + rec.flags |= 0x01; + } + else + { + char* a = 0; + switch (size) + { + case 0: + //sprintf(str_disassembly,"UNDEFINED"); + rec.flags |= 0x02; + break; + case 1: + { + a = Disassemble(addr + 1, opcode); + // special case: an RTS opcode + if (opcode[0] == 0x60) + { + // add the beginning address of the subroutine that we exit from + unsigned int caller_addr = GetMem(((X.S) + 1)|0x0100) + (GetMem(((X.S) + 2)|0x0100) << 8) - 0x2; + if (GetMem(caller_addr) == 0x20) + { + // this was a JSR instruction - take the subroutine address from it + unsigned int call_addr = GetMem(caller_addr + 1) + (GetMem(caller_addr + 2) << 8); + rec.callAddr = call_addr; + } + } + break; + } + case 2: + a = Disassemble(addr + 2, opcode); + break; + case 3: + a = Disassemble(addr + 3, opcode); + break; + } + + if (a) + { + //if (logging_options & LOG_SYMBOLIC) + //{ + // loadNameFiles(); + // tempAddressesLog.resize(0); + // // Insert Name and Comment lines if needed + // Name* node = findNode(getNamesPointerForAddress(addr), addr); + // if (node) + // { + // if (node->name) + // { + // strcpy(str_decoration, node->name); + // strcat(str_decoration, ":"); + // tempAddressesLog.push_back(addr); + // //OutputLogLine(str_decoration, &tempAddressesLog); + // } + // if (node->comment) + // { + // // make a copy + // strcpy(str_decoration_comment, node->comment); + // strcat(str_decoration_comment, "\r\n"); + // tracer_decoration_comment = str_decoration_comment; + // // divide the str_decoration_comment into strings (Comment1, Comment2, ...) + // char* tracer_decoration_comment_end_pos = strstr(tracer_decoration_comment, "\r\n"); + // while (tracer_decoration_comment_end_pos) + // { + // tracer_decoration_comment_end_pos[0] = 0; // set \0 instead of \r + // strcpy(str_decoration, "; "); + // strcat(str_decoration, tracer_decoration_comment); + // //OutputLogLine(str_decoration, &tempAddressesLog); + // tracer_decoration_comment_end_pos += 2; + // tracer_decoration_comment = tracer_decoration_comment_end_pos; + // tracer_decoration_comment_end_pos = strstr(tracer_decoration_comment_end_pos, "\r\n"); + // } + // } + // } + // + // //replaceNames(ramBankNames, a, &tempAddressesLog); + // //for(int i=0;isetFont(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; + pxLineWidth = pxCharWidth * LOG_LINE_MAX_LEN; + + viewLines = (viewHeight / pxLineSpacing) + 1; +} +//---------------------------------------------------------------------------- +void QTraceLogView::setScrollBars( QScrollBar *h, QScrollBar *v ) +{ + hbar = h; vbar = v; +} +//---------------------------------------------------- +void QTraceLogView::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 QTraceLogView::paintEvent(QPaintEvent *event) +{ + int x,y, v, ofs, row, start, end, nrow; + QPainter painter(this); + char line[256]; + traceRecord_t rec[64]; + + painter.setFont(font); + viewWidth = event->rect().width(); + viewHeight = event->rect().height(); + + nrow = (viewHeight / pxLineSpacing); + + if (nrow < 1 ) nrow = 1; + + viewLines = nrow; + + painter.fillRect( 0, 0, viewWidth, viewHeight, this->palette().color(QPalette::Background) ); + + painter.setPen( this->palette().color(QPalette::WindowText)); + + v = vbar->value(); + + if ( v < viewLines ) v = viewLines; + + ofs = recBufMax - v; + + end = recBufHead - ofs; + + if ( end < 0 ) end += recBufMax; + + start = (end - nrow); + + if ( start < 0 ) start += recBufMax; + + row = 0; + while ( start != end ) + { + rec[row] = recBuf[ start ]; row++; + start = (start + 1) % recBufMax; + } + + pxLineXScroll = (int)(0.010f * (float)hbar->value() * (float)(pxLineWidth - viewWidth) ); + + x = -pxLineXScroll; + y = pxLineSpacing; + + for (row=0; row +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct traceRecord_t +{ + struct { + uint16_t PC; + uint8_t A; + uint8_t X; + uint8_t Y; + uint8_t S; + uint8_t P; + } cpu; + + uint8_t opCode[3]; + uint8_t opSize; + uint8_t asmTxtSize; + char asmTxt[64]; + + uint64_t frameCount; + uint64_t cycleCount; + uint64_t instrCount; + uint64_t flags; + + int32_t callAddr; + int32_t romAddr; + int32_t bank; + int32_t skippedLines; + + traceRecord_t(void); + + int appendAsmText( const char *txt ); + + int convToText( char *line ); +}; + +class QTraceLogView : public QWidget +{ + Q_OBJECT + + public: + QTraceLogView(QWidget *parent = 0); + ~QTraceLogView(void); + + void setScrollBars( QScrollBar *h, QScrollBar *v ); + protected: + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + + void calcFontData(void); + + protected: + QFont font; + QScrollBar *vbar; + QScrollBar *hbar; + + int pxCharWidth; + int pxCharHeight; + int pxLineSpacing; + int pxLineLead; + int pxCursorHeight; + int pxLineXScroll; + int pxLineWidth; + int viewLines; + int viewWidth; + int viewHeight; +}; + +class TraceLoggerDialog_t : public QDialog +{ + Q_OBJECT + + public: + TraceLoggerDialog_t(QWidget *parent = 0); + ~TraceLoggerDialog_t(void); + + protected: + QTimer *updateTimer; + QCheckBox *logLastCbox; + QCheckBox *logFileCbox; + QComboBox *logMaxLinesComboBox; + + QCheckBox *autoUpdateCbox; + QCheckBox *logRegCbox; + QCheckBox *logFrameCbox; + QCheckBox *logEmuMsgCbox; + QCheckBox *logProcStatFlagCbox; + QCheckBox *logCyclesCountCbox; + QCheckBox *logBreakpointCbox; + QCheckBox *useStackPointerCbox; + QCheckBox *toLeftDisassemblyCbox; + QCheckBox *logInstrCountCbox; + QCheckBox *logBankNumCbox; + QCheckBox *symTraceEnaCbox; + QCheckBox *logNewMapCodeCbox; + QCheckBox *logNewMapDataCbox; + + QPushButton *selLogFileButton; + QPushButton *startStopButton; + + QTraceLogView *traceView; + QScrollBar *hbar; + QScrollBar *vbar; + + int traceViewCounter; + + void closeEvent(QCloseEvent *bar); + + private: + + public slots: + void closeWindow(void); + private slots: + void updatePeriodic(void); + void toggleLoggingOnOff(void); + void logRegStateChanged(int state); + void logFrameStateChanged(int state); + void logEmuMsgStateChanged(int state); + void symTraceEnaStateChanged(int state); + void logProcStatFlagStateChanged(int state); + void logCyclesCountStateChanged(int state); + void logBreakpointStateChanged(int state); + void useStackPointerStateChanged(int state); + void toLeftDisassemblyStateChanged(int state); + void logInstrCountStateChanged(int state); + void logBankNumStateChanged(int state); + void logNewMapCodeChanged(int state); + void logNewMapDataChanged(int state); + void logMaxLinesChanged(int index); + void hbarChanged(int value); + void vbarChanged(int value); + void openLogFile(void); +}; + +int initTraceLogBuffer( int maxRecs ); + +void openTraceLoggerWindow( QWidget *parent ); diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index a161701c..35d6b0f2 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -259,6 +259,15 @@ InitConfig() config->addOption("hexEditBgColor", "SDL.HexEditBgColor", "#000000"); config->addOption("hexEditFgColor", "SDL.HexEditFgColor", "#FFFFFF"); + // Debugger Options + config->addOption("autoLoadDebugFiles", "SDL.AutoLoadDebugFiles", 1); + config->addOption("autoOpenDebugger" , "SDL.AutoOpenDebugger" , 0); + + // Code Data Logger Options + config->addOption("autoSaveCDL" , "SDL.AutoSaveCDL", 1); + config->addOption("autoLoadCDL" , "SDL.AutoLoadCDL", 1); + config->addOption("autoResumeCDL", "SDL.AutoResumeCDL", 0); + // overwrite the config file? config->addOption("no-config", "SDL.NoConfig", 0); diff --git a/src/drivers/Qt/fceuWrapper.cpp b/src/drivers/Qt/fceuWrapper.cpp index 9fe90965..0f204e16 100644 --- a/src/drivers/Qt/fceuWrapper.cpp +++ b/src/drivers/Qt/fceuWrapper.cpp @@ -15,6 +15,9 @@ #include "Qt/nes_shm.h" #include "Qt/unix-netplay.h" #include "Qt/HexEditor.h" +#include "Qt/SymbolicDebug.h" +#include "Qt/CodeDataLogger.h" +#include "Qt/ConsoleDebugger.h" #include "Qt/ConsoleWindow.h" #include "Qt/fceux_git_info.h" @@ -159,11 +162,6 @@ FCEUD_GetTimeFreq(void) return 1000; } -void FCEUD_DebugBreakpoint( int addr ) -{ - // TODO -} - /** * Initialize all of the subsystem drivers: video, audio, and joystick. */ @@ -215,7 +213,7 @@ DriverKill() */ int LoadGame(const char *path) { - int gg_enabled; + int gg_enabled, autoLoadDebug, autoOpenDebugger; if (isloaded){ CloseGame(); @@ -234,6 +232,24 @@ int LoadGame(const char *path) hexEditorLoadBookmarks(); + g_config->getOption( "SDL.AutoLoadDebugFiles", &autoLoadDebug ); + + if ( autoLoadDebug ) + { + loadGameDebugBreakpoints(); + } + + g_config->getOption( "SDL.AutoOpenDebugger", &autoOpenDebugger ); + + if ( autoOpenDebugger && !debuggerWindowIsOpen() ) + { + consoleWindow->openDebugWindow(); + } + + debugSymbolTable.loadGameSymbols(); + + CDLoggerROMChanged(); + int state_to_load; g_config->getOption("SDL.AutoLoadState", &state_to_load); if (state_to_load >= 0 && state_to_load < 10){ @@ -281,6 +297,12 @@ CloseGame(void) return(0); } hexEditorSaveBookmarks(); + saveGameDebugBreakpoints(); + debuggerClearAllBreakpoints(); + + debugSymbolTable.save(); + debugSymbolTable.clear(); + CDLoggerROMClosed(); int state_to_save; g_config->getOption("SDL.AutoSaveState", &state_to_save); @@ -928,17 +950,23 @@ static void DoFun(int frameskip, int periodic_saves) void fceuWrapperLock(void) { mutexPending++; - consoleWindow->mutex->lock(); + if ( consoleWindow != NULL ) + { + consoleWindow->mutex->lock(); + } mutexPending--; mutexLocks++; } bool fceuWrapperTryLock(int timeout) { - bool lockAcq; + bool lockAcq = false; mutexPending++; - lockAcq = consoleWindow->mutex->tryLock( timeout ); + if ( consoleWindow != NULL ) + { + lockAcq = consoleWindow->mutex->tryLock( timeout ); + } mutexPending--; if ( lockAcq ) @@ -952,7 +980,10 @@ void fceuWrapperUnLock(void) { if ( mutexLocks > 0 ) { - consoleWindow->mutex->unlock(); + if ( consoleWindow != NULL ) + { + consoleWindow->mutex->unlock(); + } mutexLocks--; } else diff --git a/src/drivers/sdl/debugger.cpp b/src/drivers/sdl/debugger.cpp index c5982e24..ea23deea 100644 --- a/src/drivers/sdl/debugger.cpp +++ b/src/drivers/sdl/debugger.cpp @@ -1709,6 +1709,11 @@ void FCEUD_DebugBreakpoint(int bp_num) } } +void FCEUD_TraceInstruction(uint8 *opcode, int size) +{ + // Place holder to allow for compiling. GTK GUI doesn't support this. Qt Does. +} + static void closeDebuggerWindow (GtkWidget * w, GdkEvent * e, debuggerWin_t * dw) { std::list < debuggerWin_t * >::iterator it; diff --git a/src/drivers/win/header_editor.cpp b/src/drivers/win/header_editor.cpp index 797e4b52..d9fba610 100644 --- a/src/drivers/win/header_editor.cpp +++ b/src/drivers/win/header_editor.cpp @@ -628,7 +628,7 @@ void SetHeaderData(HWND hwnd, iNES_HEADER* header) { // Mapper# int mapper = header->ROM_type >> 4 | header->ROM_type2 & 0xF0; if (ines20) - mapper |= (header->ROM_type3 & 0xF0) << 4; + mapper |= (header->ROM_type3 & 0x0F) << 8; sprintf(buf, "%d ", mapper); if (SendDlgItemMessage(hwnd, IDC_MAPPER_COMBO, CB_SELECTSTRING, 0, (LPARAM)buf) == CB_ERR) SetDlgItemText(hwnd, IDC_MAPPER_COMBO, buf); diff --git a/src/ines.cpp b/src/ines.cpp index 00abebeb..b6ef2473 100644 --- a/src/ines.cpp +++ b/src/ines.cpp @@ -1002,7 +1002,7 @@ int iNesSaveAs(const char* name) } //para edit: added function below -char *iNesShortFName() { +char *iNesShortFName(void) { char *ret; if (!(ret = strrchr(LoadedRomFName, '\\'))) diff --git a/src/ines.h b/src/ines.h index 6f9d65e8..5561aef9 100644 --- a/src/ines.h +++ b/src/ines.h @@ -46,6 +46,7 @@ extern uint8 *ExtraNTARAM; extern int iNesSave(void); //bbit Edited: line added extern int iNesSaveAs(const char* name); extern char LoadedRomFName[2048]; //bbit Edited: line added +extern char *iNesShortFName(void); extern const TMasterRomInfo* MasterRomInfo; extern TMasterRomInfoParams MasterRomInfoParams; diff --git a/src/ppu.h b/src/ppu.h index a3d2ded5..4555264d 100644 --- a/src/ppu.h +++ b/src/ppu.h @@ -48,3 +48,7 @@ enum PPUPHASE { }; extern PPUPHASE ppuphase; + +extern unsigned char *cdloggervdata; +extern unsigned int cdloggerVideoDataSize; +extern volatile int rendercount, vromreadcount, undefinedvromcount; diff --git a/web/contact.html b/web/contact.html index e730f709..5e039d9b 100644 --- a/web/contact.html +++ b/web/contact.html @@ -21,29 +21,22 @@
- - - - - + + + + + +

Contact

diff --git a/web/documentation.html b/web/documentation.html index 86b5f128..af76c791 100644 --- a/web/documentation.html +++ b/web/documentation.html @@ -29,34 +29,25 @@ Links
-
- - - - - - - -
-
+
+ + + + + + +
+

Getting Started with FCEUX

Tips for performing basic functions

Playing games

diff --git a/web/download.html b/web/download.html index ea94a569..25b841cc 100644 --- a/web/download.html +++ b/web/download.html @@ -20,32 +20,24 @@ Links
-
- - - - - - -
+
+ + + + + + +

Downloads

The FCEUX team maintains two ports: SDL and Win32. diff --git a/web/links.html b/web/links.html index fe5586a0..ff63b798 100644 --- a/web/links.html +++ b/web/links.html @@ -21,18 +21,22 @@

- - - + + + + + +

FCEUX Users

diff --git a/web/movies.html b/web/movies.html index 9f825a98..cff79a6e 100644 --- a/web/movies.html +++ b/web/movies.html @@ -29,30 +29,23 @@
- + + + + + - - - - -
+

Movies

Introduction

diff --git a/web/osx.html b/web/osx.html index cb6670c6..66555757 100644 --- a/web/osx.html +++ b/web/osx.html @@ -31,33 +31,24 @@ Links
-
- - - - - - - -
+
+ + + + + + +
To run FCEUX in Mac OSX, you must have the following library dependencies installed on your system:

diff --git a/web/pressrelease-2.2.3.html b/web/pressrelease-2.2.3.html index bd7cff5e..d534a6bf 100644 --- a/web/pressrelease-2.2.3.html +++ b/web/pressrelease-2.2.3.html @@ -21,18 +21,23 @@

- - -
+ + + + + + +

FCEUX 2.2.3 Release

28 July 2016

diff --git a/web/version.html b/web/version.html index 99d653d1..ba947d8e 100644 --- a/web/version.html +++ b/web/version.html @@ -27,29 +27,22 @@
- - - - - + + + + + +

Version History