From a72acc9f1af7c9faa937ecf93d68c4e7bd3424d0 Mon Sep 17 00:00:00 2001 From: Sour Date: Sun, 10 Nov 2019 17:35:29 -0500 Subject: [PATCH] Core: Rewrite DMC/OAM DMA, tweak PPU timings, add option to simulate PPU/CPU alignments --- Core/APU.cpp | 24 +- Core/APU.h | 5 +- Core/ApuFrameCounter.h | 9 +- Core/BaseExpansionAudio.cpp | 11 +- Core/BaseExpansionAudio.h | 3 - Core/CPU.cpp | 278 +++++++++++------- Core/CPU.h | 27 +- Core/Console.cpp | 5 +- Core/DeltaModulationChannel.cpp | 15 +- Core/DeltaModulationChannel.h | 4 +- Core/EmulationSettings.h | 54 +--- Core/MemoryAccessCounter.cpp | 2 +- Core/MesenMovie.cpp | 8 - Core/MovieRecorder.cpp | 4 - Core/MovieRecorder.h | 2 - Core/PPU.cpp | 64 ++-- Core/PPU.h | 17 +- Core/SaveStateManager.cpp | 2 +- Core/SaveStateManager.h | 2 +- Core/SoundMixer.cpp | 3 - GUI.NET/Config/EmulationInfo.cs | 6 +- GUI.NET/Dependencies/resources.ca.xml | 1 + GUI.NET/Dependencies/resources.en.xml | 1 + GUI.NET/Dependencies/resources.es.xml | 1 + GUI.NET/Dependencies/resources.fr.xml | 1 + GUI.NET/Dependencies/resources.it.xml | 1 + GUI.NET/Dependencies/resources.ja.xml | 3 +- GUI.NET/Dependencies/resources.pt.xml | 1 + GUI.NET/Dependencies/resources.ru.xml | 1 + GUI.NET/Dependencies/resources.uk.xml | 1 + GUI.NET/Dependencies/resources.zh.xml | 1 + .../Config/frmEmulationConfig.Designer.cs | 202 ++++--------- GUI.NET/Forms/Config/frmEmulationConfig.cs | 10 +- GUI.NET/InteropEmu.cs | 3 +- InteropDLL/ConsoleWrapper.cpp | 1 - Libretro/libretro.cpp | 10 +- 36 files changed, 341 insertions(+), 442 deletions(-) diff --git a/Core/APU.cpp b/Core/APU.cpp index 887febf3..1f1ca96f 100644 --- a/Core/APU.cpp +++ b/Core/APU.cpp @@ -171,7 +171,7 @@ void APU::SetNeedToRun() bool APU::NeedToRun(uint32_t currentCycle) { - if(_needToRun || _deltaModulationChannel->NeedToRun()) { + if(_deltaModulationChannel->NeedToRun() || _needToRun) { //Need to run whenever we alter the length counters //Need to run every cycle when DMC is running to get accurate emulation (CPU stalling, interaction with sprite DMA, etc.) _needToRun = false; @@ -210,22 +210,13 @@ void APU::EndFrame() void APU::ProcessCpuClock() { if(_apuEnabled) { - if(_settings->GetOverclockRate() == 100 || !_settings->GetOverclockAdjustApu()) { - Exec(); - } else { - _cyclesNeeded += 1.0 / ((double)_settings->GetOverclockRate() / 100.0); - while(_cyclesNeeded >= 1.0) { - Exec(); - _cyclesNeeded--; - } - } + Exec(); } } void APU::Reset(bool softReset) { _apuEnabled = true; - _cyclesNeeded = 0; _currentCycle = 0; _previousCycle = 0; _squareChannel[0]->Reset(softReset); @@ -253,7 +244,7 @@ void APU::StreamState(bool saving) SnapshotInfo deltaModulationChannel{ _deltaModulationChannel.get() }; SnapshotInfo frameCounter{ _frameCounter.get() }; SnapshotInfo mixer{ _mixer.get() }; - Stream(_nesModel, squareChannel0, squareChannel1, triangleChannel, noiseChannel, deltaModulationChannel, frameCounter, mixer, _cyclesNeeded); + Stream(_nesModel, squareChannel0, squareChannel1, triangleChannel, noiseChannel, deltaModulationChannel, frameCounter, mixer); } void APU::AddExpansionAudioDelta(AudioChannel channel, int16_t delta) @@ -275,9 +266,14 @@ bool APU::IsApuEnabled() return _apuEnabled; } -void APU::FillDmcReadBuffer() +uint16_t APU::GetDmcReadAddress() { - _deltaModulationChannel->FillReadBuffer(); + return _deltaModulationChannel->GetDmcReadAddress(); +} + +void APU::SetDmcReadBuffer(uint8_t value) +{ + _deltaModulationChannel->SetDmcReadBuffer(value); } ApuState APU::GetState() diff --git a/Core/APU.h b/Core/APU.h index 83c65597..8d33baf4 100644 --- a/Core/APU.h +++ b/Core/APU.h @@ -40,8 +40,6 @@ class APU : public Snapshotable, public IMemoryHandler NesModel _nesModel; - double _cyclesNeeded; - private: __forceinline bool NeedToRun(uint32_t currentCycle); @@ -73,6 +71,7 @@ class APU : public Snapshotable, public IMemoryHandler void AddExpansionAudioDelta(AudioChannel channel, int16_t delta); void SetApuStatus(bool enabled); bool IsApuEnabled(); - void FillDmcReadBuffer(); + uint16_t GetDmcReadAddress(); + void SetDmcReadBuffer(uint8_t value); void SetNeedToRun(); }; \ No newline at end of file diff --git a/Core/ApuFrameCounter.h b/Core/ApuFrameCounter.h index cfada017..8cf019da 100644 --- a/Core/ApuFrameCounter.h +++ b/Core/ApuFrameCounter.h @@ -64,8 +64,7 @@ public: void StreamState(bool saving) override { - int32_t unusednextIrqCycle = 0; - Stream(unusednextIrqCycle, _previousCycle, _currentStep, _stepMode, _inhibitIRQ, _nesModel, _blockFrameCounterTick, _writeDelayCounter, _newValue); + Stream(_previousCycle, _currentStep, _stepMode, _inhibitIRQ, _nesModel, _blockFrameCounterTick, _writeDelayCounter, _newValue); if(!saving) { SetNesModel(_nesModel); @@ -185,11 +184,11 @@ public: //Reset sequence after $4017 is written to if(_console->GetCpu()->GetCycleCount() & 0x01) { - //"If the write occurs during an APU cycle, the effects occur 3 CPU cycles after the $4017 write cycle" - _writeDelayCounter = 3; - } else { //"If the write occurs between APU cycles, the effects occur 4 CPU cycles after the write cycle. " _writeDelayCounter = 4; + } else { + //"If the write occurs during an APU cycle, the effects occur 3 CPU cycles after the $4017 write cycle" + _writeDelayCounter = 3; } _inhibitIRQ = (value & 0x40) == 0x40; diff --git a/Core/BaseExpansionAudio.cpp b/Core/BaseExpansionAudio.cpp index 56db268c..a1085e8b 100644 --- a/Core/BaseExpansionAudio.cpp +++ b/Core/BaseExpansionAudio.cpp @@ -10,20 +10,11 @@ BaseExpansionAudio::BaseExpansionAudio(shared_ptr console) void BaseExpansionAudio::StreamState(bool saving) { - Stream(_clocksNeeded); } void BaseExpansionAudio::Clock() { if(_console->GetApu()->IsApuEnabled()) { - if(_console->GetSettings()->GetOverclockRate() == 100 || !_console->GetSettings()->GetOverclockAdjustApu()) { - ClockAudio(); - } else { - _clocksNeeded += 1.0 / ((double)_console->GetSettings()->GetOverclockRate() / 100); - while(_clocksNeeded >= 1.0) { - ClockAudio(); - _clocksNeeded--; - } - } + ClockAudio(); } } \ No newline at end of file diff --git a/Core/BaseExpansionAudio.h b/Core/BaseExpansionAudio.h index 450940e2..ce21441f 100644 --- a/Core/BaseExpansionAudio.h +++ b/Core/BaseExpansionAudio.h @@ -7,9 +7,6 @@ class MemoryManager; class BaseExpansionAudio : public Snapshotable { -private: - double _clocksNeeded = 0; - protected: shared_ptr _console = nullptr; diff --git a/Core/CPU.cpp b/Core/CPU.cpp index 2716a544..9089dda9 100644 --- a/Core/CPU.cpp +++ b/Core/CPU.cpp @@ -1,4 +1,6 @@ #include "stdafx.h" +#include +#include #include "CPU.h" #include "PPU.h" #include "APU.h" @@ -60,12 +62,15 @@ CPU::CPU(shared_ptr console) _state = {}; _cycleCount = 0; _operand = 0; - _spriteDmaCounter = 0; _spriteDmaTransfer = false; - _dmcCounter = 0; + _spriteDmaOffset = 0; + _needHalt = false; + _ppuOffset = 0; + _startClockCount = 6; + _endClockCount = 6; + _masterClock = 0; _dmcDmaRunning = false; _cpuWrite = false; - _writeAddr = 0; _irqMask = 0; _state = {}; _prevRunIrq = false; @@ -76,12 +81,10 @@ void CPU::Reset(bool softReset, NesModel model) { _state.NMIFlag = false; _state.IRQFlag = 0; - _cycleCount = -1; _spriteDmaTransfer = false; - _spriteDmaCounter = 0; - - _dmcCounter = -1; + _spriteDmaOffset = 0; + _needHalt = false; _dmcDmaRunning = false; _warnOnCrash = true; @@ -106,13 +109,50 @@ void CPU::Reset(bool softReset, NesModel model) _runIrq = false; } - //The CPU takes some cycles before starting its execution after a reset/power up - for(int i = 0; i < (model == NesModel::NTSC ? 28 : 30); i++) { - _console->GetPpu()->Exec(); + if(!softReset) { + uint8_t ppuDivider; + uint8_t cpuDivider; + switch(model) { + default: + case NesModel::NTSC: + ppuDivider = 4; + cpuDivider = 12; + break; + + case NesModel::PAL: + ppuDivider = 5; + cpuDivider = 16; + break; + + case NesModel::Dendy: + ppuDivider = 5; + cpuDivider = 15; + break; + } + + _cycleCount = -1; + _masterClock = 0; + + uint8_t cpuOffset = 0; + if(_console->GetSettings()->CheckFlag(EmulationFlags::RandomizeCpuPpuAlignment)) { + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution<> distPpu(0, ppuDivider - 1); + std::uniform_int_distribution<> distCpu(0, cpuDivider - 1); + _ppuOffset = distPpu(mt); + cpuOffset += distCpu(mt); + } else { + _ppuOffset = 2; + cpuOffset = 0; + } + + _masterClock += cpuDivider + cpuOffset; } - for(int i = 0; i < 10; i++) { - _console->GetApu()->ProcessCpuClock(); + //The CPU takes 8 cycles before it starts executing the ROM's code after a reset/power up + for(int i = 0; i < 8; i++) { + StartCpuCycle(true); + EndCpuCycle(true); } } @@ -200,19 +240,10 @@ void CPU::MemoryWrite(uint16_t addr, uint8_t value, MemoryOperationType operatio _writeCounter++; } #else - _cpuWrite = true;; - _writeAddr = addr; - IncCycleCount(); - while(_dmcDmaRunning) { - IncCycleCount(); - } - + _cpuWrite = true; + StartCpuCycle(false); _memoryManager->Write(addr, value, operationType); - - //DMA DMC might have started after a write to $4015, stall CPU if needed - while(_dmcDmaRunning) { - IncCycleCount(); - } + EndCpuCycle(false); _cpuWrite = false; #endif } @@ -228,20 +259,11 @@ uint8_t CPU::MemoryRead(uint16_t addr, MemoryOperationType operationType) { } return value; #else - IncCycleCount(); - while(_dmcDmaRunning) { - //Stall CPU until we can process a DMC read - if((addr != 0x4016 && addr != 0x4017 && (_cycleCount & 0x01)) || _dmcCounter == 1) { - //While the CPU is stalled, reads are performed on the current address - //Reads are only performed every other cycle? This fixes "dma_2007_read" test - //This behavior causes the $4016/7 data corruption when a DMC is running. - //When reading $4016/7, only the last read counts (because this only occurs to low-to-high transitions, i.e once in this case) - _memoryManager->Read(addr); - } - IncCycleCount(); - } + ProcessPendingDma(addr); + StartCpuCycle(true); uint8_t value = _memoryManager->Read(addr, operationType); + EndCpuCycle(true); return value; #endif } @@ -292,90 +314,112 @@ uint16_t CPU::FetchOperand() } -void CPU::IncCycleCount() +void CPU::EndCpuCycle(bool forRead) { - _cycleCount++; + _masterClock += forRead ? (_endClockCount + 1) : (_endClockCount - 1); + _console->GetPpu()->Run(_masterClock - _ppuOffset); - if(_dmcDmaRunning) { - //CPU is being stalled by the DMC's DMA transfer - _dmcCounter--; - if(_dmcCounter == 0) { - //Update the DMC buffer when the stall period is completed - _dmcDmaRunning = false; - #ifndef DUMMYCPU - _console->GetApu()->FillDmcReadBuffer(); - _console->DebugAddTrace("DMC DMA End"); - #endif - } + //"it's really the status of the interrupt lines at the end of the second-to-last cycle that matters." + //Keep the irq lines values from the previous cycle. The before-to-last cycle's values will be used + _prevRunIrq = _runIrq; + _runIrq = _state.NMIFlag || ((_state.IRQFlag & _irqMask) > 0 && !CheckFlag(PSFlags::Interrupt)); +} + +void CPU::StartCpuCycle(bool forRead) +{ + _masterClock += forRead ? (_startClockCount - 1) : (_startClockCount + 1); + _cycleCount++; + _console->GetPpu()->Run(_masterClock - _ppuOffset); + _console->ProcessCpuClock(); +} + +void CPU::ProcessPendingDma(uint16_t readAddress) +{ + if(!_needHalt) { + return; } - _console->ProcessCpuClock(); + //"If this cycle is a read, hijack the read, discard the value, and prevent all other actions that occur on this cycle (PC not incremented, etc)" + StartCpuCycle(true); + _memoryManager->Read(readAddress, MemoryOperationType::DummyRead); + EndCpuCycle(true); + _needHalt = false; - if(!_spriteDmaTransfer && !_dmcDmaRunning) { - //IRQ flags are ignored during Sprite DMA - fixes irq_and_dma + uint16_t spriteDmaCounter = 0; + uint8_t spriteReadAddr = 0; + uint8_t readValue = 0; + bool skipDummyReads = (readAddress == 0x4016 || readAddress == 0x4017); - //"it's really the status of the interrupt lines at the end of the second-to-last cycle that matters." - //Keep the irq lines values from the previous cycle. The before-to-last cycle's values will be used - _prevRunIrq = _runIrq; - _runIrq = _state.NMIFlag || ((_state.IRQFlag & _irqMask) > 0 && !CheckFlag(PSFlags::Interrupt)); + auto processCycle = [this] { + //Sprite DMA cycles count as halt/dummy cycles for the DMC DMA when both run at the same time + if(_needHalt) { + _needHalt = false; + } else if(_needDummyRead) { + _needDummyRead = false; + } + StartCpuCycle(true); + }; + + while(_dmcDmaRunning || _spriteDmaTransfer) { + bool getCycle = (_cycleCount & 0x01) == 0; + if(getCycle) { + if(_dmcDmaRunning && !_needHalt && !_needDummyRead) { + //DMC DMA is ready to read a byte (both halt and dummy read cycles were performed before this) + processCycle(); + readValue = _memoryManager->Read(_console->GetApu()->GetDmcReadAddress(), MemoryOperationType::DmcRead); + EndCpuCycle(true); + _console->GetApu()->SetDmcReadBuffer(readValue); + _dmcDmaRunning = false; + } else if(_spriteDmaTransfer) { + //DMC DMA is not running, or not ready, run sprite DMA + processCycle(); + readValue = _memoryManager->Read(_spriteDmaOffset * 0x100 + spriteReadAddr); + EndCpuCycle(true); + spriteReadAddr++; + spriteDmaCounter++; + } else { + //DMC DMA is running, but not ready (need halt/dummy read) and sprite DMA isn't runnnig, perform a dummy read + assert(_needHalt || _needDummyRead); + processCycle(); + if(!skipDummyReads) { + _memoryManager->Read(readAddress, MemoryOperationType::DummyRead); + } + EndCpuCycle(true); + } + } else { + if(_spriteDmaTransfer && (spriteDmaCounter & 0x01)) { + //Sprite DMA write cycle (only do this if a sprite dma read was performed last cycle) + processCycle(); + _memoryManager->Write(0x2004, readValue, MemoryOperationType::Write); + EndCpuCycle(true); + spriteDmaCounter++; + if(spriteDmaCounter == 0x200) { + _spriteDmaTransfer = false; + } + } else { + //Align to read cycle before starting sprite DMA (or align to perform DMC read) + processCycle(); + if(!skipDummyReads) { + _memoryManager->Read(readAddress, MemoryOperationType::DummyRead); + } + EndCpuCycle(true); + } + } } } void CPU::RunDMATransfer(uint8_t offsetValue) { - _console->DebugAddTrace("Sprite DMA Start"); _spriteDmaTransfer = true; - - //"The CPU is suspended during the transfer, which will take 513 or 514 cycles after the $4014 write tick." - //"(1 dummy read cycle while waiting for writes to complete, +1 if on an odd CPU cycle, then 256 alternating read/write cycles.)" - if(_cycleCount % 2 != 0) { - DummyRead(); - } - DummyRead(); - - _spriteDmaCounter = 256; - - //DMA transfer starts at SpriteRamAddr and wraps around - for(int i = 0; i < 0x100; i++) { - //Read value - uint8_t readValue = MemoryRead(offsetValue * 0x100 + i); - - //Write to sprite ram via $2004 ("DMA is implemented in the 2A03/7 chip and works by repeatedly writing to OAMDATA") - MemoryWrite(0x2004, readValue); - - _spriteDmaCounter--; - } - - _spriteDmaTransfer = false; - - _console->DebugAddTrace("Sprite DMA End"); + _spriteDmaOffset = offsetValue; + _needHalt = true; } void CPU::StartDmcTransfer() { - //"DMC DMA adds 4 cycles normally, 2 if it lands on the $4014 write or during OAM DMA" - //3 cycles if it lands on the last write cycle of any instruction - _console->DebugAddTrace("DMC DMA Start"); _dmcDmaRunning = true; - if(_spriteDmaTransfer) { - if(_spriteDmaCounter == 2) { - _dmcCounter = 1; - } else if(_spriteDmaCounter == 1) { - _dmcCounter = 3; - } else { - _dmcCounter = 2; - } - } else { - if(_cpuWrite) { - if(_writeAddr == 0x4014) { - _dmcCounter = 2; - } else { - _dmcCounter = 3; - } - } else { - _dmcCounter = 4; - } - } + _needDummyRead = true; + _needHalt = true; } uint32_t CPU::GetClockRate(NesModel model) @@ -388,21 +432,39 @@ uint32_t CPU::GetClockRate(NesModel model) } } +void CPU::SetMasterClockDivider(NesModel region) +{ + switch(region) { + case NesModel::NTSC: + _startClockCount = 6; + _endClockCount = 6; + break; + + case NesModel::PAL: + _startClockCount = 8; + _endClockCount = 8; + break; + + case NesModel::Dendy: + _startClockCount = 7; + _endClockCount = 8; + break; + } +} + void CPU::StreamState(bool saving) { EmulationSettings* settings = _console->GetSettings(); - uint32_t overclockRate = settings->GetOverclockRateSetting(); - bool overclockAdjustApu = settings->GetOverclockAdjustApu(); uint32_t extraScanlinesBeforeNmi = settings->GetPpuExtraScanlinesBeforeNmi(); uint32_t extraScanlinesAfterNmi = settings->GetPpuExtraScanlinesAfterNmi(); uint32_t dipSwitches = _console->GetSettings()->GetDipSwitches(); Stream(_state.PC, _state.SP, _state.PS, _state.A, _state.X, _state.Y, _cycleCount, _state.NMIFlag, - _state.IRQFlag, _dmcCounter, _dmcDmaRunning, _spriteDmaCounter, _spriteDmaTransfer, - overclockRate, overclockAdjustApu, extraScanlinesBeforeNmi, extraScanlinesBeforeNmi, dipSwitches); + _state.IRQFlag, _dmcDmaRunning, _spriteDmaTransfer, + extraScanlinesBeforeNmi, extraScanlinesBeforeNmi, dipSwitches, + _needDummyRead, _needHalt, _startClockCount, _endClockCount, _ppuOffset, _masterClock); if(!saving) { - settings->SetOverclockRate(overclockRate, overclockAdjustApu); settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi); settings->SetDipSwitches(dipSwitches); } diff --git a/Core/CPU.h b/Core/CPU.h index bc963a36..1ea90e7f 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -32,19 +32,23 @@ private: typedef void(CPU::*Func)(); uint64_t _cycleCount; + uint64_t _masterClock; + uint8_t _ppuOffset; + uint8_t _startClockCount; + uint8_t _endClockCount; uint16_t _operand; Func _opTable[256]; AddrMode _addrMode[256]; AddrMode _instAddrMode; - uint16_t _spriteDmaCounter; - bool _spriteDmaTransfer; + bool _needHalt = false; + bool _spriteDmaTransfer = false; + bool _dmcDmaRunning = false; + bool _needDummyRead = false; + uint8_t _spriteDmaOffset; - int8_t _dmcCounter; - bool _dmcDmaRunning; bool _cpuWrite = false; - uint16_t _writeAddr = 0; uint8_t _irqMask; @@ -69,8 +73,10 @@ private: bool _isDummyRead[10]; #endif - void IncCycleCount(); - uint16_t FetchOperand(); + __forceinline void StartCpuCycle(bool forRead); + __forceinline void ProcessPendingDma(uint16_t readAddress); + __forceinline uint16_t FetchOperand(); + __forceinline void EndCpuCycle(bool forRead); void IRQ(); uint8_t GetOPCode() @@ -778,6 +784,7 @@ public: CPU(shared_ptr console); uint64_t GetCycleCount() { return _cycleCount; } + void SetMasterClockDivider(NesModel region); void SetNmiFlag() { _state.NMIFlag = true; } void ClearNmiFlag() { _state.NMIFlag = false; } void SetIrqMask(uint8_t mask) { _irqMask = mask; } @@ -832,11 +839,13 @@ public: _cycleCount = c->_cycleCount; _operand = c->_operand; - _spriteDmaCounter = c->_spriteDmaCounter; _spriteDmaTransfer = c->_spriteDmaTransfer; - _dmcCounter = c->_dmcCounter; + _needHalt = c->_needHalt; _dmcDmaRunning = c->_dmcDmaRunning; _cpuWrite = c->_cpuWrite; + _needDummyRead = c->_needDummyRead; + _needHalt = c->_needHalt; + _spriteDmaOffset = c->_spriteDmaOffset; _irqMask = c->_irqMask; _prevRunIrq = c->_prevRunIrq; _runIrq = c->_runIrq; diff --git a/Core/Console.cpp b/Core/Console.cpp index cf52a960..b29afb9f 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -421,9 +421,6 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile) string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC"); string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")"; MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetRomInfo().RomName, false)); - if(_settings->GetOverclockRate() != 100) { - MessageManager::DisplayMessage("ClockRate", std::to_string(_settings->GetOverclockRate()) + "%"); - } _settings->ClearFlags(EmulationFlags::ForceMaxSpeed); if(_slave) { @@ -451,7 +448,6 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile) void Console::ProcessCpuClock() { _mapper->ProcessCpuClock(); - _ppu->ProcessCpuClock(); _apu->ProcessCpuClock(); } @@ -967,6 +963,7 @@ void Console::UpdateNesModel(bool sendNotification) } } + _cpu->SetMasterClockDivider(model); _mapper->SetNesModel(model); _ppu->SetNesModel(model); _apu->SetNesModel(model); diff --git a/Core/DeltaModulationChannel.cpp b/Core/DeltaModulationChannel.cpp index 048f7105..76c4a718 100644 --- a/Core/DeltaModulationChannel.cpp +++ b/Core/DeltaModulationChannel.cpp @@ -58,10 +58,15 @@ void DeltaModulationChannel::StartDmcTransfer() } } -void DeltaModulationChannel::FillReadBuffer() +uint16_t DeltaModulationChannel::GetDmcReadAddress() +{ + return _currentAddr; +} + +void DeltaModulationChannel::SetDmcReadBuffer(uint8_t value) { if(_bytesRemaining > 0) { - _readBuffer = _console->GetMemoryManager()->Read(_currentAddr, MemoryOperationType::DmcRead); + _readBuffer = value; _bufferEmpty = false; //"The address is incremented; if it exceeds $FFFF, it is wrapped around to $8000." @@ -204,12 +209,16 @@ void DeltaModulationChannel::SetEnabled(bool enabled) _needToRun = false; } else if(_bytesRemaining == 0) { InitSample(); - StartDmcTransfer(); + _needInit = true; } } bool DeltaModulationChannel::NeedToRun() { + if(_needInit && (_console->GetCpu()->GetCycleCount() & 0x01) == 0) { + StartDmcTransfer(); + _needInit = false; + } return _needToRun; } diff --git a/Core/DeltaModulationChannel.h b/Core/DeltaModulationChannel.h index 8c254286..436a3fe9 100644 --- a/Core/DeltaModulationChannel.h +++ b/Core/DeltaModulationChannel.h @@ -26,6 +26,7 @@ private: uint8_t _bitsRemaining = 0; bool _silenceFlag = true; bool _needToRun = false; + bool _needInit = false; uint8_t _lastValue4011 = 0; @@ -47,7 +48,8 @@ public: void SetEnabled(bool enabled); void StartDmcTransfer(); - void FillReadBuffer(); + uint16_t GetDmcReadAddress(); + void SetDmcReadBuffer(uint8_t value); ApuDmcState GetState(); }; \ No newline at end of file diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 74b9b7b1..2126f8cc 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -95,6 +95,8 @@ enum EmulationFlags : uint64_t VsDualMuteMaster = 0x200000000000000, VsDualMuteSlave = 0x400000000000000, + RandomizeCpuPpuAlignment = 0x800000000000000, + ForceMaxSpeed = 0x4000000000000000, ConsoleMode = 0x8000000000000000, }; @@ -648,13 +650,9 @@ private: uint32_t _rewindBufferSize = 300; - bool _hasOverclock = false; - uint32_t _overclockRate = 100; - bool _overclockAdjustApu = true; bool _disableOverclocking = false; uint32_t _extraScanlinesBeforeNmi = 0; uint32_t _extraScanlinesAfterNmi = 0; - double _effectiveOverclockRate = 100; OverscanDimensions _overscan; VideoFilterType _videoFilterType = VideoFilterType::None; @@ -1041,55 +1039,11 @@ public: } uint32_t GetEmulationSpeed(bool ignoreTurbo = false); - - void UpdateEffectiveOverclockRate() - { - if(_disableOverclocking) { - _effectiveOverclockRate = 100; - } else { - _effectiveOverclockRate = _overclockRate; - } - _hasOverclock = _effectiveOverclockRate != 100; - _audioSettingsChanged = true; - } - + void DisableOverclocking(bool disabled) { if(_disableOverclocking != disabled) { _disableOverclocking = disabled; - UpdateEffectiveOverclockRate(); - } - } - - uint32_t GetOverclockRateSetting() - { - return _overclockRate; - } - - bool HasOverclock() - { - return _hasOverclock; - } - - double GetOverclockRate() - { - return _effectiveOverclockRate; - } - - bool GetOverclockAdjustApu() - { - return _overclockAdjustApu; - } - - void SetOverclockRate(uint32_t overclockRate, bool adjustApu) - { - if(_overclockRate != overclockRate || _overclockAdjustApu != adjustApu) { - _overclockRate = overclockRate; - _overclockAdjustApu = adjustApu; - - UpdateEffectiveOverclockRate(); - - MessageManager::DisplayMessage("ClockRate", std::to_string((uint32_t)GetOverclockRate()) + "%"); } } @@ -1112,8 +1066,6 @@ public: _extraScanlinesBeforeNmi = extraScanlinesBeforeNmi; _extraScanlinesAfterNmi = extraScanlinesAfterNmi; - - UpdateEffectiveOverclockRate(); } } diff --git a/Core/MemoryAccessCounter.cpp b/Core/MemoryAccessCounter.cpp index 3d6352f4..dd670414 100644 --- a/Core/MemoryAccessCounter.cpp +++ b/Core/MemoryAccessCounter.cpp @@ -197,7 +197,7 @@ void MemoryAccessCounter::GetNametableChangedData(bool ntChangedData[]) uint64_t cpuCycle = _debugger->GetConsole()->GetCpu()->GetCycleCount(); NesModel model = _debugger->GetConsole()->GetModel(); double frameRate = model == NesModel::NTSC ? 60.1 : 50.01; - double overclockRate = _debugger->GetConsole()->GetPpu()->GetOverclockRate() * (_debugger->GetConsole()->GetSettings()->GetOverclockRate() / 100); + double overclockRate = _debugger->GetConsole()->GetPpu()->GetOverclockRate() * 100; int32_t cyclesPerFrame = (int32_t)(_debugger->GetConsole()->GetCpu()->GetClockRate(model) / frameRate * overclockRate); for(int i = 0; i < 0x1000; i++) { diff --git a/Core/MesenMovie.cpp b/Core/MesenMovie.cpp index 0c477ae6..35fc57dc 100644 --- a/Core/MesenMovie.cpp +++ b/Core/MesenMovie.cpp @@ -247,14 +247,6 @@ void MesenMovie::ApplySettings() settings->SetZapperDetectionRadius(LoadInt(_settings, MovieKeys::ZapperDetectionRadius)); - uint32_t cpuClockRate = LoadInt(_settings, MovieKeys::CpuClockRate); - if(cpuClockRate != 100) { - bool adjustApu = LoadBool(_settings, MovieKeys::OverclockAdjustApu); - settings->SetOverclockRate(cpuClockRate, adjustApu); - } else { - settings->SetOverclockRate(100, true); - } - settings->SetPpuNmiConfig( LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi), LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi) diff --git a/Core/MovieRecorder.cpp b/Core/MovieRecorder.cpp index 908963a8..cf0ba710 100644 --- a/Core/MovieRecorder.cpp +++ b/Core/MovieRecorder.cpp @@ -107,14 +107,10 @@ void MovieRecorder::GetGameSettings(stringstream &out) WriteString(out, MovieKeys::ExpansionDevice, ExpansionPortDeviceNames[(int)settings->GetExpansionDevice()]); } - WriteInt(out, MovieKeys::CpuClockRate, settings->GetOverclockRateSetting()); WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, settings->GetPpuExtraScanlinesBeforeNmi()); WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, settings->GetPpuExtraScanlinesAfterNmi()); WriteInt(out, MovieKeys::InputPollScanline, settings->GetInputPollScanline()); - if(settings->GetOverclockRateSetting() != 100) { - WriteBool(out, MovieKeys::OverclockAdjustApu, settings->GetOverclockAdjustApu()); - } WriteBool(out, MovieKeys::DisablePpu2004Reads, settings->CheckFlag(EmulationFlags::DisablePpu2004Reads)); WriteBool(out, MovieKeys::DisablePaletteRead, settings->CheckFlag(EmulationFlags::DisablePaletteRead)); WriteBool(out, MovieKeys::DisableOamAddrBug, settings->CheckFlag(EmulationFlags::DisableOamAddrBug)); diff --git a/Core/MovieRecorder.h b/Core/MovieRecorder.h index 5d1072d6..8a3f22b4 100644 --- a/Core/MovieRecorder.h +++ b/Core/MovieRecorder.h @@ -71,10 +71,8 @@ namespace MovieKeys constexpr const char* Controller3 = "Controller3"; constexpr const char* Controller4 = "Controller4"; constexpr const char* ExpansionDevice = "ExpansionDevice"; - constexpr const char* CpuClockRate = "CpuClockRate"; constexpr const char* ExtraScanlinesBeforeNmi = "ExtraScanlinesBeforeNmi"; constexpr const char* ExtraScanlinesAfterNmi = "ExtraScanlinesAfterNmi"; - constexpr const char* OverclockAdjustApu = "OverclockAdjustApu"; constexpr const char* DisablePpu2004Reads = "DisablePpu2004Reads"; constexpr const char* DisablePaletteRead = "DisablePaletteRead"; constexpr const char* DisableOamAddrBug = "DisableOamAddrBug"; diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 4c547393..9904d254 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -15,6 +15,8 @@ PPU::PPU(shared_ptr console) { _console = console; + _masterClock = 0; + _masterClockDivider = 4; _settings = _console->GetSettings(); _outputBuffers[0] = new uint16_t[256 * 240]; @@ -42,7 +44,7 @@ PPU::~PPU() void PPU::Reset() { - _cyclesNeeded = 0; + _preventVblFlag = false; _needStateUpdate = false; _prevRenderingEnabled = false; @@ -73,7 +75,6 @@ void PPU::Reset() _oamCopyDone = false; _renderingEnabled = false; _prevRenderingEnabled = false; - _cyclesNeeded = 0.0; memset(_hasSprite, 0, sizeof(_hasSprite)); memset(_spriteTiles, 0, sizeof(_spriteTiles)); @@ -118,18 +119,21 @@ void PPU::SetNesModel(NesModel model) _vblankEnd = 260; _standardNmiScanline = 241; _standardVblankEnd = 260; + _masterClockDivider = 4; break; case NesModel::PAL: _nmiScanline = 241; _vblankEnd = 310; _standardNmiScanline = 241; _standardVblankEnd = 310; + _masterClockDivider = 5; break; case NesModel::Dendy: _nmiScanline = 291; _vblankEnd = 310; _standardNmiScanline = 291; _standardVblankEnd = 310; + _masterClockDivider = 5; break; } @@ -561,8 +565,7 @@ void PPU::UpdateStatusFlag() _console->GetCpu()->ClearNmiFlag(); if(_cycle == 0) { - //"Reading one PPU clock before reads it as clear and never sets the flag or generates NMI for that frame. " - _state.Status = ((uint8_t)_statusFlags.SpriteOverflow << 5) | ((uint8_t)_statusFlags.Sprite0Hit << 6); + _preventVblFlag = true; } } } @@ -639,8 +642,8 @@ void PPU::WriteVram(uint16_t addr, uint8_t value) void PPU::LoadTileInfo() { if(IsRenderingEnabled()) { - switch((_cycle - 1) & 0x07) { - case 0: { + switch(_cycle & 0x07) { + case 1: { _previousTile = _currentTile; _currentTile = _nextTile; @@ -653,18 +656,18 @@ void PPU::LoadTileInfo() break; } - case 2: { + case 3: { uint8_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02); _nextTile.PaletteOffset = ((ReadVram(GetAttributeAddr()) >> shift) & 0x03) << 2; break; } - case 3: + case 5: _nextTile.LowByte = ReadVram(_nextTile.TileAddr); _nextTile.AbsoluteTileAddr = _console->GetMapper()->ToAbsoluteChrAddress(_nextTile.TileAddr); break; - case 5: + case 7: _nextTile.HighByte = ReadVram(_nextTile.TileAddr + 8); break; } @@ -864,10 +867,10 @@ void PPU::UpdateGrayscaleAndIntensifyBits() int pixelNumber; if(_scanline >= 240) { pixelNumber = 61439; - } else if(_cycle < 3) { + } else if(_cycle < 4) { pixelNumber = (_scanline << 8) - 1; } else if(_cycle <= 258) { - pixelNumber = (_scanline << 8) + _cycle - 3; + pixelNumber = (_scanline << 8) + _cycle - 4; } else { pixelNumber = (_scanline << 8) + 255; } @@ -932,7 +935,7 @@ void PPU::ProcessScanline() //"OAMADDR is set to 0 during each of ticks 257-320 (the sprite tile loading interval) of the pre-render and visible scanlines." (When rendering) _state.SpriteRamAddr = 0; - if((_cycle - 260) % 8 == 0) { + if((_cycle - 261) % 8 == 0) { //Cycle 260, 268, etc. This is an approximation (each tile is actually loaded in 8 steps (e.g from 257 to 264)) LoadSpriteTileInfo(); } else if((_cycle - 257) % 8 == 0) { @@ -1157,7 +1160,6 @@ void PPU::BeginVBlank() void PPU::TriggerNmi() { - _statusFlags.VerticalBlank = true; if(_flags.VBlank) { _console->GetCpu()->SetNmiFlag(); } @@ -1222,7 +1224,6 @@ void PPU::Exec() SendFrame(); _frameCount++; } else if(_scanline == _nmiScanline) { - BeginVBlank(); } } else { //Cycle > 0 @@ -1231,6 +1232,12 @@ void PPU::Exec() _console->DebugProcessPpuCycle(); if(_scanline < 240) { ProcessScanline(); + } else if(_cycle == 1 && _scanline == _nmiScanline) { + if(!_preventVblFlag) { + _statusFlags.VerticalBlank = true; + BeginVBlank(); + } + _preventVblFlag = false; } else if(_nesModel == NesModel::PAL && _scanline >= _palSpriteEvalScanline) { //"On a PAL machine, because of its extended vertical blank, the PPU begins refreshing OAM roughly 21 scanlines after NMI[2], to prevent it //from decaying during the longer hiatus of rendering. Additionally, it will continue to refresh during the visible portion of the screen @@ -1299,31 +1306,6 @@ void PPU::UpdateState() } } -void PPU::ProcessCpuClock() -{ - if(!_settings->HasOverclock()) { - Exec(); - Exec(); - Exec(); - if(_nesModel == NesModel::PAL && _console->GetCpu()->GetCycleCount() % 5 == 0) { - //PAL PPU runs 3.2 clocks for every CPU clock, so we need to run an extra clock every 5 CPU clocks - Exec(); - } - } else { - if(_nesModel == NesModel::PAL) { - //PAL PPU runs 3.2 clocks for every CPU clock, so we need to run an extra clock every 5 CPU clocks - _cyclesNeeded += 3.2 / (_settings->GetOverclockRate() / 100.0); - } else { - _cyclesNeeded += 3.0 / (_settings->GetOverclockRate() / 100.0); - } - - while(_cyclesNeeded >= 1.0) { - Exec(); - _cyclesNeeded--; - } - } -} - uint8_t* PPU::GetSpriteRam() { //Used by debugger @@ -1371,8 +1353,8 @@ void PPU::StreamState(bool saving) _nextTile.PaletteOffset, _nextTile.TileAddr, _previousTile.LowByte, _previousTile.HighByte, _previousTile.PaletteOffset, _spriteIndex, _spriteCount, _secondaryOAMAddr, _sprite0Visible, _oamCopybuffer, _spriteInRange, _sprite0Added, _spriteAddrH, _spriteAddrL, _oamCopyDone, _nesModel, _prevRenderingEnabled, _renderingEnabled, _openBus, _ignoreVramRead, paletteRam, spriteRam, secondarySpriteRam, - openBusDecayStamp, _cyclesNeeded, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay, - _needStateUpdate, _ppuBusAddress); + openBusDecayStamp, disablePpu2004Reads, disablePaletteRead, disableOamAddrBug, _overflowBugCounter, _updateVramAddr, _updateVramAddrDelay, + _needStateUpdate, _ppuBusAddress, _preventVblFlag, _masterClock); for(int i = 0; i < 64; i++) { Stream(_spriteTiles[i].SpriteX, _spriteTiles[i].LowByte, _spriteTiles[i].HighByte, _spriteTiles[i].PaletteOffset, _spriteTiles[i].HorizontalMirror, _spriteTiles[i].BackgroundPriority); diff --git a/Core/PPU.h b/Core/PPU.h index 23ecabdd..97f37167 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -36,6 +36,8 @@ class PPU : public IMemoryHandler, public Snapshotable int32_t _scanline; uint32_t _cycle; uint32_t _frameCount; + uint64_t _masterClock; + uint8_t _masterClockDivider; uint8_t _memoryReadBuffer; uint8_t _paletteRAM[0x20]; @@ -53,7 +55,7 @@ class PPU : public IMemoryHandler, public Snapshotable uint16_t _vblankEnd; uint16_t _nmiScanline; uint16_t _palSpriteEvalScanline; - + PPUControlFlags _flags; PPUStatusFlags _statusFlags; @@ -91,8 +93,7 @@ class PPU : public IMemoryHandler, public Snapshotable bool _needStateUpdate; bool _renderingEnabled; bool _prevRenderingEnabled; - - double _cyclesNeeded; + bool _preventVblFlag; uint16_t _updateVramAddr; uint8_t _updateVramAddrDelay; @@ -199,7 +200,7 @@ class PPU : public IMemoryHandler, public Snapshotable double GetOverclockRate(); void Exec(); - void ProcessCpuClock(); + __forceinline void Run(uint64_t runTo); uint32_t GetFrameCount() { @@ -240,3 +241,11 @@ class PPU : public IMemoryHandler, public Snapshotable return _currentOutputBuffer[y << 8 | x]; } }; + +void PPU::Run(uint64_t runTo) +{ + while(_masterClock + _masterClockDivider <= runTo) { + Exec(); + _masterClock += _masterClockDivider; + } +} diff --git a/Core/SaveStateManager.cpp b/Core/SaveStateManager.cpp index e92da3bf..ff0225d7 100644 --- a/Core/SaveStateManager.cpp +++ b/Core/SaveStateManager.cpp @@ -129,7 +129,7 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired) } stream.read((char*)&fileFormatVersion, sizeof(fileFormatVersion)); - if(fileFormatVersion <= 10) { + if(fileFormatVersion <= 11) { MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion"); return false; } else { diff --git a/Core/SaveStateManager.h b/Core/SaveStateManager.h index cc868bc1..07d43629 100644 --- a/Core/SaveStateManager.h +++ b/Core/SaveStateManager.h @@ -14,7 +14,7 @@ private: string GetStateFilepath(int stateIndex); public: - static constexpr uint32_t FileFormatVersion = 11; + static constexpr uint32_t FileFormatVersion = 12; SaveStateManager(shared_ptr console); diff --git a/Core/SoundMixer.cpp b/Core/SoundMixer.cpp index 10dd2fc3..cef52765 100644 --- a/Core/SoundMixer.cpp +++ b/Core/SoundMixer.cpp @@ -203,9 +203,6 @@ void SoundMixer::SetNesModel(NesModel model) void SoundMixer::UpdateRates(bool forceUpdate) { uint32_t newRate = _console->GetCpu()->GetClockRate(_model); - if(!_settings->GetOverclockAdjustApu()) { - newRate = (uint32_t)(newRate * (double)_settings->GetOverclockRate() / 100); - } if(_settings->CheckFlag(EmulationFlags::IntegerFpsMode)) { //Adjust sample rate when running at 60.0 fps instead of 60.1 diff --git a/GUI.NET/Config/EmulationInfo.cs b/GUI.NET/Config/EmulationInfo.cs index 2787113f..29434f63 100644 --- a/GUI.NET/Config/EmulationInfo.cs +++ b/GUI.NET/Config/EmulationInfo.cs @@ -22,12 +22,10 @@ namespace Mesen.GUI.Config public bool EnableOamDecay = false; public bool UseNes101Hvc101Behavior = false; public bool EnableMapperRandomPowerOnState = false; + public bool RandomizeCpuPpuAlignment = false; public bool UseAlternativeMmc3Irq = false; - [MinMax(1, 1000)] public UInt32 OverclockRate = 100; - public bool OverclockAdjustApu = true; - [MinMax(0, 1000)] public UInt32 PpuExtraScanlinesBeforeNmi = 0; [MinMax(0, 1000)] public UInt32 PpuExtraScanlinesAfterNmi = 0; @@ -60,8 +58,8 @@ namespace Mesen.GUI.Config InteropEmu.SetFlag(EmulationFlags.EnableOamDecay, emulationInfo.EnableOamDecay); InteropEmu.SetFlag(EmulationFlags.UseNes101Hvc101Behavior, emulationInfo.UseNes101Hvc101Behavior); InteropEmu.SetFlag(EmulationFlags.RandomizeMapperPowerOnState, emulationInfo.EnableMapperRandomPowerOnState); + InteropEmu.SetFlag(EmulationFlags.RandomizeCpuPpuAlignment, emulationInfo.RandomizeCpuPpuAlignment); - InteropEmu.SetOverclockRate(emulationInfo.OverclockRate, emulationInfo.OverclockAdjustApu); InteropEmu.SetPpuNmiConfig(emulationInfo.PpuExtraScanlinesBeforeNmi, emulationInfo.PpuExtraScanlinesAfterNmi); InteropEmu.SetRamPowerOnState(emulationInfo.RamPowerOnState); diff --git a/GUI.NET/Dependencies/resources.ca.xml b/GUI.NET/Dependencies/resources.ca.xml index 1258b9a0..5b0847a2 100644 --- a/GUI.NET/Dependencies/resources.ca.xml +++ b/GUI.NET/Dependencies/resources.ca.xml @@ -321,6 +321,7 @@ No reestableixis la PPU al reiniciar la consola (comportament de la Famicom) Simula el comportament de la NES/HVC-101 (Top-loader / AV Famicom) Randomize power-on state for mappers + Randomize power-on CPU/PPU alignment Forçament Forçament de CPU diff --git a/GUI.NET/Dependencies/resources.en.xml b/GUI.NET/Dependencies/resources.en.xml index e127eac8..77f0288f 100644 --- a/GUI.NET/Dependencies/resources.en.xml +++ b/GUI.NET/Dependencies/resources.en.xml @@ -320,6 +320,7 @@ Do not reset PPU when resetting console (Famicom behavior) Use NES/HVC-101 (Top-loader / AV Famicom) behavior Randomize power-on state for mappers + Randomize power-on CPU/PPU alignment Default power on state for RAM: diff --git a/GUI.NET/Dependencies/resources.es.xml b/GUI.NET/Dependencies/resources.es.xml index 47bf4c4a..3eef6967 100644 --- a/GUI.NET/Dependencies/resources.es.xml +++ b/GUI.NET/Dependencies/resources.es.xml @@ -320,6 +320,7 @@ No resetear la PPU al resetear la consola (Famicom) Simular el comportamiento de NES/HVC-101 (Top-loader / AV Famicom) Aleatorizar el estado de encendido para los mappers + Randomize power-on CPU/PPU alignment Overclocking Overclocking de CPU diff --git a/GUI.NET/Dependencies/resources.fr.xml b/GUI.NET/Dependencies/resources.fr.xml index 0d0ed5da..358b90ed 100644 --- a/GUI.NET/Dependencies/resources.fr.xml +++ b/GUI.NET/Dependencies/resources.fr.xml @@ -320,6 +320,7 @@ Ne pas faire un reset du PPU lors du reset de la console (Famicom) Simuler le comportement du NES/HVC-101 (Top Loader / AV Famicom) Démarrer le jeu avec le mapper dans un état aléatoire + Démarrer le jeu avec un alignement CPU/PPU aléatoire État initial de la mémoire au démarrage : diff --git a/GUI.NET/Dependencies/resources.it.xml b/GUI.NET/Dependencies/resources.it.xml index 04cb2cfc..a7753ff6 100644 --- a/GUI.NET/Dependencies/resources.it.xml +++ b/GUI.NET/Dependencies/resources.it.xml @@ -320,6 +320,7 @@ Non resettare la PPU quando resetti la console (Famicom) Usa NES/HVC-101 (Top-loader / AV Famicom) Valori casuali durante accensione per i mapper + Randomize power-on CPU/PPU alignment Stato di accensione predefinito per la RAM: diff --git a/GUI.NET/Dependencies/resources.ja.xml b/GUI.NET/Dependencies/resources.ja.xml index 4d20aa51..beae0834 100644 --- a/GUI.NET/Dependencies/resources.ja.xml +++ b/GUI.NET/Dependencies/resources.ja.xml @@ -318,7 +318,8 @@ PPUのパレットラムを読み込み不可能にする ゲーム機をリセットする時に、PPUをリセットしない (ファミコン同様) AV仕様ファミコン(HVC-101とNES-101)の仕様を使う - ランダムな状態でゲームを起動する + ランダムなMapper状態でゲームを起動する + ランダムなCPU/PPUアラインメントでゲームを起動する 起動時のメモリの状態 : diff --git a/GUI.NET/Dependencies/resources.pt.xml b/GUI.NET/Dependencies/resources.pt.xml index b7ebbfea..064f17b3 100644 --- a/GUI.NET/Dependencies/resources.pt.xml +++ b/GUI.NET/Dependencies/resources.pt.xml @@ -320,6 +320,7 @@ Não reiniciar a PPU ao reiniciar o console (Comportamento do Famicom) Utilizar comportamento do NES/HVC-101 (Top-loader / AV Famicom) Aleatorizar o estado de ativação para mapeadores + Randomize power-on CPU/PPU alignment Estado inicial da RAM durante o início: diff --git a/GUI.NET/Dependencies/resources.ru.xml b/GUI.NET/Dependencies/resources.ru.xml index 774c32bd..84836a5d 100644 --- a/GUI.NET/Dependencies/resources.ru.xml +++ b/GUI.NET/Dependencies/resources.ru.xml @@ -320,6 +320,7 @@ Do not reset PPU when resetting console (Famicom behavior) Use NES/HVC-101 (Top-loader / AV Famicom) behavior Randomize power-on state for mappers + Randomize power-on CPU/PPU alignment Разгон Разгон CPU diff --git a/GUI.NET/Dependencies/resources.uk.xml b/GUI.NET/Dependencies/resources.uk.xml index cecb6c7d..19a9f725 100644 --- a/GUI.NET/Dependencies/resources.uk.xml +++ b/GUI.NET/Dependencies/resources.uk.xml @@ -320,6 +320,7 @@ Не перезавантажити PPU при скиданні консолі (Famicom) Імітувати поведінку NES/HVC-101 (Top-loader / AV Famicom) поведінку Почати гру з маппером у випадковому стані + Randomize power-on CPU/PPU alignment Розгін Розгін CPU diff --git a/GUI.NET/Dependencies/resources.zh.xml b/GUI.NET/Dependencies/resources.zh.xml index a4d62b86..431ab59c 100644 --- a/GUI.NET/Dependencies/resources.zh.xml +++ b/GUI.NET/Dependencies/resources.zh.xml @@ -346,6 +346,7 @@ PPU 不随主机重设 (FC 真机) 模拟 HVC-101/NES-101 (Top-Loader/带 AV 端子主机) 随机 Mapper 开机状态 + Randomize power-on CPU/PPU alignment 默认开机 RAM 状态: 超频 diff --git a/GUI.NET/Forms/Config/frmEmulationConfig.Designer.cs b/GUI.NET/Forms/Config/frmEmulationConfig.Designer.cs index 19729b4e..9d7e16b7 100644 --- a/GUI.NET/Forms/Config/frmEmulationConfig.Designer.cs +++ b/GUI.NET/Forms/Config/frmEmulationConfig.Designer.cs @@ -48,6 +48,7 @@ namespace Mesen.GUI.Forms.Config this.lblRewindSpeedHint = new System.Windows.Forms.Label(); this.tpgAdvanced = new System.Windows.Forms.TabPage(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.chkRandomizeCpuPpuAlignment = new Mesen.GUI.Controls.ctrlRiskyOption(); this.lblMiscSettings = new System.Windows.Forms.Label(); this.chkMapperRandomPowerOnState = new Mesen.GUI.Controls.ctrlRiskyOption(); this.chkEnableOamDecay = new Mesen.GUI.Controls.ctrlRiskyOption(); @@ -69,15 +70,9 @@ namespace Mesen.GUI.Forms.Config this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel(); this.lblEffectiveClockRateDendy = new System.Windows.Forms.Label(); this.lblEffectiveClockRateValueDendy = new System.Windows.Forms.Label(); - this.chkOverclockAdjustApu = new System.Windows.Forms.CheckBox(); this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel(); this.lblEffectiveClockRatePal = new System.Windows.Forms.Label(); this.lblEffectiveClockRateValuePal = new System.Windows.Forms.Label(); - this.grpOverclocking = new System.Windows.Forms.GroupBox(); - this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); - this.lblClockRate = new System.Windows.Forms.Label(); - this.nudOverclockRate = new Mesen.GUI.Controls.MesenNumericUpDown(); - this.lblClockRatePercent = 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(); @@ -105,8 +100,6 @@ namespace Mesen.GUI.Forms.Config this.tableLayoutPanel3.SuspendLayout(); this.flowLayoutPanel4.SuspendLayout(); this.flowLayoutPanel3.SuspendLayout(); - this.grpOverclocking.SuspendLayout(); - this.tableLayoutPanel2.SuspendLayout(); this.grpPpuTiming.SuspendLayout(); this.tableLayoutPanel5.SuspendLayout(); this.flowLayoutPanel2.SuspendLayout(); @@ -115,7 +108,7 @@ namespace Mesen.GUI.Forms.Config // // baseConfigPanel // - this.baseConfigPanel.Location = new System.Drawing.Point(0, 321); + this.baseConfigPanel.Location = new System.Drawing.Point(0, 338); this.baseConfigPanel.Size = new System.Drawing.Size(533, 29); // // tabMain @@ -127,7 +120,7 @@ namespace Mesen.GUI.Forms.Config this.tabMain.Location = new System.Drawing.Point(0, 0); this.tabMain.Name = "tabMain"; this.tabMain.SelectedIndex = 0; - this.tabMain.Size = new System.Drawing.Size(533, 321); + this.tabMain.Size = new System.Drawing.Size(533, 338); this.tabMain.TabIndex = 2; // // tpgGeneral @@ -136,7 +129,7 @@ namespace Mesen.GUI.Forms.Config this.tpgGeneral.Location = new System.Drawing.Point(4, 22); this.tpgGeneral.Name = "tpgGeneral"; this.tpgGeneral.Padding = new System.Windows.Forms.Padding(3); - this.tpgGeneral.Size = new System.Drawing.Size(525, 295); + this.tpgGeneral.Size = new System.Drawing.Size(525, 312); this.tpgGeneral.TabIndex = 0; this.tpgGeneral.Text = "General"; this.tpgGeneral.UseVisualStyleBackColor = true; @@ -161,7 +154,7 @@ namespace Mesen.GUI.Forms.Config this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel4.Size = new System.Drawing.Size(519, 289); + this.tableLayoutPanel4.Size = new System.Drawing.Size(519, 306); this.tableLayoutPanel4.TabIndex = 0; // // flowLayoutPanel9 @@ -356,7 +349,7 @@ namespace Mesen.GUI.Forms.Config 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(525, 295); + this.tpgAdvanced.Size = new System.Drawing.Size(525, 312); this.tpgAdvanced.TabIndex = 1; this.tpgAdvanced.Text = "Advanced"; this.tpgAdvanced.UseVisualStyleBackColor = true; @@ -365,25 +358,27 @@ namespace Mesen.GUI.Forms.Config // this.tableLayoutPanel1.ColumnCount = 1; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel1.Controls.Add(this.lblMiscSettings, 0, 4); + this.tableLayoutPanel1.Controls.Add(this.chkRandomizeCpuPpuAlignment, 0, 3); + this.tableLayoutPanel1.Controls.Add(this.lblMiscSettings, 0, 5); this.tableLayoutPanel1.Controls.Add(this.chkMapperRandomPowerOnState, 0, 2); this.tableLayoutPanel1.Controls.Add(this.chkEnableOamDecay, 0, 1); - this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel8, 0, 3); - this.tableLayoutPanel1.Controls.Add(this.chkDisablePaletteRead, 0, 10); - this.tableLayoutPanel1.Controls.Add(this.chkDisableOamAddrBug, 0, 8); - this.tableLayoutPanel1.Controls.Add(this.chkDisablePpuReset, 0, 7); - this.tableLayoutPanel1.Controls.Add(this.chkDisablePpu2004Reads, 0, 9); - this.tableLayoutPanel1.Controls.Add(this.chkUseNes101Hvc101Behavior, 0, 6); - this.tableLayoutPanel1.Controls.Add(this.chkAllowInvalidInput, 0, 11); - this.tableLayoutPanel1.Controls.Add(this.chkUseAlternativeMmc3Irq, 0, 5); + this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel8, 0, 4); + this.tableLayoutPanel1.Controls.Add(this.chkDisablePaletteRead, 0, 11); + this.tableLayoutPanel1.Controls.Add(this.chkDisableOamAddrBug, 0, 9); + this.tableLayoutPanel1.Controls.Add(this.chkDisablePpuReset, 0, 8); + this.tableLayoutPanel1.Controls.Add(this.chkDisablePpu2004Reads, 0, 10); + this.tableLayoutPanel1.Controls.Add(this.chkUseNes101Hvc101Behavior, 0, 7); + this.tableLayoutPanel1.Controls.Add(this.chkAllowInvalidInput, 0, 12); + this.tableLayoutPanel1.Controls.Add(this.chkUseAlternativeMmc3Irq, 0, 6); this.tableLayoutPanel1.Controls.Add(this.lblDeveloperSettings, 0, 0); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 13; + this.tableLayoutPanel1.RowCount = 14; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); @@ -394,15 +389,28 @@ namespace Mesen.GUI.Forms.Config this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(519, 289); + this.tableLayoutPanel1.Size = new System.Drawing.Size(519, 306); this.tableLayoutPanel1.TabIndex = 0; // + // chkRandomizeCpuPpuAlignment + // + this.chkRandomizeCpuPpuAlignment.AutoSize = true; + this.chkRandomizeCpuPpuAlignment.Checked = false; + this.chkRandomizeCpuPpuAlignment.Dock = System.Windows.Forms.DockStyle.Fill; + this.chkRandomizeCpuPpuAlignment.Location = new System.Drawing.Point(10, 66); + this.chkRandomizeCpuPpuAlignment.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); + this.chkRandomizeCpuPpuAlignment.MinimumSize = new System.Drawing.Size(0, 23); + this.chkRandomizeCpuPpuAlignment.Name = "chkRandomizeCpuPpuAlignment"; + this.chkRandomizeCpuPpuAlignment.Size = new System.Drawing.Size(509, 23); + this.chkRandomizeCpuPpuAlignment.TabIndex = 36; + this.chkRandomizeCpuPpuAlignment.Text = "Randomize power-on CPU/PPU alignment"; + // // lblMiscSettings // this.lblMiscSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.lblMiscSettings.AutoSize = true; this.lblMiscSettings.ForeColor = System.Drawing.SystemColors.GrayText; - this.lblMiscSettings.Location = new System.Drawing.Point(0, 98); + this.lblMiscSettings.Location = new System.Drawing.Point(0, 118); this.lblMiscSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 2); this.lblMiscSettings.Name = "lblMiscSettings"; this.lblMiscSettings.Size = new System.Drawing.Size(115, 13); @@ -439,7 +447,7 @@ namespace Mesen.GUI.Forms.Config this.flowLayoutPanel8.Controls.Add(this.lblRamPowerOnState); this.flowLayoutPanel8.Controls.Add(this.cboRamPowerOnState); this.flowLayoutPanel8.Dock = System.Windows.Forms.DockStyle.Fill; - this.flowLayoutPanel8.Location = new System.Drawing.Point(7, 66); + this.flowLayoutPanel8.Location = new System.Drawing.Point(7, 86); this.flowLayoutPanel8.Margin = new System.Windows.Forms.Padding(7, 0, 0, 0); this.flowLayoutPanel8.Name = "flowLayoutPanel8"; this.flowLayoutPanel8.Size = new System.Drawing.Size(512, 27); @@ -468,7 +476,7 @@ namespace Mesen.GUI.Forms.Config // this.chkDisablePaletteRead.Checked = false; this.chkDisablePaletteRead.Dock = System.Windows.Forms.DockStyle.Fill; - this.chkDisablePaletteRead.Location = new System.Drawing.Point(10, 228); + this.chkDisablePaletteRead.Location = new System.Drawing.Point(10, 248); this.chkDisablePaletteRead.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkDisablePaletteRead.MinimumSize = new System.Drawing.Size(0, 21); this.chkDisablePaletteRead.Name = "chkDisablePaletteRead"; @@ -480,7 +488,7 @@ namespace Mesen.GUI.Forms.Config // this.chkDisableOamAddrBug.Checked = false; this.chkDisableOamAddrBug.Dock = System.Windows.Forms.DockStyle.Fill; - this.chkDisableOamAddrBug.Location = new System.Drawing.Point(10, 182); + this.chkDisableOamAddrBug.Location = new System.Drawing.Point(10, 202); this.chkDisableOamAddrBug.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkDisableOamAddrBug.MinimumSize = new System.Drawing.Size(0, 21); this.chkDisableOamAddrBug.Name = "chkDisableOamAddrBug"; @@ -492,7 +500,7 @@ namespace Mesen.GUI.Forms.Config // this.chkDisablePpuReset.Checked = false; this.chkDisablePpuReset.Dock = System.Windows.Forms.DockStyle.Fill; - this.chkDisablePpuReset.Location = new System.Drawing.Point(10, 159); + this.chkDisablePpuReset.Location = new System.Drawing.Point(10, 179); this.chkDisablePpuReset.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkDisablePpuReset.MinimumSize = new System.Drawing.Size(0, 21); this.chkDisablePpuReset.Name = "chkDisablePpuReset"; @@ -504,7 +512,7 @@ namespace Mesen.GUI.Forms.Config // this.chkDisablePpu2004Reads.Checked = false; this.chkDisablePpu2004Reads.Dock = System.Windows.Forms.DockStyle.Fill; - this.chkDisablePpu2004Reads.Location = new System.Drawing.Point(10, 205); + this.chkDisablePpu2004Reads.Location = new System.Drawing.Point(10, 225); this.chkDisablePpu2004Reads.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkDisablePpu2004Reads.MinimumSize = new System.Drawing.Size(0, 21); this.chkDisablePpu2004Reads.Name = "chkDisablePpu2004Reads"; @@ -515,7 +523,7 @@ namespace Mesen.GUI.Forms.Config // chkUseNes101Hvc101Behavior // this.chkUseNes101Hvc101Behavior.AutoSize = true; - this.chkUseNes101Hvc101Behavior.Location = new System.Drawing.Point(13, 139); + this.chkUseNes101Hvc101Behavior.Location = new System.Drawing.Point(13, 159); this.chkUseNes101Hvc101Behavior.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3); this.chkUseNes101Hvc101Behavior.Name = "chkUseNes101Hvc101Behavior"; this.chkUseNes101Hvc101Behavior.Size = new System.Drawing.Size(292, 17); @@ -528,7 +536,7 @@ namespace Mesen.GUI.Forms.Config this.chkAllowInvalidInput.AutoSize = true; this.chkAllowInvalidInput.Checked = false; this.chkAllowInvalidInput.Dock = System.Windows.Forms.DockStyle.Fill; - this.chkAllowInvalidInput.Location = new System.Drawing.Point(10, 251); + this.chkAllowInvalidInput.Location = new System.Drawing.Point(10, 271); this.chkAllowInvalidInput.Margin = new System.Windows.Forms.Padding(10, 0, 0, 0); this.chkAllowInvalidInput.MinimumSize = new System.Drawing.Size(0, 23); this.chkAllowInvalidInput.Name = "chkAllowInvalidInput"; @@ -539,7 +547,7 @@ namespace Mesen.GUI.Forms.Config // chkUseAlternativeMmc3Irq // this.chkUseAlternativeMmc3Irq.AutoSize = true; - this.chkUseAlternativeMmc3Irq.Location = new System.Drawing.Point(13, 116); + this.chkUseAlternativeMmc3Irq.Location = new System.Drawing.Point(13, 136); this.chkUseAlternativeMmc3Irq.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3); this.chkUseAlternativeMmc3Irq.Name = "chkUseAlternativeMmc3Irq"; this.chkUseAlternativeMmc3Irq.Size = new System.Drawing.Size(197, 17); @@ -566,7 +574,7 @@ namespace Mesen.GUI.Forms.Config this.tpgOverclocking.Location = new System.Drawing.Point(4, 22); this.tpgOverclocking.Name = "tpgOverclocking"; this.tpgOverclocking.Padding = new System.Windows.Forms.Padding(3); - this.tpgOverclocking.Size = new System.Drawing.Size(525, 295); + this.tpgOverclocking.Size = new System.Drawing.Size(525, 312); this.tpgOverclocking.TabIndex = 2; this.tpgOverclocking.Text = "Overclocking"; this.tpgOverclocking.UseVisualStyleBackColor = true; @@ -576,7 +584,7 @@ namespace Mesen.GUI.Forms.Config 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(12, 15); + this.picHint.Location = new System.Drawing.Point(12, 24); 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); @@ -591,9 +599,7 @@ namespace Mesen.GUI.Forms.Config this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel3.Controls.Add(this.lblOverclockHint, 0, 0); this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel4, 0, 5); - this.tableLayoutPanel3.Controls.Add(this.chkOverclockAdjustApu, 0, 6); this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel3, 0, 4); - this.tableLayoutPanel3.Controls.Add(this.grpOverclocking, 0, 2); this.tableLayoutPanel3.Controls.Add(this.grpPpuTiming, 0, 1); this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel2, 0, 3); this.tableLayoutPanel3.Controls.Add(this.flowLayoutPanel7, 0, 7); @@ -609,7 +615,7 @@ namespace Mesen.GUI.Forms.Config 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.Size = new System.Drawing.Size(519, 289); + this.tableLayoutPanel3.Size = new System.Drawing.Size(519, 306); this.tableLayoutPanel3.TabIndex = 0; // // lblOverclockHint @@ -619,7 +625,7 @@ namespace Mesen.GUI.Forms.Config 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(513, 41); + this.lblOverclockHint.Size = new System.Drawing.Size(517, 41); this.lblOverclockHint.TabIndex = 1; this.lblOverclockHint.Text = resources.GetString("lblOverclockHint.Text"); // @@ -628,7 +634,7 @@ namespace Mesen.GUI.Forms.Config this.flowLayoutPanel4.Controls.Add(this.lblEffectiveClockRateDendy); this.flowLayoutPanel4.Controls.Add(this.lblEffectiveClockRateValueDendy); this.flowLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill; - this.flowLayoutPanel4.Location = new System.Drawing.Point(0, 203); + this.flowLayoutPanel4.Location = new System.Drawing.Point(0, 152); this.flowLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel4.Name = "flowLayoutPanel4"; this.flowLayoutPanel4.Size = new System.Drawing.Size(519, 20); @@ -653,22 +659,12 @@ namespace Mesen.GUI.Forms.Config this.lblEffectiveClockRateValueDendy.TabIndex = 1; this.lblEffectiveClockRateValueDendy.Text = "100%"; // - // chkOverclockAdjustApu - // - this.chkOverclockAdjustApu.AutoSize = true; - this.chkOverclockAdjustApu.Location = new System.Drawing.Point(3, 226); - this.chkOverclockAdjustApu.Name = "chkOverclockAdjustApu"; - this.chkOverclockAdjustApu.Size = new System.Drawing.Size(401, 17); - this.chkOverclockAdjustApu.TabIndex = 10; - this.chkOverclockAdjustApu.Text = "Do not overclock APU (prevents sound pitch changes caused by overclocking)"; - this.chkOverclockAdjustApu.UseVisualStyleBackColor = true; - // // flowLayoutPanel3 // this.flowLayoutPanel3.Controls.Add(this.lblEffectiveClockRatePal); this.flowLayoutPanel3.Controls.Add(this.lblEffectiveClockRateValuePal); this.flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; - this.flowLayoutPanel3.Location = new System.Drawing.Point(0, 186); + this.flowLayoutPanel3.Location = new System.Drawing.Point(0, 135); this.flowLayoutPanel3.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel3.Name = "flowLayoutPanel3"; this.flowLayoutPanel3.Size = new System.Drawing.Size(519, 17); @@ -693,89 +689,6 @@ namespace Mesen.GUI.Forms.Config this.lblEffectiveClockRateValuePal.TabIndex = 1; this.lblEffectiveClockRateValuePal.Text = "100%"; // - // grpOverclocking - // - this.grpOverclocking.Controls.Add(this.tableLayoutPanel2); - this.grpOverclocking.Dock = System.Windows.Forms.DockStyle.Fill; - this.grpOverclocking.Location = new System.Drawing.Point(3, 121); - this.grpOverclocking.Name = "grpOverclocking"; - this.grpOverclocking.Size = new System.Drawing.Size(513, 45); - this.grpOverclocking.TabIndex = 6; - this.grpOverclocking.TabStop = false; - this.grpOverclocking.Text = "Overclocking"; - // - // tableLayoutPanel2 - // - this.tableLayoutPanel2.ColumnCount = 4; - this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel2.Controls.Add(this.lblClockRate, 0, 0); - this.tableLayoutPanel2.Controls.Add(this.nudOverclockRate, 1, 0); - this.tableLayoutPanel2.Controls.Add(this.lblClockRatePercent, 2, 0); - this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 16); - this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); - this.tableLayoutPanel2.Name = "tableLayoutPanel2"; - this.tableLayoutPanel2.RowCount = 1; - this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel2.Size = new System.Drawing.Size(507, 26); - this.tableLayoutPanel2.TabIndex = 0; - // - // lblClockRate - // - this.lblClockRate.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.lblClockRate.AutoSize = true; - this.lblClockRate.Location = new System.Drawing.Point(3, 7); - this.lblClockRate.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); - this.lblClockRate.Name = "lblClockRate"; - this.lblClockRate.Size = new System.Drawing.Size(107, 13); - this.lblClockRate.TabIndex = 1; - this.lblClockRate.Text = "Clock Rate Multiplier:"; - // - // nudOverclockRate - // - this.nudOverclockRate.DecimalPlaces = 0; - this.nudOverclockRate.Increment = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.nudOverclockRate.Location = new System.Drawing.Point(110, 3); - this.nudOverclockRate.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3); - this.nudOverclockRate.Maximum = new decimal(new int[] { - 1000, - 0, - 0, - 0}); - this.nudOverclockRate.MaximumSize = new System.Drawing.Size(10000, 20); - this.nudOverclockRate.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.nudOverclockRate.MinimumSize = new System.Drawing.Size(0, 21); - this.nudOverclockRate.Name = "nudOverclockRate"; - this.nudOverclockRate.Size = new System.Drawing.Size(46, 21); - this.nudOverclockRate.TabIndex = 1; - this.nudOverclockRate.Value = new decimal(new int[] { - 100, - 0, - 0, - 0}); - // - // lblClockRatePercent - // - this.lblClockRatePercent.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.lblClockRatePercent.AutoSize = true; - this.lblClockRatePercent.Location = new System.Drawing.Point(156, 7); - this.lblClockRatePercent.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); - this.lblClockRatePercent.Name = "lblClockRatePercent"; - this.lblClockRatePercent.Size = new System.Drawing.Size(90, 13); - this.lblClockRatePercent.TabIndex = 1; - this.lblClockRatePercent.Text = "% (Default: 100%)"; - // // grpPpuTiming // this.grpPpuTiming.Controls.Add(this.tableLayoutPanel5); @@ -893,7 +806,7 @@ namespace Mesen.GUI.Forms.Config this.flowLayoutPanel2.Controls.Add(this.lblEffectiveClockRate); this.flowLayoutPanel2.Controls.Add(this.lblEffectiveClockRateValue); this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; - this.flowLayoutPanel2.Location = new System.Drawing.Point(0, 169); + this.flowLayoutPanel2.Location = new System.Drawing.Point(0, 118); this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel2.Name = "flowLayoutPanel2"; this.flowLayoutPanel2.Size = new System.Drawing.Size(519, 17); @@ -923,10 +836,10 @@ namespace Mesen.GUI.Forms.Config this.flowLayoutPanel7.Controls.Add(this.chkShowLagCounter); this.flowLayoutPanel7.Controls.Add(this.btnResetLagCounter); this.flowLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill; - this.flowLayoutPanel7.Location = new System.Drawing.Point(0, 246); + this.flowLayoutPanel7.Location = new System.Drawing.Point(0, 172); this.flowLayoutPanel7.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel7.Name = "flowLayoutPanel7"; - this.flowLayoutPanel7.Size = new System.Drawing.Size(519, 43); + this.flowLayoutPanel7.Size = new System.Drawing.Size(519, 134); this.flowLayoutPanel7.TabIndex = 12; // // chkShowLagCounter @@ -961,12 +874,12 @@ namespace Mesen.GUI.Forms.Config this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoSize = true; - this.ClientSize = new System.Drawing.Size(533, 350); + this.ClientSize = new System.Drawing.Size(533, 367); this.Controls.Add(this.tabMain); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; this.MinimizeBox = false; - this.MinimumSize = new System.Drawing.Size(503, 322); + this.MinimumSize = new System.Drawing.Size(503, 367); this.Name = "frmEmulationConfig"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Emulation Settings"; @@ -992,14 +905,10 @@ namespace Mesen.GUI.Forms.Config this.tpgOverclocking.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.picHint)).EndInit(); this.tableLayoutPanel3.ResumeLayout(false); - this.tableLayoutPanel3.PerformLayout(); this.flowLayoutPanel4.ResumeLayout(false); this.flowLayoutPanel4.PerformLayout(); this.flowLayoutPanel3.ResumeLayout(false); this.flowLayoutPanel3.PerformLayout(); - this.grpOverclocking.ResumeLayout(false); - this.tableLayoutPanel2.ResumeLayout(false); - this.tableLayoutPanel2.PerformLayout(); this.grpPpuTiming.ResumeLayout(false); this.tableLayoutPanel5.ResumeLayout(false); this.tableLayoutPanel5.PerformLayout(); @@ -1020,11 +929,6 @@ namespace Mesen.GUI.Forms.Config private ctrlRiskyOption chkAllowInvalidInput; private System.Windows.Forms.TabPage tpgOverclocking; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; - private System.Windows.Forms.GroupBox grpOverclocking; - private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; - private System.Windows.Forms.Label lblClockRate; - private MesenNumericUpDown nudOverclockRate; - private System.Windows.Forms.Label lblClockRatePercent; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel6; private MesenNumericUpDown nudEmulationSpeed; @@ -1042,7 +946,6 @@ namespace Mesen.GUI.Forms.Config private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3; private System.Windows.Forms.Label lblEffectiveClockRatePal; private System.Windows.Forms.Timer tmrUpdateClockRate; - private System.Windows.Forms.CheckBox chkOverclockAdjustApu; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel4; private System.Windows.Forms.Label lblEffectiveClockRateDendy; private System.Windows.Forms.Label lblEffectiveClockRateValueDendy; @@ -1072,5 +975,6 @@ namespace Mesen.GUI.Forms.Config private System.Windows.Forms.CheckBox chkUseNes101Hvc101Behavior; private System.Windows.Forms.Label lblDeveloperSettings; private System.Windows.Forms.Label lblMiscSettings; + private ctrlRiskyOption chkRandomizeCpuPpuAlignment; } } \ No newline at end of file diff --git a/GUI.NET/Forms/Config/frmEmulationConfig.cs b/GUI.NET/Forms/Config/frmEmulationConfig.cs index fb12bd34..47cae146 100644 --- a/GUI.NET/Forms/Config/frmEmulationConfig.cs +++ b/GUI.NET/Forms/Config/frmEmulationConfig.cs @@ -37,9 +37,7 @@ namespace Mesen.GUI.Forms.Config AddBinding("EnableOamDecay", chkEnableOamDecay); AddBinding("UseNes101Hvc101Behavior", chkUseNes101Hvc101Behavior); AddBinding("EnableMapperRandomPowerOnState", chkMapperRandomPowerOnState); - - AddBinding("OverclockRate", nudOverclockRate); - AddBinding("OverclockAdjustApu", chkOverclockAdjustApu); + AddBinding("RandomizeCpuPpuAlignment", chkRandomizeCpuPpuAlignment); AddBinding("PpuExtraScanlinesBeforeNmi", nudExtraScanlinesBeforeNmi); AddBinding("PpuExtraScanlinesAfterNmi", nudExtraScanlinesAfterNmi); @@ -57,9 +55,9 @@ namespace Mesen.GUI.Forms.Config private void tmrUpdateClockRate_Tick(object sender, EventArgs e) { - decimal clockRateMultiplierNtsc = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 262)); - decimal clockRateMultiplierPal = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312)); - decimal clockRateMultiplierDendy = (nudOverclockRate.Value * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312)); + decimal clockRateMultiplierNtsc = (100 * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 262)); + decimal clockRateMultiplierPal = (100 * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312)); + decimal clockRateMultiplierDendy = (100 * (1 + (nudExtraScanlinesAfterNmi.Value + nudExtraScanlinesBeforeNmi.Value) / 312)); lblEffectiveClockRateValue.Text = (1789773 * clockRateMultiplierNtsc / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierNtsc).ToString() + "%)"; lblEffectiveClockRateValuePal.Text = (1662607 * clockRateMultiplierPal / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierPal).ToString() + "%)"; lblEffectiveClockRateValueDendy.Text = (1773448 * clockRateMultiplierDendy / 100000000).ToString("#.####") + " mhz (" + ((int)clockRateMultiplierDendy).ToString() + "%)"; diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 7d8d9957..15c6961c 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -201,7 +201,6 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void SetTurboRewindSpeed(UInt32 turboSpeed, UInt32 rewindSpeed); [DllImport(DLLPath)] public static extern void SetRewindBufferSize(UInt32 seconds); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsRewinding(); - [DllImport(DLLPath)] public static extern void SetOverclockRate(UInt32 overclockRate, [MarshalAs(UnmanagedType.I1)]bool adjustApu); [DllImport(DLLPath)] public static extern void SetPpuNmiConfig(UInt32 extraScanlinesBeforeNmi, UInt32 extraScanlineAfterNmi); [DllImport(DLLPath)] public static extern void SetOverscanDimensions(UInt32 left, UInt32 right, UInt32 top, UInt32 bottom); [DllImport(DLLPath)] public static extern void SetVideoScale(double scale, ConsoleId consoleId = ConsoleId.Master); @@ -1704,6 +1703,8 @@ namespace Mesen.GUI VsDualMuteMaster = 0x200000000000000, VsDualMuteSlave = 0x400000000000000, + RandomizeCpuPpuAlignment = 0x800000000000000, + ForceMaxSpeed = 0x4000000000000000, ConsoleMode = 0x8000000000000000, } diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index ec485225..9632ea6e 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -640,7 +640,6 @@ namespace InteropEmu { shared_ptr rewindManager = _console->GetRewindManager(); return rewindManager ? rewindManager->IsRewinding() : false; } - DllExport void __stdcall SetOverclockRate(uint32_t overclockRate, bool adjustApu) { _settings->SetOverclockRate(overclockRate, adjustApu); } DllExport void __stdcall SetPpuNmiConfig(uint32_t extraScanlinesBeforeNmi, uint32_t extraScanlinesAfterNmi) { _settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi); } DllExport void __stdcall SetVideoScale(double scale, ConsoleId consoleId) { GetConsoleById(consoleId)->GetSettings()->SetVideoScale(scale); } DllExport void __stdcall SetScreenRotation(uint32_t angle) { _settings->SetScreenRotation(angle); } diff --git a/Libretro/libretro.cpp b/Libretro/libretro.cpp index c6f02d6c..412b8fca 100644 --- a/Libretro/libretro.cpp +++ b/Libretro/libretro.cpp @@ -13,6 +13,7 @@ #include "../Core/EmulationSettings.h" #include "../Core/CheatManager.h" #include "../Core/HdData.h" +#include "../Core/SaveStateManager.h" #include "../Core/DebuggerTypes.h" #include "../Core/GameDatabase.h" #include "../Utilities/FolderUtilities.h" @@ -658,7 +659,7 @@ extern "C" { RETRO_API bool retro_serialize(void *data, size_t size) { std::stringstream ss; - _console->SaveState(ss); + _console->GetSaveStateManager()->SaveState(ss); string saveStateData = ss.str(); memset(data, 0, size); @@ -669,8 +670,9 @@ extern "C" { RETRO_API bool retro_unserialize(const void *data, size_t size) { - _console->LoadState((uint8_t*)data, (uint32_t)size); - return true; + std::stringstream ss; + ss.write((char*)data, size); + return _console->GetSaveStateManager()->LoadState(ss, false); } RETRO_API void retro_cheat_reset() @@ -996,7 +998,7 @@ extern "C" { //Retroarch doesn't like this for netplay or rewinding - it requires the states to always be the exact same size //So we need to send a large enough size to Retroarch to ensure Mesen's state will always fit within that buffer. std::stringstream ss; - _console->SaveState(ss); + _console->GetSaveStateManager()->SaveState(ss); //Round up to the next 1kb multiple _saveStateSize = ((ss.str().size() * 2) + 0x400) & ~0x3FF;