From 73726114c59d3baeab4a83761845c383cf230fe7 Mon Sep 17 00:00:00 2001 From: Matthew Budd Date: Mon, 5 Oct 2020 22:49:09 -0400 Subject: [PATCH] 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?