Files
DbNesEmulator/nescorelib/mappers/mapper001.cpp
2018-12-16 22:19:06 +01:00

217 lines
5.4 KiB
C++

#include "mapper001.h"
#include "utils/datastreamutils.h"
QString Mapper001::name() const
{
return QStringLiteral("MMC1");
}
quint8 Mapper001::mapper() const
{
return 1;
}
void Mapper001::hardReset()
{
Board::hardReset();
m_cpuCycles = 0;
// Registers
m_addressReg = 0;
m_reg = { 0x0C, 0, 0, 0 };
m_reg[0] = 0x0C;
m_flagC = false;
m_flagS = true;
m_flagP = true;
m_prgHijackedBit = 0;
// Buffers
m_buffer = 0;
m_shift = 0;
//if (Chips.Contains("MMC1B") || Chips.Contains("MMC1B2"))
// TogglePRGRAMEnable(false);
m_enableWramEnable = true; // !Chips.Contains("MMC1A");
// use hijacked
m_useHijacked = (prgRom16KbMask() & 0x10) == 0x10;
if (m_useHijacked)
m_prgHijackedBit = 0x10;
// SRAM Switch ?
m_useSramSwitch = false;
if (prgRam8KbCount() > 0)
{
m_useSramSwitch = true;
m_sramSwitchMask = m_useHijacked ? 0x08 : 0x18;
m_sramSwitchMask &= prgRam8KbMask() << 3;
if (m_sramSwitchMask == 0)
m_useSramSwitch = false;
}
switch16kPrg(0xF | m_prgHijackedBit, PRGArea::AreaC000);
}
void Mapper001::writePrg(quint16 address, quint8 value)
{
// Too close writes ignored !
if (m_cpuCycles > 0)
return;
m_cpuCycles = 3;// Make save cycles ...
//Temporary reg port ($8000-FFFF):
//[r... ...d]
//r = reset flag
//d = data bit
//r is set
if ((value & 0x80) == 0x80)
{
m_reg[0] |= 0x0C;//bits 2,3 of reg $8000 are set (16k PRG mode, $8000 swappable)
m_flagS = true;
m_flagP = true;
m_shift = 0;
m_buffer = 0;//hidden temporary reg is reset
return;
}
//d is set
if ((value & 0x01) == 0x01)
m_buffer |= 1 << m_shift; //'d' proceeds as the next bit written in the 5-bit sequence
if (++m_shift < 5)
return;
// If this completes the 5-bit sequence:
// - temporary reg is copied to actual internal reg (which reg depends on the last address written to)
m_addressReg = (address & 0x7FFF) >> 13;
m_reg[m_addressReg] = m_buffer;
// - temporary reg is reset (so that next write is the "first" write)
m_shift = 0;
m_buffer = 0;
// Update internal registers ...
switch (m_addressReg)
{
case 0:// $8000-9FFF [Flags and mirroring]
// Flags
m_flagC = m_reg[0] & 0x10;
m_flagP = m_reg[0] & 0x08;
m_flagS = m_reg[0] & 0x04;
updatePRG();
updateCHR();
// Mirroring
switch (m_reg[0] & 3)
{
case 0: switch1kNmt(Mirroring::OneScA); break;
case 1: switch1kNmt(Mirroring::OneScB); break;
case 2: switch1kNmt(Mirroring::Vertical); break;
case 3: switch1kNmt(Mirroring::Horizontal); break;
}
break;
case 1:// $A000-BFFF [CHR REG 0]
// CHR
if (!m_flagC)
switch8kChr(m_reg[1] >> 1);
else
switch4kChr(m_reg[1], CHRArea::Area0000);
// SRAM
if (m_useSramSwitch)
switch8kPrg((m_reg[1] & m_sramSwitchMask) >> 3, PRGArea::Area6000);
// PRG hijack
if (m_useHijacked)
{
m_prgHijackedBit = m_reg[1] & 0x10;
updatePRG();
}
break;
case 2:// $C000-DFFF [CHR REG 1]
// CHR
if (m_flagC)
switch4kChr(m_reg[2], CHRArea::Area1000);
// SRAM
if (m_useSramSwitch)
switch8kPrg((m_reg[2] & m_sramSwitchMask) >> 3, PRGArea::Area6000);
// PRG hijack
if (m_useHijacked)
{
m_prgHijackedBit = m_reg[2] & 0x10;
updatePRG();
}
break;
case 3:// $E000-FFFF [PRG REG]
if (m_enableWramEnable)
togglePrgRamEnable((m_reg[3] & 0x10) == 0);
updatePRG();
break;
}
}
void Mapper001::onCpuClock()
{
if (m_cpuCycles > 0)
m_cpuCycles--;
}
void Mapper001::readState(QDataStream &dataStream)
{
Board::readState(dataStream);
dataStream >> m_reg >> m_shift >> m_buffer >> m_flagP >> m_flagC >> m_flagS >> m_enableWramEnable
>> m_prgHijackedBit >> m_useHijacked >> m_useSramSwitch >> m_cpuCycles;
}
void Mapper001::writeState(QDataStream &dataStream) const
{
Board::writeState(dataStream);
dataStream << m_reg << m_shift << m_buffer << m_flagP << m_flagC << m_flagS << m_enableWramEnable
<< m_prgHijackedBit << m_useHijacked << m_useSramSwitch << m_cpuCycles;
}
int Mapper001::prgRam8KbDefaultBlkCount() const
{
return 4;
}
int Mapper001::chrRom1KbDefaultBlkCount() const
{
return 64;
}
void Mapper001::updateCHR()
{
if (!m_flagC)
switch8kChr(m_reg[1] >> 1);
else
{
switch4kChr(m_reg[1], CHRArea::Area0000);
switch4kChr(m_reg[2], CHRArea::Area1000);
}
// SRAM
if (m_useSramSwitch)
switch8kPrg((m_reg[1] & m_sramSwitchMask) >> 3, PRGArea::Area6000);
}
void Mapper001::updatePRG()
{
if (!m_flagP)
{
switch32kPrg(((m_reg[3] & 0xF) | m_prgHijackedBit) >> 1, PRGArea::Area8000);
}
else
{
if (m_flagS)
{
switch16kPrg((m_reg[3] & 0xF) | m_prgHijackedBit, PRGArea::Area8000);
switch16kPrg(0xF | m_prgHijackedBit, PRGArea::AreaC000);
}
else
{
switch16kPrg(m_prgHijackedBit, PRGArea::Area8000);
switch16kPrg((m_reg[3] & 0xF) | m_prgHijackedBit, PRGArea::AreaC000);
}
}
}