From 30d28c83a5ea5f0419a30f60ba68443f7c0d02b7 Mon Sep 17 00:00:00 2001 From: Sour Date: Tue, 24 Dec 2024 22:40:54 +0900 Subject: [PATCH] SNES: Performance improvements This makes performance equal or slightly better to what it was before all of the recent accuracy improvements --- Core/SNES/InternalRegisters.cpp | 4 ++- Core/SNES/SnesCpu.cpp | 6 +++- Core/SNES/SnesCpu.h | 2 +- Core/SNES/SnesDmaController.h | 2 ++ Core/SNES/SnesMemoryManager.cpp | 64 +++++++++++++++++++++++++++++++-- Core/SNES/SnesMemoryManager.h | 9 ++++- Core/SNES/Spc.Instructions.cpp | 44 +++++++++++++++++++++++ Core/SNES/Spc.cpp | 42 ---------------------- Core/SNES/Spc.h | 6 ++-- 9 files changed, 127 insertions(+), 52 deletions(-) diff --git a/Core/SNES/InternalRegisters.cpp b/Core/SNES/InternalRegisters.cpp index 3a8cf8f0c..74542de2e 100644 --- a/Core/SNES/InternalRegisters.cpp +++ b/Core/SNES/InternalRegisters.cpp @@ -408,5 +408,7 @@ void InternalRegisters::Serialize(Serializer& s) SV(_hCounter); SV(_vCounter); - SV(_irqEnabled); + if(!s.IsSaving()) { + _irqEnabled = _state.EnableHorizontalIrq || _state.EnableVerticalIrq; + } } diff --git a/Core/SNES/SnesCpu.cpp b/Core/SNES/SnesCpu.cpp index edeb1adce..e8441981c 100644 --- a/Core/SNES/SnesCpu.cpp +++ b/Core/SNES/SnesCpu.cpp @@ -121,7 +121,11 @@ void SnesCpu::IdleTakeBranch() void SnesCpu::ProcessCpuCycle() { _state.CycleCount++; - _state.IrqLock = _dmaController->ProcessPendingTransfers(); + if(_dmaController->HasPendingTransfer()){ + _state.IrqLock = _dmaController->ProcessPendingTransfers(); + } else { + _state.IrqLock = false; + } DetectNmiSignalEdge(); } diff --git a/Core/SNES/SnesCpu.h b/Core/SNES/SnesCpu.h index d34deb9b7..754d82f96 100644 --- a/Core/SNES/SnesCpu.h +++ b/Core/SNES/SnesCpu.h @@ -323,7 +323,7 @@ class SnesCpu : public ISerializable void AddrMode_StkRel(); void AddrMode_StkRelIndIdxY(); - void RunOp(); + __forceinline void RunOp(); __noinline void ProcessHaltedState(); __forceinline void CheckForInterrupts(); diff --git a/Core/SNES/SnesDmaController.h b/Core/SNES/SnesDmaController.h index e1b4e6a95..d65164cad 100644 --- a/Core/SNES/SnesDmaController.h +++ b/Core/SNES/SnesDmaController.h @@ -49,6 +49,8 @@ class SnesDmaController final : public ISerializable void BeginHdmaTransfer(); void BeginHdmaInit(); + __forceinline bool HasPendingTransfer() { return _needToProcess; } + bool ProcessPendingTransfers(); void Write(uint16_t addr, uint8_t value); diff --git a/Core/SNES/SnesMemoryManager.cpp b/Core/SNES/SnesMemoryManager.cpp index dc259e1fa..4b49954ad 100644 --- a/Core/SNES/SnesMemoryManager.cpp +++ b/Core/SNES/SnesMemoryManager.cpp @@ -21,7 +21,10 @@ void SnesMemoryManager::Initialize(SnesConsole *console) { _masterClock = 0; _openBus = 0; + _cpuSpeed = 8; + UpdateExecCallbacks(); + _console = console; _emu = console->GetEmulator(); _regs = console->GetInternalRegisters(); @@ -125,6 +128,33 @@ void SnesMemoryManager::GenerateMasterClockTable() } } +template +void SnesMemoryManager::IncMasterClock() +{ + if constexpr(clocks == 2) { + Exec(); + } else if constexpr(clocks == 4) { + Exec(); + Exec(); + } else if constexpr(clocks == 6) { + Exec(); + Exec(); + Exec(); + } else if constexpr(clocks == 8) { + Exec(); + Exec(); + Exec(); + Exec(); + } else if constexpr(clocks == 12) { + Exec(); + Exec(); + Exec(); + Exec(); + Exec(); + Exec(); + } +} + void SnesMemoryManager::IncMasterClock4() { Exec(); @@ -236,7 +266,7 @@ void SnesMemoryManager::ProcessEvent() uint8_t SnesMemoryManager::Read(uint32_t addr, MemoryOperationType type) { - IncrementMasterClockValue(_cpuSpeed - 4); + (this->*_execRead)(); uint8_t value; IMemoryHandler *handler = _mappings.GetHandler(addr); @@ -311,7 +341,8 @@ void SnesMemoryManager::PeekBlock(uint32_t addr, uint8_t *dest) void SnesMemoryManager::Write(uint32_t addr, uint8_t value, MemoryOperationType type) { - IncrementMasterClockValue(_cpuSpeed); + (this->*_execWrite)(); + if(_emu->ProcessMemoryWrite(addr, value, type)) { IMemoryHandler* handler = _mappings.GetHandler(addr); if(handler) { @@ -389,7 +420,30 @@ uint8_t SnesMemoryManager::GetCpuSpeed() void SnesMemoryManager::SetCpuSpeed(uint8_t speed) { - _cpuSpeed = speed; + if(_cpuSpeed != speed) { + _cpuSpeed = speed; + UpdateExecCallbacks(); + } +} + +void SnesMemoryManager::UpdateExecCallbacks() +{ + switch(_cpuSpeed) { + case 6: + _execRead = &SnesMemoryManager::IncMasterClock<2>; + _execWrite = &SnesMemoryManager::IncMasterClock<6>; + break; + + case 8: + _execRead = &SnesMemoryManager::IncMasterClock<4>; + _execWrite = &SnesMemoryManager::IncMasterClock<8>; + break; + + case 12: + _execRead = &SnesMemoryManager::IncMasterClock<8>; + _execWrite = &SnesMemoryManager::IncMasterClock<12>; + break; + } } MemoryType SnesMemoryManager::GetMemoryTypeBusA() @@ -420,4 +474,8 @@ void SnesMemoryManager::Serialize(Serializer &s) SV(_memTypeBusA); SV(_nextEvent); SV(_nextEventClock); SVArray(_workRam, SnesMemoryManager::WorkRamSize); SV(_registerHandlerB); + + if(!s.IsSaving()) { + UpdateExecCallbacks(); + } } diff --git a/Core/SNES/SnesMemoryManager.h b/Core/SNES/SnesMemoryManager.h index 6ad8f100a..00f9b96e1 100644 --- a/Core/SNES/SnesMemoryManager.h +++ b/Core/SNES/SnesMemoryManager.h @@ -59,7 +59,14 @@ class SnesMemoryManager : public ISerializable vector> _workRamHandlers; uint8_t _masterClockTable[0x800] = {}; - void Exec(); + typedef void(SnesMemoryManager::*Func)(); + Func _execRead = nullptr; + Func _execWrite = nullptr; + + template void IncMasterClock(); + void UpdateExecCallbacks(); + + __forceinline void Exec(); void ProcessEvent(); diff --git a/Core/SNES/Spc.Instructions.cpp b/Core/SNES/Spc.Instructions.cpp index 6e2d01296..294b43bdb 100644 --- a/Core/SNES/Spc.Instructions.cpp +++ b/Core/SNES/Spc.Instructions.cpp @@ -1,7 +1,51 @@ #include "pch.h" #include "SNES/Spc.h" +#include "SNES/SnesMemoryManager.h" +#include "Shared/Emulator.h" #include "Utilities/HexUtilities.h" +void Spc::Run() +{ + if(!_enabled) { + //Used to temporarily disable the SPC when overclocking is enabled + return; + } else if(_state.StopState != SnesCpuStopState::Running) { + //STOP or SLEEP were executed - execution is stopped forever. + _emu->ProcessHaltedCpu(); + return; + } + + uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio); + while(_state.Cycle < targetCycle) { + ProcessCycle(); + } +} + +void Spc::ProcessCycle() +{ + if(_opStep == SpcOpStep::ReadOpCode) { +#ifndef DUMMYSPC + _emu->ProcessInstruction(); +#endif + _opCode = GetOpCode(); + _opStep = SpcOpStep::Addressing; + _opSubStep = 0; + } else { + Exec(); + } + + if(_pendingCpuRegUpdate) { + //There appears to be a delay between the moment the CPU writes + //to a register and when the SPC can see the new value + //This makes the SPC see the newly written value after a 1 SPC cycle delay + //This allows Kishin Douji Zenki to boot without freezing + for(int i = 0; i < 4; i++) { + _state.CpuRegs[i] = _state.NewCpuRegs[i]; + } + _pendingCpuRegUpdate = false; + } +} + void Spc::EndOp() { _opStep = SpcOpStep::ReadOpCode; diff --git a/Core/SNES/Spc.cpp b/Core/SNES/Spc.cpp index b62febe2b..c1c830a69 100644 --- a/Core/SNES/Spc.cpp +++ b/Core/SNES/Spc.cpp @@ -378,48 +378,6 @@ void Spc::DspWriteRam(uint16_t addr, uint8_t value) _ram[addr] = value; } -void Spc::Run() -{ - if(!_enabled) { - //Used to temporarily disable the SPC when overclocking is enabled - return; - } else if(_state.StopState != SnesCpuStopState::Running) { - //STOP or SLEEP were executed - execution is stopped forever. - _emu->ProcessHaltedCpu(); - return; - } - - uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio); - while(_state.Cycle < targetCycle) { - ProcessCycle(); - } -} - -void Spc::ProcessCycle() -{ - if(_opStep == SpcOpStep::ReadOpCode) { -#ifndef DUMMYSPC - _emu->ProcessInstruction(); -#endif - _opCode = GetOpCode(); - _opStep = SpcOpStep::Addressing; - _opSubStep = 0; - } else { - Exec(); - } - - if(_pendingCpuRegUpdate) { - //There appears to be a delay between the moment the CPU writes - //to a register and when the SPC can see the new value - //This makes the SPC see the newly written value after a 1 SPC cycle delay - //This allows Kishin Douji Zenki to boot without freezing - for(int i = 0; i < 4; i++) { - _state.CpuRegs[i] = _state.NewCpuRegs[i]; - } - _pendingCpuRegUpdate = false; - } -} - void Spc::ProcessEndFrame() { Run(); diff --git a/Core/SNES/Spc.h b/Core/SNES/Spc.h index e04916a4c..9b907c3d6 100644 --- a/Core/SNES/Spc.h +++ b/Core/SNES/Spc.h @@ -279,11 +279,11 @@ class Spc : public ISerializable uint8_t GetOpCode(); uint8_t ReadOperandByte(); - void IncCycleCount(int32_t addr); + __forceinline void IncCycleCount(int32_t addr); void EndOp(); void EndAddr(); - void ProcessCycle(); - void Exec(); + __forceinline void ProcessCycle(); + __forceinline void Exec(); void UpdateClockRatio(); void ExitExecLoop();