From c015e6d02a7f1f652eb4045d2eccb80029cdee10 Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 26 Oct 2022 18:50:50 +0800 Subject: [PATCH 1/8] Implement "Battletoads" 3-phase dot crawl https://forums.nesdev.org/viewtopic.php?p=30625#p30625 --- Core/BaseVideoFilter.cpp | 8 +++++++- Core/BaseVideoFilter.h | 5 ++++- Core/BisqwitNtscFilter.cpp | 5 ++--- Core/Console.cpp | 5 +++++ Core/Console.h | 2 ++ Core/NtscFilter.cpp | 2 +- Core/PPU.cpp | 21 +++++++++++++++------ Core/PPU.h | 8 ++++++++ 8 files changed, 44 insertions(+), 12 deletions(-) diff --git a/Core/BaseVideoFilter.cpp b/Core/BaseVideoFilter.cpp index 8d6c75b09..4fb91aa76 100644 --- a/Core/BaseVideoFilter.cpp +++ b/Core/BaseVideoFilter.cpp @@ -52,11 +52,17 @@ bool BaseVideoFilter::IsOddFrame() return _isOddFrame; } +uint8_t BaseVideoFilter::GetFieldPhase() +{ + return _fieldPhase; +} + void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber) { _frameLock.Acquire(); _overscan = _console->GetSettings()->GetOverscanDimensions(); - _isOddFrame = frameNumber % 2; + _isOddFrame = frameNumber & 0x01; + _fieldPhase = frameNumber % 3; UpdateBufferSize(); OnBeforeApplyFilter(); ApplyFilter(ppuOutputBuffer); diff --git a/Core/BaseVideoFilter.h b/Core/BaseVideoFilter.h index ed9ad630b..a990bd1ba 100644 --- a/Core/BaseVideoFilter.h +++ b/Core/BaseVideoFilter.h @@ -15,6 +15,8 @@ class BaseVideoFilter SimpleLock _frameLock; OverscanDimensions _overscan; bool _isOddFrame; + // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 + uint8_t _fieldPhase; void UpdateBufferSize(); @@ -23,13 +25,14 @@ class BaseVideoFilter virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0; virtual void OnBeforeApplyFilter(); - bool IsOddFrame(); + uint8_t GetFieldPhase(); public: BaseVideoFilter(shared_ptr console); virtual ~BaseVideoFilter(); uint32_t* GetOutputBuffer(); + bool IsOddFrame(); void SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber); void TakeScreenshot(string romName, VideoFilterType filterType); void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr, bool rawScreenshot = false); diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index b77a6cfe1..83e5fce51 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -50,8 +50,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider } else { outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top); } - - DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (IsOddFrame() ? 8 : 0) + 327360); + DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (_console->IsDotBypassed() ? (GetFieldPhase() * 4) : (IsOddFrame() ? 0 : 8)) + 327360); _workDone = true; } @@ -71,7 +70,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _workDone = false; _waitWork.Signal(); - DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (IsOddFrame() ? 8 : 0) + GetOverscan().Top*341*8); + DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (_console->IsDotBypassed() ? (GetFieldPhase() * 4) : (IsOddFrame() ? 0 : 8)) + GetOverscan().Top * 341 * 8); while(!_workDone) {} } diff --git a/Core/Console.cpp b/Core/Console.cpp index 9b03835c5..993f74e97 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -568,6 +568,11 @@ uint32_t Console::GetFrameCount() return _ppu ? _ppu->GetFrameCount() : 0; } +bool Console::IsDotBypassed() +{ + return _ppu ? _ppu->IsDotBypassed() : false; +} + NesModel Console::GetModel() { return _model; diff --git a/Core/Console.h b/Core/Console.h index 96fb7786c..9eefa4aef 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -204,6 +204,8 @@ class Console : public std::enable_shared_from_this VirtualFile GetPatchFile(); RomInfo GetRomInfo(); uint32_t GetFrameCount(); + // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 + bool IsDotBypassed(); NesModel GetModel(); uint32_t GetLagCounter(); diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index 33cac76c6..4ce963b3f 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -132,7 +132,7 @@ void NtscFilter::OnBeforeApplyFilter() void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, IsOddFrame() ? 0 : 1, PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); + nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, (_console->IsDotBypassed() ? GetFieldPhase() : IsOddFrame()), PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); GenerateArgbFrame(_ntscBuffer); } diff --git a/Core/PPU.cpp b/Core/PPU.cpp index c3c86db03..c2f580124 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -105,6 +105,8 @@ void PPU::Reset() memset(_oamDecayCycles, 0, sizeof(_oamDecayCycles)); _enableOamDecay = _settings->CheckFlag(EmulationFlags::EnableOamDecay); + _isDotBypassed = false; + UpdateMinimumDrawCycles(); } @@ -997,14 +999,21 @@ void PPU::ProcessScanline() LoadTileInfo(); } } else if(_cycle == 337 || _cycle == 339) { - if(IsRenderingEnabled()) { - ReadVram(GetNameTableAddr()); - - if(_scanline == -1 && _cycle == 339 && (_frameCount & 0x01) && _nesModel == NesModel::NTSC && _settings->GetPpuModel() == PpuModel::Ppu2C02) { - //This behavior is NTSC-specific - PAL frames are always the same number of cycles - //"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340) + if(_scanline == -1 && _cycle == 339 && (_frameCount & 0x01) && _nesModel == NesModel::NTSC && _settings->GetPpuModel() == PpuModel::Ppu2C02) { + if (IsRenderingEnabled()) { + //This behavior is NTSC-specific - PAL frames are always the same number of cycles + //"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340) _cycle = 340; + _isDotBypassed = false; } + else { + // "By keeping rendering disabled until after the time when the clock is skipped on odd frames, + // you can get a different color dot crawl pattern than normal." + _isDotBypassed = true; + } + } + if (IsRenderingEnabled()) { + ReadVram(GetNameTableAddr()); } } } diff --git a/Core/PPU.h b/Core/PPU.h index 755041865..46c4570e4 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -109,6 +109,9 @@ class PPU : public IMemoryHandler, public Snapshotable bool _enableOamDecay; bool _corruptOamRow[32]; + // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 + bool _isDotBypassed; + void UpdateStatusFlag(); void SetControlRegister(uint8_t value); @@ -217,6 +220,11 @@ class PPU : public IMemoryHandler, public Snapshotable return _frameCount; } + bool IsDotBypassed() + { + return _isDotBypassed; + } + uint32_t GetFrameCycle() { return ((_scanline + 1) * 341) + _cycle; From b9268282ae7310bee97dac5379a6e3bb37a4270a Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 26 Oct 2022 19:07:29 +0800 Subject: [PATCH 2/8] Correct blargg_ntsc starting phase --- Core/NtscFilter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index 4ce963b3f..5056ae553 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -132,7 +132,7 @@ void NtscFilter::OnBeforeApplyFilter() void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, (_console->IsDotBypassed() ? GetFieldPhase() : IsOddFrame()), PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); + nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, (_console->IsDotBypassed() ? GetFieldPhase() : (IsOddFrame() ? 0 : 2)), PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); GenerateArgbFrame(_ntscBuffer); } From 1a21018d5eef44f786229f75bf93de453570531c Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 26 Oct 2022 20:04:38 +0800 Subject: [PATCH 3/8] Refactor starting phase --- Core/BaseVideoFilter.cpp | 6 ------ Core/BaseVideoFilter.h | 3 --- Core/BisqwitNtscFilter.cpp | 4 ++-- Core/Console.cpp | 4 ++-- Core/Console.h | 2 +- Core/NtscFilter.cpp | 2 +- Core/PPU.cpp | 22 ++++++++-------------- Core/PPU.h | 6 +++--- 8 files changed, 17 insertions(+), 32 deletions(-) diff --git a/Core/BaseVideoFilter.cpp b/Core/BaseVideoFilter.cpp index 4fb91aa76..95ab58812 100644 --- a/Core/BaseVideoFilter.cpp +++ b/Core/BaseVideoFilter.cpp @@ -52,17 +52,11 @@ bool BaseVideoFilter::IsOddFrame() return _isOddFrame; } -uint8_t BaseVideoFilter::GetFieldPhase() -{ - return _fieldPhase; -} - void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber) { _frameLock.Acquire(); _overscan = _console->GetSettings()->GetOverscanDimensions(); _isOddFrame = frameNumber & 0x01; - _fieldPhase = frameNumber % 3; UpdateBufferSize(); OnBeforeApplyFilter(); ApplyFilter(ppuOutputBuffer); diff --git a/Core/BaseVideoFilter.h b/Core/BaseVideoFilter.h index a990bd1ba..add010d91 100644 --- a/Core/BaseVideoFilter.h +++ b/Core/BaseVideoFilter.h @@ -15,8 +15,6 @@ class BaseVideoFilter SimpleLock _frameLock; OverscanDimensions _overscan; bool _isOddFrame; - // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 - uint8_t _fieldPhase; void UpdateBufferSize(); @@ -25,7 +23,6 @@ class BaseVideoFilter virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0; virtual void OnBeforeApplyFilter(); - uint8_t GetFieldPhase(); public: BaseVideoFilter(shared_ptr console); diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index 83e5fce51..4723ec2a6 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -50,7 +50,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider } else { outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top); } - DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (_console->IsDotBypassed() ? (GetFieldPhase() * 4) : (IsOddFrame() ? 0 : 8)) + 327360); + DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (_console->GetStartingPhase() * 4) + 327360); _workDone = true; } @@ -70,7 +70,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _workDone = false; _waitWork.Signal(); - DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (_console->IsDotBypassed() ? (GetFieldPhase() * 4) : (IsOddFrame() ? 0 : 8)) + GetOverscan().Top * 341 * 8); + DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (_console->GetStartingPhase() * 4) + GetOverscan().Top * 341 * 8); while(!_workDone) {} } diff --git a/Core/Console.cpp b/Core/Console.cpp index 993f74e97..ad609cc87 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -568,9 +568,9 @@ uint32_t Console::GetFrameCount() return _ppu ? _ppu->GetFrameCount() : 0; } -bool Console::IsDotBypassed() +uint8_t Console::GetStartingPhase() { - return _ppu ? _ppu->IsDotBypassed() : false; + return _ppu ? _ppu->GetStartingPhase() : 0; } NesModel Console::GetModel() diff --git a/Core/Console.h b/Core/Console.h index 9eefa4aef..d714e4f08 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -205,7 +205,7 @@ class Console : public std::enable_shared_from_this RomInfo GetRomInfo(); uint32_t GetFrameCount(); // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 - bool IsDotBypassed(); + uint8_t GetStartingPhase(); NesModel GetModel(); uint32_t GetLagCounter(); diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index 5056ae553..09a1e223f 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -132,7 +132,7 @@ void NtscFilter::OnBeforeApplyFilter() void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, (_console->IsDotBypassed() ? GetFieldPhase() : (IsOddFrame() ? 0 : 2)), PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); + nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, _console->GetStartingPhase(), PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); GenerateArgbFrame(_ntscBuffer); } diff --git a/Core/PPU.cpp b/Core/PPU.cpp index c2f580124..74a480ba0 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -105,7 +105,7 @@ void PPU::Reset() memset(_oamDecayCycles, 0, sizeof(_oamDecayCycles)); _enableOamDecay = _settings->CheckFlag(EmulationFlags::EnableOamDecay); - _isDotBypassed = false; + _startingPhase = 0; UpdateMinimumDrawCycles(); } @@ -999,21 +999,13 @@ void PPU::ProcessScanline() LoadTileInfo(); } } else if(_cycle == 337 || _cycle == 339) { - if(_scanline == -1 && _cycle == 339 && (_frameCount & 0x01) && _nesModel == NesModel::NTSC && _settings->GetPpuModel() == PpuModel::Ppu2C02) { - if (IsRenderingEnabled()) { - //This behavior is NTSC-specific - PAL frames are always the same number of cycles - //"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340) - _cycle = 340; - _isDotBypassed = false; - } - else { - // "By keeping rendering disabled until after the time when the clock is skipped on odd frames, - // you can get a different color dot crawl pattern than normal." - _isDotBypassed = true; - } - } if (IsRenderingEnabled()) { ReadVram(GetNameTableAddr()); + if (_scanline == -1 && _cycle == 339 && (_frameCount & 0x01) && _nesModel == NesModel::NTSC && _settings->GetPpuModel() == PpuModel::Ppu2C02) { + //This behavior is NTSC-specific - PAL frames are always the same number of cycles + //"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340) + _cycle = 340; + } } } } @@ -1351,6 +1343,8 @@ void PPU::Exec() } } + _startingPhase = _cycle % 3; + if(_needStateUpdate) { UpdateState(); } diff --git a/Core/PPU.h b/Core/PPU.h index 46c4570e4..9007d03f9 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -110,7 +110,7 @@ class PPU : public IMemoryHandler, public Snapshotable bool _corruptOamRow[32]; // https://forums.nesdev.org/viewtopic.php?p=30625#p30625 - bool _isDotBypassed; + uint8_t _startingPhase; void UpdateStatusFlag(); @@ -220,9 +220,9 @@ class PPU : public IMemoryHandler, public Snapshotable return _frameCount; } - bool IsDotBypassed() + uint8_t GetStartingPhase() { - return _isDotBypassed; + return _startingPhase; } uint32_t GetFrameCycle() From cfa8f94589e1b2540cdddf74b3e5ab7cf0f72410 Mon Sep 17 00:00:00 2001 From: Persune Date: Fri, 28 Oct 2022 16:05:48 +0800 Subject: [PATCH 4/8] Refactor border color --- Core/NtscFilter.cpp | 18 +++++++++--------- Core/NtscFilter.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index 09a1e223f..308097010 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -55,14 +55,7 @@ void NtscFilter::OnBeforeApplyFilter() } } - if (_console->GetModel() == NesModel::NTSC) { - // BG color borders on NTSC machines - _ntsc_border = _console->GetPpu()->GetCurrentBgColor(); - } - else { - // black borders on other machines - _ntsc_border = 15; - } + _ntscBorder = (_console->GetModel() == NesModel::NTSC); PictureSettings pictureSettings = _console->GetSettings()->GetPictureSettings(); NtscFilterSettings ntscSettings = _console->GetSettings()->GetNtscFilterSettings(); @@ -132,7 +125,14 @@ void NtscFilter::OnBeforeApplyFilter() void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, _console->GetStartingPhase(), PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); + nes_ntsc_blit(&_ntscData, + ppuOutputBuffer, + _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, + PPU::ScreenWidth, + _console->GetStartingPhase(), + PPU::ScreenWidth, + 240, _ntscBuffer, + NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); GenerateArgbFrame(_ntscBuffer); } diff --git a/Core/NtscFilter.h b/Core/NtscFilter.h index b304511f1..071448633 100644 --- a/Core/NtscFilter.h +++ b/Core/NtscFilter.h @@ -14,7 +14,7 @@ class NtscFilter : public BaseVideoFilter bool _useExternalPalette = true; uint8_t _palette[512 * 3]; uint32_t* _ntscBuffer; - uint16_t _ntsc_border; + bool _ntscBorder = true; void GenerateArgbFrame(uint32_t *outputBuffer); From d789f728f2a57d46b2096f0e2b041bddff3a5b8f Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 2 Nov 2022 22:03:31 +0800 Subject: [PATCH 5/8] Clarify PPU scanline timings --- Core/PPU.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 74a480ba0..716a7dac0 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -114,30 +114,31 @@ void PPU::SetNesModel(NesModel model) { _nesModel = model; + // https://www.nesdev.org/wiki/Cycle_reference_chart switch(_nesModel) { case NesModel::Auto: //Should never be Auto break; case NesModel::NTSC: - _nmiScanline = 241; - _vblankEnd = 260; - _standardNmiScanline = 241; - _standardVblankEnd = 260; + _nmiScanline = 240 + 1; // picture height + postrender blanking lines + _vblankEnd = 240 + 20; // picture height + (postrender blanking lines - 1) + vblank length + _standardNmiScanline = _nmiScanline; + _standardVblankEnd = _vblankEnd; _masterClockDivider = 4; break; case NesModel::PAL: - _nmiScanline = 241; - _vblankEnd = 310; - _standardNmiScanline = 241; - _standardVblankEnd = 310; + _nmiScanline = 239 + 1; // picture height + postrender blanking lines + _vblankEnd = 239 + 70; // picture height + (postrender blanking lines - 1) + vblank length + _standardNmiScanline = _nmiScanline; + _standardVblankEnd = _vblankEnd; _masterClockDivider = 5; break; case NesModel::Dendy: - _nmiScanline = 291; - _vblankEnd = 310; - _standardNmiScanline = 291; - _standardVblankEnd = 310; + _nmiScanline = 239 + 51; // picture height + postrender blanking lines + _vblankEnd = 239 + 50 + 20; // picture height + (postrender blanking lines - 1) + vblank length + _standardNmiScanline = _nmiScanline; + _standardVblankEnd = _vblankEnd; _masterClockDivider = 5; break; } From b62a261af9fe63f45b70e734fba521247bbd2c0b Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 2 Nov 2022 22:44:36 +0800 Subject: [PATCH 6/8] Correct PAL and Dendy scanline timings --- Core/PPU.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 716a7dac0..c4187ea20 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -121,22 +121,28 @@ void PPU::SetNesModel(NesModel model) break; case NesModel::NTSC: - _nmiScanline = 240 + 1; // picture height + postrender blanking lines - _vblankEnd = 240 + 20; // picture height + (postrender blanking lines - 1) + vblank length + // picture height + postrender blanking lines + _nmiScanline = 240 + 1; + // picture height + (postrender blanking lines - 1) + vblank length + _vblankEnd = 240 + 20; _standardNmiScanline = _nmiScanline; _standardVblankEnd = _vblankEnd; _masterClockDivider = 4; break; case NesModel::PAL: - _nmiScanline = 239 + 1; // picture height + postrender blanking lines - _vblankEnd = 239 + 70; // picture height + (postrender blanking lines - 1) + vblank length + // (picture height + border line) + postrender blanking lines + _nmiScanline = 240 + 1; + // (picture height + border line) + (postrender blanking lines - 1) + vblank length + 50 + _vblankEnd = 240 + 20 + 50; _standardNmiScanline = _nmiScanline; _standardVblankEnd = _vblankEnd; _masterClockDivider = 5; break; case NesModel::Dendy: - _nmiScanline = 239 + 51; // picture height + postrender blanking lines - _vblankEnd = 239 + 50 + 20; // picture height + (postrender blanking lines - 1) + vblank length + // (picture height + border line) + postrender blanking lines + 50 + _nmiScanline = 240 + 1 + 50; + // (picture height + border line) + (postrender blanking lines - 1) + 50 + vblank length + _vblankEnd = 240 + 50 + 20; _standardNmiScanline = _nmiScanline; _standardVblankEnd = _vblankEnd; _masterClockDivider = 5; @@ -1344,11 +1350,11 @@ void PPU::Exec() } } - _startingPhase = _cycle % 3; - if(_needStateUpdate) { UpdateState(); } + + _startingPhase = _cycle % 3; } void PPU::UpdateState() From 2536e6c48357dd4ecf1a761fdb7458e5733e6c1d Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 2 Nov 2022 22:49:34 +0800 Subject: [PATCH 7/8] Field phase is constant on PAL and Dendy --- Core/BisqwitNtscFilter.cpp | 4 ++-- Core/NtscFilter.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/BisqwitNtscFilter.cpp b/Core/BisqwitNtscFilter.cpp index 4723ec2a6..bd2baa037 100644 --- a/Core/BisqwitNtscFilter.cpp +++ b/Core/BisqwitNtscFilter.cpp @@ -50,7 +50,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr console, int resDivider } else { outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top); } - DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (_console->GetStartingPhase() * 4) + 327360); + DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, ((_console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + 327360); _workDone = true; } @@ -70,7 +70,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) _workDone = false; _waitWork.Signal(); - DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (_console->GetStartingPhase() * 4) + GetOverscan().Top * 341 * 8); + DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), ((_console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + GetOverscan().Top * 341 * 8); while(!_workDone) {} } diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index 308097010..ba5305417 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -129,7 +129,7 @@ void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) ppuOutputBuffer, _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, PPU::ScreenWidth, - _console->GetStartingPhase(), + _console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0, PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); From f6c8e14bd415167fe407606aa63fd83fb56f2b35 Mon Sep 17 00:00:00 2001 From: Persune Date: Wed, 2 Nov 2022 23:53:58 +0800 Subject: [PATCH 8/8] Fetch BG color on each scanline instead --- Core/NtscFilter.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index ba5305417..9fe0a1830 100644 --- a/Core/NtscFilter.cpp +++ b/Core/NtscFilter.cpp @@ -9,7 +9,7 @@ NtscFilter::NtscFilter(shared_ptr console) : BaseVideoFilter(console) memset(_palette, 0, sizeof(_palette)); memset(&_ntscData, 0, sizeof(_ntscData)); _ntscSetup = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - _ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240]; + _ntscBuffer = new uint32_t[NES_NTSC_OUT_WIDTH(256) * 240](); } FrameInfo NtscFilter::GetFrameInfo() @@ -125,14 +125,22 @@ void NtscFilter::OnBeforeApplyFilter() void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer) { - nes_ntsc_blit(&_ntscData, - ppuOutputBuffer, - _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, - PPU::ScreenWidth, - _console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0, - PPU::ScreenWidth, - 240, _ntscBuffer, - NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4); + uint8_t phase = _console->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0; + for (int i = 0; i < 240; i++) { + nes_ntsc_blit(&_ntscData, + // input += in_row_width; + ppuOutputBuffer + PPU::ScreenWidth * i, + _ntscBorder ? _console->GetPpu()->GetCurrentBgColor() : 0x0F, + PPU::ScreenWidth, + phase, + PPU::ScreenWidth, + 1, + // rgb_out = (char*) rgb_out + out_pitch; + reinterpret_cast(_ntscBuffer) + (NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4 * i), + NES_NTSC_OUT_WIDTH(PPU::ScreenWidth) * 4); + + phase = (phase + 1) % 3; + } GenerateArgbFrame(_ntscBuffer); }