diff --git a/include/cartridge_impls/cartridge.hpp b/include/cartridge_impls/cartridge.hpp index a975fb6984eb6756f60de5bbf9ac4c7b4d1ee219..c2924466da017040d47ce4b1ae0b199aa51dd2d5 100644 --- a/include/cartridge_impls/cartridge.hpp +++ b/include/cartridge_impls/cartridge.hpp @@ -70,8 +70,21 @@ public: ~MBC1Cartridge() = default; }; -class MBC1MCartridge final : public CoreCartridge { +class MBC2Cartridge final : public CoreCartridge { +private: + // MBC2 has RAM consisting of 512 half-bytes + constexpr static unsigned halfByteRamSize = 512; + constexpr static uint8_t halfByteMask = 0b1111; + + constexpr static uint8_t ramEnableValue = + 0x0A; // if any other value is written to RAM enable register, RAM is disabled + + uint8_t selectedRomBankRegister = 1; // 4 bit register for selecting ROM bank + bool ramEnabled = false; // RAM enabled flag + +public: + MBC2Cartridge( std::vector&& rom_ ); uint8_t read( const uint16_t address ) override; void write( const uint16_t address, const uint8_t value ) override; - ~MBC1MCartridge() = default; + ~MBC2Cartridge() = default; }; diff --git a/src/cartridge_impls/mbc2.cpp b/src/cartridge_impls/mbc2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51f5fca261b821e0fb9114bf6a76478cc1f6cd99 --- /dev/null +++ b/src/cartridge_impls/mbc2.cpp @@ -0,0 +1,98 @@ +#include "cartridge_impls/cartridge.hpp" +#include "core/cartridge.hpp" +#include "core/logging.hpp" +#include +#include + +MBC2Cartridge::MBC2Cartridge( std::vector&& rom_ ) : CoreCartridge( std::move( rom_ ) ) { + logDebug( "MBC2Cartridge constructor" ); + + if( getRomSize() > RomSize::_256KiB ) { + logError( 0, "ROM size greater than 256 KiB is not supported by MBC2 cartridge." ); + return; + } + + if( getRamSize() > RamSize::_0KiB ) { + logError( 0, "RAM size other than 0 KiB is not supported by MBC2 cartridge." ); + return; + } + + logDebug( std::format( "Initialize RAM consisting of {} half-bytes", halfByteRamSize ) ); + ramBanks = std::vector>( 1, std::vector( halfByteRamSize, 0 ) ); +}; + +uint8_t MBC2Cartridge::read( const uint16_t address ) { + logDebug( std::format( "Trying to read at address {}", toHex( address ) ) ); + + if( isInPrimaryRomRange( address ) ) { + const auto returnValue = romBanks[0][address]; + logInfo( std::format( "Read value {} from ROM bank 0 at address {}", toHex( returnValue ), + toHex( address ) ) ); + return returnValue; + } + + if( isInSecondaryRomRange( address ) ) { + const uint16_t offset = address % romBankSize; + const auto returnValue = romBanks[selectedRomBankRegister][offset]; + logInfo( std::format( "Read value {} from ROM bank {} at offset {}", toHex( returnValue ), + selectedRomBankRegister, toHex( offset ) ) ); + return returnValue; + } + + if( isInRamRange( address ) ) { + if( ramEnabled ) { + const auto ramBankAddress = ( address - ramStartAddress ) % halfByteRamSize; + const uint8_t returnValue = ramBanks[0][ramBankAddress] & halfByteMask; + logInfo( std::format( "Read value {} from half-byte RAM at offset {}", toHex( returnValue ), + toHex( ramBankAddress ) ) ); + return returnValue; + } + logWarning( 0, std::format( "RAM is not enabled. Invalid read from address {}. Returning {}", + toHex( address ), toHex( invalidReadValue ) ) ); + return invalidReadValue; + } + + logWarning( 0, std::format( "Read operation from address {} is not supported", toHex( address ) ) ); + return invalidReadValue; +} + +void MBC2Cartridge::write( const uint16_t address, const uint8_t value ) { + logDebug( std::format( "Trying to write value {} to address {}", toHex( value ), toHex( address ) ) ); + + if( isInPrimaryRomRange( address ) ) { + if( address & 0x100 ) { // 8th bit is set + selectedRomBankRegister = value & halfByteMask; + if( selectedRomBankRegister == 0 ) { + selectedRomBankRegister = 1; // ROM bank 0 not allowed + } + + logInfo( std::format( "Selected ROM bank register set to {:#06b}", selectedRomBankRegister ) ); + return; + } + + if( value == ramEnableValue ) { + ramEnabled = true; + logInfo( "RAM enabled" ); + return; + } else { + ramEnabled = false; + logInfo( "RAM disabled" ); + return; + } + } + + if( isInRamRange( address ) ) { + if( ramEnabled ) { + const uint16_t offset = ( address - ramStartAddress ) % halfByteRamSize; + ramBanks[0][offset] = value & halfByteMask; + + logInfo( std::format( "Wrote value {} to half-byte RAM offset {}", toHex( value ), + toHex( offset ) ) ); + return; + } + logWarning( 0, "RAM is not enabled. Cannot write to RAM." ); + return; + } + + logWarning( 0, std::format( "Write operation to address {} is not supported", toHex( address ) ) ); +} diff --git a/src/core/cartridge.cpp b/src/core/cartridge.cpp index cdaf7bb0e580d05e82c6a22199550e4b2947bfb5..84a83176798d088eb428c20a0cb9599efbbf25e6 100644 --- a/src/core/cartridge.cpp +++ b/src/core/cartridge.cpp @@ -150,13 +150,15 @@ void CoreCartridge::initRam( const CoreCartridge::RamSizeByte size ) { CoreCartridge::CoreCartridge( std::vector&& rom_ ) : rom( std::move( rom_ ) ) { logDebug( "CoreCartridge constructor" ); + const auto cartridgeType = rom[addr::cartridgeType]; + logDebug( std::format( "Read cartridgeType byte: {}", toHex( cartridgeType ) ) ); + const auto romSizeByte = rom[addr::romSize]; - logDebug( std::format( "Read ROM size byte: {} bytes", toHex( romSizeByte ) ) ); + logDebug( std::format( "Read ROM size byte: {}", toHex( romSizeByte ) ) ); initRom( static_cast( romSizeByte ) ); - const auto ramSizeByte = rom[addr::ramSize]; - logDebug( std::format( "Read RAM size byte: {} bytes", toHex( ramSizeByte ) ) ); + logDebug( std::format( "Read RAM size byte: {}", toHex( ramSizeByte ) ) ); initRam( static_cast( ramSizeByte ) ); }; @@ -171,6 +173,10 @@ std::unique_ptr CoreCartridge::create( CartridgeType type, std::v case MBC1RB: return std::make_unique( std::move( rom ) ); + case MBC2: + case MBC2B: + return std::make_unique( std::move( rom ) ); + case RR: logError( 0, "Cartridge type ROM+RAM is not supported." ); break; diff --git a/src/raylib/main.cpp b/src/raylib/main.cpp index 67d5f7548f0255cd0379bc012e2f2e474d1a54ba..03b50ce5061ba7d28cc02f99a0974fc9e0833224 100644 --- a/src/raylib/main.cpp +++ b/src/raylib/main.cpp @@ -41,7 +41,7 @@ int main() { romFile.close(); - CoreCartridge::CartridgeType type = static_cast( romData[addr::romSize] ); + CoreCartridge::CartridgeType type = static_cast( romData[addr::cartridgeType] ); std::unique_ptr cartridge { CoreCartridge::create( type, std::move( romData ) ) }; logDebug( std::format( "Read cartridge type byte: {}", toHex( cartridge->read( addr::cartridgeType ) ) ) );