2016-06-18 19:49:00 +00:00
# pragma once
2014-06-24 06:47:32 +00:00
# include "stdafx.h"
2015-07-02 03:17:14 +00:00
# include "CPU.h"
2014-06-24 06:47:32 +00:00
# include "BaseMapper.h"
2014-06-24 14:19:24 +00:00
class MMC1 : public BaseMapper
2014-06-24 06:47:32 +00:00
{
private :
enum class MMC1Registers
{
Reg8000 = 0 ,
RegA000 = 1 ,
RegC000 = 2 ,
RegE000 = 3
} ;
enum class PrgMode
{
_16k = 16 ,
_32k = 32 ,
} ;
enum class ChrMode
{
_4k = 4 ,
_8k = 8 ,
} ;
enum class SlotSelect
{
x8000 = 0x8000 ,
xC000 = 0xC000 ,
} ;
uint8_t _writeBuffer = 0 ;
uint8_t _shiftCount = 0 ;
bool _wramDisable ;
ChrMode _chrMode ;
PrgMode _prgMode ;
SlotSelect _slotSelect ;
2014-06-24 14:31:33 +00:00
uint8_t _chrReg0 ;
2014-06-24 06:47:32 +00:00
uint8_t _chrReg1 ;
uint8_t _prgReg ;
2014-07-29 00:52:47 +00:00
int32_t _lastWriteCycle = - 1 ;
2014-06-24 06:47:32 +00:00
2016-07-26 23:19:28 +00:00
bool _forceWramOn ;
MMC1Registers _lastChrReg ;
2014-06-24 06:47:32 +00:00
private :
bool HasResetFlag ( uint8_t value )
{
return ( value & 0x80 ) = = 0x80 ;
}
void ResetBuffer ( )
{
_shiftCount = 0 ;
_writeBuffer = 0 ;
}
bool IsBufferFull ( uint8_t value )
{
if ( HasResetFlag ( value ) ) {
//When 'r' is set:
// - 'd' is ignored
// - hidden temporary reg is reset (so that the next write is the "first" write)
// - bits 2,3 of reg $8000 are set (16k PRG mode, $8000 swappable)
// - other bits of $8000 (and other regs) are unchanged
ResetBuffer ( ) ;
_state . Reg8000 | = 0x0C ;
2018-07-16 23:23:50 +00:00
UpdateState ( ) ;
2014-06-24 06:47:32 +00:00
return false ;
} else {
_writeBuffer > > = 1 ;
_writeBuffer | = ( ( value < < 4 ) & 0x10 ) ;
_shiftCount + + ;
return _shiftCount = = 5 ;
}
}
2016-06-18 19:49:00 +00:00
protected :
struct
{
uint8_t Reg8000 ;
uint8_t RegA000 ;
uint8_t RegC000 ;
uint8_t RegE000 ;
} _state ;
virtual void UpdateState ( )
2014-06-24 06:47:32 +00:00
{
switch ( _state . Reg8000 & 0x03 ) {
2015-07-30 02:10:34 +00:00
case 0 : SetMirroringType ( MirroringType : : ScreenAOnly ) ; break ;
case 1 : SetMirroringType ( MirroringType : : ScreenBOnly ) ; break ;
case 2 : SetMirroringType ( MirroringType : : Vertical ) ; break ;
case 3 : SetMirroringType ( MirroringType : : Horizontal ) ; break ;
2014-06-24 06:47:32 +00:00
}
_wramDisable = ( _state . RegE000 & 0x10 ) = = 0x10 ;
_slotSelect = ( ( _state . Reg8000 & 0x04 ) = = 0x04 ) ? SlotSelect : : x8000 : SlotSelect : : xC000 ;
_prgMode = ( ( _state . Reg8000 & 0x08 ) = = 0x08 ) ? PrgMode : : _16k : PrgMode : : _32k ;
_chrMode = ( ( _state . Reg8000 & 0x10 ) = = 0x10 ) ? ChrMode : : _4k : ChrMode : : _8k ;
2014-06-24 14:31:33 +00:00
_chrReg0 = _state . RegA000 & 0x1F ;
_chrReg1 = _state . RegC000 & 0x1F ;
2014-06-24 06:47:32 +00:00
_prgReg = _state . RegE000 & 0x0F ;
2016-07-26 23:19:28 +00:00
uint8_t extraReg = _lastChrReg = = MMC1Registers : : RegC000 & & _chrMode = = ChrMode : : _4k ? _chrReg1 : _chrReg0 ;
uint8_t prgBankSelect = 0 ;
if ( _prgSize = = 0x80000 ) {
//512kb carts use bit 7 of $A000/$C000 to select page
//This is used for SUROM (Dragon Warrior 3/4, Dragon Quest 4)
prgBankSelect = extraReg & 0x10 ;
}
if ( _wramDisable & & ! _forceWramOn ) {
RemoveCpuMemoryMapping ( 0x6000 , 0x7FFF ) ;
} else {
if ( _saveRamSize + _workRamSize > 0x4000 ) {
//SXROM, 32kb of save ram
SetCpuMemoryMapping ( 0x6000 , 0x7FFF , ( extraReg > > 2 ) & 0x03 , HasBattery ( ) ? PrgMemoryType : : SaveRam : PrgMemoryType : : WorkRam ) ;
} else if ( _saveRamSize + _workRamSize > 0x2000 ) {
if ( _saveRamSize = = 0x2000 & & _workRamSize = = 0x2000 ) {
//SOROM, half of the 16kb ram is battery backed
SetCpuMemoryMapping ( 0x6000 , 0x7FFF , 0 , ( extraReg > > 3 ) & 0x01 ? PrgMemoryType : : WorkRam : PrgMemoryType : : SaveRam ) ;
} else {
//Unknown, shouldn't happen
SetCpuMemoryMapping ( 0x6000 , 0x7FFF , ( extraReg > > 2 ) & 0x01 , HasBattery ( ) ? PrgMemoryType : : SaveRam : PrgMemoryType : : WorkRam ) ;
}
} else {
//Everything else - 8kb of work or save ram
SetCpuMemoryMapping ( 0x6000 , 0x7FFF , 0 , HasBattery ( ) ? PrgMemoryType : : SaveRam : PrgMemoryType : : WorkRam ) ;
}
}
2018-07-07 18:52:51 +00:00
if ( _romInfo . SubMapperID = = 5 ) {
2016-06-03 23:16:31 +00:00
//SubMapper 5
//"001: 5 Fixed PRG SEROM, SHROM, SH1ROM use a fixed 32k PRG ROM with no banking support.
SelectPrgPage2x ( 0 , 0 ) ;
} else {
if ( _prgMode = = PrgMode : : _32k ) {
2016-07-26 23:19:28 +00:00
SelectPrgPage2x ( 0 , ( _prgReg & 0xFE ) | prgBankSelect ) ;
2016-06-03 23:16:31 +00:00
} else if ( _prgMode = = PrgMode : : _16k ) {
if ( _slotSelect = = SlotSelect : : x8000 ) {
2016-07-26 23:19:28 +00:00
SelectPRGPage ( 0 , _prgReg | prgBankSelect ) ;
SelectPRGPage ( 1 , 0x0F | prgBankSelect ) ;
2016-06-03 23:16:31 +00:00
} else if ( _slotSelect = = SlotSelect : : xC000 ) {
2016-07-26 23:19:28 +00:00
SelectPRGPage ( 0 , 0 | prgBankSelect ) ;
SelectPRGPage ( 1 , _prgReg | prgBankSelect ) ;
2016-06-03 23:16:31 +00:00
}
2014-06-24 06:47:32 +00:00
}
2016-06-03 23:16:31 +00:00
}
2014-06-24 06:47:32 +00:00
if ( _chrMode = = ChrMode : : _8k ) {
2014-06-28 00:21:23 +00:00
SelectCHRPage ( 0 , _chrReg0 & 0x1E ) ;
SelectCHRPage ( 1 , ( _chrReg0 & 0x1E ) + 1 ) ;
2014-06-24 06:47:32 +00:00
} else if ( _chrMode = = ChrMode : : _4k ) {
2014-06-24 14:31:33 +00:00
SelectCHRPage ( 0 , _chrReg0 ) ;
SelectCHRPage ( 1 , _chrReg1 ) ;
2014-06-24 06:47:32 +00:00
}
}
2016-12-18 04:14:47 +00:00
virtual void StreamState ( bool saving ) override
2014-06-26 01:52:37 +00:00
{
BaseMapper : : StreamState ( saving ) ;
2016-07-26 23:19:28 +00:00
Stream ( _state . Reg8000 , _state . RegA000 , _state . RegC000 , _state . RegE000 , _writeBuffer , _shiftCount , _lastWriteCycle , _lastChrReg ) ;
if ( ! saving ) {
UpdateState ( ) ;
}
2014-06-26 01:52:37 +00:00
}
2016-12-18 04:14:47 +00:00
virtual uint16_t GetPRGPageSize ( ) override { return 0x4000 ; }
virtual uint16_t GetCHRPageSize ( ) override { return 0x1000 ; }
2014-06-24 14:19:24 +00:00
2016-12-18 04:14:47 +00:00
virtual void InitMapper ( ) override
2014-06-24 06:47:32 +00:00
{
2018-06-25 02:20:40 +00:00
_state . Reg8000 = GetPowerOnByte ( ) | 0x0C ; //On powerup: bits 2,3 of $8000 are set (this ensures the $8000 is bank 0, and $C000 is the last bank - needed for SEROM/SHROM/SH1ROM which do no support banking)
2018-04-15 02:12:05 +00:00
_state . RegA000 = GetPowerOnByte ( ) ;
_state . RegC000 = GetPowerOnByte ( ) ;
2018-07-07 18:52:51 +00:00
_state . RegE000 = ( _romInfo . DatabaseInfo . Board . find ( " MMC1B " ) ! = string : : npos ? 0x10 : 0x00 ) ; //WRAM Disable: enabled by default for MMC1B
2014-06-24 06:47:32 +00:00
2016-07-26 23:19:28 +00:00
//"MMC1A: PRG RAM is always enabled" - Normally these roms should be classified as mapper 155
2018-07-07 18:52:51 +00:00
_forceWramOn = ( _romInfo . DatabaseInfo . Board . compare ( " MMC1A " ) = = 0 ) ;
2016-07-26 23:19:28 +00:00
_lastChrReg = MMC1Registers : : RegA000 ;
2015-08-09 23:20:49 +00:00
2014-06-24 06:47:32 +00:00
UpdateState ( ) ;
}
2016-12-18 04:14:47 +00:00
virtual void WriteRegister ( uint16_t addr , uint8_t value ) override
2014-06-24 06:47:32 +00:00
{
2018-07-01 19:21:05 +00:00
int32_t currentCycle = _console - > GetCpu ( ) - > GetCycleCount ( ) ;
2014-07-29 00:52:47 +00:00
//Ignore write if within 2 cycles of another write (i.e the real write after a dummy write)
if ( abs ( currentCycle - _lastWriteCycle ) > = 2 ) {
if ( IsBufferFull ( value ) ) {
switch ( ( MMC1Registers ) ( ( addr & 0x6000 ) > > 13 ) ) {
case MMC1Registers : : Reg8000 : _state . Reg8000 = _writeBuffer ; break ;
2016-07-26 23:19:28 +00:00
case MMC1Registers : : RegA000 :
_lastChrReg = MMC1Registers : : RegA000 ;
_state . RegA000 = _writeBuffer ;
break ;
case MMC1Registers : : RegC000 :
_lastChrReg = MMC1Registers : : RegC000 ;
_state . RegC000 = _writeBuffer ;
break ;
2014-07-29 00:52:47 +00:00
case MMC1Registers : : RegE000 : _state . RegE000 = _writeBuffer ; break ;
}
UpdateState ( ) ;
//Reset buffer after writing 5 bits
ResetBuffer ( ) ;
2014-06-24 06:47:32 +00:00
}
}
2014-07-29 00:52:47 +00:00
_lastWriteCycle = currentCycle ;
2014-06-24 06:47:32 +00:00
}
} ;