diff options
| author | Louis Burda <quent.burda@gmail.com> | 2022-06-02 15:28:40 +0200 |
|---|---|---|
| committer | Louis Burda <quent.burda@gmail.com> | 2022-06-02 15:28:40 +0200 |
| commit | 5bc16063c29aa4d3d287ebd163ccdbcbf54c4f9f (patch) | |
| tree | c131f947a37b3af2d14d41e9eda098bdec2d061c /gearboy/src/Memory.cpp | |
| parent | 78a5f810b22f0d8cafa05f638b0cb2e889824859 (diff) | |
| download | cscg2022-gearboy-master.tar.gz cscg2022-gearboy-master.zip | |
Diffstat (limited to 'gearboy/src/Memory.cpp')
| -rw-r--r-- | gearboy/src/Memory.cpp | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/gearboy/src/Memory.cpp b/gearboy/src/Memory.cpp new file mode 100644 index 00000000..88eb9975 --- /dev/null +++ b/gearboy/src/Memory.cpp @@ -0,0 +1,736 @@ +/* + * Gearboy - Nintendo Game Boy Emulator + * Copyright (C) 2012 Ignacio Sanchez + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ + * + */ + +#include <iostream> +#include <fstream> +#include "Memory.h" +#include "Processor.h" +#include "Video.h" + +Memory::Memory() +{ + InitPointer(m_pProcessor); + InitPointer(m_pVideo); + InitPointer(m_pMap); + InitPointer(m_pDisassembledMap); + InitPointer(m_pDisassembledROMMap); + InitPointer(m_pWRAMBanks); + InitPointer(m_pLCDRAMBank1); + InitPointer(m_pCommonMemoryRule); + InitPointer(m_pIORegistersMemoryRule); + InitPointer(m_pCurrentMemoryRule); + InitPointer(m_pBootromDMG); + InitPointer(m_pBootromGBC); + m_bCGB = false; + m_iCurrentWRAMBank = 1; + m_iCurrentLCDRAMBank = 0; + m_bHDMAEnabled = false; + m_iHDMABytes = 0; + for (int i = 0; i < 5; i++) + m_HDMA[i] = 0; + m_HDMASource = 0; + m_HDMADestination = 0; + m_bBootromDMGEnabled = false; + m_bBootromGBCEnabled = false; + m_bBootromRegistryDisabled = false; + m_bBootromDMGLoaded = false; + m_bBootromGBCLoaded = false; +} + +Memory::~Memory() +{ + InitPointer(m_pProcessor); + InitPointer(m_pVideo); + SafeDeleteArray(m_pMap); + SafeDeleteArray(m_pWRAMBanks); + SafeDeleteArray(m_pLCDRAMBank1); + InitPointer(m_pCommonMemoryRule); + InitPointer(m_pIORegistersMemoryRule); + InitPointer(m_pCurrentMemoryRule); + SafeDeleteArray(m_pBootromDMG); + SafeDeleteArray(m_pBootromGBC); + + if (IsValidPointer(m_pDisassembledROMMap)) + { + for (int i = 0; i < MAX_ROM_SIZE; i++) + { + SafeDelete(m_pDisassembledROMMap[i]); + } + SafeDeleteArray(m_pDisassembledROMMap); + } + + if (IsValidPointer(m_pDisassembledMap)) + { + for (int i = 0; i < 65536; i++) + { + SafeDelete(m_pDisassembledMap[i]); + } + SafeDeleteArray(m_pDisassembledMap); + } +} + +void Memory::SetProcessor(Processor* pProcessor) +{ + m_pProcessor = pProcessor; +} + +void Memory::SetVideo(Video* pVideo) +{ + m_pVideo = pVideo; +} + +void Memory::Init() +{ + m_pMap = new u8[65536]; + m_pWRAMBanks = new u8[0x8000]; + m_pLCDRAMBank1 = new u8[0x2000]; + m_pBootromDMG = new u8[0x100]; + m_pBootromGBC = new u8[0x900]; +#ifndef GEARBOY_DISABLE_DISASSEMBLER + m_pDisassembledMap = new stDisassembleRecord*[65536]; + for (int i = 0; i < 65536; i++) + { + InitPointer(m_pDisassembledMap[i]); + } + + m_pDisassembledROMMap = new stDisassembleRecord*[MAX_ROM_SIZE]; + for (int i = 0; i < MAX_ROM_SIZE; i++) + { + InitPointer(m_pDisassembledROMMap[i]); + } +#endif + m_BreakpointsCPU.clear(); + m_BreakpointsMem.clear(); + InitPointer(m_pRunToBreakpoint); + Reset(false); +} + +void Memory::Reset(bool bCGB) +{ + m_bCGB = bCGB; + InitPointer(m_pCommonMemoryRule); + InitPointer(m_pIORegistersMemoryRule); + InitPointer(m_pCurrentMemoryRule); + m_iCurrentWRAMBank = 1; + m_iCurrentLCDRAMBank = 0; + m_bHDMAEnabled = false; + m_iHDMABytes = 0; + m_bBootromRegistryDisabled = false; + + if (IsBootromEnabled()) + ResetBootromDisassembledMemory(); + + for (int i = 0; i < 65536; i++) + { + m_pMap[i] = 0x00; + + if ((i >= 0x8000) && (i < 0xA000)) + { + m_pMap[i] = 0x00; + m_pLCDRAMBank1[i - 0x8000] = 0x00; + } + else if ((i >= 0xC000) && (i < 0xE000)) + { + if ((i & 0x8) ^((i & 0x800) >> 8)) + { + if (m_bCGB) + { + m_pMap[i] = 0x00; + if (i >= 0xD000) + { + for (int a = 0; a < 8; a++) + { + if (a != 2) + m_pWRAMBanks[(i - 0xD000) + (0x1000 * a)] = m_pMap[i - 0x1000]; + else + m_pWRAMBanks[(i - 0xD000) + (0x1000 * a)] = 0x00; + } + } + } + else + m_pMap[i] = 0x0f; + } + else + { + m_pMap[i] = 0xff; + if (i >= 0xD000) + { + for (int a = 0; a < 8; a++) + { + if (a != 2) + m_pWRAMBanks[(i - 0xD000) + (0x1000 * a)] = m_pMap[i - 0x1000]; + else + m_pWRAMBanks[(i - 0xD000) + (0x1000 * a)] = 0x00; + } + } + } + } + else if (i >= 0xFF00) + { + if (m_bCGB) + m_pMap[i] = kInitialValuesForColorFFXX[i - 0xFF00]; + else + m_pMap[i] = kInitialValuesForFFXX[i - 0xFF00]; + } + else + { + m_pMap[i] = 0xFF; + } + } + + if (m_bCGB) + { + for (int i = 0; i < 5; i++) + { + m_HDMA[i] = m_pMap[0xFF51 + i]; + } + + u8 hdma1 = m_HDMA[0]; + u8 hdma2 = m_HDMA[1]; + u8 hdma3 = m_HDMA[2]; + u8 hdma4 = m_HDMA[3]; + + if (hdma1 > 0x7f && hdma1 < 0xa0) + hdma1 = 0; + + m_HDMASource = (hdma1 << 8) | (hdma2 & 0xF0); + m_HDMADestination = ((hdma3 & 0x1F) << 8) | (hdma4 & 0xF0); + m_HDMADestination |= 0x8000; + } +} + +void Memory::SetCurrentRule(MemoryRule* pRule) +{ + m_pCurrentMemoryRule = pRule; +} + +void Memory::SetCommonRule(CommonMemoryRule* pRule) +{ + m_pCommonMemoryRule = pRule; +} + +void Memory::SetIORule(IORegistersMemoryRule* pRule) +{ + m_pIORegistersMemoryRule = pRule; +} + +MemoryRule* Memory::GetCurrentRule() +{ + return m_pCurrentMemoryRule; +} + +u8* Memory::GetMemoryMap() +{ + return m_pMap; +} + +void Memory::LoadBank0and1FromROM(u8* pTheROM) +{ + // loads the first 32KB only (bank 0 and 1) + for (int i = 0; i < 0x8000; i++) + { + m_pMap[i] = pTheROM[i]; + } +} + +void Memory::MemoryDump(const char* szFilePath) +{ + if (!IsValidPointer(m_pDisassembledMap)) + return; + + using namespace std; + + ofstream myfile(szFilePath, ios::out | ios::trunc); + + if (myfile.is_open()) + { + for (int i = 0; i < 65536; i++) + { + if (IsValidPointer(m_pDisassembledMap[i]) && (m_pDisassembledMap[i]->name[0] != 0)) + { + myfile << "0x" << hex << i << "\t " << m_pDisassembledMap[i]->name << "\n"; + i += (m_pDisassembledMap[i]->size - 1); + } + else + { + myfile << "0x" << hex << i << "\t [0x" << hex << (int) m_pMap[i] << "]\n"; + } + } + + myfile.close(); + } +} + +void Memory::PerformDMA(u8 value) +{ + if (m_bCGB) + { + u16 address = value << 8; + if (address < 0xE000) + { + if (address >= 0x8000 && address < 0xA000) + { + for (int i = 0; i < 0xA0; i++) + Load(0xFE00 + i, ReadCGBLCDRAM(address + i, false)); + } + else if (address >= 0xD000 && address < 0xE000) + { + for (int i = 0; i < 0xA0; i++) + Load(0xFE00 + i, ReadCGBWRAM(address + i)); + } + else + { + for (int i = 0; i < 0xA0; i++) + Load(0xFE00 + i, Read(address + i)); + } + } + } + else + { + u16 address = value << 8; + if (address >= 0x8000 && address < 0xE000) + { + for (int i = 0; i < 0xA0; i++) + Load(0xFE00 + i, Read(address + i)); + } + } +} + +void Memory::SwitchCGBDMA(u8 value) +{ + m_iHDMABytes = 16 + ((value & 0x7f) * 16); + + if (m_bHDMAEnabled) + { + if (IsSetBit(value, 7)) + { + m_HDMA[4] = value & 0x7F; + } + else + { + m_HDMA[4] = 0xFF; + m_bHDMAEnabled = false; + } + } + else + { + if (IsSetBit(value, 7)) + { + m_bHDMAEnabled = true; + m_HDMA[4] = value & 0x7F; + if (m_pVideo->GetCurrentStatusMode() == 0) + { + m_pProcessor->AddCycles(PerformHDMA()); + } + } + else + { + PerformGDMA(value); + } + } +} + +unsigned int Memory::PerformHDMA() +{ + u16 source = m_HDMASource & 0xFFF0; + u16 destination = (m_HDMADestination & 0x1FF0) | 0x8000; + + if (source >= 0xD000 && source < 0xE000) + { + for (int i = 0; i < 0x10; i++) + WriteCGBLCDRAM(destination + i, ReadCGBWRAM(source + i)); + } + else + { + for (int i = 0; i < 0x10; i++) + WriteCGBLCDRAM(destination + i, Read(source + i)); + } + + m_HDMADestination += 0x10; + if (m_HDMADestination == 0xA000) + m_HDMADestination = 0x8000; + + m_HDMASource += 0x10; + if (m_HDMASource == 0x8000) + m_HDMASource = 0xA000; + + m_HDMA[1] = m_HDMASource & 0xFF; + m_HDMA[0] = m_HDMASource >> 8; + + m_HDMA[3] = m_HDMADestination & 0xFF; + m_HDMA[2] = m_HDMADestination >> 8; + + m_iHDMABytes -= 0x10; + m_HDMA[4]--; + + if (m_HDMA[4] == 0xFF) + m_bHDMAEnabled = false; + + // return clock cycles used + return (m_pProcessor->CGBSpeed() ? 17 : 9) * 4; +} + +void Memory::PerformGDMA(u8 value) +{ + u16 source = m_HDMASource & 0xFFF0; + u16 destination = (m_HDMADestination & 0x1FF0) | 0x8000; + + if (source >= 0xD000 && source < 0xE000) + { + for (int i = 0; i < m_iHDMABytes; i++) + WriteCGBLCDRAM(destination + i, ReadCGBWRAM(source + i)); + } + else + { + for (int i = 0; i < m_iHDMABytes; i++) + WriteCGBLCDRAM(destination + i, Read(source + i)); + } + + m_HDMADestination += m_iHDMABytes; + m_HDMASource += m_iHDMABytes; + + for (int i = 0; i < 5; i++) + m_HDMA[i] = 0xFF; + + int clock_cycles = 0; + + if (m_pProcessor->CGBSpeed()) + clock_cycles = 2 + 16 * ((value & 0x7f) + 1); + else + clock_cycles = 1 + 8 * ((value & 0x7f) + 1); + + m_pProcessor->AddCycles(clock_cycles * 4); +} + +bool Memory::IsHDMAEnabled() const +{ + return m_bHDMAEnabled; +} + +void Memory::SetHDMARegister(int reg, u8 value) +{ + switch (reg) + { + case 1: + { + // HDMA1 + if (value > 0x7f && value < 0xa0) + value = 0; + m_HDMASource = (value << 8) | (m_HDMASource & 0xF0); + break; + } + case 2: + { + // HDMA2 + value &= 0xF0; + m_HDMASource = (m_HDMASource & 0xFF00) | value; + break; + } + case 3: + { + // HDMA3 + value &= 0x1F; + m_HDMADestination = (value << 8) | (m_HDMADestination & 0xF0); + m_HDMADestination |= 0x8000; + break; + } + case 4: + { + // HDMA4 + value &= 0xF0; + m_HDMADestination = (m_HDMADestination & 0x1F00) | value; + m_HDMADestination |= 0x8000; + break; + } + } + + m_HDMA[reg - 1] = value; +} + +u8 Memory::GetHDMARegister(int reg) const +{ + return m_HDMA[reg - 1]; +} + +u8* Memory::GetCGBRAM() +{ + return m_pWRAMBanks; +} + +int Memory::GetCurrentCGBRAMBank() +{ + return m_iCurrentWRAMBank; +} + +int Memory::GetCurrentLCDRAMBank() +{ + return m_iCurrentLCDRAMBank; +} + +void Memory::SaveState(std::ostream& stream) +{ + using namespace std; + + stream.write(reinterpret_cast<const char*> (m_pMap), 65536); + stream.write(reinterpret_cast<const char*> (&m_iCurrentWRAMBank), sizeof(m_iCurrentWRAMBank)); + stream.write(reinterpret_cast<const char*> (&m_iCurrentLCDRAMBank), sizeof(m_iCurrentLCDRAMBank)); + stream.write(reinterpret_cast<const char*> (m_pWRAMBanks), 0x8000); + stream.write(reinterpret_cast<const char*> (m_pLCDRAMBank1), 0x2000); + stream.write(reinterpret_cast<const char*> (&m_bHDMAEnabled), sizeof(m_bHDMAEnabled)); + stream.write(reinterpret_cast<const char*> (&m_iHDMABytes), sizeof(m_iHDMABytes)); + stream.write(reinterpret_cast<const char*> (m_HDMA), sizeof(m_HDMA)); + stream.write(reinterpret_cast<const char*> (&m_HDMASource), sizeof(m_HDMASource)); + stream.write(reinterpret_cast<const char*> (&m_HDMADestination), sizeof(m_HDMADestination)); +} + +void Memory::LoadState(std::istream& stream) +{ + using namespace std; + + stream.read(reinterpret_cast<char*> (m_pMap), 65536); + stream.read(reinterpret_cast<char*> (&m_iCurrentWRAMBank), sizeof(m_iCurrentWRAMBank)); + stream.read(reinterpret_cast<char*> (&m_iCurrentLCDRAMBank), sizeof(m_iCurrentLCDRAMBank)); + stream.read(reinterpret_cast<char*> (m_pWRAMBanks), 0x8000); + stream.read(reinterpret_cast<char*> (m_pLCDRAMBank1), 0x2000); + stream.read(reinterpret_cast<char*> (&m_bHDMAEnabled), sizeof(m_bHDMAEnabled)); + stream.read(reinterpret_cast<char*> (&m_iHDMABytes), sizeof(m_iHDMABytes)); + stream.read(reinterpret_cast<char*> (m_HDMA), sizeof(m_HDMA)); + stream.read(reinterpret_cast<char*> (&m_HDMASource), sizeof(m_HDMASource)); + stream.read(reinterpret_cast<char*> (&m_HDMADestination), sizeof(m_HDMADestination)); +} + +u8* Memory::GetROM0() +{ + return m_pCurrentMemoryRule->GetRomBank0(); +} + +u8* Memory::GetROM1() +{ + return m_pCurrentMemoryRule->GetCurrentRomBank1(); +} + +u8* Memory::GetVRAM() +{ + if (m_bCGB) + return (m_iCurrentLCDRAMBank == 1) ? m_pLCDRAMBank1 : m_pMap + 0x8000; + else + return m_pMap + 0x8000; +} + +u8* Memory::GetRAM() +{ + return m_pCurrentMemoryRule->GetCurrentRamBank(); +} + +u8* Memory::GetWRAM0() +{ + return m_bCGB ? m_pWRAMBanks : m_pMap + 0xC000; +} + +u8* Memory::GetWRAM1() +{ + return m_bCGB ? m_pWRAMBanks + (0x1000 * m_iCurrentWRAMBank) : m_pMap + 0xD000; +} + +std::vector<Memory::stDisassembleRecord*>* Memory::GetBreakpointsCPU() +{ + return &m_BreakpointsCPU; +} + +std::vector<Memory::stMemoryBreakpoint>* Memory::GetBreakpointsMem() +{ + return &m_BreakpointsMem; +} + +Memory::stDisassembleRecord* Memory::GetRunToBreakpoint() +{ + return m_pRunToBreakpoint; +} + +void Memory::SetRunToBreakpoint(Memory::stDisassembleRecord* pBreakpoint) +{ + m_pRunToBreakpoint = pBreakpoint; +} + +void Memory::EnableBootromDMG(bool enable) +{ + m_bBootromDMGEnabled = enable; + + if (m_bBootromDMGEnabled) + { + Log("DMG Bootrom enabled"); + } + else + { + Log("DMG Bootrom disabled"); + } +} + +void Memory::EnableBootromGBC(bool enable) +{ + m_bBootromGBCEnabled = enable; + + if (m_bBootromGBCEnabled) + { + Log("GBC Bootrom enabled"); + } + else + { + Log("GBC Bootrom disabled"); + } +} + +void Memory::LoadBootromDMG(const char* szFilePath) +{ + Log("Loading DMG Bootrom %s...", szFilePath); + + LoadBootroom(szFilePath, false); +} + +void Memory::LoadBootromGBC(const char* szFilePath) +{ + Log("Loading GBC Bootrom %s...", szFilePath); + + LoadBootroom(szFilePath, true); +} + +bool Memory::IsBootromEnabled() +{ + return (m_bBootromDMGEnabled && m_bBootromDMGLoaded && !m_bCGB) || (m_bBootromGBCEnabled && m_bBootromGBCLoaded && m_bCGB); +} + +void Memory::DisableBootromRegistry() +{ + if (!m_bBootromRegistryDisabled && IsBootromEnabled()) + { + ResetBootromDisassembledMemory(); + } + + m_bBootromRegistryDisabled = true; +} + +bool Memory::IsBootromRegistryEnabled() +{ + return !m_bBootromRegistryDisabled; +} + +void Memory::ResetDisassembledMemory() +{ + #ifndef GEARBOY_DISABLE_DISASSEMBLER + + if (IsValidPointer(m_pDisassembledROMMap)) + { + for (int i = 0; i < MAX_ROM_SIZE; i++) + { + SafeDelete(m_pDisassembledROMMap[i]); + } + } + if (IsValidPointer(m_pDisassembledMap)) + { + for (int i = 0; i < 65536; i++) + { + SafeDelete(m_pDisassembledMap[i]); + } + } + + #endif +} + +void Memory::ResetBootromDisassembledMemory() +{ + #ifndef GEARBOY_DISABLE_DISASSEMBLER + + m_BreakpointsCPU.clear(); + + if (IsValidPointer(m_pDisassembledROMMap)) + { + for (int i = 0; i < 0x0100; i++) + { + SafeDelete(m_pDisassembledROMMap[i]); + } + } + if (IsValidPointer(m_pDisassembledMap)) + { + for (int i = 0; i < 0x0100; i++) + { + SafeDelete(m_pDisassembledMap[i]); + } + } + + if (m_bCGB) + { + if (IsValidPointer(m_pDisassembledROMMap)) + { + for (int i = 0x0200; i < 0x0900; i++) + { + SafeDelete(m_pDisassembledROMMap[i]); + } + } + if (IsValidPointer(m_pDisassembledMap)) + { + for (int i = 0x0200; i < 0x0900; i++) + { + SafeDelete(m_pDisassembledMap[i]); + } + } + } + + #endif +} + +void Memory::LoadBootroom(const char* szFilePath, bool gbc) +{ + using namespace std; + + int expectedSize = gbc ? 0x900 : 0x100; + u8* bootrom = gbc ? m_pBootromGBC : m_pBootromDMG; + + ifstream file(szFilePath, ios::in | ios::binary | ios::ate); + + bool ret = false; + + if (file.is_open()) + { + int size = static_cast<int> (file.tellg()); + + if (size == expectedSize) + { + file.seekg(0, ios::beg); + file.read(reinterpret_cast<char*>(bootrom), size); + file.close(); + + ret = true; + + Log("Bootrom %s loaded", szFilePath); + } + else + { + Log("Incompatible bootrom size (expected 0x%X): 0x%X", expectedSize, size); + } + } + else + { + Log("There was a problem opening the file %s", szFilePath); + } + + if (gbc) + m_bBootromGBCLoaded = ret; + else + m_bBootromDMGLoaded = ret; +} |
