From faf4d62ef4e64e2ac6c3afdecd55bae3e8280603 Mon Sep 17 00:00:00 2001 From: Sour Date: Tue, 26 May 2020 00:05:35 -0400 Subject: [PATCH] GB: Allow picking GB model (original vs color) and using a boot rom +Adds boot rom/sprite ram in hex editor +Allow colorized GB games in GBC mode --- Core/CartTypes.h | 18 ++++- Core/DebugUtilities.h | 1 + Core/Debugger.cpp | 1 + Core/Disassembler.cpp | 4 + Core/Disassembler.h | 3 + Core/FirmwareHelper.h | 42 +++++++++- Core/Gameboy.cpp | 41 +++++++++- Core/Gameboy.h | 31 ++++---- Core/GbApu.cpp | 1 + Core/GbCpu.cpp | 31 ++++---- Core/GbMemoryManager.cpp | 44 +++++------ Core/GbMemoryManager.h | 7 -- Core/GbPpu.cpp | 58 ++++++++------ Core/GbPpu.h | 2 + Core/GbTimer.cpp | 69 +++++++++-------- Core/GbTimer.h | 19 ++--- Core/GbTypes.h | 16 ++++ Core/LabelManager.cpp | 2 + Core/LuaApi.cpp | 1 + Core/MemoryDumper.cpp | 10 +++ Core/NecDsp.cpp | 14 ++-- Core/SettingTypes.h | 10 +++ Core/SnesMemoryType.h | 4 +- UI/Config/EmulationConfig.cs | 10 +++ UI/Debugger/Breakpoints/Breakpoint.cs | 1 + UI/Debugger/Breakpoints/frmBreakpoint.cs | 3 + UI/Debugger/Controls/ctrlMemoryType.cs | 5 ++ UI/Debugger/Labels/CodeLabel.cs | 2 + UI/Debugger/Labels/LabelManager.cs | 1 + UI/Debugger/Labels/ctrlLabelList.cs | 1 + UI/Debugger/Labels/frmEditLabel.cs | 3 + UI/Debugger/Profiler/ctrlProfiler.cs | 1 + UI/Dependencies/resources.en.xml | 7 ++ .../Config/frmEmulationConfig.Designer.cs | 76 ++++++++++++++++++- UI/Forms/Config/frmEmulationConfig.cs | 3 + UI/Interop/DebugApi.cs | 6 +- UI/Interop/DebugState.cs | 1 + UI/Interop/EmuApi.cs | 18 ++++- UI/Utilities/FirmwareHelper.cs | 33 ++++---- 39 files changed, 437 insertions(+), 163 deletions(-) diff --git a/Core/CartTypes.h b/Core/CartTypes.h index 887d645..707fe71 100644 --- a/Core/CartTypes.h +++ b/Core/CartTypes.h @@ -48,6 +48,22 @@ enum class CoprocessorType Gameboy }; +enum class FirmwareType +{ + CX4, + DSP1, + DSP1B, + DSP2, + DSP3, + DSP4, + ST010, + ST011, + ST018, + Satellaview, + Gameboy, + GameboyColor +}; + struct RomInfo { SnesCartInformation Header; @@ -69,4 +85,4 @@ namespace CartFlags ExHiRom = 16, CopierHeader = 32 }; -} \ No newline at end of file +} diff --git a/Core/DebugUtilities.h b/Core/DebugUtilities.h index 5c76454..de715be 100644 --- a/Core/DebugUtilities.h +++ b/Core/DebugUtilities.h @@ -50,6 +50,7 @@ public: case SnesMemoryType::GbCartRam: case SnesMemoryType::GbVideoRam: case SnesMemoryType::GbHighRam: + case SnesMemoryType::GbBootRom: return CpuType::Gameboy; default: diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index e756583..c010a7a 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -533,6 +533,7 @@ AddressInfo Debugger::GetRelativeAddress(AddressInfo absAddress, CpuType cpuType case SnesMemoryType::GbWorkRam: case SnesMemoryType::GbCartRam: case SnesMemoryType::GbHighRam: + case SnesMemoryType::GbBootRom: return { _cart->GetGameboy()->GetRelativeAddress(absAddress), SnesMemoryType::GameboyMemory }; case SnesMemoryType::DspProgramRom: diff --git a/Core/Disassembler.cpp b/Core/Disassembler.cpp index 8742a0a..a1f19dc 100644 --- a/Core/Disassembler.cpp +++ b/Core/Disassembler.cpp @@ -77,6 +77,8 @@ Disassembler::Disassembler(shared_ptr console, shared_ptrDebugGetMemorySize(SnesMemoryType::GbCartRam) : 0; _gbHighRam = _gameboy ? _gameboy->DebugGetMemory(SnesMemoryType::GbHighRam) : nullptr; _gbHighRamSize = _gameboy ? _gameboy->DebugGetMemorySize(SnesMemoryType::GbHighRam) : 0; + _gbBootRom = _gameboy ? _gameboy->DebugGetMemory(SnesMemoryType::GbBootRom) : nullptr; + _gbBootRomSize = _gameboy ? _gameboy->DebugGetMemorySize(SnesMemoryType::GbBootRom) : 0; _prgCache = vector(_prgRomSize); _sramCache = vector(_sramSize); @@ -93,6 +95,7 @@ Disassembler::Disassembler(shared_ptr console, shared_ptr(_gbWorkRamSize); _gbCartRamCache = vector(_gbCartRamSize); _gbHighRamCache = vector(_gbHighRamSize); + _gbBootRomCache = vector(_gbBootRomSize); for(int i = 0; i < (int)DebugUtilities::GetLastCpuType(); i++) { _needDisassemble[i] = true; @@ -113,6 +116,7 @@ Disassembler::Disassembler(shared_ptr console, shared_ptr 0) { //Build cache for the entire DSP chip (since it only contains instructions) diff --git a/Core/Disassembler.h b/Core/Disassembler.h index 37cce91..437d4c0 100644 --- a/Core/Disassembler.h +++ b/Core/Disassembler.h @@ -58,6 +58,7 @@ private: vector _gbWorkRamCache; vector _gbCartRamCache; vector _gbHighRamCache; + vector _gbBootRomCache; SimpleLock _disassemblyLock; vector _disassembly; @@ -105,6 +106,8 @@ private: uint32_t _gbCartRamSize; uint8_t* _gbHighRam; uint32_t _gbHighRamSize; + uint8_t* _gbBootRom; + uint32_t _gbBootRomSize; DisassemblerSource GetSource(SnesMemoryType type); vector& GetDisassemblyList(CpuType type); diff --git a/Core/FirmwareHelper.h b/Core/FirmwareHelper.h index 01f7eeb..6661a4c 100644 --- a/Core/FirmwareHelper.h +++ b/Core/FirmwareHelper.h @@ -7,7 +7,7 @@ struct MissingFirmwareMessage { const char* Filename; - CoprocessorType FirmwareType; + FirmwareType Firmware; uint32_t Size; }; @@ -49,8 +49,20 @@ private: return false; } + static bool AttemptLoadFirmware(uint8_t** out, string filename, uint32_t size) + { + VirtualFile firmware(FolderUtilities::CombinePath(FolderUtilities::GetFirmwareFolder(), filename)); + if(firmware.IsValid() && firmware.GetSize() == size) { + *out = new uint8_t[firmware.GetSize()]; + firmware.ReadFile(*out, (uint32_t)firmware.GetSize()); + return true; + } + + return false; + } + public: - static bool LoadDspFirmware(Console *console, CoprocessorType coprocessorType, string combinedFilename, string splitFilenameProgram, string splitFilenameData, vector &programRom, vector &dataRom, vector &embeddedFirware, uint32_t programSize = 0x1800, uint32_t dataSize = 0x800) + static bool LoadDspFirmware(Console *console, FirmwareType type, string combinedFilename, string splitFilenameProgram, string splitFilenameData, vector &programRom, vector &dataRom, vector &embeddedFirware, uint32_t programSize = 0x1800, uint32_t dataSize = 0x800) { if(embeddedFirware.size() == programSize + dataSize) { programRom.insert(programRom.end(), embeddedFirware.begin(), embeddedFirware.begin() + programSize); @@ -62,7 +74,7 @@ public: MissingFirmwareMessage msg; msg.Filename = combinedFilename.c_str(); - msg.FirmwareType = coprocessorType; + msg.Firmware = type; msg.Size = programSize + dataSize; console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg); @@ -83,7 +95,7 @@ public: MissingFirmwareMessage msg; msg.Filename = "BsxBios.sfc"; - msg.FirmwareType = CoprocessorType::Satellaview; + msg.Firmware = FirmwareType::Satellaview; msg.Size = 1024*1024; console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg); @@ -94,4 +106,26 @@ public: MessageManager::DisplayMessage("Error", "Could not find firmware file for BS-X"); return false; } + + static bool LoadGbBootRom(Console* console, uint8_t** bootRom, FirmwareType type) + { + string filename = type == FirmwareType::Gameboy ? "dmg_boot.bin" : "cgb_boot.bin"; + uint32_t size = type == FirmwareType::Gameboy ? 256 : 2304; + if(AttemptLoadFirmware(bootRom, filename, size)) { + return true; + } + + MissingFirmwareMessage msg; + msg.Filename = filename.c_str(); + msg.Firmware = type; + msg.Size = size; + console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg); + + if(AttemptLoadFirmware(bootRom, filename, size)) { + return true; + } + + MessageManager::DisplayMessage("Error", "Could not find boot rom: " + filename); + return false; + } }; \ No newline at end of file diff --git a/Core/Gameboy.cpp b/Core/Gameboy.cpp index b86513c..9db45f9 100644 --- a/Core/Gameboy.cpp +++ b/Core/Gameboy.cpp @@ -14,6 +14,7 @@ #include "GameboyHeader.h" #include "EmuSettings.h" #include "MessageManager.h" +#include "FirmwareHelper.h" #include "../Utilities/VirtualFile.h" #include "../Utilities/Serializer.h" @@ -50,7 +51,16 @@ Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile) gb->_cartRam = new uint8_t[gb->_cartRamSize]; gb->_hasBattery = header.HasBattery(); - gb->_cgbMode = (header.CgbFlag & 0x80) != 0; + shared_ptr settings = console->GetSettings(); + EmulationConfig cfg = settings->GetEmulationConfig(); + switch(cfg.GbModel) { + default: + case GameboyModel::Auto: gb->_cgbMode = (header.CgbFlag & 0x80) != 0; break; + + case GameboyModel::Gameboy: gb->_cgbMode = false; break; + case GameboyModel::GameboyColor: gb->_cgbMode = true; break; + } + gb->_workRamSize = gb->_cgbMode ? 0x8000 : 0x2000; gb->_videoRamSize = gb->_cgbMode ? 0x4000 : 0x2000; @@ -59,6 +69,15 @@ Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile) gb->_spriteRam = new uint8_t[Gameboy::SpriteRamSize]; gb->_highRam = new uint8_t[Gameboy::HighRamSize]; + gb->_useBootRom = cfg.GbUseBootRom; + gb->_bootRomSize = 0; + if(gb->_useBootRom) { + gb->_useBootRom = FirmwareHelper::LoadGbBootRom(console, &gb->_bootRom, gb->_cgbMode ? FirmwareType::GameboyColor : FirmwareType::Gameboy); + if(gb->_useBootRom) { + gb->_bootRomSize = gb->_cgbMode ? 9 * 256 : 256; + } + } + return gb; } @@ -77,11 +96,13 @@ Gameboy::~Gameboy() delete[] _highRam; delete[] _workRam; + + delete[] _bootRom; } void Gameboy::PowerOn() { - EmuSettings* settings = _console->GetSettings().get(); + shared_ptr settings = _console->GetSettings(); settings->InitializeRam(_cartRam, _cartRamSize); settings->InitializeRam(_workRam, _workRamSize); settings->InitializeRam(_spriteRam, Gameboy::SpriteRamSize); @@ -148,9 +169,11 @@ uint32_t Gameboy::DebugGetMemorySize(SnesMemoryType type) switch(type) { case SnesMemoryType::GbPrgRom: return _prgRomSize; case SnesMemoryType::GbWorkRam: return _workRamSize; - case SnesMemoryType::GbVideoRam: return _videoRamSize; 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; } } @@ -160,9 +183,11 @@ uint8_t* Gameboy::DebugGetMemory(SnesMemoryType type) switch(type) { case SnesMemoryType::GbPrgRom: return _prgRom; case SnesMemoryType::GbWorkRam: return _workRam; - case SnesMemoryType::GbVideoRam: return _videoRam; 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; } } @@ -209,6 +234,9 @@ AddressInfo Gameboy::GetAbsoluteAddress(uint16_t addr) } 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; } @@ -234,6 +262,11 @@ bool Gameboy::IsCgb() return _cgbMode; } +bool Gameboy::UseBootRom() +{ + return _useBootRom; +} + uint64_t Gameboy::GetCycleCount() { return _cpu->GetCycleCount(); diff --git a/Core/Gameboy.h b/Core/Gameboy.h index f755815..eeddb0b 100644 --- a/Core/Gameboy.h +++ b/Core/Gameboy.h @@ -19,7 +19,7 @@ private: static constexpr int SpriteRamSize = 0xA0; static constexpr int HighRamSize = 0x7F; - Console* _console; + Console* _console = nullptr; unique_ptr _memoryManager; unique_ptr _cpu; @@ -29,23 +29,27 @@ private: unique_ptr _timer; unique_ptr _dmaController; - bool _hasBattery; - bool _cgbMode; + bool _hasBattery = false; + bool _cgbMode = false; + bool _useBootRom = false; - uint8_t* _prgRom; - uint32_t _prgRomSize; + uint8_t* _prgRom = nullptr; + uint32_t _prgRomSize = 0; - uint8_t* _cartRam; - uint32_t _cartRamSize; + uint8_t* _cartRam = nullptr; + uint32_t _cartRamSize = 0; - uint8_t* _workRam; - uint32_t _workRamSize; + uint8_t* _workRam = nullptr; + uint32_t _workRamSize = 0; - uint8_t* _videoRam; - uint32_t _videoRamSize; + uint8_t* _videoRam = nullptr; + uint32_t _videoRamSize = 0; - uint8_t* _spriteRam; - uint8_t* _highRam; + uint8_t* _spriteRam = nullptr; + uint8_t* _highRam = nullptr; + + uint8_t* _bootRom = nullptr; + uint32_t _bootRomSize = 0; public: static Gameboy* Create(Console* console, VirtualFile& romFile); @@ -70,6 +74,7 @@ public: int32_t GetRelativeAddress(AddressInfo& absAddress); bool IsCgb(); + bool UseBootRom(); uint64_t GetCycleCount(); uint64_t GetApuCycleCount(); diff --git a/Core/GbApu.cpp b/Core/GbApu.cpp index deda1ac..15b6d81 100644 --- a/Core/GbApu.cpp +++ b/Core/GbApu.cpp @@ -11,6 +11,7 @@ GbApu::GbApu(Console* console, Gameboy* gameboy) _console = console; _soundMixer = console->GetSoundMixer().get(); _gameboy = gameboy; + _state = {}; _soundBuffer = new int16_t[GbApu::MaxSamples*2]; memset(_soundBuffer, 0, GbApu::MaxSamples*2*sizeof(int16_t)); diff --git a/Core/GbCpu.cpp b/Core/GbCpu.cpp index fbc97bf..32f76b1 100644 --- a/Core/GbCpu.cpp +++ b/Core/GbCpu.cpp @@ -10,23 +10,24 @@ GbCpu::GbCpu(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager) _console = console; _gameboy = gameboy; _memoryManager = memoryManager; + _state = {}; -#ifdef USEBOOTROM - _state.PC = 0; - _state.SP = 0xFFFF; -#else - _state.PC = 0x100; - _state.SP = 0xFFFE; - _state.A = gameboy->IsCgb() ? 0x11 : 0x01; - _state.B = 0x00; - _state.C = 0x13; - _state.D = 0x00; - _state.E = 0xD8; - _state.H = 0x01; - _state.L = 0x4D; - _state.Flags = 0xB0; -#endif + if(_gameboy->UseBootRom()) { + _state.PC = 0; + _state.SP = 0xFFFF; + } else { + _state.PC = 0x100; + _state.SP = 0xFFFE; + _state.A = _gameboy->IsCgb() ? 0x11 : 0x01; + _state.B = 0x00; + _state.C = 0x13; + _state.D = 0x00; + _state.E = 0xD8; + _state.H = 0x01; + _state.L = 0x4D; + _state.Flags = 0xB0; + } } GbCpu::~GbCpu() diff --git a/Core/GbMemoryManager.cpp b/Core/GbMemoryManager.cpp index 228670a..5fcd4a4 100644 --- a/Core/GbMemoryManager.cpp +++ b/Core/GbMemoryManager.cpp @@ -18,25 +18,8 @@ void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer, GbDmaController* dmaController) { - _state = {}; - _state.CgbWorkRamBank = 1; - - _prgRom = gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom); - _prgRomSize = gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom); - _cartRam = gameboy->DebugGetMemory(SnesMemoryType::GbCartRam); - _cartRamSize = gameboy->DebugGetMemorySize(SnesMemoryType::GbCartRam); - _workRam = gameboy->DebugGetMemory(SnesMemoryType::GbWorkRam); - _workRamSize = gameboy->DebugGetMemorySize(SnesMemoryType::GbWorkRam); _highRam = gameboy->DebugGetMemory(SnesMemoryType::GbHighRam); -#ifdef USEBOOTROM - VirtualFile bootRom("Firmware\\boot.bin"); - bootRom.ReadFile(_bootRom, 256); - _state.DisableBootRom = false; -#else - _state.DisableBootRom = true; -#endif - _apu = apu; _ppu = ppu; _gameboy = gameboy; @@ -47,6 +30,10 @@ void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbP _controlManager = console->GetControlManager().get(); _settings = console->GetSettings().get(); + _state = {}; + _state.CgbWorkRamBank = 1; + _state.DisableBootRom = !_gameboy->UseBootRom(); + MapRegisters(0x8000, 0x9FFF, RegisterAccess::ReadWrite); MapRegisters(0xFE00, 0xFFFF, RegisterAccess::ReadWrite); @@ -73,7 +60,10 @@ void GbMemoryManager::RefreshMappings() _cart->RefreshMappings(); if(!_state.DisableBootRom) { - _reads[0] = _bootRom; + Map(0x0000, 0x00FF, GbMemoryType::BootRom, 0, true); + if(_gameboy->IsCgb()) { + Map(0x0200, 0x08FF, GbMemoryType::BootRom, 0x200, true); + } } } @@ -220,7 +210,7 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr) return _dmaController->Read(); } else if(addr >= 0xFF80) { return _highRam[addr & 0x7F]; //80-FE - } else if(addr >= 0xFF4D) { + } else if(addr >= 0xFF4C) { if(_gameboy->IsCgb()) { switch(addr) { //FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch @@ -238,9 +228,9 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr) } } LogDebug("[Debug] GB - Missing read handler: $" + HexUtilities::ToHex(addr)); - return 0xFF; //4D-7F, open bus + return 0xFF; //4C-7F, open bus } else if(addr >= 0xFF40) { - return _ppu->Read(addr); //40-4C + return _ppu->Read(addr); //40-4B } else if(addr >= 0xFF10) { return _apu->Read(addr); //10-3F } else { @@ -277,8 +267,8 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value) _dmaController->Write(value); } else if(addr >= 0xFF80) { _highRam[addr & 0x7F] = value; //80-FE - } else if(addr >= 0xFF4D) { - //4D-7F + } else if(addr >= 0xFF4C) { + //4C-7F if(addr == 0xFF50) { if(value & 0x01) { _state.DisableBootRom = true; @@ -295,6 +285,12 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value) _dmaController->WriteCgb(addr, value); break; + case 0xFF4C: //CGB - "LCDMODE", set by boot rom to turn off CGB features for the LCD for DMG games + if(!_state.DisableBootRom) { + _ppu->WriteCgbRegister(addr, value); + } + break; + case 0xFF4F: //CGB - VRAM banking case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette _ppu->WriteCgbRegister(addr, value); @@ -312,7 +308,7 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value) } } } else if(addr >= 0xFF40) { - _ppu->Write(addr, value); //40-4C + _ppu->Write(addr, value); //40-4B } else if(addr >= 0xFF10) { _apu->Write(addr, value); //10-3F } else { diff --git a/Core/GbMemoryManager.h b/Core/GbMemoryManager.h index ec0d9eb..d870165 100644 --- a/Core/GbMemoryManager.h +++ b/Core/GbMemoryManager.h @@ -28,13 +28,6 @@ private: GbTimer* _timer; GbDmaController* _dmaController; - uint8_t _bootRom[256]; - uint8_t* _prgRom = nullptr; - uint32_t _prgRomSize = 0; - uint8_t* _workRam = nullptr; - uint32_t _workRamSize = 0; - uint8_t* _cartRam = nullptr; - uint32_t _cartRamSize = 0; uint8_t* _highRam = nullptr; uint8_t* _reads[0x100] = {}; diff --git a/Core/GbPpu.cpp b/Core/GbPpu.cpp index 1dcae03..dc432fe 100644 --- a/Core/GbPpu.cpp +++ b/Core/GbPpu.cpp @@ -22,10 +22,6 @@ void GbPpu::Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryMana _memoryManager = memoryManager; _vram = vram; _oam = oam; - - _state = {}; - _state.Mode = PpuMode::HBlank; - _lastFrameTime = 0; _outputBuffers[0] = new uint16_t[256 * 240]; _outputBuffers[1] = new uint16_t[256 * 240]; @@ -39,17 +35,31 @@ void GbPpu::Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryMana memset(_eventViewerBuffers[1], 0, 456 * 154 * sizeof(uint16_t)); _currentEventViewerBuffer = _eventViewerBuffers[0]; -#ifndef USEBOOTROM - Write(0xFF40, 0x91); - Write(0xFF42, 0x00); - Write(0xFF43, 0x00); - Write(0xFF45, 0x00); - Write(0xFF47, 0xFC); - Write(0xFF48, 0xFF); - Write(0xFF49, 0xFF); - Write(0xFF4A, 0); - Write(0xFF4B, 0); -#endif + _state = {}; + _state.Mode = PpuMode::HBlank; + _state.CgbEnabled = _gameboy->IsCgb(); + _lastFrameTime = 0; + + if(!_gameboy->IsCgb()) { + for(int i = 0; i < 4; i++) { + //Init default palette for use with DMG + _state.CgbBgPalettes[i] = bwRgbPalette[i]; + _state.CgbObjPalettes[i] = bwRgbPalette[i]; + _state.CgbObjPalettes[i+4] = bwRgbPalette[i]; + } + } + + if(!_gameboy->UseBootRom()) { + Write(0xFF40, 0x91); + Write(0xFF42, 0x00); + Write(0xFF43, 0x00); + Write(0xFF45, 0x00); + Write(0xFF47, 0xFC); + Write(0xFF48, 0xFF); + Write(0xFF49, 0xFF); + Write(0xFF4A, 0); + Write(0xFF4B, 0); + } } GbPpu::~GbPpu() @@ -213,7 +223,7 @@ void GbPpu::RunDrawCycle() } uint16_t rgbColor; - if(_gameboy->IsCgb()) { + if(_state.CgbEnabled) { if(isSprite) { rgbColor = _state.CgbObjPalettes[entry.Color | ((entry.Attributes & 0x07) << 2)]; } else { @@ -221,9 +231,10 @@ void GbPpu::RunDrawCycle() } } else { if(isSprite) { - rgbColor = bwRgbPalette[(((entry.Attributes & 0x10) ? _state.ObjPalette1 : _state.ObjPalette0) >> (entry.Color * 2)) & 0x03]; + uint8_t colorIndex = (((entry.Attributes & 0x10) ? _state.ObjPalette1 : _state.ObjPalette0) >> (entry.Color * 2)) & 0x03; + rgbColor = _state.CgbObjPalettes[((entry.Attributes & 0x10) ? 4 : 0) | colorIndex]; } else { - rgbColor = bwRgbPalette[(_state.BgPalette >> (entry.Color * 2)) & 0x03]; + rgbColor = _state.CgbBgPalettes[(_state.BgPalette >> (entry.Color * 2)) & 0x03]; } } _currentBuffer[outOffset] = rgbColor; @@ -282,7 +293,7 @@ void GbPpu::ClockSpriteFetcher() uint8_t sprTile = _oam[_fetchSprite + 2]; uint8_t sprAttr = _oam[_fetchSprite + 3]; bool vMirror = (sprAttr & 0x40) != 0; - uint16_t tileBank = _gameboy->IsCgb() ? ((sprAttr & 0x08) ? 0x2000 : 0x0000) : 0; + uint16_t tileBank = _state.CgbEnabled ? ((sprAttr & 0x08) ? 0x2000 : 0x0000) : 0; uint8_t sprOffsetY = vMirror ? (_state.LargeSprites ? 15 : 7) - (_state.Scanline - sprY) : (_state.Scanline - sprY); if(_state.LargeSprites) { @@ -308,7 +319,7 @@ void GbPpu::ClockSpriteFetcher() void GbPpu::FindNextSprite() { - if(_prevSprite < _spriteCount && _fetchSprite < 0 && (_state.SpritesEnabled || _gameboy->IsCgb())) { + if(_prevSprite < _spriteCount && _fetchSprite < 0 && (_state.SpritesEnabled || _state.CgbEnabled)) { for(int i = _prevSprite; i < _spriteCount; i++) { if((int)_spriteX[i] - 8 == _drawnPixels) { _fetchSprite = _spriteIndexes[i]; @@ -349,7 +360,7 @@ void GbPpu::ClockTileFetcher() uint16_t tileAddr = tilemapAddr + _fetchColumn + row * 32; uint8_t tileIndex = _vram[tileAddr]; - uint8_t attributes = _gameboy->IsCgb() ? _vram[tileAddr | 0x2000] : 0; + uint8_t attributes = _state.CgbEnabled ? _vram[tileAddr | 0x2000] : 0; bool vMirror = (attributes & 0x40) != 0; uint16_t tileBank = (attributes & 0x08) ? 0x2000 : 0x0000; @@ -648,12 +659,13 @@ uint8_t GbPpu::ReadCgbRegister(uint16_t addr) case 0xFF6B: return (_state.CgbObjPalettes[_state.CgbObjPalPosition >> 1] >> ((_state.CgbObjPalPosition & 0x01) ? 8 : 0) & 0xFF); } LogDebug("[Debug] GBC - Missing read handler: $" + HexUtilities::ToHex(addr)); - return 0; + return 0xFF; } void GbPpu::WriteCgbRegister(uint16_t addr, uint8_t value) { switch(addr) { + case 0xFF4C: _state.CgbEnabled = (value & 0x0C) == 0; break; case 0xFF4F: _state.CgbVramBank = value & 0x01; break; case 0xFF68: @@ -708,7 +720,7 @@ void GbPpu::Serialize(Serializer& s) _state.WindowEnabled, _state.BgTileSelect, _state.BgTilemapSelect, _state.LargeSprites, _state.SpritesEnabled, _state.BgEnabled, _state.Status, _state.FrameCount, _lastFrameTime, _state.LyCoincidenceFlag, _state.CgbBgPalAutoInc, _state.CgbBgPalPosition, - _state.CgbObjPalAutoInc, _state.CgbObjPalPosition, _state.CgbVramBank + _state.CgbObjPalAutoInc, _state.CgbObjPalPosition, _state.CgbVramBank, _state.CgbEnabled ); s.StreamArray(_state.CgbBgPalettes, 4 * 8); diff --git a/Core/GbPpu.h b/Core/GbPpu.h index cfe6d9f..0c8fbc4 100644 --- a/Core/GbPpu.h +++ b/Core/GbPpu.h @@ -64,6 +64,8 @@ private: public: virtual ~GbPpu(); + void Reset(bool useBootRom); + void Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager, uint8_t* vram, uint8_t* oam); GbPpuState GetState(); diff --git a/Core/GbTimer.cpp b/Core/GbTimer.cpp index 2849504..ab66621 100644 --- a/Core/GbTimer.cpp +++ b/Core/GbTimer.cpp @@ -9,9 +9,12 @@ GbTimer::GbTimer(GbMemoryManager* memoryManager, GbApu* apu) _apu = apu; _memoryManager = memoryManager; + _state = {}; + _state.TimerDivider = 1024; + //Passes boot_div-dmgABCmgb //But that test depends on LCD power on timings, so may be wrong. - _divider = 0x06; + _state.Divider = 0x06; } GbTimer::~GbTimer() @@ -20,45 +23,45 @@ GbTimer::~GbTimer() void GbTimer::Exec() { - _reloaded = false; - if(_needReload) { + _state.Reloaded = false; + if(_state.NeedReload) { ReloadCounter(); } - SetDivider(_divider + 4); + SetDivider(_state.Divider + 4); } void GbTimer::ReloadCounter() { - _counter = _modulo; + _state.Counter = _state.Modulo; _memoryManager->RequestIrq(GbIrqSource::Timer); - _needReload = false; - _reloaded = true; + _state.NeedReload = false; + _state.Reloaded = true; } void GbTimer::SetDivider(uint16_t newValue) { - if(_timerEnabled && !(newValue & _timerDivider) && (_divider & _timerDivider)) { - _counter++; - if(_counter == 0) { - _needReload = true; + if(_state.TimerEnabled && !(newValue & _state.TimerDivider) && (_state.Divider & _state.TimerDivider)) { + _state.Counter++; + if(_state.Counter == 0) { + _state.NeedReload = true; } } uint16_t frameSeqBit = _memoryManager->IsHighSpeed() ? 0x2000 : 0x1000; - if(!(newValue & frameSeqBit) && (_divider & frameSeqBit)) { + if(!(newValue & frameSeqBit) && (_state.Divider & frameSeqBit)) { _apu->ClockFrameSequencer(); } - _divider = newValue; + _state.Divider = newValue; } uint8_t GbTimer::Read(uint16_t addr) { switch(addr) { - case 0xFF04: return _divider >> 8; - case 0xFF05: return _counter; //FF05 - TIMA - Timer counter (R/W) - case 0xFF06: return _modulo; //FF06 - TMA - Timer Modulo (R/W) - case 0xFF07: return _control | 0xF8; //FF07 - TAC - Timer Control (R/W) + case 0xFF04: return _state.Divider >> 8; + case 0xFF05: return _state.Counter; //FF05 - TIMA - Timer counter (R/W) + case 0xFF06: return _state.Modulo; //FF06 - TMA - Timer Modulo (R/W) + case 0xFF07: return _state.Control | 0xF8; //FF07 - TAC - Timer Control (R/W) } return 0; } @@ -73,29 +76,29 @@ void GbTimer::Write(uint16_t addr, uint8_t value) case 0xFF05: //FF05 - TIMA - Timer counter (R/W) - if(_needReload) { + if(_state.NeedReload) { //Writing to TIMA when a reload is pending will cancel the reload and IRQ request - _needReload = false; + _state.NeedReload = false; } - if(!_reloaded) { + if(!_state.Reloaded) { //Writes to TIMA on the cycle TIMA was reloaded with TMA are ignored - _counter = value; + _state.Counter = value; } break; case 0xFF06: //FF06 - TMA - Timer Modulo (R/W) - _modulo = value; - if(_reloaded) { + _state.Modulo = value; + if(_state.Reloaded) { //Writing to TMA on the same cycle it was reloaded into TIMA will also update TIMA - _counter = value; + _state.Counter = value; } break; case 0xFF07: { //FF07 - TAC - Timer Control (R/W) - _control = value; + _state.Control = value; bool enabled = (value & 0x04) != 0; uint16_t newDivider = 0; switch(value & 0x03) { @@ -105,25 +108,25 @@ void GbTimer::Write(uint16_t addr, uint8_t value) case 3: newDivider = 1 << 7; break; } - if(_timerEnabled) { + if(_state.TimerEnabled) { //When changing the value of TAC, the TIMA register can get incremented due to a glitch bool incrementCounter; if(enabled) { - incrementCounter = (_divider & _timerDivider) != 0 && (_divider & newDivider) == 0; + incrementCounter = (_state.Divider & _state.TimerDivider) != 0 && (_state.Divider & newDivider) == 0; } else { - incrementCounter = (_divider & _timerDivider) != 0; + incrementCounter = (_state.Divider & _state.TimerDivider) != 0; } if(incrementCounter) { - _counter++; - if(_counter == 0) { + _state.Counter++; + if(_state.Counter == 0) { ReloadCounter(); } } } - _timerEnabled = enabled; - _timerDivider = newDivider; + _state.TimerEnabled = enabled; + _state.TimerDivider = newDivider; break; } } @@ -131,5 +134,5 @@ void GbTimer::Write(uint16_t addr, uint8_t value) void GbTimer::Serialize(Serializer& s) { - s.Stream(_divider, _counter, _modulo, _control, _timerEnabled, _timerDivider, _needReload, _reloaded); + s.Stream(_state.Divider, _state.Counter, _state.Modulo, _state.Control, _state.TimerEnabled, _state.TimerDivider, _state.NeedReload, _state.Reloaded); } diff --git a/Core/GbTimer.h b/Core/GbTimer.h index d35dd19..9a8a8f4 100644 --- a/Core/GbTimer.h +++ b/Core/GbTimer.h @@ -2,6 +2,7 @@ #include "stdafx.h" #include "../Utilities/ISerializable.h" #include "../Utilities/Serializer.h" +#include "GbTypes.h" class GbMemoryManager; class GbApu; @@ -9,19 +10,9 @@ class GbApu; class GbTimer : public ISerializable { private: - GbMemoryManager* _memoryManager; - GbApu* _apu; - - uint16_t _divider = 0; - - bool _needReload = false; //Set after TIMA (_counter) overflowed, next cycle will reload TMA into TIMA - bool _reloaded = false; //Set during the cycle on which TIMA is reloaded (affects TMA/TIMA writes) - uint8_t _counter = 0; - uint8_t _modulo = 0; - - uint8_t _control = 0; - bool _timerEnabled = false; - uint16_t _timerDivider = 1024; + GbMemoryManager* _memoryManager = nullptr; + GbApu* _apu = nullptr; + GbTimerState _state = {}; void SetDivider(uint16_t value); void ReloadCounter(); @@ -30,6 +21,8 @@ public: GbTimer(GbMemoryManager* memoryManager, GbApu* apu); virtual ~GbTimer(); + void Reset(); + void Exec(); uint8_t Read(uint16_t addr); diff --git a/Core/GbTypes.h b/Core/GbTypes.h index 0231500..da4e481 100644 --- a/Core/GbTypes.h +++ b/Core/GbTypes.h @@ -179,6 +179,7 @@ struct GbPpuState uint8_t Status; uint32_t FrameCount; + bool CgbEnabled; uint8_t CgbVramBank; uint8_t CgbBgPalPosition; @@ -204,6 +205,20 @@ struct GbDmaControllerState bool CgbHdmaMode; }; +struct GbTimerState +{ + uint16_t Divider = 0; + + bool NeedReload = false; //Set after TIMA (_counter) overflowed, next cycle will reload TMA into TIMA + bool Reloaded = false; //Set during the cycle on which TIMA is reloaded (affects TMA/TIMA writes) + uint8_t Counter = 0; + uint8_t Modulo = 0; + + uint8_t Control = 0; + bool TimerEnabled = false; + uint16_t TimerDivider = 1024; +}; + struct GbSquareState { uint16_t SweepPeriod; @@ -320,6 +335,7 @@ enum class GbMemoryType PrgRom = (int)SnesMemoryType::GbPrgRom, WorkRam = (int)SnesMemoryType::GbWorkRam, CartRam = (int)SnesMemoryType::GbCartRam, + BootRom = (int)SnesMemoryType::GbBootRom, }; struct GbMemoryManagerState diff --git a/Core/LabelManager.cpp b/Core/LabelManager.cpp index 17c1193..44eed7a 100644 --- a/Core/LabelManager.cpp +++ b/Core/LabelManager.cpp @@ -60,6 +60,7 @@ int64_t LabelManager::GetLabelKey(uint32_t absoluteAddr, SnesMemoryType memType) case SnesMemoryType::GbWorkRam: return absoluteAddr | ((uint64_t)13 << 32); case SnesMemoryType::GbCartRam: return absoluteAddr | ((uint64_t)14 << 32); case SnesMemoryType::GbHighRam: return absoluteAddr | ((uint64_t)15 << 32); + case SnesMemoryType::GbBootRom: return absoluteAddr | ((uint64_t)16 << 32); default: return -1; } } @@ -82,6 +83,7 @@ SnesMemoryType LabelManager::GetKeyMemoryType(uint64_t key) case ((uint64_t)13 << 32): return SnesMemoryType::GbWorkRam; break; case ((uint64_t)14 << 32): return SnesMemoryType::GbCartRam; break; case ((uint64_t)15 << 32): return SnesMemoryType::GbHighRam; break; + case ((uint64_t)16 << 32): return SnesMemoryType::GbBootRom; break; } throw std::runtime_error("Invalid label key"); diff --git a/Core/LuaApi.cpp b/Core/LuaApi.cpp index 27177be..4872220 100644 --- a/Core/LuaApi.cpp +++ b/Core/LuaApi.cpp @@ -158,6 +158,7 @@ int LuaApi::GetLibrary(lua_State *lua) lua_pushintvalue(gbCartRam, SnesMemoryType::GbCartRam); lua_pushintvalue(gbVideoRam, SnesMemoryType::GbVideoRam); lua_pushintvalue(gbHighRam, SnesMemoryType::GbHighRam); + lua_pushintvalue(gbBootRom, SnesMemoryType::GbBootRom); lua_settable(lua, -3); lua_pushliteral(lua, "counterOpType"); diff --git a/Core/MemoryDumper.cpp b/Core/MemoryDumper.cpp index 32a9920..cc6e5cc 100644 --- a/Core/MemoryDumper.cpp +++ b/Core/MemoryDumper.cpp @@ -69,6 +69,8 @@ void MemoryDumper::SetMemoryState(SnesMemoryType type, uint8_t *buffer, uint32_t case SnesMemoryType::GbVideoRam: case SnesMemoryType::GbCartRam: case SnesMemoryType::GbHighRam: + case SnesMemoryType::GbBootRom: + case SnesMemoryType::GbSpriteRam: if(_cartridge->GetGameboy()) { memcpy(_cartridge->GetGameboy()->DebugGetMemory(type), buffer, length); } @@ -112,6 +114,8 @@ uint32_t MemoryDumper::GetMemorySize(SnesMemoryType type) case SnesMemoryType::GbVideoRam: case SnesMemoryType::GbCartRam: case SnesMemoryType::GbHighRam: + case SnesMemoryType::GbBootRom: + case SnesMemoryType::GbSpriteRam: return _cartridge->GetGameboy() ? _cartridge->GetGameboy()->DebugGetMemorySize(type) : 0; } } @@ -191,6 +195,8 @@ void MemoryDumper::GetMemoryState(SnesMemoryType type, uint8_t *buffer) case SnesMemoryType::GbVideoRam: case SnesMemoryType::GbCartRam: case SnesMemoryType::GbHighRam: + case SnesMemoryType::GbBootRom: + case SnesMemoryType::GbSpriteRam: if(_cartridge->GetGameboy()) { memcpy(buffer, _cartridge->GetGameboy()->DebugGetMemory(type), _cartridge->GetGameboy()->DebugGetMemorySize(type)); } @@ -261,6 +267,8 @@ void MemoryDumper::SetMemoryValue(SnesMemoryType memoryType, uint32_t address, u case SnesMemoryType::GbVideoRam: case SnesMemoryType::GbCartRam: case SnesMemoryType::GbHighRam: + case SnesMemoryType::GbBootRom: + case SnesMemoryType::GbSpriteRam: if(_cartridge->GetGameboy()) { _cartridge->GetGameboy()->DebugGetMemory(memoryType)[address] = value; } @@ -309,6 +317,8 @@ uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address case SnesMemoryType::GbVideoRam: case SnesMemoryType::GbCartRam: case SnesMemoryType::GbHighRam: + case SnesMemoryType::GbBootRom: + case SnesMemoryType::GbSpriteRam: return _cartridge->GetGameboy() ? _cartridge->GetGameboy()->DebugGetMemory(memoryType)[address] : 0; } } diff --git a/Core/NecDsp.cpp b/Core/NecDsp.cpp index f3ebf88..ae89c8a 100644 --- a/Core/NecDsp.cpp +++ b/Core/NecDsp.cpp @@ -92,13 +92,13 @@ NecDsp* NecDsp::InitCoprocessor(CoprocessorType type, Console *console, vector programRom; vector dataRom; switch(type) { - case CoprocessorType::DSP1: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp1.rom", "dsp1.program.rom", "dsp1.data.rom", programRom, dataRom, embeddedFirware); break; - case CoprocessorType::DSP1B: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp1b.rom", "dsp1b.program.rom", "dsp1b.data.rom", programRom, dataRom, embeddedFirware); break; - case CoprocessorType::DSP2: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp2.rom", "dsp2.program.rom", "dsp2.data.rom", programRom, dataRom, embeddedFirware); break; - case CoprocessorType::DSP3: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp3.rom", "dsp3.program.rom", "dsp3.data.rom", programRom, dataRom, embeddedFirware); break; - case CoprocessorType::DSP4: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "dsp4.rom", "dsp4.program.rom", "dsp4.data.rom", programRom, dataRom, embeddedFirware); break; - case CoprocessorType::ST010: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "st010.rom", "st010.program.rom", "st010.data.rom", programRom, dataRom, embeddedFirware, 0xC000, 0x1000); break; - case CoprocessorType::ST011: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, type, "st011.rom", "st011.program.rom", "st011.data.rom", programRom, dataRom, embeddedFirware, 0xC000, 0x1000); break; + case CoprocessorType::DSP1: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP1, "dsp1.rom", "dsp1.program.rom", "dsp1.data.rom", programRom, dataRom, embeddedFirware); break; + case CoprocessorType::DSP1B: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP1B, "dsp1b.rom", "dsp1b.program.rom", "dsp1b.data.rom", programRom, dataRom, embeddedFirware); break; + case CoprocessorType::DSP2: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP2, "dsp2.rom", "dsp2.program.rom", "dsp2.data.rom", programRom, dataRom, embeddedFirware); break; + case CoprocessorType::DSP3: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP3, "dsp3.rom", "dsp3.program.rom", "dsp3.data.rom", programRom, dataRom, embeddedFirware); break; + case CoprocessorType::DSP4: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::DSP4, "dsp4.rom", "dsp4.program.rom", "dsp4.data.rom", programRom, dataRom, embeddedFirware); break; + case CoprocessorType::ST010: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::ST010, "st010.rom", "st010.program.rom", "st010.data.rom", programRom, dataRom, embeddedFirware, 0xC000, 0x1000); break; + case CoprocessorType::ST011: firmwareLoaded = FirmwareHelper::LoadDspFirmware(console, FirmwareType::ST011, "st011.rom", "st011.program.rom", "st011.data.rom", programRom, dataRom, embeddedFirware, 0xC000, 0x1000); break; default: break; } diff --git a/Core/SettingTypes.h b/Core/SettingTypes.h index 67acde7..9a3c19b 100644 --- a/Core/SettingTypes.h +++ b/Core/SettingTypes.h @@ -270,6 +270,13 @@ enum class ConsoleRegion Pal = 2 }; +enum class GameboyModel +{ + Auto = 0, + Gameboy = 1, + GameboyColor = 2, +}; + struct EmulationConfig { uint32_t EmulationSpeed = 100; @@ -290,6 +297,9 @@ struct EmulationConfig RamState RamPowerOnState = RamState::Random; int64_t BsxCustomDate = -1; + + bool GbUseBootRom = false; + GameboyModel GbModel = GameboyModel::Auto; }; struct PreferencesConfig diff --git a/Core/SnesMemoryType.h b/Core/SnesMemoryType.h index 2d559a9..80e5f0c 100644 --- a/Core/SnesMemoryType.h +++ b/Core/SnesMemoryType.h @@ -28,7 +28,9 @@ enum class SnesMemoryType GbPrgRom, GbWorkRam, GbCartRam, - GbVideoRam, GbHighRam, + GbBootRom, + GbVideoRam, + GbSpriteRam, Register }; \ No newline at end of file diff --git a/UI/Config/EmulationConfig.cs b/UI/Config/EmulationConfig.cs index e37d1e6..7fe971e 100644 --- a/UI/Config/EmulationConfig.cs +++ b/UI/Config/EmulationConfig.cs @@ -29,6 +29,9 @@ namespace Mesen.GUI.Config public long BsxCustomDate = -1; + [MarshalAs(UnmanagedType.I1)] public bool GbUseBootRom = false; + public GameboyModel GbModel = GameboyModel.Auto; + public void ApplyConfig() { ConfigApi.SetEmulationConfig(this); @@ -48,4 +51,11 @@ namespace Mesen.GUI.Config AllZeros = 1, AllOnes = 2, } + + public enum GameboyModel + { + Auto = 0, + Gameboy = 1, + GameboyColor = 2, + } } diff --git a/UI/Debugger/Breakpoints/Breakpoint.cs b/UI/Debugger/Breakpoints/Breakpoint.cs index 303efe8..2af026d 100644 --- a/UI/Debugger/Breakpoints/Breakpoint.cs +++ b/UI/Debugger/Breakpoints/Breakpoint.cs @@ -135,6 +135,7 @@ namespace Mesen.GUI.Debugger case SnesMemoryType.GbWorkRam: type = "WRAM"; break; case SnesMemoryType.GbCartRam: type = "SRAM"; break; case SnesMemoryType.GbHighRam: type = "HRAM"; break; + case SnesMemoryType.GbBootRom: type = "BOOT"; break; case SnesMemoryType.Register: type = "REG"; break; } diff --git a/UI/Debugger/Breakpoints/frmBreakpoint.cs b/UI/Debugger/Breakpoints/frmBreakpoint.cs index bfdbf28..94528d6 100644 --- a/UI/Debugger/Breakpoints/frmBreakpoint.cs +++ b/UI/Debugger/Breakpoints/frmBreakpoint.cs @@ -89,6 +89,9 @@ namespace Mesen.GUI.Debugger if(DebugApi.GetMemorySize(SnesMemoryType.GbCartRam) > 0) { cboBreakpointType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbCartRam)); } + if(DebugApi.GetMemorySize(SnesMemoryType.GbBootRom) > 0) { + cboBreakpointType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbBootRom)); + } } this.toolTip.SetToolTip(this.picExpressionWarning, "Condition contains invalid syntax or symbols."); diff --git a/UI/Debugger/Controls/ctrlMemoryType.cs b/UI/Debugger/Controls/ctrlMemoryType.cs index a8d8845..21e20b3 100644 --- a/UI/Debugger/Controls/ctrlMemoryType.cs +++ b/UI/Debugger/Controls/ctrlMemoryType.cs @@ -57,7 +57,12 @@ namespace Mesen.GUI.Debugger.Controls if(DebugApi.GetMemorySize(SnesMemoryType.GbCartRam) > 0) { this.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbCartRam)); } + if(DebugApi.GetMemorySize(SnesMemoryType.GbBootRom) > 0) { + this.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbBootRom)); + } + this.Items.Add("-"); this.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbVideoRam)); + this.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbSpriteRam)); } } diff --git a/UI/Debugger/Labels/CodeLabel.cs b/UI/Debugger/Labels/CodeLabel.cs index cda4f90..351429d 100644 --- a/UI/Debugger/Labels/CodeLabel.cs +++ b/UI/Debugger/Labels/CodeLabel.cs @@ -34,6 +34,7 @@ namespace Mesen.GUI.Debugger.Labels case SnesMemoryType.GbWorkRam: sb.Append("GBWRAM:"); break; case SnesMemoryType.GbCartRam: sb.Append("GBSRAM:"); break; case SnesMemoryType.GbHighRam: sb.Append("GBHRAM:"); break; + case SnesMemoryType.GbBootRom: sb.Append("GBBOOT:"); break; } sb.Append(Address.ToString("X4")); @@ -74,6 +75,7 @@ namespace Mesen.GUI.Debugger.Labels case "GBWRAM": type = SnesMemoryType.GsuWorkRam; break; case "GBSRAM": type = SnesMemoryType.GbCartRam; break; case "GBHRAM": type = SnesMemoryType.GbHighRam; break; + case "GBBOOT": type = SnesMemoryType.GbBootRom; break; default: return null; } diff --git a/UI/Debugger/Labels/LabelManager.cs b/UI/Debugger/Labels/LabelManager.cs index 8811886..53c686a 100644 --- a/UI/Debugger/Labels/LabelManager.cs +++ b/UI/Debugger/Labels/LabelManager.cs @@ -92,6 +92,7 @@ namespace Mesen.GUI.Debugger.Labels case SnesMemoryType.GbWorkRam: return address | ((ulong)13 << 32); case SnesMemoryType.GbCartRam: return address | ((ulong)14 << 32); case SnesMemoryType.GbHighRam: return address | ((ulong)15 << 32); + case SnesMemoryType.GbBootRom: return address | ((ulong)16 << 32); } throw new Exception("Invalid type"); } diff --git a/UI/Debugger/Labels/ctrlLabelList.cs b/UI/Debugger/Labels/ctrlLabelList.cs index ae91eb2..ab7631d 100644 --- a/UI/Debugger/Labels/ctrlLabelList.cs +++ b/UI/Debugger/Labels/ctrlLabelList.cs @@ -141,6 +141,7 @@ namespace Mesen.GUI.Debugger.Controls case SnesMemoryType.GbWorkRam: prefix = "WRAM: $"; break; case SnesMemoryType.GbCartRam: prefix = "SRAM: $"; break; case SnesMemoryType.GbHighRam: prefix = "HRAM: $"; break; + case SnesMemoryType.GbBootRom: prefix = "BOOT: $"; break; default: throw new Exception("Unsupported type"); } int relAddress = label.GetRelativeAddress(_cpuType).Address; diff --git a/UI/Debugger/Labels/frmEditLabel.cs b/UI/Debugger/Labels/frmEditLabel.cs index b799666..b4e7ba6 100644 --- a/UI/Debugger/Labels/frmEditLabel.cs +++ b/UI/Debugger/Labels/frmEditLabel.cs @@ -64,6 +64,9 @@ namespace Mesen.GUI.Debugger if(DebugApi.GetMemorySize(SnesMemoryType.GbCartRam) > 0) { cboType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbCartRam)); } + if(DebugApi.GetMemorySize(SnesMemoryType.GbBootRom) > 0) { + cboType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GbBootRom)); + } } } diff --git a/UI/Debugger/Profiler/ctrlProfiler.cs b/UI/Debugger/Profiler/ctrlProfiler.cs index d559dae..726e31b 100644 --- a/UI/Debugger/Profiler/ctrlProfiler.cs +++ b/UI/Debugger/Profiler/ctrlProfiler.cs @@ -124,6 +124,7 @@ namespace Mesen.GUI.Debugger.Controls case SnesMemoryType.GbWorkRam: functionName = "WRAM: $"; break; case SnesMemoryType.GbCartRam: functionName = "SRAM: $"; break; case SnesMemoryType.GbHighRam: functionName = "HRAM: $"; break; + case SnesMemoryType.GbBootRom: functionName = "BOOT: $"; break; default: throw new Exception("Unsupported type"); } diff --git a/UI/Dependencies/resources.en.xml b/UI/Dependencies/resources.en.xml index 159e8cf..c211d74 100644 --- a/UI/Dependencies/resources.en.xml +++ b/UI/Dependencies/resources.en.xml @@ -960,10 +960,17 @@ GB - PRG ROM GB - Work RAM GB - Video RAM + GB - Sprite RAM GB - Cart RAM GB - High RAM + GB - Boot ROM Register + + Auto + Game Boy + Game Boy Color + 2 bpp 4 bpp diff --git a/UI/Forms/Config/frmEmulationConfig.Designer.cs b/UI/Forms/Config/frmEmulationConfig.Designer.cs index d1a33ba..04e7fdd 100644 --- a/UI/Forms/Config/frmEmulationConfig.Designer.cs +++ b/UI/Forms/Config/frmEmulationConfig.Designer.cs @@ -75,6 +75,11 @@ this.radBsxLocalTime = new System.Windows.Forms.RadioButton(); this.radBsxCustomTime = new System.Windows.Forms.RadioButton(); this.dtpBsxCustomTime = new System.Windows.Forms.DateTimePicker(); + this.tpgGameboy = new System.Windows.Forms.TabPage(); + this.tableLayoutPanel7 = new System.Windows.Forms.TableLayoutPanel(); + this.lblGameboy = new System.Windows.Forms.Label(); + this.cboGameboyModel = new System.Windows.Forms.ComboBox(); + this.chkUseBootRom = new System.Windows.Forms.CheckBox(); this.tabMain.SuspendLayout(); this.tpgGeneral.SuspendLayout(); this.tableLayoutPanel4.SuspendLayout(); @@ -93,6 +98,8 @@ this.tpgBsx.SuspendLayout(); this.grpBsxDateTime.SuspendLayout(); this.tableLayoutPanel6.SuspendLayout(); + this.tpgGameboy.SuspendLayout(); + this.tableLayoutPanel7.SuspendLayout(); this.SuspendLayout(); // // baseConfigPanel @@ -104,9 +111,10 @@ // tabMain // this.tabMain.Controls.Add(this.tpgGeneral); + this.tabMain.Controls.Add(this.tpgGameboy); + this.tabMain.Controls.Add(this.tpgBsx); this.tabMain.Controls.Add(this.tpgAdvanced); this.tabMain.Controls.Add(this.tpgOverclocking); - this.tabMain.Controls.Add(this.tpgBsx); this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tabMain.Location = new System.Drawing.Point(0, 0); this.tabMain.Name = "tabMain"; @@ -814,6 +822,64 @@ this.dtpBsxCustomTime.Value = new System.DateTime(2000, 1, 1, 0, 0, 0, 0); this.dtpBsxCustomTime.Enter += new System.EventHandler(this.dtpBsxCustomTime_Enter); // + // tpgGameboy + // + this.tpgGameboy.Controls.Add(this.tableLayoutPanel7); + this.tpgGameboy.Location = new System.Drawing.Point(4, 22); + this.tpgGameboy.Name = "tpgGameboy"; + this.tpgGameboy.Padding = new System.Windows.Forms.Padding(3); + this.tpgGameboy.Size = new System.Drawing.Size(437, 264); + this.tpgGameboy.TabIndex = 6; + this.tpgGameboy.Text = "Game Boy"; + this.tpgGameboy.UseVisualStyleBackColor = true; + // + // tableLayoutPanel7 + // + this.tableLayoutPanel7.ColumnCount = 2; + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel7.Controls.Add(this.lblGameboy, 0, 0); + this.tableLayoutPanel7.Controls.Add(this.cboGameboyModel, 1, 0); + this.tableLayoutPanel7.Controls.Add(this.chkUseBootRom, 0, 1); + this.tableLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel7.Location = new System.Drawing.Point(3, 3); + this.tableLayoutPanel7.Name = "tableLayoutPanel7"; + this.tableLayoutPanel7.RowCount = 3; + this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel7.Size = new System.Drawing.Size(431, 258); + this.tableLayoutPanel7.TabIndex = 0; + // + // lblGameboy + // + this.lblGameboy.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblGameboy.AutoSize = true; + this.lblGameboy.Location = new System.Drawing.Point(3, 7); + this.lblGameboy.Name = "lblGameboy"; + this.lblGameboy.Size = new System.Drawing.Size(91, 13); + this.lblGameboy.TabIndex = 0; + this.lblGameboy.Text = "Game Boy Model:"; + // + // cboGameboyModel + // + this.cboGameboyModel.FormattingEnabled = true; + this.cboGameboyModel.Location = new System.Drawing.Point(100, 3); + this.cboGameboyModel.Name = "cboGameboyModel"; + this.cboGameboyModel.Size = new System.Drawing.Size(119, 21); + this.cboGameboyModel.TabIndex = 1; + // + // chkUseBootRom + // + this.chkUseBootRom.AutoSize = true; + this.tableLayoutPanel7.SetColumnSpan(this.chkUseBootRom, 2); + this.chkUseBootRom.Location = new System.Drawing.Point(3, 30); + this.chkUseBootRom.Name = "chkUseBootRom"; + this.chkUseBootRom.Size = new System.Drawing.Size(89, 17); + this.chkUseBootRom.TabIndex = 2; + this.chkUseBootRom.Text = "Use boot rom"; + this.chkUseBootRom.UseVisualStyleBackColor = true; + // // frmEmulationConfig // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -857,6 +923,9 @@ this.grpBsxDateTime.ResumeLayout(false); this.tableLayoutPanel6.ResumeLayout(false); this.tableLayoutPanel6.PerformLayout(); + this.tpgGameboy.ResumeLayout(false); + this.tableLayoutPanel7.ResumeLayout(false); + this.tableLayoutPanel7.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -911,5 +980,10 @@ private System.Windows.Forms.RadioButton radBsxCustomTime; private System.Windows.Forms.DateTimePicker dtpBsxCustomTime; private System.Windows.Forms.DateTimePicker dtpBsxCustomDate; + private System.Windows.Forms.TabPage tpgGameboy; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel7; + private System.Windows.Forms.Label lblGameboy; + private System.Windows.Forms.ComboBox cboGameboyModel; + private System.Windows.Forms.CheckBox chkUseBootRom; } } \ No newline at end of file diff --git a/UI/Forms/Config/frmEmulationConfig.cs b/UI/Forms/Config/frmEmulationConfig.cs index a32c8f3..db66ce3 100644 --- a/UI/Forms/Config/frmEmulationConfig.cs +++ b/UI/Forms/Config/frmEmulationConfig.cs @@ -37,6 +37,9 @@ namespace Mesen.GUI.Forms.Config AddBinding(nameof(EmulationConfig.PpuExtraScanlinesBeforeNmi), nudExtraScanlinesBeforeNmi); AddBinding(nameof(EmulationConfig.PpuExtraScanlinesAfterNmi), nudExtraScanlinesAfterNmi); AddBinding(nameof(EmulationConfig.GsuClockSpeed), nudGsuClockSpeed); + + AddBinding(nameof(EmulationConfig.GbModel), cboGameboyModel); + AddBinding(nameof(EmulationConfig.GbUseBootRom), chkUseBootRom); long customDate = ConfigManager.Config.Emulation.BsxCustomDate; if(customDate >= 0) { diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index fa3ea99..96d5844 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -213,8 +213,10 @@ namespace Mesen.GUI GbPrgRom, GbWorkRam, GbCartRam, - GbVideoRam, GbHighRam, + GbBootRom, + GbVideoRam, + GbSpriteRam, Register, } @@ -245,6 +247,7 @@ namespace Mesen.GUI case SnesMemoryType.GbWorkRam: case SnesMemoryType.GbCartRam: case SnesMemoryType.GbHighRam: + case SnesMemoryType.GbBootRom: return CpuType.Gameboy; default: @@ -281,6 +284,7 @@ namespace Mesen.GUI case SnesMemoryType.GbWorkRam: case SnesMemoryType.GbCartRam: case SnesMemoryType.GbHighRam: + case SnesMemoryType.GbBootRom: return true; } diff --git a/UI/Interop/DebugState.cs b/UI/Interop/DebugState.cs index f18bf4f..284a46d 100644 --- a/UI/Interop/DebugState.cs +++ b/UI/Interop/DebugState.cs @@ -736,6 +736,7 @@ namespace Mesen.GUI public byte Status; public UInt32 FrameCount; + [MarshalAs(UnmanagedType.I1)] public bool CgbEnabled; public byte CgbVramBank; public byte CgbBgPalPosition; diff --git a/UI/Interop/EmuApi.cs b/UI/Interop/EmuApi.cs index 5751b19..310e7c2 100644 --- a/UI/Interop/EmuApi.cs +++ b/UI/Interop/EmuApi.cs @@ -208,10 +208,26 @@ namespace Mesen.GUI Gameboy } + public enum FirmwareType + { + CX4, + DSP1, + DSP1B, + DSP2, + DSP3, + DSP4, + ST010, + ST011, + ST018, + Satellaview, + Gameboy, + GameboyColor + } + public struct MissingFirmwareMessage { public IntPtr Filename; - public CoprocessorType FirmwareType; + public FirmwareType Firmware; public UInt32 Size; } } diff --git a/UI/Utilities/FirmwareHelper.cs b/UI/Utilities/FirmwareHelper.cs index cc35d78..e921cbb 100644 --- a/UI/Utilities/FirmwareHelper.cs +++ b/UI/Utilities/FirmwareHelper.cs @@ -28,38 +28,41 @@ namespace Mesen.GUI.Utilities } } - private static List GetExpectedHashes(CoprocessorType type) + private static List GetExpectedHashes(FirmwareType type) { switch(type) { - case CoprocessorType.CX4: return new List() { "AE8D4D1961B93421FF00B3CAA1D0F0CE7783E749772A3369C36B3DBF0D37EF18" }; - case CoprocessorType.DSP1: return new List() { "91E87D11E1C30D172556BED2211CCE2EFA94BA595F58C5D264809EF4D363A97B" }; - case CoprocessorType.DSP1B: return new List() { "D789CB3C36B05C0B23B6C6F23BE7AA37C6E78B6EE9CEAC8D2D2AA9D8C4D35FA9" }; - case CoprocessorType.DSP2: return new List() { "03EF4EF26C9F701346708CB5D07847B5203CF1B0818BF2930ACD34510FFDD717" }; - case CoprocessorType.DSP3: return new List() { "0971B08F396C32E61989D1067DDDF8E4B14649D548B2188F7C541B03D7C69E4E" }; - case CoprocessorType.DSP4: return new List() { "752D03B2D74441E430B7F713001FA241F8BBCFC1A0D890ED4143F174DBE031DA" }; - case CoprocessorType.ST010: return new List() { "FA9BCED838FEDEA11C6F6ACE33D1878024BDD0D02CC9485899D0BDD4015EC24C" }; - case CoprocessorType.ST011: return new List() { "8B2B3F3F3E6E29F4D21D8BC736B400BC988B7D2214EBEE15643F01C1FEE2F364" }; - case CoprocessorType.ST018: return new List() { "6DF209AB5D2524D1839C038BE400AE5EB20DAFC14A3771A3239CD9E8ACD53806" }; - case CoprocessorType.Satellaview: return new List() { + case FirmwareType.CX4: return new List() { "AE8D4D1961B93421FF00B3CAA1D0F0CE7783E749772A3369C36B3DBF0D37EF18" }; + case FirmwareType.DSP1: return new List() { "91E87D11E1C30D172556BED2211CCE2EFA94BA595F58C5D264809EF4D363A97B" }; + case FirmwareType.DSP1B: return new List() { "D789CB3C36B05C0B23B6C6F23BE7AA37C6E78B6EE9CEAC8D2D2AA9D8C4D35FA9" }; + case FirmwareType.DSP2: return new List() { "03EF4EF26C9F701346708CB5D07847B5203CF1B0818BF2930ACD34510FFDD717" }; + case FirmwareType.DSP3: return new List() { "0971B08F396C32E61989D1067DDDF8E4B14649D548B2188F7C541B03D7C69E4E" }; + case FirmwareType.DSP4: return new List() { "752D03B2D74441E430B7F713001FA241F8BBCFC1A0D890ED4143F174DBE031DA" }; + case FirmwareType.ST010: return new List() { "FA9BCED838FEDEA11C6F6ACE33D1878024BDD0D02CC9485899D0BDD4015EC24C" }; + case FirmwareType.ST011: return new List() { "8B2B3F3F3E6E29F4D21D8BC736B400BC988B7D2214EBEE15643F01C1FEE2F364" }; + case FirmwareType.ST018: return new List() { "6DF209AB5D2524D1839C038BE400AE5EB20DAFC14A3771A3239CD9E8ACD53806" }; + case FirmwareType.Satellaview: return new List() { "27CFDB99F7E4252BF3740D420147B63C4C88616883BC5E7FE43F2F30BF8C8CBB", //Japan, no DRM "A49827B45FF9AC9CF5B4658190E1428E59251BC82D8A63D8E9E0F71E439F008F", //English, no DRM "3CE321496EDC5D77038DE2034EB3FB354D7724AFD0BC7FD0319F3EB5D57B984D", //Japan, original "77D94D64D745014BF8B51280A4204056CDEB9D41EA30EAE80DBC006675BEBEF8", //English, DRM }; + case FirmwareType.Gameboy: return new List() { "CF053ECCB4CCAFFF9E67339D4E78E98DCE7D1ED59BE819D2A1BA2232C6FCE1C7", "26E71CF01E301E5DC40E987CD2ECBF6D0276245890AC829DB2A25323DA86818E" }; + case FirmwareType.GameboyColor: return new List() { "B4F2E416A35EEF52CBA161B159C7C8523A92594FACB924B3EDE0D722867C50C7", "3A307A41689BEE99A9A32EA021BF45136906C86B2E4F06C806738398E4F92E45" }; + } - throw new Exception("Unexpected coprocessor type"); + throw new Exception("Unexpected firmware type"); } public static void RequestFirmwareFile(MissingFirmwareMessage msg) { string filename = Utf8Marshaler.GetStringFromIntPtr(msg.Filename); - if(MesenMsgBox.Show("FirmwareNotFound", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, msg.FirmwareType.ToString(), filename, msg.Size.ToString()) == DialogResult.OK) { + if(MesenMsgBox.Show("FirmwareNotFound", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, msg.Firmware.ToString(), filename, msg.Size.ToString()) == DialogResult.OK) { using(OpenFileDialog ofd = new OpenFileDialog()) { ofd.SetFilter(ResourceHelper.GetMessage("FilterAll")); if(ofd.ShowDialog(frmMain.Instance) == DialogResult.OK) { - List expectedHashes = GetExpectedHashes(msg.FirmwareType); + List expectedHashes = GetExpectedHashes(msg.Firmware); if(!expectedHashes.Contains(GetFileHash(ofd.FileName))) { - if(MesenMsgBox.Show("FirmwareMismatch", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, msg.FirmwareType.ToString(), GetFileHash(ofd.FileName), expectedHashes[0]) != DialogResult.OK) { + if(MesenMsgBox.Show("FirmwareMismatch", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, msg.Firmware.ToString(), GetFileHash(ofd.FileName), expectedHashes[0]) != DialogResult.OK) { //Files don't match and user cancelled the action return; }