diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index c3e220d..295b95c 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -16,6 +16,7 @@ Ppu::Ppu(shared_ptr console) _outputBuffers[0] = new uint16_t[256 * 224]; _outputBuffers[1] = new uint16_t[256 * 224]; + _subScreenBuffer = new uint16_t[256 * 224]; _currentBuffer = _outputBuffers[0]; @@ -38,6 +39,7 @@ Ppu::Ppu(shared_ptr console) Ppu::~Ppu() { delete[] _vram; + delete[] _subScreenBuffer; delete[] _outputBuffers[0]; delete[] _outputBuffers[1]; } @@ -61,7 +63,7 @@ void Ppu::Exec() if(_cycle == 340) { _cycle = -1; _scanline++; - if(_scanline < 225) { + if(_scanline < 224) { RenderScanline(); } else if(_scanline == 225) { //Reset OAM address at the start of vblank? @@ -80,6 +82,7 @@ void Ppu::Exec() } else if(_scanline == 261) { _regs->SetNmiFlag(false); _scanline = 0; + RenderScanline(); } if(_regs->IsVerticalIrqEnabled() && !_regs->IsHorizontalIrqEnabled() && _scanline == _regs->GetVerticalTimer()) { @@ -115,19 +118,124 @@ uint8_t _spriteCount = 0; uint8_t _spritePriority[256] = {}; uint16_t _spritePixels[256] = {}; -void Ppu::RenderScanline() +template +void Ppu::DrawSprites() { - for(int x = 0; x < 256; x++) { - if(_spritePriority[x] != 0xFF && _spritePixels[x] != 0xFFFF) { - _currentBuffer[(_scanline << 8) | x] = _spritePixels[x]; + if(forMainScreen) { + for(int x = 0; x < 256; x++) { + if(!_filled[x] && _spritePriority[x] == priority) { + _currentBuffer[(_scanline << 8) | x] = _spritePixels[x]; + _filled[x] = true; + } + } + } else { + for(int x = 0; x < 256; x++) { + if(!_subScreenFilled[x] && _spritePriority[x] == priority) { + _subScreenBuffer[(_scanline << 8) | x] = _spritePixels[x]; + _subScreenFilled[x] = true; + } } } +} -/* switch(_bgMode) { - case 1: +void Ppu::RenderScanline() +{ + memset(_filled, 0, sizeof(_filled)); + memset(_subScreenFilled, 0, sizeof(_subScreenFilled)); + + switch(_bgMode) { + case 0: + DrawSprites<3, true>(); + RenderTilemap<0, 2, true, true>(); + RenderTilemap<1, 2, true, true>(); + DrawSprites<2, true>(); + RenderTilemap<0, 2, false, true>(); + RenderTilemap<1, 2, false, true>(); + DrawSprites<1, true>(); + RenderTilemap<2, 2, true, true>(); + RenderTilemap<3, 2, true, true>(); + DrawSprites<0, true>(); + RenderTilemap<2, 2, false, true>(); + RenderTilemap<3, 2, false, true>(); + RenderBgColor(); break; - }*/ + case 1: + //Main screen + if(_mode1Bg3Priority) { + RenderTilemap<2, 2, true, true>(); + } + DrawSprites<3, true>(); + RenderTilemap<0, 4, true, true>(); + RenderTilemap<1, 4, true, true>(); + DrawSprites<2, true>(); + RenderTilemap<0, 4, false, true>(); + RenderTilemap<1, 4, false, true>(); + DrawSprites<1, true>(); + if(!_mode1Bg3Priority) { + RenderTilemap<2, 2, true, true>(); + } + DrawSprites<0, true>(); + RenderTilemap<2, 2, false, true>(); + RenderBgColor(); + + //Subscreen + if(_mode1Bg3Priority) { + RenderTilemap<2, 2, true, false>(); + } + DrawSprites<3, false>(); + RenderTilemap<0, 4, true, false>(); + RenderTilemap<1, 4, true, false>(); + DrawSprites<2, false>(); + RenderTilemap<0, 4, false, false>(); + RenderTilemap<1, 4, false, false>(); + DrawSprites<1, false>(); + if(!_mode1Bg3Priority) { + RenderTilemap<2, 2, true, false>(); + } + DrawSprites<0, true>(); + RenderTilemap<2, 2, false, false>(); + RenderBgColor(); + + ApplyColorMath(); + break; + + case 2: + DrawSprites<3, true>(); + RenderTilemap<0, 4, true, true>(); + DrawSprites<2, true>(); + RenderTilemap<1, 4, true, true>(); + DrawSprites<1, true>(); + RenderTilemap<0, 4, false, true>(); + DrawSprites<0, true>(); + RenderTilemap<1, 4, false, true>(); + RenderBgColor(); + break; + + case 3: + DrawSprites<3, true>(); + RenderTilemap<0, 8, true, true>(); + DrawSprites<2, true>(); + RenderTilemap<1, 4, true, true>(); + DrawSprites<1, true>(); + RenderTilemap<0, 8, false, true>(); + DrawSprites<0, true>(); + RenderTilemap<1, 4, false, true>(); + RenderBgColor(); + break; + + case 5: + RenderTilemap<1, 2, false, true>(); + RenderTilemap<0, 4, false, true>(); + RenderBgColor(); + break; + + case 6: + RenderTilemap<0, 8, false, true>(); + RenderBgColor(); + break; + } + //Process sprites for next scanline memset(_spritePriority, 0xFF, sizeof(_spritePriority)); memset(_spritePixels, 0xFFFF, sizeof(_spritePixels)); @@ -141,7 +249,7 @@ void Ppu::RenderScanline() uint8_t largeSprite = (highTableValue & 0x02) >> 1; uint8_t height = _oamSizes[_oamMode][largeSprite][1] << 3; - if(y > _scanline + 1 || y + height < _scanline + 1) { + if(y > _scanline + 1 || y + height <= _scanline + 1) { //Not visible on this scanline continue; } @@ -180,8 +288,8 @@ void Ppu::RenderScanline() uint8_t xOffset; int columnOffset; if(info.HorizontalMirror) { - xOffset = (width - (x - info.X)) & 0x07; - columnOffset = (width - (x - info.X)) >> 3; + xOffset = (width - (x - info.X) - 1) & 0x07; + columnOffset = (width - (x - info.X) - 1) >> 3; } else { xOffset = (x - info.X) & 0x07; columnOffset = (x - info.X) >> 3; @@ -215,175 +323,114 @@ void Ppu::RenderScanline() } -void Ppu::RenderTilemap(uint8_t layerIndex, uint8_t bpp) +template +void Ppu::RenderBgColor() { - if(((_mainScreenLayers >> layerIndex) & 0x01) == 0) { - //This screen is disabled + uint16_t bgColor = _cgram[0] | (_cgram[1] << 8); + for(int x = 0; x < 256; x++) { + if(forMainScreen) { + if(!_filled[x]) { + _currentBuffer[(_scanline << 8) | x] = bgColor; + } + } else { + if(!_subScreenFilled[x]) { + _subScreenBuffer[(_scanline << 8) | x] = bgColor; + } + } + } +} + +template +void Ppu::RenderTilemap() +{ + if(forMainScreen) { + if(((_mainScreenLayers >> layerIndex) & 0x01) == 0) { + //This screen is disabled + return; + } + } else { + if(((_subScreenLayers >> layerIndex) & 0x01) == 0) { + //This screen is disabled + return; + } + } + + LayerConfig &config = _layerConfig[layerIndex]; + uint16_t tilemapAddr = config.TilemapAddress; + uint16_t chrAddr = config.ChrAddress; + uint32_t addr = tilemapAddr + ((_scanline >> 3) << 6) - 2; + + for(int x = 0; x < 256; x++) { + if((x & 0x07) == 0) { + addr += 2; + } + + if(forMainScreen) { + if(_filled[x] || ((uint8_t)processHighPriority != ((_vram[addr + 1] & 0x20) >> 5))) { + continue; + } + } else { + if(_subScreenFilled[x] || ((uint8_t)processHighPriority != ((_vram[addr + 1] & 0x20) >> 5))) { + continue; + } + } + + uint8_t palette = (_vram[addr + 1] >> 2) & 0x07; + uint16_t tileIndex = ((_vram[addr + 1] & 0x03) << 8) | _vram[addr]; + bool vMirror = (_vram[addr + 1] & 0x80) != 0; + bool hMirror = (_vram[addr + 1] & 0x40) != 0; + + uint16_t tileStart = chrAddr + tileIndex * 8 * bpp; + uint8_t yOffset = vMirror ? (7 - (_scanline & 0x07)) : (_scanline & 0x07); + + uint16_t color = 0; + uint8_t shift = hMirror ? (x & 0x07) : (7 - (x & 0x07)); + for(int plane = 0; plane < bpp; plane++) { + uint8_t offset = (plane >> 1) * 16; + color |= (((_vram[tileStart + yOffset * 2 + offset + (plane & 0x01)] >> shift) & 0x01) << bpp); + color >>= 1; + } + + if(color > 0) { + uint16_t paletteRamOffset = (palette * (1 << bpp) + color) * 2; + uint16_t paletteColor = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8); + + if(forMainScreen) { + _currentBuffer[(_scanline << 8) | x] = paletteColor; + _filled[x] = true; + } else { + _subScreenBuffer[(_scanline << 8) | x] = paletteColor; + _subScreenFilled[x] = true; + } + } + } +} + +void Ppu::ApplyColorMath() +{ + bool useColorMath = _colorMathEnabled;// >> layerIndex) & 0x01) != 0; + if(!useColorMath) { return; } - bool useColorMath = ((_colorMathEnabled >> layerIndex) & 0x01) != 0; + for(int x = 0; x < 256; x++) { + uint16_t &mainPixel = _currentBuffer[(_scanline << 8) | x]; + uint16_t &subPixel = _subScreenBuffer[(_scanline << 8) | x]; + + uint8_t halfShift = _colorMathHalveResult ? 1 : 0; + uint16_t r = std::min(((mainPixel & 0x001F) + (subPixel & 0x001F)) >> halfShift, 0x1F); + uint16_t g = std::min((((mainPixel >> 5) & 0x001F) + ((subPixel >> 5) & 0x001F)) >> halfShift, 0x1F); + uint16_t b = std::min((((mainPixel >> 10) & 0x001F) + ((subPixel >> 10) & 0x001F)) >> halfShift, 0x1F); - LayerConfig &config = _layerConfig[layerIndex]; - - uint16_t tilemapAddr = config.TilemapAddress; - uint16_t chrAddr = config.ChrAddress; - - uint32_t addr = tilemapAddr; - for(int y = 0; y < 28; y++) { - for(int x = 0; x < 32; x++) { - uint8_t palette = (_vram[addr + 1] >> 2) & 0x07; - uint16_t tileIndex = ((_vram[addr + 1] & 0x03) << 8) | _vram[addr]; - bool vMirror = (_vram[addr + 1] & 0x80) != 0; - bool hMirror = (_vram[addr + 1] & 0x40) != 0; - - uint16_t tileStart = chrAddr + tileIndex * 8 * bpp; - for(int i = 0; i < 8; i++) { - uint8_t yOffset = vMirror ? (7 - i) : i; - for(int j = 0; j < 8; j++) { - uint16_t color = 0; - uint8_t shift = hMirror ? j : (7 - j); - for(int plane = 0; plane < bpp; plane++) { - uint8_t offset = (plane >> 1) * 16; - color |= (((_vram[tileStart + i * 2 + offset + (plane & 0x01)] >> shift) & 0x01) << bpp); - color >>= 1; - } - - if(color > 0) { - uint16_t paletteRamOffset = (palette * (1 << bpp) + color) * 2; - uint16_t paletteColor = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8); - - uint16_t ¤tPixel = _currentBuffer[(y * 8 + yOffset) * 256 + x * 8 + j]; - if(useColorMath && layerIndex == 0) { - uint8_t halfShift = _colorMathHalveResult ? 1 : 0; - uint16_t r = std::min(((currentPixel & 0x001F) + (paletteColor & 0x001F)) >> halfShift, 0x1F); - uint16_t g = std::min((((currentPixel >> 5) & 0x001F) + ((paletteColor >> 5) & 0x001F)) >> halfShift, 0x1F); - uint16_t b = std::min((((currentPixel >> 10) & 0x001F) + ((paletteColor >> 10) & 0x001F)) >> halfShift, 0x1F); - - paletteColor = r | (g << 5) | (b << 10); - } - currentPixel = paletteColor; - } - } - } - addr+=2; - } + mainPixel = r | (g << 5) | (b << 10); } } void Ppu::SendFrame() { - /*uint16_t bgColor = _cgram[0] | (_cgram[1]); - for(int i = 0; i < 256 * 224; i++) { - _currentBuffer[i] = bgColor; - } - - switch(_bgMode) { - case 0: - RenderTilemap(3, 2); - RenderTilemap(2, 2); - RenderTilemap(1, 2); - RenderTilemap(0, 2); - break; - - case 1: - RenderTilemap(2, 2); - RenderTilemap(1, 4); - RenderTilemap(0, 4); - break; - - case 2: - RenderTilemap(1, 4); - RenderTilemap(0, 4); - break; - - case 3: - RenderTilemap(1, 4); - RenderTilemap(0, 8); - break; - - case 5: - RenderTilemap(1, 2); - RenderTilemap(0, 4); - break; - - case 6: - RenderTilemap(0, 8); - break; - } - - //Draw sprites - if(_mainScreenLayers & 0x10) { - DrawSprites(); - } - */ _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone); _console->GetVideoDecoder()->UpdateFrame(_currentBuffer, _frameCount); _currentBuffer = _currentBuffer == _outputBuffers[0] ? _outputBuffers[1] : _outputBuffers[0]; - - uint16_t bgColor = _cgram[0] | (_cgram[1]); - for(int i = 0; i < 256 * 224; i++) { - _currentBuffer[i] = bgColor; - } -} - -void Ppu::DrawSprites() -{ - for(int i = 0; i < 512; i += 4) { - uint8_t y = _oamRam[i + 1]; - if(y >= 225) { - continue; - } - - int16_t x = _oamRam[i]; - uint8_t tileRow = (_oamRam[i + 2] & 0xF0) >> 4; - uint8_t tileColumn = _oamRam[i + 2] & 0x0F; - uint8_t flags = _oamRam[i + 3]; - - uint8_t palette = (flags >> 1) & 0x07; - bool useSecondTable = (flags & 0x01) != 0; - - //TODO: vertical, horizontal flip, priority - - uint8_t highTableOffset = i >> 4; - uint8_t shift = ((i >> 2) & 0x03) << 1; - - uint8_t highTableValue = _oamRam[0x200 | highTableOffset] >> shift; - bool negativeX = (highTableValue & 0x01) != 0; - uint8_t size = (highTableValue & 0x02) >> 1; - - if(negativeX) { - x = -x; - } - - for(int rowOffset = 0; rowOffset < _oamSizes[_oamMode][size][1]; rowOffset++) { - for(int columnOffset = 0; columnOffset < _oamSizes[_oamMode][size][0]; columnOffset++) { - uint8_t column = (tileColumn + columnOffset) & 0x0F; - uint8_t row = (tileRow + rowOffset) & 0x0F; - uint8_t tileIndex = (row << 4) | column; - - uint16_t tileStart = ((_oamBaseAddress + (tileIndex << 4) + (useSecondTable ? _oamAddressOffset : 0)) & 0x7FFF) << 1; - uint16_t bpp = 4; - for(int i = 0; i < 8; i++) { - for(int j = 0; j < 8; j++) { - uint16_t color = 0; - for(int plane = 0; plane < bpp; plane++) { - uint8_t offset = (plane >> 1) * 16; - color |= (((_vram[tileStart + i * 2 + offset + (plane & 0x01)] >> (7 - j)) & 0x01) << bpp); - color >>= 1; - } - - if(color != 0) { - uint16_t paletteRamOffset = 256 + ((palette * (1 << bpp) + color) * 2); - - uint16_t paletteColor = _cgram[paletteRamOffset] | (_cgram[paletteRamOffset + 1] << 8); - _currentBuffer[(y + i + rowOffset * 8) * 256 + x + j + columnOffset * 8] = paletteColor; - } - } - } - } - } - } } uint8_t* Ppu::GetVideoRam() @@ -449,9 +496,7 @@ void Ppu::Write(uint32_t addr, uint8_t value) case 0x2105: _bgMode = value & 0x07; - - //TODO - //_mode1Bg3Priority = (value & 0x08) != 0; + _mode1Bg3Priority = (value & 0x08) != 0; _layerConfig[0].LargeTiles = (value & 0x10) != 0; _layerConfig[1].LargeTiles = (value & 0x20) != 0; @@ -526,6 +571,11 @@ void Ppu::Write(uint32_t addr, uint8_t value) //TM - Main Screen Designation _mainScreenLayers = value & 0x1F; break; + + case 0x212D: + //TS - Subscreen Designation + _subScreenLayers = value & 0x1F; + break; case 0x2130: //CGWSEL - Color Addition Select diff --git a/Core/Ppu.h b/Core/Ppu.h index dd41d0b..1890a00 100644 --- a/Core/Ppu.h +++ b/Core/Ppu.h @@ -32,8 +32,10 @@ private: uint32_t _frameCount = 0; uint8_t _bgMode = 0; + bool _mode1Bg3Priority = false; uint8_t _mainScreenLayers = 0; + uint8_t _subScreenLayers = 0; LayerConfig _layerConfig[4]; uint8_t *_vram; @@ -46,8 +48,12 @@ private: uint8_t _cgram[Ppu::CgRamSize]; uint16_t *_outputBuffers[2]; + bool _filled[256]; uint16_t *_currentBuffer; + bool _subScreenFilled[256]; + uint16_t *_subScreenBuffer; + uint8_t _oamMode = 0; uint16_t _oamBaseAddress = 0; uint16_t _oamAddressOffset = 0; @@ -68,9 +74,19 @@ private: bool _colorMathSubstractMode = false; bool _colorMathHalveResult = false; - void RenderTilemap(uint8_t layerIndex, uint8_t bpp); + template + void RenderBgColor(); + + template + void RenderTilemap(); + + template void DrawSprites(); + void RenderScanline(); + void ApplyColorMath(); + void SendFrame(); + public: Ppu(shared_ptr console); ~Ppu(); @@ -80,10 +96,6 @@ public: void Exec(); - void RenderScanline(); - - void SendFrame(); - uint8_t* GetVideoRam(); uint8_t* GetCgRam(); uint8_t* GetSpriteRam();