From a009e899a208991b773299a7856a21bd53e341bd Mon Sep 17 00:00:00 2001 From: Sour Date: Fri, 22 Feb 2019 20:15:55 -0500 Subject: [PATCH] PPU: Window support (except color window) --- Core/Ppu.cpp | 130 +++++++++++++++++++++++++++++++++++++++++++++++- Core/Ppu.h | 12 +++++ Core/PpuTypes.h | 34 +++++++++++++ 3 files changed, 174 insertions(+), 2 deletions(-) diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index 3c4d57e..43312ed 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -126,17 +126,32 @@ void Ppu::Exec() template void Ppu::RenderSprites() { + uint8_t activeWindowCount = 0; if(forMainScreen) { if(_pixelsDrawn == 256 || (_mainScreenLayers & 0x10) == 0) { return; } - } else if(_subPixelsDrawn == 256 || (_subScreenLayers & 0x10) == 0) { - return; + if(_windowMaskMain[Ppu::SpriteLayerIndex]) { + activeWindowCount = (uint8_t)_window[0].ActiveLayers[Ppu::SpriteLayerIndex] + (uint8_t)_window[1].ActiveLayers[Ppu::SpriteLayerIndex]; + } + } else { + if(_subPixelsDrawn == 256 || (_subScreenLayers & 0x10) == 0) { + return; + } + + if(_windowMaskSub[Ppu::SpriteLayerIndex]) { + activeWindowCount = (uint8_t)_window[0].ActiveLayers[Ppu::SpriteLayerIndex] + (uint8_t)_window[1].ActiveLayers[Ppu::SpriteLayerIndex]; + } } if(forMainScreen) { for(int x = 0; x < 256; x++) { if(!_rowPixelFlags[x] && _spritePriority[x] == priority) { + if(activeWindowCount && ProcessMaskWindow(activeWindowCount, x)) { + //This pixel was masked + continue; + } + _currentBuffer[(_scanline << 8) | x] = _spritePixels[x]; _rowPixelFlags[x] |= PixelFlags::Filled | (((_colorMathEnabled & 0x10) && _spritePalette[x] > 3) ? PixelFlags::AllowColorMath : 0); } @@ -144,6 +159,11 @@ void Ppu::RenderSprites() } else { for(int x = 0; x < 256; x++) { if(!_subScreenFilled[x] && _spritePriority[x] == priority) { + if(activeWindowCount && ProcessMaskWindow(activeWindowCount, x)) { + //This pixel was masked + continue; + } + _subScreenBuffer[x] = _spritePixels[x]; _subScreenFilled[x] = true; } @@ -431,6 +451,11 @@ void Ppu::RenderTilemap() } } + uint8_t activeWindowCount = 0; + if((forMainScreen && _windowMaskMain[layerIndex]) || (!forMainScreen && _windowMaskSub[layerIndex])) { + activeWindowCount = (uint8_t)_window[0].ActiveLayers[layerIndex] + (uint8_t)_window[1].ActiveLayers[layerIndex]; + } + bool applyMosaic = forMainScreen && ((_mosaicEnabled >> layerIndex) & 0x01) != 0; bool mosaicScanline = applyMosaic && (_scanline - _mosaicStartScanline) % _mosaicSize != 0; @@ -459,6 +484,11 @@ void Ppu::RenderTilemap() } } + if(activeWindowCount && ProcessMaskWindow(activeWindowCount, x)) { + //This pixel was masked + continue; + } + if(mosaicScanline || (applyMosaic && x % _mosaicSize != 0)) { //If this is not the top-left pixels in the mosaic pattern, override it with the top-left pixel data _currentBuffer[(_scanline << 8) | x] = _mosaicColor[x]; @@ -569,6 +599,39 @@ void Ppu::ApplyColorMath() } } +template +bool Ppu::ProcessMaskWindow(uint8_t activeWindowCount, int x) +{ + if(activeWindowCount == 1) { + if(_window[0].ActiveLayers[layerIndex]) { + return _window[0].PixelNeedsMasking(x); + } else { + return _window[1].PixelNeedsMasking(x); + } + } else { + switch(_maskLogic[layerIndex]) { + default: + case WindowMaskLogic::Or: return _window[0].PixelNeedsMasking(x) | _window[1].PixelNeedsMasking(x); + case WindowMaskLogic::And: return _window[0].PixelNeedsMasking(x) & _window[1].PixelNeedsMasking(x); + case WindowMaskLogic::Xor: return _window[0].PixelNeedsMasking(x) ^ _window[1].PixelNeedsMasking(x); + case WindowMaskLogic::Xnor: return !(_window[0].PixelNeedsMasking(x) ^ _window[1].PixelNeedsMasking(x)); + } + } +} + +void Ppu::ProcessWindowMaskSettings(uint8_t value, uint8_t offset) +{ + _window[0].ActiveLayers[0 + offset] = (value & 0x02) != 0; + _window[0].ActiveLayers[1 + offset] = (value & 0x20) != 0; + _window[0].InvertedLayers[0 + offset] = (value & 0x01) != 0; + _window[0].InvertedLayers[1 + offset] = (value & 0x10) != 0; + + _window[1].ActiveLayers[0 + offset] = (value & 0x08) != 0; + _window[1].ActiveLayers[1 + offset] = (value & 0x80) != 0; + _window[1].InvertedLayers[0 + offset] = (value & 0x04) != 0; + _window[1].InvertedLayers[1 + offset] = (value & 0x40) != 0; +} + void Ppu::SendFrame() { _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone); @@ -876,6 +939,55 @@ void Ppu::Write(uint32_t addr, uint8_t value) _cgramAddress = (_cgramAddress + 1) & (Ppu::CgRamSize - 1); break; + case 0x2123: + //W12SEL - Window Mask Settings for BG1 and BG2 + ProcessWindowMaskSettings(value, 0); + break; + + case 0x2124: + //W34SEL - Window Mask Settings for BG3 and BG4 + ProcessWindowMaskSettings(value, 2); + break; + + case 0x2125: + //WOBJSEL - Window Mask Settings for OBJ and Color Window + ProcessWindowMaskSettings(value, 4); + break; + + case 0x2126: + //WH0 - Window 1 Left Position + _window[0].Left = value; + break; + + case 0x2127: + //WH1 - Window 1 Right Position + _window[0].Right = value; + break; + + case 0x2128: + //WH2 - Window 2 Left Position + _window[1].Left = value; + break; + + case 0x2129: + //WH3 - Window 2 Right Position + _window[1].Right = value; + break; + + case 0x212A: + //WBGLOG - Window mask logic for BG + _maskLogic[0] = (WindowMaskLogic)(value & 0x03); + _maskLogic[1] = (WindowMaskLogic)((value >> 2) & 0x03); + _maskLogic[2] = (WindowMaskLogic)((value >> 4) & 0x03); + _maskLogic[3] = (WindowMaskLogic)((value >> 6) & 0x03); + break; + + case 0x212B: + //WOBJLOG - Window mask logic for OBJs and Color Window + _maskLogic[5] = (WindowMaskLogic)((value >> 0) & 0x03); + _maskLogic[6] = (WindowMaskLogic)((value >> 2) & 0x03); + break; + case 0x212C: //TM - Main Screen Designation _mainScreenLayers = value & 0x1F; @@ -885,6 +997,20 @@ void Ppu::Write(uint32_t addr, uint8_t value) //TS - Subscreen Designation _subScreenLayers = value & 0x1F; break; + + case 0x212E: + //TMW - Window Mask Designation for the Main Screen + for(int i = 0; i < 5; i++) { + _windowMaskMain[i] = ((value >> i) & 0x01) != 0; + } + break; + + case 0x212F: + //TSW - Window Mask Designation for the Subscreen + for(int i = 0; i < 5; i++) { + _windowMaskSub[i] = ((value >> i) & 0x01) != 0; + } + break; case 0x2130: //CGWSEL - Color Addition Select diff --git a/Core/Ppu.h b/Core/Ppu.h index 86a42ae..cde284b 100644 --- a/Core/Ppu.h +++ b/Core/Ppu.h @@ -27,6 +27,8 @@ public: constexpr static uint32_t VideoRamSize = 0x10000; private: + constexpr static int SpriteLayerIndex = 4; + constexpr static const uint8_t _oamSizes[8][2][2] = { { { 1, 1 }, { 2, 2 } }, //8x8 + 16x16 { { 1, 1 }, { 4, 4 } }, //8x8 + 32x32 @@ -54,6 +56,11 @@ private: uint8_t _mainScreenLayers = 0; uint8_t _subScreenLayers = 0; LayerConfig _layerConfig[4]; + + WindowConfig _window[2]; + WindowMaskLogic _maskLogic[6]; + bool _windowMaskMain[5]; + bool _windowMaskSub[5]; uint8_t *_vram; uint16_t _vramAddress; @@ -150,6 +157,11 @@ private: void RenderScanline(); void ApplyColorMath(); + + template + bool ProcessMaskWindow(uint8_t activeWindowCount, int x); + void ProcessWindowMaskSettings(uint8_t value, uint8_t offset); + void SendFrame(); public: diff --git a/Core/PpuTypes.h b/Core/PpuTypes.h index 9e9f72f..f81ff6a 100644 --- a/Core/PpuTypes.h +++ b/Core/PpuTypes.h @@ -20,4 +20,38 @@ struct LayerConfig bool VerticalMirroring; bool LargeTiles; +}; + +struct WindowConfig +{ + bool ActiveLayers[6]; + bool InvertedLayers[6]; + uint8_t Left; + uint8_t Right; + + template + bool PixelNeedsMasking(int x) + { + if(InvertedLayers[layerIndex]) { + if(Left > Right) { + return true; + } else { + return x < Left || x > Right; + } + } else { + if(Left > Right) { + return false; + } else { + return x >= Left && x <= Right; + } + } + } +}; + +enum class WindowMaskLogic +{ + Or = 0, + And = 1, + Xor = 2, + Xnor = 3 }; \ No newline at end of file