Skip to content
This repository was archived by the owner on Sep 11, 2023. It is now read-only.

Commit c015e6d

Browse files
committed
Implement "Battletoads" 3-phase dot crawl
https://forums.nesdev.org/viewtopic.php?p=30625#p30625
1 parent a6c9741 commit c015e6d

File tree

8 files changed

+44
-12
lines changed

8 files changed

+44
-12
lines changed

Core/BaseVideoFilter.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,17 @@ bool BaseVideoFilter::IsOddFrame()
5252
return _isOddFrame;
5353
}
5454

55+
uint8_t BaseVideoFilter::GetFieldPhase()
56+
{
57+
return _fieldPhase;
58+
}
59+
5560
void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
5661
{
5762
_frameLock.Acquire();
5863
_overscan = _console->GetSettings()->GetOverscanDimensions();
59-
_isOddFrame = frameNumber % 2;
64+
_isOddFrame = frameNumber & 0x01;
65+
_fieldPhase = frameNumber % 3;
6066
UpdateBufferSize();
6167
OnBeforeApplyFilter();
6268
ApplyFilter(ppuOutputBuffer);

Core/BaseVideoFilter.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class BaseVideoFilter
1515
SimpleLock _frameLock;
1616
OverscanDimensions _overscan;
1717
bool _isOddFrame;
18+
// https://forums.nesdev.org/viewtopic.php?p=30625#p30625
19+
uint8_t _fieldPhase;
1820

1921
void UpdateBufferSize();
2022

@@ -23,13 +25,14 @@ class BaseVideoFilter
2325

2426
virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0;
2527
virtual void OnBeforeApplyFilter();
26-
bool IsOddFrame();
28+
uint8_t GetFieldPhase();
2729

2830
public:
2931
BaseVideoFilter(shared_ptr<Console> console);
3032
virtual ~BaseVideoFilter();
3133

3234
uint32_t* GetOutputBuffer();
35+
bool IsOddFrame();
3336
void SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber);
3437
void TakeScreenshot(string romName, VideoFilterType filterType);
3538
void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr, bool rawScreenshot = false);

Core/BisqwitNtscFilter.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr<Console> console, int resDivider
5050
} else {
5151
outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top);
5252
}
53-
54-
DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (IsOddFrame() ? 8 : 0) + 327360);
53+
DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (_console->IsDotBypassed() ? (GetFieldPhase() * 4) : (IsOddFrame() ? 0 : 8)) + 327360);
5554

5655
_workDone = true;
5756
}
@@ -71,7 +70,7 @@ void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
7170

7271
_workDone = false;
7372
_waitWork.Signal();
74-
DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (IsOddFrame() ? 8 : 0) + GetOverscan().Top*341*8);
73+
DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (_console->IsDotBypassed() ? (GetFieldPhase() * 4) : (IsOddFrame() ? 0 : 8)) + GetOverscan().Top * 341 * 8);
7574
while(!_workDone) {}
7675
}
7776

Core/Console.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,11 @@ uint32_t Console::GetFrameCount()
568568
return _ppu ? _ppu->GetFrameCount() : 0;
569569
}
570570

571+
bool Console::IsDotBypassed()
572+
{
573+
return _ppu ? _ppu->IsDotBypassed() : false;
574+
}
575+
571576
NesModel Console::GetModel()
572577
{
573578
return _model;

Core/Console.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ class Console : public std::enable_shared_from_this<Console>
204204
VirtualFile GetPatchFile();
205205
RomInfo GetRomInfo();
206206
uint32_t GetFrameCount();
207+
// https://forums.nesdev.org/viewtopic.php?p=30625#p30625
208+
bool IsDotBypassed();
207209
NesModel GetModel();
208210

209211
uint32_t GetLagCounter();

Core/NtscFilter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ void NtscFilter::OnBeforeApplyFilter()
132132

133133
void NtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
134134
{
135-
nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, IsOddFrame() ? 0 : 1, PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4);
135+
nes_ntsc_blit(&_ntscData, ppuOutputBuffer, _ntsc_border, PPU::ScreenWidth, (_console->IsDotBypassed() ? GetFieldPhase() : IsOddFrame()), PPU::ScreenWidth, 240, _ntscBuffer, NES_NTSC_OUT_WIDTH(PPU::ScreenWidth)*4);
136136
GenerateArgbFrame(_ntscBuffer);
137137
}
138138

Core/PPU.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ void PPU::Reset()
105105
memset(_oamDecayCycles, 0, sizeof(_oamDecayCycles));
106106
_enableOamDecay = _settings->CheckFlag(EmulationFlags::EnableOamDecay);
107107

108+
_isDotBypassed = false;
109+
108110
UpdateMinimumDrawCycles();
109111
}
110112

@@ -997,14 +999,21 @@ void PPU::ProcessScanline()
997999
LoadTileInfo();
9981000
}
9991001
} else if(_cycle == 337 || _cycle == 339) {
1000-
if(IsRenderingEnabled()) {
1001-
ReadVram(GetNameTableAddr());
1002-
1003-
if(_scanline == -1 && _cycle == 339 && (_frameCount & 0x01) && _nesModel == NesModel::NTSC && _settings->GetPpuModel() == PpuModel::Ppu2C02) {
1004-
//This behavior is NTSC-specific - PAL frames are always the same number of cycles
1005-
//"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340)
1002+
if(_scanline == -1 && _cycle == 339 && (_frameCount & 0x01) && _nesModel == NesModel::NTSC && _settings->GetPpuModel() == PpuModel::Ppu2C02) {
1003+
if (IsRenderingEnabled()) {
1004+
//This behavior is NTSC-specific - PAL frames are always the same number of cycles
1005+
//"With rendering enabled, each odd PPU frame is one PPU clock shorter than normal" (skip from 339 to 0, going over 340)
10061006
_cycle = 340;
1007+
_isDotBypassed = false;
10071008
}
1009+
else {
1010+
// "By keeping rendering disabled until after the time when the clock is skipped on odd frames,
1011+
// you can get a different color dot crawl pattern than normal."
1012+
_isDotBypassed = true;
1013+
}
1014+
}
1015+
if (IsRenderingEnabled()) {
1016+
ReadVram(GetNameTableAddr());
10081017
}
10091018
}
10101019
}

Core/PPU.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ class PPU : public IMemoryHandler, public Snapshotable
109109
bool _enableOamDecay;
110110
bool _corruptOamRow[32];
111111

112+
// https://forums.nesdev.org/viewtopic.php?p=30625#p30625
113+
bool _isDotBypassed;
114+
112115
void UpdateStatusFlag();
113116

114117
void SetControlRegister(uint8_t value);
@@ -217,6 +220,11 @@ class PPU : public IMemoryHandler, public Snapshotable
217220
return _frameCount;
218221
}
219222

223+
bool IsDotBypassed()
224+
{
225+
return _isDotBypassed;
226+
}
227+
220228
uint32_t GetFrameCycle()
221229
{
222230
return ((_scanline + 1) * 341) + _cycle;

0 commit comments

Comments
 (0)