From 760f777f5a08854e56057e3604d662b6d1ef07cf Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 20 Aug 2012 13:49:12 +0200 Subject: [PATCH 01/31] Simulate a small delay on GC Memcard operations This was not needed for most games before because the external exception was itself delayed. aram-dma-fixes changed that and made the external exception happen a lot quicker, breaking games that relied on the memcard operations delay. Fixes issue 5583. --- .../Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp | 38 +++++++++++++------ .../Core/Core/Src/HW/EXI_DeviceMemoryCard.h | 11 +++++- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp index 6f4bc502b6..4705e849be 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp @@ -47,13 +47,22 @@ void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate) pThis->Flush(); } +void CEXIMemoryCard::CmdDoneCallback(u64 userdata, int cyclesLate) +{ + int card_index = (int)userdata; + CEXIMemoryCard* pThis = (CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index); + if (pThis) + pThis->CmdDone(); +} + CEXIMemoryCard::CEXIMemoryCard(const int index) : card_index(index) , m_bDirty(false) { m_strFilename = (card_index == 0) ? SConfig::GetInstance().m_strMemoryCardA : SConfig::GetInstance().m_strMemoryCardB; // we're potentially leaking events here, since there's no UnregisterEvent until emu shutdown, but I guess it's inconsequential - et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardA" : "memcardB", FlushCallback); + et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardFlushA" : "memcardFlushB", FlushCallback); + et_cmd_done = CoreTiming::RegisterEvent((card_index == 0) ? "memcardDoneA" : "memcardDoneB", CmdDoneCallback); interruptSwitch = 0; m_bInterruptSet = 0; @@ -175,6 +184,21 @@ bool CEXIMemoryCard::IsPresent() return true; } +void CEXIMemoryCard::CmdDone() +{ + status |= MC_STATUS_READY; + status &= ~MC_STATUS_BUSY; + + m_bInterruptSet = 1; + m_bDirty = true; +} + +void CEXIMemoryCard::CmdDoneLater(u64 cycles) +{ + CoreTiming::RemoveEvent(et_cmd_done); + CoreTiming::ScheduleEvent(cycles, et_cmd_done, (u64)card_index); +} + void CEXIMemoryCard::SetCS(int cs) { // So that memory card won't be invalidated during flushing @@ -198,11 +222,7 @@ void CEXIMemoryCard::SetCS(int cs) //??? - status |= MC_STATUS_READY; - status &= ~MC_STATUS_BUSY; - - m_bInterruptSet = 1; - m_bDirty = true; + CmdDoneLater(5000); } break; @@ -229,11 +249,7 @@ void CEXIMemoryCard::SetCS(int cs) address = (address & ~0x1FF) | ((address+1) & 0x1FF); } - status |= MC_STATUS_READY; - status &= ~MC_STATUS_BUSY; - - m_bInterruptSet = 1; - m_bDirty = true; + CmdDoneLater(5000); } // Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h index dfdc6349aa..33b94ca530 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h @@ -47,9 +47,18 @@ private: // through the userdata parameter, so that it can then call Flush on the right card. static void FlushCallback(u64 userdata, int cyclesLate); + // Scheduled when a command that required delayed end signaling is done. + static void CmdDoneCallback(u64 userdata, int cyclesLate); + // Flushes the memory card contents to disk. void Flush(bool exiting = false); + // Signals that the command that was previously executed is now done. + void CmdDone(); + + // Variant of CmdDone which schedules an event later in the future to complete the command. + void CmdDoneLater(u64 cycles); + enum { cmdNintendoID = 0x00, @@ -71,7 +80,7 @@ private: std::string m_strFilename; int card_index; - int et_this_card; + int et_this_card, et_cmd_done; //! memory card state // STATE_TO_SAVE From 1b6240f7f972caef7c53a7a51ff71b4653b3f00a Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 25 Jan 2013 20:04:31 +1100 Subject: [PATCH 02/31] Changed cmdidle to be idle on breakpoint. Added low watermark interrupts generated by the gather pipe. Fixes Gladius from not booting. Fixes issue 5518. --- .../Core/VideoCommon/Src/CommandProcessor.cpp | 22 ++++++++++++++----- .../Core/VideoCommon/Src/CommandProcessor.h | 1 + Source/Core/VideoCommon/Src/Fifo.cpp | 4 +++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index a4dddf537f..1c0b43a835 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -57,6 +57,7 @@ static bool bProcessFifoAllDistance = false; volatile bool isPossibleWaitingSetDrawDone = false; volatile bool isHiWatermarkActive = false; +volatile bool isLoWatermarkActive = false; volatile bool interruptSet= false; volatile bool interruptWaiting= false; volatile bool interruptTokenWaiting = false; @@ -88,6 +89,7 @@ void DoState(PointerWrap &p) p.Do(bProcessFifoToLoWatermark); p.Do(bProcessFifoAllDistance); p.Do(isHiWatermarkActive); + p.Do(isLoWatermarkActive); p.Do(isPossibleWaitingSetDrawDone); p.Do(interruptSet); p.Do(interruptWaiting); @@ -119,7 +121,7 @@ void Init() m_tokenReg = 0; memset(&fifo,0,sizeof(fifo)); - fifo.CPCmdIdle = 1 ; + fifo.CPCmdIdle = 1; fifo.CPReadIdle = 1; fifo.bFF_Breakpoint = 0; fifo.bFF_HiWatermark = 0; @@ -136,6 +138,7 @@ void Init() bProcessFifoAllDistance = false; isPossibleWaitingSetDrawDone = false; isHiWatermarkActive = false; + isLoWatermarkActive = false; et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); } @@ -294,7 +297,6 @@ void Read16(u16& _rReturnValue, const u32 _Address) void Write16(const u16 _Value, const u32 _Address) { - INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address); switch (_Address & 0xFFF) @@ -405,7 +407,8 @@ void Write16(const u16 _Value, const u32 _Address) { GPFifo::ResetGatherPipe(); ResetVideoBuffer(); - }else + } + else { ResetVideoBuffer(); } @@ -514,7 +517,9 @@ void AbortFrame() void SetOverflowStatusFromGatherPipe() { fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); + fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); isHiWatermarkActive = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && m_CPCtrlReg.GPReadEnable; + isLoWatermarkActive = fifo.bFF_LoWatermark && fifo.bFF_LoWatermarkInt && m_CPCtrlReg.GPReadEnable; if (isHiWatermarkActive) { @@ -522,6 +527,12 @@ void SetOverflowStatusFromGatherPipe() INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); } + else if (isLoWatermarkActive) + { + interruptSet = true; + INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); + ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); + } } void SetCpStatus() @@ -562,7 +573,8 @@ void SetCpStatus() bool interrupt = (bpInt || ovfInt || undfInt) && m_CPCtrlReg.GPReadEnable; - isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable; + isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable; + isLoWatermarkActive = undfInt && m_CPCtrlReg.GPReadEnable; if (interrupt != interruptSet && !interruptWaiting) { @@ -618,7 +630,7 @@ void SetCpStatusRegister() // Here always there is one fifo attached to the GPU m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; - m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance; + m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || AtBreakpoint(); m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index 5d31453537..d70bcf475c 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -31,6 +31,7 @@ namespace CommandProcessor extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread. extern volatile bool isPossibleWaitingSetDrawDone; //This one is used for sync gfx thread and emulator thread. extern volatile bool isHiWatermarkActive; +extern volatile bool isLoWatermarkActive; extern volatile bool interruptSet; extern volatile bool interruptWaiting; extern volatile bool interruptTokenWaiting; diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index 50aa21ebbf..e21cc94da2 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -155,6 +155,7 @@ void RunGpuLoop() VideoFifo_CheckAsyncRequest(); CommandProcessor::SetCpStatus(); + // check if we are able to run this buffer while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) { @@ -172,7 +173,7 @@ void RunGpuLoop() _assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 , "Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce inestabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32); - ReadDataFromFifo(uData, 32); + ReadDataFromFifo(uData, 32); OpcodeDecoder_Run(g_bSkipCurrentFrame); @@ -180,6 +181,7 @@ void RunGpuLoop() Common::AtomicAdd(fifo.CPReadWriteDistance, -32); if((GetVideoBufferEndPtr() - g_pVideoData) == 0) Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer); + CommandProcessor::SetCpStatus(); // This call is pretty important in DualCore mode and must be called in the FIFO Loop. From 867bfaa696807b3cb99b7af6cb9302f39d355ba3 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Feb 2013 20:50:40 +1100 Subject: [PATCH 03/31] Changed cmdidle to match the isGpuReadingData flag. Fixes the random freezes in The Last Story. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index 1c0b43a835..12b11d7ea5 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -630,7 +630,7 @@ void SetCpStatusRegister() // Here always there is one fifo attached to the GPU m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; - m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || AtBreakpoint(); + m_CPStatusReg.CommandIdle = !fifo.isGpuReadingData; m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; From 9a4e9da7c25f1aa72f5b47e9914f464e966ad7f9 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 3 Feb 2013 18:05:46 +1100 Subject: [PATCH 04/31] Set the token and finish interrupt signal from the video thread. This fixes the inconsistent state caused by lag from the scheduler. Fixes Rayman 3: Hoodlum Havoc and Shamu's Deep Sea Adventures. Fixes issue 5401. Fixes issue 5589. --- .../Core/VideoCommon/Src/CommandProcessor.cpp | 8 +-- .../Core/VideoCommon/Src/CommandProcessor.h | 2 +- Source/Core/VideoCommon/Src/Fifo.cpp | 2 +- Source/Core/VideoCommon/Src/PixelEngine.cpp | 54 +++++++------------ Source/Core/VideoCommon/Src/PixelEngine.h | 1 - 5 files changed, 22 insertions(+), 45 deletions(-) diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index 12b11d7ea5..7e976f256e 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -464,7 +464,7 @@ void STACKALIGN GatherPipeBursted() } if (IsOnThread()) - SetOverflowStatusFromGatherPipe(); + SetWatermarkFromGatherPipe(); // update the fifo-pointer if (fifo.CPWritePointer >= fifo.CPEnd) @@ -514,7 +514,7 @@ void AbortFrame() } -void SetOverflowStatusFromGatherPipe() +void SetWatermarkFromGatherPipe() { fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); @@ -634,10 +634,6 @@ void SetCpStatusRegister() m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; - // HACK to compensate for slow response to PE interrupts in Time Splitters: Future Perfect - if (IsOnThread()) - PixelEngine::ResumeWaitingForPEInterrupt(); - INFO_LOG(COMMANDPROCESSOR,"\t Read from STATUS_REGISTER : %04x", m_CPStatusReg.Hex); DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s" , m_CPStatusReg.Breakpoint ? "ON" : "OFF" diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index d70bcf475c..84b8fa7d66 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -162,7 +162,7 @@ bool AllowIdleSkipping(); void SetCpClearRegister(); void SetCpControlRegister(); void SetCpStatusRegister(); -void SetOverflowStatusFromGatherPipe(); +void SetWatermarkFromGatherPipe(); void ProcessFifoToLoWatermark(); void ProcessFifoAllDistance(); void ProcessFifoEvents(); diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index e21cc94da2..d4b4db3e42 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -157,7 +157,7 @@ void RunGpuLoop() CommandProcessor::SetCpStatus(); // check if we are able to run this buffer - while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) + while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint()) { if (!GpuRunningState) break; diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index a488e52b42..6d4659e309 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -359,14 +359,14 @@ void UpdateInterrupts() void UpdateTokenInterrupt(bool active) { - ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active); - interruptSetToken = active; + ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active); + interruptSetToken = active; } void UpdateFinishInterrupt(bool active) { - ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); - interruptSetFinish = active; + ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); + interruptSetFinish = active; } // TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate). @@ -376,20 +376,14 @@ void UpdateFinishInterrupt(bool active) // Called only if BPMEM_PE_TOKEN_INT_ID is ack by GP void SetToken_OnMainThread(u64 userdata, int cyclesLate) { - //if (userdata >> 16) - //{ - g_bSignalTokenInterrupt = true; - //_dbg_assert_msg_(PIXELENGINE, (CommandProcessor::fifo.PEToken == (userdata&0xFFFF)), "WTF? BPMEM_PE_TOKEN_INT_ID's token != BPMEM_PE_TOKEN_ID's token" ); - INFO_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken); - UpdateInterrupts(); - CommandProcessor::interruptTokenWaiting = false; - IncrementCheckContextId(); - //} + INFO_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken); + UpdateInterrupts(); + CommandProcessor::interruptTokenWaiting = false; + IncrementCheckContextId(); } void SetFinish_OnMainThread(u64 userdata, int cyclesLate) { - g_bSignalFinishInterrupt = 1; UpdateInterrupts(); CommandProcessor::interruptFinishWaiting = false; CommandProcessor::isPossibleWaitingSetDrawDone = false; @@ -399,23 +393,20 @@ void SetFinish_OnMainThread(u64 userdata, int cyclesLate) // THIS IS EXECUTED FROM VIDEO THREAD void SetToken(const u16 _token, const int _bSetTokenAcknowledge) { - // TODO?: set-token-value and set-token-INT could be merged since set-token-INT own the token value. + // we do it directly from videoThread because of + // Super Monkey Ball + // XXX: No 16-bit atomic store available, so cheat and use 32-bit. + // That's what we've always done. We're counting on fifo.PEToken to be + // 4-byte padded. + Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token); + if (_bSetTokenAcknowledge) // set token INT { - - Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token); CommandProcessor::interruptTokenWaiting = true; CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); + g_bSignalTokenInterrupt = true; } - else // set token value - { - // we do it directly from videoThread because of - // Super Monkey Ball - // XXX: No 16-bit atomic store available, so cheat and use 32-bit. - // That's what we've always done. We're counting on fifo.PEToken to be - // 4-byte padded. - Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token); - } + IncrementCheckContextId(); } @@ -425,6 +416,7 @@ void SetFinish() { CommandProcessor::interruptFinishWaiting = true; CoreTiming::ScheduleEvent_Threadsafe(0, et_SetFinishOnMainThread, 0); + g_bSignalFinishInterrupt = true; INFO_LOG(PIXELENGINE, "VIDEO Set Finish"); IncrementCheckContextId(); } @@ -438,7 +430,6 @@ void ResetSetFinish() { UpdateFinishInterrupt(false); g_bSignalFinishInterrupt = false; - } else { @@ -453,7 +444,6 @@ void ResetSetToken() { UpdateTokenInterrupt(false); g_bSignalTokenInterrupt = false; - } else { @@ -466,12 +456,4 @@ bool WaitingForPEInterrupt() { return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken); } - -void ResumeWaitingForPEInterrupt() -{ - interruptSetFinish = false; - interruptSetToken = false; - CommandProcessor::interruptFinishWaiting = false; - CommandProcessor::interruptTokenWaiting = false; -} } // end of namespace PixelEngine diff --git a/Source/Core/VideoCommon/Src/PixelEngine.h b/Source/Core/VideoCommon/Src/PixelEngine.h index 64f959009f..5e64300ef3 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.h +++ b/Source/Core/VideoCommon/Src/PixelEngine.h @@ -81,7 +81,6 @@ void SetFinish(void); void ResetSetFinish(void); void ResetSetToken(void); bool WaitingForPEInterrupt(); -void ResumeWaitingForPEInterrupt(); // Bounding box functionality. Paper Mario (both) are a couple of the few games that use it. extern u16 bbox[4]; From 2c8c8db731fe4d22fac0ec2f2064f6bceea35cd3 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 3 Feb 2013 23:52:18 +1100 Subject: [PATCH 05/31] Used AtomicStore to write to the signal interrupts. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 6d4659e309..60d69ab1fd 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -110,8 +110,8 @@ static UPEAlphaReadReg m_AlphaRead; static UPECtrlReg m_Control; //static u16 m_Token; // token value most recently encountered -static bool g_bSignalTokenInterrupt; -static bool g_bSignalFinishInterrupt; +volatile bool g_bSignalTokenInterrupt; +volatile bool g_bSignalFinishInterrupt; static int et_SetTokenOnMainThread; static int et_SetFinishOnMainThread; @@ -404,7 +404,7 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge) { CommandProcessor::interruptTokenWaiting = true; CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); - g_bSignalTokenInterrupt = true; + Common::AtomicStore(*(volatile u32*)&g_bSignalTokenInterrupt, 1); } IncrementCheckContextId(); @@ -416,7 +416,7 @@ void SetFinish() { CommandProcessor::interruptFinishWaiting = true; CoreTiming::ScheduleEvent_Threadsafe(0, et_SetFinishOnMainThread, 0); - g_bSignalFinishInterrupt = true; + Common::AtomicStore(*(volatile u32*)&g_bSignalFinishInterrupt, 1); INFO_LOG(PIXELENGINE, "VIDEO Set Finish"); IncrementCheckContextId(); } From 5f672baca319a8227c01c100275d4eaaa860782b Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 9 Feb 2013 17:46:48 +1100 Subject: [PATCH 06/31] Used Atomic functions to update variables shared between the CPU and GPU threads. Changed the variables from bool to u32 to match the atomic functions. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 31 +++++++++++---------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 60d69ab1fd..20601e6c3b 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -32,6 +32,7 @@ #include "HW/ProcessorInterface.h" #include "DLCache.h" #include "State.h" +#include "Thread.h" namespace PixelEngine { @@ -110,14 +111,14 @@ static UPEAlphaReadReg m_AlphaRead; static UPECtrlReg m_Control; //static u16 m_Token; // token value most recently encountered -volatile bool g_bSignalTokenInterrupt; -volatile bool g_bSignalFinishInterrupt; +volatile u32 g_bSignalTokenInterrupt; +volatile u32 g_bSignalFinishInterrupt; static int et_SetTokenOnMainThread; static int et_SetFinishOnMainThread; -volatile bool interruptSetToken = false; -volatile bool interruptSetFinish = false; +volatile u32 interruptSetToken = 0; +volatile u32 interruptSetFinish = 0; u16 bbox[4]; bool bbox_active; @@ -161,10 +162,10 @@ void Init() m_AlphaModeConf.Hex = 0; m_AlphaRead.Hex = 0; - g_bSignalTokenInterrupt = false; - g_bSignalFinishInterrupt = false; - interruptSetToken = false; - interruptSetFinish = false; + g_bSignalTokenInterrupt = 0; + g_bSignalFinishInterrupt = 0; + interruptSetToken = 0; + interruptSetFinish = 0; et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread); et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread); @@ -211,7 +212,7 @@ void Read16(u16& _uReturnValue, const u32 _iAddress) break; case PE_TOKEN_REG: - _uReturnValue = CommandProcessor::fifo.PEToken; + _uReturnValue = Common::AtomicLoad(*(volatile u32*)&CommandProcessor::fifo.PEToken); INFO_LOG(PIXELENGINE, "(r16) TOKEN_REG : %04x", _uReturnValue); break; @@ -312,8 +313,8 @@ void Write16(const u16 _iValue, const u32 _iAddress) { UPECtrlReg tmpCtrl(_iValue); - if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = false; - if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = false; + if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = 0; + if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = 0; m_Control.PETokenEnable = tmpCtrl.PETokenEnable; m_Control.PEFinishEnable = tmpCtrl.PEFinishEnable; @@ -360,13 +361,13 @@ void UpdateInterrupts() void UpdateTokenInterrupt(bool active) { ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active); - interruptSetToken = active; + Common::AtomicStore(interruptSetToken, active ? 1 : 0); } void UpdateFinishInterrupt(bool active) { ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); - interruptSetFinish = active; + Common::AtomicStore(interruptSetFinish, active ? 1 : 0); } // TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate). @@ -443,7 +444,7 @@ void ResetSetToken() if (g_bSignalTokenInterrupt) { UpdateTokenInterrupt(false); - g_bSignalTokenInterrupt = false; + g_bSignalTokenInterrupt = 0; } else { @@ -454,6 +455,6 @@ void ResetSetToken() bool WaitingForPEInterrupt() { - return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken); + return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || Common::AtomicLoad(interruptSetFinish) || Common::AtomicLoad(interruptSetToken)); } } // end of namespace PixelEngine From 056930cac809b1b9fbeeae3166c4cfaaf487c5a7 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 10 Feb 2013 13:33:56 +1100 Subject: [PATCH 07/31] Updated PC on a few memory writes that were missed in the FifoBusy branch. Thanks to hk.konpie for the tip. Fixes issue 5993. --- Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp | 4 ++++ Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp | 2 ++ Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp | 1 + Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp | 3 +++ Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp | 3 +++ 5 files changed, 13 insertions(+) diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp index 31caca0a43..bba434ce52 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp @@ -291,7 +291,10 @@ static void ImHere() void Jit64::Cleanup() { if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) + { + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write ABI_CallFunction((void *)&GPFifo::CheckGatherPipe); + } // SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time. if (MMCR0.Hex || MMCR1.Hex) @@ -561,6 +564,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32) { js.fifoBytesThisBlock -= 32; + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write ABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0)); } diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp index 1dae2ec17e..9a9f7ec2d4 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp @@ -303,6 +303,7 @@ void Jit64::stX(UGeckoInstruction inst) addr += offset; if ((addr & 0xFFFFF000) == 0xCC008000 && jo.optimizeGatherPipe) { + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write gpr.FlushLockX(ABI_PARAM1); MOV(32, R(ABI_PARAM1), gpr.R(s)); if (update) @@ -330,6 +331,7 @@ void Jit64::stX(UGeckoInstruction inst) } else { + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write switch (accessSize) { case 32: ABI_CallFunctionAC(thunks.ProtectFunction(true ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), 2), gpr.R(s), addr); break; diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp index 3b14179cd4..5f2c024ffa 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp @@ -42,6 +42,7 @@ const u8 GC_ALIGNED16(pbswapShuffle2x4[16]) = {3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, #if 0 static void WriteDual32(u64 value, u32 address) { + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write Memory::Write_U32((u32)(value >> 32), address); Memory::Write_U32((u32)value, address + 4); } diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp index d56c9ffee8..46da3ddf9c 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp @@ -389,7 +389,10 @@ static void ImHere() void JitIL::Cleanup() { if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) + { + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write ABI_CallFunction((void *)&GPFifo::CheckGatherPipe); + } // SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time. if (MMCR0.Hex || MMCR1.Hex) diff --git a/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp b/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp index 34b9a681e9..28e12ad1b3 100644 --- a/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp +++ b/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp @@ -32,6 +32,7 @@ #include "../../HW/GPFifo.h" #include "../../Core.h" #include "JitAsmCommon.h" +#include "JitBase.h" using namespace Gen; @@ -179,6 +180,7 @@ void CommonAsmRoutines::GenQuantizedStores() { MOV(64, MComplex(RBX, RCX, SCALE_1, 0), R(RAX)); FixupBranch skip_complex = J(); SetJumpTarget(too_complex); + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write ABI_CallFunctionRR(thunks.ProtectFunction((void *)&WriteDual32, 2), RAX, RCX); SetJumpTarget(skip_complex); RET(); @@ -195,6 +197,7 @@ void CommonAsmRoutines::GenQuantizedStores() { MOV(32, MDisp(ECX, 4+(u32)Memory::base), R(EAX)); FixupBranch arg2 = J(); SetJumpTarget(argh); + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write MOV(32, R(EAX), M(((char*)&psTemp))); ABI_CallFunctionRR(thunks.ProtectFunction((void *)&Memory::Write_U32, 2), EAX, ECX); MOV(32, R(EAX), M(((char*)&psTemp)+4)); From 0cafc33edafe235573f847500e81652a5a1b2ad0 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 10 Feb 2013 18:24:30 +1100 Subject: [PATCH 08/31] Moved the token update to the CPU thread. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 20601e6c3b..33fedd1a81 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -377,8 +377,13 @@ void UpdateFinishInterrupt(bool active) // Called only if BPMEM_PE_TOKEN_INT_ID is ack by GP void SetToken_OnMainThread(u64 userdata, int cyclesLate) { + // XXX: No 16-bit atomic store available, so cheat and use 32-bit. + // That's what we've always done. We're counting on fifo.PEToken to be + // 4-byte padded. + Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, userdata & 0xffff); INFO_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken); - UpdateInterrupts(); + if (userdata >> 16) + UpdateInterrupts(); CommandProcessor::interruptTokenWaiting = false; IncrementCheckContextId(); } @@ -394,20 +399,13 @@ void SetFinish_OnMainThread(u64 userdata, int cyclesLate) // THIS IS EXECUTED FROM VIDEO THREAD void SetToken(const u16 _token, const int _bSetTokenAcknowledge) { - // we do it directly from videoThread because of - // Super Monkey Ball - // XXX: No 16-bit atomic store available, so cheat and use 32-bit. - // That's what we've always done. We're counting on fifo.PEToken to be - // 4-byte padded. - Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token); - if (_bSetTokenAcknowledge) // set token INT { - CommandProcessor::interruptTokenWaiting = true; - CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); Common::AtomicStore(*(volatile u32*)&g_bSignalTokenInterrupt, 1); } + CommandProcessor::interruptTokenWaiting = true; + CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); IncrementCheckContextId(); } @@ -455,6 +453,6 @@ void ResetSetToken() bool WaitingForPEInterrupt() { - return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || Common::AtomicLoad(interruptSetFinish) || Common::AtomicLoad(interruptSetToken)); + return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || Common::AtomicLoad(interruptSetFinish) || Common::AtomicLoad(interruptSetToken)); } } // end of namespace PixelEngine From 17c4fcfd90b0f36ee05e098e582518f6dce20e37 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 10 Feb 2013 18:40:50 +1100 Subject: [PATCH 09/31] Removed the updating of PC in GenQuantizedStores() as the generated code is static. --- Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp b/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp index 28e12ad1b3..a97a969c93 100644 --- a/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp +++ b/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp @@ -180,7 +180,6 @@ void CommonAsmRoutines::GenQuantizedStores() { MOV(64, MComplex(RBX, RCX, SCALE_1, 0), R(RAX)); FixupBranch skip_complex = J(); SetJumpTarget(too_complex); - MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write ABI_CallFunctionRR(thunks.ProtectFunction((void *)&WriteDual32, 2), RAX, RCX); SetJumpTarget(skip_complex); RET(); @@ -197,7 +196,6 @@ void CommonAsmRoutines::GenQuantizedStores() { MOV(32, MDisp(ECX, 4+(u32)Memory::base), R(EAX)); FixupBranch arg2 = J(); SetJumpTarget(argh); - MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write MOV(32, R(EAX), M(((char*)&psTemp))); ABI_CallFunctionRR(thunks.ProtectFunction((void *)&Memory::Write_U32, 2), EAX, ECX); MOV(32, R(EAX), M(((char*)&psTemp)+4)); From f26650e55bf1fc1a59c890008f00a96041b13fa8 Mon Sep 17 00:00:00 2001 From: skidau Date: Mon, 11 Feb 2013 08:42:16 +1100 Subject: [PATCH 10/31] Removed the updating of PC in the Cleanup function. This was causing Metroid Prime to hang. --- Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp | 1 - Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp index bba434ce52..7b00d9a843 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp @@ -292,7 +292,6 @@ void Jit64::Cleanup() { if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) { - MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write ABI_CallFunction((void *)&GPFifo::CheckGatherPipe); } diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp index 46da3ddf9c..905123841b 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp @@ -390,7 +390,6 @@ void JitIL::Cleanup() { if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) { - MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write ABI_CallFunction((void *)&GPFifo::CheckGatherPipe); } From 9bff8e00c86188354363e7b41dfe6f06c781cf5a Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 16 Feb 2013 12:51:09 +1100 Subject: [PATCH 11/31] Added preliminary support to synchronise the timing of the CPU and GPU threads. A new option has been added to the game properties for this purpose. This option may help with random freezes in Dual Core mode. Fixes Gladius and Baten Kaitos: Eternal Wings and the Lost Ocean Fixes issue 5150. --- Data/User/GameConfig/GKBEAF.ini | 1 + Data/User/GameConfig/GKBPAF.ini | 1 + Data/User/GameConfig/GLSE64.ini | 3 +- Source/Core/Core/Src/BootManager.cpp | 5 +- Source/Core/Core/Src/ConfigManager.cpp | 1 + Source/Core/Core/Src/CoreParameter.cpp | 3 +- Source/Core/Core/Src/CoreParameter.h | 1 + Source/Core/Core/Src/HW/SystemTimers.cpp | 18 ++++ Source/Core/DolphinWX/Src/ISOProperties.cpp | 15 ++- Source/Core/DolphinWX/Src/ISOProperties.h | 3 +- .../Core/VideoCommon/Src/CommandProcessor.cpp | 93 ++++++++++--------- .../Core/VideoCommon/Src/CommandProcessor.h | 8 +- Source/Core/VideoCommon/Src/Fifo.cpp | 75 ++++++++------- .../Core/VideoCommon/Src/OpcodeDecoding.cpp | 85 +++++++++++++---- Source/Core/VideoCommon/Src/OpcodeDecoding.h | 2 +- Source/Core/VideoCommon/Src/PixelEngine.cpp | 5 - Source/Core/VideoCommon/Src/PixelEngine.h | 1 - 17 files changed, 211 insertions(+), 109 deletions(-) diff --git a/Data/User/GameConfig/GKBEAF.ini b/Data/User/GameConfig/GKBEAF.ini index 9934a2ba80..5e7ee300f9 100644 --- a/Data/User/GameConfig/GKBEAF.ini +++ b/Data/User/GameConfig/GKBEAF.ini @@ -1,5 +1,6 @@ # GKBEAF - Baten Kaitos [Core] Values set here will override the main dolphin settings. +SyncGPU = 1 [EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set. EmulationStateId = 4 EmulationIssues = diff --git a/Data/User/GameConfig/GKBPAF.ini b/Data/User/GameConfig/GKBPAF.ini index 936380ca29..afb3661d63 100644 --- a/Data/User/GameConfig/GKBPAF.ini +++ b/Data/User/GameConfig/GKBPAF.ini @@ -1,5 +1,6 @@ # GKBPAF - Baten Kaitos [Core] Values set here will override the main dolphin settings. +SyncGPU = 1 [EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set. EmulationStateId = 4 EmulationIssues = diff --git a/Data/User/GameConfig/GLSE64.ini b/Data/User/GameConfig/GLSE64.ini index f9b38b357c..70bea42316 100644 --- a/Data/User/GameConfig/GLSE64.ini +++ b/Data/User/GameConfig/GLSE64.ini @@ -1,8 +1,9 @@ # GLSE64 - LucasArts Gladius [Core] Values set here will override the main dolphin settings. TLBHack = 1 +SyncGPU = 1 [EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set. -EmulationStateId = 1 +EmulationStateId = 4 EmulationIssues = [OnFrame] Add memory patches to be applied every frame here. [ActionReplay] Add action replay cheats here. diff --git a/Source/Core/Core/Src/BootManager.cpp b/Source/Core/Core/Src/BootManager.cpp index 1e9e612fe0..88e0cebcb6 100644 --- a/Source/Core/Core/Src/BootManager.cpp +++ b/Source/Core/Core/Src/BootManager.cpp @@ -55,7 +55,7 @@ namespace BootManager struct ConfigCache { bool valid, bCPUThread, bSkipIdle, bEnableFPRF, bMMU, bDCBZOFF, - bVBeam, bFastDiscSpeed, bMergeBlocks, bDSPHLE, bHLE_BS2; + bVBeam, bSyncGPU, bFastDiscSpeed, bMergeBlocks, bDSPHLE, bHLE_BS2; int iTLBHack, iCPUCore; std::string strBackend; }; @@ -95,6 +95,7 @@ bool BootCore(const std::string& _rFilename) config_cache.bDCBZOFF = StartUp.bDCBZOFF; config_cache.iTLBHack = StartUp.iTLBHack; config_cache.bVBeam = StartUp.bVBeam; + config_cache.bSyncGPU = StartUp.bSyncGPU; config_cache.bFastDiscSpeed = StartUp.bFastDiscSpeed; config_cache.bMergeBlocks = StartUp.bMergeBlocks; config_cache.bDSPHLE = StartUp.bDSPHLE; @@ -109,6 +110,7 @@ bool BootCore(const std::string& _rFilename) game_ini.Get("Core", "TLBHack", &StartUp.iTLBHack, StartUp.iTLBHack); game_ini.Get("Core", "DCBZ", &StartUp.bDCBZOFF, StartUp.bDCBZOFF); game_ini.Get("Core", "VBeam", &StartUp.bVBeam, StartUp.bVBeam); + game_ini.Get("Core", "SyncGPU", &StartUp.bSyncGPU, StartUp.bSyncGPU); game_ini.Get("Core", "FastDiscSpeed", &StartUp.bFastDiscSpeed, StartUp.bFastDiscSpeed); game_ini.Get("Core", "BlockMerging", &StartUp.bMergeBlocks, StartUp.bMergeBlocks); game_ini.Get("Core", "DSPHLE", &StartUp.bDSPHLE, StartUp.bDSPHLE); @@ -168,6 +170,7 @@ void Stop() StartUp.bDCBZOFF = config_cache.bDCBZOFF; StartUp.iTLBHack = config_cache.iTLBHack; StartUp.bVBeam = config_cache.bVBeam; + StartUp.bSyncGPU = config_cache.bSyncGPU; StartUp.bFastDiscSpeed = config_cache.bFastDiscSpeed; StartUp.bMergeBlocks = config_cache.bMergeBlocks; StartUp.bDSPHLE = config_cache.bDSPHLE; diff --git a/Source/Core/Core/Src/ConfigManager.cpp b/Source/Core/Core/Src/ConfigManager.cpp index f6194c1bb7..5b7a94e2ef 100644 --- a/Source/Core/Core/Src/ConfigManager.cpp +++ b/Source/Core/Core/Src/ConfigManager.cpp @@ -399,6 +399,7 @@ void SConfig::LoadSettings() ini.Get("Core", "MMU", &m_LocalCoreStartupParameter.bMMU, false); ini.Get("Core", "TLBHack", &m_LocalCoreStartupParameter.iTLBHack, 0); ini.Get("Core", "VBeam", &m_LocalCoreStartupParameter.bVBeam, false); + ini.Get("Core", "SyncGPU", &m_LocalCoreStartupParameter.bSyncGPU, false); ini.Get("Core", "FastDiscSpeed", &m_LocalCoreStartupParameter.bFastDiscSpeed, false); ini.Get("Core", "DCBZ", &m_LocalCoreStartupParameter.bDCBZOFF, false); ini.Get("Core", "FrameLimit", &m_Framelimit, 1); // auto frame limit by default diff --git a/Source/Core/Core/Src/CoreParameter.cpp b/Source/Core/Core/Src/CoreParameter.cpp index a7a44be5ea..0a6bb795d5 100644 --- a/Source/Core/Core/Src/CoreParameter.cpp +++ b/Source/Core/Core/Src/CoreParameter.cpp @@ -50,7 +50,7 @@ SCoreStartupParameter::SCoreStartupParameter() bDPL2Decoder(false), iLatency(14), bRunCompareServer(false), bRunCompareClient(false), bMMU(false), bDCBZOFF(false), iTLBHack(0), bVBeam(false), - bFastDiscSpeed(false), + bSyncGPU(false), bFastDiscSpeed(false), SelectedLanguage(0), bWii(false), bConfirmStop(false), bHideCursor(false), bAutoHideCursor(false), bUsePanicHandlers(true), bOnScreenDisplayMessages(true), @@ -78,6 +78,7 @@ void SCoreStartupParameter::LoadDefaults() bDCBZOFF = false; iTLBHack = 0; bVBeam = false; + bSyncGPU = false; bFastDiscSpeed = false; bMergeBlocks = false; SelectedLanguage = 0; diff --git a/Source/Core/Core/Src/CoreParameter.h b/Source/Core/Core/Src/CoreParameter.h index 55d20b7b1b..896f111ab7 100644 --- a/Source/Core/Core/Src/CoreParameter.h +++ b/Source/Core/Core/Src/CoreParameter.h @@ -116,6 +116,7 @@ struct SCoreStartupParameter bool bDCBZOFF; int iTLBHack; bool bVBeam; + bool bSyncGPU; bool bFastDiscSpeed; int SelectedLanguage; diff --git a/Source/Core/Core/Src/HW/SystemTimers.cpp b/Source/Core/Core/Src/HW/SystemTimers.cpp index a041a3b0ad..10f5e3204e 100644 --- a/Source/Core/Core/Src/HW/SystemTimers.cpp +++ b/Source/Core/Core/Src/HW/SystemTimers.cpp @@ -74,6 +74,7 @@ IPC_HLE_PERIOD: For the Wiimote this is the call schedule: #include "Thread.h" #include "Timer.h" #include "VideoBackendBase.h" +#include "CommandProcessor.h" namespace SystemTimers @@ -110,6 +111,7 @@ int et_Dec; int et_VI; int et_SI; int et_AI; +int et_CP; int et_AudioDMA; int et_DSP; int et_IPC_HLE; @@ -127,6 +129,9 @@ int // This is a fixed value, don't change it AUDIO_DMA_PERIOD, + // Regulates the speed of the Command Processor + CP_PERIOD, + // This is completely arbitrary. If we find that we need lower latency, we can just // increase this number. IPC_HLE_PERIOD; @@ -187,6 +192,12 @@ void SICallback(u64 userdata, int cyclesLate) CoreTiming::ScheduleEvent(SerialInterface::GetTicksToNextSIPoll() - cyclesLate, et_SI); } +void CPCallback(u64 userdata, int cyclesLate) +{ + CommandProcessor::Update(); + CoreTiming::ScheduleEvent(CP_PERIOD - cyclesLate, et_CP); +} + void DecrementerCallback(u64 userdata, int cyclesLate) { PowerPC::ppcState.spr[SPR_DEC] = 0xFFFFFFFF; @@ -272,6 +283,9 @@ void Init() // System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA AUDIO_DMA_PERIOD = CPU_CORE_CLOCK / (AudioInterface::GetAIDSampleRate() * 4 / 32); + // Emulated gekko <-> flipper bus speed ratio (cpu clock / flipper clock) + CP_PERIOD = GetTicksPerSecond() / 10000; + Common::Timer::IncreaseResolution(); // store and convert localtime at boot to timebase ticks CoreTiming::SetFakeTBStartValue((u64)(CPU_CORE_CLOCK / TIMER_RATIO) * (u64)CEXIIPL::GetGCTime()); @@ -284,6 +298,8 @@ void Init() et_AI = CoreTiming::RegisterEvent("AICallback", AICallback); et_VI = CoreTiming::RegisterEvent("VICallback", VICallback); et_SI = CoreTiming::RegisterEvent("SICallback", SICallback); + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSyncGPU) + et_CP = CoreTiming::RegisterEvent("CPCallback", CPCallback); et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback); et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback); et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback); @@ -294,6 +310,8 @@ void Init() CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_SI); CoreTiming::ScheduleEvent(AUDIO_DMA_PERIOD, et_AudioDMA); + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSyncGPU) + CoreTiming::ScheduleEvent(CP_PERIOD, et_CP); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_PatchEngine); diff --git a/Source/Core/DolphinWX/Src/ISOProperties.cpp b/Source/Core/DolphinWX/Src/ISOProperties.cpp index c44aad5d5b..f7b35b8269 100644 --- a/Source/Core/DolphinWX/Src/ISOProperties.cpp +++ b/Source/Core/DolphinWX/Src/ISOProperties.cpp @@ -321,6 +321,8 @@ void CISOProperties::CreateGUIControls(bool IsWad) DCBZOFF->SetToolTip(_("Bypass the clearing of the data cache by the DCBZ instruction. Usually leave this option disabled.")); VBeam = new wxCheckBox(m_GameConfig, ID_VBEAM, _("Accurate VBeam emulation"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); VBeam->SetToolTip(_("If the FPS is erratic, this option may help. (ON = Compatible, OFF = Fast)")); + SyncGPU = new wxCheckBox(m_GameConfig, ID_SYNCGPU, _("Sychronise GPU thread"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); + SyncGPU->SetToolTip(_("Synchonises the GPU and CPU threads to help prevent random freezes in Dual Core mode. (ON = Compatible, OFF = Fast)")); FastDiscSpeed = new wxCheckBox(m_GameConfig, ID_DISCSPEED, _("Speed up Disc Transfer Rate"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); FastDiscSpeed->SetToolTip(_("Enable fast disc access. Needed for a few games. (ON = Fast, OFF = Compatible)")); BlockMerging = new wxCheckBox(m_GameConfig, ID_MERGEBLOCKS, _("Enable Block Merging"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); @@ -362,9 +364,10 @@ void CISOProperties::CreateGUIControls(bool IsWad) sbCoreOverrides->Add(CPUThread, 0, wxLEFT, 5); sbCoreOverrides->Add(SkipIdle, 0, wxLEFT, 5); sbCoreOverrides->Add(MMU, 0, wxLEFT, 5); - sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5); sbCoreOverrides->Add(TLBHack, 0, wxLEFT, 5); + sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5); sbCoreOverrides->Add(VBeam, 0, wxLEFT, 5); + sbCoreOverrides->Add(SyncGPU, 0, wxLEFT, 5); sbCoreOverrides->Add(FastDiscSpeed, 0, wxLEFT, 5); sbCoreOverrides->Add(BlockMerging, 0, wxLEFT, 5); sbCoreOverrides->Add(DSPHLE, 0, wxLEFT, 5); @@ -941,6 +944,11 @@ void CISOProperties::LoadGameConfig() else VBeam->Set3StateValue(wxCHK_UNDETERMINED); + if (GameIni.Get("Core", "SyncGPU", &bTemp)) + SyncGPU->Set3StateValue((wxCheckBoxState)bTemp); + else + SyncGPU->Set3StateValue(wxCHK_UNDETERMINED); + if (GameIni.Get("Core", "FastDiscSpeed", &bTemp)) FastDiscSpeed->Set3StateValue((wxCheckBoxState)bTemp); else @@ -1030,6 +1038,11 @@ bool CISOProperties::SaveGameConfig() else GameIni.Set("Core", "VBeam", VBeam->Get3StateValue()); + if (SyncGPU->Get3StateValue() == wxCHK_UNDETERMINED) + GameIni.DeleteKey("Core", "SyncGPU"); + else + GameIni.Set("Core", "SyncGPU", SyncGPU->Get3StateValue()); + if (FastDiscSpeed->Get3StateValue() == wxCHK_UNDETERMINED) GameIni.DeleteKey("Core", "FastDiscSpeed"); else diff --git a/Source/Core/DolphinWX/Src/ISOProperties.h b/Source/Core/DolphinWX/Src/ISOProperties.h index 499b242516..41e7d250a0 100644 --- a/Source/Core/DolphinWX/Src/ISOProperties.h +++ b/Source/Core/DolphinWX/Src/ISOProperties.h @@ -70,7 +70,7 @@ private: // Core wxCheckBox *CPUThread, *SkipIdle, *MMU, *DCBZOFF, *TLBHack; - wxCheckBox *VBeam, *FastDiscSpeed, *BlockMerging, *DSPHLE; + wxCheckBox *VBeam, *SyncGPU, *FastDiscSpeed, *BlockMerging, *DSPHLE; // Wii wxCheckBox *EnableWideScreen; // Video @@ -130,6 +130,7 @@ private: ID_DCBZOFF, ID_TLBHACK, ID_VBEAM, + ID_SYNCGPU, ID_DISCSPEED, ID_MERGEBLOCKS, ID_AUDIO_DSP_HLE, diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index 7e976f256e..eb03730fc3 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -32,6 +32,8 @@ #include "HW/GPFifo.h" #include "HW/Memmap.h" #include "DLCache.h" +#include "HW/SystemTimers.h" +#include "Core.h" namespace CommandProcessor { @@ -64,6 +66,8 @@ volatile bool interruptTokenWaiting = false; volatile bool interruptFinishWaiting = false; volatile bool waitingForPEInterruptDisable = false; +volatile u32 VITicks = CommandProcessor::m_cpClockOrigin; + bool IsOnThread() { return SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread; @@ -464,7 +468,7 @@ void STACKALIGN GatherPipeBursted() } if (IsOnThread()) - SetWatermarkFromGatherPipe(); + SetCpStatus(); // update the fifo-pointer if (fifo.CPWritePointer >= fifo.CPEnd) @@ -514,27 +518,6 @@ void AbortFrame() } -void SetWatermarkFromGatherPipe() -{ - fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); - fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); - isHiWatermarkActive = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && m_CPCtrlReg.GPReadEnable; - isLoWatermarkActive = fifo.bFF_LoWatermark && fifo.bFF_LoWatermarkInt && m_CPCtrlReg.GPReadEnable; - - if (isHiWatermarkActive) - { - interruptSet = true; - INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); - ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); - } - else if (isLoWatermarkActive) - { - interruptSet = true; - INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); - ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); - } -} - void SetCpStatus() { // overflow & underflow check @@ -542,30 +525,33 @@ void SetCpStatus() fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); // breakpoint - if (fifo.bFF_BPEnable) - { - if (fifo.CPBreakpoint == fifo.CPReadPointer) - { - if (!fifo.bFF_Breakpoint) + if (Core::IsGPUThread()) + { + if (fifo.bFF_BPEnable) + { + if (fifo.CPBreakpoint == fifo.CPReadPointer) { - INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer); - fifo.bFF_Breakpoint = true; - IncrementCheckContextId(); + if (!fifo.bFF_Breakpoint) + { + INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer); + fifo.bFF_Breakpoint = true; + IncrementCheckContextId(); + } } - } + else + { + if (fifo.bFF_Breakpoint) + INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer); + fifo.bFF_Breakpoint = false; + } + } else { if (fifo.bFF_Breakpoint) INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer); - fifo.bFF_Breakpoint = false; + fifo.bFF_Breakpoint = false; } - } - else - { - if (fifo.bFF_Breakpoint) - INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer); - fifo.bFF_Breakpoint = false; - } + } bool bpInt = fifo.bFF_Breakpoint && fifo.bFF_BPInt; bool ovfInt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt; @@ -581,10 +567,19 @@ void SetCpStatus() u64 userdata = interrupt?1:0; if (IsOnThread()) { - if(!interrupt || bpInt || undfInt) + if(!interrupt || bpInt || undfInt || ovfInt) { - interruptWaiting = true; - CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); + if (Core::IsGPUThread()) + { + interruptWaiting = true; + CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); + } + else if (Core::IsCPUThread()) + { + interruptSet = interrupt; + INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); + ProcessorInterface::SetInterrupt(INT_CAUSE_CP, interrupt); + } } } else @@ -608,7 +603,7 @@ void ProcessFifoAllDistance() if (IsOnThread()) { while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && - fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) + fifo.CPReadWriteDistance && !AtBreakpoint()) Common::YieldCPU(); } bProcessFifoAllDistance = false; @@ -629,8 +624,8 @@ void SetCpStatusRegister() { // Here always there is one fifo attached to the GPU m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; - m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; - m_CPStatusReg.CommandIdle = !fifo.isGpuReadingData; + m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || (fifo.CPReadPointer == fifo.CPWritePointer); + m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || !fifo.bFF_GPReadEnable; m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; @@ -701,4 +696,12 @@ void SetCpClearRegister() // } } +void Update() +{ + while (VITicks > m_cpClockOrigin && fifo.isGpuReadingData && IsOnThread()) + Common::YieldCPU(); + + if (fifo.isGpuReadingData) + Common::AtomicAdd(VITicks, SystemTimers::GetTicksPerSecond() / 10000); +} } // end of namespace CommandProcessor diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index 84b8fa7d66..6190c48e14 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -141,6 +141,9 @@ union UCPClearReg UCPClearReg(u16 _hex) {Hex = _hex; } }; +// Can be any number, low enough to not be below the number of clocks executed by the GPU per CP_PERIOD +const static u32 m_cpClockOrigin = 200000; + // Init void Init(); void Shutdown(); @@ -162,11 +165,14 @@ bool AllowIdleSkipping(); void SetCpClearRegister(); void SetCpControlRegister(); void SetCpStatusRegister(); -void SetWatermarkFromGatherPipe(); void ProcessFifoToLoWatermark(); void ProcessFifoAllDistance(); void ProcessFifoEvents(); void AbortFrame(); + +void Update(); +extern volatile u32 VITicks; + } // namespace CommandProcessor #endif // _COMMANDPROCESSOR_H diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index d4b4db3e42..4103f23bf2 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -26,6 +26,7 @@ #include "Fifo.h" #include "HW/Memmap.h" #include "Core.h" +#include "CoreTiming.h" volatile bool g_bSkipCurrentFrame = false; extern u8* g_pVideoData; @@ -72,6 +73,7 @@ void Fifo_Init() videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE); size = 0; GpuRunningState = false; + Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin); } void Fifo_Shutdown() @@ -123,7 +125,7 @@ void ReadDataFromFifo(u8* _uData, u32 len) size -= pos; if (size + len > FIFO_SIZE) { - PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos); + PanicAlert("FIFO out of bounds (sz = %i, len = %i at %08x)", size, len, pos); } memmove(&videoBuffer[0], &videoBuffer[pos], size); g_pVideoData = videoBuffer; @@ -147,6 +149,7 @@ void RunGpuLoop() std::lock_guard lk(m_csHWVidOccupied); GpuRunningState = true; SCPFifoStruct &fifo = CommandProcessor::fifo; + u32 cyclesExecuted = 0; while (GpuRunningState) { @@ -156,6 +159,8 @@ void RunGpuLoop() CommandProcessor::SetCpStatus(); + Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin); + // check if we are able to run this buffer while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint()) { @@ -163,24 +168,30 @@ void RunGpuLoop() fifo.isGpuReadingData = true; CommandProcessor::isPossibleWaitingSetDrawDone = fifo.bFF_GPLinkEnable ? true : false; - - u32 readPtr = fifo.CPReadPointer; - u8 *uData = Memory::GetPointer(readPtr); - if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase; + if (Common::AtomicLoad(CommandProcessor::VITicks) > CommandProcessor::m_cpClockOrigin || !Core::g_CoreStartupParameter.bSyncGPU) + { + u32 readPtr = fifo.CPReadPointer; + u8 *uData = Memory::GetPointer(readPtr); + + if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase; else readPtr += 32; - - _assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 , - "Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce inestabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32); - - ReadDataFromFifo(uData, 32); - - OpcodeDecoder_Run(g_bSkipCurrentFrame); - Common::AtomicStore(fifo.CPReadPointer, readPtr); - Common::AtomicAdd(fifo.CPReadWriteDistance, -32); - if((GetVideoBufferEndPtr() - g_pVideoData) == 0) - Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer); + _assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 , + "Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce instabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32); + + ReadDataFromFifo(uData, 32); + + cyclesExecuted = OpcodeDecoder_Run(g_bSkipCurrentFrame); + + if (Common::AtomicLoad(CommandProcessor::VITicks) > cyclesExecuted && Core::g_CoreStartupParameter.bSyncGPU) + Common::AtomicAdd(CommandProcessor::VITicks, -(s32)cyclesExecuted); + + Common::AtomicStore(fifo.CPReadPointer, readPtr); + Common::AtomicAdd(fifo.CPReadWriteDistance, -32); + if((GetVideoBufferEndPtr() - g_pVideoData) == 0) + Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer); + } CommandProcessor::SetCpStatus(); @@ -190,7 +201,7 @@ void RunGpuLoop() VideoFifo_CheckAsyncRequest(); CommandProcessor::isPossibleWaitingSetDrawDone = false; } - + fifo.isGpuReadingData = false; @@ -219,23 +230,23 @@ bool AtBreakpoint() void RunGpu() { - SCPFifoStruct &fifo = CommandProcessor::fifo; - while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() ) - { - u8 *uData = Memory::GetPointer(fifo.CPReadPointer); + SCPFifoStruct &fifo = CommandProcessor::fifo; + while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() ) + { + u8 *uData = Memory::GetPointer(fifo.CPReadPointer); - SaveSSEState(); - LoadDefaultSSEState(); - ReadDataFromFifo(uData, 32); - OpcodeDecoder_Run(g_bSkipCurrentFrame); - LoadSSEState(); + SaveSSEState(); + LoadDefaultSSEState(); + ReadDataFromFifo(uData, 32); + u32 count = OpcodeDecoder_Run(g_bSkipCurrentFrame); + LoadSSEState(); - //DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base"); + //DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base"); - if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase; - else fifo.CPReadPointer += 32; + if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase; + else fifo.CPReadPointer += 32; - fifo.CPReadWriteDistance -= 32; - } - CommandProcessor::SetCpStatus(); + fifo.CPReadWriteDistance -= 32; + } + CommandProcessor::SetCpStatus(); } diff --git a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp index 4f7f86d655..662dbc66a8 100644 --- a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp +++ b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp @@ -136,29 +136,38 @@ void ExecuteDisplayList(u32 address, u32 size) InterpretDisplayList(address, size); } -bool FifoCommandRunnable() +u32 FifoCommandRunnable(u32 &command_size) { + u32 cycleTime = 0; u32 buffer_size = (u32)(GetVideoBufferEndPtr() - g_pVideoData); if (buffer_size == 0) - return false; // can't peek + return 0; // can't peek u8 cmd_byte = DataPeek8(0); - u32 command_size = 0; switch (cmd_byte) { case GX_NOP: // Hm, this means that we scan over nop streams pretty slowly... + command_size = 1; + cycleTime = 6; + break; case GX_CMD_INVL_VC: // Invalidate Vertex Cache - no parameters + command_size = 1; + cycleTime = 6; + break; case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that command_size = 1; + cycleTime = 6; break; case GX_LOAD_BP_REG: command_size = 5; + cycleTime = 12; break; case GX_LOAD_CP_REG: command_size = 6; + cycleTime = 12; break; case GX_LOAD_INDX_A: @@ -166,10 +175,39 @@ bool FifoCommandRunnable() case GX_LOAD_INDX_C: case GX_LOAD_INDX_D: command_size = 5; + cycleTime = 6; // TODO break; - case GX_CMD_CALL_DL: - command_size = 9; + case GX_CMD_CALL_DL: + { + // FIXME: Calculate the cycle time of the display list. + //u32 address = DataPeek32(1); + //u32 size = DataPeek32(5); + //u8* old_pVideoData = g_pVideoData; + //u8* startAddress = Memory::GetPointer(address); + + //// Avoid the crash if Memory::GetPointer failed .. + //if (startAddress != 0) + //{ + // g_pVideoData = startAddress; + // u8 *end = g_pVideoData + size; + // u32 step = 0; + // while (g_pVideoData < end) + // { + // cycleTime += FifoCommandRunnable(step); + // g_pVideoData += step; + // } + //} + //else + //{ + // cycleTime = 45; + //} + + //// reset to the old pointer + //g_pVideoData = old_pVideoData; + command_size = 9; + cycleTime = 45; // This is unverified + } break; case GX_LOAD_XF_REG: @@ -180,11 +218,12 @@ bool FifoCommandRunnable() command_size = 1 + 4; u32 Cmd2 = DataPeek32(1); int transfer_size = ((Cmd2 >> 16) & 15) + 1; - command_size += transfer_size * 4; + command_size += transfer_size * 4; + cycleTime = 18 + 6 * transfer_size; } else { - return false; + return 0; } } break; @@ -198,10 +237,11 @@ bool FifoCommandRunnable() command_size = 1 + 2; u16 numVertices = DataPeek16(1); command_size += numVertices * VertexLoaderManager::GetVertexSize(cmd_byte & GX_VAT_MASK); + cycleTime = 12 * numVertices; // This depends on the number of pixels rendered } else { - return false; + return 0; } } else @@ -248,11 +288,19 @@ bool FifoCommandRunnable() } if (command_size > buffer_size) - return false; + return 0; // INFO_LOG("OP detected: cmd_byte 0x%x size %i buffer %i",cmd_byte, command_size, buffer_size); + if (cycleTime == 0) + cycleTime = 6; - return true; + return cycleTime; +} + +u32 FifoCommandRunnable() +{ + u32 command_size = 0; + return FifoCommandRunnable(command_size); } static void Decode() @@ -461,16 +509,15 @@ void OpcodeDecoder_Shutdown() } } -void OpcodeDecoder_Run(bool skipped_frame) +u32 OpcodeDecoder_Run(bool skipped_frame) { - if (!skipped_frame) + u32 totalCycles = 0; + u32 cycles = FifoCommandRunnable(); + while (cycles > 0) { - while (FifoCommandRunnable()) - Decode(); - } - else - { - while (FifoCommandRunnable()) - DecodeSemiNop(); + skipped_frame ? DecodeSemiNop() : Decode(); + totalCycles += cycles; + cycles = FifoCommandRunnable(); } + return totalCycles; } diff --git a/Source/Core/VideoCommon/Src/OpcodeDecoding.h b/Source/Core/VideoCommon/Src/OpcodeDecoding.h index f2e9cd1321..71a15477a3 100644 --- a/Source/Core/VideoCommon/Src/OpcodeDecoding.h +++ b/Source/Core/VideoCommon/Src/OpcodeDecoding.h @@ -50,6 +50,6 @@ extern bool g_bRecordFifoData; void OpcodeDecoder_Init(); void OpcodeDecoder_Shutdown(); -void OpcodeDecoder_Run(bool skipped_frame); +u32 OpcodeDecoder_Run(bool skipped_frame); void ExecuteDisplayList(u32 address, u32 size); #endif // _OPCODE_DECODING_H diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 33fedd1a81..24e8a02ab4 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -450,9 +450,4 @@ void ResetSetToken() } CommandProcessor::interruptTokenWaiting = false; } - -bool WaitingForPEInterrupt() -{ - return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || Common::AtomicLoad(interruptSetFinish) || Common::AtomicLoad(interruptSetToken)); -} } // end of namespace PixelEngine diff --git a/Source/Core/VideoCommon/Src/PixelEngine.h b/Source/Core/VideoCommon/Src/PixelEngine.h index 5e64300ef3..4da0f938d8 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.h +++ b/Source/Core/VideoCommon/Src/PixelEngine.h @@ -80,7 +80,6 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge); void SetFinish(void); void ResetSetFinish(void); void ResetSetToken(void); -bool WaitingForPEInterrupt(); // Bounding box functionality. Paper Mario (both) are a couple of the few games that use it. extern u16 bbox[4]; From 0c882cca664f1e26ad6b68cce2518af165f0c591 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 16 Feb 2013 13:54:58 +1100 Subject: [PATCH 12/31] Profile stores, fp stores and ps stores only to the fifo write addresses list. This should make the JIT a little faster as it will not be checking for external exceptions unnecessarily. --- Source/Core/Core/Src/HW/GPFifo.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/Src/HW/GPFifo.cpp b/Source/Core/Core/Src/HW/GPFifo.cpp index 3af387792f..63fff83db6 100644 --- a/Source/Core/Core/Src/HW/GPFifo.cpp +++ b/Source/Core/Core/Src/HW/GPFifo.cpp @@ -98,12 +98,17 @@ void STACKALIGN CheckGatherPipe() memmove(m_gatherPipe, m_gatherPipe + cnt, m_gatherPipeCount); // Profile where the FIFO writes are occurring. - if (jit && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end())) + if (jit && PC != 0 && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end())) { - jit->js.fifoWriteAddresses.insert(PC); + // Log only stores, fp stores and ps stores, filtering out other instructions arrived via optimizeGatherPipe + int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; + if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_PS && GetOpInfo(Memory::ReadUnchecked_U32(PC))->opname=="psq_st")) + { + jit->js.fifoWriteAddresses.insert(PC); - // Invalidate the JIT block so that it gets recompiled with the external exception check included. - jit->GetBlockCache()->InvalidateICache(PC, 4); + // Invalidate the JIT block so that it gets recompiled with the external exception check included. + jit->GetBlockCache()->InvalidateICache(PC, 4); + } } } } From 45efced2809a7201909f85f2ee87d4625939d932 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 16 Feb 2013 18:30:20 +1100 Subject: [PATCH 13/31] Moved the setting of the Finish interrupt signal back to the main thread as it was causing Wii games like Resident Evil 4 (Wii) to hang. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 24e8a02ab4..aec160ddd2 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -390,6 +390,7 @@ void SetToken_OnMainThread(u64 userdata, int cyclesLate) void SetFinish_OnMainThread(u64 userdata, int cyclesLate) { + Common::AtomicStore(*(volatile u32*)&g_bSignalFinishInterrupt, 1); UpdateInterrupts(); CommandProcessor::interruptFinishWaiting = false; CommandProcessor::isPossibleWaitingSetDrawDone = false; @@ -415,7 +416,6 @@ void SetFinish() { CommandProcessor::interruptFinishWaiting = true; CoreTiming::ScheduleEvent_Threadsafe(0, et_SetFinishOnMainThread, 0); - Common::AtomicStore(*(volatile u32*)&g_bSignalFinishInterrupt, 1); INFO_LOG(PIXELENGINE, "VIDEO Set Finish"); IncrementCheckContextId(); } From 0e2c3f3483e230079b5b6107d3cc634f60d92c78 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 16 Feb 2013 22:54:10 +1100 Subject: [PATCH 14/31] Increased the cycle time of the vertex command. Fixes "Speed Challenge: Jacques Villeneuve's Racing Vision". --- Source/Core/VideoCommon/Src/Fifo.cpp | 2 +- Source/Core/VideoCommon/Src/OpcodeDecoding.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index 4103f23bf2..1c4ddb9d5e 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -184,7 +184,7 @@ void RunGpuLoop() cyclesExecuted = OpcodeDecoder_Run(g_bSkipCurrentFrame); - if (Common::AtomicLoad(CommandProcessor::VITicks) > cyclesExecuted && Core::g_CoreStartupParameter.bSyncGPU) + if (Common::AtomicLoad(CommandProcessor::VITicks) > cyclesExecuted && Core::g_CoreStartupParameter.bSyncGPU) Common::AtomicAdd(CommandProcessor::VITicks, -(s32)cyclesExecuted); Common::AtomicStore(fifo.CPReadPointer, readPtr); diff --git a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp index 662dbc66a8..c7764dba71 100644 --- a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp +++ b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp @@ -237,7 +237,7 @@ u32 FifoCommandRunnable(u32 &command_size) command_size = 1 + 2; u16 numVertices = DataPeek16(1); command_size += numVertices * VertexLoaderManager::GetVertexSize(cmd_byte & GX_VAT_MASK); - cycleTime = 12 * numVertices; // This depends on the number of pixels rendered + cycleTime = 393 * numVertices; // This depends on the number of pixels rendered } else { From 36f2082a5f50da83035c1f6a3be059f018d3299b Mon Sep 17 00:00:00 2001 From: skidau Date: Wed, 20 Feb 2013 23:38:25 +1100 Subject: [PATCH 15/31] Made vertex loading take constant time. Fixes a hang in Gladius. --- Source/Core/VideoCommon/Src/OpcodeDecoding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp index c7764dba71..bb6c555c07 100644 --- a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp +++ b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp @@ -237,7 +237,7 @@ u32 FifoCommandRunnable(u32 &command_size) command_size = 1 + 2; u16 numVertices = DataPeek16(1); command_size += numVertices * VertexLoaderManager::GetVertexSize(cmd_byte & GX_VAT_MASK); - cycleTime = 393 * numVertices; // This depends on the number of pixels rendered + cycleTime = 1600; // This depends on the number of pixels rendered } else { From bde9a459cdd7a465cf9c9e275bd4562bb072ae7e Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 23 Feb 2013 23:50:02 +1100 Subject: [PATCH 16/31] Added a patch that bypasses the FIFO reset code in Wallace and Gromit: Project Zoo, allowing it to go in-game. --- Data/User/GameConfig/GWLE6L.ini | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Data/User/GameConfig/GWLE6L.ini diff --git a/Data/User/GameConfig/GWLE6L.ini b/Data/User/GameConfig/GWLE6L.ini new file mode 100644 index 0000000000..60775fccd0 --- /dev/null +++ b/Data/User/GameConfig/GWLE6L.ini @@ -0,0 +1,10 @@ +# GWLE6L - Project Zoo +[Core] Values set here will override the main dolphin settings. +TLBHack = 1 +[EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set. +EmulationStateId = 3 +EmulationIssues = +[OnFrame] Add memory patches to be applied every frame here. ++$Bypass FIFO reset +0x8028EF00:dword:0x48000638 +[ActionReplay] Add action replay cheats here. From 40b65fdc042fbf51e1020ba798f0ed88c815c438 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 24 Feb 2013 17:10:33 +1100 Subject: [PATCH 17/31] Merge aram-dma-fixes (r76a13604ef49b522281af75675f044d59a74e871) --- Source/Core/Core/Src/HW/DSP.cpp | 4 ---- .../Interpreter_SystemRegisters.cpp | 1 + Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp | 3 ++- .../Src/PowerPC/Jit64/Jit_SystemRegisters.cpp | 18 ++++++++++++++++++ Source/Core/Core/Src/PowerPC/Jit64IL/IR.h | 4 ++-- .../Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp | 18 ++++++++++++++++++ .../PowerPC/Jit64IL/JitIL_SystemRegisters.cpp | 2 +- 7 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index 5945cb7141..0ff8e2db49 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -436,10 +436,6 @@ void Write16(const u16 _Value, const u32 _Address) if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0; if (tmpControl.DSP) g_dspState.DSPControl.DSP = 0; - // Tracking DMAState fixes Knockout Kings 2003 in DSP HLE mode - if (GetDSPEmulator()->IsLLE()) - g_dspState.DSPControl.DMAState = 0; // keep g_ARAM DMA State zero - // unknown g_dspState.DSPControl.unk3 = tmpControl.unk3; g_dspState.DSPControl.pad = tmpControl.pad; diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp index 5a199b56c8..7d3521a80a 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp @@ -234,6 +234,7 @@ void Interpreter::mtmsr(UGeckoInstruction _inst) { // Privileged? MSR = m_GPR[_inst.RS]; + PowerPC::CheckExceptions(); m_EndBlock = true; } diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp index 7b00d9a843..bce13c9e6c 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp @@ -303,7 +303,8 @@ void Jit64::Cleanup() void Jit64::WriteExit(u32 destination, int exit_num) { Cleanup(); - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); + + SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); //If nobody has taken care of this yet (this can be removed when all branches are done) JitBlock *b = js.curBlock; diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp index 14827129f8..b977e82b28 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp @@ -122,7 +122,25 @@ void Jit64::mtmsr(UGeckoInstruction inst) gpr.UnlockAll(); gpr.Flush(FLUSH_ALL); fpr.Flush(FLUSH_ALL); + + // If some exceptions are pending and EE are now enabled, force checking + // external exceptions when going out of mtmsr in order to execute delayed + // interrupts as soon as possible. + MOV(32, R(EAX), M(&MSR)); + TEST(32, R(EAX), Imm32(0x8000)); + FixupBranch eeDisabled = J_CC(CC_Z); + + MOV(32, R(EAX), M((void*)&PowerPC::ppcState.Exceptions)); + TEST(32, R(EAX), R(EAX)); + FixupBranch noExceptionsPending = J_CC(CC_Z); + + MOV(32, M(&PC), Imm32(js.compilerPC + 4)); + WriteExternalExceptionExit(); + + SetJumpTarget(eeDisabled); + SetJumpTarget(noExceptionsPending); WriteExit(js.compilerPC + 4, 0); + js.firstFPInstructionFound = false; } diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h index 77f7d6977b..7fabd946f9 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h @@ -276,8 +276,8 @@ public: InstLoc EmitLoadMSR() { return FoldZeroOp(LoadMSR, 0); } - InstLoc EmitStoreMSR(InstLoc val) { - return FoldUOp(StoreMSR, val); + InstLoc EmitStoreMSR(InstLoc val, InstLoc pc) { + return FoldBiOp(StoreMSR, val, pc); } InstLoc EmitStoreFPRF(InstLoc value) { return FoldUOp(StoreFPRF, value); diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp index ce47e2c3ac..a1e1f32603 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp @@ -994,8 +994,26 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak break; } case StoreMSR: { + unsigned InstLoc = ibuild->GetImmValue(getOp2(I)); regStoreInstToConstLoc(RI, 32, getOp1(I), &MSR); regNormalRegClear(RI, I); + + // If some exceptions are pending and EE are now enabled, force checking + // external exceptions when going out of mtmsr in order to execute delayed + // interrupts as soon as possible. + Jit->MOV(32, R(EAX), M(&MSR)); + Jit->TEST(32, R(EAX), Imm32(0x8000)); + FixupBranch eeDisabled = Jit->J_CC(CC_Z); + + Jit->MOV(32, R(EAX), M((void*)&PowerPC::ppcState.Exceptions)); + Jit->TEST(32, R(EAX), R(EAX)); + FixupBranch noExceptionsPending = Jit->J_CC(CC_Z); + + Jit->MOV(32, M(&PC), Imm32(InstLoc + 4)); + Jit->WriteExceptionExit(); // TODO: Implement WriteExternalExceptionExit for JitIL + + Jit->SetJumpTarget(eeDisabled); + Jit->SetJumpTarget(noExceptionsPending); break; } case StoreGQR: { diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp index f3e25dc2dd..ac914bec94 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp @@ -106,7 +106,7 @@ void JitIL::mfspr(UGeckoInstruction inst) // -------------- void JitIL::mtmsr(UGeckoInstruction inst) { - ibuild.EmitStoreMSR(ibuild.EmitLoadGReg(inst.RS)); + ibuild.EmitStoreMSR(ibuild.EmitLoadGReg(inst.RS), ibuild.EmitIntConst(js.compilerPC)); ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4)); } // ============== From 6b28b3929a60a63b68216fbe2c60712b1da30ea0 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 24 Feb 2013 21:32:14 +1100 Subject: [PATCH 18/31] Delayed the interrupts in the EXI Channel. Fixes issue 5580. --- Source/Core/Core/Src/HW/EXI_Channel.cpp | 14 +++++++++----- Source/Core/Core/Src/HW/EXI_Channel.h | 4 +++- Source/Core/Core/Src/HW/ProcessorInterface.cpp | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Source/Core/Core/Src/HW/EXI_Channel.cpp b/Source/Core/Core/Src/HW/EXI_Channel.cpp index b38009f0b1..f31fc59b3f 100644 --- a/Source/Core/Core/Src/HW/EXI_Channel.cpp +++ b/Source/Core/Core/Src/HW/EXI_Channel.cpp @@ -25,9 +25,9 @@ #define EXI_WRITE 1 #define EXI_READWRITE 2 - #include "ProcessorInterface.h" #include "../PowerPC/PowerPC.h" +#include "CoreTiming.h" CEXIChannel::CEXIChannel(u32 ChannelId) : m_DMAMemoryAddress(0), @@ -45,6 +45,8 @@ CEXIChannel::CEXIChannel(u32 ChannelId) : for (int i = 0; i < NUM_DEVICES; i++) m_pDevices[i] = EXIDevice_Create(EXIDEVICE_NONE, m_ChannelId); + + updateInterrupts = CoreTiming::RegisterEvent("EXIInterrupt", UpdateInterrupts); } CEXIChannel::~CEXIChannel() @@ -88,12 +90,12 @@ void CEXIChannel::AddDevice(IEXIDevice* pDevice, const int device_num, bool noti if (m_ChannelId != 2) { m_Status.EXTINT = 1; - UpdateInterrupts(); + CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); } } } -void CEXIChannel::UpdateInterrupts() +void CEXIChannel::UpdateInterrupts(u64 userdata, int cyclesLate) { ExpansionInterface::UpdateInterrupts(); } @@ -149,7 +151,9 @@ void CEXIChannel::Read32(u32& _uReturnValue, const u32 _iRegister) if (m_ChannelId == 2) m_Status.EXT = 0; else + { m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0; + } _uReturnValue = m_Status.Hex; break; @@ -213,7 +217,7 @@ void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) if (pDevice != NULL) pDevice->SetCS(m_Status.CHIP_SELECT); - UpdateInterrupts(); + CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); } break; @@ -264,7 +268,7 @@ void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) if(!m_Control.TSTART) // completed ! { m_Status.TCINT = 1; - UpdateInterrupts(); + CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); } } break; diff --git a/Source/Core/Core/Src/HW/EXI_Channel.h b/Source/Core/Core/Src/HW/EXI_Channel.h index ef6c0a4e0f..dfbd8558f9 100644 --- a/Source/Core/Core/Src/HW/EXI_Channel.h +++ b/Source/Core/Core/Src/HW/EXI_Channel.h @@ -110,6 +110,9 @@ private: // Since channels operate a bit differently from each other u32 m_ChannelId; + int updateInterrupts; + + static void UpdateInterrupts(u64 userdata, int cyclesLate); public: // get device IEXIDevice* GetDevice(const u8 _CHIP_SELECT); @@ -129,7 +132,6 @@ public: void Update(); bool IsCausingInterrupt(); - void UpdateInterrupts(); void DoState(PointerWrap &p); void PauseAndLock(bool doLock, bool unpauseOnUnlock); diff --git a/Source/Core/Core/Src/HW/ProcessorInterface.cpp b/Source/Core/Core/Src/HW/ProcessorInterface.cpp index 9e72cc2169..d4142efaae 100644 --- a/Source/Core/Core/Src/HW/ProcessorInterface.cpp +++ b/Source/Core/Core/Src/HW/ProcessorInterface.cpp @@ -99,7 +99,7 @@ void Init() m_ResetCode = 0x80000000; // Cold reset m_InterruptCause = INT_CAUSE_RST_BUTTON | INT_CAUSE_VI; - toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", &ToggleResetButtonCallback); + toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback); } void Read16(u16& _uReturnValue, const u32 _iAddress) From e69c58ee8919b4c5f575b3e28b5a56b3fa265164 Mon Sep 17 00:00:00 2001 From: skidau Date: Tue, 26 Feb 2013 18:10:02 +1100 Subject: [PATCH 19/31] Removed the disc seek times for GC games and removed the disc speed option on Wii games. Checked for external exceptions only in mtmsr. Fixes issue 5545. --- Source/Core/Core/Src/HW/DVDInterface.cpp | 7 +------ Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Source/Core/Core/Src/HW/DVDInterface.cpp b/Source/Core/Core/Src/HW/DVDInterface.cpp index 214fb5dc02..b03edc5132 100644 --- a/Source/Core/Core/Src/HW/DVDInterface.cpp +++ b/Source/Core/Core/Src/HW/DVDInterface.cpp @@ -33,10 +33,6 @@ // Disc transfer rate measured in bytes per second static const u32 DISC_TRANSFER_RATE_GC = 3125 * 1024; -static const u32 DISC_TRANSFER_RATE_WII = 7926 * 1024; - -// Disc access time measured in milliseconds -static const u32 DISC_ACCESS_TIME_MS = 128; namespace DVDInterface { @@ -501,8 +497,7 @@ void Write32(const u32 _iValue, const u32 _iAddress) if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) { u64 ticksUntilTC = m_DILENGTH.Length * - (SystemTimers::GetTicksPerSecond() / (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii?DISC_TRANSFER_RATE_WII:DISC_TRANSFER_RATE_GC)) + - (SystemTimers::GetTicksPerSecond() * DISC_ACCESS_TIME_MS / 1000); + (SystemTimers::GetTicksPerSecond() / (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii ? 1 : DISC_TRANSFER_RATE_GC)); CoreTiming::ScheduleEvent((int)ticksUntilTC, tc); } else diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp index b977e82b28..defcf3d73d 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp @@ -131,7 +131,7 @@ void Jit64::mtmsr(UGeckoInstruction inst) FixupBranch eeDisabled = J_CC(CC_Z); MOV(32, R(EAX), M((void*)&PowerPC::ppcState.Exceptions)); - TEST(32, R(EAX), R(EAX)); + TEST(32, R(EAX), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER)); FixupBranch noExceptionsPending = J_CC(CC_Z); MOV(32, M(&PC), Imm32(js.compilerPC + 4)); From b83a1e3b66d83a081f18763ac0a21a1822ac4cb0 Mon Sep 17 00:00:00 2001 From: skidau Date: Tue, 26 Feb 2013 19:39:11 +1100 Subject: [PATCH 20/31] Disabled "Speed up disc transfer" from the ZTP GC game ini. --- Data/User/GameConfig/GZ2E01.ini | 1 - Data/User/GameConfig/GZ2J01.ini | 1 - Data/User/GameConfig/GZ2P01.ini | 1 - 3 files changed, 3 deletions(-) diff --git a/Data/User/GameConfig/GZ2E01.ini b/Data/User/GameConfig/GZ2E01.ini index f84b0006e7..e4de22aad5 100644 --- a/Data/User/GameConfig/GZ2E01.ini +++ b/Data/User/GameConfig/GZ2E01.ini @@ -1,6 +1,5 @@ # GZ2E01 - The Legend of Zelda: Twilight Princess [Core] -FastDiscSpeed = 1 [EmuState] #The Emulation State. EmulationStateId = 4 diff --git a/Data/User/GameConfig/GZ2J01.ini b/Data/User/GameConfig/GZ2J01.ini index da0b6ceb42..f2f3b3d48f 100644 --- a/Data/User/GameConfig/GZ2J01.ini +++ b/Data/User/GameConfig/GZ2J01.ini @@ -1,6 +1,5 @@ # GZ2J01 - The Legend of Zelda: Twilight Princess [Core] -FastDiscSpeed = 1 [EmuState] #The Emulation State. EmulationStateId = 4 diff --git a/Data/User/GameConfig/GZ2P01.ini b/Data/User/GameConfig/GZ2P01.ini index 4ed0b6f6ef..bb6c58e20e 100644 --- a/Data/User/GameConfig/GZ2P01.ini +++ b/Data/User/GameConfig/GZ2P01.ini @@ -1,7 +1,6 @@ # GZ2P01 - The Legend of Zelda Twilight Princess [Core] #Values set here will override the main dolphin settings. -FastDiscSpeed = 1 [Speedhacks] 0x803420bc=200 [EmuState] From 0399959c3901b6297a90de28c71498d89fb75a78 Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 1 Mar 2013 00:59:38 +1100 Subject: [PATCH 21/31] Skipped the EE check if there is a CP interrupt pending. Fixes issue 4336. --- .../Src/PowerPC/Jit64/Jit_SystemRegisters.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp index defcf3d73d..d988d8c799 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp @@ -28,6 +28,7 @@ #include "Jit.h" #include "JitRegCache.h" +#include "HW/ProcessorInterface.h" void Jit64::mtspr(UGeckoInstruction inst) { @@ -126,19 +127,23 @@ void Jit64::mtmsr(UGeckoInstruction inst) // If some exceptions are pending and EE are now enabled, force checking // external exceptions when going out of mtmsr in order to execute delayed // interrupts as soon as possible. - MOV(32, R(EAX), M(&MSR)); - TEST(32, R(EAX), Imm32(0x8000)); + TEST(32, M(&MSR), Imm32(0x8000)); FixupBranch eeDisabled = J_CC(CC_Z); - MOV(32, R(EAX), M((void*)&PowerPC::ppcState.Exceptions)); - TEST(32, R(EAX), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER)); + TEST(32, M((void*)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER)); FixupBranch noExceptionsPending = J_CC(CC_Z); + // Check if a CP interrupt is waiting and keep the GPU emulation in sync (issue 4336) + TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP)); + FixupBranch cpInt = J_CC(CC_NZ); + MOV(32, M(&PC), Imm32(js.compilerPC + 4)); WriteExternalExceptionExit(); - SetJumpTarget(eeDisabled); + SetJumpTarget(cpInt); SetJumpTarget(noExceptionsPending); + SetJumpTarget(eeDisabled); + WriteExit(js.compilerPC + 4, 0); js.firstFPInstructionFound = false; From 7a04ec6f9220dd66147c86baaebc2f9e05d65ead Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 1 Mar 2013 11:51:11 +1100 Subject: [PATCH 22/31] Implemented proper timing for the sample counter in the AudioInterface, removing the previous hack. Cleaned up some of the audio streaming code. --- Source/Core/Core/Src/HW/AudioInterface.cpp | 27 +++++++++++++++++----- Source/Core/Core/Src/HW/AudioInterface.h | 2 +- Source/Core/Core/Src/HW/DSP.cpp | 11 +++++---- Source/Core/Core/Src/HW/SystemTimers.cpp | 15 ------------ 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Source/Core/Core/Src/HW/AudioInterface.cpp b/Source/Core/Core/Src/HW/AudioInterface.cpp index 0a33e7c773..15e1f0bb29 100644 --- a/Source/Core/Core/Src/HW/AudioInterface.cpp +++ b/Source/Core/Core/Src/HW/AudioInterface.cpp @@ -145,6 +145,8 @@ static void GenerateAudioInterrupt(); static void UpdateInterrupts(); static void IncreaseSampleCount(const u32 _uAmount); void ReadStreamBlock(s16* _pPCM); +u64 GetAIPeriod(); +int et_AI; void Init() { @@ -159,6 +161,8 @@ void Init() g_AISSampleRate = 48000; g_AIDSampleRate = 32000; + + et_AI = CoreTiming::RegisterEvent("AICallback", Update); } void Shutdown() @@ -178,11 +182,8 @@ void Read32(u32& _rReturnValue, const u32 _Address) break; case AI_SAMPLE_COUNTER: + Update(0, 0); _rReturnValue = m_SampleCounter; - // HACK - AI SRC init will do while (oldval == sample_counter) {} - // in order to pass this, we need to increment the counter whenever read - if (m_Control.PSTAT) - m_SampleCounter++; break; case AI_INTERRUPT_TIMING: @@ -236,6 +237,9 @@ void Write32(const u32 _Value, const u32 _Address) // Tell Drive Interface to start/stop streaming DVDInterface::g_bStream = tmpAICtrl.PSTAT; + + CoreTiming::RemoveEvent(et_AI); + CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI); } // AI Interrupt @@ -271,6 +275,8 @@ void Write32(const u32 _Value, const u32 _Address) case AI_INTERRUPT_TIMING: m_InterruptTiming = _Value; + CoreTiming::RemoveEvent(et_AI); + CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI); DEBUG_LOG(AUDIO_INTERFACE, "Set interrupt: %08x samples", m_InterruptTiming); break; @@ -448,7 +454,7 @@ unsigned int GetAIDSampleRate() return g_AIDSampleRate; } -void Update() +void Update(u64 userdata, int cyclesLate) { if (m_Control.PSTAT) { @@ -458,8 +464,17 @@ void Update() const u32 Samples = static_cast(Diff / g_CPUCyclesPerSample); g_LastCPUTime += Samples * g_CPUCyclesPerSample; IncreaseSampleCount(Samples); - } + } + CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2) - cyclesLate, et_AI); } } +u64 GetAIPeriod() +{ + u64 period = g_CPUCyclesPerSample * m_InterruptTiming; + if (period == 0) + period = 32000 * g_CPUCyclesPerSample; + return period; +} + } // end of namespace AudioInterface diff --git a/Source/Core/Core/Src/HW/AudioInterface.h b/Source/Core/Core/Src/HW/AudioInterface.h index 1504198621..06fd3aec79 100644 --- a/Source/Core/Core/Src/HW/AudioInterface.h +++ b/Source/Core/Core/Src/HW/AudioInterface.h @@ -31,7 +31,7 @@ void Init(); void Shutdown(); void DoState(PointerWrap &p); -void Update(); +void Update(u64 userdata, int cyclesLate); // Called by DSP emulator void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate); diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index 0ff8e2db49..78f8ab5ed0 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -47,6 +47,7 @@ #include "../PowerPC/PowerPC.h" #include "../ConfigManager.h" #include "../DSPEmulator.h" +#include "SystemTimers.h" namespace DSP { @@ -152,7 +153,7 @@ struct AudioDMA SourceAddress = 0; ReadAddress = 0; AudioDMAControl.Hex = 0; - BlocksLeft = 0; + BlocksLeft = 1; } }; @@ -355,7 +356,7 @@ void Read16(u16& _uReturnValue, const u32 _iAddress) // AI case AUDIO_DMA_BLOCKS_LEFT: - _uReturnValue = g_audioDMA.BlocksLeft > 0 ? g_audioDMA.BlocksLeft - 1 : 0; // AUDIO_DMA_BLOCKS_LEFT is zero based + _uReturnValue = g_audioDMA.BlocksLeft; break; case AUDIO_DMA_START_LO: @@ -665,7 +666,7 @@ void UpdateDSPSlice(int cycles) { // This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm) void UpdateAudioDMA() { - if (g_audioDMA.AudioDMAControl.Enable && g_audioDMA.BlocksLeft) + if (g_audioDMA.AudioDMAControl.Enable && g_audioDMA.BlocksLeft > -1) { // Read audio at g_audioDMA.ReadAddress in RAM and push onto an // external audio fifo in the emulator, to be mixed with the disc @@ -675,7 +676,7 @@ void UpdateAudioDMA() g_audioDMA.BlocksLeft--; g_audioDMA.ReadAddress += 32; - if (g_audioDMA.BlocksLeft == 0) + if (g_audioDMA.BlocksLeft == -1) { dsp_emulator->DSP_SendAIBuffer(g_audioDMA.SourceAddress, 8*g_audioDMA.AudioDMAControl.NumBlocks); GenerateDSPInterrupt(DSP::INT_AID); @@ -687,7 +688,7 @@ void UpdateAudioDMA() { // Send silence. Yeah, it's a bit of a waste to sample rate convert // silence. or hm. Maybe we shouldn't do this :) - //dsp_emulator->DSP_SendAIBuffer(0, AudioInterface::GetAIDSampleRate()); + dsp_emulator->DSP_SendAIBuffer(0, AudioInterface::GetAIDSampleRate()); } } diff --git a/Source/Core/Core/Src/HW/SystemTimers.cpp b/Source/Core/Core/Src/HW/SystemTimers.cpp index 10f5e3204e..27ba4a62f9 100644 --- a/Source/Core/Core/Src/HW/SystemTimers.cpp +++ b/Source/Core/Core/Src/HW/SystemTimers.cpp @@ -110,7 +110,6 @@ enum int et_Dec; int et_VI; int et_SI; -int et_AI; int et_CP; int et_AudioDMA; int et_DSP; @@ -120,9 +119,6 @@ int et_PatchEngine; // PatchEngine updates every 1/60th of a second by default // These are badly educated guesses // Feel free to experiment. Set these in Init below. int - // This one should simply be determined by the increasing counter in AI. - AI_PERIOD, - // These shouldn't be period controlled either, most likely. DSP_PERIOD, @@ -148,12 +144,6 @@ u32 ConvertMillisecondsToTicks(u32 _Milliseconds) return GetTicksPerSecond() / 1000 * _Milliseconds; } -void AICallback(u64 userdata, int cyclesLate) -{ - AudioInterface::Update(); - CoreTiming::ScheduleEvent(AI_PERIOD - cyclesLate, et_AI); -} - // DSP/CPU timeslicing. void DSPCallback(u64 userdata, int cyclesLate) { @@ -277,9 +267,6 @@ void Init() if (DSP::GetDSPEmulator()->IsLLE()) DSP_PERIOD = 12000; // TO BE TWEAKED - // This is the biggest question mark. - AI_PERIOD = GetTicksPerSecond() / 80; - // System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA AUDIO_DMA_PERIOD = CPU_CORE_CLOCK / (AudioInterface::GetAIDSampleRate() * 4 / 32); @@ -295,7 +282,6 @@ void Init() CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks()); et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback); - et_AI = CoreTiming::RegisterEvent("AICallback", AICallback); et_VI = CoreTiming::RegisterEvent("VICallback", VICallback); et_SI = CoreTiming::RegisterEvent("SICallback", SICallback); if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSyncGPU) @@ -305,7 +291,6 @@ void Init() et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback); et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback); - CoreTiming::ScheduleEvent(AI_PERIOD, et_AI); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine(), et_VI); CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_SI); From 4c6bbec600ccef4d41245979ff40e6a022053fc8 Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 1 Mar 2013 20:23:55 +1100 Subject: [PATCH 23/31] Changed the maximum slice length to 1000 from 20000. This makes the emulator check external exceptions more regularly. Fixes the audio cutting in and out in the Resident Evil GC games using DSP HLE. Triggered the ARAM interrupt by the scheduler instead of directly in function. Fixes a hang in Mario Super Baseball. Fixes issue 5825. Fixes issue 5096. --- Source/Core/Core/Src/CoreTiming.cpp | 2 +- Source/Core/Core/Src/HW/DSP.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/Src/CoreTiming.cpp b/Source/Core/Core/Src/CoreTiming.cpp index 525f7165f2..a19d1c5cf8 100644 --- a/Source/Core/Core/Src/CoreTiming.cpp +++ b/Source/Core/Core/Src/CoreTiming.cpp @@ -24,7 +24,7 @@ #include "StringUtil.h" #include "VideoBackendBase.h" -#define MAX_SLICE_LENGTH 20000 +#define MAX_SLICE_LENGTH 1000 namespace CoreTiming { diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index 78f8ab5ed0..d38b244338 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -698,7 +698,7 @@ void Do_ARAM_DMA() if (!GetDSPEmulator()->IsLLE()) g_dspState.DSPControl.DMAState = 1; - GenerateDSPInterrupt(INT_ARAM, true); + CoreTiming::ScheduleEvent_Threadsafe(0, et_GenerateDSPInterrupt, INT_ARAM | (1<<16)); // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks if (g_arDMA.Cnt.dir) From 3874b46a9311cf9ef2430c3c8b0b5264cbbe8dda Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Mar 2013 12:37:55 +1100 Subject: [PATCH 24/31] Used an immediate GenerateDSPInterrupt when transferring data from ARAM to MRAM and a scheduled DSP interrupt when transferring data from MRAM to ARAM. Changed the maximum timeslice back to 20000 as it was causing slowdown. --- Source/Core/Core/Src/CoreTiming.cpp | 2 +- Source/Core/Core/Src/HW/DSP.cpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/Src/CoreTiming.cpp b/Source/Core/Core/Src/CoreTiming.cpp index a19d1c5cf8..525f7165f2 100644 --- a/Source/Core/Core/Src/CoreTiming.cpp +++ b/Source/Core/Core/Src/CoreTiming.cpp @@ -24,7 +24,7 @@ #include "StringUtil.h" #include "VideoBackendBase.h" -#define MAX_SLICE_LENGTH 1000 +#define MAX_SLICE_LENGTH 20000 namespace CoreTiming { diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index d38b244338..9d5211bcfa 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -698,7 +698,10 @@ void Do_ARAM_DMA() if (!GetDSPEmulator()->IsLLE()) g_dspState.DSPControl.DMAState = 1; - CoreTiming::ScheduleEvent_Threadsafe(0, et_GenerateDSPInterrupt, INT_ARAM | (1<<16)); + if (g_arDMA.Cnt.dir) + CoreTiming::ScheduleEvent_Threadsafe(0, et_GenerateDSPInterrupt, INT_ARAM | (1<<16)); + else + GenerateDSPInterrupt(INT_ARAM); // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks if (g_arDMA.Cnt.dir) From 5bb6ac8164c4cbd8e24ad6878c4e6d0e1a00c593 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Mar 2013 16:18:55 +1100 Subject: [PATCH 25/31] Increased the FIFO buffer size to 2MB from 1MB. Fixes Killer 7's Angel boss. Fixes issue 5415. --- Source/Core/VideoCommon/Src/Fifo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/Src/Fifo.h b/Source/Core/VideoCommon/Src/Fifo.h index 75e7d782f5..f465db8f85 100644 --- a/Source/Core/VideoCommon/Src/Fifo.h +++ b/Source/Core/VideoCommon/Src/Fifo.h @@ -23,7 +23,7 @@ class PointerWrap; -#define FIFO_SIZE (1024*1024) +#define FIFO_SIZE (2*1024*1024) extern volatile bool g_bSkipCurrentFrame; From 643f18d28bac1d4bf671a6aca4626490345f214e Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Mar 2013 22:35:10 +1100 Subject: [PATCH 26/31] Added direct GameCube controller commands to the Serial Interface emulation. Fixes the controls in MaxPlay Classic Games Volume 1 and the Action Replay disc. --- Data/User/GameConfig/GNHE5d.ini | 4 ++-- Source/Core/Core/Src/HW/SI_DeviceGCController.cpp | 15 ++++++++++++++- Source/Core/Core/Src/HW/SI_DeviceGCController.h | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Data/User/GameConfig/GNHE5d.ini b/Data/User/GameConfig/GNHE5d.ini index a9ba54d048..a1c2082fde 100644 --- a/Data/User/GameConfig/GNHE5d.ini +++ b/Data/User/GameConfig/GNHE5d.ini @@ -3,8 +3,8 @@ #Values set here will override the main dolphin settings. [EmuState] #The Emulation State. 1 is worst, 5 is best, 0 is not set. -EmulationStateId = 1 -EmulationIssues = +EmulationStateId = 4 +EmulationIssues = Enable the GameCube BIOS to allow the game to boot. [OnFrame] +$Nop Hack 0x80025BA0:dword:0x60000000 diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp index 8c4c562b82..f6af63da53 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp +++ b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp @@ -69,6 +69,19 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength) *(u32*)&_pBuffer[0] = SI_GC_CONTROLLER; break; + case CMD_DIRECT: + { + INFO_LOG(SERIALINTERFACE, "PAD - Direct (Length: %d)", _iLength); + u32 high, low; + GetData(high, low); + for (int i = 0; i < (_iLength - 1) / 2; i++) + { + _pBuffer[0 + i] = (high >> (i * 8)) & 0xff; + _pBuffer[4 + i] = (low >> (i * 8)) & 0xff; + } + } + break; + case CMD_ORIGIN: { INFO_LOG(SERIALINTERFACE, "PAD - Get Origin"); @@ -96,7 +109,7 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength) default: { ERROR_LOG(SERIALINTERFACE, "unknown SI command (0x%x)", command); - PanicAlert("SI: Unknown command"); + PanicAlert("SI: Unknown command (0x%x)", command); } break; } diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCController.h b/Source/Core/Core/Src/HW/SI_DeviceGCController.h index 058dde3582..b89f433331 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCController.h +++ b/Source/Core/Core/Src/HW/SI_DeviceGCController.h @@ -31,6 +31,7 @@ private: enum EBufferCommands { CMD_RESET = 0x00, + CMD_DIRECT = 0x40, CMD_ORIGIN = 0x41, CMD_RECALIBRATE = 0x42, }; From 6d1a0f5c2a40f6c9a244264b0ee073405d518e28 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Mar 2013 23:43:37 +1100 Subject: [PATCH 27/31] Enabled Synchronise GPU on "SPEED CHALLENGE - Jacques Villeneuve's Racing Vision". Required to go in-game. --- Data/User/GameConfig/GSZP41.ini | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Data/User/GameConfig/GSZP41.ini diff --git a/Data/User/GameConfig/GSZP41.ini b/Data/User/GameConfig/GSZP41.ini new file mode 100644 index 0000000000..e60e164899 --- /dev/null +++ b/Data/User/GameConfig/GSZP41.ini @@ -0,0 +1,17 @@ +# GSZP41 - SPEED CHALLENGE - Jacques Villeneuve's Racing Vision +[Core] Values set here will override the main dolphin settings. +SyncGPU = 1 +[EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set. +EmulationStateId = 4 +EmulationIssues = +[OnFrame] Add memory patches to be applied every frame here. +[ActionReplay] Add action replay cheats here. +[Video] +ProjectionHack = 0 +PH_SZNear = 0 +PH_SZFar = 0 +PH_ExtraParam = 0 +PH_ZNear = +PH_ZFar = +UseBBox = 1 +[Gecko] From 6dbd80d73e55f26fb8676b83779dbfb42d434d41 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 3 Mar 2013 15:20:15 +1100 Subject: [PATCH 28/31] Reverted the audio streaming tweak from r7a04ec6f9220dd66147c86baaebc2f9e05d65ead as it was causing audio clipping in some Wii games like Fire Emblem: Radiant Dawn. --- Source/Core/Core/Src/HW/DSP.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index 9d5211bcfa..9dfaf35a55 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -47,7 +47,6 @@ #include "../PowerPC/PowerPC.h" #include "../ConfigManager.h" #include "../DSPEmulator.h" -#include "SystemTimers.h" namespace DSP { @@ -153,7 +152,7 @@ struct AudioDMA SourceAddress = 0; ReadAddress = 0; AudioDMAControl.Hex = 0; - BlocksLeft = 1; + BlocksLeft = 0; } }; @@ -356,7 +355,7 @@ void Read16(u16& _uReturnValue, const u32 _iAddress) // AI case AUDIO_DMA_BLOCKS_LEFT: - _uReturnValue = g_audioDMA.BlocksLeft; + _uReturnValue = g_audioDMA.BlocksLeft > 0 ? g_audioDMA.BlocksLeft - 1 : 0; // AUDIO_DMA_BLOCKS_LEFT is zero based break; case AUDIO_DMA_START_LO: @@ -666,7 +665,7 @@ void UpdateDSPSlice(int cycles) { // This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm) void UpdateAudioDMA() { - if (g_audioDMA.AudioDMAControl.Enable && g_audioDMA.BlocksLeft > -1) + if (g_audioDMA.AudioDMAControl.Enable && g_audioDMA.BlocksLeft) { // Read audio at g_audioDMA.ReadAddress in RAM and push onto an // external audio fifo in the emulator, to be mixed with the disc @@ -676,7 +675,7 @@ void UpdateAudioDMA() g_audioDMA.BlocksLeft--; g_audioDMA.ReadAddress += 32; - if (g_audioDMA.BlocksLeft == -1) + if (g_audioDMA.BlocksLeft == 0) { dsp_emulator->DSP_SendAIBuffer(g_audioDMA.SourceAddress, 8*g_audioDMA.AudioDMAControl.NumBlocks); GenerateDSPInterrupt(DSP::INT_AID); From 61b01474fa2d2ad1da81742316e339984b4033cc Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 3 Mar 2013 23:35:07 +1100 Subject: [PATCH 29/31] Bumped up the disc transfer speed enough to prevent audio stuttering in Gauntlet: Dark Legacy. Fixes issue 4644. --- Source/Core/Core/Src/HW/DVDInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/Src/HW/DVDInterface.cpp b/Source/Core/Core/Src/HW/DVDInterface.cpp index b03edc5132..57c85a82ed 100644 --- a/Source/Core/Core/Src/HW/DVDInterface.cpp +++ b/Source/Core/Core/Src/HW/DVDInterface.cpp @@ -32,7 +32,7 @@ #include "../Movie.h" // Disc transfer rate measured in bytes per second -static const u32 DISC_TRANSFER_RATE_GC = 3125 * 1024; +static const u32 DISC_TRANSFER_RATE_GC = 4 * 1024 * 1024; namespace DVDInterface { From 0f1ef49da19cd8636a812d7c881052280df6444d Mon Sep 17 00:00:00 2001 From: skidau Date: Wed, 6 Mar 2013 17:08:40 +1100 Subject: [PATCH 30/31] Used a scheduled event to generate the ARAM DMA interrupt if the DMA is greater than a certain size. Fixes NFS:HP2 GC. --- Source/Core/Core/Src/HW/DSP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index 9dfaf35a55..fa1d23f186 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -697,7 +697,7 @@ void Do_ARAM_DMA() if (!GetDSPEmulator()->IsLLE()) g_dspState.DSPControl.DMAState = 1; - if (g_arDMA.Cnt.dir) + if (g_arDMA.Cnt.dir || g_arDMA.Cnt.count > 10240) CoreTiming::ScheduleEvent_Threadsafe(0, et_GenerateDSPInterrupt, INT_ARAM | (1<<16)); else GenerateDSPInterrupt(INT_ARAM); From d3e431af9e8e1f73dbcd4b0fd377749efc254f55 Mon Sep 17 00:00:00 2001 From: skidau Date: Thu, 7 Mar 2013 22:16:00 +1100 Subject: [PATCH 31/31] Set g_bSignalTokenInterrupt on the main thread. Fixes the random hang in Harry Potter: Prisoner of Azkaban. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index aec160ddd2..ce4881252f 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -383,7 +383,10 @@ void SetToken_OnMainThread(u64 userdata, int cyclesLate) Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, userdata & 0xffff); INFO_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken); if (userdata >> 16) + { + Common::AtomicStore(*(volatile u32*)&g_bSignalTokenInterrupt, 1); UpdateInterrupts(); + } CommandProcessor::interruptTokenWaiting = false; IncrementCheckContextId(); }