217 lines
5.4 KiB
C++
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);
|
|
}
|
|
}
|
|
}
|