From 5c1789014202b897ba12ab4e3a60fc51a3c28f74 Mon Sep 17 00:00:00 2001 From: Sour Date: Sun, 7 Jul 2019 18:18:20 -0400 Subject: [PATCH] PPU: Implemented cycle-by-cycle fetches for tilemap/tile data (for modes 0-6) --- Core/Ppu.cpp | 463 +++++++++++++++++++++++++++++++++--------------- Core/Ppu.h | 47 +++-- Core/PpuTypes.h | 28 +++ 3 files changed, 369 insertions(+), 169 deletions(-) diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index 1e93818..d08c7a5 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -131,6 +131,244 @@ PpuState Ppu::GetState() return state; } +template +void Ppu::GetTilemapData(uint8_t layerIndex, uint8_t columnIndex) +{ + /* The current layer's options */ + LayerConfig &config = _layerConfig[layerIndex]; + + /* Layer's tilemap start address */ + uint16_t tilemapAddr = config.TilemapAddress >> 1; + + uint16_t vScroll = config.VScroll; + uint16_t hScroll = hiResMode ? (config.HScroll << 1) : config.HScroll; + if(_hOffset || _vOffset) { + uint16_t enableBit = layerIndex == 0 ? 0x2000 : 0x4000; + if(_bgMode == 4) { + if((_hOffset & 0x8000) == 0 && (_hOffset & enableBit)) { + hScroll = (hScroll & 0x07) | (_hOffset & 0x3F8); + } + if((_hOffset & 0x8000) != 0 && (_hOffset & enableBit)) { + vScroll = (_hOffset & 0x3FF); + } + } else { + if(_hOffset & enableBit) { + hScroll = (hScroll & 0x07) | (_hOffset & 0x3F8); + } + if(_vOffset & enableBit) { + vScroll = (_vOffset & 0x3FF); + } + } + } + if(hiResMode) { + hScroll >>= 1; + } + + /* Current scanline (in interlaced mode, switches between even and odd rows every frame */ + uint16_t realY = IsDoubleHeight() ? (_oddFrame ? ((_scanline << 1) + 1) : (_scanline << 1)) : _scanline; + + /* The current row of tiles (e.g scanlines 16-23 is row 2) */ + uint16_t row = (realY + vScroll) >> (config.LargeTiles ? 4 : 3); + + /* Tilemap offset based on the current row & tilemap size options */ + uint16_t addrVerticalScrollingOffset = config.DoubleHeight ? ((row & 0x20) << (config.DoubleWidth ? 6 : 5)) : 0; + + /* The start address for tiles on this row */ + uint16_t baseOffset = tilemapAddr + addrVerticalScrollingOffset + ((row & 0x1F) << 5); + + /* The current column index (in terms of 8x8 or 16x16 tiles) */ + uint16_t column = columnIndex + (hScroll >> 3); + if(!hiResMode && config.LargeTiles) { + //For 16x16 tiles, need to return the same tile for 2 columns 8 pixel columns in a row + column >>= 1; + } + + /* The tilemap address to read the tile data from */ + uint16_t addr = (baseOffset + (column & 0x1F) + (config.DoubleWidth ? ((column & 0x20) << 5) : 0)) << 1; + + uint16_t tilemapData = _vram[addr] | (_vram[addr + 1] << 8); + _layerData[layerIndex].Tiles[columnIndex].TilemapData = tilemapData; + _layerData[layerIndex].Tiles[columnIndex].VScroll = vScroll; +} + +template +void Ppu::GetChrData(uint8_t layerIndex, uint8_t column, uint8_t plane) +{ + LayerConfig &config = _layerConfig[layerIndex]; + TileData &tileData = _layerData[layerIndex].Tiles[column]; + uint16_t tilemapData = tileData.TilemapData; + + bool largeTileWidth = hiResMode || config.LargeTiles; + + uint16_t chrAddr = config.ChrAddress; + bool vMirror = (tilemapData & 0x8000) != 0; + bool hMirror = (tilemapData & 0x4000) != 0; + + uint16_t realY = IsDoubleHeight() ? (_oddFrame ? ((_scanline << 1) + 1) : (_scanline << 1)) : _scanline; + + bool useSecondTile = secondTile; + if(!hiResMode && config.LargeTiles) { + //For 16x16 tiles, need to return the 2nd part of the tile every other column + useSecondTile = (((column << 3) + config.HScroll) & 0x08) == 0x08; + } + + uint16_t tileIndex = tilemapData & 0x03FF; + if(largeTileWidth || config.LargeTiles) { + tileIndex = ( + tileIndex + + (config.LargeTiles ? (((realY + tileData.VScroll) & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0) + + (largeTileWidth ? (useSecondTile ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0)) : 0) + ) & 0x3FF; + } + + uint16_t tileStart = chrAddr + tileIndex * 8 * bpp; + + uint8_t baseYOffset = (realY + tileData.VScroll) & 0x07; + + uint8_t yOffset = vMirror ? (7 - baseYOffset) : baseYOffset; + uint16_t pixelStart = tileStart + yOffset * 2 + (plane << 4); + + uint16_t chrData = _vram[pixelStart] | (_vram[(uint16_t)(pixelStart + 1)] << 8); + _layerData[layerIndex].Tiles[column].ChrData[plane + (secondTile ? bpp / 2 : 0)] = chrData; +} + +void Ppu::GetHorizontalOffsetByte(uint8_t columnIndex) +{ + uint16_t columnOffset = (((columnIndex << 3) + (_layerConfig[2].HScroll & ~0x07)) >> 3) & (_layerConfig[2].DoubleWidth ? 0x3F : 0x1F); + uint16_t rowOffset = (_layerConfig[2].VScroll >> 3) & (_layerConfig[2].DoubleHeight ? 0x3F : 0x1F); + + uint16_t tileOffset = (columnOffset << 1) + (rowOffset << 6); + uint16_t hOffsetAddr = _layerConfig[2].TilemapAddress + tileOffset; + + _hOffset = _vram[hOffsetAddr] | (_vram[hOffsetAddr + 1] << 8); +} + +void Ppu::GetVerticalOffsetByte(uint8_t columnIndex) +{ + uint16_t columnOffset = (((columnIndex << 3) + (_layerConfig[2].HScroll & ~0x07)) >> 3) & (_layerConfig[2].DoubleWidth ? 0x3F : 0x1F); + uint16_t rowOffset = (_layerConfig[2].VScroll >> 3) & (_layerConfig[2].DoubleHeight ? 0x3F : 0x1F); + + uint16_t tileOffset = (columnOffset << 1) + (rowOffset << 6); + + //The vertical offset is 0x40 bytes later - but wraps around within the tilemap based on the tilemap size (0x800 or 0x1000 bytes) + uint16_t vOffsetAddr = _layerConfig[2].TilemapAddress + ((tileOffset + 0x40) & (_layerConfig[2].DoubleHeight ? 0xFFF : 0x7FF)); + + _vOffset = _vram[vOffsetAddr] | (_vram[vOffsetAddr + 1] << 8); +} + +void Ppu::FetchTileData() +{ + if(_fetchStartX == 0) { + _hOffset = 0; + _vOffset = 0; + } + + if(_bgMode == 0) { + for(int x = _fetchStartX; x <= _fetchEndX; x++) { + switch(x & 0x07) { + case 0: GetTilemapData(3, x >> 3); break; + case 1: GetTilemapData(2, x >> 3); break; + case 2: GetTilemapData(1, x >> 3); break; + case 3: GetTilemapData(0, x >> 3); break; + + case 4: GetChrData(3, x >> 3, 0); break; + case 5: GetChrData(2, x >> 3, 0); break; + case 6: GetChrData(1, x >> 3, 0); break; + case 7: GetChrData(0, x >> 3, 0); break; + } + } + } else if(_bgMode == 1) { + for(int x = _fetchStartX; x <= _fetchEndX; x++) { + switch(x & 0x07) { + case 0: GetTilemapData(2, x >> 3); break; + case 1: GetTilemapData(1, x >> 3); break; + case 2: GetTilemapData(0, x >> 3); break; + case 3: GetChrData(2, x >> 3, 0); break; + case 4: GetChrData(1, x >> 3, 0); break; + case 5: GetChrData(1, x >> 3, 1); break; + case 6: GetChrData(0, x >> 3, 0); break; + case 7: GetChrData(0, x >> 3, 1); break; + } + } + } else if(_bgMode == 2) { + for(int x = _fetchStartX; x <= _fetchEndX; x++) { + switch(x & 0x07) { + case 0: GetTilemapData(1, x >> 3); break; + case 1: GetTilemapData(0, x >> 3); break; + + case 2: GetHorizontalOffsetByte(x >> 3); break; + case 3: GetVerticalOffsetByte(x >> 3); break; + + case 4: GetChrData(1, x >> 3, 0); break; + case 5: GetChrData(1, x >> 3, 1); break; + case 6: GetChrData(0, x >> 3, 0); break; + case 7: GetChrData(0, x >> 3, 1); break; + } + } + } else if(_bgMode == 3) { + for(int x = _fetchStartX; x <= _fetchEndX; x++) { + switch(x & 0x07) { + case 0: GetTilemapData(1, x >> 3); break; + case 1: GetTilemapData(0, x >> 3); break; + + case 2: GetChrData(1, x >> 3, 0); break; + case 3: GetChrData(1, x >> 3, 1); break; + + case 4: GetChrData(0, x >> 3, 0); break; + case 5: GetChrData(0, x >> 3, 1); break; + case 6: GetChrData(0, x >> 3, 2); break; + case 7: GetChrData(0, x >> 3, 3); break; + } + } + } else if(_bgMode == 4) { + for(int x = _fetchStartX; x <= _fetchEndX; x++) { + switch(x & 0x07) { + case 0: GetTilemapData(1, x >> 3); break; + case 1: GetTilemapData(0, x >> 3); break; + + case 2: GetHorizontalOffsetByte(x >> 3); break; + + case 3: GetChrData(1, x >> 3, 0); break; + + case 4: GetChrData(0, x >> 3, 0); break; + case 5: GetChrData(0, x >> 3, 1); break; + case 6: GetChrData(0, x >> 3, 2); break; + case 7: GetChrData(0, x >> 3, 3); break; + } + } + } else if(_bgMode == 5) { + for(int x = _fetchStartX; x <= _fetchEndX; x++) { + switch(x & 0x07) { + case 0: GetTilemapData(1, x >> 3); break; + case 1: GetTilemapData(0, x >> 3); break; + + case 2: GetChrData(1, x >> 3, 0); break; + case 3: GetChrData(1, x >> 3, 0); break; + + case 4: GetChrData(0, x >> 3, 0); break; + case 5: GetChrData(0, x >> 3, 1); break; + case 6: GetChrData(0, x >> 3, 0); break; + case 7: GetChrData(0, x >> 3, 1); break; + } + } + } else if(_bgMode == 6) { + for(int x = _fetchStartX; x <= _fetchEndX; x++) { + switch(x & 0x07) { + case 0: GetTilemapData(1, x >> 3); break; + case 1: GetTilemapData(0, x >> 3); break; + + case 2: GetHorizontalOffsetByte(x >> 3); break; + case 3: GetVerticalOffsetByte(x >> 3); break; + + case 4: GetChrData(0, x >> 3, 0); break; + case 5: GetChrData(0, x >> 3, 1); break; + case 6: GetChrData(0, x >> 3, 0); break; + case 7: GetChrData(0, x >> 3, 1); break; + } + } + } +} + bool Ppu::ProcessEndOfScanline(uint16_t hClock) { if(hClock >= 1364 || (hClock == 1360 && _scanline == 240 && _oddFrame && !_screenInterlace)) { @@ -139,6 +377,9 @@ bool Ppu::ProcessEndOfScanline(uint16_t hClock) _drawStartX = 0; _drawEndX = 0; + _fetchStartX = 0; + _fetchEndX = 0; + _pixelsDrawn = 0; _subPixelsDrawn = 0; memset(_rowPixelFlags, 0, sizeof(_rowPixelFlags)); @@ -465,6 +706,18 @@ void Ppu::RenderScanline() if(_drawStartX > 255 || (_allowFrameSkip && (_frameCount & 0x03) != 0)) { return; } + + _fetchEndX = std::min((_memoryManager->GetHClock() - 6*4) >> 2, 263); + if(_fetchStartX > _fetchEndX) { + return; + } + FetchTileData(); + _fetchStartX = _fetchEndX + 1; + + if(_memoryManager->GetHClock() < 22 * 4) { + return; + } + _drawEndX = std::min((_memoryManager->GetHClock() - 22*4) >> 2, 255); uint8_t bgMode = _bgMode; @@ -597,7 +850,7 @@ void Ppu::RenderSprites() } } -template +template void Ppu::RenderTilemap() { if(!IsRenderRequired(layerIndex)) { @@ -621,39 +874,11 @@ void Ppu::RenderTilemap() /* The current layer's options */ LayerConfig &config = _layerConfig[layerIndex]; - /* Layer's tilemap start address */ - uint16_t tilemapAddr = config.TilemapAddress >> 1; - - /* Layer's CHR data start address */ - uint16_t chrAddr = config.ChrAddress; - - /* The current row of tiles (e.g scanlines 16-23 is row 2) */ - uint16_t row = (realY + config.VScroll) >> (largeTileHeight ? 4 : 3); - - /* The vertical offset to read in the tile we're processing */ - uint8_t baseYOffset = (realY + config.VScroll) & 0x07; - - /* Tilemap offset based on the current row & tilemap size options */ - uint16_t addrVerticalScrollingOffset = config.DoubleHeight ? ((row & 0x20) << (config.DoubleWidth ? 6 : 5)) : 0; - - /* The start address for tiles on this row */ - uint16_t baseOffset = tilemapAddr + addrVerticalScrollingOffset + ((row & 0x1F) << 5); - - uint16_t vScroll = config.VScroll; uint16_t hScroll = hiResMode ? (config.HScroll << 1) : config.HScroll; - //"Offset per tile" mode (modes 2, 4 and 6 support this) - bool offsetPerTileMode = _bgMode == 2 || _bgMode == 4 || _bgMode == 6; - /* The current pixel x position (normally 0-255, but 0-511 in hi-res mode - even on subscreen, odd on main screen) */ uint16_t realX; - - /* The current column index (in terms of 8x8 or 16x16 tiles) */ - uint16_t column; - - /* The tilemap address to read the tile data from */ - uint16_t addr; - + for(int x = _drawStartX; x <= _drawEndX; x++) { if(hiResMode) { realX = (x << 1) + (forMainScreen ? 1 : 0); @@ -661,23 +886,27 @@ void Ppu::RenderTilemap() realX = x; } - if(offsetPerTileMode) { - ProcessOffsetMode(x, realX, realY, hScroll, vScroll, addr); - - //Need to recalculate this because vScroll may change from one tile to another - baseYOffset = (realY + vScroll) & 0x07; + int lookupIndex; + int chrDataOffset; + if(hiResMode) { + lookupIndex = (x + (config.HScroll & 0x07)) >> 2; + chrDataOffset = lookupIndex & 0x01; + lookupIndex >>= 1; } else { - column = (realX + hScroll) >> (largeTileWidth ? 4 : 3); - addr = (baseOffset + (column & 0x1F) + (config.DoubleWidth ? ((column & 0x20) << 5) : 0)) << 1; + lookupIndex = (x + (config.HScroll & 0x07)) >> 3; + chrDataOffset = 0; } + TileData &tileData = _layerData[layerIndex].Tiles[lookupIndex]; + uint16_t tilemapData = tileData.TilemapData; + //Skip pixels that were filled by previous layers (or that don't match the priority level currently being processed) if(forMainScreen) { - if((!applyMosaic && _rowPixelFlags[x]) || ((uint8_t)processHighPriority != ((_vram[addr + 1] & 0x20) >> 5))) { + if((!applyMosaic && _rowPixelFlags[x]) || ((uint8_t)processHighPriority != ((tilemapData & 0x2000) >> 13))) { continue; } } else { - if((!applyMosaic && _subScreenFilled[x]) || ((uint8_t)processHighPriority != ((_vram[addr + 1] & 0x20) >> 5))) { + if((!applyMosaic && _subScreenFilled[x]) || ((uint8_t)processHighPriority != ((tilemapData & 0x2000) >> 13))) { continue; } } @@ -688,32 +917,17 @@ void Ppu::RenderTilemap() } //The pixel is empty, not clipped and not part of a mosaic pattern, process it - bool vMirror = (_vram[addr + 1] & 0x80) != 0; - bool hMirror = (_vram[addr + 1] & 0x40) != 0; + bool hMirror = (tilemapData & 0x4000) != 0; - uint16_t tileIndex = ((_vram[addr + 1] & 0x03) << 8) | _vram[addr]; - if(largeTileWidth || largeTileHeight) { - tileIndex = ( - tileIndex + - (largeTileHeight ? (((realY + vScroll) & 0x08) ? (vMirror ? 0 : 16) : (vMirror ? 16 : 0)) : 0) + - (largeTileWidth ? (((realX + hScroll) & 0x08) ? (hMirror ? 0 : 1) : (hMirror ? 1 : 0)) : 0) - ) & 0x3FF; - } - - uint16_t tileStart = chrAddr + tileIndex * 8 * bpp; - - uint8_t yOffset = vMirror ? (7 - baseYOffset) : baseYOffset; - uint16_t pixelStart = tileStart + yOffset * 2; - uint8_t xOffset = (realX + hScroll) & 0x07; uint8_t shift = hMirror ? xOffset : (7 - xOffset); - uint16_t color = GetTilePixelColor(pixelStart, shift); + uint8_t color = GetTilePixelColor(tileData.ChrData + (chrDataOffset ? bpp / 2 : 0), shift); if(color > 0) { uint16_t paletteColor; if(bpp == 8 && directColorMode) { - uint8_t palette = (_vram[addr + 1] >> 2) & 0x07; + uint8_t palette = (tilemapData >> 10) & 0x07; paletteColor = ( ((((color & 0x07) << 1) | (palette & 0x01)) << 1) | (((color & 0x38) | ((palette & 0x02) << 1)) << 4) | @@ -721,7 +935,7 @@ void Ppu::RenderTilemap() ); } else { /* Ignore palette bits for 256-color layers */ - uint8_t palette = bpp == 8 ? 0 : (_vram[addr + 1] >> 2) & 0x07; + uint8_t palette = bpp == 8 ? 0 : (tilemapData >> 10) & 0x07; uint16_t paletteRamOffset = basePaletteOffset + (palette * (1 << bpp) + color) * 2; paletteColor = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8); } @@ -756,61 +970,6 @@ void Ppu::RenderTilemap() } } -template -void Ppu::ProcessOffsetMode(uint8_t x, uint16_t realX, uint16_t realY, uint16_t &hScroll, uint16_t &vScroll, uint16_t &addr) -{ - constexpr uint16_t enableBit = layerIndex == 0 ? 0x2000 : 0x4000; - LayerConfig &config = _layerConfig[layerIndex]; - - hScroll = hiResMode ? (config.HScroll << 1) : config.HScroll; - vScroll = config.VScroll; - - //TODO: Check+fix behavior with 16x16 tiles - //TODO: Test mode 4/6 behavior - int columnIndex = (realX + (hScroll & 0x07)) >> (hiResMode ? 4 : 3); - if(columnIndex > 0) { - //For all tiles after the first tile on the row, check if an active offset exists and use it - uint16_t columnOffset = ((((columnIndex - 1) << 3) + (_layerConfig[2].HScroll & ~0x07)) >> 3) & (_layerConfig[2].DoubleWidth ? 0x3F : 0x1F); - uint16_t rowOffset = (_layerConfig[2].VScroll >> 3) & (_layerConfig[2].DoubleHeight ? 0x3F : 0x1F); - - uint16_t tileOffset = (columnOffset << 1) + (rowOffset << 6); - uint16_t hOffsetAddr = _layerConfig[2].TilemapAddress + tileOffset; - - if(_bgMode == 4) { - uint16_t offsetValue = _vram[hOffsetAddr] | (_vram[hOffsetAddr + 1] << 8); - - if((offsetValue & 0x8000) == 0 && (offsetValue & enableBit)) { - hScroll = (hScroll & 0x07) | (offsetValue & 0x3F8); - } - if((offsetValue & 0x8000) != 0 && (offsetValue & enableBit)) { - vScroll = (offsetValue & 0x3FF); - } - } else { - //The vertical offset is 0x40 bytes later - but wraps around within the tilemap based on the tilemap size (0x800 or 0x1000 bytes) - uint16_t vOffsetAddr = _layerConfig[2].TilemapAddress + ((tileOffset + 0x40) & (_layerConfig[2].DoubleHeight ? 0xFFF : 0x7FF)); - - uint16_t hOffsetValue = _vram[hOffsetAddr] | (_vram[hOffsetAddr + 1] << 8); - uint16_t vOffsetValue = _vram[vOffsetAddr] | (_vram[vOffsetAddr + 1] << 8); - - if(hOffsetValue & enableBit) { - hScroll = (hScroll & 0x07) | (hOffsetValue & 0x3F8); - } - if(vOffsetValue & enableBit) { - vScroll = (vOffsetValue & 0x3FF); - } - } - } - - //Recalculate the tile's address based on the new scroll offsets - uint16_t tilemapAddr = config.TilemapAddress >> 1; - uint16_t offsetModeRow = (realY + vScroll) >> (largeTileHeight ? 4 : 3); - uint16_t offsetModeColumn = (realX + hScroll) >> (largeTileWidth ? 4 : 3); - - uint16_t addrVerticalScrollingOffset = config.DoubleHeight ? ((offsetModeRow & 0x20) << (config.DoubleWidth ? 6 : 5)) : 0; - uint16_t offsetModeBaseAddress = tilemapAddr + addrVerticalScrollingOffset + ((offsetModeRow & 0x1F) << 5); - addr = (offsetModeBaseAddress + (offsetModeColumn & 0x1F) + (config.DoubleWidth ? ((offsetModeColumn & 0x20) << 5) : 0)) << 1; -} - template bool Ppu::IsRenderRequired(uint8_t layerIndex) { @@ -828,6 +987,33 @@ bool Ppu::IsRenderRequired(uint8_t layerIndex) return true; } +template +uint8_t Ppu::GetTilePixelColor(const uint16_t chrData[4], const uint8_t shift) +{ + uint8_t color; + if(bpp == 2) { + color = (chrData[0] >> shift) & 0x01; + color |= (chrData[0] >> (7 + shift)) & 0x02; + } else if(bpp == 4) { + color = (chrData[0] >> shift) & 0x01; + color |= (chrData[0] >> (7 + shift)) & 0x02; + color |= ((chrData[1] >> shift) & 0x01) << 2; + color |= ((chrData[1] >> (7 + shift)) & 0x02) << 2; + } else if(bpp == 8) { + color = (chrData[0] >> shift) & 0x01; + color |= (chrData[0] >> (7 + shift)) & 0x02; + color |= ((chrData[1] >> shift) & 0x01) << 2; + color |= ((chrData[1] >> (7 + shift)) & 0x02) << 2; + color |= ((chrData[2] >> shift) & 0x01) << 4; + color |= ((chrData[2] >> (7 + shift)) & 0x02) << 4; + color |= ((chrData[3] >> shift) & 0x01) << 6; + color |= ((chrData[3] >> (7 + shift)) & 0x02) << 6; + } else { + throw std::runtime_error("unsupported bpp"); + } + return color; +} + template uint16_t Ppu::GetTilePixelColor(const uint16_t pixelStart, const uint8_t shift) { @@ -1809,35 +1995,45 @@ void Ppu::Serialize(Serializer &s) ); } + for(int i = 0; i < 4; i++) { + for(int j = 0; j < 33; j++) { + s.Stream( + _layerData[i].Tiles[j].ChrData[0], _layerData[i].Tiles[j].ChrData[1], _layerData[i].Tiles[j].ChrData[2], _layerData[i].Tiles[j].ChrData[3], + _layerData[i].Tiles[j].TilemapData, _layerData[i].Tiles[j].VScroll + ); + } + } + s.Stream(_hOffset, _vOffset, _fetchStartX, _fetchEndX); + s.StreamArray(_vram, Ppu::VideoRamSize); s.StreamArray(_oamRam, Ppu::SpriteRamSize); s.StreamArray(_cgram, Ppu::CgRamSize); } /* Everything below this point is used to select the proper arguments for templates */ -template +template void Ppu::RenderTilemap() { if(_directColorMode) { - RenderTilemap(); + RenderTilemap(); } else { - RenderTilemap(); + RenderTilemap(); } } -template +template void Ppu::RenderTilemap() { bool applyMosaic = forMainScreen && ((_mosaicEnabled >> layerIndex) & 0x01) != 0 && (_mosaicSize > 1 || _bgMode == 5 || _bgMode == 6); if(applyMosaic) { - RenderTilemap(); + RenderTilemap(); } else { - RenderTilemap(); + RenderTilemap(); } } -template +template void Ppu::RenderTilemap() { uint8_t activeWindowCount = 0; @@ -1846,32 +2042,11 @@ void Ppu::RenderTilemap() } if(activeWindowCount == 0) { - RenderTilemap(); + RenderTilemap(); } else if(activeWindowCount == 1) { - RenderTilemap(); + RenderTilemap(); } else { - RenderTilemap(); - } -} - -template -void Ppu::RenderTilemap() -{ - bool largeTileWidth = _layerConfig[layerIndex].LargeTiles | hiResMode; - bool largeTileHeight = _layerConfig[layerIndex].LargeTiles; - - if(largeTileWidth) { - if(largeTileHeight) { - RenderTilemap(); - } else { - RenderTilemap(); - } - } else { - if(largeTileHeight) { - RenderTilemap(); - } else { - RenderTilemap(); - } + RenderTilemap(); } } diff --git a/Core/Ppu.h b/Core/Ppu.h index 0ab9875..599b174 100644 --- a/Core/Ppu.h +++ b/Core/Ppu.h @@ -7,22 +7,6 @@ class Console; class InternalRegisters; class MemoryManager; -struct SpriteInfo -{ - uint8_t Index; - int16_t X; - uint8_t Y; - bool HorizontalMirror; - bool VerticalMirror; - uint8_t Priority; - - uint8_t TileColumn; - uint8_t TileRow; - uint8_t Palette; - bool UseSecondTable; - uint8_t LargeSprite; -}; - class Ppu : public ISerializable { public: @@ -38,6 +22,12 @@ private: shared_ptr _regs; shared_ptr _memoryManager; + LayerData _layerData[4]; + uint16_t _hOffset = 0; + uint16_t _vOffset = 0; + uint16_t _fetchStartX = 0; + uint16_t _fetchEndX = 0; + bool _forcedVblank = false; uint8_t _screenBrightness = 0; @@ -145,6 +135,16 @@ private: template void RenderSprites(); + template + void GetTilemapData(uint8_t layerIndex, uint8_t columnIndex); + + template + void GetChrData(uint8_t layerIndex, uint8_t column, uint8_t plane); + + void GetHorizontalOffsetByte(uint8_t columnIndex); + void GetVerticalOffsetByte(uint8_t columnIndex); + void FetchTileData(); + template void RenderMode0(); template void RenderMode1(); template void RenderMode2(); @@ -163,24 +163,21 @@ private: template __forceinline void RenderTilemap(); - template + template __forceinline void RenderTilemap(); - template + template __forceinline void RenderTilemap(); - template - __forceinline void RenderTilemap(); - - template + template void RenderTilemap(); - template - void ProcessOffsetMode(uint8_t x, uint16_t realX, uint16_t realY, uint16_t &hScroll, uint16_t &vScroll, uint16_t &addr); - template __forceinline bool IsRenderRequired(uint8_t layerIndex); + template + uint8_t GetTilePixelColor(const uint16_t chrData[4], const uint8_t shift); + template __forceinline uint16_t GetTilePixelColor(const uint16_t pixelStart, const uint8_t shift); diff --git a/Core/PpuTypes.h b/Core/PpuTypes.h index 6cc3f04..f2119ac 100644 --- a/Core/PpuTypes.h +++ b/Core/PpuTypes.h @@ -1,6 +1,34 @@ #pragma once #include "stdafx.h" +struct SpriteInfo +{ + uint8_t Index; + int16_t X; + uint8_t Y; + bool HorizontalMirror; + bool VerticalMirror; + uint8_t Priority; + + uint8_t TileColumn; + uint8_t TileRow; + uint8_t Palette; + bool UseSecondTable; + uint8_t LargeSprite; +}; + +struct TileData +{ + uint16_t TilemapData; + uint16_t VScroll; + uint16_t ChrData[4]; +}; + +struct LayerData +{ + TileData Tiles[33]; +}; + struct LayerConfig { uint16_t TilemapAddress;