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-16 01:16:57 -05:00
_outputBuffers [ 0 ] = new uint16_t [ 256 * 224 ] ;
_outputBuffers [ 1 ] = new uint16_t [ 256 * 224 ] ;
2019-02-19 01:26:48 -05:00
_subScreenBuffer = new uint16_t [ 256 * 224 ] ;
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 ;
2019-02-19 01:26:48 -05:00
delete [ ] _subScreenBuffer ;
2019-02-16 01:16:57 -05:00
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-19 01:26:48 -05:00
if ( _scanline < 224 ) {
2019-02-18 22:27:22 -05:00
RenderScanline ( ) ;
} else if ( _scanline = = 225 ) {
2019-02-17 21:09:33 -05:00
//Reset OAM address at the start of vblank?
_internalOamAddress = ( _oamRamAddress < < 1 ) ;
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-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-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-18 22:27:22 -05:00
struct SpriteInfo
{
int16_t X ;
bool HorizontalMirror ;
bool VerticalMirror ;
uint8_t Priority ;
uint8_t TileColumn ;
uint8_t TileRow ;
uint8_t Palette ;
bool UseSecondTable ;
uint8_t LargeSprite ;
} ;
SpriteInfo _sprites [ 32 ] = { } ;
uint8_t _spriteCount = 0 ;
uint8_t _spritePriority [ 256 ] = { } ;
uint16_t _spritePixels [ 256 ] = { } ;
2019-02-19 01:26:48 -05:00
template < uint8_t priority , bool forMainScreen >
void Ppu : : DrawSprites ( )
2019-02-18 22:27:22 -05:00
{
2019-02-19 01:26:48 -05:00
if ( forMainScreen ) {
2019-02-19 17:23:21 -05:00
if ( ( _mainScreenLayers & 0x10 ) = = 0 ) {
return ;
}
2019-02-19 01:26:48 -05:00
for ( int x = 0 ; x < 256 ; x + + ) {
if ( ! _filled [ x ] & & _spritePriority [ x ] = = priority ) {
_currentBuffer [ ( _scanline < < 8 ) | x ] = _spritePixels [ x ] ;
_filled [ x ] = true ;
}
}
} else {
2019-02-19 17:23:21 -05:00
if ( ( _subScreenLayers & 0x10 ) = = 0 ) {
return ;
}
2019-02-19 01:26:48 -05:00
for ( int x = 0 ; x < 256 ; x + + ) {
if ( ! _subScreenFilled [ x ] & & _spritePriority [ x ] = = priority ) {
_subScreenBuffer [ ( _scanline < < 8 ) | x ] = _spritePixels [ x ] ;
_subScreenFilled [ x ] = true ;
}
2019-02-18 22:27:22 -05:00
}
}
2019-02-19 01:26:48 -05:00
}
void Ppu : : RenderScanline ( )
{
memset ( _filled , 0 , sizeof ( _filled ) ) ;
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 :
DrawSprites < 3 , true > ( ) ;
RenderTilemap < 0 , 2 , true , true > ( ) ;
RenderTilemap < 1 , 2 , true , true > ( ) ;
DrawSprites < 2 , true > ( ) ;
RenderTilemap < 0 , 2 , false , true > ( ) ;
RenderTilemap < 1 , 2 , false , true > ( ) ;
DrawSprites < 1 , true > ( ) ;
RenderTilemap < 2 , 2 , true , true > ( ) ;
RenderTilemap < 3 , 2 , true , true > ( ) ;
DrawSprites < 0 , true > ( ) ;
RenderTilemap < 2 , 2 , false , true > ( ) ;
RenderTilemap < 3 , 2 , false , true > ( ) ;
RenderBgColor < true > ( ) ;
break ;
2019-02-18 22:27:22 -05:00
case 1 :
2019-02-19 01:26:48 -05:00
//Main screen
if ( _mode1Bg3Priority ) {
RenderTilemap < 2 , 2 , true , true > ( ) ;
}
DrawSprites < 3 , true > ( ) ;
RenderTilemap < 0 , 4 , true , true > ( ) ;
RenderTilemap < 1 , 4 , true , true > ( ) ;
DrawSprites < 2 , true > ( ) ;
RenderTilemap < 0 , 4 , false , true > ( ) ;
RenderTilemap < 1 , 4 , false , true > ( ) ;
DrawSprites < 1 , true > ( ) ;
if ( ! _mode1Bg3Priority ) {
RenderTilemap < 2 , 2 , true , true > ( ) ;
}
DrawSprites < 0 , true > ( ) ;
RenderTilemap < 2 , 2 , false , true > ( ) ;
RenderBgColor < true > ( ) ;
//Subscreen
if ( _mode1Bg3Priority ) {
RenderTilemap < 2 , 2 , true , false > ( ) ;
}
DrawSprites < 3 , false > ( ) ;
RenderTilemap < 0 , 4 , true , false > ( ) ;
RenderTilemap < 1 , 4 , true , false > ( ) ;
DrawSprites < 2 , false > ( ) ;
RenderTilemap < 0 , 4 , false , false > ( ) ;
RenderTilemap < 1 , 4 , false , false > ( ) ;
DrawSprites < 1 , false > ( ) ;
if ( ! _mode1Bg3Priority ) {
RenderTilemap < 2 , 2 , true , false > ( ) ;
}
DrawSprites < 0 , true > ( ) ;
RenderTilemap < 2 , 2 , false , false > ( ) ;
RenderBgColor < false > ( ) ;
ApplyColorMath ( ) ;
break ;
case 2 :
DrawSprites < 3 , true > ( ) ;
RenderTilemap < 0 , 4 , true , true > ( ) ;
DrawSprites < 2 , true > ( ) ;
RenderTilemap < 1 , 4 , true , true > ( ) ;
DrawSprites < 1 , true > ( ) ;
RenderTilemap < 0 , 4 , false , true > ( ) ;
DrawSprites < 0 , true > ( ) ;
RenderTilemap < 1 , 4 , false , true > ( ) ;
RenderBgColor < true > ( ) ;
break ;
case 3 :
DrawSprites < 3 , true > ( ) ;
RenderTilemap < 0 , 8 , true , true > ( ) ;
DrawSprites < 2 , true > ( ) ;
RenderTilemap < 1 , 4 , true , true > ( ) ;
DrawSprites < 1 , true > ( ) ;
RenderTilemap < 0 , 8 , false , true > ( ) ;
DrawSprites < 0 , true > ( ) ;
RenderTilemap < 1 , 4 , false , true > ( ) ;
RenderBgColor < true > ( ) ;
break ;
case 5 :
RenderTilemap < 1 , 2 , false , true > ( ) ;
RenderTilemap < 0 , 4 , false , true > ( ) ;
RenderBgColor < true > ( ) ;
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-18 22:27:22 -05:00
//Process sprites for next scanline
memset ( _spritePriority , 0xFF , sizeof ( _spritePriority ) ) ;
memset ( _spritePixels , 0xFFFF , sizeof ( _spritePixels ) ) ;
_spriteCount = 0 ;
2019-02-19 18:01:27 -05:00
uint16_t totalWidth = 0 ;
2019-02-18 22:27:22 -05:00
for ( int i = 0 ; i < 512 ; i + = 4 ) {
uint8_t y = _oamRam [ i + 1 ] ;
uint8_t highTableOffset = i > > 4 ;
uint8_t shift = ( ( i > > 2 ) & 0x03 ) < < 1 ;
uint8_t highTableValue = _oamRam [ 0x200 | highTableOffset ] > > shift ;
uint8_t largeSprite = ( highTableValue & 0x02 ) > > 1 ;
uint8_t height = _oamSizes [ _oamMode ] [ largeSprite ] [ 1 ] < < 3 ;
2019-02-19 01:26:48 -05:00
if ( y > _scanline + 1 | | y + height < = _scanline + 1 ) {
2019-02-18 22:27:22 -05:00
//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 < info . X + width ; x + + ) {
if ( _spritePixels [ x ] = = 0xFFFF ) {
uint8_t xOffset ;
int columnOffset ;
if ( info . HorizontalMirror ) {
2019-02-19 01:26:48 -05:00
xOffset = ( width - ( x - info . X ) - 1 ) & 0x07 ;
columnOffset = ( width - ( x - info . X ) - 1 ) > > 3 ;
2019-02-18 22:27:22 -05:00
} 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 ;
}
}
}
2019-02-19 18:01:27 -05:00
totalWidth + = width ;
if ( totalWidth > = 34 * 8 ) {
_timeOver = true ;
}
2019-02-18 22:27:22 -05:00
2019-02-19 18:01:27 -05:00
_spriteCount + + ;
2019-02-18 22:27:22 -05:00
if ( _spriteCount = = 32 ) {
2019-02-19 18:01:27 -05:00
_rangeOver = true ;
}
if ( _timeOver | | _rangeOver ) {
2019-02-18 22:27:22 -05:00
break ;
}
}
}
2019-02-19 01:26:48 -05:00
template < bool forMainScreen >
void Ppu : : RenderBgColor ( )
2019-02-13 18:44:39 -05:00
{
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 ) {
if ( ! _filled [ x ] ) {
_currentBuffer [ ( _scanline < < 8 ) | x ] = bgColor ;
}
} else {
if ( ! _subScreenFilled [ x ] ) {
_subScreenBuffer [ ( _scanline < < 8 ) | x ] = bgColor ;
}
}
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-19 01:26:48 -05:00
template < uint8_t layerIndex , uint8_t bpp , bool processHighPriority , bool forMainScreen >
void Ppu : : RenderTilemap ( )
{
if ( forMainScreen ) {
if ( ( ( _mainScreenLayers > > layerIndex ) & 0x01 ) = = 0 ) {
//This screen is disabled
return ;
}
} else {
if ( ( ( _subScreenLayers > > layerIndex ) & 0x01 ) = = 0 ) {
//This screen is disabled
return ;
}
}
2019-02-17 23:53:19 -05:00
2019-02-17 23:26:49 -05:00
LayerConfig & config = _layerConfig [ layerIndex ] ;
2019-02-19 17:23:21 -05:00
uint16_t tilemapAddr = config . TilemapAddress > > 1 ;
2019-02-17 00:32:41 -05:00
uint16_t chrAddr = config . ChrAddress ;
2019-02-19 01:26:48 -05:00
2019-02-19 17:23:21 -05:00
for ( int x = 0 ; x < 256 ; x + + ) {
uint16_t row = ( _scanline + config . VScroll ) > > 3 ;
uint16_t column = ( x + config . HScroll ) > > 3 ;
uint32_t addr = tilemapAddr + ( ( row & 0x1F ) < < 5 ) + ( column & 0x1F ) + ( config . VerticalMirroring ? ( ( row & 0x20 ) < < ( config . HorizontalMirroring ? 6 : 5 ) ) : 0 ) + ( config . HorizontalMirroring ? ( ( column & 0x20 ) < < 5 ) : 0 ) ;
addr < < = 1 ;
2019-02-19 01:26:48 -05:00
if ( forMainScreen ) {
if ( _filled [ x ] | | ( ( uint8_t ) processHighPriority ! = ( ( _vram [ addr + 1 ] & 0x20 ) > > 5 ) ) ) {
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-19 01:26:48 -05:00
uint8_t palette = ( _vram [ addr + 1 ] > > 2 ) & 0x07 ;
uint16_t tileIndex = ( ( _vram [ addr + 1 ] & 0x03 ) < < 8 ) | _vram [ addr ] ;
bool vMirror = ( _vram [ addr + 1 ] & 0x80 ) ! = 0 ;
bool hMirror = ( _vram [ addr + 1 ] & 0x40 ) ! = 0 ;
2019-02-17 00:32:41 -05:00
2019-02-19 01:26:48 -05:00
uint16_t tileStart = chrAddr + tileIndex * 8 * bpp ;
2019-02-19 17:23:21 -05:00
uint8_t yOffset = ( _scanline + config . VScroll ) & 0x07 ;
if ( vMirror ) {
yOffset = 7 - yOffset ;
}
2019-02-17 00:32:41 -05:00
2019-02-19 01:26:48 -05:00
uint16_t color = 0 ;
2019-02-19 17:23:21 -05:00
uint8_t xOffset = ( x + config . HScroll ) & 0x07 ;
uint8_t shift = hMirror ? xOffset : ( 7 - xOffset ) ;
2019-02-19 01:26:48 -05:00
for ( int plane = 0 ; plane < bpp ; plane + + ) {
uint8_t offset = ( plane > > 1 ) * 16 ;
color | = ( ( ( _vram [ tileStart + yOffset * 2 + offset + ( plane & 0x01 ) ] > > shift ) & 0x01 ) < < bpp ) ;
color > > = 1 ;
}
2019-02-13 18:44:39 -05:00
2019-02-19 01:26:48 -05:00
if ( color > 0 ) {
uint16_t paletteRamOffset = ( palette * ( 1 < < bpp ) + color ) * 2 ;
uint16_t paletteColor = _cgram [ paletteRamOffset ] | ( _cgram [ paletteRamOffset + 1 ] < < 8 ) ;
2019-02-17 00:32:41 -05:00
2019-02-19 01:26:48 -05:00
if ( forMainScreen ) {
_currentBuffer [ ( _scanline < < 8 ) | x ] = paletteColor ;
_filled [ x ] = true ;
} else {
_subScreenBuffer [ ( _scanline < < 8 ) | x ] = paletteColor ;
_subScreenFilled [ x ] = true ;
}
}
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-19 01:26:48 -05:00
void Ppu : : ApplyColorMath ( )
{
bool useColorMath = _colorMathEnabled ; // >> layerIndex) & 0x01) != 0;
if ( ! useColorMath ) {
return ;
2019-02-17 23:26:49 -05:00
}
2019-02-18 22:27:22 -05:00
2019-02-19 01:26:48 -05:00
for ( int x = 0 ; x < 256 ; x + + ) {
uint16_t & mainPixel = _currentBuffer [ ( _scanline < < 8 ) | x ] ;
uint16_t & subPixel = _subScreenBuffer [ ( _scanline < < 8 ) | x ] ;
uint8_t halfShift = _colorMathHalveResult ? 1 : 0 ;
uint16_t r = std : : min ( ( ( mainPixel & 0x001F ) + ( subPixel & 0x001F ) ) > > halfShift , 0x1F ) ;
uint16_t g = std : : min ( ( ( ( mainPixel > > 5 ) & 0x001F ) + ( ( subPixel > > 5 ) & 0x001F ) ) > > halfShift , 0x1F ) ;
uint16_t b = std : : min ( ( ( ( mainPixel > > 10 ) & 0x001F ) + ( ( subPixel > > 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-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 ) ;
_console - > GetVideoDecoder ( ) - > UpdateFrame ( _currentBuffer , _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-13 18:44:39 -05:00
uint8_t Ppu : : Read ( uint16_t addr )
{
switch ( addr ) {
2019-02-19 18:01:27 -05:00
case 0x213E :
//TODO open bus on bit 4
return (
( _timeOver ? 0x80 : 0 ) |
( _rangeOver ? 0x40 : 0 ) |
0x01 //PPU chip version
) ;
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
}
return 0 ;
}
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 ;
//TODO Apply brightness
_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 ;
}
} else if ( _internalOamAddress > = 512 ) {
uint16_t address = 0x200 | ( _internalOamAddress & 0x1F ) ;
_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 :
_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 ;
_layerConfig [ 2 ] . LargeTiles = ( value & 0x30 ) ! = 0 ;
_layerConfig [ 3 ] . LargeTiles = ( value & 0x40 ) ! = 0 ;
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 :
//TODO Mode 7 portion of register
case 0x210F : case 0x2111 : case 0x2113 :
_layerConfig [ ( addr - 0x210D ) > > 1 ] . HScroll = ( ( value < < 8 ) | ( _hvScrollLatchValue & ~ 0x07 ) | ( _hScrollLatchValue & 0x07 ) ) & 0x3FF ;
_hvScrollLatchValue = value ;
_hScrollLatchValue = value ;
break ;
case 0x210E :
//TODO Mode 7 portion of register
case 0x2110 : case 0x2112 : case 0x2114 :
_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 ;
}
_vramAddressRemapping = ( value & 0x0C ) > > 2 ;
_vramAddrIncrementOnSecondReg = ( value & 0x80 ) ! = 0 ;
break ;
case 0x2116 :
//VMADDL - VRAM Address low byte
2019-02-16 01:16:57 -05:00
_vramAddress = ( _vramAddress & 0x7F00 ) | value ;
2019-02-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 ;
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-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-17 23:53:19 -05:00
case 0x2130 :
//CGWSEL - Color Addition Select
_colorMathClipMode = ( value > > 6 ) & 0x03 ;
_colorMathPreventMode = ( value > > 4 ) & 0x03 ;
_colorMathAddSubscreen = ( value & 0x02 ) ! = 0 ;
2019-02-18 22:27:22 -05:00
_directColorMode = ( value & 0x01 ) ! = 0 ;
2019-02-17 23:53:19 -05:00
break ;
case 0x2131 :
_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-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
}
}