diff --git a/Core/BaseVideoFilter.cpp b/Core/BaseVideoFilter.cpp index 8d6c75b09..95ab58812 100644 --- a/Core/BaseVideoFilter.cpp +++ b/Core/BaseVideoFilter.cpp @@ -56,7 +56,7 @@ void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber) { _frameLock.Acquire(); _overscan = _console->GetSettings()->GetOverscanDimensions(); - _isOddFrame = frameNumber % 2; + _isOddFrame = frameNumber & 0x01; UpdateBufferSize(); OnBeforeApplyFilter(); ApplyFilter(ppuOutputBuffer); diff --git a/Core/BaseVideoFilter.h b/Core/BaseVideoFilter.h index ed9ad630b..add010d91 100644 --- a/Core/BaseVideoFilter.h +++ b/Core/BaseVideoFilter.h @@ -23,13 +23,13 @@ class BaseVideoFilter virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0; virtual void OnBeforeApplyFilter(); - bool IsOddFrame(); 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..bd2baa037 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->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + 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->GetModel() == NesModel::NTSC ? _console->GetStartingPhase() : 0) * 4) + GetOverscan().Top * 341 * 8); while(!_workDone) {} } diff --git a/Core/Console.cpp b/Core/Console.cpp index 9b03835c5..ad609cc87 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -568,6 +568,11 @@ uint32_t Console::GetFrameCount() return _ppu ? _ppu->GetFrameCount() : 0; } +uint8_t Console::GetStartingPhase() +{ + return _ppu ? _ppu->GetStartingPhase() : 0; +} + NesModel Console::GetModel() { return _model; diff --git a/Core/Console.h b/Core/Console.h index 96fb7786c..d714e4f08 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 + uint8_t GetStartingPhase(); NesModel GetModel(); uint32_t GetLagCounter(); diff --git a/Core/NtscFilter.cpp b/Core/NtscFilter.cpp index 33cac76c6..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() @@ -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,22 @@ 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); + 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); } 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); diff --git a/Core/PPU.cpp b/Core/PPU.cpp index d15416485..fee1169b7 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); + _startingPhase = 0; + UpdateMinimumDrawCycles(); } @@ -112,30 +114,37 @@ 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; + // 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 = 241; - _vblankEnd = 310; - _standardNmiScanline = 241; - _standardVblankEnd = 310; + // (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 = 291; - _vblankEnd = 310; - _standardNmiScanline = 291; - _standardVblankEnd = 310; + // (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; break; } @@ -999,10 +1008,9 @@ void PPU::ProcessScanline() LoadTileInfo(); } } else if(_cycle == 337 || _cycle == 339) { - if(IsRenderingEnabled()) { + if (IsRenderingEnabled()) { ReadVram(GetNameTableAddr()); - - if(_scanline == -1 && _cycle == 339 && (_frameCount & 0x01) && _nesModel == NesModel::NTSC && _settings->GetPpuModel() == PpuModel::Ppu2C02) { + 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; @@ -1347,6 +1355,8 @@ void PPU::Exec() if(_needStateUpdate) { UpdateState(); } + + _startingPhase = _cycle % 3; } void PPU::UpdateState() diff --git a/Core/PPU.h b/Core/PPU.h index 755041865..9007d03f9 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 + uint8_t _startingPhase; + void UpdateStatusFlag(); void SetControlRegister(uint8_t value); @@ -217,6 +220,11 @@ class PPU : public IMemoryHandler, public Snapshotable return _frameCount; } + uint8_t GetStartingPhase() + { + return _startingPhase; + } + uint32_t GetFrameCycle() { return ((_scanline + 1) * 341) + _cycle;