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 ] = {
2021-03-10 11:13:28 -05:00
{ { 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-03-31 14:50:12 -04:00
} ;
2019-07-12 23:34:19 -04:00
Ppu : : Ppu ( Console * console )
2019-02-13 18:44:39 -05:00
{
_console = console ;
2019-07-11 23:03:02 -04:00
_vram = new uint16_t [ Ppu : : VideoRamSize > > 1 ] ;
2019-07-06 14:25:51 -04:00
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-07-11 23:30:40 -04:00
_skipRender = false ;
2019-07-12 23:34:19 -04:00
_regs = _console - > GetInternalRegisters ( ) . get ( ) ;
2019-07-19 19:39:38 -04:00
_settings = _console - > GetSettings ( ) . get ( ) ;
_spc = _console - > GetSpc ( ) . get ( ) ;
2019-07-12 23:34:19 -04:00
_memoryManager = _console - > GetMemoryManager ( ) . get ( ) ;
2019-02-16 01:16:57 -05:00
_currentBuffer = _outputBuffers [ 0 ] ;
2021-03-10 11:13:28 -05:00
2019-12-11 21:44:42 -05:00
_state = { } ;
_state . ForcedVblank = true ;
_state . VramIncrementValue = 1 ;
2021-03-10 11:13:28 -05:00
if ( _settings - > GetEmulationConfig ( ) . EnableRandomPowerOnState ) {
2019-12-11 21:44:42 -05:00
RandomizeState ( ) ;
}
2019-02-13 18:44:39 -05:00
2019-07-19 19:39:38 -04:00
_settings - > InitializeRam ( _vram , Ppu : : VideoRamSize ) ;
_settings - > InitializeRam ( _cgram , Ppu : : CgRamSize ) ;
_settings - > InitializeRam ( _oamRam , Ppu : : SpriteRamSize ) ;
2019-02-16 01:16:57 -05:00
2019-07-08 15:24:43 -04:00
memset ( _spriteIndexes , 0xFF , sizeof ( _spriteIndexes ) ) ;
2021-03-10 11:13:28 -05:00
2019-07-19 19:39:38 -04:00
UpdateNmiScanline ( ) ;
2019-02-13 18:44:39 -05:00
}
2019-03-16 12:20:18 -04:00
void Ppu : : Reset ( )
{
_scanline = 0 ;
2019-10-10 23:54:38 -04:00
_state . 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 ( ) ;
2021-03-10 11:13:28 -05:00
if ( hClock < = 1292 ) {
2019-10-11 16:07:30 -04:00
return hClock > > 2 ;
2021-03-10 11:13:28 -05:00
} else if ( hClock < = 1310 ) {
2019-10-19 21:26:52 -04:00
return ( hClock - 2 ) > > 2 ;
2021-03-10 11:13:28 -05:00
} else {
2019-10-19 21:26:52 -04:00
return ( hClock - 4 ) > > 2 ;
2019-10-11 16:07:30 -04:00
}
2019-03-07 20:12:32 -05:00
}
2019-07-19 19:39:38 -04:00
uint16_t Ppu : : GetNmiScanline ( )
{
return _nmiScanline ;
}
2019-04-10 16:04:55 -04:00
uint16_t Ppu : : GetVblankStart ( )
{
2019-07-19 19:39:38 -04:00
return _vblankStartScanline ;
2019-04-10 16:04:55 -04:00
}
2019-02-13 18:44:39 -05:00
PpuState Ppu : : GetState ( )
{
2019-03-03 16:34:23 -05:00
PpuState state ;
2019-07-14 21:45:12 -04:00
GetState ( state , false ) ;
return state ;
}
2021-03-10 11:13:28 -05:00
void Ppu : : GetState ( PpuState & state , bool returnPartialState )
2019-07-14 21:45:12 -04:00
{
2021-03-10 11:13:28 -05:00
if ( ! returnPartialState ) {
2019-10-10 23:54:38 -04:00
state = _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 ;
2019-02-13 18:44:39 -05:00
}
2021-03-10 11:13:28 -05:00
template < bool hiResMode >
2019-07-07 18:18:20 -04:00
void Ppu : : GetTilemapData ( uint8_t layerIndex , uint8_t columnIndex )
{
/* The current layer's options */
2021-03-10 11:13:28 -05:00
LayerConfig & config = _state . Layers [ layerIndex ] ;
2019-07-07 18:18:20 -04:00
uint16_t vScroll = config . VScroll ;
2021-03-10 11:13:28 -05:00
uint16_t hScroll = hiResMode ? ( config . HScroll < < 1 ) : config . HScroll ;
if ( _hOffset | | _vOffset ) {
2019-07-07 18:18:20 -04:00
uint16_t enableBit = layerIndex = = 0 ? 0x2000 : 0x4000 ;
2021-03-10 11:13:28 -05:00
if ( _state . BgMode = = 4 ) {
if ( ( _hOffset & 0x8000 ) = = 0 & & ( _hOffset & enableBit ) ) {
2019-07-07 18:18:20 -04:00
hScroll = ( hScroll & 0x07 ) | ( _hOffset & 0x3F8 ) ;
}
2021-03-10 11:13:28 -05:00
if ( ( _hOffset & 0x8000 ) ! = 0 & & ( _hOffset & enableBit ) ) {
2019-07-07 18:18:20 -04:00
vScroll = ( _hOffset & 0x3FF ) ;
}
2021-03-10 11:13:28 -05:00
} else {
if ( _hOffset & enableBit ) {
2019-07-07 18:18:20 -04:00
hScroll = ( hScroll & 0x07 ) | ( _hOffset & 0x3F8 ) ;
}
2021-03-10 11:13:28 -05:00
if ( _vOffset & enableBit ) {
2019-07-07 18:18:20 -04:00
vScroll = ( _vOffset & 0x3FF ) ;
}
}
}
2021-03-10 11:13:28 -05:00
if ( hiResMode ) {
2019-07-07 18:18:20 -04:00
hScroll > > = 1 ;
}
2019-07-13 08:25:28 -04:00
uint16_t realY = IsDoubleHeight ( ) ? ( _oddFrame ? ( ( _scanline < < 1 ) + 1 ) : ( _scanline < < 1 ) ) : _scanline ;
2021-03-10 11:13:28 -05:00
if ( _state . MosaicEnabled & & ( _state . MosaicEnabled & ( 1 < < layerIndex ) ) ) {
2019-07-11 21:45:56 -04:00
//Keep the "scanline" to what it was at the start of this mosaic block
2019-10-10 23:54:38 -04:00
realY - = _state . MosaicSize - _mosaicScanlineCounter ;
2021-03-10 11:13:28 -05:00
if ( IsDoubleHeight ( ) ) {
2019-10-10 23:54:38 -04:00
realY - = _state . MosaicSize - _mosaicScanlineCounter ;
2019-07-13 08:25:28 -04:00
}
2019-07-08 09:13:08 -04:00
}
2019-07-07 18:18:20 -04:00
/* 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 */
2019-07-11 23:03:02 -04:00
uint16_t baseOffset = config . TilemapAddress + addrVerticalScrollingOffset + ( ( row & 0x1F ) < < 5 ) ;
2019-07-07 18:18:20 -04:00
/* The current column index (in terms of 8x8 or 16x16 tiles) */
uint16_t column = columnIndex + ( hScroll > > 3 ) ;
2021-03-10 11:13:28 -05:00
if ( ! hiResMode & & config . LargeTiles ) {
2019-07-07 18:18:20 -04:00
//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 */
2019-07-11 23:30:40 -04:00
uint16_t addr = baseOffset + ( column & 0x1F ) + ( config . DoubleWidth ? ( column & 0x20 ) < < 5 : 0 ) ;
2020-05-26 18:10:19 -04:00
_layerData [ layerIndex ] . Tiles [ columnIndex ] . TilemapData = _vram [ addr & 0x7FFF ] ;
2019-07-07 18:18:20 -04:00
_layerData [ layerIndex ] . Tiles [ columnIndex ] . VScroll = vScroll ;
}
2021-03-10 11:13:28 -05:00
template < bool hiResMode , uint8_t bpp , bool secondTile >
2019-07-07 18:18:20 -04:00
void Ppu : : GetChrData ( uint8_t layerIndex , uint8_t column , uint8_t plane )
{
2021-03-10 11:13:28 -05:00
LayerConfig & config = _state . Layers [ layerIndex ] ;
TileData & tileData = _layerData [ layerIndex ] . Tiles [ column ] ;
2019-07-07 18:18:20 -04:00
uint16_t tilemapData = tileData . TilemapData ;
bool largeTileWidth = hiResMode | | config . LargeTiles ;
bool vMirror = ( tilemapData & 0x8000 ) ! = 0 ;
bool hMirror = ( tilemapData & 0x4000 ) ! = 0 ;
2019-07-13 08:25:28 -04:00
uint16_t realY = IsDoubleHeight ( ) ? ( _oddFrame ? ( ( _scanline < < 1 ) + 1 ) : ( _scanline < < 1 ) ) : _scanline ;
2021-03-10 11:13:28 -05:00
if ( _state . MosaicEnabled & & ( _state . MosaicEnabled & ( 1 < < layerIndex ) ) ) {
2019-07-11 21:45:56 -04:00
//Keep the "scanline" to what it was at the start of this mosaic block
2019-10-10 23:54:38 -04:00
realY - = _state . MosaicSize - _mosaicScanlineCounter ;
2021-03-10 11:13:28 -05:00
if ( IsDoubleHeight ( ) ) {
2019-10-10 23:54:38 -04:00
realY - = _state . MosaicSize - _mosaicScanlineCounter + ( _oddFrame ? 1 : 0 ) ;
2019-07-13 08:25:28 -04:00
}
2019-07-08 09:13:08 -04:00
}
2019-07-07 18:18:20 -04:00
bool useSecondTile = secondTile ;
2021-03-10 11:13:28 -05:00
if ( ! hiResMode & & config . LargeTiles ) {
2019-07-07 18:18:20 -04:00
//For 16x16 tiles, need to return the 2nd part of the tile every other column
useSecondTile = ( ( ( column < < 3 ) + config . HScroll ) & 0x08 ) = = 0x08 ;
}
2019-07-12 23:53:47 -04:00
uint16_t tileIndex = tilemapData & 0x3FF ;
2021-03-10 11:13:28 -05:00
if ( largeTileWidth ) {
2019-07-07 18:18:20 -04:00
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 ;
}
2019-07-11 23:03:02 -04:00
uint16_t tileStart = config . ChrAddress + tileIndex * 4 * bpp ;
2021-03-10 11:13:28 -05:00
2019-07-07 18:18:20 -04:00
uint8_t baseYOffset = ( realY + tileData . VScroll ) & 0x07 ;
uint8_t yOffset = vMirror ? ( 7 - baseYOffset ) : baseYOffset ;
2019-07-11 23:03:02 -04:00
uint16_t pixelStart = tileStart + yOffset + ( plane < < 3 ) ;
2019-07-12 23:53:47 -04:00
tileData . ChrData [ plane + ( secondTile ? bpp / 2 : 0 ) ] = _vram [ pixelStart & 0x7FFF ] ;
2019-07-07 18:18:20 -04:00
}
void Ppu : : GetHorizontalOffsetByte ( uint8_t columnIndex )
{
2021-03-10 11:13:28 -05:00
uint16_t columnOffset = ( ( ( columnIndex < < 3 ) + ( _state . Layers [ 2 ] . HScroll & ~ 0x07 ) ) > > 3 ) & ( _state . Layers [ 2 ] . DoubleWidth ? 0x3F : 0x1F ) ;
2019-10-10 23:54:38 -04:00
uint16_t rowOffset = ( _state . Layers [ 2 ] . VScroll > > 3 ) & ( _state . Layers [ 2 ] . DoubleHeight ? 0x3F : 0x1F ) ;
2019-07-07 18:18:20 -04:00
2020-05-26 18:10:19 -04:00
_hOffset = _vram [ ( _state . Layers [ 2 ] . TilemapAddress + columnOffset + ( rowOffset < < 5 ) ) & 0x7FFF ] ;
2019-07-07 18:18:20 -04:00
}
void Ppu : : GetVerticalOffsetByte ( uint8_t columnIndex )
{
2021-03-10 11:13:28 -05:00
uint16_t columnOffset = ( ( ( columnIndex < < 3 ) + ( _state . Layers [ 2 ] . HScroll & ~ 0x07 ) ) > > 3 ) & ( _state . Layers [ 2 ] . DoubleWidth ? 0x3F : 0x1F ) ;
2019-10-10 23:54:38 -04:00
uint16_t rowOffset = ( _state . Layers [ 2 ] . VScroll > > 3 ) & ( _state . Layers [ 2 ] . DoubleHeight ? 0x3F : 0x1F ) ;
2019-07-07 18:18:20 -04:00
2019-07-11 23:03:02 -04:00
uint16_t tileOffset = columnOffset + ( rowOffset < < 5 ) ;
2019-07-07 18:18:20 -04:00
//The vertical offset is 0x40 bytes later - but wraps around within the tilemap based on the tilemap size (0x800 or 0x1000 bytes)
2021-03-10 11:13:28 -05:00
uint16_t vOffsetAddr = _state . Layers [ 2 ] . TilemapAddress + ( ( tileOffset + 0x20 ) & ( _state . Layers [ 2 ] . DoubleHeight ? 0x7FF : 0x3FF ) ) ;
2019-07-07 18:18:20 -04:00
2020-05-26 18:10:19 -04:00
_vOffset = _vram [ vOffsetAddr & 0x7FFF ] ;
2019-07-07 18:18:20 -04:00
}
void Ppu : : FetchTileData ( )
{
2021-03-10 11:13:28 -05:00
if ( _state . ForcedVblank ) {
2019-07-08 15:24:43 -04:00
return ;
}
2021-03-10 11:13:28 -05:00
if ( _fetchBgStart = = 0 ) {
2019-07-07 18:18:20 -04:00
_hOffset = 0 ;
_vOffset = 0 ;
}
2021-03-10 11:13:28 -05:00
if ( _state . BgMode = = 0 ) {
for ( int x = _fetchBgStart ; x < = _fetchBgEnd ; 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 ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
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 ;
2019-07-07 18:18:20 -04:00
}
}
2021-03-10 11:13:28 -05:00
} else if ( _state . BgMode = = 1 ) {
for ( int x = _fetchBgStart ; x < = _fetchBgEnd ; 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 ;
2019-07-07 18:18:20 -04:00
}
}
2021-03-10 11:13:28 -05:00
} else if ( _state . BgMode = = 2 ) {
for ( int x = _fetchBgStart ; x < = _fetchBgEnd ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < false > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < false > ( 0 , x > > 3 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
case 2 : GetHorizontalOffsetByte ( x > > 3 ) ; break ;
case 3 : GetVerticalOffsetByte ( x > > 3 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
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 ;
2019-07-07 18:18:20 -04:00
}
}
2021-03-10 11:13:28 -05:00
} else if ( _state . BgMode = = 3 ) {
for ( int x = _fetchBgStart ; x < = _fetchBgEnd ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < false > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < false > ( 0 , x > > 3 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
case 2 : GetChrData < false , 4 > ( 1 , x > > 3 , 0 ) ; break ;
case 3 : GetChrData < false , 4 > ( 1 , x > > 3 , 1 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
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 ;
2019-07-07 18:18:20 -04:00
}
}
2021-03-10 11:13:28 -05:00
} else if ( _state . BgMode = = 4 ) {
for ( int x = _fetchBgStart ; x < = _fetchBgEnd ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < false > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < false > ( 0 , x > > 3 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
case 2 : GetHorizontalOffsetByte ( x > > 3 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
case 3 : GetChrData < false , 2 > ( 1 , x > > 3 , 0 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
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 ;
2019-07-07 18:18:20 -04:00
}
}
2021-03-10 11:13:28 -05:00
} else if ( _state . BgMode = = 5 ) {
for ( int x = _fetchBgStart ; x < = _fetchBgEnd ; x + + ) {
switch ( x & 0x07 ) {
case 0 : GetTilemapData < true > ( 1 , x > > 3 ) ; break ;
case 1 : GetTilemapData < true > ( 0 , x > > 3 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
case 2 : GetChrData < true , 2 > ( 1 , x > > 3 , 0 ) ; break ;
case 3 : GetChrData < true , 2 , true > ( 1 , x > > 3 , 0 ) ; break ;
2019-07-07 18:18:20 -04:00
2021-03-10 11:13:28 -05:00
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-07-07 18:18:20 -04:00
}
}
2021-03-10 11:13:28 -05:00
} else if ( _state . BgMode = = 6 ) {
for ( int x = _fetchBgStart ; x < = _fetchBgEnd ; 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-07-07 18:18:20 -04:00
}
}
}
}
2019-04-20 11:46:51 -04:00
bool Ppu : : ProcessEndOfScanline ( uint16_t hClock )
2019-02-13 18:44:39 -05:00
{
2021-03-10 11:13:28 -05:00
if ( hClock > = 1364 | | ( hClock = = 1360 & & _scanline = = 240 & & _oddFrame & & ! _state . 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."
2021-03-10 11:13:28 -05:00
if ( _scanline < _vblankStartScanline ) {
2019-07-08 15:24:43 -04:00
RenderScanline ( ) ;
2021-03-10 11:13:28 -05:00
if ( _scanline = = 0 ) {
2020-02-29 19:36:17 -05:00
_overscanFrame = _state . OverscanMode ;
2019-10-10 23:54:38 -04:00
_mosaicScanlineCounter = _state . MosaicEnabled ? _state . MosaicSize + 1 : 0 ;
2020-03-01 18:34:48 -05:00
//Update overclock timings once per frame
UpdateNmiScanline ( ) ;
2021-03-10 11:13:28 -05:00
if ( ! _skipRender ) {
if ( ! _interlacedFrame ) {
2019-10-19 21:27:29 -04:00
_currentBuffer = _currentBuffer = = _outputBuffers [ 0 ] ? _outputBuffers [ 1 ] : _outputBuffers [ 0 ] ;
}
2021-03-10 11:13:28 -05:00
2020-02-29 19:36:17 -05:00
//If we're not skipping this frame, reset the high resolution/interlace flags
_useHighResOutput = IsDoubleWidth ( ) | | _state . ScreenInterlace ;
_interlacedFrame = _state . ScreenInterlace ;
2019-10-08 21:04:32 -04:00
}
2019-10-07 22:37:37 -04:00
}
2021-03-10 11:13:28 -05:00
if ( _mosaicScanlineCounter ) {
2019-07-19 19:39:38 -04:00
_mosaicScanlineCounter - - ;
2021-03-10 11:13:28 -05:00
if ( _state . MosaicEnabled & & ! _mosaicScanlineCounter ) {
2019-10-10 23:54:38 -04:00
_mosaicScanlineCounter = _state . MosaicSize ;
2019-07-19 19:39:38 -04:00
}
2019-07-11 21:45:56 -04:00
}
2019-07-10 22:50:12 -04:00
2019-07-19 19:39:38 -04:00
_drawStartX = 0 ;
_drawEndX = 0 ;
_fetchBgStart = 0 ;
_fetchBgEnd = 0 ;
_fetchSpriteStart = 0 ;
_fetchSpriteEnd = 0 ;
_spriteEvalStart = 0 ;
_spriteEvalEnd = 0 ;
_spriteFetchingDone = false ;
memset ( _hasSpritePriority , 0 , sizeof ( _hasSpritePriority ) ) ;
memcpy ( _spritePriority , _spritePriorityCopy , sizeof ( _spritePriority ) ) ;
2021-03-10 11:13:28 -05:00
for ( int i = 0 ; i < 255 ; i + + ) {
if ( _spritePriority [ i ] < 4 ) {
2019-07-19 19:39:38 -04:00
_hasSpritePriority [ _spritePriority [ i ] ] = true ;
}
2019-07-12 23:53:47 -04:00
}
2019-07-19 19:39:38 -04:00
memcpy ( _spritePalette , _spritePaletteCopy , sizeof ( _spritePalette ) ) ;
memcpy ( _spriteColors , _spriteColorsCopy , sizeof ( _spriteColors ) ) ;
2019-07-08 15:24:43 -04:00
2019-07-19 19:39:38 -04:00
memset ( _spriteIndexes , 0xFF , sizeof ( _spriteIndexes ) ) ;
2019-07-07 18:18:20 -04:00
2020-01-11 17:40:42 -05:00
memset ( _mainScreenFlags , 0 , sizeof ( _mainScreenFlags ) ) ;
memset ( _subScreenPriority , 0 , sizeof ( _subScreenPriority ) ) ;
2019-07-19 19:39:38 -04:00
}
_scanline + + ;
2019-02-19 18:01:27 -05:00
2021-03-10 11:13:28 -05:00
if ( _scanline = = _nmiScanline ) {
2019-08-09 20:47:12 -04:00
ProcessLocationLatchRequest ( ) ;
_latchRequest = false ;
2019-02-17 21:09:33 -05:00
//Reset OAM address at the start of vblank?
2021-03-10 11:13:28 -05:00
if ( ! _state . 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-10-10 23:54:38 -04:00
_internalOamAddress = ( _state . OamRamAddress < < 1 ) ;
2019-02-19 18:41:59 -05:00
}
2019-02-17 21:09:33 -05:00
2019-07-19 19:39:38 -04:00
VideoConfig cfg = _settings - > GetVideoConfig ( ) ;
2021-03-10 11:13:28 -05:00
_configVisibleLayers = ( cfg . HideBgLayer0 ? 0 : 1 ) | ( cfg . HideBgLayer1 ? 0 : 2 ) | ( cfg . HideBgLayer2 ? 0 : 4 ) | ( cfg . HideBgLayer3 ? 0 : 8 ) | ( cfg . HideSprites ? 0 : 16 ) ;
2019-05-20 15:35:09 -04:00
2019-05-12 21:18:05 -04:00
_console - > ProcessEvent ( EventType : : EndFrame ) ;
2019-02-17 19:54:29 -05:00
_frameCount + + ;
2019-07-19 19:39:38 -04:00
_spc - > ProcessEndFrame ( ) ;
2019-02-17 19:54:29 -05:00
_regs - > SetNmiFlag ( true ) ;
2019-02-13 18:44:39 -05:00
SendFrame ( ) ;
2019-10-30 20:42:19 -04:00
_console - > ProcessEndOfFrame ( ) ;
2021-03-10 11:13:28 -05:00
} else if ( _scanline > = _vblankEndScanline + 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-13 08:55:13 -04:00
_skipRender = (
2019-07-19 19:39:38 -04:00
! _settings - > GetVideoConfig ( ) . DisableFrameSkipping & &
2019-07-13 08:55:13 -04:00
! _console - > GetRewindManager ( ) - > IsRewinding ( ) & &
! _console - > GetVideoRenderer ( ) - > IsRecording ( ) & &
2019-07-19 19:39:38 -04:00
( _settings - > GetEmulationSpeed ( ) = = 0 | | _settings - > GetEmulationSpeed ( ) > 150 ) & &
2019-07-13 08:55:13 -04:00
_frameSkipTimer . GetElapsedMS ( ) < 10
) ;
2021-03-10 11:13:28 -05:00
if ( _console - > IsRunAheadFrame ( ) ) {
2019-12-26 12:03:38 -05:00
_skipRender = true ;
}
2019-07-06 14:58:09 -04:00
2019-07-19 19:39:38 -04:00
//Ensure the SPC is re-enabled for the next frame
_spc - > SetSpcState ( true ) ;
2019-02-13 18:44:39 -05:00
}
2019-07-19 19:39:38 -04:00
UpdateSpcState ( ) ;
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-07-19 19:39:38 -04:00
void Ppu : : UpdateSpcState ( )
2019-04-14 15:22:34 -04:00
{
2019-07-19 19:39:38 -04:00
//When using overclocking, turn off the SPC during the extra scanlines
2021-03-10 11:13:28 -05:00
if ( _overclockEnabled & & _scanline > _vblankStartScanline ) {
if ( _scanline > _adjustedVblankEndScanline ) {
2019-07-19 19:39:38 -04:00
//Disable APU for extra lines after NMI
_spc - > SetSpcState ( false ) ;
2021-03-10 11:13:28 -05:00
} else if ( _scanline > = _vblankStartScanline & & _scanline < _nmiScanline ) {
2019-07-19 19:39:38 -04:00
//Disable APU for extra lines before NMI
_spc - > SetSpcState ( false ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-19 19:39:38 -04:00
_spc - > SetSpcState ( true ) ;
}
}
}
void Ppu : : UpdateNmiScanline ( )
{
EmulationConfig cfg = _settings - > GetEmulationConfig ( ) ;
2021-03-10 11:13:28 -05:00
if ( _console - > GetRegion ( ) = = ConsoleRegion : : Ntsc ) {
if ( ! _state . ScreenInterlace | | _oddFrame ) {
2019-07-19 19:39:38 -04:00
_baseVblankEndScanline = 261 ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-19 19:39:38 -04:00
_baseVblankEndScanline = 262 ;
2019-04-14 15:22:34 -04:00
}
2021-03-10 11:13:28 -05:00
} else {
if ( ! _state . ScreenInterlace | | _oddFrame ) {
2019-07-19 19:39:38 -04:00
_baseVblankEndScanline = 311 ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-19 19:39:38 -04:00
_baseVblankEndScanline = 312 ;
2019-04-14 15:22:34 -04:00
}
}
2019-07-19 19:39:38 -04:00
_overclockEnabled = cfg . PpuExtraScanlinesBeforeNmi > 0 | | cfg . PpuExtraScanlinesAfterNmi > 0 ;
_adjustedVblankEndScanline = _baseVblankEndScanline + cfg . PpuExtraScanlinesBeforeNmi ;
_vblankEndScanline = _baseVblankEndScanline + cfg . PpuExtraScanlinesAfterNmi + cfg . PpuExtraScanlinesBeforeNmi ;
2019-10-10 23:54:38 -04:00
_vblankStartScanline = _state . OverscanMode ? 240 : 225 ;
2019-07-19 19:39:38 -04:00
_nmiScanline = _vblankStartScanline + cfg . PpuExtraScanlinesBeforeNmi ;
}
uint16_t Ppu : : GetRealScanline ( )
{
2021-03-10 11:13:28 -05:00
if ( ! _overclockEnabled ) {
2019-07-19 19:39:38 -04:00
return _scanline ;
}
2021-03-10 11:13:28 -05:00
if ( _scanline > _vblankStartScanline & & _scanline < = _nmiScanline ) {
2019-07-19 19:39:38 -04:00
//Pretend to be just before vblank until extra scanlines are over
return _vblankStartScanline - 1 ;
2021-03-10 11:13:28 -05:00
} else if ( _scanline > _nmiScanline ) {
if ( _scanline > _adjustedVblankEndScanline ) {
2019-07-19 19:39:38 -04:00
//Pretend to be at the end of vblank until extra scanlines are over
return _baseVblankEndScanline ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-19 19:39:38 -04:00
//Number the regular scanlines as they would normally be
return _scanline - _nmiScanline + _vblankStartScanline ;
}
}
return _scanline ;
}
2019-10-08 21:04:32 -04:00
uint16_t Ppu : : GetVblankEndScanline ( )
{
return _vblankEndScanline ;
}
2019-07-19 19:39:38 -04:00
uint16_t Ppu : : GetLastScanline ( )
{
return _baseVblankEndScanline ;
2019-04-14 15:22:34 -04:00
}
2019-02-22 22:35:53 -05:00
void Ppu : : EvaluateNextLineSprites ( )
{
2021-03-10 11:13:28 -05:00
if ( _spriteEvalStart = = 0 ) {
2019-07-10 22:50:12 -04:00
_spriteCount = 0 ;
2019-10-10 23:54:38 -04:00
_oamEvaluationIndex = _state . EnableOamPriority ? ( ( _internalOamAddress & 0x1FC ) > > 2 ) : 0 ;
2019-07-10 22:50:12 -04:00
}
2021-03-10 11:13:28 -05:00
if ( _state . ForcedVblank ) {
2019-04-20 11:46:51 -04:00
return ;
}
2021-03-10 11:13:28 -05:00
for ( int i = _spriteEvalStart ; i < = _spriteEvalEnd ; i + + ) {
if ( ! ( i & 0x01 ) ) {
2019-07-10 22:50:12 -04:00
//First cycle, read X & Y and high oam byte
FetchSpritePosition ( _oamEvaluationIndex < < 2 ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-10 22:50:12 -04:00
//Second cycle: Check if sprite is in range, if so, keep its index
2021-03-10 11:13:28 -05:00
if ( _currentSprite . IsVisible ( _scanline , _state . ObjInterlace ) ) {
if ( _spriteCount < 32 ) {
2019-07-10 22:50:12 -04:00
_spriteIndexes [ _spriteCount ] = _oamEvaluationIndex ;
_spriteCount + + ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-10 22:50:12 -04:00
_rangeOver = true ;
}
}
_oamEvaluationIndex = ( _oamEvaluationIndex + 1 ) & 0x7F ;
2019-03-02 18:00:27 -05:00
}
2019-07-10 22:50:12 -04:00
}
}
2019-02-22 22:35:53 -05:00
2019-07-10 22:50:12 -04:00
void Ppu : : FetchSpriteData ( )
{
//From H=272 to 339, fetch a single word of CHR data on every cycle (for up to 34 sprites)
2021-03-10 11:13:28 -05:00
if ( _fetchSpriteStart = = 0 ) {
2019-07-10 22:50:12 -04:00
memset ( _spritePriorityCopy , 0xFF , sizeof ( _spritePriorityCopy ) ) ;
2019-02-22 22:35:53 -05:00
2019-07-10 22:50:12 -04:00
_spriteTileCount = 0 ;
_currentSprite . Index = 0xFF ;
2019-03-02 13:51:42 -05:00
2021-03-10 11:13:28 -05:00
if ( _spriteCount = = 0 ) {
2019-07-10 22:50:12 -04:00
_spriteFetchingDone = true ;
return ;
2019-02-24 13:09:22 -05:00
}
2019-02-22 22:35:53 -05:00
2019-07-10 22:50:12 -04:00
_oamTimeIndex = _spriteIndexes [ _spriteCount - 1 ] ;
2019-03-02 13:51:42 -05:00
}
2021-03-10 11:13:28 -05:00
for ( int x = _fetchSpriteStart ; x < = _fetchSpriteEnd ; x + + ) {
if ( x > = 2 ) {
2019-07-10 22:50:12 -04:00
//Fetch the tile using the OAM data loaded on the past 2 cycles, before overwriting it in FetchSpriteAttributes below
2021-03-10 11:13:28 -05:00
if ( ! _state . ForcedVblank ) {
2019-07-10 22:50:12 -04:00
FetchSpriteTile ( x & 0x01 ) ;
}
2019-03-02 13:51:42 -05:00
2021-03-10 11:13:28 -05:00
if ( ( x & 1 ) & & _spriteCount = = 0 & & _currentSprite . ColumnOffset = = 0 ) {
2019-07-10 22:50:12 -04:00
//End this step
_spriteFetchingDone = true ;
break ;
}
2019-03-02 18:00:27 -05:00
}
2019-07-10 22:50:12 -04:00
2021-03-10 11:13:28 -05:00
if ( _spriteCount > 0 ) {
if ( x & 1 ) {
2019-07-10 22:50:12 -04:00
FetchSpriteAttributes ( ( _oamTimeIndex < < 2 ) | 0x02 ) ;
2021-03-10 11:13:28 -05:00
if ( _spriteCount > 0 ) {
2019-07-10 22:50:12 -04:00
_oamTimeIndex = _spriteIndexes [ _spriteCount - 1 ] ;
}
2021-03-10 11:13:28 -05:00
} else {
2019-07-10 22:50:12 -04:00
FetchSpritePosition ( _oamTimeIndex < < 2 ) ;
}
2019-02-22 22:35:53 -05:00
}
2019-07-10 22:50:12 -04:00
}
}
2019-02-22 22:35:53 -05:00
2019-07-10 22:50:12 -04:00
void Ppu : : FetchSpritePosition ( uint16_t oamAddress )
{
uint8_t highTableOffset = oamAddress > > 4 ;
uint8_t shift = ( ( oamAddress > > 1 ) & 0x06 ) ;
uint8_t highTableValue = _oamRam [ 0x200 | highTableOffset ] > > shift ;
uint8_t largeSprite = ( highTableValue & 0x02 ) > > 1 ;
uint16_t oamValue = _oamRam [ oamAddress ] | ( _oamRam [ oamAddress + 1 ] < < 8 ) ;
uint16_t sign = ( highTableValue & 0x01 ) < < 8 ;
uint8_t spriteIndex = oamAddress > > 2 ;
_currentSprite . X = ( int16_t ) ( ( sign | ( oamValue & 0xFF ) ) < < 7 ) > > 7 ;
_currentSprite . Y = ( oamValue > > 8 ) ;
2019-10-10 23:54:38 -04:00
_currentSprite . Width = _oamSizes [ _state . OamMode ] [ largeSprite ] [ 0 ] < < 3 ;
2021-03-10 11:13:28 -05:00
if ( spriteIndex ! = _currentSprite . Index ) {
2019-08-06 16:23:08 -04:00
_currentSprite . Index = oamAddress > > 2 ;
_currentSprite . ColumnOffset = ( _currentSprite . Width / 8 ) ;
2021-03-10 11:13:28 -05:00
if ( _currentSprite . X < = - 8 & & _currentSprite . X ! = - 256 ) {
2019-08-06 16:23:08 -04:00
//Skip the first tiles of the sprite (because the tiles are hidden to the left of the screen)
_currentSprite . ColumnOffset + = _currentSprite . X / 8 ;
}
}
2019-07-08 15:24:43 -04:00
2019-10-10 23:54:38 -04:00
uint8_t height = _oamSizes [ _state . OamMode ] [ largeSprite ] [ 1 ] < < 3 ;
2019-07-10 22:50:12 -04:00
_currentSprite . Height = height ;
}
2019-02-22 22:35:53 -05:00
2019-07-10 22:50:12 -04:00
void Ppu : : FetchSpriteAttributes ( uint16_t oamAddress )
{
_spriteTileCount + + ;
2021-03-10 11:13:28 -05:00
if ( _spriteTileCount > 34 ) {
2019-07-10 22:50:12 -04:00
_timeOver = true ;
}
2019-04-04 20:46:29 -04:00
2019-07-10 22:50:12 -04:00
uint8_t flags = _oamRam [ oamAddress + 1 ] ;
bool useSecondTable = ( flags & 0x01 ) ! = 0 ;
_currentSprite . Palette = ( flags > > 1 ) & 0x07 ;
_currentSprite . Priority = ( flags > > 4 ) & 0x03 ;
_currentSprite . HorizontalMirror = ( flags & 0x40 ) ! = 0 ;
2019-07-08 15:24:43 -04:00
2019-08-06 16:23:08 -04:00
_currentSprite . ColumnOffset - - ;
2021-03-10 11:13:28 -05:00
2019-07-10 22:50:12 -04:00
uint8_t yOffset ;
int rowOffset ;
int yGap = ( _scanline - _currentSprite . Y ) ;
2021-03-10 11:13:28 -05:00
if ( _state . ObjInterlace ) {
2019-07-10 22:50:12 -04:00
yGap < < = 1 ;
yGap | = _oddFrame ;
2019-07-08 15:24:43 -04:00
}
2019-07-10 22:50:12 -04:00
bool verticalMirror = ( flags & 0x80 ) ! = 0 ;
2021-03-10 11:13:28 -05:00
if ( verticalMirror ) {
2019-07-10 22:50:12 -04:00
yOffset = ( _currentSprite . Height - 1 - yGap ) & 0x07 ;
rowOffset = ( _currentSprite . Height - 1 - yGap ) > > 3 ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-10 22:50:12 -04:00
yOffset = yGap & 0x07 ;
rowOffset = yGap > > 3 ;
2019-07-08 15:24:43 -04:00
}
2019-02-22 22:35:53 -05:00
2019-08-06 22:22:04 -04:00
uint8_t columnCount = ( _currentSprite . Width / 8 ) ;
2019-07-10 22:50:12 -04:00
uint8_t tileRow = ( _oamRam [ oamAddress ] & 0xF0 ) > > 4 ;
uint8_t tileColumn = _oamRam [ oamAddress ] & 0x0F ;
uint8_t row = ( tileRow + rowOffset ) & 0x0F ;
2021-03-10 11:13:28 -05:00
uint8_t columnOffset = _currentSprite . HorizontalMirror ? _currentSprite . ColumnOffset : ( columnCount - _currentSprite . ColumnOffset - 1 ) ;
2019-07-10 22:50:12 -04:00
uint8_t tileIndex = ( row < < 4 ) | ( ( tileColumn + columnOffset ) & 0x0F ) ;
2020-05-26 18:10:19 -04:00
uint16_t tileStart = ( _state . OamBaseAddress + ( tileIndex < < 4 ) + ( useSecondTable ? _state . OamAddressOffset : 0 ) ) ;
_currentSprite . FetchAddress = ( tileStart + yOffset ) & 0x7FFF ;
2019-08-06 16:23:08 -04:00
2019-08-06 22:22:04 -04:00
int16_t x = _currentSprite . X = = - 256 ? 0 : _currentSprite . X ;
int16_t endTileX = x + ( ( columnCount - _currentSprite . ColumnOffset - 1 ) < < 3 ) + 8 ;
_currentSprite . DrawX = _currentSprite . X + ( ( columnCount - _currentSprite . ColumnOffset - 1 ) < < 3 ) ;
2021-03-10 11:13:28 -05:00
if ( _currentSprite . ColumnOffset = = 0 | | endTileX > = 256 ) {
2019-08-06 16:23:08 -04:00
//Last tile of the sprite, or skip the remaining tiles (because the tiles are hidden to the right of the screen)
_spriteCount - - ;
_currentSprite . ColumnOffset = 0 ;
}
2019-07-10 22:50:12 -04:00
}
void Ppu : : FetchSpriteTile ( bool secondCycle )
{
//The timing for the fetches should be (mostly) accurate (H=272 to 339)
2019-07-11 23:03:02 -04:00
uint16_t chrData = _vram [ _currentSprite . FetchAddress ] ;
2019-07-10 22:50:12 -04:00
_currentSprite . ChrData [ secondCycle ] = chrData ;
2019-03-02 13:51:42 -05:00
2021-03-10 11:13:28 -05:00
if ( ! secondCycle ) {
2020-05-26 18:10:19 -04:00
_currentSprite . FetchAddress = ( _currentSprite . FetchAddress + 8 ) & 0x7FFF ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-10 22:50:12 -04:00
int16_t xPos = _currentSprite . DrawX ;
2021-03-10 11:13:28 -05:00
for ( int x = 0 ; x < 8 ; x + + ) {
if ( xPos + x < 0 | | xPos + x > 255 ) {
2019-07-08 15:24:43 -04:00
continue ;
}
2019-07-10 22:50:12 -04:00
uint8_t xOffset = _currentSprite . HorizontalMirror ? ( ( 7 - x ) & 0x07 ) : x ;
uint8_t color = GetTilePixelColor < 4 > ( _currentSprite . ChrData , 7 - xOffset ) ;
2019-07-08 15:24:43 -04:00
2021-03-10 11:13:28 -05:00
if ( color ! = 0 ) {
2019-07-10 22:50:12 -04:00
_spriteColorsCopy [ xPos + x ] = color ;
_spritePriorityCopy [ xPos + x ] = _currentSprite . Priority ;
_spritePaletteCopy [ xPos + x ] = _currentSprite . Palette ;
2019-03-02 13:51:42 -05:00
}
2019-02-22 22:35:53 -05:00
}
}
}
2019-02-22 18:41:43 -05:00
void Ppu : : RenderMode0 ( )
{
2021-03-10 11:13:28 -05:00
constexpr uint8_t spritePriorities [ 4 ] = { 3 , 6 , 9 , 12 } ;
2020-01-11 17:40:42 -05:00
RenderSprites ( spritePriorities ) ;
RenderTilemap < 0 , 2 , 8 , 11 , 0 > ( ) ;
RenderTilemap < 1 , 2 , 7 , 10 , 32 > ( ) ;
RenderTilemap < 2 , 2 , 2 , 5 , 64 > ( ) ;
RenderTilemap < 3 , 2 , 1 , 4 , 96 > ( ) ;
2019-02-22 18:41:43 -05:00
}
void Ppu : : RenderMode1 ( )
{
2021-03-10 11:13:28 -05:00
constexpr uint8_t spritePriorities [ 4 ] = { 2 , 4 , 7 , 10 } ;
2020-01-11 17:40:42 -05:00
RenderSprites ( spritePriorities ) ;
RenderTilemap < 0 , 4 , 6 , 9 > ( ) ;
RenderTilemap < 1 , 4 , 5 , 8 > ( ) ;
2021-03-10 11:13:28 -05:00
if ( ! _state . Mode1Bg3Priority ) {
2020-01-11 17:40:42 -05:00
RenderTilemap < 2 , 2 , 1 , 3 > ( ) ;
2021-03-10 11:13:28 -05:00
} else {
2020-01-11 17:40:42 -05:00
RenderTilemap < 2 , 2 , 1 , 11 > ( ) ;
2019-02-22 18:41:43 -05:00
}
}
void Ppu : : RenderMode2 ( )
{
2021-03-10 11:13:28 -05:00
constexpr uint8_t spritePriorities [ 4 ] = { 2 , 4 , 6 , 8 } ;
2020-01-11 17:40:42 -05:00
RenderSprites ( spritePriorities ) ;
RenderTilemap < 0 , 4 , 3 , 7 > ( ) ;
RenderTilemap < 1 , 4 , 1 , 5 > ( ) ;
2019-02-22 18:41:43 -05:00
}
void Ppu : : RenderMode3 ( )
{
2021-03-10 11:13:28 -05:00
constexpr uint8_t spritePriorities [ 4 ] = { 2 , 4 , 6 , 8 } ;
2020-01-11 17:40:42 -05:00
RenderSprites ( spritePriorities ) ;
RenderTilemap < 0 , 8 , 3 , 7 > ( ) ;
RenderTilemap < 1 , 4 , 1 , 5 > ( ) ;
2019-02-22 18:41:43 -05:00
}
void Ppu : : RenderMode4 ( )
{
2021-03-10 11:13:28 -05:00
constexpr uint8_t spritePriorities [ 4 ] = { 2 , 4 , 6 , 8 } ;
2020-01-11 17:40:42 -05:00
RenderSprites ( spritePriorities ) ;
RenderTilemap < 0 , 8 , 3 , 7 > ( ) ;
RenderTilemap < 1 , 2 , 1 , 5 > ( ) ;
2019-02-22 18:41:43 -05:00
}
2019-02-23 21:39:35 -05:00
void Ppu : : RenderMode5 ( )
{
2021-03-10 11:13:28 -05:00
constexpr uint8_t spritePriorities [ 4 ] = { 2 , 4 , 6 , 8 } ;
2020-01-11 17:40:42 -05:00
RenderSprites ( spritePriorities ) ;
RenderTilemap < 0 , 4 , 3 , 7 > ( ) ;
RenderTilemap < 1 , 2 , 1 , 5 > ( ) ;
2019-02-23 21:39:35 -05:00
}
2019-02-24 10:30:19 -05:00
void Ppu : : RenderMode6 ( )
{
2021-03-10 11:13:28 -05:00
constexpr uint8_t spritePriorities [ 4 ] = { 2 , 3 , 4 , 6 } ;
2020-01-11 17:40:42 -05:00
RenderSprites ( spritePriorities ) ;
RenderTilemap < 0 , 4 , 1 , 5 > ( ) ;
2019-02-24 10:30:19 -05:00
}
2019-02-23 15:40:32 -05:00
void Ppu : : RenderMode7 ( )
{
2021-03-10 11:13:28 -05:00
constexpr uint8_t spritePriorities [ 4 ] = { 2 , 4 , 6 , 7 } ;
2020-01-11 17:40:42 -05:00
RenderSprites ( spritePriorities ) ;
RenderTilemapMode7 < 0 , 3 , 3 > ( ) ;
2021-03-10 11:13:28 -05:00
if ( _state . ExtBgEnabled ) {
2020-01-11 17:40:42 -05:00
RenderTilemapMode7 < 1 , 1 , 5 > ( ) ;
2019-02-23 16:04:04 -05:00
}
2019-02-23 15:40:32 -05:00
}
2019-02-19 01:26:48 -05:00
void Ppu : : RenderScanline ( )
{
2019-07-08 15:24:43 -04:00
int32_t hPos = GetCycle ( ) ;
2019-07-10 22:50:12 -04:00
2021-03-10 11:13:28 -05:00
if ( hPos < = 255 | | _spriteEvalEnd < 255 ) {
2019-07-10 22:50:12 -04:00
_spriteEvalEnd = std : : min ( hPos , 255 ) ;
2021-03-10 11:13:28 -05:00
if ( _spriteEvalStart < = _spriteEvalEnd ) {
2019-07-10 22:50:12 -04:00
EvaluateNextLineSprites ( ) ;
}
_spriteEvalStart = _spriteEvalEnd + 1 ;
}
2021-03-10 11:13:28 -05:00
if ( ! _skipRender & & ( hPos < = 263 | | _fetchBgEnd < 263 ) ) {
2019-07-08 15:24:43 -04:00
//Fetch tilemap and tile CHR data, as needed, between H=0 and H=263
2019-07-10 22:50:12 -04:00
_fetchBgEnd = std : : min ( hPos , 263 ) ;
2021-03-10 11:13:28 -05:00
if ( _fetchBgStart < = _fetchBgEnd ) {
2019-07-08 15:24:43 -04:00
FetchTileData ( ) ;
}
2019-07-10 22:50:12 -04:00
_fetchBgStart = _fetchBgEnd + 1 ;
2021-03-10 11:13:28 -05:00
}
2019-07-07 18:18:20 -04:00
2019-07-08 15:24:43 -04:00
//Render the scanline
2021-03-10 11:13:28 -05:00
if ( ! _skipRender & & _drawStartX < = 255 & & hPos > 22 & & _scanline > 0 ) {
2019-07-08 15:24:43 -04:00
_drawEndX = std : : min ( hPos - 22 , 255 ) ;
2019-02-19 01:26:48 -05:00
2021-03-10 11:13:28 -05:00
if ( _state . ForcedVblank ) {
2020-01-11 17:40:42 -05:00
//Forced blank, output black
memset ( _mainScreenBuffer + _drawStartX , 0 , ( _drawEndX - _drawStartX + 1 ) * 2 ) ;
memset ( _subScreenBuffer + _drawStartX , 0 , ( _drawEndX - _drawStartX + 1 ) * 2 ) ;
2021-03-10 11:13:28 -05:00
} else {
switch ( _state . BgMode ) {
case 0 : RenderMode0 ( ) ; break ;
case 1 : RenderMode1 ( ) ; break ;
case 2 : RenderMode2 ( ) ; break ;
case 3 : RenderMode3 ( ) ; break ;
case 4 : RenderMode4 ( ) ; break ;
case 5 : RenderMode5 ( ) ; break ;
case 6 : RenderMode6 ( ) ; break ;
case 7 : RenderMode7 ( ) ; break ;
2020-01-11 17:40:42 -05:00
}
RenderBgColor ( ) ;
2019-07-08 15:24:43 -04:00
}
2019-02-22 18:41:43 -05:00
2019-07-08 15:24:43 -04:00
ApplyColorMath ( ) ;
ApplyBrightness < true > ( ) ;
ApplyHiResMode ( ) ;
2019-03-06 22:25:14 -05:00
2019-07-08 15:24:43 -04:00
_drawStartX = _drawEndX + 1 ;
}
2021-03-10 11:13:28 -05:00
if ( hPos > = 270 & & ! _spriteFetchingDone ) {
2019-07-10 22:50:12 -04:00
//Fetch sprite data from OAM and calculated which CHR data needs to be loaded (between H=270 and H=337)
2019-07-08 15:24:43 -04:00
//Fetch sprite CHR data, as needed, between H=272 and H=339
2019-07-10 22:50:12 -04:00
_fetchSpriteEnd = std : : min ( hPos - 270 , 69 ) ;
2021-03-10 11:13:28 -05:00
if ( _fetchSpriteStart < = _fetchSpriteEnd ) {
2019-07-08 15:24:43 -04:00
FetchSpriteData ( ) ;
}
2019-07-10 22:50:12 -04:00
_fetchSpriteStart = _fetchSpriteEnd + 1 ;
2019-02-19 01:26:48 -05:00
}
2019-02-18 22:27:22 -05:00
}
2019-02-19 01:26:48 -05:00
void Ppu : : RenderBgColor ( )
2019-02-13 18:44:39 -05:00
{
2020-01-11 17:40:42 -05:00
uint8_t pixelFlags = ( _state . ColorMathEnabled & 0x20 ) ? PixelFlags : : AllowColorMath : 0 ;
2021-03-10 11:13:28 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
if ( ( _mainScreenFlags [ x ] & 0x0F ) = = 0 ) {
2019-07-12 23:53:47 -04:00
_mainScreenBuffer [ x ] = _cgram [ 0 ] ;
2020-01-11 17:40:42 -05:00
_mainScreenFlags [ x ] = pixelFlags ;
2019-07-12 23:53:47 -04:00
}
2021-03-10 11:13:28 -05:00
if ( _subScreenPriority [ x ] = = 0 ) {
2019-07-12 23:53:47 -04:00
_subScreenBuffer [ x ] = _cgram [ 0 ] ;
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
2020-01-11 17:40:42 -05:00
void Ppu : : RenderSprites ( const uint8_t priority [ 4 ] )
2019-02-24 01:30:55 -05:00
{
2021-03-10 11:13:28 -05:00
if ( ! IsRenderRequired ( Ppu : : SpriteLayerIndex ) ) {
2019-02-24 01:30:55 -05:00
return ;
}
2019-10-10 23:54:38 -04:00
bool drawMain = ( bool ) ( ( ( _state . MainScreenLayers & _configVisibleLayers ) > > Ppu : : SpriteLayerIndex ) & 0x01 ) ;
bool drawSub = ( bool ) ( ( ( _state . SubScreenLayers & _configVisibleLayers ) > > Ppu : : SpriteLayerIndex ) & 0x01 ) ;
2019-07-13 01:07:54 -04:00
2019-07-12 23:53:47 -04:00
uint8_t mainWindowCount = 0 ;
uint8_t subWindowCount = 0 ;
2021-03-10 11:13:28 -05:00
if ( _state . WindowMaskMain [ Ppu : : SpriteLayerIndex ] ) {
mainWindowCount = ( uint8_t ) _state . Window [ 0 ] . ActiveLayers [ Ppu : : SpriteLayerIndex ] + ( uint8_t ) _state . Window [ 1 ] . ActiveLayers [ Ppu : : SpriteLayerIndex ] ;
2019-07-12 23:53:47 -04:00
}
2021-03-10 11:13:28 -05:00
if ( _state . WindowMaskSub [ Ppu : : SpriteLayerIndex ] ) {
subWindowCount = ( uint8_t ) _state . Window [ 0 ] . ActiveLayers [ Ppu : : SpriteLayerIndex ] + ( uint8_t ) _state . Window [ 1 ] . ActiveLayers [ Ppu : : SpriteLayerIndex ] ;
2019-02-24 01:30:55 -05:00
}
2021-03-10 11:13:28 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
if ( _spritePriority [ x ] < = 3 ) {
2020-01-11 17:40:42 -05:00
uint8_t spritePrio = priority [ _spritePriority [ x ] ] ;
2021-03-10 11:13:28 -05:00
if ( drawMain & & ( ( _mainScreenFlags [ x ] & 0x0F ) < spritePrio ) & & ! ProcessMaskWindow < Ppu : : SpriteLayerIndex > ( mainWindowCount , x ) ) {
2019-07-11 17:37:01 -04:00
uint16_t paletteRamOffset = 128 + ( _spritePalette [ x ] < < 4 ) + _spriteColors [ x ] ;
_mainScreenBuffer [ x ] = _cgram [ paletteRamOffset ] ;
2021-03-10 11:13:28 -05:00
_mainScreenFlags [ x ] = spritePrio | ( ( ( _state . ColorMathEnabled & 0x10 ) & & _spritePalette [ x ] > 3 ) ? PixelFlags : : AllowColorMath : 0 ) ;
2019-02-24 01:30:55 -05:00
}
2019-07-12 23:53:47 -04:00
2021-03-10 11:13:28 -05:00
if ( drawSub & & ( _subScreenPriority [ x ] < spritePrio ) & & ! ProcessMaskWindow < Ppu : : SpriteLayerIndex > ( subWindowCount , x ) ) {
2019-07-11 17:37:01 -04:00
uint16_t paletteRamOffset = 128 + ( _spritePalette [ x ] < < 4 ) + _spriteColors [ x ] ;
_subScreenBuffer [ x ] = _cgram [ paletteRamOffset ] ;
2020-01-11 17:40:42 -05:00
_subScreenPriority [ x ] = spritePrio ;
2019-02-24 01:30:55 -05:00
}
}
}
}
2021-03-10 11:13:28 -05:00
template < uint8_t layerIndex , uint8_t bpp , uint8_t normalPriority , uint8_t highPriority , uint16_t basePaletteOffset , bool hiResMode , bool applyMosaic , bool directColorMode >
2019-02-23 01:28:41 -05:00
void Ppu : : RenderTilemap ( )
2019-02-19 01:26:48 -05:00
{
2019-10-10 23:54:38 -04:00
bool drawMain = ( bool ) ( ( ( _state . MainScreenLayers & _configVisibleLayers ) > > layerIndex ) & 0x01 ) ;
bool drawSub = ( bool ) ( ( ( _state . SubScreenLayers & _configVisibleLayers ) > > layerIndex ) & 0x01 ) ;
2019-07-12 23:53:47 -04:00
2021-03-10 11:13:28 -05:00
uint8_t mainWindowCount = _state . WindowMaskMain [ layerIndex ] ? ( uint8_t ) _state . Window [ 0 ] . ActiveLayers [ layerIndex ] + ( uint8_t ) _state . Window [ 1 ] . ActiveLayers [ layerIndex ] : 0 ;
uint8_t subWindowCount = _state . WindowMaskSub [ layerIndex ] ? ( uint8_t ) _state . Window [ 0 ] . ActiveLayers [ layerIndex ] + ( uint8_t ) _state . Window [ 1 ] . ActiveLayers [ layerIndex ] : 0 ;
2019-07-12 23:53:47 -04:00
2019-10-10 23:54:38 -04:00
uint16_t hScrollOriginal = _state . Layers [ layerIndex ] . HScroll ;
2019-07-11 21:45:56 -04:00
uint16_t hScroll = hiResMode ? ( hScrollOriginal < < 1 ) : hScrollOriginal ;
2019-02-23 21:39:35 -05:00
2021-03-10 11:13:28 -05:00
TileData * tileData = _layerData [ layerIndex ] . Tiles ;
2019-02-24 18:45:47 -05:00
2020-10-11 14:33:50 -04:00
uint8_t mosaicCounter = applyMosaic ? _state . MosaicSize - ( _drawStartX % _state . MosaicSize ) : 0 ;
2019-07-12 23:53:47 -04:00
2019-07-11 21:45:56 -04:00
uint8_t lookupIndex ;
uint8_t chrDataOffset ;
2019-07-12 23:53:47 -04:00
uint8_t hiresSubColor ;
2020-01-11 17:40:42 -05:00
uint8_t pixelFlags = ( ( ( _state . ColorMathEnabled > > layerIndex ) & 0x01 ) ? PixelFlags : : AllowColorMath : 0 ) ;
2019-02-23 21:39:35 -05:00
2021-03-10 11:13:28 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
if ( hiResMode ) {
2019-07-11 21:45:56 -04:00
lookupIndex = ( x + ( hScrollOriginal & 0x07 ) ) > > 2 ;
2019-07-12 23:53:47 -04:00
chrDataOffset = ( lookupIndex & 0x01 ) * bpp / 2 ;
2019-07-07 18:18:20 -04:00
lookupIndex > > = 1 ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-11 21:45:56 -04:00
lookupIndex = ( x + ( hScrollOriginal & 0x07 ) ) > > 3 ;
2019-02-24 18:45:47 -05:00
}
2019-02-19 17:23:21 -05:00
2019-07-11 21:45:56 -04:00
uint16_t tilemapData = tileData [ lookupIndex ] . TilemapData ;
2019-07-12 08:33:09 -04:00
uint16_t * chrData = tileData [ lookupIndex ] . ChrData ;
2019-07-08 09:13:08 -04:00
bool hMirror = ( tilemapData & 0x4000 ) ! = 0 ;
2019-07-12 23:53:47 -04:00
uint8_t color ;
2021-03-10 11:13:28 -05:00
if ( hiResMode ) {
2019-07-12 23:53:47 -04:00
uint8_t xOffset = ( ( x < < 1 ) + 1 + hScroll ) & 0x07 ;
uint8_t shift = hMirror ? xOffset : ( 7 - xOffset ) ;
color = GetTilePixelColor < bpp > ( chrData + chrDataOffset , shift ) ;
2021-03-10 11:13:28 -05:00
2019-07-12 23:53:47 -04:00
xOffset = ( ( x < < 1 ) + hScroll ) & 0x07 ;
shift = hMirror ? xOffset : ( 7 - xOffset ) ;
hiresSubColor = GetTilePixelColor < bpp > ( chrData + chrDataOffset , shift ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-12 23:53:47 -04:00
uint8_t xOffset = ( x + hScroll ) & 0x07 ;
uint8_t shift = hMirror ? xOffset : ( 7 - xOffset ) ;
color = GetTilePixelColor < bpp > ( chrData , shift ) ;
2019-07-08 23:46:31 -04:00
}
2019-07-13 08:25:28 -04:00
uint8_t paletteIndex = ( tilemapData > > 10 ) & 0x07 ;
2020-01-11 17:40:42 -05:00
uint8_t priority = ( tilemapData & 0x2000 ) ? highPriority : normalPriority ;
2019-07-13 08:25:28 -04:00
2021-03-10 11:13:28 -05:00
if ( applyMosaic ) {
if ( mosaicCounter = = _state . MosaicSize ) {
2019-07-11 21:45:56 -04:00
mosaicCounter = 1 ;
2021-03-10 11:13:28 -05:00
if ( hiResMode ) {
2019-07-13 08:25:28 -04:00
color = hiresSubColor ;
}
_mosaicColor [ layerIndex ] = ( paletteIndex < < 8 ) | color ;
2020-01-11 17:40:42 -05:00
_mosaicPriority [ layerIndex ] = priority ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-11 21:45:56 -04:00
mosaicCounter + + ;
2019-07-08 23:46:31 -04:00
color = _mosaicColor [ layerIndex ] & 0xFF ;
2019-07-13 08:25:28 -04:00
paletteIndex = _mosaicColor [ layerIndex ] > > 8 ;
2020-01-11 17:40:42 -05:00
priority = _mosaicPriority [ layerIndex ] ;
2021-03-10 11:13:28 -05:00
if ( hiResMode ) {
2019-07-13 08:25:28 -04:00
hiresSubColor = color ;
}
2021-03-10 11:13:28 -05:00
}
2019-07-08 09:13:08 -04:00
}
2021-03-10 11:13:28 -05:00
if ( color > 0 ) {
2020-01-11 17:40:42 -05:00
uint16_t rgbColor = GetRgbColor < bpp , directColorMode , basePaletteOffset > ( paletteIndex , color ) ;
2021-03-10 11:13:28 -05:00
if ( drawMain & & ( _mainScreenFlags [ x ] & 0x0F ) < priority & & ! ProcessMaskWindow < layerIndex > ( mainWindowCount , x ) ) {
2020-01-11 17:40:42 -05:00
DrawMainPixel ( x , rgbColor , priority | pixelFlags ) ;
2019-07-12 23:53:47 -04:00
}
2021-03-10 11:13:28 -05:00
if ( ! hiResMode & & drawSub & & _subScreenPriority [ x ] < priority & & ! ProcessMaskWindow < layerIndex > ( subWindowCount , x ) ) {
2020-01-11 17:40:42 -05:00
DrawSubPixel ( x , rgbColor , priority ) ;
2019-07-12 23:53:47 -04:00
}
}
2021-03-10 11:13:28 -05:00
if ( hiResMode ) {
if ( hiresSubColor > 0 & & drawSub & & _subScreenPriority [ x ] < priority & & ! ProcessMaskWindow < layerIndex > ( subWindowCount , x ) ) {
uint16_t hiresSubRgbColor = GetRgbColor < bpp , directColorMode , basePaletteOffset > ( paletteIndex , hiresSubColor ) ;
2020-01-11 17:40:42 -05:00
DrawSubPixel ( x , hiresSubRgbColor , priority ) ;
2019-02-23 15:40:32 -05:00
}
}
}
}
2021-03-10 11:13:28 -05:00
template < uint8_t bpp , bool directColorMode , uint8_t basePaletteOffset >
2019-07-12 23:53:47 -04:00
uint16_t Ppu : : GetRgbColor ( uint8_t paletteIndex , uint8_t colorIndex )
2019-02-24 01:30:55 -05:00
{
2021-03-10 11:13:28 -05:00
if ( bpp = = 8 & & directColorMode ) {
2019-07-12 23:53:47 -04:00
return (
( ( ( ( colorIndex & 0x07 ) < < 1 ) | ( paletteIndex & 0x01 ) ) < < 1 ) |
( ( ( colorIndex & 0x38 ) | ( ( paletteIndex & 0x02 ) < < 1 ) ) < < 4 ) |
( ( ( colorIndex & 0xC0 ) | ( ( paletteIndex & 0x04 ) < < 3 ) ) < < 7 )
) ;
2021-03-10 11:13:28 -05:00
} else if ( bpp = = 8 ) {
2019-07-12 23:53:47 -04:00
//Ignore palette bits for 256-color layers
return _cgram [ basePaletteOffset + colorIndex ] ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-12 23:53:47 -04:00
return _cgram [ basePaletteOffset + paletteIndex * ( 1 < < bpp ) + colorIndex ] ;
}
}
bool Ppu : : IsRenderRequired ( uint8_t layerIndex )
{
2021-03-10 11:13:28 -05:00
if ( ( ( _state . MainScreenLayers & _configVisibleLayers ) > > layerIndex ) & 0x01 ) {
2019-07-12 23:53:47 -04:00
return true ;
2019-02-24 01:30:55 -05:00
}
2021-03-10 11:13:28 -05:00
if ( ( ( _state . SubScreenLayers & _configVisibleLayers ) > > layerIndex ) & 0x01 ) {
2019-07-12 23:53:47 -04:00
return true ;
}
return false ;
2019-02-24 01:30:55 -05:00
}
2021-03-10 11:13:28 -05:00
template < uint8_t bpp >
2019-07-07 18:18:20 -04:00
uint8_t Ppu : : GetTilePixelColor ( const uint16_t chrData [ 4 ] , const uint8_t shift )
{
uint8_t color ;
2021-03-10 11:13:28 -05:00
if ( bpp = = 2 ) {
2019-07-07 18:18:20 -04:00
color = ( chrData [ 0 ] > > shift ) & 0x01 ;
color | = ( chrData [ 0 ] > > ( 7 + shift ) ) & 0x02 ;
2021-03-10 11:13:28 -05:00
} else if ( bpp = = 4 ) {
2019-07-07 18:18:20 -04:00
color = ( chrData [ 0 ] > > shift ) & 0x01 ;
color | = ( chrData [ 0 ] > > ( 7 + shift ) ) & 0x02 ;
color | = ( ( chrData [ 1 ] > > shift ) & 0x01 ) < < 2 ;
color | = ( ( chrData [ 1 ] > > ( 7 + shift ) ) & 0x02 ) < < 2 ;
2021-03-10 11:13:28 -05:00
} else if ( bpp = = 8 ) {
2019-07-07 18:18:20 -04:00
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 ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-07 18:18:20 -04:00
throw std : : runtime_error ( " unsupported bpp " ) ;
}
return color ;
}
2021-03-10 11:13:28 -05:00
template < uint8_t layerIndex , uint8_t normalPriority , uint8_t highPriority , bool applyMosaic , bool directColorMode >
2019-02-23 15:40:32 -05:00
void Ppu : : RenderTilemapMode7 ( )
{
2021-03-10 11:13:28 -05:00
uint8_t mainWindowCount = _state . WindowMaskMain [ layerIndex ] ? ( uint8_t ) _state . Window [ 0 ] . ActiveLayers [ layerIndex ] + ( uint8_t ) _state . Window [ 1 ] . ActiveLayers [ layerIndex ] : 0 ;
uint8_t subWindowCount = _state . WindowMaskSub [ layerIndex ] ? ( uint8_t ) _state . Window [ 0 ] . ActiveLayers [ layerIndex ] + ( uint8_t ) _state . Window [ 1 ] . ActiveLayers [ layerIndex ] : 0 ;
2019-10-10 23:54:38 -04:00
bool drawMain = ( bool ) ( ( ( _state . MainScreenLayers & _configVisibleLayers ) > > layerIndex ) & 0x01 ) ;
bool drawSub = ( bool ) ( ( ( _state . SubScreenLayers & _configVisibleLayers ) > > layerIndex ) & 0x01 ) ;
2019-07-12 23:53:47 -04: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
2021-03-10 11:13:28 -05:00
if ( _drawStartX = = 0 ) {
2019-07-08 19:25:53 -04:00
//Keep the same scroll offsets for the entire scanline
2019-10-10 23:54:38 -04:00
_state . Mode7 . HScrollLatch = _state . Mode7 . HScroll ;
_state . Mode7 . VScrollLatch = _state . Mode7 . VScroll ;
2019-07-08 19:25:53 -04:00
}
2019-10-10 23:54:38 -04:00
int32_t hScroll = ( ( int32_t ) _state . Mode7 . HScrollLatch < < 19 ) > > 19 ;
int32_t vScroll = ( ( int32_t ) _state . Mode7 . VScrollLatch < < 19 ) > > 19 ;
int32_t centerX = ( ( int32_t ) _state . Mode7 . CenterX < < 19 ) > > 19 ;
int32_t centerY = ( ( int32_t ) _state . Mode7 . CenterY < < 19 ) > > 19 ;
uint16_t realY = _state . Mode7 . VerticalMirroring ? ( 255 - _scanline ) : _scanline ;
2019-07-13 15:40:00 -04:00
2021-03-10 11:13:28 -05:00
if ( applyMosaic ) {
2019-07-13 15:40:00 -04:00
//Keep the "scanline" to what it was at the start of this mosaic block
2019-10-10 23:54:38 -04:00
realY - = _state . MosaicSize - _mosaicScanlineCounter ;
2019-07-13 15:40:00 -04:00
}
2020-10-11 14:33:50 -04:00
uint8_t mosaicCounter = applyMosaic ? _state . MosaicSize - ( _drawStartX % _state . MosaicSize ) : 0 ;
2019-02-23 15:40:32 -05:00
2019-07-13 00:02:36 -04:00
int32_t xValue = (
2019-10-10 23:54:38 -04:00
( ( _state . Mode7 . Matrix [ 0 ] * clip ( hScroll - centerX ) ) & ~ 63 ) +
( ( _state . Mode7 . Matrix [ 1 ] * realY ) & ~ 63 ) +
( ( _state . Mode7 . Matrix [ 1 ] * clip ( vScroll - centerY ) ) & ~ 63 ) +
2019-02-23 15:40:32 -05:00
( centerX < < 8 )
) ;
2019-07-13 00:02:36 -04:00
int32_t yValue = (
2019-10-10 23:54:38 -04:00
( ( _state . Mode7 . Matrix [ 2 ] * clip ( hScroll - centerX ) ) & ~ 63 ) +
( ( _state . Mode7 . Matrix [ 3 ] * realY ) & ~ 63 ) +
( ( _state . Mode7 . Matrix [ 3 ] * clip ( vScroll - centerY ) ) & ~ 63 ) +
2019-02-23 15:40:32 -05:00
( centerY < < 8 )
) ;
2019-10-10 23:54:38 -04:00
int16_t xStep = _state . Mode7 . Matrix [ 0 ] ;
int16_t yStep = _state . Mode7 . Matrix [ 2 ] ;
2021-03-10 11:13:28 -05:00
if ( _state . Mode7 . HorizontalMirroring ) {
2019-07-13 14:16:50 -04:00
//Calculate the value at the end of the scanline, and then start going backwards
xValue + = xStep * _drawEndX ;
yValue + = yStep * _drawEndX ;
xStep = - xStep ;
yStep = - yStep ;
}
xValue + = xStep * _drawStartX ;
yValue + = yStep * _drawStartX ;
2021-03-10 11:13:28 -05:00
2020-01-11 17:40:42 -05:00
uint8_t pixelFlags = ( ( _state . ColorMathEnabled > > layerIndex ) & 0x01 ) ? PixelFlags : : AllowColorMath : 0 ;
2019-02-23 15:40:32 -05:00
2021-03-10 11:13:28 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2019-07-13 00:02:36 -04:00
int32_t xOffset = xValue > > 8 ;
int32_t yOffset = yValue > > 8 ;
2019-07-13 14:16:50 -04:00
xValue + = xStep ;
yValue + = yStep ;
2020-01-11 17:40:42 -05:00
2019-02-24 10:29:11 -05:00
uint8_t tileIndex ;
2021-03-10 11:13:28 -05:00
if ( ! _state . Mode7 . LargeMap ) {
2019-02-23 15:40:32 -05:00
yOffset & = 0x3FF ;
xOffset & = 0x3FF ;
2019-07-11 23:03:02 -04:00
tileIndex = ( uint8_t ) _vram [ ( ( yOffset & ~ 0x07 ) < < 4 ) | ( xOffset > > 3 ) ] ;
2021-03-10 11:13:28 -05:00
} else {
if ( yOffset < 0 | | yOffset > 0x3FF | | xOffset < 0 | | xOffset > 0x3FF ) {
if ( _state . Mode7 . FillWithTile0 ) {
2019-02-24 10:29:11 -05:00
tileIndex = 0 ;
2021-03-10 11:13:28 -05:00
} else {
2019-02-23 15:40:32 -05:00
//Draw nothing for this pixel, we're outside the map
continue ;
2019-02-20 17:39:14 -05:00
}
2021-03-10 11:13:28 -05:00
} else {
2019-07-11 23:03:02 -04:00
tileIndex = ( uint8_t ) _vram [ ( ( yOffset & ~ 0x07 ) < < 4 ) | ( xOffset > > 3 ) ] ;
2019-02-23 15:40:32 -05:00
}
}
2019-02-24 01:11:26 -05:00
uint16_t colorIndex ;
2020-01-11 17:40:42 -05:00
uint8_t priority ;
2021-03-10 11:13:28 -05:00
if ( layerIndex = = 1 ) {
2019-07-11 23:03:02 -04:00
uint8_t color = _vram [ ( ( tileIndex < < 6 ) + ( ( yOffset & 0x07 ) < < 3 ) + ( xOffset & 0x07 ) ) ] > > 8 ;
2020-01-11 17:40:42 -05:00
priority = ( color & 0x80 ) ? highPriority : normalPriority ;
2019-02-24 01:11:26 -05:00
colorIndex = ( color & 0x7F ) ;
2021-03-10 11:13:28 -05:00
} else {
2020-01-11 17:40:42 -05:00
priority = normalPriority ;
2019-07-11 23:03:02 -04:00
colorIndex = _vram [ ( ( tileIndex < < 6 ) + ( ( yOffset & 0x07 ) < < 3 ) + ( xOffset & 0x07 ) ) ] > > 8 ;
2019-02-23 16:04:04 -05:00
}
2019-02-23 15:40:32 -05:00
2021-03-10 11:13:28 -05:00
if ( applyMosaic ) {
if ( mosaicCounter = = _state . MosaicSize ) {
2019-07-13 15:40:00 -04:00
mosaicCounter = 1 ;
_mosaicColor [ layerIndex ] = colorIndex ;
2020-01-11 17:40:42 -05:00
_mosaicPriority [ layerIndex ] = priority ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-13 15:40:00 -04:00
mosaicCounter + + ;
colorIndex = _mosaicColor [ layerIndex ] ;
2020-01-11 17:40:42 -05:00
priority = _mosaicPriority [ layerIndex ] ;
2019-07-13 15:40:00 -04:00
}
}
2021-03-10 11:13:28 -05:00
if ( colorIndex > 0 ) {
2019-02-24 01:11:26 -05:00
uint16_t paletteColor ;
2021-03-10 11:13:28 -05:00
if ( directColorMode ) {
2019-02-24 01:11:26 -05:00
paletteColor = ( ( colorIndex & 0x07 ) < < 2 ) | ( ( colorIndex & 0x38 ) < < 4 ) | ( ( colorIndex & 0xC0 ) < < 7 ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-11 17:37:01 -04:00
paletteColor = _cgram [ colorIndex ] ;
2019-02-24 01:11:26 -05:00
}
2021-03-10 11:13:28 -05:00
if ( drawMain & & ( _mainScreenFlags [ x ] & 0x0F ) < priority & & ! ProcessMaskWindow < layerIndex > ( mainWindowCount , x ) ) {
2020-01-11 17:40:42 -05:00
DrawMainPixel ( x , paletteColor , priority | pixelFlags ) ;
2021-03-10 11:13:28 -05:00
}
2019-07-12 23:53:47 -04:00
2021-03-10 11:13:28 -05:00
if ( drawSub & & _subScreenPriority [ x ] < priority & & ! ProcessMaskWindow < layerIndex > ( subWindowCount , x ) ) {
2020-01-11 17:40:42 -05:00
DrawSubPixel ( x , paletteColor , priority ) ;
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 ;
2020-01-11 17:40:42 -05:00
_mainScreenFlags [ x ] = flags ;
2019-02-23 15:40:32 -05:00
}
2020-01-11 17:40:42 -05:00
void Ppu : : DrawSubPixel ( uint8_t x , uint16_t color , uint8_t priority )
2019-02-23 15:40:32 -05:00
{
2019-02-24 01:11:26 -05:00
_subScreenBuffer [ x ] = color ;
2020-01-11 17:40:42 -05:00
_subScreenPriority [ x ] = priority ;
2019-02-23 15:40:32 -05:00
}
2019-02-19 01:26:48 -05:00
void Ppu : : ApplyColorMath ( )
{
2021-03-10 11:13:28 -05:00
uint8_t activeWindowCount = ( uint8_t ) _state . Window [ 0 ] . ActiveLayers [ Ppu : : ColorWindowIndex ] + ( uint8_t ) _state . Window [ 1 ] . ActiveLayers [ Ppu : : ColorWindowIndex ] ;
2019-10-10 23:54:38 -04:00
bool hiResMode = _state . HiResMode | | _state . BgMode = = 5 | | _state . BgMode = = 6 ;
2019-02-22 22:19:20 -05:00
2021-03-10 11:13:28 -05:00
if ( hiResMode ) {
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2020-07-02 10:37:06 -04:00
bool isInsideWindow = ProcessMaskWindow < Ppu : : ColorWindowIndex > ( activeWindowCount , x ) ;
2019-04-07 23:19:33 -04:00
2020-07-02 10:37:06 -04:00
//Keep original subscreen color, which is used to apply color math to the main screen after
uint16_t subPixel = _subScreenBuffer [ x ] ;
2019-04-07 23:19:33 -04:00
//Apply the color math based on the previous main pixel
2020-07-02 10:37:06 -04:00
uint16_t prevMainPixel = x > 0 ? _mainScreenBuffer [ x - 1 ] : 0 ;
int prevX = x > 0 ? x - 1 : 0 ;
2019-04-07 23:19:33 -04:00
ApplyColorMathToPixel ( _subScreenBuffer [ x ] , prevMainPixel , prevX , isInsideWindow ) ;
2020-07-02 10:37:06 -04:00
ApplyColorMathToPixel ( _mainScreenBuffer [ x ] , subPixel , x , isInsideWindow ) ;
}
2021-03-10 11:13:28 -05:00
} else {
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2020-07-02 10:37:06 -04:00
bool isInsideWindow = ProcessMaskWindow < Ppu : : ColorWindowIndex > ( activeWindowCount , x ) ;
ApplyColorMathToPixel ( _mainScreenBuffer [ x ] , _subScreenBuffer [ x ] , x , isInsideWindow ) ;
2019-04-02 23:44:00 -04:00
}
}
}
2019-02-22 22:19:20 -05:00
2021-03-10 11:13:28 -05:00
void Ppu : : ApplyColorMathToPixel ( uint16_t & pixelA , uint16_t pixelB , int x , bool isInsideWindow )
2019-04-02 23:44:00 -04:00
{
2019-10-10 23:54:38 -04:00
uint8_t halfShift = ( uint8_t ) _state . ColorMathHalveResult ;
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
2021-03-10 11:13:28 -05:00
switch ( _state . ColorMathClipMode ) {
default :
case ColorWindowMode : : Never : break ;
2019-02-22 22:19:20 -05:00
2021-03-10 11:13:28 -05:00
case ColorWindowMode : : OutsideWindow :
if ( ! isInsideWindow ) {
pixelA = 0 ;
halfShift = 0 ;
}
break ;
2019-02-19 01:26:48 -05:00
2021-03-10 11:13:28 -05:00
case ColorWindowMode : : InsideWindow :
if ( isInsideWindow ) {
pixelA = 0 ;
halfShift = 0 ;
}
break ;
case ColorWindowMode : : Always : pixelA = 0 ; break ;
2019-04-02 23:44:00 -04:00
}
2019-02-19 23:35:43 -05:00
2021-03-10 11:13:28 -05:00
if ( ! ( _mainScreenFlags [ x ] & PixelFlags : : AllowColorMath ) ) {
2019-04-07 23:19:33 -04:00
//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
2021-03-10 11:13:28 -05:00
switch ( _state . ColorMathPreventMode ) {
default :
case ColorWindowMode : : Never : break ;
2019-02-19 23:35:43 -05:00
2021-03-10 11:13:28 -05:00
case ColorWindowMode : : OutsideWindow :
if ( ! isInsideWindow ) {
return ;
}
break ;
case ColorWindowMode : : InsideWindow :
if ( isInsideWindow ) {
return ;
}
break ;
2019-04-02 23:44:00 -04:00
2021-03-10 11:13:28 -05:00
case ColorWindowMode : : Always : return ;
2019-04-02 23:44:00 -04:00
}
uint16_t otherPixel ;
2021-03-10 11:13:28 -05:00
if ( _state . ColorMathAddSubscreen ) {
if ( _subScreenPriority [ x ] > 0 ) {
2019-04-02 23:44:00 -04:00
otherPixel = pixelB ;
2021-03-10 11:13:28 -05:00
} else {
2019-04-02 23:44:00 -04:00
//there's nothing in the subscreen at this pixel, use the fixed color and disable halve operation
2019-10-10 23:54:38 -04:00
otherPixel = _state . FixedColor ;
2019-04-02 23:44:00 -04:00
halfShift = 0 ;
2019-02-19 23:35:43 -05:00
}
2021-03-10 11:13:28 -05:00
} else {
2019-10-10 23:54:38 -04:00
otherPixel = _state . FixedColor ;
2019-04-02 23:44:00 -04:00
}
2019-07-13 00:02:00 -04:00
constexpr unsigned int mask = 0x1F ;
2021-03-10 11:13:28 -05:00
if ( _state . ColorMathSubstractMode ) {
2019-07-13 00:02:00 -04:00
uint16_t r = std : : max ( ( int ) ( ( pixelA & mask ) - ( otherPixel & mask ) ) , 0 ) > > halfShift ;
uint16_t g = std : : max ( ( int ) ( ( ( pixelA > > 5U ) & mask ) - ( ( otherPixel > > 5U ) & mask ) ) , 0 ) > > halfShift ;
uint16_t b = std : : max ( ( int ) ( ( ( pixelA > > 10U ) & mask ) - ( ( otherPixel > > 10U ) & mask ) ) , 0 ) > > halfShift ;
2019-04-02 23:44:00 -04:00
2019-07-13 00:02:00 -04:00
pixelA = r | ( g < < 5U ) | ( b < < 10U ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-13 00:02:00 -04:00
uint16_t r = std : : min ( ( ( pixelA & mask ) + ( otherPixel & mask ) ) > > halfShift , mask ) ;
uint16_t g = std : : min ( ( ( ( pixelA > > 5U ) & mask ) + ( ( otherPixel > > 5U ) & mask ) ) > > halfShift , mask ) ;
uint16_t b = std : : min ( ( ( ( pixelA > > 10U ) & mask ) + ( ( otherPixel > > 10U ) & mask ) ) > > halfShift , mask ) ;
2019-04-02 23:44:00 -04:00
2019-07-13 00:02:00 -04:00
pixelA = r | ( g < < 5U ) | ( b < < 10U ) ;
2019-02-18 22:27:22 -05:00
}
2019-02-17 23:26:49 -05:00
}
2021-03-10 11:13:28 -05:00
template < bool forMainScreen >
2019-02-22 22:31:20 -05:00
void Ppu : : ApplyBrightness ( )
{
2021-03-10 11:13:28 -05:00
if ( _state . ScreenBrightness ! = 15 ) {
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
uint16_t & pixel = ( forMainScreen ? _mainScreenBuffer : _subScreenBuffer ) [ x ] ;
2019-10-10 23:54:38 -04:00
uint16_t r = ( pixel & 0x1F ) * _state . ScreenBrightness / 15 ;
uint16_t g = ( ( pixel > > 5 ) & 0x1F ) * _state . ScreenBrightness / 15 ;
uint16_t b = ( ( pixel > > 10 ) & 0x1F ) * _state . ScreenBrightness / 15 ;
2019-02-22 22:31:20 -05:00
pixel = r | ( g < < 5 ) | ( b < < 10 ) ;
}
}
}
2019-07-06 14:03:27 -04:00
void Ppu : : ConvertToHiRes ( )
{
2020-02-29 19:36:17 -05:00
bool useHighResOutput = _useHighResOutput | | IsDoubleWidth ( ) | | _state . ScreenInterlace ;
2021-03-10 11:13:28 -05:00
if ( ! useHighResOutput | | _useHighResOutput = = useHighResOutput | | _scanline > = _vblankStartScanline | | _scanline = = 0 ) {
2020-02-29 19:36:17 -05:00
return ;
}
//Convert standard res picture to high resolution when the PPU starts drawing in high res mid frame
_useHighResOutput = useHighResOutput ;
uint16_t scanline = _overscanFrame ? ( _scanline - 1 ) : ( _scanline + 6 ) ;
2019-07-06 14:03:27 -04:00
2021-03-10 11:13:28 -05:00
if ( _drawStartX > 0 ) {
for ( int x = 0 ; x < _drawStartX ; x + + ) {
2019-07-06 14:03:27 -04:00
_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 ) ) ;
}
2021-03-10 11:13:28 -05:00
for ( int i = scanline - 1 ; i > = 0 ; i - - ) {
for ( int x = 0 ; x < 256 ; x + + ) {
2019-07-06 14:03:27 -04:00
_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
2020-02-29 19:36:17 -05:00
uint16_t scanline = _overscanFrame ? ( _scanline - 1 ) : ( _scanline + 6 ) ;
2019-07-06 14:03:27 -04:00
2021-03-10 11:13:28 -05:00
if ( ! _useHighResOutput ) {
memcpy ( _currentBuffer + ( scanline < < 8 ) + _drawStartX , _mainScreenBuffer + _drawStartX , ( _drawEndX - _drawStartX + 1 ) < < 1 ) ;
} else {
2019-11-02 22:55:56 -04:00
_interlacedFrame | = _state . ScreenInterlace ;
2021-03-10 11:13:28 -05:00
uint32_t screenY = _state . ScreenInterlace ? ( _oddFrame ? ( ( scanline < < 1 ) + 1 ) : ( scanline < < 1 ) ) : ( scanline < < 1 ) ;
2019-07-06 14:03:27 -04:00
uint32_t baseAddr = ( screenY < < 9 ) ;
2021-03-10 11:13:28 -05:00
if ( IsDoubleWidth ( ) ) {
2019-07-06 14:03:27 -04:00
ApplyBrightness < false > ( ) ;
2021-03-10 11:13:28 -05:00
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2019-07-06 14:03:27 -04:00
_currentBuffer [ baseAddr + ( x < < 1 ) ] = _subScreenBuffer [ x ] ;
_currentBuffer [ baseAddr + ( x < < 1 ) + 1 ] = _mainScreenBuffer [ x ] ;
}
2021-03-10 11:13:28 -05:00
} else {
for ( int x = _drawStartX ; x < = _drawEndX ; x + + ) {
2019-07-06 14:03:27 -04:00
_currentBuffer [ baseAddr + ( x < < 1 ) ] = _mainScreenBuffer [ x ] ;
_currentBuffer [ baseAddr + ( x < < 1 ) + 1 ] = _mainScreenBuffer [ x ] ;
}
2019-03-02 18:00:27 -05:00
}
2021-03-10 11:13:28 -05:00
if ( ! _state . ScreenInterlace ) {
2019-07-06 14:03:27 -04:00
//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
}
}
2021-03-10 11:13:28 -05:00
template < uint8_t layerIndex >
2019-02-22 20:15:55 -05:00
bool Ppu : : ProcessMaskWindow ( uint8_t activeWindowCount , int x )
{
2021-03-10 11:13:28 -05:00
switch ( activeWindowCount ) {
case 1 :
if ( _state . Window [ 0 ] . ActiveLayers [ layerIndex ] ) {
return _state . Window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) ;
}
return _state . Window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ;
2019-07-12 23:53:47 -04:00
2021-03-10 11:13:28 -05:00
case 2 :
switch ( _state . MaskLogic [ layerIndex ] ) {
default :
case WindowMaskLogic : : Or : return _state . Window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) | _state . Window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ;
case WindowMaskLogic : : And : return _state . Window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) & _state . Window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ;
case WindowMaskLogic : : Xor : return _state . Window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) ^ _state . Window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ;
case WindowMaskLogic : : Xnor : return ! ( _state . Window [ 0 ] . PixelNeedsMasking < layerIndex > ( x ) ^ _state . Window [ 1 ] . PixelNeedsMasking < layerIndex > ( x ) ) ;
}
2019-02-22 20:15:55 -05:00
}
2019-07-12 23:53:47 -04:00
return false ;
2019-02-22 20:15:55 -05:00
}
void Ppu : : ProcessWindowMaskSettings ( uint8_t value , uint8_t offset )
{
2019-10-10 23:54:38 -04:00
_state . Window [ 0 ] . ActiveLayers [ 0 + offset ] = ( value & 0x02 ) ! = 0 ;
_state . Window [ 0 ] . ActiveLayers [ 1 + offset ] = ( value & 0x20 ) ! = 0 ;
_state . Window [ 0 ] . InvertedLayers [ 0 + offset ] = ( value & 0x01 ) ! = 0 ;
_state . Window [ 0 ] . InvertedLayers [ 1 + offset ] = ( value & 0x10 ) ! = 0 ;
2019-02-22 20:15:55 -05:00
2019-10-10 23:54:38 -04:00
_state . Window [ 1 ] . ActiveLayers [ 0 + offset ] = ( value & 0x08 ) ! = 0 ;
_state . Window [ 1 ] . ActiveLayers [ 1 + offset ] = ( value & 0x80 ) ! = 0 ;
_state . Window [ 1 ] . InvertedLayers [ 0 + offset ] = ( value & 0x04 ) ! = 0 ;
_state . Window [ 1 ] . InvertedLayers [ 1 + offset ] = ( value & 0x40 ) ! = 0 ;
2019-02-22 20:15:55 -05:00
}
2019-02-19 01:26:48 -05:00
void Ppu : : SendFrame ( )
2019-02-17 23:26:49 -05:00
{
2019-07-11 23:30:40 -04:00
uint16_t width = _useHighResOutput ? 512 : 256 ;
uint16_t height = _useHighResOutput ? 478 : 239 ;
2021-03-10 11:13:28 -05:00
if ( ! _overscanFrame ) {
2019-03-14 23:30:47 -04:00
//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-10-16 20:22:45 -04:00
_console - > GetNotificationManager ( ) - > SendNotification ( ConsoleNotificationType : : PpuFrameDone ) ;
2019-03-12 12:06:42 -04:00
bool isRewinding = _console - > GetRewindManager ( ) - > IsRewinding ( ) ;
2020-02-07 18:23:23 -05:00
2019-07-02 19:56:00 -04:00
# ifdef LIBRETRO
_console - > GetVideoDecoder ( ) - > UpdateFrameSync ( _currentBuffer , width , height , _frameCount , isRewinding ) ;
# else
2021-03-10 11:13:28 -05:00
if ( isRewinding | | _interlacedFrame ) {
2019-03-12 12:06:42 -04:00
_console - > GetVideoDecoder ( ) - > UpdateFrameSync ( _currentBuffer , width , height , _frameCount , isRewinding ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-02-23 21:39:35 -05:00
_console - > GetVideoDecoder ( ) - > UpdateFrame ( _currentBuffer , width , height , _frameCount ) ;
}
2020-02-07 18:23:23 -05:00
# endif
2019-10-19 15:23:17 -04:00
2021-03-10 11:13:28 -05:00
if ( ! _skipRender ) {
2019-10-19 15:23:17 -04:00
_frameSkipTimer . Reset ( ) ;
}
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-10-18 19:34:55 -04:00
uint16_t * Ppu : : GetPreviousScreenBuffer ( )
{
return _currentBuffer = = _outputBuffers [ 0 ] ? _outputBuffers [ 1 ] : _outputBuffers [ 0 ] ;
}
2019-02-15 21:33:13 -05:00
uint8_t * Ppu : : GetVideoRam ( )
{
2019-07-11 23:03:02 -04:00
return ( uint8_t * ) _vram ;
2019-02-15 21:33:13 -05:00
}
uint8_t * Ppu : : GetCgRam ( )
{
2019-07-11 17:37:01 -04:00
return ( uint8_t * ) _cgram ;
2019-02-15 21:33:13 -05:00
}
uint8_t * Ppu : : GetSpriteRam ( )
{
2019-07-11 23:30:40 -04:00
return ( uint8_t * ) _oamRam ;
2019-02-15 21:33:13 -05:00
}
2019-03-02 18:00:27 -05:00
bool Ppu : : IsDoubleHeight ( )
{
2019-10-10 23:54:38 -04:00
return _state . ScreenInterlace & & ( _state . BgMode = = 5 | | _state . BgMode = = 6 ) ;
2019-03-02 18:00:27 -05:00
}
bool Ppu : : IsDoubleWidth ( )
{
2019-10-10 23:54:38 -04:00
return _state . HiResMode | | _state . BgMode = = 5 | | _state . BgMode = = 6 ;
2019-03-02 18:00:27 -05:00
}
2019-08-09 20:47:12 -04:00
void Ppu : : SetLocationLatchRequest ( uint16_t x , uint16_t y )
{
//Used by super scope
_latchRequest = true ;
_latchRequestX = x ;
_latchRequestY = y ;
}
void Ppu : : ProcessLocationLatchRequest ( )
{
//Used by super scope
2021-03-10 11:13:28 -05:00
if ( _latchRequest ) {
2019-08-09 20:47:12 -04:00
uint16_t cycle = GetCycle ( ) ;
uint16_t scanline = GetRealScanline ( ) ;
2021-03-10 11:13:28 -05:00
if ( scanline > _latchRequestY | | ( _latchRequestY = = scanline & & cycle > = _latchRequestX ) ) {
2019-08-09 20:47:12 -04:00
_latchRequest = false ;
_horizontalLocation = _latchRequestX ;
_verticalLocation = _latchRequestY ;
_locationLatched = true ;
}
}
}
2019-02-21 07:27:47 -05:00
void Ppu : : LatchLocationValues ( )
{
2019-04-10 21:38:10 -04:00
_horizontalLocation = GetCycle ( ) ;
2019-07-19 19:39:38 -04:00
_verticalLocation = GetRealScanline ( ) ;
2019-02-21 18:11:31 -05:00
_locationLatched = true ;
2019-02-21 07:27:47 -05:00
}
2019-07-10 22:50:12 -04:00
void Ppu : : UpdateOamAddress ( )
{
2019-10-10 23:54:38 -04:00
_internalOamAddress = ( _state . OamRamAddress < < 1 ) ;
2019-07-10 22:50:12 -04:00
}
uint16_t Ppu : : GetOamAddress ( )
{
2021-03-10 11:13:28 -05:00
if ( _state . ForcedVblank | | _scanline > = _vblankStartScanline ) {
2019-07-10 22:50:12 -04:00
return _internalOamAddress ;
2021-03-10 11:13:28 -05:00
} else {
if ( _memoryManager - > GetHClock ( ) < = 255 * 4 ) {
2019-07-10 22:50:12 -04:00
return _oamEvaluationIndex < < 2 ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-10 22:50:12 -04:00
return _oamTimeIndex < < 2 ;
}
}
}
2019-02-24 09:38:22 -05:00
void Ppu : : UpdateVramReadBuffer ( )
{
2019-03-01 22:24:18 -05:00
uint16_t addr = GetVramAddress ( ) ;
2019-10-10 23:54:38 -04:00
_state . VramReadBuffer = _vram [ addr ] ;
2019-03-01 22:24:18 -05:00
}
uint16_t Ppu : : GetVramAddress ( )
{
2019-10-10 23:54:38 -04:00
uint16_t addr = _state . VramAddress ;
2021-03-10 11:13:28 -05:00
switch ( _state . 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-03-01 22:24:18 -05:00
}
2019-02-24 09:38:22 -05:00
}
2019-02-13 18:44:39 -05:00
uint8_t Ppu : : Read ( uint16_t addr )
{
2021-03-10 11:13:28 -05:00
if ( _scanline < _vblankStartScanline ) {
2019-07-12 23:53:47 -04:00
RenderScanline ( ) ;
}
2021-03-10 11:13:28 -05:00
switch ( addr ) {
case 0x2134 :
_state . Ppu1OpenBus = ( ( int16_t ) _state . Mode7 . Matrix [ 0 ] * ( ( int16_t ) _state . Mode7 . Matrix [ 1 ] > > 8 ) ) & 0xFF ;
return _state . Ppu1OpenBus ;
2019-03-09 10:29:19 -05:00
2021-03-10 11:13:28 -05:00
case 0x2135 :
_state . Ppu1OpenBus = ( ( ( int16_t ) _state . Mode7 . Matrix [ 0 ] * ( ( int16_t ) _state . Mode7 . Matrix [ 1 ] > > 8 ) ) > > 8 ) & 0xFF ;
return _state . Ppu1OpenBus ;
2019-03-09 10:29:19 -05:00
2021-03-10 11:13:28 -05:00
case 0x2136 :
_state . Ppu1OpenBus = ( ( ( int16_t ) _state . Mode7 . Matrix [ 0 ] * ( ( int16_t ) _state . Mode7 . Matrix [ 1 ] > > 8 ) ) > > 16 ) & 0xFF ;
return _state . Ppu1OpenBus ;
2019-02-21 08:15:00 -05:00
2021-03-10 11:13:28 -05:00
case 0x2137 :
//SLHV - Software Latch for H/V Counter
//Latch values on read, and return open bus
if ( _regs - > GetIoPortOutput ( ) & 0x80 ) {
//Only latch H/V counters if bit 7 of $4201 is set.
LatchLocationValues ( ) ;
}
break ;
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-07-10 22:50:12 -04:00
uint16_t oamAddr = GetOamAddress ( ) ;
2019-02-19 18:41:59 -05:00
uint8_t value ;
2021-03-10 11:13:28 -05:00
if ( oamAddr < 512 ) {
2019-07-10 22:50:12 -04:00
value = _oamRam [ oamAddr ] ;
_console - > ProcessPpuRead ( oamAddr , value , SnesMemoryType : : SpriteRam ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-07-10 22:50:12 -04:00
value = _oamRam [ 0x200 | ( oamAddr & 0x1F ) ] ;
_console - > ProcessPpuRead ( 0x200 | ( oamAddr & 0x1F ) , value , SnesMemoryType : : SpriteRam ) ;
2019-02-19 18:41:59 -05:00
}
2021-03-10 11:13:28 -05:00
2019-02-19 18:41:59 -05:00
_internalOamAddress = ( _internalOamAddress + 1 ) & 0x3FF ;
2019-10-10 23:54:38 -04:00
_state . Ppu1OpenBus = value ;
2019-02-19 18:41:59 -05:00
return value ;
}
2021-03-10 11:13:28 -05:00
case 0x2139 : {
2019-02-21 07:27:47 -05:00
//VMDATALREAD - VRAM Data Read low byte
2019-10-10 23:54:38 -04:00
uint8_t returnValue = ( uint8_t ) _state . VramReadBuffer ;
2019-03-01 22:24:18 -05:00
_console - > ProcessPpuRead ( GetVramAddress ( ) , returnValue , SnesMemoryType : : VideoRam ) ;
2021-03-10 11:13:28 -05:00
if ( ! _state . VramAddrIncrementOnSecondReg ) {
2019-02-24 09:38:22 -05:00
UpdateVramReadBuffer ( ) ;
2019-10-10 23:54:38 -04:00
_state . VramAddress = ( _state . VramAddress + _state . VramIncrementValue ) & 0x7FFF ;
2019-02-21 07:27:47 -05:00
}
2019-10-10 23:54:38 -04:00
_state . Ppu1OpenBus = returnValue ;
2019-02-21 07:27:47 -05:00
return returnValue ;
}
2021-03-10 11:13:28 -05:00
case 0x213A : {
2019-02-21 07:27:47 -05:00
//VMDATAHREAD - VRAM Data Read high byte
2019-10-10 23:54:38 -04:00
uint8_t returnValue = ( uint8_t ) ( _state . VramReadBuffer > > 8 ) ;
2019-03-01 22:24:18 -05:00
_console - > ProcessPpuRead ( GetVramAddress ( ) + 1 , returnValue , SnesMemoryType : : VideoRam ) ;
2021-03-10 11:13:28 -05:00
if ( _state . VramAddrIncrementOnSecondReg ) {
2019-02-24 09:38:22 -05:00
UpdateVramReadBuffer ( ) ;
2019-10-10 23:54:38 -04:00
_state . VramAddress = ( _state . VramAddress + _state . VramIncrementValue ) & 0x7FFF ;
2019-02-21 07:27:47 -05:00
}
2019-10-10 23:54:38 -04:00
_state . Ppu1OpenBus = returnValue ;
2019-02-21 07:27:47 -05:00
return returnValue ;
}
2021-03-10 11:13:28 -05:00
case 0x213B : {
2019-02-21 07:27:47 -05:00
//CGDATAREAD - CGRAM Data read
2019-07-11 17:37:01 -04:00
uint8_t value ;
2021-03-10 11:13:28 -05:00
if ( _state . CgramAddressLatch ) {
2019-10-10 23:54:38 -04:00
value = ( ( _cgram [ _state . CgramAddress ] > > 8 ) & 0x7F ) | ( _state . Ppu2OpenBus & 0x80 ) ;
_state . CgramAddress + + ;
2021-03-10 11:13:28 -05:00
2019-10-10 23:54:38 -04:00
_console - > ProcessPpuRead ( ( _state . CgramAddress > > 1 ) + 1 , value , SnesMemoryType : : CGRam ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-10-10 23:54:38 -04:00
value = ( uint8_t ) _cgram [ _state . CgramAddress ] ;
_console - > ProcessPpuRead ( _state . CgramAddress > > 1 , value , SnesMemoryType : : CGRam ) ;
2019-03-09 10:29:19 -05:00
}
2019-10-10 23:54:38 -04:00
_state . CgramAddressLatch = ! _state . CgramAddressLatch ;
2021-03-10 11:13:28 -05:00
2019-10-10 23:54:38 -04:00
_state . Ppu2OpenBus = value ;
2019-02-21 07:27:47 -05:00
return value ;
}
2021-03-10 11:13:28 -05:00
case 0x213C : {
2019-02-21 07:27:47 -05:00
//OPHCT - Horizontal Scanline Location
2019-08-09 20:47:12 -04:00
ProcessLocationLatchRequest ( ) ;
2019-02-21 17:45:11 -05:00
uint8_t value ;
2021-03-10 11:13:28 -05:00
if ( _horizontalLocToggle ) {
2019-02-21 07:27:47 -05:00
//"Note that the value read is only 9 bits: bits 1-7 of the high byte are PPU2 Open Bus."
2019-10-10 23:54:38 -04:00
value = ( ( _horizontalLocation & 0x100 ) > > 8 ) | ( _state . Ppu2OpenBus & 0xFE ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-02-21 17:45:11 -05:00
value = _horizontalLocation & 0xFF ;
2019-02-21 07:27:47 -05:00
}
2019-10-10 23:54:38 -04:00
_state . Ppu2OpenBus = value ;
2019-02-21 17:45:11 -05:00
_horizontalLocToggle = ! _horizontalLocToggle ;
return value ;
}
2019-02-21 07:27:47 -05:00
2021-03-10 11:13:28 -05:00
case 0x213D : {
2019-02-21 07:27:47 -05:00
//OPVCT - Vertical Scanline Location
2019-08-09 20:47:12 -04:00
ProcessLocationLatchRequest ( ) ;
2019-02-21 17:45:11 -05:00
uint8_t value ;
2021-03-10 11:13:28 -05:00
if ( _verticalLocationToggle ) {
2019-02-21 07:27:47 -05:00
//"Note that the value read is only 9 bits: bits 1-7 of the high byte are PPU2 Open Bus."
2019-10-10 23:54:38 -04:00
value = ( ( _verticalLocation & 0x100 ) > > 8 ) | ( _state . Ppu2OpenBus & 0xFE ) ;
2021-03-10 11:13:28 -05:00
} else {
2019-02-21 17:45:11 -05:00
value = _verticalLocation & 0xFF ;
2019-02-21 07:27:47 -05:00
}
2019-10-10 23:54:38 -04:00
_state . Ppu2OpenBus = value ;
2019-02-21 17:45:11 -05:00
_verticalLocationToggle = ! _verticalLocationToggle ;
return value ;
}
2019-02-21 07:27:47 -05:00
2021-03-10 11:13:28 -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-10-10 23:54:38 -04:00
( _state . Ppu1OpenBus & 0x10 ) |
2019-02-21 18:11:31 -05:00
0x01 //PPU (5c77) chip version
2019-02-19 18:01:27 -05:00
) ;
2019-10-10 23:54:38 -04:00
_state . Ppu1OpenBus = value ;
2019-03-09 10:29:19 -05:00
return value ;
}
2019-02-19 18:01:27 -05:00
2021-03-10 11:13:28 -05:00
case 0x213F : {
2019-02-21 18:11:31 -05:00
//STAT78 - PPU Status Flag and Version
2019-08-09 20:47:12 -04:00
ProcessLocationLatchRequest ( ) ;
2019-02-21 18:11:31 -05:00
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-10-10 23:54:38 -04:00
( _state . Ppu2OpenBus & 0x20 ) |
2019-03-14 15:25:35 -04:00
( _console - > GetRegion ( ) = = ConsoleRegion : : Pal ? 0x10 : 0 ) |
2019-07-25 22:22:09 -04:00
0x03 //PPU (5c78) chip version
2019-02-21 18:11:31 -05:00
) ;
2021-03-10 11:13:28 -05:00
if ( _regs - > GetIoPortOutput ( ) & 0x80 ) {
2019-02-21 18:11:31 -05:00
_locationLatched = false ;
2019-03-01 22:24:18 -05:00
2020-10-11 14:33:50 -04:00
//"The high/low selector is reset to ?elow?f when $213F is read" (the selector is NOT reset when the counter is latched)
2019-03-01 22:24:18 -05:00
_horizontalLocToggle = false ;
_verticalLocationToggle = false ;
2019-02-21 18:11:31 -05:00
}
2019-10-10 23:54:38 -04:00
_state . Ppu2OpenBus = value ;
2019-02-21 18:11:31 -05:00
return value ;
}
2021-03-10 11:13:28 -05:00
default :
LogDebug ( " [Debug] Unimplemented register read: " + HexUtilities : : ToHex ( addr ) ) ;
break ;
2019-02-13 18:44:39 -05:00
}
2021-03-10 11:13:28 -05:00
2019-03-09 10:29:19 -05:00
uint16_t reg = addr & 0x210F ;
2021-03-10 11:13:28 -05:00
if ( ( reg > = 0x2104 & & reg < = 0x2106 ) | | ( reg > = 0x2108 & & reg < = 0x210A ) ) {
2019-03-09 10:29:19 -05:00
//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.
2019-10-10 23:54:38 -04:00
return _state . Ppu1OpenBus ;
2019-03-09 10:29:19 -05:00
}
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 )
{
2021-03-10 11:13:28 -05:00
if ( _scanline < _vblankStartScanline ) {
2019-03-04 17:49:14 -05:00
RenderScanline ( ) ;
}
2021-03-10 11:13:28 -05:00
switch ( addr ) {
case 0x2100 :
if ( _state . ForcedVblank & & _scanline = = _nmiScanline ) {
//"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."
UpdateOamAddress ( ) ;
}
_state . ForcedVblank = ( value & 0x80 ) ! = 0 ;
_state . ScreenBrightness = value & 0x0F ;
break ;
case 0x2101 :
_state . OamMode = ( value & 0xE0 ) > > 5 ;
_state . OamBaseAddress = ( value & 0x07 ) < < 13 ;
_state . OamAddressOffset = ( ( ( value & 0x18 ) > > 3 ) + 1 ) < < 12 ;
break ;
case 0x2102 :
_state . OamRamAddress = ( _state . OamRamAddress & 0x100 ) | value ;
2019-07-10 22:50:12 -04:00
UpdateOamAddress ( ) ;
2021-03-10 11:13:28 -05:00
break ;
2019-02-17 19:54:29 -05:00
2021-03-10 11:13:28 -05:00
case 0x2103 :
_state . OamRamAddress = ( _state . OamRamAddress & 0xFF ) | ( ( value & 0x01 ) < < 8 ) ;
UpdateOamAddress ( ) ;
_state . EnableOamPriority = ( value & 0x80 ) ! = 0 ;
break ;
case 0x2104 : {
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
//This is approximated by _oamRenderAddress (but is not cycle accurate) - needed for Uniracers
2019-07-10 22:50:12 -04:00
uint16_t oamAddr = GetOamAddress ( ) ;
2021-03-10 11:13:28 -05:00
if ( oamAddr < 512 ) {
if ( oamAddr & 0x01 ) {
2019-07-10 22:50:12 -04:00
_console - > ProcessPpuWrite ( oamAddr - 1 , _oamWriteBuffer , SnesMemoryType : : SpriteRam ) ;
_oamRam [ oamAddr - 1 ] = _oamWriteBuffer ;
2021-03-10 11:13:28 -05:00
2019-07-10 22:50:12 -04:00
_console - > ProcessPpuWrite ( oamAddr , value , SnesMemoryType : : SpriteRam ) ;
_oamRam [ oamAddr ] = value ;
2021-03-10 11:13:28 -05:00
} else {
2019-02-17 21:09:33 -05:00
_oamWriteBuffer = value ;
}
2021-03-10 11:13:28 -05:00
}
2019-07-10 22:50:12 -04:00
2021-03-10 11:13:28 -05:00
if ( ! _state . ForcedVblank & & _scanline < _nmiScanline ) {
2019-07-10 22:50:12 -04:00
//During rendering the high table is also written to when writing to OAM
oamAddr = 0x200 | ( ( oamAddr & 0x1F0 ) > > 4 ) ;
}
2021-03-10 11:13:28 -05:00
if ( oamAddr > = 512 ) {
2019-07-10 22:50:12 -04:00
uint16_t address = 0x200 | ( oamAddr & 0x1F ) ;
2021-03-10 11:13:28 -05:00
if ( ( oamAddr & 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
}
2021-03-10 11:13:28 -05:00
case 0x2105 :
if ( _state . BgMode ! = ( value & 0x07 ) ) {
LogDebug ( " [Debug] Entering mode: " + std : : to_string ( value & 0x07 ) + " (SL: " + std : : to_string ( _scanline ) + " ) " ) ;
}
_state . BgMode = value & 0x07 ;
ConvertToHiRes ( ) ;
2020-02-29 19:36:17 -05:00
2021-03-10 11:13:28 -05:00
_state . Mode1Bg3Priority = ( value & 0x08 ) ! = 0 ;
2019-02-17 01:09:47 -05:00
2021-03-10 11:13:28 -05:00
_state . Layers [ 0 ] . LargeTiles = ( value & 0x10 ) ! = 0 ;
_state . Layers [ 1 ] . LargeTiles = ( value & 0x20 ) ! = 0 ;
_state . Layers [ 2 ] . LargeTiles = ( value & 0x40 ) ! = 0 ;
_state . Layers [ 3 ] . LargeTiles = ( value & 0x80 ) ! = 0 ;
break ;
2019-02-17 00:32:41 -05:00
2021-03-10 11:13:28 -05:00
case 0x2106 : {
2019-02-20 17:39:14 -05:00
//MOSAIC - Screen Pixelation
2019-10-10 23:54:38 -04:00
_state . MosaicSize = ( ( value & 0xF0 ) > > 4 ) + 1 ;
2020-01-11 20:52:30 -05:00
uint8_t mosaicEnabled = value & 0x0F ;
2021-03-10 11:13:28 -05:00
if ( ! _state . MosaicEnabled & & mosaicEnabled ) {
2020-10-11 14:33:50 -04:00
//"If this register is set during the frame, the ?starting scanline is the current scanline, otherwise it is the first visible scanline of the frame."
2020-01-11 20:52:30 -05:00
//This is only done when mosaic is turned on from an off state (FF6 mosaic effect looks wrong otherwise)
//FF6's mosaic effect is broken on some screens without this.
_mosaicScanlineCounter = _state . MosaicSize + 1 ;
}
_state . MosaicEnabled = mosaicEnabled ;
2019-02-20 17:39:14 -05:00
break ;
2019-07-10 23:13:06 -04:00
}
2019-02-20 17:39:14 -05:00
2021-03-10 11:13:28 -05:00
case 0x2107 : case 0x2108 : case 0x2109 : case 0x210A :
//BG 1-4 Tilemap Address and Size (BG1SC, BG2SC, BG3SC, BG4SC)
_state . Layers [ addr - 0x2107 ] . TilemapAddress = ( value & 0x7C ) < < 8 ;
_state . Layers [ addr - 0x2107 ] . DoubleWidth = ( value & 0x01 ) ! = 0 ;
_state . Layers [ addr - 0x2107 ] . DoubleHeight = ( value & 0x02 ) ! = 0 ;
break ;
case 0x210B : case 0x210C :
//BG1+2 / BG3+4 Chr Address (BG12NBA / BG34NBA)
_state . Layers [ ( addr - 0x210B ) * 2 ] . ChrAddress = ( value & 0x07 ) < < 12 ;
_state . Layers [ ( addr - 0x210B ) * 2 + 1 ] . ChrAddress = ( value & 0x70 ) < < 8 ;
2019-02-13 18:44:39 -05:00
break ;
2021-03-10 11:13:28 -05:00
case 0x210D :
//M7HOFS - Mode 7 BG Horizontal Scroll
//BG1HOFS - BG1 Horizontal Scroll
_state . Mode7 . HScroll = ( ( value < < 8 ) | ( _state . Mode7 . ValueLatch ) ) & 0x1FFF ;
_state . Mode7 . ValueLatch = value ;
//no break, keep executing to set the matching BG1 HScroll register, too
case 0x210F : case 0x2111 : case 0x2113 :
//BGXHOFS - BG1/2/3/4 Horizontal Scroll
_state . Layers [ ( addr - 0x210D ) > > 1 ] . HScroll = ( ( value < < 8 ) | ( _hvScrollLatchValue & ~ 0x07 ) | ( _hScrollLatchValue & 0x07 ) ) & 0x3FF ;
_hvScrollLatchValue = value ;
_hScrollLatchValue = value ;
2019-02-19 17:23:21 -05:00
break ;
2021-03-10 11:13:28 -05:00
case 0x210E :
//M7VOFS - Mode 7 BG Vertical Scroll
//BG1VOFS - BG1 Vertical Scroll
_state . Mode7 . VScroll = ( ( value < < 8 ) | ( _state . Mode7 . ValueLatch ) ) & 0x1FFF ;
_state . Mode7 . ValueLatch = value ;
//no break, keep executing to set the matching BG1 HScroll register, too
case 0x2110 : case 0x2112 : case 0x2114 :
//BGXVOFS - BG1/2/3/4 Vertical Scroll
_state . Layers [ ( addr - 0x210E ) > > 1 ] . VScroll = ( ( value < < 8 ) | _hvScrollLatchValue ) & 0x3FF ;
_hvScrollLatchValue = value ;
2019-02-19 01:26:48 -05:00
break ;
2019-02-22 20:15:55 -05:00
2021-03-10 11:13:28 -05:00
case 0x2115 :
//VMAIN - Video Port Control
switch ( value & 0x03 ) {
case 0 : _state . VramIncrementValue = 1 ; break ;
case 1 : _state . VramIncrementValue = 32 ; break ;
case 2 :
case 3 : _state . VramIncrementValue = 128 ; break ;
}
2019-02-22 20:15:55 -05:00
2021-03-10 11:13:28 -05:00
_state . VramAddressRemapping = ( value & 0x0C ) > > 2 ;
_state . VramAddrIncrementOnSecondReg = ( value & 0x80 ) ! = 0 ;
break ;
2019-02-17 23:53:19 -05:00
2021-03-10 11:13:28 -05:00
case 0x2116 :
//VMADDL - VRAM Address low byte
_state . VramAddress = ( _state . VramAddress & 0x7F00 ) | value ;
UpdateVramReadBuffer ( ) ;
break ;
2019-02-17 23:26:49 -05:00
2021-03-10 11:13:28 -05:00
case 0x2117 :
//VMADDH - VRAM Address high byte
_state . VramAddress = ( _state . VramAddress & 0x00FF ) | ( ( value & 0x7F ) < < 8 ) ;
UpdateVramReadBuffer ( ) ;
break ;
2019-02-19 23:35:43 -05:00
2021-03-10 11:13:28 -05:00
case 0x2118 :
//VMDATAL - VRAM Data Write low byte
if ( _scanline > = _nmiScanline | | _state . 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 ( ) ] = value | ( _vram [ GetVramAddress ( ) ] & 0xFF00 ) ;
}
//The VRAM address is incremented even outside of vblank/forced blank
if ( ! _state . VramAddrIncrementOnSecondReg ) {
_state . VramAddress = ( _state . VramAddress + _state . VramIncrementValue ) & 0x7FFF ;
}
break ;
case 0x2119 :
//VMDATAH - VRAM Data Write high byte
if ( _scanline > = _nmiScanline | | _state . 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 ( ) ] = ( value < < 8 ) | ( _vram [ GetVramAddress ( ) ] & 0xFF ) ;
}
//The VRAM address is incremented even outside of vblank/forced blank
if ( _state . VramAddrIncrementOnSecondReg ) {
_state . VramAddress = ( _state . VramAddress + _state . VramIncrementValue ) & 0x7FFF ;
}
break ;
case 0x211A :
//M7SEL - Mode 7 Settings
_state . Mode7 . LargeMap = ( value & 0x80 ) ! = 0 ;
_state . Mode7 . FillWithTile0 = ( value & 0x40 ) ! = 0 ;
_state . Mode7 . HorizontalMirroring = ( value & 0x01 ) ! = 0 ;
_state . 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)
_state . Mode7 . Matrix [ addr - 0x211B ] = ( value < < 8 ) | _state . Mode7 . ValueLatch ;
_state . Mode7 . ValueLatch = value ;
break ;
case 0x211F :
//M7X - Mode 7 Center X
_state . Mode7 . CenterX = ( ( value < < 8 ) | _state . Mode7 . ValueLatch ) ;
_state . Mode7 . ValueLatch = value ;
break ;
case 0x2120 :
//M7Y - Mode 7 Center Y
_state . Mode7 . CenterY = ( ( value < < 8 ) | _state . Mode7 . ValueLatch ) ;
_state . Mode7 . ValueLatch = value ;
break ;
case 0x2121 :
//CGRAM Address(CGADD)
_state . CgramAddress = value ;
_state . CgramAddressLatch = false ;
break ;
case 0x2122 :
//CGRAM Data write (CGDATA)
if ( _state . CgramAddressLatch ) {
//MSB ignores the 7th bit (colors are 15-bit only)
_console - > ProcessPpuWrite ( _state . CgramAddress > > 1 , _state . CgramWriteBuffer , SnesMemoryType : : CGRam ) ;
_console - > ProcessPpuWrite ( ( _state . CgramAddress > > 1 ) + 1 , value & 0x7F , SnesMemoryType : : CGRam ) ;
_cgram [ _state . CgramAddress ] = _state . CgramWriteBuffer | ( ( value & 0x7F ) < < 8 ) ;
_state . CgramAddress + + ;
} else {
_state . CgramWriteBuffer = value ;
}
_state . CgramAddressLatch = ! _state . CgramAddressLatch ;
break ;
case 0x2123 :
//W12SEL - Window Mask Settings for BG1 and BG2
ProcessWindowMaskSettings ( value , 0 ) ;
break ;
case 0x2124 :
//W34SEL - Window Mask Settings for BG3 and BG4
ProcessWindowMaskSettings ( value , 2 ) ;
break ;
case 0x2125 :
//WOBJSEL - Window Mask Settings for OBJ and Color Window
ProcessWindowMaskSettings ( value , 4 ) ;
break ;
case 0x2126 :
//WH0 - Window 1 Left Position
_state . Window [ 0 ] . Left = value ;
break ;
case 0x2127 :
//WH1 - Window 1 Right Position
_state . Window [ 0 ] . Right = value ;
break ;
case 0x2128 :
//WH2 - Window 2 Left Position
_state . Window [ 1 ] . Left = value ;
break ;
case 0x2129 :
//WH3 - Window 2 Right Position
_state . Window [ 1 ] . Right = value ;
break ;
case 0x212A :
//WBGLOG - Window mask logic for BG
_state . MaskLogic [ 0 ] = ( WindowMaskLogic ) ( value & 0x03 ) ;
_state . MaskLogic [ 1 ] = ( WindowMaskLogic ) ( ( value > > 2 ) & 0x03 ) ;
_state . MaskLogic [ 2 ] = ( WindowMaskLogic ) ( ( value > > 4 ) & 0x03 ) ;
_state . MaskLogic [ 3 ] = ( WindowMaskLogic ) ( ( value > > 6 ) & 0x03 ) ;
break ;
case 0x212B :
//WOBJLOG - Window mask logic for OBJs and Color Window
_state . MaskLogic [ 4 ] = ( WindowMaskLogic ) ( ( value > > 0 ) & 0x03 ) ;
_state . MaskLogic [ 5 ] = ( WindowMaskLogic ) ( ( value > > 2 ) & 0x03 ) ;
break ;
case 0x212C :
//TM - Main Screen Designation
_state . MainScreenLayers = value & 0x1F ;
break ;
case 0x212D :
//TS - Subscreen Designation
_state . SubScreenLayers = value & 0x1F ;
break ;
case 0x212E :
//TMW - Window Mask Designation for the Main Screen
for ( int i = 0 ; i < 5 ; i + + ) {
_state . WindowMaskMain [ i ] = ( ( value > > i ) & 0x01 ) ! = 0 ;
}
break ;
case 0x212F :
//TSW - Window Mask Designation for the Subscreen
for ( int i = 0 ; i < 5 ; i + + ) {
_state . WindowMaskSub [ i ] = ( ( value > > i ) & 0x01 ) ! = 0 ;
}
break ;
case 0x2130 :
//CGWSEL - Color Addition Select
_state . ColorMathClipMode = ( ColorWindowMode ) ( ( value > > 6 ) & 0x03 ) ;
_state . ColorMathPreventMode = ( ColorWindowMode ) ( ( value > > 4 ) & 0x03 ) ;
_state . ColorMathAddSubscreen = ( value & 0x02 ) ! = 0 ;
_state . DirectColorMode = ( value & 0x01 ) ! = 0 ;
break ;
case 0x2131 :
//CGADSUB - Color math designation
_state . ColorMathEnabled = value & 0x3F ;
_state . ColorMathSubstractMode = ( value & 0x80 ) ! = 0 ;
_state . ColorMathHalveResult = ( value & 0x40 ) ! = 0 ;
break ;
case 0x2132 :
//COLDATA - Fixed Color Data
if ( value & 0x80 ) { //B
_state . FixedColor = ( _state . FixedColor & ~ 0x7C00 ) | ( ( value & 0x1F ) < < 10 ) ;
}
if ( value & 0x40 ) { //G
_state . FixedColor = ( _state . FixedColor & ~ 0x3E0 ) | ( ( value & 0x1F ) < < 5 ) ;
}
if ( value & 0x20 ) { //R
_state . FixedColor = ( _state . FixedColor & ~ 0x1F ) | ( value & 0x1F ) ;
}
break ;
case 0x2133 : {
2019-02-23 16:04:04 -05:00
//SETINI - Screen Mode/Video Select
//_externalSync = (value & 0x80) != 0; //NOT USED
2019-10-10 23:54:38 -04:00
_state . ExtBgEnabled = ( value & 0x40 ) ! = 0 ;
_state . HiResMode = ( value & 0x08 ) ! = 0 ;
_state . OverscanMode = ( value & 0x04 ) ! = 0 ;
_state . ObjInterlace = ( value & 0x02 ) ! = 0 ;
2020-01-26 10:34:07 -05:00
bool interlace = ( value & 0x01 ) ! = 0 ;
2021-03-10 11:13:28 -05:00
if ( _state . ScreenInterlace ! = interlace ) {
2020-01-26 10:34:07 -05:00
_state . ScreenInterlace = interlace ;
2021-03-10 11:13:28 -05:00
if ( _scanline > = _vblankStartScanline & & interlace ) {
2020-01-26 10:34:07 -05:00
//Clear buffer when turning on interlace mode during vblank
memset ( GetPreviousScreenBuffer ( ) , 0 , 512 * 478 * sizeof ( uint16_t ) ) ;
}
}
2020-02-29 19:36:17 -05:00
ConvertToHiRes ( ) ;
2019-02-23 16:04:04 -05:00
break ;
2020-01-26 10:34:07 -05:00
}
2019-02-23 16:04:04 -05:00
2021-03-10 11:13:28 -05:00
default :
LogDebug ( " [Debug] Unimplemented register write: " + HexUtilities : : ToHex ( addr ) + " = " + HexUtilities : : ToHex ( value ) ) ;
break ;
2019-02-13 18:44:39 -05:00
}
}
2019-02-24 11:14:24 -05:00
2021-03-10 11:13:28 -05:00
void Ppu : : Serialize ( Serializer & s )
2019-03-12 09:15:57 -04:00
{
2019-07-10 22:50:12 -04:00
uint16_t unused_oamRenderAddress = 0 ;
2019-03-12 09:15:57 -04:00
s . Stream (
2019-10-10 23:54:38 -04:00
_state . ForcedVblank , _state . ScreenBrightness , _scanline , _frameCount , _drawStartX , _drawEndX , _state . BgMode ,
2021-03-10 11:13:28 -05:00
_state . Mode1Bg3Priority , _state . MainScreenLayers , _state . SubScreenLayers , _state . VramAddress , _state . VramIncrementValue , _state . VramAddressRemapping ,
_state . VramAddrIncrementOnSecondReg , _state . VramReadBuffer , _state . Ppu1OpenBus , _state . Ppu2OpenBus , _state . CgramAddress , _state . MosaicSize , _state . MosaicEnabled ,
_mosaicScanlineCounter , _state . OamMode , _state . OamBaseAddress , _state . OamAddressOffset , _state . OamRamAddress , _state . EnableOamPriority ,
_internalOamAddress , _oamWriteBuffer , _timeOver , _rangeOver , _state . HiResMode , _state . ScreenInterlace , _state . ObjInterlace ,
_state . OverscanMode , _state . DirectColorMode , _state . ColorMathClipMode , _state . ColorMathPreventMode , _state . ColorMathAddSubscreen , _state . ColorMathEnabled ,
_state . ColorMathSubstractMode , _state . ColorMathHalveResult , _state . FixedColor , _hvScrollLatchValue , _hScrollLatchValue ,
2019-03-12 09:15:57 -04:00
_horizontalLocation , _horizontalLocToggle , _verticalLocation , _verticalLocationToggle , _locationLatched ,
2021-03-10 11:13:28 -05:00
_state . MaskLogic [ 0 ] , _state . MaskLogic [ 1 ] , _state . MaskLogic [ 2 ] , _state . MaskLogic [ 3 ] , _state . MaskLogic [ 4 ] , _state . MaskLogic [ 5 ] ,
_state . WindowMaskMain [ 0 ] , _state . WindowMaskMain [ 1 ] , _state . WindowMaskMain [ 2 ] , _state . WindowMaskMain [ 3 ] , _state . WindowMaskMain [ 4 ] ,
_state . WindowMaskSub [ 0 ] , _state . WindowMaskSub [ 1 ] , _state . WindowMaskSub [ 2 ] , _state . WindowMaskSub [ 3 ] , _state . WindowMaskSub [ 4 ] ,
_state . Mode7 . CenterX , _state . Mode7 . CenterY , _state . ExtBgEnabled , _state . Mode7 . FillWithTile0 , _state . Mode7 . HorizontalMirroring ,
_state . Mode7 . HScroll , _state . Mode7 . LargeMap , _state . Mode7 . Matrix [ 0 ] , _state . Mode7 . Matrix [ 1 ] , _state . Mode7 . Matrix [ 2 ] , _state . Mode7 . Matrix [ 3 ] ,
_state . Mode7 . ValueLatch , _state . Mode7 . VerticalMirroring , _state . Mode7 . VScroll , unused_oamRenderAddress , _oddFrame , _vblankStartScanline ,
_state . CgramAddressLatch , _state . CgramWriteBuffer , _nmiScanline , _vblankEndScanline , _adjustedVblankEndScanline , _baseVblankEndScanline ,
2019-07-19 19:59:09 -04:00
_overclockEnabled
2019-03-12 09:15:57 -04:00
) ;
2021-03-10 11:13:28 -05:00
for ( int i = 0 ; i < 4 ; i + + ) {
2019-03-12 09:15:57 -04:00
s . Stream (
2021-03-10 11:13:28 -05:00
_state . Layers [ i ] . ChrAddress , _state . Layers [ i ] . DoubleHeight , _state . Layers [ i ] . DoubleWidth , _state . Layers [ i ] . HScroll ,
2019-10-10 23:54:38 -04:00
_state . Layers [ i ] . LargeTiles , _state . Layers [ i ] . TilemapAddress , _state . Layers [ i ] . VScroll
2019-03-12 09:15:57 -04:00
) ;
}
2021-03-10 11:13:28 -05:00
for ( int i = 0 ; i < 2 ; i + + ) {
2019-03-12 09:15:57 -04:00
s . Stream (
2021-03-10 11:13:28 -05:00
_state . Window [ i ] . ActiveLayers [ 0 ] , _state . Window [ i ] . ActiveLayers [ 1 ] , _state . Window [ i ] . ActiveLayers [ 2 ] , _state . Window [ i ] . ActiveLayers [ 3 ] , _state . Window [ i ] . ActiveLayers [ 4 ] , _state . Window [ i ] . ActiveLayers [ 5 ] ,
_state . Window [ i ] . InvertedLayers [ 0 ] , _state . Window [ i ] . InvertedLayers [ 1 ] , _state . Window [ i ] . InvertedLayers [ 2 ] , _state . Window [ i ] . InvertedLayers [ 3 ] , _state . Window [ i ] . InvertedLayers [ 4 ] , _state . Window [ i ] . InvertedLayers [ 5 ] ,
2019-10-10 23:54:38 -04:00
_state . Window [ i ] . Left , _state . Window [ i ] . Right
2019-03-12 09:15:57 -04:00
) ;
}
2019-07-11 23:03:02 -04:00
s . StreamArray ( _vram , Ppu : : VideoRamSize > > 1 ) ;
s . StreamArray ( _oamRam , Ppu : : SpriteRamSize ) ;
2019-07-11 17:37:01 -04:00
s . StreamArray ( _cgram , Ppu : : CgRamSize > > 1 ) ;
2021-03-10 11:13:28 -05:00
for ( int i = 0 ; i < 4 ; i + + ) {
for ( int j = 0 ; j < 33 ; j + + ) {
2019-07-07 18:18:20 -04:00
s . Stream (
2021-03-10 11:13:28 -05:00
_layerData [ i ] . Tiles [ j ] . ChrData [ 0 ] , _layerData [ i ] . Tiles [ j ] . ChrData [ 1 ] , _layerData [ i ] . Tiles [ j ] . ChrData [ 2 ] , _layerData [ i ] . Tiles [ j ] . ChrData [ 3 ] ,
2019-07-07 18:18:20 -04:00
_layerData [ i ] . Tiles [ j ] . TilemapData , _layerData [ i ] . Tiles [ j ] . VScroll
) ;
}
}
2019-07-10 22:50:12 -04:00
s . Stream ( _hOffset , _vOffset , _fetchBgStart , _fetchBgEnd , _fetchSpriteStart , _fetchSpriteEnd ) ;
2019-03-12 09:15:57 -04:00
}
2019-12-11 21:44:42 -05:00
void Ppu : : RandomizeState ( )
{
_state . ScreenBrightness = _settings - > GetRandomValue ( 0x0F ) ;
_state . Mode7 . CenterX = _settings - > GetRandomValue ( 0xFFFF ) ;
_state . Mode7 . CenterY = _settings - > GetRandomValue ( 0xFFFF ) ;
_state . Mode7 . FillWithTile0 = _settings - > GetRandomBool ( ) ;
_state . Mode7 . HorizontalMirroring = _settings - > GetRandomBool ( ) ;
_state . Mode7 . HScroll = _settings - > GetRandomValue ( 0x1FFF ) ;
_state . Mode7 . HScrollLatch = _settings - > GetRandomValue ( 0x1FFF ) ;
_state . Mode7 . LargeMap = _settings - > GetRandomBool ( ) ;
_state . Mode7 . Matrix [ 0 ] = _settings - > GetRandomValue ( 0xFFFF ) ;
_state . Mode7 . Matrix [ 1 ] = _settings - > GetRandomValue ( 0xFFFF ) ;
_state . Mode7 . Matrix [ 2 ] = _settings - > GetRandomValue ( 0xFFFF ) ;
_state . Mode7 . Matrix [ 3 ] = _settings - > GetRandomValue ( 0xFFFF ) ;
_state . Mode7 . ValueLatch = _settings - > GetRandomValue ( 0xFF ) ;
_state . Mode7 . VerticalMirroring = _settings - > GetRandomBool ( ) ;
_state . Mode7 . VScroll = _settings - > GetRandomValue ( 0x1FFF ) ;
_state . Mode7 . VScrollLatch = _settings - > GetRandomValue ( 0x1FFF ) ;
_state . BgMode = _settings - > GetRandomValue ( 7 ) ;
_state . Mode1Bg3Priority = _settings - > GetRandomBool ( ) ;
_state . MainScreenLayers = _settings - > GetRandomValue ( 0x1F ) ;
_state . SubScreenLayers = _settings - > GetRandomValue ( 0x1F ) ;
2021-03-10 11:13:28 -05:00
for ( int i = 0 ; i < 4 ; i + + ) {
2019-12-11 21:44:42 -05:00
_state . Layers [ i ] . TilemapAddress = _settings - > GetRandomValue ( 0x1F ) < < 10 ;
_state . Layers [ i ] . ChrAddress = _settings - > GetRandomValue ( 0x07 ) < < 12 ;
_state . Layers [ i ] . HScroll = _settings - > GetRandomValue ( 0x1FFF ) ;
_state . Layers [ i ] . VScroll = _settings - > GetRandomValue ( 0x1FFF ) ;
_state . Layers [ i ] . DoubleWidth = _settings - > GetRandomBool ( ) ;
_state . Layers [ i ] . DoubleHeight = _settings - > GetRandomBool ( ) ;
_state . Layers [ i ] . LargeTiles = _settings - > GetRandomBool ( ) ;
}
2021-03-10 11:13:28 -05:00
for ( int i = 0 ; i < 2 ; i + + ) {
2019-12-11 21:44:42 -05:00
_state . Window [ i ] . Left = _settings - > GetRandomValue ( 0xFF ) ;
_state . Window [ i ] . Right = _settings - > GetRandomValue ( 0xFF ) ;
2021-03-10 11:13:28 -05:00
for ( int j = 0 ; j < 6 ; j + + ) {
2019-12-11 21:44:42 -05:00
_state . Window [ i ] . ActiveLayers [ j ] = _settings - > GetRandomBool ( ) ;
_state . Window [ i ] . InvertedLayers [ j ] = _settings - > GetRandomBool ( ) ;
}
}
2021-03-10 11:13:28 -05:00
for ( int i = 0 ; i < 6 ; i + + ) {
2019-12-11 21:44:42 -05:00
_state . MaskLogic [ i ] = ( WindowMaskLogic ) _settings - > GetRandomValue ( 3 ) ;
}
2021-03-10 11:13:28 -05:00
for ( int i = 0 ; i < 5 ; i + + ) {
2019-12-11 21:44:42 -05:00
_state . WindowMaskMain [ i ] = _settings - > GetRandomBool ( ) ;
_state . WindowMaskSub [ i ] = _settings - > GetRandomBool ( ) ;
}
_state . VramAddress = _settings - > GetRandomValue ( 0x7FFF ) ;
2021-03-10 11:13:28 -05:00
switch ( _settings - > GetRandomValue ( 0x03 ) ) {
case 0 : _state . VramIncrementValue = 1 ; break ;
case 1 : _state . VramIncrementValue = 32 ; break ;
case 2 : case 3 : _state . VramIncrementValue = 128 ; break ;
2019-12-11 21:44:42 -05:00
}
_state . VramAddressRemapping = _settings - > GetRandomValue ( 0x03 ) ;
_state . VramAddrIncrementOnSecondReg = _settings - > GetRandomBool ( ) ;
_state . VramReadBuffer = _settings - > GetRandomValue ( 0xFFFF ) ;
_state . Ppu1OpenBus = _settings - > GetRandomValue ( 0xFF ) ;
_state . Ppu2OpenBus = _settings - > GetRandomValue ( 0xFF ) ;
_state . CgramAddress = _settings - > GetRandomValue ( 0xFF ) ;
_state . CgramWriteBuffer = _settings - > GetRandomValue ( 0xFF ) ;
_state . CgramAddressLatch = _settings - > GetRandomBool ( ) ;
_state . MosaicSize = _settings - > GetRandomValue ( 0x0F ) + 1 ;
_state . MosaicEnabled = _settings - > GetRandomValue ( 0x0F ) ;
_state . OamRamAddress = _settings - > GetRandomValue ( 0x1FF ) ;
_state . OamMode = _settings - > GetRandomValue ( 0x07 ) ;
_state . OamBaseAddress = _settings - > GetRandomValue ( 0x07 ) < < 13 ;
_state . OamAddressOffset = ( _settings - > GetRandomValue ( 0x03 ) + 1 ) < < 12 ;
_state . EnableOamPriority = _settings - > GetRandomBool ( ) ;
_state . ExtBgEnabled = _settings - > GetRandomBool ( ) ;
_state . HiResMode = _settings - > GetRandomBool ( ) ;
_state . ScreenInterlace = _settings - > GetRandomBool ( ) ;
_state . ObjInterlace = _settings - > GetRandomBool ( ) ;
_state . OverscanMode = _settings - > GetRandomBool ( ) ;
_state . DirectColorMode = _settings - > GetRandomBool ( ) ;
_state . ColorMathClipMode = ( ColorWindowMode ) _settings - > GetRandomValue ( 3 ) ;
_state . ColorMathPreventMode = ( ColorWindowMode ) _settings - > GetRandomValue ( 3 ) ;
_state . ColorMathAddSubscreen = _settings - > GetRandomBool ( ) ;
_state . ColorMathEnabled = _settings - > GetRandomValue ( 0x3F ) ;
_state . ColorMathSubstractMode = _settings - > GetRandomBool ( ) ;
_state . ColorMathHalveResult = _settings - > GetRandomBool ( ) ;
_state . FixedColor = _settings - > GetRandomValue ( 0x7FFF ) ;
}
2019-02-24 11:14:24 -05:00
/* Everything below this point is used to select the proper arguments for templates */
2021-03-10 11:13:28 -05:00
template < uint8_t layerIndex , uint8_t bpp , uint8_t normalPriority , uint8_t highPriority , uint16_t basePaletteOffset , bool hiResMode , bool applyMosaic >
2019-02-24 11:14:24 -05:00
void Ppu : : RenderTilemap ( )
{
2021-03-10 11:13:28 -05:00
if ( _state . DirectColorMode ) {
2020-01-11 17:40:42 -05:00
RenderTilemap < layerIndex , bpp , normalPriority , highPriority , basePaletteOffset , hiResMode , applyMosaic , true > ( ) ;
2021-03-10 11:13:28 -05:00
} else {
2020-01-11 17:40:42 -05:00
RenderTilemap < layerIndex , bpp , normalPriority , highPriority , basePaletteOffset , hiResMode , applyMosaic , false > ( ) ;
2019-02-24 11:14:24 -05:00
}
}
2021-03-10 11:13:28 -05:00
template < uint8_t layerIndex , uint8_t bpp , uint8_t normalPriority , uint8_t highPriority , uint16_t basePaletteOffset , bool hiResMode >
2019-02-24 11:14:24 -05:00
void Ppu : : RenderTilemap ( )
{
2021-03-10 11:13:28 -05:00
bool applyMosaic = ( ( _state . MosaicEnabled > > layerIndex ) & 0x01 ) ! = 0 & & ( _state . MosaicSize > 1 | | _state . BgMode = = 5 | | _state . BgMode = = 6 ) ;
2019-02-24 11:14:24 -05:00
2021-03-10 11:13:28 -05:00
if ( applyMosaic ) {
2020-01-11 17:40:42 -05:00
RenderTilemap < layerIndex , bpp , normalPriority , highPriority , basePaletteOffset , hiResMode , true > ( ) ;
2021-03-10 11:13:28 -05:00
} else {
2020-01-11 17:40:42 -05:00
RenderTilemap < layerIndex , bpp , normalPriority , highPriority , basePaletteOffset , hiResMode , false > ( ) ;
2019-02-24 11:14:24 -05:00
}
}
2021-03-10 11:13:28 -05:00
template < uint8_t layerIndex , uint8_t bpp , uint8_t normalPriority , uint8_t highPriority , uint16_t basePaletteOffset >
2019-02-24 11:14:24 -05:00
void Ppu : : RenderTilemap ( )
{
2021-03-10 11:13:28 -05:00
if ( ! IsRenderRequired ( layerIndex ) ) {
2019-07-12 08:33:09 -04:00
return ;
}
2021-03-10 11:13:28 -05:00
if ( _state . BgMode = = 5 | | _state . BgMode = = 6 ) {
2020-01-11 17:40:42 -05:00
RenderTilemap < layerIndex , bpp , normalPriority , highPriority , basePaletteOffset , true > ( ) ;
2021-03-10 11:13:28 -05:00
} else {
2020-01-11 17:40:42 -05:00
RenderTilemap < layerIndex , bpp , normalPriority , highPriority , basePaletteOffset , false > ( ) ;
2019-02-24 11:14:24 -05:00
}
}
2021-03-10 11:13:28 -05:00
template < uint8_t layerIndex , uint8_t normalPriority , uint8_t highPriority >
2019-02-24 11:14:24 -05:00
void Ppu : : RenderTilemapMode7 ( )
{
2021-03-10 11:13:28 -05:00
if ( ! IsRenderRequired ( layerIndex ) ) {
2020-01-11 17:40:42 -05:00
return ;
}
2019-10-10 23:54:38 -04:00
bool applyMosaic = ( ( _state . MosaicEnabled > > layerIndex ) & 0x01 ) ! = 0 ;
2019-02-24 11:14:24 -05:00
2021-03-10 11:13:28 -05:00
if ( applyMosaic ) {
2020-01-11 17:40:42 -05:00
RenderTilemapMode7 < layerIndex , normalPriority , highPriority , true > ( ) ;
2021-03-10 11:13:28 -05:00
} else {
2020-01-11 17:40:42 -05:00
RenderTilemapMode7 < layerIndex , normalPriority , highPriority , false > ( ) ;
2019-02-24 11:14:24 -05:00
}
}
2021-03-10 11:13:28 -05:00
template < uint8_t layerIndex , uint8_t normalPriority , uint8_t highPriority , bool applyMosaic >
2019-02-24 11:14:24 -05:00
void Ppu : : RenderTilemapMode7 ( )
{
2021-03-10 11:13:28 -05:00
if ( _state . DirectColorMode ) {
2020-01-11 17:40:42 -05:00
RenderTilemapMode7 < layerIndex , normalPriority , highPriority , applyMosaic , true > ( ) ;
2021-03-10 11:13:28 -05:00
} else {
2020-01-11 17:40:42 -05:00
RenderTilemapMode7 < layerIndex , normalPriority , highPriority , applyMosaic , false > ( ) ;
2019-02-24 11:14:24 -05:00
}
2021-03-10 11:13:28 -05:00
}