Merge pull request #181 from mjbudd77/master

Added Name Table Viewer Feature for Qt GUI
This commit is contained in:
mjbudd77 2020-10-06 21:52:13 -04:00 committed by GitHub
commit c5e39608c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 948 additions and 13 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,776 @@
// NameTableViewer.cpp
#include <stdio.h>
#include <stdint.h>
#include <QDir>
#include <QPainter>
#include <QInputDialog>
#include <QMessageBox>
#include "../../types.h"
#include "../../fceu.h"
#include "../../cart.h"
#include "../../ppu.h"
#include "../../ines.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;
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 attview = 0;
static int hidepal = 0;
static bool drawScrollLines = true;
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 cache[0x400];
} 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 = NT_NONE, oldntmirroring = NT_NONE;
static void initNameTableViewer(void);
static void ChangeMirroring(void);
//----------------------------------------------------
int openNameTableViewWindow( QWidget *parent )
{
if ( nameTableViewWindow != NULL )
{
return -1;
}
initNameTableViewer();
nameTableViewWindow = new ppuNameTableViewerDialog_t(parent);
nameTableViewWindow->show();
return 0;
}
//----------------------------------------------------
ppuNameTableViewerDialog_t::ppuNameTableViewerDialog_t(QWidget *parent)
: QDialog( parent )
{
QVBoxLayout *mainLayout, *vbox;
QHBoxLayout *hbox;
QGridLayout *grid;
QGroupBox *frame;
char stmp[64];
nameTableViewWindow = this;
setWindowTitle( tr("Name Table Viewer") );
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") );
showScrollLineCbox->setChecked( drawScrollLines );
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( 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 );
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(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 &)));
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 );
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 );
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 );
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");
}
//----------------------------------------------------
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();
}
//----------------------------------------------------
void ppuNameTableViewerDialog_t::periodicUpdate(void)
{
updateMirrorButtons();
if ( redrawtables )
{
this->update();
redrawtables = false;
}
}
//----------------------------------------------------
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 )
{
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;
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::showScrollLinesChanged(int state)
{
drawScrollLines = (state != Qt::Unchecked);
}
//----------------------------------------------------
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;
}
//----------------------------------------------------
ppuNameTableView_t::ppuNameTableView_t(QWidget *parent)
: QWidget(parent)
{
this->parent = (ppuNameTableViewerDialog_t*)parent;
this->setFocusPolicy(Qt::StrongFocus);
this->setMouseTracking(true);
viewWidth = 256 * 2;
viewHeight = 240 * 2;
setMinimumWidth( viewWidth );
setMinimumHeight( viewHeight );
}
//----------------------------------------------------
ppuNameTableView_t::~ppuNameTableView_t(void)
{
}
//----------------------------------------------------
void ppuNameTableView_t::resizeEvent(QResizeEvent *event)
{
viewWidth = event->size().width();
viewHeight = event->size().height();
//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)
{
computeNameTableProperties( event->pos().x(), event->pos().y() );
}
//----------------------------------------------------------------------------
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)
{
ppuNameTable_t *nt;
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();
w = viewWidth / (256*2);
h = viewHeight / (240*2);
//printf("%ix%i\n", viewWidth, viewHeight );
xx = 0; yy = 0;
for (n=0; n<4; n++)
{
nt = &nameTable[n];
nt->w = w; nt->h = h;
nt->x = xx = (n%2) * (viewWidth / 2);
nt->y = yy = (n/2) * (viewHeight / 2);
for (j=0; j<30; j++)
{
jj = (j*8);
for (i=0; i<32; i++)
{
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++)
{
painter.fillRect( xx+(ii+x)*w, yy+(jj+y)*h, w, h, nt->tile[j][i].pixel[y][x].color );
}
}
}
}
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)
{
//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]))
{
int temp = (((y&2)<<1)+(x&2));
a = (table[attraddr] & (3<<temp)) >> 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
}
}
}
}
//----------------------------------------------------
void FCEUD_UpdateNTView(int scanline, bool drawall)
{
if (nameTableViewWindow == 0)
{
return;
}
if ( (scanline != -1) && (scanline != NTViewScanline) )
{
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
}
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]) && (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;
redrawtables = true;
return;
}
//----------------------------------------------------

View File

@ -0,0 +1,114 @@
// NameTableViewer.h
#pragma once
#include <QWidget>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QPushButton>
#include <QRadioButton>
#include <QLabel>
#include <QFrame>
#include <QTimer>
#include <QSlider>
#include <QLineEdit>
#include <QGroupBox>
#include <QCloseEvent>
struct ppuNameTablePixel_t
{
QColor color;
};
struct ppuNameTableTile_t
{
struct ppuNameTablePixel_t pixel[8][8];
int x;
int y;
};
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
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);
void computeNameTableProperties( int x, int y );
ppuNameTableViewerDialog_t *parent;
int viewWidth;
int viewHeight;
};
class ppuNameTableViewerDialog_t : public QDialog
{
Q_OBJECT
public:
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);
ppuNameTableView_t *ntView;
QCheckBox *showScrollLineCbox;
QCheckBox *showAttrbCbox;
QCheckBox *ignorePaletteCbox;
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);
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 showAttrbChanged(int state);
void ignorePaletteChanged(int state);
void showScrollLinesChanged(int state);
void refreshSliderChanged(int value);
void scanLineChanged( const QString &txt );
};
int openNameTableViewWindow( QWidget *parent );

View File

@ -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;
@ -493,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];
}
@ -556,10 +582,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)

View File

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

View File

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

View File

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