2015-07-29 22:10:34 -04:00
# pragma once
# include "stdafx.h"
# include "BaseMapper.h"
# include "PPU.h"
2016-06-12 11:28:45 -04:00
# include "MMC5Audio.h"
2015-07-29 22:10:34 -04:00
class MMC5 : public BaseMapper
{
private :
2019-01-12 13:18:29 -05:00
static constexpr int ExRamSize = 0x400 ;
static constexpr uint8_t NtWorkRamIndex = 4 ;
static constexpr uint8_t NtEmptyIndex = 2 ;
static constexpr uint8_t NtFillModeIndex = 3 ;
2016-01-24 11:18:50 -05:00
2018-07-01 15:21:05 -04:00
unique_ptr < MMC5Audio > _audio ;
2016-06-12 11:28:45 -04:00
2015-07-29 22:10:34 -04:00
uint8_t _prgRamProtect1 ;
uint8_t _prgRamProtect2 ;
uint8_t _fillModeTile ;
uint8_t _fillModeColor ;
bool _verticalSplitEnabled ;
bool _verticalSplitRightSide ;
uint8_t _verticalSplitDelimiterTile ;
uint8_t _verticalSplitScroll ;
uint8_t _verticalSplitBank ;
2016-07-02 14:48:46 -04:00
bool _splitInSplitRegion ;
uint32_t _splitVerticalScroll ;
uint32_t _splitTile ;
int32_t _splitTileNumber ;
2015-07-29 22:10:34 -04:00
uint8_t _multiplierValue1 ;
uint8_t _multiplierValue2 ;
uint8_t _nametableMapping ;
uint8_t _extendedRamMode ;
//Extended attribute mode fields (used when _extendedRamMode == 1)
uint16_t _exAttributeLastNametableFetch ;
int8_t _exAttrLastFetchCounter ;
uint8_t _exAttrSelectedChrBank ;
uint8_t _prgMode ;
uint8_t _prgBanks [ 5 ] ;
//CHR-related fields
uint8_t _chrMode ;
2019-01-12 13:18:29 -05:00
uint8_t _chrUpperBits ;
2015-07-29 22:10:34 -04:00
uint16_t _chrBanks [ 12 ] ;
uint16_t _lastChrReg ;
bool _spriteFetch ;
bool _largeSprites ;
//IRQ counter related fields
uint8_t _irqCounterTarget ;
bool _irqEnabled ;
int16_t _previousScanline ;
uint8_t _irqCounter ;
bool _irqPending ;
bool _ppuInFrame ;
2017-04-14 14:09:54 -04:00
MemoryOperationType _lastVramOperationType ;
2015-07-29 22:10:34 -04:00
void SwitchPrgBank ( uint16_t reg , uint8_t value )
{
_prgBanks [ reg - 0x5113 ] = value ;
UpdatePrgBanks ( ) ;
}
void GetCpuBankInfo ( uint16_t reg , uint8_t & bankNumber , PrgMemoryType & memoryType , uint8_t & accessType )
{
bankNumber = _prgBanks [ reg - 0x5113 ] ;
memoryType = PrgMemoryType : : PrgRom ;
2016-12-30 13:42:12 -05:00
if ( ( ( ( bankNumber & 0x80 ) = = 0x00 ) & & reg ! = 0x5117 ) | | reg = = 0x5113 ) {
2015-07-29 22:10:34 -04:00
bankNumber & = 0x07 ;
2015-08-18 18:02:40 -04:00
accessType = MemoryAccessType : : Read ;
if ( _prgRamProtect1 = = 0x02 & & _prgRamProtect2 = = 0x01 ) {
2015-07-29 22:10:34 -04:00
accessType | = MemoryAccessType : : Write ;
}
2019-01-12 13:18:29 -05:00
// WRAM/SRAM mirroring logic (only supports existing/known licensed MMC5 boards)
// Bank number
// 0 1 2 3 4 5 6 7
// --------------------------
// None : - - - - - - - -
// 1x 8kb : 0 0 0 0 - - - -
// 2x 8kb : 0 0 0 0 1 1 1 1
// 1x 32kb : 0 1 2 3 - - - -
int32_t realWorkRamSize = _workRamSize - MMC5 : : ExRamSize ;
if ( IsNes20 ( ) | | _romInfo . IsInDatabase ) {
memoryType = PrgMemoryType : : WorkRam ;
if ( HasBattery ( ) & & ( bankNumber < = 3 | | _saveRamSize > 0x2000 ) ) {
memoryType = PrgMemoryType : : SaveRam ;
}
if ( _saveRamSize + realWorkRamSize ! = 0x4000 & & bankNumber > = 4 ) {
//When not 2x 8kb (=16kb), banks 4/5/6/7 select the empty socket and return open bus
accessType = MemoryAccessType : : NoAccess ;
}
} else {
memoryType = HasBattery ( ) ? PrgMemoryType : : SaveRam : PrgMemoryType : : WorkRam ;
}
if ( memoryType = = PrgMemoryType : : WorkRam ) {
//Properly mirror work ram (by ignoring the extra 1kb ExRAM section)
bankNumber & = ( realWorkRamSize / 0x2000 ) - 1 ;
if ( _workRamSize = = MMC5 : : ExRamSize ) {
accessType = MemoryAccessType : : NoAccess ;
}
}
2015-07-29 22:10:34 -04:00
} else {
accessType = MemoryAccessType : : Read ;
bankNumber & = 0x7F ;
}
}
void UpdatePrgBanks ( )
{
uint8_t value ;
PrgMemoryType memoryType ;
uint8_t accessType ;
2019-01-12 13:18:29 -05:00
2015-07-29 22:10:34 -04:00
GetCpuBankInfo ( 0x5113 , value , memoryType , accessType ) ;
SetCpuMemoryMapping ( 0x6000 , 0x7FFF , value , memoryType , accessType ) ;
2019-01-12 13:18:29 -05:00
2015-07-29 22:10:34 -04:00
//PRG Bank 0
//Mode 0,1,2 - Ignored
//Mode 3 - Select an 8KB PRG bank at $8000-$9FFF
if ( _prgMode = = 3 ) {
GetCpuBankInfo ( 0x5114 , value , memoryType , accessType ) ;
SetCpuMemoryMapping ( 0x8000 , 0x9FFF , value , memoryType , accessType ) ;
}
//PRG Bank 1
//Mode 0 - Ignored
//Mode 1,2 - Select a 16KB PRG bank at $8000-$BFFF (ignore bottom bit)
//Mode 3 - Select an 8KB PRG bank at $A000-$BFFF
GetCpuBankInfo ( 0x5115 , value , memoryType , accessType ) ;
if ( _prgMode = = 1 | | _prgMode = = 2 ) {
SetCpuMemoryMapping ( 0x8000 , 0xBFFF , value & 0xFE , memoryType , accessType ) ;
} else if ( _prgMode = = 3 ) {
SetCpuMemoryMapping ( 0xA000 , 0xBFFF , value , memoryType , accessType ) ;
}
//Mode 0,1 - Ignored
//Mode 2,3 - Select an 8KB PRG bank at $C000-$DFFF
if ( _prgMode = = 2 | | _prgMode = = 3 ) {
GetCpuBankInfo ( 0x5116 , value , memoryType , accessType ) ;
SetCpuMemoryMapping ( 0xC000 , 0xDFFF , value , memoryType , accessType ) ;
}
//Mode 0 - Select a 32KB PRG ROM bank at $8000-$FFFF (ignore bottom 2 bits)
//Mode 1 - Select a 16KB PRG ROM bank at $C000-$FFFF (ignore bottom bit)
//Mode 2,3 - Select an 8KB PRG ROM bank at $E000-$FFFF
GetCpuBankInfo ( 0x5117 , value , memoryType , accessType ) ;
if ( _prgMode = = 0 ) {
SetCpuMemoryMapping ( 0x8000 , 0xFFFF , value & 0x7C , memoryType , accessType ) ;
} else if ( _prgMode = = 1 ) {
SetCpuMemoryMapping ( 0xC000 , 0xFFFF , value & 0x7E , memoryType , accessType ) ;
} else if ( _prgMode = = 2 | | _prgMode = = 3 ) {
SetCpuMemoryMapping ( 0xE000 , 0xFFFF , value & 0x7F , memoryType , accessType ) ;
}
}
void SwitchChrBank ( uint16_t reg , uint8_t value )
{
_chrBanks [ reg - 0x5120 ] = value | ( _chrUpperBits < < 8 ) ;
2019-01-12 13:18:29 -05:00
2017-04-23 19:26:56 -04:00
if ( _largeSprites ) {
_lastChrReg = reg ;
} else {
//Using 8x8 sprites resets the last written to bank logic
//Unsure about this part (hasn't been tested specifically, but would make sense)
_lastChrReg = 0 ;
}
2017-04-14 14:09:54 -04:00
UpdateChrBanks ( ) ;
2015-07-29 22:10:34 -04:00
}
2017-04-14 14:09:54 -04:00
void UpdateChrBanks ( )
2015-07-29 22:10:34 -04:00
{
2017-04-23 19:26:56 -04:00
if ( ! _largeSprites ) {
//Using 8x8 sprites resets the last written to bank logic
_lastChrReg = 0 ;
}
bool chrA = ! _largeSprites | | ( _largeSprites & & _spriteFetch ) | | ( _lastVramOperationType ! = MemoryOperationType : : PpuRenderingRead & & _lastChrReg < = 0x5127 ) ;
2015-07-29 22:10:34 -04:00
if ( _chrMode = = 0 ) {
2016-01-24 11:18:50 -05:00
SelectChrPage8x ( 0 , _chrBanks [ chrA ? 0x07 : 0x0B ] < < 3 ) ;
2015-07-29 22:10:34 -04:00
} else if ( _chrMode = = 1 ) {
2016-01-24 11:18:50 -05:00
SelectChrPage4x ( 0 , _chrBanks [ chrA ? 0x03 : 0x0B ] < < 2 ) ;
SelectChrPage4x ( 1 , _chrBanks [ chrA ? 0x07 : 0x0B ] < < 2 ) ;
2015-07-29 22:10:34 -04:00
} else if ( _chrMode = = 2 ) {
2016-01-24 11:18:50 -05:00
SelectChrPage2x ( 0 , _chrBanks [ chrA ? 0x01 : 0x09 ] < < 1 ) ;
SelectChrPage2x ( 1 , _chrBanks [ chrA ? 0x03 : 0x0B ] < < 1 ) ;
SelectChrPage2x ( 2 , _chrBanks [ chrA ? 0x05 : 0x09 ] < < 1 ) ;
SelectChrPage2x ( 3 , _chrBanks [ chrA ? 0x07 : 0x0B ] < < 1 ) ;
2015-07-29 22:10:34 -04:00
} else if ( _chrMode = = 3 ) {
2016-01-24 11:18:50 -05:00
SelectCHRPage ( 0 , _chrBanks [ chrA ? 0x00 : 0x08 ] ) ;
SelectCHRPage ( 1 , _chrBanks [ chrA ? 0x01 : 0x09 ] ) ;
SelectCHRPage ( 2 , _chrBanks [ chrA ? 0x02 : 0x0A ] ) ;
SelectCHRPage ( 3 , _chrBanks [ chrA ? 0x03 : 0x0B ] ) ;
SelectCHRPage ( 4 , _chrBanks [ chrA ? 0x04 : 0x08 ] ) ;
SelectCHRPage ( 5 , _chrBanks [ chrA ? 0x05 : 0x09 ] ) ;
SelectCHRPage ( 6 , _chrBanks [ chrA ? 0x06 : 0x0A ] ) ;
SelectCHRPage ( 7 , _chrBanks [ chrA ? 0x07 : 0x0B ] ) ;
2015-07-29 22:10:34 -04:00
}
}
2016-12-17 23:14:47 -05:00
void ProcessCpuClock ( ) override
2016-01-24 21:35:24 -05:00
{
2018-07-01 15:21:05 -04:00
_audio - > Clock ( ) ;
2016-01-24 21:35:24 -05:00
}
2016-12-17 23:14:47 -05:00
virtual void NotifyVRAMAddressChange ( uint16_t addr ) override
2015-07-29 22:10:34 -04:00
{
2018-07-01 15:21:05 -04:00
PPU * ppu = _console - > GetPpu ( ) ;
if ( ppu - > GetControlFlags ( ) . BackgroundEnabled | | ppu - > GetControlFlags ( ) . SpritesEnabled ) {
int16_t currentScanline = ppu - > GetCurrentScanline ( ) ;
2017-04-02 17:41:24 -04:00
if ( currentScanline ! = _previousScanline ) {
if ( currentScanline > = 239 | | currentScanline < 0 ) {
_ppuInFrame = false ;
2015-07-29 22:10:34 -04:00
} else {
2017-04-02 17:41:24 -04:00
if ( ! _ppuInFrame ) {
_ppuInFrame = true ;
_irqCounter = 0 ;
_irqPending = false ;
2018-07-01 15:21:05 -04:00
_console - > GetCpu ( ) - > ClearIrqSource ( IRQSource : : External ) ;
2017-04-02 17:41:24 -04:00
} else {
_irqCounter + + ;
if ( _irqCounter = = _irqCounterTarget ) {
_irqPending = true ;
if ( _irqEnabled ) {
2018-07-01 15:21:05 -04:00
_console - > GetCpu ( ) - > SetIrqSource ( IRQSource : : External ) ;
2017-04-02 17:41:24 -04:00
}
2015-07-29 22:10:34 -04:00
}
}
}
2017-04-02 17:41:24 -04:00
_previousScanline = currentScanline ;
2015-07-29 22:10:34 -04:00
}
2017-04-02 17:41:24 -04:00
} else {
_ppuInFrame = false ;
2015-07-29 22:10:34 -04:00
}
}
void SetNametableMapping ( uint8_t value )
{
_nametableMapping = value ;
2019-01-12 13:18:29 -05:00
uint8_t nametables [ 4 ] = {
2016-01-24 11:18:50 -05:00
0 , //"0 - On-board VRAM page 0"
1 , //"1 - On-board VRAM page 1"
_extendedRamMode < = 1 ? NtWorkRamIndex : NtEmptyIndex , //"2 - Internal Expansion RAM, only if the Extended RAM mode allows it ($5104 is 00/01); otherwise, the nametable will read as all zeros,"
NtFillModeIndex //"3 - Fill-mode data"
2015-07-29 22:10:34 -04:00
} ;
2019-01-09 20:19:16 -05:00
for ( int i = 0 ; i < 4 ; i + + ) {
uint8_t nametableId = nametables [ ( value > > ( i * 2 ) ) & 0x03 ] ;
if ( nametableId = = NtWorkRamIndex ) {
SetPpuMemoryMapping ( 0x2000 + i * 0x400 , 0x2000 + i * 0x400 + 0x3FF , _workRam , MemoryAccessType : : ReadWrite ) ;
} else {
SetNametable ( i , nametableId ) ;
}
}
2015-07-29 22:10:34 -04:00
}
void SetExtendedRamMode ( uint8_t mode )
{
_extendedRamMode = mode ;
2019-01-12 13:18:29 -05:00
MemoryAccessType accessType ;
2015-07-29 22:10:34 -04:00
if ( _extendedRamMode < = 1 ) {
//"Mode 0/1 - Not readable (returns open bus), can only be written while the PPU is rendering (otherwise, 0 is written)"
//See overridden WriteRam function for implementation
2019-01-12 13:18:29 -05:00
accessType = MemoryAccessType : : Write ;
2015-07-29 22:10:34 -04:00
} else if ( _extendedRamMode = = 2 ) {
//"Mode 2 - Readable and writable"
2019-01-12 13:18:29 -05:00
accessType = MemoryAccessType : : ReadWrite ;
2015-07-29 22:10:34 -04:00
} else {
//"Mode 3 - Read-only"
2019-01-12 13:18:29 -05:00
accessType = MemoryAccessType : : Read ;
2015-07-29 22:10:34 -04:00
}
2019-01-12 13:18:29 -05:00
SetCpuMemoryMapping ( 0x5C00 , 0x5FFF , PrgMemoryType : : WorkRam , _workRamSize - MMC5 : : ExRamSize , accessType ) ;
2015-07-29 22:10:34 -04:00
SetNametableMapping ( _nametableMapping ) ;
}
void SetFillModeTile ( uint8_t tile )
{
_fillModeTile = tile ;
2019-01-09 20:19:16 -05:00
memset ( GetNametable ( NtFillModeIndex ) , tile , 32 * 30 ) ; //32 tiles per row, 30 rows
2015-07-29 22:10:34 -04:00
}
void SetFillModeColor ( uint8_t color )
{
_fillModeColor = color ;
2019-04-07 08:13:13 -04:00
uint8_t attributeByte = color | color < < 2 | color < < 4 | color < < 6 ;
memset ( GetNametable ( NtFillModeIndex ) + 32 * 30 , attributeByte , 64 ) ; //Attribute table is 64 bytes
2015-07-29 22:10:34 -04:00
}
bool IsSpriteFetch ( )
{
2018-07-01 15:21:05 -04:00
return _console - > GetPpu ( ) - > GetCurrentCycle ( ) > = 257 & & _console - > GetPpu ( ) - > GetCurrentCycle ( ) < 321 ;
2015-07-29 22:10:34 -04:00
}
protected :
2016-12-17 23:14:47 -05:00
virtual uint16_t GetPRGPageSize ( ) override { return 0x2000 ; }
2019-01-12 13:18:29 -05:00
virtual uint16_t GetCHRPageSize ( ) override { return 0x400 ; }
2016-12-17 23:14:47 -05:00
virtual uint16_t RegisterStartAddress ( ) override { return 0x5000 ; }
virtual uint16_t RegisterEndAddress ( ) override { return 0x5206 ; }
virtual uint32_t GetSaveRamPageSize ( ) override { return 0x2000 ; }
2019-01-12 13:18:29 -05:00
virtual uint32_t GetWorkRamPageSize ( ) override { return 0x2000 ; }
2016-12-17 23:14:47 -05:00
virtual bool ForceSaveRamSize ( ) override { return true ; }
virtual bool ForceWorkRamSize ( ) override { return true ; }
2019-01-12 13:18:29 -05:00
virtual uint32_t GetSaveRamSize ( ) override
{
if ( IsNes20 ( ) ) {
2019-01-26 16:49:26 -05:00
return _romInfo . NesHeader . GetSaveRamSize ( ) ;
2019-01-12 13:18:29 -05:00
} else if ( _romInfo . IsInDatabase ) {
return _romInfo . DatabaseInfo . SaveRamSize ;
} else {
//Emulate as if a single 64k block of work/save ram existed
return _romInfo . HasBattery ? 0x10000 : 0 ;
}
}
virtual uint32_t GetWorkRamSize ( ) override
{
if ( IsNes20 ( ) ) {
2019-01-26 16:49:26 -05:00
return _romInfo . NesHeader . GetWorkRamSize ( ) + MMC5 : : ExRamSize ;
2019-01-12 13:18:29 -05:00
} else if ( _romInfo . IsInDatabase ) {
return _romInfo . DatabaseInfo . WorkRamSize + MMC5 : : ExRamSize ;
} else {
//Emulate as if a single 64k block of work/save ram existed (+ 1kb of ExRAM)
return ( _romInfo . HasBattery ? 0 : 0x10000 ) + MMC5 : : ExRamSize ;
}
}
2016-12-17 23:14:47 -05:00
virtual bool AllowRegisterRead ( ) override { return true ; }
virtual void InitMapper ( ) override
2015-07-29 22:10:34 -04:00
{
2018-07-01 15:21:05 -04:00
_audio . reset ( new MMC5Audio ( _console ) ) ;
2019-01-12 13:18:29 -05:00
2015-07-29 22:10:34 -04:00
_chrMode = 0 ;
_prgRamProtect1 = 0 ;
_prgRamProtect2 = 0 ;
_extendedRamMode = 0 ;
2019-01-12 13:18:29 -05:00
_nametableMapping = 0 ;
2015-07-29 22:10:34 -04:00
_fillModeColor = 0 ;
_fillModeTile = 0 ;
_verticalSplitScroll = 0 ;
_verticalSplitBank = 0 ;
2019-06-23 20:10:55 -04:00
_verticalSplitEnabled = false ;
_verticalSplitDelimiterTile = 0 ;
_verticalSplitRightSide = false ;
2015-07-29 22:10:34 -04:00
_multiplierValue1 = 0 ;
_multiplierValue2 = 0 ;
_chrUpperBits = 0 ;
memset ( _chrBanks , 0 , sizeof ( _chrBanks ) ) ;
_lastChrReg = 0 ;
_spriteFetch = false ;
_largeSprites = false ;
_exAttrLastFetchCounter = 0 ;
_exAttributeLastNametableFetch = 0 ;
_exAttrSelectedChrBank = 0 ;
2019-06-23 20:10:55 -04:00
_irqPending = false ;
2015-07-29 22:10:34 -04:00
_irqCounterTarget = 0 ;
_irqCounter = 0 ;
_irqEnabled = false ;
_previousScanline = - 1 ;
_ppuInFrame = false ;
2019-06-23 20:10:55 -04:00
_lastVramOperationType = MemoryOperationType : : Read ;
2016-07-02 14:48:46 -04:00
_splitInSplitRegion = false ;
_splitVerticalScroll = 0 ;
_splitTile = 0 ;
_splitTileNumber = - 1 ;
2019-01-09 20:19:16 -05:00
memset ( GetNametable ( NtEmptyIndex ) , 0 , BaseMapper : : NametableSize ) ;
2015-07-29 22:10:34 -04:00
2019-01-12 13:18:29 -05:00
SetExtendedRamMode ( 0 ) ;
2015-07-29 22:10:34 -04:00
//"Additionally, Romance of the 3 Kingdoms 2 seems to expect it to be in 8k PRG mode ($5100 = $03)."
WriteRegister ( 0x5100 , 0x03 ) ;
//"Games seem to expect $5117 to be $FF on powerup (last PRG page swapped in)."
WriteRegister ( 0x5117 , 0xFF ) ;
}
2016-12-17 23:14:47 -05:00
void StreamState ( bool saving ) override
2015-07-29 22:10:34 -04:00
{
BaseMapper : : StreamState ( saving ) ;
2016-01-24 21:54:05 -05:00
2016-06-02 23:56:11 -04:00
ArrayInfo < uint8_t > prgBanks = { _prgBanks , 5 } ;
ArrayInfo < uint16_t > chrBanks = { _chrBanks , 12 } ;
2018-07-01 15:21:05 -04:00
SnapshotInfo audio { _audio . get ( ) } ;
2016-06-02 20:20:26 -04:00
Stream ( _prgRamProtect1 , _prgRamProtect2 , _fillModeTile , _fillModeColor , _verticalSplitEnabled , _verticalSplitRightSide ,
_verticalSplitDelimiterTile , _verticalSplitScroll , _verticalSplitBank , _multiplierValue1 , _multiplierValue2 ,
_nametableMapping , _extendedRamMode , _exAttributeLastNametableFetch , _exAttrLastFetchCounter , _exAttrSelectedChrBank ,
2016-06-02 23:56:11 -04:00
_prgMode , prgBanks , _chrMode , _chrUpperBits , chrBanks , _lastChrReg ,
2019-01-09 20:19:16 -05:00
_spriteFetch , _largeSprites , _irqCounterTarget , _irqEnabled , _previousScanline , _irqCounter , _irqPending , _ppuInFrame , audio ,
2017-04-14 14:09:54 -04:00
_splitInSplitRegion , _splitVerticalScroll , _splitTile , _splitTileNumber , _lastVramOperationType ) ;
2016-06-02 20:20:26 -04:00
2016-01-24 21:54:05 -05:00
if ( ! saving ) {
UpdatePrgBanks ( ) ;
2019-01-09 20:19:16 -05:00
SetNametableMapping ( _nametableMapping ) ;
2016-01-24 21:54:05 -05:00
}
2015-07-29 22:10:34 -04:00
}
2016-12-17 23:14:47 -05:00
virtual void WriteRAM ( uint16_t addr , uint8_t value ) override
2015-07-29 22:10:34 -04:00
{
2015-08-18 18:02:40 -04:00
if ( addr > = 0x5C00 & & addr < = 0x5FFF & & _extendedRamMode < = 1 ) {
2018-07-01 15:21:05 -04:00
PPUControlFlags flags = _console - > GetPpu ( ) - > GetControlFlags ( ) ;
2015-08-18 18:02:40 -04:00
if ( ! flags . BackgroundEnabled & & ! flags . SpritesEnabled ) {
//Expansion RAM ($5C00-$5FFF, read/write)
//Mode 0/1 - Not readable (returns open bus), can only be written while the PPU is rendering (otherwise, 0 is written)
value = 0 ;
}
2015-07-29 22:10:34 -04:00
}
BaseMapper : : WriteRAM ( addr , value ) ;
}
2017-03-31 22:14:16 -04:00
virtual uint8_t MapperReadVRAM ( uint16_t addr , MemoryOperationType memoryOperationType ) override
2015-07-29 22:10:34 -04:00
{
2018-07-01 15:21:05 -04:00
PPU * ppu = _console - > GetPpu ( ) ;
if ( _spriteFetch ! = IsSpriteFetch ( ) | | _largeSprites ! = ppu - > GetControlFlags ( ) . LargeSprites | | _lastVramOperationType ! = memoryOperationType ) {
2017-04-14 14:09:54 -04:00
_lastVramOperationType = memoryOperationType ;
_spriteFetch = IsSpriteFetch ( ) ;
2018-07-01 15:21:05 -04:00
_largeSprites = ppu - > GetControlFlags ( ) . LargeSprites ;
2017-04-14 14:09:54 -04:00
UpdateChrBanks ( ) ;
}
2016-07-02 14:48:46 -04:00
if ( _extendedRamMode < = 1 & & _verticalSplitEnabled & & memoryOperationType = = MemoryOperationType : : PpuRenderingRead ) {
2018-07-01 15:21:05 -04:00
uint32_t cycle = ppu - > GetCurrentCycle ( ) ;
int32_t scanline = ppu - > GetCurrentScanline ( ) ;
2016-07-02 14:48:46 -04:00
if ( cycle = = 321 ) {
_splitTileNumber = - 1 ;
if ( scanline = = - 1 ) {
_splitVerticalScroll = _verticalSplitScroll ;
} else if ( scanline < 240 ) {
_splitVerticalScroll + + ;
}
if ( _splitVerticalScroll > = 240 ) {
_splitVerticalScroll - = 240 ;
}
}
if ( ( cycle - 1 ) % 8 = = 0 & & cycle ! = 337 ) {
_splitTileNumber + + ;
}
if ( cycle < 256 | | cycle > = 321 ) {
if ( addr > = 0x2000 ) {
if ( ( addr & 0x3FF ) < 0x3C0 ) {
if ( ( _verticalSplitRightSide & & _splitTileNumber > = _verticalSplitDelimiterTile ) | | ( ! _verticalSplitRightSide & & _splitTileNumber < _verticalSplitDelimiterTile ) ) {
//Split region
_splitInSplitRegion = true ;
_splitTile = ( ( _splitVerticalScroll & 0xF8 ) < < 2 ) | _splitTileNumber ;
return InternalReadRam ( 0x5C00 + _splitTile ) ;
} else {
//Regular data, result can get modified by ex ram mode code below
_splitInSplitRegion = false ;
}
} else if ( _splitInSplitRegion ) {
return InternalReadRam ( 0x5FC0 + ( ( _splitTile > > 4 ) & ~ 0x07 ) + ( ( _splitTile & 0x3F ) > > 2 ) ) ;
}
} else if ( _splitInSplitRegion ) {
return _chrRom [ ( _verticalSplitBank % ( GetCHRPageCount ( ) / 4 ) ) * 0x1000 + ( ( ( addr & ~ 0x07 ) | ( _splitVerticalScroll & 0x07 ) ) & 0xFFF ) ] ;
}
}
}
2017-04-14 14:09:54 -04:00
if ( _extendedRamMode = = 1 & & ! IsSpriteFetch ( ) & & memoryOperationType = = MemoryOperationType : : PpuRenderingRead ) {
2015-07-29 22:10:34 -04:00
//"In Mode 1, nametable fetches are processed normally, and can come from CIRAM nametables, fill mode, or even Expansion RAM, but attribute fetches are replaced by data from Expansion RAM."
//"Each byte of Expansion RAM is used to enhance the tile at the corresponding address in every nametable"
//When fetching NT data, we set a flag and then alter the VRAM values read by the PPU on the following 3 cycles (palette, tile low/high byte)
2017-04-14 14:09:54 -04:00
if ( addr > = 0x2000 & & ( addr & 0x3FF ) < 0x3C0 ) {
//Nametable fetches
_exAttributeLastNametableFetch = addr & 0x03FF ;
_exAttrLastFetchCounter = 3 ;
} else if ( _exAttrLastFetchCounter > 0 ) {
//Attribute fetches
_exAttrLastFetchCounter - - ;
switch ( _exAttrLastFetchCounter ) {
case 2 :
{
//PPU palette fetch
//Check work ram (expansion ram) to see which tile/palette to use
//Use InternalReadRam to bypass the fact that the ram is supposed to be write-only in mode 0/1
uint8_t value = InternalReadRam ( 0x5C00 + _exAttributeLastNametableFetch ) ;
//"The pattern fetches ignore the standard CHR banking bits, and instead use the top two bits of $5130 and the bottom 6 bits from Expansion RAM to choose a 4KB bank to select the tile from."
_exAttrSelectedChrBank = ( ( value & 0x3F ) | ( _chrUpperBits < < 6 ) ) % ( _chrRomSize / 0x1000 ) ;
//Return a byte containing the same palette 4 times - this allows the PPU to select the right palette no matter the shift value
uint8_t palette = ( value & 0xC0 ) > > 6 ;
return palette | palette < < 2 | palette < < 4 | palette < < 6 ;
2016-06-12 11:32:57 -04:00
}
2017-04-14 14:09:54 -04:00
case 1 :
case 0 :
//PPU tile data fetch (high byte & low byte)
return _chrRom [ _exAttrSelectedChrBank * 0x1000 + ( addr & 0xFFF ) ] ;
2015-07-29 22:10:34 -04:00
}
}
}
2017-03-31 22:14:16 -04:00
return BaseMapper : : MapperReadVRAM ( addr , memoryOperationType ) ;
2015-07-29 22:10:34 -04:00
}
2016-12-17 23:14:47 -05:00
void WriteRegister ( uint16_t addr , uint8_t value ) override
2015-07-29 22:10:34 -04:00
{
if ( addr > = 0x5113 & & addr < = 0x5117 ) {
SwitchPrgBank ( addr , value ) ;
} else if ( addr > = 0x5120 & & addr < = 0x512B ) {
SwitchChrBank ( addr , value ) ;
} else {
switch ( addr ) {
2016-06-12 11:28:45 -04:00
case 0x5000 : case 0x5001 : case 0x5002 : case 0x5003 : case 0x5004 : case 0x5005 : case 0x5006 : case 0x5007 : case 0x5010 : case 0x5011 : case 0x5015 :
2018-07-01 15:21:05 -04:00
_audio - > WriteRegister ( addr , value ) ;
2016-06-12 11:28:45 -04:00
break ;
2015-07-29 22:10:34 -04:00
case 0x5100 : _prgMode = value & 0x03 ; UpdatePrgBanks ( ) ; break ;
case 0x5101 : _chrMode = value & 0x03 ; UpdateChrBanks ( ) ; break ;
case 0x5102 : _prgRamProtect1 = value & 0x03 ; UpdatePrgBanks ( ) ; break ;
case 0x5103 : _prgRamProtect2 = value & 0x03 ; UpdatePrgBanks ( ) ; break ;
case 0x5104 : SetExtendedRamMode ( value & 0x03 ) ; break ;
case 0x5105 : SetNametableMapping ( value ) ; break ;
case 0x5106 : SetFillModeTile ( value ) ; break ;
case 0x5107 : SetFillModeColor ( value & 0x03 ) ; break ;
case 0x5130 : _chrUpperBits = value & 0x03 ; break ;
case 0x5200 :
_verticalSplitEnabled = ( value & 0x80 ) = = 0x80 ;
_verticalSplitRightSide = ( value & 0x40 ) = = 0x40 ;
_verticalSplitDelimiterTile = ( value & 0x1F ) ;
break ;
case 0x5201 : _verticalSplitScroll = value ; break ;
case 0x5202 : _verticalSplitBank = value ; break ;
case 0x5203 : _irqCounterTarget = value ; break ;
case 0x5204 :
_irqEnabled = ( value & 0x80 ) = = 0x80 ;
if ( ! _irqEnabled ) {
2018-07-01 15:21:05 -04:00
_console - > GetCpu ( ) - > ClearIrqSource ( IRQSource : : External ) ;
2015-07-29 22:10:34 -04:00
} else if ( _irqEnabled & & _irqPending ) {
2018-07-01 15:21:05 -04:00
_console - > GetCpu ( ) - > SetIrqSource ( IRQSource : : External ) ;
2015-07-29 22:10:34 -04:00
}
break ;
case 0x5205 : _multiplierValue1 = value ; break ;
case 0x5206 : _multiplierValue2 = value ; break ;
default :
break ;
}
}
}
2016-12-17 23:14:47 -05:00
uint8_t ReadRegister ( uint16_t addr ) override
2015-07-29 22:10:34 -04:00
{
switch ( addr ) {
2016-06-12 11:28:45 -04:00
case 0x5010 : case 0x5015 :
2018-07-01 15:21:05 -04:00
return _audio - > ReadRegister ( addr ) ;
2016-06-12 11:28:45 -04:00
2015-07-29 22:10:34 -04:00
case 0x5204 :
{
uint8_t value = ( _ppuInFrame ? 0x40 : 0x00 ) | ( _irqPending ? 0x80 : 0x00 ) ;
_irqPending = false ;
2018-07-01 15:21:05 -04:00
_console - > GetCpu ( ) - > ClearIrqSource ( IRQSource : : External ) ;
2015-07-29 22:10:34 -04:00
return value ;
}
case 0x5205 : return ( _multiplierValue1 * _multiplierValue2 ) & 0xFF ;
case 0x5206 : return ( _multiplierValue1 * _multiplierValue2 ) > > 8 ;
}
2016-06-12 11:28:45 -04:00
2018-07-01 15:21:05 -04:00
return _console - > GetMemoryManager ( ) - > GetOpenBus ( ) ;
2015-07-29 22:10:34 -04:00
}
2018-01-07 13:15:25 -05:00
public :
bool IsExtendedAttributes ( )
{
return _extendedRamMode = = 1 ;
}
uint8_t GetExAttributeNtPalette ( uint16_t ntAddr )
{
ntAddr & = 0x3FF ;
uint8_t value = InternalReadRam ( 0x5C00 + ntAddr ) ;
return ( value & 0xC0 ) > > 6 ;
}
uint32_t GetExAttributeAbsoluteTileAddr ( uint16_t ntAddr , uint16_t chrAddr )
{
ntAddr & = 0x3FF ;
uint8_t value = InternalReadRam ( 0x5C00 + ntAddr ) ;
//"The pattern fetches ignore the standard CHR banking bits, and instead use the top two bits of $5130 and the bottom 6 bits from Expansion RAM to choose a 4KB bank to select the tile from."
uint16_t chrBank = ( ( value & 0x3F ) | ( _chrUpperBits < < 6 ) ) % ( _chrRomSize / 0x1000 ) ;
return chrBank * 0x1000 + ( chrAddr & 0xFFF ) ;
}
uint8_t GetExAttributeTileData ( uint16_t ntAddr , uint16_t chrAddr )
{
return _chrRom [ GetExAttributeAbsoluteTileAddr ( ntAddr , chrAddr ) ] ;
}
2015-07-29 22:10:34 -04:00
} ;