From 760f777f5a08854e56057e3604d662b6d1ef07cf Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Mon, 20 Aug 2012 13:49:12 +0200 Subject: 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. --- Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp | 38 +++++++++++++++++------- Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h | 11 ++++++- 2 files changed, 37 insertions(+), 12 deletions(-) (limited to 'Source/Core') 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 -- cgit v1.2.3 From 1b6240f7f972caef7c53a7a51ff71b4653b3f00a Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 25 Jan 2013 20:04:31 +1100 Subject: Changed cmdidle to be idle on breakpoint. Added low watermark interrupts generated by the gather pipe. Fixes Gladius from not booting. Fixes issue 5518. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 22 +++++++++++++++++----- Source/Core/VideoCommon/Src/CommandProcessor.h | 1 + Source/Core/VideoCommon/Src/Fifo.cpp | 4 +++- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'Source/Core') 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. -- cgit v1.2.3 From 867bfaa696807b3cb99b7af6cb9302f39d355ba3 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Feb 2013 20:50:40 +1100 Subject: 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(-) (limited to 'Source/Core') 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; -- cgit v1.2.3 From 9a4e9da7c25f1aa72f5b47e9914f464e966ad7f9 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 3 Feb 2013 18:05:46 +1100 Subject: 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. --- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 8 +--- Source/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(-) (limited to 'Source/Core') 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]; -- cgit v1.2.3 From 2c8c8db731fe4d22fac0ec2f2064f6bceea35cd3 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 3 Feb 2013 23:52:18 +1100 Subject: Used AtomicStore to write to the signal interrupts. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Source/Core') 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(); } -- cgit v1.2.3 From 5f672baca319a8227c01c100275d4eaaa860782b Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 9 Feb 2013 17:46:48 +1100 Subject: 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(-) (limited to 'Source/Core') 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 -- cgit v1.2.3 From 056930cac809b1b9fbeeae3166c4cfaaf487c5a7 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 10 Feb 2013 13:33:56 +1100 Subject: 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(+) (limited to 'Source/Core') 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)); -- cgit v1.2.3 From 0cafc33edafe235573f847500e81652a5a1b2ad0 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 10 Feb 2013 18:24:30 +1100 Subject: Moved the token update to the CPU thread. --- Source/Core/VideoCommon/Src/PixelEngine.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'Source/Core') 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 -- cgit v1.2.3 From 17c4fcfd90b0f36ee05e098e582518f6dce20e37 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 10 Feb 2013 18:40:50 +1100 Subject: 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(-) (limited to 'Source/Core') 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)); -- cgit v1.2.3 From f26650e55bf1fc1a59c890008f00a96041b13fa8 Mon Sep 17 00:00:00 2001 From: skidau Date: Mon, 11 Feb 2013 08:42:16 +1100 Subject: 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(-) (limited to 'Source/Core') 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); } -- cgit v1.2.3 From 9bff8e00c86188354363e7b41dfe6f06c781cf5a Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 16 Feb 2013 12:51:09 +1100 Subject: 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. --- 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 +- Source/Core/VideoCommon/Src/CommandProcessor.cpp | 93 ++++++++++++------------ Source/Core/VideoCommon/Src/CommandProcessor.h | 8 +- Source/Core/VideoCommon/Src/Fifo.cpp | 75 +++++++++++-------- Source/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 - 14 files changed, 207 insertions(+), 108 deletions(-) (limited to 'Source/Core') 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) - { - while (FifoCommandRunnable()) - Decode(); - } - else + u32 totalCycles = 0; + u32 cycles = FifoCommandRunnable(); + while (cycles > 0) { - 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]; -- cgit v1.2.3 From 0c882cca664f1e26ad6b68cce2518af165f0c591 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 16 Feb 2013 13:54:58 +1100 Subject: 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(-) (limited to 'Source/Core') 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); + } } } } -- cgit v1.2.3 From 45efced2809a7201909f85f2ee87d4625939d932 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 16 Feb 2013 18:30:20 +1100 Subject: 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(-) (limited to 'Source/Core') 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(); } -- cgit v1.2.3 From 0e2c3f3483e230079b5b6107d3cc634f60d92c78 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 16 Feb 2013 22:54:10 +1100 Subject: 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(-) (limited to 'Source/Core') 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 { -- cgit v1.2.3 From 36f2082a5f50da83035c1f6a3be059f018d3299b Mon Sep 17 00:00:00 2001 From: skidau Date: Wed, 20 Feb 2013 23:38:25 +1100 Subject: 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(-) (limited to 'Source/Core') 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 { -- cgit v1.2.3 From 40b65fdc042fbf51e1020ba798f0ed88c815c438 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 24 Feb 2013 17:10:33 +1100 Subject: Merge aram-dma-fixes (r76a13604ef49b522281af75675f044d59a74e871) --- Source/Core/Core/Src/HW/DSP.cpp | 4 ---- .../Interpreter/Interpreter_SystemRegisters.cpp | 1 + Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp | 3 ++- .../Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp | 18 ++++++++++++++++++ Source/Core/Core/Src/PowerPC/Jit64IL/IR.h | 4 ++-- Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp | 18 ++++++++++++++++++ .../Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp | 2 +- 7 files changed, 42 insertions(+), 8 deletions(-) (limited to 'Source/Core') 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)); } // ============== -- cgit v1.2.3 From 6b28b3929a60a63b68216fbe2c60712b1da30ea0 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 24 Feb 2013 21:32:14 +1100 Subject: 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(-) (limited to 'Source/Core') 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) -- cgit v1.2.3 From e69c58ee8919b4c5f575b3e28b5a56b3fa265164 Mon Sep 17 00:00:00 2001 From: skidau Date: Tue, 26 Feb 2013 18:10:02 +1100 Subject: 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(-) (limited to 'Source/Core') 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)); -- cgit v1.2.3 From 0399959c3901b6297a90de28c71498d89fb75a78 Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 1 Mar 2013 00:59:38 +1100 Subject: Skipped the EE check if there is a CP interrupt pending. Fixes issue 4336. --- .../Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'Source/Core') 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; -- cgit v1.2.3 From 7a04ec6f9220dd66147c86baaebc2f9e05d65ead Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 1 Mar 2013 11:51:11 +1100 Subject: 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(-) (limited to 'Source/Core') 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); -- cgit v1.2.3 From 4c6bbec600ccef4d41245979ff40e6a022053fc8 Mon Sep 17 00:00:00 2001 From: skidau Date: Fri, 1 Mar 2013 20:23:55 +1100 Subject: 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(-) (limited to 'Source/Core') 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) -- cgit v1.2.3 From 3874b46a9311cf9ef2430c3c8b0b5264cbbe8dda Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Mar 2013 12:37:55 +1100 Subject: 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(-) (limited to 'Source/Core') 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) -- cgit v1.2.3 From 5bb6ac8164c4cbd8e24ad6878c4e6d0e1a00c593 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Mar 2013 16:18:55 +1100 Subject: 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(-) (limited to 'Source/Core') 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; -- cgit v1.2.3 From 643f18d28bac1d4bf671a6aca4626490345f214e Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 2 Mar 2013 22:35:10 +1100 Subject: Added direct GameCube controller commands to the Serial Interface emulation. Fixes the controls in MaxPlay Classic Games Volume 1 and the Action Replay disc. --- Source/Core/Core/Src/HW/SI_DeviceGCController.cpp | 15 ++++++++++++++- Source/Core/Core/Src/HW/SI_DeviceGCController.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Source/Core') 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, }; -- cgit v1.2.3 From 6dbd80d73e55f26fb8676b83779dbfb42d434d41 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 3 Mar 2013 15:20:15 +1100 Subject: 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(-) (limited to 'Source/Core') 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); -- cgit v1.2.3 From 61b01474fa2d2ad1da81742316e339984b4033cc Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 3 Mar 2013 23:35:07 +1100 Subject: 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(-) (limited to 'Source/Core') 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 { -- cgit v1.2.3 From 0f1ef49da19cd8636a812d7c881052280df6444d Mon Sep 17 00:00:00 2001 From: skidau Date: Wed, 6 Mar 2013 17:08:40 +1100 Subject: 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(-) (limited to 'Source/Core') 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); -- cgit v1.2.3 From d3e431af9e8e1f73dbcd4b0fd377749efc254f55 Mon Sep 17 00:00:00 2001 From: skidau Date: Thu, 7 Mar 2013 22:16:00 +1100 Subject: 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(+) (limited to 'Source/Core') 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(); } -- cgit v1.2.3 From be217bf0966e75891e931c63be469d844e3e7713 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 7 Mar 2013 20:28:18 -0500 Subject: Add a comment about Qualcomm in load stores. --- Source/Core/Common/Src/ArmEmitter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Source/Core') diff --git a/Source/Core/Common/Src/ArmEmitter.cpp b/Source/Core/Common/Src/ArmEmitter.cpp index a56724b78b..85e18e26aa 100644 --- a/Source/Core/Common/Src/ArmEmitter.cpp +++ b/Source/Core/Common/Src/ArmEmitter.cpp @@ -535,7 +535,9 @@ void ARMXEmitter::MRS (ARMReg dest) void ARMXEmitter::WriteStoreOp(u32 op, ARMReg dest, ARMReg src, s16 op2) { - bool Index = op2 != 0 ? true : false; + // Qualcomm chipsets get /really/ angry if you don't use index, even if the offset is zero. + // bool Index = op2 != 0 ? true : false; + bool Index = true; bool Add = op2 >= 0 ? true : false; u32 imm = abs(op2); Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (src << 12) | imm); -- cgit v1.2.3 From f6d45ea4617515aa91a60e27c00a2902b1c81b18 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 8 Mar 2013 10:52:04 -0600 Subject: Fix a potential issue when someone has a CPU core that isn't available on that host set in the INI file, it would just fail out. Now it defaults to interpreter. --- Source/Core/Core/Src/PowerPC/JitInterface.cpp | 1 + Source/Core/Core/Src/PowerPC/JitInterface.h | 3 --- Source/Core/Core/Src/PowerPC/PowerPC.cpp | 9 ++++++++- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'Source/Core') diff --git a/Source/Core/Core/Src/PowerPC/JitInterface.cpp b/Source/Core/Core/Src/PowerPC/JitInterface.cpp index 8fcdd9837f..2f56f013be 100644 --- a/Source/Core/Core/Src/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/Src/PowerPC/JitInterface.cpp @@ -81,6 +81,7 @@ namespace JitInterface default: { PanicAlert("Unrecognizable cpu_core: %d", core); + jit = NULL; return NULL; break; } diff --git a/Source/Core/Core/Src/PowerPC/JitInterface.h b/Source/Core/Core/Src/PowerPC/JitInterface.h index a01c3dfa6e..9213072ab9 100644 --- a/Source/Core/Core/Src/PowerPC/JitInterface.h +++ b/Source/Core/Core/Src/PowerPC/JitInterface.h @@ -51,6 +51,3 @@ namespace JitInterface extern bool bFakeVMEM; extern bool bMMU; -#ifdef _M_ARM -#include "JitArm32/Jit.h" -#endif diff --git a/Source/Core/Core/Src/PowerPC/PowerPC.cpp b/Source/Core/Core/Src/PowerPC/PowerPC.cpp index d4447331e7..e65b310f5a 100644 --- a/Source/Core/Core/Src/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/Src/PowerPC/PowerPC.cpp @@ -161,13 +161,18 @@ void Init(int cpu_core) switch (cpu_core) { - case 0: + case 0: { cpu_core_base = interpreter; break; } default: cpu_core_base = JitInterface::InitJitCore(cpu_core); + if (!cpu_core_base) // Handle Situations where JIT core isn't available + { + WARN_LOG(POWERPC, "Jit core %d not available. Defaulting to interpreter.", cpu_core); + cpu_core_base = interpreter; + } break; } @@ -213,6 +218,8 @@ void SetMode(CoreMode new_mode) case MODE_JIT: // Switching from interpreter to JIT. // Don't really need to do much. It'll work, the cache will refill itself. cpu_core_base = JitInterface::GetCore(); + if (!cpu_core_base) // Has a chance to not get a working JIT core if one isn't active on host + cpu_core_base = interpreter; break; } } -- cgit v1.2.3 From 723371e022bd9cfd3f60282dd3072e3c89b00606 Mon Sep 17 00:00:00 2001 From: Rachel Bryk Date: Fri, 8 Mar 2013 20:05:53 -0500 Subject: Wow, I'm dumb. Fix mismatched set/get. --- Source/Core/Core/Src/ConfigManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core') diff --git a/Source/Core/Core/Src/ConfigManager.cpp b/Source/Core/Core/Src/ConfigManager.cpp index 8b6fec58e2..317bddb933 100644 --- a/Source/Core/Core/Src/ConfigManager.cpp +++ b/Source/Core/Core/Src/ConfigManager.cpp @@ -413,7 +413,7 @@ void SConfig::LoadSettings() ini.Get("Core", "GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, ""); // Movie - ini.Get("General", "PauseMovie", &m_PauseMovie, false); + ini.Get("Movie", "PauseMovie", &m_PauseMovie, false); ini.Get("Movie", "Author", &m_strMovieAuthor, ""); // DSP -- cgit v1.2.3 From 13a64e992d7645c8658784f78802329f1459ffa2 Mon Sep 17 00:00:00 2001 From: Rachel Bryk Date: Fri, 8 Mar 2013 22:47:56 -0500 Subject: Fix a typo. --- Source/Core/DolphinWX/Src/ConfigMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core') diff --git a/Source/Core/DolphinWX/Src/ConfigMain.cpp b/Source/Core/DolphinWX/Src/ConfigMain.cpp index 329906a301..cbd780e6aa 100644 --- a/Source/Core/DolphinWX/Src/ConfigMain.cpp +++ b/Source/Core/DolphinWX/Src/ConfigMain.cpp @@ -875,7 +875,7 @@ void CConfigMain::OnOk(wxCommandEvent& WXUNUSED (event)) { Close(); - // Save the config. Dolphin crashes to often to save the settings on closing only + // Save the config. Dolphin crashes too often to only save the settings on closing SConfig::GetInstance().SaveSettings(); } -- cgit v1.2.3 From 9a3633924df3a30dccea7ddb67ffddcf4ea5ee5c Mon Sep 17 00:00:00 2001 From: Rachel Bryk Date: Sat, 9 Mar 2013 02:44:24 -0500 Subject: Dolphin needs to be restarted before playing back a wiimote movie, so let's suggest that instead of giving an unhelpful error message. --- Source/Core/Core/Src/Movie.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Source/Core') diff --git a/Source/Core/Core/Src/Movie.cpp b/Source/Core/Core/Src/Movie.cpp index 6c646f6870..e72163cddb 100644 --- a/Source/Core/Core/Src/Movie.cpp +++ b/Source/Core/Core/Src/Movie.cpp @@ -1043,7 +1043,7 @@ bool PlayWiimote(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf, if (size != sizeInMovie) { - PanicAlertT("Fatal desync. Aborting playback. (Error in PlayWiimote: %u != %u, byte %u.)%s", (u32)sizeInMovie, (u32)size, (u32)g_currentByte, (g_numPads & 0xF)?" Try re-creating the recording with all GameCube controllers disabled (in Configure > Gamecube > Device Settings).":""); + PanicAlertT("Fatal desync. Aborting playback. (Error in PlayWiimote: %u != %u, byte %u.)%s", (u32)sizeInMovie, (u32)size, (u32)g_currentByte, (g_numPads & 0xF)?" Try re-creating the recording with all GameCube controllers disabled (in Configure > Gamecube > Device Settings), or restarting Dolphin (Dolphin currently must be restarted every time before playing back a wiimote movie).":""); EndPlayInput(!g_bReadOnly); return false; } -- cgit v1.2.3 From b94b4a9e8f4ef0610154431f23f57ea67c99bcde Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 11 Mar 2013 13:57:55 -0500 Subject: Rebase ArmEmitter on PPSSPP's base. The loadstores are making my heart cry at this point. --- Source/Core/Common/Src/ArmEmitter.cpp | 439 ++++++++++++++------- Source/Core/Common/Src/ArmEmitter.h | 87 +++- Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp | 16 +- .../Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp | 2 +- .../Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp | 22 +- .../Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp | 2 +- .../PowerPC/JitArm32/JitArm_SystemRegisters.cpp | 4 +- Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp | 8 +- .../Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp | 4 +- 9 files changed, 401 insertions(+), 183 deletions(-) (limited to 'Source/Core') diff --git a/Source/Core/Common/Src/ArmEmitter.cpp b/Source/Core/Common/Src/ArmEmitter.cpp index 85e18e26aa..6b7a6f32ad 100644 --- a/Source/Core/Common/Src/ArmEmitter.cpp +++ b/Source/Core/Common/Src/ArmEmitter.cpp @@ -83,65 +83,104 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated) } } -void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize) +void ARMXEmitter::MOVI2F(ARMReg dest, float val, ARMReg tempReg) +{ + union {float f; u32 u;} conv; + conv.f = val; + MOVI2R(tempReg, conv.u); + VMOV(dest, tempReg); + // TODO: VMOV an IMM directly if possible + // Otherwise, use a literal pool and VLDR directly (+- 1020) +} + +void ARMXEmitter::ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch) { Operand2 op2; bool inverse; - if (!optimize) - { - // Only used in backpatch atm - // Only support ARMv7 right now - if (cpu_info.bArmV7) { - MOVW(reg, val & 0xFFFF); - MOVT(reg, val, true); + if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + if (!inverse) { + AND(rd, rs, op2); + } else { + BIC(rd, rs, op2); } - else + } else { + MOVI2R(scratch, val); + AND(rd, rs, scratch); + } +} + +void ARMXEmitter::ORI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch) +{ + Operand2 op2; + if (TryMakeOperand2(val, op2)) { + ORR(rd, rs, op2); + } else { + MOVI2R(scratch, val); + ORR(rd, rs, scratch); + } +} + +void ARMXEmitter::FlushLitPool() +{ + for(std::vector::iterator it = currentLitPool.begin(); it != currentLitPool.end(); ++it) { + // Search for duplicates + for(std::vector::iterator old_it = currentLitPool.begin(); old_it != it; ++old_it) { + if ((*old_it).val == (*it).val) + (*it).loc = (*old_it).loc; + } + + // Write the constant to Literal Pool + if (!(*it).loc) { - // ARMv6 version won't use backpatch for now - // Run again with optimizations - MOVI2R(reg, val); + (*it).loc = (s32)code; + Write32((*it).val); } - } else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { - if (!inverse) - MOV(reg, op2); - else - MVN(reg, op2); + s32 offset = (*it).loc - (s32)(*it).ldr_address - 8; + + // Backpatch the LDR + *(u32*)(*it).ldr_address |= (offset >= 0) << 23 | abs(offset); + } + // TODO: Save a copy of previous pools in case they are still in range. + currentLitPool.clear(); +} + +void ARMXEmitter::AddNewLit(u32 val) +{ + LiteralPool pool_item; + pool_item.loc = 0; + pool_item.val = val; + pool_item.ldr_address = code; + currentLitPool.push_back(pool_item); +} + +void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize) +{ + Operand2 op2; + bool inverse; + + if (cpu_info.bArmV7 && !optimize) + { + // For backpatching on ARMv7 + MOVW(reg, val & 0xFFFF); + MOVT(reg, val, true); + } + else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + inverse ? MVN(reg, op2) : MOV(reg, op2); } else { - if (cpu_info.bArmV7) { - // ARMv7 - can use MOVT/MOVW, best choice + if (cpu_info.bArmV7) + { + // Use MOVW+MOVT for ARMv7+ MOVW(reg, val & 0xFFFF); if(val & 0xFFFF0000) MOVT(reg, val, true); } else { - // ARMv6 - fallback sequence. - // TODO: Optimize further. Can for example choose negation etc. - // Literal pools is another way to do this but much more complicated - // so I can't really be bothered for an outdated CPU architecture like ARMv6. - bool first = true; - int shift = 16; - for (int i = 0; i < 4; i++) { - if (val & 0xFF) { - if (first) { - MOV(reg, Operand2((u8)val, (u8)(shift & 0xF))); - first = false; - } else { - ORR(reg, reg, Operand2((u8)val, (u8)(shift & 0xF))); - } - } - shift -= 4; - val >>= 8; - } + // Use literal pool for ARMv6. + AddNewLit(val); + LDRLIT(reg, 0, 0); // To be backpatched later } } } -// Moves IMM to memory location -void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val) -{ - // This moves imm to a memory location - MOVW(R14, val); MOVT(R14, val, true); - MOVW(R12, op); MOVT(R12, op, true); - STR(R12, R14); // R10 is what we want to store -} + void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) { MOVI2R(reg, (u32)(func)); BL(reg); @@ -151,6 +190,9 @@ void ARMXEmitter::SetCodePtr(u8 *ptr) { code = ptr; startcode = code; +#ifdef IOS + lastCacheFlushEnd = ptr; +#endif } const u8 *ARMXEmitter::GetCodePtr() const @@ -194,10 +236,11 @@ void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end) #elif defined(BLACKBERRY) msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE); #elif defined(IOS) - sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); + if (start != NULL) + sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); #elif !defined(_WIN32) #ifndef ANDROID - start = startcode; + start = startcode; // Should be Linux Only #endif __builtin___clear_cache(start, end); #endif @@ -474,6 +517,7 @@ void ARMXEmitter::LSL (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedData void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, true, dest, src, op2);} void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);} void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, true, dest, src, op2);} +void ARMXEmitter::LSR (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(3, false, dest, src, op2);} void ARMXEmitter::MUL (ARMReg dest, ARMReg src, ARMReg op2) { Write32(condition | (dest << 16) | (src << 8) | (9 << 4) | op2); @@ -497,10 +541,38 @@ void ARMXEmitter::SMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) Write4OpMultiply(0xC, destLo, destHi, rn, rm); } +void ARMXEmitter::UMLAL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) +{ + Write4OpMultiply(0xA, destLo, destHi, rn, rm); +} + +void ARMXEmitter::SMLAL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) +{ + Write4OpMultiply(0xE, destLo, destHi, rn, rm); +} + +void ARMXEmitter::UBFX(ARMReg dest, ARMReg rn, u8 lsb, u8 width) +{ + Write32(condition | (0x7E0 << 16) | ((width - 1) << 16) | (dest << 12) | (lsb << 7) | (5 << 4) | rn); +} + +void ARMXEmitter::CLZ(ARMReg rd, ARMReg rm) +{ + Write32(condition | (0x16F << 16) | (rd << 12) | (0xF1 << 4) | rm); +} + +void ARMXEmitter::BFI(ARMReg rd, ARMReg rn, u8 lsb, u8 width) +{ + u32 msb = (lsb + width - 1); + if (msb > 31) msb = 31; + Write32(condition | (0x7C0 << 16) | (msb << 16) | (rd << 12) | (lsb << 7) | (1 << 4) | rn); +} + void ARMXEmitter::SXTB (ARMReg dest, ARMReg op2) { Write32(condition | (0x6AF << 16) | (dest << 12) | (7 << 4) | op2); } + void ARMXEmitter::SXTH (ARMReg dest, ARMReg op2, u8 rotation) { SXTAH(dest, (ARMReg)15, op2, rotation); @@ -511,9 +583,13 @@ void ARMXEmitter::SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation) // information Write32(condition | (0x6B << 20) | (src << 16) | (dest << 12) | (rotation << 10) | (7 << 4) | op2); } -void ARMXEmitter::REV (ARMReg dest, ARMReg src ) +void ARMXEmitter::RBIT(ARMReg dest, ARMReg src) { - Write32(condition | (107 << 20) | (15 << 16) | (dest << 12) | (243 << 4) | src); + Write32(condition | (0x6F << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src); +} +void ARMXEmitter::REV (ARMReg dest, ARMReg src) +{ + Write32(condition | (0x6B << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src); } void ARMXEmitter::REV16(ARMReg dest, ARMReg src) { @@ -533,29 +609,46 @@ void ARMXEmitter::MRS (ARMReg dest) Write32(condition | (16 << 20) | (15 << 16) | (dest << 12)); } -void ARMXEmitter::WriteStoreOp(u32 op, ARMReg dest, ARMReg src, s16 op2) +void ARMXEmitter::WriteStoreOp(u32 op, ARMReg src, ARMReg dest, s16 op2) +{ + // Qualcomm chipsets get /really/ angry if you don't use index, even if the offset is zero. + // bool Index = op2 != 0 ? true : false; + bool Index = true; + bool Add = op2 >= 0 ? true : false; + u32 imm = abs(op2); + Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (src << 12) | imm); +} +void ARMXEmitter::STR (ARMReg result, ARMReg base, s16 op) { WriteStoreOp(0x40, base, result, op);} +void ARMXEmitter::STRH (ARMReg result, ARMReg base, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x04 << 20) | (base << 16) | (result << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F)); +} +void ARMXEmitter::STRB (ARMReg result, ARMReg base, s16 op) { WriteStoreOp(0x44, base, result, op);} +void ARMXEmitter::STR (ARMReg result, ARMReg base, Operand2 op2, bool Index, bool Add) +{ + Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | op2.IMMSR()); +} +void ARMXEmitter::STR (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | offset); +} +void ARMXEmitter::STRH (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add) { - // Qualcomm chipsets get /really/ angry if you don't use index, even if the offset is zero. - // bool Index = op2 != 0 ? true : false; - bool Index = true; - bool Add = op2 >= 0 ? true : false; - u32 imm = abs(op2); - Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (src << 12) | imm); + Write32(condition | (0x00 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | (0xB << 4) | offset); } -void ARMXEmitter::STR (ARMReg dest, ARMReg src, s16 op) { WriteStoreOp(0x40, dest, src, op);} -void ARMXEmitter::STRB(ARMReg dest, ARMReg src, s16 op) { WriteStoreOp(0x44, dest, src, op);} -void ARMXEmitter::STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +void ARMXEmitter::STRB (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add) { - Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | offset); + Write32(condition | (0x64 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | offset); } void ARMXEmitter::LDREX(ARMReg dest, ARMReg base) { Write32(condition | (25 << 20) | (base << 16) | (dest << 12) | 0xF9F); } -void ARMXEmitter::STREX(ARMReg dest, ARMReg base, ARMReg op) +void ARMXEmitter::STREX(ARMReg result, ARMReg base, ARMReg op) { - _assert_msg_(DYNA_REC, (dest != base && dest != op), "STREX dest can't be other two registers"); - Write32(condition | (24 << 20) | (base << 16) | (dest << 12) | (0xF9 << 4) | op); + _assert_msg_(DYNA_REC, (result != base && result != op), "STREX dest can't be other two registers"); + Write32(condition | (24 << 20) | (base << 16) | (result << 12) | (0xF9 << 4) | op); } void ARMXEmitter::DMB () { @@ -572,12 +665,44 @@ void ARMXEmitter::LDRH(ARMReg dest, ARMReg src, Operand2 op) u8 Imm = op.Imm8(); Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F)); } +void ARMXEmitter::LDRSH(ARMReg dest, ARMReg src, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xF << 4) | (Imm & 0x0F)); +} void ARMXEmitter::LDRB(ARMReg dest, ARMReg src, s16 op) { WriteStoreOp(0x45, src, dest, op);} +void ARMXEmitter::LDRSB(ARMReg dest, ARMReg src, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xD << 4) | (Imm & 0x0F)); +} -void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +void ARMXEmitter::LDR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add) +{ + Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | op2.IMMSR()); +} +void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) { Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset); } +void ARMXEmitter::LDRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xB << 4) | offset); +} +void ARMXEmitter::LDRSH(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xF << 4) | offset); +} +void ARMXEmitter::LDRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x65 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset); +} +void ARMXEmitter::LDRSB(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xD << 4) | offset); +} +void ARMXEmitter::LDRLIT (ARMReg dest, u32 offset, bool Add) { Write32(condition | 0x05 << 24 | Add << 23 | 0x1F << 16 | dest << 12 | offset);} + void ARMXEmitter::WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList) { Write32(condition | (op << 20) | (WriteBack << 21) | (dest << 16) | RegList); @@ -673,9 +798,8 @@ void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset) _assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VLDR: Offset needs to be word aligned and small enough"); - if (imm & 0xC03) { + if (imm & 0xC03) ERROR_LOG(DYNA_REC, "VLDR: Bad offset %08x", imm); - } bool single_reg = Dest < D0; @@ -683,13 +807,13 @@ void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset) if (single_reg) { - Write32(NO_COND | (0xD << 24) | (Add << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \ + Write32(condition | (0xD << 24) | (Add << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \ | ((Dest & 0x1E) << 11) | (10 << 8) | (imm >> 2)); } else { - Write32(NO_COND | (0xD << 24) | (Add << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \ + Write32(condition | (0xD << 24) | (Add << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \ | ((Dest & 0xF) << 12) | (11 << 8) | (imm >> 2)); } } @@ -703,9 +827,8 @@ void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, s16 offset) _assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VSTR: Offset needs to be word aligned and small enough"); - if (imm & 0xC03) { + if (imm & 0xC03) ERROR_LOG(DYNA_REC, "VSTR: Bad offset %08x", imm); - } bool single_reg = Src < D0; @@ -713,17 +836,16 @@ void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, s16 offset) if (single_reg) { - Write32(NO_COND | (0xD << 24) | (Add << 23) | ((Src & 0x1) << 22) | (Base << 16) \ + Write32(condition | (0xD << 24) | (Add << 23) | ((Src & 0x1) << 22) | (Base << 16) \ | ((Src & 0x1E) << 11) | (10 << 8) | (imm >> 2)); - } else { - Write32(NO_COND | (0xD << 24) | (Add << 23) | ((Src & 0x10) << 18) | (Base << 16) \ + Write32(condition | (0xD << 24) | (Add << 23) | ((Src & 0x10) << 18) | (Base << 16) \ | ((Src & 0xF) << 12) | (11 << 8) | (imm >> 2)); } } -void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm) +void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm, bool E) { _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP"); bool single_reg = Vd < D0; @@ -733,16 +855,16 @@ void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \ - | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \ + | (E << 7) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \ - | (0x2F << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF)); + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \ + | (E << 7) | (0x2C << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF)); } } -void ARMXEmitter::VCMP(ARMReg Vd) +void ARMXEmitter::VCMP(ARMReg Vd, bool E) { _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP"); bool single_reg = Vd < D0; @@ -751,15 +873,26 @@ void ARMXEmitter::VCMP(ARMReg Vd) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \ - | (0x2B << 6)); + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \ + | (E << 7) | (0x29 << 6)); } else { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \ - | (0x2F << 6)); + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \ + | (E << 7) | (0x2C << 6)); } } + +void ARMXEmitter::VMRS_APSR() { + Write32(condition | 0xEF10A10 | (15 << 12)); +} +void ARMXEmitter::VMRS(ARMReg Rt) { + Write32(condition | (0xEF << 20) | (1 << 16) | (Rt << 12) | 0xA10); +} +void ARMXEmitter::VMSR(ARMReg Rt) { + Write32(condition | (0xEE << 20) | (1 << 16) | (Rt << 12) | 0xA10); +} + void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm) { _assert_msg_(DYNA_REC, Vd < Q0, "Pased invalid dest register to VSQRT"); @@ -773,13 +906,13 @@ void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | ((Vn & 0x1E) << 15) \ + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | ((Vn & 0x1E) << 15) \ | ((Vd & 0x1E) << 11) | (0xA << 8) | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) \ | (Vm >> 1)); } else { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \ + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \ | ((Vd & 0xF) << 12) | (0xB << 8) | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) \ | (Vm & 0xF)); } @@ -795,47 +928,17 @@ void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ | ((Vd & 0x1E) << 11) | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ | ((Vd & 0xF) << 12) | (0x2F << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } } // VFP and ASIMD -void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm) -{ - _assert_msg_(DYNA_REC, Vd < Q0, "VABS doesn't currently support Quad reg"); - _assert_msg_(DYNA_REC, Vd >= S0, "VABS doesn't support ARM Regs"); - bool single_reg = Vd < D0; - bool double_reg = Vd < Q0; - - Vd = SubBase(Vd); - Vm = SubBase(Vm); - - if (single_reg) - { - Write32(NO_COND | (0xEB << 20) | ((Vd & 0x1) << 22) | ((Vd & 0x1E) << 11) \ - | (0xAC << 4) | ((Vm & 0x1) << 5) | (Vm >> 1)); - } - else - { - if (double_reg) - { - Write32(NO_COND | (0xEB << 20) | ((Vd & 0x10) << 18) | ((Vd & 0xF) << 12) \ - | (0xBC << 4) | ((Vm & 0x10) << 1) | (Vm & 0xF)); - } - else - { - _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VADD with Quad Reg without support!"); - // XXX: TODO - } - } -} - void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) { _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD"); @@ -850,7 +953,7 @@ void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); } @@ -858,7 +961,7 @@ void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) { if (double_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } @@ -885,7 +988,7 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ | ((Vn & 0x1) << 7) | (1 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); } @@ -893,7 +996,7 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) { if (double_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ | ((Vn & 0x10) << 3) | (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } @@ -906,6 +1009,7 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) } } } +// VFP and ASIMD void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm) { _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD"); @@ -920,7 +1024,7 @@ void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x2 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x2 << 20) \ | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); } @@ -928,46 +1032,87 @@ void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm) { if (double_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x2 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x2 << 20) \ | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } else { _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VMUL with Quad Reg without support!"); - // XXX: TODO } } } -void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm) +void ARMXEmitter::VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm) { - _assert_msg_(DYNA_REC, Vd < Q0, "VNEG doesn't currently support Quad reg"); - _assert_msg_(DYNA_REC, Vd >= S0, "VNEG doesn't support ARM Regs"); + _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VMLA"); + _assert_msg_(DYNA_REC, Vn >= S0, "Passed invalid Vn to VMLA"); + _assert_msg_(DYNA_REC, Vm >= S0, "Passed invalid Vm to VMLA"); bool single_reg = Vd < D0; bool double_reg = Vd < Q0; Vd = SubBase(Vd); + Vn = SubBase(Vn); Vm = SubBase(Vm); if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ - | ((Vd & 0x1E) << 11) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + Write32(condition | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x0 << 20) \ + | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ + | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + _assert_msg_(DYNA_REC, false, "VMLA: Please implement!"); + } +} + +void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd < Q0, "VABS doesn't currently support Quad reg"); + _assert_msg_(DYNA_REC, Vd >= S0, "VABS doesn't support ARM Regs"); + bool single_reg = Vd < D0; + bool double_reg = Vd < Q0; + + Vd = SubBase(Vd); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(condition | (0xEB << 20) | ((Vd & 0x1) << 22) | ((Vd & 0x1E) << 11) \ + | (0xAC << 4) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else { if (double_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ - | ((Vd & 0xF) << 12) | (0x2D << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + Write32(condition | (0xEB << 20) | ((Vd & 0x10) << 18) | ((Vd & 0xF) << 12) \ + | (0xBC << 4) | ((Vm & 0x10) << 1) | (Vm & 0xF)); } else { - _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VNEG with Quad Reg without support!"); + _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VADD with Quad Reg without support!"); // XXX: TODO - } + } +} + +void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm) +{ + bool single_reg = Vd < D0; + + Vd = SubBase(Vd); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ + | ((Vd & 0x1E) << 11) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ + | ((Vd & 0xF) << 12) | (0x2D << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } } @@ -978,7 +1123,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high) Dest = SubBase(Dest); - Write32(NO_COND | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \ + Write32(condition | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \ | (11 << 8) | ((Dest & 0x10) << 3) | (1 << 4)); } @@ -992,7 +1137,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) { // Moving to a Neon register FROM ARM Reg Dest = (ARMReg)(Dest - S0); - Write32(NO_COND | (0xE0 << 20) | ((Dest & 0x1E) << 15) | (Src << 12) \ + Write32(condition | (0xE0 << 20) | ((Dest & 0x1E) << 15) | (Src << 12) \ | (0xA << 8) | ((Dest & 0x1) << 7) | (1 << 4)); return; } @@ -1012,7 +1157,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) { // Moving to ARM Reg from Neon Register Src = (ARMReg)(Src - S0); - Write32(NO_COND | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \ + Write32(condition | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \ | (0xA << 8) | ((Src & 0x1) << 7) | (1 << 4)); return; } @@ -1042,7 +1187,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) if (Single) { - Write32(NO_COND | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \ + Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \ | (0x5 << 9) | (1 << 6) | ((Src & 0x1) << 5) | ((Src & 0x1E) >> 1)); } else @@ -1059,10 +1204,28 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) } else { - Write32(NO_COND | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x3 << 20) | ((Dest & 0xF) << 12) \ + Write32(condition | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x3 << 20) | ((Dest & 0xF) << 12) \ | (0x2D << 6) | ((Src & 0x10) << 1) | (Src & 0xF)); } } } +void ARMXEmitter::VCVT(ARMReg Dest, ARMReg Source, int flags) +{ + bool single_reg = (Dest < D0) && (Source < D0); + int op = ((flags & TO_INT) ? (flags & ROUND_TO_ZERO) : (flags & IS_SIGNED)) ? 1 : 0; + int op2 = ((flags & TO_INT) ? (flags & IS_SIGNED) : 0) ? 1 : 0; + Dest = SubBase(Dest); + Source = SubBase(Source); + + if (single_reg) + { + Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \ + | ((Dest & 0x1E) << 11) | (op << 7) | (0x29 << 6) | ((Source & 0x1) << 5) | (Source >> 1)); + } else { + Write32(condition | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \ + | ((Dest & 0xF) << 12) | (1 << 8) | (op << 7) | (0x29 << 6) | ((Source & 0x10) << 1) | (Source & 0xF)); + } +} + } diff --git a/Source/Core/Common/Src/ArmEmitter.h b/Source/Core/Common/Src/ArmEmitter.h index d3155285f3..ce10008a37 100644 --- a/Source/Core/Common/Src/ArmEmitter.h +++ b/Source/Core/Common/Src/ArmEmitter.h @@ -25,6 +25,7 @@ #if defined(__SYMBIAN32__) || defined(PANDORA) #include #endif +#include #undef _IP #undef R0 @@ -32,6 +33,12 @@ #undef _LR #undef _PC +// VCVT flags +#define TO_FLOAT 0 +#define TO_INT 1 << 0 +#define IS_SIGNED 1 << 1 +#define ROUND_TO_ZERO 1 << 2 + namespace ArmGen { enum ARMReg @@ -171,7 +178,7 @@ public: Value = base; } - Operand2(u8 shift, ShiftType type, ARMReg base)// For IMM shifted register + Operand2(ARMReg base, ShiftType type, u8 shift)// For IMM shifted register { if(shift == 32) shift = 0; switch (type) @@ -327,6 +334,13 @@ struct FixupBranch int type; //0 = B 1 = BL }; +struct LiteralPool +{ + s32 loc; + u8* ldr_address; + u32 val; +}; + typedef const u8* JumpTarget; class ARMXEmitter @@ -336,8 +350,9 @@ private: u8 *code, *startcode; u8 *lastCacheFlushEnd; u32 condition; + std::vector currentLitPool; - void WriteStoreOp(u32 op, ARMReg dest, ARMReg src, s16 op2); + void WriteStoreOp(u32 op, ARMReg src, ARMReg dest, s16 op2); void WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList); void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, ARMReg op2); void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2); @@ -373,6 +388,10 @@ public: void FlushIcacheSection(u8 *start, u8 *end); u8 *GetWritableCodePtr(); + void FlushLitPool(); + void AddNewLit(u32 val); + + CCFlags GetCC() { return CCFlags(condition >> 28); } void SetCC(CCFlags cond = CC_AL); // Special purpose instructions @@ -425,8 +444,10 @@ public: void LSL (ARMReg dest, ARMReg src, ARMReg op2); void LSLS(ARMReg dest, ARMReg src, Operand2 op2); void LSLS(ARMReg dest, ARMReg src, ARMReg op2); + void LSR (ARMReg dest, ARMReg src, Operand2 op2); void SBC (ARMReg dest, ARMReg src, Operand2 op2); void SBCS(ARMReg dest, ARMReg src, Operand2 op2); + void RBIT(ARMReg dest, ARMReg src); void REV (ARMReg dest, ARMReg src); void REV16 (ARMReg dest, ARMReg src); void RSC (ARMReg dest, ARMReg src, Operand2 op2); @@ -457,33 +478,53 @@ public: void UMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); void SMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void UMLAL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void SMLAL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void SXTB(ARMReg dest, ARMReg op2); void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0); void SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation = 0); + void BFI(ARMReg rd, ARMReg rn, u8 lsb, u8 width); + void UBFX(ARMReg dest, ARMReg op2, u8 lsb, u8 width); + void CLZ(ARMReg rd, ARMReg rm); + // Using just MSR here messes with our defines on the PPC side of stuff (when this code was in dolphin...) // Just need to put an underscore here, bit annoying. void _MSR (bool nzcvq, bool g, Operand2 op2); - void _MSR (bool nzcvq, bool g, ARMReg src ); + void _MSR (bool nzcvq, bool g, ARMReg src); void MRS (ARMReg dest); // Memory load/store operations - void LDR (ARMReg dest, ARMReg src, s16 op2 = 0); + void LDR (ARMReg dest, ARMReg src, s16 op2 = 0); + void LDRH (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDRSH(ARMReg dest, ARMReg src, s16 op2 = 0); + void LDRB (ARMReg dest, ARMReg src, s16 op2 = 0); + void LDRSB(ARMReg dest, ARMReg src, Operand2 op2 = 0); // Offset adds to the base register in LDR - void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); - void LDRH(ARMReg dest, ARMReg src, Operand2 op = 0); - void LDRB(ARMReg dest, ARMReg src, s16 op2 = 0); - void STR (ARMReg dest, ARMReg src, s16 op2 = 0); + void LDR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add); + void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRSH(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRSB(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRLIT(ARMReg dest, u32 offset, bool Add); + + void STR (ARMReg result, ARMReg base, s16 op2 = 0); + void STRH (ARMReg result, ARMReg base, Operand2 op2 = 0); + void STRB (ARMReg result, ARMReg base, s16 op2 = 0); // Offset adds on to the destination register in STR - void STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void STR (ARMReg result, ARMReg base, Operand2 op2, bool Index, bool Add); + void STR (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add); + void STRH (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add); + void STRB (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add); - void STRB(ARMReg dest, ARMReg src, s16 op2 = 0); void STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...); void LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...); // Exclusive Access operations void LDREX(ARMReg dest, ARMReg base); - // dest contains the result if the instruction managed to store the value - void STREX(ARMReg dest, ARMReg base, ARMReg op); + // result contains the result if the instruction managed to store the value + void STREX(ARMReg result, ARMReg base, ARMReg op); void DMB (); void SVC(Operand2 op); @@ -501,9 +542,9 @@ public: // VFP Only void VLDR(ARMReg Dest, ARMReg Base, s16 offset); void VSTR(ARMReg Src, ARMReg Base, s16 offset); - void VCMP(ARMReg Vd, ARMReg Vm); + void VCMP(ARMReg Vd, ARMReg Vm, bool E); // Compares against zero - void VCMP(ARMReg Vd); + void VCMP(ARMReg Vd, bool E); void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VSQRT(ARMReg Vd, ARMReg Vm); @@ -513,13 +554,25 @@ public: void VABS(ARMReg Vd, ARMReg Vm); void VNEG(ARMReg Vd, ARMReg Vm); void VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm); + void VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VMOV(ARMReg Dest, ARMReg Src, bool high); void VMOV(ARMReg Dest, ARMReg Src); + void VCVT(ARMReg Dest, ARMReg Src, int flags); + + void VMRS_APSR(); + void VMRS(ARMReg Rt); + void VMSR(ARMReg Rt); void QuickCallFunction(ARMReg scratchreg, void *func); - // Utility functions + + // Wrapper around MOVT/MOVW with fallbacks. void MOVI2R(ARMReg reg, u32 val, bool optimize = true); - void ARMABI_MOVI2M(Operand2 op, Operand2 val); + void MOVI2F(ARMReg dest, float val, ARMReg tempReg); + + void ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch); + void ORI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch); + + }; // class ARMXEmitter @@ -556,7 +609,9 @@ public: // Call this when shutting down. Don't rely on the destructor, even though it'll do the job. void FreeCodeSpace() { +#ifndef __SYMBIAN32__ FreeMemoryPages(region, region_size); +#endif region = NULL; region_size = 0; } diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp index 8748ebac2d..45b17ab847 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp @@ -148,20 +148,20 @@ void JitArm::DoDownCount() if(js.downcountAmount < 255) // We can enlarge this if we used rotations { SUBS(rB, rB, js.downcountAmount); - STR(rA, rB); + STR(rB, rA); } else { ARMReg rC = gpr.GetReg(false); MOVI2R(rC, js.downcountAmount); SUBS(rB, rB, rC); - STR(rA, rB); + STR(rB, rA); } gpr.Unlock(rA, rB); } void JitArm::WriteExitDestInR(ARMReg Reg) { - STR(R9, Reg, PPCSTATE_OFF(pc)); + STR(Reg, R9, PPCSTATE_OFF(pc)); Cleanup(); DoDownCount(); MOVI2R(Reg, (u32)asm_routines.dispatcher); @@ -170,7 +170,7 @@ void JitArm::WriteExitDestInR(ARMReg Reg) } void JitArm::WriteRfiExitDestInR(ARMReg Reg) { - STR(R9, Reg, PPCSTATE_OFF(pc)); + STR(Reg, R9, PPCSTATE_OFF(pc)); Cleanup(); DoDownCount(); @@ -209,7 +209,7 @@ void JitArm::WriteExit(u32 destination, int exit_num) { ARMReg A = gpr.GetReg(false); MOVI2R(A, destination); - STR(R9, A, PPCSTATE_OFF(pc)); + STR(A, R9, PPCSTATE_OFF(pc)); MOVI2R(A, (u32)asm_routines.dispatcher); B(A); } @@ -384,7 +384,7 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo LDR(A, R9, PPCSTATE_OFF(msr)); TST(A, Shift); FixupBranch b1 = B_CC(CC_NEQ); - STR(R9, C, PPCSTATE_OFF(pc)); + STR(C, R9, PPCSTATE_OFF(pc)); MOVI2R(A, (u32)asm_routines.fpException); B(A); SetJumpTarget(b1); @@ -397,7 +397,7 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo MOVI2R(rA, (u32)&b->runCount); // Load in to register LDR(rB, rA); // Load the actual value in to R11. ADD(rB, rB, 1); // Add one to the value - STR(rA, rB); // Now store it back in the memory location + STR(rB, rA); // Now store it back in the memory location // get start tic PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStart); gpr.Unlock(rA, rB); @@ -461,7 +461,7 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo MOVI2R(RA, (u32)&opinfo->runCount); LDR(RB, RA); ADD(RB, RB, 1); - STR(RA, RB); + STR(RB, RA); gpr.Unlock(RA, RB); } if (!ops[i].skip) diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp index f95c2e58e6..690f56fdf6 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp @@ -38,7 +38,7 @@ using namespace ArmGen; ARMXEmitter emit((u8 *)location); emit.MOVI2R(R11, address); emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher); - emit.STR(R9, R11, PPCSTATE_OFF(pc)); + emit.STR(R11, R9, PPCSTATE_OFF(pc)); emit.B(R12); } diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp index 3aa972248d..8163415d8b 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp @@ -52,7 +52,7 @@ void JitArm::sc(UGeckoInstruction inst) ARMReg rA = gpr.GetReg(); LDR(rA, R9, PPCSTATE_OFF(Exceptions)); ORR(rA, rA, EXCEPTION_SYSCALL); - STR(R9, rA, PPCSTATE_OFF(Exceptions)); + STR(rA, R9, PPCSTATE_OFF(Exceptions)); gpr.Unlock(rA); WriteExceptionExit(); @@ -84,14 +84,14 @@ void JitArm::rfi(UGeckoInstruction inst) LDR(rD, R9, PPCSTATE_OFF(msr)); AND(rD, rD, rB); // rD = Masked MSR - STR(R9, rD, PPCSTATE_OFF(msr)); + STR(rD, R9, PPCSTATE_OFF(msr)); LDR(rB, R9, PPCSTATE_OFF(spr[SPR_SRR1])); // rB contains SRR1 here AND(rB, rB, rC); // rB contains masked SRR1 here ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1 - STR(R9, rB, PPCSTATE_OFF(msr)); // STR rB in to rA + STR(rB, R9, PPCSTATE_OFF(msr)); // STR rB in to rA LDR(rA, R9, PPCSTATE_OFF(spr[SPR_SRR0])); @@ -117,7 +117,7 @@ void JitArm::bx(UGeckoInstruction inst) ARMReg rA = gpr.GetReg(false); u32 Jumpto = js.compilerPC + 4; MOVI2R(rA, Jumpto); - STR(R9, rA, PPCSTATE_OFF(spr[SPR_LR])); + STR(rA, R9, PPCSTATE_OFF(spr[SPR_LR])); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } // If this is not the last instruction of a block, @@ -168,7 +168,7 @@ void JitArm::bcx(UGeckoInstruction inst) { LDR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); SUBS(rB, rB, 1); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_CTR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); //SUB(32, M(&CTR), Imm8(1)); if (inst.BO & BO_BRANCH_IF_CTR_0) @@ -193,7 +193,7 @@ void JitArm::bcx(UGeckoInstruction inst) { u32 Jumpto = js.compilerPC + 4; MOVI2R(rB, Jumpto); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_LR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_LR])); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); // Careful, destroys R14, R12 } gpr.Unlock(rA, rB); @@ -235,7 +235,7 @@ void JitArm::bcctrx(UGeckoInstruction inst) { u32 Jumpto = js.compilerPC + 4; MOVI2R(rA, Jumpto); - STR(R9, rA, PPCSTATE_OFF(spr[SPR_LR])); + STR(rA, R9, PPCSTATE_OFF(spr[SPR_LR])); // ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } MVN(rB, 0x3); // 0xFFFFFFFC @@ -269,7 +269,7 @@ void JitArm::bcctrx(UGeckoInstruction inst) if (inst.LK_3){ u32 Jumpto = js.compilerPC + 4; MOVI2R(rB, Jumpto); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_LR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_LR])); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR @@ -290,7 +290,7 @@ void JitArm::bclrx(UGeckoInstruction inst) ARMReg rA = gpr.GetReg(false); u32 Jumpto = js.compilerPC + 4; MOVI2R(rA, Jumpto); - STR(R9, rA, PPCSTATE_OFF(spr[SPR_LR])); + STR(rA, R9, PPCSTATE_OFF(spr[SPR_LR])); // ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } return; @@ -305,7 +305,7 @@ void JitArm::bclrx(UGeckoInstruction inst) { LDR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); SUBS(rB, rB, 1); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_CTR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); //SUB(32, M(&CTR), Imm8(1)); if (inst.BO & BO_BRANCH_IF_CTR_0) @@ -341,7 +341,7 @@ void JitArm::bclrx(UGeckoInstruction inst) if (inst.LK){ u32 Jumpto = js.compilerPC + 4; MOVI2R(rB, Jumpto); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_LR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_LR])); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp index 9d248ebd02..1617b0fef6 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp @@ -141,7 +141,7 @@ void JitArm::StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset) switch (accessSize) { case 32: - STR(dest, value); // 8 + STR(value, dest); // 8 break; case 16: // Not implemented diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp index bbad7b780a..7eb8963e15 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp @@ -70,7 +70,7 @@ void JitArm::mtspr(UGeckoInstruction inst) } // OK, this is easy. - STR(R9, RD, PPCSTATE_OFF(spr) + iIndex * 4); + STR(RD, R9, PPCSTATE_OFF(spr) + iIndex * 4); } void JitArm::mfspr(UGeckoInstruction inst) @@ -99,6 +99,6 @@ void JitArm::mtmsr(UGeckoInstruction inst) // Don't interpret this, if we do we get thrown out //JITDISABLE(SystemRegisters) - STR(R9, gpr.R(inst.RS), PPCSTATE_OFF(msr)); + STR(gpr.R(inst.RS), R9, PPCSTATE_OFF(msr)); WriteExit(js.compilerPC + 4, 0); } diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp index a5ae0a1e81..28263baa32 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp @@ -102,10 +102,10 @@ void JitArmAsmRoutineManager::Generate() fpException = GetCodePtr(); LDR(R0, R9, PPCSTATE_OFF(Exceptions)); ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE); - STR(R9, R0, PPCSTATE_OFF(Exceptions)); + STR(R0, R9, PPCSTATE_OFF(Exceptions)); QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); LDR(R0, R9, PPCSTATE_OFF(npc)); - STR(R9, R0, PPCSTATE_OFF(pc)); + STR(R0, R9, PPCSTATE_OFF(pc)); B(dispatcher); SetJumpTarget(bail); @@ -117,10 +117,10 @@ void JitArmAsmRoutineManager::Generate() // Does exception checking testExceptions = GetCodePtr(); LDR(R0, R9, PPCSTATE_OFF(pc)); - STR(R9, R0, PPCSTATE_OFF(npc)); + STR(R0, R9, PPCSTATE_OFF(npc)); QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); LDR(R0, R9, PPCSTATE_OFF(npc)); - STR(R9, R0, PPCSTATE_OFF(pc)); + STR(R0, R9, PPCSTATE_OFF(pc)); // Check the state pointer to see if we are exiting // Gets checked on every exception check MOVI2R(R0, (u32)PowerPC::GetStatePtr()); diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp index 18777fd8c9..2a9aa2154f 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp @@ -148,7 +148,7 @@ ARMReg ArmRegCache::R(u32 preg) return ArmCRegs[a].Reg; } // Alright, we couldn't get a free space, dump that least used register - emit->STR(R9, ArmCRegs[Num].Reg, PPCSTATE_OFF(gpr) + ArmCRegs[Num].PPCReg * 4); + emit->STR(ArmCRegs[Num].Reg, R9, PPCSTATE_OFF(gpr) + ArmCRegs[Num].PPCReg * 4); emit->LDR(ArmCRegs[Num].Reg, R9, PPCSTATE_OFF(gpr) + preg * 4); ArmCRegs[Num].PPCReg = preg; ArmCRegs[Num].LastLoad = 0; @@ -160,7 +160,7 @@ void ArmRegCache::Flush() for(u8 a = 0; a < NUMPPCREG; ++a) if (ArmCRegs[a].PPCReg != 33) { - emit->STR(R9, ArmCRegs[a].Reg, PPCSTATE_OFF(gpr) + ArmCRegs[a].PPCReg * 4); + emit->STR(ArmCRegs[a].Reg, R9, PPCSTATE_OFF(gpr) + ArmCRegs[a].PPCReg * 4); ArmCRegs[a].PPCReg = 33; ArmCRegs[a].LastLoad = 0; } -- cgit v1.2.3 From 8406d9972d9c992ee76d9bfddc11598a0419c00f Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Tue, 12 Mar 2013 02:35:29 +0000 Subject: Fix JIT from rebasing on PPSSPP ArmEmitter. --- Source/Core/Common/Src/ArmEmitter.cpp | 2 +- Source/Core/Common/Src/ArmEmitter.h | 2 +- Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp | 3 ++- Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp | 1 + Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp | 3 ++- Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp | 10 +++++----- 6 files changed, 12 insertions(+), 9 deletions(-) (limited to 'Source/Core') diff --git a/Source/Core/Common/Src/ArmEmitter.cpp b/Source/Core/Common/Src/ArmEmitter.cpp index 6b7a6f32ad..c6fd34f55c 100644 --- a/Source/Core/Common/Src/ArmEmitter.cpp +++ b/Source/Core/Common/Src/ArmEmitter.cpp @@ -616,7 +616,7 @@ void ARMXEmitter::WriteStoreOp(u32 op, ARMReg src, ARMReg dest, s16 op2) bool Index = true; bool Add = op2 >= 0 ? true : false; u32 imm = abs(op2); - Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (src << 12) | imm); + Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (src << 16) | (dest << 12) | imm); } void ARMXEmitter::STR (ARMReg result, ARMReg base, s16 op) { WriteStoreOp(0x40, base, result, op);} void ARMXEmitter::STRH (ARMReg result, ARMReg base, Operand2 op) diff --git a/Source/Core/Common/Src/ArmEmitter.h b/Source/Core/Common/Src/ArmEmitter.h index ce10008a37..20d867fa39 100644 --- a/Source/Core/Common/Src/ArmEmitter.h +++ b/Source/Core/Common/Src/ArmEmitter.h @@ -497,7 +497,7 @@ public: // Memory load/store operations void LDR (ARMReg dest, ARMReg src, s16 op2 = 0); void LDRH (ARMReg dest, ARMReg src, Operand2 op2 = 0); - void LDRSH(ARMReg dest, ARMReg src, s16 op2 = 0); + void LDRSH(ARMReg dest, ARMReg src, Operand2 op2 = 0); void LDRB (ARMReg dest, ARMReg src, s16 op2 = 0); void LDRSB(ARMReg dest, ARMReg src, Operand2 op2 = 0); // Offset adds to the base register in LDR diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp index 45b17ab847..2c25268c23 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp @@ -361,8 +361,9 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo // Downcount flag check, Only valid for linked blocks { FixupBranch skip = B_CC(CC_PL); - ARMABI_MOVI2M((u32)&PC, js.blockStart); ARMReg rA = gpr.GetReg(false); + MOVI2R(rA, js.blockStart); + STR(rA, R9, PPCSTATE_OFF(pc)); MOVI2R(rA, (u32)asm_routines.doTiming); B(rA); SetJumpTarget(skip); diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp index 690f56fdf6..1706efafb0 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp @@ -23,6 +23,7 @@ // locating performance issues. #include "../JitInterface.h" +#include "Jit.h" #include "JitArmCache.h" diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp index 8163415d8b..5a1a4659a6 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp @@ -48,8 +48,9 @@ void JitArm::sc(UGeckoInstruction inst) gpr.Flush(); fpr.Flush(); - ARMABI_MOVI2M((u32)&PC, js.compilerPC + 4); // Destroys R12 and R14 ARMReg rA = gpr.GetReg(); + MOVI2R(rA, js.compilerPC + 4); + STR(rA, R9, PPCSTATE_OFF(pc)); LDR(rA, R9, PPCSTATE_OFF(Exceptions)); ORR(rA, rA, EXCEPTION_SYSCALL); STR(rA, R9, PPCSTATE_OFF(Exceptions)); diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp index 964bd575c4..f9da7180e3 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp @@ -40,7 +40,7 @@ void JitArm::GenerateRC(int cr) { SetCC(CC_MI); MOV(rB, 0x8); // Result < 0 SetCC(); - STRB(R9, rB, PPCSTATE_OFF(cr_fast) + cr); + STRB(rB, R9, PPCSTATE_OFF(cr_fast) + cr); gpr.Unlock(rB); } void JitArm::ComputeRC(int cr) { @@ -51,7 +51,7 @@ void JitArm::ComputeRC(int cr) { SetCC(CC_GT); MOV(rB, 0x4); // Result > 0 SetCC(); - STRB(R9, rB, PPCSTATE_OFF(cr_fast) + cr); + STRB(rB, R9, PPCSTATE_OFF(cr_fast) + cr); gpr.Unlock(rB); } @@ -232,7 +232,7 @@ void JitArm::cmpli(UGeckoInstruction inst) SetCC(CC_HI); MOV(rA, 0x4); // Result > 0 SetCC(); - STRB(R9, rA, PPCSTATE_OFF(cr_fast) + crf); + STRB(rA, R9, PPCSTATE_OFF(cr_fast) + crf); gpr.Unlock(rA); } @@ -269,7 +269,7 @@ void JitArm::rlwimix(UGeckoInstruction inst) ARMReg rB = gpr.GetReg(); MOVI2R(rA, mask); - Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it. + Operand2 Shift(RS, ST_ROR, 32 - inst.SH); // This rotates left, while ARM has only rotate right, so swap it. if (inst.Rc) { BIC (rB, RA, rA); // RA & ~mask @@ -296,7 +296,7 @@ void JitArm::rlwinmx(UGeckoInstruction inst) ARMReg rA = gpr.GetReg(); MOVI2R(rA, mask); - Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it. + Operand2 Shift(RS, ST_ROR, 32 - inst.SH); // This rotates left, while ARM has only rotate right, so swap it. if (inst.Rc) { ANDS(RA, rA, Shift); -- cgit v1.2.3