From 1f12627c8ff2e2aca4d2bf794576f5859bf78bb4 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Mon, 5 Oct 2020 20:52:09 -0400 Subject: [PATCH 01/12] Added initial file for Qt name table viewer --- src/CMakeLists.txt | 1 + src/drivers/Qt/ConsoleWindow.cpp | 16 ++++ src/drivers/Qt/ConsoleWindow.h | 2 + src/drivers/Qt/NameTableViewer.cpp | 133 +++++++++++++++++++++++++++++ src/drivers/Qt/NameTableViewer.h | 73 ++++++++++++++++ 5 files changed, 225 insertions(+) create mode 100644 src/drivers/Qt/NameTableViewer.cpp create mode 100644 src/drivers/Qt/NameTableViewer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fcf5be14..d8083ce5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -442,6 +442,7 @@ set(SRC_DRIVERS_SDL ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/AboutWindow.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/fceuWrapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/ppuViewer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/NameTableViewer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/config.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/input.cpp ${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/nes_shm.cpp diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 490566c4..71e206a9 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -34,6 +34,7 @@ #include "Qt/AboutWindow.h" #include "Qt/fceuWrapper.h" #include "Qt/ppuViewer.h" +#include "Qt/NameTableViewer.h" #include "Qt/keyscan.h" #include "Qt/nes_shm.h" @@ -510,6 +511,14 @@ void consoleWin_t::createMainMenu(void) debugMenu->addAction(ppuViewAct); + // Debug -> Name Table Viewer + ntViewAct = new QAction(tr("Name Table Viewer..."), this); + //ntViewAct->setShortcut( QKeySequence(tr("Shift+F7"))); + ntViewAct->setStatusTip(tr("Open Name Table Viewer")); + connect(ntViewAct, SIGNAL(triggered()), this, SLOT(openNTViewer(void)) ); + + debugMenu->addAction(ntViewAct); + // Debug -> Trace Logger traceLogAct = new QAction(tr("Trace Logger..."), this); //traceLogAct->setShortcut( QKeySequence(tr("Shift+F7"))); @@ -1037,6 +1046,13 @@ void consoleWin_t::openPPUViewer(void) openPPUViewWindow(this); } +void consoleWin_t::openNTViewer(void) +{ + //printf("Open GUI Name Table Viewer Window\n"); + + openNameTableViewWindow(this); +} + void consoleWin_t::openCodeDataLogger(void) { CodeDataLoggerDialog_t *cdlWin; diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index 7699b3ed..cf082440 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -94,6 +94,7 @@ class consoleWin_t : public QMainWindow QAction *traceLogAct; QAction *hexEditAct; QAction *ppuViewAct; + QAction *ntViewAct; QAction *openMovAct; QAction *stopMovAct; QAction *recMovAct; @@ -164,6 +165,7 @@ class consoleWin_t : public QMainWindow void fdsEjectDisk(void); void fdsLoadBiosFile(void); void openPPUViewer(void); + void openNTViewer(void); void openCheats(void); void openMovie(void); void stopMovie(void); diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp new file mode 100644 index 00000000..6dab16c7 --- /dev/null +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -0,0 +1,133 @@ +// NameTableViewer.cpp +#include +#include + +#include +#include +#include +#include + +#include "../../types.h" +#include "../../fceu.h" +#include "../../cart.h" +#include "../../ppu.h" +#include "../../debug.h" +#include "../../palette.h" + +#include "Qt/NameTableViewer.h" +#include "Qt/main.h" +#include "Qt/dface.h" +#include "Qt/input.h" +#include "Qt/config.h" +#include "Qt/fceuWrapper.h" + +static ppuNameTableViewerDialog_t *nameTableViewWindow = NULL; + +//---------------------------------------------------- +int openNameTableViewWindow( QWidget *parent ) +{ + if ( nameTableViewWindow != NULL ) + { + return -1; + } + + nameTableViewWindow = new ppuNameTableViewerDialog_t(parent); + + nameTableViewWindow->show(); + + return 0; +} +//---------------------------------------------------- +ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) + : QDialog( parent ) +{ + QVBoxLayout *mainLayout, *vbox; + QHBoxLayout *hbox; + QGridLayout *grid; + char stmp[64]; + + nameTableViewWindow = this; + + setWindowTitle( tr("Name Table Viewer") ); + + mainLayout = new QVBoxLayout(); + + setLayout( mainLayout ); +} +//---------------------------------------------------- +ppuNameTableViewerDialog_t::~ppuNameTableViewerDialog_t(void) +{ + nameTableViewWindow = NULL; + + printf("Name Table Viewer Window Deleted\n"); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::closeEvent(QCloseEvent *event) +{ + printf("Name Table Viewer Close Window Event\n"); + done(0); + deleteLater(); + event->accept(); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::closeWindow(void) +{ + printf("Close Window\n"); + done(0); + deleteLater(); +} +//---------------------------------------------------- +ppuNameTableView_t::ppuNameTableView_t(QWidget *parent) + : QWidget(parent) +{ + this->setFocusPolicy(Qt::StrongFocus); + this->setMouseTracking(true); + setMinimumWidth( 256 ); + setMinimumHeight( 256 ); + viewWidth = 256; + viewHeight = 256; +} +//---------------------------------------------------- +ppuNameTableView_t::~ppuNameTableView_t(void) +{ + +} +//---------------------------------------------------- +void ppuNameTableView_t::resizeEvent(QResizeEvent *event) +{ + viewWidth = event->size().width(); + viewHeight = event->size().height(); +} +//---------------------------------------------------- +void ppuNameTableView_t::mouseMoveEvent(QMouseEvent *event) +{ + //QPoint tile = convPixToTile( event->pos() ); + + //if ( (tile.x() < 16) && (tile.y() < 16) ) + //{ + // char stmp[64]; + // sprintf( stmp, "Tile: $%X%X", tile.y(), tile.x() ); + // tileLabel->setText( tr(stmp) ); + //} +} +//---------------------------------------------------------------------------- +void ppuNameTableView_t::mousePressEvent(QMouseEvent * event) +{ + //QPoint tile = convPixToTile( event->pos() ); + + if ( event->button() == Qt::LeftButton ) + { + } + else if ( event->button() == Qt::RightButton ) + { + } +} +//---------------------------------------------------- +void ppuNameTableView_t::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + viewWidth = event->rect().width(); + viewHeight = event->rect().height(); + +} +//---------------------------------------------------- diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h new file mode 100644 index 00000000..c5de8aee --- /dev/null +++ b/src/drivers/Qt/NameTableViewer.h @@ -0,0 +1,73 @@ +// NameTableViewer.h + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ppuNameTable_t +{ + struct + { + struct + { + QColor color; + } pixel[8][8]; + + int x; + int y; + + } tile[30][32]; + + int w; + int h; +}; + +class ppuNameTableView_t : public QWidget +{ + Q_OBJECT + + public: + ppuNameTableView_t( QWidget *parent = 0); + ~ppuNameTableView_t(void); + + protected: + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent * event); + + int viewWidth; + int viewHeight; +}; + +class ppuNameTableViewerDialog_t : public QDialog +{ + Q_OBJECT + + public: + ppuNameTableViewerDialog_t(QWidget *parent = 0); + ~ppuNameTableViewerDialog_t(void); + + protected: + void closeEvent(QCloseEvent *bar); + + public slots: + void closeWindow(void); + private slots: +}; + +int openNameTableViewWindow( QWidget *parent ); + From 579a8d215938c52cbc879a271349fff6c0792c16 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Mon, 5 Oct 2020 21:14:49 -0400 Subject: [PATCH 02/12] Initial layout of Qt Name Table window in work. --- src/drivers/Qt/NameTableViewer.cpp | 44 +++++++++++++++++++++++++++--- src/drivers/Qt/NameTableViewer.h | 7 +++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 6dab16c7..6940586c 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -44,6 +44,7 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) QVBoxLayout *mainLayout, *vbox; QHBoxLayout *hbox; QGridLayout *grid; + QGroupBox *frame; char stmp[64]; nameTableViewWindow = this; @@ -53,6 +54,41 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) mainLayout = new QVBoxLayout(); setLayout( mainLayout ); + + vbox = new QVBoxLayout(); + frame = new QGroupBox( tr("Name Tables") ); + ntView = new ppuNameTableView_t(this); + grid = new QGridLayout(); + + vbox->addWidget( ntView ); + frame->setLayout( vbox ); + mainLayout->addWidget( frame, 100 ); + mainLayout->addLayout( grid , 1 ); + + showScrollLineCbox = new QCheckBox( tr("Show Scroll Lines") ); + showAttrbCbox = new QCheckBox( tr("Show Attributes") ); + ignorePaletteCbox = new QCheckBox( tr("Ignore Palette") ); + + grid->addWidget( showScrollLineCbox, 0, 0, Qt::AlignLeft ); + grid->addWidget( showAttrbCbox , 1, 0, Qt::AlignLeft ); + grid->addWidget( ignorePaletteCbox , 2, 0, Qt::AlignLeft ); + + hbox = new QHBoxLayout(); + refreshSlider = new QSlider( Qt::Horizontal ); + hbox->addWidget( new QLabel( tr("Refresh: More") ) ); + hbox->addWidget( refreshSlider ); + hbox->addWidget( new QLabel( tr("Less") ) ); + grid->addLayout( hbox, 0, 1, Qt::AlignRight ); + + refreshSlider->setMinimum( 0); + refreshSlider->setMaximum(25); + refreshSlider->setValue(1); + + hbox = new QHBoxLayout(); + scanLineEdit = new QLineEdit(); + hbox->addWidget( new QLabel( tr("Display on Scanline:") ) ); + hbox->addWidget( scanLineEdit ); + grid->addLayout( hbox, 1, 1, Qt::AlignRight ); } //---------------------------------------------------- ppuNameTableViewerDialog_t::~ppuNameTableViewerDialog_t(void) @@ -82,10 +118,10 @@ ppuNameTableView_t::ppuNameTableView_t(QWidget *parent) { this->setFocusPolicy(Qt::StrongFocus); this->setMouseTracking(true); - setMinimumWidth( 256 ); - setMinimumHeight( 256 ); - viewWidth = 256; - viewHeight = 256; + viewWidth = 240 * 2; + viewHeight = 256 * 2; + setMinimumWidth( viewWidth ); + setMinimumHeight( viewHeight ); } //---------------------------------------------------- ppuNameTableView_t::~ppuNameTableView_t(void) diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h index c5de8aee..0e473967 100644 --- a/src/drivers/Qt/NameTableViewer.h +++ b/src/drivers/Qt/NameTableViewer.h @@ -64,6 +64,13 @@ class ppuNameTableViewerDialog_t : public QDialog protected: void closeEvent(QCloseEvent *bar); + ppuNameTableView_t *ntView; + QCheckBox *showScrollLineCbox; + QCheckBox *showAttrbCbox; + QCheckBox *ignorePaletteCbox; + QSlider *refreshSlider; + QLineEdit *scanLineEdit; + public slots: void closeWindow(void); private slots: From 73726114c59d3baeab4a83761845c383cf230fe7 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Mon, 5 Oct 2020 22:49:09 -0400 Subject: [PATCH 03/12] Name table view first successful drawing on Qt GUI. --- src/drivers/Qt/NameTableViewer.cpp | 350 ++++++++++++++++++++++++++++- src/drivers/Qt/NameTableViewer.h | 27 ++- src/ppu.cpp | 4 - 3 files changed, 365 insertions(+), 16 deletions(-) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 6940586c..217857ac 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -11,6 +11,7 @@ #include "../../fceu.h" #include "../../cart.h" #include "../../ppu.h" +#include "../../ines.h" #include "../../debug.h" #include "../../palette.h" @@ -22,7 +23,50 @@ #include "Qt/fceuWrapper.h" static ppuNameTableViewerDialog_t *nameTableViewWindow = NULL; +static uint8_t palcache[36]; //palette cache +static int NTViewScanline = 0; +static int NTViewSkip = 100; +static int NTViewRefresh = 1; +static int chrchanged = 0; +static int xpos = 0, ypos = 0; +//static int scrolllines = 1; +static int attview = 0; +static int hidepal = 0; +static bool redrawtables = true; + +// checkerboard tile for attribute view +static const uint8_t ATTRIBUTE_VIEW_TILE[16] = { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF }; + + +static class NTCache +{ +public: + NTCache(void) + : curr_vnapage(0) + {} + + uint8_t* curr_vnapage; + uint8_t* bitmap; + uint8_t cache[0x400]; + //HDC hdc; + //HBITMAP hbmp; + //HGDIOBJ tmpobj; +} cache[4]; + +static ppuNameTable_t nameTable[4]; + +enum NT_MirrorType +{ + NT_NONE = -1, + NT_HORIZONTAL, NT_VERTICAL, NT_FOUR_SCREEN, + NT_SINGLE_SCREEN_TABLE_0, NT_SINGLE_SCREEN_TABLE_1, + NT_SINGLE_SCREEN_TABLE_2, NT_SINGLE_SCREEN_TABLE_3, + NT_NUM_MIRROR_TYPES +}; +static NT_MirrorType ntmirroring, oldntmirroring = NT_NONE; + +static void initNameTableViewer(void); //---------------------------------------------------- int openNameTableViewWindow( QWidget *parent ) { @@ -30,6 +74,7 @@ int openNameTableViewWindow( QWidget *parent ) { return -1; } + initNameTableViewer(); nameTableViewWindow = new ppuNameTableViewerDialog_t(parent); @@ -82,13 +127,24 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) refreshSlider->setMinimum( 0); refreshSlider->setMaximum(25); - refreshSlider->setValue(1); + refreshSlider->setValue(NTViewRefresh); + + connect( refreshSlider, SIGNAL(valueChanged(int)), this, SLOT(refreshSliderChanged(int))); hbox = new QHBoxLayout(); scanLineEdit = new QLineEdit(); hbox->addWidget( new QLabel( tr("Display on Scanline:") ) ); hbox->addWidget( scanLineEdit ); grid->addLayout( hbox, 1, 1, Qt::AlignRight ); + + scanLineEdit->setMaxLength( 3 ); + scanLineEdit->setInputMask( ">900;" ); + sprintf( stmp, "%i", NTViewScanline ); + scanLineEdit->setText( tr(stmp) ); + + connect( scanLineEdit, SIGNAL(textEdited(const QString &)), this, SLOT(scanLineChanged(const QString &))); + + FCEUD_UpdateNTView( -1, true); } //---------------------------------------------------- ppuNameTableViewerDialog_t::~ppuNameTableViewerDialog_t(void) @@ -113,6 +169,24 @@ void ppuNameTableViewerDialog_t::closeWindow(void) deleteLater(); } //---------------------------------------------------- +void ppuNameTableViewerDialog_t::scanLineChanged( const QString &txt ) +{ + std::string s; + + s = txt.toStdString(); + + if ( s.size() > 0 ) + { + NTViewScanline = strtoul( s.c_str(), NULL, 10 ); + } + //printf("ScanLine: '%s' %i\n", s.c_str(), PPUViewScanline ); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::refreshSliderChanged(int value) +{ + NTViewRefresh = value; +} +//---------------------------------------------------- ppuNameTableView_t::ppuNameTableView_t(QWidget *parent) : QWidget(parent) { @@ -161,9 +235,283 @@ void ppuNameTableView_t::mousePressEvent(QMouseEvent * event) //---------------------------------------------------- void ppuNameTableView_t::paintEvent(QPaintEvent *event) { + ppuNameTable_t *nt; + int n,i,j,ii,jj,w,h,x,y,xx,yy; QPainter painter(this); viewWidth = event->rect().width(); viewHeight = event->rect().height(); + w = viewWidth / (240*2); + h = viewHeight / (256*2); + + xx = 0; yy = 0; + + for (n=0; n<4; n++) + { + nt = &nameTable[n]; + + xx = (n%2) * (viewWidth / 2); + yy = (n/2) * (viewHeight / 2); + //xx = 0; + //yy = 0; + + for (j=0; j<30; j++) + { + jj = (j*8)+yy; + + for (i=0; i<32; i++) + { + ii = (i*8)+xx; + + for (y=0; y<8; y++) + { + for (x=0; x<8; x++) + { + painter.fillRect( ii+x, jj+y, w, h, nt->tile[j][i].pixel[y][x].color ); + } + } + } + } + } +} +//---------------------------------------------------- +static void initNameTableViewer(void) +{ + //clear cache + memset(palcache,0,32); + + // forced palette (e.g. for debugging nametables when palettes are all-black) + palcache[(8*4)+0] = 0x0F; + palcache[(8*4)+1] = 0x00; + palcache[(8*4)+2] = 0x10; + palcache[(8*4)+3] = 0x20; + +} +//---------------------------------------------------- +static void ChangeMirroring(void) +{ + switch (ntmirroring) + { + case NT_HORIZONTAL: + vnapage[0] = vnapage[1] = &NTARAM[0x000]; + vnapage[2] = vnapage[3] = &NTARAM[0x400]; + break; + case NT_VERTICAL: + vnapage[0] = vnapage[2] = &NTARAM[0x000]; + vnapage[1] = vnapage[3] = &NTARAM[0x400]; + break; + case NT_FOUR_SCREEN: + vnapage[0] = &NTARAM[0x000]; + vnapage[1] = &NTARAM[0x400]; + if(ExtraNTARAM) + { + vnapage[2] = ExtraNTARAM; + vnapage[3] = ExtraNTARAM + 0x400; + } + break; + case NT_SINGLE_SCREEN_TABLE_0: + vnapage[0] = vnapage[1] = vnapage[2] = vnapage[3] = &NTARAM[0x000]; + break; + case NT_SINGLE_SCREEN_TABLE_1: + vnapage[0] = vnapage[1] = vnapage[2] = vnapage[3] = &NTARAM[0x400]; + break; + case NT_SINGLE_SCREEN_TABLE_2: + if(ExtraNTARAM) + vnapage[0] = vnapage[1] = vnapage[2] = vnapage[3] = ExtraNTARAM; + break; + case NT_SINGLE_SCREEN_TABLE_3: + if(ExtraNTARAM) + vnapage[0] = vnapage[1] = vnapage[2] = vnapage[3] = ExtraNTARAM + 0x400; + break; + default: + case NT_NONE: + break; + } + return; +} +//---------------------------------------------------- +inline void DrawChr( ppuNameTableTile_t *tile, const uint8_t *chr, int pal) +{ + int y, x, tmp, index=0, p=0; + uint8 chr0, chr1; + //uint8 *table = &VPage[0][0]; //use the background table + //pbitmap += 3* + + for (y = 0; y < 8; y++) { //todo: use index for y? + chr0 = chr[index]; + chr1 = chr[index+8]; + tmp=7; + for (x = 0; x < 8; x++) { //todo: use tmp for x? + p = (chr0>>tmp)&1; + p |= ((chr1>>tmp)&1)<<1; + p = palcache[p+(pal*4)]; + tmp--; + + tile->pixel[y][x].color.setBlue( palo[p].b ); + tile->pixel[y][x].color.setGreen( palo[p].g ); + tile->pixel[y][x].color.setRed( palo[p].r ); + } + index++; + //pbitmap += (NTWIDTH*3)-24; + } + //index+=8; + //pbitmap -= (((PALETTEBITWIDTH>>2)<<3)-24); +} +//---------------------------------------------------- +static void DrawNameTable(int scanline, int ntnum, bool invalidateCache) +{ + NTCache &c = cache[ntnum]; + uint8_t *tablecache = c.cache; + + uint8_t *table = vnapage[ntnum]; + if (table == NULL) + { + table = vnapage[ntnum&1]; + } + + int a, ptable=0; + + if (PPU[0]&0x10){ //use the correct pattern table based on this bit + ptable=0x1000; + } + + bool invalid = invalidateCache; + //if we werent asked to invalidate the cache, maybe we need to invalidate it anyway due to vnapage changing + if (!invalid) + { + invalid = (c.curr_vnapage != vnapage[ntnum]); + } + c.curr_vnapage = vnapage[ntnum]; + + //HACK: never cache anything + invalid = true; + + for (int y=0;y<30;y++) + { + for (int x=0;x<32;x++) + { + int ntaddr = (y*32)+x; + int attraddr = 0x3C0+((y>>2)<<3)+(x>>2); + if (invalid + || (table[ntaddr] != tablecache[ntaddr]) + || (table[attraddr] != tablecache[attraddr])) { + redrawtables = true; + int temp = (((y&2)<<1)+(x&2)); + a = (table[attraddr] & (3<> temp; + + //the commented out code below is all allegedly equivalent to the single line above: + //tmpx = x>>2; + //tmpy = y>>2; + //a = 0x3C0+(tmpy*8)+tmpx; + //if((((x>>1)&1) == 0) && (((y>>1)&1) == 0)) a = table[a]&0x3; + //if((((x>>1)&1) == 1) && (((y>>1)&1) == 0)) a = (table[a]&0xC)>>2; + //if((((x>>1)&1) == 0) && (((y>>1)&1) == 1)) a = (table[a]&0x30)>>4; + //if((((x>>1)&1) == 1) && (((y>>1)&1) == 1)) a = (table[a]&0xC0)>>6; + + int chr = table[ntaddr]*16; + + extern int FCEUPPU_GetAttr(int ntnum, int xt, int yt); + + //test.. instead of pretending that the nametable is a screen at 0,0 we pretend that it is at the current xscroll and yscroll + //int xpos = ((RefreshAddr & 0x400) >> 2) | ((RefreshAddr & 0x1F) << 3) | XOffset; + //int ypos = ((RefreshAddr & 0x3E0) >> 2) | ((RefreshAddr & 0x7000) >> 12); + //if(RefreshAddr & 0x800) ypos += 240; + //int refreshaddr = (xpos/8+x)+(ypos/8+y)*32; + + int refreshaddr = (x)+(y)*32; + + a = FCEUPPU_GetAttr(ntnum,x,y); + if (hidepal) a = 8; + + const uint8* chrp = FCEUPPU_GetCHR(ptable+chr,refreshaddr); + if (attview) chrp = ATTRIBUTE_VIEW_TILE; + + //a good way to do it: + DrawChr( &nameTable[ntnum].tile[y][x], chrp, a); + + tablecache[ntaddr] = table[ntaddr]; + tablecache[attraddr] = table[attraddr]; + //one could comment out the line above... + //since there are so many fewer attribute values than NT values, it might be best just to refresh the whole attr table below with the memcpy + + //obviously this whole scheme of nt cache doesnt work if an mmc5 game is playing tricks with the attribute table + } + //pbitmap += (8*3); + } + //pbitmap += 7*((NTWIDTH*3)); + } + + //this copies the attribute tables to the cache if needed. but we arent using it now because + //if(redrawtables){ + // memcpy(tablecache+0x3c0,table+0x3c0,0x40); + //} +} +//---------------------------------------------------- +void FCEUD_UpdateNTView(int scanline, bool drawall) +{ + if (nameTableViewWindow == 0) + { + return; + } + if ( (scanline != -1) && (scanline != NTViewScanline) ) + { + return; + } + + //uint8 *pbitmap = ppuv_palette; + //if (!hNTView) return; + + ppu_getScroll(xpos,ypos); + + + if (NTViewSkip < NTViewRefresh) + { + NTViewSkip++; + return; + } + NTViewSkip = 0; + + if (chrchanged) + { + drawall = 1; + } + + //update palette only if required + if (memcmp(palcache,PALRAM,32) != 0) + { + memcpy(palcache,PALRAM,32); + drawall = 1; //palette has changed, so redraw all + } + + ntmirroring = NT_NONE; + if(vnapage[0] == vnapage[1])ntmirroring = NT_HORIZONTAL; + if(vnapage[0] == vnapage[2])ntmirroring = NT_VERTICAL; + if((vnapage[0] != vnapage[1]) && (vnapage[0] != vnapage[2]))ntmirroring = NT_FOUR_SCREEN; + + if((vnapage[0] == vnapage[1]) && (vnapage[1] == vnapage[2]) && (vnapage[2] == vnapage[3])){ + if(vnapage[0] == &NTARAM[0x000])ntmirroring = NT_SINGLE_SCREEN_TABLE_0; + if(vnapage[0] == &NTARAM[0x400])ntmirroring = NT_SINGLE_SCREEN_TABLE_1; + if(vnapage[0] == ExtraNTARAM)ntmirroring = NT_SINGLE_SCREEN_TABLE_2; + if(vnapage[0] == ExtraNTARAM+0x400)ntmirroring = NT_SINGLE_SCREEN_TABLE_3; + } + + if (oldntmirroring != ntmirroring) + { + //UpdateMirroringButtons(); + oldntmirroring = ntmirroring; + } + + for (int i=0;i<4;i++) + { + DrawNameTable(scanline,i,drawall); + } + + chrchanged = 0; + + if ( nameTableViewWindow ) + { + nameTableViewWindow->update(); + } + return; } //---------------------------------------------------- diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h index 0e473967..aca06453 100644 --- a/src/drivers/Qt/NameTableViewer.h +++ b/src/drivers/Qt/NameTableViewer.h @@ -17,19 +17,22 @@ #include #include +struct ppuNameTablePixel_t +{ + QColor color; +}; + +struct ppuNameTableTile_t +{ + struct ppuNameTablePixel_t pixel[8][8]; + + int x; + int y; +}; + struct ppuNameTable_t { - struct - { - struct - { - QColor color; - } pixel[8][8]; - - int x; - int y; - - } tile[30][32]; + struct ppuNameTableTile_t tile[30][32]; int w; int h; @@ -74,6 +77,8 @@ class ppuNameTableViewerDialog_t : public QDialog public slots: void closeWindow(void); private slots: + void refreshSliderChanged(int value); + void scanLineChanged( const QString &txt ); }; int openNameTableViewWindow( QWidget *parent ); diff --git a/src/ppu.cpp b/src/ppu.cpp index 712e72ee..0d7451dc 100644 --- a/src/ppu.cpp +++ b/src/ppu.cpp @@ -1375,9 +1375,7 @@ static void DoLine(void) { GameHBIRQHook(); } -#ifdef WIN32 DEBUG(FCEUD_UpdateNTView(scanline, 0)); -#endif if (SpriteON) RefreshSprites(); @@ -2152,9 +2150,7 @@ int FCEUX_PPU_Loop(int skip) { if (sl != 0 && sl < 241) // ignore the invisible { DEBUG(FCEUD_UpdatePPUView(scanline = yp, 1)); -#ifdef WIN32 DEBUG(FCEUD_UpdateNTView(scanline = yp, 1)); -#endif } //hack to fix SDF ship intro screen with split. is it right? From 03cd068439e2ea6876db6c1535fb024466b4a7c9 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Mon, 5 Oct 2020 23:17:27 -0400 Subject: [PATCH 04/12] Qt Name table Viewer in work. --- src/drivers/Qt/NameTableViewer.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 217857ac..4d838ba7 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -30,13 +30,12 @@ static int NTViewRefresh = 1; static int chrchanged = 0; static int xpos = 0, ypos = 0; -//static int scrolllines = 1; static int attview = 0; static int hidepal = 0; static bool redrawtables = true; // checkerboard tile for attribute view -static const uint8_t ATTRIBUTE_VIEW_TILE[16] = { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF }; +static const uint8_t ATTRIBUTE_VIEW_TILE[16] = { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF }; static class NTCache @@ -47,11 +46,7 @@ public: {} uint8_t* curr_vnapage; - uint8_t* bitmap; uint8_t cache[0x400]; - //HDC hdc; - //HBITMAP hbmp; - //HGDIOBJ tmpobj; } cache[4]; static ppuNameTable_t nameTable[4]; @@ -192,8 +187,8 @@ ppuNameTableView_t::ppuNameTableView_t(QWidget *parent) { this->setFocusPolicy(Qt::StrongFocus); this->setMouseTracking(true); - viewWidth = 240 * 2; - viewHeight = 256 * 2; + viewWidth = 256 * 2; + viewHeight = 240 * 2; setMinimumWidth( viewWidth ); setMinimumHeight( viewHeight ); } @@ -207,6 +202,8 @@ void ppuNameTableView_t::resizeEvent(QResizeEvent *event) { viewWidth = event->size().width(); viewHeight = event->size().height(); + + //printf("%ix%i\n", viewWidth, viewHeight ); } //---------------------------------------------------- void ppuNameTableView_t::mouseMoveEvent(QMouseEvent *event) @@ -241,8 +238,10 @@ void ppuNameTableView_t::paintEvent(QPaintEvent *event) viewWidth = event->rect().width(); viewHeight = event->rect().height(); - w = viewWidth / (240*2); - h = viewHeight / (256*2); + w = viewWidth / (256*2); + h = viewHeight / (240*2); + + //printf("%ix%i\n", viewWidth, viewHeight ); xx = 0; yy = 0; @@ -252,22 +251,20 @@ void ppuNameTableView_t::paintEvent(QPaintEvent *event) xx = (n%2) * (viewWidth / 2); yy = (n/2) * (viewHeight / 2); - //xx = 0; - //yy = 0; for (j=0; j<30; j++) { - jj = (j*8)+yy; + jj = (j*8); for (i=0; i<32; i++) { - ii = (i*8)+xx; + ii = (i*8); for (y=0; y<8; y++) { for (x=0; x<8; x++) { - painter.fillRect( ii+x, jj+y, w, h, nt->tile[j][i].pixel[y][x].color ); + painter.fillRect( xx+(ii+x)*w, yy+(jj+y)*h, w, h, nt->tile[j][i].pixel[y][x].color ); } } } From 826a39c83e2cf35b0cd2221858ca46a52db8394d Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Tue, 6 Oct 2020 18:40:14 -0400 Subject: [PATCH 05/12] Added logic to not call window update from emulator thread in Qt GUI. Qt widget update() should always be called from within main gui thread. --- src/drivers/Qt/NameTableViewer.cpp | 27 ++++++++++++++++++++------- src/drivers/Qt/NameTableViewer.h | 2 ++ src/drivers/Qt/ppuViewer.cpp | 22 ++++++++++++++++++---- src/drivers/Qt/ppuViewer.h | 2 ++ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 4d838ba7..98f0c460 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -140,10 +140,17 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) connect( scanLineEdit, SIGNAL(textEdited(const QString &)), this, SLOT(scanLineChanged(const QString &))); FCEUD_UpdateNTView( -1, true); + + updateTimer = new QTimer( this ); + + connect( updateTimer, &QTimer::timeout, this, &ppuNameTableViewerDialog_t::periodicUpdate ); + + updateTimer->start( 33 ); // 30hz } //---------------------------------------------------- ppuNameTableViewerDialog_t::~ppuNameTableViewerDialog_t(void) { + updateTimer->stop(); nameTableViewWindow = NULL; printf("Name Table Viewer Window Deleted\n"); @@ -164,6 +171,16 @@ void ppuNameTableViewerDialog_t::closeWindow(void) deleteLater(); } //---------------------------------------------------- +void ppuNameTableViewerDialog_t::periodicUpdate(void) +{ + + if ( redrawtables ) + { + this->update(); + redrawtables = false; + } +} +//---------------------------------------------------- void ppuNameTableViewerDialog_t::scanLineChanged( const QString &txt ) { std::string s; @@ -391,8 +408,8 @@ static void DrawNameTable(int scanline, int ntnum, bool invalidateCache) int attraddr = 0x3C0+((y>>2)<<3)+(x>>2); if (invalid || (table[ntaddr] != tablecache[ntaddr]) - || (table[attraddr] != tablecache[attraddr])) { - redrawtables = true; + || (table[attraddr] != tablecache[attraddr])) + { int temp = (((y&2)<<1)+(x&2)); a = (table[attraddr] & (3<> temp; @@ -504,11 +521,7 @@ void FCEUD_UpdateNTView(int scanline, bool drawall) } chrchanged = 0; - - if ( nameTableViewWindow ) - { - nameTableViewWindow->update(); - } + redrawtables = true; return; } //---------------------------------------------------- diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h index aca06453..5d21a79d 100644 --- a/src/drivers/Qt/NameTableViewer.h +++ b/src/drivers/Qt/NameTableViewer.h @@ -73,10 +73,12 @@ class ppuNameTableViewerDialog_t : public QDialog QCheckBox *ignorePaletteCbox; QSlider *refreshSlider; QLineEdit *scanLineEdit; + QTimer *updateTimer; public slots: void closeWindow(void); private slots: + void periodicUpdate(void); void refreshSliderChanged(int value); void scanLineChanged( const QString &txt ); }; diff --git a/src/drivers/Qt/ppuViewer.cpp b/src/drivers/Qt/ppuViewer.cpp index d16d1ee4..a48be32c 100644 --- a/src/drivers/Qt/ppuViewer.cpp +++ b/src/drivers/Qt/ppuViewer.cpp @@ -42,6 +42,7 @@ static QColor ppuv_palette[PALETTEHEIGHT][PALETTEWIDTH]; static uint8_t pallast[32+3] = { 0 }; // palette cache for change comparison static uint8_t palcache[36] = { 0 }; //palette cache for drawing static uint8_t chrcache0[0x1000] = {0}, chrcache1[0x1000] = {0}, logcache0[0x1000] = {0}, logcache1[0x1000] = {0}; //cache CHR, fixes a refresh problem when right-clicking +static bool redrawWindow = true; static void initPPUViewer(void); static ppuPatternTable_t pattern0; @@ -166,11 +167,18 @@ ppuViewerDialog_t::ppuViewerDialog_t(QWidget *parent) connect( refreshSlider, SIGNAL(valueChanged(int)), this, SLOT(refreshSliderChanged(int))); FCEUD_UpdatePPUView( -1, 1 ); + + updateTimer = new QTimer( this ); + + connect( updateTimer, &QTimer::timeout, this, &ppuViewerDialog_t::periodicUpdate ); + + updateTimer->start( 33 ); // 30hz } //---------------------------------------------------- ppuViewerDialog_t::~ppuViewerDialog_t(void) { + updateTimer->stop(); ppuViewWindow = NULL; printf("PPU Viewer Window Deleted\n"); @@ -191,6 +199,15 @@ void ppuViewerDialog_t::closeWindow(void) deleteLater(); } //---------------------------------------------------- +void ppuViewerDialog_t::periodicUpdate(void) +{ + if ( redrawWindow ) + { + this->update(); + redrawWindow = false; + } +} +//---------------------------------------------------- void ppuViewerDialog_t::scanLineChanged( const QString &txt ) { std::string s; @@ -556,10 +573,7 @@ void FCEUD_UpdatePPUView(int scanline, int refreshchr) DrawPatternTable( &pattern0,chrcache0,logcache0,pindex[0]); DrawPatternTable( &pattern1,chrcache1,logcache1,pindex[1]); - if ( ppuViewWindow ) - { - ppuViewWindow->update(); - } + redrawWindow = true; } //---------------------------------------------------- ppuPalatteView_t::ppuPalatteView_t(QWidget *parent) diff --git a/src/drivers/Qt/ppuViewer.h b/src/drivers/Qt/ppuViewer.h index 54221919..9281c71c 100644 --- a/src/drivers/Qt/ppuViewer.h +++ b/src/drivers/Qt/ppuViewer.h @@ -108,10 +108,12 @@ class ppuViewerDialog_t : public QDialog QCheckBox *invertMaskCbox; QSlider *refreshSlider; QLineEdit *scanLineEdit; + QTimer *updateTimer; public slots: void closeWindow(void); private slots: + void periodicUpdate(void); void sprite8x16Changed0(int state); void sprite8x16Changed1(int state); void refreshSliderChanged(int value); From 4765bc8bdda00362db2e9d0131fe6984792981d0 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Tue, 6 Oct 2020 19:00:32 -0400 Subject: [PATCH 06/12] Added widgets for mirroring and properties frames on Qt NT viewer --- src/drivers/Qt/NameTableViewer.cpp | 39 ++++++++++++++++++++++++++++++ src/drivers/Qt/NameTableViewer.h | 9 +++++++ 2 files changed, 48 insertions(+) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 98f0c460..31d0f6c8 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -139,6 +139,45 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) connect( scanLineEdit, SIGNAL(textEdited(const QString &)), this, SLOT(scanLineChanged(const QString &))); + hbox = new QHBoxLayout(); + frame = new QGroupBox( tr("Current Mirroring") ); + grid = new QGridLayout(); + + mainLayout->addLayout( hbox, 1 ); + hbox->addWidget( frame ); + frame->setLayout( grid ); + + horzMirrorBtn = new QRadioButton( tr("Horizontal") ); + vertMirrorBtn = new QRadioButton( tr("Vertical") ); + fourScreenBtn = new QRadioButton( tr("Four Screen") ); + singleScreenBtn[0] = new QRadioButton( tr("Single Screen 0") ); + singleScreenBtn[1] = new QRadioButton( tr("Single Screen 1") ); + singleScreenBtn[2] = new QRadioButton( tr("Single Screen 2") ); + singleScreenBtn[3] = new QRadioButton( tr("Single Screen 3") ); + + grid->addWidget( horzMirrorBtn, 0, 0, Qt::AlignLeft ); + grid->addWidget( vertMirrorBtn, 1, 0, Qt::AlignLeft ); + grid->addWidget( fourScreenBtn, 2, 0, Qt::AlignLeft ); + grid->addWidget( singleScreenBtn[0], 0, 1, Qt::AlignLeft ); + grid->addWidget( singleScreenBtn[1], 1, 1, Qt::AlignLeft ); + grid->addWidget( singleScreenBtn[2], 2, 1, Qt::AlignLeft ); + grid->addWidget( singleScreenBtn[3], 3, 1, Qt::AlignLeft ); + + vbox = new QVBoxLayout(); + frame = new QGroupBox( tr("Properties") ); + hbox->addWidget( frame ); + frame->setLayout( vbox ); + + tileID = new QLabel( tr("Tile ID:") ); + tileXY = new QLabel( tr("X/Y :") ); + ppuAddrLbl = new QLabel( tr("PPU Address:") ); + attrbLbl = new QLabel( tr("Attribute:") ); + + vbox->addWidget( tileID ); + vbox->addWidget( tileXY ); + vbox->addWidget( ppuAddrLbl ); + vbox->addWidget( attrbLbl ); + FCEUD_UpdateNTView( -1, true); updateTimer = new QTimer( this ); diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h index 5d21a79d..a8c537be 100644 --- a/src/drivers/Qt/NameTableViewer.h +++ b/src/drivers/Qt/NameTableViewer.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,14 @@ class ppuNameTableViewerDialog_t : public QDialog QSlider *refreshSlider; QLineEdit *scanLineEdit; QTimer *updateTimer; + QRadioButton *horzMirrorBtn; + QRadioButton *vertMirrorBtn; + QRadioButton *fourScreenBtn; + QRadioButton *singleScreenBtn[4]; + QLabel *tileID; + QLabel *tileXY; + QLabel *ppuAddrLbl; + QLabel *attrbLbl; public slots: void closeWindow(void); From 2b15f8611ea3fd7a1601af00dd7847194eadabf2 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Tue, 6 Oct 2020 19:50:22 -0400 Subject: [PATCH 07/12] Added NT Mirroring display and selection logic for Qt GUI --- src/drivers/Qt/NameTableViewer.cpp | 85 +++++++++++++++++++++++++++++- src/drivers/Qt/NameTableViewer.h | 8 +++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 31d0f6c8..aa70168f 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -59,9 +59,10 @@ enum NT_MirrorType NT_SINGLE_SCREEN_TABLE_2, NT_SINGLE_SCREEN_TABLE_3, NT_NUM_MIRROR_TYPES }; -static NT_MirrorType ntmirroring, oldntmirroring = NT_NONE; +static NT_MirrorType ntmirroring = NT_NONE, oldntmirroring = NT_NONE; static void initNameTableViewer(void); +static void ChangeMirroring(void); //---------------------------------------------------- int openNameTableViewWindow( QWidget *parent ) { @@ -163,6 +164,16 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) grid->addWidget( singleScreenBtn[2], 2, 1, Qt::AlignLeft ); grid->addWidget( singleScreenBtn[3], 3, 1, Qt::AlignLeft ); + connect( horzMirrorBtn , SIGNAL(clicked(void)), this, SLOT(horzMirrorClicked(void))); + connect( vertMirrorBtn , SIGNAL(clicked(void)), this, SLOT(vertMirrorClicked(void))); + connect( fourScreenBtn , SIGNAL(clicked(void)), this, SLOT(fourScreenClicked(void))); + connect( singleScreenBtn[0], SIGNAL(clicked(void)), this, SLOT(singleScreen0Clicked(void))); + connect( singleScreenBtn[1], SIGNAL(clicked(void)), this, SLOT(singleScreen1Clicked(void))); + connect( singleScreenBtn[2], SIGNAL(clicked(void)), this, SLOT(singleScreen2Clicked(void))); + connect( singleScreenBtn[3], SIGNAL(clicked(void)), this, SLOT(singleScreen3Clicked(void))); + + updateMirrorButtons(); + vbox = new QVBoxLayout(); frame = new QGroupBox( tr("Properties") ); hbox->addWidget( frame ); @@ -212,6 +223,7 @@ void ppuNameTableViewerDialog_t::closeWindow(void) //---------------------------------------------------- void ppuNameTableViewerDialog_t::periodicUpdate(void) { + updateMirrorButtons(); if ( redrawtables ) { @@ -220,6 +232,77 @@ void ppuNameTableViewerDialog_t::periodicUpdate(void) } } //---------------------------------------------------- +void ppuNameTableViewerDialog_t::updateMirrorButtons(void) +{ + switch ( ntmirroring ) + { + default: + case NT_NONE: + break; + case NT_HORIZONTAL: + horzMirrorBtn->setChecked(true); + break; + case NT_VERTICAL: + vertMirrorBtn->setChecked(true); + break; + case NT_FOUR_SCREEN: + fourScreenBtn->setChecked(true); + break; + case NT_SINGLE_SCREEN_TABLE_0: + case NT_SINGLE_SCREEN_TABLE_1: + case NT_SINGLE_SCREEN_TABLE_2: + case NT_SINGLE_SCREEN_TABLE_3: + { + int i = ntmirroring - NT_SINGLE_SCREEN_TABLE_0; + + singleScreenBtn[i]->setChecked(true); + } + break; + } +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::horzMirrorClicked(void) +{ + ntmirroring = NT_HORIZONTAL; + ChangeMirroring(); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::vertMirrorClicked(void) +{ + ntmirroring = NT_VERTICAL; + ChangeMirroring(); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::fourScreenClicked(void) +{ + ntmirroring = NT_FOUR_SCREEN; + ChangeMirroring(); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::singleScreen0Clicked(void) +{ + ntmirroring = NT_SINGLE_SCREEN_TABLE_0; + ChangeMirroring(); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::singleScreen1Clicked(void) +{ + ntmirroring = NT_SINGLE_SCREEN_TABLE_1; + ChangeMirroring(); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::singleScreen2Clicked(void) +{ + ntmirroring = NT_SINGLE_SCREEN_TABLE_2; + ChangeMirroring(); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::singleScreen3Clicked(void) +{ + ntmirroring = NT_SINGLE_SCREEN_TABLE_3; + ChangeMirroring(); +} +//---------------------------------------------------- void ppuNameTableViewerDialog_t::scanLineChanged( const QString &txt ) { std::string s; diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h index a8c537be..18df775e 100644 --- a/src/drivers/Qt/NameTableViewer.h +++ b/src/drivers/Qt/NameTableViewer.h @@ -88,6 +88,14 @@ class ppuNameTableViewerDialog_t : public QDialog void closeWindow(void); private slots: void periodicUpdate(void); + void updateMirrorButtons(void); + void horzMirrorClicked(void); + void vertMirrorClicked(void); + void fourScreenClicked(void); + void singleScreen0Clicked(void); + void singleScreen1Clicked(void); + void singleScreen2Clicked(void); + void singleScreen3Clicked(void); void refreshSliderChanged(int value); void scanLineChanged( const QString &txt ); }; From c66fc35a40c6230e555c8e848a76050214e73b2d Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Tue, 6 Oct 2020 19:52:02 -0400 Subject: [PATCH 08/12] Build fix for GTK GUI missing NT view function needed by core. --- src/drivers/sdl/debugger.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/drivers/sdl/debugger.cpp b/src/drivers/sdl/debugger.cpp index 54d348cc..e304e4de 100644 --- a/src/drivers/sdl/debugger.cpp +++ b/src/drivers/sdl/debugger.cpp @@ -1719,6 +1719,11 @@ void FCEUD_UpdatePPUView(int scanline, int refreshchr) // Place holder to allow for compiling. GTK GUI doesn't support this. Qt Does. } +void FCEUD_UpdateNTView(int scanline, bool drawall) +{ + // 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; From 652cc4f2c94570ea35c2c686e6ce453c7813b31a Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Tue, 6 Oct 2020 20:08:17 -0400 Subject: [PATCH 09/12] Added logic to protect against PPU and NT view windows from crash when being opened with no game loaded. --- src/drivers/Qt/NameTableViewer.cpp | 25 ++++++++++--------------- src/drivers/Qt/ppuViewer.cpp | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index aa70168f..485dbd85 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -572,15 +572,8 @@ static void DrawNameTable(int scanline, int ntnum, bool invalidateCache) //obviously this whole scheme of nt cache doesnt work if an mmc5 game is playing tricks with the attribute table } - //pbitmap += (8*3); } - //pbitmap += 7*((NTWIDTH*3)); } - - //this copies the attribute tables to the cache if needed. but we arent using it now because - //if(redrawtables){ - // memcpy(tablecache+0x3c0,table+0x3c0,0x40); - //} } //---------------------------------------------------- void FCEUD_UpdateNTView(int scanline, bool drawall) @@ -594,12 +587,8 @@ void FCEUD_UpdateNTView(int scanline, bool drawall) return; } - //uint8 *pbitmap = ppuv_palette; - //if (!hNTView) return; - ppu_getScroll(xpos,ypos); - if (NTViewSkip < NTViewRefresh) { NTViewSkip++; @@ -618,13 +607,19 @@ void FCEUD_UpdateNTView(int scanline, bool drawall) memcpy(palcache,PALRAM,32); drawall = 1; //palette has changed, so redraw all } + + if ( vnapage[0] == NULL ) + { + return; + } ntmirroring = NT_NONE; - if(vnapage[0] == vnapage[1])ntmirroring = NT_HORIZONTAL; - if(vnapage[0] == vnapage[2])ntmirroring = NT_VERTICAL; - if((vnapage[0] != vnapage[1]) && (vnapage[0] != vnapage[2]))ntmirroring = NT_FOUR_SCREEN; + if (vnapage[0] == vnapage[1])ntmirroring = NT_HORIZONTAL; + if (vnapage[0] == vnapage[2])ntmirroring = NT_VERTICAL; + if ((vnapage[0] != vnapage[1]) && (vnapage[0] != vnapage[2]))ntmirroring = NT_FOUR_SCREEN; - if((vnapage[0] == vnapage[1]) && (vnapage[1] == vnapage[2]) && (vnapage[2] == vnapage[3])){ + if ((vnapage[0] == vnapage[1]) && (vnapage[1] == vnapage[2]) && (vnapage[2] == vnapage[3])) + { if(vnapage[0] == &NTARAM[0x000])ntmirroring = NT_SINGLE_SCREEN_TABLE_0; if(vnapage[0] == &NTARAM[0x400])ntmirroring = NT_SINGLE_SCREEN_TABLE_1; if(vnapage[0] == ExtraNTARAM)ntmirroring = NT_SINGLE_SCREEN_TABLE_2; diff --git a/src/drivers/Qt/ppuViewer.cpp b/src/drivers/Qt/ppuViewer.cpp index a48be32c..ba334f3d 100644 --- a/src/drivers/Qt/ppuViewer.cpp +++ b/src/drivers/Qt/ppuViewer.cpp @@ -510,19 +510,28 @@ void FCEUD_UpdatePPUView(int scanline, int refreshchr) if (refreshchr) { + int i10, x10; for (i = 0, x=0x1000; i < 0x1000; i++, x++) { - chrcache0[i] = VPage[i>>10][i]; - chrcache1[i] = VPage[x>>10][x]; + i10 = i>>10; + x10 = x>>10; + + if ( VPage[i10] == NULL ) + { + continue; + } + chrcache0[i] = VPage[i10][i]; + chrcache1[i] = VPage[x10][x]; + if (debug_loggingCD) { if (cdloggerVideoDataSize) { int addr; - addr = &VPage[i >> 10][i] - CHRptr[0]; + addr = &VPage[i10][i] - CHRptr[0]; if ((addr >= 0) && (addr < (int)cdloggerVideoDataSize)) logcache0[i] = cdloggervdata[addr]; - addr = &VPage[x >> 10][x] - CHRptr[0]; + addr = &VPage[x10][x] - CHRptr[0]; if ((addr >= 0) && (addr < (int)cdloggerVideoDataSize)) logcache1[i] = cdloggervdata[addr]; } From 2d403fac3ea23813214a66fa237d71b13e048971 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Tue, 6 Oct 2020 21:02:10 -0400 Subject: [PATCH 10/12] Added logic to fillout property labels on NT view Qt GUI. --- src/drivers/Qt/NameTableViewer.cpp | 109 ++++++++++++++++++++++++++--- src/drivers/Qt/NameTableViewer.h | 7 ++ 2 files changed, 106 insertions(+), 10 deletions(-) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 485dbd85..66e0e880 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -232,6 +232,27 @@ void ppuNameTableViewerDialog_t::periodicUpdate(void) } } //---------------------------------------------------- +void ppuNameTableViewerDialog_t::setPropertyLabels( int TileID, int TileX, int TileY, int NameTable, int PPUAddress, int AttAddress, int Attrib ) +{ + char stmp[64]; + + sprintf( stmp, "Tile ID: %02X", TileID); + + tileID->setText( tr(stmp) ); + + sprintf( stmp, "X/Y : %0d/%0d", TileX, TileY); + + tileXY->setText( tr(stmp) ); + + sprintf(stmp,"PPU Address: %04X",PPUAddress); + + ppuAddrLbl->setText( tr(stmp) ); + + sprintf(stmp,"Attribute: %1X (%04X)",Attrib,AttAddress); + + attrbLbl->setText( tr(stmp) ); +} +//---------------------------------------------------- void ppuNameTableViewerDialog_t::updateMirrorButtons(void) { switch ( ntmirroring ) @@ -324,6 +345,7 @@ void ppuNameTableViewerDialog_t::refreshSliderChanged(int value) ppuNameTableView_t::ppuNameTableView_t(QWidget *parent) : QWidget(parent) { + this->parent = (ppuNameTableViewerDialog_t*)parent; this->setFocusPolicy(Qt::StrongFocus); this->setMouseTracking(true); viewWidth = 256 * 2; @@ -345,16 +367,78 @@ void ppuNameTableView_t::resizeEvent(QResizeEvent *event) //printf("%ix%i\n", viewWidth, viewHeight ); } //---------------------------------------------------- +void ppuNameTableView_t::computeNameTableProperties( int x, int y ) +{ + int i, xx, yy, w, h, TileID, TileX, TileY, NameTable, PPUAddress, AttAddress, Attrib; + ppuNameTable_t *tbl = NULL; + + NameTable = 0; + + if ( vnapage[0] == NULL ) + { + return; + } + for (i=0; i<4; i++) + { + xx = nameTable[i].x; + yy = nameTable[i].y; + w = (nameTable[i].w * 256); + h = (nameTable[i].h * 240); + + if ( (x >= xx) && (x < (xx+w) ) && + (y >= yy) && (y < (yy+h) ) ) + { + tbl = &nameTable[i]; + NameTable = i; + break; + } + } + + if ( tbl == NULL ) + { + //printf("Mouse not over a tile\n"); + return; + } + xx = tbl->x; yy = tbl->y; + w = tbl->w; h = tbl->h; + + if ( (NameTable%2) == 1 ) + { + TileX = ((x - xx) / (w*8)) + 32; + } + else + { + TileX = (x - xx) / (w*8); + } + + if ( (NameTable/2) == 1 ) + { + TileY = ((y - yy) / (h*8)) + 30; + } + else + { + TileY = (y - yy) / (h*8); + } + + PPUAddress = 0x2000+(NameTable*0x400)+((TileY%30)*32)+(TileX%32); + + TileID = vnapage[(PPUAddress>>10)&0x3][PPUAddress&0x3FF]; + + AttAddress = 0x23C0 | (PPUAddress & 0x0C00) | ((PPUAddress >> 4) & 0x38) | ((PPUAddress >> 2) & 0x07); + Attrib = vnapage[(AttAddress>>10)&0x3][AttAddress&0x3FF]; + Attrib = (Attrib >> ((PPUAddress&2) | ((PPUAddress&64)>>4))) & 0x3; + + //printf("NT:%i Tile X/Y : %i/%i \n", NameTable, TileX, TileY ); + + if ( parent ) + { + parent->setPropertyLabels( TileID, TileX, TileY, NameTable, PPUAddress, AttAddress, Attrib ); + } +} +//---------------------------------------------------- void ppuNameTableView_t::mouseMoveEvent(QMouseEvent *event) { - //QPoint tile = convPixToTile( event->pos() ); - - //if ( (tile.x() < 16) && (tile.y() < 16) ) - //{ - // char stmp[64]; - // sprintf( stmp, "Tile: $%X%X", tile.y(), tile.x() ); - // tileLabel->setText( tr(stmp) ); - //} + computeNameTableProperties( event->pos().x(), event->pos().y() ); } //---------------------------------------------------------------------------- void ppuNameTableView_t::mousePressEvent(QMouseEvent * event) @@ -387,9 +471,11 @@ void ppuNameTableView_t::paintEvent(QPaintEvent *event) for (n=0; n<4; n++) { nt = &nameTable[n]; + + nt->w = w; nt->h = h; - xx = (n%2) * (viewWidth / 2); - yy = (n/2) * (viewHeight / 2); + nt->x = xx = (n%2) * (viewWidth / 2); + nt->y = yy = (n/2) * (viewHeight / 2); for (j=0; j<30; j++) { @@ -399,6 +485,9 @@ void ppuNameTableView_t::paintEvent(QPaintEvent *event) { ii = (i*8); + nt->tile[j][i].x = xx+(ii*w); + nt->tile[j][i].y = yy+(jj*h); + for (y=0; y<8; y++) { for (x=0; x<8; x++) diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h index 18df775e..58d288ef 100644 --- a/src/drivers/Qt/NameTableViewer.h +++ b/src/drivers/Qt/NameTableViewer.h @@ -35,10 +35,14 @@ struct ppuNameTable_t { struct ppuNameTableTile_t tile[30][32]; + int x; + int y; int w; int h; }; +class ppuNameTableViewerDialog_t; + class ppuNameTableView_t : public QWidget { Q_OBJECT @@ -52,7 +56,9 @@ class ppuNameTableView_t : public QWidget void resizeEvent(QResizeEvent *event); void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent * event); + void computeNameTableProperties( int x, int y ); + ppuNameTableViewerDialog_t *parent; int viewWidth; int viewHeight; }; @@ -65,6 +71,7 @@ class ppuNameTableViewerDialog_t : public QDialog ppuNameTableViewerDialog_t(QWidget *parent = 0); ~ppuNameTableViewerDialog_t(void); + void setPropertyLabels( int TileID, int TileX, int TileY, int NameTable, int PPUAddress, int AttAddress, int Attrib ); protected: void closeEvent(QCloseEvent *bar); From a79400720bb1ecaf29cd7add8a1289f9b8951842 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Tue, 6 Oct 2020 21:08:10 -0400 Subject: [PATCH 11/12] Hooked up logic for NT View attribute and hide palette functionality in Qt GUI --- TODO-SDL | 2 +- src/drivers/Qt/NameTableViewer.cpp | 17 +++++++++++++++++ src/drivers/Qt/NameTableViewer.h | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/TODO-SDL b/TODO-SDL index 335f41c3..6fc0b1bb 100644 --- a/TODO-SDL +++ b/TODO-SDL @@ -44,7 +44,7 @@ Memory Watch Window | NO | NO TAS Editor | NO | NO | 6502 Debugger Window | YES | YES | PPU Viewer | YES | NO | -Name Table Viewer | NO | NO | +Name Table Viewer | YES | NO | Memory Hex Editor | YES | YES | Trace Logger | YES | NO | Code/Data Logger | YES | NO | diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 66e0e880..240121b0 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -110,10 +110,17 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) showAttrbCbox = new QCheckBox( tr("Show Attributes") ); ignorePaletteCbox = new QCheckBox( tr("Ignore Palette") ); + //showScrollLineCbox->setChecked( attview ); + showAttrbCbox->setChecked( attview ); + ignorePaletteCbox->setChecked( hidepal ); + grid->addWidget( showScrollLineCbox, 0, 0, Qt::AlignLeft ); grid->addWidget( showAttrbCbox , 1, 0, Qt::AlignLeft ); grid->addWidget( ignorePaletteCbox , 2, 0, Qt::AlignLeft ); + connect( showAttrbCbox , SIGNAL(stateChanged(int)), this, SLOT(showAttrbChanged(int))); + connect( ignorePaletteCbox, SIGNAL(stateChanged(int)), this, SLOT(ignorePaletteChanged(int))); + hbox = new QHBoxLayout(); refreshSlider = new QSlider( Qt::Horizontal ); hbox->addWidget( new QLabel( tr("Refresh: More") ) ); @@ -337,6 +344,16 @@ void ppuNameTableViewerDialog_t::scanLineChanged( const QString &txt ) //printf("ScanLine: '%s' %i\n", s.c_str(), PPUViewScanline ); } //---------------------------------------------------- +void ppuNameTableViewerDialog_t::showAttrbChanged(int state) +{ + attview = (state != Qt::Unchecked); +} +//---------------------------------------------------- +void ppuNameTableViewerDialog_t::ignorePaletteChanged(int state) +{ + hidepal = (state != Qt::Unchecked); +} +//---------------------------------------------------- void ppuNameTableViewerDialog_t::refreshSliderChanged(int value) { NTViewRefresh = value; diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h index 58d288ef..0d27c073 100644 --- a/src/drivers/Qt/NameTableViewer.h +++ b/src/drivers/Qt/NameTableViewer.h @@ -103,6 +103,8 @@ class ppuNameTableViewerDialog_t : public QDialog void singleScreen1Clicked(void); void singleScreen2Clicked(void); void singleScreen3Clicked(void); + void showAttrbChanged(int state); + void ignorePaletteChanged(int state); void refreshSliderChanged(int value); void scanLineChanged( const QString &txt ); }; From 6dfd3fb2d0dd210d1692762d8a83536dfff9837e Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Tue, 6 Oct 2020 21:31:40 -0400 Subject: [PATCH 12/12] Added logic to draw scroll lines on NT Viewer for Qt GUI --- src/drivers/Qt/NameTableViewer.cpp | 34 ++++++++++++++++++++++++++---- src/drivers/Qt/NameTableViewer.h | 1 + 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/drivers/Qt/NameTableViewer.cpp b/src/drivers/Qt/NameTableViewer.cpp index 240121b0..c64b5b16 100644 --- a/src/drivers/Qt/NameTableViewer.cpp +++ b/src/drivers/Qt/NameTableViewer.cpp @@ -32,6 +32,7 @@ static int chrchanged = 0; static int xpos = 0, ypos = 0; static int attview = 0; static int hidepal = 0; +static bool drawScrollLines = true; static bool redrawtables = true; // checkerboard tile for attribute view @@ -110,7 +111,7 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) showAttrbCbox = new QCheckBox( tr("Show Attributes") ); ignorePaletteCbox = new QCheckBox( tr("Ignore Palette") ); - //showScrollLineCbox->setChecked( attview ); + showScrollLineCbox->setChecked( drawScrollLines ); showAttrbCbox->setChecked( attview ); ignorePaletteCbox->setChecked( hidepal ); @@ -118,8 +119,9 @@ ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent) grid->addWidget( showAttrbCbox , 1, 0, Qt::AlignLeft ); grid->addWidget( ignorePaletteCbox , 2, 0, Qt::AlignLeft ); - connect( showAttrbCbox , SIGNAL(stateChanged(int)), this, SLOT(showAttrbChanged(int))); - connect( ignorePaletteCbox, SIGNAL(stateChanged(int)), this, SLOT(ignorePaletteChanged(int))); + connect( showScrollLineCbox, SIGNAL(stateChanged(int)), this, SLOT(showScrollLinesChanged(int))); + connect( showAttrbCbox , SIGNAL(stateChanged(int)), this, SLOT(showAttrbChanged(int))); + connect( ignorePaletteCbox , SIGNAL(stateChanged(int)), this, SLOT(ignorePaletteChanged(int))); hbox = new QHBoxLayout(); refreshSlider = new QSlider( Qt::Horizontal ); @@ -344,6 +346,11 @@ void ppuNameTableViewerDialog_t::scanLineChanged( const QString &txt ) //printf("ScanLine: '%s' %i\n", s.c_str(), PPUViewScanline ); } //---------------------------------------------------- +void ppuNameTableViewerDialog_t::showScrollLinesChanged(int state) +{ + drawScrollLines = (state != Qt::Unchecked); +} +//---------------------------------------------------- void ppuNameTableViewerDialog_t::showAttrbChanged(int state) { attview = (state != Qt::Unchecked); @@ -473,8 +480,9 @@ void ppuNameTableView_t::mousePressEvent(QMouseEvent * event) void ppuNameTableView_t::paintEvent(QPaintEvent *event) { ppuNameTable_t *nt; - int n,i,j,ii,jj,w,h,x,y,xx,yy; + int n,i,j,ii,jj,w,h,x,y,xx,yy,ww,hh; QPainter painter(this); + QColor scanLineColor(255,255,255); viewWidth = event->rect().width(); viewHeight = event->rect().height(); @@ -514,7 +522,25 @@ void ppuNameTableView_t::paintEvent(QPaintEvent *event) } } } + if ( drawScrollLines ) + { + ww = nt->w * 256; + hh = nt->h * 240; + + painter.setPen( scanLineColor ); + + if ( (xpos >= xx) && (xpos < (xx+ww)) ) + { + painter.drawLine( xpos, yy, xpos, yy + hh ); + } + + if ( (ypos >= yy) && (ypos < (yy+hh)) ) + { + painter.drawLine( xx, ypos, xx + ww, ypos ); + } + } } + } //---------------------------------------------------- static void initNameTableViewer(void) diff --git a/src/drivers/Qt/NameTableViewer.h b/src/drivers/Qt/NameTableViewer.h index 0d27c073..f8865249 100644 --- a/src/drivers/Qt/NameTableViewer.h +++ b/src/drivers/Qt/NameTableViewer.h @@ -105,6 +105,7 @@ class ppuNameTableViewerDialog_t : public QDialog void singleScreen3Clicked(void); void showAttrbChanged(int state); void ignorePaletteChanged(int state); + void showScrollLinesChanged(int state); void refreshSliderChanged(int value); void scanLineChanged( const QString &txt ); };