diff --git a/Core/Gameboy.cpp b/Core/Gameboy.cpp index e3e8c67b..9d0b6991 100644 --- a/Core/Gameboy.cpp +++ b/Core/Gameboy.cpp @@ -31,7 +31,13 @@ Gameboy* Gameboy::Create(Console* console, VirtualFile &romFile, bool sgbEnabled 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)" : "")); diff --git a/Core/GbApu.cpp b/Core/GbApu.cpp index 9472703f..9a56eaaf 100644 --- a/Core/GbApu.cpp +++ b/Core/GbApu.cpp @@ -293,6 +293,17 @@ void GbApu::Write(uint16_t addr, uint8_t value) } } +uint8_t GbApu::ReadCgbRegister(uint16_t addr) +{ + switch(addr) { + case 0xFF76: return _square1->GetOutput() | (_square2->GetOutput() << 4); + case 0xFF77: return _noise->GetOutput() | (_wave->GetOutput() << 4); + } + + //Should not be called + return 0; +} + template void GbApu::ProcessLengthEnableFlag(uint8_t value, T &length, bool &lengthEnabled, bool &enabled) { diff --git a/Core/GbApu.h b/Core/GbApu.h index c5ae2c60..073a6634 100644 --- a/Core/GbApu.h +++ b/Core/GbApu.h @@ -57,6 +57,8 @@ class GbApu : public ISerializable uint8_t Read(uint16_t addr); void Write(uint16_t addr, uint8_t value); + uint8_t ReadCgbRegister(uint16_t addr); + template void ProcessLengthEnableFlag(uint8_t value, T& length, bool& lengthEnabled, bool& enabled); void Serialize(Serializer& s) override; diff --git a/Core/GbDmaController.cpp b/Core/GbDmaController.cpp index 85139cba..113b80ea 100644 --- a/Core/GbDmaController.cpp +++ b/Core/GbDmaController.cpp @@ -60,10 +60,6 @@ void GbDmaController::Write(uint8_t value) uint8_t GbDmaController::ReadCgb(uint16_t addr) { switch(addr) { - case 0xFF51: return _state.CgbDmaSource >> 8; - case 0xFF52: return _state.CgbDmaSource & 0xFF; - case 0xFF53: return _state.CgbDmaDest >> 8; - case 0xFF54: return _state.CgbDmaDest & 0xFF; case 0xFF55: return _state.CgbDmaLength | (_state.CgbHdmaDone ? 0x80 : 0); } diff --git a/Core/GbMemoryManager.cpp b/Core/GbMemoryManager.cpp index 2e796762..87ec445a 100644 --- a/Core/GbMemoryManager.cpp +++ b/Core/GbMemoryManager.cpp @@ -248,18 +248,28 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr) } else if(addr >= 0xFF4C) { if(_gameboy->IsCgb()) { switch(addr) { - //FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch - case 0xFF4D: return _state.CgbHighSpeed ? 0x80 : 0; - - case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: //CGB - DMA - return _dmaController->ReadCgb(addr); + case 0xFF55: //CGB - DMA + return _ppu->IsCgbEnabled() ? _dmaController->ReadCgb(addr) : 0xFF; case 0xFF4F: //CGB - VRAM bank case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette return _ppu->ReadCgbRegister(addr); //FF70 - SVBK - CGB Mode Only - WRAM Bank - case 0xFF70: return _state.CgbWorkRamBank; + case 0xFF70: return _ppu->IsCgbEnabled() ? (_state.CgbWorkRamBank | 0xF8) : 0xFF; + case 0xFF72: return _state.CgbRegFF72; + case 0xFF73: return _state.CgbRegFF73; + + case 0xFF74: + if(_ppu->IsCgbEnabled()) { + return _state.CgbRegFF74; + } + return 0xFF; + + case 0xFF75: return _state.CgbRegFF75 | 0x8F; + + case 0xFF76: case 0xFF77: + return _apu->ReadCgbRegister(addr); } } LogDebug("[Debug] GB - Missing read handler: $" + HexUtilities::ToHex(addr)); @@ -274,7 +284,7 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr) case 0xFF00: return ReadInputPort(); break; case 0xFF01: return _state.SerialData; //SB - Serial transfer data (R/W) - case 0xFF02: return _state.SerialControl | ~(_gameboy->IsCgb() ? 0x83 : 0x81); //SC - Serial Transfer Control (R/W) + case 0xFF02: return _state.SerialControl | 0x7E; //SC - Serial Transfer Control (R/W) case 0xFF04: case 0xFF05: case 0xFF06: case 0xFF07: return _timer->Read(addr); @@ -313,11 +323,15 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value) switch(addr) { case 0xFF4D: //FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch - _state.CgbSwitchSpeedRequest = (value & 0x01) != 0; + if(_ppu->IsCgbEnabled()) { + _state.CgbSwitchSpeedRequest = (value & 0x01) != 0; + } break; case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: //CGB - DMA - _dmaController->WriteCgb(addr, value); + if(_ppu->IsCgbEnabled()) { + _dmaController->WriteCgb(addr, value); + } break; case 0xFF4C: //CGB - "LCDMODE", set by boot rom to turn off CGB features for the LCD for DMG games @@ -333,10 +347,22 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value) case 0xFF70: //FF70 - SVBK - CGB Mode Only - WRAM Bank - _state.CgbWorkRamBank = std::max(1, value & 0x07); - RefreshMappings(); + if(_ppu->IsCgbEnabled()) { + _state.CgbWorkRamBank = std::max(1, value & 0x07); + RefreshMappings(); + } + break; + + case 0xFF72: _state.CgbRegFF72 = value; break; + case 0xFF73: _state.CgbRegFF73 = value; break; + case 0xFF74: + if(_ppu->IsCgbEnabled()) { + _state.CgbRegFF74 = value; + } break; + case 0xFF75: _state.CgbRegFF75 = value; break; + default: LogDebug("[Debug] GBC - Missing write handler: $" + HexUtilities::ToHex(addr)); break; @@ -417,6 +443,11 @@ bool GbMemoryManager::IsHighSpeed() return _state.CgbHighSpeed; } +bool GbMemoryManager::IsBootRomDisabled() +{ + return _state.DisableBootRom; +} + uint64_t GbMemoryManager::GetCycleCount() { return _state.CycleCount; @@ -485,7 +516,8 @@ void GbMemoryManager::Serialize(Serializer& s) s.Stream( _state.DisableBootRom, _state.IrqEnabled, _state.IrqRequests, _state.InputSelect, _state.ApuCycleCount, _state.CgbHighSpeed, _state.CgbSwitchSpeedRequest, _state.CgbWorkRamBank, - _state.SerialData, _state.SerialControl, _state.SerialBitCount, _state.CycleCount + _state.SerialData, _state.SerialControl, _state.SerialBitCount, _state.CycleCount, + _state.CgbRegFF72, _state.CgbRegFF73, _state.CgbRegFF74, _state.CgbRegFF75 ); s.StreamArray(_state.MemoryType, 0x100); s.StreamArray(_state.MemoryOffset, 0x100); diff --git a/Core/GbMemoryManager.h b/Core/GbMemoryManager.h index ef3f0d72..27c150bd 100644 --- a/Core/GbMemoryManager.h +++ b/Core/GbMemoryManager.h @@ -70,6 +70,8 @@ class GbMemoryManager : public ISerializable void ToggleSpeed(); bool IsHighSpeed(); + bool IsBootRomDisabled(); + uint64_t GetCycleCount(); uint64_t GetApuCycleCount(); diff --git a/Core/GbPpu.cpp b/Core/GbPpu.cpp index 5c1b4702..52a5c428 100644 --- a/Core/GbPpu.cpp +++ b/Core/GbPpu.cpp @@ -609,6 +609,11 @@ bool GbPpu::IsLcdEnabled() return _state.LcdEnabled; } +bool GbPpu::IsCgbEnabled() +{ + return _state.CgbEnabled; +} + PpuMode GbPpu::GetMode() { return _state.Mode; @@ -892,11 +897,15 @@ void GbPpu::WriteOam(uint8_t addr, uint8_t value, bool forDma) uint8_t GbPpu::ReadCgbRegister(uint16_t addr) { + if(!_state.CgbEnabled) { + return 0xFF; + } + switch(addr) { - case 0xFF4F: return _state.CgbVramBank; - case 0xFF68: return _state.CgbBgPalPosition | (_state.CgbBgPalAutoInc ? 0x80 : 0); + case 0xFF4F: return _state.CgbVramBank | 0xFE; + case 0xFF68: return _state.CgbBgPalPosition | (_state.CgbBgPalAutoInc ? 0x80 : 0) | 0x40; case 0xFF69: return (_state.CgbBgPalettes[_state.CgbBgPalPosition >> 1] >> ((_state.CgbBgPalPosition & 0x01) ? 8 : 0) & 0xFF); - case 0xFF6A: return _state.CgbObjPalPosition | (_state.CgbObjPalAutoInc ? 0x80 : 0); + case 0xFF6A: return _state.CgbObjPalPosition | (_state.CgbObjPalAutoInc ? 0x80 : 0) | 0x40; case 0xFF6B: return (_state.CgbObjPalettes[_state.CgbObjPalPosition >> 1] >> ((_state.CgbObjPalPosition & 0x01) ? 8 : 0) & 0xFF); } LogDebug("[Debug] GBC - Missing read handler: $" + HexUtilities::ToHex(addr)); @@ -905,6 +914,10 @@ uint8_t GbPpu::ReadCgbRegister(uint16_t addr) void GbPpu::WriteCgbRegister(uint16_t addr, uint8_t value) { + if(!_state.CgbEnabled && _memoryManager->IsBootRomDisabled()) { + return; + } + switch(addr) { case 0xFF4C: _state.CgbEnabled = (value & 0x0C) == 0; break; case 0xFF4F: _state.CgbVramBank = value & 0x01; break; diff --git a/Core/GbPpu.h b/Core/GbPpu.h index 979b5574..ff0415ca 100644 --- a/Core/GbPpu.h +++ b/Core/GbPpu.h @@ -87,6 +87,7 @@ class GbPpu : public ISerializable uint8_t GetScanline(); uint16_t GetCycle(); bool IsLcdEnabled(); + bool IsCgbEnabled(); PpuMode GetMode(); void Exec(); diff --git a/Core/GbTypes.h b/Core/GbTypes.h index e8aa5e13..959bc03b 100644 --- a/Core/GbTypes.h +++ b/Core/GbTypes.h @@ -353,6 +353,11 @@ struct GbMemoryManagerState bool CgbSwitchSpeedRequest; bool CgbHighSpeed; + uint8_t CgbRegFF72; + uint8_t CgbRegFF73; + uint8_t CgbRegFF74; + uint8_t CgbRegFF75; + bool DisableBootRom; uint8_t IrqRequests; uint8_t IrqEnabled; diff --git a/UI/Interop/DebugState.cs b/UI/Interop/DebugState.cs index 8d4e4e34..aea21494 100644 --- a/UI/Interop/DebugState.cs +++ b/UI/Interop/DebugState.cs @@ -632,6 +632,11 @@ public struct GbMemoryManagerState [MarshalAs(UnmanagedType.I1)] public bool CgbSwitchSpeedRequest; [MarshalAs(UnmanagedType.I1)] public bool CgbHighSpeed; + public byte CgbRegFF72; + public byte CgbRegFF73; + public byte CgbRegFF74; + public byte CgbRegFF75; + [MarshalAs(UnmanagedType.I1)] public bool DisableBootRom; public byte IrqRequests; public byte IrqEnabled;