You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
3.5 KiB
93 lines
3.5 KiB
/* GameBoyInstance.Mbc.cpp |
|
* |
|
* Handles the emulation of the various memory bank controllers (MBC) |
|
* contained within game cartridges. |
|
*/ |
|
|
|
#include <sstream> |
|
|
|
#include "../../PlipEmulationException.h" |
|
#include "../../PlipUtility.h" |
|
|
|
#include "GameBoyInstance.h" |
|
|
|
namespace Plip::Core::GameBoy { |
|
void GameBoyInstance::MbcInit() { |
|
switch(m_mbc) { |
|
case None: break; |
|
case Mbc1: |
|
m_mbcRomBank = 1; |
|
if(m_hasRam) { |
|
m_cartRam->SetReadable(false); |
|
m_cartRam->SetWritable(false); |
|
} |
|
break; |
|
default: |
|
std::stringstream ex; |
|
ex << "invalid/unsupported memory bank controller: " |
|
<< PlipUtility::FormatHex((int)m_mbc, 2); |
|
throw Plip::PlipEmulationException(ex.str().c_str()); |
|
} |
|
} |
|
|
|
void GameBoyInstance::MbcCycle(PlipMemoryValue lastWrite) { |
|
switch(m_mbc) { |
|
case None: break; |
|
case Mbc1: Mbc1Cycle(lastWrite); break; |
|
default: |
|
std::stringstream ex; |
|
ex << "invalid/unsupported memory bank controller: " |
|
<< PlipUtility::FormatHex((int)m_mbc, 2); |
|
throw Plip::PlipEmulationException(ex.str().c_str()); |
|
} |
|
} |
|
|
|
void GameBoyInstance::Mbc1Cycle(PlipMemoryValue lastWrite) { |
|
if(lastWrite.address >= 0x8000) return; |
|
|
|
if(lastWrite.address < 0x2000) { |
|
// RAM enable register. |
|
if(m_hasRam) { |
|
m_mbcRamEnabled = (lastWrite.value & 0xF) == 0xA; |
|
m_cartRam->SetReadable(m_mbcRamEnabled); |
|
m_cartRam->SetWritable(m_mbcRamEnabled); |
|
} |
|
} else if(lastWrite.address < 0x4000) { |
|
// ROM bank number. If this is set to zero, this will automatically |
|
// be bumped up by one, regardless of what's in the high bit. |
|
m_mbcRomBank &= 0b11100000; |
|
auto newBank = lastWrite.value & 0b11111; |
|
m_mbcRomBank |= newBank + (newBank == 0 ? 1 : 0); |
|
m_mbcRomBank &= (m_romBanks - 1); |
|
m_memory->AssignBlock(m_rom, m_addrBankedRom, |
|
0x4000 * m_mbcRomBank, 0x4000); |
|
} else if(lastWrite.address < 0x6000) { |
|
// RAM bank number, or the upper bits of the ROM bank number on |
|
// 1MB carts. |
|
if(m_romBanks >= 64) { |
|
// Selects the upper bits of ROM. |
|
m_mbcRomBank &= 0b11100000; |
|
m_mbcRomBank |= (lastWrite.value & 0b11) << 5; |
|
m_mbcRomBank &= (m_romBanks - 1); |
|
m_memory->AssignBlock(m_rom, m_addrBankedRom, |
|
0x4000 * m_mbcRomBank, 0x4000); |
|
|
|
// If the advanced ROM banking mode bit is set, this bit impacts |
|
// bank 0. |
|
if(m_mbcMode) { |
|
m_memory->AssignBlock(m_rom, m_addrRom, |
|
0x2000 * m_mbcRomBank & 0b01100000, |
|
0x4000); |
|
} |
|
} else if(m_hasRam && m_mbcRamEnabled && m_mbcMode == 1 && m_cartRamBanks > 1) { |
|
// Selects the RAM bank. |
|
m_mbcRamBank = (lastWrite.value & 0b11) & (m_cartRamBanks - 1); |
|
m_memory->AssignBlock(m_cartRam, m_addrCartRam, |
|
0x2000 * m_mbcRamBank, 0x2000); |
|
} |
|
} else { |
|
// Banking mode select. |
|
m_mbcMode = lastWrite.value & 0b1; |
|
} |
|
} |
|
}
|
|
|