cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

MBC1MemoryRule.cpp (9453B)


      1/*
      2 * Gearboy - Nintendo Game Boy Emulator
      3 * Copyright (C) 2012  Ignacio Sanchez
      4
      5 * This program is free software: you can redistribute it and/or modify
      6 * it under the terms of the GNU General Public License as published by
      7 * the Free Software Foundation, either version 3 of the License, or
      8 * any later version.
      9
     10 * This program is distributed in the hope that it will be useful,
     11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     13 * GNU General Public License for more details.
     14
     15 * You should have received a copy of the GNU General Public License
     16 * along with this program.  If not, see http://www.gnu.org/licenses/
     17 *
     18 */
     19
     20#include "MBC1MemoryRule.h"
     21#include "Video.h"
     22#include "Memory.h"
     23#include "Processor.h"
     24#include "Input.h"
     25#include "Cartridge.h"
     26
     27const int kMBC1RamBanksSize = 0x8000;
     28
     29MBC1MemoryRule::MBC1MemoryRule(Processor* pProcessor,
     30        Memory* pMemory, Video* pVideo, Input* pInput,
     31        Cartridge* pCartridge, Audio* pAudio) : MemoryRule(pProcessor,
     32pMemory, pVideo, pInput, pCartridge, pAudio)
     33{
     34    m_pRAMBanks = new u8[kMBC1RamBanksSize];
     35    Reset(false);
     36}
     37
     38MBC1MemoryRule::~MBC1MemoryRule()
     39{
     40    SafeDeleteArray(m_pRAMBanks);
     41}
     42
     43void MBC1MemoryRule::Reset(bool bCGB)
     44{
     45    m_bCGB = bCGB;
     46    m_iMode = 0;
     47    m_iCurrentRAMBank = 0;
     48    m_iCurrentROMBank = 1;
     49    m_HigherRomBankBits = 0;
     50    m_bRamEnabled = false;
     51    for (int i = 0; i < kMBC1RamBanksSize; i++)
     52        m_pRAMBanks[i] = 0xFF;
     53    m_CurrentROMAddress = 0x4000;
     54    m_CurrentRAMAddress = 0;
     55}
     56
     57u8 MBC1MemoryRule::PerformRead(u16 address)
     58{
     59    switch (address & 0xE000)
     60    {
     61        case 0x4000:
     62        case 0x6000:
     63        {
     64            u8* pROM = m_pCartridge->GetTheROM();
     65            return pROM[(address - 0x4000) + m_CurrentROMAddress];
     66        }
     67        case 0xA000:
     68        {
     69            if (m_bRamEnabled)
     70            {
     71                if (m_iMode == 0)
     72                {
     73                    if ((m_pCartridge->GetRAMSize() == 1) && (address >= 0xA800))
     74                    {
     75                        // only 2KB of ram
     76                        Log("--> ** Attempting to read from invalid RAM %X", address);
     77                    }
     78                    return m_pRAMBanks[address - 0xA000];
     79                }
     80                else
     81                    return m_pRAMBanks[(address - 0xA000) + m_CurrentRAMAddress];
     82            }
     83            else
     84            {
     85                Log("--> ** Attempting to read from disabled ram %X", address);
     86                return 0xFF;
     87            }
     88        }
     89        default:
     90        {
     91            return m_pMemory->Retrieve(address);
     92        }
     93    }
     94}
     95
     96void MBC1MemoryRule::PerformWrite(u16 address, u8 value)
     97{
     98    switch (address & 0xE000)
     99    {
    100        case 0x0000:
    101        {
    102            if (m_pCartridge->GetRAMSize() > 0)
    103            {
    104                bool previous = m_bRamEnabled;
    105                m_bRamEnabled = ((value & 0x0F) == 0x0A);
    106
    107                if (IsValidPointer(m_pRamChangedCallback) && previous && !m_bRamEnabled)
    108                {
    109                    (*m_pRamChangedCallback)();
    110                }
    111            }
    112            break;
    113        }
    114        case 0x2000:
    115        {
    116            if (m_iMode == 0)
    117            {
    118                m_iCurrentROMBank = (value & 0x1F) | (m_HigherRomBankBits << 5);
    119            }
    120            else
    121            {
    122                m_iCurrentROMBank = value & 0x1F;
    123            }
    124
    125            if (m_iCurrentROMBank == 0x00 || m_iCurrentROMBank == 0x20
    126                    || m_iCurrentROMBank == 0x40 || m_iCurrentROMBank == 0x60)
    127                m_iCurrentROMBank++;
    128
    129            m_iCurrentROMBank &= (m_pCartridge->GetROMBankCount() - 1);
    130            m_CurrentROMAddress = m_iCurrentROMBank * 0x4000;
    131            break;
    132        }
    133        case 0x4000:
    134        {
    135            if (m_iMode == 1)
    136            {
    137                m_iCurrentRAMBank = value & 0x03;
    138                m_iCurrentRAMBank &= (m_pCartridge->GetRAMBankCount() - 1);
    139                m_CurrentRAMAddress = m_iCurrentRAMBank * 0x2000;
    140            }
    141            else
    142            {
    143                m_HigherRomBankBits = value & 0x03;
    144                m_iCurrentROMBank = (m_iCurrentROMBank & 0x1F) | (m_HigherRomBankBits << 5);
    145
    146                if (m_iCurrentROMBank == 0x00 || m_iCurrentROMBank == 0x20
    147                        || m_iCurrentROMBank == 0x40 || m_iCurrentROMBank == 0x60)
    148                    m_iCurrentROMBank++;
    149
    150                m_iCurrentROMBank &= (m_pCartridge->GetROMBankCount() - 1);
    151                m_CurrentROMAddress = m_iCurrentROMBank * 0x4000;
    152            }
    153            break;
    154        }
    155        case 0x6000:
    156        {
    157            if ((m_pCartridge->GetRAMSize() != 3) && (value & 0x01))
    158            {
    159                Log("--> ** Attempting to change MBC1 to mode 1 with incorrect RAM banks %X %X", address, value);
    160            }
    161            else
    162            {
    163                m_iMode = value & 0x01;
    164            }
    165            break;
    166        }
    167        case 0xA000:
    168        {
    169            if (m_bRamEnabled)
    170            {
    171                if (m_iMode == 0)
    172                {
    173                    if ((m_pCartridge->GetRAMSize() == 1) && (address >= 0xA800))
    174                    {
    175                        // only 2KB of ram
    176                        Log("--> ** Attempting to write on invalid RAM %X %X", address, value);
    177                    }
    178
    179                    m_pRAMBanks[address - 0xA000] = value;
    180                }
    181                else
    182                    m_pRAMBanks[(address - 0xA000) + m_CurrentRAMAddress] = value;
    183            }
    184            else
    185            {
    186                Log("--> ** Attempting to write on RAM when ram is disabled %X %X", address, value);
    187            }
    188            break;
    189        }
    190        default:
    191        {
    192            m_pMemory->Load(address, value);
    193            break;
    194        }
    195    }
    196}
    197
    198void MBC1MemoryRule::SaveRam(std::ostream &file)
    199{
    200    Log("MBC1MemoryRule save RAM...");
    201    Log("MBC1MemoryRule saving %d banks...", m_pCartridge->GetRAMBankCount());
    202
    203    u32 ramSize = m_pCartridge->GetRAMBankCount() * 0x2000;
    204
    205    for (u32 i = 0; i < ramSize; i++)
    206    {
    207        u8 ram_byte = m_pRAMBanks[i];
    208        file.write(reinterpret_cast<const char*> (&ram_byte), 1);
    209    }
    210
    211    Log("MBC1MemoryRule save RAM done");
    212}
    213
    214bool MBC1MemoryRule::LoadRam(std::istream &file, s32 fileSize)
    215{
    216    Log("MBC1MemoryRule load RAM...");
    217    Log("MBC1MemoryRule loading %d banks...", m_pCartridge->GetRAMBankCount());
    218
    219    s32 ramSize = m_pCartridge->GetRAMBankCount() * 0x2000;
    220
    221    if ((fileSize > 0) && (fileSize != ramSize))
    222    {
    223        Log("MBC1MemoryRule incorrect size. Expected: %d Found: %d", ramSize, fileSize);
    224        return false;
    225    }
    226    else if (fileSize == 0)
    227    {
    228        // compatibility with old saves
    229        u8 mode;
    230        file.read(reinterpret_cast<char*> (&mode), 1);
    231        Log("MBC1MemoryRule load RAM mode %d", mode);
    232    }
    233
    234    for (s32 i = 0; i < ramSize; i++)
    235    {
    236        u8 ram_byte = 0;
    237        file.read(reinterpret_cast<char*> (&ram_byte), 1);
    238        m_pRAMBanks[i] = ram_byte;
    239    }
    240
    241    Log("MBC1MemoryRule load RAM done");
    242
    243    return true;
    244}
    245
    246size_t MBC1MemoryRule::GetRamSize()
    247{
    248    return m_pCartridge->GetRAMBankCount() * 0x2000;
    249}
    250
    251u8* MBC1MemoryRule::GetRamBanks()
    252{
    253    return m_pRAMBanks;
    254}
    255
    256u8* MBC1MemoryRule::GetCurrentRamBank()
    257{
    258    return m_pRAMBanks + m_CurrentRAMAddress;
    259}
    260
    261int MBC1MemoryRule::GetCurrentRamBankIndex()
    262{
    263    return m_iCurrentRAMBank;
    264}
    265
    266u8* MBC1MemoryRule::GetCurrentRomBank1()
    267{
    268    u8* pROM = m_pCartridge->GetTheROM();
    269    return &pROM[m_CurrentROMAddress];
    270}
    271
    272int MBC1MemoryRule::GetCurrentRomBank1Index()
    273{
    274    return m_iCurrentROMBank;
    275}
    276
    277u8* MBC1MemoryRule::GetRomBank0()
    278{
    279    return m_pMemory->GetMemoryMap() + 0x0000;
    280}
    281
    282int MBC1MemoryRule::GetCurrentRomBank0Index()
    283{
    284    return 0;
    285}
    286
    287void MBC1MemoryRule::SaveState(std::ostream& stream)
    288{
    289    using namespace std;
    290
    291    stream.write(reinterpret_cast<const char*> (&m_iMode), sizeof(m_iMode));
    292    stream.write(reinterpret_cast<const char*> (&m_iCurrentRAMBank), sizeof(m_iCurrentRAMBank));
    293    stream.write(reinterpret_cast<const char*> (&m_iCurrentROMBank), sizeof(m_iCurrentROMBank));
    294    stream.write(reinterpret_cast<const char*> (&m_bRamEnabled), sizeof(m_bRamEnabled));
    295    stream.write(reinterpret_cast<const char*> (&m_HigherRomBankBits), sizeof(m_HigherRomBankBits));
    296    stream.write(reinterpret_cast<const char*> (m_pRAMBanks), kMBC1RamBanksSize);
    297    stream.write(reinterpret_cast<const char*> (&m_CurrentROMAddress), sizeof(m_CurrentROMAddress));
    298    stream.write(reinterpret_cast<const char*> (&m_CurrentRAMAddress), sizeof(m_CurrentRAMAddress));
    299}
    300
    301void MBC1MemoryRule::LoadState(std::istream& stream)
    302{
    303    using namespace std;
    304
    305    stream.read(reinterpret_cast<char*> (&m_iMode), sizeof(m_iMode));
    306    stream.read(reinterpret_cast<char*> (&m_iCurrentRAMBank), sizeof(m_iCurrentRAMBank));
    307    stream.read(reinterpret_cast<char*> (&m_iCurrentROMBank), sizeof(m_iCurrentROMBank));
    308    stream.read(reinterpret_cast<char*> (&m_bRamEnabled), sizeof(m_bRamEnabled));
    309    stream.read(reinterpret_cast<char*> (&m_HigherRomBankBits), sizeof(m_HigherRomBankBits));
    310    stream.read(reinterpret_cast<char*> (m_pRAMBanks), kMBC1RamBanksSize);
    311    stream.read(reinterpret_cast<char*> (&m_CurrentROMAddress), sizeof(m_CurrentROMAddress));
    312    stream.read(reinterpret_cast<char*> (&m_CurrentRAMAddress), sizeof(m_CurrentRAMAddress));
    313}