Added support for overclocking (by adding extra scanlines before/after NMI)

This commit is contained in:
Sour 2019-07-19 19:39:38 -04:00
parent 46a02d4a9a
commit c880c55d53
12 changed files with 405 additions and 82 deletions

View File

@ -78,7 +78,17 @@ uint32_t EmuSettings::GetInputConfigVersion()
void EmuSettings::SetEmulationConfig(EmulationConfig config)
{
bool prevOverclockEnabled = _emulation.PpuExtraScanlinesAfterNmi > 0 || _emulation.PpuExtraScanlinesBeforeNmi > 0;
bool overclockEnabled = config.PpuExtraScanlinesAfterNmi > 0 || config.PpuExtraScanlinesBeforeNmi > 0;
_emulation = config;
if(prevOverclockEnabled != overclockEnabled) {
if(overclockEnabled) {
MessageManager::DisplayMessage("Overclock", "OverclockEnabled");
} else {
MessageManager::DisplayMessage("Overclock", "OverclockDisabled");
}
}
}
EmulationConfig EmuSettings::GetEmulationConfig()

View File

@ -142,7 +142,7 @@ void EventManager::DrawEvent(DebugEventInfo &evt, bool drawBackground, uint32_t
int iMax = drawBackground ? 3 : 1;
int jMin = drawBackground ? -2 : 0;
int jMax = drawBackground ? 3 : 1;
uint32_t y = evt.Scanline * 2;
uint32_t y = std::min(evt.Scanline * 2, 262 * 2);
uint32_t x = evt.Cycle * 2;
for(int i = iMin; i <= iMax; i++) {
@ -171,7 +171,7 @@ void EventManager::TakeEventSnapshot(EventViewerDisplayOptions options)
memcpy(_ppuBuffer, _ppu->GetScreenBuffer(), (_useHighResOutput ? (512 * 478) : (256*239)) * sizeof(uint16_t));
_snapshot = _debugEvents;
_snapshotScanline = scanline;
_snapshotScanline = _ppu->GetRealScanline();
if(options.ShowPreviousFrameEvents && scanline != 0) {
for(DebugEventInfo &evt : _prevDebugEvents) {
uint32_t evtKey = (evt.Scanline << 9) + evt.Cycle;

View File

@ -73,8 +73,8 @@ void InternalRegisters::ProcessIrqCounters()
bool irqLevel = (
(_enableHorizontalIrq || _enableVerticalIrq) &&
(!_enableHorizontalIrq || (_horizontalTimer <= 339 && (_ppu->GetCycle() == _horizontalTimer) && (_ppu->GetLastScanline() != _ppu->GetScanline() || _horizontalTimer < 339))) &&
(!_enableVerticalIrq || _ppu->GetScanline() == _verticalTimer)
(!_enableHorizontalIrq || (_horizontalTimer <= 339 && (_ppu->GetCycle() == _horizontalTimer) && (_ppu->GetLastScanline() != _ppu->GetRealScanline() || _horizontalTimer < 339))) &&
(!_enableVerticalIrq || _ppu->GetRealScanline() == _verticalTimer)
);
if(!_irqLevel && irqLevel) {
@ -114,12 +114,12 @@ uint8_t InternalRegisters::Read(uint16_t addr)
case 0x4212: {
uint16_t hClock = _memoryManager->GetHClock();
uint16_t scanline = _ppu->GetScanline();
uint16_t vblankStart = _ppu->GetVblankStart();
uint16_t nmiScanline = _ppu->GetNmiScanline();
//TODO TIMING (set/clear timing)
return (
(scanline >= vblankStart ? 0x80 : 0) |
(scanline >= nmiScanline ? 0x80 : 0) |
((hClock >= 1*4 && hClock <= 274*4) ? 0 : 0x40) |
((_enableAutoJoypadRead && scanline >= vblankStart && scanline <= vblankStart + 2) ? 0x01 : 0) | //Auto joypad read in progress
((_enableAutoJoypadRead && scanline >= nmiScanline && scanline <= nmiScanline + 2) ? 0x01 : 0) | //Auto joypad read in progress
(_memoryManager->GetOpenBus() & 0x3E)
);
}
@ -153,7 +153,7 @@ void InternalRegisters::Write(uint16_t addr, uint8_t value)
{
switch(addr) {
case 0x4200:
if((value & 0x30) == 0x20 && !_enableVerticalIrq && _ppu->GetScanline() == _verticalTimer) {
if((value & 0x30) == 0x20 && !_enableVerticalIrq && _ppu->GetRealScanline() == _verticalTimer) {
//When enabling vertical irqs, if the current scanline matches the target scanline, set the irq flag right away
_console->GetCpu()->SetIrqSource(IrqSource::Ppu);
}

View File

@ -13,6 +13,7 @@ std::unordered_map<string, string> MessageManager::_enResources = {
{ "Patch", u8"Patch" },
{ "Movies", u8"Movies" },
{ "NetPlay", u8"Net Play" },
{ "Overclock", u8"Overclock" },
{ "Region", u8"Region" },
{ "SaveStates", u8"Save States" },
{ "ScreenshotSaved", u8"Screenshot Saved" },
@ -51,6 +52,8 @@ std::unordered_map<string, string> MessageManager::_enResources = {
{ "MovieRecordingTo", u8"Recording to: %1" },
{ "MovieSaved", u8"Movie saved to file: %1" },
{ "NetplayVersionMismatch", u8"%1 is not running the same version of Mesen and has been disconnected." },
{ "OverclockEnabled", u8"Overclocking enabled." },
{ "OverclockDisabled", u8"Overclocking disabled." },
{ "PrgSizeWarning", u8"PRG size is smaller than 32kb" },
{ "SaveStateEmpty", u8"Slot is empty." },
{ "SaveStateIncompatibleVersion", u8"Save state is incompatible with this version of Mesen." },
@ -74,7 +77,7 @@ std::unordered_map<string, string> MessageManager::_enResources = {
std::list<string> MessageManager::_log;
SimpleLock MessageManager::_logLock;
SimpleLock MessageManager::_messageLock;
bool MessageManager::_osdEnabled = false;
bool MessageManager::_osdEnabled = true;
IMessageManager* MessageManager::_messageManager = nullptr;
void MessageManager::RegisterMessageManager(IMessageManager* messageManager)

View File

@ -51,10 +51,10 @@ void Ppu::PowerOn()
{
_skipRender = false;
_regs = _console->GetInternalRegisters().get();
_settings = _console->GetSettings().get();
_spc = _console->GetSpc().get();
_memoryManager = _console->GetMemoryManager().get();
_vblankStart = _overscanMode ? 240 : 225;
_currentBuffer = _outputBuffers[0];
_layerConfig[0] = {};
@ -64,9 +64,9 @@ void Ppu::PowerOn()
_cgramAddress = 0;
_console->GetSettings()->InitializeRam(_vram, Ppu::VideoRamSize);
_console->GetSettings()->InitializeRam(_cgram, Ppu::CgRamSize);
_console->GetSettings()->InitializeRam(_oamRam, Ppu::SpriteRamSize);
_settings->InitializeRam(_vram, Ppu::VideoRamSize);
_settings->InitializeRam(_cgram, Ppu::CgRamSize);
_settings->InitializeRam(_oamRam, Ppu::SpriteRamSize);
memset(_spriteIndexes, 0xFF, sizeof(_spriteIndexes));
@ -74,6 +74,8 @@ void Ppu::PowerOn()
_vramIncrementValue = 1;
_vramAddressRemapping = 0;
_vramAddrIncrementOnSecondReg = false;
UpdateNmiScanline();
}
void Ppu::Reset()
@ -100,9 +102,14 @@ uint16_t Ppu::GetCycle()
return (hClock - ((hClock > 1292) << 1) - ((hClock > 1310) << 1)) >> 2;
}
uint16_t Ppu::GetNmiScanline()
{
return _nmiScanline;
}
uint16_t Ppu::GetVblankStart()
{
return _vblankStart;
return _vblankStartScanline;
}
PpuState Ppu::GetState()
@ -389,70 +396,71 @@ bool Ppu::ProcessEndOfScanline(uint16_t hClock)
{
if(hClock >= 1364 || (hClock == 1360 && _scanline == 240 && _oddFrame && !_screenInterlace)) {
//"In non-interlace mode scanline 240 of every other frame (those with $213f.7=1) is only 1360 cycles."
if(_scanline < _vblankStart) {
if(_scanline < _vblankStartScanline) {
RenderScanline();
if(_mosaicScanlineCounter) {
_mosaicScanlineCounter--;
if(_mosaicEnabled && !_mosaicScanlineCounter) {
_mosaicScanlineCounter = _mosaicSize;
}
}
_drawStartX = 0;
_drawEndX = 0;
_fetchBgStart = 0;
_fetchBgEnd = 0;
_fetchSpriteStart = 0;
_fetchSpriteEnd = 0;
_spriteEvalStart = 0;
_spriteEvalEnd = 0;
_spriteFetchingDone = false;
for(int i = 0; i < 4; i++) {
_layerData[i].HasPriorityTiles = false;
}
memset(_hasSpritePriority, 0, sizeof(_hasSpritePriority));
memcpy(_spritePriority, _spritePriorityCopy, sizeof(_spritePriority));
for(int i = 0; i < 255; i++) {
if(_spritePriority[i] < 4) {
_hasSpritePriority[_spritePriority[i]] = true;
}
}
memcpy(_spritePalette, _spritePaletteCopy, sizeof(_spritePalette));
memcpy(_spriteColors, _spriteColorsCopy, sizeof(_spriteColors));
memset(_spriteIndexes, 0xFF, sizeof(_spriteIndexes));
_pixelsDrawn = 0;
_subPixelsDrawn = 0;
memset(_rowPixelFlags, 0, sizeof(_rowPixelFlags));
memset(_subScreenFilled, 0, sizeof(_subScreenFilled));
}
_scanline++;
if(_mosaicScanlineCounter) {
_mosaicScanlineCounter--;
if(_mosaicEnabled && !_mosaicScanlineCounter) {
_mosaicScanlineCounter = _mosaicSize;
}
}
_drawStartX = 0;
_drawEndX = 0;
_fetchBgStart = 0;
_fetchBgEnd = 0;
_fetchSpriteStart = 0;
_fetchSpriteEnd = 0;
_spriteEvalStart = 0;
_spriteEvalEnd = 0;
_spriteFetchingDone = false;
for(int i = 0; i < 4; i++) {
_layerData[i].HasPriorityTiles = false;
}
memset(_hasSpritePriority, 0, sizeof(_hasSpritePriority));
memcpy(_spritePriority, _spritePriorityCopy, sizeof(_spritePriority));
for(int i = 0; i < 255; i++) {
if(_spritePriority[i] < 4) {
_hasSpritePriority[_spritePriority[i]] = true;
}
}
memcpy(_spritePalette, _spritePaletteCopy, sizeof(_spritePalette));
memcpy(_spriteColors, _spriteColorsCopy, sizeof(_spriteColors));
memset(_spriteIndexes, 0xFF, sizeof(_spriteIndexes));
_pixelsDrawn = 0;
_subPixelsDrawn = 0;
memset(_rowPixelFlags, 0, sizeof(_rowPixelFlags));
memset(_subScreenFilled, 0, sizeof(_subScreenFilled));
if(_scanline == _vblankStart) {
if(_scanline == _nmiScanline) {
//Reset OAM address at the start of vblank?
if(!_forcedVblank) {
//TODO, the timing of this may be slightly off? should happen at H=10 based on anomie's docs
_internalOamAddress = (_oamRamAddress << 1);
}
VideoConfig cfg = _console->GetSettings()->GetVideoConfig();
VideoConfig cfg = _settings->GetVideoConfig();
_configVisibleLayers = (cfg.HideBgLayer0 ? 0 : 1) | (cfg.HideBgLayer1 ? 0 : 2) | (cfg.HideBgLayer2 ? 0 : 4) | (cfg.HideBgLayer3 ? 0 : 8) | (cfg.HideSprites ? 0 : 16);
_console->ProcessEvent(EventType::EndFrame);
_frameCount++;
_console->GetSpc()->ProcessEndFrame();
_spc->ProcessEndFrame();
_regs->SetNmiFlag(true);
SendFrame();
if(_regs->IsNmiEnabled()) {
_console->GetCpu()->SetNmiFlag();
}
} else if(_scanline >= GetLastScanline() + 1) {
} else if(_scanline >= _vblankEndScanline + 1) {
//"Frames are 262 scanlines in non-interlace mode, while in interlace mode frames with $213f.7=0 are 263 scanlines"
_oddFrame ^= 1;
_regs->SetNmiFlag(false);
@ -462,10 +470,10 @@ bool Ppu::ProcessEndOfScanline(uint16_t hClock)
_console->ProcessEvent(EventType::StartFrame);
_skipRender = (
!_console->GetSettings()->GetVideoConfig().DisableFrameSkipping &&
!_settings->GetVideoConfig().DisableFrameSkipping &&
!_console->GetRewindManager()->IsRewinding() &&
!_console->GetVideoRenderer()->IsRecording() &&
(_console->GetSettings()->GetEmulationSpeed() == 0 || _console->GetSettings()->GetEmulationSpeed() > 150) &&
(_settings->GetEmulationSpeed() == 0 || _settings->GetEmulationSpeed() > 150) &&
_frameSkipTimer.GetElapsedMS() < 10
);
if(!_skipRender) {
@ -474,27 +482,87 @@ bool Ppu::ProcessEndOfScanline(uint16_t hClock)
}
_mosaicScanlineCounter = _mosaicEnabled ? _mosaicSize + 1 : 0;
//Update overclock timings once per frame
UpdateNmiScanline();
//Ensure the SPC is re-enabled for the next frame
_spc->SetSpcState(true);
}
UpdateSpcState();
return true;
}
return false;
}
uint16_t Ppu::GetLastScanline()
void Ppu::UpdateSpcState()
{
//When using overclocking, turn off the SPC during the extra scanlines
if(_overclockEnabled && _scanline > _vblankStartScanline) {
EmulationConfig cfg = _settings->GetEmulationConfig();
if(_scanline > _adjustedVblankEndScanline) {
//Disable APU for extra lines after NMI
_spc->SetSpcState(false);
} else if(_scanline >= _vblankStartScanline && _scanline < _nmiScanline) {
//Disable APU for extra lines before NMI
_spc->SetSpcState(false);
} else {
_spc->SetSpcState(true);
}
}
}
void Ppu::UpdateNmiScanline()
{
EmulationConfig cfg = _settings->GetEmulationConfig();
if(_console->GetRegion() == ConsoleRegion::Ntsc) {
if(!_screenInterlace || _oddFrame) {
return 261;
_baseVblankEndScanline = 261;
} else {
return 262;
_baseVblankEndScanline = 262;
}
} else {
if(!_screenInterlace || _oddFrame) {
return 311;
_baseVblankEndScanline = 311;
} else {
return 312;
_baseVblankEndScanline = 312;
}
}
_overclockEnabled = cfg.PpuExtraScanlinesBeforeNmi > 0 || cfg.PpuExtraScanlinesAfterNmi > 0;
_adjustedVblankEndScanline = _baseVblankEndScanline + cfg.PpuExtraScanlinesBeforeNmi;
_vblankEndScanline = _baseVblankEndScanline + cfg.PpuExtraScanlinesAfterNmi + cfg.PpuExtraScanlinesBeforeNmi;
_vblankStartScanline = _overscanMode ? 240 : 225;
_nmiScanline = _vblankStartScanline + cfg.PpuExtraScanlinesBeforeNmi;
}
uint16_t Ppu::GetRealScanline()
{
if(!_overclockEnabled) {
return _scanline;
}
if(_scanline > _vblankStartScanline && _scanline <= _nmiScanline) {
//Pretend to be just before vblank until extra scanlines are over
return _vblankStartScanline - 1;
} else if(_scanline > _nmiScanline) {
if(_scanline > _adjustedVblankEndScanline) {
//Pretend to be at the end of vblank until extra scanlines are over
return _baseVblankEndScanline;
} else {
//Number the regular scanlines as they would normally be
return _scanline - _nmiScanline + _vblankStartScanline;
}
}
return _scanline;
}
uint16_t Ppu::GetLastScanline()
{
return _baseVblankEndScanline;
}
void Ppu::EvaluateNextLineSprites()
@ -1499,7 +1567,7 @@ bool Ppu::IsDoubleWidth()
void Ppu::LatchLocationValues()
{
_horizontalLocation = GetCycle();
_verticalLocation = _scanline;
_verticalLocation = GetRealScanline();
_locationLatched = true;
}
@ -1511,7 +1579,7 @@ void Ppu::UpdateOamAddress()
uint16_t Ppu::GetOamAddress()
{
if(_forcedVblank || _scanline >= _vblankStart) {
if(_forcedVblank || _scanline >= _vblankStartScanline) {
return _internalOamAddress;
} else {
if(_memoryManager->GetHClock() <= 255 * 4) {
@ -1542,7 +1610,7 @@ uint16_t Ppu::GetVramAddress()
uint8_t Ppu::Read(uint16_t addr)
{
if(_scanline < _vblankStart) {
if(_scanline < _vblankStartScanline) {
RenderScanline();
}
@ -1704,13 +1772,13 @@ uint8_t Ppu::Read(uint16_t addr)
void Ppu::Write(uint32_t addr, uint8_t value)
{
if(_scanline < _vblankStart) {
if(_scanline < _vblankStartScanline) {
RenderScanline();
}
switch(addr) {
case 0x2100:
if(_forcedVblank && _scanline == _vblankStart) {
if(_forcedVblank && _scanline == _nmiScanline) {
//"writing this register on the first line of V-Blank (225 or 240, depending on overscan) when force blank is currently active causes the OAM Address Reset to occur."
UpdateOamAddress();
}
@ -1753,7 +1821,7 @@ void Ppu::Write(uint32_t addr, uint8_t value)
}
}
if(!_forcedVblank && _scanline < _vblankStart) {
if(!_forcedVblank && _scanline < _nmiScanline) {
//During rendering the high table is also written to when writing to OAM
oamAddr = 0x200 | ((oamAddr & 0x1F0) >> 4);
}
@ -1864,7 +1932,7 @@ void Ppu::Write(uint32_t addr, uint8_t value)
case 0x2118:
//VMDATAL - VRAM Data Write low byte
if(_scanline >= _vblankStart || _forcedVblank) {
if(_scanline >= _nmiScanline || _forcedVblank) {
//Only write the value if in vblank or forced blank (writes to VRAM outside vblank/forced blank are not allowed)
_console->ProcessPpuWrite(GetVramAddress() << 1, value, SnesMemoryType::VideoRam);
_vram[GetVramAddress()] = value | (_vram[GetVramAddress()] & 0xFF00);
@ -1878,7 +1946,7 @@ void Ppu::Write(uint32_t addr, uint8_t value)
case 0x2119:
//VMDATAH - VRAM Data Write high byte
if(_scanline >= _vblankStart || _forcedVblank) {
if(_scanline >= _nmiScanline || _forcedVblank) {
//Only write the value if in vblank or forced blank (writes to VRAM outside vblank/forced blank are not allowed)
_console->ProcessPpuWrite((GetVramAddress() << 1) + 1, value, SnesMemoryType::VideoRam);
_vram[GetVramAddress()] = (value << 8) | (_vram[GetVramAddress()] & 0xFF);
@ -2044,9 +2112,9 @@ void Ppu::Write(uint32_t addr, uint8_t value)
_mode7.ExtBgEnabled = (value & 0x40) != 0;
_hiResMode = (value & 0x08) != 0;
_overscanMode = (value & 0x04) != 0;
_vblankStart = _overscanMode ? 240 : 225;
_objInterlace = (value & 0x02) != 0;
_screenInterlace = (value & 0x01) != 0;
UpdateNmiScanline();
break;
default:
@ -2072,8 +2140,8 @@ void Ppu::Serialize(Serializer &s)
_windowMaskSub[0], _windowMaskSub[1], _windowMaskSub[2], _windowMaskSub[3], _windowMaskSub[4],
_mode7.CenterX, _mode7.CenterY, _mode7.ExtBgEnabled, _mode7.FillWithTile0, _mode7.HorizontalMirroring,
_mode7.HScroll, _mode7.LargeMap, _mode7.Matrix[0], _mode7.Matrix[1], _mode7.Matrix[2], _mode7.Matrix[3],
_mode7.ValueLatch, _mode7.VerticalMirroring, _mode7.VScroll, unused_oamRenderAddress, _oddFrame, _vblankStart,
_cgramAddressLatch, _cgramWriteBuffer
_mode7.ValueLatch, _mode7.VerticalMirroring, _mode7.VScroll, unused_oamRenderAddress, _oddFrame, _vblankStartScanline,
_cgramAddressLatch, _cgramWriteBuffer, _nmiScanline, _vblankEndScanline, _adjustedVblankEndScanline, _baseVblankEndScanline
);
for(int i = 0; i < 4; i++) {

View File

@ -7,6 +7,8 @@
class Console;
class InternalRegisters;
class MemoryManager;
class Spc;
class EmuSettings;
class Ppu : public ISerializable
{
@ -22,6 +24,8 @@ private:
Console* _console;
InternalRegisters* _regs;
MemoryManager* _memoryManager;
Spc* _spc;
EmuSettings* _settings;
//Temporary data used for the tilemap/tile fetching
LayerData _layerData[4] = {};
@ -49,7 +53,14 @@ private:
uint16_t _scanline = 0;
uint32_t _frameCount = 0;
uint16_t _vblankStart;
uint16_t _vblankStartScanline;
uint16_t _vblankEndScanline;
uint16_t _baseVblankEndScanline;
uint16_t _adjustedVblankEndScanline;
uint16_t _nmiScanline;
bool _overclockEnabled;
uint8_t _oddFrame = 0;
uint16_t _drawStartX = 0;
@ -247,14 +258,18 @@ public:
void RenderScanline();
uint32_t GetFrameCount();
uint16_t GetRealScanline();
uint16_t GetScanline();
uint16_t GetCycle();
uint16_t GetNmiScanline();
uint16_t GetVblankStart();
PpuState GetState();
void GetState(PpuState &state, bool returnPartialState);
bool ProcessEndOfScanline(uint16_t hClock);
void UpdateSpcState();
void UpdateNmiScanline();
uint16_t GetLastScanline();
bool IsHighResOutput();

View File

@ -37,6 +37,7 @@ Spc::Spc(Console* console)
_tmp3 = 0;
_operandA = 0;
_operandB = 0;
_enabled = true;
_clockRatio = (double)2048000 / _console->GetMasterClockRate();
}
@ -74,6 +75,21 @@ void Spc::Reset()
_dsp->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
}
void Spc::SetSpcState(bool enabled)
{
//Used by overclocking logic to disable SPC during the extra scanlines added to the PPU
if(_enabled != enabled) {
if(enabled) {
//When re-enabling, adjust the cycle counter to prevent running extra cycles
_state.Cycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio);
} else {
//Catch up SPC before disabling it
Run();
}
_enabled = enabled;
}
}
void Spc::Idle()
{
IncCycleCount(-1);
@ -283,7 +299,7 @@ void Spc::CpuWriteRegister(uint32_t addr, uint8_t value)
void Spc::Run()
{
if(_state.StopState != CpuStopState::Running) {
if(!_enabled || _state.StopState != CpuStopState::Running) {
//STOP or SLEEP were executed - execution is stopped forever.
return;
}

View File

@ -42,6 +42,8 @@ private:
SpcOpStep _opStep;
uint8_t _opSubStep;
bool _enabled;
SpcState _state;
uint8_t* _ram;
uint8_t _spcBios[64] {
@ -280,6 +282,8 @@ public:
Spc(Console* console);
virtual ~Spc();
void SetSpcState(bool enabled);
void Run();
void Reset();

View File

@ -287,7 +287,7 @@
<Control ID="tpgOverclocking">Overclocking</Control>
<Control ID="grpOverclocking">Overclocking</Control>
<Control ID="lblOverclockHint">Overclocking can help reduce or remove slowdowns in games (but it can also cause issues).&#13;The most compatible way to overclock is by increasing the "Scanline before NMI" value by a few hundred lines (e.g 400+)</Control>
<Control ID="lblOverclockHint">Overclocking can help reduce or remove slowdowns in games (but it can also cause issues). To overclock, try setting the "before NMI" value to a few hundred lines (e.g 300+). If this breaks the game, try lowering the value or using the "after NMI" field instead.</Control>
<Control ID="lblClockRate">Clock Rate Multiplier:</Control>
<Control ID="lblClockRatePercent">% (Default: 100%)</Control>

View File

@ -27,6 +27,7 @@
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmEmulationConfig));
this.tabMain = new System.Windows.Forms.TabControl();
this.tpgGeneral = new System.Windows.Forms.TabPage();
this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel();
@ -48,6 +49,16 @@
this.flowLayoutPanel8 = new System.Windows.Forms.FlowLayoutPanel();
this.lblRamPowerOnState = new System.Windows.Forms.Label();
this.cboRamPowerOnState = new System.Windows.Forms.ComboBox();
this.tpgOverclocking = new System.Windows.Forms.TabPage();
this.picHint = new System.Windows.Forms.PictureBox();
this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel();
this.lblOverclockHint = new System.Windows.Forms.Label();
this.grpPpuTiming = new System.Windows.Forms.GroupBox();
this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel();
this.nudExtraScanlinesAfterNmi = new Mesen.GUI.Controls.MesenNumericUpDown();
this.nudExtraScanlinesBeforeNmi = new Mesen.GUI.Controls.MesenNumericUpDown();
this.lblExtraScanlinesBeforeNmi = new System.Windows.Forms.Label();
this.lblExtraScanlinesAfterNmi = new System.Windows.Forms.Label();
this.tabMain.SuspendLayout();
this.tpgGeneral.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
@ -56,6 +67,11 @@
this.flowLayoutPanel10.SuspendLayout();
this.tpgAdvanced.SuspendLayout();
this.flowLayoutPanel8.SuspendLayout();
this.tpgOverclocking.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.picHint)).BeginInit();
this.tableLayoutPanel3.SuspendLayout();
this.grpPpuTiming.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
@ -68,11 +84,12 @@
//
this.tabMain.Controls.Add(this.tpgGeneral);
this.tabMain.Controls.Add(this.tpgAdvanced);
this.tabMain.Controls.Add(this.tpgOverclocking);
this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill;
this.tabMain.Location = new System.Drawing.Point(0, 0);
this.tabMain.Name = "tabMain";
this.tabMain.SelectedIndex = 0;
this.tabMain.Size = new System.Drawing.Size(414, 319);
this.tabMain.Size = new System.Drawing.Size(414, 290);
this.tabMain.TabIndex = 2;
//
// tpgGeneral
@ -323,7 +340,7 @@
this.tpgAdvanced.Location = new System.Drawing.Point(4, 22);
this.tpgAdvanced.Name = "tpgAdvanced";
this.tpgAdvanced.Padding = new System.Windows.Forms.Padding(3);
this.tpgAdvanced.Size = new System.Drawing.Size(406, 293);
this.tpgAdvanced.Size = new System.Drawing.Size(406, 264);
this.tpgAdvanced.TabIndex = 3;
this.tpgAdvanced.Text = "Advanced";
this.tpgAdvanced.UseVisualStyleBackColor = true;
@ -336,7 +353,7 @@
this.flowLayoutPanel8.Location = new System.Drawing.Point(3, 3);
this.flowLayoutPanel8.Margin = new System.Windows.Forms.Padding(7, 0, 0, 0);
this.flowLayoutPanel8.Name = "flowLayoutPanel8";
this.flowLayoutPanel8.Size = new System.Drawing.Size(400, 287);
this.flowLayoutPanel8.Size = new System.Drawing.Size(400, 258);
this.flowLayoutPanel8.TabIndex = 4;
//
// lblRamPowerOnState
@ -358,6 +375,173 @@
this.cboRamPowerOnState.Size = new System.Drawing.Size(176, 21);
this.cboRamPowerOnState.TabIndex = 1;
//
// tpgOverclocking
//
this.tpgOverclocking.Controls.Add(this.picHint);
this.tpgOverclocking.Controls.Add(this.tableLayoutPanel3);
this.tpgOverclocking.Location = new System.Drawing.Point(4, 22);
this.tpgOverclocking.Name = "tpgOverclocking";
this.tpgOverclocking.Size = new System.Drawing.Size(406, 264);
this.tpgOverclocking.TabIndex = 4;
this.tpgOverclocking.Text = "Overclocking";
this.tpgOverclocking.UseVisualStyleBackColor = true;
//
// picHint
//
this.picHint.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.picHint.BackgroundImage = global::Mesen.GUI.Properties.Resources.Help;
this.picHint.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
this.picHint.Location = new System.Drawing.Point(8, 20);
this.picHint.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
this.picHint.Name = "picHint";
this.picHint.Size = new System.Drawing.Size(16, 16);
this.picHint.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.picHint.TabIndex = 8;
this.picHint.TabStop = false;
//
// tableLayoutPanel3
//
this.tableLayoutPanel3.ColumnCount = 1;
this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel3.Controls.Add(this.lblOverclockHint, 0, 0);
this.tableLayoutPanel3.Controls.Add(this.grpPpuTiming, 0, 1);
this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel3.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel3.Name = "tableLayoutPanel3";
this.tableLayoutPanel3.RowCount = 3;
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel3.Size = new System.Drawing.Size(406, 264);
this.tableLayoutPanel3.TabIndex = 1;
//
// lblOverclockHint
//
this.lblOverclockHint.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.lblOverclockHint.Location = new System.Drawing.Point(3, 0);
this.lblOverclockHint.Name = "lblOverclockHint";
this.lblOverclockHint.Padding = new System.Windows.Forms.Padding(25, 0, 0, 0);
this.lblOverclockHint.Size = new System.Drawing.Size(400, 54);
this.lblOverclockHint.TabIndex = 1;
this.lblOverclockHint.Text = resources.GetString("lblOverclockHint.Text");
//
// grpPpuTiming
//
this.grpPpuTiming.Controls.Add(this.tableLayoutPanel5);
this.grpPpuTiming.Dock = System.Windows.Forms.DockStyle.Fill;
this.grpPpuTiming.Location = new System.Drawing.Point(3, 57);
this.grpPpuTiming.Name = "grpPpuTiming";
this.grpPpuTiming.Size = new System.Drawing.Size(400, 71);
this.grpPpuTiming.TabIndex = 7;
this.grpPpuTiming.TabStop = false;
this.grpPpuTiming.Text = "PPU Vertical Blank Configuration";
//
// tableLayoutPanel5
//
this.tableLayoutPanel5.ColumnCount = 2;
this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel5.Controls.Add(this.nudExtraScanlinesAfterNmi, 1, 1);
this.tableLayoutPanel5.Controls.Add(this.nudExtraScanlinesBeforeNmi, 1, 0);
this.tableLayoutPanel5.Controls.Add(this.lblExtraScanlinesBeforeNmi, 0, 0);
this.tableLayoutPanel5.Controls.Add(this.lblExtraScanlinesAfterNmi, 0, 1);
this.tableLayoutPanel5.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel5.Location = new System.Drawing.Point(3, 16);
this.tableLayoutPanel5.Name = "tableLayoutPanel5";
this.tableLayoutPanel5.RowCount = 3;
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel5.Size = new System.Drawing.Size(394, 52);
this.tableLayoutPanel5.TabIndex = 0;
//
// nudExtraScanlinesAfterNmi
//
this.nudExtraScanlinesAfterNmi.DecimalPlaces = 0;
this.nudExtraScanlinesAfterNmi.Increment = new decimal(new int[] {
1,
0,
0,
0});
this.nudExtraScanlinesAfterNmi.Location = new System.Drawing.Point(165, 30);
this.nudExtraScanlinesAfterNmi.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.nudExtraScanlinesAfterNmi.Maximum = new decimal(new int[] {
1000,
0,
0,
0});
this.nudExtraScanlinesAfterNmi.MaximumSize = new System.Drawing.Size(10000, 20);
this.nudExtraScanlinesAfterNmi.Minimum = new decimal(new int[] {
0,
0,
0,
0});
this.nudExtraScanlinesAfterNmi.MinimumSize = new System.Drawing.Size(0, 21);
this.nudExtraScanlinesAfterNmi.Name = "nudExtraScanlinesAfterNmi";
this.nudExtraScanlinesAfterNmi.Size = new System.Drawing.Size(46, 21);
this.nudExtraScanlinesAfterNmi.TabIndex = 3;
this.nudExtraScanlinesAfterNmi.Value = new decimal(new int[] {
100,
0,
0,
0});
//
// nudExtraScanlinesBeforeNmi
//
this.nudExtraScanlinesBeforeNmi.DecimalPlaces = 0;
this.nudExtraScanlinesBeforeNmi.Increment = new decimal(new int[] {
1,
0,
0,
0});
this.nudExtraScanlinesBeforeNmi.Location = new System.Drawing.Point(165, 3);
this.nudExtraScanlinesBeforeNmi.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.nudExtraScanlinesBeforeNmi.Maximum = new decimal(new int[] {
1000,
0,
0,
0});
this.nudExtraScanlinesBeforeNmi.MaximumSize = new System.Drawing.Size(10000, 20);
this.nudExtraScanlinesBeforeNmi.Minimum = new decimal(new int[] {
0,
0,
0,
0});
this.nudExtraScanlinesBeforeNmi.MinimumSize = new System.Drawing.Size(0, 21);
this.nudExtraScanlinesBeforeNmi.Name = "nudExtraScanlinesBeforeNmi";
this.nudExtraScanlinesBeforeNmi.Size = new System.Drawing.Size(46, 21);
this.nudExtraScanlinesBeforeNmi.TabIndex = 2;
this.nudExtraScanlinesBeforeNmi.Value = new decimal(new int[] {
100,
0,
0,
0});
//
// lblExtraScanlinesBeforeNmi
//
this.lblExtraScanlinesBeforeNmi.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblExtraScanlinesBeforeNmi.AutoSize = true;
this.lblExtraScanlinesBeforeNmi.Location = new System.Drawing.Point(3, 7);
this.lblExtraScanlinesBeforeNmi.Name = "lblExtraScanlinesBeforeNmi";
this.lblExtraScanlinesBeforeNmi.Size = new System.Drawing.Size(159, 13);
this.lblExtraScanlinesBeforeNmi.TabIndex = 0;
this.lblExtraScanlinesBeforeNmi.Text = "Additional scanlines before NMI:";
//
// lblExtraScanlinesAfterNmi
//
this.lblExtraScanlinesAfterNmi.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblExtraScanlinesAfterNmi.AutoSize = true;
this.lblExtraScanlinesAfterNmi.Location = new System.Drawing.Point(3, 34);
this.lblExtraScanlinesAfterNmi.Name = "lblExtraScanlinesAfterNmi";
this.lblExtraScanlinesAfterNmi.Size = new System.Drawing.Size(150, 13);
this.lblExtraScanlinesAfterNmi.TabIndex = 1;
this.lblExtraScanlinesAfterNmi.Text = "Additional scanlines after NMI:";
//
// frmEmulationConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -370,8 +554,8 @@
this.Name = "frmEmulationConfig";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Emulation Config";
this.Controls.SetChildIndex(this.tabMain, 0);
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
this.Controls.SetChildIndex(this.tabMain, 0);
this.tabMain.ResumeLayout(false);
this.tpgGeneral.ResumeLayout(false);
this.tpgGeneral.PerformLayout();
@ -386,6 +570,13 @@
this.tpgAdvanced.ResumeLayout(false);
this.flowLayoutPanel8.ResumeLayout(false);
this.flowLayoutPanel8.PerformLayout();
this.tpgOverclocking.ResumeLayout(false);
this.tpgOverclocking.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.picHint)).EndInit();
this.tableLayoutPanel3.ResumeLayout(false);
this.grpPpuTiming.ResumeLayout(false);
this.tableLayoutPanel5.ResumeLayout(false);
this.tableLayoutPanel5.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@ -414,5 +605,15 @@
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel8;
private System.Windows.Forms.Label lblRamPowerOnState;
private System.Windows.Forms.ComboBox cboRamPowerOnState;
private System.Windows.Forms.TabPage tpgOverclocking;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
private System.Windows.Forms.Label lblOverclockHint;
private System.Windows.Forms.GroupBox grpPpuTiming;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5;
private Controls.MesenNumericUpDown nudExtraScanlinesAfterNmi;
private Controls.MesenNumericUpDown nudExtraScanlinesBeforeNmi;
private System.Windows.Forms.Label lblExtraScanlinesBeforeNmi;
private System.Windows.Forms.Label lblExtraScanlinesAfterNmi;
private System.Windows.Forms.PictureBox picHint;
}
}

View File

@ -28,6 +28,9 @@ namespace Mesen.GUI.Forms.Config
AddBinding(nameof(EmulationConfig.Region), cboRegion);
AddBinding(nameof(EmulationConfig.RamPowerOnState), cboRamPowerOnState);
AddBinding(nameof(EmulationConfig.PpuExtraScanlinesBeforeNmi), nudExtraScanlinesBeforeNmi);
AddBinding(nameof(EmulationConfig.PpuExtraScanlinesAfterNmi), nudExtraScanlinesAfterNmi);
}
protected override void OnApply()

View File

@ -120,4 +120,7 @@
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="lblOverclockHint.Text" xml:space="preserve">
<value>Overclocking can help reduce or remove slowdowns in games (but it can also cause issues). To overclock, try setting the "before NMI" value to a few hundred lines (e.g 300+). If this breaks the game, try lowering the value or using the "after NMI" field instead.</value>
</data>
</root>