2019-02-13 18:44:39 -05:00
# include "stdafx.h"
# include "Ppu.h"
# include "Console.h"
2019-02-13 23:03:01 -05:00
# include "MemoryManager.h"
2019-02-13 18:44:39 -05:00
# include "Cpu.h"
2019-02-16 11:23:01 -05:00
# include "Spc.h"
2019-02-17 15:37:31 -05:00
# include "InternalRegisters.h"
2019-02-17 19:54:29 -05:00
# include "ControlManager.h"
2019-02-13 23:03:01 -05:00
# include "VideoDecoder.h"
2019-02-15 21:33:13 -05:00
# include "NotificationManager.h"
2019-02-26 22:27:09 -05:00
# include "DmaController.h"
# include "MessageManager.h"
# include "../Utilities/HexUtilities.h"
2019-02-13 18:44:39 -05:00
Ppu : : Ppu ( shared_ptr < Console > console )
{
_console = console ;
2019-02-17 15:37:31 -05:00
_regs = console - > GetInternalRegisters ( ) ;
2019-02-13 18:44:39 -05:00
2019-02-23 21:39:35 -05:00
_outputBuffers [ 0 ] = new uint16_t [ 512 * 478 ] ;
_outputBuffers [ 1 ] = new uint16_t [ 512 * 478 ] ;
2019-02-24 11:14:24 -05:00
memset ( _outputBuffers [ 0 ] , 0 , 512 * 478 ) ;
memset ( _outputBuffers [ 1 ] , 0 , 512 * 478 ) ;
2019-02-16 01:16:57 -05:00
_currentBuffer = _outputBuffers [ 0 ] ;
2019-02-13 18:44:39 -05:00
_layerConfig [ 0 ] = { } ;
_layerConfig [ 1 ] = { } ;
_layerConfig [ 2 ] = { } ;
_layerConfig [ 3 ] = { } ;
_cgramAddress = 0 ;
2019-02-16 01:16:57 -05:00
_vram = new uint8_t [ Ppu : : VideoRamSize ] ;
memset ( _vram , 0 , Ppu : : VideoRamSize ) ;
2019-02-24 01:11:26 -05:00
memset ( _oamRam , 0 , Ppu : : SpriteRamSize ) ;
memset ( _cgram , 0 , Ppu : : CgRamSize ) ;
2019-02-16 01:16:57 -05:00
2019-02-13 18:44:39 -05:00
_vramAddress = 0 ;
_vramIncrementValue = 1 ;
_vramAddressRemapping = 0 ;
_vramAddrIncrementOnSecondReg = false ;
}
2019-02-16 01:16:57 -05:00
Ppu : : ~ Ppu ( )
{
delete [ ] _vram ;
delete [ ] _outputBuffers [ 0 ] ;
delete [ ] _outputBuffers [ 1 ] ;
}
2019-02-17 19:54:29 -05:00
uint32_t Ppu : : GetFrameCount ( )
{
return _frameCount ;
}
2019-02-13 18:44:39 -05:00
PpuState Ppu : : GetState ( )
{
return {
_cycle ,
_scanline ,
2019-03-01 22:24:18 -05:00
_frameCount ,
_overscanMode
2019-02-13 18:44:39 -05:00
} ;
}
void Ppu : : Exec ( )
{
if ( _cycle = = 340 ) {
2019-02-16 11:23:01 -05:00
_cycle = - 1 ;
2019-02-13 18:44:39 -05:00
_scanline + + ;
2019-02-19 18:01:27 -05:00
_rangeOver = false ;
_timeOver = false ;
2019-03-01 23:10:22 -05:00
if ( _scanline = = ( _overscanMode ? 240 : 225 ) ) {
2019-02-17 21:09:33 -05:00
//Reset OAM address at the start of vblank?
2019-02-19 18:41:59 -05:00
if ( ! _forcedVblank ) {
_internalOamAddress = ( _oamRamAddress < < 1 ) ;
}
2019-02-17 21:09:33 -05:00
2019-02-17 19:54:29 -05:00
_frameCount + + ;
2019-02-16 11:23:01 -05:00
_console - > GetSpc ( ) - > ProcessEndFrame ( ) ;
2019-02-17 19:54:29 -05:00
_console - > GetControlManager ( ) - > UpdateInputState ( ) ;
_regs - > ProcessAutoJoypadRead ( ) ;
_regs - > SetNmiFlag ( true ) ;
2019-02-13 18:44:39 -05:00
SendFrame ( ) ;
2019-02-17 15:37:31 -05:00
if ( _regs - > IsNmiEnabled ( ) ) {
2019-02-13 18:44:39 -05:00
_console - > GetCpu ( ) - > SetNmiFlag ( ) ;
}
2019-02-21 22:10:41 -05:00
} else if ( _scanline = = 240 & & _cycle = = 0 & & _frameCount & 0x01 ) {
//Skip 1 tick every other frame
_cycle + + ;
2019-02-17 01:09:47 -05:00
} else if ( _scanline = = 261 ) {
2019-02-17 19:54:29 -05:00
_regs - > SetNmiFlag ( false ) ;
2019-02-13 18:44:39 -05:00
_scanline = 0 ;
2019-02-21 22:10:41 -05:00
2019-02-20 17:39:14 -05:00
if ( _mosaicEnabled ) {
_mosaicStartScanline = 0 ;
}
2019-02-19 21:09:12 -05:00
_console - > GetDmaController ( ) - > InitHdmaChannels ( ) ;
2019-02-13 18:44:39 -05:00
}
2019-02-17 01:09:47 -05:00
2019-02-17 15:37:31 -05:00
if ( _regs - > IsVerticalIrqEnabled ( ) & & ! _regs - > IsHorizontalIrqEnabled ( ) & & _scanline = = _regs - > GetVerticalTimer ( ) ) {
2019-02-17 01:09:47 -05:00
//An IRQ will occur sometime just after the V Counter reaches the value set in $4209/$420A.
_console - > GetCpu ( ) - > SetIrqSource ( IrqSource : : Ppu ) ;
}
}
2019-02-17 15:37:31 -05:00
if ( _regs - > IsHorizontalIrqEnabled ( ) & & _cycle = = _regs - > GetHorizontalTimer ( ) & & ( ! _regs - > IsVerticalIrqEnabled ( ) | | _scanline = = _regs - > GetVerticalTimer ( ) ) ) {
2019-02-17 01:09:47 -05:00
//An IRQ will occur sometime just after the H Counter reaches the value set in $4207/$4208.
_console - > GetCpu ( ) - > SetIrqSource ( IrqSource : : Ppu ) ;
2019-02-13 18:44:39 -05:00
}
_cycle + + ;
2019-02-19 21:09:12 -05:00
2019-03-01 23:10:22 -05:00
if ( _cycle = = 278 & & _scanline < = ( _overscanMode ? 239 : 224 ) ) {
if ( _scanline > 0 ) {
RenderScanline ( ) ;
}
2019-02-19 21:09:12 -05:00
_console - > GetDmaController ( ) - > ProcessHdmaChannels ( ) ;
2019-02-21 22:10:41 -05:00
} else if ( _cycle = = 134 ) {
//TODO Approximation
2019-02-21 23:35:51 -05:00
_console - > GetMemoryManager ( ) - > IncrementMasterClockValue < 40 > ( ) ;
2019-02-19 21:09:12 -05:00
}
2019-02-13 18:44:39 -05:00
}
2019-02-22 22:35:53 -05:00
void Ppu : : EvaluateNextLineSprites ( )
{
memset ( _spritePriority , 0xFF , sizeof ( _spritePriority ) ) ;
memset ( _spritePixels , 0xFF , sizeof ( _spritePixels ) ) ;
memset ( _spritePalette , 0 , sizeof ( _spritePalette ) ) ;
_spriteCount = 0 ;
uint16_t totalWidth = 0 ;
2019-02-24 19:21:19 -05:00
uint16_t screenY = _objInterlace ? ( ( _frameCount & 0x01 ) ? ( ( _scanline < < 1 ) + 1 ) : ( _scanline < < 1 ) ) : _scanline ;
2019-02-22 22:35:53 -05:00
for ( int i = 0 ; i < 512 ; i + = 4 ) {
uint8_t y = _oamRam [ i + 1 ] ;
uint8_t highTableOffset = i > > 4 ;
uint8_t shift = ( ( i > > 2 ) & 0x03 ) < < 1 ;
uint8_t highTableValue = _oamRam [ 0x200 | highTableOffset ] > > shift ;
uint8_t largeSprite = ( highTableValue & 0x02 ) > > 1 ;
uint8_t height = _oamSizes [ _oamMode ] [ largeSprite ] [ 1 ] < < 3 ;
2019-02-24 19:21:19 -05:00
if ( y > screenY + 1 | | y + height < = screenY + 1 ) {
2019-02-22 22:35:53 -05:00
//Not visible on this scanline
continue ;
}
SpriteInfo & info = _sprites [ _spriteCount ] ;
info . LargeSprite = largeSprite ;
uint8_t width = _oamSizes [ _oamMode ] [ info . LargeSprite ] [ 0 ] < < 3 ;
2019-02-24 13:09:22 -05:00
uint16_t sign = ( highTableValue & 0x01 ) < < 8 ;
info . X = ( int16_t ) ( ( sign | _oamRam [ i ] ) < < 7 ) > > 7 ;
if ( info . X ! = - 256 & & info . X + width < = 0 | | info . X > 255 ) {
//Sprite is not visible (and must be ignored for time/range flag calculations)
//Sprites at X=-256 are always used when considering Time/Range flag calculations, but not actually drawn.
continue ;
}
2019-02-22 22:35:53 -05:00
info . TileRow = ( _oamRam [ i + 2 ] & 0xF0 ) > > 4 ;
info . TileColumn = _oamRam [ i + 2 ] & 0x0F ;
uint8_t flags = _oamRam [ i + 3 ] ;
info . UseSecondTable = ( flags & 0x01 ) ! = 0 ;
info . Palette = ( flags > > 1 ) & 0x07 ;
info . Priority = ( flags > > 4 ) & 0x03 ;
info . HorizontalMirror = ( flags & 0x40 ) ! = 0 ;
info . VerticalMirror = ( flags & 0x80 ) ! = 0 ;
uint8_t yOffset ;
int rowOffset ;
if ( info . VerticalMirror ) {
2019-02-24 19:21:19 -05:00
yOffset = ( height - 1 - ( screenY + 1 - y ) ) & 0x07 ;
rowOffset = ( height - 1 - ( screenY + 1 - y ) ) > > 3 ;
2019-02-22 22:35:53 -05:00
} else {
2019-02-24 19:21:19 -05:00
yOffset = ( screenY + 1 - y ) & 0x07 ;
rowOffset = ( screenY + 1 - y ) > > 3 ;
2019-02-22 22:35:53 -05:00
}
uint8_t row = ( info . TileRow + rowOffset ) & 0x0F ;
2019-02-24 13:09:22 -05:00
for ( int x = std : : max < int16_t > ( info . X , 0 ) ; x < info . X + width & & x < 256 ; x + + ) {
2019-02-22 22:35:53 -05:00
if ( _spritePixels [ x ] = = 0xFFFF ) {
uint8_t xOffset ;
int columnOffset ;
if ( info . HorizontalMirror ) {
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 ;
}
uint8_t column = ( info . TileColumn + columnOffset ) & 0x0F ;
uint8_t tileIndex = ( row < < 4 ) | column ;
uint16_t tileStart = ( ( _oamBaseAddress + ( tileIndex < < 4 ) + ( info . UseSecondTable ? _oamAddressOffset : 0 ) ) & 0x7FFF ) < < 1 ;
2019-02-24 13:09:22 -05:00
uint16_t color = GetTilePixelColor < 4 > ( tileStart + yOffset * 2 , 7 - xOffset ) ;
2019-02-22 22:35:53 -05:00
if ( color ! = 0 ) {
2019-02-24 13:09:22 -05:00
uint16_t paletteRamOffset = 256 + ( ( ( info . Palette < < 4 ) + color ) < < 1 ) ;
2019-02-22 22:35:53 -05:00
_spritePixels [ x ] = _cgram [ paletteRamOffset ] | ( _cgram [ paletteRamOffset + 1 ] < < 8 ) ;
_spritePriority [ x ] = info . Priority ;
_spritePalette [ x ] = info . Palette ;
}
}
}
totalWidth + = width ;
if ( totalWidth > = 34 * 8 ) {
_timeOver = true ;
}
_spriteCount + + ;
if ( _spriteCount = = 32 ) {
_rangeOver = true ;
}
if ( _timeOver | | _rangeOver ) {
break ;
}
}
}
2019-02-22 18:41:43 -05:00
template < bool forMainScreen >
void Ppu : : RenderMode0 ( )
{
RenderSprites < 3 , forMainScreen > ( ) ;
RenderTilemap < 0 , 2 , true , forMainScreen > ( ) ;
RenderTilemap < 1 , 2 , true , forMainScreen , 64 > ( ) ;
RenderSprites < 2 , forMainScreen > ( ) ;
RenderTilemap < 0 , 2 , false , forMainScreen > ( ) ;
RenderTilemap < 1 , 2 , false , forMainScreen , 64 > ( ) ;
RenderSprites < 1 , forMainScreen > ( ) ;
RenderTilemap < 2 , 2 , true , forMainScreen , 128 > ( ) ;
RenderTilemap < 3 , 2 , true , forMainScreen , 192 > ( ) ;
RenderSprites < 0 , forMainScreen > ( ) ;
RenderTilemap < 2 , 2 , false , forMainScreen , 128 > ( ) ;
RenderTilemap < 3 , 2 , false , forMainScreen , 192 > ( ) ;
RenderBgColor < forMainScreen > ( ) ;
}
template < bool forMainScreen >
void Ppu : : RenderMode1 ( )
{
if ( _mode1Bg3Priority ) {
RenderTilemap < 2 , 2 , true , forMainScreen > ( ) ;
}
RenderSprites < 3 , forMainScreen > ( ) ;
RenderTilemap < 0 , 4 , true , forMainScreen > ( ) ;
RenderTilemap < 1 , 4 , true , forMainScreen > ( ) ;
RenderSprites < 2 , forMainScreen > ( ) ;
RenderTilemap < 0 , 4 , false , forMainScreen > ( ) ;
RenderTilemap < 1 , 4 , false , forMainScreen > ( ) ;
RenderSprites < 1 , forMainScreen > ( ) ;
if ( ! _mode1Bg3Priority ) {
RenderTilemap < 2 , 2 , true , forMainScreen > ( ) ;
}
RenderSprites < 0 , true > ( ) ;
RenderTilemap < 2 , 2 , false , forMainScreen > ( ) ;
RenderBgColor < forMainScreen > ( ) ;
}
template < bool forMainScreen >
void Ppu : : RenderMode2 ( )
{
RenderSprites < 3 , forMainScreen > ( ) ;
RenderTilemap < 0 , 4 , true , forMainScreen > ( ) ;
RenderSprites < 2 , forMainScreen > ( ) ;
RenderTilemap < 1 , 4 , true , forMainScreen > ( ) ;
RenderSprites < 1 , forMainScreen > ( ) ;
RenderTilemap < 0 , 4 , false , forMainScreen > ( ) ;
RenderSprites < 0 , forMainScreen > ( ) ;
RenderTilemap < 1 , 4 , false , forMainScreen > ( ) ;
RenderBgColor < forMainScreen > ( ) ;
}
template < bool forMainScreen >
void Ppu : : RenderMode3 ( )
{
RenderSprites < 3 , forMainScreen > ( ) ;
RenderTilemap < 0 , 8 , true , forMainScreen > ( ) ;
RenderSprites < 2 , forMainScreen > ( ) ;
RenderTilemap < 1 , 4 , true , forMainScreen > ( ) ;
RenderSprites < 1 , forMainScreen > ( ) ;
RenderTilemap < 0 , 8 , false , forMainScreen > ( ) ;
RenderSprites < 0 , forMainScreen > ( ) ;
RenderTilemap < 1 , 4 , false , forMainScreen > ( ) ;
RenderBgColor < forMainScreen > ( ) ;
}
template < bool forMainScreen >
void Ppu : : RenderMode4 ( )
{
RenderSprites < 3 , forMainScreen > ( ) ;
RenderTilemap < 0 , 8 , true , forMainScreen > ( ) ;
RenderSprites < 2 , forMainScreen > ( ) ;
RenderTilemap < 1 , 4 , true , forMainScreen > ( ) ;
RenderSprites < 1 , forMainScreen > ( ) ;
RenderTilemap < 0 , 8 , false , forMainScreen > ( ) ;
RenderSprites < 0 , forMainScreen > ( ) ;
RenderTilemap < 1 , 4 , false , forMainScreen > ( ) ;
RenderBgColor < forMainScreen > ( ) ;
}
2019-02-23 21:39:35 -05:00
template < bool forMainScreen >
void Ppu : : RenderMode5 ( )
{
RenderSprites < 3 , forMainScreen > ( ) ;
RenderTilemap < 0 , 4 , true , forMainScreen > ( ) ;
RenderSprites < 2 , forMainScreen > ( ) ;
RenderTilemap < 1 , 2 , true , forMainScreen > ( ) ;
RenderSprites < 1 , forMainScreen > ( ) ;
RenderTilemap < 0 , 4 , false , forMainScreen > ( ) ;
RenderSprites < 0 , forMainScreen > ( ) ;
RenderTilemap < 1 , 2 , false , forMainScreen > ( ) ;
RenderBgColor < forMainScreen > ( ) ;
}
2019-02-24 10:30:19 -05:00
template < bool forMainScreen >
void Ppu : : RenderMode6 ( )
{
RenderSprites < 3 , forMainScreen > ( ) ;
RenderTilemap < 0 , 4 , true , forMainScreen > ( ) ;
RenderSprites < 2 , forMainScreen > ( ) ;
RenderSprites < 1 , forMainScreen > ( ) ;
RenderTilemap < 0 , 4 , false , forMainScreen > ( ) ;
RenderSprites < 0 , forMainScreen > ( ) ;
RenderBgColor < forMainScreen > ( ) ;
}
2019-02-23 15:40:32 -05:00
template < bool forMainScreen >
void Ppu : : RenderMode7 ( )
{
RenderSprites < 3 , forMainScreen > ( ) ;
RenderSprites < 2 , forMainScreen > ( ) ;
2019-02-23 16:04:04 -05:00
if ( _mode7 . ExtBgEnabled ) {
2019-02-24 01:11:26 -05:00
RenderTilemapMode7 < 1 , forMainScreen , true > ( ) ;
2019-02-23 16:04:04 -05:00
}
2019-02-23 15:40:32 -05:00
RenderSprites < 1 , forMainScreen > ( ) ;
2019-02-24 01:11:26 -05:00
RenderTilemapMode7 < 0 , forMainScreen , false > ( ) ;
2019-02-23 15:40:32 -05:00
RenderSprites < 0 , forMainScreen > ( ) ;
2019-02-23 16:04:04 -05:00
if ( _mode7 . ExtBgEnabled ) {
2019-02-24 01:11:26 -05:00
RenderTilemapMode7 < 1 , forMainScreen , false > ( ) ;
2019-02-23 16:04:04 -05:00
}
2019-02-23 15:40:32 -05:00
RenderBgColor < forMainScreen > ( ) ;
}
2019-02-19 01:26:48 -05:00
void Ppu : : RenderScanline ( )
{
2019-02-21 22:40:08 -05:00
_pixelsDrawn = 0 ;
_subPixelsDrawn = 0 ;
2019-02-19 23:35:43 -05:00
memset ( _rowPixelFlags , 0 , sizeof ( _rowPixelFlags ) ) ;
2019-02-19 01:26:48 -05:00
memset ( _subScreenFilled , 0 , sizeof ( _subScreenFilled ) ) ;
2019-02-19 18:01:27 -05:00
if ( _forcedVblank ) {
RenderBgColor < true > ( ) ;
2019-02-24 11:14:24 -05:00
RenderBgColor < false > ( ) ;
2019-02-24 01:11:26 -05:00
ApplyHiResMode ( ) ;
2019-02-19 18:01:27 -05:00
return ;
}
2019-02-19 01:26:48 -05:00
switch ( _bgMode ) {
case 0 :
2019-02-22 18:41:43 -05:00
RenderMode0 < true > ( ) ;
RenderMode0 < false > ( ) ;
2019-02-19 01:26:48 -05:00
break ;
2019-02-18 22:27:22 -05:00
case 1 :
2019-02-22 18:41:43 -05:00
RenderMode1 < true > ( ) ;
RenderMode1 < false > ( ) ;
2019-02-19 01:26:48 -05:00
break ;
case 2 :
2019-02-22 18:41:43 -05:00
RenderMode2 < true > ( ) ;
RenderMode2 < false > ( ) ;
2019-02-19 01:26:48 -05:00
break ;
case 3 :
2019-02-22 18:41:43 -05:00
RenderMode3 < true > ( ) ;
RenderMode3 < false > ( ) ;
break ;
case 4 :
RenderMode4 < true > ( ) ;
RenderMode4 < false > ( ) ;
2019-02-19 01:26:48 -05:00
break ;
case 5 :
2019-02-23 21:39:35 -05:00
RenderMode5 < true > ( ) ;
RenderMode5 < false > ( ) ;
2019-02-18 22:27:22 -05:00
break ;
2019-02-19 01:26:48 -05:00
case 6 :
2019-02-24 10:30:19 -05:00
RenderMode6 < true > ( ) ;
RenderMode6 < false > ( ) ;
2019-02-19 01:26:48 -05:00
break ;
2019-02-22 18:41:43 -05:00
case 7 :
2019-02-23 15:40:32 -05:00
RenderMode7 < true > ( ) ;
RenderMode7 < false > ( ) ;
2019-02-22 18:41:43 -05:00
break ;
2019-02-19 01:26:48 -05:00
}
2019-02-19 23:35:43 -05:00
ApplyColorMath ( ) ;
2019-02-23 21:39:35 -05:00
ApplyBrightness < true > ( ) ;
ApplyHiResMode ( ) ;
2019-02-19 01:26:48 -05:00
2019-02-18 22:27:22 -05:00
//Process sprites for next scanline
2019-02-22 22:35:53 -05:00
EvaluateNextLineSprites ( ) ;
2019-02-18 22:27:22 -05:00
}
2019-02-19 01:26:48 -05:00
template < bool forMainScreen >
void Ppu : : RenderBgColor ( )
2019-02-13 18:44:39 -05:00
{
2019-02-22 22:35:53 -05:00
if ( ( forMainScreen & & _pixelsDrawn = = 256 ) | | ( ! forMainScreen & & _subPixelsDrawn = = 256 ) ) {
2019-02-21 22:40:08 -05:00
return ;
}
2019-02-19 01:26:48 -05:00
uint16_t bgColor = _cgram [ 0 ] | ( _cgram [ 1 ] < < 8 ) ;
for ( int x = 0 ; x < 256 ; x + + ) {
if ( forMainScreen ) {
2019-02-19 23:35:43 -05:00
if ( ! _rowPixelFlags [ x ] ) {
2019-02-21 22:40:08 -05:00
uint8_t pixelFlags = PixelFlags : : Filled | ( ( _colorMathEnabled & 0x20 ) ? PixelFlags : : AllowColorMath : 0 ) ;
2019-02-23 21:39:35 -05:00
_mainScreenBuffer [ x ] = bgColor ;
2019-02-19 23:35:43 -05:00
_rowPixelFlags [ x ] = pixelFlags ;
2019-02-19 01:26:48 -05:00
}
} else {
if ( ! _subScreenFilled [ x ] ) {
2019-02-21 22:40:08 -05:00
_subScreenBuffer [ x ] = bgColor ;
2019-02-19 01:26:48 -05:00
}
}
2019-02-17 23:26:49 -05:00
}
2019-02-19 01:26:48 -05:00
}
2019-02-17 23:26:49 -05:00
2019-02-24 01:30:55 -05:00
template < uint8_t priority , bool forMainScreen >
void Ppu : : RenderSprites ( )
{
if ( ! IsRenderRequired < forMainScreen > ( Ppu : : SpriteLayerIndex ) ) {
return ;
}
uint8_t activeWindowCount = 0 ;
if ( forMainScreen ) {
if ( _windowMaskMain [ Ppu : : SpriteLayerIndex ] ) {
activeWindowCount = ( uint8_t ) _window [ 0 ] . ActiveLayers [ Ppu : : SpriteLayerIndex ] + ( uint8_t ) _window [ 1 ] . ActiveLayers [ Ppu : : SpriteLayerIndex ] ;
}
} else {
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 < Ppu : : SpriteLayerIndex > ( activeWindowCount , x ) ) {
//This pixel was masked
continue ;
}
_mainScreenBuffer [ x ] = _spritePixels [ x ] ;
_rowPixelFlags [ x ] | = PixelFlags : : Filled | ( ( ( _colorMathEnabled & 0x10 ) & & _spritePalette [ x ] > 3 ) ? PixelFlags : : AllowColorMath : 0 ) ;
}
}
} else {
for ( int x = 0 ; x < 256 ; x + + ) {
if ( ! _subScreenFilled [ x ] & & _spritePriority [ x ] = = priority ) {
if ( activeWindowCount & & ProcessMaskWindow < Ppu : : SpriteLayerIndex > ( activeWindowCount , x ) ) {
//This pixel was masked
continue ;
}
_subScreenBuffer [ x ] = _spritePixels [ x ] ;
_subScreenFilled [ x ] = true ;
}
}
}
}
2019-02-24 11:14:24 -05:00
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode , bool largeTileWidth , bool largeTileHeight , uint8_t activeWindowCount , bool applyMosaic , bool directColorMode >
2019-02-23 01:28:41 -05:00
void Ppu : : RenderTilemap ( )
2019-02-19 01:26:48 -05:00
{
2019-02-24 01:30:55 -05:00
if ( ! IsRenderRequired < forMainScreen > ( layerIndex ) ) {
return ;
2019-02-19 01:26:48 -05:00
}
2019-02-17 23:53:19 -05:00
2019-02-23 21:39:35 -05:00
/* Current scanline (in interlaced mode, switches between even and odd rows every frame */
uint16_t realY = _screenInterlace ? ( ( _frameCount & 0x01 ) ? ( ( _scanline < < 1 ) + 1 ) : ( _scanline < < 1 ) ) : _scanline ;
2019-02-20 17:39:14 -05:00
2019-02-23 21:39:35 -05:00
/* True when the entire scanline has to be replaced by a mosaic pattern */
bool mosaicScanline = applyMosaic & & ( realY - _mosaicStartScanline ) % _mosaicSize ! = 0 ;
/* Keeps track of whether or not the pixel is allowed to participate in color math */
2019-02-19 23:35:43 -05:00
uint8_t pixelFlags = PixelFlags : : Filled | ( ( ( _colorMathEnabled > > layerIndex ) & 0x01 ) ? PixelFlags : : AllowColorMath : 0 ) ;
2019-02-23 21:39:35 -05:00
/* The current layer's options */
2019-02-17 23:26:49 -05:00
LayerConfig & config = _layerConfig [ layerIndex ] ;
2019-02-23 21:39:35 -05:00
/* Layer's tilemap start address */
2019-02-19 17:23:21 -05:00
uint16_t tilemapAddr = config . TilemapAddress > > 1 ;
2019-02-23 21:39:35 -05:00
/* Layer's CHR data start address */
2019-02-17 00:32:41 -05:00
uint16_t chrAddr = config . ChrAddress ;
2019-02-21 22:40:08 -05:00
2019-02-23 21:39:35 -05:00
/* 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 */
2019-02-21 22:40:08 -05:00
uint16_t addrVerticalScrollingOffset = config . VerticalMirroring ? ( ( row & 0x20 ) < < ( config . HorizontalMirroring ? 6 : 5 ) ) : 0 ;
2019-02-23 21:39:35 -05:00
/* The start address for tiles on this row */
uint16_t baseOffset = tilemapAddr + addrVerticalScrollingOffset + ( ( row & 0x1F ) < < 5 ) ;
2019-02-23 08:54:46 -05:00
2019-02-24 18:45:47 -05:00
uint16_t vScroll = config . VScroll ;
uint16_t hScroll = config . HScroll ;
//"Offset per tile" mode (modes 2, 4 and 6 support this)
bool offsetPerTileMode = ( _bgMode & 0x03 ) = = 2 ;
/* 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 ;
2019-02-19 17:23:21 -05:00
for ( int x = 0 ; x < 256 ; x + + ) {
2019-02-24 11:14:24 -05:00
if ( hiResMode ) {
2019-02-23 21:39:35 -05:00
realX = ( x < < 1 ) + ( forMainScreen ? 1 : 0 ) ;
} else {
realX = x ;
}
2019-02-24 18:45:47 -05:00
if ( offsetPerTileMode ) {
ProcessOffsetMode < layerIndex , largeTileWidth , largeTileHeight > ( x , realX , realY , hScroll , vScroll , addr ) ;
//Need to recalculate this because vScroll may change from one tile to another
baseYOffset = ( realY + vScroll ) & 0x07 ;
} else {
column = ( realX + hScroll ) > > ( largeTileWidth ? 4 : 3 ) ;
addr = ( baseOffset + ( column & 0x1F ) + ( config . HorizontalMirroring ? ( ( column & 0x20 ) < < 5 ) : 0 ) ) < < 1 ;
}
2019-02-19 17:23:21 -05:00
2019-02-23 15:40:32 -05:00
//Skip pixels that were filled by previous layers (or that don't match the priority level currently being processed)
2019-02-19 01:26:48 -05:00
if ( forMainScreen ) {
2019-02-19 23:35:43 -05:00
if ( _rowPixelFlags [ x ] | | ( ( uint8_t ) processHighPriority ! = ( ( _vram [ addr + 1 ] & 0x20 ) > > 5 ) ) ) {
2019-02-19 01:26:48 -05:00
continue ;
}
} else {
if ( _subScreenFilled [ x ] | | ( ( uint8_t ) processHighPriority ! = ( ( _vram [ addr + 1 ] & 0x20 ) > > 5 ) ) ) {
continue ;
2019-02-13 23:03:01 -05:00
}
}
2019-02-17 00:32:41 -05:00
2019-02-22 20:15:55 -05:00
if ( activeWindowCount & & ProcessMaskWindow < layerIndex > ( activeWindowCount , x ) ) {
2019-02-23 15:40:32 -05:00
//This pixel was masked, skip it
2019-02-22 20:15:55 -05:00
continue ;
}
2019-02-23 08:54:46 -05:00
if ( applyMosaic & & ( mosaicScanline | | x % _mosaicSize ! = 0 ) ) {
2019-02-20 17:39:14 -05:00
//If this is not the top-left pixels in the mosaic pattern, override it with the top-left pixel data
2019-02-23 21:39:35 -05:00
_mainScreenBuffer [ x ] = _mosaicColor [ x ] ;
2019-02-20 17:39:14 -05:00
_rowPixelFlags [ x ] = pixelFlags ;
2019-02-21 22:40:08 -05:00
if ( forMainScreen ) {
_pixelsDrawn + + ;
} else {
_subPixelsDrawn + + ;
}
2019-02-20 17:39:14 -05:00
continue ;
}
2019-02-23 15:40:32 -05:00
//The pixel is empty, not clipped and not part of a mosaic pattern, process it
2019-02-19 01:26:48 -05:00
bool vMirror = ( _vram [ addr + 1 ] & 0x80 ) ! = 0 ;
bool hMirror = ( _vram [ addr + 1 ] & 0x40 ) ! = 0 ;
2019-02-17 00:32:41 -05:00
2019-02-23 01:28:41 -05:00
uint16_t tileIndex = ( ( _vram [ addr + 1 ] & 0x03 ) < < 8 ) | _vram [ addr ] ;
2019-02-23 21:39:35 -05:00
if ( largeTileWidth | | largeTileHeight ) {
2019-02-23 01:28:41 -05:00
tileIndex = (
tileIndex +
2019-02-24 18:45:47 -05:00
( largeTileHeight ? ( ( ( realY + vScroll ) & 0x08 ) ? ( vMirror ? 0 : 16 ) : ( vMirror ? 16 : 0 ) ) : 0 ) +
( largeTileWidth ? ( ( ( realX + hScroll ) & 0x08 ) ? ( hMirror ? 0 : 1 ) : ( hMirror ? 1 : 0 ) ) : 0 )
2019-02-23 01:28:41 -05:00
) & 0x3FF ;
}
2019-02-19 01:26:48 -05:00
uint16_t tileStart = chrAddr + tileIndex * 8 * bpp ;
2019-02-19 17:23:21 -05:00
2019-02-21 22:40:08 -05:00
uint8_t yOffset = vMirror ? ( 7 - baseYOffset ) : baseYOffset ;
uint16_t pixelStart = tileStart + yOffset * 2 ;
2019-02-24 18:45:47 -05:00
uint8_t xOffset = ( realX + hScroll ) & 0x07 ;
2019-02-19 17:23:21 -05:00
uint8_t shift = hMirror ? xOffset : ( 7 - xOffset ) ;
2019-02-23 21:39:35 -05:00
2019-02-24 01:30:55 -05:00
uint16_t color = GetTilePixelColor < bpp > ( pixelStart , shift ) ;
2019-02-13 18:44:39 -05:00
2019-02-19 01:26:48 -05:00
if ( color > 0 ) {
2019-02-24 01:11:26 -05:00
/* Ignore palette bits for 256-color layers */
uint16_t paletteColor ;
if ( bpp = = 8 & & directColorMode ) {
uint8_t palette = ( _vram [ addr + 1 ] > > 2 ) & 0x07 ;
paletteColor = (
( ( ( color & 0x07 ) | ( palette & 0x01 ) ) < < 1 ) |
( ( ( color & 0x38 ) | ( ( palette & 0x02 ) < < 1 ) ) < < 3 ) |
( ( ( color & 0xC0 ) | ( ( palette & 0x04 ) < < 3 ) ) < < 7 )
) ;
} else {
uint8_t palette = bpp = = 8 ? 0 : ( _vram [ addr + 1 ] > > 2 ) & 0x07 ;
uint16_t paletteRamOffset = basePaletteOffset + ( palette * ( 1 < < bpp ) + color ) * 2 ;
paletteColor = _cgram [ paletteRamOffset ] | ( _cgram [ paletteRamOffset + 1 ] < < 8 ) ;
}
2019-02-17 00:32:41 -05:00
2019-02-19 01:26:48 -05:00
if ( forMainScreen ) {
2019-02-24 01:11:26 -05:00
DrawMainPixel < applyMosaic > ( x , paletteColor , pixelFlags ) ;
2019-02-23 15:40:32 -05:00
} else {
2019-02-24 01:11:26 -05:00
DrawSubPixel ( x , paletteColor ) ;
2019-02-23 15:40:32 -05:00
}
}
}
}
2019-02-24 18:45:47 -05:00
template < uint8_t layerIndex , bool largeTileWidth , bool largeTileHeight >
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 = config . HScroll ;
vScroll = config . VScroll ;
//TODO: Check+fix behavior with 16x16 tiles
//TODO: Test mode 4/6 behavior
if ( ( realX + hScroll ) & ~ 0x07 ) {
//For all tiles after the first tile on the row, check if an active offset exists and use it
uint16_t columnOffset = ( ( ( x - 8 ) & ~ 0x07 ) + ( _layerConfig [ 2 ] . HScroll & ~ 0x07 ) ) > > 3 ;
uint16_t rowOffset = ( _layerConfig [ 2 ] . VScroll > > 3 ) ;
uint16_t hOffsetAddr = _layerConfig [ 2 ] . TilemapAddress + ( columnOffset < < 1 ) + ( rowOffset < < 6 ) ;
if ( _bgMode = = 4 ) {
int16_t offsetValue = _vram [ hOffsetAddr ] | ( _vram [ hOffsetAddr + 1 ] < < 8 ) ;
if ( ( offsetValue & 0x8000 ) = = 0 & & ( offsetValue & enableBit ) ) {
hScroll = ( hScroll & 0x07 ) | ( ( x & ~ 0x07 ) + ( offsetValue & 0x3F8 ) ) ;
}
if ( ( offsetValue & 0x8000 ) ! = 0 & & ( offsetValue & enableBit ) ) {
vScroll = ( offsetValue & 0x3FF ) ;
}
} else {
uint16_t vOffsetAddr = hOffsetAddr + 0x40 ;
int16_t hOffsetValue = _vram [ hOffsetAddr ] | ( _vram [ hOffsetAddr + 1 ] < < 8 ) ;
int16_t vOffsetValue = _vram [ vOffsetAddr ] | ( _vram [ vOffsetAddr + 1 ] < < 8 ) ;
if ( hOffsetValue & enableBit ) {
hScroll = ( hScroll & 0x07 ) | ( ( x & ~ 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 . VerticalMirroring ? ( ( offsetModeRow & 0x20 ) < < ( config . HorizontalMirroring ? 6 : 5 ) ) : 0 ;
uint16_t offsetModeBaseAddress = tilemapAddr + addrVerticalScrollingOffset + ( ( offsetModeRow & 0x1F ) < < 5 ) ;
addr = ( offsetModeBaseAddress + ( offsetModeColumn & 0x1F ) + ( config . HorizontalMirroring ? ( ( offsetModeColumn & 0x20 ) < < 5 ) : 0 ) ) < < 1 ;
}
2019-02-24 01:30:55 -05:00
template < bool forMainScreen >
bool Ppu : : IsRenderRequired ( uint8_t layerIndex )
{
if ( forMainScreen ) {
if ( _pixelsDrawn = = 256 | | ( ( _mainScreenLayers > > layerIndex ) & 0x01 ) = = 0 ) {
//This screen is disabled, or we've drawn all pixels already
return false ;
}
} else {
if ( _subPixelsDrawn = = 256 | | ( ( _subScreenLayers > > layerIndex ) & 0x01 ) = = 0 ) {
//This screen is disabled, or we've drawn all pixels already
return false ;
}
}
return true ;
}
template < uint8_t bpp >
uint16_t Ppu : : GetTilePixelColor ( const uint16_t pixelStart , const uint8_t shift )
{
uint16_t color ;
if ( bpp = = 2 ) {
color = ( ( ( _vram [ pixelStart + 0 ] > > shift ) & 0x01 ) < < 0 ) ;
color | = ( ( ( _vram [ pixelStart + 1 ] > > shift ) & 0x01 ) < < 1 ) ;
} else if ( bpp = = 4 ) {
color = ( ( ( _vram [ pixelStart + 0 ] > > shift ) & 0x01 ) < < 0 ) ;
color | = ( ( ( _vram [ pixelStart + 1 ] > > shift ) & 0x01 ) < < 1 ) ;
color | = ( ( ( _vram [ pixelStart + 16 ] > > shift ) & 0x01 ) < < 2 ) ;
color | = ( ( ( _vram [ pixelStart + 17 ] > > shift ) & 0x01 ) < < 3 ) ;
} else if ( bpp = = 8 ) {
color = ( ( ( _vram [ pixelStart + 0 ] > > shift ) & 0x01 ) < < 0 ) ;
color | = ( ( ( _vram [ pixelStart + 1 ] > > shift ) & 0x01 ) < < 1 ) ;
color | = ( ( ( _vram [ pixelStart + 16 ] > > shift ) & 0x01 ) < < 2 ) ;
color | = ( ( ( _vram [ pixelStart + 17 ] > > shift ) & 0x01 ) < < 3 ) ;
color | = ( ( ( _vram [ pixelStart + 32 ] > > shift ) & 0x01 ) < < 4 ) ;
color | = ( ( ( _vram [ pixelStart + 33 ] > > shift ) & 0x01 ) < < 5 ) ;
color | = ( ( ( _vram [ pixelStart + 48 ] > > shift ) & 0x01 ) < < 6 ) ;
color | = ( ( ( _vram [ pixelStart + 49 ] > > shift ) & 0x01 ) < < 7 ) ;
} else {
throw std : : runtime_error ( " unsupported bpp " ) ;
}
return color ;
}
2019-02-24 01:11:26 -05:00
template < uint8_t layerIndex , bool forMainScreen , bool processHighPriority , bool applyMosaic , bool directColorMode >
2019-02-23 15:40:32 -05:00
void Ppu : : RenderTilemapMode7 ( )
{
2019-02-24 01:30:55 -05:00
if ( ! IsRenderRequired < forMainScreen > ( layerIndex ) ) {
return ;
2019-02-23 15:40:32 -05:00
}
constexpr auto clip = [ ] ( int32_t val ) { return ( val & 0x2000 ) ? ( val | ~ 0x3ff ) : ( val & 0x3ff ) ; } ;
int32_t lutX [ 256 ] ;
int32_t lutY [ 256 ] ;
int32_t hScroll = ( ( int32_t ) _mode7 . HScroll < < 19 ) > > 19 ;
int32_t vScroll = ( ( int32_t ) _mode7 . VScroll < < 19 ) > > 19 ;
int32_t centerX = ( ( int32_t ) _mode7 . CenterX < < 19 ) > > 19 ;
int32_t centerY = ( ( int32_t ) _mode7 . CenterY < < 19 ) > > 19 ;
2019-02-23 21:39:35 -05:00
uint16_t realY = _mode7 . VerticalMirroring ? ( 255 - _scanline ) : _scanline ;
2019-02-23 15:40:32 -05:00
lutX [ 0 ] = (
( ( _mode7 . Matrix [ 0 ] * clip ( hScroll - centerX ) ) & ~ 63 ) +
( ( _mode7 . Matrix [ 1 ] * realY ) & ~ 63 ) +
( ( _mode7 . Matrix [ 1 ] * clip ( vScroll - centerY ) ) & ~ 63 ) +
( centerX < < 8 )
) ;
lutY [ 0 ] = (
( ( _mode7 . Matrix [ 2 ] * clip ( hScroll - centerX ) ) & ~ 63 ) +
( ( _mode7 . Matrix [ 3 ] * realY ) & ~ 63 ) +
( ( _mode7 . Matrix [ 3 ] * clip ( vScroll - centerY ) ) & ~ 63 ) +
( centerY < < 8 )
) ;
for ( int x = 1 ; x < 256 ; x + + ) {
lutX [ x ] = lutX [ x - 1 ] + _mode7 . Matrix [ 0 ] ;
lutY [ x ] = lutY [ x - 1 ] + _mode7 . Matrix [ 2 ] ;
}
uint8_t pixelFlags = PixelFlags : : Filled | ( ( ( _colorMathEnabled > > layerIndex ) & 0x01 ) ? PixelFlags : : AllowColorMath : 0 ) ;
for ( int x = 0 ; x < 256 ; x + + ) {
uint16_t realX = _mode7 . HorizontalMirroring ? ( 255 - x ) : x ;
if ( forMainScreen ) {
if ( _rowPixelFlags [ x ] ) {
continue ;
}
} else {
if ( _subScreenFilled [ x ] ) {
continue ;
}
}
int32_t xOffset = ( lutX [ realX ] > > 8 ) ;
int32_t yOffset = ( lutY [ realX ] > > 8 ) ;
2019-02-24 10:29:11 -05:00
uint8_t tileIndex ;
2019-02-23 15:40:32 -05:00
if ( ! _mode7 . LargeMap ) {
yOffset & = 0x3FF ;
xOffset & = 0x3FF ;
2019-02-24 10:29:11 -05:00
tileIndex = _vram [ ( ( ( yOffset & ~ 0x07 ) < < 4 ) | ( xOffset > > 3 ) ) < < 1 ] ;
2019-02-23 15:40:32 -05:00
} else {
if ( yOffset < 0 | | yOffset > 0x3FF | | xOffset < 0 | | xOffset > 0x3FF ) {
if ( _mode7 . FillWithTile0 ) {
2019-02-24 10:29:11 -05:00
tileIndex = 0 ;
2019-02-23 15:40:32 -05:00
} else {
//Draw nothing for this pixel, we're outside the map
continue ;
2019-02-20 17:39:14 -05:00
}
2019-02-24 10:29:11 -05:00
} else {
tileIndex = _vram [ ( ( ( yOffset & ~ 0x07 ) < < 4 ) | ( xOffset > > 3 ) ) < < 1 ] ;
2019-02-23 15:40:32 -05:00
}
}
2019-02-24 01:11:26 -05:00
uint16_t colorIndex ;
2019-02-23 16:04:04 -05:00
if ( layerIndex = = 1 ) {
uint8_t color = _vram [ ( ( ( tileIndex < < 6 ) + ( ( yOffset & 0x07 ) < < 3 ) + ( xOffset & 0x07 ) ) < < 1 ) + 1 ] ;
if ( ( ( uint8_t ) processHighPriority < < 7 ) ! = ( color & 0x80 ) ) {
//Wrong priority, skip this pixel
continue ;
}
2019-02-24 01:11:26 -05:00
colorIndex = ( color & 0x7F ) ;
2019-02-23 16:04:04 -05:00
} else {
2019-02-24 01:11:26 -05:00
colorIndex = ( _vram [ ( ( ( tileIndex < < 6 ) + ( ( yOffset & 0x07 ) < < 3 ) + ( xOffset & 0x07 ) ) < < 1 ) + 1 ] ) ;
2019-02-23 16:04:04 -05:00
}
2019-02-23 15:40:32 -05:00
2019-02-24 01:11:26 -05:00
if ( colorIndex > 0 ) {
uint16_t paletteColor ;
if ( directColorMode ) {
paletteColor = ( ( colorIndex & 0x07 ) < < 2 ) | ( ( colorIndex & 0x38 ) < < 4 ) | ( ( colorIndex & 0xC0 ) < < 7 ) ;
} else {
paletteColor = _cgram [ colorIndex < < 1 ] | ( _cgram [ ( colorIndex < < 1 ) + 1 ] < < 8 ) ;
}
2019-02-23 15:40:32 -05:00
if ( forMainScreen ) {
2019-02-24 01:11:26 -05:00
DrawMainPixel < applyMosaic > ( x , paletteColor , pixelFlags ) ;
2019-02-19 01:26:48 -05:00
} else {
2019-02-24 01:11:26 -05:00
DrawSubPixel ( x , paletteColor ) ;
2019-02-19 01:26:48 -05:00
}
}
2019-02-17 00:32:41 -05:00
}
2019-02-19 01:26:48 -05:00
}
2019-02-17 22:44:57 -05:00
2019-02-23 15:40:32 -05:00
template < bool applyMosaic >
2019-02-24 01:11:26 -05:00
void Ppu : : DrawMainPixel ( uint8_t x , uint16_t color , uint8_t flags )
2019-02-23 15:40:32 -05:00
{
2019-02-24 01:11:26 -05:00
_mainScreenBuffer [ x ] = color ;
2019-02-23 15:40:32 -05:00
_rowPixelFlags [ x ] = flags ;
if ( applyMosaic & & x % _mosaicSize = = 0 ) {
//This is the source for the mosaic pattern, store it for use in the next scanlines
for ( int i = 0 ; i < _mosaicSize & & x + i < 256 ; i + + ) {
2019-02-24 01:11:26 -05:00
_mosaicColor [ x + i ] = color ;
2019-02-23 15:40:32 -05:00
}
}
_pixelsDrawn + + ;
}
2019-02-24 01:11:26 -05:00
void Ppu : : DrawSubPixel ( uint8_t x , uint16_t color )
2019-02-23 15:40:32 -05:00
{
2019-02-24 01:11:26 -05:00
_subScreenBuffer [ x ] = color ;
2019-02-23 15:40:32 -05:00
_subScreenFilled [ x ] = true ;
_subPixelsDrawn + + ;
}
2019-02-19 01:26:48 -05:00
void Ppu : : ApplyColorMath ( )
{
2019-02-19 23:35:43 -05:00
if ( ! _colorMathEnabled ) {
2019-02-19 01:26:48 -05:00
return ;
2019-02-17 23:26:49 -05:00
}
2019-02-18 22:27:22 -05:00
2019-02-22 22:19:20 -05:00
uint8_t activeWindowCount = ( uint8_t ) _window [ 0 ] . ActiveLayers [ Ppu : : ColorWindowIndex ] + ( uint8_t ) _window [ 1 ] . ActiveLayers [ Ppu : : ColorWindowIndex ] ;
2019-02-19 01:26:48 -05:00
for ( int x = 0 ; x < 256 ; x + + ) {
2019-02-19 23:35:43 -05:00
if ( _rowPixelFlags [ x ] & PixelFlags : : AllowColorMath ) {
uint8_t halfShift = _colorMathHalveResult ? 1 : 0 ;
2019-02-23 21:39:35 -05:00
uint16_t & mainPixel = _mainScreenBuffer [ x ] ;
2019-02-22 22:19:20 -05:00
bool isInsideWindow = activeWindowCount & & ProcessMaskWindow < Ppu : : ColorWindowIndex > ( activeWindowCount , x ) ;
//Set color to black as needed based on clip mode
switch ( _colorMathClipMode ) {
default :
case ColorWindowMode : : Never : break ;
case ColorWindowMode : : OutsideWindow :
if ( ! isInsideWindow ) {
mainPixel = 0 ;
halfShift = 0 ;
}
break ;
case ColorWindowMode : : InsideWindow :
if ( isInsideWindow ) {
mainPixel = 0 ;
halfShift = 0 ;
}
break ;
case ColorWindowMode : : Always : mainPixel = 0 ; break ;
}
//Prevent color math as needed based on mode
switch ( _colorMathPreventMode ) {
default :
case ColorWindowMode : : Never : break ;
case ColorWindowMode : : OutsideWindow :
if ( ! isInsideWindow ) {
continue ;
}
break ;
case ColorWindowMode : : InsideWindow :
if ( isInsideWindow ) {
continue ;
}
break ;
case ColorWindowMode : : Always : continue ;
}
uint16_t otherPixel ;
2019-02-19 23:35:43 -05:00
if ( _colorMathAddSubscreen ) {
if ( _subScreenFilled [ x ] ) {
2019-02-21 22:40:08 -05:00
otherPixel = _subScreenBuffer [ x ] ;
2019-02-19 23:35:43 -05:00
} else {
//there's nothing in the subscreen at this pixel, use the fixed color and disable halve operation
otherPixel = _fixedColor ;
halfShift = 0 ;
}
} else {
otherPixel = _fixedColor ;
}
2019-02-19 01:26:48 -05:00
2019-02-19 23:35:43 -05:00
if ( _colorMathSubstractMode ) {
uint16_t r = std : : max ( ( mainPixel & 0x001F ) - ( otherPixel & 0x001F ) , 0 ) > > halfShift ;
uint16_t g = std : : max ( ( ( mainPixel > > 5 ) & 0x001F ) - ( ( otherPixel > > 5 ) & 0x001F ) , 0 ) > > halfShift ;
uint16_t b = std : : max ( ( ( mainPixel > > 10 ) & 0x001F ) - ( ( otherPixel > > 10 ) & 0x001F ) , 0 ) > > halfShift ;
mainPixel = r | ( g < < 5 ) | ( b < < 10 ) ;
} else {
uint16_t r = std : : min ( ( ( mainPixel & 0x001F ) + ( otherPixel & 0x001F ) ) > > halfShift , 0x1F ) ;
uint16_t g = std : : min ( ( ( ( mainPixel > > 5 ) & 0x001F ) + ( ( otherPixel > > 5 ) & 0x001F ) ) > > halfShift , 0x1F ) ;
uint16_t b = std : : min ( ( ( ( mainPixel > > 10 ) & 0x001F ) + ( ( otherPixel > > 10 ) & 0x001F ) ) > > halfShift , 0x1F ) ;
mainPixel = r | ( g < < 5 ) | ( b < < 10 ) ;
}
}
2019-02-18 22:27:22 -05:00
}
2019-02-17 23:26:49 -05:00
}
2019-02-23 21:39:35 -05:00
template < bool forMainScreen >
2019-02-22 22:31:20 -05:00
void Ppu : : ApplyBrightness ( )
{
if ( _screenBrightness ! = 15 ) {
for ( int x = 0 ; x < 256 ; x + + ) {
2019-02-23 21:39:35 -05:00
uint16_t & pixel = ( forMainScreen ? _mainScreenBuffer : _subScreenBuffer ) [ x ] ;
2019-02-22 22:31:20 -05:00
uint16_t r = ( pixel & 0x1F ) * _screenBrightness / 15 ;
uint16_t g = ( ( pixel > > 5 ) & 0x1F ) * _screenBrightness / 15 ;
uint16_t b = ( ( pixel > > 10 ) & 0x1F ) * _screenBrightness / 15 ;
pixel = r | ( g < < 5 ) | ( b < < 10 ) ;
}
}
}
2019-02-23 21:39:35 -05:00
void Ppu : : ApplyHiResMode ( )
{
2019-03-01 23:10:22 -05:00
uint16_t scanline = _scanline - 1 ;
uint32_t screenY = _screenInterlace ? ( ( _frameCount & 0x01 ) ? ( ( scanline < < 1 ) + 1 ) : ( scanline < < 1 ) ) : scanline ;
2019-02-23 21:39:35 -05:00
2019-02-24 11:14:24 -05:00
if ( _hiResMode | | _bgMode = = 5 | | _bgMode = = 6 ) {
2019-02-23 21:39:35 -05:00
ApplyBrightness < false > ( ) ;
for ( int i = 0 ; i < 512 ; i + = 2 ) {
_currentBuffer [ ( screenY < < 9 ) + i ] = _subScreenBuffer [ i > > 1 ] ;
_currentBuffer [ ( screenY < < 9 ) + i + 1 ] = _mainScreenBuffer [ i > > 1 ] ;
}
} else {
memcpy ( _currentBuffer + ( screenY < < 8 ) , _mainScreenBuffer , sizeof ( _mainScreenBuffer ) ) ;
}
}
2019-02-22 20:15:55 -05:00
template < uint8_t layerIndex >
bool Ppu : : ProcessMaskWindow ( uint8_t activeWindowCount , int x )
{
if ( activeWindowCount = = 1 ) {
if ( _window [ 0 ] . ActiveLayers [ layerIndex ] ) {
return _window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) ;
} else {
return _window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ;
}
} else {
switch ( _maskLogic [ layerIndex ] ) {
default :
case WindowMaskLogic : : Or : return _window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) | _window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ;
case WindowMaskLogic : : And : return _window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) & _window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ;
case WindowMaskLogic : : Xor : return _window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) ^ _window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ;
case WindowMaskLogic : : Xnor : return ! ( _window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) ^ _window [ 1 ] . PixelNeedsMasking < layerIndex > ( 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 ;
}
2019-02-19 01:26:48 -05:00
void Ppu : : SendFrame ( )
2019-02-17 23:26:49 -05:00
{
2019-02-19 01:26:48 -05:00
_console - > GetNotificationManager ( ) - > SendNotification ( ConsoleNotificationType : : PpuFrameDone ) ;
2019-02-23 21:39:35 -05:00
uint16_t width ;
uint16_t height ;
2019-02-24 11:14:24 -05:00
if ( _hiResMode | | _bgMode = = 5 | | _bgMode = = 6 ) {
2019-02-23 21:39:35 -05:00
width = 512 ;
} else {
width = 256 ;
}
if ( _screenInterlace & & ( _bgMode = = 5 | | _bgMode = = 6 ) ) {
height = _overscanMode ? 478 : 448 ;
} else {
height = _overscanMode ? 239 : 224 ;
}
if ( _screenInterlace ) {
_console - > GetVideoDecoder ( ) - > UpdateFrameSync ( _currentBuffer , width , height , _frameCount ) ;
} else {
_console - > GetVideoDecoder ( ) - > UpdateFrame ( _currentBuffer , width , height , _frameCount ) ;
_currentBuffer = _currentBuffer = = _outputBuffers [ 0 ] ? _outputBuffers [ 1 ] : _outputBuffers [ 0 ] ;
}
2019-02-13 18:44:39 -05:00
}
2019-02-15 21:33:13 -05:00
uint8_t * Ppu : : GetVideoRam ( )
{
return _vram ;
}
uint8_t * Ppu : : GetCgRam ( )
{
return _cgram ;
}
uint8_t * Ppu : : GetSpriteRam ( )
{
2019-02-17 21:09:33 -05:00
return _oamRam ;
2019-02-15 21:33:13 -05:00
}
2019-02-21 07:27:47 -05:00
void Ppu : : LatchLocationValues ( )
{
_horizontalLocation = _cycle ;
_verticalLocation = _scanline ;
2019-02-21 18:11:31 -05:00
_locationLatched = true ;
2019-02-21 07:27:47 -05:00
}
2019-02-24 09:38:22 -05:00
void Ppu : : UpdateVramReadBuffer ( )
{
2019-03-01 22:24:18 -05:00
uint16_t addr = GetVramAddress ( ) ;
_vramReadBuffer = _vram [ addr < < 1 ] | ( _vram [ ( addr < < 1 ) + 1 ] < < 8 ) ;
}
uint16_t Ppu : : GetVramAddress ( )
{
uint16_t addr = _vramAddress ;
switch ( _vramAddressRemapping ) {
default :
case 0 : return addr ;
case 1 : return ( addr & 0xFF00 ) | ( ( addr & 0xE0 ) > > 5 ) | ( ( addr & 0x1F ) < < 3 ) ;
case 2 : return ( addr & 0xFE00 ) | ( ( addr & 0x1C0 ) > > 6 ) | ( ( addr & 0x3F ) < < 3 ) ;
case 3 : return ( addr & 0xFC00 ) | ( ( addr & 0x380 ) > > 7 ) | ( ( addr & 0x7F ) < < 3 ) ;
}
2019-02-24 09:38:22 -05:00
}
2019-02-13 18:44:39 -05:00
uint8_t Ppu : : Read ( uint16_t addr )
{
switch ( addr ) {
2019-02-23 15:40:32 -05:00
case 0x2134 : return ( ( int16_t ) _mode7 . Matrix [ 0 ] * ( ( int16_t ) _mode7 . Matrix [ 1 ] > > 8 ) ) & 0xFF ;
case 0x2135 : return ( ( ( int16_t ) _mode7 . Matrix [ 0 ] * ( ( int16_t ) _mode7 . Matrix [ 1 ] > > 8 ) ) > > 8 ) & 0xFF ;
case 0x2136 : return ( ( ( int16_t ) _mode7 . Matrix [ 0 ] * ( ( int16_t ) _mode7 . Matrix [ 1 ] > > 8 ) ) > > 16 ) & 0xFF ;
2019-02-21 08:15:00 -05:00
2019-02-21 07:27:47 -05:00
case 0x2137 :
//SLHV - Software Latch for H/V Counter
//Latch values on read, and return open bus
LatchLocationValues ( ) ;
break ;
2019-02-19 18:41:59 -05:00
case 0x2138 : {
2019-02-21 07:27:47 -05:00
//OAMDATAREAD - Data for OAM read
2019-02-19 18:41:59 -05:00
uint8_t value ;
if ( _internalOamAddress < 512 ) {
value = _oamRam [ _internalOamAddress ] ;
2019-03-01 20:27:49 -05:00
_console - > ProcessPpuRead ( _internalOamAddress , value , SnesMemoryType : : SpriteRam ) ;
2019-02-19 18:41:59 -05:00
} else {
value = _oamRam [ 0x200 | ( _internalOamAddress & 0x1F ) ] ;
2019-03-01 20:27:49 -05:00
_console - > ProcessPpuRead ( 0x200 | ( _internalOamAddress & 0x1F ) , value , SnesMemoryType : : SpriteRam ) ;
2019-02-19 18:41:59 -05:00
}
_internalOamAddress = ( _internalOamAddress + 1 ) & 0x3FF ;
return value ;
}
2019-02-21 07:27:47 -05:00
case 0x2139 : {
//VMDATALREAD - VRAM Data Read low byte
2019-02-24 09:38:22 -05:00
uint8_t returnValue = ( uint8_t ) _vramReadBuffer ;
2019-03-01 22:24:18 -05:00
_console - > ProcessPpuRead ( GetVramAddress ( ) , returnValue , SnesMemoryType : : VideoRam ) ;
2019-02-21 07:27:47 -05:00
if ( ! _vramAddrIncrementOnSecondReg ) {
2019-02-24 09:38:22 -05:00
UpdateVramReadBuffer ( ) ;
2019-02-21 07:27:47 -05:00
_vramAddress = ( _vramAddress + _vramIncrementValue ) & 0x7FFF ;
}
return returnValue ;
}
case 0x213A : {
//VMDATAHREAD - VRAM Data Read high byte
2019-02-24 09:38:22 -05:00
uint8_t returnValue = ( uint8_t ) ( _vramReadBuffer > > 8 ) ;
2019-03-01 22:24:18 -05:00
_console - > ProcessPpuRead ( GetVramAddress ( ) + 1 , returnValue , SnesMemoryType : : VideoRam ) ;
2019-02-21 07:27:47 -05:00
if ( _vramAddrIncrementOnSecondReg ) {
2019-02-24 09:38:22 -05:00
UpdateVramReadBuffer ( ) ;
2019-02-21 07:27:47 -05:00
_vramAddress = ( _vramAddress + _vramIncrementValue ) & 0x7FFF ;
}
return returnValue ;
}
2019-02-21 17:45:11 -05:00
case 0x213B : {
2019-02-21 07:27:47 -05:00
//CGDATAREAD - CGRAM Data read
uint8_t value = _cgram [ _cgramAddress ] ;
2019-03-01 20:27:49 -05:00
_console - > ProcessPpuRead ( _cgramAddress , value , SnesMemoryType : : CGRam ) ;
2019-02-21 07:27:47 -05:00
_cgramAddress = ( _cgramAddress + 1 ) & ( Ppu : : CgRamSize - 1 ) ;
return value ;
}
2019-02-21 17:45:11 -05:00
case 0x213C : {
2019-02-21 07:27:47 -05:00
//OPHCT - Horizontal Scanline Location
2019-02-21 17:45:11 -05:00
uint8_t value ;
2019-02-21 07:27:47 -05:00
if ( _horizontalLocToggle ) {
//"Note that the value read is only 9 bits: bits 1-7 of the high byte are PPU2 Open Bus."
2019-02-21 17:45:11 -05:00
value = ( ( _horizontalLocation & 0x100 ) > > 8 ) | ( ( addr > > 8 ) & 0xFE ) ;
2019-02-21 07:27:47 -05:00
} else {
2019-02-21 17:45:11 -05:00
value = _horizontalLocation & 0xFF ;
2019-02-21 07:27:47 -05:00
}
2019-02-21 17:45:11 -05:00
_horizontalLocToggle = ! _horizontalLocToggle ;
return value ;
}
2019-02-21 07:27:47 -05:00
2019-02-21 17:45:11 -05:00
case 0x213D : {
2019-02-21 07:27:47 -05:00
//OPVCT - Vertical Scanline Location
2019-02-21 17:45:11 -05:00
uint8_t value ;
2019-02-21 07:27:47 -05:00
if ( _verticalLocationToggle ) {
//"Note that the value read is only 9 bits: bits 1-7 of the high byte are PPU2 Open Bus."
2019-02-21 17:45:11 -05:00
value = ( ( _verticalLocation & 0x100 ) > > 8 ) | ( ( addr > > 8 ) & 0xFE ) ;
2019-02-21 07:27:47 -05:00
} else {
2019-02-21 17:45:11 -05:00
value = _verticalLocation & 0xFF ;
2019-02-21 07:27:47 -05:00
}
2019-02-21 17:45:11 -05:00
_verticalLocationToggle = ! _verticalLocationToggle ;
return value ;
}
2019-02-21 07:27:47 -05:00
2019-02-19 18:01:27 -05:00
case 0x213E :
2019-02-21 18:11:31 -05:00
//STAT77 - PPU Status Flag and Version
2019-02-19 18:01:27 -05:00
//TODO open bus on bit 4
2019-02-21 07:27:47 -05:00
2019-02-19 18:01:27 -05:00
return (
( _timeOver ? 0x80 : 0 ) |
( _rangeOver ? 0x40 : 0 ) |
2019-02-21 18:11:31 -05:00
0x01 //PPU (5c77) chip version
2019-02-19 18:01:27 -05:00
) ;
2019-02-21 18:11:31 -05:00
case 0x213F : {
//STAT78 - PPU Status Flag and Version
//TODO open bus on bit 5
uint8_t value = (
( ( _frameCount & 0x01 ) ? 0x80 : 0 ) |
( _locationLatched ? 0x40 : 0 ) |
//TODO (_isPal ? 0x10 : 0)
0x02 //PPU (5c78) chip version
) ;
if ( _regs - > GetIoPortOutput ( ) & 0x80 ) {
_locationLatched = false ;
2019-03-01 22:24:18 -05:00
//"The high/low selector is reset to <20> elow<6F> f when $213F is read" (the selector is NOT reset when the counter is latched)
_horizontalLocToggle = false ;
_verticalLocationToggle = false ;
2019-02-21 18:11:31 -05:00
}
return value ;
}
2019-02-17 14:42:35 -05:00
default :
MessageManager : : DisplayMessage ( " Debug " , " Unimplemented register read: " + HexUtilities : : ToHex ( addr ) ) ;
break ;
2019-02-13 18:44:39 -05:00
}
2019-02-21 07:27:47 -05:00
return addr > > 8 ;
2019-02-13 18:44:39 -05:00
}
void Ppu : : Write ( uint32_t addr , uint8_t value )
{
switch ( addr ) {
2019-02-19 18:01:27 -05:00
case 0x2100 :
_forcedVblank = ( value & 0x80 ) ! = 0 ;
_screenBrightness = value & 0x0F ;
//TODO : Also, writing this register on the first line of V-Blank (225 or 240, depending on overscan) when force blank is currently active causes the OAM Address Reset to occur.
break ;
2019-02-17 19:54:29 -05:00
case 0x2101 :
2019-02-17 21:09:33 -05:00
_oamMode = ( value & 0xE0 ) > > 5 ;
_oamBaseAddress = ( value & 0x07 ) < < 13 ;
2019-02-17 22:44:57 -05:00
_oamAddressOffset = ( ( ( value & 0x18 ) > > 3 ) + 1 ) < < 12 ;
2019-02-17 19:54:29 -05:00
break ;
case 0x2102 :
2019-02-17 21:09:33 -05:00
_oamRamAddress = ( _oamRamAddress & 0x100 ) | value ;
_internalOamAddress = ( _oamRamAddress < < 1 ) ;
2019-02-17 19:54:29 -05:00
break ;
case 0x2103 :
2019-02-17 21:09:33 -05:00
_oamRamAddress = ( _oamRamAddress & 0xFF ) | ( ( value & 0x01 ) < < 8 ) ;
_internalOamAddress = ( _oamRamAddress < < 1 ) ;
_enableOamPriority = ( value & 0x80 ) ! = 0 ;
2019-02-17 19:54:29 -05:00
break ;
case 0x2104 :
2019-02-17 21:09:33 -05:00
if ( _internalOamAddress < 512 ) {
if ( _internalOamAddress & 0x01 ) {
2019-03-01 20:27:49 -05:00
_console - > ProcessPpuWrite ( _internalOamAddress - 1 , _oamWriteBuffer , SnesMemoryType : : SpriteRam ) ;
2019-02-17 21:09:33 -05:00
_oamRam [ _internalOamAddress - 1 ] = _oamWriteBuffer ;
2019-03-01 20:27:49 -05:00
_console - > ProcessPpuWrite ( _internalOamAddress , value , SnesMemoryType : : SpriteRam ) ;
2019-02-17 21:09:33 -05:00
_oamRam [ _internalOamAddress ] = value ;
} else {
_oamWriteBuffer = value ;
}
2019-02-19 18:41:59 -05:00
} else {
2019-02-17 21:09:33 -05:00
uint16_t address = 0x200 | ( _internalOamAddress & 0x1F ) ;
2019-02-19 18:41:59 -05:00
if ( ( _internalOamAddress & 0x01 ) = = 0 ) {
_oamWriteBuffer = value ;
}
2019-03-01 20:27:49 -05:00
_console - > ProcessPpuWrite ( address , value , SnesMemoryType : : SpriteRam ) ;
2019-02-17 21:09:33 -05:00
_oamRam [ address ] = value ;
}
_internalOamAddress = ( _internalOamAddress + 1 ) & 0x3FF ;
2019-02-17 19:54:29 -05:00
break ;
2019-02-17 00:32:41 -05:00
case 0x2105 :
2019-02-23 21:39:35 -05:00
if ( _bgMode ! = ( value & 0x07 ) ) {
MessageManager : : DisplayMessage ( " Debug " , " Entering mode: " + std : : to_string ( value & 0x07 ) ) ;
}
2019-02-17 00:32:41 -05:00
_bgMode = value & 0x07 ;
2019-02-19 01:26:48 -05:00
_mode1Bg3Priority = ( value & 0x08 ) ! = 0 ;
2019-02-17 01:09:47 -05:00
2019-02-17 00:32:41 -05:00
_layerConfig [ 0 ] . LargeTiles = ( value & 0x10 ) ! = 0 ;
_layerConfig [ 1 ] . LargeTiles = ( value & 0x20 ) ! = 0 ;
2019-02-23 01:28:41 -05:00
_layerConfig [ 2 ] . LargeTiles = ( value & 0x40 ) ! = 0 ;
_layerConfig [ 3 ] . LargeTiles = ( value & 0x80 ) ! = 0 ;
2019-02-17 00:32:41 -05:00
break ;
2019-02-20 17:39:14 -05:00
case 0x2106 :
//MOSAIC - Screen Pixelation
_mosaicSize = ( ( value & 0xF0 ) > > 4 ) + 1 ;
_mosaicEnabled = value & 0x0F ;
if ( _mosaicEnabled ) {
//"If this register is set during the frame, the <20> estarting scanline<6E> f is the current scanline, otherwise it is the first visible scanline of the frame."
_mosaicStartScanline = _scanline ;
}
break ;
2019-02-13 18:44:39 -05:00
case 0x2107 : case 0x2108 : case 0x2109 : case 0x210A :
//BG 1-4 Tilemap Address and Size (BG1SC, BG2SC, BG3SC, BG4SC)
2019-02-13 23:03:01 -05:00
_layerConfig [ addr - 0x2107 ] . TilemapAddress = ( value & 0xFC ) < < 9 ;
2019-02-19 17:23:21 -05:00
_layerConfig [ addr - 0x2107 ] . HorizontalMirroring = ( value & 0x01 ) ! = 0 ;
_layerConfig [ addr - 0x2107 ] . VerticalMirroring = ( value & 0x02 ) ! = 0 ;
2019-02-13 18:44:39 -05:00
break ;
case 0x210B : case 0x210C :
//BG1+2 / BG3+4 Chr Address (BG12NBA / BG34NBA)
2019-02-19 17:23:21 -05:00
_layerConfig [ ( addr - 0x210B ) * 2 ] . ChrAddress = ( value & 0x0F ) < < 13 ;
_layerConfig [ ( addr - 0x210B ) * 2 + 1 ] . ChrAddress = ( value & 0xF0 ) < < 9 ;
2019-02-13 18:44:39 -05:00
break ;
2019-02-19 17:23:21 -05:00
case 0x210D :
2019-02-23 15:40:32 -05:00
//M7HOFS - Mode 7 BG Horizontal Scroll
//BG1HOFS - BG1 Horizontal Scroll
_mode7 . HScroll = ( ( value < < 8 ) | ( _mode7 . ValueLatch ) ) & 0x1FFF ;
_mode7 . ValueLatch = value ;
//no break, keep executing to set the matching BG1 HScroll register, too
2019-02-19 17:23:21 -05:00
case 0x210F : case 0x2111 : case 0x2113 :
2019-02-23 15:40:32 -05:00
//BGXHOFS - BG1/2/3/4 Horizontal Scroll
2019-02-19 17:23:21 -05:00
_layerConfig [ ( addr - 0x210D ) > > 1 ] . HScroll = ( ( value < < 8 ) | ( _hvScrollLatchValue & ~ 0x07 ) | ( _hScrollLatchValue & 0x07 ) ) & 0x3FF ;
_hvScrollLatchValue = value ;
_hScrollLatchValue = value ;
break ;
case 0x210E :
2019-02-23 15:40:32 -05:00
//M7VOFS - Mode 7 BG Vertical Scroll
//BG1VOFS - BG1 Vertical Scroll
_mode7 . VScroll = ( ( value < < 8 ) | ( _mode7 . ValueLatch ) ) & 0x1FFF ;
_mode7 . ValueLatch = value ;
//no break, keep executing to set the matching BG1 HScroll register, too
2019-02-19 17:23:21 -05:00
case 0x2110 : case 0x2112 : case 0x2114 :
2019-02-23 15:40:32 -05:00
//BGXVOFS - BG1/2/3/4 Vertical Scroll
2019-02-19 17:23:21 -05:00
_layerConfig [ ( addr - 0x210E ) > > 1 ] . VScroll = ( ( value < < 8 ) | _hvScrollLatchValue ) & 0x3FF ;
_hvScrollLatchValue = value ;
break ;
2019-02-13 18:44:39 -05:00
case 0x2115 :
//VMAIN - Video Port Control
switch ( value & 0x03 ) {
case 0 : _vramIncrementValue = 1 ; break ;
case 1 : _vramIncrementValue = 32 ; break ;
case 2 :
case 3 : _vramIncrementValue = 128 ; break ;
}
2019-02-21 07:27:47 -05:00
//TODO : Remapping is not implemented yet
2019-02-13 18:44:39 -05:00
_vramAddressRemapping = ( value & 0x0C ) > > 2 ;
2019-02-21 07:27:47 -05:00
2019-02-13 18:44:39 -05:00
_vramAddrIncrementOnSecondReg = ( value & 0x80 ) ! = 0 ;
break ;
case 0x2116 :
//VMADDL - VRAM Address low byte
2019-02-16 01:16:57 -05:00
_vramAddress = ( _vramAddress & 0x7F00 ) | value ;
2019-02-24 09:38:22 -05:00
UpdateVramReadBuffer ( ) ;
2019-02-13 18:44:39 -05:00
break ;
case 0x2117 :
//VMADDH - VRAM Address high byte
2019-02-16 01:16:57 -05:00
_vramAddress = ( _vramAddress & 0x00FF ) | ( ( value & 0x7F ) < < 8 ) ;
2019-02-24 09:38:22 -05:00
UpdateVramReadBuffer ( ) ;
2019-02-13 18:44:39 -05:00
break ;
case 0x2118 :
//VMDATAL - VRAM Data Write low byte
2019-03-01 22:24:18 -05:00
_console - > ProcessPpuWrite ( GetVramAddress ( ) < < 1 , value , SnesMemoryType : : VideoRam ) ;
2019-03-01 20:27:49 -05:00
2019-03-01 22:24:18 -05:00
_vram [ GetVramAddress ( ) < < 1 ] = value ;
2019-02-13 18:44:39 -05:00
if ( ! _vramAddrIncrementOnSecondReg ) {
2019-02-16 10:24:43 -05:00
_vramAddress = ( _vramAddress + _vramIncrementValue ) & 0x7FFF ;
2019-02-13 18:44:39 -05:00
}
break ;
case 0x2119 :
//VMDATAH - VRAM Data Write high byte
2019-03-01 22:24:18 -05:00
_console - > ProcessPpuWrite ( ( GetVramAddress ( ) < < 1 ) + 1 , value , SnesMemoryType : : VideoRam ) ;
2019-03-01 20:27:49 -05:00
2019-03-01 22:24:18 -05:00
_vram [ ( GetVramAddress ( ) < < 1 ) + 1 ] = value ;
2019-02-13 18:44:39 -05:00
if ( _vramAddrIncrementOnSecondReg ) {
2019-02-16 10:24:43 -05:00
_vramAddress = ( _vramAddress + _vramIncrementValue ) & 0x7FFF ;
2019-02-13 18:44:39 -05:00
}
break ;
2019-02-23 15:40:32 -05:00
case 0x211A :
//M7SEL - Mode 7 Settings
_mode7 . LargeMap = ( value & 0x80 ) ! = 0 ;
_mode7 . FillWithTile0 = ( value & 0x40 ) ! = 0 ;
_mode7 . HorizontalMirroring = ( value & 0x01 ) ! = 0 ;
_mode7 . VerticalMirroring = ( value & 0x02 ) ! = 0 ;
break ;
case 0x211B : case 0x211C : case 0x211D : case 0x211E :
//M7A/B/C/D - Mode 7 Matrix A/B/C/D (A/B are also used with $2134/6)
_mode7 . Matrix [ addr - 0x211B ] = ( value < < 8 ) | _mode7 . ValueLatch ;
_mode7 . ValueLatch = value ;
break ;
case 0x211F :
//M7X - Mode 7 Center X
_mode7 . CenterX = ( ( value < < 8 ) | _mode7 . ValueLatch ) ;
_mode7 . ValueLatch = value ;
2019-02-21 08:15:00 -05:00
break ;
2019-02-23 15:40:32 -05:00
case 0x2120 :
//M7Y - Mode 7 Center Y
_mode7 . CenterY = ( ( value < < 8 ) | _mode7 . ValueLatch ) ;
_mode7 . ValueLatch = value ;
2019-02-21 08:15:00 -05:00
break ;
2019-02-13 18:44:39 -05:00
case 0x2121 :
//CGRAM Address(CGADD)
2019-02-13 23:03:01 -05:00
_cgramAddress = value * 2 ;
2019-02-13 18:44:39 -05:00
break ;
case 0x2122 :
//CGRAM Data write (CGDATA)
2019-03-01 20:27:49 -05:00
_console - > ProcessPpuWrite ( _cgramAddress , value , SnesMemoryType : : CGRam ) ;
2019-02-13 18:44:39 -05:00
_cgram [ _cgramAddress ] = value ;
2019-02-16 08:08:16 -05:00
_cgramAddress = ( _cgramAddress + 1 ) & ( Ppu : : CgRamSize - 1 ) ;
2019-02-13 18:44:39 -05:00
break ;
2019-02-22 20:15:55 -05:00
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
2019-02-24 19:02:21 -05:00
_maskLogic [ 4 ] = ( WindowMaskLogic ) ( ( value > > 0 ) & 0x03 ) ;
_maskLogic [ 5 ] = ( WindowMaskLogic ) ( ( value > > 2 ) & 0x03 ) ;
2019-02-22 20:15:55 -05:00
break ;
2019-02-17 23:26:49 -05:00
case 0x212C :
//TM - Main Screen Designation
_mainScreenLayers = value & 0x1F ;
break ;
2019-02-19 01:26:48 -05:00
case 0x212D :
//TS - Subscreen Designation
_subScreenLayers = value & 0x1F ;
break ;
2019-02-22 20:15:55 -05:00
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 ;
2019-02-17 23:53:19 -05:00
case 0x2130 :
//CGWSEL - Color Addition Select
2019-02-22 22:19:20 -05:00
_colorMathClipMode = ( ColorWindowMode ) ( ( value > > 6 ) & 0x03 ) ;
_colorMathPreventMode = ( ColorWindowMode ) ( ( value > > 4 ) & 0x03 ) ;
2019-02-17 23:53:19 -05:00
_colorMathAddSubscreen = ( value & 0x02 ) ! = 0 ;
2019-02-24 19:21:19 -05:00
_directColorMode = ( value & 0x01 ) ! = 0 ;
2019-02-17 23:53:19 -05:00
break ;
case 0x2131 :
2019-02-19 23:35:43 -05:00
//CGADSUB - Color math designation
2019-02-17 23:53:19 -05:00
_colorMathEnabled = value & 0x3F ;
_colorMathSubstractMode = ( value & 0x80 ) ! = 0 ;
2019-02-18 00:24:46 -05:00
_colorMathHalveResult = ( value & 0x40 ) ! = 0 ;
2019-02-17 23:53:19 -05:00
break ;
2019-02-17 23:26:49 -05:00
2019-02-19 23:35:43 -05:00
case 0x2132 :
//COLDATA - Fixed Color Data
if ( value & 0x80 ) { //B
_fixedColor = ( _fixedColor & ~ 0x7C00 ) | ( ( value & 0x1F ) < < 10 ) ;
}
if ( value & 0x40 ) { //G
_fixedColor = ( _fixedColor & ~ 0x3E0 ) | ( ( value & 0x1F ) < < 5 ) ;
}
if ( value & 0x20 ) { //R
_fixedColor = ( _fixedColor & ~ 0x1F ) | ( value & 0x1F ) ;
}
break ;
2019-02-23 16:04:04 -05:00
case 0x2133 :
//SETINI - Screen Mode/Video Select
//_externalSync = (value & 0x80) != 0; //NOT USED
_mode7 . ExtBgEnabled = ( value & 0x40 ) ! = 0 ;
2019-02-24 11:14:24 -05:00
_hiResMode = ( value & 0x08 ) ! = 0 ;
2019-02-23 21:39:35 -05:00
_overscanMode = ( value & 0x04 ) ! = 0 ;
2019-02-24 19:21:19 -05:00
_objInterlace = ( value & 0x02 ) ! = 0 ;
2019-02-23 21:39:35 -05:00
_screenInterlace = ( value & 0x01 ) ! = 0 ;
2019-02-23 16:04:04 -05:00
break ;
2019-02-17 14:42:35 -05:00
default :
2019-02-19 18:01:27 -05:00
MessageManager : : DisplayMessage ( " Debug " , " Unimplemented register write: " + HexUtilities : : ToHex ( addr ) + " = " + HexUtilities : : ToHex ( value ) ) ;
2019-02-17 14:42:35 -05:00
break ;
2019-02-13 18:44:39 -05:00
}
}
2019-02-24 11:14:24 -05:00
/* Everything below this point is used to select the proper arguments for templates */
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode , bool largeTileWidth , bool largeTileHeight , uint8_t activeWindowCount , bool applyMosaic >
void Ppu : : RenderTilemap ( )
{
if ( _directColorMode ) {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , largeTileWidth , largeTileHeight , activeWindowCount , applyMosaic , true > ( ) ;
} else {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , largeTileWidth , largeTileHeight , activeWindowCount , applyMosaic , false > ( ) ;
}
}
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode , bool largeTileWidth , bool largeTileHeight , uint8_t activeWindowCount >
void Ppu : : RenderTilemap ( )
{
bool applyMosaic = forMainScreen & & ( ( _mosaicEnabled > > layerIndex ) & 0x01 ) ! = 0 ;
if ( applyMosaic ) {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , largeTileWidth , largeTileHeight , activeWindowCount , true > ( ) ;
} else {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , largeTileWidth , largeTileHeight , activeWindowCount , false > ( ) ;
}
}
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode , bool largeTileWidth , bool largeTileHeight >
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 ] ;
}
if ( activeWindowCount = = 0 ) {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , largeTileWidth , largeTileHeight , 0 > ( ) ;
} else if ( activeWindowCount = = 1 ) {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , largeTileWidth , largeTileHeight , 1 > ( ) ;
} else {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , largeTileWidth , largeTileHeight , 2 > ( ) ;
}
}
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode >
void Ppu : : RenderTilemap ( )
{
bool largeTileWidth = _layerConfig [ layerIndex ] . LargeTiles | hiResMode ;
bool largeTileHeight = _layerConfig [ layerIndex ] . LargeTiles ;
if ( largeTileWidth ) {
if ( largeTileHeight ) {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , true , true > ( ) ;
} else {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , true , false > ( ) ;
}
} else {
if ( largeTileHeight ) {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , false , true > ( ) ;
} else {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , false , false > ( ) ;
}
}
}
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset >
void Ppu : : RenderTilemap ( )
{
if ( _hiResMode | | _bgMode = = 5 | | _bgMode = = 6 ) {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , true > ( ) ;
} else {
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , false > ( ) ;
}
}
template < uint8_t layerIndex , bool forMainScreen , bool processHighPriority >
void Ppu : : RenderTilemapMode7 ( )
{
bool applyMosaic = forMainScreen & & ( ( _mosaicEnabled > > layerIndex ) & 0x01 ) ! = 0 ;
if ( applyMosaic ) {
RenderTilemapMode7 < layerIndex , forMainScreen , processHighPriority , true > ( ) ;
} else {
RenderTilemapMode7 < layerIndex , forMainScreen , processHighPriority , false > ( ) ;
}
}
template < uint8_t layerIndex , bool forMainScreen , bool processHighPriority , bool applyMosaic >
void Ppu : : RenderTilemapMode7 ( )
{
if ( _directColorMode ) {
RenderTilemapMode7 < layerIndex , forMainScreen , processHighPriority , applyMosaic , true > ( ) ;
} else {
RenderTilemapMode7 < layerIndex , forMainScreen , processHighPriority , applyMosaic , false > ( ) ;
}
}