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