Files
GBpp/src/mbc.cpp
2026-03-18 01:09:03 -07:00

183 lines
5.1 KiB
C++

#include "addressSpace.hpp"
#include "defines.hpp"
void AddressSpace::determineMBCInfo() {
MBC = static_cast<MBCType_enum>(memoryLayout.romBank0[0x147]);
romSize = 32768 * (1 << memoryLayout.romBank0[0x147]);
romBanks = 1 << (memoryLayout.romBank0[0x147] + 1);
const Byte ramSize = memoryLayout.romBank0[0x0149];
switch (ramSize) {
case 0x02:
externalRamSize = 8196;
externalRamBanks = 1;
break;
case 0x03:
externalRamSize = 32768;
externalRamBanks = 4;
break;
case 0x04:
externalRamSize = 131072;
externalRamBanks = 16;
break;
case 0x05:
externalRamSize = 65536;
externalRamBanks = 8;
break;
default:
externalRamSize = 0;
externalRamBanks = 0;
break;
}
if (MBC == MBC2 || MBC == MBC2Battery) {
// only the lower 4 bits are usable
externalRamSize = 512;
}
}
Byte *AddressSpace::MBCWrite(const Word address) {
if (MBC == MBC1) {
if (address <= 0x1FFF)
return &ramEnable;
if (address <= 0x3FFF)
return &romBankRegister;
if (address <= 0x5FFF)
return &twoBitBankRegister;
if (address <= 0x7FFF)
return &romRamSelect;
} else if (MBC == MBC1Ram || MBC == MBC1RamBattery) {
if (address <= 0x1FFF)
return &ramEnable;
// bits 0-4
if (address <= 0x3FFF)
return &romBankRegister;
if (address <= 0x5FFF) {
return &twoBitBankRegister;
}
if (address <= 0x7FFF)
return &romRamSelect;
} else if (MBC == MBC2 || MBC == MBC2Battery) {
if (address <= 0x3FFF) {
if (address & 0x0100)
return &romBankRegister;
else
return &ramEnable;
}
} else if (MBC == MBC3 || MBC == MBC3TimerBattery) {
if (address <= 0x1FFF)
return &ramEnable;
} else if (MBC == MBC3Ram || MBC == MBC3RamBattery ||
MBC == MBC3TimerRamBattery) {
if (address <= 0x1FFF)
return &ramEnable;
if (address <= 0x3FFF)
return &romBankRegister;
if (address <= 0x5FFF)
return &ramBankRTCRegister;
}
return &dummyVal;
}
void AddressSpace::MBCUpdate() {
// TODO: multicart roms need to be able to switch the first rom bank as well
// see: https://gbdev.io/pandocs/MBC1.html
if (MBC == MBC1) {
// Selected ROM Bank = (Secondary Bank << 5) + ROM Bank
romBankRegister &= 0b11111;
twoBitBankRegister &= 0b11;
// 512 KiB can only have 8KiB of ram
if (romSize >= 524288) {
if (romBankRegister == 0)
selectedRomBank = (twoBitBankRegister << 5) + 1;
selectedRomBank = (twoBitBankRegister << 5) + romBankRegister;
} else {
if (romBankRegister == 0)
selectedRomBank = 1;
else
selectedRomBank = romBankRegister;
}
if (romBankRegister == 0x20 || romBankRegister == 0x40 ||
romBankRegister == 0x60)
romBankRegister += 1;
selectedExternalRamBank = 0;
loadRomBank();
loadRamBank();
} else if (MBC == MBC1Ram || MBC == MBC1RamBattery) {
// Selected ROM Bank = (Secondary Bank << 5) + ROM Bank
romBankRegister &= 0b11111;
twoBitBankRegister &= 0b11;
// 512 KiB can only have 8KiB of ram
if (romSize >= 524288) {
if (romBankRegister == 0)
selectedRomBank = (twoBitBankRegister << 5) + 1;
selectedRomBank = (twoBitBankRegister << 5) + romBankRegister;
selectedExternalRamBank = 0;
} else {
if (romBankRegister == 0)
selectedRomBank = 1;
else
selectedRomBank = romBankRegister;
selectedExternalRamBank = twoBitBankRegister;
}
if (romBankRegister == 0x20 || romBankRegister == 0x40 ||
romBankRegister == 0x60)
romBankRegister += 1;
loadRomBank();
loadRamBank();
} else if (MBC == MBC2 || MBC == MBC2Battery) {
selectedRomBank = romBankRegister & 0x0F;
selectedExternalRamBank = 0;
loadRomBank();
loadRamBank();
} else if (MBC == MBC3 || MBC == MBC3TimerBattery) {
selectedRomBank = romBankRegister & 0x7F;
selectedExternalRamBank = 0;
if (MBC == MBC3TimerBattery) {
if(ramBankRTCRegister >= 0x8 && ramBankRTCRegister <= 0xC)
enabledRTCRegister = static_cast<enabledRTCRegister_enum>(ramBankRTCRegister);
else
enabledRTCRegister = NONE;
}
loadRomBank();
loadRamBank();
} else if (MBC == MBC3Ram || MBC == MBC3RamBattery ||
MBC == MBC3TimerRamBattery) {
selectedRomBank = romBankRegister & 0x7F;
selectedExternalRamBank = ramBankRTCRegister & 0b11;
if (MBC == MBC3TimerRamBattery) {
if(ramBankRTCRegister >= 0x8 && ramBankRTCRegister <= 0xC)
enabledRTCRegister = static_cast<enabledRTCRegister_enum>(ramBankRTCRegister);
else
enabledRTCRegister = NONE;
}
loadRomBank();
loadRamBank();
}
}
void AddressSpace::loadRomBank() {
memoryLayout.romBankSwitch = game.data() + (ROM_BANK_SIZE * selectedRomBank);
}
void AddressSpace::createRamBank() {
if (externalRamSize) {
cartridgeRam = new Byte[externalRamSize];
memoryLayout.externalRam = cartridgeRam;
}
}
void AddressSpace::loadRamBank() {
if (cartridgeRam != nullptr) {
memoryLayout.externalRam =
cartridgeRam + (RAM_BANK_SIZE * selectedExternalRamBank);
}
}