2014-07-08 18:36:21 -04:00
# pragma once
# include "stdafx.h"
# include "BaseMapper.h"
2016-01-24 15:17:25 -05:00
# include "VrcIrq.h"
2014-07-08 18:36:21 -04:00
enum class VRCVariant
{
VRC2a , //Mapper 22
VRC2b , //23
VRC2c , //25
VRC4a , //21
VRC4b , //25
VRC4c , //21
VRC4d , //25
VRC4e , //23
VRC4_27 , //27
2016-01-24 14:20:35 -05:00
VRC6a ,
VRC6b
2014-07-08 18:36:21 -04:00
} ;
class VRC2_4 : public BaseMapper
{
private :
2018-07-01 15:21:05 -04:00
unique_ptr < VrcIrq > _irq ;
2014-07-08 18:36:21 -04:00
VRCVariant _variant ;
2016-06-15 22:01:15 -04:00
bool _useHeuristics ;
2014-07-08 18:36:21 -04:00
uint8_t _prgReg0 ;
uint8_t _prgReg1 ;
uint8_t _prgMode ;
uint8_t _hiCHRRegs [ 8 ] ;
uint8_t _loCHRRegs [ 8 ] ;
2020-05-17 11:16:15 -04:00
uint8_t _latch = 0 ;
2014-07-08 18:36:21 -04:00
2016-06-15 22:01:15 -04:00
void DetectVariant ( )
{
2018-07-07 14:52:51 -04:00
switch ( _romInfo . MapperID ) {
2016-06-15 22:01:15 -04:00
default :
case 21 :
//Conflicts: VRC4c
2018-07-07 14:52:51 -04:00
switch ( _romInfo . SubMapperID ) {
2016-06-15 22:01:15 -04:00
default :
case 0 : _variant = VRCVariant : : VRC4a ; break ;
case 1 : _variant = VRCVariant : : VRC4a ; break ;
case 2 : _variant = VRCVariant : : VRC4c ; break ;
}
break ;
case 22 : _variant = VRCVariant : : VRC2a ; break ;
case 23 :
//Conflicts: VRC4e
2018-07-07 14:52:51 -04:00
switch ( _romInfo . SubMapperID ) {
2016-06-15 22:01:15 -04:00
default :
case 0 : _variant = VRCVariant : : VRC2b ; break ;
case 2 : _variant = VRCVariant : : VRC4e ; break ;
case 3 : _variant = VRCVariant : : VRC2b ; break ;
}
break ;
case 25 :
//Conflicts: VRC2c, VRC4d
2018-07-07 14:52:51 -04:00
switch ( _romInfo . SubMapperID ) {
2016-06-15 22:01:15 -04:00
default :
case 0 : _variant = VRCVariant : : VRC4b ; break ;
case 1 : _variant = VRCVariant : : VRC4b ; break ;
case 2 : _variant = VRCVariant : : VRC4d ; break ;
case 3 : _variant = VRCVariant : : VRC2c ; break ;
}
break ;
case 27 : _variant = VRCVariant : : VRC4_27 ; break ; //Untested
}
2018-07-07 14:52:51 -04:00
_useHeuristics = ( _romInfo . SubMapperID = = 0 ) & & _romInfo . MapperID ! = 22 & & _romInfo . MapperID ! = 27 ;
2016-06-15 22:01:15 -04:00
}
2014-07-08 18:36:21 -04:00
protected :
2020-05-17 11:16:15 -04:00
uint16_t GetPRGPageSize ( ) override { return 0x2000 ; }
uint16_t GetCHRPageSize ( ) override { return 0x0400 ; }
bool AllowRegisterRead ( ) override { return true ; }
2014-07-08 18:36:21 -04:00
2016-12-17 23:14:47 -05:00
void InitMapper ( ) override
2014-07-08 18:36:21 -04:00
{
2018-07-01 15:21:05 -04:00
_irq . reset ( new VrcIrq ( _console ) ) ;
2016-06-15 22:01:15 -04:00
DetectVariant ( ) ;
2020-05-17 11:16:15 -04:00
//PRG mode only exists for VRC4+ (so keep it as 0 at all times for VRC2)
_prgMode = _variant > = VRCVariant : : VRC4a ? ( GetPowerOnByte ( ) & 0x01 ) : 0 ;
2018-04-14 22:12:05 -04:00
_prgReg0 = GetPowerOnByte ( ) & 0x1F ;
_prgReg1 = GetPowerOnByte ( ) & 0x1F ;
2020-05-17 11:16:15 -04:00
_latch = false ;
2018-04-14 22:12:05 -04:00
for ( int i = 0 ; i < 8 ; i + + ) {
_loCHRRegs [ i ] = GetPowerOnByte ( ) & 0x0F ;
_hiCHRRegs [ i ] = GetPowerOnByte ( ) & 0x1F ;
}
2014-07-08 18:36:21 -04:00
UpdateState ( ) ;
2020-05-17 11:16:15 -04:00
RemoveRegisterRange ( 0 , 0xFFFF , MemoryOperation : : Read ) ;
if ( ! _useHeuristics & & _variant < = VRCVariant : : VRC2c & & _workRamSize = = 0 & & _saveRamSize = = 0 ) {
AddRegisterRange ( 0x6000 , 0x7FFF , MemoryOperation : : Any ) ;
}
2014-07-08 18:36:21 -04:00
}
2018-07-01 15:21:05 -04:00
2016-12-17 23:14:47 -05:00
void ProcessCpuClock ( ) override
2016-01-24 15:17:25 -05:00
{
2018-07-07 14:52:51 -04:00
if ( ( _useHeuristics & & _romInfo . MapperID ! = 22 ) | | _variant > = VRCVariant : : VRC4a ) {
2017-09-02 16:38:09 -04:00
//Only VRC4 supports IRQs
2018-07-01 15:21:05 -04:00
_irq - > ProcessCpuClock ( ) ;
2017-09-02 16:38:09 -04:00
}
2016-01-24 15:17:25 -05:00
}
2014-07-08 18:36:21 -04:00
void UpdateState ( )
{
for ( int i = 0 ; i < 8 ; i + + ) {
uint32_t page = _loCHRRegs [ i ] | ( _hiCHRRegs [ i ] < < 4 ) ;
if ( _variant = = VRCVariant : : VRC2a ) {
//"On VRC2a (mapper 022) only the high 7 bits of the CHR regs are used -- the low bit is ignored. Therefore, you effectively have to right-shift the CHR page by 1 to get the actual page number."
page > > = 1 ;
}
SelectCHRPage ( i , page ) ;
}
if ( _prgMode = = 0 ) {
SelectPRGPage ( 0 , _prgReg0 ) ;
SelectPRGPage ( 1 , _prgReg1 ) ;
SelectPRGPage ( 2 , - 2 ) ;
SelectPRGPage ( 3 , - 1 ) ;
} else {
SelectPRGPage ( 0 , - 2 ) ;
SelectPRGPage ( 1 , _prgReg1 ) ;
SelectPRGPage ( 2 , _prgReg0 ) ;
SelectPRGPage ( 3 , - 1 ) ;
}
}
2020-05-17 11:16:15 -04:00
uint8_t ReadRegister ( uint16_t addr ) override
{
//Microwire interface ($6000-$6FFF) (VRC2 only)
return _latch | ( _console - > GetMemoryManager ( ) - > GetOpenBus ( ) & 0xFE ) ;
}
2016-12-17 23:14:47 -05:00
void WriteRegister ( uint16_t addr , uint8_t value ) override
2014-07-12 22:22:40 -04:00
{
2020-05-17 11:16:15 -04:00
if ( addr < 0x8000 ) {
//Microwire interface ($6000-$6FFF) (VRC2 only)
_latch = value & 0x01 ;
return ;
}
2016-06-15 22:01:15 -04:00
addr = TranslateAddress ( addr ) & 0xF00F ;
2014-07-12 22:22:40 -04:00
if ( addr > = 0x8000 & & addr < = 0x8006 ) {
_prgReg0 = value & 0x1F ;
2016-07-17 17:18:47 -04:00
} else if ( ( _variant < = VRCVariant : : VRC2c & & addr > = 0x9000 & & addr < = 0x9003 ) | | ( _variant > = VRCVariant : : VRC4a & & addr > = 0x9000 & & addr < = 0x9001 ) ) {
2016-06-15 22:01:15 -04:00
uint8_t mask = 0x03 ;
2017-09-02 16:38:09 -04:00
if ( ! _useHeuristics & & ( _variant > = VRCVariant : : VRC2a & & _variant < = VRCVariant : : VRC2c ) ) {
//When we are certain this is a VRC2 game, only use the first bit for mirroring selection
2016-06-15 22:01:15 -04:00
mask = 0x01 ;
}
switch ( value & mask ) {
2015-07-29 22:10:34 -04:00
case 0 : SetMirroringType ( MirroringType : : Vertical ) ; break ;
case 1 : SetMirroringType ( MirroringType : : Horizontal ) ; break ;
case 2 : SetMirroringType ( MirroringType : : ScreenAOnly ) ; break ;
case 3 : SetMirroringType ( MirroringType : : ScreenBOnly ) ; break ;
2014-07-12 22:22:40 -04:00
}
2016-07-17 17:18:47 -04:00
} else if ( _variant > = VRCVariant : : VRC4a & & addr > = 0x9002 & & addr < = 0x9003 ) {
_prgMode = ( value > > 1 ) & 0x01 ;
2014-07-12 22:22:40 -04:00
} else if ( addr > = 0xA000 & & addr < = 0xA006 ) {
_prgReg1 = value & 0x1F ;
} else if ( addr > = 0xB000 & & addr < = 0xE006 ) {
uint8_t regNumber = ( ( ( ( addr > > 12 ) & 0x07 ) - 3 ) < < 1 ) + ( ( addr > > 1 ) & 0x01 ) ;
bool lowBits = ( addr & 0x01 ) = = 0x00 ;
if ( lowBits ) {
//The other reg contains the low 4 bits
_loCHRRegs [ regNumber ] = value & 0x0F ;
} else {
//One reg contains the high 5 bits
_hiCHRRegs [ regNumber ] = value & 0x1F ;
}
2016-01-24 15:17:25 -05:00
} else if ( addr = = 0xF000 ) {
2018-07-01 15:21:05 -04:00
_irq - > SetReloadValueNibble ( value , false ) ;
2016-01-24 15:17:25 -05:00
} else if ( addr = = 0xF001 ) {
2018-07-01 15:21:05 -04:00
_irq - > SetReloadValueNibble ( value , true ) ;
2014-07-12 22:22:40 -04:00
} else if ( addr = = 0xF002 ) {
2018-07-01 15:21:05 -04:00
_irq - > SetControlValue ( value ) ;
2014-07-12 22:22:40 -04:00
} else if ( addr = = 0xF003 ) {
2018-07-01 15:21:05 -04:00
_irq - > AcknowledgeIrq ( ) ;
2014-07-12 22:22:40 -04:00
}
UpdateState ( ) ;
}
2014-07-08 18:36:21 -04:00
public :
uint16_t TranslateAddress ( uint16_t addr )
{
2016-06-15 22:01:15 -04:00
uint32_t A0 , A1 ;
2014-07-08 18:36:21 -04:00
2016-06-15 22:01:15 -04:00
if ( _useHeuristics ) {
switch ( _variant ) {
case VRCVariant : : VRC2c :
case VRCVariant : : VRC4b :
case VRCVariant : : VRC4d :
//Mapper 25
//ORing both values should make most games work.
//VRC2c & VRC4b (Both uses the same bits)
A0 = ( addr > > 1 ) & 0x01 ;
A1 = ( addr & 0x01 ) ;
//VRC4d
A0 | = ( addr > > 3 ) & 0x01 ;
A1 | = ( addr > > 2 ) & 0x01 ;
break ;
case VRCVariant : : VRC4a :
case VRCVariant : : VRC4c :
//Mapper 21
//VRC4a
A0 = ( addr > > 1 ) & 0x01 ;
A1 = ( addr > > 2 ) & 0x01 ;
//VRC4c
A0 | = ( addr > > 6 ) & 0x01 ;
A1 | = ( addr > > 7 ) & 0x01 ;
break ;
case VRCVariant : : VRC2b :
case VRCVariant : : VRC4e :
//Mapper 23
//VRC2b
A0 = addr & 0x01 ;
A1 = ( addr > > 1 ) & 0x01 ;
//VRC4e
A0 | = ( addr > > 2 ) & 0x01 ;
A1 | = ( addr > > 3 ) & 0x01 ;
break ;
default :
throw std : : runtime_error ( " not supported " ) ;
break ;
}
} else {
switch ( _variant ) {
case VRCVariant : : VRC2a :
//Mapper 22
A0 = ( addr > > 1 ) & 0x01 ;
A1 = ( addr & 0x01 ) ;
break ;
case VRCVariant : : VRC4_27 :
//Mapper 27
A0 = addr & 0x01 ;
A1 = ( addr > > 1 ) & 0x01 ;
break ;
case VRCVariant : : VRC2c :
case VRCVariant : : VRC4b :
//Mapper 25
A0 = ( addr > > 1 ) & 0x01 ;
A1 = ( addr & 0x01 ) ;
break ;
case VRCVariant : : VRC4d :
//Mapper 25
A0 = ( addr > > 3 ) & 0x01 ;
A1 = ( addr > > 2 ) & 0x01 ;
break ;
case VRCVariant : : VRC4a :
//Mapper 21
A0 = ( addr > > 1 ) & 0x01 ;
A1 = ( addr > > 2 ) & 0x01 ;
break ;
case VRCVariant : : VRC4c :
//Mapper 21
A0 = ( addr > > 6 ) & 0x01 ;
A1 = ( addr > > 7 ) & 0x01 ;
break ;
case VRCVariant : : VRC2b :
//Mapper 23
A0 = addr & 0x01 ;
A1 = ( addr > > 1 ) & 0x01 ;
break ;
case VRCVariant : : VRC4e :
//Mapper 23
A0 = ( addr > > 2 ) & 0x01 ;
A1 = ( addr > > 3 ) & 0x01 ;
break ;
default :
throw std : : runtime_error ( " not supported " ) ;
break ;
}
2016-01-24 15:20:57 -05:00
2014-07-08 18:36:21 -04:00
}
return ( addr & 0xFF00 ) | ( A1 < < 1 ) | A0 ;
}
2016-12-17 23:14:47 -05:00
void StreamState ( bool saving ) override
2014-07-08 18:36:21 -04:00
{
2015-07-29 22:10:34 -04:00
BaseMapper : : StreamState ( saving ) ;
2016-06-02 23:56:11 -04:00
ArrayInfo < uint8_t > loChrRegs = { _loCHRRegs , 8 } ;
ArrayInfo < uint8_t > hiChrRegs = { _hiCHRRegs , 8 } ;
2018-07-01 15:21:05 -04:00
SnapshotInfo irq { _irq . get ( ) } ;
2020-05-17 11:16:15 -04:00
Stream ( _prgReg0 , _prgReg1 , _prgMode , loChrRegs , hiChrRegs , _latch , irq ) ;
2014-07-08 18:36:21 -04:00
}
} ;