From 2ccef883759931d2d86aeb280cd0c18ffa36fe72 Mon Sep 17 00:00:00 2001 From: Sour Date: Sat, 30 May 2020 23:23:35 -0400 Subject: [PATCH] GB: PPU accuracy improvements Passes all moooneye-gb tests --- Core/GbCpu.cpp | 21 +- Core/GbCpu.h | 1 + Core/GbDmaController.cpp | 8 +- Core/GbMemoryManager.cpp | 8 +- Core/GbPpu.cpp | 329 ++++++++++++++------- Core/GbPpu.h | 8 +- Core/GbTimer.cpp | 10 +- Core/GbTimer.h | 2 - Core/GbTypes.h | 8 +- UI/Debugger/PpuViewer/frmRegisterViewer.cs | 2 +- UI/Debugger/frmDebugger.cs | 17 +- UI/Interop/DebugState.cs | 8 +- 12 files changed, 297 insertions(+), 125 deletions(-) diff --git a/Core/GbCpu.cpp b/Core/GbCpu.cpp index 108db08..88b6ca4 100644 --- a/Core/GbCpu.cpp +++ b/Core/GbCpu.cpp @@ -353,22 +353,30 @@ void GbCpu::ExecOpCode(uint8_t opCode) } void GbCpu::IncCycleCount() +{ + _memoryManager->Exec(); + _memoryManager->Exec(); +} + +void GbCpu::HalfCycle() { _memoryManager->Exec(); } uint8_t GbCpu::ReadOpCode() { - IncCycleCount(); + HalfCycle(); uint8_t value = _memoryManager->Read(_state.PC, MemoryOperationType::ExecOpCode); + HalfCycle(); _state.PC++; return value; } uint8_t GbCpu::ReadCode() { - IncCycleCount(); + HalfCycle(); uint8_t value = _memoryManager->Read(_state.PC, MemoryOperationType::ExecOperand); + HalfCycle(); _state.PC++; return value; } @@ -382,14 +390,17 @@ uint16_t GbCpu::ReadCodeWord() uint8_t GbCpu::Read(uint16_t addr) { - IncCycleCount(); - return _memoryManager->Read(addr, MemoryOperationType::Read); + HalfCycle(); + uint8_t value = _memoryManager->Read(addr, MemoryOperationType::Read); + HalfCycle(); + return value; } void GbCpu::Write(uint16_t addr, uint8_t value) { - IncCycleCount(); + HalfCycle(); _memoryManager->Write(addr, value); + HalfCycle(); } bool GbCpu::CheckFlag(uint8_t flag) diff --git a/Core/GbCpu.h b/Core/GbCpu.h index 01d8892..8bf43d5 100644 --- a/Core/GbCpu.h +++ b/Core/GbCpu.h @@ -30,6 +30,7 @@ public: void ExecOpCode(uint8_t opCode); __forceinline void IncCycleCount(); + __forceinline void HalfCycle(); __forceinline uint8_t ReadOpCode(); __forceinline uint8_t ReadCode(); __forceinline uint16_t ReadCodeWord(); diff --git a/Core/GbDmaController.cpp b/Core/GbDmaController.cpp index 4b94130..f989232 100644 --- a/Core/GbDmaController.cpp +++ b/Core/GbDmaController.cpp @@ -110,16 +110,18 @@ void GbDmaController::ProcessDmaBlock() //TODO check invalid dma sources/etc. //4 cycles for setup _memoryManager->Exec(); + _memoryManager->Exec(); for(int i = 0; i < 16; i++) { uint16_t dst = 0x8000 | ((_state.CgbDmaDest + i) & 0x1FFF); //2 or 4 cycles per byte transfered (2x more cycles in high speed mode - effective speed is the same in both modes - if(_memoryManager->IsHighSpeed() || (i & 0x01)) { - //TODO: Not perfectly accurate (in "slow" mode mode both occur on the same cycle) + _memoryManager->Exec(); + uint8_t value = _memoryManager->Read(_state.CgbDmaSource + i, MemoryOperationType::DmaRead); + if(_memoryManager->IsHighSpeed()) { _memoryManager->Exec(); } - _memoryManager->Write(dst, _memoryManager->Read(_state.CgbDmaSource + i, MemoryOperationType::DmaRead)); + _memoryManager->Write(dst, value); } //Source/Dest/Length are all modified by the DMA process and keep their last value after DMA completes diff --git a/Core/GbMemoryManager.cpp b/Core/GbMemoryManager.cpp index 927bf94..9559a78 100644 --- a/Core/GbMemoryManager.cpp +++ b/Core/GbMemoryManager.cpp @@ -74,11 +74,13 @@ void GbMemoryManager::RefreshMappings() void GbMemoryManager::Exec() { - _state.CycleCount += 4; - _state.ApuCycleCount += _state.CgbHighSpeed ? 2 : 4; + _state.CycleCount += 2; + _state.ApuCycleCount += _state.CgbHighSpeed ? 1 : 2; _timer->Exec(); _ppu->Exec(); - _dmaController->Exec(); + if((_state.CycleCount & 0x03) == 0) { + _dmaController->Exec(); + } if(_state.SerialBitCount && (_state.CycleCount & 0x1FF) == 0) { _state.SerialData = (_state.SerialData << 1) | 0x01; diff --git a/Core/GbPpu.cpp b/Core/GbPpu.cpp index 0fd8e90..7eaa01b 100644 --- a/Core/GbPpu.cpp +++ b/Core/GbPpu.cpp @@ -38,6 +38,7 @@ void GbPpu::Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryMana _currentEventViewerBuffer = _eventViewerBuffers[0]; _state = {}; + _state.Cycle = -1; _state.Mode = PpuMode::HBlank; _state.CgbEnabled = _gameboy->IsCgb(); _lastFrameTime = 0; @@ -96,69 +97,40 @@ void GbPpu::Exec() return; } - uint8_t cyclesToRun = _memoryManager->IsHighSpeed() ? 2 : 4; + uint8_t cyclesToRun = _memoryManager->IsHighSpeed() ? 1 : 2; for(int i = 0; i < cyclesToRun; i++) { + _state.Cycle++; + if(_state.IdleCycles > 0) { + _state.IdleCycles--; + ProcessPpuCycle(); + continue; + } + ExecCycle(); } } void GbPpu::ExecCycle() { - _state.Cycle++; + PpuMode oldMode = _state.IrqMode; - PpuMode oldMode = _state.Mode; - bool oldCoincidenceFlag = _state.LyCoincidenceFlag; - - switch(_state.Cycle) { - case 4: { - if(_state.Scanline < 144) { - _spriteCount = 0; - ChangeMode(PpuMode::OamEvaluation); - } else if(_state.Scanline == 144) { - ChangeMode(PpuMode::VBlank); - _windowCounter = -1; - _memoryManager->RequestIrq(GbIrqSource::VerticalBlank); - SendFrame(); - } - break; + if(_state.Scanline < 144) { + if(_state.Scanline == 0 && _isFirstFrame) { + ProcessFirstScanlineAfterPowerOn(); + } else { + ProcessVisibleScanline(); } - - case 84: { - if(_state.Scanline < 144) { - _latchWindowX = _state.WindowX; - _latchWindowY = _state.WindowY; - _latchWindowEnabled = _state.WindowEnabled; - ChangeMode(PpuMode::Drawing); - ResetRenderer(); - } - break; - } - - case 456: { - _state.Cycle = 0; - _state.Scanline++; - - if(_state.Scanline < 144) { - ChangeMode(PpuMode::HBlank); - } else if(_state.Scanline == 154) { - _state.Scanline = 0; - ChangeMode(PpuMode::HBlank); - _console->ProcessEvent(EventType::StartFrame); - if(_console->IsDebugging()) { - _currentEventViewerBuffer = _currentEventViewerBuffer == _eventViewerBuffers[0] ? _eventViewerBuffers[1] : _eventViewerBuffers[0]; - } - } - break; - } - } + } else { + ProcessVblankScanline(); + } if(_state.Mode == PpuMode::Drawing) { - if(_drawnPixels < 160) { - RunDrawCycle(); - } else { - ChangeMode(PpuMode::HBlank); + RunDrawCycle(); + if(_drawnPixels == 160) { + //Mode turns to hblank on the same cycle as the last pixel is output (IRQ is on next cycle) + _state.Mode = PpuMode::HBlank; if(_state.Scanline < 143) { - //"This mode will transfer one block (16 bytes) during each H-Blank. No data is transferred during VBlank (LY = 143 – 153" + //"This mode will transfer one block (16 bytes) during each H-Blank. No data is transferred during VBlank (LY = 143 – 153)" _dmaController->ProcessHdma(); } } @@ -166,14 +138,161 @@ void GbPpu::ExecCycle() RunSpriteEvaluation(); } - UpdateLyCoincidenceFlag(); - if(_state.Mode != oldMode || _state.LyCoincidenceFlag != oldCoincidenceFlag) { + bool coincidenceFlag = (_state.LyCompare == _state.LyForCompare); + if(_state.IrqMode != oldMode || _state.LyCoincidenceFlag != coincidenceFlag) { + _state.LyCoincidenceFlag = coincidenceFlag; UpdateStatIrq(); } ProcessPpuCycle(); } +void GbPpu::ProcessVblankScanline() +{ + switch(_state.Cycle) { + case 2: + if(_state.Scanline == 144) { + _state.IrqMode = PpuMode::OamEvaluation; + } + break; + + case 4: + if(_state.Scanline < 153) { + _state.LyForCompare = _state.Scanline; + if(_state.Scanline == 144) { + _state.Mode = PpuMode::VBlank; + _state.IrqMode = PpuMode::VBlank; + _windowCounter = -1; + _memoryManager->RequestIrq(GbIrqSource::VerticalBlank); + SendFrame(); + } + } + break; + + case 6: + if(_state.Scanline == 153) { + _state.Ly = 0; + _state.LyForCompare = _state.Scanline; + } + break; + + case 8: + if(_state.Scanline == 153) { + _state.LyForCompare = -1; + } + break; + + case 12: + if(_state.Scanline == 153) { + _state.LyForCompare = 0; + } + _state.IdleCycles = 456 - 12 - 1; + break; + + case 456: + _state.Cycle = 0; + _state.Scanline++; + + if(_state.Scanline == 154) { + _state.Scanline = 0; + _console->ProcessEvent(EventType::StartFrame); + if(_console->IsDebugging()) { + _currentEventViewerBuffer = _currentEventViewerBuffer == _eventViewerBuffers[0] ? _eventViewerBuffers[1] : _eventViewerBuffers[0]; + } + } else { + _state.Ly = _state.Scanline; + _state.LyForCompare = -1; + } + break; + } +} + +void GbPpu::ProcessFirstScanlineAfterPowerOn() +{ + switch(_state.Cycle) { + case 1: + _state.IrqMode = PpuMode::NoIrq; + break; + + case 79: + _latchWindowX = _state.WindowX; + _latchWindowY = _state.WindowY; + _latchWindowEnabled = _state.WindowEnabled; + _state.Mode = PpuMode::Drawing; + _state.IrqMode = PpuMode::Drawing; + ResetRenderer(); + _rendererIdle = true; + break; + + case 84: + _rendererIdle = false; + break; + + case 448: + _state.Cycle = 0; + _state.Scanline++; + _drawnPixels = 0; + _state.Mode = PpuMode::HBlank; + _state.IrqMode = PpuMode::HBlank; + break; + } +} + +void GbPpu::ProcessVisibleScanline() +{ + if(_drawnPixels == 160) { + //IRQ flag for Hblank is 1 cycle late compared to the mode register + _state.IrqMode = PpuMode::HBlank; + _drawnPixels = 0; + _state.IdleCycles = 456 - _state.Cycle - 1; + } + + switch(_state.Cycle) { + case 3: + _state.Ly = _state.Scanline; + _state.LyForCompare = -1; + + if(_state.Scanline > 0) { + //On scanlines 1-143, the OAM IRQ fires 1 cycle early + _state.IrqMode = PpuMode::OamEvaluation; + } else { + //On scanline 0, hblank gets set for 1 cycle here + _state.Mode = PpuMode::HBlank; + } + break; + + case 4: + _spriteCount = 0; + _state.LyForCompare = _state.Scanline; + _state.Mode = PpuMode::OamEvaluation; + _state.IrqMode = PpuMode::OamEvaluation; //TODO set to noirq? + break; + + case 84: + _latchWindowX = _state.WindowX; + _latchWindowY = _state.WindowY; + _latchWindowEnabled = _state.WindowEnabled; + _state.Mode = PpuMode::Drawing; + _state.IrqMode = PpuMode::Drawing; + _rendererIdle = true; + ResetRenderer(); + break; + + case 89: + _rendererIdle = false; + break; + + case 456: + _state.Cycle = 0; + _state.Scanline++; + if(_state.Scanline == 144) { + _state.Ly = 144; + _state.LyForCompare = -1; + } + break; + } +} + void GbPpu::ProcessPpuCycle() { if(_console->IsDebugging()) { @@ -192,7 +311,7 @@ void GbPpu::ProcessPpuCycle() void GbPpu::RunDrawCycle() { - if(_state.Cycle < 89) { + if(_rendererIdle) { //Idle cycles _evtColor = EvtColor::RenderingIdle; return; @@ -213,6 +332,14 @@ void GbPpu::RunDrawCycle() return; } + FindNextSprite(); + if(_fetchSprite >= 0 && _bgFetcher.Step >= 5 && _bgFifo.Size > 0) { + _evtColor = EvtColor::RenderingOamLoad; + ClockSpriteFetcher(); + FindNextSprite(); + return; + } + if(_fetchSprite == -1 && _bgFifo.Size > 0) { if(_drawnPixels >= 0) { uint16_t outOffset = _state.Scanline * 256 + _drawnPixels; @@ -332,14 +459,6 @@ void GbPpu::FindNextSprite() void GbPpu::ClockTileFetcher() { - FindNextSprite(); - if(_fetchSprite >= 0 && _bgFetcher.Step >= 5 && _bgFifo.Size > 0) { - _evtColor = EvtColor::RenderingOamLoad; - ClockSpriteFetcher(); - FindNextSprite(); - return; - } - _evtColor = EvtColor::RenderingBgLoad; switch(_bgFetcher.Step++) { @@ -441,32 +560,14 @@ void GbPpu::PushTileToPixelFifo() _bgFetcher.Step = 0; } -void GbPpu::ChangeMode(PpuMode mode) -{ - _state.Mode = mode; -} - -void GbPpu::UpdateLyCoincidenceFlag() -{ - if(_state.Scanline < 153) { - _state.LyCoincidenceFlag = (_state.LyCompare == _state.Scanline) && (_state.Cycle >= 4 || _state.Scanline == 0); - } else { - if(_state.LyCompare == 153) { - _state.LyCoincidenceFlag = (_state.LyCompare == _state.Scanline) && _state.Cycle >= 4 && _state.Cycle < 8; - } else { - _state.LyCoincidenceFlag = (_state.LyCompare == 0) && _state.Cycle >= 12; - } - } -} - void GbPpu::UpdateStatIrq() { bool irqFlag = ( _state.LcdEnabled && ((_state.LyCoincidenceFlag && (_state.Status & GbPpuStatusFlags::CoincidenceIrq)) || - (_state.Mode == PpuMode::HBlank && (_state.Status & GbPpuStatusFlags::HBlankIrq)) || - (_state.Mode == PpuMode::OamEvaluation && (_state.Status & GbPpuStatusFlags::OamIrq)) || - (_state.Mode == PpuMode::VBlank && ((_state.Status & GbPpuStatusFlags::VBlankIrq) || (_state.Status & GbPpuStatusFlags::OamIrq)))) + (_state.IrqMode == PpuMode::HBlank && (_state.Status & GbPpuStatusFlags::HBlankIrq)) || + (_state.IrqMode == PpuMode::OamEvaluation && (_state.Status & GbPpuStatusFlags::OamIrq)) || + (_state.IrqMode == PpuMode::VBlank && (_state.Status & GbPpuStatusFlags::VBlankIrq))) ); if(irqFlag && !_state.StatIrqFlag) { @@ -546,11 +647,7 @@ uint8_t GbPpu::Read(uint16_t addr) case 0xFF42: return _state.ScrollY; //FF42 - SCY - Scroll Y (R/W) case 0xFF43: return _state.ScrollX; //FF43 - SCX - Scroll X (R/W) - case 0xFF44: - //FF44 - LY - LCDC Y-Coordinate (R) - //On the last scanline (153), at cycle >= 4 (TODO: exact cycle varies by model) , the value 0 is returned instead of 153 - return (_state.Scanline < 153 || _state.Cycle < 4) ? _state.Scanline : 0; - + case 0xFF44: return _state.Ly; //FF44 - LY - LCDC Y-Coordinate (R) case 0xFF45: return _state.LyCompare; //FF45 - LYC - LY Compare (R/W) case 0xFF47: return _state.BgPalette; //FF47 - BGP - BG Palette Data (R/W) - Non CGB Mode Only case 0xFF48: return _state.ObjPalette0; //FF48 - OBP0 - Object Palette 0 Data (R/W) - Non CGB Mode Only @@ -575,7 +672,10 @@ void GbPpu::Write(uint16_t addr, uint8_t value) //Reset LCD to top of screen when it gets turned off _state.Cycle = 0; _state.Scanline = 0; - ChangeMode(PpuMode::HBlank); + _state.Ly = 0; + _state.LyForCompare = -1; + _state.Mode = PpuMode::HBlank; + _state.IrqMode = PpuMode::HBlank; //Send a blank (white) frame _lastFrameTime = _gameboy->GetCycleCount(); @@ -587,11 +687,15 @@ void GbPpu::Write(uint16_t addr, uint8_t value) _dmaController->ProcessHdma(); } else { _isFirstFrame = true; - _state.Cycle = 4; + _state.Cycle = -1; _state.Scanline = 0; + _state.Ly = 0; + _state.LyForCompare = 0; + _state.IdleCycles = 0; ResetRenderer(); - ChangeMode(PpuMode::HBlank); - UpdateLyCoincidenceFlag(); + _state.Mode = PpuMode::HBlank; + _state.IrqMode = PpuMode::HBlank; + _state.LyCoincidenceFlag = _state.LyCompare == _state.LyForCompare; UpdateStatIrq(); _console->ProcessEvent(EventType::StartFrame); @@ -619,7 +723,7 @@ void GbPpu::Write(uint16_t addr, uint8_t value) case 0xFF42: _state.ScrollY = value; break; case 0xFF43: _state.ScrollX = value; break; - case 0xFF45: _state.LyCompare = value; break; + case 0xFF45: _state.LyCompare = value; _state.IdleCycles = 0; break; case 0xFF47: _state.BgPalette = value; break; case 0xFF48: _state.ObjPalette0 = value; break; case 0xFF49: _state.ObjPalette1 = value; break; @@ -634,7 +738,7 @@ void GbPpu::Write(uint16_t addr, uint8_t value) uint8_t GbPpu::ReadVram(uint16_t addr) { - if((int)_state.Mode <= (int)PpuMode::OamEvaluation) { + if(_state.Mode <= PpuMode::VBlank || (_state.Mode == PpuMode::OamEvaluation && _state.Cycle < 80)) { return _vram[(_state.CgbVramBank << 13) | (addr & 0x1FFF)]; } else { return 0xFF; @@ -643,7 +747,7 @@ uint8_t GbPpu::ReadVram(uint16_t addr) void GbPpu::WriteVram(uint16_t addr, uint8_t value) { - if((int)_state.Mode <= (int)PpuMode::OamEvaluation) { + if(_state.Mode <= PpuMode::OamEvaluation) { _vram[(_state.CgbVramBank << 13) | (addr & 0x1FFF)] = value; } } @@ -651,10 +755,22 @@ void GbPpu::WriteVram(uint16_t addr, uint8_t value) uint8_t GbPpu::ReadOam(uint8_t addr) { if(addr < 0xA0) { - if((int)_state.Mode >= (int)PpuMode::OamEvaluation || _memoryManager->IsOamDmaRunning()) { + if(_memoryManager->IsOamDmaRunning()) { return 0xFF; + } + + if(_state.Scanline == 0 && _isFirstFrame) { + if(_state.Mode == PpuMode::HBlank) { + return _oam[addr]; + } else { + return 0xFF; + } } else { - return _oam[addr]; + if(_state.Mode == PpuMode::VBlank || (_state.Mode == PpuMode::HBlank && _state.Cycle != 3)) { + return _oam[addr]; + } else { + return 0xFF; + } } } return 0; @@ -664,10 +780,22 @@ void GbPpu::WriteOam(uint8_t addr, uint8_t value, bool forDma) { //During DMA or rendering/oam evaluation, ignore writes to OAM //The DMA controller is always allowed to write to OAM (presumably the PPU can't read OAM during that time? TODO implement) - //On the DMG, there is apparently a ~4 clock gap (80 to 84) between OAM evaluation & rendering where writing is allowed? + //On the DMG, there is a 4 clock gap (80 to 83) between OAM evaluation & rendering where writing is allowed if(addr < 0xA0) { - if(forDma || ((int)_state.Mode <= (int)PpuMode::VBlank && !_memoryManager->IsOamDmaRunning()) || (_state.Cycle >= 80 && _state.Cycle < 84)) { + if(forDma) { _oam[addr] = value; + } else if(_memoryManager->IsOamDmaRunning()) { + return; + } + + if(_state.Scanline == 0 && _isFirstFrame) { + if(_state.Mode == PpuMode::HBlank && _state.Cycle != 77 && _state.Cycle != 78) { + _oam[addr] = value; + } + } else { + if(_state.Mode <= PpuMode::VBlank || (_state.Cycle >= 80 && _state.Cycle < 84)) { + _oam[addr] = value; + } } } } @@ -744,7 +872,8 @@ void GbPpu::Serialize(Serializer& s) _state.Status, _state.FrameCount, _lastFrameTime, _state.LyCoincidenceFlag, _state.CgbBgPalAutoInc, _state.CgbBgPalPosition, _state.CgbObjPalAutoInc, _state.CgbObjPalPosition, _state.CgbVramBank, _state.CgbEnabled, - _latchWindowX, _latchWindowY, _latchWindowEnabled, _windowCounter, _isFirstFrame + _latchWindowX, _latchWindowY, _latchWindowEnabled, _windowCounter, _isFirstFrame, _rendererIdle, + _state.IdleCycles, _state.Ly, _state.LyForCompare, _state.IrqMode ); s.StreamArray(_state.CgbBgPalettes, 4 * 8); diff --git a/Core/GbPpu.h b/Core/GbPpu.h index fecf0f7..fdd362a 100644 --- a/Core/GbPpu.h +++ b/Core/GbPpu.h @@ -49,10 +49,14 @@ private: uint8_t _spriteIndexes[10] = {}; bool _isFirstFrame = true; + bool _rendererIdle = false; __forceinline void ProcessPpuCycle(); __forceinline void ExecCycle(); + __forceinline void ProcessVblankScanline(); + void ProcessFirstScanlineAfterPowerOn(); + __forceinline void ProcessVisibleScanline(); __forceinline void RunDrawCycle(); __forceinline void RunSpriteEvaluation(); void ResetRenderer(); @@ -62,9 +66,7 @@ private: __forceinline void PushSpriteToPixelFifo(); __forceinline void PushTileToPixelFifo(); - __forceinline void ChangeMode(PpuMode mode); - __forceinline void UpdateLyCoincidenceFlag(); - __forceinline void UpdateStatIrq(); + void UpdateStatIrq(); void WriteCgbPalette(uint8_t& pos, uint16_t* pal, bool autoInc, uint8_t value); diff --git a/Core/GbTimer.cpp b/Core/GbTimer.cpp index ab66621..95b8086 100644 --- a/Core/GbTimer.cpp +++ b/Core/GbTimer.cpp @@ -23,11 +23,13 @@ GbTimer::~GbTimer() void GbTimer::Exec() { - _state.Reloaded = false; - if(_state.NeedReload) { - ReloadCounter(); + if((_state.Divider & 0x03) == 2) { + _state.Reloaded = false; + if(_state.NeedReload) { + ReloadCounter(); + } } - SetDivider(_state.Divider + 4); + SetDivider(_state.Divider + 2); } void GbTimer::ReloadCounter() diff --git a/Core/GbTimer.h b/Core/GbTimer.h index 9a8a8f4..0a23b49 100644 --- a/Core/GbTimer.h +++ b/Core/GbTimer.h @@ -21,8 +21,6 @@ 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 7fadbd0..30191d5 100644 --- a/Core/GbTypes.h +++ b/Core/GbTypes.h @@ -87,7 +87,8 @@ enum class PpuMode HBlank, VBlank, OamEvaluation, - Drawing + Drawing, + NoIrq, }; namespace GbPpuStatusFlags @@ -152,8 +153,13 @@ struct GbPpuState { uint8_t Scanline; uint16_t Cycle; + uint16_t IdleCycles; PpuMode Mode; + PpuMode IrqMode; bool StatIrqFlag; + + uint8_t Ly; + int16_t LyForCompare; uint8_t LyCompare; bool LyCoincidenceFlag; diff --git a/UI/Debugger/PpuViewer/frmRegisterViewer.cs b/UI/Debugger/PpuViewer/frmRegisterViewer.cs index 94e1317..f035bcc 100644 --- a/UI/Debugger/PpuViewer/frmRegisterViewer.cs +++ b/UI/Debugger/PpuViewer/frmRegisterViewer.cs @@ -187,7 +187,7 @@ namespace Mesen.GUI.Debugger new RegEntry("", "LCD Registers", null), new RegEntry("$FF42", "Scroll Y (SCY)", ppu.ScrollY, Format.X8), new RegEntry("$FF43", "Scroll X (SCX)", ppu.ScrollX, Format.X8), - new RegEntry("$FF44", "Y-Coordinate (LY)", (ppu.Scanline < 153 || ppu.Cycle < 4) ? ppu.Scanline : 0, Format.X8), + new RegEntry("$FF44", "Y-Coordinate (LY)", ppu.Ly, Format.X8), new RegEntry("$FF45", "LY Compare (LYC)", ppu.LyCompare, Format.X8), new RegEntry("$FF47", "BG Palette (BGP)", ppu.BgPalette, Format.X8), new RegEntry("$FF48", "OBJ Palette 0 (OBP0)", ppu.ObjPalette0, Format.X8), diff --git a/UI/Debugger/frmDebugger.cs b/UI/Debugger/frmDebugger.cs index 046eddb..65e05c2 100644 --- a/UI/Debugger/frmDebugger.cs +++ b/UI/Debugger/frmDebugger.cs @@ -298,8 +298,21 @@ namespace Mesen.GUI.Debugger mnuStepOver.Click += (s, e) => { DebugApi.Step(_cpuType, 1, StepType.StepOver); }; mnuStepOut.Click += (s, e) => { DebugApi.Step(_cpuType, 1, StepType.StepOut); }; mnuRunPpuCycle.Click += (s, e) => { DebugApi.Step(_cpuType, 1, StepType.PpuStep); }; - mnuRunScanline.Click += (s, e) => { DebugApi.Step(_cpuType, 341, StepType.PpuStep); }; - mnuRunOneFrame.Click += (s, e) => { DebugApi.Step(_cpuType, 341 * 262, StepType.PpuStep); }; //TODO ntsc/pal + mnuRunScanline.Click += (s, e) => { + if(_cpuType == CpuType.Gameboy) { + DebugApi.Step(_cpuType, 456, StepType.PpuStep); + } else { + DebugApi.Step(_cpuType, 341, StepType.PpuStep); + } + }; + mnuRunOneFrame.Click += (s, e) => { + if(_cpuType == CpuType.Gameboy) { + DebugApi.Step(_cpuType, 456*154, StepType.PpuStep); + } else { + //TODO ntsc/pal + DebugApi.Step(_cpuType, 341 * 262, StepType.PpuStep); + } + }; mnuContinue.Click += (s, e) => { DebugApi.ResumeExecution(); }; mnuBreak.Click += (s, e) => { DebugApi.Step(_cpuType, 1, StepType.Step); }; diff --git a/UI/Interop/DebugState.cs b/UI/Interop/DebugState.cs index 8e962bf..7139930 100644 --- a/UI/Interop/DebugState.cs +++ b/UI/Interop/DebugState.cs @@ -708,16 +708,22 @@ namespace Mesen.GUI HBlank, VBlank, OamEvaluation, - Drawing + Drawing, + NoIrq } public struct GbPpuState { public byte Scanline; public UInt16 Cycle; + public UInt16 IdleCycles; public PpuMode Mode; + public PpuMode IrqMode; [MarshalAs(UnmanagedType.I1)] public bool StatIrqFlag; + public byte Ly; + public Int16 LyForCompare; + public byte LyCompare; [MarshalAs(UnmanagedType.I1)] public bool LyCoincidenceFlag; public byte BgPalette;