#include "stdafx.h" #include "Console.h" #include "Gameboy.h" #include "GbCpu.h" #include "GbPpu.h" #include "GbApu.h" #include "GbCart.h" #include "GbTimer.h" #include "GbDmaController.h" #include "DebugTypes.h" #include "GbMemoryManager.h" #include "GbCartFactory.h" #include "BatteryManager.h" #include "GameboyHeader.h" #include "EmuSettings.h" #include "MessageManager.h" #include "FirmwareHelper.h" #include "GbBootRom.h" #include "../Utilities/VirtualFile.h" #include "../Utilities/Serializer.h" Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile, bool sgbEnabled) { vector romData; romFile.ReadFile(romData); GameboyHeader header; memcpy(&header, romData.data() + Gameboy::HeaderOffset, sizeof(GameboyHeader)); MessageManager::Log("-----------------------------"); MessageManager::Log("File: " + romFile.GetFileName()); MessageManager::Log("Game: " + header.GetCartName()); MessageManager::Log("Cart Type: " + std::to_string(header.CartType)); switch(header.CgbFlag & 0xC0) { case 0x00: MessageManager::Log("Supports: Game Boy"); break; case 0x80: MessageManager::Log("Supports: Game Boy Color (compatible with GB)"); break; case 0xC0: MessageManager::Log("Supports: Game Boy Color only"); break; } MessageManager::Log("File size: " + std::to_string(romData.size() / 1024) + " KB"); if(header.GetCartRamSize() > 0) { string sizeString = header.GetCartRamSize() > 1024 ? std::to_string(header.GetCartRamSize() / 1024) + " KB" : std::to_string(header.GetCartRamSize()) + " bytes"; MessageManager::Log("Cart RAM size: " + sizeString + (header.HasBattery() ? " (with battery)" : "")); } MessageManager::Log("-----------------------------"); GbCart* cart = GbCartFactory::CreateCart(header.CartType); if(cart) { Gameboy* gb = new Gameboy(); gb->Init(console, cart, romData, header, sgbEnabled); return gb; } return nullptr; } void Gameboy::Init(Console* console, GbCart* cart, std::vector& romData, GameboyHeader& header, bool sgbEnabled) { _console = console; _cart.reset(cart); _prgRomSize = (uint32_t)romData.size(); _prgRom = new uint8_t[_prgRomSize]; memcpy(_prgRom, romData.data(), romData.size()); _cartRamSize = header.GetCartRamSize(); _cartRam = new uint8_t[_cartRamSize]; _hasBattery = header.HasBattery(); shared_ptr settings = console->GetSettings(); GameboyConfig cfg = settings->GetGameboyConfig(); GameboyModel model = cfg.Model; if(model == GameboyModel::Auto) { if((header.CgbFlag & 0x80) != 0) { model = GameboyModel::GameboyColor; } else { model = GameboyModel::SuperGameboy; } } if(!sgbEnabled && model == GameboyModel::SuperGameboy) { //SGB bios isn't available, use gameboy color mode instead model = GameboyModel::GameboyColor; } _model = model; bool cgbMode = _model == GameboyModel::GameboyColor; _workRamSize = cgbMode ? 0x8000 : 0x2000; _videoRamSize = cgbMode ? 0x4000 : 0x2000; _workRam = new uint8_t[_workRamSize]; _videoRam = new uint8_t[_videoRamSize]; _spriteRam = new uint8_t[Gameboy::SpriteRamSize]; _highRam = new uint8_t[Gameboy::HighRamSize]; _bootRomSize = 0; FirmwareType type = FirmwareType::Gameboy; if(_model == GameboyModel::SuperGameboy) { type = cfg.UseSgb2 ? FirmwareType::Sgb2GameboyCpu : FirmwareType::Sgb1GameboyCpu; } else if(_model == GameboyModel::GameboyColor) { type = FirmwareType::GameboyColor; } _bootRomSize = cgbMode ? 9 * 256 : 256; if(!FirmwareHelper::LoadGbBootRom(console, &_bootRom, type)) { switch(_model) { default: case GameboyModel::Gameboy: _bootRom = new uint8_t[_bootRomSize]; memcpy(_bootRom, dmgBootRom, _bootRomSize); break; case GameboyModel::GameboyColor: _bootRom = new uint8_t[_bootRomSize]; memcpy(_bootRom, cgbBootRom, _bootRomSize); break; case GameboyModel::SuperGameboy: _bootRom = new uint8_t[_bootRomSize]; if(cfg.UseSgb2) { memcpy(_bootRom, sgb2BootRom, _bootRomSize); } else { memcpy(_bootRom, sgbBootRom, _bootRomSize); } break; } } settings->InitializeRam(_cartRam, _cartRamSize); settings->InitializeRam(_workRam, _workRamSize); settings->InitializeRam(_spriteRam, Gameboy::SpriteRamSize); settings->InitializeRam(_highRam, Gameboy::HighRamSize); settings->InitializeRam(_videoRam, _videoRamSize); LoadBattery(); } Gameboy::Gameboy() { _ppu.reset(new GbPpu()); _apu.reset(new GbApu()); _cpu.reset(new GbCpu()); _memoryManager.reset(new GbMemoryManager()); _timer.reset(new GbTimer()); _dmaController.reset(new GbDmaController()); } Gameboy::~Gameboy() { SaveBattery(); delete[] _cartRam; delete[] _prgRom; delete[] _spriteRam; delete[] _videoRam; delete[] _highRam; delete[] _workRam; delete[] _bootRom; } void Gameboy::PowerOn(SuperGameboy* superGameboy) { _superGameboy = superGameboy; _timer->Init(_memoryManager.get(), _apu.get()); _apu->Init(_console, this); _cart->Init(this, _memoryManager.get()); _memoryManager->Init(_console, this, _cart.get(), _ppu.get(), _apu.get(), _timer.get(), _dmaController.get()); _cpu->Init(_console, this, _memoryManager.get()); _ppu->Init(_console, this, _memoryManager.get(), _dmaController.get(), _videoRam, _spriteRam); _dmaController->Init(_memoryManager.get(), _ppu.get(), _cpu.get()); } void Gameboy::Exec() { _cpu->Exec(); } void Gameboy::Run(uint64_t runUntilClock) { while(_memoryManager->GetCycleCount() < runUntilClock) { _cpu->Exec(); } } void Gameboy::LoadBattery() { if(_hasBattery) { _console->GetBatteryManager()->LoadBattery(".srm", _cartRam, _cartRamSize); } } void Gameboy::SaveBattery() { if(_hasBattery) { _console->GetBatteryManager()->SaveBattery(".srm", _cartRam, _cartRamSize); } } GbState Gameboy::GetState() { GbState state; state.Type = IsCgb() ? GbType::Cgb : GbType::Gb; state.Cpu = _cpu->GetState(); state.Ppu = _ppu->GetState(); state.Apu = _apu->GetState(); state.MemoryManager = _memoryManager->GetState(); state.Dma = _dmaController->GetState(); state.Timer = _timer->GetState(); state.HasBattery = _hasBattery; return state; } uint32_t Gameboy::DebugGetMemorySize(SnesMemoryType type) { switch(type) { case SnesMemoryType::GbPrgRom: return _prgRomSize; case SnesMemoryType::GbWorkRam: return _workRamSize; case SnesMemoryType::GbCartRam: return _cartRamSize; case SnesMemoryType::GbHighRam: return Gameboy::HighRamSize; case SnesMemoryType::GbBootRom: return _bootRomSize; case SnesMemoryType::GbVideoRam: return _videoRamSize; case SnesMemoryType::GbSpriteRam: return Gameboy::SpriteRamSize; default: return 0; } } uint8_t* Gameboy::DebugGetMemory(SnesMemoryType type) { switch(type) { case SnesMemoryType::GbPrgRom: return _prgRom; case SnesMemoryType::GbWorkRam: return _workRam; case SnesMemoryType::GbCartRam: return _cartRam; case SnesMemoryType::GbHighRam: return _highRam; case SnesMemoryType::GbBootRom: return _bootRom; case SnesMemoryType::GbVideoRam: return _videoRam; case SnesMemoryType::GbSpriteRam: return _spriteRam; default: return nullptr; } } GbMemoryManager* Gameboy::GetMemoryManager() { return _memoryManager.get(); } GbPpu* Gameboy::GetPpu() { return _ppu.get(); } GbCpu* Gameboy::GetCpu() { return _cpu.get(); } void Gameboy::GetSoundSamples(int16_t* &samples, uint32_t& sampleCount) { _apu->GetSoundSamples(samples, sampleCount); } AddressInfo Gameboy::GetAbsoluteAddress(uint16_t addr) { AddressInfo addrInfo = { -1, SnesMemoryType::Register }; if(addr >= 0xFF80 && addr <= 0xFFFE) { addrInfo.Address = addr & 0x7F; addrInfo.Type = SnesMemoryType::GbHighRam; return addrInfo; } uint8_t* ptr = _memoryManager->GetMappedBlock(addr); if(!ptr) { return addrInfo; } ptr += (addr & 0xFF); if(ptr >= _prgRom && ptr < _prgRom + _prgRomSize) { addrInfo.Address = (int32_t)(ptr - _prgRom); addrInfo.Type = SnesMemoryType::GbPrgRom; } else if(ptr >= _workRam && ptr < _workRam + _workRamSize) { addrInfo.Address = (int32_t)(ptr - _workRam); addrInfo.Type = SnesMemoryType::GbWorkRam; } else if(ptr >= _cartRam && ptr < _cartRam + _cartRamSize) { addrInfo.Address = (int32_t)(ptr - _cartRam); addrInfo.Type = SnesMemoryType::GbCartRam; } else if(ptr >= _bootRom && ptr < _bootRom + _bootRomSize) { addrInfo.Address = (int32_t)(ptr - _bootRom); addrInfo.Type = SnesMemoryType::GbBootRom; } return addrInfo; } int32_t Gameboy::GetRelativeAddress(AddressInfo& absAddress) { if(absAddress.Type == SnesMemoryType::GbHighRam) { return 0xFF80 | (absAddress.Address & 0x7F); } for(int32_t i = 0; i < 0x10000; i += 0x100) { AddressInfo blockAddr = GetAbsoluteAddress(i); if(blockAddr.Type == absAddress.Type && (blockAddr.Address & ~0xFF) == (absAddress.Address & ~0xFF)) { return i | (absAddress.Address & 0xFF); } } return -1; } GameboyHeader Gameboy::GetHeader() { GameboyHeader header; memcpy(&header, _prgRom + Gameboy::HeaderOffset, sizeof(GameboyHeader)); return header; } bool Gameboy::IsCgb() { return _model == GameboyModel::GameboyColor; } bool Gameboy::IsSgb() { return _model == GameboyModel::SuperGameboy; } SuperGameboy* Gameboy::GetSgb() { return _superGameboy; } uint64_t Gameboy::GetCycleCount() { return _memoryManager->GetCycleCount(); } uint64_t Gameboy::GetApuCycleCount() { return _memoryManager->GetApuCycleCount(); } void Gameboy::Serialize(Serializer& s) { s.Stream(_cpu.get()); s.Stream(_ppu.get()); s.Stream(_apu.get()); s.Stream(_memoryManager.get()); s.Stream(_cart.get()); s.Stream(_timer.get()); s.Stream(_dmaController.get()); s.Stream(_hasBattery); s.StreamArray(_cartRam, _cartRamSize); s.StreamArray(_workRam, _workRamSize); s.StreamArray(_videoRam, _videoRamSize); s.StreamArray(_spriteRam, Gameboy::SpriteRamSize); s.StreamArray(_highRam, Gameboy::HighRamSize); } void Gameboy::SetReg(GbRegister reg, uint16_t value) { _cpu->SetReg(reg, value); }