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-04-10 22:32:28 -04:00
# include "EmuSettings.h"
2019-02-17 19:54:29 -05:00
# include "ControlManager.h"
2019-02-13 23:03:01 -05:00
# include "VideoDecoder.h"
2019-04-10 22:32:28 -04:00
# include "VideoRenderer.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"
2019-03-07 20:12:32 -05:00
# include "EventType.h"
2019-03-12 12:06:42 -04:00
# include "RewindManager.h"
2019-02-26 22:27:09 -05:00
# include "../Utilities/HexUtilities.h"
2019-03-12 09:15:57 -04:00
# include "../Utilities/Serializer.h"
2019-02-13 18:44:39 -05:00
2019-03-31 14:50:12 -04:00
static constexpr uint8_t _oamSizes [ 8 ] [ 2 ] [ 2 ] = {
{ { 1 , 1 } , { 2 , 2 } } , //8x8 + 16x16
{ { 1 , 1 } , { 4 , 4 } } , //8x8 + 32x32
{ { 1 , 1 } , { 8 , 8 } } , //8x8 + 64x64
{ { 2 , 2 } , { 4 , 4 } } , //16x16 + 32x32
{ { 2 , 2 } , { 8 , 8 } } , //16x16 + 64x64
{ { 4 , 4 } , { 8 , 8 } } , //32x32 + 64x64
{ { 2 , 4 } , { 4 , 8 } } , //16x32 + 32x64
{ { 2 , 4 } , { 4 , 4 } } //16x32 + 32x32
} ;
2019-02-13 18:44:39 -05:00
Ppu : : Ppu ( shared_ptr < Console > console )
{
_console = console ;
2019-04-10 16:04:55 -04:00
_vram = new uint8_t [ Ppu : : VideoRamSize ] ;
2019-07-06 14:25:51 -04:00
_console - > GetSettings ( ) - > InitializeRam ( _vram , Ppu : : VideoRamSize ) ;
_console - > GetSettings ( ) - > InitializeRam ( _cgram , Ppu : : CgRamSize ) ;
_console - > GetSettings ( ) - > InitializeRam ( _oamRam , Ppu : : SpriteRamSize ) ;
2019-02-23 21:39:35 -05:00
_outputBuffers [ 0 ] = new uint16_t [ 512 * 478 ] ;
_outputBuffers [ 1 ] = new uint16_t [ 512 * 478 ] ;
2019-03-14 23:30:47 -04:00
memset ( _outputBuffers [ 0 ] , 0 , 512 * 478 * sizeof ( uint16_t ) ) ;
memset ( _outputBuffers [ 1 ] , 0 , 512 * 478 * sizeof ( uint16_t ) ) ;
2019-04-10 16:04:55 -04:00
}
Ppu : : ~ Ppu ( )
{
delete [ ] _vram ;
delete [ ] _outputBuffers [ 0 ] ;
delete [ ] _outputBuffers [ 1 ] ;
}
void Ppu : : PowerOn ( )
{
2019-04-10 22:32:28 -04:00
_allowFrameSkip = false ;
2019-04-10 16:04:55 -04:00
_regs = _console - > GetInternalRegisters ( ) ;
2019-04-20 11:46:51 -04:00
_memoryManager = _console - > GetMemoryManager ( ) ;
2019-02-16 01:16:57 -05:00
2019-04-10 16:04:55 -04:00
_vblankStart = _overscanMode ? 240 : 225 ;
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
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-03-16 12:20:18 -04:00
void Ppu : : Reset ( )
{
_scanline = 0 ;
_forcedVblank = true ;
2019-04-20 09:30:51 -04:00
_oddFrame = 0 ;
2019-03-16 12:20:18 -04:00
}
2019-02-17 19:54:29 -05:00
uint32_t Ppu : : GetFrameCount ( )
{
return _frameCount ;
}
2019-03-07 20:12:32 -05:00
uint16_t Ppu : : GetScanline ( )
{
return _scanline ;
}
uint16_t Ppu : : GetCycle ( )
{
2019-04-10 21:38:10 -04:00
//"normally dots 323 and 327 are 6 master cycles instead of 4."
2019-04-20 11:46:51 -04:00
uint16_t hClock = _memoryManager - > GetHClock ( ) ;
return ( hClock - ( ( hClock > 1292 ) < < 1 ) - ( ( hClock > 1310 ) < < 1 ) ) > > 2 ;
2019-03-07 20:12:32 -05:00
}
2019-04-10 16:04:55 -04:00
uint16_t Ppu : : GetVblankStart ( )
{
return _vblankStart ;
}
2019-02-13 18:44:39 -05:00
PpuState Ppu : : GetState ( )
{
2019-03-03 16:34:23 -05:00
PpuState state ;
2019-04-10 21:38:10 -04:00
state . Cycle = GetCycle ( ) ;
2019-03-03 16:34:23 -05:00
state . Scanline = _scanline ;
2019-04-20 11:46:51 -04:00
state . HClock = _memoryManager - > GetHClock ( ) ;
2019-03-03 16:34:23 -05:00
state . FrameCount = _frameCount ;
state . OverscanMode = _overscanMode ;
state . BgMode = _bgMode ;
2019-03-23 17:23:36 -04:00
state . DirectColorMode = _directColorMode ;
state . Mode7 = _mode7 ;
2019-03-03 16:34:23 -05:00
state . Layers [ 0 ] = _layerConfig [ 0 ] ;
state . Layers [ 1 ] = _layerConfig [ 1 ] ;
state . Layers [ 2 ] = _layerConfig [ 2 ] ;
state . Layers [ 3 ] = _layerConfig [ 3 ] ;
2019-04-25 19:49:15 -04:00
state . OamMode = _oamMode ;
state . OamBaseAddress = _oamBaseAddress ;
state . OamAddressOffset = _oamAddressOffset ;
state . EnableOamPriority = _enableOamPriority ;
state . ObjInterlace = _objInterlace ;
2019-03-03 16:34:23 -05:00
return state ;
2019-02-13 18:44:39 -05:00
}
2019-07-07 18:18:20 -04:00
template < bool hiResMode >
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 < bool hiResMode , uint8_t bpp , bool secondTile >
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 < false > ( 3 , x > > 3 ) ; break ;
case 1 : GetTilemapData < false > ( 2 , x > > 3 ) ; break ;
case 2 : GetTilemapData < false > ( 1 , x > > 3 ) ; break ;
case 3 : GetTilemapData < false > ( 0 , x > > 3 ) ; break ;
case 4 : GetChrData < false , 2 > ( 3 , x > > 3 , 0 ) ; break ;
case 5 : GetChrData < false , 2 > ( 2 , x > > 3 , 0 ) ; break ;
case 6 : GetChrData < false , 2 > ( 1 , x > > 3 , 0 ) ; break ;
case 7 : GetChrData < false , 2 > ( 0 , x > > 3 , 0 ) ; break ;
}
}
} else if ( _bgMode = = 1 ) {
for ( int x = _fetchStartX ; x < = _fetchEndX ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < false > ( 2 , x > > 3 ) ; break ;
case 1 : GetTilemapData < false > ( 1 , x > > 3 ) ; break ;
case 2 : GetTilemapData < false > ( 0 , x > > 3 ) ; break ;
case 3 : GetChrData < false , 2 > ( 2 , x > > 3 , 0 ) ; break ;
case 4 : GetChrData < false , 4 > ( 1 , x > > 3 , 0 ) ; break ;
case 5 : GetChrData < false , 4 > ( 1 , x > > 3 , 1 ) ; break ;
case 6 : GetChrData < false , 4 > ( 0 , x > > 3 , 0 ) ; break ;
case 7 : GetChrData < false , 4 > ( 0 , x > > 3 , 1 ) ; break ;
}
}
} else if ( _bgMode = = 2 ) {
for ( int x = _fetchStartX ; x < = _fetchEndX ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < false > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < false > ( 0 , x > > 3 ) ; break ;
case 2 : GetHorizontalOffsetByte ( x > > 3 ) ; break ;
case 3 : GetVerticalOffsetByte ( x > > 3 ) ; break ;
case 4 : GetChrData < false , 4 > ( 1 , x > > 3 , 0 ) ; break ;
case 5 : GetChrData < false , 4 > ( 1 , x > > 3 , 1 ) ; break ;
case 6 : GetChrData < false , 4 > ( 0 , x > > 3 , 0 ) ; break ;
case 7 : GetChrData < false , 4 > ( 0 , x > > 3 , 1 ) ; break ;
}
}
} else if ( _bgMode = = 3 ) {
for ( int x = _fetchStartX ; x < = _fetchEndX ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < false > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < false > ( 0 , x > > 3 ) ; break ;
case 2 : GetChrData < false , 4 > ( 1 , x > > 3 , 0 ) ; break ;
case 3 : GetChrData < false , 4 > ( 1 , x > > 3 , 1 ) ; break ;
case 4 : GetChrData < false , 8 > ( 0 , x > > 3 , 0 ) ; break ;
case 5 : GetChrData < false , 8 > ( 0 , x > > 3 , 1 ) ; break ;
case 6 : GetChrData < false , 8 > ( 0 , x > > 3 , 2 ) ; break ;
case 7 : GetChrData < false , 8 > ( 0 , x > > 3 , 3 ) ; break ;
}
}
} else if ( _bgMode = = 4 ) {
for ( int x = _fetchStartX ; x < = _fetchEndX ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < false > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < false > ( 0 , x > > 3 ) ; break ;
case 2 : GetHorizontalOffsetByte ( x > > 3 ) ; break ;
case 3 : GetChrData < false , 2 > ( 1 , x > > 3 , 0 ) ; break ;
case 4 : GetChrData < false , 8 > ( 0 , x > > 3 , 0 ) ; break ;
case 5 : GetChrData < false , 8 > ( 0 , x > > 3 , 1 ) ; break ;
case 6 : GetChrData < false , 8 > ( 0 , x > > 3 , 2 ) ; break ;
case 7 : GetChrData < false , 8 > ( 0 , x > > 3 , 3 ) ; break ;
}
}
} else if ( _bgMode = = 5 ) {
for ( int x = _fetchStartX ; x < = _fetchEndX ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < true > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < true > ( 0 , x > > 3 ) ; break ;
case 2 : GetChrData < true , 2 > ( 1 , x > > 3 , 0 ) ; break ;
case 3 : GetChrData < true , 2 , true > ( 1 , x > > 3 , 0 ) ; break ;
case 4 : GetChrData < true , 4 > ( 0 , x > > 3 , 0 ) ; break ;
case 5 : GetChrData < true , 4 > ( 0 , x > > 3 , 1 ) ; break ;
case 6 : GetChrData < true , 4 , true > ( 0 , x > > 3 , 0 ) ; break ;
case 7 : GetChrData < true , 4 , true > ( 0 , x > > 3 , 1 ) ; break ;
}
}
} else if ( _bgMode = = 6 ) {
for ( int x = _fetchStartX ; x < = _fetchEndX ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < true > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < true > ( 0 , x > > 3 ) ; break ;
case 2 : GetHorizontalOffsetByte ( x > > 3 ) ; break ;
case 3 : GetVerticalOffsetByte ( x > > 3 ) ; break ;
case 4 : GetChrData < true , 4 > ( 0 , x > > 3 , 0 ) ; break ;
case 5 : GetChrData < true , 4 > ( 0 , x > > 3 , 1 ) ; break ;
case 6 : GetChrData < true , 4 , true > ( 0 , x > > 3 , 0 ) ; break ;
case 7 : GetChrData < true , 4 , true > ( 0 , x > > 3 , 1 ) ; break ;
}
}
}
}
2019-04-20 11:46:51 -04:00
bool Ppu : : ProcessEndOfScanline ( uint16_t hClock )
2019-02-13 18:44:39 -05:00
{
2019-04-20 11:46:51 -04:00
if ( hClock > = 1364 | | ( hClock = = 1360 & & _scanline = = 240 & & _oddFrame & & ! _screenInterlace ) ) {
2019-04-10 21:38:10 -04:00
//"In non-interlace mode scanline 240 of every other frame (those with $213f.7=1) is only 1360 cycles."
2019-02-13 18:44:39 -05:00
_scanline + + ;
2019-03-04 17:49:14 -05:00
_drawStartX = 0 ;
_drawEndX = 0 ;
2019-07-07 18:18:20 -04:00
_fetchStartX = 0 ;
_fetchEndX = 0 ;
2019-03-04 17:49:14 -05:00
_pixelsDrawn = 0 ;
_subPixelsDrawn = 0 ;
memset ( _rowPixelFlags , 0 , sizeof ( _rowPixelFlags ) ) ;
memset ( _subScreenFilled , 0 , sizeof ( _subScreenFilled ) ) ;
2019-02-19 18:01:27 -05:00
2019-04-10 16:04:55 -04:00
if ( _scanline = = _vblankStart ) {
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 ) {
2019-04-04 21:04:03 -04:00
//TODO, the timing of this may be slightly off? should happen at H=10 based on anomie's docs
2019-02-19 18:41:59 -05:00
_internalOamAddress = ( _oamRamAddress < < 1 ) ;
}
2019-02-17 21:09:33 -05:00
2019-05-20 15:35:09 -04:00
VideoConfig cfg = _console - > GetSettings ( ) - > GetVideoConfig ( ) ;
_configVisibleLayers = ( cfg . HideBgLayer0 ? 0 : 1 ) | ( cfg . HideBgLayer1 ? 0 : 2 ) | ( cfg . HideBgLayer2 ? 0 : 4 ) | ( cfg . HideBgLayer3 ? 0 : 8 ) | ( cfg . HideSprites ? 0 : 16 ) ;
2019-05-12 21:18:05 -04:00
_console - > ProcessEvent ( EventType : : EndFrame ) ;
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
_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-04-14 15:22:34 -04:00
} else if ( _scanline > = GetLastScanline ( ) + 1 ) {
2019-03-08 10:26:54 -05:00
//"Frames are 262 scanlines in non-interlace mode, while in interlace mode frames with $213f.7=0 are 263 scanlines"
2019-04-08 10:16:12 -04:00
_oddFrame ^ = 1 ;
2019-02-17 19:54:29 -05:00
_regs - > SetNmiFlag ( false ) ;
2019-02-13 18:44:39 -05:00
_scanline = 0 ;
2019-03-02 00:11:42 -05:00
_rangeOver = false ;
_timeOver = false ;
2019-03-07 20:12:32 -05:00
_console - > ProcessEvent ( EventType : : StartFrame ) ;
2019-02-21 22:10:41 -05:00
2019-07-06 14:58:09 -04:00
_allowFrameSkip = ! _console - > GetVideoRenderer ( ) - > IsRecording ( ) & & ( _console - > GetSettings ( ) - > GetEmulationSpeed ( ) = = 0 | | _console - > GetSettings ( ) - > GetEmulationSpeed ( ) > 150 ) ;
if ( ! _allowFrameSkip | | ( _frameCount & 0x03 ) = = 0 ) {
//If we're not skipping this frame, reset the high resolution flag
_useHighResOutput = false ;
}
2019-02-20 17:39:14 -05:00
if ( _mosaicEnabled ) {
2019-03-31 09:38:47 -04:00
_mosaicStartScanline = 1 ;
2019-02-20 17:39:14 -05:00
}
2019-02-13 18:44:39 -05:00
}
2019-04-20 11:46:51 -04:00
return true ;
2019-02-17 01:09:47 -05:00
}
2019-04-20 11:46:51 -04:00
return false ;
2019-02-13 18:44:39 -05:00
}
2019-04-14 15:22:34 -04:00
uint16_t Ppu : : GetLastScanline ( )
{
if ( _console - > GetRegion ( ) = = ConsoleRegion : : Ntsc ) {
if ( ! _screenInterlace | | _oddFrame ) {
return 261 ;
} else {
return 262 ;
}
} else {
if ( ! _screenInterlace | | _oddFrame ) {
return 311 ;
} else {
return 312 ;
}
}
}
2019-02-22 22:35:53 -05:00
void Ppu : : EvaluateNextLineSprites ( )
{
2019-04-20 11:46:51 -04:00
if ( _forcedVblank ) {
return ;
}
2019-02-22 22:35:53 -05:00
memset ( _spritePriority , 0xFF , sizeof ( _spritePriority ) ) ;
memset ( _spritePixels , 0xFF , sizeof ( _spritePixels ) ) ;
memset ( _spritePalette , 0 , sizeof ( _spritePalette ) ) ;
_spriteCount = 0 ;
2019-03-02 18:00:27 -05:00
uint16_t screenY = _scanline ;
2019-02-22 22:35:53 -05:00
2019-03-02 13:51:42 -05:00
uint16_t baseAddr = _enableOamPriority ? ( _internalOamAddress & 0x1FC ) : 0 ;
2019-02-22 22:35:53 -05:00
for ( int i = 0 ; i < 512 ; i + = 4 ) {
2019-03-02 13:51:42 -05:00
uint16_t addr = ( baseAddr + i ) & 0x1FF ;
uint8_t y = _oamRam [ addr + 1 ] ;
2019-02-22 22:35:53 -05:00
2019-03-02 13:51:42 -05:00
uint8_t highTableOffset = addr > > 4 ;
uint8_t shift = ( ( addr > > 2 ) & 0x03 ) < < 1 ;
2019-02-22 22:35:53 -05:00
uint8_t highTableValue = _oamRam [ 0x200 | highTableOffset ] > > shift ;
uint8_t largeSprite = ( highTableValue & 0x02 ) > > 1 ;
uint8_t height = _oamSizes [ _oamMode ] [ largeSprite ] [ 1 ] < < 3 ;
2019-03-02 18:00:27 -05:00
if ( _objInterlace ) {
height / = 2 ;
}
2019-02-22 22:35:53 -05:00
2019-04-02 23:46:14 -04:00
uint8_t endY = ( y + height ) & 0xFF ;
2019-03-03 00:31:28 -05:00
2019-04-02 23:46:14 -04:00
bool visible = ( screenY > = y & & screenY < endY ) | | ( endY < y & & screenY < endY ) ;
if ( ! visible ) {
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 ;
2019-03-02 13:51:42 -05:00
info . X = ( int16_t ) ( ( sign | _oamRam [ addr ] ) < < 7 ) > > 7 ;
info . Y = y ;
if ( info . X ! = - 256 & & ( info . X + width < = 0 | | info . X > 255 ) ) {
2019-02-24 13:09:22 -05:00
//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
2019-04-04 20:46:29 -04:00
info . Index = i > > 2 ;
2019-03-02 13:51:42 -05:00
info . TileRow = ( _oamRam [ addr + 2 ] & 0xF0 ) > > 4 ;
info . TileColumn = _oamRam [ addr + 2 ] & 0x0F ;
2019-02-22 22:35:53 -05:00
2019-03-02 13:51:42 -05:00
uint8_t flags = _oamRam [ addr + 3 ] ;
2019-02-22 22:35:53 -05:00
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 ;
2019-03-02 13:51:42 -05:00
if ( _spriteCount < 32 ) {
_spriteCount + + ;
} else {
_rangeOver = true ;
break ;
}
}
uint16_t spriteTileCount = 0 ;
for ( int i = ( int16_t ) _spriteCount - 1 ; i > = 0 ; i - - ) {
SpriteInfo & info = _sprites [ i ] ;
uint8_t height = _oamSizes [ _oamMode ] [ info . LargeSprite ] [ 1 ] < < 3 ;
uint8_t width = _oamSizes [ _oamMode ] [ info . LargeSprite ] [ 0 ] < < 3 ;
2019-02-22 22:35:53 -05:00
uint8_t yOffset ;
int rowOffset ;
2019-03-02 18:00:27 -05:00
int yGap = ( screenY - info . Y ) ;
if ( _objInterlace ) {
yGap < < = 1 ;
2019-04-08 10:16:12 -04:00
yGap | = _oddFrame ;
2019-03-02 18:00:27 -05:00
}
2019-02-22 22:35:53 -05:00
if ( info . VerticalMirror ) {
2019-03-02 18:00:27 -05:00
yOffset = ( height - 1 - yGap ) & 0x07 ;
rowOffset = ( height - 1 - yGap ) > > 3 ;
2019-02-22 22:35:53 -05:00
} else {
2019-03-02 18:00:27 -05:00
yOffset = yGap & 0x07 ;
rowOffset = yGap > > 3 ;
2019-02-22 22:35:53 -05:00
}
uint8_t row = ( info . TileRow + rowOffset ) & 0x0F ;
2019-03-02 13:51:42 -05:00
int prevColumnOffset = - 1 ;
2019-02-22 22:35:53 -05:00
2019-04-04 20:46:29 -04:00
//Keep the last address the PPU used while rendering sprites (needed for Uniracers, which writes to OAM during rendering)
_oamRenderAddress = 0x200 + ( info . Index > > 2 ) ;
2019-03-02 13:51:42 -05:00
for ( int x = std : : max < int16_t > ( info . X , 0 ) ; x < info . X + width & & x < 256 ; x + + ) {
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 ;
}
2019-02-22 22:35:53 -05:00
2019-03-02 13:51:42 -05:00
if ( prevColumnOffset ! = columnOffset ) {
spriteTileCount + + ;
if ( spriteTileCount > 34 ) {
_timeOver = true ;
return ;
2019-02-22 22:35:53 -05:00
}
2019-03-02 13:51:42 -05:00
prevColumnOffset = columnOffset ;
2019-02-22 22:35:53 -05:00
}
2019-03-02 13:51:42 -05:00
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-22 22:35:53 -05:00
2019-03-02 13:51:42 -05:00
uint16_t color = GetTilePixelColor < 4 > ( tileStart + yOffset * 2 , 7 - xOffset ) ;
if ( color ! = 0 ) {
uint16_t paletteRamOffset = 256 + ( ( ( info . Palette < < 4 ) + color ) < < 1 ) ;
2019-03-16 14:04:21 -04:00
_spritePixels [ x ] = paletteRamOffset ;
2019-03-02 13:51:42 -05:00
_spritePriority [ x ] = info . Priority ;
_spritePalette [ x ] = info . Palette ;
}
2019-02-22 22:35:53 -05:00
}
}
}
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 > ( ) ;
}
2019-03-08 22:22:55 -05:00
RenderSprites < 0 , forMainScreen > ( ) ;
2019-02-22 18:41:43 -05:00
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 > ( ) ;
2019-03-03 01:01:30 -05:00
RenderTilemap < 1 , 2 , true , forMainScreen > ( ) ;
2019-02-22 18:41:43 -05:00
RenderSprites < 1 , forMainScreen > ( ) ;
RenderTilemap < 0 , 8 , false , forMainScreen > ( ) ;
RenderSprites < 0 , forMainScreen > ( ) ;
2019-03-03 01:01:30 -05:00
RenderTilemap < 1 , 2 , false , forMainScreen > ( ) ;
2019-02-22 18:41:43 -05:00
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-04-10 22:32:28 -04:00
if ( _drawStartX > 255 | | ( _allowFrameSkip & & ( _frameCount & 0x03 ) ! = 0 ) ) {
2019-03-07 20:13:44 -05:00
return ;
}
2019-07-07 18:18:20 -04:00
_fetchEndX = std : : min ( ( _memoryManager - > GetHClock ( ) - 6 * 4 ) > > 2 , 263 ) ;
if ( _fetchStartX > _fetchEndX ) {
return ;
}
FetchTileData ( ) ;
_fetchStartX = _fetchEndX + 1 ;
if ( _memoryManager - > GetHClock ( ) < 22 * 4 ) {
return ;
}
2019-04-20 11:46:51 -04:00
_drawEndX = std : : min ( ( _memoryManager - > GetHClock ( ) - 22 * 4 ) > > 2 , 255 ) ;
2019-02-19 01:26:48 -05:00
2019-03-06 22:25:14 -05:00
uint8_t bgMode = _bgMode ;
2019-02-19 18:01:27 -05:00
if ( _forcedVblank ) {
2019-03-06 22:25:14 -05:00
bgMode = 8 ;
2019-02-19 18:01:27 -05:00
}
2019-03-06 22:25:14 -05:00
switch ( bgMode ) {
2019-02-19 01:26:48 -05:00
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-03-06 22:25:14 -05:00
case 8 :
2019-03-25 23:46:02 -04:00
//Forced blank, output black
memset ( _mainScreenBuffer + _drawStartX , 0 , ( _drawEndX - _drawStartX + 1 ) * 2 ) ;
memset ( _subScreenBuffer + _drawStartX , 0 , ( _drawEndX - _drawStartX + 1 ) * 2 ) ;
2019-03-06 22:25:14 -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-03-04 17:49:14 -05:00
_drawStartX = _drawEndX + 1 ;
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 ) ;
2019-03-04 17:49:14 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2019-02-19 01:26:48 -05:00
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 ) {
2019-03-04 17:49:14 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2019-02-24 01:30:55 -05:00
if ( ! _rowPixelFlags [ x ] & & _spritePriority [ x ] = = priority ) {
if ( activeWindowCount & & ProcessMaskWindow < Ppu : : SpriteLayerIndex > ( activeWindowCount , x ) ) {
//This pixel was masked
continue ;
}
2019-03-16 14:04:21 -04:00
uint16_t paletteRamOffset = _spritePixels [ x ] ;
_mainScreenBuffer [ x ] = _cgram [ paletteRamOffset ] | ( _cgram [ paletteRamOffset + 1 ] < < 8 ) ;
2019-02-24 01:30:55 -05:00
_rowPixelFlags [ x ] | = PixelFlags : : Filled | ( ( ( _colorMathEnabled & 0x10 ) & & _spritePalette [ x ] > 3 ) ? PixelFlags : : AllowColorMath : 0 ) ;
}
}
} else {
2019-03-06 22:24:35 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2019-02-24 01:30:55 -05:00
if ( ! _subScreenFilled [ x ] & & _spritePriority [ x ] = = priority ) {
if ( activeWindowCount & & ProcessMaskWindow < Ppu : : SpriteLayerIndex > ( activeWindowCount , x ) ) {
//This pixel was masked
continue ;
}
2019-03-16 14:04:21 -04:00
uint16_t paletteRamOffset = _spritePixels [ x ] ;
_subScreenBuffer [ x ] = _cgram [ paletteRamOffset ] | ( _cgram [ paletteRamOffset + 1 ] < < 8 ) ;
2019-02-24 01:30:55 -05:00
_subScreenFilled [ x ] = true ;
}
}
}
}
2019-07-07 18:18:20 -04:00
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode , 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 */
2019-04-08 10:16:12 -04:00
uint16_t realY = IsDoubleHeight ( ) ? ( _oddFrame ? ( ( _scanline < < 1 ) + 1 ) : ( _scanline < < 1 ) ) : _scanline ;
2019-02-20 17:39:14 -05:00
2019-02-23 21:39:35 -05:00
/* 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-03-31 09:38:47 -04:00
/* True when the entire scanline has to be replaced by a mosaic pattern */
bool mosaicScanline = applyMosaic & & ( realY - _mosaicStartScanline ) % _mosaicSize ! = 0 ;
if ( applyMosaic & & ( ( realY - _mosaicStartScanline ) % _mosaicSize = = 0 ) ) {
//On each "first" line of the mosaic pattern, clear the entire buffer before processing the scanline
memset ( _mosaicColor [ layerIndex ] [ processHighPriority ] , 0xFF , 256 * 2 ) ;
}
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
2019-03-02 21:37:32 -05:00
uint16_t hScroll = hiResMode ? ( config . HScroll < < 1 ) : config . HScroll ;
2019-02-24 18:45:47 -05:00
/* 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 ;
2019-07-07 18:18:20 -04:00
2019-03-04 17:49:14 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; 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-07-07 18:18:20 -04:00
int lookupIndex ;
int chrDataOffset ;
if ( hiResMode ) {
lookupIndex = ( x + ( config . HScroll & 0x07 ) ) > > 2 ;
chrDataOffset = lookupIndex & 0x01 ;
lookupIndex > > = 1 ;
2019-02-24 18:45:47 -05:00
} else {
2019-07-07 18:18:20 -04:00
lookupIndex = ( x + ( config . HScroll & 0x07 ) ) > > 3 ;
chrDataOffset = 0 ;
2019-02-24 18:45:47 -05:00
}
2019-02-19 17:23:21 -05:00
2019-07-07 18:18:20 -04:00
TileData & tileData = _layerData [ layerIndex ] . Tiles [ lookupIndex ] ;
uint16_t tilemapData = tileData . TilemapData ;
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-07-07 18:18:20 -04:00
if ( ( ! applyMosaic & & _rowPixelFlags [ x ] ) | | ( ( uint8_t ) processHighPriority ! = ( ( tilemapData & 0x2000 ) > > 13 ) ) ) {
2019-02-19 01:26:48 -05:00
continue ;
}
} else {
2019-07-07 18:18:20 -04:00
if ( ( ! applyMosaic & & _subScreenFilled [ x ] ) | | ( ( uint8_t ) processHighPriority ! = ( ( tilemapData & 0x2000 ) > > 13 ) ) ) {
2019-02-19 01:26:48 -05:00
continue ;
2019-02-13 23:03:01 -05:00
}
}
2019-02-17 00:32:41 -05:00
2019-03-31 09:38:47 -04:00
if ( ! applyMosaic & & 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 15:40:32 -05:00
//The pixel is empty, not clipped and not part of a mosaic pattern, process it
2019-07-07 18:18:20 -04:00
bool hMirror = ( tilemapData & 0x4000 ) ! = 0 ;
2019-02-19 17:23:21 -05:00
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-07-07 18:18:20 -04:00
uint8_t color = GetTilePixelColor < bpp > ( tileData . ChrData + ( chrDataOffset ? bpp / 2 : 0 ) , 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
uint16_t paletteColor ;
if ( bpp = = 8 & & directColorMode ) {
2019-07-07 18:18:20 -04:00
uint8_t palette = ( tilemapData > > 10 ) & 0x07 ;
2019-02-24 01:11:26 -05:00
paletteColor = (
2019-06-27 20:57:00 -04:00
( ( ( ( color & 0x07 ) < < 1 ) | ( palette & 0x01 ) ) < < 1 ) |
( ( ( color & 0x38 ) | ( ( palette & 0x02 ) < < 1 ) ) < < 4 ) |
2019-02-24 01:11:26 -05:00
( ( ( color & 0xC0 ) | ( ( palette & 0x04 ) < < 3 ) ) < < 7 )
) ;
} else {
2019-03-03 16:34:23 -05:00
/* Ignore palette bits for 256-color layers */
2019-07-07 18:18:20 -04:00
uint8_t palette = bpp = = 8 ? 0 : ( tilemapData > > 10 ) & 0x07 ;
2019-02-24 01:11:26 -05:00
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-03-31 09:38:47 -04:00
if ( applyMosaic ) {
bool skipDraw = activeWindowCount & & ProcessMaskWindow < layerIndex > ( activeWindowCount , x ) ;
if ( ! mosaicScanline & & ( x % _mosaicSize ) = = 0 ) {
//If this is the top-left pixel, save its color and use it
for ( int i = 0 ; i < _mosaicSize & & x + i < 256 ; i + + ) {
_mosaicColor [ layerIndex ] [ processHighPriority ] [ x + i ] = paletteColor ;
}
} else {
//Otherwise, use the top-left pixel's color
paletteColor = _mosaicColor [ layerIndex ] [ processHighPriority ] [ x ] ;
if ( paletteColor = = 0xFFFF ) {
continue ;
}
}
if ( ! skipDraw & & ! _rowPixelFlags [ x ] ) {
//If this pixel isn't hidden by the window or already set, draw it
DrawMainPixel ( x , paletteColor , pixelFlags ) ;
}
} else {
DrawMainPixel ( 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 01:30:55 -05:00
template < bool forMainScreen >
bool Ppu : : IsRenderRequired ( uint8_t layerIndex )
{
if ( forMainScreen ) {
2019-05-20 15:35:09 -04:00
if ( _pixelsDrawn = = 256 | | ( ( ( _mainScreenLayers & _configVisibleLayers ) > > layerIndex ) & 0x01 ) = = 0 ) {
2019-02-24 01:30:55 -05:00
//This screen is disabled, or we've drawn all pixels already
return false ;
}
} else {
2019-05-20 15:35:09 -04:00
if ( _subPixelsDrawn = = 256 | | ( ( ( _subScreenLayers & _configVisibleLayers ) > > layerIndex ) & 0x01 ) = = 0 ) {
2019-02-24 01:30:55 -05:00
//This screen is disabled, or we've drawn all pixels already
return false ;
}
}
return true ;
}
2019-07-07 18:18:20 -04:00
template < uint8_t bpp >
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 ;
}
2019-02-24 01:30:55 -05:00
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
}
2019-05-07 20:55:44 -04:00
auto clip = [ ] ( int32_t val ) { return ( val & 0x2000 ) ? ( val | ~ 0x3ff ) : ( val & 0x3ff ) ; } ;
2019-02-23 15:40:32 -05:00
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 ] ;
}
2019-03-26 19:24:21 -04:00
uint8_t activeWindowCount = 0 ;
if ( ( forMainScreen & & _windowMaskMain [ layerIndex ] ) | | ( ! forMainScreen & & _windowMaskSub [ layerIndex ] ) ) {
activeWindowCount = ( uint8_t ) _window [ 0 ] . ActiveLayers [ layerIndex ] + ( uint8_t ) _window [ 1 ] . ActiveLayers [ layerIndex ] ;
}
2019-02-23 15:40:32 -05:00
uint8_t pixelFlags = PixelFlags : : Filled | ( ( ( _colorMathEnabled > > layerIndex ) & 0x01 ) ? PixelFlags : : AllowColorMath : 0 ) ;
2019-03-04 17:49:14 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2019-02-23 15:40:32 -05:00
uint16_t realX = _mode7 . HorizontalMirroring ? ( 255 - x ) : x ;
if ( forMainScreen ) {
if ( _rowPixelFlags [ x ] ) {
continue ;
}
} else {
if ( _subScreenFilled [ x ] ) {
continue ;
}
}
2019-03-25 19:10:39 -04:00
if ( activeWindowCount & & ProcessMaskWindow < layerIndex > ( activeWindowCount , x ) ) {
//This pixel was masked, skip it
continue ;
}
2019-02-23 15:40:32 -05:00
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-03-31 09:38:47 -04:00
DrawMainPixel ( 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-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 ;
_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-04-10 16:23:02 -04:00
uint8_t activeWindowCount = ( uint8_t ) _window [ 0 ] . ActiveLayers [ Ppu : : ColorWindowIndex ] + ( uint8_t ) _window [ 1 ] . ActiveLayers [ Ppu : : ColorWindowIndex ] ;
2019-04-02 23:44:00 -04:00
bool hiResMode = _hiResMode | | _bgMode = = 5 | | _bgMode = = 6 ;
uint16_t prevMainPixel = 0 ;
2019-04-10 16:23:02 -04:00
int prevX = _drawStartX > 0 ? _drawStartX - 1 : 0 ;
2019-02-22 22:19:20 -05:00
2019-03-06 22:24:35 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2019-04-07 23:19:33 -04:00
bool isInsideWindow = activeWindowCount & & ProcessMaskWindow < Ppu : : ColorWindowIndex > ( activeWindowCount , x ) ;
uint16_t subPixel = _subScreenBuffer [ x ] ;
if ( hiResMode ) {
//Apply the color math based on the previous main pixel
ApplyColorMathToPixel ( _subScreenBuffer [ x ] , prevMainPixel , prevX , isInsideWindow ) ;
prevMainPixel = _mainScreenBuffer [ x ] ;
prevX = x ;
2019-04-02 23:44:00 -04:00
}
2019-04-07 23:19:33 -04:00
ApplyColorMathToPixel ( _mainScreenBuffer [ x ] , subPixel , x , isInsideWindow ) ;
2019-04-02 23:44:00 -04:00
}
}
2019-02-22 22:19:20 -05:00
2019-04-02 23:44:00 -04:00
void Ppu : : ApplyColorMathToPixel ( uint16_t & pixelA , uint16_t pixelB , int x , bool isInsideWindow )
{
uint8_t halfShift = _colorMathHalveResult ? 1 : 0 ;
2019-02-22 22:19:20 -05:00
2019-04-02 23:44:00 -04:00
//Set color to black as needed based on clip mode
switch ( _colorMathClipMode ) {
default :
case ColorWindowMode : : Never : break ;
2019-02-22 22:19:20 -05:00
2019-04-02 23:44:00 -04:00
case ColorWindowMode : : OutsideWindow :
if ( ! isInsideWindow ) {
pixelA = 0 ;
halfShift = 0 ;
2019-02-22 22:19:20 -05:00
}
2019-04-02 23:44:00 -04:00
break ;
2019-02-22 22:19:20 -05:00
2019-04-02 23:44:00 -04:00
case ColorWindowMode : : InsideWindow :
if ( isInsideWindow ) {
pixelA = 0 ;
halfShift = 0 ;
2019-02-19 23:35:43 -05:00
}
2019-04-02 23:44:00 -04:00
break ;
2019-02-19 01:26:48 -05:00
2019-04-02 23:44:00 -04:00
case ColorWindowMode : : Always : pixelA = 0 ; break ;
}
2019-02-19 23:35:43 -05:00
2019-04-07 23:19:33 -04:00
if ( ! ( _rowPixelFlags [ x ] & PixelFlags : : AllowColorMath ) ) {
//Color math doesn't apply to this pixel
return ;
}
2019-04-02 23:44:00 -04:00
//Prevent color math as needed based on mode
switch ( _colorMathPreventMode ) {
default :
case ColorWindowMode : : Never : break ;
case ColorWindowMode : : OutsideWindow :
if ( ! isInsideWindow ) {
return ;
}
break ;
2019-02-19 23:35:43 -05:00
2019-04-02 23:44:00 -04:00
case ColorWindowMode : : InsideWindow :
if ( isInsideWindow ) {
return ;
2019-02-19 23:35:43 -05:00
}
2019-04-02 23:44:00 -04:00
break ;
case ColorWindowMode : : Always : return ;
}
uint16_t otherPixel ;
if ( _colorMathAddSubscreen ) {
if ( _subScreenFilled [ x ] ) {
otherPixel = pixelB ;
} else {
//there's nothing in the subscreen at this pixel, use the fixed color and disable halve operation
otherPixel = _fixedColor ;
halfShift = 0 ;
2019-02-19 23:35:43 -05:00
}
2019-04-02 23:44:00 -04:00
} else {
otherPixel = _fixedColor ;
}
if ( _colorMathSubstractMode ) {
uint16_t r = std : : max ( ( pixelA & 0x001F ) - ( otherPixel & 0x001F ) , 0 ) > > halfShift ;
uint16_t g = std : : max ( ( ( pixelA > > 5 ) & 0x001F ) - ( ( otherPixel > > 5 ) & 0x001F ) , 0 ) > > halfShift ;
uint16_t b = std : : max ( ( ( pixelA > > 10 ) & 0x001F ) - ( ( otherPixel > > 10 ) & 0x001F ) , 0 ) > > halfShift ;
pixelA = r | ( g < < 5 ) | ( b < < 10 ) ;
} else {
uint16_t r = std : : min ( ( ( pixelA & 0x001F ) + ( otherPixel & 0x001F ) ) > > halfShift , 0x1F ) ;
uint16_t g = std : : min ( ( ( ( pixelA > > 5 ) & 0x001F ) + ( ( otherPixel > > 5 ) & 0x001F ) ) > > halfShift , 0x1F ) ;
uint16_t b = std : : min ( ( ( ( pixelA > > 10 ) & 0x001F ) + ( ( otherPixel > > 10 ) & 0x001F ) ) > > halfShift , 0x1F ) ;
pixelA = 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 ) {
2019-03-06 22:24:35 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; 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-07-06 14:03:27 -04:00
void Ppu : : ConvertToHiRes ( )
{
uint16_t scanline = _overscanMode ? ( _scanline - 1 ) : ( _scanline + 6 ) ;
if ( _drawStartX > 0 ) {
for ( int x = 0 ; x < _drawStartX ; x + + ) {
_currentBuffer [ ( scanline < < 10 ) + ( x < < 1 ) ] = _currentBuffer [ ( scanline < < 8 ) + x ] ;
_currentBuffer [ ( scanline < < 10 ) + ( x < < 1 ) + 1 ] = _currentBuffer [ ( scanline < < 8 ) + x ] ;
}
memcpy ( _currentBuffer + ( scanline < < 10 ) + 512 , _currentBuffer + ( scanline < < 10 ) , 512 * sizeof ( uint16_t ) ) ;
}
for ( int i = scanline - 1 ; i > = 0 ; i - - ) {
for ( int x = 0 ; x < 256 ; x + + ) {
_currentBuffer [ ( i < < 10 ) + ( x < < 1 ) ] = _currentBuffer [ ( i < < 8 ) + x ] ;
_currentBuffer [ ( i < < 10 ) + ( x < < 1 ) + 1 ] = _currentBuffer [ ( i < < 8 ) + x ] ;
}
memcpy ( _currentBuffer + ( i < < 10 ) + 512 , _currentBuffer + ( i < < 10 ) , 512 * sizeof ( uint16_t ) ) ;
}
}
2019-02-23 21:39:35 -05:00
void Ppu : : ApplyHiResMode ( )
{
2019-03-14 23:30:47 -04:00
//When overscan mode is off, center the 224-line picture in the center of the 239-line output buffer
2019-04-02 21:56:48 -04:00
uint16_t scanline = _overscanMode ? ( _scanline - 1 ) : ( _scanline + 6 ) ;
2019-02-23 21:39:35 -05:00
2019-07-06 14:03:27 -04:00
bool useHighResOutput = _useHighResOutput | | IsDoubleWidth ( ) | | IsDoubleHeight ( ) ;
if ( _useHighResOutput ! = useHighResOutput ) {
//Convert standard res picture to high resolution when the PPU starts drawing in high res mid frame
ConvertToHiRes ( ) ;
_useHighResOutput = useHighResOutput ;
}
if ( ! _useHighResOutput ) {
memcpy ( _currentBuffer + ( scanline < < 8 ) + _drawStartX , _mainScreenBuffer + _drawStartX , ( _drawEndX - _drawStartX + 1 ) < < 2 ) ;
2019-02-23 21:39:35 -05:00
} else {
2019-07-06 14:03:27 -04:00
uint32_t screenY = IsDoubleHeight ( ) ? ( _oddFrame ? ( ( scanline < < 1 ) + 1 ) : ( scanline < < 1 ) ) : ( scanline < < 1 ) ;
uint32_t baseAddr = ( screenY < < 9 ) ;
if ( IsDoubleWidth ( ) ) {
ApplyBrightness < false > ( ) ;
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
_currentBuffer [ baseAddr + ( x < < 1 ) ] = _subScreenBuffer [ x ] ;
_currentBuffer [ baseAddr + ( x < < 1 ) + 1 ] = _mainScreenBuffer [ x ] ;
}
} else {
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
_currentBuffer [ baseAddr + ( x < < 1 ) ] = _mainScreenBuffer [ x ] ;
_currentBuffer [ baseAddr + ( x < < 1 ) + 1 ] = _mainScreenBuffer [ x ] ;
}
2019-03-02 18:00:27 -05:00
}
2019-07-06 14:03:27 -04:00
if ( ! IsDoubleHeight ( ) ) {
//Copy this line's content to the next line (between the current start & end bounds)
memcpy (
_currentBuffer + baseAddr + 512 + ( _drawStartX < < 1 ) ,
_currentBuffer + baseAddr + ( _drawStartX < < 1 ) ,
( _drawEndX - _drawStartX + 1 ) < < 2
) ;
}
2019-02-23 21:39:35 -05:00
}
}
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-07-06 14:03:27 -04:00
uint16_t width = _useHighResOutput ? 512 : 256 ;
uint16_t height = _useHighResOutput ? 478 : 239 ;
2019-03-14 23:30:47 -04:00
2019-02-19 01:26:48 -05:00
_console - > GetNotificationManager ( ) - > SendNotification ( ConsoleNotificationType : : PpuFrameDone ) ;
2019-02-23 21:39:35 -05:00
2019-03-14 23:30:47 -04:00
if ( ! _overscanMode ) {
//Clear the top 7 and bottom 8 rows
2019-07-06 14:03:27 -04:00
int top = ( _useHighResOutput ? 14 : 7 ) ;
int bottom = ( _useHighResOutput ? 16 : 8 ) ;
memset ( _currentBuffer , 0 , width * top * sizeof ( uint16_t ) ) ;
memset ( _currentBuffer + width * ( height - bottom ) , 0 , width * bottom * sizeof ( uint16_t ) ) ;
2019-03-14 23:30:47 -04:00
}
2019-02-23 21:39:35 -05:00
2019-03-12 12:06:42 -04:00
bool isRewinding = _console - > GetRewindManager ( ) - > IsRewinding ( ) ;
2019-07-02 19:56:00 -04:00
# ifdef LIBRETRO
_console - > GetVideoDecoder ( ) - > UpdateFrameSync ( _currentBuffer , width , height , _frameCount , isRewinding ) ;
# else
2019-03-12 12:06:42 -04:00
if ( isRewinding | | _screenInterlace ) {
_console - > GetVideoDecoder ( ) - > UpdateFrameSync ( _currentBuffer , width , height , _frameCount , isRewinding ) ;
2019-02-23 21:39:35 -05:00
} else {
_console - > GetVideoDecoder ( ) - > UpdateFrame ( _currentBuffer , width , height , _frameCount ) ;
2019-04-10 22:32:28 -04:00
if ( ! _allowFrameSkip | | ( _frameCount & 0x03 ) = = 0 ) {
_currentBuffer = _currentBuffer = = _outputBuffers [ 0 ] ? _outputBuffers [ 1 ] : _outputBuffers [ 0 ] ;
}
2019-02-23 21:39:35 -05:00
}
2019-07-02 19:56:00 -04:00
# endif
2019-02-13 18:44:39 -05:00
}
2019-07-06 14:03:27 -04:00
bool Ppu : : IsHighResOutput ( )
{
return _useHighResOutput ;
}
2019-03-07 20:12:32 -05:00
uint16_t * Ppu : : GetScreenBuffer ( )
{
return _currentBuffer ;
}
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-03-02 18:00:27 -05:00
bool Ppu : : IsDoubleHeight ( )
{
return _screenInterlace & & ( _bgMode = = 5 | | _bgMode = = 6 ) ;
}
bool Ppu : : IsDoubleWidth ( )
{
return _hiResMode | | _bgMode = = 5 | | _bgMode = = 6 ;
}
2019-02-21 07:27:47 -05:00
void Ppu : : LatchLocationValues ( )
{
2019-04-10 21:38:10 -04:00
_horizontalLocation = GetCycle ( ) ;
2019-02-21 07:27:47 -05:00
_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-03-09 10:29:19 -05:00
case 0x2134 :
_ppu1OpenBus = ( ( int16_t ) _mode7 . Matrix [ 0 ] * ( ( int16_t ) _mode7 . Matrix [ 1 ] > > 8 ) ) & 0xFF ;
return _ppu1OpenBus ;
case 0x2135 :
_ppu1OpenBus = ( ( ( int16_t ) _mode7 . Matrix [ 0 ] * ( ( int16_t ) _mode7 . Matrix [ 1 ] > > 8 ) ) > > 8 ) & 0xFF ;
return _ppu1OpenBus ;
case 0x2136 :
_ppu1OpenBus = ( ( ( int16_t ) _mode7 . Matrix [ 0 ] * ( ( int16_t ) _mode7 . Matrix [ 1 ] > > 8 ) ) > > 16 ) & 0xFF ;
return _ppu1OpenBus ;
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
2019-06-30 19:51:52 -04:00
if ( _regs - > GetIoPortOutput ( ) & 0x80 ) {
//Only latch H/V counters if bit 7 of $4201 is set.
LatchLocationValues ( ) ;
}
2019-02-21 07:27:47 -05:00
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-04-04 20:46:29 -04:00
//When trying to read/write during rendering, the internal address used by the PPU's sprite rendering is used
2019-04-10 16:04:55 -04:00
uint16_t addr = ( _forcedVblank | | ( _scanline > = _vblankStart ) ) ? _internalOamAddress : _oamRenderAddress ;
2019-02-19 18:41:59 -05:00
uint8_t value ;
2019-04-04 20:46:29 -04:00
if ( addr < 512 ) {
value = _oamRam [ addr ] ;
_console - > ProcessPpuRead ( addr , value , SnesMemoryType : : SpriteRam ) ;
2019-02-19 18:41:59 -05:00
} else {
2019-04-04 20:46:29 -04:00
value = _oamRam [ 0x200 | ( addr & 0x1F ) ] ;
_console - > ProcessPpuRead ( 0x200 | ( addr & 0x1F ) , value , SnesMemoryType : : SpriteRam ) ;
2019-02-19 18:41:59 -05:00
}
_internalOamAddress = ( _internalOamAddress + 1 ) & 0x3FF ;
2019-03-09 10:29:19 -05:00
_ppu1OpenBus = value ;
2019-02-19 18:41:59 -05:00
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 ;
}
2019-03-09 10:29:19 -05:00
_ppu1OpenBus = returnValue ;
2019-02-21 07:27:47 -05:00
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 ;
}
2019-03-09 10:29:19 -05:00
_ppu1OpenBus = returnValue ;
2019-02-21 07:27:47 -05:00
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-09 10:29:19 -05:00
if ( _cgramAddress & 0x01 ) {
value = ( value & 0x7F ) | ( _ppu2OpenBus & 0x80 ) ;
}
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 ) ;
2019-03-09 10:29:19 -05:00
_ppu2OpenBus = value ;
2019-02-21 07:27:47 -05:00
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-03-09 10:29:19 -05:00
value = ( ( _horizontalLocation & 0x100 ) > > 8 ) | ( _ppu2OpenBus & 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-03-09 10:29:19 -05:00
_ppu2OpenBus = value ;
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-03-09 10:29:19 -05:00
value = ( ( _verticalLocation & 0x100 ) > > 8 ) | ( _ppu2OpenBus & 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-03-09 10:29:19 -05:00
_ppu2OpenBus = value ;
2019-02-21 17:45:11 -05:00
_verticalLocationToggle = ! _verticalLocationToggle ;
return value ;
}
2019-02-21 07:27:47 -05:00
2019-03-09 10:29:19 -05:00
case 0x213E : {
2019-02-21 18:11:31 -05:00
//STAT77 - PPU Status Flag and Version
2019-03-09 10:29:19 -05:00
uint8_t value = (
2019-02-19 18:01:27 -05:00
( _timeOver ? 0x80 : 0 ) |
( _rangeOver ? 0x40 : 0 ) |
2019-03-09 10:29:19 -05:00
( _ppu1OpenBus & 0x10 ) |
2019-02-21 18:11:31 -05:00
0x01 //PPU (5c77) chip version
2019-02-19 18:01:27 -05:00
) ;
2019-03-09 10:29:19 -05:00
_ppu1OpenBus = value ;
return value ;
}
2019-02-19 18:01:27 -05:00
2019-02-21 18:11:31 -05:00
case 0x213F : {
//STAT78 - PPU Status Flag and Version
uint8_t value = (
2019-04-08 10:16:12 -04:00
( _oddFrame ? 0x80 : 0 ) |
2019-02-21 18:11:31 -05:00
( _locationLatched ? 0x40 : 0 ) |
2019-03-09 10:29:19 -05:00
( _ppu2OpenBus & 0x20 ) |
2019-03-14 15:25:35 -04:00
( _console - > GetRegion ( ) = = ConsoleRegion : : Pal ? 0x10 : 0 ) |
2019-02-21 18:11:31 -05:00
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
}
2019-03-09 10:29:19 -05:00
_ppu2OpenBus = value ;
2019-02-21 18:11:31 -05:00
return value ;
}
2019-02-17 14:42:35 -05:00
default :
2019-03-12 09:15:57 -04:00
MessageManager : : Log ( " [Debug] Unimplemented register read: " + HexUtilities : : ToHex ( addr ) ) ;
2019-02-17 14:42:35 -05:00
break ;
2019-02-13 18:44:39 -05:00
}
2019-03-09 10:29:19 -05:00
uint16_t reg = addr & 0x210F ;
if ( ( reg > = 0x2104 & & reg < = 0x2106 ) | | ( reg > = 0x2108 & & reg < = 0x210A ) ) {
//Registers matching $21x4-6 or $21x8-A (where x is 0-2) return the last value read from any of the PPU1 registers $2134-6, $2138-A, or $213E.
return _ppu1OpenBus ;
}
2019-03-09 00:31:54 -05:00
return _console - > GetMemoryManager ( ) - > GetOpenBus ( ) ;
2019-02-13 18:44:39 -05:00
}
void Ppu : : Write ( uint32_t addr , uint8_t value )
{
2019-04-20 11:46:51 -04:00
if ( _scanline < _vblankStart & & _scanline > 0 & & _memoryManager - > GetHClock ( ) > = 22 * 4 & & _memoryManager - > GetHClock ( ) < = 278 * 4 ) {
2019-03-04 17:49:14 -05:00
RenderScanline ( ) ;
}
2019-02-13 18:44:39 -05:00
switch ( addr ) {
2019-02-19 18:01:27 -05:00
case 0x2100 :
2019-04-10 16:04:55 -04:00
if ( _forcedVblank & & _scanline = = _vblankStart ) {
2019-04-04 21:04:03 -04:00
//"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."
_internalOamAddress = ( _oamRamAddress < < 1 ) ;
}
2019-02-19 18:01:27 -05:00
_forcedVblank = ( value & 0x80 ) ! = 0 ;
_screenBrightness = value & 0x0F ;
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 ;
2019-04-04 20:46:29 -04:00
case 0x2104 : {
//When trying to read/write during rendering, the internal address used by the PPU's sprite rendering is used
//This is approximated by _oamRenderAddress (but is not cycle accurate) - needed for Uniracers
2019-04-10 16:04:55 -04:00
uint16_t addr = ( _forcedVblank | | ( _scanline > = _vblankStart ) ) ? _internalOamAddress : _oamRenderAddress ;
2019-04-04 20:46:29 -04:00
if ( addr < 512 ) {
if ( addr & 0x01 ) {
_console - > ProcessPpuWrite ( addr - 1 , _oamWriteBuffer , SnesMemoryType : : SpriteRam ) ;
_oamRam [ addr - 1 ] = _oamWriteBuffer ;
2019-03-01 20:27:49 -05:00
2019-04-04 20:46:29 -04:00
_console - > ProcessPpuWrite ( addr , value , SnesMemoryType : : SpriteRam ) ;
_oamRam [ addr ] = value ;
2019-02-17 21:09:33 -05:00
} else {
_oamWriteBuffer = value ;
}
2019-02-19 18:41:59 -05:00
} else {
2019-04-04 20:46:29 -04:00
uint16_t address = 0x200 | ( addr & 0x1F ) ;
if ( ( addr & 0x01 ) = = 0 ) {
2019-02-19 18:41:59 -05:00
_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-04-04 20:46:29 -04:00
}
2019-02-17 00:32:41 -05:00
case 0x2105 :
2019-02-23 21:39:35 -05:00
if ( _bgMode ! = ( value & 0x07 ) ) {
2019-03-13 22:56:33 -04:00
MessageManager : : Log ( " [Debug] Entering mode: " + std : : to_string ( value & 0x07 ) + " (SL: " + std : : to_string ( _scanline ) + " ) " ) ;
2019-02-23 21:39:35 -05:00
}
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 ) {
2019-03-31 09:38:47 -04:00
//"If this register is set during the frame, the <20> starting scanline is the current scanline, otherwise it is the first visible scanline of the frame."
2019-02-20 17:39:14 -05:00
_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-03-03 16:34:23 -05:00
_layerConfig [ addr - 0x2107 ] . DoubleWidth = ( value & 0x01 ) ! = 0 ;
_layerConfig [ addr - 0x2107 ] . DoubleHeight = ( 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
2019-02-13 18:44:39 -05:00
_vramAddressRemapping = ( value & 0x0C ) > > 2 ;
_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-06-26 16:39:57 -04:00
if ( _scanline > = _vblankStart | | _forcedVblank ) {
//Only write the value if in vblank or forced blank (writes to VRAM outside vblank/forced blank are not allowed)
_console - > ProcessPpuWrite ( GetVramAddress ( ) < < 1 , value , SnesMemoryType : : VideoRam ) ;
_vram [ GetVramAddress ( ) < < 1 ] = value ;
2019-03-15 21:09:39 -04:00
}
2019-06-26 16:39:57 -04:00
//The VRAM address is incremented even outside of vblank/forced blank
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-06-26 16:39:57 -04:00
if ( _scanline > = _vblankStart | | _forcedVblank ) {
//Only write the value if in vblank or forced blank (writes to VRAM outside vblank/forced blank are not allowed)
_console - > ProcessPpuWrite ( ( GetVramAddress ( ) < < 1 ) + 1 , value , SnesMemoryType : : VideoRam ) ;
_vram [ ( GetVramAddress ( ) < < 1 ) + 1 ] = value ;
2019-03-15 21:09:39 -04:00
}
2019-06-26 16:39:57 -04:00
//The VRAM address is incremented even outside of vblank/forced blank
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-10 23:18:47 -04:00
if ( _cgramAddress & 0x01 ) {
//MSB ignores the 7th bit (colors are 15-bit only)
2019-06-29 09:16:53 -04:00
_console - > ProcessPpuWrite ( _cgramAddress - 1 , _cgramWriteBuffer , SnesMemoryType : : CGRam ) ;
_cgram [ _cgramAddress - 1 ] = _cgramWriteBuffer ;
_console - > ProcessPpuWrite ( _cgramAddress , value & 0x7F , SnesMemoryType : : CGRam ) ;
2019-03-10 23:18:47 -04:00
_cgram [ _cgramAddress ] = value & 0x7F ;
} else {
2019-06-29 09:16:53 -04:00
_cgramWriteBuffer = value ;
2019-03-10 23:18:47 -04:00
}
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-04-10 16:04:55 -04:00
_vblankStart = _overscanMode ? 240 : 225 ;
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-03-12 09:15:57 -04:00
MessageManager : : Log ( " [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
2019-03-12 09:15:57 -04:00
void Ppu : : Serialize ( Serializer & s )
{
s . Stream (
2019-04-10 21:38:10 -04:00
_forcedVblank , _screenBrightness , _scanline , _frameCount , _drawStartX , _drawEndX , _bgMode ,
2019-03-12 09:15:57 -04:00
_mode1Bg3Priority , _mainScreenLayers , _subScreenLayers , _vramAddress , _vramIncrementValue , _vramAddressRemapping ,
_vramAddrIncrementOnSecondReg , _vramReadBuffer , _ppu1OpenBus , _ppu2OpenBus , _cgramAddress , _mosaicSize , _mosaicEnabled ,
_mosaicStartScanline , _oamMode , _oamBaseAddress , _oamAddressOffset , _oamRamAddress , _enableOamPriority ,
_internalOamAddress , _oamWriteBuffer , _timeOver , _rangeOver , _hiResMode , _screenInterlace , _objInterlace ,
_overscanMode , _directColorMode , _colorMathClipMode , _colorMathPreventMode , _colorMathAddSubscreen , _colorMathEnabled ,
_colorMathSubstractMode , _colorMathHalveResult , _fixedColor , _hvScrollLatchValue , _hScrollLatchValue ,
_horizontalLocation , _horizontalLocToggle , _verticalLocation , _verticalLocationToggle , _locationLatched ,
_maskLogic [ 0 ] , _maskLogic [ 1 ] , _maskLogic [ 2 ] , _maskLogic [ 3 ] , _maskLogic [ 4 ] , _maskLogic [ 5 ] ,
_windowMaskMain [ 0 ] , _windowMaskMain [ 1 ] , _windowMaskMain [ 2 ] , _windowMaskMain [ 3 ] , _windowMaskMain [ 4 ] ,
_windowMaskSub [ 0 ] , _windowMaskSub [ 1 ] , _windowMaskSub [ 2 ] , _windowMaskSub [ 3 ] , _windowMaskSub [ 4 ] ,
_mode7 . CenterX , _mode7 . CenterY , _mode7 . ExtBgEnabled , _mode7 . FillWithTile0 , _mode7 . HorizontalMirroring ,
_mode7 . HScroll , _mode7 . LargeMap , _mode7 . Matrix [ 0 ] , _mode7 . Matrix [ 1 ] , _mode7 . Matrix [ 2 ] , _mode7 . Matrix [ 3 ] ,
2019-04-20 11:46:51 -04:00
_mode7 . ValueLatch , _mode7 . VerticalMirroring , _mode7 . VScroll , _oamRenderAddress , _oddFrame , _vblankStart
2019-03-12 09:15:57 -04:00
) ;
for ( int i = 0 ; i < 4 ; i + + ) {
s . Stream (
_layerConfig [ i ] . ChrAddress , _layerConfig [ i ] . DoubleHeight , _layerConfig [ i ] . DoubleWidth , _layerConfig [ i ] . HScroll ,
_layerConfig [ i ] . LargeTiles , _layerConfig [ i ] . TilemapAddress , _layerConfig [ i ] . VScroll
) ;
}
for ( int i = 0 ; i < 2 ; i + + ) {
s . Stream (
_window [ i ] . ActiveLayers [ 0 ] , _window [ i ] . ActiveLayers [ 1 ] , _window [ i ] . ActiveLayers [ 2 ] , _window [ i ] . ActiveLayers [ 3 ] , _window [ i ] . ActiveLayers [ 4 ] , _window [ i ] . ActiveLayers [ 5 ] ,
_window [ i ] . InvertedLayers [ 0 ] , _window [ i ] . InvertedLayers [ 1 ] , _window [ i ] . InvertedLayers [ 2 ] , _window [ i ] . InvertedLayers [ 3 ] , _window [ i ] . InvertedLayers [ 4 ] , _window [ i ] . InvertedLayers [ 5 ] ,
_window [ i ] . Left , _window [ i ] . Right
) ;
}
2019-07-07 19:27:39 -04:00
s . StreamArray ( _vram , Ppu : : VideoRamSize ) ;
s . StreamArray ( _oamRam , Ppu : : SpriteRamSize ) ;
s . StreamArray ( _cgram , Ppu : : CgRamSize ) ;
2019-07-07 18:18:20 -04:00
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 ) ;
2019-03-12 09:15:57 -04:00
}
2019-02-24 11:14:24 -05:00
/* Everything below this point is used to select the proper arguments for templates */
2019-07-07 18:18:20 -04:00
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode , uint8_t activeWindowCount , bool applyMosaic >
2019-02-24 11:14:24 -05:00
void Ppu : : RenderTilemap ( )
{
if ( _directColorMode ) {
2019-07-07 18:18:20 -04:00
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , activeWindowCount , applyMosaic , true > ( ) ;
2019-02-24 11:14:24 -05:00
} else {
2019-07-07 18:18:20 -04:00
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , activeWindowCount , applyMosaic , false > ( ) ;
2019-02-24 11:14:24 -05:00
}
}
2019-07-07 18:18:20 -04:00
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode , uint8_t activeWindowCount >
2019-02-24 11:14:24 -05:00
void Ppu : : RenderTilemap ( )
{
2019-03-31 09:38:47 -04:00
bool applyMosaic = forMainScreen & & ( ( _mosaicEnabled > > layerIndex ) & 0x01 ) ! = 0 & & ( _mosaicSize > 1 | | _bgMode = = 5 | | _bgMode = = 6 ) ;
2019-02-24 11:14:24 -05:00
if ( applyMosaic ) {
2019-07-07 18:18:20 -04:00
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , activeWindowCount , true > ( ) ;
2019-02-24 11:14:24 -05:00
} else {
2019-07-07 18:18:20 -04:00
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , activeWindowCount , false > ( ) ;
2019-02-24 11:14:24 -05:00
}
}
2019-07-07 18:18:20 -04:00
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset , bool hiResMode >
2019-02-24 11:14:24 -05:00
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 ) {
2019-07-07 18:18:20 -04:00
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , 0 > ( ) ;
2019-02-24 11:14:24 -05:00
} else if ( activeWindowCount = = 1 ) {
2019-07-07 18:18:20 -04:00
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , 1 > ( ) ;
2019-02-24 11:14:24 -05:00
} else {
2019-07-07 18:18:20 -04:00
RenderTilemap < layerIndex , bpp , processHighPriority , forMainScreen , basePaletteOffset , hiResMode , 2 > ( ) ;
2019-02-24 11:14:24 -05:00
}
}
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen , uint16_t basePaletteOffset >
void Ppu : : RenderTilemap ( )
{
2019-03-02 21:37:32 -05:00
if ( _bgMode = = 5 | | _bgMode = = 6 ) {
2019-02-24 11:14:24 -05:00
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 > ( ) ;
}
}