Merge pull request #23 from TASVideos/master

Sync code to the newest
This commit is contained in:
owomomo 2020-10-05 07:29:50 +08:00 committed by GitHub
commit e0aa1e1edc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 7332 additions and 218 deletions

2
README
View File

@ -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:

View File

@ -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 |

View File

@ -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

View File

@ -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
}

View File

@ -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);

View File

@ -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 );

View File

@ -0,0 +1,840 @@
// CodeDataLogger.cpp
//
#include <QDir>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#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);
}
//----------------------------------------------------

View File

@ -0,0 +1,73 @@
// CodeDataLogger.h
//
#pragma once
#include <QWidget>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QPushButton>
#include <QLabel>
#include <QFrame>
#include <QTimer>
#include <QGroupBox>
#include <QCloseEvent>
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);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,285 @@
// ConsoleDebugger.h
//
#pragma once
#include <QWidget>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QGroupBox>
#include <QPushButton>
#include <QFont>
#include <QLabel>
#include <QTimer>
#include <QFrame>
#include <QGroupBox>
#include <QTreeView>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QLineEdit>
#include <QTextEdit>
#include <QPlainTextEdit>
#include <QScrollBar>
#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 <int, debuggerBookmark_t*> bmMap;
std::map <int, debuggerBookmark_t*>::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 <dbg_asm_entry_t*> 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;

View File

@ -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;
}
//---------------------------------------------------------------------------

View File

@ -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 );

View File

@ -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() );

View File

@ -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);

View File

@ -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 <HexEditorDialog_t*> 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;
}
//----------------------------------------------------------------------------

View File

@ -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 );

View File

@ -1,4 +1,4 @@
// GamePadConf.h
// HotKeyConf.h
//
#pragma once

View File

@ -0,0 +1,568 @@
// SymbolicDebug.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <int, debugSymbol_t*>::iterator it;
for (it=symMap.begin(); it!=symMap.end(); it++)
{
delete it->second;
}
symMap.clear();
}
//--------------------------------------------------------------
int debugSymbolPage_t::addSymbol( debugSymbol_t*sym )
{
std::map <int, debugSymbol_t*>::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 <int, debugSymbol_t*>::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 <int, debugSymbol_t*>::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 <int, debugSymbol_t*>::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 <int, debugSymbolPage_t*>::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<<debuggerPageSize);
//nPages = 1<<(15-debuggerPageSize);
nPages = romSize / pageSize;
printf("RomSize: %i NumPages: %i \n", romSize, nPages );
for(int i=0;i<nPages;i++)
{
//printf("Loading Page Offset: $%06X\n", pageSize*i );
loadFileNL( i );
}
print();
return 0;
}
//--------------------------------------------------------------
int debugSymbolTable_t::addSymbolAtBankOffset( int bank, int ofs, debugSymbol_t *sym )
{
debugSymbolPage_t *page;
std::map <int, debugSymbolPage_t*>::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 <int, debugSymbolPage_t*>::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 <int, debugSymbolPage_t*>::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 <int, debugSymbolPage_t*>::iterator it;
for (it=pageMap.begin(); it!=pageMap.end(); it++)
{
page = it->second;
page->print();
}
}
//--------------------------------------------------------------

View File

@ -0,0 +1,76 @@
// SymbolicDebug.h
//
#ifndef __SYMBOLIC_DEBUG_H__
#define __SYMBOLIC_DEBUG_H__
#include <string>
#include <list>
#include <map>
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 <int, debugSymbol_t*> 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 <int, debugSymbolPage_t*> pageMap;
};
extern debugSymbolTable_t debugSymbolTable;
//struct MemoryMappedRegister
//{
// char* offset;
// char* name;
//};
int generateNLFilenameForBank(int bank, char *NLfilename);
int generateNLFilenameForAddress(int address, char *NLfilename);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,153 @@
// TraceLogger.h
//
#pragma once
#include <QWidget>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QPushButton>
#include <QLabel>
#include <QFrame>
#include <QTimer>
#include <QGroupBox>
#include <QScrollBar>
#include <QCloseEvent>
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 );

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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, '\\')))

View File

@ -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;

View File

@ -48,3 +48,7 @@ enum PPUPHASE {
};
extern PPUPHASE ppuphase;
extern unsigned char *cdloggervdata;
extern unsigned int cdloggerVideoDataSize;
extern volatile int rendercount, vromreadcount, undefinedvromcount;

View File

@ -21,29 +21,22 @@
</div>
<div id="container">
<div id="left_col">
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<div id="page_content">
<h1>Contact</h1>

View File

@ -29,34 +29,25 @@
<a href="links.html">Links</a>
</div>
<div id="container">
<div id="left_col">
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<div id="page_content">
<div id="left_col">
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<div id="page_content">
<h1>Getting Started with FCEUX</h1>
<p>Tips for performing basic functions</p>
<h2>Playing games</h2>

View File

@ -20,32 +20,24 @@
<a href="links.html">Links</a>
</div>
<div id="container">
<div id="left_col">
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<div id="left_col">
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<div id="page_content">
<h1>Downloads</h1>
<p>The FCEUX team maintains two ports: SDL and Win32.

View File

@ -21,18 +21,22 @@
</div>
<div id="container">
<div id="left_col">
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<div id="page_content">
<h1>FCEUX Users</h1>

View File

@ -29,30 +29,23 @@
</div>
<div id="container">
<div id="left_col">
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div>
<div id="page_content">
<h1>Movies</h1>
<h2>Introduction</h2>

View File

@ -31,33 +31,24 @@
<a href="links.html">Links</a>
</div>
<div id="container">
<div id="left_col">
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<div id="left_col">
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<div id="page_content">
To run FCEUX in Mac OSX, you must have the following library dependencies installed on your system:
<p>

View File

@ -21,18 +21,23 @@
</div>
<div id="container">
<div id="left_col">
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<div id="page_content">
<h1>FCEUX 2.2.3 Release</h1>
<p>28 July 2016</p>

View File

@ -27,29 +27,22 @@
</div>
<div id="container">
<div id="left_col">
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script type="text/javascript"><!--
google_ad_client = "pub-0764229755985190";
/* 180x150, created 5/22/10 */
google_ad_slot = "7648866201";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;width:180px;height:150px"
data-ad-client="ca-pub-0764229755985190"
data-ad-slot="7648866201"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<div id="page_content">
<h1>Version History</h1>