From 5753edf6efd139e8a8edcfe68a1c1c56cbca72a8 Mon Sep 17 00:00:00 2001 From: DH Date: Sun, 30 Jun 2013 11:46:29 +0300 Subject: [PATCH] - Improved sc function binder. - Improved GLGSRender. --- Utilities/Array.h | 151 +- Utilities/IdManager.h | 2 +- Utilities/Thread.cpp | 76 +- Utilities/Thread.h | 76 +- bin/dev_hdd0/game/TEST12345/USRDIR/tetris.elf | Bin 207840 -> 548524 bytes bin/make_fself.cmd | 1 + rpcs3.sln | 422 +-- rpcs3/AppConnector.cpp | 32 + rpcs3/AppConnector.h | 33 + rpcs3/Emu/Cell/DisAsm.h | 24 + rpcs3/Emu/Cell/PPCThread.cpp | 76 +- rpcs3/Emu/Cell/PPCThread.h | 17 +- rpcs3/Emu/Cell/PPCThreadManager.cpp | 24 +- rpcs3/Emu/Cell/PPCThreadManager.h | 4 +- rpcs3/Emu/Cell/PPUDecoder.h | 269 +- rpcs3/Emu/Cell/PPUDisAsm.h | 1135 ++++++-- rpcs3/Emu/Cell/PPUInterpreter.h | 2593 +++++++++++++++-- rpcs3/Emu/Cell/PPUOpcodes.h | 845 ++++-- rpcs3/Emu/Cell/PPUThread.cpp | 106 +- rpcs3/Emu/Cell/PPUThread.h | 571 ++-- rpcs3/Emu/Cell/SPUDecoder.h | 196 +- rpcs3/Emu/Cell/SPUDisAsm.h | 655 ++++- rpcs3/Emu/Cell/SPUInterpreter.h | 924 +++++- rpcs3/Emu/Cell/SPUOpcodes.h | 466 ++- rpcs3/Emu/Cell/SPUThread.cpp | 9 + rpcs3/Emu/Cell/SPUThread.h | 4 +- rpcs3/Emu/DbgConsole.cpp | 8 +- rpcs3/Emu/DbgConsole.h | 4 +- rpcs3/Emu/FS/VFS.cpp | 91 + rpcs3/Emu/FS/VFS.h | 15 + rpcs3/Emu/FS/vfsDevice.cpp | 201 ++ rpcs3/Emu/FS/vfsDevice.h | 39 + rpcs3/Emu/FS/vfsDirBase.cpp | 3 + rpcs3/Emu/FS/vfsDirBase.h | 19 + rpcs3/Emu/FS/vfsFileBase.cpp | 43 + rpcs3/Emu/FS/vfsFileBase.h | 24 + rpcs3/Emu/FS/vfsLocalFile.cpp | 95 + rpcs3/Emu/FS/vfsLocalFile.h | 27 + rpcs3/Emu/FS/vfsStream.cpp | 74 + rpcs3/Emu/FS/vfsStream.h | 33 + rpcs3/Emu/FS/vfsStreamMemory.cpp | 36 + rpcs3/Emu/FS/vfsStreamMemory.h | 16 + rpcs3/Emu/GS/GCM.h | 17 +- rpcs3/Emu/GS/GL/FragmentProgram.cpp | 116 +- rpcs3/Emu/GS/GL/FragmentProgram.h | 18 +- rpcs3/Emu/GS/GL/GLBuffers.cpp | 112 +- rpcs3/Emu/GS/GL/GLBuffers.h | 44 +- rpcs3/Emu/GS/GL/GLGSRender.cpp | 840 ++++-- rpcs3/Emu/GS/GL/GLGSRender.h | 122 +- rpcs3/Emu/GS/GL/GLProcTable.tbl | 87 +- rpcs3/Emu/GS/GL/OpenGL.cpp | 43 +- rpcs3/Emu/GS/GL/OpenGL.h | 27 +- rpcs3/Emu/GS/GL/Program.cpp | 35 +- rpcs3/Emu/GS/GL/Program.h | 11 + rpcs3/Emu/GS/GL/ProgramBuffer.cpp | 15 +- rpcs3/Emu/GS/GL/ShaderParam.h | 43 +- rpcs3/Emu/GS/GL/VertexProgram.cpp | 187 +- rpcs3/Emu/GS/GL/VertexProgram.h | 24 +- rpcs3/Emu/GS/GSManager.cpp | 6 +- rpcs3/Emu/GS/GSManager.h | 26 +- rpcs3/Emu/GS/GSRender.cpp | 15 +- rpcs3/Emu/GS/GSRender.h | 66 +- rpcs3/Emu/GS/Null/NullGSRender.h | 143 +- rpcs3/Emu/GS/RSXThread.cpp | 69 +- rpcs3/Emu/GS/RSXThread.h | 70 +- rpcs3/Emu/GS/sysutil_video.h | 91 +- rpcs3/Emu/Io/Windows/WindowsPadHandler.h | 40 +- rpcs3/Emu/Memory/Memory.cpp | 229 +- rpcs3/Emu/Memory/Memory.h | 167 +- rpcs3/Emu/Memory/MemoryBlock.h | 33 +- rpcs3/Emu/SysCalls/Callback.cpp | 66 + rpcs3/Emu/SysCalls/Callback.h | 146 + rpcs3/Emu/SysCalls/FuncList.cpp | 62 +- rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp | 40 + rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp | 75 + rpcs3/Emu/SysCalls/Modules/sys_fs.cpp | 26 + rpcs3/Emu/SysCalls/Modules/sys_io.cpp | 20 + rpcs3/Emu/SysCalls/SC_FUNC.h | 367 +++ rpcs3/Emu/SysCalls/SysCalls.cpp | 404 +++ rpcs3/Emu/SysCalls/SysCalls.h | 467 ++- rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp | 98 + rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp | 153 +- rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp | 126 +- rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp | 104 +- rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp | 56 + rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp | 78 + rpcs3/Emu/SysCalls/lv2/SC_Mutex.h | 25 + rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp | 227 +- rpcs3/Emu/SysCalls/lv2/SC_Pad.cpp | 16 +- rpcs3/Emu/SysCalls/lv2/SC_Process.cpp | 23 +- rpcs3/Emu/SysCalls/lv2/SC_Resc.cpp | 12 +- rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 39 +- rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp | 96 + rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp | 190 +- .../Emu/SysCalls/lv2/SC_SysUtil_MsgDialog.cpp | 76 + rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp | 20 +- rpcs3/Emu/SysCalls/lv2/SC_Time.cpp | 8 +- rpcs3/Emu/System.cpp | 129 +- rpcs3/Emu/System.h | 41 +- rpcs3/Gui/CompilerELF.cpp | 427 ++- rpcs3/Gui/CompilerELF.h | 42 +- rpcs3/Gui/ConLog.cpp | 72 +- rpcs3/Gui/ConLog.h | 6 +- rpcs3/Gui/Debugger.cpp | 148 + rpcs3/Gui/Debugger.h | 17 + rpcs3/Gui/DisAsmFrame.cpp | 18 +- rpcs3/Gui/DisAsmFrame.h | 1 + rpcs3/Gui/FrameBase.h | 7 +- rpcs3/Gui/GameViewer.cpp | 26 +- rpcs3/Gui/GameViewer.h | 3 +- rpcs3/Gui/InterpreterDisAsm.cpp | 190 +- rpcs3/Gui/InterpreterDisAsm.h | 15 +- rpcs3/Gui/MainFrame.cpp | 198 +- rpcs3/Gui/MainFrame.h | 28 +- rpcs3/Gui/Plugins.h | 362 +++ rpcs3/Ini.h | 36 +- rpcs3/Loader/ELF.cpp | 23 +- rpcs3/Loader/ELF.h | 10 +- rpcs3/Loader/ELF32.cpp | 47 +- rpcs3/Loader/ELF32.h | 20 +- rpcs3/Loader/ELF64.cpp | 123 +- rpcs3/Loader/ELF64.h | 28 +- rpcs3/Loader/Loader.cpp | 88 +- rpcs3/Loader/Loader.h | 42 +- rpcs3/Loader/PSF.cpp | 16 +- rpcs3/Loader/PSF.h | 5 +- rpcs3/Loader/SELF.cpp | 25 +- rpcs3/Loader/SELF.h | 12 +- rpcs3/rpcs3.cpp | 23 +- rpcs3/rpcs3.h | 32 +- rpcs3/rpcs3.vcxproj | 18 + rpcs3/rpcs3.vcxproj.filters | 60 + rpcs3/stdafx.h | 29 +- 133 files changed, 13624 insertions(+), 3898 deletions(-) create mode 100644 bin/make_fself.cmd create mode 100644 rpcs3/AppConnector.cpp create mode 100644 rpcs3/AppConnector.h create mode 100644 rpcs3/Emu/FS/VFS.cpp create mode 100644 rpcs3/Emu/FS/VFS.h create mode 100644 rpcs3/Emu/FS/vfsDevice.cpp create mode 100644 rpcs3/Emu/FS/vfsDevice.h create mode 100644 rpcs3/Emu/FS/vfsDirBase.cpp create mode 100644 rpcs3/Emu/FS/vfsDirBase.h create mode 100644 rpcs3/Emu/FS/vfsFileBase.cpp create mode 100644 rpcs3/Emu/FS/vfsFileBase.h create mode 100644 rpcs3/Emu/FS/vfsLocalFile.cpp create mode 100644 rpcs3/Emu/FS/vfsLocalFile.h create mode 100644 rpcs3/Emu/FS/vfsStream.cpp create mode 100644 rpcs3/Emu/FS/vfsStream.h create mode 100644 rpcs3/Emu/FS/vfsStreamMemory.cpp create mode 100644 rpcs3/Emu/FS/vfsStreamMemory.h create mode 100644 rpcs3/Emu/SysCalls/Callback.cpp create mode 100644 rpcs3/Emu/SysCalls/Callback.h create mode 100644 rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp create mode 100644 rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp create mode 100644 rpcs3/Emu/SysCalls/Modules/sys_fs.cpp create mode 100644 rpcs3/Emu/SysCalls/Modules/sys_io.cpp create mode 100644 rpcs3/Emu/SysCalls/SC_FUNC.h create mode 100644 rpcs3/Emu/SysCalls/SysCalls.cpp create mode 100644 rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp create mode 100644 rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp create mode 100644 rpcs3/Emu/SysCalls/lv2/SC_Mutex.h create mode 100644 rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp create mode 100644 rpcs3/Emu/SysCalls/lv2/SC_SysUtil_MsgDialog.cpp create mode 100644 rpcs3/Gui/Debugger.cpp create mode 100644 rpcs3/Gui/Debugger.h create mode 100644 rpcs3/Gui/Plugins.h diff --git a/Utilities/Array.h b/Utilities/Array.h index 09937423a5..44507c880f 100644 --- a/Utilities/Array.h +++ b/Utilities/Array.h @@ -23,6 +23,7 @@ public: const u32 to = from + count; if(to > GetCount()) return false; + for(u32 i=0; i() + { + return m_array; + } + protected: void _InsertRoomEnd(const u32 size) { @@ -180,7 +209,6 @@ template struct Stack : public Array } }; - template class ArrayF { u32 m_count; @@ -193,7 +221,7 @@ public: { } - ~ArrayF() + virtual ~ArrayF() { Clear(); } @@ -262,6 +290,53 @@ public: return *m_array[num]; } + T** operator + (u32 right) const + { + return m_array + right; + } + + T* operator ->() + { + return *m_array; + } + + inline T** GetPtr() + { + return m_array; + } + inline u32 GetCount() const { return m_count; } T& operator[](u32 num) const { return *m_array[num]; } }; + +template struct ScopedPtr +{ +private: + T* m_ptr; + +public: + ScopedPtr() : m_ptr(nullptr) + { + } + + ScopedPtr(T* ptr) : m_ptr(ptr) + { + } + + ~ScopedPtr() + { + Swap(nullptr); + } + + operator T*() { return m_ptr; } + operator const T*() const { return m_ptr; } + + T* operator ->() { return m_ptr; } + const T* operator ->() const { return m_ptr; } + + void Swap(T* ptr) + { + delete m_ptr; + m_ptr = ptr; + } +}; \ No newline at end of file diff --git a/Utilities/IdManager.h b/Utilities/IdManager.h index 75ada2b795..11a25f51b0 100644 --- a/Utilities/IdManager.h +++ b/Utilities/IdManager.h @@ -128,7 +128,7 @@ public: id.m_used = false; id.m_attr = 0; id.m_name.Clear(); - if(free_data) free(id.m_data); + if(free_data) delete id.m_data; id.m_data = NULL; if(IDToNum(_id) == IDs.GetCount()-1) Cleanup(); return true; diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index e53f9d8603..f46e890c5a 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1,6 +1,19 @@ #include "stdafx.h" #include "Thread.h" +ThreadBase* GetCurrentNamedThread() +{ + ThreadExec* thr = (ThreadExec*)::wxThread::This(); + return thr ? thr->m_parent : nullptr; +} + +ThreadBase::ThreadBase(bool detached, const wxString& name) + : m_detached(detached) + , m_name(name) + , m_executor(nullptr) +{ +} + void ThreadBase::Start() { if(m_executor) return; @@ -8,22 +21,77 @@ void ThreadBase::Start() m_executor = new ThreadExec(m_detached, this); } +void ThreadBase::Resume() +{ + if(m_executor) + { + m_executor->Resume(); + } +} + +void ThreadBase::Pause() +{ + if(m_executor) + { + m_executor->Pause(); + } +} + void ThreadBase::Stop(bool wait) { if(!m_executor) return; ThreadExec* exec = m_executor; m_executor = nullptr; - exec->Stop(wait); + + if(!m_detached) + { + if(wait) + { + exec->Wait(); + } + + exec->Stop(false); + delete exec; + } + else + { + exec->Stop(wait); + } } -bool ThreadBase::IsAlive() +bool ThreadBase::Wait() const +{ + return m_executor != nullptr && m_executor->Wait() != (wxThread::ExitCode)-1; +} + +bool ThreadBase::IsRunning() const +{ + return m_executor != nullptr && m_executor->IsRunning(); +} + +bool ThreadBase::IsPaused() const +{ + return m_executor != nullptr && m_executor->IsPaused(); +} + +bool ThreadBase::IsAlive() const { return m_executor != nullptr; } -bool ThreadBase::TestDestroy() +bool ThreadBase::TestDestroy() const { - if(!m_executor) return true; + if(!m_executor || !m_executor->m_parent) return true; return m_executor->TestDestroy(); +} + +wxString ThreadBase::GetThreadName() const +{ + return m_name; +} + +void ThreadBase::SetThreadName(const wxString& name) +{ + m_name = name; } \ No newline at end of file diff --git a/Utilities/Thread.h b/Utilities/Thread.h index dc8124c6e4..38f6807aeb 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -9,33 +9,41 @@ protected: wxString m_name; bool m_detached; +public: + wxMutex m_main_mutex; + protected: - ThreadBase(bool detached = true, const wxString& name = "Unknown ThreadBase") - : m_detached(detached) - , m_name(name) - , m_executor(nullptr) - { - } + ThreadBase(bool detached = true, const wxString& name = "Unknown ThreadBase"); public: ThreadExec* m_executor; virtual void Task()=0; - void Start(); - void Stop(bool wait = true); + virtual void Start(); + virtual void Resume(); + virtual void Pause(); + virtual void Stop(bool wait = true); - bool IsAlive(); - bool TestDestroy(); + virtual bool Wait() const; + virtual bool IsRunning() const; + virtual bool IsPaused() const; + virtual bool IsAlive() const; + virtual bool TestDestroy() const; + virtual wxString GetThreadName() const; + virtual void SetThreadName(const wxString& name); }; +ThreadBase* GetCurrentNamedThread(); + class ThreadExec : public wxThread { - ThreadBase* m_parent; - wxSemaphore m_wait_for_exit; + wxCriticalSection m_wait_for_exit; volatile bool m_alive; public: + ThreadBase* m_parent; + ThreadExec(bool detached, ThreadBase* parent) : wxThread(detached ? wxTHREAD_DETACHED : wxTHREAD_JOINABLE) , m_parent(parent) @@ -50,20 +58,26 @@ public: if(!m_alive) return; m_parent = nullptr; - Delete(); - if(wait && m_alive) m_wait_for_exit.Wait(); + + //if(wait) + { + Delete(); + //wxCriticalSectionLocker lock(m_wait_for_exit); + } } ExitCode Entry() { + //wxCriticalSectionLocker lock(m_wait_for_exit); m_parent->Task(); m_alive = false; - m_wait_for_exit.Post(); if(m_parent) m_parent->m_executor = nullptr; return (ExitCode)0; } }; +//ThreadBase* GetCurrentThread(); + template class MTPacketBuffer { protected: @@ -71,6 +85,7 @@ protected: volatile u32 m_put, m_get; Array m_buffer; u32 m_max_buffer_size; + mutable wxCriticalSection m_cs_main; void CheckBusy() { @@ -91,18 +106,43 @@ public: void Flush() { + wxCriticalSectionLocker lock(m_cs_main); m_put = m_get = 0; m_buffer.Clear(); m_busy = false; } - virtual void Push(const T& v) = 0; - virtual T Pop() = 0; +private: + virtual void _push(const T& v) = 0; + virtual T _pop() = 0; - bool HasNewPacket() const { return m_put != m_get; } +public: + void Push(const T& v) + { + wxCriticalSectionLocker lock(m_cs_main); + _push(v); + } + + T Pop() + { + wxCriticalSectionLocker lock(m_cs_main); + return _pop(); + } + + bool HasNewPacket() const { wxCriticalSectionLocker lock(m_cs_main); return m_put != m_get; } bool IsBusy() const { return m_busy; } }; +static __forceinline bool SemaphorePostAndWait(wxSemaphore& sem) +{ + if(sem.TryWait() != wxSEMA_BUSY) return false; + + sem.Post(); + sem.Wait(); + + return true; +} + /* class StepThread : public ThreadBase { diff --git a/bin/dev_hdd0/game/TEST12345/USRDIR/tetris.elf b/bin/dev_hdd0/game/TEST12345/USRDIR/tetris.elf index 1921a7701de611aeaf5f2250d4db9c69bae55c5b..5ef2081a36bda12cf98c72c8ce37c85e8fe5ca0a 100644 GIT binary patch literal 548524 zcmeFa4SZDPnecs|$q)vJ8tZ7IgfNkyiBL%`?M_I51knzJTC7PyMNI@N6nvjpXN_S}sI#G0?(B}!O_J>t5_^|@PU9{Rp0-5K3-RDd) ze2UubyYD{B?>C&2`}4Z5@B6wxPWYNR*ScKBcU1D9OI^sTq5m9lPLGpZs-K>D%k;BU z`ISeNsxc~?zYX4o>Gb4(cI{*!vt@--hbHYF&!-v;;k_$NCIq1ytIvs>lzhc~8o7KWRZv+GGA)Rdb!C zd?YD*86Lp9S*XD~e446Yex27f)l4DS7k8Ctv0 zjNERy>)gs$cdhaT-zqli#w%ZV^;>4^E7Xn}GU0dL(b<*R>C`HDQsD4MN2 z;b%%qE##+ojdDjvLFhG!9>II~mKV+NHT;hDn?*O-bz2W8)w)Evr494(ls2BW+wea+ z)JDK{&!d7L>vqdy-EU>cyZi#K%RkmndmD6*3GHY*B<%_PdB59v zUj#4MJk+IX8lSZag^z@vnsb~n>{W{z-?SzNl*+FwfX_#P!?BDLn)K0La75Z;)+~h# zxO1h=NEjL}K{g7gzg%#T*k!pazu=>6ugYYcC4n^K0fF6*PH3Ya&7<@$eCqI& zhu`iuX}?0Z|LE{dnxE32e_i^R)TsH^Pun%&9+wGCnuDVT{x-D4CH=+dD;k;*m@EAi z6(kaMO-B+prwwKll0TU6&a^8gg=184~(9PP4?I)q3@BK(n$}H_XCw}K7-yE zr+h+(@H_pcjDvj9t1;2Jr|PTp;l$hUd3*?e+q#Cvme~GT#x|%k9lS;s0{?>={w&7g znS@0Yd`wubjMBBXJrEcL2ZJy=G}2>xIMJiWw($6|m9;0fr@*}=1@6uy+(~)}9g;L) zybmRLs;@&G7M|+sQhj~<4E3>Lc))Q+f3<2B9zb_T(bsM0?-;VaAs8HOew=G$eq3f` z1y_#~-tfwpQpQs#xTgQO9uMuHXQGCb*7I1e_AcKTg*OzJRpGrW~@{88FI&=-ym)C5O-V1|4)4JyZ-H&C7!=t=Ska%4b* z7&=aYiQVq1g`w*E1e;WJbS?u3W<)kY>LkLYpXU&@>2 z#o&VX@VK33!1y0iX8i&dF$a>o?Hm*e}7i0j{_4bd|NcDg^&R(^BYO9MkXa zfZAf){%o5kf}_$*%`f05ihNPOvku*AJ*_gr>xXah$EnvN{^rv!59w56lSTb#wW<|( zdDc+x!{d3YbzhY_6>lwmN77H`tv_!aq%Z9STo->;$_Y;iJ%zsl#$6hA;i<+l`0HT} zZ*Bce4sTukB4t{aNWGK!YVlpVJ@{GhHN;nczGkpr%~x4lrCfOCiz$4y?01rOJYOvy zBWco>$b&ydpE)*vl_&oEOW`f}O56B@(76}dYQBP=z0ech+5m5L!dsi+tvI~39p35| z-lDD>dX)v#uUAPQf@{I^iL`>Z=EHk)g-${*p_S0irq|Z-NqUvF4^-GZ(%&L52~VWL zqi^hFPoo_UtV{b%I7(eUm?HA(pGTE zXp^?8g5Mo&cKyU!5L`Oiq?{^ISw%eE{5(}^f_nL;zD)Z%yx~+`ekL$TqM)xTC z9HBCHZ=~!9-bbp8?vcvFPtS{Kdr?tt;_#tdb=6UyCnxCp=FhF_#9_uXQs^Zx^zrw0 zBXRhsN?#Md)LMKjrm|#whrXmt$&a|K`GVidS-!7TmS#>VS`5zBY%jR?hN^Dz3f^VT zA@X0elsN_X1$T$Ry)o&hY9x5uuJKl7({0ithB2pI&)=6x9VxH#d6#~>$1w(Q+kLgl z?p~qxZP8B6dk+*|aVT9~C4IbiNB$LEGpwpZFXRgi zO5V%O%-?;r^1tWu$lvbw)@NP8GviQGmi(Q0Xog4r*0eijRn6r+@6e;_DxSK}_kQN% z@4~#@k0{Tf8G*d+N7TgkRutv|bJp%gm4E0#UtV`3{Vpxd+r3bE_4s1OMA6siZ97I` zEZz!A#P**!4;qXQ;492Wx5u#8HN_S(98$Xiu7wArUh7)53wv-!d+P`Eh^P!X3b-!g5mEmgMZUv9}s-mJ7<1?%Uu^sC7Qa*M-{lSd8eY?w; zj~&oYv4OGe{DSFerttE|xhl4UpPw-t^If+_`U+iMZtjk#6_l;vx#k$WepCgr-us#B z7w=WMJ~*Uq|Mh!cs=8u#hq888tEO(|e7kd1G@!oo<0>@-@1mxk{qTmSKor&H~BbMVT@(EsV)_#<gD@-XTF(0>6t}fZ0 zwbhtutu;ymYQfJW4cKEzzH;E#zHr5o%qgO?(GRu4*P(*pn;27WsNg0q_&p3ydaT9h zgi^-xxKSbY#M)s5=(7}Gq_e*o-Y8dlim`FU*@~ zigqcJ_qG=NssX0$(I5Mwc_f?Fn-uXY%1lPsAh7&ZMJ260BC8NaM~UVHkqN$s|; zwB2R3#h)=26-QL((MQ!4&tbdbm!e;9>C2DbZbc8}^1R!kjq)L*hw+s@lDg%IzB|H;@b{@FeUknW1<$?EXeapJ34L}#8-K2$=k(xv1G+wn?kmObvdR;G zdRgYs(XdfvkNeNR@~<-{%6lOF$?L^uv&h5FXOLbfeiP|Ml1@ARNz8p6TXc6nvc(+1 zdUYRsvu>QsA7jC%i67%1l&w)E;Ba5~t>K#zJsx7*e|}l?fzYb{`E(!trlsMhjibK4 z=uX-|UMP2Lg_Of5mAR}glgx8?(w3!tMw`Qv=mP^eg5NrfW_WR#@F09wB0L5!#p6G* zwh8|T&4riXnN50rC%iSo+6>=p5#E9ahMmYy!fU|Z(e;e67~f!l@JF{{xDTntnzv5R zS0Aml@%yK9j_0e79ypn==Di}cP2sD_Dg2V6lfQkAl!LGQXXdL)J;%{JWvMi$E#_W# zoG`gL;USv_;0^vE=4@!WAL5k{HwLRloAAmq(Lcfq!7*vb#zzkn;XfDA7(A=} z=g@?MUVOMr@twu56h99BnOxr}{PXjV9ll!T@Ri@@tDo<4_-cOtzC@qUGvcy#^;akM z^hb!3+-_}0_ICAmB=+}rB@RdO82e1+t}MWQEW$>(RYv6k-`B)`w1f>aI@SmV7YDA4 zOgDlu=Hea72*3VEv-J%l7%I8K3#`=5@&})QM~Q!j3jW{?Zy=s`22S!j!wdd zuUo2oq4cZG$QAgm^d<9&P{Aemt1hpMui1`g_{!SVoJq~<0^stLwW$SVYgJRI;C652 z1pHlJ<`iNkBZBHC?0?N;)9k=i*F||3&6ZF~%cr#h=Feb6) zf&V*)xXol~6J4M_som1Gi6{Iyi9|2>>ji&;k6v)s3+{TsugH(!68@Vn?L{sH*SU%9 zNqpls?5YU*z9x9@FH3X_&V?@ESvBSdH{bRYN#^{wsqx6?{$aKcC9@-83|9dP0Gl#~aT&En%{vkd*rOq36 zRyuE{=se-A&q|jRU3W@8MDEPwSX$9L<%y#e$vMv{;Mx;ROEX)h8>OrrzlM*c@Uc8cA}W4_`OT3o)qP~2+SAV*l{oQ!=4OYP z)AZr@X9kpigusX|S&AQK6~|;w`8OX)n)qRYTjpREKgM&z^^y)gy&9jSmnZ)1N0D4* zMP^vt;$yAes4n2A_!<0M{L=Wj`3>Wj&TlwB55E!oM)DiQFN5DX{LbZf9=}X}=kxnQ zq*__cBb8cyg))|pRIcWODyxZ#0E%!c)kn}X9RU8*^tZmpI) z2hYuMv%FJfXg*i%D@kMz)>^$R4tzcjz)l^ z0}hVLhj1kNmi%Glr;|UNaky8Nr{D-aB7X$=BgwaMRGxw(d`0rlA^%+RZ5)-S;E4Du z`R9}W1@bST{Y=_FAN+g)99}^GUh*#_|043gNWPc+3(3ET{4bK9MgGO)Uqb$9^0Ual znEXq~A5DHX`D4h>A%86S+2oHQKZpFWRjABwywn_{0kW#tk7| zF05V?nGx9`-eu424OJsDgA7H{c|Eir<@Zp)b=}XTPKxX>S3P)uG05E7mK`HuNEu|O z2ib|j6aS~nPA>9rDe@rl;X@{T$V4tOaVau!HZs!*?#&dL2|6x&EHx zW#;=@XTnRt<7H-!)_=&1jsuae`L;Z9AUB0)S;IRfQ(NIrb4X^cpCxIHhB6(Q3DWOk zEi?O6=a9^Ne~FZz&QFVX~U73PFrT??vONr z37Od>V+*+EBz1!+GB_kNZ)*8)WX9p&axI6B%tU2hg)K9$?bChncX{HC_vPKGC%E4f zaFzd3(s=I_Iit+);ShAFT~&t8Ter5;G>;;|C`oJ zv~eEe%v?1Ae{GaH2VHfptY0%;{HigU?hgH$z_Y9+fS_BoDSn~Mz828=$;+m|L+SbIKrmzS1izFDtxC_A#1|DqqD5>ME1CSEWZy zsTiTI6dVWC=#QjL2fyOyBFA6CZnr)R7(4aY<1XuPcaE``{#@E;N`Lqpy1i=k1T?6Ycgs*u%yWB6 zC{r5E5qW;&`o`NMnKD-mhgg$%i#3h)$WG!}ZyKFJ3fFY^X;&Uo;Anamliy+(!5AfU1i2wnxpA`kGn6>!be za>zS$ID8F0@9y%%TOG3h&R>;CpcCfrNAJrT%t7@1LG-=0#n^#f^nNdTzZbpVi%sao zPV{0Ida(h$*Z_a7tMEDt{z7g=j$~iI(4P3qTxk9ytHEfSLV9`P%`)WwsFt@X;k#8j zz7MYr52UyKNO%dD&cSXkyPq|*<}7>vP+9x%_4r=fzWqk}G|D}yWg|!Jve#?eS=3kl z2Z_`arN z)ZafLM!hW=qdw^Uj4|p3^pin9p3t2xuii(z=A1as3y6g+^N-*lhwoA^I(h_uFA!Z# z{JNsj$9e$k2(o@4YuvJ45i0n=i)h@DDRJtV(wFG?7M|sae|cSSA~b0O z7v+i94xrQ5fTK+|UNS6jv~dted%;mG1xM-N=(1DcC}nMAeR6H&bKoc)99@=*ql||J zapYEp;Nf%QXlRXOrt~HDuZ1W6$gfgxw6Q$#>hs_z29Ew@<7HG0IQpsmt_ma5;6I@g z=MBV`u$R@%8kJ!#93BJTFQ>HiQ`-2R)GKWtRVs2&+@n-St}(mPH`w-k+J4+=J3F3Z zED~CE{UoATFU*H#k7FwqQC_!G);_9)@=u(m{1udc!YS{_z$hQ(f98~*D6k_lX^{f> zh%o?HK>Q2f9wYqsETbF;s8`a(n#xUuk>VWMtML9-hYf&lZv4Nq?tg-EKX7W@w(|8A$)J){lJ+ z1~R*^jIky0*ldor;Vmw;;SDb!mU82ZdVUb8S8mxiU%8aE9(=ga(l_<`PAZI{FEW-y zDx5QbbINh+l%?(NLzC93G<*p+zJmA@8;WlN)?aH_qxgu94#s)=>%e2337+CA{Nf;c zG48|Xi9x>xqZXXie11Hn#ZT(D9z6WU#)I@buPSrOyd}P`6*bs<6{Y{Mb(1%sl$W{} zh(9JcL|;sTe#h30l+bwG`MA5it4d+{Ml+xuc7Cjm9_ zd`T})?AcnLc;$6F?Q7bfMy}d~A1e!n57dnt-qXV0<%vJkiEVD})3DH2-y)|@m$c)^ z9(&R3cEzs{pF-@k_!S57Da5zX`*+XMw-9-;*e@C3oJweM(uTT{_Ms#+m9{4!UO68bl%(vjmllNc32kvr@O3u*lSi6wg|q(Hg#t!;y`J}?`3@&x^$Pd zAL*0y5_QeH5tID^xazi+e%^H~v0Upf0K`cx0OJXeR;L@zQRqF1}F$MP)21ls5Cb}T}2((=L@t&lOlePoKx5r3Z#pnz<`-=>+#`eRH zC2gv2<*e$0yw-4Rpr-XL=DKewH$G2iYoqdreh5ts{EtwvU{q>(7dslPx~L|ojEBUYip>dDWzA(=qc|(+7I^9pFyCIzUUk-W&=(KM-+q-i z+B%?i6;~-EGE2E5OOQj>by^yXnwDFQ+SXahLm#&YtSPWhJrQ;d^VPtAHFNcaz)l?O zEMOld?O-E1ldu>0)F^>n{0M=+bt&)$WgYp|wK8we{z2UF4^A_02>ugu1?CHb{=uEd zb?{97!6o4LME}4w|2Y3(X#dH2?Vm7j36)G4+$R!z8=pk)OTzaCzSO-UXS;{SO3qKd zGpJjuY~S0J7s^ZOR#%p-TV0u{`s>tl6XTG~M;M2_hi1sf39v6NAKno8_M?dr7~VHb0D`ztB5o}p*fS6;n?zw^2D}tBwzYs&&Ek-=q*WZGA4-<0;HT{)Wx+~-TV*4y;im3Te)?*&h+JJpDP+6%6F!4-SlqwM!N z>D+U14BBC9G%lXO=WrG3J|s5U`i!*y_!4P9G9%)1_CA+^@6E!Wk=rBL@Lad-Z`5?j zg(hXtq5D(S*6LO;zX(}hot)(!Se*?{qemZiaSjCV+zlZQ!NbVE&R7#p7} z>kpGEb=vKbJpKmeIa)@ES?hHX>4$V#^CTWUNy|U{F+pO{l1{&Dh6WcgHGy{F!#6_G zUgXk9KCx&!4h@{-8~+uH{=`^x-tt*$JZtusv1Wfc^ME|!(c@VIyNork%fVj(`T3+5 z!0T6#mk<9Ehkj1WsrYt61EJNtt;Bd;U(&Q>4lH`05M9@eTs9+*_E}V91euH>lP8^T zmUSQO*w650Y zsznc=%ZJWZM^tBDmyy-C&+u{YL;lCPMj`h)__47@V;B2b1jc{tHRT*l_!-t=#-K~a z@V-aR;j7JhAA-f-gXi17y-<4*5OqeEWoVBwfpIQqQjw`R)7F@$&oHSEbzX=i3Xf z(RH6gCeaH4*MwE_H?-+N*GBJqOY7Y>*8KH3BIq1L@1FLYaieYH0@yf>zdrCK_&XII z#MTPFw4HTxW*i)2(>9}P?X&6NPjD`HmGOd;{l(}ws{{KZ>s~iGdR6pWk;F**Hp(3f z=$W9zGqj$Gs9+;=7xXRbELUumv7uXBTGx<=zx(4zuJ&tk<^BS_#>HJmtCzDUk@>f* zeHHoeH%BSelFPZS(fB9$l;YD{HxJ#lBfNv01mPv=$JT%FiSU-qYgd#>-x{xsS@5Xs zSPCxvx%OFY>wfJgWF`4=sNI5N(guI0v|DgM()602YyFSq{r?->|M^R2#(jYv|M9s0 z)5Q+%=l$hypZ|*B;CLM6KQC#Y5BK?zQ?z@Rq}jNCoDb<*qZSnxU`U^LbT=pl+K4#GwHKH#fC6ra{Rp?O3J#JcV zfpf0j+XhaB?`55UIpr{EyB;18pI`bd-b~#xu~V1XdE%cX^?RA{!w0Vn>UY`-7{Awg zr=D>Ne(n1$uDC<9dec5U2vf&YYdwf?hp!rHBp|JR?ZeM9q#=+@)+oL;K+pXdR{ z$8hd4`nOpr^2ogBpEchdKUcf#Wf{vS&(-GGa&*#ME%#%ouYJAa=4$`?yTQKoT|xDEqiCO^~Ac4yT|JEbG4`L5IBot@E|-& z9ESPE*aArtIk)37t2F;end9ebV+|=Mw&KLO8ZjxuiQ!b^Uy09U^>^5JpfYEeEHNwM z(wWP+J8cp_(&Rik=cS1!QC8a-%41jbS?Ods*2i9J{7J+o{+rn2@Ne*`))M1b%i4!4 zN9%_FceQeh4Mhf68}6Njo_6~~oKeCzlRl(uYlyK(%*yGHxv}nxHL1eJ+ao^gR<^%h zWkyFUVu-&elW}Ssk=FVf){?36?0=wo5X=}B# zHRe}RPuq#rtT(yZs^#w!&XjeYaHcGVJWqCT9Ff>)@qxrq{IY0$FMGTv;Bzh4c%57~ z0iQ3CI4b9>2j@xUL%yY~EwQ&nmj}Nu0e=*mF85|?I$UQdA2#-G?L&8w);>$s z4Tq)@mu;rsnB0vN+{(VD*S=!1FRK|CJmL?~udJbdk@dy}+W%OtO39xo@Acc#xxba) z4~Sc(-(9z#c^v)g`%Hws&_$oM6FlnmZ~6i*eZL9qXAtWh8WU?`*IDjdX~T|*CSoO3 zR&KJrs)Z_XruG=49pmm|ZYyI12Qo&%sf-Pn%@f9S0b`nzGN#GNF$L29o8F(QaY=i6 zEVL)%(0FA%Rq%QiW9W3opySti45w&E&X5azKJH4?0B@$O!As2i<9!KY=2^p!7zyi0 zZld`}St5F*I?;9{l87DYNNhOLmFPUOFTq`eiMT$aF6Ysmdja%)0GvyYRwsy=y7y1A zM&HP@hv%(4_wc-hXE)CUJh$__nPO!uDqEt-=eJ`ZT{_yoKS%-n-yBB+sm$;O&<%lT?rgM`pK&tM)?aV z|1XsPGv#ON^1k3gT|TeUL-}{NT^Ve8#0=lG+6>pXn&CB1nZZXNH$$EW&EN~aF@x{! zG$*YaePeiQ8uwb3Q|@LnTy+EGkeNrXrcANf`ZTd7xx4UI;?z0lo~OB=Y_G~@%~!Ru ze}(;6LD~T4>9st;b^5YN>#~M}^KJYsG}*#aXtBx4Nv?soD~f&8&rJ{qPB7<4u;)u< zHbxREcnNzJ)SF&&EHXM)xw)GdT0y(%%3FCK&#@{q%>IZ9Z~WYFfaiHO{u`gcR;*Kb zmBrAj1RCE4yhfs`@~Zf`*pU26H6zDLs4T&~^^Ee(dlLG-p|V2-uM#&(OlO^4to27SI-blRu~7TjgdXH&InlWz!LwGOpb8}4Z|8y>1R8-96@ z+3@>?@J%^$ya&xmD>*B7PcM0Cq@81)Kgn$P zWhDC+Jm7N#GBL{Li%`MMY<0idWKWIRa)rKQt{c9}lGp(4S>f0R-ueyH^-y=@sp{UK z>&lvWOTMl%X96+5x&rn-^w{^Gl)W~rByKPL6hcQga<0nO9^EhNbyCK;Z!%lt#2>>B$Fa=~$ja!e^u2}XY2H9h5>vSL$pI!|!d+*6!Twaq(1)K`$VWmDeD*H^VdO@LL-1cOw6bcn0_zo1k?(@*Y&ZW@zbK z=w0?=&3+ZVihg~Ub=nuw%ot~!gYVvKb}V5}ndb(xW0pEMx`TMc0p)HP!Co_LQRSVS zow=6tJ;Qh|eRf*q>}RJ{sAs1M4MR(dX_NgbYJ&MBZFJJ!leEEptA}L16}c51cPo3n zW`4`d{DV7YwCr91L?Qk%9s>h>;k=zIpb)#Z>~ct0y_`Ji`VB6odl3Qc>{rp*rJ z)%@`X>_d`9UTpQOw8!A}4C{c(th@($eT(0@z-*-Vqfnr#HQ|q z<_}Unjq=#qtv65(JKI!D-d9ZaR;lPl%h&{6bn3JptGQ`tjI(-1nb))g=E$sS61?_V!>oI3LeeeWf7%@Eyis<{HP zZ|S{&wjWy~vdj9RKec`!eNl_};8GS}SL+EWhkxB7<>YQ}d{j%H7niz^F=t?{DKx&E zGg1rn`Mp#1jAF0EtF~9fnSSGr>|4)!J&;a z&f}oZcIk^4!}?Ev$-o!2VM6aZFcsF7E8m?ve9BLn?EhfD;^%{fK7fTgT~6K;m4Y8P zKClhTaQtW+KNaSFe3^HZXDYsx+=b>J&U=+|R~L+VsCp>~0zQe6w{FKKp$o9b?w0R< zK6nKV;BEc{c+Y~KW1@d$dY%PLX?gzvVXFC$2h+%9{{dmD{Y%3Xveaepd0Bt8UW*ic z)!C1&(d)bsbSCS(=+e!pck9bOhL(ujrGRZ={nT>fOr#3tH4cFKT@*wg>;;nss~(tF+zKB=Tz>(0bJ)`%*ArYtSKb)>`fsmvh+e zC}qMUpJP9Uv7p3KR(+iL!?5Sfs-{tvv@3eY+=xGVC%X9>RVs6aXfr+n^~LAJca!rI z7XF*Z+JP^G{cj;|7wx5womxLjUVFCo8>{K@3|!I8&02y6vvW`z5mBHv|h1g>OWB4bSY=q=)#j8vKA zdnuPioow{FFMoS1br>tSyb|K%P+&Z-v* zZ%?wcPmmYMgU7R3Cx#b`3jXbgIb8Wc^{F@#5?#Khnfn@-U(Q`v zcPiKNudB2+{G?U6>h4w5>U*oo)IH?8$sb03I{CxNuO$sa|& zNq!yq_mN+}d^mT44d*V3%efEhPVNX}&7AxY`3=@vPx-~WY5F(+P4FSK10PL zjo_?poGEckzXhF_aSuo?>-rDsJF~6+YG=f$m@x(A@c3ffoY=DblC{(9c>@n5SWE-rr;jk?I1cW3&vd9(Xe z(b6xL29?Z3cba!BRqi|BwW5jOa~Sx%1G$|JPOs&iIU%%*L${hppMM*G2c`92NVcbsu)J2qbC!H%9xuO4jbs z4{pvfdt`3m7kfh+=xWjTnX=a;1-`kT6TVCh-+rx|y+xNh@D%``2i+V7zEa>@tlXBs z$5>B-YZh?LLWj>1*p9`Eo4`$poEy*BjBQ?2v`AH1=xyOk{;nvRsiN4Tk_4A9yfmMa3sjMzH5&? z5`reTzzbvW6~F`b)6JNLE*an^lYB4vS>zAd=UFO?{A}`lmRG)VXH^%>r7%WZN+b^!s&dk zrFfBwFD%MAip!}i{K@*G%4hQe-^v{11+_xpA->~=Mp=%ZIay>-#`wu{*q@&qkI10N zUn@R@v@bpd@G7y%!0!frH?;6TcMt7*WGxvVEsL{Mo>u(7@ajI|Do6H+ZvhQ51YUt# zU}kJ{vFkm0Y*macPem8j=NwHfgWmlMj_b%=JudiH{0s4MTfgg!OU5DNl5xto>~Tsg zz{B4%?hF|p{+Xsvc$FTPa>kXJ3iIj4_3ulwk84k&+0rx6Y$`On0-7}(uQyxKoA%f~ z&N~y$tU;aMcl7h=)V?iAxX~%6)cMfB)~BLl9DNT>PAfa?DPNod--6ExUv`pbG<@)l zwn^{~ysyL`0*^L)qa65bc*XzLGNI>n$VHNONcR$N-G*OszV;oto3GC>ROq%FOxD5W zTa)ghD^%61U8?8ReQJLqo*5MxmhU&%(ka2l*E;1I`fR+qrg_CqAzx_*WukXbK#niKFi>fZ=R7Vj()FtA?=|q=9yi~ z`7T$O^`zT)$Isl&T%a4jYBzgv`CgB?8((^NHQ(kbD)zP&L3;;%`z_t&f9a_AtsEDoX6o!pDm~jxFg@LFR{<);{jaYNviLc7^?; z%pJ3Ux4ESpd&2!=;M|9ekTcWve0T|EHzLK;9nlT4%~=dJ+s|RJi*!zwrD_~e-fH&zjn|kRu@+(?Z2Mr z6PIcJAVwjy#dqz-N3~-WWh&a3!(4#xL|f?j;47Sw_LE2KLU`p!m#R6kPy4cM@QIFV z=yQ>4U5o0$MJ%i)!_V&U7-9gNGcV2pAMkIzhkJIX>$#*`zGFAFVBkH;qs)pH__}^1 zc*NJC%rNSEl4Z!>L_AXLf#jtZW7nm9sb9~$QDkI!SPiFKo368zcKI%h*bV73Ds#H& zs(?CKLv6Y|Etk`#^vAvd%9k#`jo3<&mD4>Lh%iXo0<2d znZc!Ma_&+!HdJtfS-I583HGSTJ|%zOY*wiFx%KJxIvQ;T1a53n*3?qQm#xPqaam9G zY}Z8c7m%Mp{>1u=xGQg=l>xHi9{9UA>qpanHvG-r3i4U+_Rr<-jCrM1 zmo>8PF6WDYORoVUpY96F$`Ki@c-P8lT|k`U+r*j{GM8i>E3`Dt1W#$9f}Liy$F;fY z1(&LNbWAgL*ot0+j6Dsk-v|CLB7du)|EoONuO|0BL^o@G>^=w|agNBI8^U|YhCVme zdxm+v$b%ob_yn0)FZ?TaJD>+`KBrCLahXFmF2oPZP(I<~u(gN#GvQ_7V~N+n~)U?p7_v<-yX8nktuCG^>i|QI> z{!Bcs3B9SF^;W!)KFw6>(z>J{T2QMpLQ5-NCB)@&~gj@Ul8LiIHW z9xBHC;^}!C=jU_Z!`$%3?Q-6;oUbEvQ1Dm9df2}fP>?VBk3H8KpB}m4?4M%=vC(qf+u68M%`OntJ_J_Sx;^H)$HVvxG5c+V^=S-fnK9?Pd5BJv<*6 zUK?C;iFe`y)LDYeJUCpwZJQaI!(AJ*uo(sD9p7-i)1~>vU-pBS|v^x@ax;meWPvB=j3-WNM@CHlbrpR@)lv^z7s659kO#?XYGv)6&kbgZyb{8EWS zfpeL2X7*PTCnR2ip3{4w8B-Kp*Cyw_8{bx58Q0MMSz~hOwhz1#x0m_ZyvNmmKO33% z;R~pnO^N@;eyB353p{UQuk>1>#8oFI?mAn(uVNSysdo= z^Y->NFbC&eA~k8%(muOY}7e-I<5G<(w0;<`PVhC!v_b^~HAPFgFRCEWSGjcB_qac-q(WU= z7%C7ukIf?n9EH!s=C?p3i9vyvDN=wgTXMuJ@d09Lo{CY!gVH{2$0_G8ena__WratB3OU55jzE9z zVGP=MF~rZSp=`}3mBlVEh%8n4p@lcg{&B^=Hh<*1w(XF$BWrf*njZ@x?TLxfwm+5v zGh>&#!vtn&$9fQ-y@|6Ls&SU!0Xx|jDqu`9z9q-^g+3UvQz!Q&zOI}V8p4n6%f`>~ zeck`M)7NXCZaf2f1g|IeajNm$U+46({`CB@@Es|0X8u@sjg&bve=N+EGXJ^xBT^t^ z{Ojt&Z95AZ%UNea! z9Hh^K^eHy!ApIVs--Gmfkbaq0#h6!Z=oQ;Vzu2#MFZ~j0=*D*KL09*ns|RBY=*84N zmdhB0FYlibnePv)Csy7j-@(0CuS)_8a$lnLpPX}%*a5mLQ*?GfeMyhu{@WwVil(bg zMPaqg`bng-=r;Az{g+0bC>pCe{RtKKzoEJt?_qv{zIfyY_H?6H9{Gw&7hgl~Ree|K z{i>p`r@Vt5yo34butcB#0rpTo%3bFVsJSbrs}0;&_nPSQ);Cm-;Adq*?ZQV;(RFGM zeuyx=Z`^t|-(|L|Hyscrw^VDrd##7UcbNNn;Y8z{0@*5U?Ms4P|g*LXYyp8Ww zgpKTgs?|JYkGc46!OO{Gp14ZV{bP+NI$@Ll4I}UVXCqDiXN<`*M!t_ZQ^rVpdW^-- z7=`z{B6Ew^8KtI|aX!iqzg+Mqdvs+UQNKp!WQ?8t5XB=BeN+B{*yR#pYpm;VUtB4% zKE`ik@y*M*GOm>gqeT30zKqSZtg)h@@e$L%PHjMcaxc*CAKPP9?)&eLG&8m= za}0CQ$;!*#e!YGwBjcxI-^f`C>l173JQuI43cab2-i( zMFCg$H=Oa+Vw>g^)he&xv6_41C||=G`kdB;THF$HaJmlMY5aFzDD}vTv2Jrze0Q5q z@3!d@h92-ya1r}R)%SiWW#uelz*VK?QpyVqD|2LB`@X4?k9_;_!FK69*1uPd(Rtc7 zkyk@rr`?a*!25dV{fU6OTbChizoZGDtn^CVx_9h$jb`#{k8ihbhvdn(5F8lKm%nWo zZ#y9GcKgP5>iV5`c&0{=CD|9xEYXRM|MnNszYETh{?Dv`Z@x>){QK(Pn@30=C+pvu zFH5>DH(%2@K3Ny#>oNT2(ZB!A=22~PzWTn5|Ku@WuIC5;XXxKuTK>_CXQ6+0YI!>EH3sOZtCa{rlVBIph13`uDe5&z`J*m)UeVPXFHZRq4alZ8vMV z)Vgp`|2n?KDfMq{hlcNr`gf@=lhlbr`nUEDyY8vFNs zQazSrU!s4JL%sisxqEJM%@E($#P_xEeVg%pqeqC3;QPk#eK+9q#_@T#_jlp@>NQDq zSmIOCAO7P4k?Z5nCDKHi%FA-PdKz_%cuU8;tCg#H3a>7+Iui{x51Un(;rHib1Z&Q5$oNYGv}Nm zU2z$+z{6elH8KXiQRgL=B(T(o|1E2up>gFVYxBVNy#n2S12G7@jQ zZIXqr)NXLsl6u0*aZQ%LdCz8i+XJrOcS%{R!8JLcc2~Y?1* z#ZMcDi`$J4V+T~aKgZ=EMxlxtT$vq=uk{+fFV^51&wbI{`|iqO9ZIz{xUyTX<$0?s zpKn^LCv#jATW?XSbHH_F>#Z&y#(wS?KGgcO(bw8;93_8pF|Y|A z)Gtx;EfD|8CAM7w?lyv5zQ&{cjX%rZLG&c^t{b%Nk-H7J2m2WFGtNFH65$_8JTYxg z$Ty(R*at_yC^&-yk*m{M$3QEt?=&cktWvqJ@BuN&2U$Dvo1f9__L;W zjfSl`$FLkaW-(^YZyQZ1bj%94Uiw)2h~LcB3K6Hz6o*wN5kgTW>Wcw&oc4(?-e4y+&Z=)68>#6?$f!JuSaAUf@49 zEnTcLoQam#-z9ZF8!dasNZlb?-my>q2KSjZ?&Z6%8u!UP(#Q}x%JpJM%7J^nA@M(T z8#eA$XK{{-PZ?0#rtDSSQ}{kx@vUmpsd2CN7u~83JGdWa4fkz4g&Z{?M*}Cy(Oj*UPK|rk zqfdwXevzYZ+Wy~aEteDPndha%G*2%_*jv8+scohI-lQBY(lWx?(wKZJ0sYNdU!rN~ z{n*i=_oI1}*6X`W-sT(D*9a}(^X--^GGAgJ$+1fuLTnf`jF}AA21Z$Q?a0Cl+|jCbtjYOyA8T>l)>WKV2{R_*AlPrSI~RGR|Ihi|e> zwM@r7+f|;8)AD0v*>QA%IoI|Bufwkk80A`j%KCx$Sgbp^tx;-dtpS<7&bf~#Ip!p+ zQa`lhW|Msd&NsCZ55e!9sqi!Zjtk!KS8Ie1EPOb}CYAyl{@&$UejJ(4l>FA`Sfgaj z(jT!&MVwWiKi2pLcIlV%%G&~JyCLJEPr3X3IGk~JjlN^P;)j`27*9r+bMSH=0Dr*~ zy281mV8~x#PZ@XN6Z1*-A!C$&nz8u;FFv(T$_PG_Wps?lDHkvK-8UHH_BPT2>Ypx_wq?FSKU%g< zMGJw=X}6j=g52lS88Bvtq%QSxl@4EJ*BU^=_kg#q=r8A8b5s7OuxgVoQzf0-yE8EhNhL9#a7~&@UgTZ^p-Q+ z_+G#`OZ(;G+d(I{e+0gv)g$|BGB|@_5?6GeR3r5+NTHL~YX&IwY+ zv3r4EN?Y|iKcn4yL-6zUk2E~%9NT}Ec2DyfGAMkXD*Hn)Cwc^C@l{1ur5tu#fA>b} zrs^BV2gu;t(`5pSZ7UzvcoMxRI2WCXec9#wt@R^+Z$ZZGy%}99TKt@vFZ|tK4bRE^ zIKsKd9QI!@4;J1ReiNAje|e;5`5V;&*6+`QmzrdqfPSV2)W04R7}!Hy3m;}V`J0O= zR~$nRty5W2cQyPed8~uTet|iwpJtxW&K}68IZIhjKimTrFs{}9yp|(iVvR`8wMC}D z$#Huetaj}u1+4~o_IyKNI8B?^NZwhsi4LYML&~FLO8@e>dQZ#dUj|p#+;!IDIu2KF zDDEjC&r0uPj$&SAQ8gP528MV!!l0A6frN-dP$juF>$w zxlLQ=7?Wk6n-yfesVZ@d{d`A5(=PEAZ)JZY^UTHr=mJ~z9GRZEPhb%LWJurXy>Xwc z?-C-<_V;Z+tsg7!3B1L76GuhQX&E$jeJrrpI`3-jgB2fv|A*uvP~=0O-yYe5oS(O9 zIy##?)>ynPL5D}dS1N08Pv(xu#L+XEa`p(eu|CgcfPAwlcj?3N? zxKeS9Ui%Bu_tiRS|J3v?yZgkxhv@sN#`h=F_nKkS#^-CN- z*AB_@^ams_6^dmao=)n?ma{JvXY>XR|x8{hK8(G9$99_$`%HyijEm zE4PTz>hG4J%e#;4Q}S)P^X`9B>n=mS`ysJDdZ zwncPY1A9)x%Dbu_A60#z{V?uT%;Fxn^Tb|+t6nu3mz)Q1vrnTtY{?yKoWmzSyyR8c z<1S|tI7jUA+IdE7(i(|{xcuQYrOXqI(uHf)d5!4ONo#dF=RSV=)5;Nj4OQx=j|a!7 zi)8P&zJF#7vEC5-Of32Cvg{QZt>sB<(xg?kUne?S?uQqjMZ>j@cnv;}(7b-U)Y-XF za6hEmjo1YG_md{@!WZ7Q(fB{N;paS~vgCYHsNlEWNo(xACQ_~ySecuMe>yjb3t9ir z{e|uLkbDoNo_+oFr+G8kpOk5&OsM!xue2q$5noHU2k&*WE+OBevi&FM(_4oB1ii5- za(B~7-@g&Kuw$0sJK46hw|ceawlp1z6ibE?&|(o6>xtRSR>Pw zoFhr$1(`D+`!GD~MQqi;8sCV-K-elREn72?9o{gTGiU=|+xGo_y!6T6^YuJXUGj?j zEx4!tmhf7Mb06C;KCjH90!}+sAIw_5HzA-nsazk}tdya5WB-cc-1rb{+eB zV<~iV?C{I0q^!_R z+45P$`;fyu_**?zHu}QH-yN!tIlIFD+TY)0uqWQ|$Q-Ma|ZE8 za@7-jlOW!btG4KUKLBnt5WFs+aukyZ8YvvTl89;`qu-JZW;W&PL+xt zL2hHBkD2eU53f569n?!aRdJ6_+di+9?=f1M@AIg)-kvkK7HN2FIHJI@9ynt2e1m1> zd|TX+$4skQJyD)`N$WrMK={IM4d28X+#dM6NBd5U8D2jOulMyb_Fl&B&owfylR5d= zH1434{u7I&O~&|y4TBLCc!h4^clsHJQ6)M+_VNUb+9fhhJI0cjXo>H~cr@+=hrr#s zPsIf8)%)yk0-84dR@nIa-Bzh@tgqX_)PX(*FzJ1-0oU)e{*O@3 zAK@En^ufIhwoc-28AoH6YC?z9FqZYyi`rv>_Rd&@miBnmc6%)MX#R5IMU$m%X;bJb zeapCHj>ee8ww%{ys9J%Ou{9w(Go`#u*Tk-H&j+T|4UQbB36;Cd(0Aw0^^fA4z-i|V z2>;4=%Ar+ldE%dw{QCs$b05mFqhY?iH-z7NMhJfFx%|L-^0!MLLT7mvB!Vb{s!$oBz!0^I=omY_)Fn0%I^M1 z+W+)?xm(j(bV$HetZ|M1e7%<0F80bhwifwp!}e|{ene%9-Nnwv!MTA9Ta7bp+pBGC zF7ZbAi?!1&a()1L*56@dEo9KnIyToM{)pIFvAe56$gS#~C1YTJd)9;pp_A;piuIn_ z){-CI*eiU0f__!8`XF}iE!o@tmcElD;QA-6zbh6ZpXeU3VWQ(CX5-+{BRW^h(##jl z@B?qL4{Y1L0!xZ6^hn$)H1!WdHcVj0CSn)j$g_z&+cu0bBhRP&o}!FP+br7Bwq`4~ z<~ymj=C@_i@26=qavVtSC zLC2j7fumFWG{*Lcb3H$m1IAa6NgK4)AC;65blrP24Sj zANP{u$E}d`qL6Y6ow!Fq&XIeAqikP}v}I1cw@G))U6aAsi8S0E5?J}>L0rb%dW`P{ z7?aH_;{OcV~CFvG0JD*AL@9O zz-#e-JRf&y-j{O1>qS$Yaq$clr0{ac=4Eux9KkF7Z~EWdy?=aE)wMUg&m@o_SmS+T z8*PvY1e-{_pkg~QK@!FG(4bH3g^Cp)!VkTm_}tiHotPPBCX-;O_`X5(Hq-Yg-5)_UTjf8f?{tV*ha+~@@wAjckOfLN0{(Kdi%N0^9SV2IeV|Y_F8MN zwf5S3uk9KwaVsb3t#Q151N)$L#n-eSif@|kugiEr{R5CSeMTo>#fXnscMZUvBd+3y zK6{uv^|d0ttS-5OFexXRuh_?-`D$pmCk@C-h;&}vhj=4kip^|-%m5Z*kCC%8^cQrU zy~hMkiC=%!mjB3E8RQ@N6fosnH)8H>660K;#NGn;xOD~Mgz~R)UBWbs*9kX<9+&br z$6p*z$Jji*PH2L*^x9FD)(^M)aZX3CjUq1k)m*|L&rCj57y z=hAmL7GsVDKMZrNmfj}J)t(;buDppp$+aT8ruxYU!pDA^*#(!YIR$If(i=}Hw|gPR za!Ac|EyKCbHg$=qw%PXV5{_}SI~Dt*GdPxzzC^7)i03{$e+Is&x5g8u`}-J+;1|zq zn(CXzCWxgb% zeZgVawRAyX0iyM<&6V_RCwh@@m0@wNs(7}s2TR-P)NlU!Bzf0#D0_RuI zeF}Sf>^a*En+}`{vn+YNY}YT`m9I)$D~O-nmJjezrpTN7an#lw(y%5zP5ce@=z6pv zG4(%eWZjtYd$jUXz<%zh;KBP9{1ybSa3_b@^JVR?XdYyO2k^m*8w|y7%Z};y0iL_h zwC77)Q&a8o{exGCQ(k2dJl@bf$Y%cfVzvVrZ$A#1JPvu}n8v)a7V}EE0}Zw&)CswS z{@}XT7U+&P=#Jg4EPYob=~lFMpq;Y9{)_IPf^m)hl9obm*m}%6Q?bUm4|*`GKTY>B4f8G=cB^+S5~nO=qfxEzSHZm-w1ohk}=jGV}|nP{yd%gGi(`q61dU_LY}c|o6pPMw|MM9dz7>FKXo%d zp{ZWUifxZHuIhsw`dW+~@@RM$#7p1@KJgwH@wM2-YQTNwkqeU%J!i)~1c;eXUO3(; z8>EFj){55l)x%yK#CXH{OkW%Au|3X2zp7u278+pubL?wbW1a9m=Sao^$lKmszGL4( zETFel#|Jns9Z2^LNPJ+Ff2;O8C~F)Glr@=SINF90Hrk!zZ1YpRR9{7oH!Hq$xA@NO~Rk*AQ;g}%ew57e9I^Sy(={c_IRUjccOwXg|d zgS1~TQRGnDlJAKRh`n1ex-j)c!qqW|?*{m6!JJ{$d64g52V(08e$=0)9l@t( zGxfbKzmTUi-M?MOSf&8~(Uy?+v=`*Rt>dJu&}P?GmR)@a_QVSraZ7A7N8|F{Bm9;C z{tCV)?+Ksdgz!05Oh>iQ!m2O*PtfLcyMDeC^`XmGf%eqrs4x9vzf8wN^H>glg{F{^ zjEG%^E+aiov&+4fU51W}v&*pK@cX%L-_i@+CwAEjJ{wm{d!~B!A+}ZU4(#&Xu*=V= zr3GJ(vCE*9seUQ=5a)pW(K*{RtkZ3>KJOG~lTTqD zfjd%7^^W*q0q{!vBJ#lYi!yB7plv1S8gGAIu3?|iR=M8muwBLr?IFvXD)};gr`+RR5$|fp-zA*4#n8#p z;oHThmpO-m4)4Rf7&NEv51z^R6CI8|m4vsQBHVoFCF-J8fKPoC-K&xPA;0TAIyo=% znQfw2lmF;~DHm5YR@h_!V2s{$u@hY8`VK1SMjkFmc}7I8() z%R4ZyN#*!9RY>{<8ns6Ie$_`TulxS}i}{Q_NrsqysntYACA_fS^oTScGV zqVL*`{1#8&@!EcaKc-{p3;YiH_Qlf|u;S?ZwKrLAX!?$y!ta`AOYh<{=o?wz4bHF1 z+g`Z!h z@fw(|@%*LeMBvSJ!t$95)K_6&n}7?>l=o-rIb44!;4|P(=U5&}5={{#YvQK1;bQ(gB*$f+mTr}NVz8@28? z{uX*<<6Xqh=Ht2Pk+NLYxiCHQGm%%nqbg$vJu-=WhaSm0U5`Axg6;p;=#hUBew|&9 zY!>*$dm%UFx$!ROZ_FT76LKwnvUMn|*aL`v`gZ8{{|B>&5=Z1-IV)co&YcKNtKO?xf_l61r>d5!omCCU zchwefRE4nrVqd{JoU8WXT=r*LABX;cv+O?A5&9+bQQ=Ir4>`xaj94jhkUe==HDe!8 zx8&ZMi*wbPh$SQE7|#YH2UjX{bF~~cMj=kFTDHgK9~-`ED$ZE@RBPQjwHtd^4`3hY z!PQgkJY+dyXLMim-aqLJ_CLn-X`mKh<#h{{X%0GbRDoWaJ$y3jI(G zN8Yh%1wJENY|C7n$sdC=`99P!vE9xaribV zGsdBUH&?mQhdTcwu&)`&5BCV@?K48pp)be-w%%V=)ePKQz`Hrf1GX9cdWwBQF0g$C zHzGI2VPiM*nAL4Z4hZl8`MpjuuNV5-A^E%x8*Nsf4zN$Z*p573$QkzwpYbR3r3?T5 zh`!VV{|5A>88q1hUTy&`TIl*ZBRSFvv9_{K z+h|wF&XM--Z|eT4!hC#ZE)SG(tqwc#XvZ zx=W91(N7UQn4|lcP!Hz3$Ua5&pi^k4Vd-%#up)XhCm=M?xx)tO!JK=oZ)ebhW2B6& z=g^NqdT@;N#r{q`2pKi3nEAEv0c8!oCjh^&Nx9?J=VSKFA?Do%zi>DF!glVH;2Jgj zmD!YcoKr}%&tYR9)@<;l{W#!p-;>xUtQDNkz9$QXzTczk7%TCv7WO{_?_T0N_#fSv7r9-`2R6TI zj-3w-Wol`!S9{?zO*Oi8KCrCu0>_en+IHIbXgRE{!yhn}vtM9OMb0hQboj~1ENhpe zKcI7ZIYmdqM=c+soR#~_DLNiLe2dtEXRDU74)HB$lsvLL8&%Fcskq}KnfYT@KR`UL z65R^lR_FfOj{DRRlg3?1IzQNADI>ZS@Ff2f;3&x*whn!f@8ge2U*>{N^Wne6=AHuW zBDtq@*{v*F3tuY;pL?R8KD1E_`Pk$~epmj+b0)_a@}&wN{XXNQscJ4~J@8=(e7rjN zdr!en3ZdM3__!_b12;<^u`$MO$s^`7_C@lDO*Q@~dBnyT2e226`En0R9)UPtM0H-4pzb?VvRgAJ^Ulq)#tTwU(ip_G4~JF zpGv|Ztur5mY{75C**h8kpuOLsdj@dc47x}2e)%Bxj(`7vZG!H|`xO)zq|3Ve$BP=evPs6eub^PU)H>${xI@> zaXv8@dB4UW?-%kM!>2c&+>X3oz!hy~oICA@i+m@hy`yx<{2W62VGnosV%->vvSi{N&v8h$YF`y|6q}jtwHuus<00=b1;W zVV2M^l1uGNLO<=R3>P`o@$gy2EC+f-^N7Jdnrh6ee6Pz7`&5@f9vZ z9AXnLj1ER4+qcdoo_8KG^eqSfvcbm;`e!x|pN!x>aM10I%>_5mrtN?sy7a z<0lh0;;t3EgD+=7PwgFi2MgY3Y~Xr@1*Ym)TI_~7qcvI3jw!%@wgp&ncre? zlm16>8@A8FP2OwV4%M*EAlxqFJB{17R>^Z>x)o}=rOuH34M|(3u&l+C1E%9pE`P`K zBv)`0*pBzl1l!_C>P!n~!3q6dkAAnu_xs=uwiE65i^8L5zmr#L7=7|y_xs=ztP|hw z+|hieaXTvfJ2Ou%I$*)AU_Id04BR>f;U;ny#jQp9AH{8$Q^V+$_Zqi%J|et$+^o4Y zKA1^qnudvaV$-d`f_sRtfZHbE))kN2J1h7t1~=({6gQl!LIG3t%6khpO~W`@{>lf0 zBW07m@VY!Fz^$mtQF~`w%A=P5DCg6Q*QPGwz0s9-V=l?DlJwvy*4Hu=#G3Kj0^61$ zomb#153`Ie|A6q(DxZq-FY();^0)Gx#_!u7%5wtzqIw|J8pAze|3JIV!Kz?dJni1T zjc}s0`xkBJB6=XHUDNKAyw|jQ`*PNa=j&A)`A*~ZcW=n^x#2d9Wr5ok@U;zjCz7K& z>a7>~Ee1E~f0VC|ZVNX}7t}Q!Z+Tf~5N;RqoyKjX(BjPFA@zg>cQNaMX2|OuY^4u{ z{l=F5eCslPi{kcz^gjkSO9yw$d+mQ6&@_ybx$zGX4)Qw`A^y_Q2^%77bFd?wurZym zC7rM(ov<^=@!&*`haBX1m~=aAqNTIAM;5Z(y_Il4#}?4BJD$e}q#sdSH}$h@46aXU z7@x^|&Eo?%uuME1vtQ*q_%pn}x!kT_w6=GN3%_9>R`C18AkL<}J0g>1XiIPpt+iL; z4Ug|*4rqGm^*0Mv zMgmy(MDhB`n;KS6Lp-d0rTch^l(k^(PXO!E7+At5?2FkI534Uj!_v0Wf+ci_=i{3q z2b%UjnPT9m&cZgH!GreZ4^EMuib-SE!40^RlQug?D?Z9c#@hwUXDtv1uy z9%%^JJnNslY65NItwK+<0l#Dai)=rB3}%VkgN!L1^tnGJ(sYB-Z--%^e)7m@8TE#htO7XsYG$Rf#plWCp$DPek-(#(jvJqhR3Ce zdHlPVB5lU=csKA7*^TG%m>s&!Pt$ap|GtO6W7-@Q)22B@n?h&lXMCGorsH>832#uF zxA7f#un27;&i?PS_&dsjq&mtK=q-0RQKrZT-e*O*dZu6Hm`N^?CBGwBNE!Ff530($ zxKI0c0^61`eb(#BLxiu}di_{zTWp7O7-{e3pbK_X&Zo!7ax!h}s(TOv8!he1m_@sj zt#;wtydg3)sLjP}2Yt*&yO|~7*B^?sD`UqIVwAC>7|EVRlF+#GcBo>815kvqI0XC1&zWXSfr^!#h|>x7FnjTwNsuq3?q zP5zG3-{^}Ob2H<|+~PY}*7aqaC0U3$9rjkZkgtI2^^f9govD`HD&HahJOhT<`*{AQ zY+(QPQr8-cl}t5<_tfFteyK~Hk*Ug1cLd847HP?Ert*F&^)Y5#*iR354@#N1F`e1Z zcgRuRh5mq6dx!CNltxA=Wf*iWf=uO!EER%I)QcF)daVA|%Q~maaIn>M{Pq;T#mjK! z3cmY1evZcRwRx-`#Zhf0j=}j=n;~1%>~Xdlc7=FSry`y%a&|h7Ro%c*#!x(tU;cn? zK%Zn6`1!ZDPyg*nE5HZ*cI&@A`wK$+-7`o39(answcMRNs=xJxbltu@{db^4{|#58 zKj6#tA{WrpTl~)k8+hi%UlpupERS&r(=q*0G|4f@vRWpFENcqhy6kmA`=Q~QFGt~C zCvts;vQNa6ZQ}D8$_jrh93DPP*&AZuPCrXok#P&|=rfo7#-S+OYd<(IzqnyxEpLOe6!}o|u9gwmZqo!l;ZBiD` zC@ZwI%09^V(YE&rf33Ffl{&iZTkUe_KkWC3dt$!j@)_+N#lLp^JIJ>2Z@=9>b))Hc zZI9H^I0!8@4u3wz_bkiYA+Q6|Pwmsy#Pt8qgZjV!T7f71uZijZpQZoOHomR)61N(8 zXSG+sdcY6%D?Ev3?PPu1Kt68h6w~;M^cCv@acArjl$%F53CcyzFVerm8hFTp`xeV1 zp8@fa{-ZAQL&~~0&hypX?pIE9037T^kTa%3oiB~xf%Yh;E3Uf$pr3$%QwZAXIbKevQFVS+Na!co=-YQ@S;pY zcER&eqz&mp+&t^Xxjow^K!@y-@lP9wHq((G!nyi=_|MUMFhcOX7HmOPm9a>P^7c@D233^UJW-fc+G+?oYeXJ3VEyC)NCW#=Lx2 zO8d^@A0a0i_9o!2Tm$*(Qm_{nxzCWWlI0htZ1#Q=|8j3mM}Hi?-FN)`3fxtmk9*?R z;IFCva~0bIJrbUI0IqjX_Il?D4cY?PD%;9i(To_oySEth38*^eN|!uSJ5*?Oukf>8 z;_j`8xBqAyX$iggpTdvIW03d7$OG^=bLZf0+L7oV_J3Gq4B`{qrm`912_8Z}{avIp z=RJfWv`=)e0?w-J9db>T_bI49ZXj$OuQ~ViKi-SRw&BC0{XZTejLHwthw+2*h7Gg( z@W&}q<`&4OpZO>IR)D^3)bHn-XE3^c8lGgGnQk_XV_OLqdb=z5GU$!9H*1{GmmzM& z(k}K$4BlsfbChSlYiT=a^g)0y&X-2i6TquXuYtOx|B>-zjUU|cl1zDrPTJzRAGklR z$34dw^%ZzL6W=y@A4u8kUWRf1fFpgy1js7ym2;*nU4v)JJ@_o=!%D)-b`Vz?%N%3& zc(cYe$8^R2)H)14;}ggcnXSicc$ds!z)!26dY_8=KxD7-xJAFKIi_%@iN2#}<&d() z-2Uq5bmDT}bQ6ETdx~{64ZR=o`AmJ1JACfv{{C=zlx}7(w@2o zIXZbCfFch-_ad9_XP~*5tO$k7Cm`J#P>{@o!7d?n7s%=X-+V=cDHlp~>f? zXQjycP&OkmJ@weq^qf4JyaYXEZzXKY0j%ey!QOodwxr!ttz5j5skcdW)CE;4e?w-w z@^Fp>wz#{v%9u8D19HLEKo`6_e8$Yju?7S=E?VnYGIK>rEAP@OT5C)TK3lyPFqvz5 zsl@jTRf}`U*wgit$YCHW@p<9E{OY|vKk@)JCNGln>c!T1b^P`7jQK^(B|01V-jek> zb;JjX8;5xc0ytx^VRk8V&%~coUwRGu3mpyrKKlrtVbfJ$Y_;USazQVib{-$`%%RWY zfA0`sV9d5~46`5lJbv=Nd?(&NTh09nI7eIEMxBCn=33G~`tG=~m~}D#PwNETkxvgf zH*o(4XpOPm1wW-5>l(7w5BlvE`pv`f5$GwvS-)x?^JJ`^M;K^#J#CP6R=*BCZen|}${p4Gq82X2PRD^y>9;w@;>Okvhpx?^G^!u*pL`}bO!n+Hj-*+2{e+>QB zYx*UHhM?bi(60{k3n3q`!Mefu)vYLdNqrM?Mq{puZ*6r=s?}6a%KQnmX^WxFCW|(k zz?)XZ;ewOYd^D-pJjnT?}*N+ez#oQQeLYH}@nPVDt ziJhAP_X*d;wnwbGOmFD1SS~s^MwSFefHre)*`;PROcBnCQJW;o6c9Y z*zUjNAwmC{ZzMIg|8`ywiJxJtC1_J*H`-<&$7^h!_m)9zUP>HUhB%>3jAMlyCoy?P zF&`kk;R8?8XZHt+MD8uVgQuG+a`ilpyrx{}@&jBSL=KLnqU(S&*B-)q}uTYm;Dzbv|h2TLma&x#!!Y>bFdBAG}9xF*3(=k?j1y@Y|!;MGSt_m!+ANM{l?%s2)-v{ox0dG4!y`Sy3MRpLzEX?NjVfbJwEaK7#oxO9F>CmY_N^TC;X2R&H2vNkd=J|CW9ms8R=s&vJ&{4oL9KiQ z-;@5Jp2>RXpY$Jpi&@G=euelWn9h-V2$HU zzP%qdlst8Xlq##HkN=lLxWl}Nb%@hYF%{^uw36^IUL_4BHjnug;Q(*uWMbaTOvEVU zo=&Xyam_CkYeQJy2``P%oU~;;2C;6^oAWZ#A8%tnunZ=9O7K>_xG@_HTOQ}GYP?8S#bo+-2vliX&-ZSoEMMJKaTyS-~r0K z`6a5hVEIz-&})ZdA{Q}#9^>j*XLzD$`9t{qXPG!uV@?cydHONIjw%@NPG}= z7#F16GB$|2+qcHV0ow@^Ja&|XJ8RH~QIN$i`JaK0K|C--$#?Vxa`Xvvy0`KD`Nv2G zW|Hr$&$1oO7MOr-ghT>pBETgqXxg%`)q5tW-EJYi683s*}pMP_u}`-nBVL1 z`$WudPyBw_a1q+c85B1@Drb?z9v?f#eu8FkvFPah_Yb1$HBGLP@$a91I=rW#KTE>L z8qWet?4$)Ni)Ep6=1E-f2=dcwoue?$-Mspanbqn>%$rk9-fzW9)_W!B=f8yO$<=3bG`Z;Z9ytimUtvqtH0`Mb9W8#x%` z$L*E&xc%J}e!p`q?v_NGyi<~J7{j*4j^BbKy0h{p3FA%mL+T*jovZZP{rT!38y~h$ zoK`QUo_XU5;tN@5!I(MYU8pg0`-KG|m4mSj`!KLFaC1$L>mt~$k;;jfoz6|URv~v} z^-;8iwbRQ)-u%zjxcpT$ljCz6PI!UsOIg@#$l0XT$FXl=ByrRCi$nffY4C_`}u6Dbxr&W zJt%QjpFaa}GQ(+=5&knLs>E5xAx=0Qal#)c$d)l_NWMu7dY{Pu`Nmxs2N!=G`Vs4A z4-q#Dm)rQv_S02h? z&*~)jTNy6+4df+kljIr3m`U~HOeFJ#bDn3aM+67-V>s@ZHDDX8`225+*&bzyagejh z)AWCEWA71k2<{I~&@Sc^8Eg-I#r?nE7v4Y?>WK^AXv1yc>v{=oQdZ)<2-%DHB%A>9o6+J)?178gNIRS`+PMxGI(8 zXh!>!*cXmr-v-_rUz-63BHc1+@{Y{2rWYx&yIT*vrKeutM8CP zoI~#3!CbjRU?c9vJHJfz9l@PA$lP|SrcD5U<8pX|2lcQIBJaSh6a9?zk9~y<)^9qBZwpRD7n#226;mI+dP5pEVN=rOQU?j9em`y@FjIe4%6!n~7le3>h}KOerFPR}`)$I~-R8O*ThDL8|+LQ~$!1)5@vZ=zh&=Qq{& zu4R47Hs;jXT#tzMgR9Kjpm$wl0~nLL876 zdq3*!&daZ z+iSoOKPFd}Vj;?yPW=_@4>XWrc3h^ z0w<0x-qCzVy0AUaIGr#wU3Sa((Y$HCL+Y*vO(S$UdF0W4&}a_kWuQIx$1%?5yYC%$ z!49?5R5}Nne6!xOOywc+b+^bHT$VJ~53%-9l;ozJWZ62rGn22rjP1E9FkW#Vr;Jga zh4>BZY=|_!+sp674P({N?-mK&Iw1eYA@iiuamf8~$USxWamfC0$o_H2{&9?mjD&d66@};V8my>=$=TL)-!T z#j8&U?H$h_k^aTW;U^DS?FAz3K{s)o7&g3!f7vGHRArniMB1H#F*zFhG10#1*dVw{ zyH{H6-X!gw1Wpf1o1}Au#~=5j%?$MM;z*m26ZQ{nPO|%nHnZ6#`C<1J@h!bKK3V5( z(z5*g2x%j(Umvf5o|~-OX_B!kv_#*~#x(c+%ISZvCf&cYCIj{*6K$mi=GTl`b+xr$ z9DBw6!{J|!2YruA{7CN?M+^+{q;&Y|)6nl#Y`1ckmfuRzEgxqg7Q3FdoVEk^>Tn-0 z_}L`7i#VYDMegruy53l9IKj^{+5^lF(jJ`&o?eeOe<5wEWx?klAFD+MmsLOKCjCvv zdf^r6z3i6NyuU;98Z<8ndqto44t?Tg`>UZ#^jOpJDn6HlD}-LavyF1dH8`B1F*Tl{ z3ic_2=TZyLc}5HHoEgFMVc_dcJ6FPbOl~MRhb4gg|nbvxwr=g&Y^y=f;0L(p8WbFAJ)$Ne+QoYBw zM(!)(+}?DI7P%7t8T5W~D(lX8YRr@>JED1llk-s@6tg=EM zwBfhf>JV7qx9-c`YgiuN%dGFS`Iov~bbN?3n136~SnbW?vy{PusdfnctZ&j+z}kYj zllV-%0mNzmuXhJ>wYNbxq7LRw?YL7(?ilRZ5p%y_mUWL2)=+wJ#}Ve?yFLGmeRVHQ za9942{=yC~s-w;PCjKqx!+l0`kVCyy&7lwA!rpq*#DV{ zzh{z3$noiNxlP8SslFyQqu?OqvJk%2GdOn^CzmDt#DjRo$tGeOb=y>W!z*eI^3P<{ zzYKfvGG+5V(IxIjA)BBB=k=!guFwGS#mL;;SN2Zgd$#Z%bJv-E!1k!Cu`h*p6GAo* zz>nGvnc$fe`23n~x8FAik(chKWfGWL~eP%t-Go5ujKC_hb)8I19%Xxfn z3r>KQ?x_YYA-xWv<68gY;nQ&6YL@c;LhyK(H}~o&?iOs~Y|{<-m!=>MGXVBl#i0|+8SvGbx~$1?|u^Afro?zIMb|h_r#Q&#B!X2v+sI*Pmk+A z#&>cMWkf#!Hfa&n5B;@)oAfu13--nS#y(&i7lJ;?q=(1n5Zc&$h3?mQ#PZal3vfMw zy3O%A>|dRq#2YQ^wgk4cgj5}D>-MQZdsG1>JSfDk2U=e=WZNdu7HuL zalh?l!U|T^6b6r|kl1eZp!f}b1Had)BHBWZ1J4-5+~EHfuT>5B7PH^OXy+aGBIDpO zWVA)jX~B+}>Jzc4mVdWU+K$leJ0b_f+y5){6Mb;Cv44MRhmj2*_- z_Cf}h;`<5qP1YPBS5X^cW)Y5ULlCERqI_gulEnSy;f^HeU^`}Es%gS+mZM(G&$8%? zzk~J)S@-`yZ^56`vhq(`*&f#tOe1_LpMyO@SNMp(yhFYn$9Q61!I=Hp(2t|+wW=Z4 zV`Sg4C#lA^HQO|F7xuIb{VcA z;oPV2OL%AWjpny_9)J5qz5`FXkOOIGJ%lk6(L)$J5j`YlR#wT_7d<4gDCi-~$wUvy zyzu*Xv5nQgVmm&ODc?JZ^w7zR3wEfLtjAar<&wCXj&EPg`jltE9eOAlPnv!Wzlknt zvUSm7=_h0&I-kBmMU^axc-@bkQOiLxI1UF8am`q=D8&-ShZ-;kxJ>A}{fBI&*}; zj?+aKzrlAByF`3a`(z)k;JY|o1e%I2ip1;BMHk&7a(aHcs7Uzz`E=0(*Aj27i+aRo zit3`Di#<%V7ysOK(a(noU1*EXOBaoNi}kcF>i&?=TF<=ng#A9Ei)5^q2>q=xi|sO6 z7yX;?OY5S~RtYTn6%pJ1t?_(k>7piCGifqXp=-)v!;GqDs}Dd2<-jMJT`*2%7QByn zPm)T8UsMX2a~JGV89h6Uw4Ni#*H&yyg3siFA5{cD%Iq}^=abJN);LY}3GpsE6-Y)5 z8@|qph45=(=Mje*wJJ%at$~e1OgEi#{#Dq^wkBWqALqu>JUI`o1BJ||mCA}V?>SrdU-Dn#5 z8%PJraGgh`BJS3Qy-}m|+ND~0=f<=f3-*Nj0tG6yU~3o${=XEQYUB*KdT6SmI*(r6n@rh_*rGQ`w@!+J*gM1`AZt-D6X~k8mT&9`{2G|jG;`q zlz7a1)Q(bM8v7--aANe0& z=JyNfS8%E|4*^cZ(VlByTrA}LQT!G3Nzc0q^Vy!=FU(`|P_E6U1>+i|5B#N*c)tsA zQto|88*=Z9@v)YngnM7~ed;lLU;K=->%A{m%UDDktr8m$KjOX@h#O@&Iwo(|;}rCJ zvK2C9yeN8+bx`+&%ym%7R4<6k5@+jMD`I&1d}o%NzisPn!hS5|E0^`L1y}>cemVLH z*b{*@O=}OE|iW%xb+ zCgQKkc=iH%R__U07unNrUUZF*HIhaHeV}tGXsBZVfXjAb#$HHbgqSDV>-45_Z5@ms z#@|A~X?1OrwR+hzuVrJ}4we=F)A*MrKEprO@(dm5aa;5BEX>b~4L@uQ_6-{$*q<`k zpGB}ewXi+d3uZLKeznPd!!NYh3F})pA1{Q1@`GC$ngl3 zQ87U|-4{W3ZNy&P$FcABB28bBo1duU8;_`6++$1LGv-zj?szrd3H=>V%{T5aTrqQc zo{ee4+_hEaewfGd&Tm%;Y+`HI;vsSZ9 zXHC6i0Dhbkw2s7lL|z#ah>82~ox~C#BiesIvWKuKC(t+L@jNhvf0PS9#s_8H)ka%> zuu04PT(O0)r8LmiE6-=&=#!e}7%%SJU!;iq6yXkqa^*mem_~EI{T}o$;&=R=#=o>_CE=Ss;&(kS zX?}yx5q%EXpkG3tL3H;ZeU7<9c|v{uOIx4&Hxk~^`uvj{R^wd8x$5%*BdX7JZ2Y}X zsmq~@ROLG8zL2&P)a|BYn)EBU!ICUW}d z2j~;mo5UvU->7YZ+ML)X+;WJ0No*6oDmX=L!ncKf=v%}lupd!f0RJmShv7VJVXXeb z?@_dGT6Z0~nC)uagXVOzLs@AukMQFjkYUBsViU< zT6Q@*GPe>xZQ@I8i*+oWg7d6K`=EHKoRvhGR_Kxg+BSwKY14L9UaWG-+CP`>K7Ny>9XVF>4u9o@L)m=ly@+ zMY`wlvFSF?#6MHlOZzMj8n%*e-l=A3KE@+_>^85n#Ow6W{TSGWl%#$@+V5w;!@1^Hj)(o~K^3%IZf%Mqu08p;Nc`4J})=d&C3p*W-O# zyuEzeV114S?9YNd?Qf$Dp-#-2K;LfE+sm`IXU4yGHt^g;I(XJ%y^ia#G4wIjbsKG5 z(a$*AU>@CpHbRN*@x&Kd&(@2@Yy0=1owL~EJn`LG&Kf&QsV~s(5bF*79_qA`aKLNf z$nj*`=v2!_XGCrE<`-B_+vuY*uHt2Gs?b&IjBkrB(>7Y?6bYWPZS-2<--X%e#y5z| z`P=Bv#E;N6`smAqq3v#y_>GorPGFRqDaH8$HjexRfw#`{P^lKKCUKiUD-4VNR*Q6wvJ)=2`_u zuqJ2CwX|RH&RsY&c}{gb)%#qu6ZUyX{MQWluQ;oVcpLf)|J4Qmwdi&~;9@SR=dNEJ#=3g$y6yu$%Qx|ZX71td z%I7$5I69ioLG&MVgbJk{Izjy`!aegk!4F@Owi^ep6Z}xdKjidpv`&zHjCj~F$Irh`aIe@K zj*0kng1xtDo%%mkv7HO)*ZHjzWXpKB`*q>#1aE5@O0-U3bgf|D60Z~d-9~=HSc_RF zxLx#f{5ru`7qcAcr`HMAJjmbHT0qP?!P6qMfET+?p!Xf@z}d(nh_NHi*lj50*EdY{ z^d7Y(>L%vV%&A~m2eIYdwO(y-j$_u$3@SZ*B@b7>Q>YE&wf5* z%p^F2zr1|#*?F9+!MTOy?$h6^lX&k>@E&LBm)9nEk2Cel>k_=j8UN+=3Etz3|MF0R z_c-Ifym|0@ea3&e`1suOb36P2lv_`qpT9wG;?)rIoA~gYuKk^Otc>~1_N!xlvt3Wj z?_~U58vQ+RkozpjBX@N&{Fs3dZ1oZG-Lh1N6<<+U>*<2urr@`w!f(rfzc$Ik`?Ij# z(~JF~SO@BcAD70vw9Mq^#0NqfhHrbPoNd$R+S=`NZ63WQI()bGUsj9m3C@2kBe-+< zQjXi`Ju}~%PQG(LEp1&%_#21#o4V8O)5iSXD-@tlO zHhKLA@(lcV?(Q_)WZ&B`zlEFl9>6UdxOw7n`|b;bBe+49fm<WEYyyt-1LJRI53%5MrwlW^KhaTp)7~G`)QQVT877TfB;ihRAhuhXS1Rwlo zCjX*Fo)gfmsLE)$(;vAL%Gx(PEM4Ym$^Ala`YZnV%NtBb`CEizpZ(J`*eYc-4SsUZ zQ0?HHyhq0fhUIAd>t1D9@MjWeus)s!&lm@pB~KY6cl z`==(>8HC%ldzEx9*M~0YgucLhNMSydiuq6)=0h2n4>@m#{r3JL-<;-ADgx73tUBeo- zIwU#DUHSag)eE4euV zta+@jVHL}GL2P7ZJYN0n8kW`>7OW`kJ-WW>cu?zVp?&ca8n6EKkT0AUTP(aFe)KI6 z1F+7aEyw?B~5B7ThhX zzYKW#ZXNhQGjds0=1A)QUBTzo+i=&X6OfWslGQaUMFl39)Ta5(OxFn6IqmgAg4Y2w)N89{N)D$tBq}u zhHTHiGe^$nB{#{KMLURL_H+Oi_5<7}vKXa_Q4QzA=D~s0&~I}8ND}?fB%!e@)4KcR z%b@dA^b2dwt`P5P$(T+$kdEA+VLI*?o{@HlNiKD*6WKWZ?w2ch_e;jbtV>wzyN35A z8SB7z6JR|o-?1hl=kA5SzZ}MQ=c_9<&+fIxzLqJBlc%^}CNO{bUOB@CpNMo^wG90q zz@0+_$nl>1;{e+Ojd%0j1K&*NqNxyR5_oa zchjwRD^YH?^<0g6ep(9r{2m@v&?bMmCoS1DUjL{}N!yG3&psx18ThLn%cP$#yfxl` zk>9Pdub!oBO-$J}XDNGEOj+p{`acv7*Tj@9J_}qUrtD>BF8kh_{4Q}U$j9m{Ntdx& zUN8qUjmJN1e4f~tUXY;={^0(H*S$pJJmy`F>hUnFBgFH(-J6s2T zfOp{#-&t_xNnP1XmL+mwwI}awIMj(H;VfNNw^Ph|fHf2UO8xU6n|(X%_mpeE+xH$T z^V|oY>ev0qyB5^RzB|In!})SK z8{tPDzbchh=2u4kIE=gJARD+VfU-iFVOh}1U>kt{5ayz_Xd~3yq!3$XTaQg4p27DX zTZnnN+zpH|QpY_=a+gAGxR3k4_?tAxo+ZSE!+pLLvx_RO#vYzj%rhEP8TRie_Xy>j zIZ_qQR4D`8`-#|3&QNz>VBJ{YVXs>sVZvTW8-z>vHteE?aC30yV~c{%KDLBszPaAa zIw-H#o`I8ZMTf3q?I{92bNnL{;iHxeiO*CSQyMm&+0J0Nz=io(Fn_Ok0?l%s9vUjY zB-{7jk63@P%JGaydfs26l5h7XXLaB=XvucEL08I0p_E-TL|M>9%Hl6{iHAIt`6u{D zo(V4lh%tbd^aFzHkVkD-+KpU4A&+_9)HE&@e5w0=tsPjmL+-C}$J6OE_{SKj_>BcD z$kUY$?h!^#2qTq!^CEW@_5_x*kK&u64Lj!~R^0C|NG)pOO=v$bw;+-&1KI;L+JaVbPt2QtfYt%lRH*|gr`vk8@=YB}=RnDN( zzpZ?gcE7J5cvFtRli(?~Av{5wl+{??Fb`KO?%rFFOJ;%bl;{2{()kUv-AMXiJ%0w< z1z+mn|1WZZ7CdX~onYb62tNzsDh7uN#3{)~584Dy1M>(6eaIs&#EmqR_CY7+?i5;r ze~9VSk&cALHmyG8vQL1Q2c012Vga*0me%AsX)WIYKff&L;53Dtkpn-pbWY!;q=h+b z?g;+%J%@d0>gG@R3^?;degcxKv?Y{;aRNU8vQmq2yA6D+CqH`UiGHY~eq&pp%cKIF zRY9Lxe7^=Q@g4oreL>y9Ihz1C8M$UB6Bf>X?3T4Mr1v5YlvB{fVXCSf{2lbEX?nlL zHqk{P$i+-G{JX4+c2|bTW7b=cGPw@^=gV9cO@NSlXzWrAnZGNopfC9fAgtW|x`YpMQiD$P5!#O+&wv&IdGA1a(3!n+RS z0OlEEkNReMH=eNu?BV&~&@#y8QLM!wUnzNDpCcnazA(lQ?#$si`OuY=wNC2jRIryi zeNqeF;cpuLWguUd(9)8lkjT*xV}f`S&aAS!WrR6XmEV*A-W}u({U_4xOz;SU<$%Lz z*0Jdo?+!kFE4C*0KrN z#d&o#8FEu7-`A=f?un)UfI2tu9c;=`ekV=j_u6o~Zrhi8E_kvYb&vJ8I#wn*c4-&r z?^0G6i=eC&Y*)9K%D0N}VS6*J9G~fW+$GPq#@z|%XSqKHx-1QHD*K)BmwD3C19MfH z&oBGBxA?=V?ARFGgVl+?HP0?We*8>t&g{~TF>1DV(cMrl-%LN{Q)V}QyN@X+-j^bm zZK*F*#C_qTe9flFp~OnmH1QFI7KhY&wnNwzCCYhhs+yhuP3XH^oHtI@xi}vGDRdgX zuR^&~_>Q|Jr`U9(-XmWl_RAVm)VYL7yg|G2ipB7ekl&Q;KfV#R5ILCPgNeQrIROlT zML7W-Eq_dOH|rFHu(48k#TN&>+6WJz_epoz_YWOP+5^AKi68gn>v1||1Aoh&J{h-O zFMmhmGsyd^lAfmxv1QZiS688pYoV)m3ymlo_%Nq>JO zWc5ka3f`xoOdikw7BygBLK61cg=WjWo0Y#(&PU%zeazi_xBiE_ayWI%9Ul>{#5mR( zo3xBWryJTvYrSN26+dbC0Xx&9uHYG^Os&H`53%lZO{yI9XeSQ>!1*}(CNx2Ry>$Vc z(?PCc^sy6k0G*IGOCfI-&Iy1%pxHk1I|Myz+arv*LgqOFuVw(R0h|Y_MowG@a@md| z9=h$I*bhq%PX?x{ysTv^iT(HaA-fGiqZz)|-KG9@DwDR`x2>ZTIV_W8-C2F%7;*P_ zKTgN@G@ebT-(ac{7xO*gc+`98$l;TT{v!4=@RZf=4g9X#H72nf>5cF8mYh0R4t1Q* zK~_ywc^k`p`#3nfyP* zbT5Jo?q$7MbBEuAIDzO0%0Tfzc(VoXUcz&giN0@85TMDuj?0dz-gp0NLH1fXy zV-VPbiLm^9drX6zrCgmFXL zFw@s&mg>7LaMrI)87xa*7CuA>?;WrSrImnPIW5`Aa}%&tM+lewB=6v}9OSt~;)nh& zVSnK7yk(cc_f~#moA~||@gy9=1FWYb20 zHY)fIa70q=fvJi{uOf4P08#FSif&2>s$&G4cJ;uMqnc8E1h+Ht&(~gkjq}R|x!W zV_Q=j`97vC8G~p`=GAO#uU10+42iIi9CZwy`T;H9kOMb=UQbw7I&F0 z+m-wKiT}(wfYCm&TE=S;>Fj<^IgbM-bf=!{0-jBC^m|Btx)0!7&q17%#olMn6-ki8 z?~Cric$tPcY8Ly4Jfvw8*HCADoX&E-we6*dE2jCr^D*b(>A1H!m*qs~BR*-}8MKo1 zSRVWMGJI>1%k6HAGmN1EC-81idqbl^(;E0Les$4IHSB&%{~Sd5G{(nKKZACOI@EVe zmHO6pW0qZzwN0O+Bh7-S-`gUHpb#@~|DixAk2IaY8<@H;J<+ zv!#?WO4^-?UY^h^{~Vz+Xw30I-coin$CB{JGDZL|4Sv4(dpXE$?XDi~ zM9eK4Uz-0!(D>G|3u&>~x@+p541*fiw7@A53T!p;6czvOw6AY&?}EC)10U5N?t zek!TwbQZ?J=$@cs>rJh&}^fMLx-0vAzri%UHX40m(Dm?0} z^jP3XCtW6NP$}*O%E`Uio(H%`T$MAfNm}G=z#i-xmF?ctvD3Q(I30z~*bwe^RjbtQ zPQ;B6CqmmN?DEoPGRjmdgJ*$hv&jRY|FNX!pGd|&jt${(~X=iC@8Z`%$_#ZVu9I zVhHqGi@UAv1O2W-`5z_CoUp-A*oWWEK9fG^b1M4)+NE{u652UIyBxGXi82BDrGkEG z-LDD#oS+}??VVH<=--UGl%IX5n~J(=pnC@DIssSLhs>aEYJRn$P(Kay&p>@A;0g?n znx6-God_QVahm>xz~d6Yt#<$Th=a5se)-9;)f>WH;A<*yN^>8-^=QTNuRAf8^42`A zE^&9@dCxz%-R)m@tU-GfKlr+1)dV%HV);%-9p;~_O4Nv!*VM(pISqVhK|9%c91N7Z zpGQ037381`a?mC1OjD_73+;ye6-8fnt|?KMRIEUokLfl&!%N&1=1wFPMqAUr?x=eW zZTY|McyXk<810T=o4TEWG|0pyMQi#tyPJ4v71MX>{;(~y zt#YY(*$hf4~>TvB0tC-ix->PABMe ziTmH!pKs-PMl5hwT%`Ns-mBYkx9xO-ZX;Jcu98|J{pl~#bn89p{t5asJ-$C^NB8Fk z!K3bftCs$N?q8^=#q+U$a98-NiNC@(_GdWyf#l5UBC8);u+LznMXv)kz2f*%4Zc9$ z*bl&RzW6v`4-X!#NMT#-2Wa+#Y9x3DUzBbGeYlA2fFFS0=Lem>RMgn#2HXztx&u7# z;5+Jz{OYjKiS(){Qe!JdsIin!z({xRRbv4s4S1)5CTi74+~e|^#FIB`m_7RdL%DVt z91k6&H+Y>Eg>xO@Bv&jKIEr-y7T`bzG5{+LaMMAH4A{lZzMvDa;_!jqBjN7eK)AiP zDcsS!1GW!q;d1|XGw*&dwxAq*4`Z(v`qdXKW9*=90(=+V4dHwL>U7L0j^24JW!Wr$ z$}-0Ky=7)8>_Mt`u`0zqC2oC}*szBDoN&AM%3IvCa^G6UI8a3{?g`6HdOm*?Y{LnR zi@l)L%5XbuqtpGLn7d64cjx;sX6A-FU^AT^sfZVb!|ff=Nfp)5<)~lr0RDXw`gl0} zj`u|;Fi%K(_Xy~epF%HOgun00oi+^<)5J2L1!Uzn}QmYW(|Z z6X3tX!oT*Y-oIRrw(Eia8sJ}V;m>&&^lVP$yYLC2PtaBvIEQVVc}HBm#V!ssZ4}Bflg1vwZxf`eljl1)Uh*P}md9MbIv6e;LEE;zsR+ZO%X?K1_@#5xTCm!|FJ~-{#2Ge{uxTf4Ja-dM&vN7yt-KCn zy1zs4RSEFT0ltd{;hQ=b-k3H(0NPZbyEL6#;qVX0~7s} zQQUL$IOS(pJ$YU5YwR6{oZk7rQ*Iv|xzYhE^ zq#QBk!@V`uxN6tqDqiPlKIwCh=gTM0GK{Nr*QIN@>MObsKB51!?Qw(t=h?IX4a!Ih zUth1=+qeL0h$#)OmC$j}XQa){vXo_{4e3^}8=I>^mlmN3e6GB3k84c0r(y!e`9&D> zh%=*1k2kx-&Gqs+=rrilF|t01vvHPQZ67?|@<+oi^usP33U?r{)H&G&uE*Nr=&xZH z(5~18t_3Et3%*T^U!FRu<;Kxc@QQMTpi>GCE61H1Q|+XjB*V zV`#JK$DHPipxr*e?h|=^T>O~67#(=jhkFII%zj22*$uf(raekzBXPIRq6GG&8@fBS zX*%NRBOvRz;}v6n_=_bn{)fZ&rf-~V>28C1$^+eB)c6_g-+aUFF~CsI@*dQfKIfRpAELTk$Uj=Q7;v?yqo=q z?HBZ!^ed)MwB<%o<6rhlUy5iO2leH2TRs3=KBzCiw2Zg`L4T z39}#C&Lr>Gc4l~=wlm40|9<+jr~C7dos;V;k3qk_2A`@q+z-2(1bdrg^FQ(pzGuO^ zpiMYx*RkGGr{yAfm&gTlPtn?wbD(#^&^zH6y%Vre~PJLAGoF*R_%L5q8eHhC< z7*pMmu{;$r?8A7T8}8v)4r!mhJCtB7H(5S?NRQ?27VxtLWBEn2+Y%kikf&3vpxwY= zSwg!5@b3p=Y){0_VoV&>^%c-Zo@k3egu2`Z6b`FAW$6L-hrE z>Fj;!4Gr0s2CFYxFU9o*^YTS*zJDJ+*ZuIh+Eca_?1Ik)|AKdHLN3B-#-_d z{OQRdZF1PM$(;#oviOjH4#nAmvXpHx{&kEk5dZqD^o4!E-4BT4ME&co!E*GsrOn9$ zpPx2=sy-K)f-N`^S<5^kdu*S=8l&tPpiZ#&4B)=YJIWO1HkhMvzehnE*E5gGni$`4 zUCLFaF2Y)LQAzl)mt|#r>mb&_0moE#$vQOF#PnL{ul4>^tQXtss(ODB%5rZP%HK;E zXDeU$A;!g5>auC~srTSNcN9LMRH0Ir=V#X`EKa7DsrxSWF2dDu6q2c3@w{ z5$v(T`lRPyu?CNI=w<7kbKe+(?_Pv|XajO%Eb~0$E&zQXXS=Z{3+Di_SKm-wCPd2r z0LoNyfAh%ED6Rri(J!pwlLmMew}@c2oi(DkVHlSxBU z9e$lS$T}=^1Fh8pe1n)b+d+d#9!ybv< z7|Z)Q!GFkEJ7tu#)_fZwy2G>Jcj+$7H#E-g3axB9BJXo6#tdcILwcI(T?}2&<7vOiLGn>zhUF&|%dpX+A)DQUV+kOS(etzgSo$s|+_CB@E^bg+#d9C&N5yub6 zJ>SicW!#G(dn@W8!?K4+@`H09iSQWvRh8@oZ}TNVZ=oz`AY%zM@Ph_J<`S+QruEYQ z&)nO9M|GX|zH3W^Gy!b>s{|+JX82DeC+G!gZ54R2=owyhJ$0Z z8fVdK$9k@FMna6WuVxN3^s-q?{7jK&zv~R`W5i0_&XxVik7iUpV&?y$`fFwF?rb#m zKKh>0S>)L!@mTjm7GIHw`TY|rr*#T&?D5l0zs~zMC_jt)EpbZvc8mQsMO%BtqsWYX z;M3c))BR8SRZO$KP`s{>udG zEO(m#@5^`};C(soD|lbd`wHF%d0)xDR-Q5 zBh+_#1~^7}f1dXjcz;pjl@CkL-Hwm8#wc^OC*c^uk@m)oa$$-pHpgKf zM9Ve7ft)CW7LRl%>{&*Qp@b7;|LM+E*^^e@t;)Xod6mdq++;`=aSQ9eDBnUedrWtsm`Z$ z<8?kZPcZC3CN~FtUtN@fpHDiq?tTb!g!AFzd;_Wp5x8Ii9H|(hy(XGS7%-% zc08gvesgtnS$wwE27f?&`CWm;r_ToJXh3ZpOq%rvX~&BTFXfEGC_g{HGM#tGuZEw_ zKCC4!y^&u_#5B$ld?Ee=C;c|Yj=m$y+<36GjU(4Sfo^1aFSxRCZ^RNe-JVHi*S%ol zG9$!HGcLoAt1h>%gIk(RXj#0ZFs2w4a z?ho14E}S5{ZOmZA{O7y%ofik%B75sLY#!__VqUr{r8BxWx;`K29J@ylSrvNLR7zIG zADQo-lf*h2abrQoG1XHox%sDGSG|&7(nlUWv{}7%)1w0}J&Yg3{<(nNGj{nU=#zbh zvTK+#`j@2PZ|09n_Lu|Mm4VcL{M4*vT|Pxl6vruh_>2i{EwOp-bdE)EYqhD}y28|u zgSLv?DYaWSnyRf`+Na=z$dwY_N?vemBl77x5%Q@-(GQ&Vj@{U5`pLs^pr^qc>T%3r z1_;Kcoe%U#qVbN=>QKMa$SN7qD5ztnI?s zkw@Ck85d>b;VUN(U+^5`K+cI;a!!QEIT0r3M1-6ZjT-;t8FE$weND$^H&~rTYMDb;oQ>+XigjFCZ7@6t~v}!2@<0{cr5_(HCEFyXAi7R``RBbO&jt$KC?Q5k?cEG)_;QK6Yh#WhUrtJ~U-6=wY7|?im^#d-GJJ8r6Y@bs#K$QH zMlnNg6$3||{)Ut(rOsh+Fi5P%SbSRnYnDNaH~!A%?(^m&A48_5WOG@2YY*}i8w&MZ648^%{Ktu z_Sta~jqCfl%u%_eva}2uap#f9Cuqc-OOg{5dDMp7Ye(+I zkb80DUJ7}(P4a-TH1(~-yq>h@_^j16_bk3^q*3D@+lKG+KF0qQ?Ney@rtq(Qe3Dg~ z56S19tQZZ)N^BivIe7xUBMnbxj|f&TS8_0UA9)Qj@QtX$LEuy}IKb_lM@$}zY znHl3_#f zzN=#2X`_lWS>eqeY_>4m$b5r+_KJU39_nuXX3wclbfUd_!XNbM+JkvG1O|7%MFX~S zD>Rt4dviN!+uF?wUCep>@Z zQ`_>R@qAkcGRS*gr!j)A+H&Q z+D}J3`{|HtKlNyzUo4~XrnAz)-Z+=3=R2Cp=rGg{3s^hjK$Aubbra|#;}KaO~@-!Tsf2aHWS_p$hs^JB8J>v_jmx4Sw7 zo8mjejheHFZ|&aBKlwo{`FGMCAF;gK6op2mJJ2D?7YYK&_m73^ZnLo}6Shf?UWq$z zOwTiIdAQt*y^WsaOtz%-f^1SVX!Qc{L$|{o-Ht%F#2}I%jXq$T8uh(?4>pS8Uf6Rg z9!gHYw%qC?%Rj)TFH0qDq)+!RhkQ@MN9>)CR4{8Xpv4s9dG0y+z=vvHOea<;S^r)yR> zJ?CGV@WDr0!`idA)Pt`^uqE!iE1-Jn{H*=1gg21cn^(E~q4+KA;ZcpBV9|H_#?C|e z23ufsqJ896UA5bOe)%~2w7^BV{f=+?89xusu~?y7uhuBYhjSqJ9rEi~YNNXrfQ}hf z+Y`EBkL!06t%p*42d;8CGqhiJ)p(O@18u7UpWwR>J+Yp>FdjasXUdsx@aKB*3cRJ9 z8RPI)ujm`za($-_$p195|0VRatL6%X7_Q$z|eE zebf~?$hnG{xn>FL!n4r}1J>qo?4B)GF90*V_NIPY3wH__fn9J%%xA5S-IJG1vIp7c z?C1QpGGy5E$&>OoXW^4q4@T)1_OT-$G50shUo2Y+`!6LsegYn^ea{Z;mEA6z9o^rr z@ucpmt#eIk({4lP1#_q7PaVD~&#ScuTVp^SH7c8Y;dS5}|6SRr zntyYDly^~J?q|GvBv0eaVbRek$z$@7)Os?E9I}rdPkA~Q7^;%cCu4z*GJDa_J7vT0 zeyPeQ{dO*Pelv6%F|T}1xS+o&`ZTfjWcko!vvu>$o!rabNJ;+J6`HEe3*a5sH`jJ> zomtOWp216ZlJ8(XK3C51Yz0h(78n^p|mBUu_!;|MlTm>^rifoVEBL zN3^a9eQbE_JEmakGiK>l^13_=9k0s%{q^d1=zZqSx*+mk4r_>aS-W^WIU5@JwLHx? zJNRY?-;m28sC*IFF;RZ9k8S=5t~LD1`PK5P=)^vwzMQY%FEtk6{D0@!`?#;zfO!rc z-y3JISCjeC7Oq3_l9te#U=izKt71(h#V_{H#il^U(ueT&8z<_zcM;*G)Ui1Hlvxhn z7?q_Dg_PAkrCNPAy*#$WF}G~dAs8b;{*S=_B-PO?{uGIY?!~uf4dJ}>4#SiLstVDbL3c6 z#2lSi3n8b_a^T)6dCA`2+*-wu>Q()!8@_x;dziP3czJz}o5lZ~wMJ-Zwd#=Vuqykv z*5@2C-+NyD6W?=fzV-CsrZ1}fi8?m2*Ob2#3s=R)(2c_J z!Q0LH9G!-`Od1%O?B0Gm_+rbV)O@E zAK!;=IgKCOXb-sTzEFbo`@jE{=7j!k+sA9Ej;-S1fo9=IcDM9Y`~mFvB>qU|NHWu%GA9T153g1GW5^Z(OT&$2psgV!5dda0C->j+u|yb>N0Pf8|TA zuhOm&WA|A5M)O<6IIx#XeoH1f!Jf1^Z0D2M8m|6y_thY0+vLkwmHp-)<=}^Q*WUU* zWaJafU%4hv76kS{w;_|gjK9xGaLDPwxn_N0Hv5fsJHxqn1g`9tC>U6%>n>BNaZDbC z$L@pn?Kp1yGqs_*WT!NTb9{+h7$`m@TCz6PNpn1%G*QO5@eKHpJh6Pb%ULh^!Ctks z)M1*#6M3}4q3g|^qJ_H6rm7oVB>Kd+6Xag8E~e*p6Q%w}u4Vj`SFeU&Ilo%^SwTO^ zvu76XSz`PLvClH^Gqd}#&m_7`>61#M1=m}D$;J7DYZ+EE*}9@>Du-(ZK?d3&Fu z7!tp7C@F6scx#ki7oQ}mLyd_rI0SdnSFVSk)kyNy z;zO!W;~;vCn~-dSh&vXN<@Wo&+&Cf+1C(tMK14H=Z4oUu8P_J}?5hi*=gpnieifoQ z*@@&@xB(m(*^-h!)&|0!fv!UF-N8nckxl8+=&ycZ%Ra;TwAhW(Io|Uh>AAHX54-C( z@XQS32>Ar z$BCI61;^x&0?s(?n$U=Jnr~V3=iLQ~U`V>S0Qq;A`OW%8DKro9BF*8PUN#S=!3RFi zJCnP@de^__cC)^9WmNgAv?f4IJvxK-ptDB!viu-zAn&5WJ=ZdR0qFj1^_bcLCwqar z)WUl$8&4G8iTvvS9MxO->#frsQa>oWHnCzR8UG1S#%H%9<432+_#2V&`_9G41tnV- zJ(gYEVP*WcKAUSpGTxQ%rdI953ttZ?wv#ap>l@JlwYNu$ZBO!fTYx~);j2M@j9v$0!JM93!&Ie2-wI;XGuAfOC zL$?)a#v)O-()z#h;M~h|pXM9P6$Vsygp>kYY+h->9Wk%X)8DCwJFVXaU&d9n6)_K5 zT9B-^_PVDRtej>%qz`72)z|~I$t&!?yZ+8t_>uAB&{kzKNlr3w+ObaZ@bzZ3C;iPk zzYEi?7T0P^`p341Za7CfZ>1Mf_jcM`#?X|FiCUF?&i0LZrp$NqWyRLlA04x@!jlus z8-22Uzeo*(Wjg&nu_;~+{57@P&___L6=4Z&_e_>sgoMFl1zq0Yr z(B*1(4ZFblBN|Q?97v?>n)P)JC!E)sO3IdOUaxf$Y!P3?_1B+t4t1e#Lj#6*PP2`? zoYr?kUjt1ezGY1%FE48{;alUyt75#fc^G+DE||C~PTwI;Kg;(MIX$7Zm~5RFxIb5_XaZ91-B;t*czkeKk3+#z5cfB4NR21J;-U$AI*=b|!Gx4o&Jy&q?={RJp z)0q$bcl&%-_Ncy3M2p*-?{o9BHSI52-255tOUwYVJ4c%`MSslSL~Ek6^i7?9?fKDnmJTT| zTEypE#5%75=f3_TQ`B=m`+5Gv_%eQQ>6>+{aAxZPZ@Idr;~QU*-PeL%1K*KG;zbqL zoC&2|`nNb2?bD{^Sr0bBB{~sIO=o<|1XurDQ$&Bp^?eU=%YN&@n$I`tkLrIj_|Z23 z#h-_M-hE!@XHdShlYU~yw9D_HwmrY?`P|=}r*zQrq8I05`3(PWsMWz2HMtIzTX)LE z6LDT@7TU%$q`k2=(gbh+=Se{K%XwOR#%y;!J@v930G{cE`BA{YC2gHI(%uK8Hkt{ zdTd?0@r~?4Z)aHt%ot}0_Cmd5dh6$zq0zMIeCQ|G{(Hzj%KOom$RkL6_UI}5{WX1W z_xltY`9)<59Llw=c4@=PbL!bg9sM2isp}=v%l8@PFIMj+caOq>X6Vd3GYo8>Qg%rbJ%|C)_?6-)5fwT>3ciN^}S@TVj&V$E3JGZ0#b4|o$f3&Li(>QWv0Uf*c!;w4%a&koafJ8mj} zEmPDtI2*f*@Ad7q$D>PFe>wW2w56GF{E^~A8W+ZNMB@}O-?9AwZzJGxG>1#@Gl=eV z1fyt8ItCtIKXEqcDJR65Z(|;Rw!xpos5s>lXV$*t)Lv$7j(9)TAEEW#_yt`4Y@6iI z_xx+r{QLM*Iv%*E^Jn7#&s_fG{3KxT_|va*X3qiVo1HNWe>M)xv$_AqJpMGf^J(Ae zJNU6?0D2yxU5`I&^ZpipwnJ}iZ{*JcYjauO4R(jepRpPEa|HeznSno3^uHfEI2!0Ays<+`!;ya{F(J`mUu+IXt>Ok4JrX zgiT}`k6Iso9*^4Z#iLhMN5+&7O!BGkUtK;O_@}g`osh?;I;UImG!-%5eqOM{r#YEt zaSEOW^7ypPlp1i$hyVNCGWY) zIGIIr+0XEOY%T8`_6&T%n%N=IG~eP=HSJHXFtw^9^mAet`7Sv(G-G-6?eh3Y@u9lb zY_u+t9ZD=L2!wmHN7KN=da)z8RFCSIxxMUB557=hc5!=pp{davjB#xzXF2-3uz%5` z7Ot8E&wx?=3%d1Zv$^`czA9eSk?+xsw{-KZW%X6`tZ>$TRIjr`=y{0zgWTVM5=z!?TK<6CtIJ&U<8g>;}TqJ+)=FCu? zwm-WHF4PsHL%U5%6%P@T-hRMd%deZYcft9pqWZZa#!AIo`uJ;ycFK>2pzlIuQCv{2Z~sulyf%YCmyH?UxeO zztfgj7-?({mWP!0T|T^dVN;TbjJG8gc<-z7-!~G=!aL$LZEWV!>&BtQeQQ!>O9XEs zbv7WMvS0=;rhZShJMqjq>^#n+z}Dzw?#5m>NAgSfsoTzZDZs9|j`CjI5jJbpcD#t3 zCtFQ0R!p4>hFv)G;Ltus=-Tdq(f3`q&|m6kno~|(pXN!$?TRZ>pX4(IF9&RFu{$RB zK?8~-`3Q70s<`LJ7l6N8psh!>j_5?V5{K)A!QsN@g+cHzvG)#T5X59a| zIoeWeI{8E&f513VGm@<<$qwmztuMdH_lv_%W(QPW->R&>Q(1kpx!Txxrb!;n9uwWU zaGFeN7Bs~;W19!Mxd)!I#Bw)AyFSIcC+)LRd^xq zZ4-x(9#alcc%jxEwCoFe~@Cj4fII-=q6 zYt(gZ!+-o7V%CZ%W`X_9Z$lKT`{~ymGzHx(Key4MYyqMs3v-g30fSoN` zOX#{2S;=09f8*YVl(%;w_U3yZitB8DZiGgjLFT!8A7b9#2koIihMVEMy$@zozR`xa z_ldswzK5dkO>Yz3-);9j?6^Z@&XEt?#T)tjlJ#*CEE5RdT=4eh2*X*`|=SWye>eN08g& zrO2AKI_EC_`S~&SG8*n)WmvvD^s02H^29Ba zf13Ns&e^U#$Fu*!*mQIKDA!J|8@X0;UBR`2EB2}risB#CdVEijyY69OC^2>o!LzGW zk97D);o-^|-rr9>n#=zQ*HXb@{jT^3ZN1pZZ&7!vS9b$rJUBz$d2MI+<=)HYRLp~H zSK6(jju3XBp>54Is;MhgH(NR}8`C#D_Y1G!{3QI06q(7r(f-GU#J*R!Rg^rV<23vp#1T=4Ya>; z%KNBqzKgs*Gb>tHT)}{n84YxA>tUvtjWn}YZ zc;qrXarq_IDqGFq<$2_HXeFlu{DL1tv7G&^cj^3`0oDeFuIwR}`XyqiPhsQzGdAfL z@o#8Pd>b_W#QYdB5BLw1yTP%s46>QKY472iI-`391?L7w$D$SO6=$MMNeQC2VEOY*C)uM zfS6^;H(d)rsFieNbL(^Z)Eb~m6+^gV6F|HB=(YL@;zvezem@a zPhLt?Dk_x)Z#3d{B(Jv@RnS`T<*i)kEyNr^*{ROWRN)$ zKEN&G-;Skzm+Mcy9c${sKA!bm<`wQ5hdXC;V?e-_azF>=+gp{*e30rr9|LD_Jd;7j zj1MF?Vw)z$h(-9N=15hd?L@St(XNs7nG|@AM$B$&TP#IJQ->>`pCIRhQ9grgw)?%* z%UDp~wjIB=axpIWp~r||4`obQ4nM$@%Ha(DGs&>#9D|L?Cw2B=wl{8m{H0{7S8Ipc zbM(Od-ejG%hx=4tJvnEnb9Syy-uH8#&&P_zYN4DT-e?Dw0`ALzXZ5sl;$QK*@GV}i zQGbAIu`A!N9?Ih<=h7*7XP)Jq3_P_Do*9D2Qt((eJS86Mho=t1Q}`p;n?PJYJT-M+ z!Vlr8o$!=mO#dr9)gzvg&)o7Zs+Z_jx?^l*dy~ zS)Ph5>Y2(@Jr^xcl_lqYKb8j0GvL7T6#ce51wPKwfB32wzRJK?`{1j7_)76TT3cnV zqVTt5>{mIQbFQ zwr+s8?)#mbb!|w<2%CTdO_Zs+z`I`@~!5sd@0ofc81U zLpJviywwRG^}dEQ~D~!A40> z<`?Q(iamheka&dJCnC7=1$sWbTa?&xe=Ht@uXE&f51 zs9zPk_n#|w;5RhMyMGYtDYrZrjnD1>lJ?NW&3oJ8jA>H|VT?icjhBpB*{w0&GIvmZ z!7_3i`8qb4Ai3sDieJ|PQ_dRQAG`h*d@Vc^JZj&<)Q7M7PAfOxi|lS+m3`{7QQ*=Z z_4p0tjn=1*PeFAi!uE{YmWJ-EeV0afAj1#UDMl-LHXwMzPh|J9U%g!Aled3ACKo4KwTJ$5Cvg(uI$h`;Gqv~rX{j_^*GsLgkem>cye%=;*VnzIYll^S1uH2D4 zi~r30k3bTfsvqc)1Nk_^cX}v3H!?XUA`Yjm9IbR+Ej^^s%A6p%WKJDRM zI)*=X4*pWX54!6N%^b#GwnWdsdxgePIHH|4{_f-2$aUwU z9rk^h%JX-8#}A2(k3O*iTWmBrGTw%ch(*^`?m$nZ&=Z|jf56x1ir#bZI=W&Xx}qN& z%>XnrRQXEEvHxq4&Fr^y z%CvXZ?*C%1O#C&wf*1m4@^o8a3(#|rG}|81zT7n;&MpZTcf9JPw|I1BEe?Ez~afj{h^A!vIT+8%+n zN6&#nXnTw`T0;&JKRHOs$U#!hK2IArqJASA*LQS5v-_dhjODS9(5~Vd{xjG1_*aYD zwa$sGG7lXkPs)Ge*NLse`as0|pBC;EIM%!!{>>sQwk4nDye#6Ilp9I0N9yyc>=!Mr zQjTd)M%C{E`aK@kexo?ciLsLFM)>=v>SdosyI@kg`E7Q->b7Cayi$x^q8yH2e^q0U zgMqVzwz>T>mG=Bm&KB2rYJP?aZP8MFJy~UeX{M}7z@8Ip`_Zs?~CSc6P zGd(#95f$j zD)HUhuV-Dtxi1Vp%?@yieGhHVsuXS|XW^Zirtf>Pc)N*d12@8laMSy$TgKvKmr)K0 z&C}HW5V5|3FNfER@I@@Vg_kidpTJ8sasU|MnKF$}bFgTc#z+0zb348S`_8H0`!cm- zV-mgoL$lrJm;R3L64%!7_IU-UBhb9Dh`2^`YYvC-PG8fUAAU=|sCY%ot-1NRXfhGZ zof(f_UdUdmXl~DiQ^ww{pEXCJS>OZ5rS$2wgTPCD7r{Xiem<7y_+=no^2cqWNBopl z<|nEPV^1!G@A!uGt{0=AOW*U|5#dC#m^g2f>>MRte*7vqv$Gmo;Y8z`j~C{{=fP1~ zbM^0kD-n;}nwf(QJ+a@QuuAPqkEnf(SugKZzK*r6<^{k1E#NIn#2dZuJepvfT$*tF zIy+u8ptgapV#?S93$g5i+m{y~ItGsO$GI(p9#p>)x47p#aK?_-@HO7$#8EDj?o0Cb zBGyiMR!Vt$AHv@UjV@Dvn-~7@Tk+Y2oDVZ?j5fmKld-8wITx?*-F_!stvnXV7nbP! zWBPe*vFcE}-0OUC8}EeecV4*Yo@>jLNnUwnd|sdTGVb)@>P5R&J78&_d?Qy+3vcpM zi)SdAFu+_lg;d?2D}l@Wa(Oq$;leM?`VmtxG%Q`lUc*j(M% zTD{m;nU7*ug+{Vj(M}HE$xil2^ttDU5U&cZ$%g};MMvx(_;=!a9K2;;L$>rv7Au~O zeN?le^g(-3z@^{C=aqiRbIBLU6v-IIxluG7dkTCE$bUv|EMOA;dhyN6onQ{T~taE$6dS{fY_TYVq_=<$&tYH^Bbdbebkdcfa1d{WchabjZ0vl>(0CwvX;39kR4>Qg>9`0#w{<<3<7RrYZ`+}WA> za_2V64rYqT8L(b?aEcf2xfwaKshBgeiWfJ{2DeNFo2#mbq2^c3{U*hEd_0h-zJvMx z*+62`67F^7eC{H%JY*cjye#ioPhOe|=5mv8xOSum2YfgS{<87PoEfdL$-r}M@Y?w!%M*2j5+XTPjKDtZt+gkFt?ejAiY@eyS8$MRu)?Wt=q%{82 zy?t_zOlNoQ-i=xAsmaAGdw)Ne`&+S9DR^9Y(iK}J-cc-8{3JHl`uE4uH%LY~M^l=^ z+;58YUb2;Eami!X-YO_zjI~TP6DY}=gL|$84z}j%=Nb8S2 z$=Q17&gT2wn0;&+Wc`FKW9@>8{ROk_{sK>SVry*lFO}VpsJ=<-TQ)W%#CqAm6;?kC zCO==!8lM@c<6WNYuhg1P#*-n~X)V}b1;A$XT>A+YZMSk|Z__}okHoc>y!Ts^{Zl@! zs&M$YsgX`v^r-ANa$eBae49Xafch97&jtmnU=yr5i!NV&lbh9OOcZm{4_pVgm?E87 zr?CZg(vEqJA$`D_gsm?&88)rMDwEe z)GN@C?E3r25%h?quTIVFur-RHtHbi2VN+noJK^SPFP4fqMZ9=|wyK)C@@Q(n(}Uni zW0%_tf*n6VU#{lUQd25#uZnL9ErmqSq9JhLR~%I0<_`tfOEO{COHYfAhy}D|gg?<# z+_<@p_zqu|o!bGs*=Ffw0%qoJkATAy=0W1ToJHF$y+qZI33|zcz2T%;ZuhZlnApd% zw5iM6$5LtcvDo-OY>Fbug!p%fVFJIB2RXk<#BBRT)VO&J^p5)*LIbSppfhvdhI0T=LuaXT`|Mn zQ|w9YJ$2IT!PeeWouSSeL*E|o%JXSCAz`eJ>5iopNY=HTJ8i3#M)Rx%>*={K)D) z`R=U#+vDme^i_(nxBH8%t^yA5yv_E3oP6qAVpCxZn#?4s-)Hj^sL#@=J-ic*9HhUt z-syGS)Y+G}A9ZO+`%#~pvLCggx!QNH;z|_9>g6s7)>Rjvk8-&Sva8)atH~E~ebrt| z_Ms{#LBIN&nE!f!zsq?a2BuQ@PWx3ge>>vh<6-bYY>7M9gpS6?-fnCekKDfE+G+cW zl`F%9Mj8tw-}Ckq_lHJ!&Ui$Bo&o+4_Dg*%*=1>LL*5?aCOg)4kFgzV_Qzy|XB+#G z2p7gQzUl*gsTJPe;g}ss&7IgwywlxF+-_|=$qw}&yDYbtxc$xXKCz#;6JE&VjbFv| z@x$(BeK)(@9iJ^Xym5T&9&x*;SmV?B#y!Pl?l@(YOF{gdiEowA5x|f<{^1g-1WR$DzOSK8X zQ?c8slgT=M#4eAyeiOTY`vCLeeV@mM{`{TT&l|&``@o0F>U)(>KGsN|%$Q&VzGL9D zMmSZxR32Vqw}q3*GwTgGYQW`6aLHBoi708_H+}+KemoHOgUc$5%PMj{8OoO+ll?Q5 zX?Qq0!dj%whpfKlZf0gZ|GN|nm$=x$_~Ft zJKN}MFMRF#Fw-W*{AI6vmDn}?^0kK?Gpsxd+T$^H%3QeQn6XRq9CN9`@sFj=@Yo(R zGWL=gU6uXiKQMeIg)MLMZr|IvPkX>wyZNS`Pv&d*rR6Vpv|sah<$RMo)O;S@oe>>O z$Qj97V8G7m)P7&tSjn%Rv$-QeDdPZRW>vN$q56|QeV@CxIK$fGtOc=nrb=rs8ST+k z{qe8xoIF0s7pg+qw=7vqd$~Qx)}GTmUG^M(9;8o$b?dP$+F)OwDs-q-1BL;_5yIJzvSe!`GXmY|DYVI zTkO0&bt8Q9Z_1%MzQ@|P^PDQyH`>`(7Ggam%)ZY^J$=%cXdOlC8u6naj(bG+yL#k9{L;;{3Km%s2lq3J%EeKC+|LoZdm4M)Iomi?jcsgrG%3 zE@;-wT)WMzPxf(M%r4|Y23^O8;5jNEhs{;+CniV^)3#OF?^zkThkJd8{km*J3F{}j zh_zw<5Z4+(;dLX^>xakoCciV%NX({b*yWQ=y(~S?vA(W&Lis2Q)^5GGvtPQY{s=yr z@OK$Mz)hDe)9^}5gs!edL8HY68C%})>SPzTM*{iUvX{Y`XJXM zTvrP&?vHg+mOd%Ya|pbMmne&V;(P8D!`a4l9oG$9Kf^W3bv@S>uJ@^J?i)LgAYWU_ z(@+VmvOW~T-inyQpH9HrhrBw&xRh9%cU^e*wKlKq_1v#_V_2@ORegKrIc0Ux;T^d9SQZPq@4zt|61Yvmt2#(G zZH=tAwXoh6m3*-NKJYS07mn6P%>->Q9vVZ@g}Wv@nTI}~Hn8DVzXferzv8>5jlO(3 zIGCR3>+Wk;dt05~6E1~2aCJZa`jBwv^@FiDaCiv zr~ZdH*Icv#pHcQ&o9k-=cE5=NFXV;0=6q{w+zlSxltD37^J3Q?>!O+Ba ze9yXDN_0r>gOpkA(bMPn`$7KxuUyx2m7i!W*A}i#is3?L_f4x)byQMDuq+iEl!@ia zc+V?=@jma}+B7m&eN=q0%GFhqOOj_Potw_sGRNA99*6!pQ^uHkz3(c;SE5@F2f%7$ ztbx_$!pn~n#$IL~dG{w`%FVKQExvUN*Z4Nt9#MQ&3VCexHua4)5MRmMGeu5>kEVR{ zM)}+2?Q6>=XVF)uS7k2`WuSL#j3LI{r?C?KGmcHPb-!1CIdE@a+}n8e8MW;_-{AJg z=I8pX_pFI$@Zb0W!7AK(&zJIdYu>jtlmo}v^F05nTszb1_hUmi(qWfRk^Ag#7viUZ zcmGrN$CruwYYrAIV_kLxU*9LLyyWI#zC4dut_CkYGHo)KUm}Lf@!}&Jyxh%uh}U9H z8&_=Pqu3+t7uH&C!!D-{UOduV{RiJ7rzLA^*fiRo1`fer(3k=u^&Fxi|KIu*4^C}M zf9HPh28NPp_2}Dz)l=)9slHtMB`h`0icPkid#-2Gznc|y>-LRKzVmHx@AKO%SiNP0 z_%7yqvIXELbP0N4;p+ipg!RQLfA~{p>{?Y##O$;FW=p@vjm`0m{4JCGJUNT~Cp4R?*;14&R`D%7s1a(Bt%o^zR<{0)s;scx&qnP_@6O0{o*Ss{B zjhO#t*9HW4_-b~w^agSy!hGxF9={_G{L*>oLce4evdrJe6&>i0a78Ej!(8DND-S-~ zu=_odbq{b|oxVl>rd2vy*x$oFy2ID;&o(!u>6&Xk?)m+wtJKr4rD|(keNa9Gqk8{} zd#zlfgmjQtT>pDhbskDa&Wt^huj#^jB}#k^P$NPha`fsUOfxaC*Ga10O!_>1E9C1J&r zSUD2xB?eXHpSZ`=NS>-6#55Mv-^H3)Idd@d47nQx*Lto?`Gxo`=11JSe}L!0*;ZHdb&?m&L`>#$sz>i0+N*l^+IU5m=C8JR^)_|QHy_8>wOeu<0ocgJy-;mY{>#<)V?z6)F#ci$-2JYJbCo`P3qH%{jj>1D}y z@e4WaEWgx;i6wCDRN11d`F0)8CV3^C$1CAUUV+XIQ1)KR!ZWkWxrbLuE^>ueO3u%F z2ws7H=Ph{)UU?t9A|BZSk1S)2^~H||#3SMr@d+{s+;ZmN?N;v8B;qSICN?fYF%kI8 zYILrtzz7{Oz41y}f_xRWbX7|TLBa?jdcLh&6KvQuBci9+UooF|$C*ACN}jUcym0k&ejA*_KGH&u zHrONP(FWhxT-f(b*=JvW-S~Xu)VzA*+d9`QpU1t{iL{<8Stz-f{Iq1?U2hmy$y&yC zy9fIRXM%lZyza2`=1IKnxo;A$mv$0kn{{Ie$AE4=Oy-9Vd`ZeAeX z0oe0o>|ec+W_;35;LA6hfp3V_32K~>ZOhfhB%Sy|Y3gR&W~38jU2Pt3YmT^$df;V8 z@rUrA)6P|L@N}AU9i?ZjY(l>#?q_|xYE?`zLE4Kaxsmc?zamd8FWe*DoB3o87vZbF z9dhN+Z(jqR2l3H;{0;KpF=Q*gBidUo`4~e^M$9g|R^`gF-){Hn=)_JOcyl|^iDk1_ zYNr1n-zGid`i6J8vTyY+S4LOCF1ba&uOlz#A8%f0@5|Kxx*&P^P0K&V-@D=;iS2k7 zWq&&~z1~oXDcg)q(_QTHfarqrR{l|KXspx*yr6iw&)`qn3mr3e`0c3bd3)Hxk#iaydQ0vow05e~!xh`r=!a5lv8L zsGfGLJk8(_3L;m->?uPBBU|g2M9q>VVRKJ9$+{1*PU5M=T6YeydsCAZ;+ z&RqPX^bvS$nY0z0#*k+#h+ol*;xkV^Uhekf5_XzBBLN%H+H>eVQ^VMxi|qbgbdjA~ zGEcU)r1Yf54&J@I+V*?tq#Y~UTm4~&t4 zb5L{og8i0We_Xs?9@7{MPQnxHsCL_dHW#~XjpgdEty{r-pL1^#ad)3-P9F90{Zr?_ zA7cyM9nOPoFYPUvioai12nYBXy}tZ9Dj4*)_`z$ly`uzsb+XJ->U_ZM?^vnV-+N~6 z?{ClNwNLzz+rHOd|I~WNN+;{RxQ(*+OojDg+vNL8lBM>&Ti2x{F8}{(h}fq==tk>u zJ{SI99qXKHioJV#7SXEgOX#^)zz|@fmzmDcTg@Pmbqr)=$?3XEq70Umi{s zbRNY&dQ`UVs_gcEb>Y2e?biHx*XSJEkHe~aa?E~HnjzM}?ei~>dGFfYzkhuJJbE?@ zXB7gY)x{RZE2+Hka^HO}2mh7zllZ^#n*FYM;#~xPNss&`tbe2;zQd>F^I}%DOZ~2T$Gq8euZ-QtqWt9Dmm+| z&;)p1uIJvfTAo$#EU0J5aliLG#Pdp?SLKz}9HX||XTtK20=r$mxpK;l$KgDoJC@^2 zkgrDiL2>=;cdnoh-PZ3)Io5vi;!5s)QscAZCz?~(dG>G({jm5@Y+o0#WT)J-45@pO z=u7n0@Um&LeC3$7GuRQV55?53biUdae!O-wHm5sv(60422=0ivcb?j<3ud!w8#zAA zGwUx{$_kR6^Zq^F*&9b@=;y+B*U#32YA?T^Gt~LPcUkA%Gt~KqcKv7%xhMB%J;l`vGb+38gs?` zFeZzcTPx4~q3{V)J25tsbQ}`@cyv6Cf9|m|HIHT&cr-f{G56f2KIPH)OmtlH?)voU zGw%RT3*Kd&Uwj93-uf=vZ#4!U4OHgfAjUM1PXn|c%;O8i-A|)|yPMU9x?B zdxM$1DtqEDq6WL>cIK?aT7fGExHjOw@vANT+WnHD`&U^{>}5UC z##cnlr}nEJ$xK@hb?E#0jN&MretoZc^zB4Fe6M=)>ag-p^rr8qFURw7>R-X%ChuKN zhefS!bmRIzdu5NC*Zs;%-Z{F`*C`j*!UZv-1Hyj}kKptc#=l#*7LIezwGJaZfa50h zN6-2DzT7=ma@MzeUNEU&+;7&s^s~!P#P%hr!>hQ5yS z{%*aWf$#S5&hmfWyBr?2LwobAj%;-f6I(F=4GuzsL(t)pTm2^eA148J)M_sfo?7&*Zr8iInKa2`egA)pP->3Xm1$08Tlyu*?}CH zUYGi)Z|QGaeqXi665mj7#TKWuX`NZZ`NYO_2th`(7drR9J|di>l0*ucdUap z;5EiK2puR^w(L@?>E`@JtCj&HLs=4p8D$Jnl{2T^W& zFLF!#2y7|pS}*wCXiMsUwbAUf?RzwJ(8YBY@gL5HpY6IO}gfBwUbA)Ile}hulcOry_FuJ zZ|~CPS!4VER`v;XzDt|u&tDas`E};WosS`JGT=^maZ*=W*_WGk&sjl74cJ^A#4W)q zqt>T)(iyOOlvD7}F2`Trz`hoELGy~_@*l)P8RI1H|1rGhJTXDL#7A{xkze>y;5W(d zwkH=rrzta=XVL$}{$uS$W>3@M{BLOUSl2xCM+N)ksGoK2_e^giHd{I|+YfEeev-2J zb@P0m>Sb(gK0KQTl>QNCXXhNqxL#zObml&6hko&+@}i^j9qf&r;_Vo|OS=~Il94>Q zX#zgUlapp>E3sUv-_{NO&QZVRb=sJze(3f#x?<{6_MG{F9nW$m0^j9ed#i7^ez|i< z@_JP^@paW9yFGRvFu!E+kM47J2)>AE`jOsS9-5XRVIu z1oyq_Gx)c<*rhRZfI5-0Can4xdq=St^b=X(_R-QWV<0)-FrMv?m{0sva4R;>gT2rD zR{b~%?vA;*i(8(}!(E%|&(%vkw*SJN>Y~mZ?pE_IJ<$f^M!g}PSsNs;d=8%#>4MH- z=<=FmDRcL)A}@8WqEi=k;S3G>hylkxP=$@Qf%BF8K7RNR{677Bi3N8A5)XVRu(_H& zo34M}*S+7AcAqn)cLuYkMX&lz$iF>qd?@|#!MmAN*}uCcc-g<7U6UzWqUXe)9pYT8 z0KSZ2*?{(0$tQ26thHAen~Ze7Hzv7wP}fG!t6w(CguLB7ADaBT9<{~3L+W)*{Rn+t zMGP=8AFRieDsJq~FowoP>;Lu1C$|WCgGWc#jCio&qe}!o9!P%8xpl{@KJfd>OR6&m zQ}QR!4DHIs&4*8Ys2`F2@rs4v?aD9wyeW=9yC4wTRut6PgwSwb9dX&c&~faskDPAy zo25+$OsFnm3KEJXUul+v&SwV>Za~)KOWz#bdb;@o#8?x{n0zD@j+GR@qI@s$l2tm_ zb9sF5!!h;}g_DoghA&l_5WiY}Rs4ed%K4S?^Yg=2&K?6lWuYSe{s46p6~>a=YdL$2 zJm`P_;aKwXjp5b1loz_HQMy}nq_gDZL+|afxt=eI2Gl3|(I4Lkyupds_XDGa#9YIK z#QlyXGmY3hoWYKNDzwe`WBW&6*LSLuZ!4#l&6PtAAJ?8RyFR|#EZ3OC_LF0Qx)n#Q z{o1Mne$_tRjNa$J$e_I>!WOxlI{s&b<9{eyh( z<9lCh|Aj9Db72nV>1A`}_$HKtnKqheW4qu2R_(8mK5+yCIAKq8_M-e;_U}}qa&GXq z`(E^6=Re*aUg|H?{@oZhi1wSHlS~hP(_U%B_v|J438B9!zQG9@Dp`ljA~sUz&RKaH z@$J3Y`WA0zjI>`QW%Zn`AAFYM>-5)cG|m}x8)ps5Cm*Jr&*~e=K-w*5{v#e~)BFdY zsGs?d{_cm5-ymQ0oV5s}cUIQ`Q%?4(Z@Ipzf5;GgpL;(tQ=jnV9?zBa%H1$iIqkE_ zl~eurvqw}OIcOvo)sEs5E-f_vi0{i5f3{yI?HPDe@E9?xpO5lQzPyv16Ajq?t;hvz z3hlK`3D4jvNPn$7n`thy_2tgv!ngL^)3$}*YrB$psO%HrJ2?G4`sM(zd@yQQ1A`9~ zA1HaI_`o>w?xS9eApI-A-z_~cpneMv!W;10J>0ZeGBJ)b_IK5*Y9qAH8BWg!&aR-& zBn;4B+99?fW&hSbaKB3n+2g{e=s4tqZo({-n&M^h*Qzhb59!qMl#lh>^)oA8{MV?c8(fuyR~HnvZvjWA+rwK8&HGCv*XLr|icBPdWjdZocB$&<(~f zSOf<&yBz$MB~uq{e^5M2n3I+!`39K6;LVxvXP|Rt8ara6i{b0xTs`DNJ3_mIYM1%h z0OR3HS4%Dx1QN?4jiIjWP;y&2ZD;oqx8$w&WqT9LZnFAS<$-UUIy&j+gpW}^TH8Nt z4`{8MxQG2}L-K)sJjlA5uX$rJu~24-WPx(a#Y(KL+o(EuH?Dd)(%MIxx_9AY=DK(h`$=BaISI9m&B5z*PC_kpFY(4Z$o~KQ@h`(K#r`L|cWv>M z@um+s`y;38kx$kRk-T7hhN(9Ghqq=V^qBl)a|L%#MS;d0GL@e=XYBH&vy zVwQlfTI}QN7~`s>AwSSA_>p`z4ZYwEp0%{o=jNc2eAL*A-{|j5W9!=AFPUy&>lg1{ zY2bXc8_klm&s0c{tabD(6yt#==^*u#5Rs#>@@xDhPRJk}ehu z(gm91oTI*Td~fvu-&?&wd$P@Qx?$mNLr$vfYist8Z4v&_NyT+xY+3x1&}eO9MWhy; z%w9$DbapR3h`HdY2EHe6%NS>ydEd-Xo_gEmqe$NKgIGOdhz)UJQ6D^VGP_ne&&eI$ zq_JQOkr`vce+Bo@qrETV9$F%2#K~+8JXIzwgC8lJF z_}a?W1|#Inm%v=U<2Xz4oAJ zJpKqW>@)9=HTlhw=91Czg-CrNl%aZjR`G_>aK zu>_ZoM2zGt@1l&2eU2_TFIg-ekQ{C1j7wg5<$Y)tcBGtK$U!1TJ}h2z*ZNU&RKA?_U^WtdG)uhtP3~SW=4@GHaY}J=mS>?M-M7 zc}SVx3zu9=MMsn=^|0b3%)8FRk~-=Zv5*>`~M_!1Yi*zBdgma z!v!~z&iXC+#`UYG%>a8@&ja^`%gFUB*u_`onSJO+6MvqwxEf5q)gP^Hp5DvomCNV= z^dLTX@^@;FBRa+A?EP)Mm#@!MZhgJ;0`ekf+XO_X#Bh`?%1Ex5mQaT?AlM_mFP)gQ zbqi9!eBmr@oTUxHd={9_$yNuZbHIW=I*gxg09}O7oIKRPK>tS6H|+M)iW51r%#_0? z!6fHAFsCc$S-IN92G}kd8@thzZ#i(s&qA%HZOH(7#rLJ>1w-lt z`ZiPg@CoOD=A?=0{>D1&Md;u_dY1gV;MPBBzlc6~RMu2dyezTejzIDYVYT0$nuXjN zMaDhKoN^a?b{^4rUS{p)709BE(2BKL75mP)@sH}fa(o2HCCY?EIgt7acE3A^UW*Pg=f*89v?W$v4v+^T82TE%3UpEEd&n^P z&wj@B6>OMdSEkiLe>T4cx=MO0)WsDzVp~n{iLYTxylBdGzLMtMJBrBD|8x9;0Ka1) z_pd_R@KU^bPE6kr?~%*f7fe_14RJn->j93+Ggav4jN9MXWAt;aJ9Zi)#?Qv5Ut%mT zEwFpihU#uJ{!u^g?sm`cK<-y*ua@*#>t8#O7VxB&0_496XD5pCat1*0wr?)1ZL#Z2} zMcFI6%|QJKILNS{lsa;I2|@#=FvN4}`HM}_f?DQB!T9X)w&ZSXffd-!=uN8!v7f1< z#+w6)7i^9b%4l#$Y?%qAkbR$o@ekxNMj%yS27rU zm2Lfwb7<>5PXE?toCDNTBAn>?ju%I&6_R5_ygZ3N_&J7^9`LNX; z#P49|Rl9UlIQZ}-tp)JA-c&9bFp6C-)0)MiBd)EormH-(WniQ>d3a@D%ZLeeJm=BO z(?5xcX10ExIXrZe+;st7!iK6A?Qj;1J64A~{so!@-sI^k*u1~6^A+&sYno%{DXHd- zqvP>8oV(UFY_yi5vN}r*{K^I`-Esr?FQLsF$qDuZ_DYF6Pu1LwGGW=c;9tH3;*~GF z>hVWa!xP!1)7rjU?{aM;qdP5s(B2#QIrb}<)2QWGdj^3 z-RBsWbBq!6y$_usx;_k#3_#O^=nNYhW$F23wmXMgY~FqKmz9a?8#iHO~KF5KKolDA~r>-Ynu+p7JFo)o~@z-o$c8?!bTdT4cf9RDz zL|JRklkaR>{gddlI%1OhfS+>~IfMOpI_~mQ2%Akdna4M-F7p*HOCG%|(D5qzb-(dV z%&||7uT4I7A)FlkRjlDe!S+-IxR`^E!*>%tQ4m*~nwL<28*{#5t{dt{>>RG2cPb|y zEElXDMC~Z|HYldGY6SUJO68*?c>R7L!zHG`tPp%KEVAZ_aYU_`j16!Xb{$%$k^WkgdsY{H_ zqv|hl-_LtvV{MHKjrC{jzNpMQvNbyKPwz4ttgSJ~IDDa2KD!od4&ky2&)+ zd9>ptvL9GeD}hcq@+J;EHrL-SQ_h*50nSS-%h@2%YQBB3;mvvV*LxQ2RKLb;4nWS4 z@$dL1vi67ak6aCJ{Sh=EydC`&w0iC@Tw1;ACl`nHciFk=?ki)~U$X1U1(kE@`WM&S zvR95%&#>gY%znMij!Z`eMz{v-7NX@zh-{?*P0*u&Of{lJ9dyc+0*-YKPQrWx?k5O zc)<7PSIR?{XKX#l@_V~V@k@_IO#3WI*d)KCZ)W$0ZZwsN;Qvh<);2ApZTv3%qPt_s zFWP58J;N?`lBs>NWBuH__TNd<|IqizH@+Vm@;OtAo#?ml_I)|fafNyG(zDb%W;R!w z;JTD?h(Ym@LtK5ixMmmqkl$!mfq!A2xvp-Rsa%sO4<)+_0_<@-EgL4i44O5gwTXqy z$LGLr53|Q4SQtw__LEo#wqf^;%#}A1FLsu7kPc|^j49pmw9Nz3ruTxIabnKe5)NNe zeDUJ@)OYC`=@{lD)(^(KMV<4bDTU39Fu4jexz96K92m?J#=FVIUCeC!gP_^&2+9oBWk%Guj<^kV&Nq8H=R z%V&QmoF)I-e$yg)k>5M%o*z->^2TB4Wqfb)OB!474!>ypl8^RVdg0!++fI(B9(ome zIfqOl$H5cG!Y8n4;Q7;Qx~BSLsAt1e--l^=x4xMHBd%r2?N*}IWRonvS8keR% z5C86xjGlEE+3in0%AEDYwQZ8cI{&U`1?&3gWzQa{NZ&`!nvFU?;g{@9b!s%;=t%sW zb{spCkFxf2;@WcQGoHhrdR|;cJvm?Gu;!tZvGvoY_3w2*X3C{=z4m=-pLX?KIb~eG zpUzsa@LfHv_Zk<<1XKpQdbRp2|CjKQ1}5rreO|^~pZtPy3i3BTEa*A5c@FS-Ya7Yb zi27eCA6Fgzt@wtOf!HSd-qHd(=QZ(XGF1-TS2yf9Hyxi>54br-E)S+7;B+7Tc-rDL zh%d(P(w*>nwMKAH!7IA9VfU4Idh?s%)ymeGTL zwSjDPar<08Zm;Z{f?I4M*WS83=Hm8Br}y5&>*W#g7CcrKG5_UrQE&^c%aC`a9p`8R zK2EH~zlrab9C%L8r;%&pro4x_J2=&Q{Kfbk`mS`Q7+;r;uEe*#XiAZ(wGF#wZATX! z!Nz*Xg~N9wwc8o7>(OCJV5ZXSJHE`0-i^jSLYV^7YT@SR|pwbZ4L5$NO6pw8BC>BGM3o}>@c-kNrO`zJS^C-lYxs^s#3D%j7e4b!;7S6rNizS;(`wx0#ed zH#faoS(xwtO=c$Lpj}(uzYg2V3bAQEFSsmGfoyOk9DD>i&-3A{8l$RoLTR|W? z$IU&0Jol>{zGL`sY%%LtfzS!|ZIwW)D}e6{qL+d)#p_7k=~?3bPsQpUFoC++?DYjF zlGnh*oFIEzGBfwLdG0AZA7k|;KqkxgO=k)R}sJ3m~!Np&GW;+4HzNYbe(hFwVrq0y_I&# zPZYzGbKP&RIN#1a%{#@VM|N&~!F|`Kv1MrkJuof0x%Az1ALn!{7+;9*8Gd*b$cgt4#QNh^bBjCBi_mU;O+p2 z4^Avhmcx6yE#0LUZxQxiUx4-h(!Lnh8FS{}OSdyPJvT55r_W~T;8Gemmpg-F)8*oL zuTJ;p8btT-gaUX`$*xVQwU6?%Zw6{Ose7K8p9rcRF&9 zFR)WId3Sr*Am{EGH=W4R=!H9V9|X@$S1G6c0F|qE$|>)hD1AeYOv7{LE$kSD$76Rx zKPrmVz2V!~XL8!hp&j~BGi`lEaw+S9|5}@rU`xNMs#j&w$}Eff{p5n(n~|gOr{*rrg57^ldeq+YGsRrY4ZPR9Tl<7f z*PEM(Y2%llO?=u6iPk0SXw41o%W19Ijvcv=^&gvw=t^?1;~PR;9am4%ns5CA(2-)B z?~26#!BnbDbr^d^8Tddp(VU20kB#VY@r0~An71ibN8G5zIgzH(#oNiJSVdNzwEh8Q z4a6Z8tv3jd#nE{=)(?o^hnjv~P>YOlK;zIDz=v6P40}IwRz7|+^Y+3f^zDY|_3J|k z!C1aa>`lftzTu19iylzH9cuSEI1(@2c+}+3ZBNas&^e0NTOjo?`E^F^XCQJ){@u#?ha0L4r@uRXZV9hGf zzM}f0y@$(SRuqffWnqOq#`*yvd;JJ|k5BbUzr^kfoIFk9wbAPnTpRu&{WtNw1KbmmcPP0Ko%9s)``#dV z^NJ{2;M9wL@=D&rT8pF)?<#b4eQ1k1dy^Z`IfB?4uES@6@5u`4>=tj#d)g^4*(t9{ z>nyMX*ZMa14E6ZjyR(|&53**yl2_S2mpltyee{^CkHV+;=2QFNshDyl(5(^KGnf_~ovsgS#xwYA!hQlb9#Nf+y?lYPyk#FaDcb zse1*p{5RGH|MiRiI`74Q#d~9I#52*iHtaZU$Lu|X!fpFf<`i>UG%6YvKe046ZSP~P zYOer?uFYf?KVa>0_<-X3TteSV!6kJ--$!TYICqJq;~m)LE}`RL(Oi~}YoTNI3x0pj z3?1jZ4n4YbEZx~V4z0|h zvx1I8&}vR=d&4X`K02F@bJkiqmH!O(sJGK`jsh;s=r~q)!nYCr6Wn{DHWrxh zbudTT;nN!jOwPtJ_z!bjx}NNJosUA(d+fYyUH2Gk&IW)tWPPh`PK{gU=PT7d7B| z02?>_rT)tk=j^-xFj{1tYJ<D{{j zKyneh!^W3KXIx_Ol<(ka-PzRi#^WZZg0*zx2r>eEh&WmnU)ZrJXFTv9Be^p=FH{*F zSa0Fh9OQ2ALga%l=M_f>?uv*vN_SOX?H&Plu=J#=c{-YJZ3ouhUUpPlcLVD?{%xWa zczxD>Fmjna;YZGV!Y=lTsa0Pc+rjz%S?`z3yq^k7{(-+0V>iXFfSvHdTj;LWZ5A$F z{01ESe)hbJ-+uRfmHYm*`@YNSa*cKjKD7t!uGN@!&C|Qwl=JTV)^($^?Dw>T93Z>Z z)+{f_juovPNEme24gXetULC&Fe=$CCDNm2x6RK=(;O^_&v0-gTmZSaHb+`DUFRTwh zJH^%qFJCtN9KW^ULm$vZF0^2F=R+6y*zhe)oL`8(p3?m!f&KRU;RfMli$T^Z#1~)* zJ~unLKYk~^tei29?3{BTurt@JW-dFi5!jz>AUZ{>|2Aj*V^LC3C@RLJcX_|{~LB*%-yox7| z+CId;MO=mzoOe}(XZX>Is5DOdZYt-e!Xqi(xhy=labx4|-l3=Hvz&QlE;VLe z+Klb|Y3BYVbp)#mZT}{^)Z-&T$y%|Gr$d^X@#`Bx@cgU{x)%HdvuCS2rol1xxU4Ny ze96UQ$j$dzx?(DNrJr2<*5rPx(>G{@y(7_I?ooc*Ju`kkB%5EeKfA|dMw+Yeow(?8 zFy~0;O7@I)us%M1)bc~O-Stl0pSJ5?{PE%0-!{14eqh_B-|N1U-7_B-ys~`{yZQDi zU;HuYl5=VU*UHZVJ@c^!f5k@jIArtGTE|&X_?P#f;A#0+ zAN=bD@vq#S(4Wh{X8T*@Hd)#B_%9?I${$XJSi<9EILp+fO_Z5FG?B z?uUKw3j1LfdFETIcYo9AYtL}DujgMB4JEN{aYh3=ddn|A7vYh)a)rI4x2zn@ceDE- ze1~3w&k{@=hpx>Mm+P&Q~%r|`A{&%}w$NeUV{b&{I zx8e+3T*tM3i}z?tcVowfJK_nE!N(XHe!l8scbQ-0Y?!iCIX*-MZSc2!_6vGeUnOJim< zcnSTDUP^!QxpvJiyGUc4TXO9UR@Xq^FR{KGtb-Bh3>Rs00KSKR#zp$lp8PKdem#Bt zvQK)8rLQjN3piwXL{BCQk5rHN$fa~3KfYgXa_L}`!#h9O;>@qfEi*QzRCbMJK2GMc zS6!6&F}(kA$*$%M{BM@s;+NL`rTuv13CGvQ+ASttOzz0;`M-3TuZ`9a(=u@;!Qm{r{@KwPnaJJ$MGj(~Z*kx!`{XZr z)K}zY@HM@2mY?QK3?dI_14=n_QGbP4M2~a+=sOh=&e|j@*zxpGpv;V;_5H3F`;95Iw9IzqxleEi^ZCHo7SK zI5x8mcqDyr&YHez{Y`c53Llsr=iLjxaP|;0iNGtFbPfH8p5X4CMU!8Pn)Ck>x3+EK zA+mQIU2KNzo-zAKfkMXcqzP$nfY=$5n~Gv_uP^%K`jFP2cG>v1v*+}QrM?>CkZ8`g z-vLb__r*W)PZ8NB(dnk6b4;kETrp87i+xHqBxKWEY?`^e_lOTU{33rkY;9Q;5okFV zTcz`zXXGD%G4|;iKfWTj!&4h|t`*+Dhy8}ymx%woK7=38bZiah(97r>{ZL)nKZqx| zFCLv%ZSHENt+^(bZyxPEAj={%s;&{8#p&(RJg%3 ze2YF&XXat|{Wkage)oMVWeR4WkHP2I?gevZq$hsA39`@J3;ka_BO{vEc_hWSfNrX2 zTmGv3@mD7!(D`pX$0E9N=;t4vap`BE2D)bbF7Egfz5%mj9=N!c^Zy%O@Xc|Z&}WM2Uy-HjIp*g0{X@(j_Azf83Sqm0?ub1D z4lF!fJaqg|=wZNki68V?4)7L4;(~K#34KF`w|qP*JGRB$D^6aEzpuO+Q9X*|MA=0% zzenmcM%9B)`l-ORnW^{>@F_>G(mUBdqH}U0it%Ub0fvp4qW5TAwhrDmX8v&y9QVHHIg7?NEcF4GY4P{yYk?4Dyx~#yaQkv1vF&A{9$>hu%;Y8(vuC`@b6Dpj zh+AF~mz{_8u7~&3V|X5>{&cL?sV}yfnCGH(=9B7dxx?hBPL0{t*+8A>P1+L?{gTI{ zz6*Bhv*0JaiugzGarR5659@u$8NqVsP1@3VteF^2&{6dVGa2ZvPq7YUbBfG^2l)K~ zXfUCe9iq9(Bs7ShvTV>17hgKd$(ne$y_PkbHub*a2lRt;ZcU5L3S^+7_~Y1mxkr%o ztF+@e=gh74@#2d)N1~Wz7B^-|{Uc^|WDaF54(8g}O{&Mi=};Xs-e+l?e!#;WzE1x* z*JES!iKoT~bgm%ttny0Vh2LK>FE+{^<+=Lz^V_0{8!b&}O%xwi3|M!(MdJ6eb4CwT z(B3P<^5L0%7FhYP5BZ~;oV((IQHi@oT#R7@pLrcrejNO1{w8#`No!T0)NIvQvN+PZ zDBMI7;DB*A6`Jc+zha2g^f~fe{6P7?5MQD}@O9wz%592;CfX0E|1pcFVkhR=-@eOj zvrqp8OWL&OZq;sd(T!hoeJu~wKy$$J>Fn8K`m{@J(;s31ZKzIV;|?KbX$%1>R{u2) z)rFnh&ZW!Yli1AMX6ozP>=bDpD(nZL2R9`Ve6sXe0IKk@XFo+h12u7F>VZ+9GBh;p>;+a31|0}f*B zOCK8COgzoX=(peCzAMjR(xJp2)>Fzf?PQ<-ksI)LVO>5@780!ypKC~Sx-m2GL(ZI) zC|_gd@UFy{yQ4Nr?TsV*D zEH&{}jc9$+^-ImQio3C6s@9>@3 zyjL5G;EithrEI?0by^%a=frG0F8KTCmGU9)7gq;~aYTHHrp@@Q=YHBpJd#4KnURgB zz14T%hh4~lI@wqX&AQ%sHZI?=&e6O0ueb9Jx}9u6woAHnJBa__X>*u9>dsKZe8?3) zBF^Y3?H@(!=0(KcnRgG>oF)di=1U(w)jykzbncZ+)`^8#Q*R=vC~_+CSK-~w|FZ?z-Z&H zr-f@5u6Md{O~_AEccR<%0(}oSJ8^7TOMvk~U_2oCIr_+r!01bM{&s8tM%>w51MuBD zc+tf@LC*0PC+pzB=)1u^oyGLFgK6=Mz&%XY8uCO#QkQ4&&&>Y5jiq`;D|Uif`9Gd=mGXgN+%F9TVSmuMvAh*nvMsqCY*nL+9JnIQ@p@@v5Ky zP5RcD`5))irs5UCD+V<8-znc${~I$meNpArKlRO?Z&bhNtNNyXs=vgS2zR_jpFcN& zZ-RMKWz<)}%%1gf`#BqSoJ~I32dtXRoHDJo=v=LCpR;p%Kz#+LHs&q7*ZQT}F(luv zrwhl8nV>Bz7zFoB+dI}9GymQ8$GMwidJ6iuU;VRin$Gk&8fX!Rlp^=M&4?3lcodHSGoF8e{>*XDwFV z=xEN9ijOIN9nG%{L<>I_iQWEDU#udOj)l#($eh&oqKR9s{LS!f4z`4ZSwh^}y5YYbU~K-Wl~5yF^s|jdgB}eDce{9v)Qv5N8x}^ZZ>! zT=eOuPksKNz6Jk8^=ypjyJBAoe#+mae*67F>MBwG0I@jv?Ndw~+BeZHlBF^!wXJql z$6B?u@!E(x&yudZAbmh~ot_4$Tr%W?#Ny z!PUls`Bhf_+GAd4?cZ~hH6CTsGrZWYnOBW}7Ts~Jk2jkm^F+;uvkaq1q4 z22=fYvRJ=N=(5UbGZWtbd8^Y`NtSfw$SUe0_W9Lgw`IpUWlRY8`ugaHGcPhn`E7m8 z;DgWdJ5R81>YYBPddYvVIrzYIbaOs!MpibL`){sh&dctiaYExSM#F+>W*mM}yPvxE zW4s2p#quxJH~F9Fd#vFzk?Np@h4&f8!Q4Le40)efW%BIY$Z^NXeF4q4=dM)WN^dpA z`&ZdI^P+jzS4LmMFR6^D{F8_S7Hi}8&#&jne0aVw5-UKKIa?E<-n{;wP&fS%{G$ol zrr&O#?YT57GZA~`LSKR63)z?}&{V%-En55DKI1#;?d}V-n5~(l2G#yF1`DqBd^Bkl+Tp#TXa1DU9X&>>p*37@DjQnkIm4vPyMRi zXcjO=OS{o|@bY}<6`vGfy{x%0cctjmk55mmd}SrP7@Fi+j$DnOq%9W*KAdG#JHSPA zXLgK$-$aM~SD?d^E71z_fJd-=J00d)`uz2Dm~Z)x==@4`50z%dBdyqEd17bJ5Pv!p zLD!Ujdb+?AG~e{eY1Z3q;QxKi72X|n!Gy7Eh2Ip$f@>?IdHBTTF_*q{=k?QhQ37r+hwq-Qm@YVVib+9&PjtW0& zpWiTd&Q z>1_XkW;Ol3IZ6mIoXCzuS4nmCW!tRN}M)LkuAWr-h`4K;<&Y#)#;=OMCaZ4Qmn1|C2Vpxqz6sIn;T+nOMS6|J70>-HGcYbLv&2%FT6g+XV>7!2zd(F zyDPuN*X}nr>{@2W8|f(`?lEHyJP%HSoI^}hRYvDExO)cUz>qu{LyZ&g!bZ zzk zzAqLA&ur^t=Y{q(OIZhEWtE{=TN5-8yjk;8=ZF}CNdr$iZ~gFDN8i2E@(uI{Xcu3( zRhdcYOR~4sv+tHrB|?mleC z*yoH9oqVhAuUwZ&?}&weYGdGtZdGRaBF6Nb^u$ahewekh%Uxc^!iRIcS=dBRnWgj* zU%t%YtB=L>q^30Gyqs9yYqo$_ChtUWA@5kwe zc_*;h4QvK}4Q#;KFk{MV_M08(b=#We;wKa4UI+h%%5|go>#=7A5AsCkdhyru?uf0* ziL4v(?pQZAcV+wm{EgbuE7y&D5;+st(5I-mAnv5Ac#fjzlh^t<7v|AfLE7ta z@AcDOMMCXr{(=K*+iZI9eJhf;biW7=5>1Kx?M>ZzZ>ip^<{$Jw!_f*NQp73@L$2Om^vW4~w;ots58#)gCA#RH1mJj`ST4Qa@oV7M4 zcukSpm+W~0-FenORu3K-kH!nXykgvM`meoi6F=$o!QYGFC$)-)2VZ(R#u+)apDcIQ zXWpG*&7Ic1*M8O=#|Jz!L_gjiW)RTGk#{zROi2A#zwu{!MP)DT|Mb(;6}(zqoe#o~ zN9pq(`RHVFJ08WRH4U7$11I=R7ETU~Mq{d9?}bOvzxWq$EQ{l#UERifjm8^%1UWkAUw()A>ZwS0{><8~!_YH#on-SxJN=>B z@K@IacAEU~#wEL>4|2XCw}kp{Ku&q@R`jq?lg3Hx?r66^qENZ{t#5 zbR+q=E15ZE+h*>5QFDp85@D_+tUXry%%`1u+ra7jW51sY)zNPC9iIQu(zaxG>~_YQ z66}T>w%o3lrv0Hoeox0Qy%ag?;RUh<)uP5uhn@@R^*UoYBX7gon zfgb%kw9^+&_|xz*WER$V`YZehFVP?4U&K9M(Z|t=xPOrGJsgeU7rqxB&YaL(A-<~V zWnO4)t26i9)6vIygo1nSja~rv@VEHb^j6vIqKkvj&wIB(uNBKk_Hb;(b0rrxW`656)ulTc5-r@nvlsqK3=xf6 zYZ>!#Iq=XPg={U(T?*6>PI@f7XUP!Bl{&Ybza{=)gK)XC<_Y#iXix2^J=$u2ds~@< zv)h8Eda0)v7#=(q3C1UXFz5c2Oc?%FL_bZu+sdp>(6sj7`$g-L zgIxN2n{7?>3;dPvoz(I;<1c1q=%dsmlyuvvT?{pUP0e#1LTGGc(f0rF@b|+v)*;N?@H7c^3=Nj3-IeX z>B=jm;9|VlpSKd4KT2KAu|>CPe@r@_W6qOh5}0(C-Aa3y%A= zrxu;6i6};L~tb4oB>jT>F=Q&5uBc!x3`RughV;#xg^7b;>G3FoS+ac1h zG2UWQz{%^~=Dw$X?~wCOWxVt)$LIXdq0f4k)8sxWle5$PeVE_CGG~PGtqmAtp&iEG zyNG#0CB3f@6KkQBUB^X>XTjrH&1dNNEO>5VwEY|7>f?8!yQF+gI}2)MCzouD zxo&=#HY2nN4ep8d**SL`@jXfs>@kZsD{mm7xN-TXl{Z0uls}5TqrOpY6LlALkXLnf zJkQ*|DSul>gXLM+gwKD;t-n_JB0r-}_+s>b=5Mq4XYzfrYmrko^Q4k_QUnYH3$w&|^5gZz|Zteg~m(1aE(F`J^@B_a7-;hU#( zI-96#J@tAzzxRN_*Oa=a4^ZdAY`@6kxvXzFwDF8HhUo}-i`9SSw|Q+JGR01v-LJcK zh7$NE0Yei=SiUgN${E0M2KITo1W&hK{|xNs9H88?S+J-5>78!<=bZX4j0+BST9ld-S(Rb@!?W5dqpc;g+9LmSiQvf{Gro>jwf}bi;@Ex2ee)c+ zqEm(z)gpU#uMgR?^n2#?$Nk{@|FLo6ZtM76>3%s62l|Xx{fPq~;cLgwz8z7&b{zd1 zc>J$g+&-!OkoY$^7auLaZ(~YzF(&Vh*S-yppR-wT65T0}%5erx_fq(6nY~kI$ZIq| zXp^;eo~7wPr`aU<)bE*70_-Y8M}ZD-OMf)pY=2xj#lP~cA0}(DYvmwwvj%ru&shTg zV}sf$oHKVyvU4Ykcl`Z+sJ=O4nc5)ul23L-3**SI9GFDDW?}Lp!IeBd!Nt;Q_w?3m zKLi)SEV>go@BHvq$=$T`ZimL-`iRrUWEvZJ8r#qrhYluF?*Fv=e^~QGvVi(x=ZU4y zbMKNbaC8&%^9jxMsc&)N_aX;(n6QnbmPv@#7lgcND^CAPBJ1hgf(8ZMB zt=n^H-M{?X%j(YMdqJ&P`VDuE%nO@ueADU}%z3q+ho&%T3UdBHOJjR{z6RQN9*kyUPU7dHFYf;|=oyczDS@ccy&d|iU zZ@FzJG$w3r3tf1p-1b^$=8|_Av|Ki8F2VaIUexzX>A+h=U6uxf`@l}8ZMEyt0JM0n z-fdSrj#vFs<_VuGP)ZBj?2&dZgbIMLUjY-o5qynp>f<$aRs9 zCgbnA)|pfF`=**!JONLH9)xSnyY0Y&J+FUNeY~^w6=#0s|CQcxHJj$+hvHA9LN(9j zZ_(V1KWBz^bUtD4Hh1$Tl^2`%R^5q3jMmp>*XaH@`xD4dUGp4S3SS)N{^U?y_?~;B zv1@%TZRi(a#)eKpJ)YQ&(hbq~&_`u=2=1Lc81tide4TIQmwrPhNU0pS+FA1)I@NQc zZTXRpKN8)89zwm)Rz7|oob87<^_7M69Usk1)7KYF`@in_L(Ln`Q-4Hrfb~l}DgX>m z@vLGkc%5gE|6##YJQ92j0;8P3MhAAH0qiNyXzapc*Yy|S3_nR%QJ&aM7lvn*Pf}j{ z`xf?Y`7dV63tw&>HTC;097!z9)_38s<$DQJLuiR#D8Fl`D2kSpgtR^@Z}j3?{j0;= zk-!=_5rQXv2wpSftgjQPi#ilCg?zY?A+7yu5_v=V6Y^%im1lK-%D0gh4eK{LmUwUt z3HiLX=KJv1h0LFy*zqru9@$d{tDLLcapJ^);-O_K2^8Z)Qia+p!1w4qdSGI-^vb>ujhR%TEeqb%AHf1Wr&nXt*yL}a@x^Rh#Ocz> z7SBQ1JWmdKh9>uOzF}|vwv(ql!;>CQ4{&cef7;V9;qinfNOJ9!7EkL$(!)I| zp2)jmI;7w8og>QX^Z#Lh7yO z=_BskYD)`yJeBz96-WLEo(zmMKk(tOV->uiZP{R+wKfyZo2Xxj4pYXvCH%hIt@9q$ z$^UzK7A3y?KnZh^c%BcyyA1)(j6H`1U&(<6sv4(XwzfI^gU6HfbChvf`I@oXc#4eo zEaN@Pc+WE4vyAsF<2}oG$AHZkV;*D7W592WagPDJF~&Z|*pCCtKPHN-(i1? zC8N?`AJ0&B96WEw2aolheHGw^oOX)edG7Ba)lGa0-OUJ0^Q?b2Jh>_`0{`A`4r`9~ z3#Q-y8)AI0CVd^*>`$QKpXXOKn36AvR;Y8zt>>^)Pfqms_Z}!a3cfN)&b;r*7wsNw z%>2Z{Y=0@fHs$$MwZ@lTRQ{#E2%Ep{ws)JB8~F7fZjCwj6ivna`Bl%5*THdKs@b2L z^|M;t%-x64y!@(u58wYpyxmkrpEpCPq&XO9@tD+pY%kA?AHQ0vJ|;XKGg|5?V88O< z`BHq)_L_S{Ka5%NRX$PeH+lRnim&ndmOpCVO??G5LG$hv9J91`n37hjyau? z*i;An{fyOX$ZIkq1wFvDve#n{b9X~XV^&_}<9~2v-*=wX?=SoZ5!dE<= z%A=$yf8L(2GKVMj%O3dSy&9v&u5oXNR#StfH+5QjrXy?I@fS1x5{+NJ<;uJLR-3Pj zd_95*`0==T{p2mPdGU$T__xpDo^qS#)lucqn6-aby}|kqaL;(oRLzLVj~{`L-^e^f zrfx9rdRcj6pZ}>ZS!#A_Pg=IT<%^AZqyl^VAp0cLLD@sHV+J419H}8@0OKvnJPzIL zogVMLBn`n|zlaf_~4=$9PiFvw=|jfsqh#N2<}6c3PjOJ>K<# z53=@#FaPv`CCM7Inm(4+_)V$S!kPhd1ACmsHQm?+AIYz(dD(dE{PZwCbr;W`oDD}= z|7z+@WSDn>MCO8QQKDhw7=~nw>bLm53C9Nh3--Oxdn_XwH;0c>PQO(j{}0ymfp`9Q zKx2V~Sy=Pq%z0!OD;J6{Oql{p>&vfq=p0#ZB>0F~h@aOX(fAAdp>gwvd!|ZFnfSh9 zHCBI~`(>G@%&DXFAs^W1$(9`Z{4GB2Mp@auhIOg!gIg7gycgI9lb(mG3o;q}IYw&E zntbXnRzK~S_hwo}SFsPK?VV`xZsL2e4;-uIF3YdrCn;K!KiM8^f!zU(>Gjx69~F)k zw3k~Nv+@YIh4wjDdoOg@fPJrGyRUsI-{(5GCtkOU-|aq~rv|6f`d-~8eG&Z5$|wBB zqIa>s#2xZooQ>S)Iizu`FZfmrsb7J7z9nqG0-~A7i1oe72o4&*{A;>5Q>Kl+{4u^G zZ4Rvo9)d%5%?$3jFr>PKS7$Al@Pp_|TJ2kp30V+ zUJXr`f%8gm9T=lz&cE)!d}P?mnL~D{U+y0+CL1;h$n1+Z<{TXHkfPvT;)@qE6Swn zvoa}m;NG-{wKOih-t!~m)&ItGil_8^-A%G7g&oG;8O5DM{NJ3s;epCPn6tl6nG*Uk zm;8s9e}nnB&y=8pt_}?Fp2jUqk-_-?zSw<@UyW6)c?qAZQ0xQ5pMZ|UKjAT7S^iDv znYab#4=wMbTs`|pH+iTx7(zbd zT$NL|=16)m>(G5>EBb)$h%bm9`5EBpf`Z*Ecx(L3FNtM z#<3@pQW@aG-YNIU;s1~A@<+f&@er3`N2`n$>U=(Ft_cZN*z^z42j*zzX_du?L*>wg zxc~Q0%=@qvmPC;gVx?XmC13mt?R>|C99%o7te`#@5Oh1Z+ zyA&|7{_^{#L-^xb7;?q}ze_U&J=MCnEpTxwAI}%vcg60zV%{CToCfqRT{?YQbcKvx zEFRzaeY2W9g<-8L$o}|CcnT)ie?X==%6`Mpq{ob~H<6mGH{+8nCXHTDV(*;Yb7Lg> z$I#;*&jHSs2!s>wIf&}l)L{|;OGaG-b{le+>k_**0`m;u5 zgbUku^hb1*ZR8JMv?tV?t>Qb09J5w?Oo=C$6C0Tm$Z^SAO%=W*c5d)4Kpcwgnj^fg z)!a(nhz}3*2AaWVlPB|UlC~!IJ2nZsm%iDAgioEbV&0^6XD;#Uxi1v?bgzDMw)MNF z%6%W?eZYC|_3^&edGA@q`=I;2)t!@;{(yb9?ke#B_@T}>ZPoq*@U^xC!C&@+u|Y@2 z7LO2&r#XG}6y0dkxl-e7Hb@TVp5@1#QVJxSM_GV#E~8k39P zP-%+)_!C;X=pi3{Ig(gxh~@3&+_7n>e%yu3gtqW0z0$ZYbp6M-HT|$~rgNRbi^bh6d|e4o#MZF*r`)or;huJA z4VkE4_1N;lFSwtL_p8)n@kY6-S@k3m)W^HpS?_jcWttxJ*+3t*P@eFOMC0_)X7Lzk z=3=Y`%`N<~W*j@>C-=>)Q_%i7{D{XTn=0?@e9M6g^n1pI$9Ghp)*RkFEttSl zEllu^jmI_zVO{ZfU3olSO+r>lyLqEciM*9fg+crl8zW6~6W3cfjD0SmGIzH*Wm<(idnF83#Mh^y}iW8dKD8_LB3Ju^aKjW9)ht^Bd00rKV&4YcIaLxYlIX1)T+! zuMhQT&CvSOIQ_rs_-k#*HK#A0w=UATWX95@ci7x&b!K@rd+#03!LPM%F#Y->m9w!; z?&jTP=dNkPL%E=6R^PJcoI760mLGfN9h6^f>+Lh|$(C2&8>j!STV=&Bv%JyrN>>gQ zJhJn~&Xvsc`S=L_yJxo zt=Rh?0G{*mw^Ynq@NKRv(V3U2+EP1HVvGt42;%2?* z^7U9*NhpiUhjRE%y`llW4X~D-iQZ%J=8;ad|AY44&6PUOr#e_0BmH@H4u_-v@Uh6( z??tY%Wj$M>M?V&+{)EXpP~e=sL|54)+|ZYT{r6($xCgnc09)wB%y>Wh{`;5m8yGxO zhFx?n|7pvi>&%auD{@y>JgAsPoVj|9b8d4ukAIuV>!VzATLFGq>=l^@r*%G3_3ppP zsk^xxpEtedjL9>++uzE2>Q--M<-Oi%Y|Z8A9h@hX-X3{|ceZ`tg%7y%Uj0@3yi*-&NAMI}(Vb|U z_y^k8aN*np4QQRV?U3(N{I4bslHe>^Kzf#7d|+74*6M`u@=li)KyqcfqAZtE-FrSibLL9~eN;zAhQIA>s~cP}UY)Jg+8#EmMwA~tPIIZ}nU%F%mcIAq4lCd6Jdc%Mp2w^Wd>e)7+59SEugNBxAm&b4 z;8A=^?gtKS*ZL$QOJ{7H{;t*g)V|t@ML9FD(1g?;XJ-OxOJ{uKLo0h%V6*W&HjjC- zC-))oZs@fFo!HY0?^eE=gyu?aLrpz;a0|Y{XRzVlN+0OkuOvy%To7Pk4q8`QvT*RmtC%T?fB|z4#tu!?bZWc?V98Z1$Ub zqx0}5-|USb(}ZH>(_5R%iN9B9?uP%2*X+f{9tVeeNe79&wO6q9`s0tMWG6~9rYb8_ zgInqT(T!f8gVX1}E;y@gjah9*7pFq8%@f=W&b^7?mc54(zLioG~voLxODw{er$KYpNakg5TFBG*;<;?$~F>{OmIq$ISRN=2#3` zesGH~f(HPc8zUkr8{4M zsii4wXxTXf?w;m}LU&0Bwu&8#y?nS2_-65&wY|Y>ODw)^9^%|_ zuNZNrRtx{J-{Xv}Xn=8nm(|p9t~%)aroB{+wF^%^E8GZXmycERO6{wUs)K!xx8=D6 z9*fwkP(Jp0uzL(e3*>9SeQ^z+wlpMIIsI_qb^WEZNWW`maP#f=2xj1Hbrx38K@gbi zq~F*MCvydlEFAFBc}RXv+9O7`)1JC)!MEX`bMEalF*zbWB)=cV{UPxp%}I@yI=0TP zH@I&qp)>oyfW7OfZL`0jZ<7DQi26qi#(?Fu`=&-T2Y7ET%XKN$FQNISLB7^6^8k7GjlFqdKzbbM>L1euXbe}%CJviy3wOljy%-&vptbjF-a>T$~#rnIDwfw*9J8&z;S1dzJjhm(a*?M!GdJ`MH4WD9;@m#BC zPp9+bf5?5m!F{fGpG|s7zjN128`Da1aR53;54ZO*M+&VhweV_sIdlIK9eVlB-2U11 z$u1*17PkoE&!loQT z-f+%JpLSzR5wACTiZe5h-KhPAE$ar5m%m#PL5?m0c16S#w0;rEFFerHRQTSj_|BVc z#&~v;M8fFZ^U#y-;K`FUVhv2vaP88 z_B#e?DG9dq)*c~=czWNj^#|Go}h5ENC#v{)JTdCFPLx zZVoArlus%k6_VzX=8=j>^GVl`7LbZb?;`yc>D?qB={=-1L;Q664D1qA0*vGDkGJXmXaz+%Saz0eVDYIR7v^>>7%5Nk+hC{oOCnk zw@Cp~HE9K@hEz+cBdsK@A_YnHq+1?YW7hJ#mFH^GZKO|-ZYMR6){yQXH9oT0e3JAj z((jN$q&rEUCVhs~L~17eE@>TUJ?XQg&ynsTZ6N&~>Gw&WCxuBZq%V**k~YzYFY??% zx|?(l>0VMRX*20QQX6^xfb@r?|3|3`Et8iV!<63F?` zQjbe(_S~Xq37+;Y@%DJ9Kgb@P5wn?qFQ8-fqe~s0tVgG6;cnwW_U2A=_c8neeuwX$ z8IG=+XLTy+S<=O0A9Jisqkv&j69WWqj!;>C6$m^ znZ3iL-kFr!&p95x%~?x=pUyc*I!Zc&TS)==)>H1BF}YPP-xcruPfT17WO7ac?tzZ) zGyfl#pTMt@JiCM=et&6nF*XZen>tz8P;oG~e>85oI&Q+YS$M16AF3{!g=! zf8C+j+*>0t?sC9JSD-bzx`FR&{5MDQKFfZ%>@$j+v}S3%SNHI-S2781u61yO@70#k z7WmCPQ+)P#rvL01_6O}(&C3joRb-Bi5kz{dEi*j!U}j|Oo0-(u@yz(x7;$4-?y~XW z8Z&=o{qB&L`bKLC(cjj*6wPP<>AJ!oe5fZfH?Oba8@c_m{UQHwX8nU_G#2_!yq-IJ z=ReQ=f%Kcao2r*i4(gloT;cobNI_no3$w2CzzBc!HE)B}=VxF=d+2Pl;56?tIJE+& z&A@5yM;tf-BV@-@GkAG(6uekIOw{8OW=99&jB zmD{iJt3S33c*%~@8UOUA>T+9tWLEjjGj#Fh?_Ht&S#&{+EvNnBj^oBpA4AMFul!cJ zxbtUb?YnapelCpb{*BS=Y~Eb?D0Sbb^>gqH{w?12w$3vuCp#wJawH3A?TwoH*>ybq z;I|9I+mE>R>`eOQjvug=Z34$X!FSvG*1t8#o{qKiIdpL$B3>}sVcWI*AZk84yUz82 zuX)3QgLFIm=UX%%koALsM+z%vyJ4eMq?up~>E|pOnK5S=4sdMKHUVpg@yjq^jT_^Wv@v6Cg`5d1` zD_+ePdxvFqp1bsmFGr7^=azn_7B2q_@qW+x?W3Gyy728tXLq4JQ>S|j0)2&L@xOSL zeT%JTm*JWNW4@n*j(|L&880rK6ceDUvl zeUx7|1J^gIEiAF0{svg)Tn(1u!Gfj66}x@ShdJl*+Duy$1FWUij+XYkHCVG>a-)r} zU#hi~^>HAI&kAd2zs^5Tu;0}2=bV+m?*h2D%AURBiErX}i7m6m#{M0jNSXxe?!_~= zti7^n9-YKrqcOA2?rkQvd)ER7D?i5X`X}xe@`lpadc&-jn4_{-5viSshb;wNrdspZ!)tU--V?>bm%@^e!xH>8_)H z-DW#^&vnI|zVdq$eI{O%N3!;(c}jl9cf(Wp;CCJW9LWw2KRNfMz<1$On;0|pCVM_- zka$k7sBdpHCp_>P+Ui+YxH?-ua`OTAo{v+BhpDfFvE-n$5{o^4_etH~k-Ou`ZxT0F zb-eXj*TO>S@x*lWG-3PDUa}WhaK{Dq;e^TIy`IF5@S@i}Y3Gvc2Q{y9H#%cZFDzWI zG0z?^eXwUH14$FnScn7E4LvzAA9B1t^15W7waDwfAtO@y3xa3dkR~yFDRB~Rb+3T z-^Qx8Ysec(+57Ce0z+J&IyNg??xsNpSC*aL;Nr1 ze_6Wpm-{V0*k9Bj9x<6L3TnT2a#;rcS>LyX?8C0@xd)p0I`6m>mGvw4R^l&4bMZMI1Kx9y$MUF;SVmFo zV)zqk?t0jd_d;VwQpgP4Qvy6Q1G9b~68!0-87(a;Anq4?&PA2a+~{fO&+*(bTIwC5 zf3?J?Q+z(buu6J}V{5ulKbiCrSHg>bhj(al(952Vm-9x%vmeCn|7=A>yfAnwXBjy1 zL^gvTY;WCbu*EISTxkA0dIfP0u(|B!Zo{qU4;!u?r$9P`7dwhlUfO+b#pV1k`ay!! zhpbMXJamH`^aq>t?Fb1Oo`IXZV&0=qsE*v+nYwb(GcHeRLn-@y_V4UF=?52oUslJQ z_4JK*|D_2yzW6NVzR_Q5ll%cDrk@ea%1krm!4UuXWdDn2dqu%NpY-i{1xZGG%BTU#H8^e@`_ zT4n2#9S@xXUgOyMu>Hx_SH?LH?15J2z~0XH{H(n%VjLR>Z5d$e%Fd}Z@bPiW0~jm5 zB8OyKi1)1*tRlfvoDpV!OZ-7FuyA6`+%?jBzQx8@Is}}&!B?gms#kKhVMk^fm|bWt z*FCN!f#$Z7Ky^K`gzVbLgjUukZffj4e9i9wcB7_jbRMyE=9;qe*ss$3PMY5Im z&HQZLai?DabPV;$)=8gi%wGD1?Zl^kjoyKu&E5-J;DO<$(W}`|ye~q>!=y8$ad;WG z<6T&*#BcGoh3@Y%ev1#*k{aB1@VuO@B<6y(!SwQcTH~$e9QoyA)cB(Jd3_oma=?0* zcYB31{Ma44ExM+In+qkXM=-lmJ(>&XROcejTtj#G*_T+KyWIDu9QaRdUH1a8D#uhKz!~>slg7=y{$kOlr6KWyLI*y` z8A~eoh7Im0c{nraIm7e)?DH*L-X}Z%Bz6EZ&|T61Bw;y=IxCZd|I>$%qAY9?wj4; z96@NhnRnY_=69>ycUxlS(~M~%tS5d0e`Ngzob#<2oh>|HYU^1Y|3=URknw|iCbSof z+%&N)`>uZPgx`Hva`D}si6Zx1uIm1XA3j&;Dc0GnY@XoW^C>6K`4Qnf3C`ggMf$D1 zKJp5OH%fNo`7qBQPk(UVdF*v2?Tq34LCJdbr^v-og7f90%&8vMnS}f@+qc^2?}I8wm(0_J_0<~{pDt#;4p`EG)y zw71>%yt_U)`$F#8AU|5ij&)ar{Ok)kYlQ4nu{Po{y7sBa#kC^+BlQEi>(kiY>3Zh$ zIo+Esof?!(I&s>Yp7i7x>C!bG>FeI{$z+Z{`Za9d(d#1H&!B7Me4khp-qy*MoRYLB zCp1}_Q!rVdQ#`pp2di98<>cv{K;!gdPldsG!ee&sIQci$XA%9eDS2~Yt?(z<^}Nr* z$;WTts=EUJVo+tsQ?I+6*svFZQ1&yuO+dbc_KK>sw8!iR}Jg-i8Ll9#%rSt$Cd%y1d7~{PDtKWQ<&N z&|=P}G)1qQ@I~f9SFAnckq$7#ej~n8u8e~Xv$>o&Ejxbd6aQ?7hsRdj;hS9#eJjR3 zW`fh1Ly7l7lL_fz9OGoqD*4BrTUXdXys?08b&f7udS`BPtex$*Y^N9I>b|5;Cr-bEK-^M>AeKO{b$_#@e= ze)3ed&iI_~6HjEO&H%8d&iOj$M44^L-}W|W4AtePOyd|6y&;qBLMPQYG_Kz?7RP>k z;f&yzc%Qd{`RRdHH17gDf#Zck?%dcK@Nga!I%FN*DcHI^JX( zo4>I7?BHBuZF8Qve*3?^aAdNjFg2Mh9Jet)uzhI`1i&YB6`9;$n2s!x{cCQ4>@kg* z)%Sxp8;eQjqIQ4ByOw+6e;~a!_+xKZ=U>wAFF&v;5Hk-qn2%oE%Yrs`$}R;hNdJvs z)A3M7wwJX$8JBmh{Lh%Tc3jnd<`MUp>?$pkjoqUd9%_&G@T0d+7_wL-LYxNP0Jilt;=Z6_5%^ zb4l|^MWp$pYe)-7#iVzUev9;Ol8^Kr(tAnoBbAWePr8=ybTjF;C$2NOvJ1I$BmmzTvi`{Qe~i9a`mV=!lKzh+uN>R5 z>;1-)3|RkU@r*9&Vs4p-#oy4GOyW9k!{`cc*!3B7>J1}P)7BVu+xFc0ZF|55UlVlA znfRH4MIsm8gx{dco0d7e$#6&4%pB%^d+b8fFRE>Pe&I)%oi0B*L(Fc=8^F;QEsplX zx3LdR4mxpb;RAhkKgaT%bX~HpaKr5NiT32b6pi^=Kfmxm$gWq?58BLMqo3zS7dOav zsfynkf0KA3elv=>+m$MmUzv9;WA0;Y@|Wt-Sj8h|=abxZwS0}4Kl@kVFlJ-xyz#}C z2j`nQf2#iF)AI{Xc;*+kum+qconLaIe!l-i%lxtv$@!Hh_RkNT7@QwGae98kNzeSy z$&gA|iOn-Vaq{&1uJeOMThE^^icENl5)-9GT@&?1JrgZO zy%WizzKQ)s2PX!L`X^2o9iH?Q;Wu4$bh5r^XtJef7(3<2XgxBu_A>StrP7}Heb_9= zv00|kPm6aY=lAxs6s_(_7B#T`6HB{6G+K~G&kQ8z0|)PuIzt_R4-1#NHxrv?2>Ej` z;PDP8ON&=zup0w*W55nu>4x4@f zoW1})kAcr);PM!_JO(b0fy-my^4Q?~;4yG{99$j;m&fbphmV8HCVr!P5V0xOwCL{Uxdmy~H(=bFWF3U&g{ub5r_^)C zY&>JU@CzI7u;UZ(A?P*!55%3m(iziElTX#LnFL zzk@FiF6e*x^n$}DJPQU+lrA`WqJF{9iIxS!Cz164yfbF#F! z;ADMq@yV9rl9S0||H=KuWhV!VD^H#-4xArc;6HzQLD__7LFGj0g1|)mg5X5Uf`*CY zg3!eN1?wjU7lbEHFKC_gEZ90(x_~{t1&PU)1znTL1wE7d7oaQZy!(Q_$ma$=CI~ePs=s^Z<|ZZpXyTYu;lEt=bCZV z9ou+UM04wW={2c9vN$L_o^QEkfdA|*ztxgzx#p;3uT=7y0qP!Voc^;q@tB0?nttj? z2Rw6v4u8F77@BC}oZ?!(?bO^7-vYl?qx4C1pgn@rH<6W~h1v58{O2vlmXBOsLir-v z9HPBpbjEf)^&g$_&D7vEY29<1I(^L$vY>}H=7`pqi^%zzu1F5{p&&X9a7Y2CRDE%) z=3xqW3HK>rrg@kGUYds~U^WVVM#0Zn@N*XYoCQB;!OvOna~AxZ1wUuO&lva_13zQn zXAJy|fuAw(GX`$Pz|9NL%L~xUG3ezO^l}V(IR?EPgI%CIP`KHdN~fg9EV;4rNzN$>8E|JY)A~&%(9dR+<*hYQLs*y&RGIn#)1Eccp?19 zoUbq?X?$%Or~hi#bIs)bxdrfqV)2B|Tg^i8gU(Llt0~2fiCh9-C=veQyGIiq z_(Ea^|H8d^!_|0zWOxVv<{|j-PCMtV?v!qq4yFIZp?WiZ&q-%b6MiCI@&B^-?(tFA zXWsXBav~_LX*X?Ci;@Vopti(adUrwq6A#_!xIVEB3+;kU1T9$lY}}>y4rId25Nw0e zeI}y!h6*@YwF}nn#;RLvQIl17@zB>pw{{oLp=uXMd%tL_EhL$l=l!{Uzey$`iJ;wk z+xxlynAgnw4&TFd{$AJhy}m7V=mg<^26PZpoe=V2zd?PFm^(8z9eUd|<-t+?E*sh3 zuC^r0C7bkawjE?gO?98ij!MPo`hf-V170C_30U+4i+*5%9bbYSUxpoDE?jwX<9O5a zy+tL@A1o?+Ay8EQLPgQ+7ix`WFrsEqyUi)c9gW(W)0~ zi<)0-Dhj_CFN(d`Rn-1sZ&BjKgGHUkdkbU74;Ho$1PT)a6@{GxwS~!nrovqV@xtzb zuEIS7y@fpk2MhNN1`78NRumo>tS#&tY${~URCsi-tB|#-f{yeim=~Oa$~H_IF#>7))kW@nuNySb=FLMI=>Cxd|q@Wev=&; zr;S5_qV7YrMf*ISD{S<5u5gvdbA`S7} zBh0Ozx%D%*e&*KC-1?bYKXdD6Uj5ALDPa8+uzng?KMky(2G&ml>!*SB)4=*^VEr_( zeg;@S1FW9`*3SUzXMpuH!1@_r{S2@URumsMVV`)fsoAl1HKw+a`cDw?bn4R~{EbI;i3RkT=7haARb?ZLQ zEhUHH4Z+G_-NgTN`;&t2;flh#m|%>rM>GjNPUktE|BdPg3*Kd+%%(T=tap9zGQ|R_UKQM)EksHt}FzpAX{lFBuMP?M0LAT}5?QH0_61ojSw>8jh z9dug{-7bZ08=>1(&}}nx8-{LU&}}<(n}BXRq1z;MOP+mEcb0B{ALvJPU~~fg7@mE4 zItF|b;9B$}nn-jNHVo4hv>~wl1U&;F(t2tC^Z3+6U+G9lDN1$gFK{S#`<g&D)Kk1 z4KKrf-_H4zEx>R)zU}3VX%RKBIX78np{VYa)my%Y+#_r*bl#uXdXGJ9)z1?zWRB!A zEM0<+zvUyIZ-4y|dHshoLuyOsT*cSRUh~@{#;EpWTSjR!*aLje1iq`y$NUSmN1sEg z%?3=6%lKKChrxuo_A?jY(m9dO3V-n$(Nyf(n^yrN+nZD8rr!wJIogwd#&m?}PtW-D z*AXOF;TZSp+?m&n_bhlDyWeMC&VC=|(JMbu`+Kx5)_xyi^&2ezLpVp@%@gfw46?g& z$B`Wub-oKUPuXg_lx289`TFD4Iba+{U)8|Jt-+8mM(K=(Y#`#`oauKU54-{!hiOIA5Yfv6%3RYtG?ZAm}Gm z73{?yw;uYBkk5V({BAYrxZh&;@6q+_wU+Ck)n57JQeUU<=2(ClC&>2knh7LTlfbht@vr zWZMp<@9q*kSI3n@duFNf*5T`b)#8DtsK5DqFrMANalDWk`NdT;%^#sWWsC1wo;&Ba zXz#4@Y*l+k@``tNXl#t>GqPRLHCIx%c-0o2QAHoRP8-^bTpzrPYcXskU@KVT=eQC- z$8+r~wB{JI&*lgJ=gu*AUncTkvI# z5GTJSu)#kW2rk)Mv}k@GH3Pd`^L*$KxYt)La>ep@DGt!lWc4l``lNt(z*^xka{9h{ zXmu_2W(~USnDlt-B64`2as@g^Kz(@ay92Q(@eN>uoP7!XUDDv3`M_vrUhr(>aX#k> z+WrYQOMa@zhw>R`YGJhCaf|hXPw49S8#d9q<1fUuJXe;*m#Z@|BBYRCfZnXxm{C2z zapwe%EAfL!&m2{p^9Z<5j<@i6DtX@E`9k4&;2?4(fLy7-erU422wr`agI23E2L8pr z-Qc*-aQtWvj(r=$V(7v#d!ON>hhBn#932H=4jAvrQhUeIR`Na1d zp3fAX2N#dV^KVi%ZV%7P49|~&XMA0TXK?D_@>rAiJ#=*y`8ng^lpM4SvXq+l&dzrS zr>gNW?)bs!rQovD6+*-$(llV|Pxc+fh{!V#tapRebmdx4p9QErK zX`h|Oj|{H4U?#Zo)=f8p6Y2eC@D>KAN$n}(+~*Q_(P$rCRn9n)&_}akmcGs;j^r$k zr1>URl6PO=+yXbFFL5_=w17Co6O#LHK1AKgRn>ce*&`lpQ$PBLjo=zTtf`Tp^0gnzy#{USoXb-A zDs)bf)EKyL{w5d}v|6?GlrF+cWz| z`p!J3XZde=N#1y;*}6h-)4XlI%v*CFODDPSlAnNYwE1ey75we-+cNxvj|yFfhb{ii z{e*CB^cv8aIO$g1r$1W2xpaQ|VfBGc;L-VX z===`R`RUvIzPtNr8@}>jg}h`eNsS|z9GA|=T~{*Zk(>o$G@^0QBXc}U{fMr0&KUEz z90%bk%g0%Iq>X^)JrH+E)QtYQ^7q7@m%SO`vLH6I>wz$z*)i4ZESP6LpWEED z_1IpV2Y0p3)PjGu@Qm4ZH9HG;@%uT=f#;XT*1Bo&eC&gEwY+iW9}ONs z7t#*b_f`Wd>T#DSHg*IY9RWw8-y`7Z2>3%+>_X3zQ-Cf9kLY-E3dpAcXV6+7xF@H8 zTHWA`+T8fCT=}0qg*`NfaV&yXE#|i7V50-Z!Wl8T1a!*&fqm6UV8{Kfk13aCjq#J7 z;eG^pQoQvpR{(rv1N->RKPvnhUU$=;YGtz?tL{ZM0^2NZTb?jD|1kGC;kNA{bsJ3% z7P^*S0;Bf>pIA4!0)^A!^T2s|*48d4*m%y(Yd0PwAJ}Z|f&&}>g8iw?LAaQ^mD=Zn zib)3=u!$Y@x>ha5&sQyfUHSJ)qg(em*@o5MbF;VZRy>-sZZ66z4W7w4+5BI!0Ds*M z+5kQ^X~+3RZdKJq;BLKYsvXjvVrmmQ53kkWm3OoUKe|I_(76Sz3kFn^&b53?wFt1O z@E;nDeGB=p8NX32aq(+L{Cu{a*q%v7=R6Z0Z=4z<@M&ECm&CpOIc`|ZKD-*Ey9*Xi zg~o0e7|ECYLuw4Nho;ZG4C=T+sBvtuFLEb zZUk@Xi}i0%7YN)W2Ce;qIKD*XcYC3yrLG|MfAkx9r7`@<5 zeK~wIZCw=mQ|m<5j4j8pXJS=b#Kmh}*yen>D_7pLWOGLYcK4Uq_`z@1xpg}0F?O45hhKd~ z_oYu7Zpce>uK-!#%VYN}vVi^v-`x17o7T1jSbamV8rWA|J_bhXUkpdTp|ckT?u&f* zUrQT4a%{tr)xLc8;UatlS>r=P1(C)w+qX`BF-}O-FkDfP2n9g3dTs7F$#MEaEpH!;uksM|wd%{*Ea2 z)V;!MW|QX8@{;&1Z=vQwzXgg3#F5b*^IZY|OB$xSO$~1O2HE`$`9+J6AKk*A_Az^S zI@Bbei|nN!Jfv$LKOFk8@Dpu&EsPFrr`=*~sGfe{j6OBpi%umU2)pXwh%Zq#vEab^ z@*d-F=8g_MFf!JF`PEKltjuv~5dQ;mE{6t>a4uG+CioHX20R3(5tyW#p^cxZFUbY) zcW$FIo7u*loy%n7$0n?`^%HglG_*ffK;QV@g2WgGu{*>I^0~>L#wJW)4_a(U>-$jV z$a2*~2uJ+=_Q1~b9se{X*ve-tIBOi$pVK<=KW>bDn0(`PqvrQv?bSCN86W+$x8*YL zJ=$37;p{)E;G?(mDSLU#j2WDH_+n1XU3Rd1zdf>nnD6i!qb~n@t%uObb|!58nc>I^ zJCDcKmkld8`xbjkK32Ua-nO+#Hdd7XJv&F^WZoU!=lAEFY@fuaSTk7M(~D2#fA2kd zS9C@mrShjot%XCG6}C>JKW|M84%+_d=1th`VYL;Gf(z_A@SLd2f4DcNt>7Var)c@` zB6KAD%Kwt%@D;yH=qCZ)H$(U47XX$q!G!*ni{9vO)Y=3ZY396%eB@A!zHQyidWG7* z-n!ZR);t4VJU;`^(e|*fL1PxY@vD}Fr+f5Mm%r!!obfJLvb!v3dTq`xOFLJ)O5}FE zY&UH1;;oPJ8=B*t9ocv2-(*NAbM0=8nV8f+7;X*Q_l?R3}o<}1h)zK z97X$!=bVvVP23~P19l%g8s6XwwPEk`tlslzBK?M7r16Cro5tw%{g1!Mjt@Dv5}Ed( z*idhLj{~zw=suKbe2-wK zF{vHx(GGHMUF(+yN;{Ub{-Pc+unDOB&eN#}ybyc0M>cDg7Y0hVZf4z*|Ij_zIRoo< z;J7Onj-shx6L17xf*a5OSkJR>Hy<9IRo!PgB&pcicsO2ixGX4o%<|s|+%o&nUq0L) z3S`Fs+?V0EwR}uyF(_SfxPr5X$q9}o$)WK4T{XI%y~cBu#3Zxvs6!f)+OErg@TqLO z%xzgHbItRO&N;0QSlgCpOwrfssK1nXjyap32Y%`to^uyAQ?GaKw0Mp7p3lx58>oDu zxiLo7PYJ*S-6p?i2iSOehW4TVr0I#_`9GLD|NG!wKUT&3PxI$bZIqn(Q}d4b``4I% zEOwelW3s*I8+p6@Ld{h)g%5A(lKztU@oQLPuj0J`e!efU&#avF5p+Y1l<6IpU_Z^o zzMhG1WG4P1;GF;u(OsYWga^w9O#i7pUp~&FkIm|16g=-$Ju7^K*Lrf#WZeqzyvdaS zV_!EtaAOuP;9EHpTSDpj_o*$$Hbze^KSBQC8#S3P{>D#OCfdyEj=KEsJT%-k_FUTz zV7cAoV50gZ&TmV(O8FGdE*#T^XLrxbi)iDs}^mC#-3VU$S0ny6Bzq z{bcTL*-Xv~xncNnkV}8os(E=n3w*XimYWlXH#wlkl_kz{(A9U0|C#$}pE~o()pcH8 zU*^KW0JMZ3&G@o{SW7RyIb_~oEpn|1UtOH|DRCQMvacgfd_{Hvu^#&QuwXN+Goioe z^OE)V$8%tIpe^mfpFpP8-s;TPe7F|=!$%NJ){FmSlLxc^fy?XB!-L_re&_U#*8VlO zW?on9P4cF?kf`=yU{AaxM<&+9=OELk|Eu;RyYT4->kG7BEqZp3N58;c z^#jE7TA!0sfREi+V8MDqxll`y>+YuSi{iNmwZ2RKC;s|h( zzUl{t%IE9{e*M6$AGjfJ$BlQ%KO5cKX)Gk&#aP6 z3}rrN_~-xH;WprJZ1LB{M#$U{DBiA{$U|BT&( zok7ljuot^`g7L&ypQ}IWj78?gToHII;aW4-w2xJMmo!;LU((Nlk;WGJkMqKjK5Crg z&o`SlvLa0^|Imhp6=jRCg^Gj|J*T~d{+OGfLC-&q%#%EY7G>9UTYMgUV)d)ZAAxq3 z#WuMDWOqR*{etP@^QXIflUeN57C*MU&gVL4iaa^S*sVE$E5>5_F@}BN;gi1Lm0{WH zKN~oiXXA^2-)dJX{0!qVG~#grIbk%DT4if$+8QO_ z>^^cvFwFfutb=90V7G`4xPKe+LiG2%t#wjg{5jw-+-2?KtZk)n*z+E}?i%yIbAt9e z{q}$CxBvCGX}{Cwhr9juv;S|Kp#ANB`(N?f|M=Uqzuj;DQosG||9Mq4!#r{!u<7eS zM#X1({2{rgdMwi8;$7CJiboI2M)sZ;sGhLoChtY~tmPAq9j47~*wn~Qcq^v%A}>+< zCNsaZWe{7AJ|eIDQ5ag89{vw#o*bTF(iKHN{UbY*MD!C~{o=^}wj3IY^DcIz?CUM$ zT|`WxBW@cVAifdE>1_r7BL3P zH{SZH+0q3a7rANl<>iXeCOkiCeG9$6Px5adZut+pA`hM)j(iti$UUxnpviKVlF(oe zvT@%)7xaisfi9H;u^)Onz&QID=ONX}j{GGulJkVD_6PYB!01BBAB~ZC)!w#`5Pxt* zzXr$r&s*8{BhHd7N56JKW8hgecWPe=%u)@ft-z&>4gRH~PI4nwEZ2yhT3H zwfxpPqbxhd26vQm#7Y*ypKI|SPV;<+Zl&_1nYp|YSn9{}m48(rm~d~c;9@xs zTx0*+>$;bF{?6i=XQWxTHG1}9W>e(MW1pZO{H>WV^tzTdbpA@d_%{|n2UlYw(=TU( zPvh+H0?sHZUtBX~uLnbSU+fQDF}!IorN3418fyX?M0Xv5CXPTW;s z>@^Rie{^-D^Ju&_ojwYE=V>gAotRywnK65KelTJOz{VUs;2l;8+^&Wg7 z``_MW{?bL2(7v~JY8r`)OYdvVq?~%uvgv>1G5-JSgm2|Uik_o4y%yH~JGBW7R&rK# zaW2hhT{Enc$IuM?=&i5l7dYYn^3TAfZ{U;^oH!@CSA2QASNUXtdfF>#eeWgVfq?7b zjIdu1RQS0`#6#iJ1MqJj{CS8oq{}%YI(Yn`+cQ9Hbb#3CAh;W>2ow+221*8-0%e>r zJ)85VD_fUQJqVv8N?#W#8oTZ(vh>xH4P@v>yWe)##9)v|dM7cTB#9MN0b!B;^n zx;4gk-kKP3uUJC(`eD~FzINs{9O}Z)(uY6(GCTjXAhs7=THKU#y`aOW_q#DN1v{;e zvoBq+KJs_iX%4^S;q!>;1gSr8M!NN5v@vaOnFc|7~Bj>;>XOZQlY`=*kUGJrI^X5pQK(d3p|w z=hzHwUBt!Mi)nM;;fjD=Sas4nfJN;qc@f~A$M@RBN6-y7OHQW_Wod(YBZmZQqlv>h zUkn;3KZCefnt3>PD{H1(m+{W_Kwsqj_m;wcF5+$uM+%{#-Fkj@y>}kjAoELu7~U}X1uq?ekB-1Y$h8B=wLWCpA!OQ7WZJQQ_@*De>4$In;Tz5a zFXue)*_;Pn$$8*Gat><5tD-5;afxK@ zevf8}zk*-pacR)8c;`1`WuEDI(XGuI^K_Fve=XVb1M|0dw#J}zmLnGr`JXOc{94f> zz758U^NSW~?pybg=jXvqFnQhUFSGIxGFbM2V#=cX4L|*UIQ~WFHvG#2W+TR@`nJHB zb+c@C#gyo$z-w#pLA8}_NA(F#x-H^sT00RZ4NK=RKFxnbhXDid23%4XMB6H}HfRNU zAdDV}p$FQL{a!w6MNVFN96PRSBsU#fE{T3S8u<$Su|80rii1T4tHZ5d_^kP#TR(V7 zxb;)$1L=YRu78%ZDt40Fknaj&U-k2u($DM$+`b1R&Z8T=AveihmOS4}0j>-{kqWS`@ebW5$pDw|*Zo?=YVmkeQRnGYn9TtWWP`L`10FJwWUQCL9ft~qyf!(m}fq!Z`;>Y)IJoST|xokOv?P9!z4h09zX1mZHco?@W z`Tn9e1s%55)zF)_hp{#%uHVc07WRxP z$^5C)q4f}S7)n1iJb$jwYh%xPOUjjh{EkdYHA|}xlOMMe{hP@glRX_hGUY+wx36{K zXG*18r7st+_GH{A;PKW*tr;@=qbsL8*z#46X7JIK)&6y6ZyeyZmiy}nJ=Rgen*Ml+a6?|O5q^p;oRkTM;BcQz-q4b+^y+`cJ;*>TH+63Pw2N}ai{P=HgP7XlyOegJ>WjduQ&`_=1MFyDVUO%(}`R~n{|1_IFa~jFVa0TW&jG;dxQ_+Pp^OSGy z!C*yQz39#IGO$&}+q~=LIUMwSLF72C(^y1T@Jcq$P_w}O@l~n`-RKryqFS&k-6iKA zKHpU~y7OHWT?cM}6Cd6cZkQML!^AdfBktarH>K7SiaSh(J8GXbK}k;gUswBz)63R`jylO}YSlGpW(&5t>fGtN>O7UGU2j~OyX)K^7%vRd z1NhAPlWF6y+R&VKK8kKcepgt;G{=_>vCUtsp_D{d`gPnr(9*Mo&< zMQ~^S7T=E(OVqf^r{d4sjz5j>L_Yqb^RRbrImF)hOs0e1qW2E+#Fe|HHo?d2Y+Hgs zw!J#{{)aT)5gPg4^ZKu6>fF?~^cx!9um5V}D&Q|W*5?^-UkhUt?fdd%LEBxlr*#Ip zP_Zd^x>E0B*N|U$%Lmk^?gQJ`y?D+=-_|va(|7~^%AIo-&h#5lKcW-yUL*SksB7dR zo6*JOsng#A$uMdZl{aoek8Uof57yb&-n^?Fn)dMQ8nXQe2I@z1s`+Z>XT(5vscmG} zed4`N_BSDm0*zc7&riB-q3%-)cLn$1iBRSxqyKFCxpN4{IBtlPdlH0sp_^ zwY#!%h&jfx<7G~zBkjMoSYx66QF3XbcbU7+l8FV`GtXR`g@f#TN7{UKb+%32*K^rt zMrdNslX^z?>)e!s`kl-B$iiKWIe{E(X76nl&a==J%Hd1sJ@iDg_}GU@OfY9prw_MR z-cXy~`pEXMq5~FMpGIrS>3hWm^}P0l3f9Ue1UFsT_pJUFuu`rId|Kf1>ZUq(#*gip zVIA<@!^xkj#9ARMV`TpXTAELPPu>BpM1vR`sgYNMM${!Ix1{)fS76rS!9Hw9oXA?bcap979g%{&gJAT~G z>Qs$qv!H9;h;P}8cXG|v`QW-+af7VAEnI{5vG&Q;qH}2FUDiZc(9{V0sfp12e`+H9 zuGK^s%AUVl9=;2i5?77hp&iiKT4bNj2^+mX9=Q!oo#dIk^G#lPvf}N|qJkI=ixTlRhyRk|L@MqI@!jObL}g%?;HPM%*n3Yg|BG?zg17a zQT|wdYj3}D2n!lEl?C}-7RpqaoyIlSu&HD|y6-4)o@v2e$z}4}@19++Jt|%<^pMW1 zO2nGTC8!v&dm1(sFUY<#{5H)FisxQR!>1cIdzl=+Dk;o0~)~KMs5gIBT6cqxhMA zsN6@vyyYgra&S1-#=5nK_j=HU$|Go(EYGg{e0ecy{pdgIPUczBgs(%d9;9u}DR(~L zuE+cp#4EM7)A%IMY@S~31op{B!@F>;T0-s9nH%{MswV{QGU2nxwZ=AJp1bgIRRv!p z$LS5`x{?q68{Mn!61@+8SbecqAs`;`Yf@x7&vvD}yTz+taeOoLYmQupKOvtw^!P)9 z*Ks}uvR(HzKA!&od-9qNSMVHrYXt{g7tFN31AD0xy}FBcOK7V{>u+eLTd)+JGlv58 zt#N2vHV4L|aX^C+**m(fzM+pz&_}Jw=PtKPHm&-S9Sco#Yi}{@$vwbPdlNf>Z<4*o zsYcp}okc8ip_|1a8^w{cr##q}qVEb9ZhJ)Wm7)vvjN0P)ALuuG75+ruZP-phU?3QW zXVBLj#%?5)oOqN9gMaeI22%BaGt+{o!*tir?lKu>$yrm;rqfD<~sYP)@91HnD_Qg*pKB}$KK?GN+zN<|G$;yQTlRZ@3179(Q>OOKg-Ee$jC2KdG4^k<(g5RPO zVEY}di_+8Avu>i@2>HabsF_i=^(i;~R(Jtgoeg~tanIiyc1(MR6qiCCr4P`TVli6x zjE_6or~msHM}RojGRvWSDQ{WuA=cmMt!#XOT2_xVI$*6>!yDv+pv$;sJfdsfo|{&~ zJM);rm{w`bZH$*3%`*AfH!@!Os#71fmvDP#`+D#d?DAp{>?gZgbRe1l)<4(#oY650 zIka|3jpKMJpJ)3d*M-s1r`tM*pU?;}09{#kF9o^<|vHMzY2VJ^Rb&fm7my zbCSx>=;NIp=95?5XY#iMKge>8i|^a@?Y;j$f9PMU)wd^Wf9}zl?LE5J-a!X>KB-!2 zZ-bW@>$2D-@=1;CQ6OJ7r8ao>Mqgi<4?@oluUmL0n-3GpROaZQ|9QFI<=R>L_Q&x_ z{q|(bZd)^sZJYW7@6j{zZ^UP^&fVeFA|$t4{+4?2wDun7;$e)RYo7Ei{u)QVZl(08 z*Wcfrt9HHLKmWFVGd~*}>$%bh=XG)Jj_83J(%yXI7vKCu)yxd}7cQxOSqJs3o8$F8 z*oB`MKU?ew{oIx5i=WO0UVLsmvrm1R3^0de-BiUpMLZp$Zeoug0t*s$jB z)ca~Prww~Y`^&72%s1z#4cT)(PH)VP4P3O38Xt7wjqS1P^v;;ESv%RWsBK%1FfZtF zlJT7P3$-;eo_h?hv1{X&eW)-G>JBZv341mB>Z=ot0Ul5y@H~KlPTkoO;yw!(>fr{*8XS<_8x1 zvviPy-|SfGy|FxEv^jPx6ZF~O_4$uqRzF^!RjE_xv%%~0A8pK&_NiDRYkI*m41Q%- zY2Kmq$CqdO(^?`+W7&C*8h5sh$Elq=x{VLNO&gy61V8wB*M!n{uJfNCg)jOV``iyE zc+TT{_*`>9_XUk7^Wv5hD!dN%di#Zhe`J2=x`o(+FC&+C+W&~?lyOw3{g!9o2bW33 z&nLckIrja{PPCV7El)S=xyaNjY(J!88OoQ6zFNl_d^=a`U9(rINufP4IXR656UDPV z90v_v!H6p`d6xRl%WBJNm8Cv4f`8`MFBECJ#FrG4e%$4h~6QrkAdye(XQm$!?6UNHr`%pV`nVa&V z<^N}mQv*yBx;~!%yybOoJ`>&d`@58J3O+Ux^u1*?!O!r!wy_P5A(SMH4Q+sNw zgSkx9R_zO!2Tx{fb9X2f&l-pQ$)Zo@HC?hzwM9ee&wK?EBn|{^?aY+L7vP_p!G0!# zM-A}ExH}$o%i_yetIlxCxnDxAVL7&|>%hiMoFlVaM!~SJ2UXs0^-PC~VI?tVU|M~8{u>yOxq-&Nd;D2ed zz!h=-gI9U_ZMctk-id8@&~A)&6SPZv#k@Z)d73+o_um_z&3M^AkmMX~{+D)fem3{5 zUF9JA@?-KGEcr0W=`-4b?zFCjk1oYd-?I+6!``l9!#q2S>+@PK{j9++ayn;8#dvQ9J`Z=4 zelrR74QNdhXup|na+r|qM&skz`@%1@?d3du9zVp6jOA`cFM+O`u(xWFBa*@K@$#LY|*ui_O4O?$Gr&M;8_Jb=|fAjf{z1hVJw6@k7BFS9Yx9!*~en381 z;qh(SDmj(5hUFk~A}fo1**jrAvS48~_lbeT*FPN27Nz1`g%{~o1E1EU(N5cIqmP~x%IEHHsx9T$ev!P zWqr;&@TP3^UFh9z?4&)|3O(W{uclz2A(-^+BFXm1JyRZ3Y$TMv2U=L%`_*SHJZ?_+H~0w*J()JJN3X);EynYsiVgF4%e>b&a;L z4qbzP^-5Q{^#V5!nX;g&j9iIh?Bfr*(yFj4t6ECiWo~K5H1dC@p-+G2X3=&f?au4C z68$la{mVatey@$Mk#A=!b0L6gOne}0GVOE&tp;e>e$BR*4Ny}Xm%_rN}@ zrP?=<-T&a}3*s5$?12cL?dtSi6?vN4Gf{Poo2K`w&UUtMBA9m5ZO_Cj)R=hGT~M{i z)qpGVgaY+jzYp#gxq7wVv4K3WYpCULwkuDh-K+$*T;fqTkC;kXwqNbLNZbIOeUT5c zIq@YvtN0-Mgd^x=ExOUpFu?|J9l!v5?$!RA=<*+hTfTt2-I;y~I2DIdU-s=G!P?cC zJ$-FF91M`BaZY@d;Mykna3*ly7>`hw%5A+9TV^J0v5z#A`a-MV?ESv{E&bm5uxxkX zvg3CBR~<3%fUnNWkqj!IjQknznS{L~9EDPsE!XpmwMO#9zyD)hQ_f8&b6b~w>z3pN z51*XiAH*&^p!Y;?l9l}57fO9|s-CC6+&11fQ5z>Yz6-qZm8G(6Yi!vu<*t_)!|P+l z^js))&sW~=y*P7-Z^^vbx`q8uE3Tz31or8*@oc>#^KCIF?0ewXg-nJ9SaTPX>*sEt zN&W!ymyb7;y3fOn{OYMW*)`<}ewA_iJ7>5*^R>`_J#?WOCh}bu-wv$R4}E0)HKX+3 zxNTk=C#TQW#S_h;z@LMy(fv6XzPvTOO2-Ga_0dw$u0pGGUJUARR2k2qc z`tF_kJpTIQ8utV~kJ~2qd3@dI<(>IFK6bA99r1Z|9oB#5Ioj9Si;bPL*30p8tQzri zB%ai}hV#s;2lX3W(HX=1leK~bHa%yT;PHp)U zjmeWqTOQGGbXlV9;`kQRW8WLA$F^+KGdX(fO8?zLyM8BaHPLsf=4JiQ+AET6=&+#o z>@CmhUfV%(DtI3G?EAR3EYx-Rr^fMd9heZ`Dfzf=xk>N(xw982mUJ>cE~71UOU|=p ztUX6!AXkjUT4K|wUl+qpis%faKxE0e#Qm@>h_hCr@0t^PUBMCXW%?AHp)Y5nD=X1) zLGlJ_&~tU<4b-DAm!j($uTsAwzOh*o=(oEyC)=;LGyMecD+{HrHyjAIz^*ymYP#_@ zblY0#2*G;ePta{;zHV!U9#Xz;Q(ex~QqwIw+o`=x*hAZ~m6Kc32QK=Yo<|3|7L$Kn59`0+CYWJkc>dgz zfLV;(ZM7jgxl#2PQoD@TY1@2gl6kkLq(f&eS05hDZN5&w+u#eIMmG7^pVD>F%UGJj zConCGp9yL2G11=UscPetWsT|nE%(^F)%{wV<}Z!&ZuHK2<`U>zk#c9W#a+VlljRdr zzW3h6mmk~E=;w<+Z@uPl{>|=;cm@9FfcFk<$Fvu%s@BEy&K+~$OTRz0$vd&u^9HmZ(AwO3 z&})x2BnM)^ed+m|-5HU>{4mc?Q+xWY`Nn!(q8iy7>G{Lbs*8ZbH(aA?IVsL!u@css zHqL|OpnZ$H@I!;z8@`r2wGp|vd_*o9ez&u?g7`Ll#rP(NGca8~*X-PCcBJ;jn&i)) z@swbVE>&LDE~6#rNHpZFL&m2g!A`cl;3T-#?g&iN7zJ~|dmC~KJ}F?HYgJE%nj3dR zH--bQh0ni5cqCtDH-48c{N6ie*YJOfFAE~NFSteWbKu4ANbE>ygqKv!wui^n~?{WFgP;bPH-(=AZKMd? z8c!E`_7Hs4(5l+PLqXw(ys}CUFK1s!6TmPPM1MybR=D}0)U{@>@XU7a49B5obM8H< z=f#`cSAE-Iei01Rx8%NHhMXpr|31?>w;+E-6ZG>leG6v7X%=pR4Q;ZgjU2Ile5JAv zpkHkHdAhE%8e@03HLVwY-u#m7;9arbP`Y~9-b0QaP}}N@em2QpqVWk&^i$%^_pDlt z-*i*vYZdx!aLPQhTEBzQT=~$rPS-pc{PgAe?ZIR4Isctc>e`8YOg9@yxEU+!*zaPn z)!0Irr{2>0eE;1021Uw?+{C6<&b?r*atJwo6d8VO0Kd@yexred$ol}YzGC!w7gp0u zekJGsXLYS>Ha6Ggcwamir%mxW`@%i>SWMpaj=Vbgyt$XqdE&AKC3{;6xEAvGDtg(EtY(1K3%J3R zZMgSa=9@Z3o|VWOTh@N-x)+4zGoMEhpf`*jT~bzN}a zd9wjxFS|9;Px2dw(x+|HyrXxPNv^5Z&st!`*;D)7wuNpv*Va(uaJG06IW~h@5=(u3 zS&UvhfWG`q^)0Gllvk>;M7NWBxg&7fiaXJl%z5seS{LL^Gktl!uPKV|k+TR-Y+D3lt_w<=tP~C&<@yA=UV`z^jN6L;j@(wa^7xxtNg`ZOoGM-q~ z9Qk8XE8CmcBLwYxa;hMu=Vjj@ryfM6-)HS)($h3It}W0!a>u3dK-W2XF127p4llPg zlW>pyTP7I8-^jy)+ei8tnx?Tc#sYuLm+2nQCq~*?=F6Z|U-Zt`m_xeUlWqTOxMeMz zV=w)g$w=WllzCD^gEsA*g7IbThkk6&$QPE0KcwGY+fN!SMqrwi%^$i)&!}O^1hWyr zugQSSzwg#PPi{W(h<=AMPgtA4vR!f$nRy}dvPbe7*{L&uI_2vbtR1Pjhb-NvG33@G z&Bj%+$x7PKcFRpi|v9b*nC(wG(_GWzS8P!Rd zu;#4EI>p<~_RA~dTej^|&0FL2bZ>eu^vC-117fK*#MhliG)+X$ZI|52SGU?rKk zB^U=so}D5a%Iqh_5mj@Pwb63DlZ7X9R^4k)X7a3P1DPqBK&QC_t#e+mGcZIKqKU2C z7cEdj=uN@TmzV9L3!YD&C@UX$Dz`ts-R9OgM@Hmim)9n8F3GsUBREKRBSRwe6Rz^* zp!kUA-jtn-oZIBfIm3~j(>O)@null4r>@j>_&TAs`EHDEmYm7V9??hX`=pQP>uz{w zt!K9`ouhFJhg`4oXzmZL);0d`&v`E3U#pBhrgLu6jlTTy>DA;B^!f)M&~u)x7dC;&h4$5kIQ4v*~7V&cIpVn7EMgYw#^KBnq9oe}=mr|G3^; zB_9N_p_0T-;)Wa0#gF>&BF?pn;{zT=nhEgSO=w0CC`^j(Ft^WqwREgma*{3yc7`-8z;hX4= zc65j0Go9#;khc}u>kiE21;08ujO~aU?3+ZA4kWtu7U2mq5D1XL1#GX%I~TTd{AqnyD9>u z@~gM84!R%NLL1F(afAypmU))3r^EOv8#`s)vr@PbQ=Q-Cs!@ZaLCFCs?be%`;6Xy(Z|AXLtzvKw(tQb0vIE&d43231c zT2OrEguH+tV}$0{gg=69$9XfMRJua+B>D%2D?PgrcIBR)3#ESZo}Bxa#?L~oE_cfw zxW--o0I_r8?2WNv=<#O5?W!a&#m;i*$3|r4+IX#N?jo+9OuLnu2lvCwMRS@)oV1wp z9SY#r4|ZqZ&sXid)(cdNOMPQw2STY-PXFmAkWs71RjCY~h2Cz(pBw^y^l$mV-oDAJ zeV?Y{n?3Pa_%y#UGaO$@eHdgovdi|O#^<0f*=q{SEiVupC$%O4&%12h`x3dn;2ND@ z5-f1bxCiY9lrOhN@%6ben=d$i>t(?w2gi}$GLCx;$F<;?@f(hr1Gw#k$CL2*E_l5A zDtO$lTPfRLxi;X}gJbk2YGR}YpVT;gJU1GADjuQ*#x?%b8=nOyxZ`N-DI-(TPG=tO&6W^ zIlVigi*7ng|B*wdqJLJNn*M42iu!nW`sdF*J@Jn8&(!PG|Hzy-&C!3;&6&US^x7!> z^G}!SzWi;be^k>hNA5_@NX87JFIYpGjM5p@T2pE-ylQxBEsPJP2KiPN-EOg!V=H#D zHdu&Xe5d>?fgxKPtoHpYXJ`E@@~H%#Wvn@CgDaKC?3AZu|a{ zz2;B3#P_G9hHCYWp7HPBeLnrm7Snkc*j%NZ=&Y^y?YF+{%FL&7`H#$}vXwE)_SV`U z8sl6g=E+(NU-(aK&pdjGI^$NmOfjBL)v?OTG08UNybsH>ozh7ztCOaiU%iF#?+BP( zXyd@nd$;|Zzg=s-%y#Bm8r^PtAS};P{V}(4`Hi_(5I$QuL2i%b*`%iYiD1-PZE=$` zG-t+BCKwSPEoJ_k-TLn4@A>D+%io@^j6Ng(e0p{?Ex$*)oc;amfk+R3}vunSVB(5xvu%@e1EYZOYQ8p@PmAJgo zUsbiyUmv!KOFRaicboj%uNX4EZ{p8>erCY4$)Np7?XeXvxidDt3SVy*U*{K$;_K<7 z__}^HZt4uiIx9zKJ#!Yf$Ja9$H#IAKzO^%@Tk_fa{G@PxY_(vEPW1UYZ>?xwbBXTm z58Hemfquz5BY(}~>#2{!*WW?zjQC1#WxV2VpY!%S{B^q3Wj|T-(WA! zrtg)CW=zi5SbpkHk})QDA+^)_tciw!Ma_D|cIe>qUd-_e^y6M>b{M`zc|7w5%?$)Vq2@Msq71z*ia z^N(z}qLg_}<61#vLtbfQ6F%gmx8@ftLa8C+DZN9#rS{Gv#Q#RYgEI(xco>d=y^ZB> zX4h;;f8Tr_(i_4N{_6v3OZ+|z|0sR8{jxN|m<Y8}Ld;jl$q5rgZ$b07>Or{7fK1@oyaRf|$2_IFB?u^dI$ZLsmZk6@d`*pS9$*I5Z z((h2_?+sU;oca>4fQM`22d={QMqb5j-68(RPDN%VVtMh`6X?lMs`Vy4Yd)V;z{U^! zv_20@=b`UnYGWkN>jvWuZ|n~r*Sq8FyL{c-1KB!9_i1lb-ds*w9lGzg^=62!+njlS?7R&ZLz$GuHc1<%BPMENk};P}GUmF8`)m23#$&pEDAT0h z6ZP$nf1#dXecJsF$1(~0S}uG0{?m`>nMvjon5>OS#ylC$CxPF3qdO0No{wR2_@xa0 zA(2ty4}ZX6KZ7aQ_KYVV;~Secj?YZKv}4)$c}`7> ztY3ya@E&l`EnA?!X>wm_4m=Hxf~h{*qvo&aE@b`I$@(n`OywKghus+)ILKU(>w`^J zt9%di+QYp1Y+a|C`Pk7qr-?a)t9v-psMr0){Daie!S@(lIlI0($$DbZPfBA0u5A6| zUfz`I-=LoYEk)Gi;v7KjNhKd_ZD+tWKLE`6tPiFCXeuONzMbetj$(7;55<;B2HaoZ zXQQ6E%d7cDW*77CYeB}Nx3wR}^Cd|SF&FIABygE9_K1AAP;;OAHQ-D;T`OHtjD4A1 z?8zL41@8~TKyV?3DHsSok>l0!kFJ%U^!_Hd-eAGmIl$sM(FWhonyn5E2o71ew4_}* z@FkiFE2bz|XkSk#{fF=K=1p*jyscvU_?e5T1z`VT&*aiCL)No5imKi^61 z!Sd*=shTXJeT-|Z==kXo<`OcST5ZPS8nT1_9uJt?3k_1 zS{GRS%+{YWNT`>g`p9L(yb74(q&en-XDIcv#k{8R-bXI^{bs{71!l_!AwD37yo`MD za`MQ71&=+TWvqtcxl@?8*3)szjRHPo?~tX zL;M++sxRI*c|Ed!>HTl$Ir%IFBiUl|$@0AAriaqYU)KHjMaTjC;K;R;*}tT7GTafy zPp+`exYGWmMskKr$gwUnI$*x!Sd-q$`RwNa&g zOuTRVm}tZHG0}!%T55w>*9CgNYGglC<4NvkqE@umzxtkhKhwrKIN5!kU4EX~ zk?^14OLPqGo>ZHOR?d=0v8E;$oY=g{rIEc(%XaIT$@e-fvv)jOx#UJYkL_QoK0F%v zv%|V(_cLF3NWXP&!)xECztU}Xk3CnnzQB7!Z&`oP_C6aU+xC%T9mbP^Y)y_oGa7?e zM%m9NKDSKl<;HpLM&p_I*qR(0Pq8M)pM72Jzq9=*pZ~Gmwfx3YiLYr-($JeX>0Nva zethxIFW3LKlO5Syc3_*cKjoTp^{%(p_;#&+ONRL}FsBa3|9!jejfLd|&zc=M_F3@b z!5tkadX+qdc9-F2V!icOO={oPX+yud$L~w=uAyG`dE^2aRUfQ(w=1dT(F-_g%x1!^I2aV>v{wL3H~SrST%Z**ritdW`Fb z*=ySIQGaj!W!MMgu*Vl-v)tw7KUt0|{&%ho*ItRRuRh}5+k62uYqcC=)N+Uu=d8wd zQw@jJ$YJ7?!}e`XerV4AdeuEPKb`j1cVpw@({2a$-6vbm0Uglj)$7=}@>o~4UdP5& ziq*@$PB4b}d5q&-)$17PBc69++w8@CNA)^}<$*7U$E^c{oUl4DJBhQPGftuoOnM!# zPcrvttX#TsIPaJ^z&_N$_ulGYi|8geLd?M#g{E#0K0vc{EMvlVuMYr zo$m8v3n|t$e@DM(!}C?4@Zu$AJe}le~(CTmwf%;fC)uZ{jjED72 zf|w4rdC^xI9eCq@kX&^1ewaS;)Q8&l^DezupYl+E(L{Y@$F-F)>s^2CW$#Y*-YLs_ z)+AatRM$urx;n_R$7=E*??=~BDS61|*5YPmWxUnoLEc3lxdQplS)2SeSw4PUp4sq$ z)#b^pm6Da|(Vg@k=UnI(s*WWgWcT{bjQs$I%@xqG13H4hww9|Jl0SxqBxzlLuXFHk&XE;K4Xw&JA7vP&6* zYHzaM^L$sV_bp$_r@40g9+SrBM02&@2owjC=w|AtRPWUO)%3%nm+B2}TI7q+*EOu$ zzXq+~7pPuDy_B=v?D^y|+Bt&gkCyZBSK!BJ*}`WzpAWKz z#{FM#*KhqQbylvY#>%IOrG1ndD@CsI)}Ok0f=3n>$c9Pbkoc9Qm}(O%J7BxJhkDPej}=OLbjqBF)>*ggJ$IXW!Y%*dAby!-6TN_4Jj9ZBa3rqQkFU-a%koO4Oh zP2o~>CTC5DpH?jc=&BZ9ViSAR;@-LPi`SizuBHZtV2^xAA|G}EU(w(pokJ?WYwN;L z*mQH~v*GpV+YN1ZiT3P#o#^I6)VMrUkIazmybSmyB|9XavHKA;#46MEww5$s$#(Z< z48KA(FeMMr1^?OQMckRC39c!JR^!)L)lVz^sW0fT6aSKn&V2Xhq9<>memiYv$M&~F zuIO)Hf_A2hcK$DuN#-k)KV;Kg;qZ0Jb#OHrYZyL?xI@qmKG5xMUMO{7xzPz@ADPQQ zspgiQ4?4b*|9O1ZY^9Iy(BF6HPuFw!|3>`0!f6b+E5EWCeoq>|%eP5>xa^p4MCUd5 ze*7KghuvcPS(46)&y`Pw|H}509~;MyZ*?bmrr|VV5%Bcfm~5TQgKC2{WQoqTCWbLv zIRlDgB+(n$b|Ramtc~TwGrFN?lNF2u{6wJhq}FGX+3?Wvu%Acs)EqT$bd?jG1(}EB zll0b-?FEalZ*|Vy@EBq_Yqo3+&D^=5t7d5&(KhTg+SOQ0N6}ZX!asA4abr85(p=SM zL!@9)c5Pzq)27bx4!H4vC{b z7z^uJ?+w`+CLU-f_AY1x&v;#VoH3ohPomv{{qqEl(o!f zdL(~}YN2+kA80)(e!2?3UwgpT>g|#v*=KU^GY0eLb;2XSDnY+xK?gjV++PH_23!Kc z-m(3E$n>t{uk6jn6@CnkcNl}MMd`!V@)|?74}30OALG^G+G&3IV8=zKzb#)Rw~ZLO zk2B4W(;fA-Hct55iM0$j24y?o@2nrLWjORotJ-0#qkNw6i#M`AhUdWPNPRmGKBLCo z>z!u@orS|2&^5@x4rC*8tx{u?ZLE2OhFQn$3Cw ztf8F~YmRMX-JV;o>x4OsQ**4*Y!+`0FWqyBbIA5L-W+mjj(s8Jw>yenPN^PMZ=i%2 zV%hjLsc!g++GkxoNlmJYKc{CVuSr#FazQ*9N?$xj&l8`FwOzb15}U#A?_pcVj86U9 ziWe8^8Qt@24E9=bA(XDQSQI&eUQMc@Utg~0vi6o|=L8n`&t9f`Z3oBJq^jMm>l5Vd zzchK?e$6lR?y2q}8>c4K?1}O`Vt!4kZm%X)GN&e0JvOXYlM0+kj%MpDonl=o>Mo8O zr%Y;IeqE{_U`S5j^nlG5oUqg6aBVUbf7*23@p2J|jxE&vk(yN(nLL`jX4OUZj>RhdI25^! zebR-Y)DojJ!IfBYiQsAWUf|?veVtghYWX7?A8T`~?_}}Nq2FxQHOYZnUzY4WuucCx z8eP<--)&uEYgk=m<9c`dEfyL5pK?4B9TCdZ9@Mx-@9FdEUy8?Ad(7kWy8ru9|GP5R zK9_BdUGDF3Jk>btB5(fBU}v22*$4|ge>pM&nmze_juwagx0L2R5{JFISN|2C3Jv|6 z!32FVX+B3zOg86igc^&VO5cYv|IfG8rs^8}Z%_{=Pqs@p>pbXTI4(mDeRDRJL2a-r zvDso(Od3DPxbcO};bv68YFp1IiU;@V`s%7x@^Q^+R94_c6rr0uYp@!Exs0TCjuZPv%Wc6TT)PhMiX&>if z*0&$4+XGK@$CxMjpv12QQ{&f27#@>+CE^Xyzh5JvMlkd09(?FR_3hV!8Hov|9&6Qg zub-h3zZOjK$a^0=uKVb@H~l&;{{Euaa$O(2hvfrb)-|v8OLU=r`|p%V4`<;B481yA zLqB}pp0V05)eq~pC)Z*D{T_|)12^efDAVzzzD=)ZHrV@|YxzU(UMBfltnqSHC_zgwg1a%yu80w{VSe% z(luUE|Fll;2=9_d|M@juzMPeR)*tduwP1b^YP`JvGQnw7jhCSxm~29R=G1t3|5UN&yWR{ky5c=-pTImI-Cr&iwym;%M$!ok^Xf*on)_j%T{7-f5qZ%(mPmh`Z zrQZCl#>*cWEstIgZZw#%=CizkW4*s?HC{HZ#P86E-(i*V=J1{W7uR^%W44v(AAfvy zKYeVZ#>@S4jCQ^I{{=N(zMIqks2VTV{O;Cx`K{u(N4kP|dJG} zf9chD`M9UoM%8$UT&L&JKNS|Mto7^Xzee0^y!!cH^wzl0^V`?YUyYwT-|FY*S+4L6 z&fT>e8;|_fu{HF;$#128zWgH~`HapktCf3OqRX$JpW6L(%~NarZ2kNg_ae#J$5>CO zetz@Zo2Z}vhU!h_*I_?XKVS7~PN<(>Gpeq|r1kR)Kc_zB*OlJ_KkC@}`AMrG@i=*5 zH_4Cw(CvDD^xA*lW?fSqebvw>Uu>lI@7vYSXAY`o!Fq92?O&^(-!wt}e6F{iZ*^e2 zniU5}*N2XseEs~q@#^OI_T&rdyU^g_G031?d8o@)Jk(Q@s}ZZCLJP5o1> zpRf8n)X)ExS3f@%w>tTaj3p*`$j3GUla%>iIb-C*qEGQvLu<=iux7cX*OFg>9@w(JS<31i6P+~XV7)?LJUga_t0l*NZ%sgR`pbMcYF{7aU+KScR+8+g4sLRJ4XbMwMQisZqdFZ+R!;1!Hy#Gj>(0d`!(`fRJYZ+06ECY7fG}ri`heew7^jMcASMdzI z#W*ie-cB;=iV}>~AG7vFpO8zVd*lTcF(=B8xU&V)iB0ihBTo1qYp8)%{@jA|RDRvD>@RfLjJ!ZhQO6TbC?VTgd?~ysr)?BK} zIV-`Ri}G$Pw#+AmN!g5AVfPoE{t|sVixG z&3<5VC(ha3NPoaR$$$2P=6_Q$Y2_r+ukFRmg_~$r{2*EgrOQ2gl>g*D+WCl@JG5NP zyvWa;@x0%*;WQUtW7|}FYIh{JgS}4qu6#Utpxu<(%|5I5prSvaPx#V|EoGT>g*rt-BXRZ+uRw` zSX$5VC2bk0C3MbZ>cf+}UJc4|YaH}?xxk_W$sXnI$-fECa%&t+L9Tc;4$da8?;cmK zcEdB+kNFMPV7g)GyNA`^lHJ7%T5f@7=1?2(|D*2hx1sGCdLnkavlwGO?};}SCk{dNK$+IFv22U))wdCT3v$1%)# zSm+Pqa?XswjWTx<{UZl`-xzFt z?nl8n6nDE)b@AtX?qZyEC|4Vi2c5^d6gcCsu=X(cYy>#}HSF2B`1>4f+W!(g$aqcg zq=MncC%2@qU?qY9bq?Kcle#p!8J1~bW_zQ9VfORFGZ;qmFNU`!a z8Okr?B9{~6x+M0)aZf`3JbuO({KUWbc-9nkQJ3mKeCl7=i^-{wCwngH8RO%$)DbE`%{WbeV4WfMFoP1zD7kb5W-iKv>{`|(B&2;MNata+7*d0{@3gQOA8h4Zrdw#`S&v&{J{G3S|h$DHTKt@*GInFGeL z%fIpQabOzXod~Amzx&jyAK+c_Z7*_M;q!^!wbSAP(4@}cjP;;~N@?OQ=xXFXhNZSj ziFF?|X{CIN%CA3}kmU=JBgPy=gLfwLzGTg3xaSb>TK65JzK9DoLOdp)<)f}c4`D-| zC4PaYbv{4pyMsp{gHY$AzL0ejLR;$R)ZT@&VT+O5hO<@F>3ZMcHv(In6M2CBkY2p8 zD~0b8H9jPE|C-=F*pB*nA7mnIt zJ3ehZ`2+Sf5soH!SJphyyNK-{01S%=FDozFJ}(>2UjuGSP(w%IKJZ-NJ0G=l3PA_3 zlW=D%Wi|1=7kFwFJe2~59^iXAaIMbZ9lxS?uli%IVNDXka(7{v3B6uMS8Tx-f@#%XIyWsKTXG6 zFYC|LG1m(X;JMttlhjGqmnGoEoPL4VKa}&?1oNH{^IH4A(bbg_RvpG`m)U!Y#}-zW8Zx?zL!oYKC4|1Mws zDV(jv8BofTv2t8@IjYaNLdIPx=V-A{2L5lyx%|ShV&!XH3|V%ADhSlw1v~#`p_%X! zRmj{j=Dkst!QaS5ZYb(z~9hrIQg5SG*~oJQTt2S$-8;a zr(>c2ijoS-$>1Kve$EC@{up(E(I@Ll2hF;`d!$bLI(;XDv4-ykZ~7PH!x#rlA8jqp zT^0r#PKy51`8keUe*@~tyqI36xAUunBirMwqplJB;_?dgbttgdRvQ>=Xhtjq@a~rL zjDQR5axJ?6FWBW-Yd+Kl;di`*^}$DSwL|{JJ8t24w%UnZGFRwn&QsuM?ho<&G0!%8 zqirAJJ8WmQsc%Cq=f%HF)GppxrwTYPYmSIHPicPW(`m;~d24JP?0}Wx!(cpr@K*v8 zz++x!edWEHUhS}ZZg~aoe!||;(ZKvn_;D-?U!K-jCF@dR4|65&bG2~oX^9n*4xxKB zpWq#|adNIgE1((nfmr)dvz_n)`$k-}o3Nq2#@$2(#Nj5yi3u0zP zdtnCUhwNw7g9krOJRH$b!J;#)4I>+4@VlkHNZ&0lr#Sdd*hG77+8=W!@_@ z2QqPnYctl+ikgO)PZw+r&V&6Wc$xiD&H%Pn-yDCUdmd79!xMK|@UGC<0sm~)JK((* zv3{bSrAQ+h}X>Q|w=2RCTXIK7B!K3yjw+po%np_tEElfK~Zphg*V3+T57bR#i$&`=0;lab?w zcx(aNQicUlPrr5@U>%PLeC+xPeH*Y*3d-BCuZMc5Wx#>vC)G*btX+n)`uAN~z}guC zujs4QTqoPIo$G4SxmvS&L+;|8=!bW3ljg}!|hGw17#{D;Ul;8gAc7_VQ-S=>Fr z8VhUF=3Q8cxsbOxho@E$H}N%Q+d<$&a^WDqc)q(YJQsTL7k@(Asfm06S=S8tC-q5y zm#$Ix5TgUgtue%hIw)~k)=Pq%R`ir{0%_+E1`)ISoq3E#oXzbv3`&L+BFq* zQ3X2&cd7TEl>I8mn?b;~kM%=`Fh|52H9cUSkTDb1skx1NPM8zN=R7JEi{DP`N)AIRMY@4KI)Y`!u za8719b#u0{0na?l)#;ykXTbz>FxW5qC|hLlf}>{jUhu*^#!JBq4_UnMvhY0Ha*bRo z);EtdOCBH%(w4LF!Uw`v_>0PErz6-hhe&78Gy7aug7#Y1c>Joj!hgjjHK3LJ;O;d- zV_0{g;F`5&BZFcm5NH0YL1&g;xGKlzzk+lKI~%o7@Q&z;VE@QSz-`f^qtripv_QV^ zQJ_iKhuiw0a~uk_4QSW<1#W-?_aX=r!rBh^)$>q)3b-fE<&GV(sSbaOFb^*UFJ3)b zz%^B>+{J{aU&;Fg{%eN{h&w*#Gk@zZ$DyIa-kQ6Zyh7ZNUjp<0 zjQYQEXPd?myl z)HMB)*k8~$VpBqot!4Z1Xawz!0}jsY^!AeQ8`oyh8)yJ|3h}OBE96d#>f~>MC8Mc) z2ONM#M#hKPOCKW4Fm9p9E#j|T!x`^&Wyjf%e!+LJo@TIPQB~4hgf_xy*%5|hQp%wZDnpUnW zxcv>2<{E|yh$oGgA6>zDaSj$N{Q(=T;t{(eUF=R3O0*=zsFpEw3&Tm zgSnmVZ$F%*`{W#Y#Kz!S(&3JG^N9n}iqQLR_&4O6Jw+Ad8=Z3_;*FY-aklP=%<~JeLS)CS)XhZbl_un#ycSY zmb%P196Sb@Aa*`##eNY!?_<4;sUNL9j+#<0J>{bxuj%9+S$k4`w0k-oI8_2O5FSGEr?k$A=o|6|K5Tvv$Ab+!`ntf@7t&DD3Y>*9Of z$gf1N0oLGIoZ&(Jd(MF{W!*j`S!xIzf{!Jd-hZ+?iy~y{YmE%pLGc zw-+sV!IwU2#uJWUZ0C4uh3peQVSE_AAohPY7-EcWyx%GPk24?qjv98?6(GOf$@K}} z(SAsg@z4KcytTp>DnuXcy3W9W@G0pAyxM2^2WDKa3wCFSK0$p0;1altxk=6R2Vk4l z)IOSs>&;-Cer_g66P<$wH=np))Eh%7{{Rd0MubQ^7a z?vGE!FRAsLF|>pn_^)qqzDYR{KR|g(Kg!~H#5APly2Q}H@80O4Px2kMv)XW;)Tx{U z^*j9{-kDC{$g;tp*L^ZizzTZ2+44`|)3=lNsm}=u%1k>v5Jx))94-gGQsVQ7>n!Fz z3SghuUzt3v^WkEddq!O6!!yn2X>py)-j?Te>2aOQME_h!T<3QmKmNz9oLhKFg4X_j z9zXte!oCVG{e8rbw@oAbY_vax`0+QjtkJYDG3Nho#E-Y1=UT{<3E2HkhE*T;z9G*_{Tr|HH51X2=~h$+NcWL#p*#fWZ$A5>0Lte4N)_&OieS(v4NXBc(tIYv0`d`dgU%yvFS&m(6a^%h`YpboDyxwhDd_)+nr zA}cJo;EwRKVpGKb?X4=jXTiwxXska8qgemyqUlRD91-tX0Ur&tI# zL)pgUcrHWzJ!9$tH+-Z=m8WdJDn*{VLd+_cI7!l)Avyc0v}T;k9NXx>I9f9r2$M`& zi$*l9y|$Y)WzmGEEjp}e?X@tUTlC?1GXo zIhXop=WxGkI(&+vwC@dO9~0!_k47eM6InimHceA#(;sYWR@kFjA8cy^@5QQiz9uoG zwH6(fx1Arh@gwBAqcoHE9V;k)>s+CysiQe(>gz0X6`O@~rC#NH$%me{kxt4Mth>?7ag_Uh)8xyXEOX7h zIQfG7FTf1;$X3pS`pg?2Dbh0JCrb!Vi>EzpkL2M*gJoEdF9>1aS7Fuf>YwpWnb>`*X#{?D zU@7XfSM$u+yKfK%h+QcEJvqM04Koh$3<>Xnfc%|0mZkuu6jkDD<`1{LHv%Lbj zj0KH`I>o%vj%xLraIxyA%|riv$4xl51eiz+4D&;*TYO{}eB;|fh&|jlvWw%g5BS1| zV8^Vpecx}(-F3-g%9ol&Y8QrfU!#E@-lZ<@;Mi$=kGqJ zX)F4p4TLA-EjU*oK6+~Y?&J6J_q6=oDbJVva(|Qg!`{d@h;E&~8ymiYpXGPSlj)4| zJ79eDcLdX%3xnzZmY9wWrs?_6%Lvo)^P!)4M&=*wNvz8oeM@|vRCsoNl%S~Z&XlZ2 z%%leK4dme%h_@Tak2RoMjV|_)cE-R3|Dg+K45s1U+A7fLdeM(~ALk8_zhxlz#(=Ig zpev1LoHY<#iT3Nf5v@;baFz3O^$s}?QAuBC=T*=}&*`%y-sl$vX0EH?3wEf(zAF%C zUk7`jT~*TFt9_O^Z+|0ll(^8jk_SF7YmGi9GLAV;-sqs{DfCf;y3AFz&+9&5OY}0P zj=k=+=w~b66Wh1*3~k9uJk#_pI;c%}BiOCaOkE5N1Ru!#-FXdg5ecr4@dRI|6LccE zQW2dGOMF@TldzBW@t$PZJAHuPqA~2pHyez7sV>4Z6jcQy9fo2(tAN`}zp|#R2s8|v z*ahE`Io&SAqs9i*!N9dQ^Ss}Kh?O;tl_Sq8tlDAUZ-#v@cWis3|MV{Bwl~c9nW5Z} zMPwc%Tx=-X z-LUQYb}WT$S7+LGfCKhOn0MH0;+)FRgPdG($&&>{V@DJvVdD?xkjalYhl?rpU-4641BLVLa;SGPG8GXnot)QXu-ZO3EaK~NSHjy84>#xo8AbkS zr4^$Cs@I=?Nsa&OxX(U6nWs5gt@e$Ek+;_&aqK?u{V~L}n@7uW?^O@ZE|(*2UX6Hm zSPetYj6n7)=!RVX(};~9MO^%EkkimSmwDNR%x~B+R~0<{r`T`*(X@uTYwLZ!2XIcf z9kR2P`A&|*!8Zphc!#mRA5`B%h!|?8?3rJo3KRR~m)}}YcltT*!{&#}uvgj7{2JVu zn7)6GoCcj=@|eir@FrClx&nJq+mK6j4RWrAq>tS29oVCsi9W8*kvyg*v|*kZX^;Dw zY+pC}oUiUZX`fe*_QBi#%~!TC9)q0J`I#MRV_B7&fx0UV@Z}nLe{0=y-}fzo?_|b| z*93R%Q*Ovq)Gb=boCdxN`IG0rX1KyX?UEX{b%n5p^|)GGck@o)=D)+eUNe|$`t#jY z3iwCAoJ;WacVrK`zFKW$j*1)a_&G`{D$M;XDkLheN` zBdnOy8N3tw)3vu21m7I4s0~0a9aDKcV;zJJt9_aJanE_a>&ChAVB3f%*fLsx+{XQ! z7v^Yi?z(Prx9Gb1XOO?q%A7&tkko#T#TI18C(H}xK$!TfJM*0EP5Wm)t8?3s7ol?s z$IFFi75&WjfqS0804{kJLFH~ak9UDX=0Fj5f%(rC;2wBe&Y)4W8M15$w2v5Ov&1h! z%ZOp_LkzPEG0cOAVRj>i*^3xvA7YrtAm95T-v=Szhq3=X0{cY4KFNiBB6VE2ZqgOv zg~x-@k&0mZ;D~vir`^m^ITd_!sGx4{HXqO36K>x4|M_(l=4achJcPDJXcKdXjdNU^ z1sARh{W~Rp7`aN!A0~}Zc6p<%#q5XU3yt<`ddB;brxSeR9nf}#>`$u&Usmy%yd=k9(R&s=*+O|YfJL%u;=W}p91334uZ zUP^7(bB^>OF4MR=gghJMVDN6jAZl@;?|eMp$md#a{7TM0F&{w7vJNC_W6Ms7II*B`tx7Zwww`XVaecf+#0pPP<`Z%fk!20K`ns4#Il?nPsyM3TB z>djMxBV{k$3h@A|%=%WiDM zy@+Alr8pqxS`I=-bVEnT{fW>c&Cna|V}{hTI5yS;{V|{hp@)>*(TROo|KDOym3ynq zhkN}`d}SWNw`3bj8N=ks z<^t@A7b1QQSQFlWah&j{J`7a-vOsLt1RP>VJMM%X1X|*q4JO#5AI5X-qsL8~{tA&l2NH8V5jjF!W9>#$ z0rnFYQr5-t@ZLw+ANmgAv&c?-UWfTkVpBslPwHPpTSCBrH}cm{>MdqpB|Zb07$8gi;U?sbfGAit#t zb#JP1@A32;`tipo)5ba^pE1Ne67F|+Ij>f1EQfw;0$r9OR#q`qZLC48tjfQ8uFqYLI7t}wnX1*el;aGfOgM`Hq|^>*$!V&W;dpd*I9{t2aZR1 z(PbE~a9r25YG&oDI%liAC5QL;ILg~DIiT&J+4`L!H!jBtP4>m%%Ip*!q%7$cXMG86Ydp(ZALGsJYd+YR;0-NnXX&fn9P zL5)1TRY&XJ3= zVRAMCPm1`l)`#|xy<3ZoBVC62F=)Jx z^uN1U^@8pXgYJ)z2fz!x{>L@#3*EnxO84XkOZI^7{h;}fN%LFC3l`mzAINv*ZBZxZ zXp<8*x#ZlJDJQ07iFZ!hZ-)d(kSyGE%hv|-K(anJlW#N}TJ7aQHpA7fnJXxi}^#O1%HO%H@fql1>^rSTT&D(c9n_CALbPj0 z@1%Y59AOUIYoEmn_&f}{qU|_Az0#@Vd5yKC^L47+QpYlJrp{&3G-PV3o!xH7nO#nx z|6@tHh5BswU=1fhuZcWd)b7gQQ?>SDpZMpr~#L3lGn>-W#h*(!l%q=I37U)kyOBSd%q3+9RTaV4IMh=r58l zJuUCAgFkcs)L}A{K5Lo|nwge!oV&{!*RJN;S=&r6Ty6ai!t}at|9fa;W0Nu6U z)LjQJKzDIZz)5&QhtX#Hk)^xJ+oo68c(>FWKku19b`83~!`}Ggmq8zhF3`Ftemu$B zDogID%3^=36#E;pO6NO@tdBQL4%_$~?-O9-q#6rqT2CnpBxG?PxZ7)^?-VaJ~x|&y{7od1>Qq z%r@TbkelLHY^8orvC}7!o6`m}$DDRFZOr?#jVX4cX~SV3vB|bhn_ZOS%+v)rS>#0O z*#z`=WPCc!aVF`}(UhrU9a;P{N8`olSj?l8G86Vw!Z!ddZ-i{p`Rk@{=Z&8;eK=z| zcrgciGoFd*_s6f2$5QDx!gx63xlyV6$Z4iLp}kAkia!Xu4gO&Zd_U6Fk?;y-`GWi4 z3;wS#_;-^MUvM+*U*u`P7essvvZWKU#wWhutZ{t7Ta&){)@s!bo46(Lmv{L(iq-ch zTb?X7UfEqux%{re+VaQYO~eCVeal)~_|qz%{K-6N^5_nH2duhM<>otQQ@@X_pQQh9 zP2B%jftF+KE7%UPNa+u9Bda{Ye3!5tbtvcSpM8~&`X_D9d6UbQk-SWMEOIufbEB4S zn!v7&Zl8RdNI&OC+VRHU^G)9Vx6UcFshqscdxzX1m7~W!pN7Nt=S|`LIizQyW6&w+ zxS<{X9du(ebmXI;<#gZf5zuV&rYiC6nn1_9K*!rn{n#Y>5wx7)+kuYlz8z>;_7+WA zemIquf2+@>B>4FKnp9dGElvC0M82jD%0~a*_$zN)v_u?{mb7kqdkXf%d;Mu+HBS!H z^ZqHc>7BgIXrXy;vVlFtzOo|3`LPh}56&o4s5J%OdaPO<8w<;x;t2L14~`kwd+b1d zN)PJmquvH=mqy4Sku|cvh*}%|xSMA%nX}RUWVLGA5k~$B_c9ICymq1<;+2=w{4w@( z${#UG%RY{Krw|KejNaOJgiUD zOntD0IULxB55R|e2KgHei2Wkh$Q$(~{virya}{C}A76#qYT{J`8w-Xe%H; zMpab!otMsZ6}_C71OjJPFqA0GxD=)n?gaBfK%^cJ}2* zo=7X_@hdEbU zPDjREa|uV%gg4Uis`LZ-0o+u|nXE|5)A9^(AdLf$yO=`{92&xd?bL~T%GoBB|Mq-R zo{@iJ&&wVK_QfufTs`dbGdB-1NsWe)TLGN|StmIakZ;GvdUPJeXpfm&fqVeS$Zl`+ z+XL*A<9nme?dIpEudC9X|Ev~xqqhY3o&3~vIck&a1+9D)If$2|zS$vFg#Yq!(5c$hMd1+eS5^1@{ofzhyCE(t75(Y z_cP0If6ZrA{4Dl$OE8aI9p7^fhQ6wtoCo(zX)7aUx}^#6PW%sH><8Gt`(}&_7*Y

CGQkrUP34ayAm?8mb^Wk`BM+y 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

r)Vu)lUaU6i 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 Fe*nkU^mzaP delta 51242 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 m_connect_arr; + +public: + ~AppConnector(); + + void Connect(int winid, int lastId, int eventType, wxObjectEventFunction func, wxObject* userData = nullptr, wxEvtHandler* eventSink = nullptr); + void Connect(int winid, int eventType, wxObjectEventFunction func, wxObject* userData = nullptr, wxEvtHandler* eventSink = nullptr); + void Connect(int eventType, wxObjectEventFunction func, wxObject* userData = nullptr, wxEvtHandler* eventSink = nullptr); +}; \ No newline at end of file diff --git a/rpcs3/Emu/Cell/DisAsm.h b/rpcs3/Emu/Cell/DisAsm.h index c51b6b5506..5411e38dbe 100644 --- a/rpcs3/Emu/Cell/DisAsm.h +++ b/rpcs3/Emu/Cell/DisAsm.h @@ -76,10 +76,34 @@ protected: return op; } + void DisAsm_V4(const wxString& op, OP_REG v0, OP_REG v1, OP_REG v2, OP_REG v3) + { + Write(wxString::Format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3)); + } + void DisAsm_V3_UIMM(const wxString& op, OP_REG v0, OP_REG v1, OP_REG v2, OP_uIMM uimm) + { + Write(wxString::Format("%s v%d,v%d,v%d,%u #%x", FixOp(op), v0, v1, v2, uimm, uimm)); + } void DisAsm_V3(const wxString& op, OP_REG v0, OP_REG v1, OP_REG v2) { Write(wxString::Format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2)); } + void DisAsm_V2_UIMM(const wxString& op, OP_REG v0, OP_REG v1, OP_uIMM uimm) + { + Write(wxString::Format("%s v%d,v%d,%u #%x", FixOp(op), v0, v1, uimm, uimm)); + } + void DisAsm_V2(const wxString& op, OP_REG v0, OP_REG v1) + { + Write(wxString::Format("%s v%d,v%d", FixOp(op), v0, v1)); + } + void DisAsm_V1_SIMM(const wxString& op, OP_REG v0, OP_sIMM simm) + { + Write(wxString::Format("%s v%d,%d #%x", FixOp(op), v0, simm, simm)); + } + void DisAsm_V1(const wxString& op, OP_REG v0) + { + Write(wxString::Format("%s v%d", FixOp(op), v0)); + } void DisAsm_V1_R2(const wxString& op, OP_REG v0, OP_REG r1, OP_REG r2) { Write(wxString::Format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2)); diff --git a/rpcs3/Emu/Cell/PPCThread.cpp b/rpcs3/Emu/Cell/PPCThread.cpp index 1e9867f83e..316f8e19ba 100644 --- a/rpcs3/Emu/Cell/PPCThread.cpp +++ b/rpcs3/Emu/Cell/PPCThread.cpp @@ -2,8 +2,14 @@ #include "PPCThread.h" #include "Gui/InterpreterDisAsm.h" +PPCThread* GetCurrentPPCThread() +{ + return (PPCThread*)GetCurrentNamedThread(); +} + PPCThread::PPCThread(PPCThreadType type) - : m_type(type) + : ThreadBase(true, "PPCThread") + , m_type(type) , DisAsmFrame(NULL) , m_arg(0) , m_dec(NULL) @@ -79,16 +85,11 @@ void PPCThread::SetId(const u32 id) m_id = id; ID& thread = Emu.GetIdManager().GetIDData(m_id); thread.m_name = GetName(); - - if(Ini.CPUDecoderMode.GetValue() != 1) return; - DisAsmFrame = new InterpreterDisAsmFrame(GetFName(), this); - (*(InterpreterDisAsmFrame*)DisAsmFrame).Show(); } void PPCThread::SetName(const wxString& name) { m_name = name; - if(DisAsmFrame) (*(InterpreterDisAsmFrame*)DisAsmFrame).SetTitle(GetFName()); } void PPCThread::NextBranchPc() @@ -119,6 +120,11 @@ void PPCThread::SetPc(const u64 pc) nPC = PC + 4; } +void PPCThread::SetEntry(const u64 pc) +{ + entry = pc; +} + void PPCThread::SetBranch(const u64 pc) { if(!Memory.IsGoodAddr(pc)) @@ -164,41 +170,95 @@ void PPCThread::Run() m_status = Runned; + SetPc(entry); InitStack(); InitRegs(); DoRun(); Emu.CheckStatus(); + + wxGetApp().SendDbgCommand(DID_START_THREAD, this); + if(DisAsmFrame) (*(InterpreterDisAsmFrame*)DisAsmFrame).DoUpdate(); } void PPCThread::Resume() { if(!IsPaused()) return; + m_status = Runned; DoResume(); Emu.CheckStatus(); + + wxGetApp().SendDbgCommand(DID_RESUME_THREAD, this); + + ThreadBase::Start(); } void PPCThread::Pause() { if(!IsRunned()) return; + m_status = Paused; DoPause(); Emu.CheckStatus(); + + wxGetApp().SendDbgCommand(DID_PAUSE_THREAD, this); + + ThreadBase::Stop(false); } void PPCThread::Stop() { if(IsStopped()) return; + m_status = Stopped; Reset(); DoStop(); Emu.CheckStatus(); + + wxGetApp().SendDbgCommand(DID_STOP_THREAD, this); + + ThreadBase::Stop(); } void PPCThread::Exec() { - if(!IsRunned()) return; + wxGetApp().SendDbgCommand(DID_EXEC_THREAD, this); + ThreadBase::Start(); +} + +void PPCThread::ExecOnce() +{ DoCode(Memory.Read32(m_offset + PC)); NextPc(); -} \ No newline at end of file +} + +void PPCThread::Task() +{ + ConLog.Write("%s enter", PPCThread::GetFName()); + + try + { + while(!Emu.IsStopped() && !TestDestroy()) + { + if(Emu.IsPaused()) + { + Sleep(1); + continue; + } + + DoCode(Memory.Read32(m_offset + PC)); + NextPc(); + } + } + catch(const wxString& e) + { + ConLog.Error("Exception: %s", e); + } + catch(const char* e) + { + ConLog.Error("Exception: %s", e); + } + + ConLog.Write("%s leave", PPCThread::GetFName()); +} diff --git a/rpcs3/Emu/Cell/PPCThread.h b/rpcs3/Emu/Cell/PPCThread.h index c7164f581b..cde0d4c04d 100644 --- a/rpcs3/Emu/Cell/PPCThread.h +++ b/rpcs3/Emu/Cell/PPCThread.h @@ -8,7 +8,7 @@ enum PPCThreadType PPC_THREAD_SPU, }; -class PPCThread// : public StepThread +class PPCThread : public ThreadBase { protected: u32 m_status; @@ -19,7 +19,6 @@ protected: PPCThreadType m_type; u64 m_arg; u64 m_prio; - wxString m_name; bool m_joinable; bool m_joining; Array argv_addr; @@ -70,9 +69,15 @@ public: wxString GetTypeString() const { return PPCThreadTypeToString(m_type); } + virtual wxString GetThreadName() const + { + return GetFName() + wxString::Format("[0x%08llx]", PC); + } + public: bool isBranch; + u64 entry; u64 PC; u64 nPC; u64 cycle; @@ -88,6 +93,7 @@ public: void PrevPc(); void SetBranch(const u64 pc); void SetPc(const u64 pc); + void SetEntry(const u64 entry); void SetError(const u32 error); @@ -118,6 +124,7 @@ public: virtual wxString RegsToString() { return wxEmptyString; } virtual void Exec(); + void ExecOnce(); virtual void AddArgv(const wxString& arg) {} @@ -127,7 +134,11 @@ protected: virtual void DoPause()=0; virtual void DoResume()=0; virtual void DoStop()=0; + + virtual void Task(); private: virtual void DoCode(const s32 code)=0; -}; \ No newline at end of file +}; + +PPCThread* GetCurrentPPCThread(); \ No newline at end of file diff --git a/rpcs3/Emu/Cell/PPCThreadManager.cpp b/rpcs3/Emu/Cell/PPCThreadManager.cpp index fb8371e86f..c14b1588a1 100644 --- a/rpcs3/Emu/Cell/PPCThreadManager.cpp +++ b/rpcs3/Emu/Cell/PPCThreadManager.cpp @@ -4,7 +4,6 @@ #include "SPUThread.h" PPCThreadManager::PPCThreadManager() - : ThreadBase(true, "PPCThreadManager") { } @@ -15,8 +14,6 @@ PPCThreadManager::~PPCThreadManager() void PPCThreadManager::Close() { - if(IsAlive()) Stop(); - while(m_threads.GetCount()) RemoveThread(m_threads[0].GetId()); } @@ -29,6 +26,7 @@ PPCThread& PPCThreadManager::AddThread(bool isPPU) ); m_threads.Add(new_thread); + wxGetApp().SendDbgCommand(DID_CREATE_THREAD, new_thread); return *new_thread; } @@ -39,6 +37,7 @@ void PPCThreadManager::RemoveThread(const u32 id) { if(m_threads[i].GetId() != id) continue; + wxGetApp().SendDbgCommand(DID_REMOVE_THREAD, &m_threads[i]); m_threads[i].Close(); m_threads.RemoveAt(i); @@ -62,19 +61,20 @@ s32 PPCThreadManager::GetThreadNumById(bool isPPU, u32 id) return -1; } -void PPCThreadManager::Exec() +PPCThread* PPCThreadManager::GetThread(u32 id) { - Start(); + for(u32 i=0; i m_ppu_threads; @@ -19,8 +19,8 @@ public: ArrayF& GetThreads() { return m_threads; } s32 GetThreadNumById(bool isPPU, u32 id); + PPCThread* GetThread(u32 id); //IdManager& GetIDs() {return m_threads_id;} void Exec(); - virtual void Task(); }; \ No newline at end of file diff --git a/rpcs3/Emu/Cell/PPUDecoder.h b/rpcs3/Emu/Cell/PPUDecoder.h index 1d759de82e..1b314972cc 100644 --- a/rpcs3/Emu/Cell/PPUDecoder.h +++ b/rpcs3/Emu/Cell/PPUDecoder.h @@ -10,6 +10,12 @@ switch(temp)\ { +#define START_OPCODES_SUB_GROUP(reg) \ + default:\ + temp=##reg##;\ + switch(temp)\ + { + #define END_OPCODES_GROUP(group) \ default:\ m_op.UNK(m_code, opcode, temp);\ @@ -17,6 +23,15 @@ }\ break +#define END_OPCODES_SUB_GROUP() \ + default:\ + m_op.UNK(m_code, opcode, temp);\ + break;\ + } +#define END_OPCODES_ND_GROUP(group) \ + } \ + break; + #define ADD_OPCODE(name, ...) case(##name##):m_op.##name##(__VA_ARGS__); break class PPU_Decoder : public Decoder @@ -36,6 +51,9 @@ class PPU_Decoder : public Decoder //This field is used to specify a special-purpose register for the mtspr and mfspr instructions OP_REG SPR() const { return GetField(11, 20); } + // + OP_REG VS() const { return GetField(6, 10); } + // OP_REG VD() const { return GetField(6, 10); } @@ -45,6 +63,20 @@ class PPU_Decoder : public Decoder // OP_REG VB() const { return GetField(16, 20); } + // + OP_REG VC() const { return GetField(21, 25); } + + // + OP_uIMM VUIMM() const { return GetField(11, 15); } + + // + OP_sIMM VSIMM() const { int i5 = GetField(11, 15); + if(i5 & 0x10) return i5 - 0x20; + return i5; } + + // + OP_uIMM VSH() const { return GetField(22, 25); } + //This field is used to specify a GPR to be used as a destination OP_REG RD() const { return GetField(6, 10); } @@ -174,8 +206,8 @@ class PPU_Decoder : public Decoder //This field is used to specify an FPR as a source OP_REG FRC() const { return GetField(21, 25); } - // - OP_REG FXM() const { return GetField(12, 19); } + //This field mask is used to identify the CR fields that are to be updated by the mtcrf instruction. + OP_REG CRM() const { return GetField(12, 19); } // const s32 SYS() const { return GetField(6, 31); } @@ -211,6 +243,8 @@ class PPU_Decoder : public Decoder //Primary opcode field OP_uIMM OPCD() const { return GetField(0, 5); } + + OP_uIMM STRM() const { return GetField(9, 10); } __forceinline u32 GetField(const u32 p) const { @@ -234,24 +268,178 @@ public: virtual void Decode(const u32 code) { - if(code == 0) - { - m_op.NULL_OP(); - return; - } - m_code = code; - u32 opcode, temp; + using namespace PPU_opcodes; + static u32 opcode, temp; + switch((opcode = OPCD())) { - ADD_OPCODE(TDI, TO(), RA(), simm16()); ADD_OPCODE(TWI, TO(), RA(), simm16()); - START_OPCODES_GROUP(G_04, ((m_code >> 1) & 0x3ff)) - ADD_OPCODE(VXOR, VD(), VA(), VB()); - END_OPCODES_GROUP(G_04); + START_OPCODES_GROUP(G_04, m_code & 0x3f) + ADD_OPCODE(VMADDFP, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMHADDSHS, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMHRADDSHS, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMLADDUHM, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMSUMMBM, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMSUMSHM, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMSUMSHS, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMSUMUBM, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMSUMUHM, VD(), VA(), VB(), VC()); + ADD_OPCODE(VMSUMUHS, VD(), VA(), VB(), VC()); + ADD_OPCODE(VNMSUBFP, VD(), VA(), VB(), VC()); + ADD_OPCODE(VPERM, VD(), VA(), VB(), VC()); + ADD_OPCODE(VSEL, VD(), VA(), VB(), VC()); + ADD_OPCODE(VSLDOI, VD(), VA(), VB(), VSH()); + + START_OPCODES_SUB_GROUP(m_code & 0x7ff); + ADD_OPCODE(MFVSCR, VD()); + ADD_OPCODE(MTVSCR, VB()); + ADD_OPCODE(VADDCUW, VD(), VA(), VB()); + ADD_OPCODE(VADDFP, VD(), VA(), VB()); + ADD_OPCODE(VADDSBS, VD(), VA(), VB()); + ADD_OPCODE(VADDSHS, VD(), VA(), VB()); + ADD_OPCODE(VADDSWS, VD(), VA(), VB()); + ADD_OPCODE(VADDUBM, VD(), VA(), VB()); + ADD_OPCODE(VADDUBS, VD(), VA(), VB()); + ADD_OPCODE(VADDUHM, VD(), VA(), VB()); + ADD_OPCODE(VADDUHS, VD(), VA(), VB()); + ADD_OPCODE(VADDUWM, VD(), VA(), VB()); + ADD_OPCODE(VADDUWS, VD(), VA(), VB()); + ADD_OPCODE(VAND, VD(), VA(), VB()); + ADD_OPCODE(VANDC, VD(), VA(), VB()); + ADD_OPCODE(VAVGSB, VD(), VA(), VB()); + ADD_OPCODE(VAVGSH, VD(), VA(), VB()); + ADD_OPCODE(VAVGSW, VD(), VA(), VB()); + ADD_OPCODE(VAVGUB, VD(), VA(), VB()); + ADD_OPCODE(VAVGUH, VD(), VA(), VB()); + ADD_OPCODE(VAVGUW, VD(), VA(), VB()); + ADD_OPCODE(VCFSX, VD(), VUIMM(), VB()); + ADD_OPCODE(VCFUX, VD(), VUIMM(), VB()); + ADD_OPCODE(VCMPBFP, VD(), VA(), VB()); + ADD_OPCODE(VCMPBFP_, VD(), VA(), VB()); + ADD_OPCODE(VCMPEQFP, VD(), VA(), VB()); + ADD_OPCODE(VCMPEQFP_, VD(), VA(), VB()); + ADD_OPCODE(VCMPEQUB, VD(), VA(), VB()); + ADD_OPCODE(VCMPEQUB_, VD(), VA(), VB()); + ADD_OPCODE(VCMPEQUH, VD(), VA(), VB()); + ADD_OPCODE(VCMPEQUH_, VD(), VA(), VB()); + ADD_OPCODE(VCMPEQUW, VD(), VA(), VB()); + ADD_OPCODE(VCMPEQUW_, VD(), VA(), VB()); + ADD_OPCODE(VCMPGEFP, VD(), VA(), VB()); + ADD_OPCODE(VCMPGEFP_, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTFP, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTFP_, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTSB, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTSB_, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTSH, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTSH_, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTSW, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTSW_, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTUB, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTUB_, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTUH, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTUH_, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTUW, VD(), VA(), VB()); + ADD_OPCODE(VCMPGTUW_, VD(), VA(), VB()); + ADD_OPCODE(VCTSXS, VD(), VUIMM(), VB()); + ADD_OPCODE(VCTUXS, VD(), VUIMM(), VB()); + ADD_OPCODE(VEXPTEFP, VD(), VB()); + ADD_OPCODE(VLOGEFP, VD(), VB()); + ADD_OPCODE(VMAXFP, VD(), VA(), VB()); + ADD_OPCODE(VMAXSB, VD(), VA(), VB()); + ADD_OPCODE(VMAXSH, VD(), VA(), VB()); + ADD_OPCODE(VMAXSW, VD(), VA(), VB()); + ADD_OPCODE(VMAXUB, VD(), VA(), VB()); + ADD_OPCODE(VMAXUH, VD(), VA(), VB()); + ADD_OPCODE(VMAXUW, VD(), VA(), VB()); + ADD_OPCODE(VMINFP, VD(), VA(), VB()); + ADD_OPCODE(VMINSB, VD(), VA(), VB()); + ADD_OPCODE(VMINSH, VD(), VA(), VB()); + ADD_OPCODE(VMINSW, VD(), VA(), VB()); + ADD_OPCODE(VMINUB, VD(), VA(), VB()); + ADD_OPCODE(VMINUH, VD(), VA(), VB()); + ADD_OPCODE(VMINUW, VD(), VA(), VB()); + ADD_OPCODE(VMRGHB, VD(), VA(), VB()); + ADD_OPCODE(VMRGHH, VD(), VA(), VB()); + ADD_OPCODE(VMRGHW, VD(), VA(), VB()); + ADD_OPCODE(VMRGLB, VD(), VA(), VB()); + ADD_OPCODE(VMRGLH, VD(), VA(), VB()); + ADD_OPCODE(VMRGLW, VD(), VA(), VB()); + ADD_OPCODE(VMULESB, VD(), VA(), VB()); + ADD_OPCODE(VMULESH, VD(), VA(), VB()); + ADD_OPCODE(VMULEUB, VD(), VA(), VB()); + ADD_OPCODE(VMULEUH, VD(), VA(), VB()); + ADD_OPCODE(VMULOSB, VD(), VA(), VB()); + ADD_OPCODE(VMULOSH, VD(), VA(), VB()); + ADD_OPCODE(VMULOUB, VD(), VA(), VB()); + ADD_OPCODE(VMULOUH, VD(), VA(), VB()); + ADD_OPCODE(VNOR, VD(), VA(), VB()); + ADD_OPCODE(VOR, VD(), VA(), VB()); + ADD_OPCODE(VPKPX, VD(), VA(), VB()); + ADD_OPCODE(VPKSHSS, VD(), VA(), VB()); + ADD_OPCODE(VPKSHUS, VD(), VA(), VB()); + ADD_OPCODE(VPKSWSS, VD(), VA(), VB()); + ADD_OPCODE(VPKSWUS, VD(), VA(), VB()); + ADD_OPCODE(VPKUHUM, VD(), VA(), VB()); + ADD_OPCODE(VPKUHUS, VD(), VA(), VB()); + ADD_OPCODE(VPKUWUM, VD(), VA(), VB()); + ADD_OPCODE(VPKUWUS, VD(), VA(), VB()); + ADD_OPCODE(VREFP, VD(), VB()); + ADD_OPCODE(VRFIM, VD(), VB()); + ADD_OPCODE(VRFIN, VD(), VB()); + ADD_OPCODE(VRFIP, VD(), VB()); + ADD_OPCODE(VRFIZ, VD(), VB()); + ADD_OPCODE(VRLB, VD(), VA(), VB()); + ADD_OPCODE(VRLH, VD(), VA(), VB()); + ADD_OPCODE(VRLW, VD(), VA(), VB()); + ADD_OPCODE(VRSQRTEFP, VD(), VB()); + ADD_OPCODE(VSL, VD(), VA(), VB()); + ADD_OPCODE(VSLB, VD(), VA(), VB()); + ADD_OPCODE(VSLH, VD(), VA(), VB()); + ADD_OPCODE(VSLO, VD(), VA(), VB()); + ADD_OPCODE(VSLW, VD(), VA(), VB()); + ADD_OPCODE(VSPLTB, VD(), VUIMM(), VB()); + ADD_OPCODE(VSPLTH, VD(), VUIMM(), VB()); + ADD_OPCODE(VSPLTISB, VD(), VSIMM()); + ADD_OPCODE(VSPLTISH, VD(), VSIMM()); + ADD_OPCODE(VSPLTISW, VD(), VSIMM()); + ADD_OPCODE(VSPLTW, VD(), VUIMM(), VB()); + ADD_OPCODE(VSR, VD(), VA(), VB()); + ADD_OPCODE(VSRAB, VD(), VA(), VB()); + ADD_OPCODE(VSRAH, VD(), VA(), VB()); + ADD_OPCODE(VSRAW, VD(), VA(), VB()); + ADD_OPCODE(VSRB, VD(), VA(), VB()); + ADD_OPCODE(VSRH, VD(), VA(), VB()); + ADD_OPCODE(VSRO, VD(), VA(), VB()); + ADD_OPCODE(VSRW, VD(), VA(), VB()); + ADD_OPCODE(VSUBCUW, VD(), VA(), VB()); + ADD_OPCODE(VSUBFP, VD(), VA(), VB()); + ADD_OPCODE(VSUBSBS, VD(), VA(), VB()); + ADD_OPCODE(VSUBSHS, VD(), VA(), VB()); + ADD_OPCODE(VSUBSWS, VD(), VA(), VB()); + ADD_OPCODE(VSUBUBM, VD(), VA(), VB()); + ADD_OPCODE(VSUBUBS, VD(), VA(), VB()); + ADD_OPCODE(VSUBUHM, VD(), VA(), VB()); + ADD_OPCODE(VSUBUHS, VD(), VA(), VB()); + ADD_OPCODE(VSUBUWM, VD(), VA(), VB()); + ADD_OPCODE(VSUBUWS, VD(), VA(), VB()); + ADD_OPCODE(VSUMSWS, VD(), VA(), VB()); + ADD_OPCODE(VSUM2SWS, VD(), VA(), VB()); + ADD_OPCODE(VSUM4SBS, VD(), VA(), VB()); + ADD_OPCODE(VSUM4SHS, VD(), VA(), VB()); + ADD_OPCODE(VSUM4UBS, VD(), VA(), VB()); + ADD_OPCODE(VUPKHPX, VD(), VB()); + ADD_OPCODE(VUPKHSB, VD(), VB()); + ADD_OPCODE(VUPKHSH, VD(), VB()); + ADD_OPCODE(VUPKLPX, VD(), VB()); + ADD_OPCODE(VUPKLSB, VD(), VB()); + ADD_OPCODE(VUPKLSH, VD(), VB()); + ADD_OPCODE(VXOR, VD(), VA(), VB()); + END_OPCODES_SUB_GROUP(); + END_OPCODES_ND_GROUP(G_04); ADD_OPCODE(MULLI, RD(), RA(), simm16()); ADD_OPCODE(SUBFIC, RD(), RA(), simm16()); @@ -300,12 +488,13 @@ public: START_OPCODES_GROUP(G_1f, GetField(21, 30)) /*0x000*/ADD_OPCODE(CMP, CRFD(), L(), RA(), RB()); /*0x004*/ADD_OPCODE(TW, TO(), RA(), RB()); + /*0x006*/ADD_OPCODE(LVSL, VD(), RA(), RB()); /*0x007*/ADD_OPCODE(LVEBX, VD(), RA(), RB()); /*0x008*/ADD_OPCODE(SUBFC, RD(), RA(), RB(), OE(), RC()); /*0x009*/ADD_OPCODE(MULHDU, RD(), RA(), RB(), RC()); /*0x00a*/ADD_OPCODE(ADDC, RD(), RA(), RB(), OE(), RC()); /*0x00b*/ADD_OPCODE(MULHWU, RD(), RA(), RB(), RC()); - /*0x013*/ADD_OPCODE(MFOCRF, GetField(11), FXM(), RD()); + /*0x013*/ADD_OPCODE(MFOCRF, GetField(11), RD(), CRM()); /*0x014*/ADD_OPCODE(LWARX, RD(), RA(), RB()); /*0x015*/ADD_OPCODE(LDX, RA(), RS(), RB()); /*0x017*/ADD_OPCODE(LWZX, RD(), RA(), RB()); @@ -314,11 +503,13 @@ public: /*0x01b*/ADD_OPCODE(SLD, RA(), RS(), RB(), RC()); /*0x01c*/ADD_OPCODE(AND, RA(), RS(), RB(), RC()); /*0x020*/ADD_OPCODE(CMPL, CRFD(), L(), RA(), RB()); + /*0x026*/ADD_OPCODE(LVSR, VD(), RA(), RB()); /*0x027*/ADD_OPCODE(LVEHX, VD(), RA(), RB()); /*0x028*/ADD_OPCODE(SUBF, RD(), RA(), RB(), OE(), RC()); /*0x036*/ADD_OPCODE(DCBST, RA(), RB()); /*0x03a*/ADD_OPCODE(CNTLZD, RA(), RS(), RC()); /*0x03c*/ADD_OPCODE(ANDC, RA(), RS(), RB(), RC()); + /*0x047*/ADD_OPCODE(LVEWX, VD(), RA(), RB()); /*0x049*/ADD_OPCODE(MULHD, RD(), RA(), RB(), RC()); /*0x04b*/ADD_OPCODE(MULHW, RD(), RA(), RB(), RC()); /*0x054*/ADD_OPCODE(LDARX, RD(), RA(), RB()); @@ -328,17 +519,20 @@ public: /*0x068*/ADD_OPCODE(NEG, RD(), RA(), OE(), RC()); /*0x077*/ADD_OPCODE(LBZUX, RD(), RA(), RB()); /*0x07c*/ADD_OPCODE(NOR, RA(), RS(), RB(), RC()); + /*0x087*/ADD_OPCODE(STVEBX, VS(), RA(), RB()); /*0x088*/ADD_OPCODE(SUBFE, RD(), RA(), RB(), OE(), RC()); /*0x08a*/ADD_OPCODE(ADDE, RD(), RA(), RB(), OE(), RC()); - /*0x090*/ADD_OPCODE(MTOCRF, FXM(), RS()); + /*0x090*/ADD_OPCODE(MTOCRF, CRM(), RS()); /*0x095*/ADD_OPCODE(STDX, RS(), RA(), RB()); /*0x096*/ADD_OPCODE(STWCX_, RS(), RA(), RB()); /*0x097*/ADD_OPCODE(STWX, RS(), RA(), RB()); + /*0x0a7*/ADD_OPCODE(STVEHX, VS(), RA(), RB()); /*0x0b5*/ADD_OPCODE(STDUX, RS(), RA(), RB()); + /*0x0c7*/ADD_OPCODE(STVEWX, VS(), RA(), RB()); /*0x0ca*/ADD_OPCODE(ADDZE, RD(), RA(), OE(), RC()); /*0x0d6*/ADD_OPCODE(STDCX_, RS(), RA(), RB()); /*0x0d7*/ADD_OPCODE(STBX, RS(), RA(), RB()); - /*0x0e7*/ADD_OPCODE(STVX, VD(), RA(), RB()); + /*0x0e7*/ADD_OPCODE(STVX, VS(), RA(), RB()); /*0x0e9*/ADD_OPCODE(MULLD, RD(), RA(), RB(), OE(), RC()); /*0x0ea*/ADD_OPCODE(ADDME, RD(), RA(), OE(), RC()); /*0x0eb*/ADD_OPCODE(MULLW, RD(), RA(), RB(), OE(), RC()); @@ -351,9 +545,12 @@ public: /*0x137*/ADD_OPCODE(LHZUX, RD(), RA(), RB()); /*0x13c*/ADD_OPCODE(XOR, RA(), RS(), RB(), RC()); /*0x153*/ADD_OPCODE(MFSPR, RD(), SPR()); + /*0x156*/ADD_OPCODE(DST, RA(), RB(), STRM(), GetField(6)); /*0x157*/ADD_OPCODE(LHAX, RD(), RA(), RB()); + /*0x167*/ADD_OPCODE(LVXL, VD(), RA(), RB()); /*0x168*/ADD_OPCODE(ABS, RD(), RA(), OE(), RC()); /*0x173*/ADD_OPCODE(MFTB, RD(), SPR()); + /*0x176*/ADD_OPCODE(DSTST, RA(), RB(), STRM(), GetField(6)); /*0x177*/ADD_OPCODE(LHAUX, RD(), RA(), RB()); /*0x197*/ADD_OPCODE(STHX, RS(), RA(), RB()); /*0x19c*/ADD_OPCODE(ORC, RA(), RS(), RB(), RC()); @@ -363,20 +560,30 @@ public: /*0x1cb*/ADD_OPCODE(DIVWU, RD(), RA(), RB(), OE(), RC()); /*0x1d3*/ADD_OPCODE(MTSPR, SPR(), RS()); /*0x1d6*///DCBI + /*0x1dc*/ADD_OPCODE(NAND, RA(), RS(), RB(), RC()); + /*0x1e7*/ADD_OPCODE(STVXL, RS(), RA(), RB()); /*0x1e9*/ADD_OPCODE(DIVD, RD(), RA(), RB(), OE(), RC()); /*0x1eb*/ADD_OPCODE(DIVW, RD(), RA(), RB(), OE(), RC()); + /*0x207*/ADD_OPCODE(LVLX, VD(), RA(), RB()); /*0x216*/ADD_OPCODE(LWBRX, RD(), RA(), RB()); /*0x217*/ADD_OPCODE(LFSX, FRD(), RA(), RB()); /*0x218*/ADD_OPCODE(SRW, RA(), RS(), RB(), RC()); /*0x21b*/ADD_OPCODE(SRD, RA(), RS(), RB(), RC()); + /*0x227*/ADD_OPCODE(LVRX, VD(), RA(), RB()); /*0x237*/ADD_OPCODE(LFSUX, FRD(), RA(), RB()); /*0x256*/ADD_OPCODE(SYNC, GetField(9, 10)); /*0x257*/ADD_OPCODE(LFDX, FRD(), RA(), RB()); /*0x277*/ADD_OPCODE(LFDUX, FRD(), RA(), RB()); + /*0x287*/ADD_OPCODE(STVLX, VS(), RA(), RB()); /*0x297*/ADD_OPCODE(STFSX, RS(), RA(), RB()); + /*0x2a7*/ADD_OPCODE(STVRX, VS(), RA(), RB()); + /*0x2d7*/ADD_OPCODE(STFDX, RS(), RA(), RB()); + /*0x307*/ADD_OPCODE(LVLXL, VD(), RA(), RB()); /*0x316*/ADD_OPCODE(LHBRX, RD(), RA(), RB()); /*0x318*/ADD_OPCODE(SRAW, RA(), RS(), RB(), RC()); - /*0x31A*/ADD_OPCODE(SRAD, RA(), RS(), RB(), RC()); + /*0x31a*/ADD_OPCODE(SRAD, RA(), RS(), RB(), RC()); + /*0x327*/ADD_OPCODE(LVRXL, VD(), RA(), RB()); + /*0x336*/ADD_OPCODE(DSS, STRM(), GetField(6)); /*0x338*/ADD_OPCODE(SRAWI, RA(), RS(), sh(), RC()); /*0x33a*/ADD_OPCODE(SRADI1, RA(), RS(), sh(), RC()); /*0x33b*/ADD_OPCODE(SRADI2, RA(), RS(), sh(), RC()); @@ -431,12 +638,11 @@ public: END_OPCODES_GROUP(G_3b); START_OPCODES_GROUP(G_3e, GetField(30, 31)) - ADD_OPCODE(STD, RS(), RA(), D()); + ADD_OPCODE(STD, RS(), RA(), D()); ADD_OPCODE(STDU, RS(), RA(), DS()); END_OPCODES_GROUP(G_3e); START_OPCODES_GROUP(G_3f, GetField(26, 30)) - ADD_OPCODE(FDIV, FRD(), FRA(), FRB(), RC()); ADD_OPCODE(FSUB, FRD(), FRA(), FRB(), RC()); ADD_OPCODE(FADD, FRD(), FRA(), FRB(), RC()); @@ -450,8 +656,7 @@ public: ADD_OPCODE(FNMADD, FRD(), FRA(), FRC(), FRB(), RC()); ADD_OPCODE(FCMPO, CRFD(), FRA(), FRB()); - default: - START_OPCODES_GROUP(0x8, GetField(21, 30)) + START_OPCODES_SUB_GROUP(GetField(21, 30)) ADD_OPCODE(FCMPU, CRFD(), FRA(), FRB()); ADD_OPCODE(FRSP, FRD(), FRB(), RC()); ADD_OPCODE(FCTIW, FRD(), FRB(), RC()); @@ -470,18 +675,26 @@ public: ADD_OPCODE(MTFSFI, CRFD(), I(), RC()); ADD_OPCODE(MFFS, FRD(), RC()); ADD_OPCODE(MTFSF, FLM(), FRB(), RC()); - END_OPCODES_GROUP(0x8); - break; - } - break; - //END_OPCODES_GROUP(G_3f); + END_OPCODES_SUB_GROUP(); + END_OPCODES_ND_GROUP(G_3f); - default: m_op.UNK(m_code, opcode, opcode); break; + default: + if(!code) + { + m_op.NULL_OP(); + break; + } + + m_op.UNK(m_code, opcode, opcode); + break; } } }; #undef START_OPCODES_GROUP +#undef START_OPCODES_SUB_GROUP #undef ADD_OPCODE #undef ADD_NULL_OPCODE -#undef END_OPCODES_GROUP \ No newline at end of file +#undef END_OPCODES_GROUP +#undef END_OPCODES_ND_GROUP +#undef END_OPCODES_SUB_GROUP \ No newline at end of file diff --git a/rpcs3/Emu/Cell/PPUDisAsm.h b/rpcs3/Emu/Cell/PPUDisAsm.h index e84cbc362a..438bb49647 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.h +++ b/rpcs3/Emu/Cell/PPUDisAsm.h @@ -33,7 +33,7 @@ public: } private: - virtual void Exit() + void Exit() { if(m_mode == NormalMode && !disasm_frame->exit) { @@ -41,63 +41,687 @@ private: } } - virtual u32 DisAsmBranchTarget(const s32 imm) + u32 DisAsmBranchTarget(const s32 imm) { return branchTarget(m_mode == NormalMode ? CPU.PC : dump_pc, imm); } private: - virtual void NULL_OP() + void NULL_OP() { Write( "null" ); } - virtual void NOP() + void NOP() { Write( "nop" ); } - virtual void TDI(OP_REG to, OP_REG ra, OP_sIMM simm16) + void TDI(OP_REG to, OP_REG ra, OP_sIMM simm16) { DisAsm_INT1_R1_IMM("tdi", to, ra, simm16); } - virtual void TWI(OP_REG to, OP_REG ra, OP_sIMM simm16) + void TWI(OP_REG to, OP_REG ra, OP_sIMM simm16) { DisAsm_INT1_R1_IMM("twi", to, ra, simm16); } START_OPCODES_GROUP(G_04) - virtual void VXOR(OP_REG vrd, OP_REG vra, OP_REG vrb) + void MFVSCR(OP_REG vd) { - DisAsm_V3("vxor", vrd, vra, vrb); + DisAsm_V1("mfvscr", vd); + } + void MTVSCR(OP_REG vb) + { + DisAsm_V1("mtvscr", vb); + } + void VADDCUW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vaddcuw", vd, va, vb); + } + void VADDFP(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vaddfp", vd, va, vb); + } + void VADDSBS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vaddsbs", vd, va, vb); + } + void VADDSHS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vaddshs", vd, va, vb); + } + void VADDSWS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vaddsws", vd, va, vb); + } + void VADDUBM(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vaddubm", vd, va, vb); + } + void VADDUBS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vaddubs", vd, va, vb); + } + void VADDUHM(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vadduhm", vd, va, vb); + } + void VADDUHS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vadduhs", vd, va, vb); + } + void VADDUWM(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vadduwm", vd, va, vb); + } + void VADDUWS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vadduws", vd, va, vb); + } + void VAND(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vand", vd, va, vb); + } + void VANDC(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vandc", vd, va, vb); + } + void VAVGSB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vavgsb", vd, va, vb); + } + void VAVGSH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vavgsh", vd, va, vb); + } + void VAVGSW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vavgsw", vd, va, vb); + } + void VAVGUB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vavgub", vd, va, vb); + } + void VAVGUH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vavguh", vd, va, vb); + } + void VAVGUW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vavguw", vd, va, vb); + } + void VCFSX(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + DisAsm_V2_UIMM("vcfsx", vd, vb, uimm5); + } + void VCFUX(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + DisAsm_V2_UIMM("vcfux", vd, vb, uimm5); + } + void VCMPBFP(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpbfp", vd, va, vb); + } + void VCMPBFP_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpbfp.", vd, va, vb); + } + void VCMPEQFP(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpeqfp", vd, va, vb); + } + void VCMPEQFP_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpeqfp.", vd, va, vb); + } + void VCMPEQUB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpequb", vd, va, vb); + } + void VCMPEQUB_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpequb.", vd, va, vb); + } + void VCMPEQUH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpequh", vd, va, vb); + } + void VCMPEQUH_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpequh.", vd, va, vb); + } + void VCMPEQUW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpequw", vd, va, vb); + } + void VCMPEQUW_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpequw.", vd, va, vb); + } + void VCMPGEFP(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgefp", vd, va, vb); + } + void VCMPGEFP_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgefp.", vd, va, vb); + } + void VCMPGTFP(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtfp", vd, va, vb); + } + void VCMPGTFP_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtfp.", vd, va, vb); + } + void VCMPGTSB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtsb", vd, va, vb); + } + void VCMPGTSB_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtsb.", vd, va, vb); + } + void VCMPGTSH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtsh", vd, va, vb); + } + void VCMPGTSH_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtsh.", vd, va, vb); + } + void VCMPGTSW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtsw", vd, va, vb); + } + void VCMPGTSW_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtsw.", vd, va, vb); + } + void VCMPGTUB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtub", vd, va, vb); + } + void VCMPGTUB_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtub.", vd, va, vb); + } + void VCMPGTUH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtuh", vd, va, vb); + } + void VCMPGTUH_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtuh.", vd, va, vb); + } + void VCMPGTUW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtuw", vd, va, vb); + } + void VCMPGTUW_(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vcmpgtuw.", vd, va, vb); + } + void VCTSXS(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + DisAsm_V2_UIMM("vctsxs", vd, vb, uimm5); + } + void VCTUXS(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + DisAsm_V2_UIMM("vctuxs", vd, vb, uimm5); + } + void VEXPTEFP(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vexptefp", vd, vb); + } + void VLOGEFP(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vlogefp", vd, vb); + } + void VMADDFP(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmaddfp", vd, va, vb, vc); + } + void VMAXFP(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmaxfp", vd, va, vb); + } + void VMAXSB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmaxsb", vd, va, vb); + } + void VMAXSH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmaxsh", vd, va, vb); + } + void VMAXSW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmaxsw", vd, va, vb); + } + void VMAXUB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmaxub", vd, va, vb); + } + void VMAXUH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmaxuh", vd, va, vb); + } + void VMAXUW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmaxuw", vd, va, vb); + } + void VMHADDSHS(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmhaddshs", vd, va, vb, vc); + } + void VMHRADDSHS(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmhraddshs", vd, va, vb, vc); + } + void VMINFP(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vminfp", vd, va, vb); + } + void VMINSB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vminsb", vd, va, vb); + } + void VMINSH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vminsh", vd, va, vb); + } + void VMINSW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vminsw", vd, va, vb); + } + void VMINUB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vminub", vd, va, vb); + } + void VMINUH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vminuh", vd, va, vb); + } + void VMINUW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vminuw", vd, va, vb); + } + void VMLADDUHM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmladduhm", vd, va, vb, vc); + } + void VMRGHB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmrghb", vd, va, vb); + } + void VMRGHH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmrghh", vd, va, vb); + } + void VMRGHW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmrghw", vd, va, vb); + } + void VMRGLB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmrglb", vd, va, vb); + } + void VMRGLH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmrglh", vd, va, vb); + } + void VMRGLW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmrglw", vd, va, vb); + } + void VMSUMMBM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmsummbm", vd, va, vb, vc); + } + void VMSUMSHM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmsumshm", vd, va, vb, vc); + } + void VMSUMSHS(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmsumshs", vd, va, vb, vc); + } + void VMSUMUBM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmsumubm", vd, va, vb, vc); + } + void VMSUMUHM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmsumuhm", vd, va, vb, vc); + } + void VMSUMUHS(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vmsumuhs", vd, va, vb, vc); + } + void VMULESB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmulesb", vd, va, vb); + } + void VMULESH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmulesh", vd, va, vb); + } + void VMULEUB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmuleub", vd, va, vb); + } + void VMULEUH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmuleuh", vd, va, vb); + } + void VMULOSB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmulosb", vd, va, vb); + } + void VMULOSH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmulosh", vd, va, vb); + } + void VMULOUB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmuloub", vd, va, vb); + } + void VMULOUH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vmulouh", vd, va, vb); + } + void VNMSUBFP(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vnmsubfp", vd, va, vb, vc); + } + void VNOR(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vnor", vd, va, vb); + } + void VOR(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vor", vd, va, vb); + } + void VPERM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vperm", vd, va, vb, vc); + } + void VPKPX(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkpx", vd, va, vb); + } + void VPKSHSS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkshss", vd, va, vb); + } + void VPKSHUS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkshus", vd, va, vb); + } + void VPKSWSS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkswss", vd, va, vb); + } + void VPKSWUS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkswus", vd, va, vb); + } + void VPKUHUM(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkuhum", vd, va, vb); + } + void VPKUHUS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkuhus", vd, va, vb); + } + void VPKUWUM(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkuwum", vd, va, vb); + } + void VPKUWUS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vpkuwus", vd, va, vb); + } + void VREFP(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vrefp", vd, vb); + } + void VRFIM(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vrfim", vd, vb); + } + void VRFIN(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vrfin", vd, vb); + } + void VRFIP(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vrfip", vd, vb); + } + void VRFIZ(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vrfiz", vd, vb); + } + void VRLB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vrlb", vd, va, vb); + } + void VRLH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vrlh", vd, va, vb); + } + void VRLW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vrlw", vd, va, vb); + } + void VRSQRTEFP(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vrsqrtefp", vd, vb); + } + void VSEL(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + DisAsm_V4("vsel", vd, va, vb, vc); + } + void VSL(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsl", vd, va, vb); + } + void VSLB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vslb", vd, va, vb); + } + void VSLDOI(OP_REG vd, OP_REG va, OP_REG vb, OP_uIMM sh) + { + DisAsm_V3_UIMM("vsldoi", vd, va, vb, sh); + } + void VSLH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vslh", vd, va, vb); + } + void VSLO(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vslo", vd, va, vb); + } + void VSLW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vslw", vd, va, vb); + } + void VSPLTB(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + DisAsm_V2_UIMM("vspltb", vd, vb, uimm5); + } + void VSPLTH(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + DisAsm_V2_UIMM("vsplth", vd, vb, uimm5); + } + void VSPLTISB(OP_REG vd, OP_sIMM simm5) + { + DisAsm_V1_SIMM("vspltisb", vd, simm5); + } + void VSPLTISH(OP_REG vd, OP_sIMM simm5) + { + DisAsm_V1_SIMM("vspltish", vd, simm5); + } + void VSPLTISW(OP_REG vd, OP_sIMM simm5) + { + DisAsm_V1_SIMM("vspltisw", vd, simm5); + } + void VSPLTW(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + DisAsm_V2_UIMM("vspltw", vd, vb, uimm5); + } + void VSR(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsr", vd, va, vb); + } + void VSRAB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsrab", vd, va, vb); + } + void VSRAH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsrah", vd, va, vb); + } + void VSRAW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsraw", vd, va, vb); + } + void VSRB(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsrb", vd, va, vb); + } + void VSRH(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsrh", vd, va, vb); + } + void VSRO(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsro", vd, va, vb); + } + void VSRW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsrw", vd, va, vb); + } + void VSUBCUW(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubcuw", vd, va, vb); + } + void VSUBFP(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubfp", vd, va, vb); + } + void VSUBSBS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubsbs", vd, va, vb); + } + void VSUBSHS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubshs", vd, va, vb); + } + void VSUBSWS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubsws", vd, va, vb); + } + void VSUBUBM(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsububm", vd, va, vb); + } + void VSUBUBS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsububs", vd, va, vb); + } + void VSUBUHM(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubuhm", vd, va, vb); + } + void VSUBUHS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubuhs", vd, va, vb); + } + void VSUBUWM(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubuwm", vd, va, vb); + } + void VSUBUWS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsubuws", vd, va, vb); + } + void VSUMSWS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsumsws", vd, va, vb); + } + void VSUM2SWS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsum2sws", vd, va, vb); + } + void VSUM4SBS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsum4sbs", vd, va, vb); + } + void VSUM4SHS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsum4shs", vd, va, vb); + } + void VSUM4UBS(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vsum4ubs", vd, va, vb); + } + void VUPKHPX(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vupkhpx", vd, vb); + } + void VUPKHSB(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vupkhsb", vd, vb); + } + void VUPKHSH(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vupkhsh", vd, vb); + } + void VUPKLPX(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vupklpx", vd, vb); + } + void VUPKLSB(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vupklsb", vd, vb); + } + void VUPKLSH(OP_REG vd, OP_REG vb) + { + DisAsm_V2("vupklsh", vd, vb); + } + void VXOR(OP_REG vd, OP_REG va, OP_REG vb) + { + DisAsm_V3("vxor", vd, va, vb); } END_OPCODES_GROUP(G_04); - virtual void MULLI(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void MULLI(OP_REG rd, OP_REG ra, OP_sIMM simm16) { DisAsm_R2_IMM("mulli", rd, ra, simm16); } - virtual void SUBFIC(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void SUBFIC(OP_REG rd, OP_REG ra, OP_sIMM simm16) { DisAsm_R2_IMM("subfic", rd, ra, simm16); } - virtual void CMPLI(OP_REG crfd, OP_REG l, OP_REG ra, OP_uIMM uimm16) + void CMPLI(OP_REG crfd, OP_REG l, OP_REG ra, OP_uIMM uimm16) { DisAsm_CR1_R1_IMM(wxString::Format("cmpl%si", l ? "d" : "w"), crfd, ra, uimm16); } - virtual void CMPI(OP_REG crfd, OP_REG l, OP_REG ra, OP_sIMM simm16) + void CMPI(OP_REG crfd, OP_REG l, OP_REG ra, OP_sIMM simm16) { DisAsm_CR1_R1_IMM(wxString::Format("cmp%si", l ? "d" : "w"), crfd, ra, simm16); } - virtual void ADDIC(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void ADDIC(OP_REG rd, OP_REG ra, OP_sIMM simm16) { DisAsm_R2_IMM("addic", rd, ra, simm16); } - virtual void ADDIC_(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void ADDIC_(OP_REG rd, OP_REG ra, OP_sIMM simm16) { DisAsm_R2_IMM("addic.", rd, ra, simm16); } - virtual void ADDI(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void ADDI(OP_REG rd, OP_REG ra, OP_sIMM simm16) { if(ra == 0) { @@ -108,7 +732,7 @@ private: DisAsm_R2_IMM("addi", rd, ra, simm16); } } - virtual void ADDIS(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void ADDIS(OP_REG rd, OP_REG ra, OP_sIMM simm16) { if(ra == 0) { @@ -120,7 +744,7 @@ private: } } - virtual void BC(OP_REG bo, OP_REG bi, OP_sIMM bd, OP_REG aa, OP_REG lk) + void BC(OP_REG bo, OP_REG bi, OP_sIMM bd, OP_REG aa, OP_REG lk) { if(m_mode == CompilerElfMode) { @@ -216,7 +840,7 @@ private: Write(wxString::Format("bc [%x:%x:%x:%x:%x], cr%d[%x], 0x%x, %d, %d", bo0, bo1, bo2, bo3, bo4, bi/4, bi%4, bd, aa, lk)); } - virtual void SC(const s32 sc_code) + void SC(const s32 sc_code) { switch(sc_code) { @@ -226,7 +850,7 @@ private: default: Write(wxString::Format("Unknown sc: %x", sc_code)); } } - virtual void B(OP_sIMM ll, OP_REG aa, OP_REG lk) + void B(OP_sIMM ll, OP_REG aa, OP_REG lk) { if(m_mode == CompilerElfMode) { @@ -255,11 +879,11 @@ private: } START_OPCODES_GROUP(G_13) - virtual void MCRF(OP_REG crfd, OP_REG crfs) + void MCRF(OP_REG crfd, OP_REG crfs) { DisAsm_CR2("mcrf", crfd, crfs); } - virtual void BCLR(OP_REG bo, OP_REG bi, OP_REG bh, OP_REG lk) + void BCLR(OP_REG bo, OP_REG bi, OP_REG bh, OP_REG lk) { const u8 bo0 = (bo & 0x10) ? 1 : 0; const u8 bo1 = (bo & 0x08) ? 1 : 0; @@ -269,43 +893,43 @@ private: if(bo0 && !bo1 && bo2 && !bo3) {Write("blr"); return;} Write(wxString::Format("bclr [%x:%x:%x:%x], cr%d[%x], %d, %d", bo0, bo1, bo2, bo3, bi/4, bi%4, bh, lk)); } - virtual void CRNOR(OP_REG bt, OP_REG ba, OP_REG bb) + void CRNOR(OP_REG bt, OP_REG ba, OP_REG bb) { DisAsm_INT3("crnor", bt, ba, bb); } - virtual void CRANDC(OP_REG bt, OP_REG ba, OP_REG bb) + void CRANDC(OP_REG bt, OP_REG ba, OP_REG bb) { DisAsm_INT3("crandc", bt, ba, bb); } - virtual void ISYNC() + void ISYNC() { Write("isync"); } - virtual void CRXOR(OP_REG bt, OP_REG ba, OP_REG bb) + void CRXOR(OP_REG bt, OP_REG ba, OP_REG bb) { DisAsm_INT3("crxor", bt, ba, bb); } - virtual void CRNAND(OP_REG bt, OP_REG ba, OP_REG bb) + void CRNAND(OP_REG bt, OP_REG ba, OP_REG bb) { DisAsm_INT3("crnand", bt, ba, bb); } - virtual void CRAND(OP_REG bt, OP_REG ba, OP_REG bb) + void CRAND(OP_REG bt, OP_REG ba, OP_REG bb) { DisAsm_INT3("crand", bt, ba, bb); } - virtual void CREQV(OP_REG bt, OP_REG ba, OP_REG bb) + void CREQV(OP_REG bt, OP_REG ba, OP_REG bb) { DisAsm_INT3("creqv", bt, ba, bb); } - virtual void CRORC(OP_REG bt, OP_REG ba, OP_REG bb) + void CRORC(OP_REG bt, OP_REG ba, OP_REG bb) { DisAsm_INT3("crorc", bt, ba, bb); } - virtual void CROR(OP_REG bt, OP_REG ba, OP_REG bb) + void CROR(OP_REG bt, OP_REG ba, OP_REG bb) { DisAsm_INT3("cror", bt, ba, bb); } - virtual void BCCTR(OP_REG bo, OP_REG bi, OP_REG bh, OP_REG lk) + void BCCTR(OP_REG bo, OP_REG bi, OP_REG bh, OP_REG lk) { switch(lk) { @@ -313,30 +937,30 @@ private: case 1: DisAsm_INT3("bcctrl", bo, bi, bh); break; } } - virtual void BCTR() + void BCTR() { Write("bctr"); } - virtual void BCTRL() + void BCTRL() { Write("bctrl"); } END_OPCODES_GROUP(G_13); - virtual void RLWIMI(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, OP_REG me, bool rc) + void RLWIMI(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, OP_REG me, bool rc) { DisAsm_R2_INT3_RC("rlwimi", ra, rs, sh, mb, me, rc); } - virtual void RLWINM(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, OP_REG me, bool rc) + void RLWINM(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, OP_REG me, bool rc) { DisAsm_R2_INT3_RC("rlwinm", ra, rs, sh, mb, me, rc); } - virtual void RLWNM(OP_REG ra, OP_REG rs, OP_REG rb, OP_REG MB, OP_REG ME, bool rc) + void RLWNM(OP_REG ra, OP_REG rs, OP_REG rb, OP_REG MB, OP_REG ME, bool rc) { DisAsm_R3_INT2_RC("rlwnm", ra, rs, rb, MB, ME, rc); } - virtual void ORI(OP_REG rs, OP_REG ra, OP_uIMM uimm16) + void ORI(OP_REG rs, OP_REG ra, OP_uIMM uimm16) { if(rs == 0 && ra == 0 && uimm16 == 0) { @@ -345,7 +969,7 @@ private: } DisAsm_R2_IMM("ori", rs, ra, uimm16); } - virtual void ORIS(OP_REG rs, OP_REG ra, OP_uIMM uimm16) + void ORIS(OP_REG rs, OP_REG ra, OP_uIMM uimm16) { if(rs == 0 && ra == 0 && uimm16 == 0) { @@ -354,25 +978,25 @@ private: } DisAsm_R2_IMM("oris", rs, ra, uimm16); } - virtual void XORI(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void XORI(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { DisAsm_R2_IMM("xori", ra, rs, uimm16); } - virtual void XORIS(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void XORIS(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { DisAsm_R2_IMM("xoris", ra, rs, uimm16); } - virtual void ANDI_(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void ANDI_(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { DisAsm_R2_IMM("andi.", ra, rs, uimm16); } - virtual void ANDIS_(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void ANDIS_(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { DisAsm_R2_IMM("andis.", ra, rs, uimm16); } START_OPCODES_GROUP(G_1e) - virtual void RLDICL(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) + void RLDICL(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) { if(sh == 0) { @@ -391,153 +1015,161 @@ private: DisAsm_R2_INT2_RC("rldicl", ra, rs, sh, mb, rc); } } - virtual void RLDICR(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG me, bool rc) + void RLDICR(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG me, bool rc) { DisAsm_R2_INT2_RC("rldicr", ra, rs, sh, me, rc); } - virtual void RLDIC(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) + void RLDIC(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) { DisAsm_R2_INT2_RC("rldic", ra, rs, sh, mb, rc); } - virtual void RLDIMI(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) + void RLDIMI(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) { DisAsm_R2_INT2_RC("rldimi", ra, rs, sh, mb, rc); } END_OPCODES_GROUP(G_1e); START_OPCODES_GROUP(G_1f) - virtual void CMP(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb) + void CMP(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb) { DisAsm_CR1_R2(wxString::Format("cmp%s", l ? "d" : "w"), crfd, ra, rb); } - virtual void TW(OP_REG to, OP_REG ra, OP_REG rb) + void TW(OP_REG to, OP_REG ra, OP_REG rb) { DisAsm_INT1_R2("tw", to, ra, rb); } - virtual void LVEBX(OP_REG vd, OP_REG ra, OP_REG rb) + void LVSL(OP_REG vd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("lvsl", vd, ra, rb); + } + void LVEBX(OP_REG vd, OP_REG ra, OP_REG rb) { DisAsm_V1_R2("lvebx", vd, ra, rb); } - virtual void SUBFC(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void SUBFC(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("subfc", rd, ra, rb, oe, rc); } - virtual void ADDC(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void ADDC(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("addc", rd, ra, rb, oe, rc); } - virtual void MULHDU(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) + void MULHDU(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) { DisAsm_R3_RC("mulhdu", rd, ra, rb, rc); } - virtual void MULHWU(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) + void MULHWU(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) { DisAsm_R3_RC("mulhwu", rd, ra, rb, rc); } - virtual void MFOCRF(OP_REG a, OP_REG fxm, OP_REG rd) + void MFOCRF(OP_uIMM a, OP_REG rd, OP_uIMM crm) { if(a) { - DisAsm_R1_IMM("mfocrf", rd, fxm); + DisAsm_R1_IMM("mfocrf", rd, crm); } else { DisAsm_R1("mfcr", rd); } } - virtual void LWARX(OP_REG rd, OP_REG ra, OP_REG rb) + void LWARX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lwarx", rd, ra, rb); } - virtual void LDX(OP_REG ra, OP_REG rs, OP_REG rb) + void LDX(OP_REG ra, OP_REG rs, OP_REG rb) { DisAsm_R3("ldx", ra, rs, rb); } - virtual void LWZX(OP_REG rd, OP_REG ra, OP_REG rb) + void LWZX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lwzx", rd, ra, rb); } - virtual void SLW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SLW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("slw", ra, rs, rb, rc); } - virtual void CNTLZW(OP_REG ra, OP_REG rs, bool rc) + void CNTLZW(OP_REG ra, OP_REG rs, bool rc) { DisAsm_R2_RC("cntlzw", ra, rs, rc); } - virtual void SLD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SLD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("sld", ra, rs, rb, rc); } - virtual void AND(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void AND(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("and", ra, rs, rb, rc); } - virtual void CMPL(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb) + void CMPL(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb) { DisAsm_CR1_R2(wxString::Format("cmpl%s", l ? "d" : "w"), crfd, ra, rb); } - virtual void LVEHX(OP_REG vd, OP_REG ra, OP_REG rb) + void LVSR(OP_REG vd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("lvsr", vd, ra, rb); + } + void LVEHX(OP_REG vd, OP_REG ra, OP_REG rb) { DisAsm_V1_R2("lvehx", vd, ra, rb); } - virtual void SUBF(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void SUBF(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("subf", rd, ra, rb, oe, rc); } - virtual void LDUX(OP_REG rd, OP_REG ra, OP_REG rb) + void LDUX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("ldux", rd, ra, rb); } - virtual void DCBST(OP_REG ra, OP_REG rb) + void DCBST(OP_REG ra, OP_REG rb) { DisAsm_R2("dcbst", ra, rb); } - virtual void CNTLZD(OP_REG ra, OP_REG rs, bool rc) + void CNTLZD(OP_REG ra, OP_REG rs, bool rc) { DisAsm_R2_RC("cntlzd", ra, rs, rc); } - virtual void ANDC(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void ANDC(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("andc", ra, rs, rb, rc); } - virtual void LVEWX(OP_REG vd, OP_REG ra, OP_REG rb) + void LVEWX(OP_REG vd, OP_REG ra, OP_REG rb) { DisAsm_V1_R2("lvewx", vd, ra, rb); } - virtual void MULHD(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) + void MULHD(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) { DisAsm_R3_RC("mulhd", rd, ra, rb, rc); } - virtual void MULHW(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) + void MULHW(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) { DisAsm_R3_RC("mulhw", rd, ra, rb, rc); } - virtual void LDARX(OP_REG rd, OP_REG ra, OP_REG rb) + void LDARX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("ldarx", rd, ra, rb); } - virtual void DCBF(OP_REG ra, OP_REG rb) + void DCBF(OP_REG ra, OP_REG rb) { DisAsm_R2("dcbf", ra, rb); } - virtual void LBZX(OP_REG rd, OP_REG ra, OP_REG rb) + void LBZX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lbzx", rd, ra, rb); } - virtual void LVX(OP_REG vrd, OP_REG ra, OP_REG rb) + void LVX(OP_REG vd, OP_REG ra, OP_REG rb) { - DisAsm_V1_R2("lvx", vrd, ra, rb); + DisAsm_V1_R2("lvx", vd, ra, rb); } - virtual void NEG(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) + void NEG(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) { DisAsm_R2_OE_RC("neg", rd, ra, oe, rc); } - virtual void LBZUX(OP_REG rd, OP_REG ra, OP_REG rb) + void LBZUX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lbzux", rd, ra, rb); } - virtual void NOR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void NOR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { if(rs == rb) { @@ -548,95 +1180,107 @@ private: DisAsm_R3_RC("nor", ra, rs, rb, rc); } } - virtual void SUBFE(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void STVEBX(OP_REG vs, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("stvebx", vs, ra, rb); + } + void SUBFE(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("subfe", rd, ra, rb, oe, rc); } - virtual void ADDE(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void ADDE(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("adde", rd, ra, rb, oe, rc); } - virtual void MTOCRF(OP_REG fxm, OP_REG rs) + void MTOCRF(OP_REG crm, OP_REG rs) { - DisAsm_INT1_R1("mtocrf", fxm, rs); + DisAsm_INT1_R1("mtocrf", crm, rs); } - virtual void STDX(OP_REG rs, OP_REG ra, OP_REG rb) + void STDX(OP_REG rs, OP_REG ra, OP_REG rb) { DisAsm_R3("stdx.", rs, ra, rb); } - virtual void STWCX_(OP_REG rs, OP_REG ra, OP_REG rb) + void STWCX_(OP_REG rs, OP_REG ra, OP_REG rb) { DisAsm_R3("stwcx.", rs, ra, rb); } - virtual void STWX(OP_REG rs, OP_REG ra, OP_REG rb) + void STWX(OP_REG rs, OP_REG ra, OP_REG rb) { DisAsm_R3("stwx", rs, ra, rb); } - virtual void STDUX(OP_REG rs, OP_REG ra, OP_REG rb) + void STVEHX(OP_REG vs, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("stvehx", vs, ra, rb); + } + void STDUX(OP_REG rs, OP_REG ra, OP_REG rb) { DisAsm_R3("stdux", rs, ra, rb); } - virtual void ADDZE(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) + void STVEWX(OP_REG vs, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("stvewx", vs, ra, rb); + } + void ADDZE(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) { DisAsm_R2_OE_RC("addze", rd, ra, oe, rc); } - virtual void STDCX_(OP_REG rs, OP_REG ra, OP_REG rb) + void STDCX_(OP_REG rs, OP_REG ra, OP_REG rb) { DisAsm_R3("stdcx.", rs, ra, rb); } - virtual void STBX(OP_REG rs, OP_REG ra, OP_REG rb) + void STBX(OP_REG rs, OP_REG ra, OP_REG rb) { DisAsm_R3("stbx", rs, ra, rb); } - virtual void STVX(OP_REG vrd, OP_REG ra, OP_REG rb) + void STVX(OP_REG vd, OP_REG ra, OP_REG rb) { - DisAsm_V1_R2("stvx", vrd, ra, rb); + DisAsm_V1_R2("stvx", vd, ra, rb); } - virtual void MULLD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void MULLD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("mulld", rd, ra, rb, oe, rc); } - virtual void ADDME(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) + void ADDME(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) { DisAsm_R2_OE_RC("addme", rd, ra, oe, rc); } - virtual void MULLW(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void MULLW(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("mullw", rd, ra, rb, oe, rc); } - virtual void DCBTST(OP_REG th, OP_REG ra, OP_REG rb) + void DCBTST(OP_REG th, OP_REG ra, OP_REG rb) { DisAsm_R3("dcbtst", th, ra, rb); } - virtual void ADD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void ADD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("add", rd, ra, rb, oe, rc); } - virtual void DCBT(OP_REG ra, OP_REG rb, OP_REG th) + void DCBT(OP_REG ra, OP_REG rb, OP_REG th) { DisAsm_R2("dcbt", ra, rb); } - virtual void LHZX(OP_REG rd, OP_REG ra, OP_REG rb) + void LHZX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lhzx", rd, ra, rb); } - virtual void EQV(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void EQV(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("eqv", ra, rs, rb, rc); } - virtual void ECIWX(OP_REG rd, OP_REG ra, OP_REG rb) + void ECIWX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("eciwx", rd, ra, rb); } - virtual void LHZUX(OP_REG rd, OP_REG ra, OP_REG rb) + void LHZUX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lhzux", rd, ra, rb); } - virtual void XOR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void XOR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("xor", ra, rs, rb, rc); } - virtual void MFSPR(OP_REG rd, OP_REG spr) + void MFSPR(OP_REG rd, OP_REG spr) { const u32 n = (spr >> 5) | ((spr & 0x1f) << 5); switch(n) @@ -647,15 +1291,30 @@ private: default: DisAsm_R1_IMM("mfspr", rd, spr); break; } } - virtual void LHAX(OP_REG rd, OP_REG ra, OP_REG rb) + void DST(OP_REG ra, OP_REG rb, OP_uIMM strm, OP_uIMM t) + { + if(t) + { + DisAsm_R2_INT1("dstt", ra, rb, strm); + } + else + { + DisAsm_R2_INT1("dst", ra, rb, strm); + } + } + void LHAX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lhax", rd, ra, rb); } - virtual void ABS(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) + void LVXL(OP_REG vd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("lvxl", vd, ra, rb); + } + void ABS(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) { DisAsm_R2_OE_RC("abs", rd, ra, oe, rc); } - virtual void MFTB(OP_REG rd, OP_REG spr) + void MFTB(OP_REG rd, OP_REG spr) { const u32 n = (spr >> 5) | ((spr & 0x1f) << 5); switch(n) @@ -665,23 +1324,34 @@ private: default: DisAsm_R1_IMM("mftb", rd, spr); break; } } - virtual void LHAUX(OP_REG rd, OP_REG ra, OP_REG rb) + void DSTST(OP_REG ra, OP_REG rb, OP_uIMM strm, OP_uIMM t) + { + if(t) + { + DisAsm_R2_INT1("dststt", ra, rb, strm); + } + else + { + DisAsm_R2_INT1("dstst", ra, rb, strm); + } + } + void LHAUX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lhaux", rd, ra, rb); } - virtual void STHX(OP_REG rs, OP_REG ra, OP_REG rb) + void STHX(OP_REG rs, OP_REG ra, OP_REG rb) { DisAsm_R3("sthx", rs, ra, rb); } - virtual void ORC(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void ORC(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("orc", ra, rs, rb, rc); } - virtual void ECOWX(OP_REG rs, OP_REG ra, OP_REG rb) + void ECOWX(OP_REG rs, OP_REG ra, OP_REG rb) { DisAsm_R3("ecowx", rs, ra, rb); } - virtual void OR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void OR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { if(rs==rb) { @@ -692,15 +1362,15 @@ private: DisAsm_R3_RC("or", ra, rs, rb, rc); } } - virtual void DIVDU(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void DIVDU(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("divdu", rd, ra, rb, oe, rc); } - virtual void DIVWU(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void DIVWU(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("divwu", rd, ra, rb, oe, rc); } - virtual void MTSPR(OP_REG spr, OP_REG rs) + void MTSPR(OP_REG spr, OP_REG rs) { const u32 n = (spr & 0x1f) + ((spr >> 5) & 0x1f); @@ -713,375 +1383,430 @@ private: } } /*0x1d6*///DCBI - virtual void DIVD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void NAND(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + { + DisAsm_R3_RC("nand", ra, rs, rb, rc); + } + void STVXL(OP_REG vs, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("stvxl", vs, ra, rb); + } + void DIVD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("divd", rd, ra, rb, oe, rc); } - virtual void DIVW(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void DIVW(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { DisAsm_R3_OE_RC("divw", rd, ra, rb, oe, rc); } - virtual void LWBRX(OP_REG rd, OP_REG ra, OP_REG rb) + void LVLX(OP_REG vd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("lvlx", vd, ra, rb); + } + void LWBRX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lwbrx", rd, ra, rb); } - virtual void LFSX(OP_REG frd, OP_REG ra, OP_REG rb) + void LFSX(OP_REG frd, OP_REG ra, OP_REG rb) { DisAsm_F1_R2("lfsx", frd, ra, rb); } - virtual void SRW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SRW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("srw", ra, rs, rb, rc); } - virtual void SRD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SRD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("srd", ra, rs, rb, rc); } - virtual void LFSUX(OP_REG frd, OP_REG ra, OP_REG rb) + void LVRX(OP_REG vd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("lvrx", vd, ra, rb); + } + void LFSUX(OP_REG frd, OP_REG ra, OP_REG rb) { DisAsm_F1_R2("lfsux", frd, ra, rb); } - virtual void SYNC(OP_REG l) + void SYNC(OP_REG l) { DisAsm_INT1("sync", l); } - virtual void LFDX(OP_REG frd, OP_REG ra, OP_REG rb) + void LFDX(OP_REG frd, OP_REG ra, OP_REG rb) { DisAsm_F1_R2("lfdx", frd, ra, rb); } - virtual void LFDUX(OP_REG frd, OP_REG ra, OP_REG rb) + void LFDUX(OP_REG frd, OP_REG ra, OP_REG rb) { DisAsm_F1_R2("lfdux", frd, ra, rb); } - virtual void STFSX(OP_REG frs, OP_REG ra, OP_REG rb) + void STVLX(OP_REG sd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("stvlx", sd, ra, rb); + } + void STFSX(OP_REG frs, OP_REG ra, OP_REG rb) { DisAsm_F1_R2("stfsx", frs, ra, rb); } - virtual void LHBRX(OP_REG rd, OP_REG ra, OP_REG rb) + void STVRX(OP_REG sd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("stvrx", sd, ra, rb); + } + void STFDX(OP_REG frs, OP_REG ra, OP_REG rb) + { + DisAsm_F1_R2("stfdx", frs, ra, rb); + } + void LVLXL(OP_REG vd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("lvlxl", vd, ra, rb); + } + void LHBRX(OP_REG rd, OP_REG ra, OP_REG rb) { DisAsm_R3("lhbrx", rd, ra, rb); } - virtual void SRAW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SRAW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("sraw", ra, rs, rb, rc); } - virtual void SRAD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SRAD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { DisAsm_R3_RC("srad", ra, rs, rb, rc); } - virtual void SRAWI(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) + void LVRXL(OP_REG vd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("lvrxl", vd, ra, rb); + } + void DSS(OP_uIMM strm, OP_uIMM a) + { + if(a) + { + Write("dssall"); + } + else + { + DisAsm_INT1("dss", strm); + } + } + void SRAWI(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) { DisAsm_R2_INT1_RC("srawi", ra, rs, sh, rc); } - virtual void SRADI1(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) + void SRADI1(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) { DisAsm_R2_INT1_RC("sradi", ra, rs, sh, rc); } - virtual void SRADI2(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) + void SRADI2(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) { DisAsm_R2_INT1_RC("sradi", ra, rs, sh, rc); } - virtual void EIEIO() + void EIEIO() { Write("eieio"); } - virtual void EXTSH(OP_REG ra, OP_REG rs, bool rc) + void STVLXL(OP_REG sd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("stvlxl", sd, ra, rb); + } + void EXTSH(OP_REG ra, OP_REG rs, bool rc) { DisAsm_R2_RC("extsh", ra, rs, rc); } - virtual void EXTSB(OP_REG ra, OP_REG rs, bool rc) + void STVRXL(OP_REG sd, OP_REG ra, OP_REG rb) + { + DisAsm_V1_R2("stvrxl", sd, ra, rb); + } + void EXTSB(OP_REG ra, OP_REG rs, bool rc) { DisAsm_R2_RC("extsb", ra, rs, rc); } - virtual void STFIWX(OP_REG frs, OP_REG ra, OP_REG rb) + void STFIWX(OP_REG frs, OP_REG ra, OP_REG rb) { DisAsm_F1_R2("stfiwx", frs, ra, rb); } - virtual void EXTSW(OP_REG ra, OP_REG rs, bool rc) + void EXTSW(OP_REG ra, OP_REG rs, bool rc) { DisAsm_R2_RC("extsw", ra, rs, rc); } /*0x3d6*///ICBI - virtual void DCBZ(OP_REG ra, OP_REG rs) + void DCBZ(OP_REG ra, OP_REG rs) { DisAsm_R2("dcbz", ra, rs); } END_OPCODES_GROUP(G_1f); - virtual void LWZ(OP_REG rd, OP_REG ra, OP_sIMM d) + void LWZ(OP_REG rd, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("lwz", rd, ra, d); } - virtual void LWZU(OP_REG rd, OP_REG ra, OP_sIMM d) + void LWZU(OP_REG rd, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("lwzu", rd, ra, d); } - virtual void LBZ(OP_REG rd, OP_REG ra, OP_sIMM d) + void LBZ(OP_REG rd, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("lbz", rd, ra, d); } - virtual void LBZU(OP_REG rd, OP_REG ra, OP_sIMM d) + void LBZU(OP_REG rd, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("lbzu", rd, ra, d); } - virtual void STW(OP_REG rs, OP_REG ra, OP_sIMM d) + void STW(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("stw", rs, ra, d); } - virtual void STWU(OP_REG rs, OP_REG ra, OP_sIMM d) + void STWU(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("stwu", rs, ra, d); } - virtual void STB(OP_REG rs, OP_REG ra, OP_sIMM d) + void STB(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("stb", rs, ra, d); } - virtual void STBU(OP_REG rs, OP_REG ra, OP_sIMM d) + void STBU(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("stbu", rs, ra, d); } - virtual void LHZ(OP_REG rs, OP_REG ra, OP_sIMM d) + void LHZ(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("lhz", rs, ra, d); } - virtual void LHZU(OP_REG rs, OP_REG ra, OP_sIMM d) + void LHZU(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("lhzu", rs, ra, d); } - virtual void STH(OP_REG rs, OP_REG ra, OP_sIMM d) + void STH(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("sth", rs, ra, d); } - virtual void STHU(OP_REG rs, OP_REG ra, OP_sIMM d) + void STHU(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("sthu", rs, ra, d); } - virtual void LMW(OP_REG rd, OP_REG ra, OP_sIMM d) + void LMW(OP_REG rd, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("lmw", rd, ra, d); } - virtual void STMW(OP_REG rs, OP_REG ra, OP_sIMM d) + void STMW(OP_REG rs, OP_REG ra, OP_sIMM d) { DisAsm_R2_IMM("stmw", rs, ra, d); } - virtual void LFS(OP_REG frd, OP_REG ra, OP_sIMM d) + void LFS(OP_REG frd, OP_REG ra, OP_sIMM d) { DisAsm_F1_IMM_R1("lfs", frd, d, ra); } - virtual void LFSU(OP_REG frd, OP_REG ra, OP_sIMM ds) + void LFSU(OP_REG frd, OP_REG ra, OP_sIMM ds) { DisAsm_F1_IMM_R1("lfsu", frd, ds, ra); } - virtual void LFD(OP_REG frd, OP_REG ra, OP_sIMM d) + void LFD(OP_REG frd, OP_REG ra, OP_sIMM d) { DisAsm_F1_IMM_R1("lfd", frd, d, ra); } - virtual void LFDU(OP_REG frd, OP_REG ra, OP_sIMM ds) + void LFDU(OP_REG frd, OP_REG ra, OP_sIMM ds) { DisAsm_F1_IMM_R1("lfdu", frd, ds, ra); } - virtual void STFS(OP_REG frs, OP_REG ra, OP_sIMM d) + void STFS(OP_REG frs, OP_REG ra, OP_sIMM d) { DisAsm_F1_IMM_R1("stfs", frs, d, ra); } - virtual void STFSU(OP_REG frs, OP_REG ra, OP_sIMM d) + void STFSU(OP_REG frs, OP_REG ra, OP_sIMM d) { DisAsm_F1_IMM_R1("stfsu", frs, d, ra); } - virtual void STFD(OP_REG frs, OP_REG ra, OP_sIMM d) + void STFD(OP_REG frs, OP_REG ra, OP_sIMM d) { DisAsm_F1_IMM_R1("stfd", frs, d, ra); } - virtual void STFDU(OP_REG frs, OP_REG ra, OP_sIMM d) + void STFDU(OP_REG frs, OP_REG ra, OP_sIMM d) { DisAsm_F1_IMM_R1("stfdu", frs, d, ra); } START_OPCODES_GROUP(G_3a) - virtual void LD(OP_REG rd, OP_REG ra, OP_sIMM ds) + void LD(OP_REG rd, OP_REG ra, OP_sIMM ds) { DisAsm_R2_IMM("ld", rd, ra, ds); } - virtual void LDU(OP_REG rd, OP_REG ra, OP_sIMM ds) + void LDU(OP_REG rd, OP_REG ra, OP_sIMM ds) { DisAsm_R2_IMM("ldu", rd, ra, ds); } END_OPCODES_GROUP(G_3a); START_OPCODES_GROUP(G_3b) - virtual void FDIVS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FDIVS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { DisAsm_F3_RC("fdivs", frd, fra, frb, rc); } - virtual void FSUBS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FSUBS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { DisAsm_F3_RC("fsubs", frd, fra, frb, rc); } - virtual void FADDS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FADDS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { DisAsm_F3_RC("fadds", frd, fra, frb, rc); } - virtual void FSQRTS(OP_REG frd, OP_REG frb, bool rc) + void FSQRTS(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fsqrts", frd, frb, rc); } - virtual void FRES(OP_REG frd, OP_REG frb, bool rc) + void FRES(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fres", frd, frb, rc); } - virtual void FMULS(OP_REG frd, OP_REG fra, OP_REG frc, bool rc) + void FMULS(OP_REG frd, OP_REG fra, OP_REG frc, bool rc) { DisAsm_F3_RC("fmuls", frd, fra, frc, rc); } - virtual void FMADDS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FMADDS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fmadds", frd, fra, frc, frb, rc); } - virtual void FMSUBS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FMSUBS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fmsubs", frd, fra, frc, frb, rc); } - virtual void FNMSUBS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FNMSUBS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fnmsubs", frd, fra, frc, frb, rc); } - virtual void FNMADDS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FNMADDS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fnmadds", frd, fra, frc, frb, rc); } END_OPCODES_GROUP(G_3b); START_OPCODES_GROUP(G_3e) - virtual void STD(OP_REG rs, OP_REG ra, OP_sIMM ds) + void STD(OP_REG rs, OP_REG ra, OP_sIMM ds) { DisAsm_R2_IMM("std", rs, ra, ds); } - virtual void STDU(OP_REG rs, OP_REG ra, OP_sIMM ds) + void STDU(OP_REG rs, OP_REG ra, OP_sIMM ds) { DisAsm_R2_IMM("stdu", rs, ra, ds); } END_OPCODES_GROUP(G_3e); START_OPCODES_GROUP(G_3f) - virtual void MTFSB1(OP_REG bt, bool rc) + void MTFSB1(OP_REG bt, bool rc) { DisAsm_F1_RC("mtfsb1", bt, rc); } - virtual void MCRFS(OP_REG bf, OP_REG bfa) + void MCRFS(OP_REG bf, OP_REG bfa) { DisAsm_F2("mcrfs", bf, bfa); } - virtual void MTFSB0(OP_REG bt, bool rc) + void MTFSB0(OP_REG bt, bool rc) { DisAsm_F1_RC("mtfsb0", bt, rc); } - virtual void MTFSFI(OP_REG crfd, OP_REG i, bool rc) + void MTFSFI(OP_REG crfd, OP_REG i, bool rc) { DisAsm_F2_RC("mtfsfi", crfd, i, rc); } - virtual void MFFS(OP_REG frd, bool rc) + void MFFS(OP_REG frd, bool rc) { DisAsm_F1_RC("mffs", frd, rc); } - virtual void MTFSF(OP_REG flm, OP_REG frb, bool rc) + void MTFSF(OP_REG flm, OP_REG frb, bool rc) { DisAsm_F2_RC("mtfsf", flm, frb, rc); } - virtual void FCMPU(OP_REG crfd, OP_REG fra, OP_REG frb) + void FCMPU(OP_REG crfd, OP_REG fra, OP_REG frb) { DisAsm_CR1_F2("fcmpu", crfd, fra, frb); } - virtual void FRSP(OP_REG frd, OP_REG frb, bool rc) + void FRSP(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("frsp", frd, frb, rc); } - virtual void FCTIW(OP_REG frd, OP_REG frb, bool rc) + void FCTIW(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fctiw", frd, frb, rc); } - virtual void FCTIWZ(OP_REG frd, OP_REG frb, bool rc) + void FCTIWZ(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fctiwz", frd, frb, rc); } - virtual void FDIV(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FDIV(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { DisAsm_F3_RC("fdiv", frd, fra, frb, rc); } - virtual void FSUB(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FSUB(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { DisAsm_F3_RC("fsub", frd, fra, frb, rc); } - virtual void FADD(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FADD(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { DisAsm_F3_RC("fadd", frd, fra, frb, rc); } - virtual void FSQRT(OP_REG frd, OP_REG frb, bool rc) + void FSQRT(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fsqrt", frd, frb, rc); } - virtual void FSEL(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FSEL(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fsel", frd, fra, frc, frb, rc); } - virtual void FMUL(OP_REG frd, OP_REG fra, OP_REG frc, bool rc) + void FMUL(OP_REG frd, OP_REG fra, OP_REG frc, bool rc) { DisAsm_F3_RC("fmul", frd, fra, frc, rc); } - virtual void FRSQRTE(OP_REG frd, OP_REG frb, bool rc) + void FRSQRTE(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("frsqrte", frd, frb, rc); } - virtual void FMSUB(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FMSUB(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fmsub", frd, fra, frc, frb, rc); } - virtual void FMADD(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FMADD(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fmadd", frd, fra, frc, frb, rc); } - virtual void FNMSUB(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FNMSUB(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fnmsub", frd, fra, frc, frb, rc); } - virtual void FNMADD(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FNMADD(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { DisAsm_F4_RC("fnmadd", frd, fra, frc, frb, rc); } - virtual void FCMPO(OP_REG crfd, OP_REG fra, OP_REG frb) + void FCMPO(OP_REG crfd, OP_REG fra, OP_REG frb) { DisAsm_F3("fcmpo", crfd, fra, frb); } - virtual void FNEG(OP_REG frd, OP_REG frb, bool rc) + void FNEG(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fneg", frd, frb, rc); } - virtual void FMR(OP_REG frd, OP_REG frb, bool rc) + void FMR(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fmr", frd, frb, rc); } - virtual void FNABS(OP_REG frd, OP_REG frb, bool rc) + void FNABS(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fnabs", frd, frb, rc); } - virtual void FABS(OP_REG frd, OP_REG frb, bool rc) + void FABS(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fabs", frd, frb, rc); } - virtual void FCTID(OP_REG frd, OP_REG frb, bool rc) + void FCTID(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fctid", frd, frb, rc); } - virtual void FCTIDZ(OP_REG frd, OP_REG frb, bool rc) + void FCTIDZ(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fctidz", frd, frb, rc); } - virtual void FCFID(OP_REG frd, OP_REG frb, bool rc) + void FCFID(OP_REG frd, OP_REG frb, bool rc) { DisAsm_F2_RC("fcfid", frd, frb, rc); } END_OPCODES_GROUP(G_3f); - virtual void UNK(const s32 code, const s32 opcode, const s32 gcode) + void UNK(const u32 code, const u32 opcode, const u32 gcode) { Write(wxString::Format("Unknown/Illegal opcode! (0x%08x : 0x%x : 0x%x)", code, opcode, gcode)); } diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index fe97ee9c9f..b186db9186 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -5,9 +5,11 @@ #include "Emu/Cell/PPUThread.h" #include "Emu/SysCalls/SysCalls.h" #include "rpcs3.h" +#include #define START_OPCODES_GROUP(x) /*x*/ #define END_OPCODES_GROUP(x) /*x*/ +#define UNIMPLEMENTED() UNK(__FUNCTION__) #if 0//def _DEBUG #define HLE_CALL_DEBUG @@ -28,11 +30,16 @@ void InitRotateMask() inited = true; } +u8 rotl8(const u8 x, const u8 n) { return (x << n) | (x >> (8 - n)); } +u8 rotr8(const u8 x, const u8 n) { return (x >> n) | (x << (8 - n)); } + +u16 rotl16(const u16 x, const u8 n) { return (x << n) | (x >> (16 - n)); } +u16 rotr16(const u16 x, const u8 n) { return (x >> n) | (x << (16 - n)); } /* -u32 rotl32(const u32 x, const u8 n) const { return (x << n) | (x >> (32 - n)); } -u32 rotr32(const u32 x, const u8 n) const { return (x >> n) | (x << (32 - n)); } -u64 rotl64(const u64 x, const u8 n) const { return (x << n) | (x >> (64 - n)); } -u64 rotr64(const u64 x, const u8 n) const { return (x >> n) | (x << (64 - n)); } +u32 rotl32(const u32 x, const u8 n) { return (x << n) | (x >> (32 - n)); } +u32 rotr32(const u32 x, const u8 n) { return (x >> n) | (x << (32 - n)); } +u64 rotl64(const u64 x, const u8 n) { return (x << n) | (x >> (64 - n)); } +u64 rotr64(const u64 x, const u8 n) { return (x >> n) | (x << (64 - n)); } */ #define rotl32 _rotl @@ -52,29 +59,40 @@ public: } private: - virtual void Exit() {} + void Exit() {} - virtual void SysCall() + void SysCall() { - CPU.GPR[3] = SysCallsManager.DoSyscall(CPU.GPR[11], CPU); + CPU.GPR[3] = CPU.DoSyscall(CPU.GPR[11]); - if((s32)CPU.GPR[3] < 0) - ConLog.Warning("SysCall[%lld] done with code [0x%x]! #pc: 0x%llx", CPU.GPR[11], (u32)CPU.GPR[3], CPU.PC); + //if((s32)CPU.GPR[3] < 0) + //ConLog.Warning("SysCall[%lld] done with code [0x%x]! #pc: 0x%llx", CPU.GPR[11], (u32)CPU.GPR[3], CPU.PC); #ifdef HLE_CALL_DEBUG - else ConLog.Write("SysCall[%lld] done with code [0x%llx]! #pc: 0x%llx", CPU.GPR[11], CPU.GPR[3], CPU.PC); + ConLog.Write("SysCall[%lld] done with code [0x%llx]! #pc: 0x%llx", CPU.GPR[11], CPU.GPR[3], CPU.PC); #endif } - virtual void NULL_OP() + void NULL_OP() { UNK("null"); } - virtual void NOP() + void NOP() { //__asm nop } + float CheckVSCR_NJ(const float v) const + { + if(!CPU.VSCR.NJ) return v; + + int fpc = _fpclass(v); + if(fpc & _FPCLASS_ND) return -0.0f; + if(fpc & _FPCLASS_PD) return 0.0f; + + return v; + } + bool CheckCondition(OP_uIMM bo, OP_uIMM bi) { const u8 bo0 = (bo & 0x10) ? 1 : 0; @@ -105,13 +123,14 @@ private: case 0x001: return CPU.XER.XER; case 0x008: return CPU.LR; case 0x009: return CPU.CTR; + case 0x100: return CPU.USPRG0; } - UNK(wxString::Format("GetRegBySPR error: Unknown spr %d!", n)); + UNK(wxString::Format("GetRegBySPR error: Unknown SPR 0x%x!", n)); return CPU.XER.XER; } - virtual void TDI(OP_uIMM to, OP_REG ra, OP_sIMM simm16) + void TDI(OP_uIMM to, OP_REG ra, OP_sIMM simm16) { s64 a = CPU.GPR[ra]; @@ -121,11 +140,11 @@ private: ((u64)a < (u64)simm16 && (to & 0x2)) || ((u64)a > (u64)simm16 && (to & 0x1)) ) { - UNK(wxString::Format("Trap! (tdi %x, r%d, %x)", to, ra, simm16), false); + UNK(wxString::Format("Trap! (tdi %x, r%d, %x)", to, ra, simm16)); } } - virtual void TWI(OP_uIMM to, OP_REG ra, OP_sIMM simm16) + void TWI(OP_uIMM to, OP_REG ra, OP_sIMM simm16) { s32 a = CPU.GPR[ra]; @@ -135,63 +154,1907 @@ private: ((u32)a < (u32)simm16 && (to & 0x2)) || ((u32)a > (u32)simm16 && (to & 0x1)) ) { - UNK(wxString::Format("Trap! (twi %x, r%d, %x)", to, ra, simm16), false); + UNK(wxString::Format("Trap! (twi %x, r%d, %x)", to, ra, simm16)); } } START_OPCODES_GROUP(G_04) - virtual void VXOR(OP_REG vrd, OP_REG vra, OP_REG vrb) + void MFVSCR(OP_REG vd) { - CPU.VPR[vrd] = CPU.VPR[vra] ^ CPU.VPR[vrb]; + CPU.VPR[vd].Clear(); + CPU.VPR[vd]._u32[0] = CPU.VSCR.VSCR; + } + void MTVSCR(OP_REG vb) + { + CPU.VSCR.VSCR = CPU.VPR[vb]._u32[0]; + CPU.VSCR.X = CPU.VSCR.Y = 0; + } + void VADDCUW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = ~CPU.VPR[va]._u32[w] < CPU.VPR[vb]._u32[w]; + } + } + void VADDFP(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = CPU.VPR[va]._f[w] + CPU.VPR[vb]._f[w]; + } + } + void VADDSBS(OP_REG vd, OP_REG va, OP_REG vb) + { + for(u32 b=0; b<16; ++b) + { + s16 result = (s16)CPU.VPR[va]._s8[b] + (s16)CPU.VPR[vb]._s8[b]; + + if (result > 0x7f) + { + CPU.VPR[vd]._s8[b] = 0x7f; + CPU.VSCR.SAT = 1; + } + else if (result < -0x80) + { + CPU.VPR[vd]._s8[b] = -0x80; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s8[b] = result; + } + } + void VADDSHS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + s32 result = (s32)CPU.VPR[va]._s16[h] + (s32)CPU.VPR[vb]._s16[h]; + + if (result > 0x7fff) + { + CPU.VPR[vd]._s16[h] = 0x7fff; + CPU.VSCR.SAT = 1; + } + else if (result < -0x8000) + { + CPU.VPR[vd]._s16[h] = -0x8000; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s16[h] = result; + } + } + void VADDSWS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + s64 result = (s64)CPU.VPR[va]._s32[w] + (s64)CPU.VPR[vb]._s32[w]; + + if (result > 0x7fffffff) + { + CPU.VPR[vd]._s32[w] = 0x7fffffff; + CPU.VSCR.SAT = 1; + } + else if (result < (s32)0x80000000) + { + CPU.VPR[vd]._s32[w] = 0x80000000; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s32[w] = result; + } + } + void VADDUBM(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = CPU.VPR[va]._u8[b] + CPU.VPR[vb]._u8[b]; + } + } + void VADDUBS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + u16 result = (u16)CPU.VPR[va]._u8[b] + (u16)CPU.VPR[vb]._u8[b]; + + if (result > 0xff) + { + CPU.VPR[vd]._u8[b] = 0xff; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._u8[b] = result; + } + } + void VADDUHM(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = CPU.VPR[va]._u16[h] + CPU.VPR[vb]._u16[h]; + } + } + void VADDUHS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + u32 result = (u32)CPU.VPR[va]._u16[h] + (u32)CPU.VPR[vb]._u16[h]; + + if (result > 0xffff) + { + CPU.VPR[vd]._u16[h] = 0xffff; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._u16[h] = result; + } + } + void VADDUWM(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] + CPU.VPR[vb]._u32[w]; + } + } + void VADDUWS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + u64 result = (u64)CPU.VPR[va]._u32[w] + (u64)CPU.VPR[vb]._u32[w]; + + if (result > 0xffffffff) + { + CPU.VPR[vd]._u32[w] = 0xffffffff; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._u32[w] = result; + } + } + void VAND(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] & CPU.VPR[vb]._u32[w]; + } + } + void VANDC(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] & (~CPU.VPR[vb]._u32[w]); + } + } + void VAVGSB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._s8[b] = (CPU.VPR[va]._s8[b] + CPU.VPR[vb]._s8[b] + 1) >> 1; + } + } + void VAVGSH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._s16[h] = (CPU.VPR[va]._s16[h] + CPU.VPR[vb]._s16[h] + 1) >> 1; + } + } + void VAVGSW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s32[w] = ((s64)CPU.VPR[va]._s32[w] + (s64)CPU.VPR[vb]._s32[w] + 1) >> 1; + } + } + void VAVGUB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + CPU.VPR[vd]._u8[b] = (CPU.VPR[va]._u8[b] + CPU.VPR[vb]._u8[b] + 1) >> 1; + } + void VAVGUH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = (CPU.VPR[va]._u16[h] + CPU.VPR[vb]._u16[h] + 1) >> 1; + } + } + void VAVGUW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = ((u64)CPU.VPR[va]._u32[w] + (u64)CPU.VPR[vb]._u32[w] + 1) >> 1; + } + } + void VCFSX(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + u32 scale = 1 << uimm5; + + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = ((float)CPU.VPR[vb]._s32[w]) / scale; + } + } + void VCFUX(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + u32 scale = 1 << uimm5; + + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = ((float)CPU.VPR[vb]._u32[w]) / scale; + } + } + void VCMPBFP(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + u32 mask = 0; + + const float A = CheckVSCR_NJ(CPU.VPR[va]._f[w]); + const float B = CheckVSCR_NJ(CPU.VPR[vb]._f[w]); + + if (A > B) mask |= 1 << 31; + if (A < -B) mask |= 1 << 30; + + CPU.VPR[vd]._u32[w] = mask; + } + } + void VCMPBFP_(OP_REG vd, OP_REG va, OP_REG vb) + { + bool allInBounds = true; + + for (uint w = 0; w < 4; w++) + { + u32 mask = 0; + + const float A = CheckVSCR_NJ(CPU.VPR[va]._f[w]); + const float B = CheckVSCR_NJ(CPU.VPR[vb]._f[w]); + + if (A > B) mask |= 1 << 31; + if (A < -B) mask |= 1 << 30; + + CPU.VPR[vd]._u32[w] = mask; + + if (mask) + allInBounds = false; + } + + // Bit n°2 of CR6 + CPU.SetCRBit(6, 0x2, allInBounds); + } + void VCMPEQFP(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._f[w] == CPU.VPR[vb]._f[w] ? 0xffffffff : 0; + } + } + void VCMPEQFP_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_equal = 0x8; + int none_equal = 0x2; + + for (uint w = 0; w < 4; w++) + { + if (CPU.VPR[va]._f[w] == CPU.VPR[vb]._f[w]) + { + CPU.VPR[vd]._u32[w] = 0xffffffff; + none_equal = 0; + } + else + { + CPU.VPR[vd]._u32[w] = 0; + all_equal = 0; + } + } + + CPU.CR.cr6 = all_equal | none_equal; + } + void VCMPEQUB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = CPU.VPR[va]._u8[b] == CPU.VPR[vb]._u8[b] ? 0xff : 0; + } + } + void VCMPEQUB_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_equal = 0x8; + int none_equal = 0x2; + + for (uint b = 0; b < 16; b++) + { + if (CPU.VPR[va]._u8[b] == CPU.VPR[vb]._u8[b]) + { + CPU.VPR[vd]._u8[b] = 0xff; + none_equal = 0; + } + else + { + CPU.VPR[vd]._u8[b] = 0; + all_equal = 0; + } + } + + CPU.CR.cr6 = all_equal | none_equal; + } + void VCMPEQUH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = CPU.VPR[va]._u16[h] == CPU.VPR[vb]._u16[h] ? 0xffff : 0; + } + } + void VCMPEQUH_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_equal = 0x8; + int none_equal = 0x2; + + for (uint h = 0; h < 8; h++) + { + if (CPU.VPR[va]._u16[h] == CPU.VPR[vb]._u16[h]) + { + CPU.VPR[vd]._u16[h] = 0xffff; + none_equal = 0; + } + else + { + CPU.VPR[vd]._u16[h] = 0; + all_equal = 0; + } + } + + CPU.CR.cr6 = all_equal | none_equal; + } + void VCMPEQUW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] == CPU.VPR[vb]._u32[w] ? 0xffffffff : 0; + } + } + void VCMPEQUW_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_equal = 0x8; + int none_equal = 0x2; + + for (uint w = 0; w < 4; w++) + { + if (CPU.VPR[va]._u32[w] == CPU.VPR[vb]._u32[w]) + { + CPU.VPR[vd]._u32[w] = 0xffffffff; + none_equal = 0; + } + else + { + CPU.VPR[vd]._u32[w] = 0; + all_equal = 0; + } + } + + CPU.CR.cr6 = all_equal | none_equal; + } + void VCMPGEFP(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._f[w] >= CPU.VPR[vb]._f[w] ? 0xffffffff : 0; + } + } + void VCMPGEFP_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_ge = 0x8; + int none_ge = 0x2; + + for (uint w = 0; w < 4; w++) + { + if (CPU.VPR[va]._f[w] >= CPU.VPR[vb]._f[w]) + { + CPU.VPR[vd]._u32[w] = 0xffffffff; + none_ge = 0; + } + else + { + CPU.VPR[vd]._u32[w] = 0; + all_ge = 0; + } + } + + CPU.CR.cr6 = all_ge | none_ge; + } + void VCMPGTFP(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._f[w] > CPU.VPR[vb]._f[w] ? 0xffffffff : 0; + } + } + void VCMPGTFP_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_ge = 0x8; + int none_ge = 0x2; + + for (uint w = 0; w < 4; w++) + { + if (CPU.VPR[va]._f[w] > CPU.VPR[vb]._f[w]) + { + CPU.VPR[vd]._u32[w] = 0xffffffff; + none_ge = 0; + } + else + { + CPU.VPR[vd]._u32[w] = 0; + all_ge = 0; + } + } + + CPU.CR.cr6 = all_ge | none_ge; + } + void VCMPGTSB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = CPU.VPR[va]._s8[b] > CPU.VPR[vb]._s8[b] ? 0xff : 0; + } + } + void VCMPGTSB_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_gt = 0x8; + int none_gt = 0x2; + + for (uint b = 0; b < 16; b++) + { + if (CPU.VPR[va]._s8[b] > CPU.VPR[vb]._s8[b]) + { + CPU.VPR[vd]._u8[b] = 0xff; + none_gt = 0; + } + else + { + CPU.VPR[vd]._u8[b] = 0; + all_gt = 0; + } + } + + CPU.CR.cr6 = all_gt | none_gt; + } + void VCMPGTSH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = CPU.VPR[va]._s16[h] > CPU.VPR[vb]._s16[h] ? 0xffff : 0; + } + } + void VCMPGTSH_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_gt = 0x8; + int none_gt = 0x2; + + for (uint h = 0; h < 8; h++) + { + if (CPU.VPR[va]._s16[h] > CPU.VPR[vb]._s16[h]) + { + CPU.VPR[vd]._u16[h] = 0xffff; + none_gt = 0; + } + else + { + CPU.VPR[vd]._u16[h] = 0; + all_gt = 0; + } + } + + CPU.CR.cr6 = all_gt | none_gt; + } + void VCMPGTSW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._s32[w] > CPU.VPR[vb]._s32[w] ? 0xffffffff : 0; + } + } + void VCMPGTSW_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_gt = 0x8; + int none_gt = 0x2; + + for (uint w = 0; w < 4; w++) + { + if (CPU.VPR[va]._s32[w] > CPU.VPR[vb]._s32[w]) + { + CPU.VPR[vd]._u32[w] = 0xffffffff; + none_gt = 0; + } + else + { + CPU.VPR[vd]._u32[w] = 0; + all_gt = 0; + } + } + + CPU.CR.cr6 = all_gt | none_gt; + } + void VCMPGTUB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = CPU.VPR[va]._u8[b] > CPU.VPR[vb]._u8[b] ? 0xff : 0; + } + } + void VCMPGTUB_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_gt = 0x8; + int none_gt = 0x2; + + for (uint b = 0; b < 16; b++) + { + if (CPU.VPR[va]._u8[b] > CPU.VPR[vb]._u8[b]) + { + CPU.VPR[vd]._u8[b] = 0xff; + none_gt = 0; + } + else + { + CPU.VPR[vd]._u8[b] = 0; + all_gt = 0; + } + } + + CPU.CR.cr6 = all_gt | none_gt; + } + void VCMPGTUH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = CPU.VPR[va]._u16[h] > CPU.VPR[vb]._u16[h] ? 0xffff : 0; + } + } + void VCMPGTUH_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_gt = 0x8; + int none_gt = 0x2; + + for (uint h = 0; h < 8; h++) + { + if (CPU.VPR[va]._u16[h] > CPU.VPR[vb]._u16[h]) + { + CPU.VPR[vd]._u16[h] = 0xffff; + none_gt = 0; + } + else + { + CPU.VPR[vd]._u16[h] = 0; + all_gt = 0; + } + } + + CPU.CR.cr6 = all_gt | none_gt; + } + void VCMPGTUW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] > CPU.VPR[vb]._u32[w] ? 0xffffffff : 0; + } + } + void VCMPGTUW_(OP_REG vd, OP_REG va, OP_REG vb) + { + int all_gt = 0x8; + int none_gt = 0x2; + + for (uint w = 0; w < 4; w++) + { + if (CPU.VPR[va]._u32[w] > CPU.VPR[vb]._u32[w]) + { + CPU.VPR[vd]._u32[w] = 0xffffffff; + none_gt = 0; + } + else + { + CPU.VPR[vd]._u32[w] = 0; + all_gt = 0; + } + } + + CPU.CR.cr6 = all_gt | none_gt; + } + void VCTSXS(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + int nScale = 1 << uimm5; + + for (uint w = 0; w < 4; w++) + { + // C rounding = Round towards 0 + s64 result = (s64)(CPU.VPR[vb]._f[w] * nScale); + + if (result > INT_MAX) + CPU.VPR[vd]._s32[w] = (int)INT_MAX; + else if (result < INT_MIN) + CPU.VPR[vd]._s32[w] = (int)INT_MIN; + else + CPU.VPR[vd]._s32[w] = (int)result; + } + } + void VCTUXS(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + int nScale = 1 << uimm5; + + for (uint w = 0; w < 4; w++) + { + // C rounding = Round towards 0 + s64 result = (s64)(CPU.VPR[vb]._f[w] * nScale); + + if (result > UINT_MAX) + CPU.VPR[vd]._u32[w] = (u32)UINT_MAX; + else if (result < 0) + CPU.VPR[vd]._u32[w] = 0; + else + CPU.VPR[vd]._u32[w] = (u32)result; + } + } + void VEXPTEFP(OP_REG vd, OP_REG vb) + { + // vd = exp(vb * log(2)) + // ISA : Note that the value placed into the element of vD may vary between implementations + // and between different executions on the same implementation. + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = exp(CPU.VPR[vb]._f[w] * log(2.0f)); + } + } + void VLOGEFP(OP_REG vd, OP_REG vb) + { + // ISA : Note that the value placed into the element of vD may vary between implementations + // and between different executions on the same implementation. + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = log(CPU.VPR[vb]._f[w]) / log(2.0f); + } + } + void VMADDFP(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = CPU.VPR[va]._f[w] * CPU.VPR[vc]._f[w] + CPU.VPR[vb]._f[w]; + } + } + void VMAXFP(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = max(CPU.VPR[va]._f[w], CPU.VPR[vb]._f[w]); + } + } + void VMAXSB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + CPU.VPR[vd]._s8[b] = max(CPU.VPR[va]._s8[b], CPU.VPR[vb]._s8[b]); + } + void VMAXSH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._s16[h] = max(CPU.VPR[va]._s16[h], CPU.VPR[vb]._s16[h]); + } + } + void VMAXSW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s32[w] = max(CPU.VPR[va]._s32[w], CPU.VPR[vb]._s32[w]); + } + } + void VMAXUB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + CPU.VPR[vd]._u8[b] = max(CPU.VPR[va]._u8[b], CPU.VPR[vb]._u8[b]); + } + void VMAXUH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = max(CPU.VPR[va]._u16[h], CPU.VPR[vb]._u16[h]); + } + } + void VMAXUW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = max(CPU.VPR[va]._u32[w], CPU.VPR[vb]._u32[w]); + } + } + void VMHADDSHS(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint h = 0; h < 8; h++) + { + s32 result = (s32)CPU.VPR[va]._s16[h] * (s32)CPU.VPR[vb]._s16[h] + (s32)CPU.VPR[vc]._s16[h]; + + if (result > INT16_MAX) + { + CPU.VPR[vd]._s16[h] = (s16)INT16_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < INT16_MIN) + { + CPU.VPR[vd]._s16[h] = (s16)INT16_MIN; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s16[h] = (s16)result; + } + } + void VMHRADDSHS(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint h = 0; h < 8; h++) + { + s32 result = (s32)CPU.VPR[va]._s16[h] * (s32)CPU.VPR[vb]._s16[h] + (s32)CPU.VPR[vc]._s16[h] + 0x4000; + + if (result > INT16_MAX) + { + CPU.VPR[vd]._s16[h] = (s16)INT16_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < INT16_MIN) + { + CPU.VPR[vd]._s16[h] = (s16)INT16_MIN; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s16[h] = (s16)result; + } + } + void VMINFP(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = min(CPU.VPR[va]._f[w], CPU.VPR[vb]._f[w]); + } + } + void VMINSB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._s8[b] = min(CPU.VPR[va]._s8[b], CPU.VPR[vb]._s8[b]); + } + } + void VMINSH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._s16[h] = min(CPU.VPR[va]._s16[h], CPU.VPR[vb]._s16[h]); + } + } + void VMINSW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s32[w] = min(CPU.VPR[va]._s32[w], CPU.VPR[vb]._s32[w]); + } + } + void VMINUB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = min(CPU.VPR[va]._u8[b], CPU.VPR[vb]._u8[b]); + } + } + void VMINUH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = min(CPU.VPR[va]._u16[h], CPU.VPR[vb]._u16[h]); + } + } + void VMINUW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = min(CPU.VPR[va]._u32[w], CPU.VPR[vb]._u32[w]); + } + } + void VMLADDUHM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = CPU.VPR[va]._u16[h] * CPU.VPR[vb]._u16[h] + CPU.VPR[vc]._u16[h]; + } + } + void VMRGHB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u8[h*2] = CPU.VPR[va]._u8[h]; + CPU.VPR[vd]._u8[h*2 + 1] = CPU.VPR[vb]._u8[h]; + } + } + void VMRGHH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u16[w*2] = CPU.VPR[va]._u16[w]; + CPU.VPR[vd]._u16[w*2 + 1] = CPU.VPR[vb]._u16[w]; + } + } + void VMRGHW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint d = 0; d < 2; d++) + { + CPU.VPR[vd]._u32[d*2] = CPU.VPR[va]._u32[d]; + CPU.VPR[vd]._u32[d*2 + 1] = CPU.VPR[vb]._u32[d]; + } + } + void VMRGLB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u8[h*2] = CPU.VPR[va]._u8[h + 8]; + CPU.VPR[vd]._u8[h*2 + 1] = CPU.VPR[vb]._u8[h + 8]; + } + } + void VMRGLH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u16[w*2] = CPU.VPR[va]._u16[w + 4]; + CPU.VPR[vd]._u16[w*2 + 1] = CPU.VPR[vb]._u16[w + 4]; + } + } + void VMRGLW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint d = 0; d < 2; d++) + { + CPU.VPR[vd]._u32[d*2] = CPU.VPR[va]._u32[d + 2]; + CPU.VPR[vd]._u32[d*2 + 1] = CPU.VPR[vb]._u32[d + 2]; + } + } + void VMSUMMBM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint w = 0; w < 4; w++) + { + s32 result = 0; + + for (uint b = 0; b < 4; b++) + { + result += CPU.VPR[va]._s8[w*4 + b] * CPU.VPR[vb]._u8[w*4 + b]; + } + + result += CPU.VPR[vc]._s32[w]; + CPU.VPR[vd]._s32[w] = result; + } + } + void VMSUMSHM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint w = 0; w < 4; w++) + { + s32 result = 0; + + for (uint h = 0; h < 2; h++) + { + result += CPU.VPR[va]._s16[w*2 + h] * CPU.VPR[vb]._s16[w*2 + h]; + } + + result += CPU.VPR[vc]._s32[w]; + CPU.VPR[vd]._s32[w] = result; + } + } + void VMSUMSHS(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint w = 0; w < 4; w++) + { + s64 result = 0; + s32 saturated = 0; + + for (uint h = 0; h < 2; h++) + { + result += CPU.VPR[va]._s16[w*2 + h] * CPU.VPR[vb]._s16[w*2 + h]; + } + + result += CPU.VPR[vc]._s32[w]; + + if (result > INT_MAX) + { + saturated = INT_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < INT_MIN) + { + saturated = INT_MIN; + CPU.VSCR.SAT = 1; + } + else + saturated = (s32)result; + + CPU.VPR[vd]._s32[w] = saturated; + } + } + void VMSUMUBM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint w = 0; w < 4; w++) + { + u32 result = 0; + + for (uint b = 0; b < 4; b++) + { + result += CPU.VPR[va]._u8[w*4 + b] * CPU.VPR[vb]._u8[w*4 + b]; + } + + result += CPU.VPR[vc]._u32[w]; + CPU.VPR[vd]._u32[w] = result; + } + } + void VMSUMUHM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint w = 0; w < 4; w++) + { + u32 result = 0; + + for (uint h = 0; h < 2; h++) + { + result += CPU.VPR[va]._u16[w*2 + h] * CPU.VPR[vb]._u16[w*2 + h]; + } + + result += CPU.VPR[vc]._u32[w]; + CPU.VPR[vd]._u32[w] = result; + } + } + void VMSUMUHS(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint w = 0; w < 4; w++) + { + u64 result = 0; + u32 saturated = 0; + + for (uint h = 0; h < 2; h++) + { + result += CPU.VPR[va]._u16[w*2 + h] * CPU.VPR[vb]._u16[w*2 + h]; + } + + result += CPU.VPR[vc]._u32[w]; + + if (result > UINT_MAX) + { + saturated = UINT_MAX; + CPU.VSCR.SAT = 1; + } + else + saturated = (u32)result; + + CPU.VPR[vd]._u32[w] = saturated; + } + } + void VMULESB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._s16[h] = (s16)CPU.VPR[va]._s8[h*2+1] * (s16)CPU.VPR[vb]._s8[h*2+1]; + } + } + void VMULESH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s32[w] = (s32)CPU.VPR[va]._s16[w*2+1] * (s32)CPU.VPR[vb]._s16[w*2+1]; + } + } + void VMULEUB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = (u16)CPU.VPR[va]._u8[h*2+1] * (u16)CPU.VPR[vb]._u8[h*2+1]; + } + } + void VMULEUH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = (u32)CPU.VPR[va]._u16[w*2+1] * (u32)CPU.VPR[vb]._u16[w*2+1]; + } + } + void VMULOSB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._s16[h] = (s16)CPU.VPR[va]._s8[h*2] * (s16)CPU.VPR[vb]._s8[h*2]; + } + } + void VMULOSH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s32[w] = (s32)CPU.VPR[va]._s16[w*2] * (s32)CPU.VPR[vb]._s16[w*2]; + } + } + void VMULOUB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = (u16)CPU.VPR[va]._u8[h*2] * (u16)CPU.VPR[vb]._u8[h*2]; + } + } + void VMULOUH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = (u32)CPU.VPR[va]._u16[w*2] * (u32)CPU.VPR[vb]._u16[w*2]; + } + } + void VNMSUBFP(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = (double)CPU.VPR[vb]._f[w] - (double)CPU.VPR[va]._f[w] * (double)CPU.VPR[vc]._f[w]; + } + } + void VNOR(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = ~(CPU.VPR[va]._u32[w] | CPU.VPR[vb]._u32[w]); + } + } + void VOR(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] | CPU.VPR[vb]._u32[w]; + } + } + void VPERM(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint b = 0; b < 16; b++) + { + u8 index = CPU.VPR[vc]._u8[b] & 0x1f; + + CPU.VPR[vd]._u8[b] = index < 0x10 ? CPU.VPR[va]._u8[0xf - index] : CPU.VPR[vb]._u8[0xf - (index - 0x10)]; + } + } + void VPKPX(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 4; h++) + { + u16 bb7 = CPU.VPR[vb]._u8[15 - (h*4 + 0)] & 0x1; + u16 bb8 = CPU.VPR[vb]._u8[15 - (h*4 + 1)] >> 3; + u16 bb16 = CPU.VPR[vb]._u8[15 - (h*4 + 2)] >> 3; + u16 bb24 = CPU.VPR[vb]._u8[15 - (h*4 + 3)] >> 3; + u16 ab7 = CPU.VPR[va]._u8[15 - (h*4 + 0)] & 0x1; + u16 ab8 = CPU.VPR[va]._u8[15 - (h*4 + 1)] >> 3; + u16 ab16 = CPU.VPR[va]._u8[15 - (h*4 + 2)] >> 3; + u16 ab24 = CPU.VPR[va]._u8[15 - (h*4 + 3)] >> 3; + + CPU.VPR[vd]._u16[3 - h] = (bb7 << 15) | (bb8 << 10) | (bb16 << 5) | bb24; + CPU.VPR[vd]._u16[4 + (3 - h)] = (ab7 << 15) | (ab8 << 10) | (ab16 << 5) | ab24; + } + } + void VPKSHSS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 8; b++) + { + s16 result = CPU.VPR[va]._s16[b]; + + if (result > INT8_MAX) + { + result = INT8_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < INT8_MIN) + { + result = INT8_MIN; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._s8[b+8] = result; + + result = CPU.VPR[vb]._s16[b]; + + if (result > INT8_MAX) + { + result = INT8_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < INT8_MIN) + { + result = INT8_MIN; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._s8[b] = result; + } + } + void VPKSHUS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 8; b++) + { + s16 result = CPU.VPR[va]._s16[b]; + + if (result > UINT8_MAX) + { + result = UINT8_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < 0) + { + result = 0; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._u8[b+8] = result; + + result = CPU.VPR[vb]._s16[b]; + + if (result > UINT8_MAX) + { + result = UINT8_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < 0) + { + result = 0; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._u8[b] = result; + } + } + void VPKSWSS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 4; h++) + { + s32 result = CPU.VPR[va]._s32[h]; + + if (result > INT16_MAX) + { + result = INT16_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < INT16_MIN) + { + result = INT16_MIN; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._s16[h+4] = result; + + result = CPU.VPR[vb]._s32[h]; + + if (result > INT16_MAX) + { + result = INT16_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < INT16_MIN) + { + result = INT16_MIN; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._s16[h] = result; + } + } + void VPKSWUS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 4; h++) + { + s32 result = CPU.VPR[va]._s32[h]; + + if (result > UINT16_MAX) + { + result = UINT16_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < 0) + { + result = 0; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._u16[h+4] = result; + + result = CPU.VPR[vb]._s32[h]; + + if (result > UINT16_MAX) + { + result = UINT16_MAX; + CPU.VSCR.SAT = 1; + } + else if (result < 0) + { + result = 0; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._u16[h] = result; + } + } + void VPKUHUM(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 8; b++) + { + CPU.VPR[vd]._u8[b+8] = CPU.VPR[va]._u8[b*2]; + CPU.VPR[vd]._u8[b ] = CPU.VPR[vb]._u8[b*2]; + } + } + void VPKUHUS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 8; b++) + { + u16 result = CPU.VPR[va]._u16[b]; + + if (result > UINT8_MAX) + { + result = UINT8_MAX; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._u8[b+8] = result; + + result = CPU.VPR[vb]._u16[b]; + + if (result > UINT8_MAX) + { + result = UINT8_MAX; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._u8[b] = result; + } + } + void VPKUWUM(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 4; h++) + { + CPU.VPR[vd]._u16[h+4] = CPU.VPR[va]._u16[h*2]; + CPU.VPR[vd]._u16[h ] = CPU.VPR[vb]._u16[h*2]; + } + } + void VPKUWUS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 4; h++) + { + u32 result = CPU.VPR[va]._u32[h]; + + if (result > UINT16_MAX) + { + result = UINT16_MAX; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._u16[h+4] = result; + + result = CPU.VPR[vb]._u32[h]; + + if (result > UINT16_MAX) + { + result = UINT16_MAX; + CPU.VSCR.SAT = 1; + } + + CPU.VPR[vd]._u16[h] = result; + } + } + void VREFP(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = 1.0f / CPU.VPR[vb]._f[w]; + } + } + void VRFIM(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = floor(CPU.VPR[vb]._f[w]); + } + } + void VRFIN(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = floor(CPU.VPR[vb]._f[w] + 0.5f); + } + } + void VRFIP(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = ceil(CPU.VPR[vb]._f[w]); + } + } + void VRFIZ(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + float f; + modf(CPU.VPR[vb]._f[w], &f); + CPU.VPR[vd]._f[w] = f; + } + } + void VRLB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + int nRot = CPU.VPR[vb]._u8[b] & 0x7; + + CPU.VPR[vd]._u8[b] = (CPU.VPR[va]._u8[b] << nRot) | (CPU.VPR[va]._u8[b] >> (8 - nRot)); + } + } + void VRLH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = rotl16(CPU.VPR[va]._u16[h], CPU.VPR[vb]._u8[h*2] & 0xf); + } + } + void VRLW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = rotl32(CPU.VPR[va]._u32[w], CPU.VPR[vb]._u8[w*4] & 0x1f); + } + } + void VRSQRTEFP(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + //TODO: accurate div + CPU.VPR[vd]._f[w] = 1.0f / sqrtf(CPU.VPR[vb]._f[w]); + } + } + void VSEL(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = (CPU.VPR[vb]._u8[b] & CPU.VPR[vc]._u8[b]) | (CPU.VPR[va]._u8[b] & (~CPU.VPR[vc]._u8[b])); + } + } + void VSL(OP_REG vd, OP_REG va, OP_REG vb) + { + u8 sh = CPU.VPR[vb]._u8[0] & 0x7; + + u32 t = 1; + + for (uint b = 0; b < 16; b++) + { + t &= (CPU.VPR[vb]._u8[b] & 0x7) == sh; + } + + if(t) + { + CPU.VPR[vd]._u8[0] = CPU.VPR[va]._u8[0] << sh; + + for (uint b = 1; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = (CPU.VPR[va]._u8[b] << sh) | (CPU.VPR[va]._u8[b-1] >> (8 - sh)); + } + } + else + { + //undefined + CPU.VPR[vd]._u32[0] = ((rand() % 0x100) << 24) | ((rand() % 0x100) << 16) | ((rand() % 0x100) << 8) | (rand() % 0x100); + CPU.VPR[vd]._u32[1] = ((rand() % 0x100) << 24) | ((rand() % 0x100) << 16) | ((rand() % 0x100) << 8) | (rand() % 0x100); + CPU.VPR[vd]._u32[2] = ((rand() % 0x100) << 24) | ((rand() % 0x100) << 16) | ((rand() % 0x100) << 8) | (rand() % 0x100); + CPU.VPR[vd]._u32[3] = ((rand() % 0x100) << 24) | ((rand() % 0x100) << 16) | ((rand() % 0x100) << 8) | (rand() % 0x100); + } + } + void VSLB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = CPU.VPR[va]._u8[b] << (CPU.VPR[vb]._u8[b] & 0x7); + } + } + void VSLDOI(OP_REG vd, OP_REG va, OP_REG vb, OP_uIMM sh) + { + for (uint b = 0; b < 16 - sh; b++) + { + CPU.VPR[vd]._u8[15 - b] = CPU.VPR[va]._u8[15 - (b + sh)]; + } + for (uint b = 16 - sh; b < 16; b++) + { + CPU.VPR[vd]._u8[15 - b] = CPU.VPR[vb]._u8[15 - (b - (16 - sh))]; + } + } + void VSLH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = CPU.VPR[va]._u16[h] << (CPU.VPR[vb]._u8[h*2] & 0xf); + } + } + void VSLO(OP_REG vd, OP_REG va, OP_REG vb) + { + u8 nShift = (CPU.VPR[vb]._u8[0] >> 3) & 0xf; + + CPU.VPR[vd].Clear(); + + for (u8 b = 0; b < 16 - nShift; b++) + { + CPU.VPR[vd]._u8[15 - b] = CPU.VPR[va]._u8[15 - (b + nShift)]; + } + } + void VSLW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] << (CPU.VPR[vb]._u8[w*4] & 0x1f); + } + } + void VSPLTB(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + u8 byte = CPU.VPR[vb]._u8[15 - uimm5]; + + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = byte; + } + } + void VSPLTH(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + assert(uimm5 < 8); + + u16 hword = CPU.VPR[vb]._u16[7 - uimm5]; + + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = hword; + } + } + void VSPLTISB(OP_REG vd, OP_sIMM simm5) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = simm5; + } + } + void VSPLTISH(OP_REG vd, OP_sIMM simm5) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = (s16)simm5; + } + } + void VSPLTISW(OP_REG vd, OP_sIMM simm5) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = (s32)simm5; + } + } + void VSPLTW(OP_REG vd, OP_uIMM uimm5, OP_REG vb) + { + assert(uimm5 < 4); + + u32 word = CPU.VPR[vb]._u32[uimm5]; + + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = word; + } + } + void VSR(OP_REG vd, OP_REG va, OP_REG vb) + { + u8 sh = CPU.VPR[vb]._u8[15] & 0x7; + + CPU.VPR[vd]._u32[0] = CPU.VPR[va]._u32[0] >> sh; + + for (uint w = 1; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = (CPU.VPR[va]._u32[w] >> sh) | (CPU.VPR[va]._u32[w - 1] << (32 - sh)); + } + } + void VSRAB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._s8[b] = CPU.VPR[va]._s8[b] >> (CPU.VPR[vb]._u8[b] & 0x7); + } + } + void VSRAH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._s16[h] = CPU.VPR[va]._s16[h] >> (CPU.VPR[vb]._u8[h*2 + 1] & 0xf); + } + } + void VSRAW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s32[w] = CPU.VPR[va]._s32[w] >> (CPU.VPR[vb]._u8[w*4 + 3] & 0x1f); + } + } + void VSRB(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = CPU.VPR[va]._u8[b] >> (CPU.VPR[vb]._u8[b] & 0x7); + } + } + void VSRH(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = CPU.VPR[va]._u16[h] >> (CPU.VPR[vb]._u8[h*2 + 1] & 0xf); + } + } + void VSRO(OP_REG vd, OP_REG va, OP_REG vb) + { + u8 nShift = (CPU.VPR[vb]._u8[15] >> 3) & 0xf; + + CPU.VPR[vd].Clear(); + + for (u8 b = nShift; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = CPU.VPR[va]._u8[b - nShift]; + } + } + void VSRW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] >> (CPU.VPR[vb]._u8[w*4 + 3] & 0x1f); + } + } + void VSUBCUW(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] < CPU.VPR[vb]._u32[w] ? 0 : 1; + } + } + void VSUBFP(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._f[w] = CPU.VPR[va]._f[w] - CPU.VPR[vb]._f[w]; + } + } + void VSUBSBS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + s16 result = (s16)CPU.VPR[va]._s8[b] - (s16)CPU.VPR[vb]._s8[b]; + + if (result < INT8_MIN) + { + CPU.VPR[vd]._s8[b] = INT8_MIN; + CPU.VSCR.SAT = 1; + } + else if (result > INT8_MAX) + { + CPU.VPR[vd]._s8[b] = INT8_MAX; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s8[b] = (s8)result; + } + } + void VSUBSHS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + s32 result = (s32)CPU.VPR[va]._s16[h] - (s32)CPU.VPR[vb]._s16[h]; + + if (result < INT16_MIN) + { + CPU.VPR[vd]._s16[h] = (s16)INT16_MIN; + CPU.VSCR.SAT = 1; + } + else if (result > INT16_MAX) + { + CPU.VPR[vd]._s16[h] = (s16)INT16_MAX; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s16[h] = (s16)result; + } + } + void VSUBSWS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + s64 result = (s64)CPU.VPR[va]._s32[w] - (s64)CPU.VPR[vb]._s32[w]; + + if (result < INT32_MIN) + { + CPU.VPR[vd]._s32[w] = (s32)INT32_MIN; + CPU.VSCR.SAT = 1; + } + else if (result > INT32_MAX) + { + CPU.VPR[vd]._s32[w] = (s32)INT32_MAX; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s32[w] = (s32)result; + } + } + void VSUBUBM(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + CPU.VPR[vd]._u8[b] = (u8)((CPU.VPR[va]._u8[b] - CPU.VPR[vb]._u8[b]) & 0xff); + } + } + void VSUBUBS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint b = 0; b < 16; b++) + { + s16 result = (s16)CPU.VPR[va]._u8[b] - (s16)CPU.VPR[vb]._u8[b]; + + if (result < 0) + { + CPU.VPR[vd]._u8[b] = 0; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._u8[b] = (u8)result; + } + } + void VSUBUHM(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._u16[h] = CPU.VPR[va]._u16[h] - CPU.VPR[vb]._u16[h]; + } + } + void VSUBUHS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + s32 result = (s32)CPU.VPR[va]._u16[h] - (s32)CPU.VPR[vb]._u16[h]; + + if (result < 0) + { + CPU.VPR[vd]._u16[h] = 0; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._u16[h] = (u16)result; + } + } + void VSUBUWM(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._u32[w] = CPU.VPR[va]._u32[w] - CPU.VPR[vb]._u32[w]; + } + } + void VSUBUWS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + s64 result = (s64)CPU.VPR[va]._u32[w] - (s64)CPU.VPR[vb]._u32[w]; + + if (result < 0) + { + CPU.VPR[vd]._u32[w] = 0; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._u32[w] = (u32)result; + } + } + void VSUMSWS(OP_REG vd, OP_REG va, OP_REG vb) + { + CPU.VPR[vd].Clear(); + + s64 sum = CPU.VPR[vb]._s32[3]; + + for (uint w = 0; w < 4; w++) + { + sum += CPU.VPR[va]._s32[w]; + } + + if (sum > INT32_MAX) + { + CPU.VPR[vd]._s32[3] = (s32)INT32_MAX; + CPU.VSCR.SAT = 1; + } + else if (sum < INT32_MIN) + { + CPU.VPR[vd]._s32[3] = (s32)INT32_MIN; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s32[3] = (s32)sum; + } + void VSUM2SWS(OP_REG vd, OP_REG va, OP_REG vb) + { + CPU.VPR[vd].Clear(); + + for (uint n = 0; n < 2; n++) + { + s64 sum = CPU.VPR[vb]._s32[n*2 + 1]; + + for (uint w = 0; w < 2; w++) + { + sum += CPU.VPR[va]._s32[n*2 + w]; + } + + if (sum > INT32_MAX) + { + CPU.VPR[vd]._s32[n*2 + 1] = (s32)INT32_MAX; + CPU.VSCR.SAT = 1; + } + else if (sum < INT32_MIN) + { + CPU.VPR[vd]._s32[n*2 + 1] = (s32)INT32_MIN; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s32[n*2 + 1] = (s32)sum; + } + } + void VSUM4SBS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + s64 sum = CPU.VPR[vb]._s32[w]; + + for (uint b = 0; b < 4; b++) + { + sum += CPU.VPR[va]._s8[w*4 + b]; + } + + if (sum > INT32_MAX) + { + CPU.VPR[vd]._s32[w] = (s32)INT32_MAX; + CPU.VSCR.SAT = 1; + } + else if (sum < INT32_MIN) + { + CPU.VPR[vd]._s32[w] = (s32)INT32_MIN; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s32[w] = (s32)sum; + } + } + void VSUM4SHS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + s64 sum = CPU.VPR[vb]._s32[w]; + + for (uint h = 0; h < 2; h++) + { + sum += CPU.VPR[va]._s16[w*2 + h]; + } + + if (sum > INT32_MAX) + { + CPU.VPR[vd]._s32[w] = (s32)INT32_MAX; + CPU.VSCR.SAT = 1; + } + else if (sum < INT32_MIN) + { + CPU.VPR[vd]._s32[w] = (s32)INT32_MIN; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._s32[w] = (s32)sum; + } + } + void VSUM4UBS(OP_REG vd, OP_REG va, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + u64 sum = CPU.VPR[vb]._u32[w]; + + for (uint b = 0; b < 4; b++) + { + sum += CPU.VPR[va]._u8[w*4 + b]; + } + + if (sum > UINT32_MAX) + { + CPU.VPR[vd]._u32[w] = (u32)UINT32_MAX; + CPU.VSCR.SAT = 1; + } + else + CPU.VPR[vd]._u32[w] = (u32)sum; + } + } + void VUPKHPX(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s8[w*4 + 0] = CPU.VPR[vb]._s8[w*2 + 0] >> 7; // signed shift sign extends + CPU.VPR[vd]._u8[w*4 + 1] = (CPU.VPR[vb]._u8[w*2 + 0] >> 2) & 0x1f; + CPU.VPR[vd]._u8[w*4 + 2] = ((CPU.VPR[vb]._u8[w*2 + 0] & 0x3) << 3) | ((CPU.VPR[vb]._u8[w*2 + 1] >> 5) & 0x7); + CPU.VPR[vd]._u8[w*4 + 3] = CPU.VPR[vb]._u8[w*2 + 1] & 0x1f; + } + } + void VUPKHSB(OP_REG vd, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._s16[h] = CPU.VPR[vb]._s8[h]; + } + } + void VUPKHSH(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s32[w] = CPU.VPR[vb]._s16[w]; + } + } + void VUPKLPX(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s8[w*4 + 0] = CPU.VPR[vb]._s8[8 + w*2 + 0] >> 7; // signed shift sign extends + CPU.VPR[vd]._u8[w*4 + 1] = (CPU.VPR[vb]._u8[8 + w*2 + 0] >> 2) & 0x1f; + CPU.VPR[vd]._u8[w*4 + 2] = ((CPU.VPR[vb]._u8[8 + w*2 + 0] & 0x3) << 3) | ((CPU.VPR[vb]._u8[8 + w*2 + 1] >> 5) & 0x7); + CPU.VPR[vd]._u8[w*4 + 3] = CPU.VPR[vb]._u8[8 + w*2 + 1] & 0x1f; + } + } + void VUPKLSB(OP_REG vd, OP_REG vb) + { + for (uint h = 0; h < 8; h++) + { + CPU.VPR[vd]._s16[h] = CPU.VPR[vb]._s8[8 + h]; + } + } + void VUPKLSH(OP_REG vd, OP_REG vb) + { + for (uint w = 0; w < 4; w++) + { + CPU.VPR[vd]._s32[w] = CPU.VPR[vb]._s16[4 + w]; + } + } + void VXOR(OP_REG vd, OP_REG va, OP_REG vb) + { + CPU.VPR[vd]._u32[0] = CPU.VPR[va]._u32[0] ^ CPU.VPR[vb]._u32[0]; + CPU.VPR[vd]._u32[1] = CPU.VPR[va]._u32[1] ^ CPU.VPR[vb]._u32[1]; + CPU.VPR[vd]._u32[2] = CPU.VPR[va]._u32[2] ^ CPU.VPR[vb]._u32[2]; + CPU.VPR[vd]._u32[3] = CPU.VPR[va]._u32[3] ^ CPU.VPR[vb]._u32[3]; } END_OPCODES_GROUP(G_04); - virtual void MULLI(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void MULLI(OP_REG rd, OP_REG ra, OP_sIMM simm16) { CPU.GPR[rd] = (s64)CPU.GPR[ra] * simm16; } - virtual void SUBFIC(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void SUBFIC(OP_REG rd, OP_REG ra, OP_sIMM simm16) { s64 RA = CPU.GPR[ra]; CPU.GPR[rd] = (s64)simm16 - RA; CPU.XER.CA = RA <= simm16; } - virtual void CMPLI(OP_REG crfd, OP_REG l, OP_REG ra, OP_uIMM uimm16) + void CMPLI(OP_REG crfd, OP_REG l, OP_REG ra, OP_uIMM uimm16) { CPU.UpdateCRn(crfd, l ? CPU.GPR[ra] : (u32)CPU.GPR[ra], uimm16); } - virtual void CMPI(OP_REG crfd, OP_REG l, OP_REG ra, OP_sIMM simm16) + void CMPI(OP_REG crfd, OP_REG l, OP_REG ra, OP_sIMM simm16) { CPU.UpdateCRn(crfd, l ? CPU.GPR[ra] : (s32)CPU.GPR[ra], simm16); } - virtual void ADDIC(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void ADDIC(OP_REG rd, OP_REG ra, OP_sIMM simm16) { const u64 RA = CPU.GPR[ra]; CPU.GPR[rd] = RA + simm16; CPU.XER.CA = CPU.IsCarry(RA, simm16); } - virtual void ADDIC_(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void ADDIC_(OP_REG rd, OP_REG ra, OP_sIMM simm16) { const u64 RA = CPU.GPR[ra]; CPU.GPR[rd] = RA + simm16; CPU.XER.CA = CPU.IsCarry(RA, simm16); CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void ADDI(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void ADDI(OP_REG rd, OP_REG ra, OP_sIMM simm16) { CPU.GPR[rd] = ra ? ((s64)CPU.GPR[ra] + simm16) : simm16; } - virtual void ADDIS(OP_REG rd, OP_REG ra, OP_sIMM simm16) + void ADDIS(OP_REG rd, OP_REG ra, OP_sIMM simm16) { CPU.GPR[rd] = ra ? ((s64)CPU.GPR[ra] + (simm16 << 16)) : (simm16 << 16); } - virtual void BC(OP_REG bo, OP_REG bi, OP_sIMM bd, OP_REG aa, OP_REG lk) + void BC(OP_REG bo, OP_REG bi, OP_sIMM bd, OP_REG aa, OP_REG lk) { if(!CheckCondition(bo, bi)) return; CPU.SetBranch(branchTarget((aa ? 0 : CPU.PC), bd)); if(lk) CPU.LR = CPU.PC + 4; } - virtual void SC(const s32 sc_code) + void SC(const s32 sc_code) { switch(sc_code) { @@ -201,67 +2064,67 @@ private: default: UNK(wxString::Format("Unknown sc: %x", sc_code)); } } - virtual void B(OP_sIMM ll, OP_REG aa, OP_REG lk) + void B(OP_sIMM ll, OP_REG aa, OP_REG lk) { CPU.SetBranch(branchTarget(aa ? 0 : CPU.PC, ll)); if(lk) CPU.LR = CPU.PC + 4; } START_OPCODES_GROUP(G_13) - virtual void MCRF(OP_REG crfd, OP_REG crfs) + void MCRF(OP_REG crfd, OP_REG crfs) { CPU.SetCR(crfd, CPU.GetCR(crfs)); } - virtual void BCLR(OP_REG bo, OP_REG bi, OP_REG bh, OP_REG lk) + void BCLR(OP_REG bo, OP_REG bi, OP_REG bh, OP_REG lk) { if(!CheckCondition(bo, bi)) return; CPU.SetBranch(branchTarget(0, CPU.LR)); if(lk) CPU.LR = CPU.PC + 4; } - virtual void CRNOR(OP_REG bt, OP_REG ba, OP_REG bb) + void CRNOR(OP_REG bt, OP_REG ba, OP_REG bb) { const u8 v = 1 ^ (CPU.IsCR(ba) | CPU.IsCR(bb)); CPU.SetCRBit2(bt, v & 0x1); } - virtual void CRANDC(OP_REG bt, OP_REG ba, OP_REG bb) + void CRANDC(OP_REG bt, OP_REG ba, OP_REG bb) { const u8 v = CPU.IsCR(ba) & (1 ^ CPU.IsCR(bb)); CPU.SetCRBit2(bt, v & 0x1); } - virtual void ISYNC() + void ISYNC() { } - virtual void CRXOR(OP_REG bt, OP_REG ba, OP_REG bb) + void CRXOR(OP_REG bt, OP_REG ba, OP_REG bb) { const u8 v = CPU.IsCR(ba) ^ CPU.IsCR(bb); CPU.SetCRBit2(bt, v & 0x1); } - virtual void CRNAND(OP_REG bt, OP_REG ba, OP_REG bb) + void CRNAND(OP_REG bt, OP_REG ba, OP_REG bb) { const u8 v = 1 ^ (CPU.IsCR(ba) & CPU.IsCR(bb)); CPU.SetCRBit2(bt, v & 0x1); } - virtual void CRAND(OP_REG bt, OP_REG ba, OP_REG bb) + void CRAND(OP_REG bt, OP_REG ba, OP_REG bb) { const u8 v = CPU.IsCR(ba) & CPU.IsCR(bb); CPU.SetCRBit2(bt, v & 0x1); } - virtual void CREQV(OP_REG bt, OP_REG ba, OP_REG bb) + void CREQV(OP_REG bt, OP_REG ba, OP_REG bb) { const u8 v = 1 ^ (CPU.IsCR(ba) ^ CPU.IsCR(bb)); CPU.SetCRBit2(bt, v & 0x1); } - virtual void CRORC(OP_REG bt, OP_REG ba, OP_REG bb) + void CRORC(OP_REG bt, OP_REG ba, OP_REG bb) { const u8 v = CPU.IsCR(ba) | (1 ^ CPU.IsCR(bb)); CPU.SetCRBit2(bt, v & 0x1); } - virtual void CROR(OP_REG bt, OP_REG ba, OP_REG bb) + void CROR(OP_REG bt, OP_REG ba, OP_REG bb) { const u8 v = CPU.IsCR(ba) | CPU.IsCR(bb); CPU.SetCRBit2(bt, v & 0x1); } - virtual void BCCTR(OP_REG bo, OP_REG bi, OP_REG bh, OP_REG lk) + void BCCTR(OP_REG bo, OP_REG bi, OP_REG bh, OP_REG lk) { if(bo & 0x10 || CPU.IsCR(bi) == (bo & 0x8)) { @@ -271,66 +2134,66 @@ private: } END_OPCODES_GROUP(G_13); - virtual void RLWIMI(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, OP_REG me, bool rc) + void RLWIMI(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, OP_REG me, bool rc) { const u32 mask = rotate_mask[32 + mb][32 + me]; CPU.GPR[ra] = (CPU.GPR[ra] & ~mask) | (rotl32(CPU.GPR[rs], sh) & mask); if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void RLWINM(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, OP_REG me, bool rc) + void RLWINM(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, OP_REG me, bool rc) { CPU.GPR[ra] = rotl32(CPU.GPR[rs], sh) & rotate_mask[32 + mb][32 + me]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void RLWNM(OP_REG ra, OP_REG rs, OP_REG rb, OP_REG mb, OP_REG me, bool rc) + void RLWNM(OP_REG ra, OP_REG rs, OP_REG rb, OP_REG mb, OP_REG me, bool rc) { CPU.GPR[ra] = rotl32(CPU.GPR[rs], CPU.GPR[rb] & 0x1f) & rotate_mask[32 + mb][32 + me]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void ORI(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void ORI(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { CPU.GPR[ra] = CPU.GPR[rs] | uimm16; } - virtual void ORIS(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void ORIS(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { CPU.GPR[ra] = CPU.GPR[rs] | (uimm16 << 16); } - virtual void XORI(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void XORI(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { CPU.GPR[ra] = CPU.GPR[rs] ^ uimm16; } - virtual void XORIS(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void XORIS(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { CPU.GPR[ra] = CPU.GPR[rs] ^ (uimm16 << 16); } - virtual void ANDI_(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void ANDI_(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { CPU.GPR[ra] = CPU.GPR[rs] & uimm16; CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void ANDIS_(OP_REG ra, OP_REG rs, OP_uIMM uimm16) + void ANDIS_(OP_REG ra, OP_REG rs, OP_uIMM uimm16) { CPU.GPR[ra] = CPU.GPR[rs] & (uimm16 << 16); CPU.UpdateCR0(CPU.GPR[ra]); } START_OPCODES_GROUP(G_1e) - virtual void RLDICL(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) + void RLDICL(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) { CPU.GPR[ra] = rotl64(CPU.GPR[rs], sh) & rotate_mask[mb][63]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void RLDICR(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG me, bool rc) + void RLDICR(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG me, bool rc) { CPU.GPR[ra] = rotl64(CPU.GPR[rs], sh) & rotate_mask[0][me]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void RLDIC(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) + void RLDIC(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) { CPU.GPR[ra] = rotl64(CPU.GPR[rs], sh) & rotate_mask[mb][63-sh]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void RLDIMI(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) + void RLDIMI(OP_REG ra, OP_REG rs, OP_REG sh, OP_REG mb, bool rc) { const u64 mask = rotate_mask[mb][63-sh]; CPU.GPR[ra] = (CPU.GPR[ra] & ~mask) | (rotl64(CPU.GPR[rs], sh) & mask); @@ -339,11 +2202,11 @@ private: END_OPCODES_GROUP(G_1e); START_OPCODES_GROUP(G_1f) - virtual void CMP(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb) + void CMP(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb) { CPU.UpdateCRn(crfd, l ? CPU.GPR[ra] : (s32)CPU.GPR[ra], l ? CPU.GPR[rb] : (s32)CPU.GPR[rb]); } - virtual void TW(OP_uIMM to, OP_REG ra, OP_REG rb) + void TW(OP_uIMM to, OP_REG ra, OP_REG rb) { s32 a = CPU.GPR[ra]; s32 b = CPU.GPR[rb]; @@ -354,16 +2217,41 @@ private: ((u32)a < (u32)b && (to & 0x2)) || ((u32)a > (u32)b && (to & 0x1)) ) { - UNK(wxString::Format("Trap! (tw %x, r%d, r%d)", to, ra, rb), false); + UNK(wxString::Format("Trap! (tw %x, r%d, r%d)", to, ra, rb)); } } - virtual void LVEBX(OP_REG vd, OP_REG ra, OP_REG rb) + void LVSL(OP_REG vd, OP_REG ra, OP_REG rb) { const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; - CPU.VPR[vd].Clear(); - CPU.VPR[vd].b(addr & 0xf) = Memory.Read8(addr); + + switch(addr & 0xf) + { + case 0x0: CPU.VPR[vd]._u64[1] = 0x0001020304050607; CPU.VPR[vd]._u64[0] = 0x08090A0B0C0D0E0F; break; + case 0x1: CPU.VPR[vd]._u64[1] = 0x0102030405060708; CPU.VPR[vd]._u64[0] = 0x090A0B0C0D0E0F10; break; + case 0x2: CPU.VPR[vd]._u64[1] = 0x0203040506070809; CPU.VPR[vd]._u64[0] = 0x0A0B0C0D0E0F1011; break; + case 0x3: CPU.VPR[vd]._u64[1] = 0x030405060708090A; CPU.VPR[vd]._u64[0] = 0x0B0C0D0E0F101112; break; + case 0x4: CPU.VPR[vd]._u64[1] = 0x0405060708090A0B; CPU.VPR[vd]._u64[0] = 0x0C0D0E0F10111213; break; + case 0x5: CPU.VPR[vd]._u64[1] = 0x05060708090A0B0C; CPU.VPR[vd]._u64[0] = 0x0D0E0F1011121314; break; + case 0x6: CPU.VPR[vd]._u64[1] = 0x060708090A0B0C0D; CPU.VPR[vd]._u64[0] = 0x0E0F101112131415; break; + case 0x7: CPU.VPR[vd]._u64[1] = 0x0708090A0B0C0D0E; CPU.VPR[vd]._u64[0] = 0x0F10111213141516; break; + case 0x8: CPU.VPR[vd]._u64[1] = 0x08090A0B0C0D0E0F; CPU.VPR[vd]._u64[0] = 0x1011121314151617; break; + case 0x9: CPU.VPR[vd]._u64[1] = 0x090A0B0C0D0E0F10; CPU.VPR[vd]._u64[0] = 0x1112131415161718; break; + case 0xa: CPU.VPR[vd]._u64[1] = 0x0A0B0C0D0E0F1011; CPU.VPR[vd]._u64[0] = 0x1213141516171819; break; + case 0xb: CPU.VPR[vd]._u64[1] = 0x0B0C0D0E0F101112; CPU.VPR[vd]._u64[0] = 0x131415161718191A; break; + case 0xc: CPU.VPR[vd]._u64[1] = 0x0C0D0E0F10111213; CPU.VPR[vd]._u64[0] = 0x1415161718191A1B; break; + case 0xd: CPU.VPR[vd]._u64[1] = 0x0D0E0F1011121314; CPU.VPR[vd]._u64[0] = 0x15161718191A1B1C; break; + case 0xe: CPU.VPR[vd]._u64[1] = 0x0E0F101112131415; CPU.VPR[vd]._u64[0] = 0x161718191A1B1C1D; break; + case 0xf: CPU.VPR[vd]._u64[1] = 0x0F10111213141516; CPU.VPR[vd]._u64[0] = 0x1718191A1B1C1D1E; break; + } } - virtual void SUBFC(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void LVEBX(OP_REG vd, OP_REG ra, OP_REG rb) + { + //const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + //CPU.VPR[vd].Clear(); + //CPU.VPR[vd]._u8[addr & 0xf] = Memory.Read8(addr); + CPU.VPR[vd]._u128 = Memory.Read128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~0xfULL); + } + void SUBFC(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const s64 RA = CPU.GPR[ra]; const s64 RB = CPU.GPR[rb]; @@ -372,7 +2260,7 @@ private: if(oe) UNK("subfco"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void ADDC(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void ADDC(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const s64 RA = CPU.GPR[ra]; const s64 RB = CPU.GPR[rb]; @@ -381,7 +2269,7 @@ private: if(oe) UNK("addco"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void MULHDU(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) + void MULHDU(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) { const u64 RA = CPU.GPR[ra]; const u64 RB = CPU.GPR[rb]; @@ -406,19 +2294,19 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void MULHWU(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) + void MULHWU(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) { CPU.GPR[rd] = (CPU.GPR[ra] * CPU.GPR[rb]) >> 32; if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void MFOCRF(OP_REG a, OP_REG fxm, OP_REG rd) + void MFOCRF(OP_uIMM a, OP_REG rd, OP_uIMM crm) { if(a) { u32 n = 0, count = 0; for(u32 i = 0; i < 8; ++i) { - if(fxm & (1 << i)) + if(crm & (1 << i)) { n = i; count++; @@ -427,32 +2315,33 @@ private: if(count == 1) { - //RT[32+4*n : 32+4*n+3] = CR[4*n : 4*n+3]; + //RD[32+4*n : 32+4*n+3] = CR[4*n : 4*n+3]; CPU.GPR[rd] = (u64)CPU.GetCR(n) << (n * 4); } - else CPU.GPR[rd] = 0; + else + CPU.GPR[rd] = 0; } else { CPU.GPR[rd] = CPU.CR.CR; } } - virtual void LWARX(OP_REG rd, OP_REG ra, OP_REG rb) + void LWARX(OP_REG rd, OP_REG ra, OP_REG rb) { const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; CPU.reserve_addr = addr; CPU.reserve = true; CPU.GPR[rd] = Memory.Read32(addr); } - virtual void LDX(OP_REG ra, OP_REG rs, OP_REG rb) + void LDX(OP_REG ra, OP_REG rs, OP_REG rb) { CPU.GPR[ra] = Memory.Read64(rs ? CPU.GPR[rs] + CPU.GPR[rb] : CPU.GPR[rb]); } - virtual void LWZX(OP_REG rd, OP_REG ra, OP_REG rb) + void LWZX(OP_REG rd, OP_REG ra, OP_REG rb) { CPU.GPR[rd] = Memory.Read32(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); } - virtual void SLW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SLW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { const u32 n = CPU.GPR[rb] & 0x1f; const u32 r = rotl32((u32)CPU.GPR[rs], n); @@ -462,7 +2351,7 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void CNTLZW(OP_REG ra, OP_REG rs, bool rc) + void CNTLZW(OP_REG ra, OP_REG rs, bool rc) { u32 i; for(i=0; i < 32; i++) @@ -474,43 +2363,68 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void SLD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SLD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = CPU.GPR[rb] & 0x40 ? 0 : CPU.GPR[rs] << (CPU.GPR[rb] & 0x3f); if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void AND(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void AND(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = CPU.GPR[rs] & CPU.GPR[rb]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void CMPL(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb) + void CMPL(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb) { CPU.UpdateCRn(crfd, l ? CPU.GPR[ra] : (u32)CPU.GPR[ra], l ? CPU.GPR[rb] : (u32)CPU.GPR[rb]); } - virtual void LVEHX(OP_REG vd, OP_REG ra, OP_REG rb) + void LVSR(OP_REG vd, OP_REG ra, OP_REG rb) { - const u64 addr = (ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~1ULL; - CPU.VPR[vd].Clear(); - CPU.VPR[vd].h((addr & 0xf) >> 1) = Memory.Read16(addr); + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + + switch(addr & 0xf) + { + case 0x0: CPU.VPR[vd]._u64[1] = 0x1011121314151617; CPU.VPR[vd]._u64[0] = 0x18191A1B1C1D1E1F; break; + case 0x1: CPU.VPR[vd]._u64[1] = 0x0F10111213141516; CPU.VPR[vd]._u64[0] = 0x1718191A1B1C1D1E; break; + case 0x2: CPU.VPR[vd]._u64[1] = 0x0E0F101112131415; CPU.VPR[vd]._u64[0] = 0x161718191A1B1C1D; break; + case 0x3: CPU.VPR[vd]._u64[1] = 0x0D0E0F1011121314; CPU.VPR[vd]._u64[0] = 0x15161718191A1B1C; break; + case 0x4: CPU.VPR[vd]._u64[1] = 0x0C0D0E0F10111213; CPU.VPR[vd]._u64[0] = 0x1415161718191A1B; break; + case 0x5: CPU.VPR[vd]._u64[1] = 0x0B0C0D0E0F101112; CPU.VPR[vd]._u64[0] = 0x131415161718191A; break; + case 0x6: CPU.VPR[vd]._u64[1] = 0x0A0B0C0D0E0F1011; CPU.VPR[vd]._u64[0] = 0x1213141516171819; break; + case 0x7: CPU.VPR[vd]._u64[1] = 0x090A0B0C0D0E0F10; CPU.VPR[vd]._u64[0] = 0x1112131415161718; break; + case 0x8: CPU.VPR[vd]._u64[1] = 0x08090A0B0C0D0E0F; CPU.VPR[vd]._u64[0] = 0x1011121314151617; break; + case 0x9: CPU.VPR[vd]._u64[1] = 0x0708090A0B0C0D0E; CPU.VPR[vd]._u64[0] = 0x0F10111213141516; break; + case 0xa: CPU.VPR[vd]._u64[1] = 0x060708090A0B0C0D; CPU.VPR[vd]._u64[0] = 0x0E0F101112131415; break; + case 0xb: CPU.VPR[vd]._u64[1] = 0x05060708090A0B0C; CPU.VPR[vd]._u64[0] = 0x0D0E0F1011121314; break; + case 0xc: CPU.VPR[vd]._u64[1] = 0x0405060708090A0B; CPU.VPR[vd]._u64[0] = 0x0C0D0E0F10111213; break; + case 0xd: CPU.VPR[vd]._u64[1] = 0x030405060708090A; CPU.VPR[vd]._u64[0] = 0x0B0C0D0E0F101112; break; + case 0xe: CPU.VPR[vd]._u64[1] = 0x0203040506070809; CPU.VPR[vd]._u64[0] = 0x0A0B0C0D0E0F1011; break; + case 0xf: CPU.VPR[vd]._u64[1] = 0x0102030405060708; CPU.VPR[vd]._u64[0] = 0x090A0B0C0D0E0F10; break; + } } - virtual void SUBF(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void LVEHX(OP_REG vd, OP_REG ra, OP_REG rb) + { + //const u64 addr = (ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~1ULL; + //CPU.VPR[vd].Clear(); + //(u16&)CPU.VPR[vd]._u8[addr & 0xf] = Memory.Read16(addr); + CPU.VPR[vd]._u128 = Memory.Read128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~0xfULL); + } + void SUBF(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { CPU.GPR[rd] = CPU.GPR[rb] - CPU.GPR[ra]; if(oe) UNK("subfo"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void LDUX(OP_REG rd, OP_REG ra, OP_REG rb) + void LDUX(OP_REG rd, OP_REG ra, OP_REG rb) { const u64 addr = CPU.GPR[ra] + CPU.GPR[rb]; CPU.GPR[rd] = Memory.Read64(addr); CPU.GPR[ra] = addr; } - virtual void DCBST(OP_REG ra, OP_REG rb) + void DCBST(OP_REG ra, OP_REG rb) { //UNK("dcbst", false); } - virtual void CNTLZD(OP_REG ra, OP_REG rs, bool rc) + void CNTLZD(OP_REG ra, OP_REG rs, bool rc) { u32 i = 0; @@ -522,18 +2436,19 @@ private: CPU.GPR[ra] = i; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void ANDC(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void ANDC(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = CPU.GPR[rs] & ~CPU.GPR[rb]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void LVEWX(OP_REG vd, OP_REG ra, OP_REG rb) + void LVEWX(OP_REG vd, OP_REG ra, OP_REG rb) { - const u64 addr = (ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~3ULL; - CPU.VPR[vd].Clear(); - CPU.VPR[vd].w((addr & 0xf) >> 2) = Memory.Read32(addr); + //const u64 addr = (ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~3ULL; + //CPU.VPR[vd].Clear(); + //(u32&)CPU.VPR[vd]._u8[addr & 0xf] = Memory.Read32(addr); + CPU.VPR[vd]._u128 = Memory.Read128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~0xfULL); } - virtual void MULHD(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) + void MULHD(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) { const s64 RA = CPU.GPR[ra]; const s64 RB = CPU.GPR[rb]; @@ -558,37 +2473,37 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void MULHW(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) + void MULHW(OP_REG rd, OP_REG ra, OP_REG rb, bool rc) { CPU.GPR[rd] = (s64)(s32)((CPU.GPR[ra] * CPU.GPR[rb]) >> 32); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void LDARX(OP_REG rd, OP_REG ra, OP_REG rb) + void LDARX(OP_REG rd, OP_REG ra, OP_REG rb) { const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; CPU.reserve_addr = addr; CPU.reserve = true; CPU.GPR[rd] = Memory.Read64(addr); } - virtual void DCBF(OP_REG ra, OP_REG rb) + void DCBF(OP_REG ra, OP_REG rb) { //UNK("dcbf", false); } - virtual void LBZX(OP_REG rd, OP_REG ra, OP_REG rb) + void LBZX(OP_REG rd, OP_REG ra, OP_REG rb) { CPU.GPR[rd] = Memory.Read8(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); } - virtual void LVX(OP_REG vrd, OP_REG ra, OP_REG rb) + void LVX(OP_REG vd, OP_REG ra, OP_REG rb) { - CPU.VPR[vrd] = Memory.Read128(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); + CPU.VPR[vd]._u128 = Memory.Read128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~0xfULL); } - virtual void NEG(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) + void NEG(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) { CPU.GPR[rd] = 0-CPU.GPR[ra]; if(oe) UNK("nego"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void LBZUX(OP_REG rd, OP_REG ra, OP_REG rb) + void LBZUX(OP_REG rd, OP_REG ra, OP_REG rb) { //if(ra == 0 || ra == rd) throw "Bad instruction [LBZUX]"; @@ -596,12 +2511,18 @@ private: CPU.GPR[rd] = Memory.Read8(addr); CPU.GPR[ra] = addr; } - virtual void NOR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void NOR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = ~(CPU.GPR[rs] | CPU.GPR[rb]); if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void SUBFE(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void STVEBX(OP_REG vs, OP_REG ra, OP_REG rb) + { + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + Memory.Write8(addr, CPU.VPR[vs]._u8[15 - eb]); + } + void SUBFE(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const s64 RA = CPU.GPR[ra]; const s64 RB = CPU.GPR[rb]; @@ -610,7 +2531,7 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); if(oe) UNK("subfeo"); } - virtual void ADDE(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void ADDE(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const s64 RA = CPU.GPR[ra]; const s64 RB = CPU.GPR[rb]; @@ -619,12 +2540,12 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); if(oe) UNK("addeo"); } - virtual void MTOCRF(OP_REG fxm, OP_REG rs) + void MTOCRF(OP_REG crm, OP_REG rs) { u32 n = 0, count = 0; for(u32 i=0; i<8; ++i) { - if(fxm & (1 << i)) + if(crm & (1 << i)) { n = i; count++; @@ -638,11 +2559,11 @@ private: } else CPU.CR.CR = 0; } - virtual void STDX(OP_REG rs, OP_REG ra, OP_REG rb) + void STDX(OP_REG rs, OP_REG ra, OP_REG rb) { Memory.Write64((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), CPU.GPR[rs]); } - virtual void STWCX_(OP_REG rs, OP_REG ra, OP_REG rb) + void STWCX_(OP_REG rs, OP_REG ra, OP_REG rb) { CPU.SetCR(0, CPU.XER.SO ? CR_SO : 0); if(!CPU.reserve) return; @@ -662,17 +2583,29 @@ private: CPU.reserve = false; } } - virtual void STWX(OP_REG rs, OP_REG ra, OP_REG rb) + void STWX(OP_REG rs, OP_REG ra, OP_REG rb) { Memory.Write32((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), CPU.GPR[rs]); } - virtual void STDUX(OP_REG rs, OP_REG ra, OP_REG rb) + void STVEHX(OP_REG vs, OP_REG ra, OP_REG rb) + { + const u64 addr = (ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~1ULL; + const u8 eb = (addr & 0xf) >> 1; + Memory.Write16(addr, CPU.VPR[vs]._u16[7 - eb]); + } + void STDUX(OP_REG rs, OP_REG ra, OP_REG rb) { const u64 addr = CPU.GPR[ra] + CPU.GPR[rb]; Memory.Write64(addr, CPU.GPR[rs]); CPU.GPR[ra] = addr; } - virtual void ADDZE(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) + void STVEWX(OP_REG vs, OP_REG ra, OP_REG rb) + { + const u64 addr = (ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~3ULL; + const u8 eb = (addr & 0xf) >> 2; + Memory.Write32(addr, CPU.VPR[vs]._u32[3 - eb]); + } + void ADDZE(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) { const s64 RA = CPU.GPR[ra]; CPU.GPR[rd] = RA + CPU.XER.CA; @@ -681,7 +2614,7 @@ private: if(oe) ConLog.Warning("addzeo"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void STDCX_(OP_REG rs, OP_REG ra, OP_REG rb) + void STDCX_(OP_REG rs, OP_REG ra, OP_REG rb) { CPU.SetCR(0, CPU.XER.SO ? CR_SO : 0); if(!CPU.reserve) return; @@ -701,21 +2634,21 @@ private: CPU.reserve = false; } } - virtual void STBX(OP_REG rs, OP_REG ra, OP_REG rb) + void STBX(OP_REG rs, OP_REG ra, OP_REG rb) { Memory.Write8((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), CPU.GPR[rs]); } - virtual void STVX(OP_REG vrd, OP_REG ra, OP_REG rb) + void STVX(OP_REG vs, OP_REG ra, OP_REG rb) { - Memory.Write128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), CPU.VPR[vrd]); + Memory.Write128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~0xfULL, CPU.VPR[vs]._u128); } - virtual void MULLD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void MULLD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { CPU.GPR[rd] = CPU.GPR[ra] * CPU.GPR[rb]; if(rc) CPU.UpdateCR0(CPU.GPR[rd]); if(oe) UNK("mulldo"); } - virtual void ADDME(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) + void ADDME(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) { const s64 RA = CPU.GPR[ra]; CPU.GPR[rd] = RA + CPU.XER.CA - 1; @@ -724,17 +2657,17 @@ private: if(oe) UNK("addmeo"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void MULLW(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void MULLW(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { CPU.GPR[rd] = (s64)(s32)((s32)CPU.GPR[ra] * (s32)CPU.GPR[rb]); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); if(oe) UNK("mullwo"); } - virtual void DCBTST(OP_REG th, OP_REG ra, OP_REG rb) + void DCBTST(OP_REG th, OP_REG ra, OP_REG rb) { //UNK("dcbtst", false); } - virtual void ADD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void ADD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const u64 RA = CPU.GPR[ra]; const u64 RB = CPU.GPR[rb]; @@ -743,50 +2676,57 @@ private: if(oe) UNK("addo"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void DCBT(OP_REG ra, OP_REG rb, OP_REG th) + void DCBT(OP_REG ra, OP_REG rb, OP_REG th) { //UNK("dcbt", false); } - virtual void LHZX(OP_REG rd, OP_REG ra, OP_REG rb) + void LHZX(OP_REG rd, OP_REG ra, OP_REG rb) { CPU.GPR[rd] = Memory.Read16(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); } - virtual void EQV(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void EQV(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = ~(CPU.GPR[rs] ^ CPU.GPR[rb]); if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void ECIWX(OP_REG rd, OP_REG ra, OP_REG rb) + void ECIWX(OP_REG rd, OP_REG ra, OP_REG rb) { //HACK! CPU.GPR[rd] = Memory.Read32(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); } - virtual void LHZUX(OP_REG rd, OP_REG ra, OP_REG rb) + void LHZUX(OP_REG rd, OP_REG ra, OP_REG rb) { const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; CPU.GPR[rd] = Memory.Read16(addr); CPU.GPR[ra] = addr; } - virtual void XOR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void XOR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = CPU.GPR[rs] ^ CPU.GPR[rb]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void MFSPR(OP_REG rd, OP_REG spr) + void MFSPR(OP_REG rd, OP_REG spr) { CPU.GPR[rd] = GetRegBySPR(spr); } - virtual void LHAX(OP_REG rd, OP_REG ra, OP_REG rb) + void DST(OP_REG ra, OP_REG rb, OP_uIMM strm, OP_uIMM t) + { + } + void LHAX(OP_REG rd, OP_REG ra, OP_REG rb) { CPU.GPR[rd] = (s64)(s16)Memory.Read16(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); } - virtual void ABS(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) + void LVXL(OP_REG vd, OP_REG ra, OP_REG rb) + { + CPU.VPR[vd]._u128 = Memory.Read128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~0xfULL); + } + void ABS(OP_REG rd, OP_REG ra, OP_REG oe, bool rc) { CPU.GPR[rd] = abs((s64)CPU.GPR[ra]); if(oe) UNK("abso"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void MFTB(OP_REG rd, OP_REG spr) + void MFTB(OP_REG rd, OP_REG spr) { const u32 n = (spr >> 5) | ((spr & 0x1f) << 5); @@ -797,33 +2737,36 @@ private: default: UNK(wxString::Format("mftb r%d, %d", rd, spr)); break; } } - virtual void LHAUX(OP_REG rd, OP_REG ra, OP_REG rb) + void DSTST(OP_REG ra, OP_REG rb, OP_uIMM strm, OP_uIMM t) + { + } + void LHAUX(OP_REG rd, OP_REG ra, OP_REG rb) { const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; CPU.GPR[rd] = (s64)(s16)Memory.Read16(addr); CPU.GPR[ra] = addr; } - virtual void STHX(OP_REG rs, OP_REG ra, OP_REG rb) + void STHX(OP_REG rs, OP_REG ra, OP_REG rb) { const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; Memory.Write16(addr, CPU.GPR[rs]); } - virtual void ORC(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void ORC(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = CPU.GPR[rs] | ~CPU.GPR[rb]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void ECOWX(OP_REG rs, OP_REG ra, OP_REG rb) + void ECOWX(OP_REG rs, OP_REG ra, OP_REG rb) { //HACK! Memory.Write32((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), CPU.GPR[rs]); } - virtual void OR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void OR(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = CPU.GPR[rs] | CPU.GPR[rb]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void DIVDU(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void DIVDU(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const u64 RA = CPU.GPR[ra]; const u64 RB = CPU.GPR[rb]; @@ -840,7 +2783,7 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void DIVWU(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void DIVWU(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const u32 RA = CPU.GPR[ra]; const u32 RB = CPU.GPR[rb]; @@ -857,12 +2800,22 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void MTSPR(OP_REG spr, OP_REG rs) + void MTSPR(OP_REG spr, OP_REG rs) { GetRegBySPR(spr) = CPU.GPR[rs]; } /*0x1d6*///DCBI - virtual void DIVD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void NAND(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + { + CPU.GPR[ra] = ~(CPU.GPR[rs] & CPU.GPR[rb]); + + if(rc) CPU.UpdateCR0(CPU.GPR[ra]); + } + void STVXL(OP_REG vs, OP_REG ra, OP_REG rb) + { + Memory.Write128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~0xfULL, CPU.VPR[vs]._u128); + } + void DIVD(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const s64 RA = CPU.GPR[ra]; const s64 RB = CPU.GPR[rb]; @@ -879,7 +2832,7 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void DIVW(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) + void DIVW(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc) { const s32 RA = CPU.GPR[ra]; const s32 RB = CPU.GPR[rb]; @@ -896,54 +2849,93 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); } - virtual void LWBRX(OP_REG rd, OP_REG ra, OP_REG rb) + void LVLX(OP_REG vd, OP_REG ra, OP_REG rb) { - CPU.GPR[rd] = *(u32*)&Memory[ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]]; + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + + Memory.ReadLeft(CPU.VPR[vd]._u8 + eb, addr, 16 - eb); } - virtual void LFSX(OP_REG frd, OP_REG ra, OP_REG rb) + void LWBRX(OP_REG rd, OP_REG ra, OP_REG rb) { - *(u64*)&CPU.FPR[frd] = Memory.Read32(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); - CPU.FPR[frd] = *(float*)&CPU.FPR[frd]; + CPU.GPR[rd] = (u32&)Memory[ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]]; } - virtual void SRW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void LFSX(OP_REG frd, OP_REG ra, OP_REG rb) + { + (u32&)CPU.FPR[frd] = Memory.Read32(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); + CPU.FPR[frd] = (float&)CPU.FPR[frd]; + } + void SRW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = CPU.GPR[rb] & 0x20 ? 0 : (u32)CPU.GPR[rs] >> (CPU.GPR[rb] & 0x1f); if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void SRD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SRD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { CPU.GPR[ra] = CPU.GPR[rb] & 0x40 ? 0 : CPU.GPR[rs] >> (CPU.GPR[rb] & 0x3f); if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void LFSUX(OP_REG frd, OP_REG ra, OP_REG rb) + void LVRX(OP_REG vd, OP_REG ra, OP_REG rb) + { + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + + Memory.ReadRight(CPU.VPR[vd]._u8, addr & ~0xf, eb); + } + void LFSUX(OP_REG frd, OP_REG ra, OP_REG rb) { const u64 addr = CPU.GPR[ra] + CPU.GPR[rb]; - *(u64*)&CPU.FPR[frd] = Memory.Read32(addr); - CPU.FPR[frd] = *(float*)&CPU.FPR[frd]; + (u64&)CPU.FPR[frd] = Memory.Read32(addr); + CPU.FPR[frd] = (float&)CPU.FPR[frd]; CPU.GPR[ra] = addr; } - virtual void SYNC(OP_REG l) + void SYNC(OP_REG l) { } - virtual void LFDX(OP_REG frd, OP_REG ra, OP_REG rb) + void LFDX(OP_REG frd, OP_REG ra, OP_REG rb) { - *(u64*)&CPU.FPR[frd] = Memory.Read64(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); + (u64&)CPU.FPR[frd] = Memory.Read64(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); } - virtual void LFDUX(OP_REG frd, OP_REG ra, OP_REG rb) + void LFDUX(OP_REG frd, OP_REG ra, OP_REG rb) { const u64 addr = CPU.GPR[ra] + CPU.GPR[rb]; - *(u64*)&CPU.FPR[frd] = Memory.Read64(addr); + (u64&)CPU.FPR[frd] = Memory.Read64(addr); CPU.GPR[ra] = addr; } - virtual void STFSX(OP_REG rs, OP_REG ra, OP_REG rb) + void STVLX(OP_REG vs, OP_REG ra, OP_REG rb) { - Memory.Write32((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), PPCdouble(CPU.GPR[rs]).To32()); + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + + Memory.WriteLeft(addr, 16 - eb, CPU.VPR[vs]._u8 + eb); } - virtual void LHBRX(OP_REG rd, OP_REG ra, OP_REG rb) + void STFSX(OP_REG frs, OP_REG ra, OP_REG rb) { - CPU.GPR[rd] = *(u16*)&Memory[ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]]; + Memory.Write32((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), PPCdouble(CPU.FPR[frs]).To32()); } - virtual void SRAW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void STVRX(OP_REG vs, OP_REG ra, OP_REG rb) + { + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + + Memory.WriteRight(addr - eb, eb, CPU.VPR[vs]._u8); + } + void STFDX(OP_REG frs, OP_REG ra, OP_REG rb) + { + Memory.Write64((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), (u64&)CPU.FPR[frs]); + } + void LVLXL(OP_REG vd, OP_REG ra, OP_REG rb) + { + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + + Memory.ReadLeft(CPU.VPR[vd]._u8 + eb, addr, 16 - eb); + } + void LHBRX(OP_REG rd, OP_REG ra, OP_REG rb) + { + CPU.GPR[rd] = (u16&)Memory[ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]]; + } + void SRAW(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { s32 RS = CPU.GPR[rs]; s32 RB = CPU.GPR[rb]; @@ -952,7 +2944,7 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void SRAD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) + void SRAD(OP_REG ra, OP_REG rs, OP_REG rb, bool rc) { s64 RS = CPU.GPR[rs]; s64 RB = CPU.GPR[rb]; @@ -961,7 +2953,17 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void SRAWI(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) + void LVRXL(OP_REG vd, OP_REG ra, OP_REG rb) + { + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + + Memory.ReadRight(CPU.VPR[vd]._u8, addr & ~0xf, eb); + } + void DSS(OP_uIMM strm, OP_uIMM a) + { + } + void SRAWI(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) { s32 RS = CPU.GPR[rs]; CPU.GPR[ra] = RS >> sh; @@ -969,7 +2971,7 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void SRADI1(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) + void SRADI1(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) { s64 RS = CPU.GPR[rs]; CPU.GPR[ra] = RS >> sh; @@ -977,100 +2979,114 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void SRADI2(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) + void SRADI2(OP_REG ra, OP_REG rs, OP_REG sh, bool rc) { SRADI1(ra, rs, sh, rc); } - virtual void EIEIO() + void EIEIO() { } - virtual void EXTSH(OP_REG ra, OP_REG rs, bool rc) + void STVLXL(OP_REG vs, OP_REG ra, OP_REG rb) + { + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + + Memory.WriteLeft(addr, 16 - eb, CPU.VPR[vs]._u8 + eb); + } + void EXTSH(OP_REG ra, OP_REG rs, bool rc) { CPU.GPR[ra] = (s64)(s16)CPU.GPR[rs]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void EXTSB(OP_REG ra, OP_REG rs, bool rc) + void STVRXL(OP_REG vs, OP_REG ra, OP_REG rb) + { + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u8 eb = addr & 0xf; + + Memory.WriteRight(addr - eb, eb, CPU.VPR[vs]._u8); + } + void EXTSB(OP_REG ra, OP_REG rs, bool rc) { CPU.GPR[ra] = (s64)(s8)CPU.GPR[rs]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } - virtual void STFIWX(OP_REG frs, OP_REG ra, OP_REG rb) + void STFIWX(OP_REG frs, OP_REG ra, OP_REG rb) { - Memory.Write32(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb], *(u32*)&CPU.FPR[frs]); + Memory.Write32(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb], (u32&)CPU.FPR[frs]); } - virtual void EXTSW(OP_REG ra, OP_REG rs, bool rc) + void EXTSW(OP_REG ra, OP_REG rs, bool rc) { CPU.GPR[ra] = (s64)(s32)CPU.GPR[rs]; if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } /*0x3d6*///ICBI - virtual void DCBZ(OP_REG ra, OP_REG rs) + void DCBZ(OP_REG ra, OP_REG rs) { //UNK("dcbz", false); } END_OPCODES_GROUP(G_1f); - virtual void LWZ(OP_REG rd, OP_REG ra, OP_sIMM d) + void LWZ(OP_REG rd, OP_REG ra, OP_sIMM d) { CPU.GPR[rd] = Memory.Read32(ra ? CPU.GPR[ra] + d : d); } - virtual void LWZU(OP_REG rd, OP_REG ra, OP_sIMM d) + void LWZU(OP_REG rd, OP_REG ra, OP_sIMM d) { const u64 addr = CPU.GPR[ra] + d; CPU.GPR[rd] = Memory.Read32(addr); CPU.GPR[ra] = addr; } - virtual void LBZ(OP_REG rd, OP_REG ra, OP_sIMM d) + void LBZ(OP_REG rd, OP_REG ra, OP_sIMM d) { CPU.GPR[rd] = Memory.Read8(ra ? CPU.GPR[ra] + d : d); } - virtual void LBZU(OP_REG rd, OP_REG ra, OP_sIMM d) + void LBZU(OP_REG rd, OP_REG ra, OP_sIMM d) { const u64 addr = CPU.GPR[ra] + d; CPU.GPR[rd] = Memory.Read8(addr); CPU.GPR[ra] = addr; } - virtual void STW(OP_REG rs, OP_REG ra, OP_sIMM d) + void STW(OP_REG rs, OP_REG ra, OP_sIMM d) { Memory.Write32(ra ? CPU.GPR[ra] + d : d, CPU.GPR[rs]); } - virtual void STWU(OP_REG rs, OP_REG ra, OP_sIMM d) + void STWU(OP_REG rs, OP_REG ra, OP_sIMM d) { const u64 addr = CPU.GPR[ra] + d; Memory.Write32(addr, CPU.GPR[rs]); CPU.GPR[ra] = addr; } - virtual void STB(OP_REG rs, OP_REG ra, OP_sIMM d) + void STB(OP_REG rs, OP_REG ra, OP_sIMM d) { Memory.Write8(ra ? CPU.GPR[ra] + d : d, CPU.GPR[rs]); } - virtual void STBU(OP_REG rs, OP_REG ra, OP_sIMM d) + void STBU(OP_REG rs, OP_REG ra, OP_sIMM d) { const u64 addr = CPU.GPR[ra] + d; Memory.Write8(addr, CPU.GPR[rs]); CPU.GPR[ra] = addr; } - virtual void LHZ(OP_REG rd, OP_REG ra, OP_sIMM d) + void LHZ(OP_REG rd, OP_REG ra, OP_sIMM d) { CPU.GPR[rd] = Memory.Read16(ra ? CPU.GPR[ra] + d : d); } - virtual void LHZU(OP_REG rd, OP_REG ra, OP_sIMM d) + void LHZU(OP_REG rd, OP_REG ra, OP_sIMM d) { const u64 addr = CPU.GPR[ra] + d; CPU.GPR[rd] = Memory.Read16(addr); CPU.GPR[ra] = addr; } - virtual void STH(OP_REG rs, OP_REG ra, OP_sIMM d) + void STH(OP_REG rs, OP_REG ra, OP_sIMM d) { Memory.Write16(ra ? CPU.GPR[ra] + d : d, CPU.GPR[rs]); } - virtual void STHU(OP_REG rs, OP_REG ra, OP_sIMM d) + void STHU(OP_REG rs, OP_REG ra, OP_sIMM d) { const u64 addr = CPU.GPR[ra] + d; Memory.Write16(addr, CPU.GPR[rs]); CPU.GPR[ra] = addr; } - virtual void LMW(OP_REG rd, OP_REG ra, OP_sIMM d) + void LMW(OP_REG rd, OP_REG ra, OP_sIMM d) { u64 addr = ra ? CPU.GPR[ra] + d : d; for(u32 i=rd; i<32; ++i, addr += 4) @@ -1078,7 +3094,7 @@ private: CPU.GPR[i] = Memory.Read32(addr); } } - virtual void STMW(OP_REG rs, OP_REG ra, OP_sIMM d) + void STMW(OP_REG rs, OP_REG ra, OP_sIMM d) { u64 addr = ra ? CPU.GPR[ra] + d : d; for(u32 i=rs; i<32; ++i, addr += 4) @@ -1086,55 +3102,55 @@ private: Memory.Write32(addr, CPU.GPR[i]); } } - virtual void LFS(OP_REG frd, OP_REG ra, OP_sIMM d) + void LFS(OP_REG frd, OP_REG ra, OP_sIMM d) { const u32 v = Memory.Read32(ra ? CPU.GPR[ra] + d : d); - CPU.FPR[frd] = *(float*)&v; + CPU.FPR[frd] = (float&)v; } - virtual void LFSU(OP_REG frd, OP_REG ra, OP_sIMM ds) + void LFSU(OP_REG frd, OP_REG ra, OP_sIMM ds) { const u64 addr = CPU.GPR[ra] + ds; const u32 v = Memory.Read32(addr); - CPU.FPR[frd] = *(float*)&v; + CPU.FPR[frd] = (float&)v; CPU.GPR[ra] = addr; } - virtual void LFD(OP_REG frd, OP_REG ra, OP_sIMM d) + void LFD(OP_REG frd, OP_REG ra, OP_sIMM d) { - *(u64*)&CPU.FPR[frd] = Memory.Read64(ra ? CPU.GPR[ra] + d : d); + (u64&)CPU.FPR[frd] = Memory.Read64(ra ? CPU.GPR[ra] + d : d); } - virtual void LFDU(OP_REG frd, OP_REG ra, OP_sIMM ds) + void LFDU(OP_REG frd, OP_REG ra, OP_sIMM ds) { const u64 addr = CPU.GPR[ra] + ds; - *(u64*)&CPU.FPR[frd] = Memory.Read64(addr); + (u64&)CPU.FPR[frd] = Memory.Read64(addr); CPU.GPR[ra] = addr; } - virtual void STFS(OP_REG frs, OP_REG ra, OP_sIMM d) + void STFS(OP_REG frs, OP_REG ra, OP_sIMM d) { Memory.Write32(ra ? CPU.GPR[ra] + d : d, PPCdouble(CPU.FPR[frs]).To32()); } - virtual void STFSU(OP_REG frs, OP_REG ra, OP_sIMM d) + void STFSU(OP_REG frs, OP_REG ra, OP_sIMM d) { const u64 addr = CPU.GPR[ra] + d; Memory.Write32(addr, PPCdouble(CPU.FPR[frs]).To32()); CPU.GPR[ra] = addr; } - virtual void STFD(OP_REG frs, OP_REG ra, OP_sIMM d) + void STFD(OP_REG frs, OP_REG ra, OP_sIMM d) { - Memory.Write64(ra ? CPU.GPR[ra] + d : d, *(u64*)&CPU.FPR[frs]); + Memory.Write64(ra ? CPU.GPR[ra] + d : d, (u64&)CPU.FPR[frs]); } - virtual void STFDU(OP_REG frs, OP_REG ra, OP_sIMM d) + void STFDU(OP_REG frs, OP_REG ra, OP_sIMM d) { const u64 addr = CPU.GPR[ra] + d; - Memory.Write64(addr, *(u64*)&CPU.FPR[frs]); + Memory.Write64(addr, (u64&)CPU.FPR[frs]); CPU.GPR[ra] = addr; } START_OPCODES_GROUP(G_3a) - virtual void LD(OP_REG rd, OP_REG ra, OP_sIMM ds) + void LD(OP_REG rd, OP_REG ra, OP_sIMM ds) { CPU.GPR[rd] = Memory.Read64(ra ? CPU.GPR[ra] + ds : ds); } - virtual void LDU(OP_REG rd, OP_REG ra, OP_sIMM ds) + void LDU(OP_REG rd, OP_REG ra, OP_sIMM ds) { //if(ra == 0 || rt == ra) return; const u64 addr = CPU.GPR[ra] + ds; @@ -1144,37 +3160,37 @@ private: END_OPCODES_GROUP(G_3a); START_OPCODES_GROUP(G_3b) - virtual void FDIVS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FDIVS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { if(CPU.FPR[frb] == 0.0) CPU.SetFPSCRException(FPSCR_ZX); CPU.FPR[frd] = static_cast(CPU.FPR[fra] / CPU.FPR[frb]); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fdivs.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FSUBS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FSUBS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { CPU.FPR[frd] = static_cast(CPU.FPR[fra] - CPU.FPR[frb]); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fsubs.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FADDS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FADDS(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { CPU.FPR[frd] = static_cast(CPU.FPR[fra] + CPU.FPR[frb]); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fadds.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FSQRTS(OP_REG frd, OP_REG frb, bool rc) + void FSQRTS(OP_REG frd, OP_REG frb, bool rc) { CPU.FPR[frd] = static_cast(sqrt((float)CPU.FPR[frb])); if(rc) UNK("fsqrts.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FRES(OP_REG frd, OP_REG frb, bool rc) + void FRES(OP_REG frd, OP_REG frb, bool rc) { if(CPU.FPR[frb] == 0.0) CPU.SetFPSCRException(FPSCR_ZX); CPU.FPR[frd] = static_cast(1.0f/CPU.FPR[frb]); if(rc) UNK("fres.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FMULS(OP_REG frd, OP_REG fra, OP_REG frc, bool rc) + void FMULS(OP_REG frd, OP_REG fra, OP_REG frc, bool rc) { CPU.FPR[frd] = static_cast(CPU.FPR[fra] * CPU.FPR[frc]); CPU.FPSCR.FI = 0; @@ -1182,25 +3198,25 @@ private: CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fmuls.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FMADDS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FMADDS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = static_cast(CPU.FPR[fra] * CPU.FPR[frc] + CPU.FPR[frb]); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fmadds.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FMSUBS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FMSUBS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = static_cast(CPU.FPR[fra] * CPU.FPR[frc] - CPU.FPR[frb]); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fmsubs.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FNMSUBS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FNMSUBS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = static_cast(-(CPU.FPR[fra] * CPU.FPR[frc] - CPU.FPR[frb])); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fnmsubs.");////CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FNMADDS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FNMADDS(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = static_cast(-(CPU.FPR[fra] * CPU.FPR[frc] + CPU.FPR[frb])); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); @@ -1209,11 +3225,11 @@ private: END_OPCODES_GROUP(G_3b); START_OPCODES_GROUP(G_3e) - virtual void STD(OP_REG rs, OP_REG ra, OP_sIMM d) + void STD(OP_REG rs, OP_REG ra, OP_sIMM d) { Memory.Write64(ra ? CPU.GPR[ra] + d : d, CPU.GPR[rs]); } - virtual void STDU(OP_REG rs, OP_REG ra, OP_sIMM ds) + void STDU(OP_REG rs, OP_REG ra, OP_sIMM ds) { //if(ra == 0 || rs == ra) return; const u64 addr = CPU.GPR[ra] + ds; @@ -1223,28 +3239,28 @@ private: END_OPCODES_GROUP(G_3e); START_OPCODES_GROUP(G_3f) - virtual void MTFSB1(OP_REG bt, bool rc) + void MTFSB1(OP_REG bt, bool rc) { - UNK("mtfsb1"); + UNIMPLEMENTED(); } - virtual void MCRFS(OP_REG bf, OP_REG bfa) + void MCRFS(OP_REG bf, OP_REG bfa) { - UNK("mcrfs"); + UNIMPLEMENTED(); } - virtual void MTFSB0(OP_REG bt, bool rc) + void MTFSB0(OP_REG bt, bool rc) { - UNK("mtfsb0"); + UNIMPLEMENTED(); } - virtual void MTFSFI(OP_REG crfd, OP_REG i, bool rc) + void MTFSFI(OP_REG crfd, OP_REG i, bool rc) { - UNK("mtfsfi"); + UNIMPLEMENTED(); } - virtual void MFFS(OP_REG frd, bool rc) + void MFFS(OP_REG frd, bool rc) { - *(u64*)&CPU.FPR[frd] = CPU.FPSCR.FPSCR; + (u64&)CPU.FPR[frd] = CPU.FPSCR.FPSCR; if(rc) UNK("mffs."); } - virtual void MTFSF(OP_REG flm, OP_REG frb, bool rc) + void MTFSF(OP_REG flm, OP_REG frb, bool rc) { u32 mask = 0; for(u32 i=0; i<8; ++i) @@ -1252,10 +3268,10 @@ private: if(flm & (1 << i)) mask |= 0xf << (i * 4); } - CPU.FPSCR.FPSCR = (CPU.FPSCR.FPSCR & ~mask) | (*(u32*)&CPU.FPR[frb] & mask); + CPU.FPSCR.FPSCR = (CPU.FPSCR.FPSCR & ~mask) | ((u32&)CPU.FPR[frb] & mask); if(rc) UNK("mtfsf."); } - virtual void FCMPU(OP_REG crfd, OP_REG fra, OP_REG frb) + void FCMPU(OP_REG crfd, OP_REG fra, OP_REG frb) { if((CPU.FPSCR.FPRF = FPRdouble::Cmp(CPU.FPR[fra], CPU.FPR[frb])) == 1) { @@ -1267,13 +3283,13 @@ private: CPU.SetCR(crfd, CPU.FPSCR.FPRF); } - virtual void FRSP(OP_REG frd, OP_REG frb, bool rc) + void FRSP(OP_REG frd, OP_REG frb, bool rc) { const double b = CPU.FPR[frb]; double b0 = b; if(CPU.FPSCR.NI) { - if ((*(u64*)&b0 & DOUBLE_EXP) < 0x3800000000000000ULL) *(u64*)&b0 &= DOUBLE_SIGN; + if (((u64&)b0 & DOUBLE_EXP) < 0x3800000000000000ULL) (u64&)b0 &= DOUBLE_SIGN; } const double r = static_cast(b0); CPU.FPSCR.FR = fabs(r) > fabs(b); @@ -1281,7 +3297,7 @@ private: CPU.FPSCR.FPRF = PPCdouble(r).GetType(); CPU.FPR[frd] = r; } - virtual void FCTIW(OP_REG frd, OP_REG frb, bool rc) + void FCTIW(OP_REG frd, OP_REG frb, bool rc) { const double b = CPU.FPR[frb]; u32 r; @@ -1337,12 +3353,12 @@ private: } } - *(u64*)&CPU.FPR[frd] = 0xfff8000000000000ull | r; - if(r == 0 && ( (*(u64*)&b) & DOUBLE_SIGN )) *(u64*)&CPU.FPR[frd] |= 0x100000000ull; + (u64&)CPU.FPR[frd] = 0xfff8000000000000ull | r; + if(r == 0 && ( (u64&)b & DOUBLE_SIGN )) (u64&)CPU.FPR[frd] |= 0x100000000ull; if(rc) UNK("fctiw."); } - virtual void FCTIWZ(OP_REG frd, OP_REG frb, bool rc) + void FCTIWZ(OP_REG frd, OP_REG frb, bool rc) { const double b = CPU.FPR[frb]; u32 value; @@ -1377,71 +3393,55 @@ private: value = (u32)i; } - *(u64*)&CPU.FPR[frd] = 0xfff8000000000000ull | value; - if (value == 0 && ( (*(u64*)&b) & DOUBLE_SIGN )) - *(u64*)&CPU.FPR[frd] |= 0x100000000ull; + (u64&)CPU.FPR[frd] = 0xfff8000000000000ull | value; + if (value == 0 && ( (u64&)b & DOUBLE_SIGN )) + (u64&)CPU.FPR[frd] |= 0x100000000ull; - /* - CPU.FPR[frd] = (double)*(u64*)&CPU.FPR[frb]; - return; - PPCdouble b = FPRdouble::ConvertToIntegerMode(CPU.FPR[frb], CPU.FPSCR, false, FPSCR_RN_ZERO); - CPU.FPR[frd] = b._double;//FPRdouble::ToInt(b, CPU.FPSCR.RN); - //PPCdouble b = FPRdouble::ConvertToIntegerMode(CPU.FPR[frb]); - //CPU.FPR[frd] = FPRdouble::ToInt(b, FPSCR_RN_ZERO); - */ if(rc) UNK("fctiwz."); } - virtual void FDIV(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FDIV(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { - /* - PPCdouble a = FPRdouble::ConvertToIntegerMode(CPU.FPR[fra]); - PPCdouble b = FPRdouble::ConvertToIntegerMode(CPU.FPR[frb]); - - if(a.type == FPR_ZERO && b.type == FPR_ZERO) + if(FPRdouble::IsINF(CPU.FPR[fra]) == 0.0 && CPU.FPR[frb] == 0.0) { CPU.FPSCR.VXZDZ = 1; } - else if(a.type == FPR_INF && b.type == FPR_INF) + else if(FPRdouble::IsINF(CPU.FPR[fra]) && FPRdouble::IsINF(CPU.FPR[frb])) { CPU.FPSCR.VXIDI = 1; } - else if(a.type != FPR_ZERO && b.type == FPR_ZERO) + else if(CPU.FPR[fra] != 0.0 && CPU.FPR[frb] == 0.0) { CPU.SetFPSCRException(FPSCR_ZX); } - PPCdouble d = a.div(b); - CPU.FPSCR.FPSCR |= FPRdouble::ConvertToFloatMode(d, CPU.FPSCR.RN); - CPU.GPR[frd] = d._double; - */ - CPU.FPR[frd] = CPU.FPR[fra] / CPU.FPR[frb]; + CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fdiv.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FSUB(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FSUB(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] - CPU.FPR[frb]; CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fsub.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FADD(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) + void FADD(OP_REG frd, OP_REG fra, OP_REG frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] + CPU.FPR[frb]; CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fadd.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FSQRT(OP_REG frd, OP_REG frb, bool rc) + void FSQRT(OP_REG frd, OP_REG frb, bool rc) { CPU.FPR[frd] = sqrt(CPU.FPR[frb]); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fsqrt.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FSEL(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FSEL(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] < 0.0 ? CPU.FPR[frc] : CPU.FPR[frb]; if(rc) UNK("fsel.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FMUL(OP_REG frd, OP_REG fra, OP_REG frc, bool rc) + void FMUL(OP_REG frd, OP_REG fra, OP_REG frc, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] * CPU.FPR[frc]; CPU.FPSCR.FI = 0; @@ -1449,35 +3449,35 @@ private: CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fmul.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FRSQRTE(OP_REG frd, OP_REG frb, bool rc) + void FRSQRTE(OP_REG frd, OP_REG frb, bool rc) { - UNK("frsqrte"); + UNIMPLEMENTED(); } - virtual void FMSUB(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FMSUB(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] * CPU.FPR[frc] - CPU.FPR[frb]; CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fmsub.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FMADD(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FMADD(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] * CPU.FPR[frc] + CPU.FPR[frb]; CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fmadd.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FNMSUB(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FNMSUB(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = -(CPU.FPR[fra] * CPU.FPR[frc] - CPU.FPR[frb]); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fnmsub.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FNMADD(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) + void FNMADD(OP_REG frd, OP_REG fra, OP_REG frc, OP_REG frb, bool rc) { CPU.FPR[frd] = -(CPU.FPR[fra] * CPU.FPR[frc] + CPU.FPR[frb]); CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fnmadd.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FCMPO(OP_REG crfd, OP_REG fra, OP_REG frb) + void FCMPO(OP_REG crfd, OP_REG fra, OP_REG frb) { if((CPU.FPSCR.FPRF = FPRdouble::Cmp(CPU.FPR[fra], CPU.FPR[frb])) == 1) { @@ -1496,27 +3496,27 @@ private: CPU.SetCR(crfd, CPU.FPSCR.FPRF); } - virtual void FNEG(OP_REG frd, OP_REG frb, bool rc) + void FNEG(OP_REG frd, OP_REG frb, bool rc) { CPU.FPR[frd] = -CPU.FPR[frb]; if(rc) UNK("fneg.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FMR(OP_REG frd, OP_REG frb, bool rc) + void FMR(OP_REG frd, OP_REG frb, bool rc) { CPU.FPR[frd] = CPU.FPR[frb]; if(rc) UNK("fmr.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FNABS(OP_REG frd, OP_REG frb, bool rc) + void FNABS(OP_REG frd, OP_REG frb, bool rc) { - *(u64*)&CPU.FPR[frd] = *(u64*)&CPU.FPR[frb] | 0x8000000000000000ULL; + (u64&)CPU.FPR[frd] = (u64&)CPU.FPR[frb] | 0x8000000000000000ULL; if(rc) UNK("fnabs.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FABS(OP_REG frd, OP_REG frb, bool rc) + void FABS(OP_REG frd, OP_REG frb, bool rc) { CPU.FPR[frd] = fabs(CPU.FPR[frb]); if(rc) UNK("fabs.");//CPU.UpdateCR1(CPU.FPR[frd]); } - virtual void FCTID(OP_REG frd, OP_REG frb, bool rc) + void FCTID(OP_REG frd, OP_REG frb, bool rc) { const double b = CPU.FPR[frb]; u64 r; @@ -1572,12 +3572,12 @@ private: } } - *(u64*)&CPU.FPR[frd] = 0xfff8000000000000ull | r; - if(r == 0 && ( (*(u64*)&b) & DOUBLE_SIGN )) *(u64*)&CPU.FPR[frd] |= 0x100000000ull; + (u64&)CPU.FPR[frd] = 0xfff8000000000000ull | r; + if(r == 0 && ( (u64&)b & DOUBLE_SIGN )) (u64&)CPU.FPR[frd] |= 0x100000000ull; if(rc) UNK("fctid."); } - virtual void FCTIDZ(OP_REG frd, OP_REG frb, bool rc) + void FCTIDZ(OP_REG frd, OP_REG frb, bool rc) { const double b = CPU.FPR[frb]; u64 r; @@ -1612,35 +3612,21 @@ private: r = (u64)i; } - *(u64*)&CPU.FPR[frd] = 0xfff8000000000000ull | r; - if(r == 0 && ( (*(u64*)&b) & DOUBLE_SIGN )) *(u64*)&CPU.FPR[frd] |= 0x100000000ull; + (u64&)CPU.FPR[frd] = 0xfff8000000000000ull | r; + if(r == 0 && ( (u64&)b & DOUBLE_SIGN )) (u64&)CPU.FPR[frd] |= 0x100000000ull; if(rc) UNK("fctidz."); } - virtual void FCFID(OP_REG frd, OP_REG frb, bool rc) + void FCFID(OP_REG frd, OP_REG frb, bool rc) { - CPU.FPR[frd] = (double)*(u64*)&CPU.FPR[frb]; + CPU.FPR[frd] = (double)(u64&)CPU.FPR[frb]; if(rc) UNK("fcfid.");//CPU.UpdateCR1(CPU.FPR[frd]); } END_OPCODES_GROUP(G_3f); - bool IsVecGcode(const u32 gcode) + void UNK(const u32 code, const u32 opcode, const u32 gcode) { - switch(gcode) - { - case 0x47: case 0x67: case 0x7: - case 0x26: case 0xe7: case 0x207: - case 0xc7: case 0x6: - return true; - } - - return false; - } - - virtual void UNK(const s32 code, const s32 opcode, const s32 gcode) - { - UNK(wxString::Format("Unknown/Illegal opcode! (0x%08x : 0x%x : 0x%x)", code, opcode, gcode), - opcode != 0x4 && !(opcode == 0x1f && IsVecGcode(gcode))); + UNK(wxString::Format("Unknown/Illegal opcode! (0x%08x : 0x%x : 0x%x)", code, opcode, gcode)); } void UNK(const wxString& err, bool pause = true) @@ -1653,11 +3639,24 @@ private: for(uint i=0; i<32; ++i) ConLog.Write("r%d = 0x%llx", i, CPU.GPR[i]); for(uint i=0; i<32; ++i) ConLog.Write("f%d = %llf", i, CPU.FPR[i]); - for(uint i=0; i<32; ++i) ConLog.Write("v%d = 0x%s", i, CPU.VPR[i].ToString()); + for(uint i=0; i<32; ++i) ConLog.Write("v%d = 0x%s [%s]", i, CPU.VPR[i].ToString(true), CPU.VPR[i].ToString()); ConLog.Write("CR = 0x%08x", CPU.CR); ConLog.Write("LR = 0x%llx", CPU.LR); ConLog.Write("CTR = 0x%llx", CPU.CTR); - ConLog.Write("XER = 0x%llx", CPU.XER); + ConLog.Write("XER = 0x%llx [CA=%lld | OV=%lld | SO=%lld]", CPU.XER, CPU.XER.CA, CPU.XER.OV, CPU.XER.SO); + ConLog.Write("FPSCR = 0x%x " + "[RN=%d | NI=%d | XE=%d | ZE=%d | UE=%d | OE=%d | VE=%d | " + "VXCVI=%d | VXSQRT=%d | VXSOFT=%d | FPRF=%d | " + "FI=%d | FR=%d | VXVC=%d | VXIMZ=%d | " + "VXZDZ=%d | VXIDI=%d | VXISI=%d | VXSNAN=%d | " + "XX=%d | ZX=%d | UX=%d | OX=%d | VX=%d | FEX=%d | FX=%d]", + CPU.FPSCR, + CPU.FPSCR.RN, + CPU.FPSCR.NI, CPU.FPSCR.XE, CPU.FPSCR.ZE, CPU.FPSCR.UE, CPU.FPSCR.OE, CPU.FPSCR.VE, + CPU.FPSCR.VXCVI, CPU.FPSCR.VXSQRT, CPU.FPSCR.VXSOFT, CPU.FPSCR.FPRF, + CPU.FPSCR.FI, CPU.FPSCR.FR, CPU.FPSCR.VXVC, CPU.FPSCR.VXIMZ, + CPU.FPSCR.VXZDZ, CPU.FPSCR.VXIDI, CPU.FPSCR.VXISI, CPU.FPSCR.VXSNAN, + CPU.FPSCR.XX, CPU.FPSCR.ZX, CPU.FPSCR.UX, CPU.FPSCR.OX, CPU.FPSCR.VX, CPU.FPSCR.FEX, CPU.FPSCR.FX); } }; diff --git a/rpcs3/Emu/Cell/PPUOpcodes.h b/rpcs3/Emu/Cell/PPUOpcodes.h index 1bd9bc5434..4a817eecc6 100644 --- a/rpcs3/Emu/Cell/PPUOpcodes.h +++ b/rpcs3/Emu/Cell/PPUOpcodes.h @@ -8,253 +8,434 @@ #define ADD_NULL_OPCODE(name) virtual void(##name##)()=0 #define END_OPCODES_GROUP(x) /*x*/ -enum PPU_MainOpcodes +namespace PPU_opcodes { - TDI = 0x02, //Trap Doubleword Immediate - TWI = 0x03, //Trap Word Immediate - G_04 = 0x04, - MULLI = 0x07, //Multiply Low Immediate - SUBFIC = 0x08, //Subtract from Immediate Carrying - //DOZI = 0x09, - CMPLI = 0x0a, //Compare Logical Immediate - CMPI = 0x0b, //Compare Immediate - ADDIC = 0x0c, //Add Immediate Carrying - ADDIC_ = 0x0d, //Add Immediate Carrying and Record - ADDI = 0x0e, //Add Immediate - ADDIS = 0x0f, //Add Immediate Shifted - BC = 0x10, //Branch Conditional - SC = 0x11, //System Call - B = 0x12, //Branch - G_13 = 0x13, - RLWIMI = 0x14, //Rotate Left Word Immediate then Mask Insert - RLWINM = 0x15, //Rotate Left Word Immediate then AND with Mask - RLWNM = 0x17, //Rotate Left Word then AND with Mask - ORI = 0x18, //OR Immediate - ORIS = 0x19, //OR Immediate Shifted - XORI = 0x1a, //XOR Immediate - XORIS = 0x1b, //XOR Immediate Shifted - ANDI_ = 0x1c, //AND Immediate - ANDIS_ = 0x1d, //AND Immediate Shifted - G_1e = 0x1e, - G_1f = 0x1f, - LWZ = 0x20, //Load Word and Zero Indexed - LWZU = 0x21, //Load Word and Zero with Update Indexed - LBZ = 0x22, //Load Byte and Zero - LBZU = 0x23, //Load Byte and Zero with Update - STW = 0x24, //Store Word - STWU = 0x25, //Store Word with Update - STB = 0x26, //Store Byte - STBU = 0x27, //Store Byte with Update - LHZ = 0x28, //Load Halfword and Zero - LHZU = 0x29, //Load Halfword and Zero with Update - LHA = 0x2a, //Load Halfword Algebraic with Update - LHAU = 0x2b, //Load Halfword Algebraic - STH = 0x2c, //Store Halfword - STHU = 0x2d, //Store Halfword with Update - LMW = 0x2e, //Load Multiple Word - STMW = 0x2f, //Store Multiple Word - LFS = 0x30, //Load Floating-Point Single - LFSU = 0x31, //Load Floating-Point Single with Update - LFD = 0x32, //Load Floating-Point Double - LFDU = 0x33, //Load Floating-Point Double with Update - STFS = 0x34, //Store Floating-Point Single - STFSU = 0x35, //Store Floating-Point Single with Update - STFD = 0x36, //Store Floating-Point Double - STFDU = 0x37, //Store Floating-Point Double with Update - LFQ = 0x38, // - LFQU = 0x39, // - G_3a = 0x3a, - G_3b = 0x3b, - G_3e = 0x3e, - G_3f = 0x3f, -}; + enum PPU_MainOpcodes + { + TDI = 0x02, //Trap Doubleword Immediate + TWI = 0x03, //Trap Word Immediate + G_04 = 0x04, + MULLI = 0x07, //Multiply Low Immediate + SUBFIC = 0x08, //Subtract from Immediate Carrying + //DOZI = 0x09, + CMPLI = 0x0a, //Compare Logical Immediate + CMPI = 0x0b, //Compare Immediate + ADDIC = 0x0c, //Add Immediate Carrying + ADDIC_ = 0x0d, //Add Immediate Carrying and Record + ADDI = 0x0e, //Add Immediate + ADDIS = 0x0f, //Add Immediate Shifted + BC = 0x10, //Branch Conditional + SC = 0x11, //System Call + B = 0x12, //Branch + G_13 = 0x13, + RLWIMI = 0x14, //Rotate Left Word Immediate then Mask Insert + RLWINM = 0x15, //Rotate Left Word Immediate then AND with Mask + RLWNM = 0x17, //Rotate Left Word then AND with Mask + ORI = 0x18, //OR Immediate + ORIS = 0x19, //OR Immediate Shifted + XORI = 0x1a, //XOR Immediate + XORIS = 0x1b, //XOR Immediate Shifted + ANDI_ = 0x1c, //AND Immediate + ANDIS_ = 0x1d, //AND Immediate Shifted + G_1e = 0x1e, + G_1f = 0x1f, + LWZ = 0x20, //Load Word and Zero Indexed + LWZU = 0x21, //Load Word and Zero with Update Indexed + LBZ = 0x22, //Load Byte and Zero + LBZU = 0x23, //Load Byte and Zero with Update + STW = 0x24, //Store Word + STWU = 0x25, //Store Word with Update + STB = 0x26, //Store Byte + STBU = 0x27, //Store Byte with Update + LHZ = 0x28, //Load Halfword and Zero + LHZU = 0x29, //Load Halfword and Zero with Update + LHA = 0x2a, //Load Halfword Algebraic with Update + LHAU = 0x2b, //Load Halfword Algebraic + STH = 0x2c, //Store Halfword + STHU = 0x2d, //Store Halfword with Update + LMW = 0x2e, //Load Multiple Word + STMW = 0x2f, //Store Multiple Word + LFS = 0x30, //Load Floating-Point Single + LFSU = 0x31, //Load Floating-Point Single with Update + LFD = 0x32, //Load Floating-Point Double + LFDU = 0x33, //Load Floating-Point Double with Update + STFS = 0x34, //Store Floating-Point Single + STFSU = 0x35, //Store Floating-Point Single with Update + STFD = 0x36, //Store Floating-Point Double + STFDU = 0x37, //Store Floating-Point Double with Update + LFQ = 0x38, // + LFQU = 0x39, // + G_3a = 0x3a, + G_3b = 0x3b, + G_3e = 0x3e, + G_3f = 0x3f, + }; -enum G_04Opcodes -{ - VXOR = 0x262, -}; + enum G_04Opcodes + { + VADDUBM = 0x0, + VMAXUB = 0x2, + VRLB = 0x4, + VCMPEQUB = 0x6, + VMULOUB = 0x8, + VADDFP = 0xa, + VMRGHB = 0xc, + VPKUHUM = 0xe, + VADDUHM = 0x40, + VMAXUH = 0x42, + VRLH = 0x44, + VCMPEQUH = 0x46, + VMULOUH = 0x48, + VSUBFP = 0x4a, + VMRGHH = 0x4c, + VPKUWUM = 0x4e, + VADDUWM = 0x80, + VMAXUW = 0x82, + VRLW = 0x84, + VCMPEQUW = 0x86, + VMRGHW = 0x8c, + VPKUHUS = 0x8e, + VCMPEQFP = 0xc6, + VPKUWUS = 0xce, + VMAXSB = 0x102, + VSLB = 0x104, + VMULOSB = 0x108, + VREFP = 0x10a, + VMRGLB = 0x10c, + VPKSHUS = 0x10e, + VMAXSH = 0x142, + VSLH = 0x144, + VMULOSH = 0x148, + VRSQRTEFP = 0x14a, + VMRGLH = 0x14c, + VPKSWUS = 0x14e, + VADDCUW = 0x180, + VMAXSW = 0x182, + VSLW = 0x184, + VEXPTEFP = 0x18a, + VMRGLW = 0x18c, + VPKSHSS = 0x18e, + VSL = 0x1c4, + VCMPGEFP = 0x1c6, + VLOGEFP = 0x1ca, + VPKSWSS = 0x1ce, + VADDUBS = 0x200, + VMINUB = 0x202, + VSRB = 0x204, + VCMPGTUB = 0x206, + VMULEUB = 0x208, + VRFIN = 0x20a, + VSPLTB = 0x20c, + VUPKHSB = 0x20e, + VADDUHS = 0x240, + VMINUH = 0x242, + VSRH = 0x244, + VCMPGTUH = 0x246, + VMULEUH = 0x248, + VRFIZ = 0x24a, + VSPLTH = 0x24c, + VUPKHSH = 0x24e, + VADDUWS = 0x280, + VMINUW = 0x282, + VSRW = 0x284, + VCMPGTUW = 0x286, + VRFIP = 0x28a, + VSPLTW = 0x28c, + VUPKLSB = 0x28e, + VSR = 0x2c4, + VCMPGTFP = 0x2c6, + VRFIM = 0x2ca, + VUPKLSH = 0x2ce, + VADDSBS = 0x300, + VMINSB = 0x302, + VSRAB = 0x304, + VCMPGTSB = 0x306, + VMULESB = 0x308, + VCFUX = 0x30a, + VSPLTISB = 0x30c, + VPKPX = 0x30e, + VADDSHS = 0x340, + VMINSH = 0x342, + VSRAH = 0x344, + VCMPGTSH = 0x346, + VMULESH = 0x348, + VCFSX = 0x34a, + VSPLTISH = 0x34c, + VUPKHPX = 0x34e, + VADDSWS = 0x380, + VMINSW = 0x382, + VSRAW = 0x384, + VCMPGTSW = 0x386, + VCTUXS = 0x38a, + VSPLTISW = 0x38c, + VCMPBFP = 0x3c6, + VCTSXS = 0x3ca, + VUPKLPX = 0x3ce, + VSUBUBM = 0x400, + VAVGUB = 0x402, + VAND = 0x404, + VCMPEQUB_ = 0x406, + VMAXFP = 0x40a, + VSLO = 0x40c, + VSUBUHM = 0x440, + VAVGUH = 0x442, + VANDC = 0x444, + VCMPEQUH_ = 0x446, + VMINFP = 0x44a, + VSRO = 0x44c, + VSUBUWM = 0x480, + VAVGUW = 0x482, + VOR = 0x484, + VCMPEQUW_ = 0x486, + VXOR = 0x4c4, + VCMPEQFP_ = 0x4c6, + VAVGSB = 0x502, + VNOR = 0x504, + VAVGSH = 0x542, + VSUBCUW = 0x580, + VAVGSW = 0x582, + VCMPGEFP_ = 0x5c6, + VSUBUBS = 0x600, + MFVSCR = 0x604, + VCMPGTUB_ = 0x606, + VSUM4UBS = 0x608, + VSUBUHS = 0x640, + MTVSCR = 0x644, + VCMPGTUH_ = 0x646, + VSUM4SHS = 0x648, + VSUBUWS = 0x680, + VCMPGTUW_ = 0x686, + VSUM2SWS = 0x688, + VCMPGTFP_ = 0x6c6, + VSUBSBS = 0x700, + VCMPGTSB_ = 0x706, + VSUM4SBS = 0x708, + VSUBSHS = 0x740, + VCMPGTSH_ = 0x746, + VSUBSWS = 0x780, + VCMPGTSW_ = 0x786, + VSUMSWS = 0x788, + VCMPBFP_ = 0x7c6, + }; -enum G_13Opcodes //Field 21 - 30 -{ - MCRF = 0x000, - BCLR = 0x010, - CRNOR = 0x021, - CRANDC = 0x081, - ISYNC = 0x096, - CRXOR = 0x0c1, - CRNAND = 0x0e1, - CRAND = 0x101, - CREQV = 0x121, - CRORC = 0x1a1, - CROR = 0x1c1, - BCCTR = 0x210, -}; + enum G_04_VA_Opcodes + { + VMHADDSHS = 0x20, + VMHRADDSHS = 0x21, + VMLADDUHM = 0x22, + VMSUMUBM = 0x24, + VMSUMMBM = 0x25, + VMSUMUHM = 0x26, + VMSUMUHS = 0x27, + VMSUMSHM = 0x28, + VMSUMSHS = 0x29, + VSEL = 0x2a, + VPERM = 0x2b, + VSLDOI = 0x2c, + VMADDFP = 0x2e, + VNMSUBFP = 0x2f, + }; -enum G_1eOpcodes //Field 27 - 29 -{ - RLDICL = 0x0, - RLDICR = 0x1, - RLDIC = 0x2, - RLDIMI = 0x3, -}; + enum G_13Opcodes //Field 21 - 30 + { + MCRF = 0x000, + BCLR = 0x010, + CRNOR = 0x021, + CRANDC = 0x081, + ISYNC = 0x096, + CRXOR = 0x0c1, + CRNAND = 0x0e1, + CRAND = 0x101, + CREQV = 0x121, + CRORC = 0x1a1, + CROR = 0x1c1, + BCCTR = 0x210, + }; -enum G_1fOpcodes //Field 21 - 30 -{ - CMP = 0x000, - TW = 0x004, - LVEBX = 0x007, //Load Vector Element Byte Indexed - SUBFC = 0x008, //Subtract from Carrying - MULHDU = 0x009, - ADDC = 0x00a, - MULHWU = 0x00b, - MFOCRF = 0x013, - LWARX = 0x014, - LDX = 0x015, - LWZX = 0x017, - SLW = 0x018, - CNTLZW = 0x01a, - SLD = 0x01b, - AND = 0x01c, - CMPL = 0x020, - LVEHX = 0x027, //Load Vector Element Halfword Indexed - SUBF = 0x028, - LDUX = 0x035, //Load Doubleword with Update Indexed - DCBST = 0x036, - CNTLZD = 0x03a, - ANDC = 0x03c, - LVEWX = 0x047, //Load Vector Element Word Indexed - MULHD = 0x049, - MULHW = 0x04b, - LDARX = 0x054, - DCBF = 0x056, - LBZX = 0x057, - LVX = 0x067, - NEG = 0x068, - LBZUX = 0x077, - NOR = 0x07c, - SUBFE = 0x088, //Subtract from Extended - ADDE = 0x08a, - MTOCRF = 0x090, - STDX = 0x095, - STWCX_ = 0x096, - STWX = 0x097, - STDUX = 0x0b5, - ADDZE = 0x0ca, - STDCX_ = 0x0d6, - STBX = 0x0d7, - STVX = 0x0e7, - MULLD = 0x0e9, - ADDME = 0x0ea, - MULLW = 0x0eb, - DCBTST = 0x0f6, - DOZ = 0x108, - ADD = 0x10a, - DCBT = 0x116, - LHZX = 0x117, - EQV = 0x11c, - ECIWX = 0x136, - LHZUX = 0x137, - XOR = 0x13c, - MFSPR = 0x153, - LHAX = 0x157, - ABS = 0x168, - MFTB = 0x173, - LHAUX = 0x177, - STHX = 0x197, //Store Halfword Indexed - ORC = 0x19c, //OR with Complement - ECOWX = 0x1b6, - OR = 0x1bc, - DIVDU = 0x1c9, - DIVWU = 0x1cb, - MTSPR = 0x1d3, - DCBI = 0x1d6, - DIVD = 0x1e9, - DIVW = 0x1eb, - LWBRX = 0x216, - LFSX = 0x217, - SRW = 0x218, - SRD = 0x21b, - LFSUX = 0x237, - SYNC = 0x256, - LFDX = 0x257, - LFDUX = 0x277, - STFSX = 0x297, - LHBRX = 0x316, - SRAW = 0x318, - SRAD = 0x31A, - SRAWI = 0x338, - SRADI1 = 0x33a, //sh_5 == 0 - SRADI2 = 0x33b, //sh_5 != 0 - EIEIO = 0x356, - EXTSH = 0x39a, - EXTSB = 0x3ba, - STFIWX = 0x3d7, - EXTSW = 0x3da, - ICBI = 0x3d6, - DCBZ = 0x3f6, -}; + enum G_1eOpcodes //Field 27 - 29 + { + RLDICL = 0x0, + RLDICR = 0x1, + RLDIC = 0x2, + RLDIMI = 0x3, + }; -enum G_3aOpcodes //Field 30 - 31 -{ - LD = 0x0, - LDU = 0x1, -}; + enum G_1fOpcodes //Field 21 - 30 + { + CMP = 0x000, + TW = 0x004, + LVSL = 0x006, //Load Vector for Shift Left + LVEBX = 0x007, //Load Vector Element Byte Indexed + SUBFC = 0x008, //Subtract from Carrying + MULHDU = 0x009, + ADDC = 0x00a, + MULHWU = 0x00b, + MFOCRF = 0x013, + LWARX = 0x014, + LDX = 0x015, + LWZX = 0x017, + SLW = 0x018, + CNTLZW = 0x01a, + SLD = 0x01b, + AND = 0x01c, + CMPL = 0x020, + LVSR = 0x026, //Load Vector for Shift Right + LVEHX = 0x027, //Load Vector Element Halfword Indexed + SUBF = 0x028, + LDUX = 0x035, //Load Doubleword with Update Indexed + DCBST = 0x036, + CNTLZD = 0x03a, + ANDC = 0x03c, + LVEWX = 0x047, //Load Vector Element Word Indexed + MULHD = 0x049, + MULHW = 0x04b, + LDARX = 0x054, + DCBF = 0x056, + LBZX = 0x057, + LVX = 0x067, //Load Vector Indexed + NEG = 0x068, + LBZUX = 0x077, + NOR = 0x07c, + STVEBX = 0x087, //Store Vector Element Byte Indexed + SUBFE = 0x088, //Subtract from Extended + ADDE = 0x08a, + MTOCRF = 0x090, + STDX = 0x095, + STWCX_ = 0x096, + STWX = 0x097, + STVEHX = 0x0a7, //Store Vector Element Halfword Indexed + STDUX = 0x0b5, + STVEWX = 0x0c7, //Store Vector Element Word Indexed + ADDZE = 0x0ca, + STDCX_ = 0x0d6, + STBX = 0x0d7, + STVX = 0x0e7, + MULLD = 0x0e9, + ADDME = 0x0ea, + MULLW = 0x0eb, + DCBTST = 0x0f6, + DOZ = 0x108, + ADD = 0x10a, + DCBT = 0x116, + LHZX = 0x117, + EQV = 0x11c, + ECIWX = 0x136, + LHZUX = 0x137, + XOR = 0x13c, + MFSPR = 0x153, + DST = 0x156, //Data Stream Touch + LHAX = 0x157, + LVXL = 0x167, //Load Vector Indexed Last + ABS = 0x168, + MFTB = 0x173, + DSTST = 0x176, //Data Stream Touch for Store + LHAUX = 0x177, + STHX = 0x197, //Store Halfword Indexed + ORC = 0x19c, //OR with Complement + ECOWX = 0x1b6, + OR = 0x1bc, + DIVDU = 0x1c9, + DIVWU = 0x1cb, + MTSPR = 0x1d3, + DCBI = 0x1d6, + NAND = 0x1dc, + STVXL = 0x1e7, //Store Vector Indexed Last + DIVD = 0x1e9, + DIVW = 0x1eb, + LVLX = 0x207, //Load Vector Left Indexed + LWBRX = 0x216, + LFSX = 0x217, + SRW = 0x218, + SRD = 0x21b, + LVRX = 0x227, //Load Vector Right Indexed + LFSUX = 0x237, + SYNC = 0x256, + LFDX = 0x257, + LFDUX = 0x277, + STVLX = 0x287, //Store Vector Left Indexed + STFSX = 0x297, + STVRX = 0x2a7, //Store Vector Right Indexed + STFDX = 0x2d7, //Store Floating-Point Double Indexed + LVLXL = 0x307, //Load Vector Left Indexed Last + LHBRX = 0x316, + SRAW = 0x318, + SRAD = 0x31a, + LVRXL = 0x327, //Load Vector Right Indexed Last + DSS = 0x336, //Data Stream Stop + SRAWI = 0x338, + SRADI1 = 0x33a, //sh_5 == 0 + SRADI2 = 0x33b, //sh_5 != 0 + EIEIO = 0x356, + STVLXL = 0x387, //Store Vector Left Indexed Last + EXTSH = 0x39a, + STVRXL = 0x3a7, //Store Vector Right Indexed Last + EXTSB = 0x3ba, + STFIWX = 0x3d7, + EXTSW = 0x3da, + ICBI = 0x3d6, + DCBZ = 0x3f6, + }; -enum G_3bOpcodes //Field 26 - 30 -{ - FDIVS = 0x12, - FSUBS = 0x14, - FADDS = 0x15, - FSQRTS = 0x16, - FRES = 0x18, - FMULS = 0x19, - FMSUBS = 0x1c, - FMADDS = 0x1d, - FNMSUBS = 0x1e, - FNMADDS = 0x1f, -}; + enum G_3aOpcodes //Field 30 - 31 + { + LD = 0x0, + LDU = 0x1, + }; -enum G_3eOpcodes //Field 30 - 31 -{ - STD = 0x0, - STDU = 0x1, -}; + enum G_3bOpcodes //Field 26 - 30 + { + FDIVS = 0x12, + FSUBS = 0x14, + FADDS = 0x15, + FSQRTS = 0x16, + FRES = 0x18, + FMULS = 0x19, + FMSUBS = 0x1c, + FMADDS = 0x1d, + FNMSUBS = 0x1e, + FNMADDS = 0x1f, + }; -enum G_3fOpcodes //Field 21 - 30 -{ - MTFSB1 = 0x026, - MCRFS = 0x040, - MTFSB0 = 0x046, - MTFSFI = 0x086, - MFFS = 0x247, - MTFSF = 0x2c7, + enum G_3eOpcodes //Field 30 - 31 + { + STD = 0x0, + STDU = 0x1, + }; - FCMPU = 0x000, - FRSP = 0x00c, - FCTIW = 0x00e, - FCTIWZ = 0x00f, - FDIV = 0x012, - FSUB = 0x014, - FADD = 0x015, - FSQRT = 0x016, - FSEL = 0x017, - FMUL = 0x019, - FRSQRTE = 0x01a, - FMSUB = 0x01c, - FMADD = 0x01d, - FNMSUB = 0x01e, - FNMADD = 0x01f, - FCMPO = 0x020, - FNEG = 0x028, - FMR = 0x048, - FNABS = 0x088, - FABS = 0x108, - FCTID = 0x32e, - FCTIDZ = 0x32f, - FCFID = 0x34e, -}; + enum G_3fOpcodes //Field 21 - 30 + { + MTFSB1 = 0x026, + MCRFS = 0x040, + MTFSB0 = 0x046, + MTFSFI = 0x086, + MFFS = 0x247, + MTFSF = 0x2c7, -//118 + FCMPU = 0x000, + FRSP = 0x00c, + FCTIW = 0x00e, + FCTIWZ = 0x00f, + FDIV = 0x012, + FSUB = 0x014, + FADD = 0x015, + FSQRT = 0x016, + FSEL = 0x017, + FMUL = 0x019, + FRSQRTE = 0x01a, + FMSUB = 0x01c, + FMADD = 0x01d, + FNMSUB = 0x01e, + FNMADD = 0x01f, + FCMPO = 0x020, + FNEG = 0x028, + FMR = 0x048, + FNABS = 0x088, + FABS = 0x108, + FCTID = 0x32e, + FCTIDZ = 0x32f, + FCFID = 0x34e, + }; +} class PPU_Opcodes { @@ -273,7 +454,163 @@ public: ADD_OPCODE(TWI,(OP_uIMM to, OP_REG ra, OP_sIMM simm16)); START_OPCODES_GROUP(G_04) - ADD_OPCODE(VXOR,(OP_REG vrd, OP_REG vra, OP_REG vrb)); + ADD_OPCODE(MFVSCR,(OP_REG vd)); + ADD_OPCODE(MTVSCR,(OP_REG vb)); + ADD_OPCODE(VADDCUW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDFP,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDSBS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDSHS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDSWS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDUBM,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDUBS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDUHM,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDUHS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDUWM,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VADDUWS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VAND,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VANDC,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VAVGSB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VAVGSH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VAVGSW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VAVGUB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VAVGUH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VAVGUW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCFSX,(OP_REG vd, OP_uIMM uimm5, OP_REG vb)); + ADD_OPCODE(VCFUX,(OP_REG vd, OP_uIMM uimm5, OP_REG vb)); + ADD_OPCODE(VCMPBFP,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPBFP_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPEQFP,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPEQFP_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPEQUB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPEQUB_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPEQUH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPEQUH_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPEQUW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPEQUW_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGEFP,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGEFP_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTFP,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTFP_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTSB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTSB_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTSH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTSH_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTSW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTSW_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTUB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTUB_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTUH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTUH_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTUW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCMPGTUW_,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VCTSXS,(OP_REG vd, OP_uIMM uimm5, OP_REG vb)); + ADD_OPCODE(VCTUXS,(OP_REG vd, OP_uIMM uimm5, OP_REG vb)); + ADD_OPCODE(VEXPTEFP,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VLOGEFP,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VMADDFP,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMAXFP,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMAXSB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMAXSH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMAXSW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMAXUB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMAXUH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMAXUW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMHADDSHS,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMHRADDSHS,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMINFP,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMINSB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMINSH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMINSW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMINUB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMINUH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMINUW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMLADDUHM,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMRGHB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMRGHH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMRGHW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMRGLB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMRGLH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMRGLW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMSUMMBM,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMSUMSHM,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMSUMSHS,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMSUMUBM,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMSUMUHM,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMSUMUHS,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VMULESB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMULESH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMULEUB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMULEUH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMULOSB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMULOSH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMULOUB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VMULOUH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VNMSUBFP,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VNOR,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VOR,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPERM,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VPKPX,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPKSHSS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPKSHUS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPKSWSS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPKSWUS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPKUHUM,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPKUHUS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPKUWUM,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VPKUWUS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VREFP,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VRFIM,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VRFIN,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VRFIP,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VRFIZ,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VRLB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VRLH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VRLW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VRSQRTEFP,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VSEL,(OP_REG vd, OP_REG va, OP_REG vb, OP_REG vc)); + ADD_OPCODE(VSL,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSLB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSLDOI,(OP_REG vd, OP_REG va, OP_REG vb, OP_uIMM sh)); + ADD_OPCODE(VSLH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSLO,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSLW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSPLTB,(OP_REG vd, OP_uIMM uimm5, OP_REG vb)); + ADD_OPCODE(VSPLTH,(OP_REG vd, OP_uIMM uimm5, OP_REG vb)); + ADD_OPCODE(VSPLTISB,(OP_REG vd, OP_sIMM simm5)); + ADD_OPCODE(VSPLTISH,(OP_REG vd, OP_sIMM simm5)); + ADD_OPCODE(VSPLTISW,(OP_REG vd, OP_sIMM simm5)); + ADD_OPCODE(VSPLTW,(OP_REG vd, OP_uIMM uimm5, OP_REG vb)); + ADD_OPCODE(VSR,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSRAB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSRAH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSRAW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSRB,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSRH,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSRO,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSRW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBCUW,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBFP,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBSBS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBSHS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBSWS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBUBM,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBUBS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBUHM,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBUHS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBUWM,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUBUWS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUMSWS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUM2SWS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUM4SBS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUM4SHS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VSUM4UBS,(OP_REG vd, OP_REG va, OP_REG vb)); + ADD_OPCODE(VUPKHPX,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VUPKHSB,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VUPKHSH,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VUPKLPX,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VUPKLSB,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VUPKLSH,(OP_REG vd, OP_REG vb)); + ADD_OPCODE(VXOR,(OP_REG vd, OP_REG va, OP_REG vb)); END_OPCODES_GROUP(G_04); ADD_OPCODE(MULLI,(OP_REG rd, OP_REG ra, OP_sIMM simm16)); @@ -323,12 +660,13 @@ public: START_OPCODES_GROUP(G_1f) /*0x000*/ADD_OPCODE(CMP,(OP_REG crfd, OP_REG l, OP_REG ra, OP_REG rb)); /*0x004*/ADD_OPCODE(TW,(OP_uIMM to, OP_REG ra, OP_REG rb)); + /*0x006*/ADD_OPCODE(LVSL,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x007*/ADD_OPCODE(LVEBX,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x008*/ADD_OPCODE(SUBFC,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); /*0x009*/ADD_OPCODE(MULHDU,(OP_REG rd, OP_REG ra, OP_REG rb, bool rc)); /*0x00a*/ADD_OPCODE(ADDC,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); /*0x00b*/ADD_OPCODE(MULHWU,(OP_REG rd, OP_REG ra, OP_REG rb, bool rc)); - /*0x013*/ADD_OPCODE(MFOCRF,(OP_REG a, OP_REG fxm, OP_REG rd)); + /*0x013*/ADD_OPCODE(MFOCRF,(OP_uIMM a, OP_REG rd, OP_uIMM crm)); /*0x014*/ADD_OPCODE(LWARX,(OP_REG rd, OP_REG ra, OP_REG rb)); /*0x015*/ADD_OPCODE(LDX,(OP_REG ra, OP_REG rs, OP_REG rb)); /*0x017*/ADD_OPCODE(LWZX,(OP_REG rd, OP_REG ra, OP_REG rb)); @@ -337,6 +675,7 @@ public: /*0x01b*/ADD_OPCODE(SLD,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); /*0x01c*/ADD_OPCODE(AND,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); /*0x020*/ADD_OPCODE(CMPL,(OP_REG bf, OP_REG l, OP_REG ra, OP_REG rb)); + /*0x026*/ADD_OPCODE(LVSR,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x027*/ADD_OPCODE(LVEHX,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x028*/ADD_OPCODE(SUBF,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); /*0x035*/ADD_OPCODE(LDUX,(OP_REG rd, OP_REG ra, OP_REG rb)); @@ -349,21 +688,24 @@ public: /*0x054*/ADD_OPCODE(LDARX,(OP_REG rd, OP_REG ra, OP_REG rb)); /*0x056*/ADD_OPCODE(DCBF,(OP_REG ra, OP_REG rb)); /*0x057*/ADD_OPCODE(LBZX,(OP_REG rd, OP_REG ra, OP_REG rb)); - /*0x067*/ADD_OPCODE(LVX,(OP_REG vrd, OP_REG ra, OP_REG rb)); + /*0x067*/ADD_OPCODE(LVX,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x068*/ADD_OPCODE(NEG,(OP_REG rd, OP_REG ra, OP_REG oe, bool rc)); /*0x077*/ADD_OPCODE(LBZUX,(OP_REG rd, OP_REG ra, OP_REG rb)); /*0x07c*/ADD_OPCODE(NOR,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); + /*0x087*/ADD_OPCODE(STVEBX,(OP_REG vs, OP_REG ra, OP_REG rb)); /*0x088*/ADD_OPCODE(SUBFE,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); /*0x08a*/ADD_OPCODE(ADDE,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); - /*0x090*/ADD_OPCODE(MTOCRF,(OP_REG fxm, OP_REG rs)); + /*0x090*/ADD_OPCODE(MTOCRF,(OP_REG crm, OP_REG rs)); /*0x095*/ADD_OPCODE(STDX,(OP_REG rs, OP_REG ra, OP_REG rb)); /*0x096*/ADD_OPCODE(STWCX_,(OP_REG rs, OP_REG ra, OP_REG rb)); /*0x097*/ADD_OPCODE(STWX,(OP_REG rs, OP_REG ra, OP_REG rb)); + /*0x0a7*/ADD_OPCODE(STVEHX,(OP_REG vs, OP_REG ra, OP_REG rb)); /*0x0b5*/ADD_OPCODE(STDUX,(OP_REG rs, OP_REG ra, OP_REG rb)); + /*0x0c7*/ADD_OPCODE(STVEWX,(OP_REG vs, OP_REG ra, OP_REG rb)); /*0x0ca*/ADD_OPCODE(ADDZE,(OP_REG rd, OP_REG ra, OP_REG oe, bool rc)); /*0x0d6*/ADD_OPCODE(STDCX_,(OP_REG rs, OP_REG ra, OP_REG rb)); /*0x0d7*/ADD_OPCODE(STBX,(OP_REG rs, OP_REG ra, OP_REG rb)); - /*0x0e7*/ADD_OPCODE(STVX,(OP_REG vrd, OP_REG ra, OP_REG rb)); + /*0x0e7*/ADD_OPCODE(STVX,(OP_REG vs, OP_REG ra, OP_REG rb)); /*0x0e9*/ADD_OPCODE(MULLD,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); /*0x0ea*/ADD_OPCODE(ADDME,(OP_REG rd, OP_REG ra, OP_REG oe, bool rc)); /*0x0eb*/ADD_OPCODE(MULLW,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); @@ -376,9 +718,12 @@ public: /*0x137*/ADD_OPCODE(LHZUX,(OP_REG rd, OP_REG ra, OP_REG rb)); /*0x13c*/ADD_OPCODE(XOR,(OP_REG rs, OP_REG ra, OP_REG rb, bool rc)); /*0x153*/ADD_OPCODE(MFSPR,(OP_REG rd, OP_REG spr)); + /*0x156*/ADD_OPCODE(DST,(OP_REG ra, OP_REG rb, OP_uIMM strm, OP_uIMM t)); /*0x157*/ADD_OPCODE(LHAX,(OP_REG rd, OP_REG ra, OP_REG rb)); + /*0x167*/ADD_OPCODE(LVXL,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x168*/ADD_OPCODE(ABS,(OP_REG rd, OP_REG ra, OP_REG oe, bool rc)); /*0x173*/ADD_OPCODE(MFTB,(OP_REG rd, OP_REG spr)); + /*0x176*/ADD_OPCODE(DSTST,(OP_REG ra, OP_REG rb, OP_uIMM strm, OP_uIMM t)); /*0x177*/ADD_OPCODE(LHAUX,(OP_REG rd, OP_REG ra, OP_REG rb)); /*0x197*/ADD_OPCODE(STHX,(OP_REG rs, OP_REG ra, OP_REG rb)); /*0x19c*/ADD_OPCODE(ORC,(OP_REG rs, OP_REG ra, OP_REG rb, bool rc)); @@ -388,25 +733,37 @@ public: /*0x1cb*/ADD_OPCODE(DIVWU,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); /*0x1d3*/ADD_OPCODE(MTSPR,(OP_REG spr, OP_REG rs)); /*0x1d6*///DCBI + /*0x1dc*/ADD_OPCODE(NAND,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); + /*0x1e7*/ADD_OPCODE(STVXL,(OP_REG vs, OP_REG ra, OP_REG rb)); /*0x1e9*/ADD_OPCODE(DIVD,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); /*0x1eb*/ADD_OPCODE(DIVW,(OP_REG rd, OP_REG ra, OP_REG rb, OP_REG oe, bool rc)); + /*0x207*/ADD_OPCODE(LVLX,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x216*/ADD_OPCODE(LWBRX,(OP_REG rd, OP_REG ra, OP_REG rb)); /*0x217*/ADD_OPCODE(LFSX,(OP_REG frd, OP_REG ra, OP_REG rb)); /*0x218*/ADD_OPCODE(SRW,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); /*0x21b*/ADD_OPCODE(SRD,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); + /*0x227*/ADD_OPCODE(LVRX,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x237*/ADD_OPCODE(LFSUX,(OP_REG frd, OP_REG ra, OP_REG rb)); /*0x256*/ADD_OPCODE(SYNC,(OP_REG l)); /*0x257*/ADD_OPCODE(LFDX,(OP_REG frd, OP_REG ra, OP_REG rb)); /*0x277*/ADD_OPCODE(LFDUX,(OP_REG frd, OP_REG ra, OP_REG rb)); - /*0x297*/ADD_OPCODE(STFSX,(OP_REG rs, OP_REG ra, OP_REG rb)); + /*0x287*/ADD_OPCODE(STVLX,(OP_REG vs, OP_REG ra, OP_REG rb)); + /*0x297*/ADD_OPCODE(STFSX,(OP_REG frs, OP_REG ra, OP_REG rb)); + /*0x2a7*/ADD_OPCODE(STVRX,(OP_REG vs, OP_REG ra, OP_REG rb)); + /*0x2d7*/ADD_OPCODE(STFDX,(OP_REG frs, OP_REG ra, OP_REG rb)); + /*0x307*/ADD_OPCODE(LVLXL,(OP_REG vd, OP_REG ra, OP_REG rb)); /*0x316*/ADD_OPCODE(LHBRX,(OP_REG rd, OP_REG ra, OP_REG rb)); /*0x318*/ADD_OPCODE(SRAW,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); - /*0x31A*/ADD_OPCODE(SRAD,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); + /*0x31a*/ADD_OPCODE(SRAD,(OP_REG ra, OP_REG rs, OP_REG rb, bool rc)); + /*0x327*/ADD_OPCODE(LVRXL,(OP_REG vd, OP_REG ra, OP_REG rb)); + /*0x336*/ADD_OPCODE(DSS,(OP_uIMM strm, OP_uIMM a)); /*0x338*/ADD_OPCODE(SRAWI,(OP_REG ra, OP_REG rs, OP_REG sh, bool rc)); /*0x33a*/ADD_OPCODE(SRADI1,(OP_REG ra, OP_REG rs, OP_REG sh, bool rc)); /*0x33b*/ADD_OPCODE(SRADI2,(OP_REG ra, OP_REG rs, OP_REG sh, bool rc)); /*0x356*/ADD_OPCODE(EIEIO,()); + /*0x387*/ADD_OPCODE(STVLXL,(OP_REG sd, OP_REG ra, OP_REG rb)); /*0x39a*/ADD_OPCODE(EXTSH,(OP_REG ra, OP_REG rs, bool rc)); + /*0x3a7*/ADD_OPCODE(STVRXL,(OP_REG sd, OP_REG ra, OP_REG rb)); /*0x3ba*/ADD_OPCODE(EXTSB,(OP_REG ra, OP_REG rs, bool rc)); /*0x3d7*/ADD_OPCODE(STFIWX,(OP_REG frs, OP_REG ra, OP_REG rb)); /*0x3da*/ADD_OPCODE(EXTSW,(OP_REG ra, OP_REG rs, bool rc)); @@ -493,7 +850,7 @@ public: ADD_OPCODE(FCFID,(OP_REG frd, OP_REG frb, bool rc)); END_OPCODES_GROUP(G_3f); - ADD_OPCODE(UNK,(const s32 code, const s32 opcode, const s32 gcode)); + ADD_OPCODE(UNK,(const u32 code, const u32 opcode, const u32 gcode)); }; #undef START_OPCODES_GROUP diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 945caabc34..0b8eb74234 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -4,11 +4,20 @@ #include "Emu/Cell/PPUInterpreter.h" #include "Emu/Cell/PPUDisAsm.h" -#include "Emu/SysCalls/SysCalls.h" - extern gcmInfo gcm_info; -PPUThread::PPUThread() : PPCThread(PPC_THREAD_PPU) +PPUThread& GetCurrentPPUThread() +{ + PPCThread* thread = GetCurrentPPCThread(); + + if(!thread || thread->IsSPU()) throw wxString("GetCurrentPPUThread: bad thread"); + + return *(PPUThread*)thread; +} + +PPUThread::PPUThread() + : PPCThread(PPC_THREAD_PPU) + , SysCalls(*this) { Reset(); } @@ -26,13 +35,14 @@ void PPUThread::DoReset() memset(GPR, 0, sizeof(GPR)); memset(SPRG, 0, sizeof(SPRG)); - CR.CR = 0; - LR = 0; - CTR = 0; - USPRG = 0; - TB = 0; - XER.XER = 0; + CR.CR = 0; + LR = 0; + CTR = 0; + USPRG0 = 0; + TB = 0; + XER.XER = 0; FPSCR.FPSCR = 0; + VSCR.VSCR = 0; cycle = 0; @@ -40,50 +50,6 @@ void PPUThread::DoReset() reserve_addr = 0; } -void PPUThread::SetBranch(const u64 pc) -{ - u64 fid, waddr; - if(Memory.MemFlags.IsFlag(pc, waddr, fid)) - { - GPR[3] = SysCallsManager.DoFunc(fid, *this); - - if((s64)GPR[3] < 0 && fid != 0x72a577ce && fid != 0x8461e528) ConLog.Write("Func[0x%llx] done with code [0x%llx]! #pc: 0x%llx", fid, GPR[3], PC); -#ifdef HLE_CALL_LOG - else ConLog.Warning("Func[0xll%x] done with code [0x%llx]! #pc: 0x%llx", fid, GPR[3], PC); -#endif - //ConLog.Warning("Func waddr: 0x%llx", waddr); - const u64 addr = Emu.GetTLSAddr(); - Memory.Write32(waddr, addr); - Memory.Write32(addr, PC + 4); - if(fid == 0x744680a2) Memory.Write32(addr+4, GPR[3]); - //Memory.Write32(addr+4, Emu.GetTLSMemsz()); - } - else if(pc == Emu.GetRSXCallback()) - { - //ConLog.Warning("gcm: callback(context=0x%llx, count=0x%llx) #pc: 0x%llx", GPR[3], GPR[4], PC); - - CellGcmContextData& ctx = *(CellGcmContextData*)Memory.GetMemFromAddr(GPR[3]); - CellGcmControl& ctrl = *(CellGcmControl*)Memory.GetMemFromAddr(gcm_info.control_addr); - - while(ctrl.put != ctrl.get) Sleep(1); - - const u32 reserve = GPR[4]; - - ctx.current = re(re(ctx.begin) + reserve); - - Emu.GetGSManager().GetRender().Pause(); - ctrl.put = ctrl.get = re(reserve); - Emu.GetGSManager().GetRender().Resume(); - - GPR[3] = 0; - - PPCThread::SetBranch(PC + 4); - return; - } - - PPCThread::SetBranch(pc); -} - void PPUThread::AddArgv(const wxString& arg) { stack_point -= arg.Len() + 1; @@ -97,6 +63,7 @@ void PPUThread::InitRegs() const u32 entry = Memory.Read32(PC); const u32 rtoc = Memory.Read32(PC + 4); + ConLog.Write("entry = 0x%x", entry); ConLog.Write("rtoc = 0x%x", rtoc); SetPc(entry); @@ -123,6 +90,7 @@ void PPUThread::InitRegs() return; } + /* const s32 tls_size = Emu.GetTLSFilesz() * thread_num; if(tls_size >= Emu.GetTLSMemsz()) @@ -131,6 +99,7 @@ void PPUThread::InitRegs() Emu.Pause(); return; } + */ stack_point = Memory.AlignAddr(stack_point, 0x200) - 0x200; @@ -154,13 +123,14 @@ void PPUThread::InitRegs() //GPR[10] = 0x131700; GPR[11] = 0x80; GPR[12] = Emu.GetMallocPageSize(); - GPR[13] = 0x10007060; + GPR[13] = Memory.MainMem.Alloc(0x10000) + 0x7060; GPR[28] = GPR[4]; GPR[29] = GPR[3]; GPR[31] = GPR[5]; CTR = PC; CR.CR = 0x22000082; + VSCR.NJ = 1; } u64 PPUThread::GetFreeStackSize() const @@ -193,8 +163,11 @@ void PPUThread::DoPause() void PPUThread::DoStop() { - delete m_dec; - m_dec = 0; + if(m_dec) + { + delete m_dec; + m_dec = nullptr; + } } bool dump_enable = false; @@ -220,36 +193,31 @@ void PPUThread::DoCode(const s32 code) m_dec->Decode(code); } -bool FPRdouble::IsINF(double d) +bool FPRdouble::IsINF(PPCdouble d) { return wxFinite(d) ? 1 : 0; } -bool FPRdouble::IsNaN(double d) +bool FPRdouble::IsNaN(PPCdouble d) { return wxIsNaN(d) ? 1 : 0; } -bool FPRdouble::IsQNaN(double d) +bool FPRdouble::IsQNaN(PPCdouble d) { - return - ((*(u64*)&d & DOUBLE_EXP) == DOUBLE_EXP) && - ((*(u64*)&d & 0x0007fffffffffffULL) == DOUBLE_ZERO) && - ((*(u64*)&d & 0x000800000000000ULL) == 0x000800000000000ULL); + return d.GetType() == FPR_QNAN; } -bool FPRdouble::IsSNaN(double d) +bool FPRdouble::IsSNaN(PPCdouble d) { - return - ((*(u64*)&d & DOUBLE_EXP) == DOUBLE_EXP) && - ((*(u64*)&d & DOUBLE_FRAC) != DOUBLE_ZERO) && - ((*(u64*)&d & 0x0008000000000000ULL) == DOUBLE_ZERO); + return d.GetType() == FPR_SNAN; } -int FPRdouble::Cmp(double a, double b) +int FPRdouble::Cmp(PPCdouble a, PPCdouble b) { if(a < b) return CR_LT; if(a > b) return CR_GT; if(a == b) return CR_EQ; + return CR_SO; } \ No newline at end of file diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 4e0d443a71..7e1c150632 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -1,5 +1,7 @@ #pragma once #include "Emu/Cell/PPCThread.h" +#include "Emu/SysCalls/SysCalls.h" +#include "rpcs3.h" enum { @@ -215,14 +217,14 @@ union CRhdr struct { - u8 cr0 : 4; - u8 cr1 : 4; - u8 cr2 : 4; - u8 cr3 : 4; - u8 cr4 : 4; - u8 cr5 : 4; - u8 cr6 : 4; u8 cr7 : 4; + u8 cr6 : 4; + u8 cr5 : 4; + u8 cr4 : 4; + u8 cr3 : 4; + u8 cr2 : 4; + u8 cr1 : 4; + u8 cr0 : 4; }; }; @@ -239,6 +241,49 @@ union XERhdr }; }; +union VSCRhdr +{ + u32 VSCR; + + struct + { + /* + Saturation. A sticky status bit indicating that some field in a saturating instruction saturated since the last + time SAT was cleared. In other words when SAT = ‘1’ it remains set to ‘1’ until it is cleared to ‘0’ by an + mtvscr instruction. + 1 The vector saturate instruction implicitly sets when saturation has occurred on the results one of + the vector instructions having saturate in its name: + Move To VSCR (mtvscr) + Vector Add Integer with Saturation (vaddubs, vadduhs, vadduws, vaddsbs, vaddshs, + vaddsws) + Vector Subtract Integer with Saturation (vsububs, vsubuhs, vsubuws, vsubsbs, vsubshs, + vsubsws) + Vector Multiply-Add Integer with Saturation (vmhaddshs, vmhraddshs) + Vector Multiply-Sum with Saturation (vmsumuhs, vmsumshs, vsumsws) + Vector Sum-Across with Saturation (vsumsws, vsum2sws, vsum4sbs, vsum4shs, + vsum4ubs) + Vector Pack with Saturation (vpkuhus, vpkuwus, vpkshus, vpkswus, vpkshss, vpkswss) + Vector Convert to Fixed-Point with Saturation (vctuxs, vctsxs) + 0 Indicates no saturation occurred; mtvscr can explicitly clear this bit. + */ + u32 SAT : 1; + u32 X : 15; + + /* + Non-Java. A mode control bit that determines whether vector floating-point operations will be performed + in a Java-IEEE-C9X–compliant mode or a possibly faster non-Java/non-IEEE mode. + 0 The Java-IEEE-C9X–compliant mode is selected. Denormalized values are handled as specified + by Java, IEEE, and C9X standard. + 1 The non-Java/non-IEEE–compliant mode is selected. If an element in a source vector register + contains a denormalized value, the value ‘0’ is used instead. If an instruction causes an underflow + exception, the corresponding element in the target VR is cleared to ‘0’. In both cases, the ‘0’ + has the same sign as the denormalized or underflowing value. + */ + u32 NJ : 1; + u32 Y : 15; + }; +}; + enum FPRType { FPR_NORM, @@ -280,35 +325,53 @@ struct PPCdouble }; FPRType type; + + operator double&() { return _double; } + operator const double&() const { return _double; } - u32 GetType() + PPCdouble& operator = (const PPCdouble& r) { - if(exp > 0 && exp < 0x7ff) return sign ? FPR_NN : FPR_PN; - - if(frac) - { - if(exp) return FPR_QNAN; - - return sign ? FPR_INF : FPR_PINF; - } - - return sign ? FPR_NZ : FPR_PZ; + _u64 = r._u64; + type = UpdateType(); + return *this; } - u32 To32() + FPRType UpdateType() const { - if (exp > 896 || (!frac && !exp)) + const int fpc = _fpclass(_double); + + switch(fpc) { - return ((_u64 >> 32) & 0xc0000000) | ((_u64 >> 29) & 0x3fffffff); + case _FPCLASS_SNAN: return FPR_SNAN; + case _FPCLASS_QNAN: return FPR_QNAN; + case _FPCLASS_NINF: return FPR_NINF; + case _FPCLASS_NN: return FPR_NN; + case _FPCLASS_ND: return FPR_ND; + case _FPCLASS_NZ: return FPR_NZ; + case _FPCLASS_PZ: return FPR_PZ; + case _FPCLASS_PD: return FPR_PD; + case _FPCLASS_PN: return FPR_PN; + case _FPCLASS_PINF: return FPR_PINF; } - if (exp >= 874) - { - return ((0x80000000 | (frac >> 21)) >> (905 - exp)) | (_u64 >> 32) & 0x80000000; - } + throw wxString::Format("PPCdouble::UpdateType() -> unknown fpclass (0x%04x).", fpc); + } - //? - return ((_u64 >> 32) & 0xc0000000) | ((_u64 >> 29) & 0x3fffffff); + FPRType GetType() const + { + return type; + } + + u32 To32() const + { + float res = _double; + + return (u32&)res; + } + + u64 To64() const + { + return (u64&)_double; } u32 GetZerosCount() @@ -369,370 +432,76 @@ struct PPCdouble struct FPRdouble { - static PPCdouble ConvertToIntegerMode(const PPCdouble& d, FPSCRhdr& fpscr, bool is_64, u32 round_mode) - { - PPCdouble ret; - - if(d.exp == 2047) - { - if (d.frac == 0) - { - ret.type = FPR_INF; - - fpscr.FI = 0; - fpscr.FR = 0; - fpscr.VXCVI = 1; - - if(fpscr.VE == 0) - { - if(is_64) - { - return d.sign ? 0x8000000000000000 : 0x7FFFFFFFFFFFFFFF; - } - else - { - return d.sign ? 0x80000000 : 0x7FFFFFFF; - } - - fpscr.FPRF = 0; - } - } - else if(d.nan == 0) - { - ret.type = FPR_SNAN; - - fpscr.FI = 0; - fpscr.FR = 0; - fpscr.VXCVI = 1; - fpscr.VXSNAN = 1; - - if(fpscr.VE == 0) - { - return is_64 ? 0x8000000000000000 : 0x80000000; - fpscr.FPRF = 0; - } - } - else - { - ret.type = FPR_QNAN; - - fpscr.FI = 0; - fpscr.FR = 0; - fpscr.VXCVI = 1; - - if(fpscr.VE == 0) - { - return is_64 ? 0x8000000000000000 : 0x80000000; - fpscr.FPRF = 0; - } - } - } - else if(d.exp > 1054) - { - fpscr.FI = 0; - fpscr.FR = 0; - fpscr.VXCVI = 1; - - if(fpscr.VE == 0) - { - if(is_64) - { - return d.sign ? 0x8000000000000000 : 0x7FFFFFFFFFFFFFFF; - } - else - { - return d.sign ? 0x80000000 : 0x7FFFFFFF; - } - - fpscr.FPRF = 0; - } - } - - ret.sign = d.sign; - - if(d.exp > 0) - { - ret.exp = d.exp - 1023; - ret.frac = 1 | d.frac; - } - else if(d.exp == 0) - { - ret.exp = -1022; - ret.frac = d.frac; - } - /* - if(d.exp == 0) - { - if (d.frac == 0) - { - d.type = FPR_ZERO; - } - else - { - const u32 z = d.GetZerosCount() - 8; - d.frac <<= z + 3; - d.exp -= 1023 - 1 + z; - d.type = FPR_NORM; - } - } - else - { - d.exp -= 1023; - d.type = FPR_NORM; - d.nan = 1; - d.frac <<= 3; - } - */ - - return ret; - } - - static u32 ConvertToFloatMode(PPCdouble& d, u32 RN) - { - /* - u32 fpscr = 0; - switch (d.type) - { - case FPR_NORM: - d.exp += 1023; - if (d.exp > 0) - { - fpscr |= Round(d, RN); - if(d.nan) - { - d.exp++; - d.frac >>= 4; - } - else - { - d.frac >>= 3; - } - - if(d.exp >= 2047) - { - d.exp = 2047; - d.frac = 0; - fpscr |= FPSCR_OX; - } - } - else - { - d.exp = -(s64)d.exp + 1; - - if(d.exp <= 56) - { - d.frac >>= d.exp; - fpscr |= Round(d, RN); - d.frac <<= 1; - if(d.nan) - { - d.exp = 1; - d.frac = 0; - } - else - { - d.exp = 0; - d.frac >>= 4; - fpscr |= FPSCR_UX; - } - } - else - { - d.exp = 0; - d.frac = 0; - fpscr |= FPSCR_UX; - } - } - break; - - case FPR_ZERO: - d.exp = 0; - d.frac = 0; - break; - - case FPR_NAN: - d.exp = 2047; - d.frac = 1; - break; - - case FPR_INF: - d.exp = 2047; - d.frac = 0; - break; - } - - return fpscr; - */ - return 0; - } - - static u32 Round(PPCdouble& d, u32 RN) - { - switch(RN) - { - case FPSCR_RN_NEAR: - if(d.frac & 0x7) - { - if((d.frac & 0x7) != 4 || d.frac & 0x8) - { - d.frac += 4; - } - - return FPSCR_XX; - } - return 0; - - case FPSCR_RN_ZERO: - if(d.frac & 0x7) return FPSCR_XX; - return 0; - - case FPSCR_RN_PINF: - if(!d.sign && (d.frac & 0x7)) - { - d.frac += 8; - return FPSCR_XX; - } - return 0; - - case FPSCR_RN_MINF: - if(d.sign && (d.frac & 0x7)) - { - d.frac += 8; - return FPSCR_XX; - } - return 0; - } - - return 0; - } - static const u64 double_sign = 0x8000000000000000ULL; static const u64 double_frac = 0x000FFFFFFFFFFFFFULL; - - static bool IsINF(double d); - static bool IsNaN(double d); - static bool IsQNaN(double d); - static bool IsSNaN(double d); - static int Cmp(double a, double b); + static bool IsINF(PPCdouble d); + static bool IsNaN(PPCdouble d); + static bool IsQNaN(PPCdouble d); + static bool IsSNaN(PPCdouble d); + + static int Cmp(PPCdouble a, PPCdouble b); }; union VPR_reg { //__m128i _m128i; u128 _u128; - s128 _i128; + s128 _s128; u64 _u64[2]; - s64 _i64[2]; + s64 _s64[2]; u32 _u32[4]; - s32 _i32[4]; + s32 _s32[4]; u16 _u16[8]; - s16 _i16[8]; + s16 _s16[8]; u8 _u8[16]; - s8 _i8[16]; - - //struct { float x, y, z, w; }; + s8 _s8[16]; + float _f[4]; + double _d[2]; VPR_reg() { Clear(); } - VPR_reg(const __m128i val){_u128._u64[0] = val.m128i_u64[0]; _u128._u64[1] = val.m128i_u64[1];} - VPR_reg(const u128 val) { _u128 = val; } - VPR_reg(const u64 val) { Clear(); _u64[0] = val; } - VPR_reg(const u32 val) { Clear(); _u32[0] = val; } - VPR_reg(const u16 val) { Clear(); _u16[0] = val; } - VPR_reg(const u8 val) { Clear(); _u8[0] = val; } - VPR_reg(const s128 val) { _i128 = val; } - VPR_reg(const s64 val) { Clear(); _i64[0] = val; } - VPR_reg(const s32 val) { Clear(); _i32[0] = val; } - VPR_reg(const s16 val) { Clear(); _i16[0] = val; } - VPR_reg(const s8 val) { Clear(); _i8[0] = val; } - - operator u128() const { return _u128; } - operator s128() const { return _i128; } - operator u64() const { return _u64[0]; } - operator s64() const { return _i64[0]; } - operator u32() const { return _u32[0]; } - operator s32() const { return _i32[0]; } - operator u16() const { return _u16[0]; } - operator s16() const { return _i16[0]; } - operator u8() const { return _u8[0]; } - operator s8() const { return _i8[0]; } - operator __m128i() { __m128i ret; ret.m128i_u64[0]=_u128._u64[0]; ret.m128i_u64[1]=_u128._u64[1]; return ret; } - operator bool() const { return _u64[0] != 0 || _u64[1] != 0; } - - wxString ToString() const + wxString ToString(bool hex=false) const { - return wxString::Format("%08x%08x%08x%08x", _u32[3], _u32[2], _u32[1], _u32[0]); + if(hex) return wxString::Format("%08x%08x%08x%08x", _u32[3], _u32[2], _u32[1], _u32[0]); + + return wxString::Format("x: %g y: %g z: %g w: %g", _f[3], _f[2], _f[1], _f[0]); } - VPR_reg operator ^ (VPR_reg right) { return _mm_xor_si128(*this, right); } - VPR_reg operator | (VPR_reg right) { return _mm_or_si128 (*this, right); } - VPR_reg operator & (VPR_reg right) { return _mm_and_si128(*this, right); } + u8 GetBit(u8 bit) + { + if(bit < 64) return (_u64[0] >> bit) & 0x1; - VPR_reg operator ^ (__m128i right) { return _mm_xor_si128(*this, right); } - VPR_reg operator | (__m128i right) { return _mm_or_si128 (*this, right); } - VPR_reg operator & (__m128i right) { return _mm_and_si128(*this, right); } + return (_u64[1] >> (bit - 64)) & 0x1; + } - bool operator == (const VPR_reg& right){ return _u64[0] == right._u64[0] && _u64[1] == right._u64[1]; } + void SetBit(u8 bit, u8 value) + { + if(bit < 64) + { + _u64[0] &= ~(1 << bit); + _u64[0] |= (value & 0x1) << bit; - bool operator == (const u128 right) { return _u64[0] == right._u64[0] && _u64[1] == right._u64[1]; } - bool operator == (const s128 right) { return _i64[0] == right._i64[0] && _i64[1] == right._i64[1]; } - bool operator == (const u64 right) { return _u64[0] == (u64)right && _u64[1] == 0; } - bool operator == (const s64 right) { return _i64[0] == (s64)right && _i64[1] == 0; } - bool operator == (const u32 right) { return _u64[0] == (u64)right && _u64[1] == 0; } - bool operator == (const s32 right) { return _i64[0] == (s64)right && _i64[1] == 0; } - bool operator == (const u16 right) { return _u64[0] == (u64)right && _u64[1] == 0; } - bool operator == (const s16 right) { return _i64[0] == (s64)right && _i64[1] == 0; } - bool operator == (const u8 right) { return _u64[0] == (u64)right && _u64[1] == 0; } - bool operator == (const s8 right) { return _i64[0] == (s64)right && _i64[1] == 0; } + return; + } - bool operator != (const VPR_reg& right){ return !(*this == right); } - bool operator != (const u128 right) { return !(*this == right); } - bool operator != (const u64 right) { return !(*this == right); } - bool operator != (const u32 right) { return !(*this == right); } - bool operator != (const u16 right) { return !(*this == right); } - bool operator != (const u8 right) { return !(*this == right); } - bool operator != (const s128 right) { return !(*this == right); } - bool operator != (const s64 right) { return !(*this == right); } - bool operator != (const s32 right) { return !(*this == right); } - bool operator != (const s16 right) { return !(*this == right); } - bool operator != (const s8 right) { return !(*this == right); } + bit -= 64; - s64& d(const u32 c) { return _i64[1 - c]; } - u64& ud(const u32 c) { return _u64[1 - c]; } - - s32& w(const u32 c) { return _i32[3 - c]; } - u32& uw(const u32 c) { return _u32[3 - c]; } - - s16& h(const u32 c) { return _i16[7 - c]; } - u16& uh(const u32 c) { return _u16[7 - c]; } - - s8& b(const u32 c) { return _i8[15 - c]; } - u8& ub(const u32 c) { return _u8[15 - c]; } + _u64[1] &= ~(1 << bit); + _u64[1] |= (value & 0x1) << bit; + } void Clear() { memset(this, 0, sizeof(*this)); } }; -/* -struct VPR_table -{ - VPR_reg t[32]; - - operator VPR_reg*() { return t; } - - VPR_reg& operator [] (int index) - { - return t[index]; - } -} -*/ - static const s32 MAX_INT_VALUE = 0x7fffffff; -class PPUThread : public PPCThread +class PPUThread + : public PPCThread + , public SysCalls { public: - double FPR[32]; //Floating Point Register + PPCdouble FPR[32]; //Floating Point Register FPSCRhdr FPSCR; //Floating Point Status and Control Register u64 GPR[32]; //General-Purpose Register VPR_reg VPR[32]; @@ -786,12 +555,16 @@ public: MSRhdr MSR; //Machine State Register PVRhdr PVR; //Processor Version Register + VSCRhdr VSCR; // Vector Status and Control Register + u64 LR; //SPR 0x008 : Link Register u64 CTR; //SPR 0x009 : Count Register - s32 USPRG; //SPR 0x100 : User-SPR General-Purpose Registers - - s32 SPRG[8]; //SPR 0x100 - 0x107 : SPR General-Purpose Registers + union + { + u64 USPRG0; //SPR 0x100 : User-SPR General-Purpose Register 0 + u64 SPRG[8]; //SPR 0x100 - 0x107 : SPR General-Purpose Registers + }; //TBR : Time-Base Registers union @@ -818,14 +591,14 @@ public: { switch(n) { - case 7: return CR.cr0; - case 6: return CR.cr1; - case 5: return CR.cr2; - case 4: return CR.cr3; - case 3: return CR.cr4; - case 2: return CR.cr5; - case 1: return CR.cr6; - case 0: return CR.cr7; + case 0: return CR.cr0; + case 1: return CR.cr1; + case 2: return CR.cr2; + case 3: return CR.cr3; + case 4: return CR.cr4; + case 5: return CR.cr5; + case 6: return CR.cr6; + case 7: return CR.cr7; } return 0; @@ -835,14 +608,14 @@ public: { switch(n) { - case 7: CR.cr0 = value; break; - case 6: CR.cr1 = value; break; - case 5: CR.cr2 = value; break; - case 4: CR.cr3 = value; break; - case 3: CR.cr4 = value; break; - case 2: CR.cr5 = value; break; - case 1: CR.cr6 = value; break; - case 0: CR.cr7 = value; break; + case 0: CR.cr0 = value; break; + case 1: CR.cr1 = value; break; + case 2: CR.cr2 = value; break; + case 3: CR.cr3 = value; break; + case 4: CR.cr4 = value; break; + case 5: CR.cr5 = value; break; + case 6: CR.cr6 = value; break; + case 7: CR.cr7 = value; break; } } @@ -850,14 +623,14 @@ public: { switch(n) { - case 7: value ? CR.cr0 |= bit : CR.cr0 &= ~bit; break; - case 6: value ? CR.cr1 |= bit : CR.cr1 &= ~bit; break; - case 5: value ? CR.cr2 |= bit : CR.cr2 &= ~bit; break; - case 4: value ? CR.cr3 |= bit : CR.cr3 &= ~bit; break; - case 3: value ? CR.cr4 |= bit : CR.cr4 &= ~bit; break; - case 2: value ? CR.cr5 |= bit : CR.cr5 &= ~bit; break; - case 1: value ? CR.cr6 |= bit : CR.cr6 &= ~bit; break; - case 0: value ? CR.cr7 |= bit : CR.cr7 &= ~bit; break; + case 0: value ? CR.cr0 |= bit : CR.cr0 &= ~bit; break; + case 1: value ? CR.cr1 |= bit : CR.cr1 &= ~bit; break; + case 2: value ? CR.cr2 |= bit : CR.cr2 &= ~bit; break; + case 3: value ? CR.cr3 |= bit : CR.cr3 &= ~bit; break; + case 4: value ? CR.cr4 |= bit : CR.cr4 &= ~bit; break; + case 5: value ? CR.cr5 |= bit : CR.cr5 &= ~bit; break; + case 6: value ? CR.cr6 |= bit : CR.cr6 &= ~bit; break; + case 7: value ? CR.cr7 |= bit : CR.cr7 &= ~bit; break; } } @@ -916,17 +689,31 @@ public: virtual wxString RegsToString() { wxString ret = PPCThread::RegsToString(); + for(uint i=0; i<32; ++i) ret += wxString::Format("GPR[%d] = 0x%llx\n", i, GPR[i]); for(uint i=0; i<32; ++i) ret += wxString::Format("FPR[%d] = %.6G\n", i, FPR[i]); + for(uint i=0; i<32; ++i) ret += wxString::Format("VPR[%d] = 0x%s [%s]\n", i, VPR[i].ToString(true), VPR[i].ToString()); ret += wxString::Format("CR = 0x%08x\n", CR); ret += wxString::Format("LR = 0x%llx\n", LR); ret += wxString::Format("CTR = 0x%llx\n", CTR); - ret += wxString::Format("XER = 0x%llx\n", XER); - ret += wxString::Format("FPSCR = 0x%x\n", FPSCR); + ret += wxString::Format("XER = 0x%llx [CA=%lld | OV=%lld | SO=%lld]\n", XER, XER.CA, XER.OV, XER.SO); + ret += wxString::Format("FPSCR = 0x%x " + "[RN=%d | NI=%d | XE=%d | ZE=%d | UE=%d | OE=%d | VE=%d | " + "VXCVI=%d | VXSQRT=%d | VXSOFT=%d | FPRF=%d | " + "FI=%d | FR=%d | VXVC=%d | VXIMZ=%d | " + "VXZDZ=%d | VXIDI=%d | VXISI=%d | VXSNAN=%d | " + "XX=%d | ZX=%d | UX=%d | OX=%d | VX=%d | FEX=%d | FX=%d]\n", + FPSCR, + FPSCR.RN, + FPSCR.NI, FPSCR.XE, FPSCR.ZE, FPSCR.UE, FPSCR.OE, FPSCR.VE, + FPSCR.VXCVI, FPSCR.VXSQRT, FPSCR.VXSOFT, FPSCR.FPRF, + FPSCR.FI, FPSCR.FR, FPSCR.VXVC, FPSCR.VXIMZ, + FPSCR.VXZDZ, FPSCR.VXIDI, FPSCR.VXISI, FPSCR.VXSNAN, + FPSCR.XX, FPSCR.ZX, FPSCR.UX, FPSCR.OX, FPSCR.VX, FPSCR.FEX, FPSCR.FX); + return ret; } - void SetBranch(const u64 pc); virtual void AddArgv(const wxString& arg); public: @@ -940,6 +727,8 @@ protected: virtual void DoResume(); virtual void DoStop(); -private: +public: virtual void DoCode(const s32 code); -}; \ No newline at end of file +}; + +PPUThread& GetCurrentPPUThread(); \ No newline at end of file diff --git a/rpcs3/Emu/Cell/SPUDecoder.h b/rpcs3/Emu/Cell/SPUDecoder.h index 1724924992..5f315a8d2a 100644 --- a/rpcs3/Emu/Cell/SPUDecoder.h +++ b/rpcs3/Emu/Cell/SPUDecoder.h @@ -31,18 +31,20 @@ class SPU_Decoder : public Decoder OP_REG RA() const { return GetField(18, 24); } OP_REG RB() const { return GetField(11, 17); } - OP_sIMM i7() const { return GetField(11, 17); } - OP_sIMM i10() const { return GetField(8, 17); } - OP_sIMM i16() const { return GetField(9, 24); } - OP_sIMM i18() const { return GetField(7, 24); } + OP_uIMM i7() const { return GetField(11, 17); } + OP_uIMM i8() const { return GetField(10, 17); } + OP_uIMM i10() const { return GetField(8, 17); } + OP_uIMM i16() const { return GetField(9, 24); } + OP_uIMM i18() const { return GetField(7, 24); } - OP_sIMM ROH() const { return GetField(16, 17); } - OP_sIMM ROL() const { return GetField(25, 31); } - OP_sIMM RO() const { return ROL()/* | (ROH() << 8)*/; } + OP_uIMM ROH() const { return GetField(16, 17); } + OP_uIMM ROL() const { return GetField(25, 31); } + OP_uIMM RO() const { return ROL() | (ROH() << 8); } OP_uIMM RR() const { return GetField(0, 10); } OP_uIMM RRR() const { return GetField(0, 3); } OP_uIMM RI7() const { return GetField(0, 10); } + OP_uIMM RI8() const { return GetField(0, 9); } OP_uIMM RI10() const { return GetField(0, 7); } OP_uIMM RI16() const { return GetField(0, 8); } OP_uIMM RI18() const { return GetField(0, 6); } @@ -93,59 +95,219 @@ public: virtual void Decode(const u32 code) { + using namespace SPU_opcodes; + m_code = code; switch(RR()) //& RI7 //0 - 10 { ADD_OPCODE(STOP,(GetField(18, 31))); ADD_OPCODE(LNOP,()); + ADD_OPCODE(SYNC,(GetField(11))); + ADD_OPCODE(DSYNC,()); + ADD_OPCODE(MFSPR,(RT(), RA())); ADD_OPCODE(RDCH,(RT(), RA())); ADD_OPCODE(RCHCNT,(RT(), RA())); ADD_OPCODE(SF,(RT(), RA(), RB())); + ADD_OPCODE(OR,(RT(), RA(), RB())); + ADD_OPCODE(BG,(RT(), RA(), RB())); + ADD_OPCODE(SFH,(RT(), RA(), RB())); + ADD_OPCODE(NOR,(RT(), RA(), RB())); + ADD_OPCODE(ABSDB,(RT(), RA(), RB())); + ADD_OPCODE(ROT,(RT(), RA(), RB())); + ADD_OPCODE(ROTM,(RT(), RA(), RB())); + ADD_OPCODE(ROTMA,(RT(), RA(), RB())); + ADD_OPCODE(SHL,(RT(), RA(), RB())); + ADD_OPCODE(ROTH,(RT(), RA(), RB())); + ADD_OPCODE(ROTHM,(RT(), RA(), RB())); + ADD_OPCODE(ROTMAH,(RT(), RA(), RB())); + ADD_OPCODE(SHLH,(RT(), RA(), RB())); + ADD_OPCODE(ROTI,(RT(), RA(), RB())); + ADD_OPCODE(ROTMI,(RT(), RA(), RB())); + ADD_OPCODE(ROTMAI,(RT(), RA(), RB())); ADD_OPCODE(SHLI,(RT(), RA(), i7())); + ADD_OPCODE(ROTHI,(RT(), RA(), i7())); + ADD_OPCODE(ROTHMI,(RT(), RA(), i7())); + ADD_OPCODE(ROTMAHI,(RT(), RA(), i7())); + ADD_OPCODE(SHLHI,(RT(), RA(), i7())); ADD_OPCODE(A,(RT(), RA(), RB())); - ADD_OPCODE(SPU_AND,(RT(), RA(), RB())); - ADD_OPCODE(LQX,(RT(), RA(), RB())); + ADD_OPCODE(AND,(RT(), RA(), RB())); + ADD_OPCODE(CG,(RT(), RA(), RB())); + ADD_OPCODE(AH,(RT(), RA(), RB())); + ADD_OPCODE(NAND,(RT(), RA(), RB())); + ADD_OPCODE(AVGB,(RT(), RA(), RB())); + ADD_OPCODE(MTSPR,(RT(), RA())); ADD_OPCODE(WRCH,(RA(), RT())); + ADD_OPCODE(BIZ,(RT(), RA())); + ADD_OPCODE(BINZ,(RT(), RA())); + ADD_OPCODE(BIHZ,(RT(), RA())); + ADD_OPCODE(BIHNZ,(RT(), RA())); + ADD_OPCODE(STOPD,(RT(), RA(), RB())); ADD_OPCODE(STQX,(RT(), RA(), RB())); ADD_OPCODE(BI,(RA())); ADD_OPCODE(BISL,(RT(), RA())); + ADD_OPCODE(IRET,(RA())); + ADD_OPCODE(BISLED,(RT(), RA())); ADD_OPCODE(HBR,(GetField(11), RO(), RA())); + ADD_OPCODE(GB,(RT(), RA())); + ADD_OPCODE(GBH,(RT(), RA())); + ADD_OPCODE(GBB,(RT(), RA())); + ADD_OPCODE(FSM,(RT(), RA())); + ADD_OPCODE(FSMH,(RT(), RA())); + ADD_OPCODE(FSMB,(RT(), RA())); + ADD_OPCODE(FREST,(RT(), RA())); + ADD_OPCODE(FRSQEST,(RT(), RA())); + ADD_OPCODE(LQX,(RT(), RA(), RB())); + ADD_OPCODE(ROTQBYBI,(RT(), RA(), RB())); + ADD_OPCODE(ROTQMBYBI,(RT(), RA(), RB())); + ADD_OPCODE(SHLQBYBI,(RT(), RA(), RB())); + ADD_OPCODE(CBX,(RT(), RA(), RB())); + ADD_OPCODE(CHX,(RT(), RA(), RB())); ADD_OPCODE(CWX,(RT(), RA(), RB())); + ADD_OPCODE(CDX,(RT(), RA(), RB())); + ADD_OPCODE(ROTQBI,(RT(), RA(), RB())); + ADD_OPCODE(ROTQMBI,(RT(), RA(), RB())); + ADD_OPCODE(SHLQBI,(RT(), RA(), RB())); ADD_OPCODE(ROTQBY,(RT(), RA(), RB())); + ADD_OPCODE(ROTQMBY,(RT(), RA(), RB())); + ADD_OPCODE(SHLQBY,(RT(), RA(), RB())); + ADD_OPCODE(ORX,(RT(), RA())); + ADD_OPCODE(CBD,(RT(), RA(), exts7(i7()))); + ADD_OPCODE(CHD,(RT(), RA(), exts7(i7()))); + ADD_OPCODE(CWD,(RT(), RA(), exts7(i7()))); + ADD_OPCODE(CDD,(RT(), RA(), exts7(i7()))); + ADD_OPCODE(ROTQBII,(RT(), RA(), i7())); + ADD_OPCODE(ROTQMBII,(RT(), RA(), i7())); + ADD_OPCODE(SHLQBII,(RT(), RA(), i7())); ADD_OPCODE(ROTQBYI,(RT(), RA(), i7())); + ADD_OPCODE(ROTQMBYI,(RT(), RA(), i7())); ADD_OPCODE(SHLQBYI,(RT(), RA(), i7())); - ADD_OPCODE(SPU_NOP,(RT())); + ADD_OPCODE(NOP,(RT())); + ADD_OPCODE(CGT,(RT(), RA(), RB())); + ADD_OPCODE(XOR,(RT(), RA(), RB())); + ADD_OPCODE(CGTH,(RT(), RA(), RB())); + ADD_OPCODE(EQV,(RT(), RA(), RB())); + ADD_OPCODE(CGTB,(RT(), RA(), RB())); + ADD_OPCODE(SUMB,(RT(), RA(), RB())); + ADD_OPCODE(HGT,(RT(), RA(), RB())); + ADD_OPCODE(CLZ,(RT(), RA())); + ADD_OPCODE(XSWD,(RT(), RA())); + ADD_OPCODE(XSHW,(RT(), RA())); + ADD_OPCODE(CNTB,(RT(), RA())); + ADD_OPCODE(XSBH,(RT(), RA())); ADD_OPCODE(CLGT,(RT(), RA(), RB())); + ADD_OPCODE(ANDC,(RT(), RA(), RB())); + ADD_OPCODE(FCGT,(RT(), RA(), RB())); + ADD_OPCODE(DFCGT,(RT(), RA(), RB())); + ADD_OPCODE(FA,(RT(), RA(), RB())); + ADD_OPCODE(FS,(RT(), RA(), RB())); + ADD_OPCODE(FM,(RT(), RA(), RB())); + ADD_OPCODE(CLGTH,(RT(), RA(), RB())); + ADD_OPCODE(ORC,(RT(), RA(), RB())); + ADD_OPCODE(FCMGT,(RT(), RA(), RB())); + ADD_OPCODE(DFCMGT,(RT(), RA(), RB())); + ADD_OPCODE(DFA,(RT(), RA(), RB())); + ADD_OPCODE(DFS,(RT(), RA(), RB())); + ADD_OPCODE(DFM,(RT(), RA(), RB())); + ADD_OPCODE(CLGTB,(RT(), RA(), RB())); + ADD_OPCODE(HLGT,(RT(), RA(), RB())); + ADD_OPCODE(DFMA,(RT(), RA(), RB())); + ADD_OPCODE(DFMS,(RT(), RA(), RB())); + ADD_OPCODE(DFNMS,(RT(), RA(), RB())); + ADD_OPCODE(DFNMA,(RT(), RA(), RB())); + ADD_OPCODE(CEQ,(RT(), RA(), RB())); + ADD_OPCODE(MPYHHU,(RT(), RA(), RB())); + ADD_OPCODE(ADDX,(RT(), RA(), RB())); + ADD_OPCODE(SFX,(RT(), RA(), RB())); + ADD_OPCODE(CGX,(RT(), RA(), RB())); + ADD_OPCODE(BGX,(RT(), RA(), RB())); + ADD_OPCODE(MPYHHA,(RT(), RA(), RB())); + ADD_OPCODE(MPYHHAU,(RT(), RA(), RB())); + ADD_OPCODE(FSCRRD,(RT())); + ADD_OPCODE(FESD,(RT(), RA())); + ADD_OPCODE(FRDS,(RT(), RA())); + ADD_OPCODE(FSCRWR,(RT(), RA())); + ADD_OPCODE(DFTSV,(RT(), RA(), i7())); + ADD_OPCODE(FCEQ,(RT(), RA(), RB())); + ADD_OPCODE(DFCEQ,(RT(), RA(), RB())); + ADD_OPCODE(MPY,(RT(), RA(), RB())); + ADD_OPCODE(MPYH,(RT(), RA(), RB())); + ADD_OPCODE(MPYHH,(RT(), RA(), RB())); + ADD_OPCODE(MPYS,(RT(), RA(), RB())); + ADD_OPCODE(CEQH,(RT(), RA(), RB())); + ADD_OPCODE(FCMEQ,(RT(), RA(), RB())); + ADD_OPCODE(DFCMEQ,(RT(), RA(), RB())); + ADD_OPCODE(MPYU,(RT(), RA(), RB())); + ADD_OPCODE(CEQB,(RT(), RA(), RB())); + ADD_OPCODE(FI,(RT(), RA(), RB())); + ADD_OPCODE(HEQ,(RT(), RA(), RB())); + } + + switch(RI8()) //0 - 9 + { + ADD_OPCODE(CFLTS,(RT(), RA(), i8())); + ADD_OPCODE(CFLTU,(RT(), RA(), i8())); + ADD_OPCODE(CSFLT,(RT(), RA(), i8())); + ADD_OPCODE(CUFLT,(RT(), RA(), i8())); } switch(RI16()) //0 - 8 { ADD_OPCODE(BRZ,(RT(), exts16(i16()))); + ADD_OPCODE(STQA,(RT(), exts16(i16()))); + ADD_OPCODE(BRNZ,(RT(), exts16(i16()))); ADD_OPCODE(BRHZ,(RT(), exts16(i16()))); ADD_OPCODE(BRHNZ,(RT(), exts16(i16()))); ADD_OPCODE(STQR,(RT(), i16())); + ADD_OPCODE(BRA,(exts16(i16()))); + ADD_OPCODE(LQA,(RT(), exts16(i16()))); + ADD_OPCODE(BRASL,(RT(), exts16(i16()))); ADD_OPCODE(BR,(exts16(i16()))); ADD_OPCODE(FSMBI,(RT(), i16())); ADD_OPCODE(BRSL,(RT(), exts16(i16()))); - ADD_OPCODE(IL,(RT(), exts16(i16()))); ADD_OPCODE(LQR,(RT(), exts16(i16()))); + ADD_OPCODE(IL,(RT(), exts16(i16()))); + ADD_OPCODE(ILHU,(RT(), i16())); + ADD_OPCODE(ILH,(RT(), i16())); + ADD_OPCODE(IOHL,(RT(), i16())); } switch(RI10()) //0 - 7 { - ADD_OPCODE(SPU_ORI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(ORI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(ORHI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(ORBI,(RT(), RA(), i10())); + ADD_OPCODE(SFI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(SFHI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(ANDI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(ANDHI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(ANDBI,(RT(), RA(), i10())); ADD_OPCODE(AI,(RT(), RA(), exts10(i10()))); ADD_OPCODE(AHI,(RT(), RA(), exts10(i10()))); ADD_OPCODE(STQD,(RT(), exts10(i10()) << 4, RA())); ADD_OPCODE(LQD,(RT(), exts10(i10()) << 4, RA())); - ADD_OPCODE(CLGTI,(RT(), RA(), i10())); - ADD_OPCODE(CLGTHI,(RT(), RA(), i10())); - ADD_OPCODE(CEQI,(RT(), RA(), i10())); + ADD_OPCODE(XORI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(XORHI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(XORBI,(RT(), RA(), i10())); + ADD_OPCODE(CGTI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(CGTHI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(CGTBI,(RT(), RA(), i10())); + ADD_OPCODE(HGTI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(CLGTI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(CLGTHI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(CLGTBI,(RT(), RA(), i10())); + ADD_OPCODE(HLGTI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(MPYI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(MPYUI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(CEQI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(CEQHI,(RT(), RA(), exts10(i10()))); + ADD_OPCODE(CEQBI,(RT(), RA(), i10())); + ADD_OPCODE(HEQI,(RT(), RA(), exts10(i10()))); } switch(RI18()) //0 - 6 { + ADD_OPCODE(HBRA,(RO(), i16() << 2)); ADD_OPCODE(HBRR,(RO(), exts16(i16()))); ADD_OPCODE(ILA,(RT(), i18())); } @@ -154,6 +316,10 @@ public: { ADD_OPCODE(SELB,(RC(), RA(), RB(), RT())); ADD_OPCODE(SHUFB,(RC(), RA(), RB(), RT())); + ADD_OPCODE(MPYA,(RC(), RA(), RB(), RT())); + ADD_OPCODE(FNMS,(RC(), RA(), RB(), RT())); + ADD_OPCODE(FMA,(RC(), RA(), RB(), RT())); + ADD_OPCODE(FMS,(RC(), RA(), RB(), RT())); } m_op.UNK(m_code, 0, 0); diff --git a/rpcs3/Emu/Cell/SPUDisAsm.h b/rpcs3/Emu/Cell/SPUDisAsm.h index bdec9631a6..4937f4adf5 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.h +++ b/rpcs3/Emu/Cell/SPUDisAsm.h @@ -58,6 +58,18 @@ private: { Write("lnop"); } + virtual void SYNC(OP_uIMM Cbit) + { + Write(wxString::Format("sync %d", Cbit)); + } + virtual void DSYNC() + { + Write("dsync"); + } + virtual void MFSPR(OP_REG rt, OP_REG sa) + { + Write(wxString::Format("mfspr %s,%s", spu_reg_name[rt], spu_reg_name[sa])); // Are SPR mapped on the GPR or are there 128 additional registers ? + } virtual void RDCH(OP_REG rt, OP_REG ra) { Write(wxString::Format("rdch %s,%s", spu_reg_name[rt], spu_ch_name[ra])); @@ -70,26 +82,142 @@ private: { Write(wxString::Format("sf %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); } + virtual void OR(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("or %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void BG(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("bg %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void SFH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("sfh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void NOR(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("nor %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ABSDB(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("absdb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROT(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rot %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTM(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rotm %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTMA(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rotma %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void SHL(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("shl %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("roth %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTHM(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rothm %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTMAH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rotmah %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void SHLH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("shlh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("roti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void ROTMI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("rotmi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void ROTMAI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("rotmai %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } virtual void SHLI(OP_REG rt, OP_REG ra, OP_sIMM i7) { Write(wxString::Format("shli %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); } + virtual void ROTHI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("rothi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void ROTHMI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("rothmi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void ROTMAHI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("rotmahi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void SHLHI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("shlhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } virtual void A(OP_REG rt, OP_REG ra, OP_REG rb) { Write(wxString::Format("a %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); } - virtual void SPU_AND(OP_REG rt, OP_REG ra, OP_REG rb) + virtual void AND(OP_REG rt, OP_REG ra, OP_REG rb) { Write(wxString::Format("and %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); } - virtual void LQX(OP_REG rt, OP_REG ra, OP_REG rb) + virtual void CG(OP_REG rt, OP_REG ra, OP_REG rb) { - Write(wxString::Format("lqx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + Write(wxString::Format("cg %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void AH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("ah %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void NAND(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("nand %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void AVGB(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("avgb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MTSPR(OP_REG rt, OP_REG sa) + { + Write(wxString::Format("mtspr %s,%s", spu_reg_name[rt], spu_reg_name[sa])); } virtual void WRCH(OP_REG ra, OP_REG rt) { Write(wxString::Format("wrch %s,%s", spu_ch_name[ra], spu_reg_name[rt])); } + virtual void BIZ(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("biz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void BINZ(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("binz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void BIHZ(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("bihz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void BIHNZ(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("bihnz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void STOPD(OP_REG rc, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("bihnz %s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb])); + } virtual void STQX(OP_REG rt, OP_REG ra, OP_REG rb) { Write(wxString::Format("stqx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); @@ -102,40 +230,418 @@ private: { Write(wxString::Format("bisl %s,%s", spu_reg_name[rt], spu_reg_name[ra])); } + virtual void IRET(OP_REG ra) + { + Write(wxString::Format("iret %s", spu_reg_name[ra])); + } + virtual void BISLED(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("bisled %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } virtual void HBR(OP_REG p, OP_REG ro, OP_REG ra) { Write(wxString::Format("hbr 0x%x,%s", DisAsmBranchTarget(ro), spu_reg_name[ra])); } + virtual void GB(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("gb %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void GBH(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("gbh %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void GBB(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("gbb %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void FSM(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("fsm %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void FSMH(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("fsmh %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void FSMB(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("fsmb %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void FREST(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("frest %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void FRSQEST(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("frsqest %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void LQX(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("lqx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTQBYBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rotqbybi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTQMBYBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rotqmbybi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void SHLQBYBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("shlqbybi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CBX(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("cbx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CHX(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("chx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } virtual void CWX(OP_REG rt, OP_REG ra, OP_REG rb) { Write(wxString::Format("cwx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); } + virtual void CDX(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("cdx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTQBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rotqbi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTQMBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rotqmbi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void SHLQBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("shlqbi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } virtual void ROTQBY(OP_REG rt, OP_REG ra, OP_REG rb) { - Write(wxString::Format("rotqby %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], rb)); + Write(wxString::Format("rotqby %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ROTQMBY(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("rotqmby %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void SHLQBY(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("shlqby %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ORX(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("orx %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void CBD(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("cbd %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void CHD(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("chd %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void CWD(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("cwd %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void CDD(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("cdd %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void ROTQBII(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("rotqbii %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void ROTQMBII(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("rotqmbii %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void SHLQBII(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("shlqbii %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); } virtual void ROTQBYI(OP_REG rt, OP_REG ra, OP_sIMM i7) { Write(wxString::Format("rotqbyi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); } + virtual void ROTQMBYI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("rotqmbyi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } virtual void SHLQBYI(OP_REG rt, OP_REG ra, OP_sIMM i7) { Write(wxString::Format("shlqbyi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); } - virtual void SPU_NOP(OP_REG rt) + virtual void NOP(OP_REG rt) { Write(wxString::Format("nop %s", spu_reg_name[rt])); } + virtual void CGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("cgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void XOR(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("xor %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CGTH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("cgth %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void EQV(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("eqv %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CGTB(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("cgtb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void SUMB(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("sumb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void HGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("hgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CLZ(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("clz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void XSWD(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("xswd %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void XSHW(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("xshw %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void CNTB(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("cntb %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void XSBH(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("xsbh %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } virtual void CLGT(OP_REG rt, OP_REG ra, OP_REG rb) { Write(wxString::Format("clgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); } + virtual void ANDC(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("andc %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void FCGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("fcgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFCGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfcgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void FA(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("fa %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void FS(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("fs %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void FM(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("fm %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CLGTH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("clgth %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ORC(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("orc %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void FCMGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("fcmgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFCMGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfcmgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFA(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfa %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFS(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfs %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFM(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfm %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CLGTB(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("clgtb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void HLGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("hlgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFMA(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfma %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFMS(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfms %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFNMS(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfnms %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFNMA(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfnma %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("ceq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MPYHHU(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("mpyhhu %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void ADDX(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("addx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void SFX(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("sfx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CGX(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("cgx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void BGX(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("bgx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MPYHHA(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("mpyhha %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MPYHHAU(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("mpyhhau %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void FSCRRD(OP_REG rt) + { + Write(wxString::Format("fscrrd %s", spu_reg_name[rt])); + } + virtual void FESD(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("fesd %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void FRDS(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("frds %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void FSCRWR(OP_REG rt, OP_REG ra) + { + Write(wxString::Format("fscrwr %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + } + virtual void DFTSV(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + Write(wxString::Format("dftsv %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + } + virtual void FCEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("fceq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFCEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfceq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MPY(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("mpy %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MPYH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("mpyh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MPYHH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("mpyhh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MPYS(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("mpys %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CEQH(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("ceqh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void FCMEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("fcmeq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void DFCMEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("dfcmeq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void MPYU(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("mpyu %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void CEQB(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("ceqb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void FI(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("fi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + virtual void HEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + Write(wxString::Format("heq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + } + + //0 - 9 + virtual void CFLTS(OP_REG rt, OP_REG ra, OP_sIMM i8) + { + Write(wxString::Format("cflts %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i8)); + } + virtual void CFLTU(OP_REG rt, OP_REG ra, OP_sIMM i8) + { + Write(wxString::Format("cfltu %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i8)); + } + virtual void CSFLT(OP_REG rt, OP_REG ra, OP_sIMM i8) + { + Write(wxString::Format("csflt %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i8)); + } + virtual void CUFLT(OP_REG rt, OP_REG ra, OP_sIMM i8) + { + Write(wxString::Format("cuflt %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i8)); + } //0 - 8 virtual void BRZ(OP_REG rt, OP_sIMM i16) { Write(wxString::Format("brz %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); } + virtual void STQA(OP_REG rt, OP_sIMM i16) + { + Write(wxString::Format("stqa %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + } + virtual void BRNZ(OP_REG rt, OP_sIMM i16) + { + Write(wxString::Format("brnz %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + } virtual void BRHZ(OP_REG rt, OP_sIMM i16) { Write(wxString::Format("brhz %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); @@ -148,6 +654,18 @@ private: { Write(wxString::Format("stqr %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); } + virtual void BRA(OP_sIMM i16) + { + Write(wxString::Format("bra 0x%x", DisAsmBranchTarget(i16))); + } + virtual void LQA(OP_REG rt, OP_sIMM i16) + { + Write(wxString::Format("lqa %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + } + virtual void BRASL(OP_REG rt, OP_sIMM i16) + { + Write(wxString::Format("brasl %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + } virtual void BR(OP_sIMM i16) { Write(wxString::Format("br 0x%x", DisAsmBranchTarget(i16))); @@ -160,20 +678,61 @@ private: { Write(wxString::Format("brsl %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); } - virtual void IL(OP_REG rt, OP_sIMM i16) - { - Write(wxString::Format("il %s,%d", spu_reg_name[rt], i16)); - } virtual void LQR(OP_REG rt, OP_sIMM i16) { Write(wxString::Format("lqr %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); } + virtual void IL(OP_REG rt, OP_sIMM i16) + { + Write(wxString::Format("il %s,%d", spu_reg_name[rt], i16)); + } + virtual void ILHU(OP_REG rt, OP_sIMM i16) + { + Write(wxString::Format("ilhu %s,%d", spu_reg_name[rt], i16)); + } + virtual void ILH(OP_REG rt, OP_sIMM i16) + { + Write(wxString::Format("ilh %s,%d", spu_reg_name[rt], i16)); + } + virtual void IOHL(OP_REG rt, OP_sIMM i16) + { + Write(wxString::Format("iolh %s,%d", spu_reg_name[rt], i16)); + } + //0 - 7 - virtual void SPU_ORI(OP_REG rt, OP_REG ra, OP_sIMM i10) + virtual void ORI(OP_REG rt, OP_REG ra, OP_sIMM i10) { Write(wxString::Format("ori %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); } + virtual void ORHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("orhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void ORBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("orbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void SFI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("sfi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void SFHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("sfhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void ANDI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("andi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void ANDHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("andhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void ANDBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("andbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } virtual void AI(OP_REG rt, OP_REG ra, OP_sIMM i10) { Write(wxString::Format("ai %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); @@ -190,6 +749,34 @@ private: { Write(wxString::Format("lqd %s,%d(%s)", spu_reg_name[rt], i10, spu_reg_name[ra])); } + virtual void XORI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("xori %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void XORHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("xorhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void XORBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("xorbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void CGTI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("cgti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void CGTHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("cgthi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void CGTBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("cgtbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void HGTI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("hgti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } virtual void CLGTI(OP_REG rt, OP_REG ra, OP_sIMM i10) { Write(wxString::Format("clgti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); @@ -198,12 +785,44 @@ private: { Write(wxString::Format("clgthi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); } + virtual void CLGTBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("clgtbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void HLGTI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("hlgti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void MPYI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("mpyi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void MPYUI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("mpyui %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } virtual void CEQI(OP_REG rt, OP_REG ra, OP_sIMM i10) { Write(wxString::Format("ceqi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); } + virtual void CEQHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("ceqhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void CEQBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("ceqbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } + virtual void HEQI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + Write(wxString::Format("heqi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + } //0 - 6 + virtual void HBRA(OP_sIMM ro, OP_sIMM i16) + { + Write(wxString::Format("hbra 0x%x,0x%x", DisAsmBranchTarget(ro), DisAsmBranchTarget(i16))); + } virtual void HBRR(OP_sIMM ro, OP_sIMM i16) { Write(wxString::Format("hbrr 0x%x,0x%x", DisAsmBranchTarget(ro), DisAsmBranchTarget(i16))); @@ -222,6 +841,22 @@ private: { Write(wxString::Format("shufb %s,%s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt])); } + virtual void MPYA(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt) + { + Write(wxString::Format("mpya %s,%s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt])); + } + virtual void FNMS(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt) + { + Write(wxString::Format("fnms %s,%s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt])); + } + virtual void FMA(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt) + { + Write(wxString::Format("fma %s,%s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt])); + } + virtual void FMS(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt) + { + Write(wxString::Format("fms %s,%s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt])); + } virtual void UNK(const s32 code, const s32 opcode, const s32 gcode) { diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index 8061849ff9..9098e80106 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -33,6 +33,18 @@ private: virtual void LNOP() { } + virtual void SYNC(OP_uIMM Cbit) + { + // TODO + } + virtual void DSYNC() + { + // TODO + } + virtual void MFSPR(OP_REG rt, OP_REG sa) + { + // TODO + } virtual void RDCH(OP_REG rt, OP_REG ra) { CPU.ReadChannel(CPU.GPR[rt], ra); @@ -49,6 +61,109 @@ private: CPU.GPR[rt]._u32[2] = CPU.GPR[rb]._u32[2] + ~CPU.GPR[ra]._u32[2] + 1; CPU.GPR[rt]._u32[3] = CPU.GPR[rb]._u32[3] + ~CPU.GPR[ra]._u32[3] + 1; } + virtual void OR(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] | CPU.GPR[rb]._u32[0]; + CPU.GPR[rt]._u32[1] = CPU.GPR[ra]._u32[1] | CPU.GPR[rb]._u32[1]; + CPU.GPR[rt]._u32[2] = CPU.GPR[ra]._u32[2] | CPU.GPR[rb]._u32[2]; + CPU.GPR[rt]._u32[3] = CPU.GPR[ra]._u32[3] | CPU.GPR[rb]._u32[3]; + } + virtual void BG(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] > CPU.GPR[rb]._u32[0] ? 0 : 1; + CPU.GPR[rt]._u32[1] = CPU.GPR[ra]._u32[1] > CPU.GPR[rb]._u32[1] ? 0 : 1; + CPU.GPR[rt]._u32[2] = CPU.GPR[ra]._u32[2] > CPU.GPR[rb]._u32[2] ? 0 : 1; + CPU.GPR[rt]._u32[3] = CPU.GPR[ra]._u32[3] > CPU.GPR[rb]._u32[3] ? 0 : 1; + } + virtual void SFH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[rb]._u16[h] - CPU.GPR[ra]._u16[h]; + } + virtual void NOR(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.GPR[rt]._u32[0] = ~(CPU.GPR[ra]._u32[0] | CPU.GPR[rb]._u32[0]); + CPU.GPR[rt]._u32[1] = ~(CPU.GPR[ra]._u32[1] | CPU.GPR[rb]._u32[1]); + CPU.GPR[rt]._u32[2] = ~(CPU.GPR[ra]._u32[2] | CPU.GPR[rb]._u32[2]); + CPU.GPR[rt]._u32[3] = ~(CPU.GPR[ra]._u32[3] | CPU.GPR[rb]._u32[3]); + } + virtual void ABSDB(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[rb]._u8[b] > CPU.GPR[ra]._u8[b] ? CPU.GPR[rb]._u8[b] - CPU.GPR[ra]._u8[b] : CPU.GPR[ra]._u8[b] - CPU.GPR[rb]._u8[b]; + } + virtual void ROT(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.GPR[rt]._u32[0] = (CPU.GPR[ra]._u32[0] << (CPU.GPR[rb]._u32[0] & 0x1f)) | (CPU.GPR[ra]._u32[0] >> (32 - (CPU.GPR[rb]._u32[0] & 0x1f))); + CPU.GPR[rt]._u32[1] = (CPU.GPR[ra]._u32[1] << (CPU.GPR[rb]._u32[1] & 0x1f)) | (CPU.GPR[ra]._u32[1] >> (32 - (CPU.GPR[rb]._u32[1] & 0x1f))); + CPU.GPR[rt]._u32[2] = (CPU.GPR[ra]._u32[2] << (CPU.GPR[rb]._u32[2] & 0x1f)) | (CPU.GPR[ra]._u32[2] >> (32 - (CPU.GPR[rb]._u32[2] & 0x1f))); + CPU.GPR[rt]._u32[3] = (CPU.GPR[ra]._u32[3] << (CPU.GPR[rb]._u32[3] & 0x1f)) | (CPU.GPR[ra]._u32[3] >> (32 - (CPU.GPR[rb]._u32[3] & 0x1f))); + } + virtual void ROTM(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.GPR[rt]._u32[0] = ((0 - CPU.GPR[rb]._u32[0]) % 64) < 32 ? CPU.GPR[ra]._u32[0] >> ((0 - CPU.GPR[rb]._u32[0]) % 64) : 0; + CPU.GPR[rt]._u32[1] = ((0 - CPU.GPR[rb]._u32[1]) % 64) < 32 ? CPU.GPR[ra]._u32[1] >> ((0 - CPU.GPR[rb]._u32[1]) % 64) : 0; + CPU.GPR[rt]._u32[2] = ((0 - CPU.GPR[rb]._u32[2]) % 64) < 32 ? CPU.GPR[ra]._u32[2] >> ((0 - CPU.GPR[rb]._u32[2]) % 64) : 0; + CPU.GPR[rt]._u32[3] = ((0 - CPU.GPR[rb]._u32[3]) % 64) < 32 ? CPU.GPR[ra]._u32[3] >> ((0 - CPU.GPR[rb]._u32[3]) % 64) : 0; + } + virtual void ROTMA(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.GPR[rt]._i32[0] = ((0 - CPU.GPR[rb]._i32[0]) % 64) < 32 ? CPU.GPR[ra]._i32[0] >> ((0 - CPU.GPR[rb]._i32[0]) % 64) : CPU.GPR[ra]._i32[0] >> 31; + CPU.GPR[rt]._i32[1] = ((0 - CPU.GPR[rb]._i32[1]) % 64) < 32 ? CPU.GPR[ra]._i32[1] >> ((0 - CPU.GPR[rb]._i32[1]) % 64) : CPU.GPR[ra]._i32[1] >> 31; + CPU.GPR[rt]._i32[2] = ((0 - CPU.GPR[rb]._i32[2]) % 64) < 32 ? CPU.GPR[ra]._i32[2] >> ((0 - CPU.GPR[rb]._i32[2]) % 64) : CPU.GPR[ra]._i32[2] >> 31; + CPU.GPR[rt]._i32[3] = ((0 - CPU.GPR[rb]._i32[3]) % 64) < 32 ? CPU.GPR[ra]._i32[3] >> ((0 - CPU.GPR[rb]._i32[3]) % 64) : CPU.GPR[ra]._i32[3] >> 31; + } + virtual void SHL(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.GPR[rt]._u32[0] = (CPU.GPR[rb]._u32[0] & 0x3f) > 31 ? 0 : CPU.GPR[ra]._u32[0] << (CPU.GPR[rb]._u32[0] & 0x3f); + CPU.GPR[rt]._u32[1] = (CPU.GPR[rb]._u32[1] & 0x3f) > 31 ? 0 : CPU.GPR[ra]._u32[1] << (CPU.GPR[rb]._u32[1] & 0x3f); + CPU.GPR[rt]._u32[2] = (CPU.GPR[rb]._u32[2] & 0x3f) > 31 ? 0 : CPU.GPR[ra]._u32[2] << (CPU.GPR[rb]._u32[2] & 0x3f); + CPU.GPR[rt]._u32[3] = (CPU.GPR[rb]._u32[3] & 0x3f) > 31 ? 0 : CPU.GPR[ra]._u32[3] << (CPU.GPR[rb]._u32[3] & 0x3f); + } + virtual void ROTH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = (CPU.GPR[ra]._u16[h] << (CPU.GPR[rb]._u16[h] & 0xf)) | (CPU.GPR[ra]._u16[h] >> (16 - (CPU.GPR[rb]._u32[h] & 0xf))); + } + virtual void ROTHM(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = ((0 - CPU.GPR[rb]._u16[h]) % 32) < 16 ? CPU.GPR[ra]._u16[h] >> ((0 - CPU.GPR[rb]._u16[h]) % 32) : 0; + } + virtual void ROTMAH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._i16[h] = ((0 - CPU.GPR[rb]._i16[h]) % 32) < 16 ? CPU.GPR[ra]._i16[h] >> ((0 - CPU.GPR[rb]._i16[h]) % 32) : CPU.GPR[ra]._i16[h] >> 15; + } + virtual void SHLH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = (CPU.GPR[rb]._u16[h] & 0x1f) > 15 ? 0 : CPU.GPR[ra]._u16[h] << (CPU.GPR[rb]._u16[h] & 0x3f); + } + virtual void ROTI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nRot = i7 & 0x1f; + CPU.GPR[rt]._u32[0] = (CPU.GPR[ra]._u32[0] << nRot) | (CPU.GPR[ra]._u32[0] >> (32 - nRot)); + CPU.GPR[rt]._u32[1] = (CPU.GPR[ra]._u32[1] << nRot) | (CPU.GPR[ra]._u32[1] >> (32 - nRot)); + CPU.GPR[rt]._u32[2] = (CPU.GPR[ra]._u32[2] << nRot) | (CPU.GPR[ra]._u32[2] >> (32 - nRot)); + CPU.GPR[rt]._u32[3] = (CPU.GPR[ra]._u32[3] << nRot) | (CPU.GPR[ra]._u32[3] >> (32 - nRot)); + } + virtual void ROTMI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nRot = (0 - i7) % 64; + CPU.GPR[rt]._u32[0] = nRot < 32 ? CPU.GPR[ra]._u32[0] >> nRot : 0; + CPU.GPR[rt]._u32[1] = nRot < 32 ? CPU.GPR[ra]._u32[1] >> nRot : 0; + CPU.GPR[rt]._u32[2] = nRot < 32 ? CPU.GPR[ra]._u32[2] >> nRot : 0; + CPU.GPR[rt]._u32[3] = nRot < 32 ? CPU.GPR[ra]._u32[3] >> nRot : 0; + } + virtual void ROTMAI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nRot = (0 - i7) % 64; + CPU.GPR[rt]._i32[0] = nRot < 32 ? CPU.GPR[ra]._i32[0] >> nRot : CPU.GPR[ra]._i32[0] >> 31; + CPU.GPR[rt]._i32[1] = nRot < 32 ? CPU.GPR[ra]._i32[1] >> nRot : CPU.GPR[ra]._i32[1] >> 31; + CPU.GPR[rt]._i32[2] = nRot < 32 ? CPU.GPR[ra]._i32[2] >> nRot : CPU.GPR[ra]._i32[2] >> 31; + CPU.GPR[rt]._i32[3] = nRot < 32 ? CPU.GPR[ra]._i32[3] >> nRot : CPU.GPR[ra]._i32[3] >> 31; + } virtual void SHLI(OP_REG rt, OP_REG ra, OP_sIMM i7) { const u32 s = i7 & 0x3f; @@ -66,6 +181,34 @@ private: CPU.GPR[rt]._u32[j] = r; } } + virtual void ROTHI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nRot = i7 & 0xf; + + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = (CPU.GPR[ra]._u16[h] << nRot) | (CPU.GPR[ra]._u16[h] >> (16 - nRot)); + } + virtual void ROTHMI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nRot = (0 - i7) % 32; + + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = nRot < 16 ? CPU.GPR[ra]._u16[h] >> nRot : 0; + } + virtual void ROTMAHI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nRot = (0 - i7) % 32; + + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._i16[h] = nRot < 16 ? CPU.GPR[ra]._i16[h] >> nRot : CPU.GPR[ra]._i16[h] >> 15; + } + virtual void SHLHI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nRot = i7 & 0x1f; + + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[0] = nRot > 15 ? 0 : CPU.GPR[ra]._u16[0] << nRot; + } virtual void A(OP_REG rt, OP_REG ra, OP_REG rb) { CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]; @@ -73,22 +216,69 @@ private: CPU.GPR[rt]._u32[2] = CPU.GPR[ra]._u32[2] + CPU.GPR[rb]._u32[2]; CPU.GPR[rt]._u32[3] = CPU.GPR[ra]._u32[3] + CPU.GPR[rb]._u32[3]; } - virtual void SPU_AND(OP_REG rt, OP_REG ra, OP_REG rb) + virtual void AND(OP_REG rt, OP_REG ra, OP_REG rb) { CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] & CPU.GPR[rb]._u32[0]; CPU.GPR[rt]._u32[1] = CPU.GPR[ra]._u32[1] & CPU.GPR[rb]._u32[1]; CPU.GPR[rt]._u32[2] = CPU.GPR[ra]._u32[2] & CPU.GPR[rb]._u32[2]; CPU.GPR[rt]._u32[3] = CPU.GPR[ra]._u32[3] & CPU.GPR[rb]._u32[3]; } - virtual void LQX(OP_REG rt, OP_REG ra, OP_REG rb) + virtual void CG(OP_REG rt, OP_REG ra, OP_REG rb) { - CPU.LSA = CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]; - CPU.GPR[rt]._u128 = CPU.ReadLSA128(); + CPU.GPR[rt]._u32[0] = ((CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]) < CPU.GPR[ra]._u32[0]) ? 1 : 0; + CPU.GPR[rt]._u32[1] = ((CPU.GPR[ra]._u32[1] + CPU.GPR[rb]._u32[1]) < CPU.GPR[ra]._u32[1]) ? 1 : 0; + CPU.GPR[rt]._u32[2] = ((CPU.GPR[ra]._u32[2] + CPU.GPR[rb]._u32[2]) < CPU.GPR[ra]._u32[2]) ? 1 : 0; + CPU.GPR[rt]._u32[3] = ((CPU.GPR[ra]._u32[3] + CPU.GPR[rb]._u32[3]) < CPU.GPR[ra]._u32[3]) ? 1 : 0; + } + virtual void AH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._u16[h] + CPU.GPR[rb]._u16[h]; + } + virtual void NAND(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.GPR[rt]._u32[0] = ~(CPU.GPR[ra]._u32[0] & CPU.GPR[rb]._u32[0]); + CPU.GPR[rt]._u32[1] = ~(CPU.GPR[ra]._u32[1] & CPU.GPR[rb]._u32[1]); + CPU.GPR[rt]._u32[2] = ~(CPU.GPR[ra]._u32[2] & CPU.GPR[rb]._u32[2]); + CPU.GPR[rt]._u32[3] = ~(CPU.GPR[ra]._u32[3] & CPU.GPR[rb]._u32[3]); + } + virtual void AVGB(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = (CPU.GPR[ra]._u8[b] + CPU.GPR[rb]._u8[b] + 1) >> 1; + } + virtual void MTSPR(OP_REG rt, OP_REG sa) + { + // TODO } virtual void WRCH(OP_REG ra, OP_REG rt) { CPU.WriteChannel(ra, CPU.GPR[rt]); } + virtual void BIZ(OP_REG rt, OP_REG ra) + { + if(CPU.GPR[rt]._u32[0] == 0) + CPU.SetBranch(CPU.GPR[ra]._u32[0] & 0xfffffffc); + } + virtual void BINZ(OP_REG rt, OP_REG ra) + { + if(CPU.GPR[rt]._u32[0] != 0) + CPU.SetBranch(CPU.GPR[ra]._u32[0] & 0xfffffffc); + } + virtual void BIHZ(OP_REG rt, OP_REG ra) + { + if(CPU.GPR[rt]._u16[0] == 0) + CPU.SetBranch(CPU.GPR[ra]._u32[0] & 0xfffffffc); + } + virtual void BIHNZ(OP_REG rt, OP_REG ra) + { + if(CPU.GPR[rt]._u16[0] != 0) + CPU.SetBranch(CPU.GPR[ra]._u32[0] & 0xfffffffc); + } + virtual void STOPD(OP_REG rc, OP_REG ra, OP_REG rb) + { + Emu.Pause(); + } virtual void STQX(OP_REG rt, OP_REG ra, OP_REG rb) { CPU.LSA = CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]; @@ -104,16 +294,154 @@ private: CPU.GPR[rt].Reset(); CPU.GPR[rt]._u32[0] = CPU.PC + 4; } + virtual void IRET(OP_REG ra) + { + // TODO + // SetBranch(SRR0); + } + virtual void BISLED(OP_REG rt, OP_REG ra) + { + // TODO + } virtual void HBR(OP_REG p, OP_REG ro, OP_REG ra) { CPU.SetBranch(CPU.GPR[ra]._u32[0]); } + virtual void GB(OP_REG rt, OP_REG ra) + { + CPU.GPR[rt].Reset(); + CPU.GPR[rt]._u32[0] = (CPU.GPR[ra]._u32[0] & 1) | + ((CPU.GPR[ra]._u32[1] & 1) << 1) | + ((CPU.GPR[ra]._u32[2] & 1) << 2) | + ((CPU.GPR[ra]._u32[3] & 1) << 3); + } + virtual void GBH(OP_REG rt, OP_REG ra) + { + CPU.GPR[rt].Reset(); + + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u32[0] |= (CPU.GPR[ra]._u16[h] & 1) << h; + } + virtual void GBB(OP_REG rt, OP_REG ra) + { + CPU.GPR[rt].Reset(); + + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u32[0] |= (CPU.GPR[ra]._u8[b] & 1) << b; + } + virtual void FSM(OP_REG rt, OP_REG ra) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = (CPU.GPR[ra]._u32[0] & (8 >> w)) ? ~0 : 0; + } + virtual void FSMH(OP_REG rt, OP_REG ra) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = (CPU.GPR[ra]._u32[0] & (128 >> h)) ? ~0 : 0; + } + virtual void FSMB(OP_REG rt, OP_REG ra) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = (CPU.GPR[ra]._u32[0] & (32768 >> b)) ? ~0 : 0; + } + virtual void FREST(OP_REG rt, OP_REG ra) + { + // TODO + } + virtual void FRSQEST(OP_REG rt, OP_REG ra) + { + // TODO + } + virtual void LQX(OP_REG rt, OP_REG ra, OP_REG rb) + { + CPU.LSA = CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]; + CPU.GPR[rt]._u128 = CPU.ReadLSA128(); + } + virtual void ROTQBYBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + const int nShift = (CPU.GPR[rb]._u32[0] >> 3) & 0xf; + + for (int b = 0; b < 8; b++) + CPU.GPR[rt]._u8[b] = nShift == 0 ? CPU.GPR[ra]._u8[b] : (CPU.GPR[ra]._u8[b] << nShift) | (CPU.GPR[ra]._u8[b] >> (16 - nShift)); + } + virtual void ROTQMBYBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + const int nShift = ((0 - CPU.GPR[rb]._u32[0]) >> 3) & 0x1f; + + for (int b = 0; b < 16; b++) + { + if (b >= nShift) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b - nShift]; + else + CPU.GPR[rt]._u8[b] = 0; + } + } + virtual void SHLQBYBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + const int nShift = (CPU.GPR[rb]._u32[0] >> 3) & 0x1f; + + for (int b = 0; b < 16; b++) + { + if ((b + nShift) < 16) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b + nShift]; + else + CPU.GPR[rt]._u8[b] = 0; + } + } + virtual void CBX(OP_REG rt, OP_REG ra, OP_REG rb) + { + int n = (CPU.GPR[rb]._u32[0] + CPU.GPR[ra]._u32[0]) & 0xf; + + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = b == n ? 3 : b | 0x10; + } + virtual void CHX(OP_REG rt, OP_REG ra, OP_REG rb) + { + int n = ((CPU.GPR[rb]._u32[0] + CPU.GPR[ra]._u32[0]) & 0xf) >> 1; + + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = h == n ? 0x0203 : (h * 2 * 0x0101 + 0x1011); + } virtual void CWX(OP_REG rt, OP_REG ra, OP_REG rb) { const u32 t = ((CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]) & 0xc) / 4; - for(u32 i=0; i<16; ++i) CPU.GPR[rt]._i8[i] = 0x1f - i; + for(u32 i=0; i<16; ++i) CPU.GPR[rt]._i8[i] = 0x10 + i; CPU.GPR[rt]._u32[t] = 0x10203; } + virtual void CDX(OP_REG rt, OP_REG ra, OP_REG rb) + { + int n = ((CPU.GPR[rb]._u32[0] + CPU.GPR[ra]._u32[0]) & 0x8) >> 2; + + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = (w == n) ? 0x00010203 : (w == (n + 1)) ? 0x04050607 : (0x01010101 * (w * 4) + 0x10111213); + } + virtual void ROTQBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + int nShift = CPU.GPR[rb]._u32[0] & 0x7; + + CPU.GPR[rt]._u32[0] = (CPU.GPR[ra]._u32[0] << nShift) | (CPU.GPR[ra]._u32[1] >> (32 - nShift)); + CPU.GPR[rt]._u32[1] = (CPU.GPR[ra]._u32[1] << nShift) | (CPU.GPR[ra]._u32[2] >> (32 - nShift)); + CPU.GPR[rt]._u32[2] = (CPU.GPR[ra]._u32[2] << nShift) | (CPU.GPR[ra]._u32[3] >> (32 - nShift)); + CPU.GPR[rt]._u32[3] = (CPU.GPR[ra]._u32[3] << nShift) | (CPU.GPR[ra]._u32[0] >> (32 - nShift)); + } + virtual void ROTQMBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + int nShift = (0 - CPU.GPR[rb]._u32[0]) % 8; + + CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] >> nShift; + CPU.GPR[rt]._u32[1] = (CPU.GPR[ra]._u32[1] >> nShift) | (CPU.GPR[ra]._u32[0] << (32 - nShift)); + CPU.GPR[rt]._u32[2] = (CPU.GPR[ra]._u32[2] >> nShift) | (CPU.GPR[ra]._u32[1] << (32 - nShift)); + CPU.GPR[rt]._u32[3] = (CPU.GPR[ra]._u32[3] >> nShift) | (CPU.GPR[ra]._u32[2] << (32 - nShift)); + } + virtual void SHLQBI(OP_REG rt, OP_REG ra, OP_REG rb) + { + const int nShift = CPU.GPR[rb]._u32[0] & 0x7; + + CPU.GPR[rt]._u32[0] = (CPU.GPR[ra]._u32[0] << nShift) | (CPU.GPR[ra]._u32[1] >> (32 - nShift)); + CPU.GPR[rt]._u32[1] = (CPU.GPR[ra]._u32[1] << nShift) | (CPU.GPR[ra]._u32[2] >> (32 - nShift)); + CPU.GPR[rt]._u32[2] = (CPU.GPR[ra]._u32[2] << nShift) | (CPU.GPR[ra]._u32[3] >> (32 - nShift)); + CPU.GPR[rt]._u32[3] = CPU.GPR[ra]._u32[3] << nShift; + } virtual void ROTQBY(OP_REG rt, OP_REG ra, OP_REG rb) { const s32 s = CPU.GPR[rb]._u8[0] & 0xf; @@ -130,6 +458,94 @@ private: } } } + virtual void ROTQMBY(OP_REG rt, OP_REG ra, OP_REG rb) + { + const int nShift = (0 - CPU.GPR[rb]._u32[0]) % 32; + + for (int b = 0; b < 16; b++) + if (b >= nShift) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b - nShift]; + else + CPU.GPR[rt]._u8[b] = 0; + } + virtual void SHLQBY(OP_REG rt, OP_REG ra, OP_REG rb) + { + const int nShift = CPU.GPR[rb]._u32[0] & 0x1f; + + for (int b = 0; b < 16; b++) + if (b + nShift < 16) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b + nShift]; + else + CPU.GPR[rt]._u8[b] = 0; + } + virtual void ORX(OP_REG rt, OP_REG ra) + { + CPU.GPR[rt].Reset(); + + CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] | CPU.GPR[ra]._u32[1] | CPU.GPR[ra]._u32[2] | CPU.GPR[ra]._u32[3]; + } + virtual void CBD(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int n = (CPU.GPR[ra]._u32[0] + i7) & 0xf; + + for (int b = 0; b < 16; b++) + if (b == n) + CPU.GPR[rt]._u8[b] = 0x3; + else + CPU.GPR[rt]._u8[b] = b | 0x10; + } + virtual void CHD(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + int n = ((CPU.GPR[ra]._u32[0] + i7) & 0xf) >> 1; + + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = h == n ? 0x0203 : (h * 2 * 0x0101 + 0x1011); + } + virtual void CWD(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int t = ((CPU.GPR[ra]._u32[0] + i7) & 0xf) >> 2; + + for (int i=0; i<16; ++i) + CPU.GPR[rt]._u8[i] = 0x10 + i; + + CPU.GPR[rt]._u32[t] = 0x10203; + } + virtual void CDD(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int t = ((CPU.GPR[ra]._u32[0] + i7) & 0xf) >> 3; + + for (int i=0; i<16; ++i) + CPU.GPR[rt]._u8[i] = 0x10 + i; + + CPU.GPR[rt]._u64[t] = (u64)0x0001020304050607; + } + virtual void ROTQBII(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + int nShift = i7 & 0x7; + + CPU.GPR[rt]._u32[0] = (CPU.GPR[ra]._u32[0] << nShift) | (CPU.GPR[ra]._u32[1] >> (32 - nShift)); + CPU.GPR[rt]._u32[1] = (CPU.GPR[ra]._u32[1] << nShift) | (CPU.GPR[ra]._u32[2] >> (32 - nShift)); + CPU.GPR[rt]._u32[2] = (CPU.GPR[ra]._u32[2] << nShift) | (CPU.GPR[ra]._u32[3] >> (32 - nShift)); + CPU.GPR[rt]._u32[3] = (CPU.GPR[ra]._u32[3] << nShift) | (CPU.GPR[ra]._u32[0] >> (32 - nShift)); + } + virtual void ROTQMBII(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + int nShift = (0 - i7) % 8; + + CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] >> nShift; + CPU.GPR[rt]._u32[1] = (CPU.GPR[ra]._u32[1] >> nShift) | (CPU.GPR[ra]._u32[0] << (32 - nShift)); + CPU.GPR[rt]._u32[2] = (CPU.GPR[ra]._u32[2] >> nShift) | (CPU.GPR[ra]._u32[1] << (32 - nShift)); + CPU.GPR[rt]._u32[3] = (CPU.GPR[ra]._u32[3] >> nShift) | (CPU.GPR[ra]._u32[2] << (32 - nShift)); + } + virtual void SHLQBII(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nShift = i7 & 0x7; + + CPU.GPR[rt]._u32[0] = (CPU.GPR[ra]._u32[0] << nShift) | (CPU.GPR[ra]._u32[1] >> (32 - nShift)); + CPU.GPR[rt]._u32[1] = (CPU.GPR[ra]._u32[1] << nShift) | (CPU.GPR[ra]._u32[2] >> (32 - nShift)); + CPU.GPR[rt]._u32[2] = (CPU.GPR[ra]._u32[2] << nShift) | (CPU.GPR[ra]._u32[3] >> (32 - nShift)); + CPU.GPR[rt]._u32[3] = CPU.GPR[ra]._u32[3] << nShift; + } virtual void ROTQBYI(OP_REG rt, OP_REG ra, OP_sIMM i7) { const u16 s = i7 & 0xf; @@ -146,6 +562,16 @@ private: } } } + virtual void ROTQMBYI(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + const int nShift = (0 - i7) % 32; + + for (int b = 0; b < 16; b++) + if (b >= nShift) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b - nShift]; + else + CPU.GPR[rt]._u8[b] = 0; + } virtual void SHLQBYI(OP_REG rt, OP_REG ra, OP_sIMM i7) { const u16 s = i7 & 0x1f; @@ -157,9 +583,82 @@ private: CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b + s]; } } - virtual void SPU_NOP(OP_REG rt) + virtual void NOP(OP_REG rt) { } + virtual void CGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._i32[w] > CPU.GPR[rb]._i32[w] ? 0xffffffff : 0; + } + virtual void XOR(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._u32[w] ^ CPU.GPR[rb]._u32[w]; + } + virtual void CGTH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._i16[h] > CPU.GPR[rb]._i16[h] ? 0xffff : 0; + } + virtual void EQV(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = (CPU.GPR[ra]._u32[w] & CPU.GPR[rb]._u32[w]) | ~(CPU.GPR[ra]._u32[w] | CPU.GPR[rb]._u32[w]); + } + virtual void CGTB(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._i8[b] > CPU.GPR[rb]._i8[b] ? 0xff : 0; + } + virtual void SUMB(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + { + CPU.GPR[rt]._u16[w*2] = CPU.GPR[ra]._u8[w*4] + CPU.GPR[ra]._u8[w*4 + 1] + CPU.GPR[ra]._u8[w*4 + 2] + CPU.GPR[ra]._u8[w*4 + 3]; + CPU.GPR[rt]._u16[w*2 + 1] = CPU.GPR[rb]._u8[w*4] + CPU.GPR[rb]._u8[w*4 + 1] + CPU.GPR[rb]._u8[w*4 + 2] + CPU.GPR[rb]._u8[w*4 + 3]; + } + } + virtual void HGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void CLZ(OP_REG rt, OP_REG ra) + { + for (int w = 0; w < 4; w++) + { + int nPos; + + for (nPos = 0; nPos < 32; nPos++) + if (CPU.GPR[ra]._u32[w] & (1 << (31 - nPos))) + break; + + CPU.GPR[rt]._u32[w] = nPos; + } + } + virtual void XSWD(OP_REG rt, OP_REG ra) + { + CPU.GPR[rt]._i64[0] = (s64)CPU.GPR[ra]._i32[1]; + CPU.GPR[rt]._i64[1] = (s64)CPU.GPR[ra]._i32[3]; + } + virtual void XSHW(OP_REG rt, OP_REG ra) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] = (s32)CPU.GPR[ra]._i16[w*2 + 1]; + } + virtual void CNTB(OP_REG rt, OP_REG ra) + { + CPU.GPR[rt].Reset(); + + for (int b = 0; b < 16; b++) + for (int i = 0; i < 8; i++) + CPU.GPR[rt]._u8[b] += (CPU.GPR[ra]._u8[b] & (1 << i)) ? 1 : 0; + } + virtual void XSBH(OP_REG rt, OP_REG ra) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._i16[h] = (s16)CPU.GPR[ra]._i8[h*2 + 1]; + } virtual void CLGT(OP_REG rt, OP_REG ra, OP_REG rb) { for(u32 i = 0; i < 4; ++i) @@ -167,12 +666,248 @@ private: CPU.GPR[rt]._u32[i] = (CPU.GPR[ra]._u32[i] > CPU.GPR[rb]._u32[i]) ? 0xffffffff : 0x00000000; } } + virtual void ANDC(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._u32[w] & (~CPU.GPR[rb]._u32[w]); + } + virtual void FCGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFCGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void FA(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void FS(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void FM(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void CLGTH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._u16[h] > CPU.GPR[rb]._u16[h] ? 0xffff : 0; + } + virtual void ORC(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._u32[w] | (~CPU.GPR[rb]._u32[w]); + } + virtual void FCMGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFCMGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFA(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFS(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFM(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void CLGTB(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b] > CPU.GPR[rb]._u8[b] ? 0xff : 0; + } + virtual void HLGT(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFMA(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFMS(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFNMS(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFNMA(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void CEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._i32[w] == CPU.GPR[rb]._i32[w] ? 0xffffffff : 0; + } + virtual void MPYHHU(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._u16[w*2] * CPU.GPR[rb]._u16[w*2]; + } + virtual void ADDX(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._u32[w] + CPU.GPR[rb]._u32[w] + (CPU.GPR[rt]._u32[w] & 1); + } + virtual void SFX(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[rb]._u32[w] - CPU.GPR[ra]._u32[w] - (1 - (CPU.GPR[rt]._u32[w] & 1)); + } + virtual void CGX(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = (CPU.GPR[ra]._u32[w] + CPU.GPR[rb]._u32[w] + (CPU.GPR[rt]._u32[w] & 1)) < CPU.GPR[ra]._u32[w] ? 1 : 0; + } + virtual void BGX(OP_REG rt, OP_REG ra, OP_REG rb) + { + s64 nResult; + + for (int w = 0; w < 4; w++) + { + nResult = (u64)CPU.GPR[rb]._u32[w] - (u64)CPU.GPR[ra]._u32[w] - (1 - (CPU.GPR[rt]._u32[w] & 1)); + CPU.GPR[rt]._u32[w] = nResult < 0 ? 0 : 1; + } + } + virtual void MPYHHA(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] += CPU.GPR[ra]._i16[w*2] * CPU.GPR[rb]._i16[w*2]; + } + virtual void MPYHHAU(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] += CPU.GPR[ra]._u16[w*2] * CPU.GPR[rb]._u16[w*2]; + } + virtual void FSCRRD(OP_REG rt) + { + // TODO + } + virtual void FESD(OP_REG rt, OP_REG ra) + { + // TODO + } + virtual void FRDS(OP_REG rt, OP_REG ra) + { + // TODO + } + virtual void FSCRWR(OP_REG rt, OP_REG ra) + { + // TODO + } + virtual void DFTSV(OP_REG rt, OP_REG ra, OP_sIMM i7) + { + // TODO + } + virtual void FCEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFCEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void MPY(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] = CPU.GPR[ra]._i16[w*2 + 1] * CPU.GPR[rb]._i16[w*2 + 1]; + } + virtual void MPYH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] = ((CPU.GPR[ra]._i32[w] >> 16) * (CPU.GPR[rb]._i32[w] & 0xffff)) << 16; + } + virtual void MPYHH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] = CPU.GPR[ra]._i16[w*2] * CPU.GPR[rb]._i16[w*2]; + } + virtual void MPYS(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] = (CPU.GPR[ra]._i16[w*2 + 1] * CPU.GPR[rb]._i16[w*2 + 1]) >> 16; + } + virtual void CEQH(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._u16[h] == CPU.GPR[rb]._u16[h] ? 0xffff : 0; + } + virtual void FCMEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void DFCMEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void MPYU(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._u16[w*2 + 1] * CPU.GPR[rb]._u16[w*2 + 1]; + } + virtual void CEQB(OP_REG rt, OP_REG ra, OP_REG rb) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b] == CPU.GPR[rb]._u8[b] ? 0xff : 0; + } + virtual void FI(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + virtual void HEQ(OP_REG rt, OP_REG ra, OP_REG rb) + { + // TODO + } + + + + //0 - 9 + virtual void CFLTS(OP_REG rt, OP_REG ra, OP_sIMM i8) + { + // TODO + } + virtual void CFLTU(OP_REG rt, OP_REG ra, OP_sIMM i8) + { + // TODO + } + virtual void CSFLT(OP_REG rt, OP_REG ra, OP_sIMM i8) + { + // TODO + } + virtual void CUFLT(OP_REG rt, OP_REG ra, OP_sIMM i8) + { + // TODO + } + + //0 - 8 virtual void BRZ(OP_REG rt, OP_sIMM i16) { if(!CPU.GPR[rt]._u32[0]) CPU.SetBranch(branchTarget(CPU.PC, i16)); } + virtual void STQA(OP_REG rt, OP_sIMM i16) + { + CPU.LSA = i16 << 2; + CPU.WriteLSA128(CPU.GPR[rt]._u128); + } + virtual void BRNZ(OP_REG rt, OP_sIMM i16) + { + if(CPU.GPR[rt]._u32[0] != 0) + CPU.SetBranch(branchTarget(CPU.PC, i16)); + } virtual void BRHZ(OP_REG rt, OP_sIMM i16) { if(!CPU.GPR[rt]._u16[0]) CPU.SetBranch(branchTarget(CPU.PC, i16)); @@ -186,6 +921,27 @@ private: CPU.LSA = branchTarget(CPU.PC, i16); CPU.WriteLSA128(CPU.GPR[rt]._u128); } + virtual void BRA(OP_sIMM i16) + { + CPU.SetBranch(i16 << 2); + } + virtual void LQA(OP_REG rt, OP_sIMM i16) + { + CPU.LSA = i16 << 2; + if(!Memory.IsGoodAddr(CPU.LSA)) + { + ConLog.Warning("LQA: Bad addr: 0x%x", CPU.LSA); + return; + } + + CPU.GPR[rt]._u128 = CPU.ReadLSA128(); + } + virtual void BRASL(OP_REG rt, OP_sIMM i16) + { + CPU.GPR[rt].Reset(); + CPU.GPR[rt]._u32[0] = CPU.PC + 4; + CPU.SetBranch(i16 << 2); + } virtual void BR(OP_sIMM i16) { CPU.SetBranch(branchTarget(CPU.PC, i16)); @@ -212,13 +968,6 @@ private: CPU.GPR[rt]._u32[0] = CPU.PC + 4; CPU.SetBranch(branchTarget(CPU.PC, i16)); } - virtual void IL(OP_REG rt, OP_sIMM i16) - { - CPU.GPR[rt]._u32[0] = i16; - CPU.GPR[rt]._u32[1] = i16; - CPU.GPR[rt]._u32[2] = i16; - CPU.GPR[rt]._u32[3] = i16; - } virtual void LQR(OP_REG rt, OP_sIMM i16) { CPU.LSA = branchTarget(CPU.PC, i16); @@ -230,15 +979,73 @@ private: CPU.GPR[rt]._u128 = CPU.ReadLSA128(); } + virtual void IL(OP_REG rt, OP_sIMM i16) + { + CPU.GPR[rt]._u32[0] = i16; + CPU.GPR[rt]._u32[1] = i16; + CPU.GPR[rt]._u32[2] = i16; + CPU.GPR[rt]._u32[3] = i16; + } + virtual void ILHU(OP_REG rt, OP_sIMM i16) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u16[w*2] = i16; + } + virtual void ILH(OP_REG rt, OP_sIMM i16) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = i16; + } + virtual void IOHL(OP_REG rt, OP_sIMM i16) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] |= i16; + } + //0 - 7 - virtual void SPU_ORI(OP_REG rt, OP_REG ra, OP_sIMM i10) + virtual void ORI(OP_REG rt, OP_REG ra, OP_sIMM i10) { for(u32 i = 0; i < 4; ++i) { CPU.GPR[rt]._u32[i] = CPU.GPR[ra]._u32[i] | i10; } } + virtual void ORHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._u16[h] | i10; + } + virtual void ORBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b] | (i10 & 0xff); + } + virtual void SFI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] = i10 - CPU.GPR[ra]._i32[w]; + } + virtual void SFHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._i16[h] = i10 - CPU.GPR[ra]._i16[h]; + } + virtual void ANDI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._u32[w] & i10; + } + virtual void ANDHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._u16[h] & i10; + } + virtual void ANDBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b] & (i10 & 0xff); + } virtual void AI(OP_REG rt, OP_REG ra, OP_sIMM i10) { for(u32 i = 0; i < 4; ++i) @@ -263,6 +1070,40 @@ private: CPU.LSA = branchTarget(0, i10 + CPU.GPR[ra]._u32[0]); CPU.GPR[rt]._u128 = CPU.ReadLSA128(); } + virtual void XORI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._u32[w] ^ i10; + } + virtual void XORHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._u16[h] ^ i10; + } + virtual void XORBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b] ^ (i10 & 0xff); + } + virtual void CGTI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[ra]._i32[w] > i10 ? 0xffffffff : 0; + } + virtual void CGTHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._i16[h] > i10 ? 0xffff : 0; + } + virtual void CGTBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._i8[b] > (s8)(i10 & 0xff) ? 0xff : 0; + } + virtual void HGTI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + // TODO + } virtual void CLGTI(OP_REG rt, OP_REG ra, OP_sIMM i10) { for(u32 i = 0; i < 4; ++i) @@ -277,6 +1118,25 @@ private: CPU.GPR[rt]._u16[i] = (CPU.GPR[rt]._u16[i] > (u16)i10) ? 0xffff : 0x0000; } } + virtual void CLGTBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b] > (u8)(i10 & 0xff) ? 0xff : 0; + } + virtual void HLGTI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + // TODO + } + virtual void MPYI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] = CPU.GPR[rt]._i16[w*2 + 1] * i10; + } + virtual void MPYUI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._u32[w] = CPU.GPR[rt]._u16[w*2 + 1] * (u16)(i10 & 0xffff); + } virtual void CEQI(OP_REG rt, OP_REG ra, OP_sIMM i10) { for(u32 i = 0; i < 4; ++i) @@ -284,8 +1144,27 @@ private: CPU.GPR[rt]._u32[i] = (CPU.GPR[rt]._u32[i] == (u32)i10) ? 0xffffffff : 0x00000000; } } + virtual void CEQHI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[ra]._i16[h] == (s16)i10 ? 0xffff : 0; + } + virtual void CEQBI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[ra]._u8[b] == (u8)(i10 & 0xff) ? 0xff : 0; + } + virtual void HEQI(OP_REG rt, OP_REG ra, OP_sIMM i10) + { + // TODO + } + //0 - 6 + virtual void HBRA(OP_sIMM ro, OP_sIMM i16) + { + // TODO + } virtual void HBRR(OP_sIMM ro, OP_sIMM i16) { //CHECK ME @@ -320,6 +1199,23 @@ private: { ConLog.Warning("SHUFB"); } + virtual void MPYA(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt) + { + for (int w = 0; w < 4; w++) + CPU.GPR[rt]._i32[w] = CPU.GPR[ra]._i16[w*2 + 1] * CPU.GPR[rb]._i16[w*2 + 1] + CPU.GPR[rc]._i32[w]; + } + virtual void FNMS(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt) + { + // TODO + } + virtual void FMA(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt) + { + // TODO + } + virtual void FMS(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt) + { + // TODO + } virtual void UNK(const s32 code, const s32 opcode, const s32 gcode) { diff --git a/rpcs3/Emu/Cell/SPUOpcodes.h b/rpcs3/Emu/Cell/SPUOpcodes.h index c46098b965..4855c53f36 100644 --- a/rpcs3/Emu/Cell/SPUOpcodes.h +++ b/rpcs3/Emu/Cell/SPUOpcodes.h @@ -8,65 +8,230 @@ #define ADD_NULL_OPCODE(name) virtual void(##name##)()=0 #define END_OPCODES_GROUP(x) /*x*/ -enum SPU_0_10_Opcodes +namespace SPU_opcodes { - STOP = 0x0, - LNOP = 0x1, - RDCH = 0xd, - RCHCNT = 0xf, - SF = 0x40, - SHLI = 0x7b, - A = 0xc0, - SPU_AND = 0xc1, - LQX = 0x1c4, - WRCH = 0x10d, - STQX = 0x144, - BI = 0x1a8, - BISL = 0x1a9, - HBR = 0x1ac, - CWX = 0x1d6, - ROTQBY = 0x1dc, - ROTQBYI = 0x1fc, - SHLQBYI = 0x1ff, - SPU_NOP = 0x201, - CLGT = 0x2c0, -}; + enum SPU_0_10_Opcodes + { + STOP = 0x0, + LNOP = 0x1, + SYNC = 0x2, + DSYNC = 0x3, + MFSPR = 0xc, + RDCH = 0xd, + RCHCNT = 0xf, + SF = 0x40, + OR = 0x41, + BG = 0x42, + SFH = 0x48, + NOR = 0x49, + ABSDB = 0x53, + ROT = 0x58, + ROTM = 0x59, + ROTMA = 0x5a, + SHL = 0x5b, + ROTH = 0x5c, + ROTHM = 0x5d, + ROTMAH = 0x5e, + SHLH = 0x5f, + ROTI = 0x78, + ROTMI = 0x79, + ROTMAI = 0x7a, + SHLI = 0x7b, + ROTHI = 0x7c, + ROTHMI = 0x7d, + ROTMAHI = 0x7e, + SHLHI = 0x7f, + A = 0xc0, + AND = 0xc1, + CG = 0xc2, + AH = 0xc8, + NAND = 0xc9, + AVGB = 0xd3, + MTSPR = 0x10c, + WRCH = 0x10d, + BIZ = 0x128, + BINZ = 0x129, + BIHZ = 0x12a, + BIHNZ = 0x12b, + STOPD = 0x140, + STQX = 0x144, + BI = 0x1a8, + BISL = 0x1a9, + IRET = 0x1aa, + BISLED = 0x1ab, + HBR = 0x1ac, + GB = 0x1b0, + GBH = 0x1b1, + GBB = 0x1b2, + FSM = 0x1b4, + FSMH = 0x1b5, + FSMB = 0x1b6, + FREST = 0x1b8, + FRSQEST = 0x1b9, + LQX = 0x1c4, + ROTQBYBI = 0x1cc, + ROTQMBYBI = 0x1cd, + SHLQBYBI = 0x1cf, + CBX = 0x1d4, + CHX = 0x1d5, + CWX = 0x1d6, + CDX = 0x1d7, + ROTQBI = 0x1d8, + ROTQMBI = 0x1d9, + SHLQBI = 0x1db, + ROTQBY = 0x1dc, + ROTQMBY = 0x1dd, + SHLQBY = 0x1df, + ORX = 0x1f0, + CBD = 0x1f4, + CHD = 0x1f5, + CWD = 0x1f6, + CDD = 0x1f7, + ROTQBII = 0x1f8, + ROTQMBII = 0x1f9, + SHLQBII = 0x1fb, + ROTQBYI = 0x1fc, + ROTQMBYI = 0x1fd, + SHLQBYI = 0x1ff, + NOP = 0x201, + CGT = 0x240, + XOR = 0x241, + CGTH = 0x248, + EQV = 0x249, + CGTB = 0x250, + SUMB = 0x253, + HGT = 0x258, + CLZ = 0x2a5, + XSWD = 0x2a6, + XSHW = 0x2ae, + CNTB = 0x2b4, + XSBH = 0x2b6, + CLGT = 0x2c0, + ANDC = 0x2c1, + FCGT = 0x2c2, + DFCGT = 0x2c3, + FA = 0x2c4, + FS = 0x2c5, + FM = 0x2c6, + CLGTH = 0x2c8, + ORC = 0x2c9, + FCMGT = 0x2ca, + DFCMGT = 0x2cb, + DFA = 0x2cc, + DFS = 0x2cd, + DFM = 0x2ce, + CLGTB = 0x2d0, + HLGT = 0x2d8, + DFMA = 0x35c, + DFMS = 0x35d, + DFNMS = 0x35e, + DFNMA = 0x35f, + CEQ = 0x3c0, + MPYHHU = 0x3ce, + ADDX = 0x340, + SFX = 0x341, + CGX = 0x342, + BGX = 0x343, + MPYHHA = 0x346, + MPYHHAU = 0x34e, + FSCRRD = 0x398, + FESD = 0x3b8, + FRDS = 0x3b9, + FSCRWR = 0x3ba, + DFTSV = 0x3bf, + FCEQ = 0x3c2, + DFCEQ = 0x3c3, + MPY = 0x3c4, + MPYH = 0x3c5, + MPYHH = 0x3c6, + MPYS = 0x3c7, + CEQH = 0x3c8, + FCMEQ = 0x3ca, + DFCMEQ = 0x3cb, + MPYU = 0x3cc, + CEQB = 0x3d0, + FI = 0x3d4, + HEQ = 0x3d8, + }; -enum SPU_0_8_Opcodes -{ - BRZ = 0x40, - BRHZ = 0x44, - BRHNZ = 0x46, - STQR = 0x47, - BR = 0x64, - FSMBI = 0x65, - BRSL = 0x66, - LQR = 0x67, - IL = 0x81, -}; + enum SPU_0_9_Opcodes + { + CFLTS = 0x1d8, + CFLTU = 0x1d9, + CSFLT = 0x1da, + CUFLT = 0x1db, + }; -enum SPU_0_7_Opcodes -{ - SPU_ORI = 0x4, - AI = 0x1c, - AHI = 0x1d, - STQD = 0x24, - LQD = 0x34, - CLGTI = 0x5c, - CLGTHI = 0x5d, - CEQI = 0x7c, -}; + enum SPU_0_8_Opcodes + { + BRZ = 0x40, + STQA = 0x41, + BRNZ = 0x42, + BRHZ = 0x44, + BRHNZ = 0x46, + STQR = 0x47, + BRA = 0x60, + LQA = 0x61, + BRASL = 0x62, + BR = 0x64, + FSMBI = 0x65, + BRSL = 0x66, + LQR = 0x67, + IL = 0x81, + ILHU = 0x82, + ILH = 0x83, + IOHL = 0xc1, + }; -enum SPU_0_6_Opcodes -{ - HBRR = 0x9, - ILA = 0x21, -}; + enum SPU_0_7_Opcodes + { + ORI = 0x4, + ORHI = 0x5, + ORBI = 0x6, + SFI = 0xc, + SFHI = 0xd, + ANDI = 0x14, + ANDHI = 0x15, + ANDBI = 0x16, + AI = 0x1c, + AHI = 0x1d, + STQD = 0x24, + LQD = 0x34, + XORI = 0x44, + XORHI = 0x45, + XORBI = 0x46, + CGTI = 0x4c, + CGTHI = 0x4d, + CGTBI = 0x4e, + HGTI = 0x4f, + CLGTI = 0x5c, + CLGTHI = 0x5d, + CLGTBI = 0x5e, + HLGTI = 0x5f, + MPYI = 0x74, + MPYUI = 0x75, + CEQI = 0x7c, + CEQHI = 0x7d, + CEQBI = 0x7e, + HEQI = 0x7f, + }; -enum SPU_0_3_Opcodes -{ - SELB = 0x8, - SHUFB = 0xb, + enum SPU_0_6_Opcodes + { + HBRA = 0x8, + HBRR = 0x9, + ILA = 0x21, + }; + + enum SPU_0_3_Opcodes + { + SELB = 0x8, + SHUFB = 0xb, + MPYA = 0xc, + FNMS = 0xd, + FMA = 0xe, + FMS = 0xf, + }; }; class SPU_Opcodes @@ -79,79 +244,216 @@ public: virtual void Exit()=0; -/* -- 1: Unk/0xb0c2c58c -> 11c: b0 c2 c5 8c shufb $6,$11,$11,$12 -- 2: Unk/0x5800c505 -> 124: 58 00 c5 05 clgt $5,$10,$3 -- 3: Unk/0x5c000184 -> 128: 5c 00 01 84 clgti $4,$3,0 -- 4: Unk/0x18210282 -> 12c: 18 21 02 82 and $2,$5,$4 -- 5: Unk/0x00000000 -> 154: 00 00 00 00 stop -- 6: Unk/0x00200000 -> 1b4: 00 20 00 00 lnop -- 7: Unk/0x0f608487 -> 1c0: 0f 60 84 87 shli $7,$9,2 -- 8: Unk/0x18140384 -> 1c8: 18 14 03 84 a $4,$7,$80 -- 9: Unk/0x38940386 -> 1cc: 38 94 03 86 lqx $6,$7,$80 -- 10: Unk/0x3b810305 -> 1d0: 3b 81 03 05 rotqby $5,$6,$4 -- 11: Unk/0x35200280 -> 1d4: 35 20 02 80 bisl $0,$5 -- 12: Unk/0x237ff982 -> 1e4: 23 7f f9 82 brhnz $2,0x1b0 -- 13: Unk/0x35800014 -> 204: 35 80 00 14 hbr 0x254,$0 -- 14: Unk/0x5d13c482 -> 224: 5d 13 c4 82 clgthi $2,$9,79 -- 15: Unk/0x3ac1828d -> 240: 3a c1 82 8d cwx $13,$5,$6 -- 16: Unk/0x2881828b -> 24c: 28 81 82 8b stqx $11,$5,$6 -- 17: Unk/0x21a00b82 -> 25c: 21 a0 0b 82 wrch $ch23,$2 -- 18: Unk/0x01e00c05 -> 260: 01 e0 0c 05 rchcnt $5,$ch24 -- 19: Unk/0x7c004284 -> 264: 7c 00 42 84 ceqi $4,$5,1 -- 20: Unk/0x207ffe84 -> 26c: 20 7f fe 84 brz $4,0x260 -- 21: Unk/0x01a00c03 -> 27c: 01 a0 0c 03 rdch $3,$ch24 -*/ //0 - 10 ADD_OPCODE(STOP,(OP_uIMM code)); ADD_OPCODE(LNOP,()); + ADD_OPCODE(SYNC, (OP_uIMM Cbit)); + ADD_OPCODE(DSYNC, ()); + ADD_OPCODE(MFSPR,(OP_REG rt, OP_REG sa)); ADD_OPCODE(RDCH,(OP_REG rt, OP_REG ra)); ADD_OPCODE(RCHCNT,(OP_REG rt, OP_REG ra)); ADD_OPCODE(SF,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(OR,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(BG,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(SFH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(NOR,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ABSDB,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTM,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTMA,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(SHL,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTHM,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTMAH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(SHLH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(ROTMI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(ROTMAI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); ADD_OPCODE(SHLI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(ROTHI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(ROTHMI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(ROTMAHI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(SHLHI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); ADD_OPCODE(A,(OP_REG rt, OP_REG ra, OP_REG rb)); - ADD_OPCODE(SPU_AND,(OP_REG rt, OP_REG ra, OP_REG rb)); - ADD_OPCODE(LQX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(AND,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CG,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(AH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(NAND,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(AVGB,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MTSPR,(OP_REG rt, OP_REG sa)); ADD_OPCODE(WRCH,(OP_REG ra, OP_REG rt)); + ADD_OPCODE(BIZ,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(BINZ,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(BIHZ,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(BIHNZ,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(STOPD,(OP_REG rc, OP_REG ra, OP_REG rb)); ADD_OPCODE(STQX,(OP_REG rt, OP_REG ra, OP_REG rb)); ADD_OPCODE(BI,(OP_REG ra)); ADD_OPCODE(BISL,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(IRET,(OP_REG ra)); + ADD_OPCODE(BISLED,(OP_REG rt, OP_REG ra)); ADD_OPCODE(HBR,(OP_REG p, OP_REG ro, OP_REG ra)); + ADD_OPCODE(GB,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(GBH,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(GBB,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(FSM,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(FSMH,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(FSMB,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(FREST,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(FRSQEST,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(LQX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTQBYBI,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTQMBYBI,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(SHLQBYBI,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CBX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CHX,(OP_REG rt, OP_REG ra, OP_REG rb)); ADD_OPCODE(CWX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CDX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTQBI,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTQMBI,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(SHLQBI,(OP_REG rt, OP_REG ra, OP_REG rb)); ADD_OPCODE(ROTQBY,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ROTQMBY,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(SHLQBY,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ORX,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(CBD,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(CHD,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(CWD,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(CDD,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(ROTQBII,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(ROTQMBII,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(SHLQBII,(OP_REG rt, OP_REG ra, OP_sIMM i7)); ADD_OPCODE(ROTQBYI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(ROTQMBYI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); ADD_OPCODE(SHLQBYI,(OP_REG rt, OP_REG ra, OP_sIMM i7)); - ADD_OPCODE(SPU_NOP,(OP_REG rt)); + ADD_OPCODE(NOP,(OP_REG rt)); + ADD_OPCODE(CGT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(XOR,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CGTH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(EQV,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CGTB,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(SUMB,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(HGT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CLZ,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(XSWD,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(XSHW,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(CNTB,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(XSBH,(OP_REG rt, OP_REG ra)); ADD_OPCODE(CLGT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ANDC,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(FCGT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFCGT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(FA,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(FS,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(FM,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CLGTH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ORC,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(FCMGT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFCMGT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFA,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFS,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFM,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CLGTB,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(HLGT,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFMA,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFMS,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFNMS,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFNMA,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CEQ,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MPYHHU,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(ADDX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(SFX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CGX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(BGX,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MPYHHA,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MPYHHAU,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(FSCRRD,(OP_REG rt)); + ADD_OPCODE(FESD,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(FRDS,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(FSCRWR,(OP_REG rt, OP_REG ra)); + ADD_OPCODE(DFTSV,(OP_REG rt, OP_REG ra, OP_sIMM i7)); + ADD_OPCODE(FCEQ,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFCEQ,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MPY,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MPYH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MPYHH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MPYS,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CEQH,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(FCMEQ,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(DFCMEQ,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(MPYU,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(CEQB,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(FI,(OP_REG rt, OP_REG ra, OP_REG rb)); + ADD_OPCODE(HEQ,(OP_REG rt, OP_REG ra, OP_REG rb)); + + //0 - 9 + ADD_OPCODE(CFLTS,(OP_REG rt, OP_REG ra, OP_sIMM i8)); + ADD_OPCODE(CFLTU,(OP_REG rt, OP_REG ra, OP_sIMM i8)); + ADD_OPCODE(CSFLT,(OP_REG rt, OP_REG ra, OP_sIMM i8)); + ADD_OPCODE(CUFLT,(OP_REG rt, OP_REG ra, OP_sIMM i8)); //0 - 8 ADD_OPCODE(BRZ,(OP_REG rt, OP_sIMM i16)); + ADD_OPCODE(STQA,(OP_REG rt, OP_sIMM i16)); + ADD_OPCODE(BRNZ,(OP_REG rt, OP_sIMM i16)); ADD_OPCODE(BRHZ,(OP_REG rt, OP_sIMM i16)); ADD_OPCODE(BRHNZ,(OP_REG rt, OP_sIMM i16)); ADD_OPCODE(STQR,(OP_REG rt, OP_sIMM i16)); + ADD_OPCODE(BRA,(OP_sIMM i16)); + ADD_OPCODE(LQA,(OP_REG rt, OP_sIMM i16)); + ADD_OPCODE(BRASL,(OP_REG rt, OP_sIMM i16)); ADD_OPCODE(BR,(OP_sIMM i16)); ADD_OPCODE(FSMBI,(OP_REG rt, OP_sIMM i16)); ADD_OPCODE(BRSL,(OP_REG rt, OP_sIMM i16)); - ADD_OPCODE(IL,(OP_REG rt, OP_sIMM i16)); ADD_OPCODE(LQR,(OP_REG rt, OP_sIMM i16)); + ADD_OPCODE(IL,(OP_REG rt, OP_sIMM i16)); + ADD_OPCODE(ILHU,(OP_REG rt, OP_sIMM i16)); + ADD_OPCODE(ILH,(OP_REG rt, OP_sIMM i16)); + ADD_OPCODE(IOHL,(OP_REG rt, OP_sIMM i16)); //0 - 7 - ADD_OPCODE(SPU_ORI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(ORI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(ORHI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(ORBI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(SFI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(SFHI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(ANDI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(ANDHI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(ANDBI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); ADD_OPCODE(AI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); ADD_OPCODE(AHI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); ADD_OPCODE(STQD,(OP_REG rt, OP_sIMM i10, OP_REG ra)); ADD_OPCODE(LQD,(OP_REG rt, OP_sIMM i10, OP_REG ra)); + ADD_OPCODE(XORI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(XORHI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(XORBI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(CGTI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(CGTHI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(CGTBI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(HGTI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); ADD_OPCODE(CLGTI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); ADD_OPCODE(CLGTHI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(CLGTBI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(HLGTI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(MPYI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(MPYUI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); ADD_OPCODE(CEQI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(CEQHI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(CEQBI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); + ADD_OPCODE(HEQI,(OP_REG rt, OP_REG ra, OP_sIMM i10)); //0 - 6 + ADD_OPCODE(HBRA,(OP_sIMM ro, OP_sIMM i16)); ADD_OPCODE(HBRR,(OP_sIMM ro, OP_sIMM i16)); ADD_OPCODE(ILA,(OP_REG rt, OP_sIMM i18)); //0 - 3 ADD_OPCODE(SELB,(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt)); ADD_OPCODE(SHUFB,(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt)); + ADD_OPCODE(MPYA,(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt)); + ADD_OPCODE(FNMS,(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt)); + ADD_OPCODE(FMA,(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt)); + ADD_OPCODE(FMS,(OP_REG rc, OP_REG ra, OP_REG rb, OP_REG rt)); ADD_OPCODE(UNK,(const s32 code, const s32 opcode, const s32 gcode)); }; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 9af4f4c653..fcac328f20 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4,6 +4,15 @@ #include "Emu/Cell/SPUInterpreter.h" #include "Emu/Cell/SPUDisAsm.h" +SPUThread& GetCurrentSPUThread() +{ + PPCThread* thread = GetCurrentPPCThread(); + + if(!thread || !thread->IsSPU()) throw wxString("GetCurrentSPUThread: bad thread"); + + return *(SPUThread*)thread; +} + SPUThread::SPUThread() : PPCThread(PPC_THREAD_SPU) { Reset(); diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 0ff9b7cd38..54e46e85f6 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -248,4 +248,6 @@ protected: private: virtual void DoCode(const s32 code); -}; \ No newline at end of file +}; + +SPUThread& GetCurrentSPUThread(); \ No newline at end of file diff --git a/rpcs3/Emu/DbgConsole.cpp b/rpcs3/Emu/DbgConsole.cpp index 9a7a54d1ba..263368600b 100644 --- a/rpcs3/Emu/DbgConsole.cpp +++ b/rpcs3/Emu/DbgConsole.cpp @@ -6,7 +6,7 @@ BEGIN_EVENT_TABLE(DbgConsole, FrameBase) END_EVENT_TABLE() DbgConsole::DbgConsole() - : FrameBase(NULL, wxID_ANY, "DbgConsole", wxEmptyString, wxDefaultSize, wxDefaultPosition) + : FrameBase(NULL, wxID_ANY, "DbgConsole", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxDEFAULT_FRAME_STYLE, true) , ThreadBase(false, "DbgConsole thread") { m_console = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, @@ -20,7 +20,7 @@ DbgConsole::DbgConsole() DbgConsole::~DbgConsole() { - Stop(); + ThreadBase::Stop(); m_dbg_buffer.Flush(); } @@ -39,7 +39,7 @@ void DbgConsole::Clear() void DbgConsole::Task() { - while(m_dbg_buffer.HasNewPacket()) + while(m_dbg_buffer.HasNewPacket() && !TestDestroy()) { DbgPacket packet = m_dbg_buffer.Pop(); m_console->SetDefaultStyle(packet.m_ch == 1 ? *m_color_red : *m_color_white); @@ -52,5 +52,7 @@ void DbgConsole::Task() void DbgConsole::OnQuit(wxCloseEvent& event) { + ThreadBase::Stop(); Hide(); + //event.Skip(); } \ No newline at end of file diff --git a/rpcs3/Emu/DbgConsole.h b/rpcs3/Emu/DbgConsole.h index 0f98807521..548fcb4458 100644 --- a/rpcs3/Emu/DbgConsole.h +++ b/rpcs3/Emu/DbgConsole.h @@ -27,7 +27,7 @@ struct _DbgBuffer : public MTPacketBuffer { } - void Push(const DbgPacket& data) + void _push(const DbgPacket& data) { const u32 stext = data.m_text.Len(); @@ -47,7 +47,7 @@ struct _DbgBuffer : public MTPacketBuffer CheckBusy(); } - DbgPacket Pop() + DbgPacket _pop() { DbgPacket ret; diff --git a/rpcs3/Emu/FS/VFS.cpp b/rpcs3/Emu/FS/VFS.cpp new file mode 100644 index 0000000000..3aa909ffd6 --- /dev/null +++ b/rpcs3/Emu/FS/VFS.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "VFS.h" + +int sort_devices(const void* _a, const void* _b) +{ + const vfsDevice& a = **(const vfsDevice**)_a; + const vfsDevice& b = **(const vfsDevice**)_b; + + if(a.GetPs3Path().Len() > b.GetPs3Path().Len()) return 1; + if(a.GetPs3Path().Len() < b.GetPs3Path().Len()) return -1; + + return 0; +} + +void VFS::Mount(const wxString& ps3_path, const wxString& local_path, vfsDevice* device) +{ + UnMount(ps3_path); + + device->SetPath(ps3_path, local_path); + m_devices.Add(device); + + if(m_devices.GetCount() > 1) + { + std::qsort(m_devices.GetPtr(), m_devices.GetCount(), sizeof(vfsDevice*), sort_devices); + } +} + +void VFS::UnMount(const wxString& ps3_path) +{ + for(u32 i=0; iGetNew(); + stream->Open(path, mode); + } + + return stream; +} + +void VFS::Create(const wxString& ps3_path) +{ + wxString path; + + if(vfsDevice* dev = GetDevice(ps3_path, path)) + { + dev->Create(path); + } +} + +void VFS::Close(vfsStream*& device) +{ + delete device; + device = nullptr; +} + +vfsDevice* VFS::GetDevice(const wxString& ps3_path, wxString& path) +{ + u32 max_eq; + s32 max_i=-1; + + for(u32 i=0; i max_eq) + { + max_eq = eq; + max_i = i; + } + } + + if(max_i < 0) return nullptr; + + path = vfsDevice::GetWinPath(m_devices[max_i].GetLocalPath(), ps3_path(max_eq, ps3_path.Len() - max_eq)); + return &m_devices[max_i]; +} diff --git a/rpcs3/Emu/FS/VFS.h b/rpcs3/Emu/FS/VFS.h new file mode 100644 index 0000000000..75e0f2dc49 --- /dev/null +++ b/rpcs3/Emu/FS/VFS.h @@ -0,0 +1,15 @@ +#pragma once +#include "vfsDevice.h" + +struct VFS +{ + ArrayF m_devices; + + void Mount(const wxString& ps3_path, const wxString& local_path, vfsDevice* device); + void UnMount(const wxString& ps3_path); + + vfsStream* Open(const wxString& ps3_path, vfsOpenMode mode); + void Create(const wxString& ps3_path); + void Close(vfsStream*& device); + vfsDevice* GetDevice(const wxString& ps3_path, wxString& path); +}; \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsDevice.cpp b/rpcs3/Emu/FS/vfsDevice.cpp new file mode 100644 index 0000000000..1560fa647b --- /dev/null +++ b/rpcs3/Emu/FS/vfsDevice.cpp @@ -0,0 +1,201 @@ +#include "stdafx.h" +#include "vfsDevice.h" + +vfsDevice::vfsDevice(const wxString& ps3_path, const wxString& local_path) + : m_ps3_path(ps3_path) + , m_local_path(GetWinPath(local_path)) +{ +} + +wxString vfsDevice::GetLocalPath() const +{ + return m_local_path; +} + +wxString vfsDevice::GetPs3Path() const +{ + return m_ps3_path; +} + +void vfsDevice::SetPath(const wxString& ps3_path, const wxString& local_path) +{ + m_ps3_path = ps3_path; + m_local_path = local_path; +} + +u32 vfsDevice::CmpPs3Path(const wxString& ps3_path) +{ + const u32 lim = min(m_ps3_path.Len(), ps3_path.Len()); + u32 ret = 0; + + for(u32 i=0; i= 0; --i) + { + if(path[i] == '\\' || path[i] == '/' || i == 0) + { + if(dir++ == end_dir_count) + { + to = i; + break; + } + } + } + + return path(from, to - from); +} + +wxString vfsDevice::GetRoot(const wxString& path) +{ + if(path.IsEmpty()) return wxEmptyString; + + u32 first_dir = path.Len() - 1; + + for(int i = path.Len() - 1, dir = 0, li = path.Len() - 1; i >= 0 && dir < 2; --i) + { + if(path[i] == '\\' || path[i] == '/' || i == 0) + { + switch(dir++) + { + case 0: + first_dir = i; + break; + + case 1: + if(!path(i + 1, li - i).Cmp("USRDIR")) return path(0, i + 1); + continue; + } + + li = i - 1; + } + } + + return path(0, first_dir + 1); +} + +wxString vfsDevice::GetRootPs3(const wxString& path) +{ + if(path.IsEmpty()) return wxEmptyString; + + static const wxString& home = "/dev_hdd0/game/"; + u32 last_dir = 0; + u32 first_dir = path.Len() - 1; + + for(int i = path.Len() - 1, dir = 0; i >= 0; --i) + { + if(path[i] == '\\' || path[i] == '/' || i == 0) + { + switch(dir++) + { + case 1: + if(!!path(i + 1, last_dir - i - 1).Cmp("USRDIR")) return wxEmptyString; + break; + + case 2: + return GetPs3Path(home + path(i + 1, last_dir - i - 1)); + } + + last_dir = i; + } + } + + return GetPs3Path(home + path(0, last_dir - 1)); +} + +wxString vfsDevice::GetWinPath(const wxString& p, bool is_dir) +{ + if(p.IsEmpty()) return wxEmptyString; + + wxString ret; + bool is_ls = false; + + for(u32 i=0; i GetEntryes()=0; + virtual void Close()=0; + + virtual bool Create(const wxString& path)=0; + virtual bool Exists(const wxString& path)=0; + virtual bool Rename(const wxString& from, const wxString& to)=0; + virtual bool Remove(const wxString& path)=0; +}; \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsFileBase.cpp b/rpcs3/Emu/FS/vfsFileBase.cpp new file mode 100644 index 0000000000..5606491d37 --- /dev/null +++ b/rpcs3/Emu/FS/vfsFileBase.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "vfsFileBase.h" + +vfsFileBase::vfsFileBase() : vfsDevice() +{ +} + +vfsFileBase::~vfsFileBase() +{ + Close(); +} + +bool Access(const wxString& path, vfsOpenMode mode) +{ + return false; +} + +bool vfsFileBase::Open(const wxString& path, vfsOpenMode mode) +{ + m_path = path; + m_mode = mode; + + vfsStream::Reset(); + + return true; +} + +bool vfsFileBase::Close() +{ + m_path = wxEmptyString; + + return vfsStream::Close(); +} + +wxString vfsFileBase::GetPath() const +{ + return m_path; +} + +vfsOpenMode vfsFileBase::GetOpenMode() const +{ + return m_mode; +} \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsFileBase.h b/rpcs3/Emu/FS/vfsFileBase.h new file mode 100644 index 0000000000..0b33913dc2 --- /dev/null +++ b/rpcs3/Emu/FS/vfsFileBase.h @@ -0,0 +1,24 @@ +#pragma once +#include "vfsDevice.h" + +struct vfsFileBase : public vfsDevice +{ +protected: + wxString m_path; + vfsOpenMode m_mode; + +public: + vfsFileBase(); + virtual ~vfsFileBase(); + + virtual bool Open(const wxString& path, vfsOpenMode mode); + virtual bool Close(); + /* + virtual bool Create(const wxString& path)=0; + virtual bool Exists(const wxString& path)=0; + virtual bool Rename(const wxString& from, const wxString& to)=0; + virtual bool Remove(const wxString& path)=0; + */ + wxString GetPath() const; + vfsOpenMode GetOpenMode() const; +}; \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsLocalFile.cpp b/rpcs3/Emu/FS/vfsLocalFile.cpp new file mode 100644 index 0000000000..244cb68e8a --- /dev/null +++ b/rpcs3/Emu/FS/vfsLocalFile.cpp @@ -0,0 +1,95 @@ +#include "stdafx.h" +#include "vfsLocalFile.h" + +static const wxFile::OpenMode vfs2wx_mode(vfsOpenMode mode) +{ + switch(mode) + { + case vfsRead: return wxFile::read; + case vfsWrite: return wxFile::write; + case vfsReadWrite: return wxFile::read_write; + case vfsWriteExcl: return wxFile::write_excl; + case vfsWriteAppend: return wxFile::write_append; + } + + return wxFile::read; +} + +static const wxSeekMode vfs2wx_seek(vfsSeekMode mode) +{ + switch(mode) + { + case vfsSeekSet: return wxFromStart; + case vfsSeekCur: return wxFromCurrent; + case vfsSeekEnd: return wxFromEnd; + } + + return wxFromStart; +} + +vfsLocalFile::vfsLocalFile() : vfsFileBase() +{ +} + +vfsLocalFile::vfsLocalFile(const wxString path, vfsOpenMode mode) : vfsFileBase() +{ + Open(path, mode); +} + +vfsDevice* vfsLocalFile::GetNew() +{ + return new vfsLocalFile(); +} + +bool vfsLocalFile::Open(const wxString& path, vfsOpenMode mode) +{ + Close(); + + if(mode == vfsRead && !m_file.Access(vfsDevice::GetWinPath(GetLocalPath(), path), vfs2wx_mode(mode))) return false; + + return m_file.Open(vfsDevice::GetWinPath(GetLocalPath(), path), vfs2wx_mode(mode)) && + vfsFileBase::Open(vfsDevice::GetPs3Path(GetPs3Path(), path), mode); +} + +bool vfsLocalFile::Create(const wxString& path) +{ + if(wxFileExists(path)) return false; + + wxFile f; + return f.Create(path); +} + +bool vfsLocalFile::Close() +{ + return m_file.Close() && vfsFileBase::Close(); +} + +u64 vfsLocalFile::GetSize() +{ + return m_file.Length(); +} + +u32 vfsLocalFile::Write(const void* src, u32 size) +{ + return m_file.Write(src, size); +} + +u32 vfsLocalFile::Read(void* dst, u32 size) +{ + return m_file.Read(dst, size); +} + +u64 vfsLocalFile::Seek(s64 offset, vfsSeekMode mode) +{ + return m_file.Seek(offset, vfs2wx_seek(mode)); +} + +u64 vfsLocalFile::Tell() const +{ + return m_file.Tell(); +} + +bool vfsLocalFile::IsOpened() const +{ + return m_file.IsOpened() && vfsFileBase::IsOpened(); +} \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsLocalFile.h b/rpcs3/Emu/FS/vfsLocalFile.h new file mode 100644 index 0000000000..d205675936 --- /dev/null +++ b/rpcs3/Emu/FS/vfsLocalFile.h @@ -0,0 +1,27 @@ +#pragma once +#include "vfsFileBase.h" + +class vfsLocalFile : public vfsFileBase +{ +private: + wxFile m_file; + +public: + vfsLocalFile(); + vfsLocalFile(const wxString path, vfsOpenMode mode = vfsRead); + vfsDevice* GetNew(); + + virtual bool Open(const wxString& path, vfsOpenMode mode = vfsRead); + virtual bool Create(const wxString& path); + virtual bool Close(); + + virtual u64 GetSize(); + + virtual u32 Write(const void* src, u32 size); + virtual u32 Read(void* dst, u32 size); + + virtual u64 Seek(s64 offset, vfsSeekMode mode = vfsSeekSet); + virtual u64 Tell() const; + + virtual bool IsOpened() const; +}; \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsStream.cpp b/rpcs3/Emu/FS/vfsStream.cpp new file mode 100644 index 0000000000..abecaf1849 --- /dev/null +++ b/rpcs3/Emu/FS/vfsStream.cpp @@ -0,0 +1,74 @@ +#include "stdafx.h" +#include "vfsStream.h" + +vfsStream::vfsStream() +{ +} + +vfsStream::~vfsStream() +{ + Close(); +} + +void vfsStream::Reset() +{ + m_pos = 0; +} + +bool vfsStream::Close() +{ + Reset(); + + return true; +} + +u64 vfsStream::GetSize() +{ + u64 last_pos = Tell(); + Seek(0, vfsSeekEnd); + u64 size = Tell(); + Seek(last_pos, vfsSeekSet); + + return size; +} + +u32 vfsStream::Write(const void* src, u32 size) +{ + m_pos += size; + + return size; +} + +u32 vfsStream::Read(void* dst, u32 size) +{ + m_pos += size; + + return size; +} + +u64 vfsStream::Seek(s64 offset, vfsSeekMode mode) +{ + switch(mode) + { + case vfsSeekSet: m_pos = offset; break; + case vfsSeekCur: m_pos += offset; break; + case vfsSeekEnd: m_pos = GetSize() + offset; break; + } + + return m_pos; +} + +u64 vfsStream::Tell() const +{ + return m_pos; +} + +bool vfsStream::Eof() +{ + return Tell() >= GetSize(); +} + +bool vfsStream::IsOpened() const +{ + return true; +} \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsStream.h b/rpcs3/Emu/FS/vfsStream.h new file mode 100644 index 0000000000..be2325e5c9 --- /dev/null +++ b/rpcs3/Emu/FS/vfsStream.h @@ -0,0 +1,33 @@ +#pragma once + +enum vfsSeekMode +{ + vfsSeekSet, + vfsSeekCur, + vfsSeekEnd, +}; + +struct vfsStream +{ +protected: + u64 m_pos; + +public: + vfsStream(); + + virtual ~vfsStream(); + + virtual void Reset(); + virtual bool Close(); + + virtual u64 GetSize(); + + virtual u32 Write(const void* src, u32 size); + virtual u32 Read(void* dst, u32 size); + + virtual u64 Seek(s64 offset, vfsSeekMode mode = vfsSeekSet); + virtual u64 Tell() const; + virtual bool Eof(); + + virtual bool IsOpened() const; +}; \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsStreamMemory.cpp b/rpcs3/Emu/FS/vfsStreamMemory.cpp new file mode 100644 index 0000000000..29aa7a853e --- /dev/null +++ b/rpcs3/Emu/FS/vfsStreamMemory.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "vfsStreamMemory.h" + +vfsStreamMemory::vfsStreamMemory() : vfsStream() +{ +} + +vfsStreamMemory::vfsStreamMemory(u64 addr) : vfsStream() +{ + Open(addr); +} + +void vfsStreamMemory::Open(u64 addr) +{ + m_addr = addr; + + vfsStream::Reset(); +} + +u32 vfsStreamMemory::Write(const void* src, u32 size) +{ + if(!Memory.IsGoodAddr(m_addr + Tell(), size)) return 0; + + memcpy(&Memory[m_addr + Tell()], src, size); + + return vfsStream::Write(src, size); +} + +u32 vfsStreamMemory::Read(void* dst, u32 size) +{ + if(!Memory.IsGoodAddr(m_addr + Tell(), size)) return 0; + + memcpy(dst, &Memory[m_addr + Tell()], size); + + return vfsStream::Read(dst, size); +} diff --git a/rpcs3/Emu/FS/vfsStreamMemory.h b/rpcs3/Emu/FS/vfsStreamMemory.h new file mode 100644 index 0000000000..7acb0d8713 --- /dev/null +++ b/rpcs3/Emu/FS/vfsStreamMemory.h @@ -0,0 +1,16 @@ +#pragma once +#include "vfsStream.h" + +struct vfsStreamMemory : public vfsStream +{ + u64 m_addr; + +public: + vfsStreamMemory(); + vfsStreamMemory(u64 addr); + + void Open(u64 addr); + + virtual u32 Write(const void* src, u32 size); + virtual u32 Read(void* dst, u32 size); +}; \ No newline at end of file diff --git a/rpcs3/Emu/GS/GCM.h b/rpcs3/Emu/GS/GCM.h index 8318c5939c..80218b991e 100644 --- a/rpcs3/Emu/GS/GCM.h +++ b/rpcs3/Emu/GS/GCM.h @@ -1,10 +1,17 @@ #pragma once +enum +{ + CELL_GCM_DISPLAY_HSYNC = 1, + CELL_GCM_DISPLAY_VSYNC = 2, + CELL_GCM_DISPLAY_HSYNC_WITH_NOISE = 3, +}; + struct CellGcmControl { - volatile u32 put; - volatile u32 get; - volatile u32 ref; + u32 put; + u32 get; + u32 ref; }; struct CellGcmConfig @@ -332,12 +339,12 @@ enum NV3089_IMAGE_IN = 0x0000C40C, }; -static const wxString getMethodName(const u32 id) +static const wxString GetMethodName(const u32 id) { struct MethodName { const u32 id; - const wxString name; + const wxString& name; } static const METHOD_NAME_LIST[] = { { NV4097_NO_OPERATION , "NoOperation" } , { NV4097_NOTIFY , "Notify" } , diff --git a/rpcs3/Emu/GS/GL/FragmentProgram.cpp b/rpcs3/Emu/GS/GL/FragmentProgram.cpp index 3db47a5a43..a2678419ff 100644 --- a/rpcs3/Emu/GS/GL/FragmentProgram.cpp +++ b/rpcs3/Emu/GS/GL/FragmentProgram.cpp @@ -49,7 +49,12 @@ wxString FragmentDecompilerThread::GetMask() wxString FragmentDecompilerThread::AddReg(u32 index) { //if(!index) return "gl_FragColor"; - return m_parr.AddParam(index ? PARAM_NONE : PARAM_OUT, "vec4", wxString::Format("r%d", index)); + return m_parr.AddParam(index ? PARAM_NONE : PARAM_OUT, "vec4", wxString::Format("r%d", index), index ? -1 : 0); +} + +wxString FragmentDecompilerThread::AddConst() +{ + return m_parr.AddParam(PARAM_CONST, "vec4", wxString::Format("fc%d", m_const_index++)); } wxString FragmentDecompilerThread::AddTex() @@ -96,8 +101,10 @@ template wxString FragmentDecompilerThread::GetSRC(T src) } break; - //case 2: //imm - //break; + case 2: //const + ConLog.Write("reg index = %d", src.tmp_reg_index); + ret += AddConst(); + break; default: ConLog.Error("Bad src type %d", src.reg_type); @@ -126,17 +133,11 @@ wxString FragmentDecompilerThread::BuildCode() for(u32 i=0; iIsAlive()) m_decompiler_thread->Delete(); - safe_delete(m_decompiler_thread); + if(m_decompiler_thread->IsAlive()) + { + m_decompiler_thread->Stop(); + } + + delete m_decompiler_thread; + m_decompiler_thread = nullptr; } Delete(); @@ -207,13 +272,17 @@ void ShaderProgram::Decompile() if(m_decompiler_thread) { Wait(); - if(m_decompiler_thread->IsAlive()) m_decompiler_thread->Delete(); - safe_delete(m_decompiler_thread); + if(m_decompiler_thread->IsAlive()) + { + m_decompiler_thread->Stop(); + } + + delete m_decompiler_thread; + m_decompiler_thread = nullptr; } m_decompiler_thread = new FragmentDecompilerThread(shader, parr, addr, size); - m_decompiler_thread->Create(); - m_decompiler_thread->Run(); + m_decompiler_thread->Start(); #endif } @@ -242,7 +311,7 @@ void ShaderProgram::Compile() memset(buf, 0, r+1); glGetShaderInfoLog(id, r, &len, buf); ConLog.Error("Failed to compile shader: %s", buf); - free(buf); + delete[] buf; } ConLog.Write(shader); @@ -255,9 +324,10 @@ void ShaderProgram::Delete() { for(u32 i=0; i wxString GetSRC(T src); wxString BuildCode(); - ExitCode Entry(); + virtual void Task(); u32 GetData(const u32 d) const { return d << 16 | d >> 16; } }; @@ -138,7 +142,13 @@ struct ShaderProgram u32 id; - void Wait() { if(m_decompiler_thread && m_decompiler_thread->IsRunning()) m_decompiler_thread->Wait(); } + void Wait() + { + if(m_decompiler_thread && m_decompiler_thread->IsAlive()) + { + m_decompiler_thread->Wait(); + } + } void Decompile(); void Compile(); diff --git a/rpcs3/Emu/GS/GL/GLBuffers.cpp b/rpcs3/Emu/GS/GL/GLBuffers.cpp index 587a702733..8d8e96957b 100644 --- a/rpcs3/Emu/GS/GL/GLBuffers.cpp +++ b/rpcs3/Emu/GS/GL/GLBuffers.cpp @@ -2,52 +2,118 @@ #include "GLBuffers.h" #include "GLGSRender.h" -BufferObject::BufferObject() - : m_id(0) - , m_type(0) +GLBufferObject::GLBufferObject() { } -BufferObject::BufferObject(u32 type) - : m_id(0) - , m_type(0) +GLBufferObject::GLBufferObject(u32 type) { Create(type); } -void BufferObject::Create(u32 type) +GLBufferObject::~GLBufferObject() { - if(m_id) return; - glGenBuffers(1, &m_id); + Delete(); +} + +void GLBufferObject::Create(GLuint type, u32 count) +{ + if(IsCreated()) return; + + m_id.InsertRoomEnd(count); + glGenBuffers(count, &m_id[0]); m_type = type; } -void BufferObject::Delete() +void GLBufferObject::Delete() { - if(!m_id) return; - glDeleteBuffers(1, &m_id); - m_id = 0; + if(!IsCreated()) return; + + glDeleteBuffers(m_id.GetCount(), &m_id[0]); + m_id.Clear(); m_type = 0; } -void BufferObject::Bind() +void GLBufferObject::Bind(u32 type, u32 num) { - if(m_id) glBindBuffer(m_type, m_id); + glBindBuffer(type, m_id[num]); } -void BufferObject::UnBind() +void GLBufferObject::UnBind(u32 type) { - glBindBuffer(m_type, 0); + glBindBuffer(type, 0); } -void BufferObject::SetData(const void* data, u32 size, u32 type) +void GLBufferObject::Bind(u32 num) { - if(!m_type) return; - Bind(); - glBufferData(m_type, size, data, type); + Bind(m_type, num); } -void VBO::Create() +void GLBufferObject::UnBind() { - BufferObject::Create(GL_ARRAY_BUFFER); + UnBind(m_type); +} + +void GLBufferObject::SetData(u32 type, const void* data, u32 size, u32 usage) +{ + glBufferData(type, size, data, usage); +} + +void GLBufferObject::SetData(const void* data, u32 size, u32 usage) +{ + SetData(m_type, data, size, usage); +} + +void GLBufferObject::SetAttribPointer(int location, int size, int type, int pointer, int stride, bool normalized) +{ + if(location < 0) return; + + glVertexAttribPointer(location, size, type, normalized ? GL_TRUE : GL_FALSE, stride, (const GLvoid*)pointer); + glEnableVertexAttribArray(location); +} + +bool GLBufferObject::IsCreated() const +{ + return m_id.GetCount() != 0; +} + +GLvbo::GLvbo() +{ +} + +void GLvbo::Create(u32 count) +{ + GLBufferObject::Create(GL_ARRAY_BUFFER, count); +} + +GLvao::GLvao() : m_id(0) +{ +} + +GLvao::~GLvao() +{ + Delete(); +} + +void GLvao::Create() +{ + if(!IsCreated()) glGenVertexArrays(1, &m_id); +} + +void GLvao::Bind() const +{ + glBindVertexArray(m_id); +} + +void GLvao::Delete() +{ + if(!IsCreated()) return; + + glDeleteVertexArrays(1, &m_id); + m_id = 0; +} + +bool GLvao::IsCreated() const +{ + return m_id != 0; } \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/GLBuffers.h b/rpcs3/Emu/GS/GL/GLBuffers.h index 8b334ece58..e42a7b4078 100644 --- a/rpcs3/Emu/GS/GL/GLBuffers.h +++ b/rpcs3/Emu/GS/GL/GLBuffers.h @@ -1,24 +1,48 @@ #pragma once #include "OpenGL.h" -struct BufferObject +struct GLBufferObject { protected: - u32 m_id; - u32 m_type; + Array m_id; + GLuint m_type; public: - BufferObject(); - BufferObject(u32 type); - - void Create(u32 type); + GLBufferObject(); + GLBufferObject(u32 type); + + ~GLBufferObject(); + + void Create(GLuint type, u32 count = 1); void Delete(); - void Bind(); + void Bind(u32 type, u32 num); + void UnBind(u32 type); + void Bind(u32 num = 0); void UnBind(); - void SetData(const void* data, u32 size, u32 type = GL_DYNAMIC_DRAW); + void SetData(u32 type, const void* data, u32 size, u32 usage = GL_DYNAMIC_DRAW); + void SetData(const void* data, u32 size, u32 usage = GL_DYNAMIC_DRAW); + void SetAttribPointer(int location, int size, int type, int pointer, int stride, bool normalized = false); + bool IsCreated() const; }; -struct VBO : public BufferObject +struct GLvbo : public GLBufferObject { + GLvbo(); + + void Create(u32 count = 1); +}; + +class GLvao +{ +protected: + GLuint m_id; + +public: + GLvao(); + ~GLvao(); + void Create(); + void Bind() const; + void Delete(); + bool IsCreated() const; }; \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/GLGSRender.cpp b/rpcs3/Emu/GS/GL/GLGSRender.cpp index e9e0ddce64..51a42300a0 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.cpp +++ b/rpcs3/Emu/GS/GL/GLGSRender.cpp @@ -1,8 +1,8 @@ #include "stdafx.h" #include "GLGSRender.h" -#include "ProgramBuffer.h" #define CMD_DEBUG 0 +#define DUMP_VERTEX_DATA 1 #if CMD_DEBUG #define CMD_LOG ConLog.Write @@ -12,19 +12,6 @@ gcmBuffer gcmBuffers[2]; -static VBO m_vbo; -static u32 m_vao_id; - -static ShaderProgram m_shader_prog; -static VertexData m_vertex_data[16]; -static Array m_vdata; -static VertexProgram m_vertex_progs[16]; -static VertexProgram* m_cur_vertex_prog; -static Program m_program; -static int m_fp_buf_num = -1; -static int m_vp_buf_num = -1; -static ProgramBuffer m_prog_buffer; - void checkForGlError(const char* situation) { GLenum err = glGetError(); @@ -35,6 +22,10 @@ void checkForGlError(const char* situation) } } +#if 0 + #define checkForGlError(x) /*x*/ +#endif + GLGSFrame::GLGSFrame() : GSFrame(NULL, "GSFrame[OpenGL]") , m_frames(0) @@ -42,12 +33,7 @@ GLGSFrame::GLGSFrame() canvas = new wxGLCanvas(this, wxID_ANY, NULL); canvas->SetSize(GetClientSize()); - Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(GLGSFrame::OnLeftDclick)); - - //Flip(); - - //Connect(canvas->GetId(), wxEVT_LEFT_DCLICK, wxMouseEventHandler(GLGSFrame::OnLeftDclick)); - //Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(GLGSFrame::OnLeftDclick)); + canvas->Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(GSFrame::OnLeftDclick), (wxObject*)0, this); } void GLGSFrame::Flip() @@ -75,6 +61,7 @@ void GLGSFrame::OnSize(wxSizeEvent& event) void GLGSFrame::SetViewport(int x, int y, u32 w, u32 h) { + /* //ConLog.Warning("SetViewport(x=%d, y=%d, w=%d, h=%d)", x, y, w, h); const wxSize client = GetClientSize(); @@ -84,15 +71,17 @@ void GLGSFrame::SetViewport(int x, int y, u32 w, u32 h) const int vy = (client.GetY() - viewport.GetY()) / 2; glViewport(vx + x, vy + y, viewport.GetWidth(), viewport.GetHeight()); + */ } GLGSRender::GLGSRender() : m_frame(NULL) - , m_draw(false) , m_rsx_thread(NULL) + , m_fp_buf_num(-1) + , m_vp_buf_num(-1) { + m_draw = false; m_frame = new GLGSFrame(); - m_vao_id = 0; } GLGSRender::~GLGSRender() @@ -103,40 +92,25 @@ GLGSRender::~GLGSRender() void GLGSRender::Enable(bool enable, const u32 cap) { - if(enable) glEnable(cap); - else glDisable(cap); + if(enable) + { + glEnable(cap); + } + else + { + glDisable(cap); + } } GLRSXThread::GLRSXThread(wxWindow* parent) - : wxThread(wxTHREAD_JOINABLE) + : ThreadBase(true, "OpenGL Thread") , m_parent(parent) - , m_paused(false) { } -void GLRSXThread::OnExit() -{ - call_stack.Clear(); - m_paused = true; -} - -void GLRSXThread::Start() -{ - Create(); - Run(); -} - extern CellGcmContextData current_context; -enum Method -{ - CELL_GCM_METHOD_FLAG_NON_INCREMENT = 0x40000000, - CELL_GCM_METHOD_FLAG_JUMP = 0x20000000, - CELL_GCM_METHOD_FLAG_CALL = 0x00000002, - CELL_GCM_METHOD_FLAG_RETURN = 0x00020000, -}; - -wxThread::ExitCode GLRSXThread::Entry() +void GLRSXThread::Task() { ConLog.Write("GL RSX thread entry"); @@ -146,19 +120,50 @@ wxThread::ExitCode GLRSXThread::Entry() InitProcTable(); glEnable(GL_TEXTURE_2D); + glSwapInterval(Ini.GSVSyncEnable.GetValue() ? 1 : 0); - m_vbo.Create(); - if(!m_vao_id) glGenVertexArrays(1, &m_vao_id); + bool draw = true; + u32 drawed = 0; + u32 skipped = 0; + + p.Init(); while(!TestDestroy() && p.m_frame && !p.m_frame->IsBeingDeleted()) { - if(m_paused || p.m_ctrl->get == p.m_ctrl->put || !Emu.IsRunned()) + wxCriticalSectionLocker lock(p.m_cs_main); + + if(p.m_ctrl->get == p.m_ctrl->put || !Emu.IsRunned()) { + SemaphorePostAndWait(p.m_sem_flush); + if(p.m_draw) { p.m_draw = false; - p.m_frame->Flip(); + + if(p.m_skip_frames) + { + if(!draw) + { + if(skipped++ >= p.m_skip_frames) + { + skipped = 0; + draw = true; + } + } + else + { + if(drawed++ >= p.m_draw_frames) + { + drawed = 0; + draw = false; + } + } + } + + if(draw) p.m_frame->Flip(); + p.m_flip_status = 0; + if(SemaphorePostAndWait(p.m_sem_flip)) continue; } Sleep(1); @@ -171,21 +176,24 @@ wxThread::ExitCode GLRSXThread::Entry() if(cmd & CELL_GCM_METHOD_FLAG_JUMP) { - p.m_ctrl->get = re32(cmd & ~(CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT)); - ConLog.Warning("rsx jump!"); + u32 addr = cmd & ~(CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT); + p.m_ctrl->get = re32(addr); + ConLog.Warning("rsx jump(0x%x)", addr); continue; } if(cmd & CELL_GCM_METHOD_FLAG_CALL) { call_stack.Push(get + 4); - p.m_ctrl->get = re32(cmd & ~CELL_GCM_METHOD_FLAG_CALL); - ConLog.Warning("rsx call!"); + u32 addr = cmd & ~CELL_GCM_METHOD_FLAG_CALL; + p.m_ctrl->get = re32(addr); + ConLog.Warning("rsx call(0x%x)", addr); continue; } if(cmd & CELL_GCM_METHOD_FLAG_RETURN) { - p.m_ctrl->get = re32(call_stack.Pop()); - ConLog.Warning("rsx return!"); + u32 addr = call_stack.Pop(); + p.m_ctrl->get = re32(addr); + ConLog.Warning("rsx return(0x%x)", addr); continue; } if(cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT) @@ -200,40 +208,37 @@ wxThread::ExitCode GLRSXThread::Entry() continue; } - p.DoCmd(cmd, cmd & 0x3ffff, mem32_t(p.m_ioAddress + get + 4), count); - p.m_ctrl->get = re32(get + (count + 1) * 4); - memset(Memory.GetMemFromAddr(p.m_ioAddress + get), 0, (count + 1) * 4); + mem32_t args(p.m_ioAddress + get + 4); + + if(!draw) + { + if((cmd & 0x3ffff) == NV406E_SET_REFERENCE) + { + p.m_ctrl->ref = re32(args[0]); + } + } + else + { + p.DoCmd(cmd, cmd & 0x3ffff, args, count); + } + + re(p.m_ctrl->get, get + (count + 1) * 4); + //memset(Memory.GetMemFromAddr(p.m_ioAddress + get), 0, (count + 1) * 4); } ConLog.Write("GL RSX thread exit..."); call_stack.Clear(); - m_vbo.Delete(); - m_cur_vertex_prog = 0; - m_program.Delete(); - m_shader_prog.Delete(); - - for(u32 i=0; i<16; ++i) m_vertex_progs[i].Delete(); - - m_prog_buffer.Clear(); - - return (ExitCode)0; -} - -void GLGSRender::Pause() -{ - if(m_rsx_thread) m_rsx_thread->m_paused = true; -} - -void GLGSRender::Resume() -{ - if(m_rsx_thread) m_rsx_thread->m_paused = false; + p.CloseOpenGL(); } void GLGSRender::Init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddress, const u32 localAddress) { if(m_frame->IsShown()) return; + m_draw_frames = 1; + m_skip_frames = 0; + m_frame->Show(); m_ioAddress = ioAddress; @@ -253,13 +258,17 @@ void GLGSRender::Draw() void GLGSRender::Close() { - if(m_rsx_thread) m_rsx_thread->Delete(); + if(m_rsx_thread) + { + m_rsx_thread->Stop(); + delete m_rsx_thread; + } if(m_frame->IsShown()) m_frame->Hide(); m_ctrl = NULL; } -void EnableVertexData() +void GLGSRender::EnableVertexData(bool indexed_draw) { Array offset_list; u32 cur_offset = 0; @@ -276,90 +285,80 @@ void EnableVertexData() memcpy(&m_vdata[pos], &m_vertex_data[i].data[0], m_vertex_data[i].data.GetCount()); } - glBindVertexArray(m_vao_id); - checkForGlError("glBindVertexArray"); - m_vbo.SetData(&m_vdata[0], m_vdata.GetCount()); - checkForGlError("m_vbo.SetData"); + m_vao.Create(); + m_vao.Bind(); + checkForGlError("initializing vao"); -#if CMD_DEBUG + m_vbo.Create(indexed_draw ? 2 : 1); + m_vbo.Bind(0); + m_vbo.SetData(&m_vdata[0], m_vdata.GetCount()); + + if(indexed_draw) + { + m_vbo.Bind(GL_ELEMENT_ARRAY_BUFFER, 1); + m_vbo.SetData(GL_ELEMENT_ARRAY_BUFFER, &m_indexed_array.m_data[0], m_indexed_array.m_data.GetCount()); + } + + checkForGlError("initializing vbo"); + +#if DUMP_VERTEX_DATA wxFile dump("VertexDataArray.dump", wxFile::write); #endif for(u32 i=0; i<16; ++i) { if(!m_vertex_data[i].IsEnabled()) continue; -#if CMD_DEBUG - dump.Write(wxString::Format("VertexData[%d]:\n", i)); -#endif - u32 gltype; - bool normalized = false; +#if DUMP_VERTEX_DATA + dump.Write(wxString::Format("VertexData[%d]:\n", i)); switch(m_vertex_data[i].type) { case 1: - gltype = GL_SHORT; normalized = true; -#if CMD_DEBUG for(u32 j = 0; j= 1 && m_vertex_data[i].type <= 7) + { + u32 gltype = gl_types[m_vertex_data[i].type - 1]; + bool normalized = gl_normalized[m_vertex_data[i].type - 1]; + + glEnableVertexAttribArray(i); + checkForGlError("glEnableVertexAttribArray"); + glVertexAttribPointer(i, m_vertex_data[i].size, gltype, normalized, 0, (void*)offset_list[i]); + checkForGlError("glVertexAttribPointer"); + } } } -void DisableVertexData() +void GLGSRender::DisableVertexData() { m_vdata.Clear(); for(u32 i=0; i<16; ++i) @@ -388,7 +413,7 @@ void DisableVertexData() } } -void LoadVertexData(u32 first, u32 count) +void GLGSRender::LoadVertexData(u32 first, u32 count) { for(u32 i=0; i<16; ++i) { @@ -398,13 +423,14 @@ void LoadVertexData(u32 first, u32 count) } } -void InitVertexData() +void GLGSRender::InitVertexData() { for(u32 i=0; iconstants4.GetCount(); ++i) { const VertexProgram::Constant4& c = m_cur_vertex_prog->constants4[i]; const wxString& name = wxString::Format("vc%d", c.id); - const int l = glGetUniformLocation(m_program.id, name); + //const int l = glGetUniformLocation(m_program.id, name); + const int l = m_program.GetLocation(name); checkForGlError("glGetUniformLocation " + name); //ConLog.Write(name + " x: %.02f y: %.02f z: %.02f w: %.02f", c.x, c.y, c.z, c.w); @@ -442,10 +468,11 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c ConLog.Write(debug); #endif - static int draw_mode = 0; + //static int draw_mode = 0; + static u32 semaphore_offset = 0; u32 index = 0; - static u32 draw_array_count = 0; + //static u32 draw_array_count = 0; switch(cmd) { @@ -465,8 +492,8 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c const u8 dimension = (args[1] >> 4) & 0xf; const u8 format = (args[1] >> 8) & 0xff; const u16 mipmap = (args[1] >> 16) & 0xffff; - CMD_LOG("offset=0x%x, location=0x%x, cubemap=0x%x, dimension=0x%x, format=0x%x, mipmap=0x%x", - offset, location, cubemap, dimension, format, mipmap); + CMD_LOG("index = %d, offset=0x%x, location=0x%x, cubemap=0x%x, dimension=0x%x, format=0x%x, mipmap=0x%x", + index, offset, location, cubemap, dimension, format, mipmap); tex.SetOffset(GetAddress(offset, location)); tex.SetFormat(cubemap, dimension, format, mipmap); @@ -480,6 +507,23 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } break; + case_16(NV4097_SET_VERTEX_DATA4UB_M, 4): + { + u32 v = args[0]; + u8 v0 = v; + u8 v1 = v >> 8; + u8 v2 = v >> 16; + u8 v3 = v >> 24; + + m_vertex_data[index].type = 7; + m_vertex_data[index].data.AddCpy(v0); + m_vertex_data[index].data.AddCpy(v1); + m_vertex_data[index].data.AddCpy(v2); + m_vertex_data[index].data.AddCpy(v3); + ConLog.Warning("index = %d, v0 = 0x%x, v1 = 0x%x, v2 = 0x%x, v3 = 0x%x", index, v0, v1, v2, v3); + } + break; + case_16(NV4097_SET_TEXTURE_CONTROL1, 0x20): //TODO break; @@ -530,44 +574,62 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } break; - case NV4097_SET_COLOR_MASK: - { - const u32 flags = args[0]; - - glColorMask( - flags & 0x0010000 ? GL_TRUE : GL_FALSE, - flags & 0x0000100 ? GL_TRUE : GL_FALSE, - flags & 0x0000001 ? GL_TRUE : GL_FALSE, - flags & 0x1000000 ? GL_TRUE : GL_FALSE); - } - break; - case NV4097_SET_COLOR_MASK_MRT: { } break; + case NV4097_SET_COLOR_MASK: + { + const u32 flags = args[0]; + + m_set_color_mask = true; + m_color_mask_a = flags & 0x1000000 ? true : false; + m_color_mask_r = flags & 0x0010000 ? true : false; + m_color_mask_g = flags & 0x0000100 ? true : false; + m_color_mask_b = flags & 0x0000001 ? true : false; + } + break; + case NV4097_SET_ALPHA_TEST_ENABLE: - Enable(args[0] ? true : false, GL_ALPHA_TEST); + m_set_alpha_test = args[0] ? true : false; + //Enable(args[0] ? true : false, GL_ALPHA_TEST); break; case NV4097_SET_BLEND_ENABLE: - Enable(args[0] ? true : false, GL_BLEND); + m_set_blend = args[0] ? true : false; + //Enable(args[0] ? true : false, GL_BLEND); break; case NV4097_SET_DEPTH_BOUNDS_TEST_ENABLE: - Enable(args[0] ? true : false, GL_DEPTH_CLAMP); + m_set_depth_bounds_test = args[0] ? true : false; + //Enable(args[0] ? true : false, GL_DEPTH_CLAMP); + break; + + case NV4097_SET_VIEWPORT_VERTICAL: + { + m_set_viewport_vertical = true; + m_viewport_y = args[0] & 0xffff; + m_viewport_h = args[0] >> 16; + } break; case NV4097_SET_VIEWPORT_HORIZONTAL: { - const u16 x = args[0] & 0xffff; - const u16 w = args[0] >> 16; - const u16 y = args[1] & 0xffff; - const u16 h = args[1] >> 16; - CMD_LOG("x=%d, y=%d, w=%d, h=%d", x, y, w, h); + m_set_viewport_horizontal = true; + m_viewport_x = args[0] & 0xffff; + m_viewport_w = args[0] >> 16; - m_frame->SetViewport(x, y, w, h); + if(count == 2) + { + m_set_viewport_vertical = true; + m_viewport_y = args[1] & 0xffff; + m_viewport_h = args[1] >> 16; + } + + CMD_LOG("x=%d, y=%d, w=%d, h=%d", m_viewport_x, m_viewport_y, m_viewport_w, m_viewport_h); + + //m_frame->SetViewport(m_viewport_x, m_viewport_y, m_viewport_w, m_viewport_h); //glViewport(x, y, w, h); } break; @@ -577,18 +639,25 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c const u32 clip_min = args[0]; const u32 clip_max = args[1]; - CMD_LOG("clip_min=%.01f, clip_max=%.01f", *(float*)&clip_min, *(float*)&clip_max); + m_set_clip = true; + m_clip_min = (float&)clip_min; + m_clip_max = (float&)clip_max; - glDepthRangef(*(float*)&clip_min, *(float*)&clip_max); + CMD_LOG("clip_min=%.01f, clip_max=%.01f", m_clip_min, m_clip_max); + + //glDepthRangef(m_clip_min, m_clip_max); } break; case NV4097_SET_DEPTH_FUNC: - glDepthFunc(args[0]); + m_set_depth_func = true; + m_depth_func = args[0]; + //glDepthFunc(m_depth_func); break; case NV4097_SET_DEPTH_TEST_ENABLE: - Enable(args[0] ? true : false, GL_DEPTH_TEST); + m_depth_test_enable = args[0] ? true : false; + //Enable(args[0] ? true : false, GL_DEPTH_TEST); break; case NV4097_SET_FRONT_POLYGON_MODE: @@ -656,130 +725,74 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c LoadVertexData(first, _count); - draw_array_count += _count; + m_draw_array_count += _count; + } + } + break; + + case NV4097_SET_INDEX_ARRAY_ADDRESS: + { + m_indexed_array.m_addr = GetAddress(args[0], args[1] & 0xf); + m_indexed_array.m_type = args[1] >> 4; + } + break; + + case NV4097_DRAW_INDEX_ARRAY: + { + for(u32 c=0; c> 24) + 1; + + if(first < m_indexed_array.m_first) m_indexed_array.m_first = first; + + for(u32 i=first; i<_count; ++i) + { + u32 index; + switch(m_indexed_array.m_type) + { + case 0: + { + int pos = m_indexed_array.m_data.GetCount(); + m_indexed_array.m_data.InsertRoomEnd(4); + index = Memory.Read32(m_indexed_array.m_addr + i * 4); + *(u32*)&m_indexed_array.m_data[pos] = index; + ConLog.Warning("index 4: %d", *(u32*)&m_indexed_array.m_data[pos]); + } + break; + + case 1: + { + int pos = m_indexed_array.m_data.GetCount(); + m_indexed_array.m_data.InsertRoomEnd(2); + index = Memory.Read16(m_indexed_array.m_addr + i * 2); + ConLog.Warning("index 2: %d", index); + *(u32*)&m_indexed_array.m_data[pos] = index; + } + break; + } + + if(index < m_indexed_array.index_min) m_indexed_array.index_min = index; + if(index > m_indexed_array.index_max) m_indexed_array.index_max = index; + } + + m_indexed_array.m_count += _count; } } break; case NV4097_SET_BEGIN_END: { - if(args[0]) //begin + if(args[0]) { - draw_mode = args[0] - 1; - break; + //begin + m_draw_mode = args[0] - 1; } - - //end - - if(!m_cur_vertex_prog) + else { - ConLog.Warning("NV4097_SET_BEGIN_END: m_cur_vertex_prog == NULL"); - - memset(m_vertex_data, 0, sizeof(VertexData) * 16); - - draw_array_count = 0; - m_vbo.UnBind(); - break; + //end + ExecCMD(); } - - if(!m_program.IsCreated()) - { - //ConLog.Write("Create program"); - m_vp_buf_num = m_prog_buffer.SearchVp(*m_cur_vertex_prog); - - if(m_vp_buf_num == -1) - { - ConLog.Warning("VP not found in buffer!"); - m_cur_vertex_prog->Decompile(); - } - - if(m_fp_buf_num == -1) - { - ConLog.Warning("FP not found in buffer!"); - m_shader_prog.Wait(); - m_shader_prog.Compile(); - - wxFile f(wxGetCwd() + "/FragmentProgram.txt", wxFile::write); - f.Write(m_shader_prog.shader); - } - - if(m_vp_buf_num == -1) - { - m_cur_vertex_prog->Wait(); - m_cur_vertex_prog->Compile(); - - wxFile f(wxGetCwd() + "/VertexProgram.txt", wxFile::write); - f.Write(m_cur_vertex_prog->shader); - } - - if(m_fp_buf_num != -1 && m_vp_buf_num != -1) - { - m_program.id = m_prog_buffer.GetProg(m_fp_buf_num, m_vp_buf_num); - } - - if(!m_program.id) - { - m_program.Create(m_cur_vertex_prog->id, m_shader_prog.id); - m_prog_buffer.Add(m_program, m_shader_prog, *m_cur_vertex_prog); - - m_program.Use(); - - GLint r = GL_FALSE; - glGetProgramiv(m_program.id, GL_VALIDATE_STATUS, &r); - if(r != GL_TRUE) - { - glGetProgramiv(m_program.id, GL_INFO_LOG_LENGTH, &r); - - if(r) - { - char* buf = new char[r+1]; - GLsizei len; - memset(buf, 0, r+1); - glGetProgramInfoLog(m_program.id, r, &len, buf); - ConLog.Error("Failed to validate program: %s", buf); - free(buf); - } - - Emu.Pause(); - } - } - } - else m_program.Use(); - - for(u32 i=0; i<16; ++i) - { - GLTexture& tex = m_frame->GetTexture(i); - if(!tex.IsEnabled()) continue; - - glActiveTexture(GL_TEXTURE0_ARB + i); - checkForGlError("glActiveTexture"); - tex.Bind(); - checkForGlError("tex.Bind"); - m_program.SetTex(i); - checkForGlError("m_program.SetTex"); - tex.Init(); - checkForGlError("tex.Init"); - //tex.Save(); - } - - if(draw_array_count) - { - EnableVertexData(); - InitVertexData(); - glDrawArrays(draw_mode, 0, draw_array_count); - checkForGlError("glDrawArrays"); - - DisableVertexData(); - draw_array_count = 0; - } - - m_program.id = 0; - m_shader_prog.id = 0; - m_cur_vertex_prog->id = 0; - - memset(m_vertex_data, 0, sizeof(VertexData) * 16); - - m_vbo.UnBind(); } break; @@ -802,11 +815,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c { m_shader_prog.Delete(); m_shader_prog.addr = GetAddress(args[0] & ~0x3, (args[0] & 0x3) - 1); - m_program.Delete(); - - m_fp_buf_num = m_prog_buffer.SearchFp(m_shader_prog); - - if(m_fp_buf_num == -1) m_shader_prog.Decompile(); + //m_program.Delete(); } break; @@ -829,7 +838,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case NV4097_SET_TRANSFORM_PROGRAM_LOAD: { - m_program.Delete(); + //m_program.Delete(); m_cur_vertex_prog = &m_vertex_progs[args[0]]; m_cur_vertex_prog->Delete(); @@ -893,10 +902,10 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c VertexProgram::Constant4 c; c.id = id; - c.x = *(float*)&x; - c.y = *(float*)&y; - c.z = *(float*)&z; - c.w = *(float*)&w; + c.x = (float&)x; + c.y = (float&)y; + c.z = (float&)z; + c.w = (float&)w; CMD_LOG("SET_TRANSFORM_CONSTANT_LOAD[%d : %d] = (%f, %f, %f, %f)", i, id, c.x, c.y, c.z, c.w); m_cur_vertex_prog->constants4.AddCpy(c); @@ -969,16 +978,30 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c glDepthMask(args[0]); break; + case NV4097_SET_SCISSOR_VERTICAL: + { + m_set_scissor_vertical = true; + m_scissor_y = args[0] & 0xffff; + m_scissor_h = args[0] >> 16; + } + break; + case NV4097_SET_SCISSOR_HORIZONTAL: { - const u16 x = args[0] & 0xffff; - const u16 w = args[0] >> 16; - const u16 y = args[1] & 0xffff; - const u16 h = args[1] >> 16; - - CMD_LOG("x=%d, y=%d, w=%d, h=%d", x, y, w, h); + m_set_scissor_horizontal = true; + m_scissor_x = args[0] & 0xffff; + m_scissor_w = args[0] >> 16; - glScissor(x, y, w, h); + if(count == 2) + { + m_set_scissor_vertical = true; + m_scissor_y = args[1] & 0xffff; + m_scissor_h = args[1] >> 16; + } + + CMD_LOG("x=%d, y=%d, w=%d, h=%d", m_scissor_x, m_scissor_y, m_scissor_w, m_scissor_h); + + //glScissor(m_scissor_x, m_scissor_y, m_scissor_w, m_scissor_h); } break; @@ -990,18 +1013,36 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } break; - case NV406E_SEMAPHORE_OFFSET: + case NV4097_BACK_END_WRITE_SEMAPHORE_RELEASE: { //TODO } break; + case NV4097_SET_SEMAPHORE_OFFSET: + case NV406E_SEMAPHORE_OFFSET: + { + semaphore_offset = args[0]; + } + break; + case NV406E_SEMAPHORE_ACQUIRE: { //TODO } break; + case NV4097_SET_RESTART_INDEX: + { + //TODO + } + break; + + case NV4097_INVALIDATE_L2: + { + //TODO + } + break; case NV4097_SET_CONTEXT_DMA_COLOR_A: { //TODO @@ -1074,10 +1115,21 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } break; + case NV4097_SET_ANTI_ALIASING_CONTROL: + { + //TODO + } + break; + + case NV4097_SET_ZSTENCIL_CLEAR_VALUE: + case NV4097_SET_ZCULL_CONTROL0: + case NV4097_SET_ZCULL_CONTROL1: + case NV4097_SET_SCULL_CONTROL: + break; default: { - wxString log = getMethodName(cmd); + wxString log = GetMethodName(cmd); log += "("; for(u32 i=0; iDecompile(); + } + + if(m_fp_buf_num == -1) + { + ConLog.Warning("FP not found in buffer!"); + m_shader_prog.Wait(); + m_shader_prog.Compile(); + + wxFile f(wxGetCwd() + "/FragmentProgram.txt", wxFile::write); + f.Write(m_shader_prog.shader); + } + + if(m_vp_buf_num == -1) + { + m_cur_vertex_prog->Wait(); + m_cur_vertex_prog->Compile(); + + wxFile f(wxGetCwd() + "/VertexProgram.txt", wxFile::write); + f.Write(m_cur_vertex_prog->shader); + } + + if(m_fp_buf_num != -1 && m_vp_buf_num != -1) + { + m_program.id = m_prog_buffer.GetProg(m_fp_buf_num, m_vp_buf_num); + } + + if(!m_program.id) + { + m_program.Create(m_cur_vertex_prog->id, m_shader_prog.id); + m_prog_buffer.Add(m_program, m_shader_prog, *m_cur_vertex_prog); + + m_program.Use(); + + GLint r = GL_FALSE; + glGetProgramiv(m_program.id, GL_VALIDATE_STATUS, &r); + if(r != GL_TRUE) + { + glGetProgramiv(m_program.id, GL_INFO_LOG_LENGTH, &r); + + if(r) + { + char* buf = new char[r+1]; + GLsizei len; + memset(buf, 0, r+1); + glGetProgramInfoLog(m_program.id, r, &len, buf); + ConLog.Error("Failed to validate program: %s", buf); + delete[] buf; + } + + Emu.Pause(); + } + } + + return true; +} + +void GLGSRender::ExecCMD() +{ + if(LoadProgram()) + { + if(m_set_color_mask) + { + glColorMask(m_color_mask_r, m_color_mask_g, m_color_mask_b, m_color_mask_a); + } + + if(m_set_viewport_horizontal && m_set_viewport_vertical) + { + glViewport(m_viewport_x, m_viewport_y, m_viewport_w, m_viewport_h); + if(m_frame->GetSize() != wxSize(m_viewport_w, m_viewport_h)) + m_frame->SetClientSize(m_viewport_w, m_viewport_h); + //m_frame->SetViewport(m_viewport_x, m_viewport_y, m_viewport_w, m_viewport_h); + } + + if(m_set_scissor_horizontal && m_set_scissor_vertical) + { + glScissor(m_scissor_x, m_scissor_y, m_scissor_w, m_scissor_h); + } + + Enable(m_depth_test_enable, GL_DEPTH_TEST); + Enable(m_set_alpha_test, GL_ALPHA_TEST); + Enable(m_set_depth_bounds_test, GL_DEPTH_CLAMP); + Enable(m_set_blend, GL_BLEND); + + if(m_set_depth_func) + { + glDepthFunc(m_depth_func); + } + + if(m_set_clip) + { + glDepthRangef(m_clip_min, m_clip_max); + } + + for(u32 i=0; i<16; ++i) + { + GLTexture& tex = m_frame->GetTexture(i); + if(!tex.IsEnabled()) continue; + + glActiveTexture(GL_TEXTURE0_ARB + i); + checkForGlError("glActiveTexture"); + tex.Bind(); + checkForGlError("tex.Bind"); + m_program.SetTex(i); + checkForGlError("m_program.SetTex"); + tex.Init(); + checkForGlError("tex.Init"); + tex.Save(); + } + + if(m_indexed_array.m_count && m_draw_array_count) + { + ConLog.Warning("m_indexed_array.m_count && draw_array_count"); + } + + if(m_indexed_array.m_count) + { + LoadVertexData(m_indexed_array.index_min, m_indexed_array.index_max - m_indexed_array.index_min + 1); + EnableVertexData(true); + InitVertexData(); + + wxFile log("IndexedDrawLog.txt", wxFile::write); + log.Write(wxString::Format("Draw mode: %d\n", m_draw_mode)); + + m_vao.Bind(); + switch(m_indexed_array.m_type) + { + case 0: + glDrawElements(m_draw_mode, m_indexed_array.m_count, GL_UNSIGNED_INT, nullptr); + checkForGlError("glDrawElements #4"); + for(uint i=0; iid = 0; + + memset(m_vertex_data, 0, sizeof(VertexData) * 16); + + if(m_vbo.IsCreated()) + { + m_vbo.UnBind(); + m_vbo.Delete(); + } + + m_vao.Delete(); + + Init(); +} + +void GLGSRender::Init() +{ + m_draw_array_count = 0; + m_draw_mode = 0; + ExecRSXCMDdata::Reset(); +} + +void GLGSRender::CloseOpenGL() +{ + Reset(); } \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/GLGSRender.h b/rpcs3/Emu/GS/GL/GLGSRender.h index 6153b2ea91..e8aef91f04 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.h +++ b/rpcs3/Emu/GS/GL/GLGSRender.h @@ -1,9 +1,11 @@ #pragma once #include "Emu/GS/GSRender.h" +#include "Emu/GS/RSXThread.h" #include "wx/glcanvas.h" #include "GLBuffers.h" #include "Program.h" #include "OpenGL.h" +#include "ProgramBuffer.h" #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "gl.lib") @@ -66,9 +68,6 @@ public: void Init() { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - if(!m_id) { glGenTextures(1, &m_id); @@ -87,9 +86,9 @@ public: //TODO: safe init checkForGlError("GLTexture::Init() -> glBindTexture"); - switch(m_format) + switch(m_format & ~(0x20 | 0x40)) { - case 0xA1: + case 0x81: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RED, GL_UNSIGNED_BYTE, Memory.GetMemFromAddr(m_offset)); checkForGlError("GLTexture::Init() -> glTexImage2D"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); @@ -99,7 +98,7 @@ public: checkForGlError("GLTexture::Init() -> glTexParameteri"); break; - case 0xA5: + case 0x85: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, Memory.GetMemFromAddr(m_offset)); checkForGlError("GLTexture::Init() -> glTexImage2D"); break; @@ -114,42 +113,32 @@ public: { if(!m_id || !m_offset) return; + ConLog.Write("start"); + u32* alldata = new u32[m_width * m_height]; + + glBindTexture(GL_TEXTURE_2D, m_id); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, alldata); + u8* data = new u8[m_width * m_height * 3]; u8* alpha = new u8[m_width * m_height]; - u8* src = Memory.GetMemFromAddr(m_offset); - u8* dst = data; + + u8* src = (u8*)alldata; + u8* dst_d = data; u8* dst_a = alpha; - - switch(m_format) + for(u32 i=0; i m_data; + int m_type; + u32 m_first; + u32 m_count; + u32 m_addr; + u32 index_max; + u32 index_min; + + IndexArrayData() + { + Reset(); + } + + void Reset() + { + m_type = 0; + m_first = ~0; + m_count = 0; + m_addr = 0; + index_min = ~0; + index_max = 0; + m_data.Clear(); + } +}; + struct GLGSFrame : public GSFrame { wxGLCanvas* canvas; @@ -197,42 +213,66 @@ private: extern gcmBuffer gcmBuffers[2]; -struct GLRSXThread : public wxThread +struct GLRSXThread : public ThreadBase { wxWindow* m_parent; Stack call_stack; - volatile bool m_paused; - GLRSXThread(wxWindow* parent); - virtual void OnExit(); - void Start(); - ExitCode Entry(); + virtual void Task(); }; class GLGSRender : public wxWindow , public GSRender + , public ExecRSXCMDdata { private: GLRSXThread* m_rsx_thread; + IndexArrayData m_indexed_array; + + ShaderProgram m_shader_prog; + VertexData m_vertex_data[16]; + Array m_vdata; + VertexProgram m_vertex_progs[16]; + VertexProgram* m_cur_vertex_prog; + Program m_program; + int m_fp_buf_num; + int m_vp_buf_num; + int m_draw_array_count; + int m_draw_mode; + ProgramBuffer m_prog_buffer; + + GLvao m_vao; + GLvbo m_vbo; + public: GLGSFrame* m_frame; - volatile bool m_draw; + u32 m_draw_frames; + u32 m_skip_frames; GLGSRender(); ~GLGSRender(); private: + void EnableVertexData(bool indexed_draw=false); + void DisableVertexData(); + void LoadVertexData(u32 first, u32 count); + void InitVertexData(); + void Enable(bool enable, const u32 cap); virtual void Init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddress, const u32 localAddress); virtual void Draw(); virtual void Close(); - virtual void Pause(); - virtual void Resume(); + bool LoadProgram(); public: void DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 count); + void CloseOpenGL(); + + virtual void ExecCMD(); + virtual void Reset(); + void Init(); }; \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/GLProcTable.tbl b/rpcs3/Emu/GS/GL/GLProcTable.tbl index 3d218ee8d9..2ebe4199e7 100644 --- a/rpcs3/Emu/GS/GL/GLProcTable.tbl +++ b/rpcs3/Emu/GS/GL/GLProcTable.tbl @@ -1,42 +1,45 @@ - -OPENGL_PROC(PFNGLGENBUFFERSPROC,glGenBuffers); -OPENGL_PROC(PFNGLDELETEBUFFERSPROC, glDeleteBuffers); -OPENGL_PROC(PFNGLBINDBUFFERPROC, glBindBuffer); -OPENGL_PROC(PFNGLISBUFFERPROC, glIsBuffer); -OPENGL_PROC(PFNGLBUFFERDATAPROC, glBufferData); -OPENGL_PROC(PFNGLBUFFERSUBDATAPROC, glBufferSubData); -OPENGL_PROC(PFNGLGETBUFFERSUBDATAPROC, glGetBufferSubData); -OPENGL_PROC(PFNGLMAPBUFFERPROC, glMapBuffer); -OPENGL_PROC(PFNGLUNMAPBUFFERPROC, glUnmapBuffer); -OPENGL_PROC(PFNGLGETBUFFERPARAMETERIVPROC, glGetBufferParameteriv); -OPENGL_PROC(PFNGLGETBUFFERPOINTERVPROC, glGetBufferPointerv); -OPENGL_PROC(PFNGLBLENDFUNCSEPARATEPROC, glBlendFuncSeparate); -OPENGL_PROC(PFNGLBLENDEQUATIONSEPARATEPROC, glBlendEquationSeparate); -OPENGL_PROC(PFNGLBLENDCOLORPROC, glBlendColor); -OPENGL_PROC(PFNGLBLENDEQUATIONPROC, glBlendEquation); -OPENGL_PROC(PFNGLCREATESHADERPROC, glCreateShader); -OPENGL_PROC(PFNGLDELETESHADERPROC, glDeleteShader); -OPENGL_PROC(PFNGLCOMPILESHADERPROC, glCompileShader); -OPENGL_PROC(PFNGLSHADERSOURCEPROC, glShaderSource); -OPENGL_PROC(PFNGLGETSHADERIVPROC, glGetShaderiv); -OPENGL_PROC(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog); -OPENGL_PROC(PFNGLACTIVETEXTUREPROC, glActiveTexture); -OPENGL_PROC(PFNGLCREATEPROGRAMPROC, glCreateProgram); -OPENGL_PROC(PFNGLDELETEPROGRAMPROC, glDeleteProgram); -OPENGL_PROC(PFNGLATTACHSHADERPROC, glAttachShader); -OPENGL_PROC(PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation); -OPENGL_PROC(PFNGLLINKPROGRAMPROC, glLinkProgram); -OPENGL_PROC(PFNGLBINDFRAGDATALOCATIONPROC, glBindFragDataLocation); -OPENGL_PROC(PFNGLBINDATTRIBLOCATIONPROC, glBindAttribLocation); -OPENGL_PROC(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation); -OPENGL_PROC(PFNGLGETPROGRAMIVPROC, glGetProgramiv); -OPENGL_PROC(PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog); -OPENGL_PROC(PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer); -OPENGL_PROC(PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray); -OPENGL_PROC(PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray); -OPENGL_PROC(PFNGLGENVERTEXARRAYSPROC, glGenVertexArrays); -OPENGL_PROC(PFNGLBINDVERTEXARRAYPROC, glBindVertexArray); -OPENGL_PROC(PFNGLDEPTHRANGEFPROC, glDepthRangef); -OPENGL_PROC(PFNGLUNIFORM1IPROC, glUniform1i); -OPENGL_PROC(PFNGLUNIFORM4FPROC, glUniform4f); -OPENGL_PROC(PFNGLUSEPROGRAMPROC, glUseProgram); \ No newline at end of file +OPENGL_PROC(PFNGLGENBUFFERSPROC, GenBuffers); +OPENGL_PROC(PFNGLDELETEBUFFERSPROC, DeleteBuffers); +OPENGL_PROC(PFNGLBINDBUFFERPROC, BindBuffer); +OPENGL_PROC(PFNGLISBUFFERPROC, IsBuffer); +OPENGL_PROC(PFNGLBUFFERDATAPROC, BufferData); +OPENGL_PROC(PFNGLBUFFERSUBDATAPROC, BufferSubData); +OPENGL_PROC(PFNGLGETBUFFERSUBDATAPROC, GetBufferSubData); +OPENGL_PROC(PFNGLMAPBUFFERPROC, MapBuffer); +OPENGL_PROC(PFNGLUNMAPBUFFERPROC, UnmapBuffer); +OPENGL_PROC(PFNGLGETBUFFERPARAMETERIVPROC, GetBufferParameteriv); +OPENGL_PROC(PFNGLGETBUFFERPOINTERVPROC, GetBufferPointerv); +OPENGL_PROC(PFNGLBLENDFUNCSEPARATEPROC, BlendFuncSeparate); +OPENGL_PROC(PFNGLBLENDEQUATIONSEPARATEPROC, BlendEquationSeparate); +OPENGL_PROC(PFNGLBLENDCOLORPROC, BlendColor); +OPENGL_PROC(PFNGLBLENDEQUATIONPROC, BlendEquation); +OPENGL_PROC(PFNGLCREATESHADERPROC, CreateShader); +OPENGL_PROC(PFNGLDELETESHADERPROC, DeleteShader); +OPENGL_PROC(PFNGLCOMPILESHADERPROC, CompileShader); +OPENGL_PROC(PFNGLSHADERSOURCEPROC, ShaderSource); +OPENGL_PROC(PFNGLGETSHADERIVPROC, GetShaderiv); +OPENGL_PROC(PFNGLGETSHADERINFOLOGPROC, GetShaderInfoLog); +OPENGL_PROC(PFNGLACTIVETEXTUREPROC, ActiveTexture); +OPENGL_PROC(PFNGLCREATEPROGRAMPROC, CreateProgram); +OPENGL_PROC(PFNGLDELETEPROGRAMPROC, DeleteProgram); +OPENGL_PROC(PFNGLATTACHSHADERPROC, AttachShader); +OPENGL_PROC(PFNGLGETATTRIBLOCATIONPROC, GetAttribLocation); +OPENGL_PROC(PFNGLLINKPROGRAMPROC, LinkProgram); +OPENGL_PROC(PFNGLBINDFRAGDATALOCATIONPROC, BindFragDataLocation); +OPENGL_PROC(PFNGLBINDATTRIBLOCATIONPROC, BindAttribLocation); +OPENGL_PROC(PFNGLGETUNIFORMLOCATIONPROC, GetUniformLocation); +OPENGL_PROC(PFNGLGETPROGRAMIVPROC, GetProgramiv); +OPENGL_PROC(PFNGLGETPROGRAMINFOLOGPROC, GetProgramInfoLog); +OPENGL_PROC(PFNGLVERTEXATTRIBPOINTERPROC, VertexAttribPointer); +OPENGL_PROC(PFNGLENABLEVERTEXATTRIBARRAYPROC, EnableVertexAttribArray); +OPENGL_PROC(PFNGLDISABLEVERTEXATTRIBARRAYPROC, DisableVertexAttribArray); +OPENGL_PROC(PFNGLGENVERTEXARRAYSPROC, GenVertexArrays); +OPENGL_PROC(PFNGLBINDVERTEXARRAYPROC, BindVertexArray); +OPENGL_PROC(PFNGLDELETEVERTEXARRAYSPROC, DeleteVertexArrays); +OPENGL_PROC(PFNGLDEPTHRANGEFPROC, DepthRangef); +OPENGL_PROC(PFNGLUNIFORM1IPROC, Uniform1i); +OPENGL_PROC(PFNGLUNIFORM1FPROC, Uniform1f); +OPENGL_PROC(PFNGLUNIFORM4FPROC, Uniform4f); +OPENGL_PROC(PFNGLUNIFORMMATRIX4FVPROC, UniformMatrix4fv); +OPENGL_PROC(PFNGLUSEPROGRAMPROC, UseProgram); +OPENGL_PROC2(PFNWGLSWAPINTERVALEXTPROC, SwapInterval, wglSwapIntervalEXT); \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/OpenGL.cpp b/rpcs3/Emu/GS/GL/OpenGL.cpp index faab88d627..4ac3fb3951 100644 --- a/rpcs3/Emu/GS/GL/OpenGL.cpp +++ b/rpcs3/Emu/GS/GL/OpenGL.cpp @@ -3,11 +3,44 @@ void InitProcTable() { - #define OPENGL_PROC(p, n) n = (p)wglGetProcAddress(#n) +#define OPENGL_PROC(p, n) OPENGL_PROC2(p, n, gl##n) +#define OPENGL_PROC2(p, n, tn) /*if(!gl##n)*/ if(!(gl##n = (p)wglGetProcAddress(#tn))) ConLog.Error("OpenGL: initialization of " #tn " failed.") #include "GLProcTable.tbl" - #undef OPENGL_PROC +#undef OPENGL_PROC +#undef OPENGL_PROC2 } -#define OPENGL_PROC(p, n) p n = NULL -#include "GLProcTable.tbl" -#undef OPENGL_PROC \ No newline at end of file +#define OPENGL_PROC(p, n) p gl##n = nullptr +#define OPENGL_PROC2(p, n, tn) OPENGL_PROC(p, n) + #include "GLProcTable.tbl" +#undef OPENGL_PROC +#undef OPENGL_PROC2 + +OpenGL::OpenGL() +{ + Close(); + Init(); +} + +OpenGL::~OpenGL() +{ + Close(); +} + +void OpenGL::Init() +{ +#define OPENGL_PROC(p, n) OPENGL_PROC2(p, n, gl##n) +#define OPENGL_PROC2(p, n, tn) if(!(n = (p)wglGetProcAddress(#tn))) ConLog.Error("OpenGL: initialization of " #tn " failed.") + #include "GLProcTable.tbl" +#undef OPENGL_PROC +#undef OPENGL_PROC2 +} + +void OpenGL::Close() +{ +#define OPENGL_PROC(p, n) n = nullptr +#define OPENGL_PROC2(p, n, tn) OPENGL_PROC(p, n) + #include "GLProcTable.tbl" +#undef OPENGL_PROC +#undef OPENGL_PROC2 +} \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/OpenGL.h b/rpcs3/Emu/GS/GL/OpenGL.h index 4c5055ad51..2614038244 100644 --- a/rpcs3/Emu/GS/GL/OpenGL.h +++ b/rpcs3/Emu/GS/GL/OpenGL.h @@ -2,8 +2,27 @@ #include #include "GL/glext.h" -#define OPENGL_PROC(p, n) extern p n -#include "GLProcTable.tbl" -#undef OPENGL_PROC +typedef BOOL (WINAPI* PFNWGLSWAPINTERVALEXTPROC) (int interval); -void InitProcTable(); \ No newline at end of file +#define OPENGL_PROC(p, n) extern p gl##n +#define OPENGL_PROC2(p, n, tn) OPENGL_PROC(p, n) + #include "GLProcTable.tbl" +#undef OPENGL_PROC +#undef OPENGL_PROC2 + +void InitProcTable(); + +struct OpenGL +{ +#define OPENGL_PROC2(p, n, tn) OPENGL_PROC(p, n) +#define OPENGL_PROC(p, n) p n + #include "GLProcTable.tbl" +#undef OPENGL_PROC +#undef OPENGL_PROC2 + + OpenGL(); + ~OpenGL(); + + void Init(); + void Close(); +}; \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/Program.cpp b/rpcs3/Emu/GS/GL/Program.cpp index 6fa6da6228..bc90e28628 100644 --- a/rpcs3/Emu/GS/GL/Program.cpp +++ b/rpcs3/Emu/GS/GL/Program.cpp @@ -6,6 +6,22 @@ Program::Program() : id(0) { } +int Program::GetLocation(const wxString& name) +{ + for(u32 i=0; i 0; @@ -19,23 +35,6 @@ void Program::Create(const u32 vp, const u32 fp) glAttachShader(id, vp); glAttachShader(id, fp); - glBindFragDataLocation(id, 0, "r0"); - static const wxString reg_table[] = - { - "in_pos", "in_weight", "in_normal", - "in_col0", "in_col1", - "in_fogc", - "in_6", "in_7", - "in_tc0", "in_tc1", "in_tc2", "in_tc3", - "in_tc4", "in_tc5", "in_tc6", "in_tc7" - }; - - for(u32 i=0; i m_locations; + +public: u32 id; Program(); + int GetLocation(const wxString& name); bool IsCreated() const; void Create(const u32 vp, const u32 fp); void Use(); diff --git a/rpcs3/Emu/GS/GL/ProgramBuffer.cpp b/rpcs3/Emu/GS/GL/ProgramBuffer.cpp index 90ca17bc49..ee6a292e73 100644 --- a/rpcs3/Emu/GS/GL/ProgramBuffer.cpp +++ b/rpcs3/Emu/GS/GL/ProgramBuffer.cpp @@ -70,11 +70,9 @@ u32 ProgramBuffer::GetProg(u32 fp, u32 vp) const void ProgramBuffer::Add(Program& prog, ShaderProgram& fp, VertexProgram& vp) { - const u32 pos = m_buf.GetCount(); - m_buf.Add(new BufferInfo()); - BufferInfo& new_buf = m_buf[pos]; + BufferInfo& new_buf = *new BufferInfo(); - ConLog.Write("Add program (%d):", pos); + ConLog.Write("Add program (%d):", m_buf.GetCount()); ConLog.Write("*** prog id = %d", prog.id); ConLog.Write("*** vp id = %d", vp.id); ConLog.Write("*** fp id = %d", fp.id); @@ -88,14 +86,13 @@ void ProgramBuffer::Add(Program& prog, ShaderProgram& fp, VertexProgram& vp) new_buf.vp_id = vp.id; new_buf.fp_id = fp.id; - new_buf.fp_data.SetCount(fp.size); - memcpy(&new_buf.fp_data[0], &Memory[fp.addr], fp.size); - - new_buf.vp_data.SetCount(vp.data.GetCount()); - memcpy(&new_buf.vp_data[0], &vp.data[0], vp.data.GetCount() * 4); + new_buf.fp_data.AddCpy(&Memory[fp.addr], fp.size); + new_buf.vp_data.CopyFrom(vp.data); new_buf.vp_shader = vp.shader; new_buf.fp_shader = fp.shader; + + m_buf.Move(&new_buf); } void ProgramBuffer::Clear() diff --git a/rpcs3/Emu/GS/GL/ShaderParam.h b/rpcs3/Emu/GS/GL/ShaderParam.h index c99679a1a2..f90ba43acc 100644 --- a/rpcs3/Emu/GS/GL/ShaderParam.h +++ b/rpcs3/Emu/GS/GL/ShaderParam.h @@ -9,11 +9,23 @@ enum ParamFlag PARAM_NONE, }; +struct ParamItem +{ + wxString name; + wxString location; + + ParamItem(const wxString& _name, int _location) + : name(_name) + , location(_location > -1 ? wxString::Format("layout (location = %d) ", _location) : "") + { + } +}; + struct ParamType { const ParamFlag flag; wxString type; - wxArrayString names; + Array items; ParamType(const ParamFlag _flag, const wxString& _type) : type(_type) @@ -23,13 +35,29 @@ struct ParamType bool SearchName(const wxString& name) { - for(u32 i=0; iSearchName(name)) t->names.Add(name); + if(!t->SearchName(name)) t->items.Move(new ParamItem(name, location)); } else { const u32 num = params.GetCount(); - params.Add(new ParamType(flag, type)); - params[num].names.Add(name); + params.Move(new ParamType(flag, type)); + params[num].items.Move(new ParamItem(name, location)); } return name; diff --git a/rpcs3/Emu/GS/GL/VertexProgram.cpp b/rpcs3/Emu/GS/GL/VertexProgram.cpp index 861f0c808b..9938ad184f 100644 --- a/rpcs3/Emu/GS/GL/VertexProgram.cpp +++ b/rpcs3/Emu/GS/GL/VertexProgram.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "VertexProgram.h" -wxString VertexDecompilerThread::GetMask() +wxString VertexDecompilerThread::GetVecMask() { wxString ret = wxEmptyString; @@ -13,7 +13,19 @@ wxString VertexDecompilerThread::GetMask() return ret.IsEmpty() || ret == "xyzw" ? wxEmptyString : ("." + ret); } -wxString VertexDecompilerThread::GetDST() +wxString VertexDecompilerThread::GetScaMask() +{ + wxString ret = wxEmptyString; + + if(d3.sca_writemask_x) ret += "x"; + if(d3.sca_writemask_y) ret += "y"; + if(d3.sca_writemask_z) ret += "z"; + if(d3.sca_writemask_w) ret += "w"; + + return ret.IsEmpty() || ret == "xyzw" ? wxEmptyString : ("." + ret); +} + +wxString VertexDecompilerThread::GetDST(bool isSca) { static const wxString reg_table[] = { @@ -26,11 +38,12 @@ wxString VertexDecompilerThread::GetDST() }; wxString ret = wxEmptyString; + u32 dst = isSca ? d3.sca_dst : d3.dst; - switch(d3.dst) + switch(dst) { case 0x0: case 0x6: - ret += reg_table[d3.dst]; + ret += reg_table[dst]; break; case 0x1f: @@ -38,13 +51,13 @@ wxString VertexDecompilerThread::GetDST() break; default: - if(d3.dst < WXSIZEOF(reg_table)) + if(dst < WXSIZEOF(reg_table)) { - ret += m_parr.AddParam(PARAM_OUT, "vec4", reg_table[d3.dst]); + ret += m_parr.AddParam(PARAM_OUT, "vec4", reg_table[dst]); } else { - ConLog.Error("Bad dst reg num: %d", d3.dst); + ConLog.Error("Bad dst reg num: %d", dst); ret += m_parr.AddParam(PARAM_OUT, "vec4", "unk"); } break; @@ -53,7 +66,7 @@ wxString VertexDecompilerThread::GetDST() return ret; } -wxString VertexDecompilerThread::GetSRC(const u32 n) +wxString VertexDecompilerThread::GetSRC(const u32 n, bool isSca) { static const wxString reg_table[] = { @@ -75,12 +88,12 @@ wxString VertexDecompilerThread::GetSRC(const u32 n) case 2: //input if(d1.input_src < WXSIZEOF(reg_table)) { - ret += m_parr.AddParam(PARAM_IN, "vec4", reg_table[d1.input_src]); + ret += m_parr.AddParam(PARAM_IN, "vec4", reg_table[d1.input_src], d1.input_src); } else { ConLog.Error("Bad input src num: %d", d1.input_src); - ret += m_parr.AddParam(PARAM_IN, "vec4", "in_unk"); + ret += m_parr.AddParam(PARAM_IN, "vec4", "in_unk", d1.input_src); } break; case 3: //const @@ -94,15 +107,27 @@ wxString VertexDecompilerThread::GetSRC(const u32 n) } static const char f[4] = {'x', 'z', 'w', 'y'}; + static const char fSca[4] = {'x', 'z', 'y', 'w'}; wxString swizzle = wxEmptyString; - swizzle += f[src[n].swz_x]; - swizzle += f[src[n].swz_y]; - swizzle += f[src[n].swz_z]; - swizzle += f[src[n].swz_w]; + if (isSca) + { + assert(src[n].swz_x == src[n].swz_y); + assert(src[n].swz_z == src[n].swz_w); + assert(src[n].swz_x == src[n].swz_z); - if(swizzle != "xyzw") ret += "." + swizzle; + ret += "." + fSca[src[n].swz_x]; + } + else + { + swizzle += f[src[n].swz_x]; + swizzle += f[src[n].swz_y]; + swizzle += f[src[n].swz_z]; + swizzle += f[src[n].swz_w]; + + if(swizzle != "xyzw") ret += "." + swizzle; + } bool abs; @@ -119,7 +144,7 @@ wxString VertexDecompilerThread::GetSRC(const u32 n) return ret; } -void VertexDecompilerThread::AddCode(wxString code, bool src_mask) +void VertexDecompilerThread::AddVecCode(wxString code, bool src_mask) { if(d0.cond == 0) return; if(d0.cond != 7) @@ -129,7 +154,22 @@ void VertexDecompilerThread::AddCode(wxString code, bool src_mask) return; } - code = GetDST() + GetMask() + " = " + (src_mask ? code + GetMask() : code); + code = GetDST() + GetVecMask() + " = " + (src_mask ? code + GetVecMask() : code); + + main += "\t" + code + ";\n"; +} + +void VertexDecompilerThread::AddScaCode(wxString code, bool src_mask) +{ + if(d0.cond == 0) return; + if(d0.cond != 7) + { + ConLog.Error("Bad cond! %d", d0.cond); + Emu.Pause(); + return; + } + + code = GetDST(true) + GetScaMask() + " = " + (src_mask ? code + GetScaMask() : code); main += "\t" + code + ";\n"; } @@ -140,17 +180,11 @@ wxString VertexDecompilerThread::BuildCode() for(u32 i=0; iIsAlive()) m_decompiler_thread->Delete(); - safe_delete(m_decompiler_thread); + if(m_decompiler_thread->IsAlive()) + { + m_decompiler_thread->Stop(); + } + + delete m_decompiler_thread; + m_decompiler_thread = nullptr; } Delete(); @@ -237,13 +306,17 @@ void VertexProgram::Decompile() if(m_decompiler_thread) { Wait(); - if(m_decompiler_thread->IsAlive()) m_decompiler_thread->Delete(); - safe_delete(m_decompiler_thread); + if(m_decompiler_thread->IsAlive()) + { + m_decompiler_thread->Stop(); + } + + delete m_decompiler_thread; + m_decompiler_thread = nullptr; } m_decompiler_thread = new VertexDecompilerThread(data, shader, parr); - m_decompiler_thread->Create(); - m_decompiler_thread->Run(); + m_decompiler_thread->Start(); #endif } @@ -272,7 +345,7 @@ void VertexProgram::Compile() memset(buf, 0, r+1); glGetShaderInfoLog(id, r, &len, buf); ConLog.Error("Failed to compile vertex shader: %s", buf); - free(buf); + delete[] buf; } ConLog.Write(shader); @@ -287,7 +360,7 @@ void VertexProgram::Delete() data.Clear(); for(u32 i=0; i& data, wxString& shader, ParamArray& parr) - : wxThread(wxTHREAD_JOINABLE) + : ThreadBase(false, "Vertex Shader Decompiler Thread") , m_data(data) , m_shader(shader) , m_parr(parr) { } - wxString GetMask(); - wxString GetDST(); - wxString GetSRC(const u32 n); - void AddCode(wxString code, bool src_mask = true); + wxString GetVecMask(); + wxString GetScaMask(); + wxString GetDST(bool isSca = false); + wxString GetSRC(const u32 n, bool isSca = false); + void AddVecCode(wxString code, bool src_mask = true); + void AddScaCode(wxString code, bool src_mask = true); wxString BuildCode(); - ExitCode Entry(); + virtual void Task(); }; struct VertexProgram @@ -149,7 +151,13 @@ struct VertexProgram Array data; ParamArray parr; - void Wait() { if(m_decompiler_thread && m_decompiler_thread->IsRunning()) m_decompiler_thread->Wait(); } + void Wait() + { + if(m_decompiler_thread && m_decompiler_thread->IsRunning()) + { + m_decompiler_thread->Wait(); + } + } void Decompile(); void Compile(); void Delete(); diff --git a/rpcs3/Emu/GS/GSManager.cpp b/rpcs3/Emu/GS/GSManager.cpp index 314f09ce73..7f9b2f1e07 100644 --- a/rpcs3/Emu/GS/GSManager.cpp +++ b/rpcs3/Emu/GS/GSManager.cpp @@ -15,6 +15,9 @@ GSManager::GSManager() : m_render(NULL) void GSManager::Init() { if(m_render) return; + + m_info.Init(); + switch(Ini.GSRenderMode.GetValue()) { default: @@ -29,9 +32,8 @@ void GSManager::Close() if(m_render) { m_render->Close(); - //free(m_render); + m_render = nullptr; } - m_render = NULL; } u8 GSManager::GetState() diff --git a/rpcs3/Emu/GS/GSManager.h b/rpcs3/Emu/GS/GSManager.h index 28ef38f34f..bac7ce4233 100644 --- a/rpcs3/Emu/GS/GSManager.h +++ b/rpcs3/Emu/GS/GSManager.h @@ -4,19 +4,31 @@ struct GSInfo { - CellVideoOutResolution outresolution; - CellVideoOutDisplayMode mode; + struct + { + u8 resolutionId; + u8 scanMode; + u8 conversion; + u8 aspect; + u8 format; + u16 refreshRates; + u32 pitch; + } mode; + //CellVideoOutDisplayMode mode; GSInfo() { - outresolution.width = 740; - outresolution.height = 480; + } - mode.resolutionId = CELL_VIDEO_OUT_RESOLUTION_576; + void Init() + { + mode.resolutionId = Ini.GSResolution.GetValue(); mode.scanMode = CELL_VIDEO_OUT_SCAN_MODE_INTERLACE; mode.conversion = CELL_VIDEO_OUT_DISPLAY_CONVERSION_NONE; - mode.aspect = CELL_VIDEO_OUT_ASPECT_4_3; + mode.aspect = Ini.GSAspectRatio.GetValue(); mode.refreshRates = CELL_VIDEO_OUT_REFRESH_RATE_50HZ; + mode.format = CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8; + mode.pitch = 4; } }; @@ -47,7 +59,7 @@ public: void Close(); GSInfo& GetInfo() { return m_info; } - GSRender& GetRender() { return *m_render; } + GSRender& GetRender() { assert(m_render); return *m_render; } u8 GetState(); u8 GetColorSpace(); diff --git a/rpcs3/Emu/GS/GSRender.cpp b/rpcs3/Emu/GS/GSRender.cpp index 4ba5bb97f8..3055b66733 100644 --- a/rpcs3/Emu/GS/GSRender.cpp +++ b/rpcs3/Emu/GS/GSRender.cpp @@ -21,10 +21,10 @@ wxSize AspectRatio(wxSize rs, const wxSize as) GSFrame::GSFrame(wxWindow* parent, const wxString& title) : wxFrame(parent, wxID_ANY, title) { - SetClientSize(720, 576); - //Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(GSFrame::OnLeftDclick)); + CellVideoOutResolution res = ResolutionTable[ResolutionIdToNum(Ini.GSResolution.GetValue())]; + SetClientSize(res.width, res.height); wxGetApp().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(GSFrame::OnKeyDown), (wxObject*)0, this); - wxGetApp().Connect(GetId(), wxEVT_CLOSE_WINDOW, wxCloseEventHandler(GSFrame::OnClose), (wxObject*)0, this); + Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(GSFrame::OnClose)); } void GSFrame::OnPaint(wxPaintEvent& event) @@ -54,7 +54,7 @@ void GSFrame::OnKeyDown(wxKeyEvent& event) { switch(event.GetKeyCode()) { - case WXK_RETURN: if(event.ControlDown()) { OnFullScreen(); return; } break; + case WXK_RETURN: if(event.AltDown()) { OnFullScreen(); return; } break; case WXK_ESCAPE: if(IsFullScreen()) { ShowFullScreen(false); return; } break; } event.Skip(); @@ -79,7 +79,8 @@ void GSFrame::SetSize(int width, int height) GSRender::GSRender() : m_ctrl(NULL) - , m_flip_status(-1) + , m_flip_status(0) + , m_flip_mode(CELL_GCM_DISPLAY_VSYNC) { } @@ -91,4 +92,8 @@ u32 GSRender::GetAddress(u32 offset, u8 location) case CELL_GCM_LOCATION_MAIN: return offset; } return 0; +} + +GSLockCurrent::GSLockCurrent(GSLockType type) : GSLock(Emu.GetGSManager().GetRender(), type) +{ } \ No newline at end of file diff --git a/rpcs3/Emu/GS/GSRender.h b/rpcs3/Emu/GS/GSRender.h index 5c81926545..86c05ece7b 100644 --- a/rpcs3/Emu/GS/GSRender.h +++ b/rpcs3/Emu/GS/GSRender.h @@ -1,6 +1,15 @@ #pragma once #include "Emu/GS/GCM.h" +enum Method +{ + CELL_GCM_METHOD_FLAG_NON_INCREMENT = 0x40000000, + CELL_GCM_METHOD_FLAG_JUMP = 0x20000000, + CELL_GCM_METHOD_FLAG_CALL = 0x00000002, + CELL_GCM_METHOD_FLAG_RETURN = 0x00020000, +}; + + wxSize AspectRatio(wxSize rs, const wxSize as); class GSFrame : public wxFrame @@ -14,15 +23,15 @@ protected: //virtual void OnSize(wxSizeEvent&); + void OnKeyDown(wxKeyEvent& event); + void OnFullScreen(); + +public: void OnLeftDclick(wxMouseEvent&) { OnFullScreen(); } - void OnKeyDown(wxKeyEvent& event); - void OnFullScreen(); - -public: //void SetSize(int width, int height); private: @@ -33,15 +42,60 @@ struct GSRender { u32 m_ioAddress, m_ioSize, m_ctrlAddress, m_localAddress; CellGcmControl* m_ctrl; + wxCriticalSection m_cs_main; + wxSemaphore m_sem_flush; + wxSemaphore m_sem_flip; int m_flip_status; + int m_flip_mode; + volatile bool m_draw; GSRender(); virtual void Init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddress, const u32 localAddress)=0; virtual void Draw()=0; virtual void Close()=0; - virtual void Pause()=0; - virtual void Resume()=0; u32 GetAddress(u32 offset, u8 location); +}; + +enum GSLockType +{ + GS_LOCK_NOT_WAIT, + GS_LOCK_WAIT_FLUSH, + GS_LOCK_WAIT_FLIP, +}; + +struct GSLock +{ +private: + GSRender& m_renderer; + GSLockType m_type; + +public: + GSLock(GSRender& renderer, GSLockType type) + : m_renderer(renderer) + , m_type(type) + { + switch(m_type) + { + case GS_LOCK_NOT_WAIT: m_renderer.m_cs_main.Enter(); break; + case GS_LOCK_WAIT_FLUSH: m_renderer.m_sem_flush.Wait(); break; + case GS_LOCK_WAIT_FLIP: m_renderer.m_sem_flip.Wait(); break; + } + } + + ~GSLock() + { + switch(m_type) + { + case GS_LOCK_NOT_WAIT: m_renderer.m_cs_main.Leave(); break; + case GS_LOCK_WAIT_FLUSH: m_renderer.m_sem_flush.Post(); break; + case GS_LOCK_WAIT_FLIP: m_renderer.m_sem_flip.Post(); break; + } + } +}; + +struct GSLockCurrent : GSLock +{ + GSLockCurrent(GSLockType type); }; \ No newline at end of file diff --git a/rpcs3/Emu/GS/Null/NullGSRender.h b/rpcs3/Emu/GS/Null/NullGSRender.h index 32ba3aa938..d9c9443b1a 100644 --- a/rpcs3/Emu/GS/Null/NullGSRender.h +++ b/rpcs3/Emu/GS/Null/NullGSRender.h @@ -5,6 +5,7 @@ struct NullGSFrame : public GSFrame { NullGSFrame() : GSFrame(NULL, "GSFrame[Null]") { + Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(GSFrame::OnLeftDclick)); } void Draw() { Draw(wxClientDC(this)); } @@ -23,22 +24,33 @@ private: } }; +struct NullRSXThread : public wxThread +{ + wxWindow* m_parent; + Stack call_stack; + + NullRSXThread(wxWindow* parent); + + virtual void OnExit(); + void Start(); + ExitCode Entry(); +}; + class NullGSRender : public wxWindow , public GSRender { private: - NullGSFrame* m_frame; - wxTimer* m_update_timer; - bool m_paused; + NullRSXThread* m_rsx_thread; public: + NullGSFrame* m_frame; + NullGSRender() - : m_frame(NULL) - , m_paused(false) + : m_frame(nullptr) + , m_rsx_thread(nullptr) { - m_update_timer = new wxTimer(this); - Connect(m_update_timer->GetId(), wxEVT_TIMER, wxTimerEventHandler(NullGSRender::OnTimer)); + m_draw = false; m_frame = new NullGSFrame(); } @@ -62,24 +74,10 @@ private: m_localAddress = localAddress; m_ctrl = (CellGcmControl*)Memory.GetMemFromAddr(m_ctrlAddress); - m_update_timer->Start(1); - } - - void OnTimer(wxTimerEvent&) - { - while(!m_paused && m_ctrl->get != m_ctrl->put && Emu.IsRunned()) - { - const u32 get = re(m_ctrl->get); - const u32 cmd = Memory.Read32(m_ioAddress + get); - const u32 count = (cmd >> 18) & 0x7ff; - mem32_t data(m_ioAddress + get + 4); - - m_ctrl->get = re32(get + (count + 1) * 4); - DoCmd(cmd, cmd & 0x3ffff, data, count); - memset(Memory.GetMemFromAddr(m_ioAddress + get), 0, (count + 1) * 4); - } + (m_rsx_thread = new NullRSXThread(this))->Start(); } +public: void DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 count) { switch(cmd) @@ -87,32 +85,107 @@ private: case NV406E_SET_REFERENCE: m_ctrl->ref = re32(args[0]); break; - - case NV4097_SET_BEGIN_END: - if(!args[0]) m_flip_status = 0; - break; } } virtual void Draw() { //if(m_frame && !m_frame->IsBeingDeleted()) m_frame->Draw(); + m_draw = true; } virtual void Close() { - m_update_timer->Stop(); + if(m_rsx_thread) m_rsx_thread->Delete(); if(m_frame->IsShown()) m_frame->Hide(); m_ctrl = NULL; } +}; - void Pause() +NullRSXThread::NullRSXThread(wxWindow* parent) + : wxThread(wxTHREAD_DETACHED) + , m_parent(parent) +{ +} + +void NullRSXThread::OnExit() +{ + call_stack.Clear(); +} + +void NullRSXThread::Start() +{ + Create(); + Run(); +} + +wxThread::ExitCode NullRSXThread::Entry() +{ + ConLog.Write("Null RSX thread entry"); + + NullGSRender& p = *(NullGSRender*)m_parent; + + while(!TestDestroy() && p.m_frame && !p.m_frame->IsBeingDeleted()) { - m_paused = true; + wxCriticalSectionLocker lock(p.m_cs_main); + + if(p.m_ctrl->get == p.m_ctrl->put || !Emu.IsRunned()) + { + SemaphorePostAndWait(p.m_sem_flush); + + if(p.m_draw) + { + p.m_draw = false; + p.m_flip_status = 0; + if(SemaphorePostAndWait(p.m_sem_flip)) continue; + } + + Sleep(1); + continue; + } + + const u32 get = re(p.m_ctrl->get); + const u32 cmd = Memory.Read32(p.m_ioAddress + get); + const u32 count = (cmd >> 18) & 0x7ff; + + if(cmd & CELL_GCM_METHOD_FLAG_JUMP) + { + p.m_ctrl->get = re32(cmd & ~(CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT)); + ConLog.Warning("rsx jump!"); + continue; + } + if(cmd & CELL_GCM_METHOD_FLAG_CALL) + { + call_stack.Push(get + 4); + p.m_ctrl->get = re32(cmd & ~CELL_GCM_METHOD_FLAG_CALL); + ConLog.Warning("rsx call!"); + continue; + } + if(cmd & CELL_GCM_METHOD_FLAG_RETURN) + { + p.m_ctrl->get = re32(call_stack.Pop()); + ConLog.Warning("rsx return!"); + continue; + } + if(cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT) + { + //ConLog.Warning("non increment cmd! 0x%x", cmd); + } + + if(cmd == 0) + { + ConLog.Warning("null cmd: addr=0x%x, put=0x%x, get=0x%x", p.m_ioAddress + get, re(p.m_ctrl->put), get); + Emu.Pause(); + continue; + } + + p.DoCmd(cmd, cmd & 0x3ffff, mem32_t(p.m_ioAddress + get + 4), count); + re(p.m_ctrl->get, get + (count + 1) * 4); } - void Resume() - { - m_paused = false; - } -}; \ No newline at end of file + ConLog.Write("Null RSX thread exit..."); + + call_stack.Clear(); + + return (ExitCode)0; +} \ No newline at end of file diff --git a/rpcs3/Emu/GS/RSXThread.cpp b/rpcs3/Emu/GS/RSXThread.cpp index 83ff3eaac5..910a7ed0e4 100644 --- a/rpcs3/Emu/GS/RSXThread.cpp +++ b/rpcs3/Emu/GS/RSXThread.cpp @@ -1,81 +1,14 @@ #include "stdafx.h" #include "RSXThread.h" -enum MethodFlag -{ - CELL_GCM_METHOD_FLAG_NON_INCREMENT = 0x40000000, - CELL_GCM_METHOD_FLAG_JUMP = 0x20000000, - CELL_GCM_METHOD_FLAG_CALL = 0x00000002, - CELL_GCM_METHOD_FLAG_RETURN = 0x00020000, -}; - -RSXThread::RSXThread( - CellGcmControl* ctrl, - u32 ioAddress, - void (&cmd)(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 count), - bool (&_flip)(), - bool (&_TestExit)() - ) +RSXThread::RSXThread(CellGcmControl* ctrl, u32 ioAddress) : m_ctrl(*ctrl) , m_ioAddress(ioAddress) - , DoCmd(cmd) - , flip(_flip) - , TestExit(_TestExit) { } void RSXThread::Task() { - while(!TestDestroy() && !TestExit()) - { - if(m_ctrl.get == m_ctrl.put) - { - flip(); - - Sleep(1); - continue; - } - - const u32 get = re(m_ctrl.get); - const u32 cmd = Memory.Read32(m_ioAddress + get); - const u32 count = (cmd >> 18) & 0x7ff; - mem32_t data(m_ioAddress + get + 4); - - if(cmd & CELL_GCM_METHOD_FLAG_JUMP) - { - m_ctrl.get = re32(cmd & ~(CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT)); - continue; - } - if(cmd & CELL_GCM_METHOD_FLAG_CALL) - { - call_stack.AddCpy(get + 4); - m_ctrl.get = re32(cmd & ~CELL_GCM_METHOD_FLAG_CALL); - continue; - } - if(cmd & CELL_GCM_METHOD_FLAG_RETURN) - { - const u32 pos = call_stack.GetCount() - 1; - m_ctrl.get = re32(call_stack[pos]); - call_stack.RemoveAt(pos); - continue; - } - if(cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT) - { - //ConLog.Warning("non increment cmd! 0x%x", cmd); - } - -#if 0 - wxString debug = getMethodName(cmd & 0x3ffff); - debug += "("; - for(u32 i=0; i call_stack; - void (&DoCmd)(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 count); - bool (&flip)(); - bool (&TestExit)(); CellGcmControl& m_ctrl; u32 m_ioAddress; protected: - RSXThread( - CellGcmControl* ctrl, - u32 ioAddress, - void (&cmd)(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 count), - bool (&_flip)(), - bool (&_TestExit)() - ); + RSXThread(CellGcmControl* ctrl, u32 ioAddress); private: virtual void Task(); diff --git a/rpcs3/Emu/GS/sysutil_video.h b/rpcs3/Emu/GS/sysutil_video.h index 139bd03800..a1a0d4c920 100644 --- a/rpcs3/Emu/GS/sysutil_video.h +++ b/rpcs3/Emu/GS/sysutil_video.h @@ -14,7 +14,6 @@ enum VideoErrorCode CELL_VIDEO_OUT_ERROR_VALUE_IS_NOT_SET = 0x8002b228, }; - enum CellVideoOut { CELL_VIDEO_OUT_PRIMARY, @@ -166,22 +165,22 @@ struct CellVideoOutDisplayMode struct CellVideoOutResolution { - u16 width; - u16 height; + u16 width; + u16 height; }; struct CellVideoOutDeviceInfo { - u8 portType; - u8 colorSpace; - u16 latency; - u8 availableModeCount; - u8 state; - u8 rgbOutputRange; - u8 reserved[5]; - CellVideoOutColorInfo colorInfo; - CellVideoOutDisplayMode availableModes[32]; - CellVideoOutKSVList ksvList; + u8 portType; + u8 colorSpace; + u16 latency; + u8 availableModeCount; + u8 state; + u8 rgbOutputRange; + u8 reserved[5]; + CellVideoOutColorInfo colorInfo; + CellVideoOutDisplayMode availableModes[32]; + CellVideoOutKSVList ksvList; }; struct CellVideoOutState @@ -194,11 +193,11 @@ struct CellVideoOutState struct CellVideoOutConfiguration { - u8 resolutionId; - u8 format; - u8 aspect; - u8 reserved[9]; - u32 pitch; + u8 resolutionId; + u8 format; + u8 aspect; + u8 reserved[9]; + u32 pitch; }; enum CellVideoOutEvent @@ -220,4 +219,58 @@ enum CellVideoOutRGBOutputRange { CELL_VIDEO_OUT_RGB_OUTPUT_RANGE_LIMITED, CELL_VIDEO_OUT_RGB_OUTPUT_RANGE_FULL, -}; \ No newline at end of file +}; + +static const CellVideoOutResolution ResolutionTable[] = +{ + {-1, -1}, //0 - 0 + {1920, 1080}, //1 - 1 + {1280, 720}, //2 - 2 + {720, 480}, //4 - 3 + {720, 576}, //5 - 4 + {1600, 1080}, //10 - 5 + {1440, 1080}, //11 - 6 + {1280, 1080}, //12 - 7 + {960, 1080}, //13 - 8 +}; + +inline static u32 ResolutionIdToNum(u32 id) +{ + static const u32 res[] = + { + 0, //0 + 1, //1 + 2, //2 + 0, //3 + 3, //4 + 4, //5 + 0, //6 + 0, //7 + 0, //8 + 0, //9 + 5, //10 + 6, //11 + 7, //12 + 8, //13 + }; + + return id <= 13 ? res[id] : 0; +} + +inline static u32 ResolutionNumToId(u32 num) +{ + static const u32 res[] = + { + 0, + 1, + 2, + 4, + 5, + 10, + 11, + 12, + 13, + }; + + return num <= 8 ? res[num] : 0; +} \ No newline at end of file diff --git a/rpcs3/Emu/Io/Windows/WindowsPadHandler.h b/rpcs3/Emu/Io/Windows/WindowsPadHandler.h index b450eed2c9..c7a8c9743e 100644 --- a/rpcs3/Emu/Io/Windows/WindowsPadHandler.h +++ b/rpcs3/Emu/Io/Windows/WindowsPadHandler.h @@ -6,11 +6,13 @@ class WindowsPadHandler : public wxWindow , public PadHandlerBase { + AppConnector m_app_connector; + public: WindowsPadHandler() : wxWindow() { - wxGetApp().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WindowsPadHandler::KeyDown), (wxObject*)0, this); - wxGetApp().Connect(wxEVT_KEY_UP, wxKeyEventHandler(WindowsPadHandler::KeyUp), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WindowsPadHandler::KeyDown), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_KEY_UP, wxKeyEventHandler(WindowsPadHandler::KeyUp), (wxObject*)0, this); } virtual void KeyDown(wxKeyEvent& event) { Key(event.GetKeyCode(), 1); event.Skip(); } @@ -32,27 +34,27 @@ public: void LoadSettings() { - m_pads.Add(new Pad( + m_pads.Move(new Pad( CELL_PAD_STATUS_CONNECTED, CELL_PAD_SETTING_PRESS_ON | CELL_PAD_SETTING_SENSOR_OFF, CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE, CELL_PAD_DEV_TYPE_STANDARD)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'A', CELL_PAD_CTRL_LEFT)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'S', CELL_PAD_CTRL_DOWN)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'D', CELL_PAD_CTRL_RIGHT)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'W', CELL_PAD_CTRL_UP)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, WXK_RETURN, CELL_PAD_CTRL_START)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'X', CELL_PAD_CTRL_R3)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'Z', CELL_PAD_CTRL_L3)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, WXK_SPACE, CELL_PAD_CTRL_SELECT)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'A', CELL_PAD_CTRL_LEFT)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'S', CELL_PAD_CTRL_DOWN)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'D', CELL_PAD_CTRL_RIGHT)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'W', CELL_PAD_CTRL_UP)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, WXK_RETURN, CELL_PAD_CTRL_START)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'X', CELL_PAD_CTRL_R3)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, 'Z', CELL_PAD_CTRL_L3)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL1, WXK_SPACE, CELL_PAD_CTRL_SELECT)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'K', CELL_PAD_CTRL_SQUARE)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'L', CELL_PAD_CTRL_CROSS)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, ';', CELL_PAD_CTRL_CIRCLE)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'O', CELL_PAD_CTRL_TRIANGLE)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'I', CELL_PAD_CTRL_R1)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'Q', CELL_PAD_CTRL_L1)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'P', CELL_PAD_CTRL_R2)); - m_pads[0].m_buttons.Add(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'E', CELL_PAD_CTRL_L2)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'K', CELL_PAD_CTRL_SQUARE)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'L', CELL_PAD_CTRL_CROSS)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, ';', CELL_PAD_CTRL_CIRCLE)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'O', CELL_PAD_CTRL_TRIANGLE)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'I', CELL_PAD_CTRL_R1)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'Q', CELL_PAD_CTRL_L1)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'P', CELL_PAD_CTRL_R2)); + m_pads[0].m_buttons.Move(new Button(CELL_PAD_BTN_OFFSET_DIGITAL2, 'E', CELL_PAD_CTRL_L2)); } }; \ No newline at end of file diff --git a/rpcs3/Emu/Memory/Memory.cpp b/rpcs3/Emu/Memory/Memory.cpp index be3bb49802..0c08be11b7 100644 --- a/rpcs3/Emu/Memory/Memory.cpp +++ b/rpcs3/Emu/Memory/Memory.cpp @@ -20,13 +20,15 @@ void MemoryBlock::Init() range_start = 0; range_size = 0; - mem = NULL; + mem = nullptr; } void MemoryBlock::InitMemory() { + if(!range_size) return; + safe_delete(mem); - mem = new u8[range_size]; + mem = (u8*)malloc(range_size); memset(mem, 0, range_size); } @@ -43,32 +45,36 @@ u64 MemoryBlock::FixAddr(const u64 addr) const bool MemoryBlock::GetMemFromAddr(void* dst, const u64 addr, const u32 size) { - if(!IsMyAddress(addr)) return false; - if(FixAddr(addr) + size > GetSize()) return false; - memcpy(dst, &mem[FixAddr(addr)], size); + if(!IsMyAddress(addr) || FixAddr(addr) + size > GetSize()) return false; + + memcpy(dst, GetMem(FixAddr(addr)), size); + return true; } bool MemoryBlock::SetMemFromAddr(void* src, const u64 addr, const u32 size) { - if(!IsMyAddress(addr)) return false; - if(FixAddr(addr) + size > GetSize()) return false; - memcpy(&mem[FixAddr(addr)], src, size); + if(!IsMyAddress(addr) || FixAddr(addr) + size > GetSize()) return false; + + memcpy(GetMem(FixAddr(addr)), src, size); + return true; } bool MemoryBlock::GetMemFFromAddr(void* dst, const u64 addr) { if(!IsMyAddress(addr)) return false; - dst = &mem[FixAddr(addr)]; + + dst = GetMem(FixAddr(addr)); + return true; } u8* MemoryBlock::GetMemFromAddr(const u64 addr) { - if(!IsMyAddress(addr)) return NULL; + if(!IsMyAddress(addr) || IsNULL()) return nullptr; - return &mem[FixAddr(addr)]; + return GetMem(FixAddr(addr)); } MemoryBlock* MemoryBlock::SetRange(const u64 start, const u32 size) @@ -80,24 +86,6 @@ MemoryBlock* MemoryBlock::SetRange(const u64 start, const u32 size) return this; } -bool MemoryBlock::SetNewSize(const u32 size) -{ - if(range_size >= size) return false; - - u8* new_mem = (u8*)realloc(mem, size); - if(!new_mem) - { - ConLog.Error("Not enought free memory"); - Emu.Pause(); - return false; - } - - mem = new_mem; - range_size = size; - - return true; -} - bool MemoryBlock::IsMyAddress(const u64 addr) { return addr >= GetStartAddr() && addr < GetEndAddr(); @@ -105,7 +93,7 @@ bool MemoryBlock::IsMyAddress(const u64 addr) __forceinline const u8 MemoryBlock::FastRead8(const u64 addr) const { - return mem[addr]; + return *GetMem(addr); } __forceinline const u16 MemoryBlock::FastRead16(const u64 addr) const @@ -126,8 +114,8 @@ __forceinline const u64 MemoryBlock::FastRead64(const u64 addr) const __forceinline const u128 MemoryBlock::FastRead128(const u64 addr) { u128 ret; - ret.hi = FastRead64(addr); - ret.lo = FastRead64(addr + 8); + ret.lo = FastRead64(addr); + ret.hi = FastRead64(addr + 8); return ret; } @@ -193,7 +181,7 @@ bool MemoryBlock::Read128(const u64 addr, u128* value) __forceinline void MemoryBlock::FastWrite8(const u64 addr, const u8 value) { - mem[addr] = value; + *GetMem(addr) = value; } __forceinline void MemoryBlock::FastWrite16(const u64 addr, const u16 value) @@ -216,8 +204,8 @@ __forceinline void MemoryBlock::FastWrite64(const u64 addr, const u64 value) __forceinline void MemoryBlock::FastWrite128(const u64 addr, const u128 value) { - FastWrite64(addr, value.hi); - FastWrite64(addr+8, value.lo); + FastWrite64(addr, value.lo); + FastWrite64(addr+8, value.hi); } bool MemoryBlock::Write8(const u64 addr, const u8 value) @@ -332,11 +320,22 @@ bool NullMemoryBlock::Write128(const u64 addr, const u128 value) } //DynamicMemoryBlock -DynamicMemoryBlock::DynamicMemoryBlock() : m_point(0) - , m_max_size(0) +DynamicMemoryBlock::DynamicMemoryBlock() : m_max_size(0) { } +const u32 DynamicMemoryBlock::GetUsedSize() const +{ + u32 size = 0; + + for(u32 i=0; i= GetStartAddr() && addr < GetStartAddr() + GetSize(); @@ -363,8 +362,7 @@ bool DynamicMemoryBlock::IsMyAddress(const u64 addr) MemoryBlock* DynamicMemoryBlock::SetRange(const u64 start, const u32 size) { m_max_size = size; - MemoryBlock::SetRange(start, 4); - m_point = GetStartAddr(); + MemoryBlock::SetRange(start, 0); return this; } @@ -372,146 +370,64 @@ MemoryBlock* DynamicMemoryBlock::SetRange(const u64 start, const u32 size) void DynamicMemoryBlock::Delete() { m_used_mem.Clear(); - m_free_mem.Clear(); - m_point = 0; m_max_size = 0; MemoryBlock::Delete(); } -void DynamicMemoryBlock::UpdateSize(u64 addr, u32 size) -{ - u32 used_size = addr + size - GetStartAddr(); - if(used_size > GetUsedSize()) SetNewSize(used_size); -} - -void DynamicMemoryBlock::CombineFreeMem() -{ - if(m_free_mem.GetCount() < 2) return; - - for(u32 i1=0; i1 m_point) + for(u32 i=0; i= free_mem_addr && m_free_mem[i].addr < addr) - { - free_mem_addr = m_free_mem[i].addr + m_free_mem[i].size; - } - } - - for(u32 i=0; i= free_mem_addr && m_used_mem[i].addr < addr) - { - free_mem_addr = m_used_mem[i].addr + m_used_mem[i].size; - } - } - - m_free_mem.AddCpy(MemBlockInfo(free_mem_addr, addr - GetStartAddr())); - } - - UpdateSize(addr, size); - - m_used_mem.AddCpy(MemBlockInfo(addr, size)); - memset(mem + (addr - GetStartAddr()), 0, size); - - m_point = addr + size; - - return true; + if(addr >= m_used_mem[i].addr && addr < m_used_mem[i].addr + m_used_mem[i].size) return false; } - for(u32 i=0; i= m_free_mem[i].addr + m_free_mem[i].size - || m_free_mem[i].size < size) continue; + AppendUsedMem(addr, size); - if(m_free_mem[i].addr != addr) - { - m_free_mem.AddCpy(MemBlockInfo(m_free_mem[i].addr, addr - m_free_mem[i].addr)); - } + return true; +} - if(m_free_mem[i].size != size) - { - m_free_mem.AddCpy(MemBlockInfo(m_free_mem[i].addr + size, m_free_mem[i].size - size)); - } - - m_free_mem.RemoveAt(i); - m_used_mem.AddCpy(MemBlockInfo(addr, size)); - - memset(mem + (addr - GetStartAddr()), 0, size); - return true; - } - - return false; +void DynamicMemoryBlock::AppendUsedMem(u64 addr, u32 size) +{ + m_used_mem.Move(new MemBlockInfo(addr, size)); } u64 DynamicMemoryBlock::Alloc(u32 size) { - for(u32 i=0; i= m_used_mem[i].addr && addr < m_used_mem[i].addr + m_used_mem[i].size) || + (m_used_mem[i].addr >= addr && m_used_mem[i].addr < addr + size)) + { + is_good_addr = false; + addr = m_used_mem[i].addr + m_used_mem[i].size; + break; + } } - m_free_mem.RemoveAt(i); - m_used_mem.AddCpy(MemBlockInfo(addr, size)); + if(!is_good_addr) continue; + + AppendUsedMem(addr, size); - memset(mem + (addr - GetStartAddr()), 0, size); return addr; } - UpdateSize(m_point, size); - - MemBlockInfo res(m_point, size); - m_used_mem.AddCpy(res); - memset(mem + (m_point - GetStartAddr()), 0, size); - - m_point += size; - - return res.addr; + return 0; } bool DynamicMemoryBlock::Alloc() @@ -520,14 +436,12 @@ bool DynamicMemoryBlock::Alloc() } bool DynamicMemoryBlock::Free(u64 addr) -{ +{ for(u32 i=0; i= _addr && addr < _addr + m_used_mem[i].size) + { + return (u8*)m_used_mem[i].mem + addr - _addr; + } + } + + ConLog.Error("GetMem(%llx) from not allocated address.", addr); + assert(0); + return nullptr; +} + //MemoryBase void MemoryBase::Write8(u64 addr, const u8 data) { diff --git a/rpcs3/Emu/Memory/Memory.h b/rpcs3/Emu/Memory/Memory.h index ac49500875..5fdcaa0b8f 100644 --- a/rpcs3/Emu/Memory/Memory.h +++ b/rpcs3/Emu/Memory/Memory.h @@ -1,44 +1,6 @@ #pragma once #include "MemoryBlock.h" -class MemoryFlags -{ - struct Flag - { - const u64 addr; - const u64 waddr; - const u64 fid; - - Flag(const u64 _addr, const u64 _waddr, const u64 _fid) - : addr(_addr) - , waddr(_waddr) - , fid(_fid) - { - } - }; - - Array m_memflags; - -public: - void Add(const u64 addr, const u64 waddr, const u64 fid) {m_memflags.Add(new Flag(addr, waddr, fid));} - void Clear() { m_memflags.Clear(); } - - bool IsFlag(const u64 addr, u64& waddr, u64& fid) - { - for(u32 i=0; i> 8) & 0xff) | ((val << 8) & 0xff00); + return _byteswap_ushort(val); + //return ((val >> 8) & 0xff) | ((val << 8) & 0xff00); } - static u32 Reverse32(const u32 val) + static __forceinline u32 Reverse32(const u32 val) { + return _byteswap_ulong(val); + /* return ((val >> 24) & 0x000000ff) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | ((val << 24) & 0xff000000); + */ } - static u64 Reverse64(const u64 val) + static __forceinline u64 Reverse64(const u64 val) { + return _byteswap_uint64(val); + /* return ((val >> 56) & 0x00000000000000ff) | ((val >> 40) & 0x000000000000ff00) | @@ -94,19 +60,19 @@ public: ((val << 24) & 0x0000ff0000000000) | ((val << 40) & 0x00ff000000000000) | ((val << 56) & 0xff00000000000000); + */ } - template static T Reverse(T val) - { - switch(sizeof(T)) - { - case 2: return Reverse16(val); - case 4: return Reverse32(val); - case 8: return Reverse64(val); - } + template static __forceinline T Reverse(T val); + template<> static __forceinline u8 Reverse(u8 val) { return val; }; + template<> static __forceinline u16 Reverse(u16 val) { return Reverse16(val); }; + template<> static __forceinline u32 Reverse(u32 val) { return Reverse32(val); }; + template<> static __forceinline u64 Reverse(u64 val) { return Reverse64(val); }; - return val; - } + template<> static __forceinline s8 Reverse(s8 val) { return val; }; + template<> static __forceinline s16 Reverse(s16 val) { return Reverse16(val); }; + template<> static __forceinline s32 Reverse(s32 val) { return Reverse32(val); }; + template<> static __forceinline s64 Reverse(s64 val) { return Reverse64(val); }; MemoryBlock& GetMemByNum(const u8 num) { @@ -170,7 +136,7 @@ public: MemoryBlocks.Add(MainMem.SetRange(0x00010000, 0x2FFF0000)); MemoryBlocks.Add(PRXMem.SetRange(0x30000000, 0x10000000)); MemoryBlocks.Add(RSXCMDMem.SetRange(0x40000000, 0x10000000)); - //MemoryBlocks.Add(MmaperMem.SetRange(0xB0000000, 0x10000000)); + MemoryBlocks.Add(MmaperMem.SetRange(0xB0000000, 0x10000000)); MemoryBlocks.Add(RSXFBMem.SetRange(0xC0000000, 0x10000000)); MemoryBlocks.Add(StackMem.SetRange(0xD0000000, 0x10000000)); //MemoryBlocks.Add(SpuRawMem.SetRange(0xE0000000, 0x10000000)); @@ -213,7 +179,6 @@ public: } MemoryBlocks.Clear(); - MemFlags.Clear(); } void Reset() @@ -243,6 +208,66 @@ public: u64 Read64(const u64 addr); u128 Read128(const u64 addr); + void ReadLeft(u8* dst, const u64 addr, const u32 size) + { + MemoryBlock& mem = GetMemByAddr(addr); + + if(mem.IsNULL()) + { + ConLog.Error("ReadLeft[%d] from null block (0x%llx)", size, addr); + return; + } + + u32 offs = mem.FixAddr(addr); + + for(u32 i=0; i void WriteData(const u64 addr, const T* data) { memcpy(GetMemFromAddr(addr), data, sizeof(T)); @@ -264,26 +289,18 @@ public: wxString ReadString(const u64 addr) { - wxString buf = wxEmptyString; - - for(u32 i=addr; ; i++) - { - const u8 c = Read8(i); - if(c == 0) break; - buf += c; - } - - return buf; + return wxString((const char*)GetMemFromAddr(addr)); } void WriteString(const u64 addr, const wxString& str) { - for(u32 i=0; i m_used_mem; - Array m_free_mem; - u64 m_point; u32 m_max_size; public: DynamicMemoryBlock(); const u32 GetSize() const { return m_max_size; } - const u32 GetUsedSize() const { return range_size; } + const u32 GetUsedSize() const; bool IsInMyRange(const u64 addr); bool IsInMyRange(const u64 addr, const u32 size); @@ -112,11 +125,13 @@ public: virtual void Delete(); - void UpdateSize(u64 addr, u32 size); - void CombineFreeMem(); - bool Alloc(u64 addr, u32 size); u64 Alloc(u32 size); bool Alloc(); bool Free(u64 addr); + + virtual u8* GetMem(u64 addr) const; + +private: + void AppendUsedMem(u64 addr, u32 size); }; diff --git a/rpcs3/Emu/SysCalls/Callback.cpp b/rpcs3/Emu/SysCalls/Callback.cpp new file mode 100644 index 0000000000..e9e98593e2 --- /dev/null +++ b/rpcs3/Emu/SysCalls/Callback.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "Callback.h" + +#include "Emu/Cell/PPCThread.h" + +Callback::Callback(u32 slot, u64 addr) + : m_addr(addr) + , m_slot(slot) + , a1(0) + , a2(0) + , a3(0) + , m_has_data(false) +{ +} + +void Callback::Handle(u64 _a1, u64 _a2, u64 _a3) +{ + a1 = _a1; + a2 = _a2; + a3 = _a3; + m_has_data = true; +} + +void Callback::Branch() +{ + m_has_data = false; + + PPCThread& new_thread = Emu.GetCPU().AddThread(true); + + new_thread.SetPc(m_addr); + new_thread.SetPrio(0x1001); + new_thread.stack_size = 0x10000; + new_thread.SetName("Callback"); + new_thread.Run(); + + ((PPUThread&)new_thread).LR = Emu.GetPPUThreadExit(); + ((PPUThread&)new_thread).GPR[3] = a1; + ((PPUThread&)new_thread).GPR[4] = a2; + ((PPUThread&)new_thread).GPR[5] = a3; + new_thread.Exec(); + + while(new_thread.IsAlive()) Sleep(1); + Emu.GetCPU().RemoveThread(new_thread.GetId()); + + ConLog.Write("Callback EXIT!"); +} + +Callback2::Callback2(u32 slot, u64 addr, u64 userdata) : Callback(slot, addr) +{ + a2 = userdata; +} + +void Callback2::Handle(u64 status) +{ + Callback::Handle(status, a2, 0); +} + +Callback3::Callback3(u32 slot, u64 addr, u64 userdata) : Callback(slot, addr) +{ + a3 = userdata; +} + +void Callback3::Handle(u64 status, u64 param) +{ + Callback::Handle(status, param, a3); +} diff --git a/rpcs3/Emu/SysCalls/Callback.h b/rpcs3/Emu/SysCalls/Callback.h new file mode 100644 index 0000000000..fe720ab18e --- /dev/null +++ b/rpcs3/Emu/SysCalls/Callback.h @@ -0,0 +1,146 @@ +#pragma once + +struct Callback +{ + u64 m_addr; + u32 m_slot; + + u64 a1; + u64 a2; + u64 a3; + + bool m_has_data; + + Callback(u32 slot, u64 addr); + void Handle(u64 a1, u64 a2, u64 a3); + void Branch(); +}; + +struct Callback2 : public Callback +{ + Callback2(u32 slot, u64 addr, u64 userdata); + void Handle(u64 status); +}; + +struct Callback3 : public Callback +{ + Callback3(u32 slot, u64 addr, u64 userdata); + void Handle(u64 status, u64 param); +}; + +struct Callbacks +{ + Array m_callbacks; + bool m_in_manager; + + Callbacks() : m_in_manager(false) + { + } + + virtual void Register(u32 slot, u64 addr, u64 userdata) + { + Unregister(slot); + } + + void Unregister(u32 slot) + { + for(u32 i=0; i m_callbacks; + Callbacks3 m_exit_callback; + + void Add(Callbacks& c) + { + if(c.m_in_manager) return; + + c.m_in_manager = true; + m_callbacks.Add(c); + } + + void Init() + { + Add(m_exit_callback); + } + + void Clear() + { + for(u32 i=0; i); + //sysPrxForUser.AddFunc(0x8a561d92, bind_func(sys_heap_delete_heap>); + sysPrxForUser.AddFunc(0xb2fcf2c8, bind_func(sys_heap_create_heap)); + } +} sysPrxForUser_init; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp new file mode 100644 index 0000000000..6c0c9f0633 --- /dev/null +++ b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" +#include "Emu/SysCalls/SC_FUNC.h" + +Module sys_fs("sys_fs", 0x000e); + +struct _sys_fs_init +{ + _sys_fs_init() + { + sys_fs.AddFunc(0x718bf5f8, bind_func(cellFsOpen)); + sys_fs.AddFunc(0x4d5ff8e2, bind_func(cellFsRead)); + sys_fs.AddFunc(0xecdcf2ab, bind_func(cellFsWrite)); + sys_fs.AddFunc(0x2cb51f0d, bind_func(cellFsClose)); + sys_fs.AddFunc(0x3f61245c, bind_func(cellFsOpendir)); + sys_fs.AddFunc(0x5c74903d, bind_func(cellFsReaddir)); + sys_fs.AddFunc(0xff42dcc3, bind_func(cellFsClosedir)); + sys_fs.AddFunc(0x7de6dced, bind_func(cellFsStat)); + sys_fs.AddFunc(0xef3efa34, bind_func(cellFsFstat)); + sys_fs.AddFunc(0xba901fe6, bind_func(cellFsMkdir)); + sys_fs.AddFunc(0xf12eecc8, bind_func(cellFsRename)); + sys_fs.AddFunc(0x2796fdf3, bind_func(cellFsRmdir)); + sys_fs.AddFunc(0x7f4677a8, bind_func(cellFsUnlink)); + sys_fs.AddFunc(0xa397d042, bind_func(cellFsLseek)); + } +} sys_fs_init; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/sys_io.cpp b/rpcs3/Emu/SysCalls/Modules/sys_io.cpp new file mode 100644 index 0000000000..7bea344fdb --- /dev/null +++ b/rpcs3/Emu/SysCalls/Modules/sys_io.cpp @@ -0,0 +1,20 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" +#include "Emu/SysCalls/SC_FUNC.h" + +Module sys_io("sys_io", 0x0017); + +struct _sys_io_init +{ + _sys_io_init() + { + sys_io.AddFunc(0x1cf98800, bind_func(cellPadInit)); + sys_io.AddFunc(0x4d9b75d5, bind_func(cellPadEnd)); + sys_io.AddFunc(0x0d5f2c14, bind_func(cellPadClearBuf)); + sys_io.AddFunc(0x8b72cda1, bind_func(cellPadGetData)); + sys_io.AddFunc(0x6bc09c61, bind_func(cellPadGetDataExtra)); + sys_io.AddFunc(0xf65544ee, bind_func(cellPadSetActDirect)); + sys_io.AddFunc(0xa703a51d, bind_func(cellPadGetInfo2)); + sys_io.AddFunc(0x578e3c98, bind_func(cellPadSetPortSetting)); + } +} sys_io_init; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/SC_FUNC.h b/rpcs3/Emu/SysCalls/SC_FUNC.h new file mode 100644 index 0000000000..9cee74bba0 --- /dev/null +++ b/rpcs3/Emu/SysCalls/SC_FUNC.h @@ -0,0 +1,367 @@ +#pragma once + +#define RESULT(x) SC_ARGS_1 = (x) + +template +class binder_func_0 : public func_caller +{ + typedef TR (*func_t)(); + const func_t m_call; + +public: + binder_func_0(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call()); } +}; + +template<> +class binder_func_0 : public func_caller +{ + typedef void (*func_t)(); + const func_t m_call; + +public: + binder_func_0(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(); } +}; + +template +class binder_func_1 : public func_caller +{ + typedef TR (*func_t)(T1); + const func_t m_call; + +public: + binder_func_1(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_1)); } +}; + +template +class binder_func_1 : public func_caller +{ + typedef void (*func_t)(T1); + const func_t m_call; + +public: + binder_func_1(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_1); } +}; + +template +class binder_func_2 : public func_caller +{ + typedef TR (*func_t)(T1, T2); + const func_t m_call; + +public: + binder_func_2(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_2)); } +}; + +template +class binder_func_2 : public func_caller +{ + typedef void (*func_t)(T1, T2); + const func_t m_call; + +public: + binder_func_2(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_2); } +}; + +template +class binder_func_3 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3); + const func_t m_call; + +public: + binder_func_3(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_3)); } +}; + +template +class binder_func_3 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3); + const func_t m_call; + +public: + binder_func_3(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_3); } +}; + +template +class binder_func_4 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4); + const func_t m_call; + +public: + binder_func_4(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_4)); } +}; + +template +class binder_func_4 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4); + const func_t m_call; + +public: + binder_func_4(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_4); } +}; + +template +class binder_func_5 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4, T5); + const func_t m_call; + +public: + binder_func_5(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_5)); } +}; + +template +class binder_func_5 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4, T5); + const func_t m_call; + +public: + binder_func_5(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_5); } +}; + +template +class binder_func_6 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4, T5, T6); + const func_t m_call; + +public: + binder_func_6(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_6)); } +}; + +template +class binder_func_6 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4, T5, T6); + const func_t m_call; + +public: + binder_func_6(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_6); } +}; + +template +class binder_func_7 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4, T5, T6, T7); + const func_t m_call; + +public: + binder_func_7(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_7)); } +}; + +template +class binder_func_7 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4, T5, T6, T7); + const func_t m_call; + +public: + binder_func_7(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_7); } +}; + +template +class binder_func_8 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8); + const func_t m_call; + +public: + binder_func_8(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_8)); } +}; + +template +class binder_func_8 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8); + const func_t m_call; + +public: + binder_func_8(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_8); } +}; + +template +class binder_func_9 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8, T9); + const func_t m_call; + +public: + binder_func_9(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_9)); } +}; + +template +class binder_func_9 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8, T9); + const func_t m_call; + +public: + binder_func_9(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_9); } +}; + +template +class binder_func_10 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); + const func_t m_call; + +public: + binder_func_10(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_10)); } +}; + +template +class binder_func_10 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); + const func_t m_call; + +public: + binder_func_10(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_10); } +}; + +template +class binder_func_11 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); + const func_t m_call; + +public: + binder_func_11(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_11)); } +}; + +template +class binder_func_11 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); + const func_t m_call; + +public: + binder_func_11(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_11); } +}; + +template +class binder_func_12 : public func_caller +{ + typedef TR (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); + const func_t m_call; + +public: + binder_func_12(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); RESULT(m_call(SC_ARGS_12)); } +}; + +template +class binder_func_12 : public func_caller +{ + typedef void (*func_t)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); + const func_t m_call; + +public: + binder_func_12(func_t call) : func_caller(), m_call(call) {} + virtual void operator()() { declCPU(); m_call(SC_ARGS_12); } +}; + +template +func_caller* bind_func(TR (*call)()) +{ + return new binder_func_0(call); +} + +template +func_caller* bind_func(TR (*call)(T1)) +{ + return new binder_func_1(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2)) +{ + return new binder_func_2(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3)) +{ + return new binder_func_3(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4)) +{ + return new binder_func_4(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5)) +{ + return new binder_func_5(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5, T6)) +{ + return new binder_func_6(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5, T6, T7)) +{ + return new binder_func_7(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5, T6, T7, T8)) +{ + return new binder_func_8(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5, T6, T7, T8, T9)) +{ + return new binder_func_9(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)) +{ + return new binder_func_10(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11)) +{ + return new binder_func_11(call); +} + +template +func_caller* bind_func(TR (*call)(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12)) +{ + return new binder_func_12(call); +} \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/SysCalls.cpp b/rpcs3/Emu/SysCalls/SysCalls.cpp new file mode 100644 index 0000000000..b81c86d8c2 --- /dev/null +++ b/rpcs3/Emu/SysCalls/SysCalls.cpp @@ -0,0 +1,404 @@ +#include "stdafx.h" +#include "SysCalls.h" +#include "SC_FUNC.h" +#include + +ArrayF g_modules_funcs_list; +ArrayF g_modules_list; + +bool IsLoadedFunc(u32 id) +{ + for(u32 i=0; i -//#include #include "ErrorCodes.h" //#define SYSCALLS_DEBUG -class SysCallBase +#define declCPU PPUThread& CPU = GetCurrentPPUThread + +class func_caller +{ +public: + virtual void operator()() = 0; +}; + +static func_caller *null_func = nullptr; + +//TODO +struct ModuleFunc +{ + u32 id; + func_caller* func; + + ModuleFunc(u32 id, func_caller* func) + : id(id) + , func(func) + { + } +}; + +class Module +{ + const char* m_name; + const u16 m_id; + bool m_is_loaded; + +public: + Array m_funcs_list; + + Module(const char* name, u16 id); + + void Load(); + void UnLoad(); + bool Load(u32 id); + bool UnLoad(u32 id); + + void SetLoaded(bool loaded = true) + { + m_is_loaded = loaded; + } + + bool IsLoaded() const + { + return m_is_loaded; + } + + u16 GetID() const + { + return m_id; + } + + wxString GetName() const + { + return m_name; + } + +public: + void Log(const u32 id, wxString fmt, ...) + { +#ifdef SYSCALLS_DEBUG + va_list list; + va_start(list, fmt); + ConLog.Write(GetName() + wxString::Format("[%d]: ", id) + wxString::FormatV(fmt, list)); + va_end(list); +#endif + } + + void Log(wxString fmt, ...) + { +#ifdef SYSCALLS_DEBUG + va_list list; + va_start(list, fmt); + ConLog.Write(GetName() + ": " + wxString::FormatV(fmt, list)); + va_end(list); +#endif + } + + void Warning(const u32 id, wxString fmt, ...) + { +//#ifdef SYSCALLS_DEBUG + va_list list; + va_start(list, fmt); + ConLog.Warning(GetName() + wxString::Format("[%d] warning: ", id) + wxString::FormatV(fmt, list)); + va_end(list); +//#endif + } + + void Warning(wxString fmt, ...) + { +//#ifdef SYSCALLS_DEBUG + va_list list; + va_start(list, fmt); + ConLog.Warning(GetName() + " warning: " + wxString::FormatV(fmt, list)); + va_end(list); +//#endif + } + + void Error(const u32 id, wxString fmt, ...) + { + va_list list; + va_start(list, fmt); + ConLog.Error(GetName() + wxString::Format("[%d] error: ", id) + wxString::FormatV(fmt, list)); + va_end(list); + } + + void Error(wxString fmt, ...) + { + va_list list; + va_start(list, fmt); + ConLog.Error(GetName() + " error: " + wxString::FormatV(fmt, list)); + va_end(list); + } + + bool CheckId(u32 id) const + { + return Emu.GetIdManager().CheckID(id) && !Emu.GetIdManager().GetIDData(id).m_name.Cmp(GetName()); + } + + bool CheckId(u32 id, ID& _id) const + { + return Emu.GetIdManager().CheckID(id) && !(_id = Emu.GetIdManager().GetIDData(id)).m_name.Cmp(GetName()); + } + + template bool CheckId(u32 id, T*& data) + { + ID id_data; + + if(!CheckId(id, id_data)) return false; + + data = (T*)id_data.m_data; + + return true; + } + + u32 GetNewId(void* data = nullptr, u8 flags = 0) + { + return Emu.GetIdManager().GetNewID(GetName(), data, flags); + } + +//protected: + __forceinline void AddFunc(u32 id, func_caller* func) + { + m_funcs_list.Move(new ModuleFunc(id, func)); + } +}; + +static s64 null_function() { return 0; } + +bool IsLoadedFunc(u32 id); +bool CallFunc(u32 id); +bool UnloadFunc(u32 id); +void UnloadModules(); +Module* GetModuleByName(const wxString& name); +Module* GetModuleById(u16 id); + +class SysCallBase //Module { private: wxString m_module_name; + //u32 m_id; public: - SysCallBase(const wxString& name) : m_module_name(name) + SysCallBase(const wxString& name/*, u32 id*/) + : m_module_name(name) + //, m_id(id) { } - wxString GetName() { return m_module_name; } + const wxString& GetName() const { return m_module_name; } void Log(const u32 id, wxString fmt, ...) { @@ -39,22 +198,22 @@ public: void Warning(const u32 id, wxString fmt, ...) { -#ifdef SYSCALLS_DEBUG +//#ifdef SYSCALLS_DEBUG va_list list; va_start(list, fmt); ConLog.Warning(GetName() + wxString::Format("[%d] warning: ", id) + wxString::FormatV(fmt, list)); va_end(list); -#endif +//#endif } void Warning(wxString fmt, ...) { -#ifdef SYSCALLS_DEBUG +//#ifdef SYSCALLS_DEBUG va_list list; va_start(list, fmt); - ConLog.Warning(GetName() + wxString::Format(" warning: ") + wxString::FormatV(fmt, list)); + ConLog.Warning(GetName() + " warning: " + wxString::FormatV(fmt, list)); va_end(list); -#endif +//#endif } void Error(const u32 id, wxString fmt, ...) @@ -69,11 +228,38 @@ public: { va_list list; va_start(list, fmt); - ConLog.Error(GetName() + wxString::Format(" error: ") + wxString::FormatV(fmt, list)); + ConLog.Error(GetName() + " error: " + wxString::FormatV(fmt, list)); va_end(list); } + + bool CheckId(u32 id) const + { + return Emu.GetIdManager().CheckID(id) && !Emu.GetIdManager().GetIDData(id).m_name.Cmp(GetName()); + } + + bool CheckId(u32 id, ID& _id) const + { + return Emu.GetIdManager().CheckID(id) && !(_id = Emu.GetIdManager().GetIDData(id)).m_name.Cmp(GetName()); + } + + template bool CheckId(u32 id, T*& data) + { + ID id_data; + + if(!CheckId(id, id_data)) return false; + + data = (T*)id_data.m_data; + + return true; + } + + u32 GetNewId(void* data = nullptr, u8 flags = 0) + { + return Emu.GetIdManager().GetNewID(GetName(), data, flags); + } }; +/* static bool CmpPath(const wxString& path1, const wxString& path2) { return path1.Len() >= path2.Len() && path1(0, path2.Len()).CmpNoCase(path2) == 0; @@ -99,37 +285,97 @@ static wxString GetWinPath(const wxString& path) return wxFileName(Emu.m_path).GetPath() + (path[0] == '/' ? path : "/" + path); } +*/ //process extern int sys_process_getpid(); +extern int sys_process_exit(int errorcode); extern int sys_game_process_exitspawn(u32 path_addr, u32 argv_addr, u32 envp_addr, u32 data, u32 data_size, int prio, u64 flags ); +//sys_semaphore +extern int sys_semaphore_create(u32 sem_addr, u32 attr_addr, int initial_val, int max_val); +extern int sys_semaphore_destroy(u32 sem); +extern int sys_semaphore_wait(u32 sem, u64 timeout); +extern int sys_semaphore_trywait(u32 sem); +extern int sys_semaphore_post(u32 sem, int count); + +//sys_lwmutex +extern int sys_lwmutex_create(u64 lwmutex_addr, u64 lwmutex_attr_addr); +extern int sys_lwmutex_destroy(u64 lwmutex_addr); +extern int sys_lwmutex_lock(u64 lwmutex_addr, u64 timeout); +extern int sys_lwmutex_trylock(u64 lwmutex_addr); +extern int sys_lwmutex_unlock(u64 lwmutex_addr); + +//sys_cond +extern int sys_cond_create(u32 cond_addr, u32 mutex_id, u32 attr_addr); +extern int sys_cond_destroy(u32 cond_id); +extern int sys_cond_wait(u32 cond_id, u64 timeout); +extern int sys_cond_signal(u32 cond_id); +extern int sys_cond_signal_all(u32 cond_id); + +//sys_mutex +extern int sys_mutex_create(u32 mutex_id_addr, u32 attr_addr); +extern int sys_mutex_destroy(u32 mutex_id); +extern int sys_mutex_lock(u32 mutex_id, u64 timeout); +extern int sys_mutex_trylock(u32 mutex_id); +extern int sys_mutex_unlock(u32 mutex_id); + +//ppu_thread +extern int sys_ppu_thread_exit(int errorcode); +extern int sys_ppu_thread_yield(); +extern int sys_ppu_thread_join(u32 thread_id, u32 vptr_addr); +extern int sys_ppu_thread_detach(u32 thread_id); +extern void sys_ppu_thread_get_join_state(u32 isjoinable_addr); +extern int sys_ppu_thread_set_priority(u32 thread_id, int prio); +extern int sys_ppu_thread_get_priority(u32 thread_id, u32 prio_addr); +extern int sys_ppu_thread_get_stack_information(u32 info_addr); +extern int sys_ppu_thread_stop(u32 thread_id); +extern int sys_ppu_thread_restart(u32 thread_id); +extern int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u32 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr); +extern int sys_ppu_thread_get_id(); + //memory extern int sys_memory_container_create(u32 cid_addr, u32 yield_size); extern int sys_memory_container_destroy(u32 cid); extern int sys_memory_allocate(u32 size, u32 flags, u32 alloc_addr_addr); +extern int sys_memory_free(u32 start_addr); extern int sys_memory_get_user_memory_size(u32 mem_info_addr); +extern int sys_mmapper_allocate_address(u32 size, u64 flags, u32 alignment, u32 alloc_addr); +extern int sys_mmapper_allocate_memory(u32 size, u64 flags, u32 mem_id_addr); +extern int sys_mmapper_map_memory(u32 start_addr, u32 mem_id, u64 flags); //cellFs -extern int cellFsOpen(const u32 path_addr, const int flags, const u32 fd_addr, const u32 arg_addr, const u64 size); -extern int cellFsRead(const u32 fd, const u32 buf_addr, const u64 nbytes, const u32 nread_addr); -extern int cellFsWrite(const u32 fd, const u32 buf_addr, const u64 nbytes, const u32 nwrite_addr); -extern int cellFsClose(const u32 fd); -extern int cellFsOpendir(const u32 path_addr, const u32 fd_addr); -extern int cellFsReaddir(const u32 fd, const u32 dir_addr, const u32 nread_addr); -extern int cellFsClosedir(const u32 fd); -extern int cellFsStat(const u32 path_addr, const u32 sb_addr); -extern int cellFsFstat(const u32 fd, const u32 sb_addr); -extern int cellFsMkdir(const u32 path_addr, const u32 mode); -extern int cellFsRename(const u32 from_addr, const u32 to_addr); -extern int cellFsRmdir(const u32 path_addr); -extern int cellFsUnlink(const u32 path_addr); -extern int cellFsLseek(const u32 fd, const s64 offset, const u32 whence, const u32 pos_addr); +extern int cellFsOpen(u32 path_addr, int flags, u32 fd_addr, u32 arg_addr, u64 size); +extern int cellFsRead(u32 fd, u32 buf_addr, u64 nbytes, u32 nread_addr); +extern int cellFsWrite(u32 fd, u32 buf_addr, u64 nbytes, u32 nwrite_addr); +extern int cellFsClose(u32 fd); +extern int cellFsOpendir(u32 path_addr, u32 fd_addr); +extern int cellFsReaddir(u32 fd, u32 dir_addr, u32 nread_addr); +extern int cellFsClosedir(u32 fd); +extern int cellFsStat(u32 path_addr, u32 sb_addr); +extern int cellFsFstat(u32 fd, u32 sb_addr); +extern int cellFsMkdir(u32 path_addr, u32 mode); +extern int cellFsRename(u32 from_addr, u32 to_addr); +extern int cellFsRmdir(u32 path_addr); +extern int cellFsUnlink(u32 path_addr); +extern int cellFsLseek(u32 fd, s64 offset, u32 whence, u32 pos_addr); //cellVideo extern int cellVideoOutGetState(u32 videoOut, u32 deviceIndex, u32 state_addr); extern int cellVideoOutGetResolution(u32 resolutionId, u32 resolution_addr); +extern int cellVideoOutConfigure(u32 videoOut, u32 config_addr, u32 option_addr, u32 waitForEvent); +extern int cellVideoOutGetConfiguration(u32 videoOut, u32 config_addr, u32 option_addr); +extern int cellVideoOutGetNumberOfDevice(u32 videoOut); +extern int cellVideoOutGetResolutionAvailability(u32 videoOut, u32 resolutionId, u32 aspect, u32 option); + +//cellSysutil +extern int cellSysutilCheckCallback(); +extern int cellSysutilRegisterCallback(int slot, u64 func_addr, u64 userdata); +extern int cellSysutilUnregisterCallback(int slot); + +//cellMsgDialog +extern int cellMsgDialogOpen2(u32 type, u32 msgString_addr, u32 callback_addr, u32 userData, u32 extParam); //cellPad extern int cellPadInit(u32 max_connect); @@ -142,17 +388,29 @@ extern int cellPadGetInfo2(u32 info_addr); extern int cellPadSetPortSetting(u32 port_no, u32 port_setting); //cellGcm -extern int cellGcmInit(const u32 context_addr, const u32 cmdSize, const u32 ioSize, const u32 ioAddress); -extern int cellGcmGetConfiguration(const u32 config_addr); -extern int cellGcmAddressToOffset(const u32 address, const u32 offset_addr); -extern int cellGcmSetDisplayBuffer(const u8 id, const u32 offset, const u32 pitch, const u32 width, const u32 height); -extern u32 cellGcmGetLabelAddress(const u32 index); +extern int cellGcmInit(u32 context_addr, u32 cmdSize, u32 ioSize, u32 ioAddress); +extern int cellGcmMapMainMemory(u32 address, u32 size, u32 offset_addr); +extern int cellGcmCallback(u32 context_addr, u32 count); +extern int cellGcmGetConfiguration(u32 config_addr); +extern int cellGcmAddressToOffset(u32 address, u32 offset_addr); +extern int cellGcmSetDisplayBuffer(u32 id, u32 offset, u32 pitch, u32 width, u32 height); +extern u32 cellGcmGetLabelAddress(u32 index); extern u32 cellGcmGetControlRegister(); -extern int cellGcmFlush(const u32 ctx, const u8 id); -extern int cellGcmSetTile(const u8 index, const u8 location, const u32 offset, const u32 size, const u32 pitch, const u8 comp, const u16 base, const u8 bank); +extern int cellGcmFlush(u32 ctx, u32 id); +extern void cellGcmSetTile(u32 index, u32 location, u32 offset, u32 size, u32 pitch, u32 comp, u32 base, u32 bank); +extern int cellGcmBindTile(u32 index); +extern int cellGcmBindZcull(u32 index, u32 offset, u32 width, u32 height, u32 cullStart, u32 zFormat, u32 aaFormat, u32 zCullDir, u32 zCullFormat, u32 sFunc, u32 sRef, u32 sMask); extern int cellGcmGetFlipStatus(); extern int cellGcmResetFlipStatus(); -extern u32 cellGcmGetTiledPitchSize(const u32 size); +extern u32 cellGcmGetTiledPitchSize(u32 size); +extern int cellGcmSetFlipMode(u32 mode); +extern u32 cellGcmGetDefaultCommandWordSize(); +extern u32 cellGcmGetDefaultSegmentWordSize(); +extern int cellGcmSetDefaultFifoSize(u32 bufferSize, u32 segmentSize); + +//sys_tty +extern int sys_tty_read(u32 ch, u64 buf_addr, u32 len, u64 preadlen_addr); +extern int sys_tty_write(u32 ch, u64 buf_addr, u32 len, u64 pwritelen_addr); //cellResc extern int cellRescSetSrc(const int idx, const u32 src_addr); @@ -173,7 +431,7 @@ extern int sys_time_get_current_time(u32 sec_addr, u32 nsec_addr); extern s64 sys_time_get_system_time(); extern u64 sys_time_get_timebase_frequency(); -#define SC_ARGS_1 CPU.GPR[3] +#define SC_ARGS_1 CPU.GPR[3] #define SC_ARGS_2 SC_ARGS_1,CPU.GPR[4] #define SC_ARGS_3 SC_ARGS_2,CPU.GPR[5] #define SC_ARGS_4 SC_ARGS_3,CPU.GPR[6] @@ -181,11 +439,18 @@ extern u64 sys_time_get_timebase_frequency(); #define SC_ARGS_6 SC_ARGS_5,CPU.GPR[8] #define SC_ARGS_7 SC_ARGS_6,CPU.GPR[9] #define SC_ARGS_8 SC_ARGS_7,CPU.GPR[10] +#define SC_ARGS_9 SC_ARGS_8,CPU.GPR[11] +#define SC_ARGS_10 SC_ARGS_9,CPU.GPR[12] +#define SC_ARGS_11 SC_ARGS_10,CPU.GPR[13] +#define SC_ARGS_12 SC_ARGS_11,CPU.GPR[14] extern bool dump_enable; +class PPUThread; class SysCalls { + PPUThread& CPU; + public: //process int lv2ProcessGetPid(PPUThread& CPU); @@ -196,138 +461,16 @@ public: int lv2ProcessGetId(PPUThread& CPU); int lv2ProcessGetPpid(PPUThread& CPU); int lv2ProcessKill(PPUThread& CPU); - int lv2ProcessExit(PPUThread& CPU); int lv2ProcessWaitForChild2(PPUThread& CPU); int lv2ProcessGetSdkVersion(PPUThread& CPU); - //ppu thread - int lv2PPUThreadCreate(PPUThread& CPU); - int lv2PPUThreadExit(PPUThread& CPU); - int lv2PPUThreadYield(PPUThread& CPU); - int lv2PPUThreadJoin(PPUThread& CPU); - int lv2PPUThreadDetach(PPUThread& CPU); - int lv2PPUThreadGetJoinState(PPUThread& CPU); - int lv2PPUThreadSetPriority(PPUThread& CPU); - int lv2PPUThreadGetPriority(PPUThread& CPU); - int lv2PPUThreadGetStackInformation(PPUThread& CPU); - int lv2PPUThreadRename(PPUThread& CPU); - int lv2PPUThreadRecoverPageFault(PPUThread& CPU); - int lv2PPUThreadGetPageFaultContext(PPUThread& CPU); - int lv2PPUThreadGetId(PPUThread& CPU); - - //lwmutex - int Lv2LwmutexCreate(PPUThread& CPU); - int Lv2LwmutexDestroy(PPUThread& CPU); - int Lv2LwmutexLock(PPUThread& CPU); - int Lv2LwmutexTrylock(PPUThread& CPU); - int Lv2LwmutexUnlock(PPUThread& CPU); - - //tty - int lv2TtyRead(PPUThread& CPU); - int lv2TtyWrite(PPUThread& CPU); +protected: + SysCalls(PPUThread& cpu); + ~SysCalls(); public: - SysCalls()// : CPU(cpu) - { - } - - ~SysCalls() - { - Close(); - } - - void Close() - { - } - - s64 DoSyscall(u32 code, PPUThread& CPU) - { - switch(code) - { - //=== lv2 === - //process - case 1: return sys_process_getpid(); - case 2: return lv2ProcessWaitForChild(CPU); - case 3: return lv2ProcessExit(CPU); - case 4: return lv2ProcessGetStatus(CPU); - case 5: return lv2ProcessDetachChild(CPU); - case 12: return lv2ProcessGetNumberOfObject(CPU); - case 13: return lv2ProcessGetId(CPU); - case 18: return lv2ProcessGetPpid(CPU); - case 19: return lv2ProcessKill(CPU); - case 22: return lv2ProcessExit(CPU); - case 23: return lv2ProcessWaitForChild2(CPU); - case 25: return lv2ProcessGetSdkVersion(CPU); - //ppu thread - //case ?: return lv2PPUThreadCreate(CPU); - //case ?: return lv2PPUThreadExit(CPU); - case 43: return lv2PPUThreadYield(CPU); - case 44: return lv2PPUThreadJoin(CPU); - case 45: return lv2PPUThreadDetach(CPU); - case 46: return lv2PPUThreadGetJoinState(CPU); - case 47: return lv2PPUThreadSetPriority(CPU); - case 48: return lv2PPUThreadGetPriority(CPU); - case 49: return lv2PPUThreadGetStackInformation(CPU); - case 56: return lv2PPUThreadRename(CPU); - case 57: return lv2PPUThreadRecoverPageFault(CPU); - case 58: return lv2PPUThreadGetPageFaultContext(CPU); - //case ?: return lv2PPUThreadGetId(CPU); - //lwmutex - case 95: return Lv2LwmutexCreate(CPU); - case 96: return Lv2LwmutexDestroy(CPU); - case 97: return Lv2LwmutexLock(CPU); - case 98: return Lv2LwmutexTrylock(CPU); - case 99: return Lv2LwmutexUnlock(CPU); - //timer - case 141: - case 142: - //wxSleep(Emu.GetCPU().GetThreads().GetCount() > 1 ? 1 : /*SC_ARGS_1*/1); - return 0; - //time - case 145: return sys_time_get_current_time(SC_ARGS_2); - case 146: return sys_time_get_system_time(); - case 147: return sys_time_get_timebase_frequency(); - //sys_spu - case 160: return sys_raw_spu_create(SC_ARGS_2); - case 169: return sys_spu_initialize(SC_ARGS_2); - case 170: return sys_spu_thread_group_create(SC_ARGS_4); - //memory - case 324: return sys_memory_container_create(SC_ARGS_2); - case 325: return sys_memory_container_destroy(SC_ARGS_1); - case 348: return sys_memory_allocate(SC_ARGS_3); - case 352: return sys_memory_get_user_memory_size(SC_ARGS_1); - //tty - case 402: return lv2TtyRead(CPU); - case 403: return lv2TtyWrite(CPU); - //file system - case 801: return cellFsOpen(SC_ARGS_5); - case 802: return cellFsRead(SC_ARGS_4); - case 803: return cellFsWrite(SC_ARGS_4); - case 804: return cellFsClose(SC_ARGS_1); - case 805: return cellFsOpendir(SC_ARGS_2); - case 806: return cellFsReaddir(SC_ARGS_3); - case 807: return cellFsClosedir(SC_ARGS_1); - case 809: return cellFsFstat(SC_ARGS_2); - case 811: return cellFsMkdir(SC_ARGS_2); - case 812: return cellFsRename(SC_ARGS_2); - case 813: return cellFsRmdir(SC_ARGS_1); - case 818: return cellFsLseek(SC_ARGS_4); - case 988: - ConLog.Warning("SysCall 988! r3: 0x%llx, r4: 0x%llx, pc: 0x%llx", - CPU.GPR[3], CPU.GPR[4], CPU.PC); - return 0; - - case 999: - dump_enable = !dump_enable; - ConLog.Warning("Dump %s", dump_enable ? "enabled" : "disabled"); - return 0; - } - - ConLog.Error("Unknown syscall: %d - %08x", code, code); - return 0; - } - - s64 DoFunc(const u32 id, PPUThread& CPU); + s64 DoSyscall(u32 code); + s64 DoFunc(const u32 id); }; -extern SysCalls SysCallsManager; \ No newline at end of file +//extern SysCalls SysCallsManager; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp new file mode 100644 index 0000000000..723fe1bfc2 --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" +#include "SC_Mutex.h" + +SysCallBase sys_cond("sys_cond"); +extern SysCallBase sys_mtx; + +struct condition_attr +{ + u32 pshared; + int flags; + u64 ipc_key; + char name[8]; +}; + +struct condition +{ + wxCondition cond; + condition_attr attr; + + condition(wxMutex& mtx, const condition_attr& attr) + : cond(mtx) + , attr(attr) + { + } +}; + +int sys_cond_create(u32 cond_addr, u32 mutex_id, u32 attr_addr) +{ + sys_cond.Log("sys_cond_create(cond_addr=0x%x, mutex_id=0x%x, attr_addr=%d)", + cond_addr, mutex_id, attr_addr); + + if(!Memory.IsGoodAddr(cond_addr) || !Memory.IsGoodAddr(attr_addr)) return CELL_EFAULT; + + condition_attr attr = (condition_attr&)Memory[attr_addr]; + + attr.pshared = re(attr.pshared); + attr.ipc_key = re(attr.ipc_key); + attr.flags = re(attr.flags); + + sys_cond.Log("*** pshared = %d", attr.pshared); + sys_cond.Log("*** ipc_key = 0x%llx", attr.ipc_key); + sys_cond.Log("*** flags = 0x%x", attr.flags); + sys_cond.Log("*** name = %s", attr.name); + + mutex* mtx_data = nullptr; + if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; + + Memory.Write32(cond_addr, sys_cond.GetNewId(new condition(mtx_data->mtx, attr))); + + return CELL_OK; +} + +int sys_cond_destroy(u32 cond_id) +{ + sys_cond.Log("sys_cond_destroy(cond_id=0x%x)", cond_id); + + if(!sys_cond.CheckId(cond_id)) return CELL_ESRCH; + + Emu.GetIdManager().RemoveID(cond_id); + return CELL_OK; +} + +int sys_cond_wait(u32 cond_id, u64 timeout) +{ + sys_cond.Log("sys_cond_wait(cond_id=0x%x, timeout=0x%llx)", cond_id, timeout); + + condition* cond_data = nullptr; + if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH; + + cond_data->cond.WaitTimeout(timeout ? timeout : INFINITE); + + return CELL_OK; +} + +int sys_cond_signal(u32 cond_id) +{ + sys_cond.Log("sys_cond_signal(cond_id=0x%x)", cond_id); + + condition* cond_data = nullptr; + if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH; + + cond_data->cond.Signal(); + + return CELL_OK; +} + +int sys_cond_signal_all(u32 cond_id) +{ + sys_cond.Log("sys_cond_signal_all(cond_id=0x%x)", cond_id); + + condition* cond_data = nullptr; + if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH; + + cond_data->cond.Broadcast(); + + return CELL_OK; +} \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp b/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp index c2f728bbeb..55e88107ef 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" -typedef u64 GPRtype; enum Lv2FsOflag { LV2_O_RDONLY = 000000, @@ -83,26 +82,22 @@ enum FsDirentType CELL_FS_TYPE_SYMLINK = 3, }; -SysCallBase sc_log("cellFs"); +extern Module sys_fs; -wxString ReadString(const u32 addr) -{ - return GetWinPath(Memory.ReadString(addr)); -} - -int cellFsOpen(const u32 path_addr, const int flags, const u32 fd_addr, const u32 arg_addr, const u64 size) +int cellFsOpen(u32 path_addr, int flags, u32 fd_addr, u32 arg_addr, u64 size) { const wxString& path = Memory.ReadString(path_addr); - sc_log.Log("cellFsOpen(path: %s, flags: 0x%x, fd_addr: 0x%x, arg_addr: 0x%x, size: 0x%llx)", + sys_fs.Log("cellFsOpen(path: %s, flags: 0x%x, fd_addr: 0x%x, arg_addr: 0x%x, size: 0x%llx)", path, flags, fd_addr, arg_addr, size); - const wxString& ppath = GetWinPath(path); + const wxString& ppath = path; //ConLog.Warning("path: %s [%s]", ppath, path); s32 _oflags = flags; if(flags & LV2_O_CREAT) { _oflags &= ~LV2_O_CREAT; + /* //create path for(uint p=1;pIsOpened()) { - sc_log.Error("'%s' not found! flags: 0x%08x", ppath, flags); + sys_fs.Error("'%s' not found! flags: 0x%08x", ppath, flags); + delete stream; + return CELL_ENOENT; } - Memory.Write32(fd_addr, - Emu.GetIdManager().GetNewID(sc_log.GetName(), new wxFile(ppath, o_mode), flags)); + Memory.Write32(fd_addr, sys_fs.GetNewId(stream, flags)); return CELL_OK; } -int cellFsRead(const u32 fd, const u32 buf_addr, const u64 nbytes, const u32 nread_addr) +int cellFsRead(u32 fd, u32 buf_addr, u64 nbytes, u32 nread_addr) { - sc_log.Log("cellFsRead(fd: %d, buf_addr: 0x%x, nbytes: 0x%llx, nread_addr: 0x%x)", + sys_fs.Log("cellFsRead(fd: %d, buf_addr: 0x%x, nbytes: 0x%llx, nread_addr: 0x%x)", fd, buf_addr, nbytes, nread_addr); - if(!Emu.GetIdManager().CheckID(fd)) return CELL_ESRCH; - const ID& id = Emu.GetIdManager().GetIDData(fd); - if(!!id.m_name.Cmp(sc_log.GetName())) return CELL_ESRCH; - wxFile& file = *(wxFile*)id.m_data; + ID id; + if(!sys_fs.CheckId(fd, id)) return CELL_ESRCH; + vfsStream& file = *(vfsStream*)id.m_data; Memory.Write64NN(nread_addr, file.Read(Memory.GetMemFromAddr(buf_addr), nbytes)); return CELL_OK; } -int cellFsWrite(const u32 fd, const u32 buf_addr, const u64 nbytes, const u32 nwrite_addr) +int cellFsWrite(u32 fd, u32 buf_addr, u64 nbytes, u32 nwrite_addr) { - sc_log.Log("cellFsWrite(fd: %d, buf_addr: 0x%x, nbytes: 0x%llx, nwrite_addr: 0x%x)", + sys_fs.Log("cellFsWrite(fd: %d, buf_addr: 0x%x, nbytes: 0x%llx, nwrite_addr: 0x%x)", fd, buf_addr, nbytes, nwrite_addr); - if(!Emu.GetIdManager().CheckID(fd)) return CELL_ESRCH; - const ID& id = Emu.GetIdManager().GetIDData(fd); - if(!!id.m_name.Cmp(sc_log.GetName())) return CELL_ESRCH; - wxFile& file = *(wxFile*)id.m_data; + ID id; + if(!sys_fs.CheckId(fd, id)) return CELL_ESRCH; + vfsStream& file = *(vfsStream*)id.m_data; Memory.Write64NN(nwrite_addr, file.Write(Memory.GetMemFromAddr(buf_addr), nbytes)); return CELL_OK; } -int cellFsClose(const u32 fd) +int cellFsClose(u32 fd) { - sc_log.Log("cellFsClose(fd: %d)", fd); - if(!Emu.GetIdManager().CheckID(fd)) return CELL_ESRCH; - const ID& id = Emu.GetIdManager().GetIDData(fd); - if(!!id.m_name.Cmp(sc_log.GetName())) return CELL_ESRCH; - wxFile& file = *(wxFile*)id.m_data; + sys_fs.Log("cellFsClose(fd: %d)", fd); + ID id; + if(!sys_fs.CheckId(fd, id)) return CELL_ESRCH; + vfsStream& file = *(vfsStream*)id.m_data; file.Close(); Emu.GetIdManager().RemoveID(fd); return CELL_OK; } -int cellFsOpendir(const u32 path_addr, const u32 fd_addr) +int cellFsOpendir(u32 path_addr, u32 fd_addr) { const wxString& path = Memory.ReadString(path_addr); - sc_log.Error("cellFsOpendir(path: %s, fd_addr: 0x%x)", path, fd_addr); + sys_fs.Error("cellFsOpendir(path_addr: 0x%x(%s), fd_addr: 0x%x)", path_addr, path, fd_addr); + if(!Memory.IsGoodAddr(path_addr, sizeof(u32)) || !Memory.IsGoodAddr(fd_addr, sizeof(u32))) return CELL_EFAULT; + return CELL_OK; } -int cellFsReaddir(const u32 fd, const u32 dir_addr, const u32 nread_addr) +int cellFsReaddir(u32 fd, u32 dir_addr, u32 nread_addr) { - sc_log.Error("cellFsReaddir(fd: %d, dir_addr: 0x%x, nread_addr: 0x%x)", fd, dir_addr, nread_addr); + sys_fs.Error("cellFsReaddir(fd: %d, dir_addr: 0x%x, nread_addr: 0x%x)", fd, dir_addr, nread_addr); return CELL_OK; } -int cellFsClosedir(const u32 fd) +int cellFsClosedir(u32 fd) { - sc_log.Error("cellFsClosedir(fd: %d)", fd); + sys_fs.Error("cellFsClosedir(fd: %d)", fd); return CELL_OK; } int cellFsStat(const u32 path_addr, const u32 sb_addr) { - const wxString& path = ReadString(path_addr); - sc_log.Log("cellFsFstat(path: %s, sb_addr: 0x%x)", path, sb_addr); + const wxString& path = Memory.ReadString(path_addr); + sys_fs.Log("cellFsFstat(path: %s, sb_addr: 0x%x)", path, sb_addr); if(!wxFileExists(path)) return CELL_ENOENT; @@ -275,11 +274,10 @@ int cellFsStat(const u32 path_addr, const u32 sb_addr) int cellFsFstat(u32 fd, u32 sb_addr) { - sc_log.Log("cellFsFstat(fd: %d, sb_addr: 0x%x)", fd, sb_addr); - if(!Emu.GetIdManager().CheckID(fd)) return CELL_ESRCH; - const ID& id = Emu.GetIdManager().GetIDData(fd); - if(!!id.m_name.Cmp(sc_log.GetName())) return CELL_ESRCH; - wxFile& file = *(wxFile*)id.m_data; + sys_fs.Log("cellFsFstat(fd: %d, sb_addr: 0x%x)", fd, sb_addr); + ID id; + if(!sys_fs.CheckId(fd, id)) return CELL_ESRCH; + vfsStream& file = *(vfsStream*)id.m_data; Lv2FsStat stat; stat.st_mode = @@ -293,7 +291,7 @@ int cellFsFstat(u32 fd, u32 sb_addr) stat.st_atime = 0; //TODO stat.st_mtime = 0; //TODO stat.st_ctime = 0; //TODO - stat.st_size = file.Length(); + stat.st_size = file.GetSize(); stat.st_blksize = 4096; mem_class_t stat_c(sb_addr); @@ -309,59 +307,58 @@ int cellFsFstat(u32 fd, u32 sb_addr) return CELL_OK; } -int cellFsMkdir(const u32 path_addr, const u32 mode) +int cellFsMkdir(u32 path_addr, u32 mode) { - const wxString& path = ReadString(path_addr); - sc_log.Log("cellFsMkdir(path: %s, mode: 0x%x)", path, mode); + const wxString& path = Memory.ReadString(path_addr); + sys_fs.Log("cellFsMkdir(path: %s, mode: 0x%x)", path, mode); if(wxDirExists(path)) return CELL_EEXIST; if(!wxMkdir(path)) return CELL_EBUSY; return CELL_OK; } -int cellFsRename(const u32 from_addr, const u32 to_addr) +int cellFsRename(u32 from_addr, u32 to_addr) { - const wxString& from = ReadString(from_addr); - const wxString& to = ReadString(to_addr); - sc_log.Log("cellFsRename(from: %s, to: %s)", from, to); + const wxString& from = Memory.ReadString(from_addr); + const wxString& to = Memory.ReadString(to_addr); + sys_fs.Log("cellFsRename(from: %s, to: %s)", from, to); if(!wxFileExists(from)) return CELL_ENOENT; if(wxFileExists(to)) return CELL_EEXIST; if(!wxRenameFile(from, to)) return CELL_EBUSY; return CELL_OK; } -int cellFsRmdir(const u32 path_addr) +int cellFsRmdir(u32 path_addr) { - const wxString& path = ReadString(path_addr); - sc_log.Log("cellFsRmdir(path: %s)", path); + const wxString& path = Memory.ReadString(path_addr); + sys_fs.Log("cellFsRmdir(path: %s)", path); if(!wxDirExists(path)) return CELL_ENOENT; if(!wxRmdir(path)) return CELL_EBUSY; return CELL_OK; } -int cellFsUnlink(const u32 path_addr) +int cellFsUnlink(u32 path_addr) { - const wxString& path = ReadString(path_addr); - sc_log.Error("cellFsUnlink(path: %s)", path); + const wxString& path = Memory.ReadString(path_addr); + sys_fs.Error("cellFsUnlink(path: %s)", path); return CELL_OK; } -int cellFsLseek(const u32 fd, const s64 offset, const u32 whence, const u32 pos_addr) +int cellFsLseek(u32 fd, s64 offset, u32 whence, u32 pos_addr) { - wxSeekMode seek_mode; - sc_log.Log("cellFsLseek(fd: %d, offset: 0x%llx, whence: %d, pos_addr: 0x%x)", fd, offset, whence, pos_addr); + vfsSeekMode seek_mode; + sys_fs.Log("cellFsLseek(fd: %d, offset: 0x%llx, whence: %d, pos_addr: 0x%x)", fd, offset, whence, pos_addr); switch(whence) { - case LV2_SEEK_SET: seek_mode = wxFromStart; break; - case LV2_SEEK_CUR: seek_mode = wxFromCurrent; break; - case LV2_SEEK_END: seek_mode = wxFromEnd; break; + case LV2_SEEK_SET: seek_mode = vfsSeekSet; break; + case LV2_SEEK_CUR: seek_mode = vfsSeekCur; break; + case LV2_SEEK_END: seek_mode = vfsSeekEnd; break; default: - sc_log.Error(fd, "Unknown seek whence! (%d)", whence); + sys_fs.Error(fd, "Unknown seek whence! (%d)", whence); return CELL_EINVAL; } - if(!Emu.GetIdManager().CheckID(fd)) return CELL_ESRCH; - const ID& id = Emu.GetIdManager().GetIDData(fd); - if(!!id.m_name.Cmp(sc_log.GetName())) return CELL_ESRCH; - wxFile& file = *(wxFile*)id.m_data; + ID id; + if(!sys_fs.CheckId(fd, id)) return CELL_ESRCH; + vfsStream& file = *(vfsStream*)id.m_data; Memory.Write64(pos_addr, file.Seek(offset, seek_mode)); return CELL_OK; } \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp index afedd391b0..d01f4e426a 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp @@ -2,15 +2,24 @@ #include "Emu/SysCalls/SysCalls.h" #include "Emu/GS/GCM.h" -SysCallBase sc_gcm("cellGcm"); +extern Module cellGcmSys; CellGcmConfig current_config; CellGcmContextData current_context; gcmInfo gcm_info; -int cellGcmInit(const u32 context_addr, const u32 cmdSize, const u32 ioSize, const u32 ioAddress) +int cellGcmMapMainMemory(u32 address, u32 size, u32 offset_addr) { - sc_gcm.Log("cellGcmInit(context_addr=0x%x,cmdSize=0x%x,ioSize=0x%x,ioAddress=0x%x)", context_addr, cmdSize, ioSize, ioAddress); + cellGcmSys.Warning("cellGcmMapMainMemory(address=0x%x,size=0x%x,offset_addr=0x%x)", address, size, offset_addr); + if(!Memory.IsGoodAddr(offset_addr, sizeof(u32))) return CELL_EFAULT; + Memory.Write32(offset_addr, address & 0xffff); + + return CELL_OK; +} + +int cellGcmInit(u32 context_addr, u32 cmdSize, u32 ioSize, u32 ioAddress) +{ + cellGcmSys.Log("cellGcmInit(context_addr=0x%x,cmdSize=0x%x,ioSize=0x%x,ioAddress=0x%x)", context_addr, cmdSize, ioSize, ioAddress); const u32 local_size = 0xf900000; //TODO const u32 local_addr = Memory.RSXFBMem.GetStartAddr(); @@ -38,7 +47,7 @@ int cellGcmInit(const u32 context_addr, const u32 cmdSize, const u32 ioSize, con Memory.WriteData(gcm_info.context_addr, current_context); Memory.Write32(context_addr, gcm_info.context_addr); - CellGcmControl& ctrl = *(CellGcmControl*)Memory.GetMemFromAddr(gcm_info.control_addr); + CellGcmControl& ctrl = (CellGcmControl&)Memory[gcm_info.control_addr]; ctrl.put = 0; ctrl.get = 0; ctrl.ref = -1; @@ -48,16 +57,40 @@ int cellGcmInit(const u32 context_addr, const u32 cmdSize, const u32 ioSize, con return CELL_OK; } -int cellGcmGetConfiguration(const u32 config_addr) +int cellGcmCallback(u32 context_addr, u32 count) { - sc_gcm.Log("cellGcmGetConfiguration(config_addr=0x%x)", config_addr); + GSLockCurrent gslock(GS_LOCK_WAIT_FLUSH); + + CellGcmContextData& ctx = (CellGcmContextData&)Memory[context_addr]; + CellGcmControl& ctrl = (CellGcmControl&)Memory[gcm_info.control_addr]; + + const s32 res = re(ctx.current) - re(ctx.begin) - re(ctrl.put); + + if(res > 0) memcpy(&Memory[re(ctx.begin)], &Memory[re(ctx.current) - res], res); + + ctx.current = re(re(ctx.begin) + res); + + ctrl.put = re(res); + ctrl.get = 0; + + return CELL_OK; +} + +int cellGcmGetConfiguration(u32 config_addr) +{ + cellGcmSys.Log("cellGcmGetConfiguration(config_addr=0x%x)", config_addr); + + if(!Memory.IsGoodAddr(config_addr, sizeof(CellGcmConfig))) return CELL_EFAULT; + Memory.WriteData(config_addr, current_config); return CELL_OK; } -int cellGcmAddressToOffset(const u32 address, const u32 offset_addr) +int cellGcmAddressToOffset(u32 address, u32 offset_addr) { - sc_gcm.Log("cellGcmAddressToOffset(address=0x%x,offset_addr=0x%x)", address, offset_addr); + cellGcmSys.Log("cellGcmAddressToOffset(address=0x%x,offset_addr=0x%x)", address, offset_addr); + if(!Memory.IsGoodAddr(offset_addr, sizeof(u32))) return CELL_EFAULT; + Memory.Write32(offset_addr, Memory.RSXFBMem.IsInMyRange(address) ? address - Memory.RSXFBMem.GetStartAddr() @@ -66,9 +99,9 @@ int cellGcmAddressToOffset(const u32 address, const u32 offset_addr) return CELL_OK; } -int cellGcmSetDisplayBuffer(const u8 id, const u32 offset, const u32 pitch, const u32 width, const u32 height) +int cellGcmSetDisplayBuffer(u32 id, u32 offset, u32 pitch, u32 width, u32 height) { - sc_gcm.Log("cellGcmSetDisplayBuffer(id=0x%x,offset=0x%x,pitch=%d,width=%d,height=%d)", + cellGcmSys.Warning("cellGcmSetDisplayBuffer(id=0x%x,offset=0x%x,pitch=%d,width=%d,height=%d)", id, offset, width ? pitch/width : pitch, width, height); if(id > 1) return CELL_EINVAL; @@ -81,22 +114,22 @@ int cellGcmSetDisplayBuffer(const u8 id, const u32 offset, const u32 pitch, cons return CELL_OK; } -u32 cellGcmGetLabelAddress(const u32 index) +u32 cellGcmGetLabelAddress(u32 index) { - sc_gcm.Log("cellGcmGetLabelAddress(index=%d)", index); + cellGcmSys.Log("cellGcmGetLabelAddress(index=%d)", index); return Memory.RSXCMDMem.GetStartAddr() + 0x10 * index; } u32 cellGcmGetControlRegister() { - sc_gcm.Log("cellGcmGetControlRegister()"); + cellGcmSys.Log("cellGcmGetControlRegister()"); return gcm_info.control_addr; } -int cellGcmFlush(const u32 ctx, const u8 id) +int cellGcmFlush(u32 ctx, u32 id) { - sc_gcm.Log("cellGcmFlush(ctx=0x%x, id=0x%x)", ctx, id); + cellGcmSys.Log("cellGcmFlush(ctx=0x%x, id=0x%x)", ctx, id); if(id > 1) return CELL_EINVAL; Emu.GetGSManager().GetRender().Draw(); @@ -104,12 +137,25 @@ int cellGcmFlush(const u32 ctx, const u8 id) return CELL_OK; } -int cellGcmSetTile(const u8 index, const u8 location, const u32 offset, const u32 size, - const u32 pitch, const u8 comp, const u16 base, const u8 bank) +void cellGcmSetTile(u32 index, u32 location, u32 offset, u32 size, u32 pitch, u32 comp, u32 base, u32 bank) { - sc_gcm.Log("cellGcmSetTile(index=%d, location=%d, offset=0x%x, size=0x%x, pitch=0x%x, comp=0x%x, base=0x%x, bank=0x%x)", + cellGcmSys.Warning("cellGcmSetTile(index=%d, location=%d, offset=0x%x, size=0x%x, pitch=0x%x, comp=0x%x, base=0x%x, bank=0x%x)", index, location, offset, size, pitch, comp, base, bank); + //return CELL_OK; +} + +int cellGcmBindTile(u32 index) +{ + cellGcmSys.Warning("TODO: cellGcmBindTile(index=%d)", index); + + return CELL_OK; +} + +int cellGcmBindZcull(u32 index, u32 offset, u32 width, u32 height, u32 cullStart, u32 zFormat, u32 aaFormat, u32 zCullDir, u32 zCullFormat, u32 sFunc, u32 sRef, u32 sMask) +{ + cellGcmSys.Warning("TODO: cellGcmBindZcull(index=%d, offset=0x%x, width=%d, height=%d, cullStart=0x%x, zFormat=0x%x, aaFormat=0x%x, zCullDir=0x%x, zCullFormat=0x%x, sFunc=0x%x, sRef=0x%x, sMask=0x%x)", index, offset, width, height, cullStart, zFormat, aaFormat, zCullDir, zCullFormat, sFunc, sRef, sMask); + return CELL_OK; } @@ -121,13 +167,51 @@ int cellGcmGetFlipStatus() int cellGcmResetFlipStatus() { Emu.GetGSManager().GetRender().m_flip_status = 1; + return CELL_OK; } -u32 cellGcmGetTiledPitchSize(const u32 size) +int cellGcmSetFlipMode(u32 mode) +{ + cellGcmSys.Warning("cellGcmSetFlipMode(mode=%d)", mode); + + switch(mode) + { + case CELL_GCM_DISPLAY_HSYNC: + case CELL_GCM_DISPLAY_VSYNC: + case CELL_GCM_DISPLAY_HSYNC_WITH_NOISE: + Emu.GetGSManager().GetRender().m_flip_mode = mode; + break; + + default: + return CELL_EINVAL; + } + + return CELL_OK; +} + +u32 cellGcmGetTiledPitchSize(u32 size) { //TODO - sc_gcm.Log("cellGcmGetTiledPitchSize(size=%d)", size); + cellGcmSys.Warning("cellGcmGetTiledPitchSize(size=%d)", size); return size; -} \ No newline at end of file +} + +u32 cellGcmGetDefaultCommandWordSize() +{ + cellGcmSys.Warning("cellGcmGetDefaultCommandWordSize()"); + return 0x400; +} + +u32 cellGcmGetDefaultSegmentWordSize() +{ + cellGcmSys.Warning("cellGcmGetDefaultSegmentWordSize()"); + return 0x100; +} + +int cellGcmSetDefaultFifoSize(u32 bufferSize, u32 segmentSize) +{ + cellGcmSys.Warning("cellGcmSetDefaultFifoSize(bufferSize=0x%x, segmentSize=0x%x)", bufferSize, segmentSize); + return CELL_OK; +} diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp index 69d782c1d7..99dba6cfca 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp @@ -29,77 +29,85 @@ struct lwmutex_attr char name[8]; }; -SysCallBase sc_lwmutex("Lwmutex"); +SysCallBase sc_lwmutex("sys_wmutex"); -//TODO -int SysCalls::Lv2LwmutexCreate(PPUThread& CPU) +int sys_lwmutex_create(u64 lwmutex_addr, u64 lwmutex_attr_addr) { - /* - //int sys_lwmutex_create(sys_lwmutex_t *lwmutex, sys_lwmutex_attribute_t *attr) - const u64 lwmutex_addr = CPU.GPR[3]; - const u64 lwmutex_attr_addr = CPU.GPR[4]; + lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; + lwmutex_attr& lmtx_attr = (lwmutex_attr&)Memory[lwmutex_attr_addr]; - //ConLog.Write("Lv2LwmutexCreate[r3: 0x%llx, r4: 0x%llx]", lwmutex_addr, lwmutex_attr_addr); + //sc_lwmutex.Warning("sys_lwmutex_create(lwmutex_addr = 0x%llx, lwmutex_attr_addr = 0x%llx)", lwmutex_addr, lwmutex_attr_addr); - lwmutex& lmtx = *(lwmutex*)Memory.GetMemFromAddr(lwmutex_addr); - lwmutex_attr& lmtx_attr = *(lwmutex_attr*)Memory.GetMemFromAddr(lwmutex_attr_addr); - - lmtx.lock_var.info.owner = CPU.GetId(); - lmtx.attribute = Emu.GetIdManager().GetNewID(wxString::Format("Lwmutex[%s]", lmtx_attr.name), NULL, lwmutex_addr); - */ + lmtx.lock_var.info.owner = 0; + lmtx.lock_var.info.waiter = 0; + + lmtx.attribute = Emu.GetIdManager().GetNewID(wxString::Format("lwmutex[%s]", lmtx_attr.name), nullptr, lwmutex_addr); /* ConLog.Write("r3:"); - ConLog.Write("*** lock_var[owner: 0x%x, waiter: 0x%x]", lmtx.lock_var.info.owner, lmtx.lock_var.info.waiter); - ConLog.Write("*** attribute: 0x%x", lmtx.attribute); - ConLog.Write("*** recursive_count: 0x%x", lmtx.recursive_count); - ConLog.Write("*** sleep_queue: 0x%x", lmtx.sleep_queue); + ConLog.Write("*** lock_var[owner: 0x%x, waiter: 0x%x]", re(lmtx.lock_var.info.owner), re(lmtx.lock_var.info.waiter)); + ConLog.Write("*** attribute: 0x%x", re(lmtx.attribute)); + ConLog.Write("*** recursive_count: 0x%x", re(lmtx.recursive_count)); + ConLog.Write("*** sleep_queue: 0x%x", re(lmtx.sleep_queue)); ConLog.Write("r4:"); - ConLog.Write("*** attr_protocol: 0x%x", lmtx_attr.attr_protocol); - ConLog.Write("*** attr_recursive: 0x%x", lmtx_attr.attr_recursive); + ConLog.Write("*** attr_protocol: 0x%x", re(lmtx_attr.attr_protocol)); + ConLog.Write("*** attr_recursive: 0x%x", re(lmtx_attr.attr_recursive)); ConLog.Write("*** name: %s", lmtx_attr.name); */ - - return 0; + return CELL_OK; } -int SysCalls::Lv2LwmutexDestroy(PPUThread& CPU) +int sys_lwmutex_destroy(u64 lwmutex_addr) { - /* - const u64 lwmutex_addr = CPU.GPR[3]; - //ConLog.Write("Lv2LwmutexDestroy[r3: 0x%llx]", lwmutex_addr); + //sc_lwmutex.Warning("sys_lwmutex_destroy(lwmutex_addr = 0x%llx)", lwmutex_addr); - lwmutex& lmtx = *(lwmutex*)Memory.GetMemFromAddr(lwmutex_addr); + lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; Emu.GetIdManager().RemoveID(lmtx.attribute); - //memset(Memory.GetMemFromAddr(lwmutex_addr), 0, sizeof(lwmutex)); - */ - return 0; + + return CELL_OK; } -int SysCalls::Lv2LwmutexLock(PPUThread& CPU) +int sys_lwmutex_lock(u64 lwmutex_addr, u64 timeout) { - //int sys_lwmutex_lock(sys_lwmutex_t *lwmutex, usecond_t timeout) - const u64 lwmutex_addr = CPU.GPR[3]; - const u64 timeout = CPU.GPR[4]; - //ConLog.Write("Lv2LwmutexLock[r3: 0x%llx, r4: 0x%llx]", lwmutex_addr, timeout); + //sc_lwmutex.Warning("sys_lwmutex_lock(lwmutex_addr = 0x%llx, timeout = 0x%llx)", lwmutex_addr, timeout); - //lwmutex& lmtx = *(lwmutex*)Memory.GetMemFromAddr(lwmutex_addr); - //lmtx.lock_var.info.waiter = CPU.GetId(); + lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; - return 0;//CELL_ESRCH; + if(!lmtx.lock_var.info.owner) + { + re(lmtx.lock_var.info.owner, GetCurrentPPUThread().GetId()); + } + else if(!lmtx.lock_var.info.waiter) + { + re(lmtx.lock_var.info.waiter, GetCurrentPPUThread().GetId()); + while(re(lmtx.lock_var.info.owner) != GetCurrentPPUThread().GetId()) Sleep(1); + } + else + { + return -1; + } + + return CELL_OK; } -int SysCalls::Lv2LwmutexTrylock(PPUThread& CPU) +int sys_lwmutex_trylock(u64 lwmutex_addr) { - //int sys_lwmutex_trylock(sys_lwmutex_t *lwmutex) - const u64 lwmutex_addr = CPU.GPR[3]; - //ConLog.Write("Lv2LwmutexTrylock[r3: 0x%llx]", lwmutex_addr); - return 0; + //sc_lwmutex.Warning("sys_lwmutex_trylock(lwmutex_addr = 0x%llx)", lwmutex_addr); + + lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; + + if(lmtx.lock_var.info.owner) return CELL_EBUSY; + + return CELL_OK; } -int SysCalls::Lv2LwmutexUnlock(PPUThread& CPU) +int sys_lwmutex_unlock(u64 lwmutex_addr) { - //int sys_lwmutex_unlock(sys_lwmutex_t *lwmutex) - const u64 lwmutex_addr = CPU.GPR[3]; - //ConLog.Write("Lv2LwmutexUnlock[r3: 0x%llx]", lwmutex_addr); - return 0; + //sc_lwmutex.Warning("sys_lwmutex_unlock(lwmutex_addr = 0x%llx)", lwmutex_addr); + + lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; + lmtx.lock_var.info.owner = lmtx.lock_var.info.waiter; + lmtx.lock_var.info.waiter = 0; + + return CELL_OK; } + diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp index 1ad20fe42b..44981568ac 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp @@ -201,6 +201,62 @@ int sys_memory_allocate(u32 size, u32 flags, u32 alloc_addr_addr) return CELL_OK; } +int sys_memory_free(u32 start_addr) +{ + sc_mem.Log("sys_memory_free(start_addr=0x%x)", start_addr); + + if(!Memory.Free(start_addr)) return CELL_EFAULT; + + return CELL_OK; +} + +struct mmapper_info +{ + u32 size; + u32 flags; + + mmapper_info(u32 _size, u32 _flags) + : size(_size) + , flags(_flags) + { + } + + mmapper_info() + { + } +}; + +int sys_mmapper_allocate_address(u32 size, u64 flags, u32 alignment, u32 alloc_addr) +{ + sc_mem.Warning("sys_mmapper_allocate_address(size=0x%x, flags=0x%llx, alignment=0x%x, alloc_addr=0x%x)", size, flags, alignment, alloc_addr); + + Memory.Write32(alloc_addr, Memory.Alloc(size, 4)); + + return CELL_OK; +} + +int sys_mmapper_allocate_memory(u32 size, u64 flags, u32 mem_id_addr) +{ + sc_mem.Warning("sys_mmapper_allocate_memory(size=0x%x, flags=0x%llx, mem_id_addr=0x%x)", size, flags, mem_id_addr); + + if(!Memory.IsGoodAddr(mem_id_addr)) return CELL_EFAULT; + Memory.Write32(mem_id_addr, sc_mem.GetNewId(new mmapper_info(size, flags))); + + return CELL_OK; +} + +int sys_mmapper_map_memory(u32 start_addr, u32 mem_id, u64 flags) +{ + sc_mem.Warning("sys_mmapper_map_memory(start_addr=0x%x, mem_id=0x%x, flags=0x%llx)", start_addr, mem_id, flags); + + mmapper_info* info; + if(!sc_mem.CheckId(mem_id, info)) return CELL_ESRCH; + + //Memory.MemoryBlocks.Add((new MemoryBlock())->SetRange(start_addr, info->size)); + + return CELL_OK; +} + struct sys_memory_info { u32 total_user_memory; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp new file mode 100644 index 0000000000..b700ec7ae3 --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp @@ -0,0 +1,78 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" +#include "SC_Mutex.h" + +SysCallBase sys_mtx("sys_mutex"); + +int sys_mutex_create(u32 mutex_id_addr, u32 attr_addr) +{ + sys_mtx.Log("sys_mutex_create(mutex_id_addr=0x%x, attr_addr=0x%x)", + mutex_id_addr, attr_addr); + + if(!Memory.IsGoodAddr(mutex_id_addr) || !Memory.IsGoodAddr(attr_addr)) return CELL_EFAULT; + + mutex_attr attr = (mutex_attr&)Memory[attr_addr]; + attr.protocol = re(attr.protocol); + attr.recursive = re(attr.recursive); + attr.pshared = re(attr.pshared); + attr.adaptive = re(attr.adaptive); + attr.ipc_key = re(attr.ipc_key); + attr.flags = re(attr.flags); + + sys_mtx.Log("*** protocol = %d", attr.protocol); + sys_mtx.Log("*** recursive = %d", attr.recursive); + sys_mtx.Log("*** pshared = %d", attr.pshared); + sys_mtx.Log("*** ipc_key = 0x%llx", attr.ipc_key); + sys_mtx.Log("*** flags = 0x%x", attr.flags); + sys_mtx.Log("*** name = %s", attr.name); + + Memory.Write32(mutex_id_addr, sys_mtx.GetNewId(new mutex(attr))); + + return CELL_OK; +} + +int sys_mutex_destroy(u32 mutex_id) +{ + sys_mtx.Log("sys_mutex_destroy(mutex_id=0x%x)", mutex_id); + + if(!sys_mtx.CheckId(mutex_id)) return CELL_ESRCH; + + Emu.GetIdManager().RemoveID(mutex_id); + return CELL_OK; +} + +int sys_mutex_lock(u32 mutex_id, u64 timeout) +{ + sys_mtx.Log("sys_mutex_lock(mutex_id=0x%x, timeout=0x%llx)", mutex_id, timeout); + + mutex* mtx_data = nullptr; + if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; + + mtx_data->mtx.Lock(); + + return CELL_OK; +} + +int sys_mutex_trylock(u32 mutex_id) +{ + sys_mtx.Log("sys_mutex_trylock(mutex_id=0x%x)", mutex_id); + + mutex* mtx_data = nullptr; + if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; + + if(mtx_data->mtx.TryLock()) return 1; + + return CELL_OK; +} + +int sys_mutex_unlock(u32 mutex_id) +{ + sys_mtx.Log("sys_mutex_unlock(mutex_id=0x%x)", mutex_id); + + mutex* mtx_data = nullptr; + if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; + + mtx_data->mtx.Unlock(); + + return CELL_OK; +} diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Mutex.h b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.h new file mode 100644 index 0000000000..b191061889 --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.h @@ -0,0 +1,25 @@ +#pragma once + +struct mutex_attr +{ + u32 protocol; + u32 recursive; + u32 pshared; + u32 adaptive; + u64 ipc_key; + int flags; + u32 pad; + char name[8]; +}; + +struct mutex +{ + wxMutex mtx; + mutex_attr attr; + + mutex(const mutex_attr& attr) + : mtx() + , attr(attr) + { + } +}; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp index 8b511aa62e..eddf0ec52e 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp @@ -1,126 +1,135 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" +extern Module sysPrxForUser; + #define PPU_THREAD_ID_INVALID 0xFFFFFFFFU -int SysCalls::lv2PPUThreadCreate(PPUThread& CPU) +int sys_ppu_thread_exit(int errorcode) { - ConLog.Write("lv2PPUThreadCreate:"); - //ConLog.Write("**** id: %d", CPU.GPR[3]); - ConLog.Write("**** entry: 0x%x", CPU.GPR[4]); - ConLog.Write("**** arg: 0x%x", CPU.GPR[5]); - ConLog.Write("**** prio: 0x%x", CPU.GPR[6]); - ConLog.Write("**** stacksize: 0x%x", CPU.GPR[7]); - ConLog.Write("**** flags: 0x%x", CPU.GPR[8]); - ConLog.Write("**** threadname: \"%s\"[0x%x]", Memory.ReadString(CPU.GPR[9]), CPU.GPR[9]); + sysPrxForUser.Log("sys_ppu_thread_exit(errorcode=%d)", errorcode); + Emu.GetCPU().RemoveThread(GetCurrentPPUThread().GetId()); - if(!Memory.IsGoodAddr(CPU.GPR[4])) return CELL_EFAULT; + return CELL_OK; +} + +int sys_ppu_thread_yield() +{ + sysPrxForUser.Log("sys_ppu_thread_yield()"); + wxThread::Yield(); + + return CELL_OK; +} + +int sys_ppu_thread_join(u32 thread_id, u32 vptr_addr) +{ + sysPrxForUser.Error("sys_ppu_thread_join(thread_id=%d, vptr_addr=0x%x)", thread_id, vptr_addr); + + return CELL_OK; +} + +int sys_ppu_thread_detach(u32 thread_id) +{ + sysPrxForUser.Error("sys_ppu_thread_detach(thread_id=%d)", thread_id); + + return CELL_OK; +} + +void sys_ppu_thread_get_join_state(u32 isjoinable_addr) +{ + sysPrxForUser.Warning("sys_ppu_thread_get_join_state(isjoinable_addr=0x%x)", isjoinable_addr); + Memory.Write32(isjoinable_addr, GetCurrentPPUThread().IsJoinable()); +} + +int sys_ppu_thread_set_priority(u32 thread_id, int prio) +{ + sysPrxForUser.Warning("sys_ppu_thread_set_priority(thread_id=%d, prio=%d)", thread_id, prio); + + PPCThread* thr = Emu.GetCPU().GetThread(thread_id); + if(!thr) return CELL_ESRCH; + + thr->SetPrio(prio); + + return CELL_OK; +} + +int sys_ppu_thread_get_priority(u32 thread_id, u32 prio_addr) +{ + sysPrxForUser.Log("sys_ppu_thread_get_priority(thread_id=%d, prio_addr=0x%x)", thread_id, prio_addr); + + PPCThread* thr = Emu.GetCPU().GetThread(thread_id); + if(!thr) return CELL_ESRCH; + if(!Memory.IsGoodAddr(prio_addr)) return CELL_EFAULT; + + Memory.Write32(prio_addr, thr->GetPrio()); + + return CELL_OK; +} + +int sys_ppu_thread_get_stack_information(u32 info_addr) +{ + sysPrxForUser.Log("sys_ppu_thread_get_stack_information(info_addr=0x%x)", info_addr); + + if(!Memory.IsGoodAddr(info_addr)) return CELL_EFAULT; + + declCPU(); + + Memory.Write32(info_addr, CPU.GetStackAddr()); + Memory.Write32(info_addr+4, CPU.GetStackSize()); + + return CELL_OK; +} + +int sys_ppu_thread_stop(u32 thread_id) +{ + sysPrxForUser.Warning("sys_ppu_thread_stop(thread_id=%d)", thread_id); + + PPCThread* thr = Emu.GetCPU().GetThread(thread_id); + if(!thr) return CELL_ESRCH; + + thr->Stop(); + + return CELL_OK; +} + +int sys_ppu_thread_restart(u32 thread_id) +{ + sysPrxForUser.Warning("sys_ppu_thread_restart(thread_id=%d)", thread_id); + + PPCThread* thr = Emu.GetCPU().GetThread(thread_id); + if(!thr) return CELL_ESRCH; + + thr->Stop(); + thr->Run(); + + return CELL_OK; +} + +int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u32 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr) +{ + sysPrxForUser.Log("sys_ppu_thread_create(thread_id_addr=0x%x, entry=0x%x, arg=0x%x, prio=%d, stacksize=0x%x, flags=0x%llx, threadname_addr=0x%x('%s'))", + thread_id_addr, entry, arg, prio, stacksize, flags, threadname_addr, Memory.ReadString(threadname_addr)); + + if(!Memory.IsGoodAddr(entry) || !Memory.IsGoodAddr(thread_id_addr) || !Memory.IsGoodAddr(threadname_addr)) return CELL_EFAULT; PPCThread& new_thread = Emu.GetCPU().AddThread(true); - Memory.Write32(CPU.GPR[3], new_thread.GetId()); - new_thread.SetPc(CPU.GPR[4]); - new_thread.SetArg(CPU.GPR[5]); - new_thread.SetPrio(CPU.GPR[6]); - new_thread.stack_size = CPU.GPR[7]; - //new_thread.flags = CPU.GPR[8]; - new_thread.SetName(Memory.ReadString(CPU.GPR[9])); + Memory.Write32(thread_id_addr, new_thread.GetId()); + new_thread.SetEntry(entry); + new_thread.SetArg(arg); + new_thread.SetPrio(prio); + new_thread.stack_size = stacksize; + //new_thread.flags = flags; + new_thread.SetName(Memory.ReadString(threadname_addr)); new_thread.Run(); + new_thread.Exec(); + return CELL_OK; } -int SysCalls::lv2PPUThreadExit(PPUThread& CPU) +int sys_ppu_thread_get_id() { - ConLog.Warning("PPU[%d] thread exit(%lld)", CPU.GetId(), CPU.GPR[3]); - Emu.GetCPU().RemoveThread(CPU.GetId()); - return CELL_OK; -} + sysPrxForUser.Log("sys_ppu_thread_get_id()"); -int SysCalls::lv2PPUThreadYield(PPUThread& CPU) -{ - //ConLog.Warning("TODO: PPU[%d] thread yield!", CPU.GetId()); - return CELL_OK; + return GetCurrentPPUThread().GetId(); } - -int SysCalls::lv2PPUThreadJoin(PPUThread& CPU) -{ - ConLog.Warning("TODO: PPU[%d] thread join!", CPU.GPR[3]); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadDetach(PPUThread& CPU) -{ - ConLog.Warning("PPU[%d] thread detach", CPU.GPR[3]); - if(!Emu.GetIdManager().CheckID(CPU.GPR[3])) return CELL_ESRCH; - const ID& id = Emu.GetIdManager().GetIDData(CPU.GPR[3]); - if(!id.m_used) return CELL_ESRCH; - PPCThread& thread = *(PPCThread*)id.m_data; - if(thread.IsJoinable()) return CELL_EINVAL; - if(thread.IsJoining()) return CELL_EBUSY; - thread.SetJoinable(false); - if(!thread.IsRunned()) Emu.GetCPU().RemoveThread(thread.GetId()); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadGetJoinState(PPUThread& CPU) -{ - ConLog.Warning("PPU[%d] get join state", CPU.GetId()); - Memory.Write32(CPU.GPR[3], CPU.IsJoinable() ? 1 : 0); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadSetPriority(PPUThread& CPU) -{ - ConLog.Write("PPU[%d] thread set priority", CPU.GPR[3]); - if(!Emu.GetIdManager().CheckID(CPU.GPR[3])) return CELL_ESRCH; - const ID& id = Emu.GetIdManager().GetIDData(CPU.GPR[3]); - CPU.SetPrio(CPU.GPR[4]); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadGetPriority(PPUThread& CPU) -{ - ConLog.Write("PPU[%d] thread get priority", CPU.GPR[3]); - if(!Emu.GetIdManager().CheckID(CPU.GPR[3])) return CELL_ESRCH; - const ID& id = Emu.GetIdManager().GetIDData(CPU.GPR[3]); - Memory.Write32(CPU.GPR[4], CPU.GetPrio()); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadGetStackInformation(PPUThread& CPU) -{ - ConLog.Write("PPU[%d] thread get stack information(0x%llx)", CPU.GetId(), CPU.GPR[3]); - Memory.Write32(CPU.GPR[3], CPU.GetStackAddr()); - Memory.Write32(CPU.GPR[3]+4, CPU.GetStackSize()); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadRename(PPUThread& CPU) -{ - ConLog.Write("PPU[%d] thread rename(%s)", CPU.GPR[3], Memory.ReadString(CPU.GPR[4])); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadRecoverPageFault(PPUThread& CPU) -{ - ConLog.Warning("TODO: PPU[%d] thread recover page fault!", CPU.GPR[3]); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadGetPageFaultContext(PPUThread& CPU) -{ - ConLog.Warning("TODO: PPU[%d] thread get page fault context!", CPU.GPR[3]); - return CELL_OK; -} - -int SysCalls::lv2PPUThreadGetId(PPUThread& CPU) -{ - //ConLog.Write("PPU[%d] thread get id(0x%llx)", CPU.GetId(), CPU.GPR[3]); - Memory.Write64(CPU.GPR[3], CPU.GetId()); - return CELL_OK; -} - -int sys_spu_thread_once(u64 once_ctrl_addr, u64 init_addr) -{ - return 0; -} \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Pad.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Pad.cpp index 677cd60635..ca1761ed7b 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Pad.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Pad.cpp @@ -2,7 +2,7 @@ #include "Emu/Io/Pad.h" #include "Emu/SysCalls/SysCalls.h" -SysCallBase sc_pad("cellPad"); +extern Module sys_io; enum CELL_PAD_ERROR_CODE { @@ -37,7 +37,7 @@ struct CellPadInfo2 int cellPadInit(u32 max_connect) { - sc_pad.Log("cellPadInit(max_connect=%d)", max_connect); + sys_io.Log("cellPadInit(max_connect=%d)", max_connect); if(Emu.GetPadManager().IsInited()) return CELL_PAD_ERROR_ALREADY_INITIALIZED; Emu.GetPadManager().Init(max_connect); return CELL_OK; @@ -45,7 +45,7 @@ int cellPadInit(u32 max_connect) int cellPadEnd() { - sc_pad.Log("cellPadEnd()"); + sys_io.Log("cellPadEnd()"); if(!Emu.GetPadManager().IsInited()) return CELL_PAD_ERROR_UNINITIALIZED; Emu.GetPadManager().Close(); return CELL_OK; @@ -53,7 +53,7 @@ int cellPadEnd() int cellPadClearBuf(u32 port_no) { - sc_pad.Log("cellPadClearBuf(port_no=%d)", port_no); + sys_io.Log("cellPadClearBuf(port_no=%d)", port_no); if(!Emu.GetPadManager().IsInited()) return CELL_PAD_ERROR_UNINITIALIZED; if(port_no >= Emu.GetPadManager().GetPads().GetCount()) return CELL_PAD_ERROR_INVALID_PARAMETER; @@ -120,7 +120,7 @@ int cellPadGetData(u32 port_no, u32 data_addr) int cellPadGetDataExtra(u32 port_no, u32 device_type_addr, u32 data_addr) { - sc_pad.Log("cellPadGetDataExtra(port_no=%d, device_type_addr=0x%x, device_type_addr=0x%x)", port_no, device_type_addr, data_addr); + sys_io.Log("cellPadGetDataExtra(port_no=%d, device_type_addr=0x%x, device_type_addr=0x%x)", port_no, device_type_addr, data_addr); if(!Emu.GetPadManager().IsInited()) return CELL_PAD_ERROR_UNINITIALIZED; if(port_no >= Emu.GetPadManager().GetPads().GetCount()) return CELL_PAD_ERROR_INVALID_PARAMETER; return CELL_OK; @@ -128,7 +128,7 @@ int cellPadGetDataExtra(u32 port_no, u32 device_type_addr, u32 data_addr) int cellPadSetActDirect(u32 port_no, u32 param_addr) { - sc_pad.Log("cellPadSetActDirect(port_no=%d, param_addr=0x%x)", port_no, param_addr); + sys_io.Log("cellPadSetActDirect(port_no=%d, param_addr=0x%x)", port_no, param_addr); if(!Emu.GetPadManager().IsInited()) return CELL_PAD_ERROR_UNINITIALIZED; if(port_no >= Emu.GetPadManager().GetPads().GetCount()) return CELL_PAD_ERROR_INVALID_PARAMETER; return CELL_OK; @@ -136,7 +136,7 @@ int cellPadSetActDirect(u32 port_no, u32 param_addr) int cellPadGetInfo2(u32 info_addr) { - sc_pad.Log("cellPadGetInfo2(info_addr=0x%x)", info_addr); + sys_io.Log("cellPadGetInfo2(info_addr=0x%x)", info_addr); if(!Emu.GetPadManager().IsInited()) return CELL_PAD_ERROR_UNINITIALIZED; CellPadInfo2 info; @@ -165,7 +165,7 @@ int cellPadGetInfo2(u32 info_addr) int cellPadSetPortSetting(u32 port_no, u32 port_setting) { - sc_pad.Log("cellPadSetPortSetting(port_no=%d, port_setting=0x%x)", port_no, port_setting); + sys_io.Log("cellPadSetPortSetting(port_no=%d, port_setting=0x%x)", port_no, port_setting); if(!Emu.GetPadManager().IsInited()) return CELL_PAD_ERROR_UNINITIALIZED; Array& pads = Emu.GetPadManager().GetPads(); if(port_no >= pads.GetCount()) return CELL_PAD_ERROR_INVALID_PARAMETER; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp index 04ecd0f15e..8f0a127121 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp @@ -8,26 +8,27 @@ int sys_process_getpid() return 1; } +int sys_process_exit(int errorcode) +{ + ConLog.Warning("sys_process_exit(%d)", errorcode); + Emu.Pause(); + return CELL_OK; +} + int sys_game_process_exitspawn( u32 path_addr, u32 argv_addr, u32 envp_addr, u32 data, u32 data_size, int prio, u64 flags ) { sc_p.Log("sys_game_process_exitspawn: "); sc_p.Log("path: %s", Memory.ReadString(path_addr)); - sc_p.Log("argv: %x", Memory.Read32(argv_addr)); - sc_p.Log("envp: %x", Memory.Read32(envp_addr)); - sc_p.Log("data: %x", data); - sc_p.Log("data_size: %x", data_size); + sc_p.Log("argv: 0x%x", Memory.Read32(argv_addr)); + sc_p.Log("envp: 0x%x", Memory.Read32(envp_addr)); + sc_p.Log("data: 0x%x", data); + sc_p.Log("data_size: 0x%x", data_size); sc_p.Log("prio: %d", prio); sc_p.Log("flags: %d", flags); return CELL_OK; } -int SysCalls::lv2ProcessGetPid(PPUThread& CPU) -{ - ConLog.Warning("lv2ProcessGetPid"); - Memory.Write32(CPU.GPR[4], CPU.GetId()); - return CELL_OK; -} int SysCalls::lv2ProcessWaitForChild(PPUThread& CPU) { ConLog.Warning("lv2ProcessWaitForChild"); @@ -69,12 +70,14 @@ int SysCalls::lv2ProcessKill(PPUThread& CPU) CPU.Close(); return CELL_OK; } +/* int SysCalls::lv2ProcessExit(PPUThread& CPU) { ConLog.Warning("lv2ProcessExit(%lld)", CPU.GPR[3]); Emu.Pause(); return CELL_OK; } +*/ int SysCalls::lv2ProcessWaitForChild2(PPUThread& CPU) { ConLog.Warning("lv2ProcessWaitForChild2[r3: 0x%llx, r4: 0x%llx, r5: 0x%llx, r6: 0x%llx, r7: 0x%llx, r8: 0x%llx]", diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Resc.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Resc.cpp index 2b2c572c99..499e93885d 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Resc.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Resc.cpp @@ -15,8 +15,16 @@ struct CellRescSrc int cellRescSetSrc(const int idx, const u32 src_addr) { sc_resc.Warning("cellRescSetSrc(idx=0x%x, src_addr=0x%x)", idx, src_addr); - const CellRescSrc& src = *(CellRescSrc*)Memory.GetMemFromAddr(src_addr); - sc_resc.Warning(" *** format=%d", src.format); + if(!Memory.IsGoodAddr(src_addr, sizeof(CellRescSrc))) return CELL_EFAULT; + + CellRescSrc src = (CellRescSrc&)Memory[src_addr]; + re(src.format, src.format); + re(src.pitch, src.pitch); + re(src.width, src.width); + re(src.height, src.height); + re(src.offset, src.offset); + + sc_resc.Warning(" *** format=0x%x", src.format); sc_resc.Warning(" *** pitch=%d", src.pitch); sc_resc.Warning(" *** width=%d", src.width); sc_resc.Warning(" *** height=%d", src.height); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index 060c0ba2f4..7e3ba1b5d5 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" +#include "Loader/ELF.h" SysCallBase sc_spu("sys_spu"); @@ -11,6 +12,34 @@ struct sys_spu_thread_group_attribute union{u32 ct;} option; }; +struct sys_spu_image +{ + u32 type; + u32 entry_point; + u32 segs_addr; + int nsegs; +}; + +u32 LoadImage(vfsStream& stream) +{ + ELFLoader l(stream); + l.LoadInfo(); + l.LoadData(Memory.MainMem.Alloc(stream.GetSize())); + + return l.GetEntry(); +} + + int sys_spu_image_open(u32 img_addr, u32 path_addr) + { + const wxString& path = Memory.ReadString(path_addr); + sc_spu.Warning("sys_spu_image_open(img_addr=0x%x, path_addr=0x%x [%s])", img_addr, path_addr, path); + + vfsLocalFile stream(path); + LoadImage(stream); + + return CELL_OK; + } + //170 int sys_spu_thread_group_create(u64 id_addr, u32 num, int prio, u64 attr_addr) { @@ -45,11 +74,11 @@ int sys_spu_thread_create(u64 thread_id_addr, u64 entry_addr, u64 arg, //160 int sys_raw_spu_create(u32 id_addr, u32 attr_addr) { - sc_spu.Log("sys_raw_spu_create(id_addr=0x%x, attr_addr=0x%x)", id_addr, attr_addr); + sc_spu.Warning("sys_raw_spu_create(id_addr=0x%x, attr_addr=0x%x)", id_addr, attr_addr); - PPCThread& new_thread = Emu.GetCPU().AddThread(false); - Emu.GetIdManager().GetNewID("sys_raw_spu", new u32(attr_addr)); - Memory.Write32(id_addr, Emu.GetCPU().GetThreadNumById(false, new_thread.GetId())); + //PPCThread& new_thread = Emu.GetCPU().AddThread(false); + //Emu.GetIdManager().GetNewID("sys_raw_spu", new u32(attr_addr)); + //Memory.Write32(id_addr, Emu.GetCPU().GetThreadNumById(false, new_thread.GetId())); return CELL_OK; } @@ -57,7 +86,7 @@ int sys_raw_spu_create(u32 id_addr, u32 attr_addr) //169 int sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu) { - sc_spu.Log("sys_spu_initialize(max_usable_spu=%d, max_raw_spu=%d)", max_usable_spu, max_raw_spu); + sc_spu.Warning("sys_spu_initialize(max_usable_spu=%d, max_raw_spu=%d)", max_usable_spu, max_raw_spu); if(!Memory.InitSpuRawMem(max_raw_spu)) { diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp new file mode 100644 index 0000000000..8b11792c2a --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_Semaphore.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" + +SysCallBase sys_sem("sys_semaphore"); + +struct semaphore_attr +{ + u32 protocol; + u32 pshared; + u64 ipc_key; + int flags; + u32 pad; + char name[8]; +}; + +struct semaphore +{ + wxSemaphore sem; + semaphore_attr attr; + + semaphore(int initial_count, int max_count, semaphore_attr attr) + : sem(initial_count, max_count) + , attr(attr) + { + } +}; + +int sys_semaphore_create(u32 sem_addr, u32 attr_addr, int initial_count, int max_count) +{ + sys_sem.Log("sys_semaphore_create(sem_addr=0x%x, attr_addr=0x%x, initial_count=%d, max_count=%d)", + sem_addr, attr_addr, initial_count, max_count); + + if(!Memory.IsGoodAddr(sem_addr) || !Memory.IsGoodAddr(attr_addr)) return CELL_EFAULT; + + semaphore_attr attr = (semaphore_attr&)Memory[attr_addr]; + attr.protocol = re(attr.protocol); + attr.pshared = re(attr.pshared); + attr.ipc_key = re(attr.ipc_key); + attr.flags = re(attr.flags); + + sys_sem.Log("*** protocol = %d", attr.protocol); + sys_sem.Log("*** pshared = %d", attr.pshared); + sys_sem.Log("*** ipc_key = 0x%llx", attr.ipc_key); + sys_sem.Log("*** flags = 0x%x", attr.flags); + sys_sem.Log("*** name = %s", attr.name); + + Memory.Write32(sem_addr, sys_sem.GetNewId(new semaphore(initial_count, max_count, attr))); + + return CELL_OK; +} + +int sys_semaphore_destroy(u32 sem) +{ + sys_sem.Log("sys_semaphore_destroy(sem=0x%x)", sem); + + if(!sys_sem.CheckId(sem)) return CELL_ESRCH; + + Emu.GetIdManager().RemoveID(sem); + return CELL_OK; +} + +int sys_semaphore_wait(u32 sem, u64 timeout) +{ + sys_sem.Log("sys_semaphore_wait(sem=0x%x, timeout=0x%llx)", sem, timeout); + + semaphore* sem_data = nullptr; + if(!sys_sem.CheckId(sem, sem_data)) return CELL_ESRCH; + + sem_data->sem.WaitTimeout(timeout ? timeout : INFINITE); + + return CELL_OK; +} + +int sys_semaphore_trywait(u32 sem) +{ + sys_sem.Log("sys_semaphore_trywait(sem=0x%x)", sem); + + semaphore* sem_data = nullptr; + if(!sys_sem.CheckId(sem, sem_data)) return CELL_ESRCH; + + if(sem_data->sem.TryWait()) return 1; + + return CELL_OK; +} + +int sys_semaphore_post(u32 sem, int count) +{ + sys_sem.Log("sys_semaphore_post(sem=0x%x, count=%d)", sem, count); + + semaphore* sem_data = nullptr; + if(!sys_sem.CheckId(sem, sem_data)) return CELL_ESRCH; + + while(count--) sem_data->sem.Post(); + + return CELL_OK; +} diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp index c8f64fe83f..b7d672f286 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp @@ -48,53 +48,157 @@ int cellVideoOutGetResolution(u32 resolutionId, u32 resolution_addr) sc_v.Log("cellVideoOutGetResolution(resolutionId=%d, resolution_addr=0x%x)", resolutionId, resolution_addr); - CellVideoOutResolution res = {0, 0}; - - switch(resolutionId) + if(!Memory.IsGoodAddr(resolution_addr, sizeof(CellVideoOutResolution))) { - case CELL_VIDEO_OUT_RESOLUTION_1080: - res.width = re16(1920); - res.height = re16(1080); - break; + return CELL_EFAULT; + } + + u32 num = ResolutionIdToNum(resolutionId); - case CELL_VIDEO_OUT_RESOLUTION_720: - res.width = re16(1280); - res.height = re16(720); - break; - - case CELL_VIDEO_OUT_RESOLUTION_480: - res.width = re16(720); - res.height = re16(480); - break; - - case CELL_VIDEO_OUT_RESOLUTION_576: - res.width = re16(720); - res.height = re16(576); - break; - - case CELL_VIDEO_OUT_RESOLUTION_1600x1080: - res.width = re16(1600); - res.height = re16(1080); - break; - - case CELL_VIDEO_OUT_RESOLUTION_1440x1080: - res.width = re16(1440); - res.height = re16(1080); - break; - - case CELL_VIDEO_OUT_RESOLUTION_1280x1080: - res.width = re16(1280); - res.height = re16(1080); - break; - - case CELL_VIDEO_OUT_RESOLUTION_960x1080: - res.width = re16(960); - res.height = re16(1080); - break; - - default: return CELL_EINVAL; + if(!num) + { + return CELL_EINVAL; } + CellVideoOutResolution res; + re(res.width, ResolutionTable[num].width); + re(res.height, ResolutionTable[num].height); + Memory.WriteData(resolution_addr, res); + + return CELL_VIDEO_OUT_SUCCEEDED; +} + +int cellVideoOutConfigure(u32 videoOut, u32 config_addr, u32 option_addr, u32 waitForEvent) +{ + sc_v.Warning("cellVideoOutConfigure(videoOut=%d, config_addr=0x%x, option_addr=0x%x, waitForEvent=0x%x)", + videoOut, config_addr, option_addr, waitForEvent); + + if(!Memory.IsGoodAddr(config_addr, sizeof(CellVideoOutConfiguration))) + { + return CELL_EFAULT; + } + + CellVideoOutConfiguration& config = (CellVideoOutConfiguration&)Memory[config_addr]; + + switch(videoOut) + { + case CELL_VIDEO_OUT_PRIMARY: + if(config.resolutionId) + { + Emu.GetGSManager().GetInfo().mode.resolutionId = config.resolutionId; + } + + Emu.GetGSManager().GetInfo().mode.format = config.format; + + if(config.aspect) + { + Emu.GetGSManager().GetInfo().mode.aspect = config.aspect; + } + + if(config.pitch) + { + Emu.GetGSManager().GetInfo().mode.pitch = re(config.pitch); + } + + return CELL_VIDEO_OUT_SUCCEEDED; + + case CELL_VIDEO_OUT_SECONDARY: + return CELL_VIDEO_OUT_SUCCEEDED; + } + + return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT; +} + +int cellVideoOutGetConfiguration(u32 videoOut, u32 config_addr, u32 option_addr) +{ + sc_v.Warning("cellVideoOutGetConfiguration(videoOut=%d, config_addr=0x%x, option_addr=0x%x)", + videoOut, config_addr, option_addr); + + if(!Memory.IsGoodAddr(config_addr, sizeof(CellVideoOutConfiguration))) return CELL_EFAULT; + + CellVideoOutConfiguration config; + memset(&config, 0, sizeof(CellVideoOutConfiguration)); + + switch(videoOut) + { + case CELL_VIDEO_OUT_PRIMARY: + config.resolutionId = Emu.GetGSManager().GetInfo().mode.resolutionId; + config.format = Emu.GetGSManager().GetInfo().mode.format; + config.aspect = Emu.GetGSManager().GetInfo().mode.aspect; + config.pitch = re(Emu.GetGSManager().GetInfo().mode.pitch); + + Memory.WriteData(config_addr, config); + + return CELL_VIDEO_OUT_SUCCEEDED; + + case CELL_VIDEO_OUT_SECONDARY: + Memory.WriteData(config_addr, config); + + return CELL_VIDEO_OUT_SUCCEEDED; + } + + return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT; +} + +int cellVideoOutGetNumberOfDevice(u32 videoOut) +{ + sc_v.Warning("cellVideoOutGetNumberOfDevice(videoOut=%d)", videoOut); + + switch(videoOut) + { + case CELL_VIDEO_OUT_PRIMARY: return 1; + case CELL_VIDEO_OUT_SECONDARY: return 0; + } + + return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT; +} + +int cellVideoOutGetResolutionAvailability(u32 videoOut, u32 resolutionId, u32 aspect, u32 option) +{ + sc_v.Warning("cellVideoOutGetResolutionAvailability(videoOut=%d, resolutionId=0x%x, option_addr=0x%x, aspect=0x%x, option=0x%x)", + videoOut, resolutionId, aspect, option); + + if(!ResolutionIdToNum(resolutionId)) + { + return CELL_EINVAL; + } + + switch(videoOut) + { + case CELL_VIDEO_OUT_PRIMARY: return 1; + case CELL_VIDEO_OUT_SECONDARY: return 0; + } + + return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT; +} + +SysCallBase sc_sysutil("cellSysutil"); + +int cellSysutilCheckCallback() +{ + //sc_sysutil.Warning("cellSysutilCheckCallback()"); + Emu.GetCallbackManager().m_exit_callback.Check(); + + return CELL_OK; +} + +int cellSysutilRegisterCallback(int slot, u64 func_addr, u64 userdata) +{ + sc_sysutil.Warning("cellSysutilRegisterCallback(slot=%d, func_addr=0x%llx, userdata=0x%llx)", slot, func_addr, userdata); + Emu.GetCallbackManager().m_exit_callback.Register(slot, func_addr, userdata); + + wxGetApp().m_MainFrame->UpdateUI(); + + return CELL_OK; +} + +int cellSysutilUnregisterCallback(int slot) +{ + sc_sysutil.Warning("cellSysutilUnregisterCallback(slot=%d)", slot); + Emu.GetCallbackManager().m_exit_callback.Unregister(slot); + + wxGetApp().m_MainFrame->UpdateUI(); + return CELL_OK; } \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SysUtil_MsgDialog.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SysUtil_MsgDialog.cpp new file mode 100644 index 0000000000..9ffc858806 --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_SysUtil_MsgDialog.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" + +SysCallBase sc_md("cellMsgDialog"); + +enum +{ + CELL_MSGDIALOG_BUTTON_NONE = -1, + CELL_MSGDIALOG_BUTTON_INVALID = 0, + CELL_MSGDIALOG_BUTTON_OK = 1, + CELL_MSGDIALOG_BUTTON_YES = 1, + CELL_MSGDIALOG_BUTTON_NO = 2, + CELL_MSGDIALOG_BUTTON_ESCAPE = 3, +}; + +enum CellMsgDialogType +{ + CELL_MSGDIALOG_DIALOG_TYPE_ERROR = 0x00000000, + CELL_MSGDIALOG_DIALOG_TYPE_NORMAL = 0x00000001, + + CELL_MSGDIALOG_BUTTON_TYPE_NONE = 0x00000000, + CELL_MSGDIALOG_BUTTON_TYPE_YESNO = 0x00000010, + + CELL_MSGDIALOG_DEFAULT_CURSOR_YES = 0x00000000, + CELL_MSGDIALOG_DEFAULT_CURSOR_NO = 0x00000100, +}; + +int cellMsgDialogOpen2(u32 type, u32 msgString_addr, u32 callback_addr, u32 userData, u32 extParam) +{ + long style = 0; + + if(type & CELL_MSGDIALOG_DIALOG_TYPE_NORMAL) + { + style |= wxICON_EXCLAMATION; + } + else + { + style |= wxICON_ERROR; + } + + if(type & CELL_MSGDIALOG_BUTTON_TYPE_YESNO) + { + style |= wxYES_NO; + } + else + { + style |= wxOK; + } + + int res = wxMessageBox(Memory.ReadString(msgString_addr), wxGetApp().GetAppName(), style); + + u64 status; + + switch(res) + { + case wxOK: status = CELL_MSGDIALOG_BUTTON_OK; break; + case wxYES: status = CELL_MSGDIALOG_BUTTON_YES; break; + case wxNO: status = CELL_MSGDIALOG_BUTTON_NO; break; + + default: + if(res) + { + status = CELL_MSGDIALOG_BUTTON_INVALID; + break; + } + + status = CELL_MSGDIALOG_BUTTON_NONE; + break; + } + + Callback2 callback(0, callback_addr, userData); + callback.Handle(status); + callback.Branch(); + + return CELL_OK; +} \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp b/rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp index 9fb5f6aa46..52f6ec1573 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp @@ -1,28 +1,18 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" -int SysCalls::lv2TtyRead(PPUThread& CPU) +int sys_tty_read(u32 ch, u64 buf_addr, u32 len, u64 preadlen_addr) { - const u32 ch = CPU.GPR[3]; - const u64 buf_addr = CPU.GPR[4]; - const u32 len = CPU.GPR[5]; - const u64 preadlen_addr = CPU.GPR[6]; - ConLog.Warning("lv2TtyRead: ch: %d, buf addr: %llx, len: %d", ch, buf_addr, len); + ConLog.Warning("sys_tty_read: ch: %d, buf addr: %llx, len: %d", ch, buf_addr, len); Memory.Write32NN(preadlen_addr, len); return CELL_OK; } -int SysCalls::lv2TtyWrite(PPUThread& CPU) +int sys_tty_write(u32 ch, u64 buf_addr, u32 len, u64 pwritelen_addr) { - const u32 ch = CPU.GPR[3]; - const u64 buf_addr = CPU.GPR[4]; - const u32 len = CPU.GPR[5]; - const u64 pwritelen_addr = CPU.GPR[6]; - //for(uint i=0; i<32; ++i) ConLog.Write("r%d = 0x%llx", i, CPU.GPR[i]); - //ConLog.Warning("lv2TtyWrite: ch: %d, buf addr: %llx, len: %d", ch, buf_addr, len); if(ch < 0 || ch > 15 || len <= 0) return CELL_EINVAL; - if(!Memory.IsGoodAddr(buf_addr)) return CELL_EINVAL; + if(!Memory.IsGoodAddr(buf_addr)) return CELL_EFAULT; Emu.GetDbgCon().Write(ch, Memory.ReadString(buf_addr, len)); @@ -31,4 +21,4 @@ int SysCalls::lv2TtyWrite(PPUThread& CPU) Memory.Write32(pwritelen_addr, len); return CELL_OK; -} \ No newline at end of file +} diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Time.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Time.cpp index b74c863920..503e177dd3 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Time.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Time.cpp @@ -8,11 +8,11 @@ static const u64 timebase_frequency = 79800000; int sys_time_get_current_time(u32 sec_addr, u32 nsec_addr) { sys_time.Log("sys_time_get_current_time(sec_addr=0x%x, nsec_addr=0x%x)", sec_addr, nsec_addr); - __timeb64 cur_time; - _ftime64_s(&cur_time); - Memory.Write64(sec_addr, cur_time.time); - Memory.Write64(nsec_addr, (u64)cur_time.millitm * 1000000); + u64 time = sys_time_get_system_time(); + + Memory.Write64(sec_addr, time / 1000000); + Memory.Write64(nsec_addr, time % 1000000); return CELL_OK; } diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 66ed66a645..7466f5540b 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -6,10 +6,11 @@ #include "Emu/Cell/PPCThreadManager.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/SPUThread.h" +#include "Gui/CompilerELF.h" -#include "Emu/SysCalls/SysCalls.h" +using namespace PPU_opcodes; -SysCalls SysCallsManager; +//SysCalls SysCallsManager; Emulator::Emulator() : m_status(Stopped) @@ -55,7 +56,7 @@ void Emulator::CheckStatus() } if(IsAllPaused) { - ConLog.Warning("all paused!"); + //ConLog.Warning("all paused!"); Pause(); return; } @@ -69,41 +70,41 @@ void Emulator::CheckStatus() } if(IsAllStoped) { - ConLog.Warning("all stoped!"); + //ConLog.Warning("all stoped!"); Pause(); //Stop(); } } -void Emulator::Run() +void Emulator::Load() { - if(IsRunned()) Stop(); - if(IsPaused()) - { - Resume(); - return; - } - - //ConLog.Write("run..."); - m_status = Runned; - + if(!wxFileExists(m_path)) return; + ConLog.Write("loading '%s'...", m_path); Memory.Init(); - GetInfo().Reset(); - //SetTLSData(0, 0, 0); - //SetMallocPageSize(0x100000); - - Loader l(m_path); - if(!l.Load()) + Memory.Write64(Memory.PRXMem.Alloc(8), 0xDEADBEEFABADCAFE); + + bool is_error; + vfsLocalFile f(m_path); + Loader l(f); + + try { - Memory.Close(); - Stop(); - return; + is_error = !l.Load() || l.GetMachine() == MACHINE_Unknown; } - - if(l.GetMachine() == MACHINE_Unknown) + catch(const wxString& e) + { + ConLog.Error(e); + is_error = true; + } + catch(...) + { + ConLog.Error("Unhandled loader error."); + is_error = true; + } + + if(is_error) { - ConLog.Error("Unknown machine type"); Memory.Close(); Stop(); return; @@ -111,38 +112,76 @@ void Emulator::Run() PPCThread& thread = GetCPU().AddThread(l.GetMachine() == MACHINE_PPC64); - Memory.MainMem.Alloc(0x10000000, 0x10010000); - Memory.PRXMem.Write64(Memory.PRXMem.GetStartAddr(), 0xDEADBEEFABADCAFE); - - thread.SetPc(l.GetEntry()); + thread.SetEntry(l.GetEntry()); thread.SetArg(thread.GetId()); Memory.StackMem.Alloc(0x1000); thread.InitStack(); thread.AddArgv(m_path); //thread.AddArgv("-emu"); - m_rsx_callback = Memory.MainMem.Alloc(0x10) + 4; + m_rsx_callback = Memory.MainMem.Alloc(4 * 4) + 4; Memory.Write32(m_rsx_callback - 4, m_rsx_callback); + mem32_t callback_data(m_rsx_callback); + callback_data += ToOpcode(ADDI) | ToRD(11) | ToRA(0) | ToIMM16(0x3ff); + callback_data += ToOpcode(SC) | ToSYS(2); + callback_data += ToOpcode(G_13) | SetField(BCLR, 21, 30) | ToBO(0x10 | 0x04) | ToBI(0) | ToLK(0); + + m_ppu_thr_exit = Memory.MainMem.Alloc(4 * 3); + + mem32_t ppu_thr_exit_data(m_ppu_thr_exit); + ppu_thr_exit_data += ToOpcode(ADDI) | ToRD(11) | ToRA(0) | ToIMM16(41); + ppu_thr_exit_data += ToOpcode(SC) | ToSYS(2); + ppu_thr_exit_data += ToOpcode(G_13) | SetField(BCLR, 21, 30) | ToBO(0x10 | 0x04) | ToBI(0) | ToLK(0); + thread.Run(); + wxGetApp().m_MainFrame->UpdateUI(); + wxCriticalSectionLocker lock(m_cs_status); + m_status = Ready; +} + +void Emulator::Run() +{ + if(!IsReady()) + { + Load(); + if(!IsReady()) return; + } + + if(IsRunned()) Stop(); + if(IsPaused()) + { + Resume(); + return; + } + + wxCriticalSectionLocker lock(m_cs_status); + //ConLog.Write("run..."); + m_status = Runned; + + m_vfs.Mount("/", vfsDevice::GetRoot(m_path), new vfsLocalFile()); + m_vfs.Mount("/dev_hdd0/", wxGetCwd() + "\\dev_hdd0\\", new vfsLocalFile()); + m_vfs.Mount("/app_home/", vfsDevice::GetRoot(m_path), new vfsLocalFile()); + m_vfs.Mount(vfsDevice::GetRootPs3(m_path), vfsDevice::GetRoot(m_path), new vfsLocalFile()); + + for(uint i=0; i %s", m_vfs.m_devices[i].GetPs3Path(), m_vfs.m_devices[i].GetLocalPath()); + //if(m_memory_viewer && m_memory_viewer->exit) safe_delete(m_memory_viewer); //m_memory_viewer->SetPC(loader.GetEntry()); //m_memory_viewer->Show(); //m_memory_viewer->ShowPC(); + wxGetApp().SendDbgCommand(DID_START_EMU); wxGetApp().m_MainFrame->UpdateUI(); if(!m_dbg_console) m_dbg_console = new DbgConsole(); GetGSManager().Init(); + GetCallbackManager().Init(); - if(Ini.CPUDecoderMode.GetValue() != 1) - { - GetCPU().Start(); - GetCPU().Exec(); - } + GetCPU().Exec(); } void Emulator::Pause() @@ -150,7 +189,9 @@ void Emulator::Pause() if(!IsRunned()) return; //ConLog.Write("pause..."); + wxCriticalSectionLocker lock(m_cs_status); m_status = Paused; + wxGetApp().SendDbgCommand(DID_PAUSE_EMU); wxGetApp().m_MainFrame->UpdateUI(); } @@ -159,7 +200,9 @@ void Emulator::Resume() if(!IsPaused()) return; //ConLog.Write("resume..."); + wxCriticalSectionLocker lock(m_cs_status); m_status = Runned; + wxGetApp().SendDbgCommand(DID_RESUME_EMU); wxGetApp().m_MainFrame->UpdateUI(); CheckStatus(); @@ -170,16 +213,22 @@ void Emulator::Stop() { if(IsStopped()) return; //ConLog.Write("shutdown..."); + { + wxCriticalSectionLocker lock(m_cs_status); + m_status = Stopped; + } m_rsx_callback = 0; - m_status = Stopped; + wxGetApp().SendDbgCommand(DID_STOP_EMU); wxGetApp().m_MainFrame->UpdateUI(); GetGSManager().Close(); GetCPU().Close(); - SysCallsManager.Close(); + //SysCallsManager.Close(); GetIdManager().Clear(); GetPadManager().Close(); + GetCallbackManager().Clear(); + UnloadModules(); CurGameInfo.Reset(); Memory.Close(); @@ -187,7 +236,7 @@ void Emulator::Stop() if(m_dbg_console) { GetDbgCon().Close(); - m_dbg_console = NULL; + GetDbgCon().Clear(); } //if(m_memory_viewer && m_memory_viewer->IsShown()) m_memory_viewer->Hide(); } diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index d3603ee12e..7834238004 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -3,9 +3,11 @@ #include "Gui/MemoryViewer.h" #include "Emu/Cell/PPCThreadManager.h" #include "Emu/Io/Pad.h" -#include "Emu/DbgConsole.h" #include "Emu/GS/GSManager.h" +#include "Emu/FS/VFS.h" +#include "Emu/DbgConsole.h" #include "Loader/Loader.h" +#include "SysCalls/Callback.h" struct EmuInfo { @@ -52,10 +54,12 @@ class Emulator Interpreter, }; + mutable wxCriticalSection m_cs_status; Status m_status; uint m_mode; u32 m_rsx_callback; + u32 m_ppu_thr_exit; MemoryViewerPanel* m_memory_viewer; //ArrayF m_cpu_threads; @@ -64,6 +68,8 @@ class Emulator IdManager m_id_manager; DbgConsole* m_dbg_console; GSManager m_gs_manager; + CallbackManager m_callback_manager; + VFS m_vfs; EmuInfo m_info; @@ -74,14 +80,16 @@ public: Emulator(); void Init(); - virtual void SetSelf(const wxString& path); - virtual void SetElf(const wxString& path); + void SetSelf(const wxString& path); + void SetElf(const wxString& path); - PPCThreadManager& GetCPU() { return m_thread_manager; } - PadManager& GetPadManager() { return m_pad_manager; } - IdManager& GetIdManager() { return m_id_manager; } - DbgConsole& GetDbgCon() { return *m_dbg_console; } - GSManager& GetGSManager() { return m_gs_manager; } + PPCThreadManager& GetCPU() { return m_thread_manager; } + PadManager& GetPadManager() { return m_pad_manager; } + IdManager& GetIdManager() { return m_id_manager; } + DbgConsole& GetDbgCon() { return *m_dbg_console; } + GSManager& GetGSManager() { return m_gs_manager; } + CallbackManager& GetCallbackManager() { return m_callback_manager; } + VFS& GetVFS() { return m_vfs; } void SetTLSData(const u64 addr, const u64 filesz, const u64 memsz) { @@ -97,17 +105,20 @@ public: u32 GetMallocPageSize() { return m_info.GetProcParam().malloc_pagesize; } u32 GetRSXCallback() const { return m_rsx_callback; } + u32 GetPPUThreadExit() const { return m_ppu_thr_exit; } void CheckStatus(); - virtual void Run(); - virtual void Pause(); - virtual void Resume(); - virtual void Stop(); + void Load(); + void Run(); + void Pause(); + void Resume(); + void Stop(); - virtual bool IsRunned() const { return m_status == Runned; } - virtual bool IsPaused() const { return m_status == Paused; } - virtual bool IsStopped() const { return m_status == Stopped; } + __forceinline bool IsRunned() const { wxCriticalSectionLocker lock(m_cs_status); return m_status == Runned; } + __forceinline bool IsPaused() const { wxCriticalSectionLocker lock(m_cs_status); return m_status == Paused; } + __forceinline bool IsStopped() const { wxCriticalSectionLocker lock(m_cs_status); return m_status == Stopped; } + __forceinline bool IsReady() const { wxCriticalSectionLocker lock(m_cs_status); return m_status == Ready; } }; extern Emulator Emu; \ No newline at end of file diff --git a/rpcs3/Gui/CompilerELF.cpp b/rpcs3/Gui/CompilerELF.cpp index 44ca0226f8..45ec23c010 100644 --- a/rpcs3/Gui/CompilerELF.cpp +++ b/rpcs3/Gui/CompilerELF.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "CompilerELF.h" +using namespace PPU_opcodes; void Write8(wxFile& f, const u8 data) { @@ -33,44 +34,6 @@ wxFont GetFont(int size) return wxFont(size, wxMODERN, wxNORMAL, wxNORMAL); } -u32 SetField(u32 src, u32 from, u32 to) -{ - return (src & ((1 << ((to - from) + 1)) - 1)) << (31 - to); -} - -u32 SetField(s32 src, u32 p) -{ - return (src & 0x1) << (31 - p); -} - -u32 ToOpcode(u32 i) { return SetField(i, 0, 5); } -u32 ToRS(u32 i) { return SetField(i, 6, 10); } -u32 ToRD(u32 i) { return SetField(i, 6, 10); } -u32 ToRA(u32 i) { return SetField(i, 11, 15); } -u32 ToRB(u32 i) { return SetField(i, 16, 20); } -u32 ToLL(u32 i) { return i & 0x03fffffc; } -u32 ToAA(u32 i) { return SetField(i, 30); } -u32 ToLK(u32 i) { return SetField(i, 31); } -u32 ToIMM16(s32 i) { return SetField(i, 16, 31); } -u32 ToD(s32 i) { return SetField(i, 16, 31); } -u32 ToDS(s32 i) -{ - if(i < 0) return ToD(i + 1); - return ToD(i); -} -u32 ToSYS(u32 i) { return SetField(i, 6, 31); } -u32 ToBF(u32 i) { return SetField(i, 6, 10); } -u32 ToBO(u32 i) { return SetField(i, 6, 10); } -u32 ToBI(u32 i) { return SetField(i, 11, 15); } -u32 ToBD(u32 i) { return i & 0xfffc; } -u32 ToMB(u32 i) { return SetField(i, 21, 25); } -u32 ToME(u32 i) { return SetField(i, 26, 30); } -u32 ToSH(u32 i) { return SetField(i, 16, 20); } -u32 ToRC(u32 i) { return SetField(i, 31); } -u32 ToOE(u32 i) { return SetField(i, 21); } -u32 ToL(u32 i) { return SetField(i, 10); } -u32 ToCRFD(u32 i) { return SetField(i, 6, 8); } - struct InstrField { u32 (*To)(u32 i); @@ -90,6 +53,8 @@ enum MASKS MASK_LL, MASK_RA_RS_SH_MB_ME, MASK_RA_RS_RB_MB_ME, + MASK_RA_RS_RB, + MASK_RD_RA_RB, MASK_RST_RA_D, MASK_RST_RA_DS, MASK_TO_RA_IMM16, @@ -126,92 +91,119 @@ static const struct Instruction { MASKS mask; wxString name; - u32 op_num; - u32 op_g; + u32 op; + + struct + { + u32 op; + u32 from; + u32 to; + } subop; + u32 smask; u32 bo; u32 bi; } m_instructions[] = { - {MASK_NO_ARG, "nop", ORI, 0, SMASK_NULL}, - {MASK_TO_RA_IMM16, "tdi", TDI, 0, SMASK_NULL}, - {MASK_TO_RA_IMM16, "twi", TWI, 0, SMASK_NULL}, + {MASK_NO_ARG, "nop", ORI, {0}, SMASK_NULL}, + {MASK_TO_RA_IMM16, "tdi", TDI, {0}, SMASK_NULL}, + {MASK_TO_RA_IMM16, "twi", TWI, {0}, SMASK_NULL}, //G_04 = 0x04, - {MASK_RSD_RA_IMM16, "mulli", MULLI, 0, SMASK_NULL}, - {MASK_RSD_RA_IMM16, "subfic", SUBFIC, 0, SMASK_NULL}, - {MASK_CRFD_RA_IMM16, "cmplwi", CMPLI, 0, SMASK_NULL}, - {MASK_CRFD_RA_IMM16, "cmpldi", CMPLI, 0, SMASK_L}, - {MASK_CRFD_RA_IMM16, "cmpwi", CMPI, 0, SMASK_NULL}, - {MASK_CRFD_RA_IMM16, "cmpdi", CMPI, 0, SMASK_L}, - {MASK_RSD_RA_IMM16, "addic", ADDIC, 0, SMASK_NULL}, - {MASK_RSD_RA_IMM16, "addic.", ADDIC_, 0, SMASK_RC}, - {MASK_RSD_RA_IMM16, "addi", ADDI, 0, SMASK_NULL}, - {MASK_RSD_IMM16, "li", ADDI, 0, SMASK_NULL}, - {MASK_RSD_RA_IMM16, "addis", ADDIS, 0, SMASK_NULL}, + {MASK_RSD_RA_IMM16, "mulli", MULLI, {0}, SMASK_NULL}, + {MASK_RSD_RA_IMM16, "subfic", SUBFIC, {0}, SMASK_NULL}, + {MASK_CRFD_RA_IMM16, "cmplwi", CMPLI, {0}, SMASK_NULL}, + {MASK_CRFD_RA_IMM16, "cmpldi", CMPLI, {0}, SMASK_L}, + {MASK_CRFD_RA_IMM16, "cmpwi", CMPI, {0}, SMASK_NULL}, + {MASK_CRFD_RA_IMM16, "cmpdi", CMPI, {0}, SMASK_L}, + {MASK_RSD_RA_IMM16, "addic", ADDIC, {0}, SMASK_NULL}, + {MASK_RSD_RA_IMM16, "addic.", ADDIC_, {0}, SMASK_RC}, + {MASK_RSD_RA_IMM16, "addi", ADDI, {0}, SMASK_NULL}, + {MASK_RSD_IMM16, "li", ADDI, {0}, SMASK_NULL}, + {MASK_RSD_RA_IMM16, "addis", ADDIS, {0}, SMASK_NULL}, - {MASK_BO_BI_BD, "bc", BC, 0, SMASK_NULL}, - {MASK_BO_BI_BD, "bca", BC, 0, SMASK_AA}, - {MASK_BO_BI_BD, "bcl", BC, 0, SMASK_LK}, - {MASK_BO_BI_BD, "bcla", BC, 0, SMASK_AA | SMASK_LK}, - {MASK_BI_BD, "bdz", BC, 0, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO3}, - {MASK_BI_BD, "bdz-", BC, 0, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO1 | BO_MASK_BO3}, - {MASK_BI_BD, "bdz+", BC, 0, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO1 | BO_MASK_BO3 | BO_MASK_BO4}, - {MASK_BI_BD, "bdnz", BC, 0, SMASK_BO, BO_MASK_BO0}, - {MASK_BI_BD, "bdnz-", BC, 0, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO1}, - {MASK_BI_BD, "bdnz+", BC, 0, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO1 | BO_MASK_BO4}, - {MASK_BI_BD, "bge", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2, BI_GE}, - {MASK_BI_BD, "ble", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2, BI_LE}, - {MASK_BI_BD, "bne", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2, BI_NE}, - {MASK_BI_BD, "bge-", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3, BI_GE}, - {MASK_BI_BD, "ble-", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3, BI_LE}, - {MASK_BI_BD, "bne-", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3, BI_NE}, - {MASK_BI_BD, "bge+", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_GE}, - {MASK_BI_BD, "ble+", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_LE}, - {MASK_BI_BD, "bne+", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_NE}, - {MASK_BI_BD, "bgt", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2, BI_GT}, - {MASK_BI_BD, "blt", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2, BI_LT}, - {MASK_BI_BD, "beq", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2, BI_EQ}, - {MASK_BI_BD, "bgt-", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3, BI_GT}, - {MASK_BI_BD, "blt-", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3, BI_LT}, - {MASK_BI_BD, "beq-", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3, BI_EQ}, - {MASK_BI_BD, "bgt+", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_GT}, - {MASK_BI_BD, "blt+", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_LT}, - {MASK_BI_BD, "beq+", BC, 0, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_EQ}, + {MASK_BO_BI_BD, "bc", BC, {0}, SMASK_NULL}, + {MASK_BO_BI_BD, "bca", BC, {0}, SMASK_AA}, + {MASK_BO_BI_BD, "bcl", BC, {0}, SMASK_LK}, + {MASK_BO_BI_BD, "bcla", BC, {0}, SMASK_AA | SMASK_LK}, + {MASK_BI_BD, "bdz", BC, {0}, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO3}, + {MASK_BI_BD, "bdz-", BC, {0}, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO1 | BO_MASK_BO3}, + {MASK_BI_BD, "bdz+", BC, {0}, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO1 | BO_MASK_BO3 | BO_MASK_BO4}, + {MASK_BI_BD, "bdnz", BC, {0}, SMASK_BO, BO_MASK_BO0}, + {MASK_BI_BD, "bdnz-", BC, {0}, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO1}, + {MASK_BI_BD, "bdnz+", BC, {0}, SMASK_BO, BO_MASK_BO0 | BO_MASK_BO1 | BO_MASK_BO4}, + {MASK_BI_BD, "bge", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2, BI_GE}, + {MASK_BI_BD, "ble", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2, BI_LE}, + {MASK_BI_BD, "bne", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2, BI_NE}, + {MASK_BI_BD, "bge-", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3, BI_GE}, + {MASK_BI_BD, "ble-", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3, BI_LE}, + {MASK_BI_BD, "bne-", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3, BI_NE}, + {MASK_BI_BD, "bge+", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_GE}, + {MASK_BI_BD, "ble+", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_LE}, + {MASK_BI_BD, "bne+", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_NE}, + {MASK_BI_BD, "bgt", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2, BI_GT}, + {MASK_BI_BD, "blt", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2, BI_LT}, + {MASK_BI_BD, "beq", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2, BI_EQ}, + {MASK_BI_BD, "bgt-", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3, BI_GT}, + {MASK_BI_BD, "blt-", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3, BI_LT}, + {MASK_BI_BD, "beq-", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3, BI_EQ}, + {MASK_BI_BD, "bgt+", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_GT}, + {MASK_BI_BD, "blt+", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_LT}, + {MASK_BI_BD, "beq+", BC, {0}, SMASK_BO | SMASK_BI, BO_MASK_BO1 | BO_MASK_BO2 | BO_MASK_BO3 | BO_MASK_BO4, BI_EQ}, - {MASK_SYS, "sc", SC, 0, SMASK_NULL}, - {MASK_LL, "b", B, 0, SMASK_NULL}, - {MASK_LL, "ba", B, 0, SMASK_AA}, - {MASK_LL, "bl", B, 0, SMASK_LK}, - {MASK_LL, "bla", B, 0, SMASK_AA | SMASK_LK}, + {MASK_SYS, "sc", SC, {0}, SMASK_NULL}, + {MASK_LL, "b", B, {0}, SMASK_NULL}, + {MASK_LL, "ba", B, {0}, SMASK_AA}, + {MASK_LL, "bl", B, {0}, SMASK_LK}, + {MASK_LL, "bla", B, {0}, SMASK_AA | SMASK_LK}, //G_13 = 0x13, - {MASK_RA_RS_SH_MB_ME, "rlwimi", RLWIMI, 0, SMASK_NULL}, - {MASK_RA_RS_SH_MB_ME, "rlwimi.", RLWIMI, 0, SMASK_RC}, - {MASK_RA_RS_SH_MB_ME, "rlwinm", RLWINM, 0, SMASK_NULL}, - {MASK_RA_RS_SH_MB_ME, "rlwinm.", RLWINM, 0, SMASK_RC}, - {MASK_RA_RS_RB_MB_ME, "rlwnm", RLWNM, 0, SMASK_NULL}, - {MASK_RA_RS_RB_MB_ME, "rlwnm.", RLWNM, 0, SMASK_RC}, - {MASK_RSD_RA_IMM16, "ori", ORI, 0, SMASK_NULL}, - {MASK_RSD_RA_IMM16, "oris", ORIS, 0, SMASK_NULL}, - {MASK_RSD_RA_IMM16, "xori", XORI, 0, SMASK_NULL}, - {MASK_RSD_RA_IMM16, "xoris", XORIS, 0, SMASK_NULL}, - {MASK_RA_RST_IMM16, "andi.", ANDI_, 0, SMASK_RC}, - {MASK_RA_RST_IMM16, "andis.", ANDIS_, 0, SMASK_RC}, + {MASK_RA_RS_SH_MB_ME, "rlwimi", RLWIMI, {0}, SMASK_NULL}, + {MASK_RA_RS_SH_MB_ME, "rlwimi.", RLWIMI, {0}, SMASK_RC}, + {MASK_RA_RS_SH_MB_ME, "rlwinm", RLWINM, {0}, SMASK_NULL}, + {MASK_RA_RS_SH_MB_ME, "rlwinm.", RLWINM, {0}, SMASK_RC}, + {MASK_RA_RS_RB_MB_ME, "rlwnm", RLWNM, {0}, SMASK_NULL}, + {MASK_RA_RS_RB_MB_ME, "rlwnm.", RLWNM, {0}, SMASK_RC}, + {MASK_RSD_RA_IMM16, "ori", ORI, {0}, SMASK_NULL}, + {MASK_RSD_RA_IMM16, "oris", ORIS, {0}, SMASK_NULL}, + {MASK_RSD_RA_IMM16, "xori", XORI, {0}, SMASK_NULL}, + {MASK_RSD_RA_IMM16, "xoris", XORIS, {0}, SMASK_NULL}, + {MASK_RA_RST_IMM16, "andi.", ANDI_, {0}, SMASK_RC}, + {MASK_RA_RST_IMM16, "andis.", ANDIS_, {0}, SMASK_RC}, //G_1e = 0x1e, - //G_1f = 0x1f, - {MASK_RST_RA_D, "lwz", LWZ, 0, SMASK_NULL}, - {MASK_RST_RA_D, "lwzu", LWZU, 0, SMASK_NULL}, - {MASK_RST_RA_D, "lbz", LBZ, 0, SMASK_NULL}, - {MASK_RST_RA_D, "lbzu", LBZU, 0, SMASK_NULL}, - {MASK_RST_RA_D, "stw", STW, 0, SMASK_NULL}, - {MASK_RST_RA_D, "stwu", STWU, 0, SMASK_NULL}, - {MASK_RST_RA_D, "stb", STB, 0, SMASK_NULL}, - {MASK_RST_RA_D, "stbu", STBU, 0, SMASK_NULL}, - {MASK_RST_RA_D, "lhz", LHZ, 0, SMASK_NULL}, - {MASK_RST_RA_D, "lhzu", LHZU, 0, SMASK_NULL}, - {MASK_RST_RA_D, "lha", LHA, 0, SMASK_NULL}, - {MASK_RST_RA_D, "lhau", LHAU, 0, SMASK_NULL}, - {MASK_RST_RA_D, "sth", STH, 0, SMASK_NULL}, + {MASK_RD_RA_RB, "lwarx", G_1f, {LWARX, 21, 30}, SMASK_NULL}, + {MASK_RA_RS_RB, "ldx", G_1f, {LDX, 21, 30}, SMASK_NULL}, + {MASK_RA_RS_RB, "slw", G_1f, {SLW, 21, 30}, SMASK_NULL}, + {MASK_RA_RS_RB, "slw.", G_1f, {SLW, 21, 30}, SMASK_RC}, + {MASK_RA_RS_RB, "sld", G_1f, {SLD, 21, 30}, SMASK_NULL}, + {MASK_RA_RS_RB, "sld.", G_1f, {SLD, 21, 30}, SMASK_RC}, + {MASK_RA_RS_RB, "and", G_1f, {AND, 21, 30}, SMASK_NULL}, + {MASK_RA_RS_RB, "and.", G_1f, {AND, 21, 30}, SMASK_RC}, + {MASK_RD_RA_RB, "subf", G_1f, {SUBF, 21, 30}, SMASK_NULL}, + {MASK_RD_RA_RB, "subf.", G_1f, {SUBF, 21, 30}, SMASK_RC}, + {MASK_RD_RA_RB, "subfo", G_1f, {SUBF, 21, 30}, SMASK_OE}, + {MASK_RD_RA_RB, "subfo.", G_1f, {SUBF, 21, 30}, SMASK_OE | SMASK_RC}, + {MASK_RA_RS_RB, "andc", G_1f, {ANDC, 21, 30}, SMASK_NULL}, + {MASK_RA_RS_RB, "andc.", G_1f, {ANDC, 21, 30}, SMASK_RC}, + {MASK_RD_RA_RB, "mulhd", G_1f, {MULHD, 21, 30}, SMASK_NULL}, + {MASK_RD_RA_RB, "mulhd.", G_1f, {MULHD, 21, 30}, SMASK_RC}, + {MASK_RD_RA_RB, "mulhw", G_1f, {MULHW, 21, 30}, SMASK_NULL}, + {MASK_RD_RA_RB, "mulhw.", G_1f, {MULHW, 21, 30}, SMASK_RC}, + {MASK_RD_RA_RB, "ldarx", G_1f, {LDARX, 21, 30}, SMASK_NULL}, + {MASK_RD_RA_RB, "lbzx", G_1f, {LBZX, 21, 30}, SMASK_NULL}, + + {MASK_RST_RA_D, "lwz", LWZ, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "lwzu", LWZU, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "lbz", LBZ, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "lbzu", LBZU, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "stw", STW, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "stwu", STWU, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "stb", STB, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "stbu", STBU, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "lhz", LHZ, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "lhzu", LHZU, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "lha", LHA, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "lhau", LHAU, {0}, SMASK_NULL}, + {MASK_RST_RA_D, "sth", STH, {0}, SMASK_NULL}, //{0, "lmw", LMW, 0, false, false}, //LFS = 0x30, //LFSU = 0x31, @@ -225,8 +217,8 @@ static const struct Instruction //G_3b = 0x3b, //G_3e = 0x3e, //G_3f = 0x3f, - {MASK_UNK, "unk", 0, 0, SMASK_NULL}, -}, m_error_instr = {MASK_ERROR, "err", 0, 0, SMASK_ERROR}; + {MASK_UNK, "unk", 0, {0}, SMASK_NULL}, +}, m_error_instr = {MASK_ERROR, "err", 0, {0}, SMASK_ERROR}; static const u32 instr_count = WXSIZEOF(m_instructions); enum ArgType @@ -271,6 +263,8 @@ Instruction GetInstruction(const wxString& str) u32 CompileInstruction(Instruction instr, Array& arr) { + if(instr.mask == MASK_ERROR) return 0; + u32 code = ToOE(instr.smask & SMASK_OE) | ToRC(instr.smask & SMASK_RC) | ToAA(instr.smask & SMASK_AA) | ToLK(instr.smask & SMASK_LK) | @@ -279,7 +273,9 @@ u32 CompileInstruction(Instruction instr, Array& arr) if(instr.smask & SMASK_BO) code |= ToBO(instr.bo); if(instr.smask & SMASK_BI) code |= ToBI(instr.bi); - code |= ToOpcode(instr.op_num); + code |= ToOpcode(instr.op); + + if(instr.subop.from < instr.subop.to) code |= SetField(instr.subop.op, instr.subop.from, instr.subop.to); switch(instr.mask) { @@ -299,10 +295,18 @@ u32 CompileInstruction(Instruction instr, Array& arr) return code | ToLL(arr[0].value); case MASK_RA_RS_SH_MB_ME: return code | ToRA(arr[0].value) | ToRS(arr[1].value) | ToSH(arr[2].value) | ToMB(arr[3].value) | ToME(arr[4].value); + case MASK_RA_RS_RB_MB_ME: + return code | ToRA(arr[0].value) | ToRS(arr[1].value) | ToRB(arr[2].value) | ToMB(arr[3].value) | ToME(arr[4].value); + case MASK_RA_RS_RB: + return code | ToRA(arr[0].value) | ToRS(arr[1].value) | ToRB(arr[2].value); + case MASK_RD_RA_RB: + return code | ToRD(arr[0].value) | ToRA(arr[1].value) | ToRB(arr[2].value); case MASK_RST_RA_D: return code | ToRS(arr[0].value) | ToRA(arr[1].value) | ToD(arr[2].value); case MASK_RST_RA_DS: return code | ToRS(arr[0].value) | ToRA(arr[1].value) | ToDS(arr[2].value); + case MASK_TO_RA_IMM16: + return code | ToTO(arr[0].value) | ToRA(arr[1].value) | ToIMM16(arr[2].value); case MASK_RSD_IMM16: return code | ToRS(arr[0].value) | ToIMM16(arr[1].value); @@ -417,78 +421,46 @@ CompilerELF::CompilerELF(wxWindow* parent) asm_list->SetFont(wxFont(-1, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); hex_list->SetFont(wxFont(-1, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); - wxGetApp().Connect(wxEVT_SCROLLWIN_TOP, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); - wxGetApp().Connect(wxEVT_SCROLLWIN_BOTTOM, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); - wxGetApp().Connect(wxEVT_SCROLLWIN_LINEUP, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); - wxGetApp().Connect(wxEVT_SCROLLWIN_LINEDOWN, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); - wxGetApp().Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); - wxGetApp().Connect(wxEVT_SCROLLWIN_THUMBRELEASE,wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_SCROLLWIN_TOP, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_SCROLLWIN_BOTTOM, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_SCROLLWIN_LINEUP, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_SCROLLWIN_LINEDOWN, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_SCROLLWIN_THUMBRELEASE,wxScrollWinEventHandler(CompilerELF::OnScroll), (wxObject*)0, this); - wxGetApp().Connect(asm_list->GetId(), wxEVT_MOUSEWHEEL, wxMouseEventHandler(CompilerELF::MouseWheel), (wxObject*)0, this); - wxGetApp().Connect(hex_list->GetId(), wxEVT_MOUSEWHEEL, wxMouseEventHandler(CompilerELF::MouseWheel), (wxObject*)0, this); + m_app_connector.Connect(asm_list->GetId(), wxEVT_MOUSEWHEEL, wxMouseEventHandler(CompilerELF::MouseWheel), (wxObject*)0, this); + m_app_connector.Connect(hex_list->GetId(), wxEVT_MOUSEWHEEL, wxMouseEventHandler(CompilerELF::MouseWheel), (wxObject*)0, this); - wxGetApp().Connect(asm_list->GetId(), wxEVT_KEY_DOWN, wxKeyEventHandler(CompilerELF::OnKeyDown), (wxObject*)0, this); - wxGetApp().Connect(hex_list->GetId(), wxEVT_KEY_DOWN, wxKeyEventHandler(CompilerELF::OnKeyDown), (wxObject*)0, this); + m_app_connector.Connect(asm_list->GetId(), wxEVT_KEY_DOWN, wxKeyEventHandler(CompilerELF::OnKeyDown), (wxObject*)0, this); + m_app_connector.Connect(hex_list->GetId(), wxEVT_KEY_DOWN, wxKeyEventHandler(CompilerELF::OnKeyDown), (wxObject*)0, this); asm_list->WriteText( - "[sysPrxForUser, sys_initialize_tls, 0x744680a2]\n" - "[sysPrxForUser, sys_process_exit, 0xe6f2c1e7]\n" - "[sys_fs, cellFsOpen, 0x718bf5f8]\n" - "[sys_fs, cellFsClose, 0x2cb51f0d]\n" - "[sys_fs, cellFsRead, 0x4d5ff8e2]\n" - "[sys_fs, cellFsWrite, 0xecdcf2ab]\n" - "[sys_fs, cellFsLseek, 0xa397d042]\n" - "[sys_fs, cellFsMkdir, 0xba901fe6]\n" - "\n" - ".int [flags, 577] #LV2_O_WRONLY | LV2_O_CREAT | LV2_O_TRUNC\n" - ".string [path, \"dev_hdd0/game/TEST12345/USRDIR/test.txt\"]\n" - ".string [str, \"Hello World!\"]\n" + ".int [sys_tty_write, 0x193]\n" + ".int [sys_process_exit, 0x003]\n" + ".string [str, \"Hello World!\\n\"]\n" ".strlen [str_len, str]\n" - ".buf [fd, 8]\n" - "\n" - ".srr [fd_hi, fd, 16]\n" - ".and [fd_lo, fd, 0xffff]\n" - "\n" - ".srr [path_hi, path, 16]\n" - ".and [path_lo, path, 0xffff]\n" + ".buf [ret, 8]\n" "\n" ".srr [str_hi, str, 16]\n" ".and [str_lo, str, 0xffff]\n" "\n" - "\tbl \tsys_initialize_tls\n" + ".srr [ret_hi, str, 16]\n" + ".and [ret_lo, str, 0xffff]\n" "\n" - "\tli \tr3, path_lo\n" - "\toris \tr3, r3, path_hi\n" - "\tli \tr4, flags\n" - "\tli \tr5, fd_lo\n" - "\toris \tr5, r5, fd_hi\n" - "\tli \tr6, 0\n" - "\tli \tr7, 0\n" - "\tbl \tcellFsOpen\n" - "\tcmpwi \tcr7, r3, 0\n" - "\tblt- \tcr7, exit_err\n" - "\n" - "\tli \tr3, fd_lo\n" - "\toris \tr3, r3, fd_hi\n" - "\tlwz \tr3, r3, 0\n" - "\tli \tr4, str_lo\n" - "\toris \tr4, r4, str_hi\n" - "\tli \tr5, str_len\n" - "\tli \tr6, 0\n" - "\tbl \tcellFsWrite\n" - "\tcmpwi \tcr7, r3, 0\n" - "\tblt- \tcr7, exit_err\n" - "\n" - "\tli \tr3, fd_lo\n" - "\toris \tr3, r3, fd_hi\n" - "\tlwz \tr3, r3, 0\n" - "\tbl \tcellFsClose\n" - "\tcmpwi \tcr7, r3, 0\n" - "\tblt- \tcr7, exit_err\n" + " li r3, 0\n" + " li r4, str_lo\n" + " oris r4, r4, str_hi\n" + " li r5, str_len\n" + " li r6, ret_lo\n" + " oris r6, r6, ret_hi\n" + " li r11, sys_tty_write\n" + " sc\n" + " cmpwi cr7, r3, 0\n" + " bne- cr7, exit_err\n" "\n" "exit_ok:\n" - "\tli \tr3, 0\n" - "\tb \texit\n" + " li r3, 0\n" + " b exit\n" "\n" "exit_err:\n" ".string [str, \"error.\\n\"]\n" @@ -497,21 +469,21 @@ CompilerELF::CompilerELF(wxWindow* parent) ".strlen [str_len, str]\n" ".int [written_len_lo, fd_lo]\n" ".int [written_len_hi, fd_hi]\n" - "\tli \tr3, 1\n" - "\tli \tr4, str_lo\n" - "\toris \tr4, r4, str_hi\n" - "\tli \tr5, str_len\n" - "\tli \tr6, written_len_lo\n" - "\toris \tr6, r6, written_len_hi\n" - "\tli \tr11, 403\n" - "\tsc\n" - "\tli \tr3, 1\n" - "\tb \texit\n" + " li r3, 1\n" + " li r4, str_lo\n" + " oris r4, r4, str_hi\n" + " li r5, str_len\n" + " li r6, written_len_lo\n" + " oris r6, r6, written_len_hi\n" + " li r11, sys_tty_write\n" + " sc\n" + " li r3, 1\n" "\n" "exit:\n" - "\tbl \tsys_process_exit\n" - "\tli \tr3, 1\n" - "\tb \texit\n" + " li r11, sys_process_exit\n" + " sc\n" + " li r3, 1\n" + " b exit\n" ); ::SendMessage((HWND)hex_list->GetHWND(), WM_VSCROLL, SB_BOTTOM, 0); @@ -685,6 +657,8 @@ void CompilerELF::OnUpdate(wxCommandEvent& event) void CompilerELF::OnScroll(wxScrollWinEvent& event) { + if(!m_aui_mgr.GetManagedWindow()) return; + const int id = event.GetEventObject() ? ((wxScrollBar*)event.GetEventObject())->GetId() : -1; wxTextCtrl* src = NULL; @@ -960,13 +934,12 @@ struct CompileProgram return false; } - bool GetArg(wxString& result, bool func = false) + int GetArg(wxString& result, bool func = false) { s64 from = -1; - for(;;) + for(char cur_char = NextChar(); !m_error; cur_char = NextChar()) { - const char cur_char = NextChar(); const bool skip = IsSkip(cur_char); const bool commit = IsCommit(cur_char); const bool endln = IsEndLn(cur_char); @@ -976,7 +949,7 @@ struct CompileProgram if(from == -1) { - if(endln || commit || end) return false; + if(endln || commit || end) return 0; if(!skip) from = p - 1; continue; } @@ -984,7 +957,7 @@ struct CompileProgram const bool text = m_asm[from] == '"'; const bool end_text = cur_char == '"'; - if((text ? end_text : skip || commit || end) || endln) + if((text ? end_text : (skip || commit || end)) || endln) { if(text && p > 2 && m_asm[p - 2] == '\\' && (p <= 3 || m_asm[p - 3] != '\\')) { @@ -997,14 +970,13 @@ struct CompileProgram m_error = true; } - const s64 to = (endln || text ? p : p - 1) - from; - bool ret = true; + const s64 to = ((endln || text) ? p : p - 1) - from; + int ret = 1; if(skip || text) { - for(;;) + for(char cur_char = NextChar(); !m_error; cur_char = NextChar()) { - const char cur_char = NextChar(); const bool skip = IsSkip(cur_char); const bool commit = IsCommit(cur_char); const bool endln = IsEndLn(cur_char); @@ -1015,20 +987,20 @@ struct CompileProgram if(commit) { - ret = false; EndLn(); + ret = -1; break; } if(endln) { - ret = false; p--; break; } WriteError(wxString::Format("Bad symbol '%c'", cur_char)); m_error = true; + break; } } @@ -1059,11 +1031,19 @@ struct CompileProgram return ret; } } + + return 0; } bool CheckEnd(bool show_err = true) { - for(;;) + if(m_error) + { + NextLn(); + return false; + } + + while(true) { const char cur_char = NextChar(); const bool skip = IsSkip(cur_char); @@ -1266,14 +1246,15 @@ struct CompileProgram m_cur_arg = 0; wxString str; - while(GetArg(str)) + while(int r = GetArg(str)) { - Arg arg(str); - DetectArgInfo(arg); - m_args.AddCpy(arg); + Arg* arg = new Arg(str); + DetectArgInfo(*arg); + m_args.Add(arg); + if(r == -1) break; } - m_end_args = true; + m_end_args = m_args.GetCount() > 0; } u32 GetBranchValue(const wxString& branch) @@ -1444,7 +1425,7 @@ struct CompileProgram { if(create) { - m_branches.Add(new Branch(name, -1, addr)); + m_branches.Move(new Branch(name, -1, addr)); return; } @@ -1500,7 +1481,7 @@ struct CompileProgram case ARG_ERR: { - m_branches.Add(new Branch(wxEmptyString, -1, 0)); + m_branches.Move(new Branch(wxEmptyString, -1, 0)); dst_branch = &m_branches[m_branches.GetCount() - 1]; } break; @@ -1579,7 +1560,7 @@ struct CompileProgram if(!founded) { const u32 addr = s_opd.sh_addr + s_opd.sh_size; - m_sp_string.Add(new SpData(src1, addr)); + m_sp_string.Move(new SpData(src1, addr)); s_opd.sh_size += src1.Len() + 1; *dst_branch = Branch(dst, -1, addr); } @@ -1879,7 +1860,7 @@ struct CompileProgram if(!CheckEnd()) continue; - m_branches.Add(new Branch(name, a_id.value, 0)); + m_branches.Move(new Branch(name, a_id.value, 0)); const u32 import = m_branches.GetCount() - 1; bool founded = false; @@ -1891,7 +1872,7 @@ struct CompileProgram break; } - if(!founded) modules.Add(new Module(module, import)); + if(!founded) modules.Move(new Module(module, import)); } u32 imports_count = 0; @@ -2117,7 +2098,7 @@ struct CompileProgram if(m_error) break; - m_branches.Add(new Branch(name, m_branch_pos)); + m_branches.Move(new Branch(name, m_branch_pos)); CheckEnd(); continue; @@ -2136,7 +2117,7 @@ struct CompileProgram break; } - if(!has_entry) m_branches.Add(new Branch("entry", 0)); + if(!has_entry) m_branches.Move(new Branch("entry", 0)); if(m_analyze) m_error = false; FirstChar(); @@ -2202,7 +2183,7 @@ struct CompileProgram break; case ARG_REG_R: - m_args.InsertCpy(0, Arg("cr0", 0, ARG_REG_CR)); + m_args.Move(0, new Arg("cr0", 0, ARG_REG_CR)); break; } } @@ -2219,13 +2200,13 @@ struct CompileProgram case MASK_BI_BD: if(!SetNextArgType(ARG_REG_CR, false)) { - m_args.InsertCpy(0, Arg("cr0", 0, ARG_REG_CR)); + m_args.Move(0, new Arg("cr0", 0, ARG_REG_CR)); } SetNextArgBranch(instr.smask & SMASK_AA); break; case MASK_SYS: - if(!SetNextArgType(ARG_IMM, false)) m_args.AddCpy(Arg("2", 2, ARG_IMM)); + if(!SetNextArgType(ARG_IMM, false)) m_args.Move(new Arg("2", 2, ARG_IMM)); break; case MASK_LL: @@ -2248,6 +2229,18 @@ struct CompileProgram SetNextArgType(ARG_IMM); break; + case MASK_RA_RS_RB: + SetNextArgType(ARG_REG_R); + SetNextArgType(ARG_REG_R); + SetNextArgType(ARG_REG_R); + break; + + case MASK_RD_RA_RB: + SetNextArgType(ARG_REG_R); + SetNextArgType(ARG_REG_R); + SetNextArgType(ARG_REG_R); + break; + case MASK_RST_RA_D: SetNextArgType(ARG_REG_R); SetNextArgType(ARG_REG_R); @@ -2527,7 +2520,7 @@ struct CompileProgram WritePhdr(f, p_loos_2); sections_names.Clear(); - free(opd_data); + delete[] opd_data; for(u32 i=0; i #include "Ini.h" +#include +#include LogWriter ConLog; LogFrame* ConLogFrame; -wxMutex mtx_wait; + +wxCriticalSection g_cs_conlog; static const uint max_item_count = 500; static const uint buffer_size = 1024 * 64; -wxSemaphore m_conlog_sem; - struct LogPacket { wxString m_prefix; @@ -24,6 +25,7 @@ struct LogPacket , m_text(text) , m_colour(colour) { + } LogPacket() @@ -37,7 +39,7 @@ struct _LogBuffer : public MTPacketBuffer { } - void Push(const LogPacket& data) + void _push(const LogPacket& data) { const u32 sprefix = data.m_prefix.Len(); const u32 stext = data.m_text.Len(); @@ -69,7 +71,7 @@ struct _LogBuffer : public MTPacketBuffer CheckBusy(); } - LogPacket Pop() + LogPacket _pop() { LogPacket ret; @@ -107,14 +109,26 @@ LogWriter::LogWriter() void LogWriter::WriteToLog(wxString prefix, wxString value, wxString colour/*, wxColour bgcolour*/) { + if(ThreadBase* thr = GetCurrentNamedThread()) + { + prefix = (prefix.IsEmpty() ? "" : prefix + " : ") + thr->GetThreadName(); + } + if(m_logfile.IsOpened()) m_logfile.Write((prefix.IsEmpty() ? wxEmptyString : "[" + prefix + "]: ") + value + "\n"); if(!ConLogFrame) return; - wxMutexLocker locker(mtx_wait); + wxCriticalSectionLocker lock(g_cs_conlog); - while(LogBuffer.IsBusy()) Sleep(1); + if(wxThread::IsMain()) + { + while(LogBuffer.IsBusy()) wxYieldIfNeeded(); + } + else + { + while(LogBuffer.IsBusy()) Sleep(1); + } //if(LogBuffer.put == LogBuffer.get) LogBuffer.Flush(); @@ -179,29 +193,23 @@ void LogWriter::SkipLn() WriteToLog(wxEmptyString, wxEmptyString, "Black"); } -BEGIN_EVENT_TABLE(LogFrame, FrameBase) +BEGIN_EVENT_TABLE(LogFrame, wxPanel) EVT_CLOSE(LogFrame::OnQuit) END_EVENT_TABLE() -LogFrame::LogFrame() - : FrameBase(NULL, wxID_ANY, "Log Console", wxEmptyString, wxSize(600, 450), wxDefaultPosition) +LogFrame::LogFrame(wxWindow* parent) + : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(600, 500)) , ThreadBase(false, "LogThread") , m_log(*new wxListView(this)) { - wxBoxSizer& s_panel( *new wxBoxSizer(wxHORIZONTAL) ); - - s_panel.Add(&m_log, wxSizerFlags().Expand()); - m_log.InsertColumn(0, wxEmptyString); m_log.InsertColumn(1, "Log"); m_log.SetBackgroundColour(wxColour("Black")); - m_log.SetColumnWidth(0, 18); - - SetSizerAndFit( &s_panel ); - - Connect( wxEVT_SIZE, wxSizeEventHandler(LogFrame::OnResize) ); - Connect( m_log.GetId(), wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, wxListEventHandler( LogFrame::OnColBeginDrag )); + wxBoxSizer& s_main = *new wxBoxSizer(wxVERTICAL); + s_main.Add(&m_log, 1, wxEXPAND); + SetSizer(&s_main); + Layout(); Show(); ThreadBase::Start(); @@ -215,7 +223,7 @@ bool LogFrame::Close(bool force) { Stop(); ConLogFrame = NULL; - return FrameBase::Close(force); + return wxWindowBase::Close(force); } void LogFrame::Task() @@ -227,12 +235,17 @@ void LogFrame::Task() Sleep(1); continue; } + else + { + wxThread::Yield(); + } const LogPacket item = LogBuffer.Pop(); while(m_log.GetItemCount() > max_item_count) { m_log.DeleteItem(0); + wxThread::Yield(); } const int cur_item = m_log.GetItemCount(); @@ -240,6 +253,8 @@ void LogFrame::Task() m_log.InsertItem(cur_item, item.m_prefix); m_log.SetItem(cur_item, 1, item.m_text); m_log.SetItemTextColour(cur_item, item.m_colour); + m_log.SetColumnWidth(0, -1); + m_log.SetColumnWidth(1, -1); ::SendMessage((HWND)m_log.GetHWND(), WM_VSCROLL, SB_BOTTOM, 0); } @@ -247,21 +262,6 @@ void LogFrame::Task() LogBuffer.Flush(); } -void LogFrame::OnColBeginDrag(wxListEvent& event) -{ - event.Veto(); -} - -void LogFrame::OnResize(wxSizeEvent& event) -{ - const wxSize size( GetClientSize() ); - - m_log.SetSize( size ); - m_log.SetColumnWidth(1, size.GetWidth() - 4 - m_log.GetColumnWidth(0)); - - event.Skip(); -} - void LogFrame::OnQuit(wxCloseEvent& event) { Stop(); diff --git a/rpcs3/Gui/ConLog.h b/rpcs3/Gui/ConLog.h index 8a6b0276fd..6d724ec485 100644 --- a/rpcs3/Gui/ConLog.h +++ b/rpcs3/Gui/ConLog.h @@ -23,20 +23,18 @@ public: }; class LogFrame - : public FrameBase + : public wxPanel , public ThreadBase { wxListView& m_log; public: - LogFrame(); + LogFrame(wxWindow* parent); ~LogFrame(); bool Close(bool force = false); private: - virtual void OnColBeginDrag(wxListEvent& event); - virtual void OnResize(wxSizeEvent& event); virtual void Task(); void OnQuit(wxCloseEvent& event); diff --git a/rpcs3/Gui/Debugger.cpp b/rpcs3/Gui/Debugger.cpp new file mode 100644 index 0000000000..31bb005d06 --- /dev/null +++ b/rpcs3/Gui/Debugger.cpp @@ -0,0 +1,148 @@ +#include "stdafx.h" +#include + +#include "Debugger.h" +#include "Emu/Memory/Memory.h" +#include "InterpreterDisAsm.h" + +class DbgEmuPanel : public wxPanel +{ + AppConnector m_app_connector; + + wxButton* m_btn_run; + wxButton* m_btn_stop; + wxButton* m_btn_restart; + +public: + DbgEmuPanel(wxWindow* parent) : wxPanel(parent) + { + m_btn_run = new wxButton(this, wxID_ANY, "Run"); + m_btn_stop = new wxButton(this, wxID_ANY, "Stop"); + m_btn_restart = new wxButton(this, wxID_ANY, "Restart"); + + wxBoxSizer& s_b_main = *new wxBoxSizer(wxHORIZONTAL); + + s_b_main.Add(m_btn_run, wxSizerFlags().Border(wxALL, 5)); + s_b_main.Add(m_btn_stop, wxSizerFlags().Border(wxALL, 5)); + s_b_main.Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND); + s_b_main.Add(m_btn_restart, wxSizerFlags().Border(wxALL, 5)); + + SetSizer(&s_b_main); + Layout(); + + UpdateUI(); + Connect(m_btn_run->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DbgEmuPanel::OnRun)); + Connect(m_btn_stop->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DbgEmuPanel::OnStop)); + Connect(m_btn_restart->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DbgEmuPanel::OnRestart)); + + m_app_connector.Connect(wxEVT_DBG_COMMAND, wxCommandEventHandler(DbgEmuPanel::HandleCommand), (wxObject*)0, this); + } + + void UpdateUI() + { + m_btn_run->Enable(!Emu.IsStopped()); + m_btn_stop->Enable(!Emu.IsStopped()); + m_btn_restart->Enable(!Emu.m_path.IsEmpty()); + } + + void OnRun(wxCommandEvent& event) + { + if(Emu.IsRunned()) + { + Emu.Pause(); + } + else if(Emu.IsPaused()) + { + Emu.Resume(); + } + else + { + Emu.Run(); + } + } + + void OnStop(wxCommandEvent& event) + { + Emu.Stop(); + } + + void OnRestart(wxCommandEvent& event) + { + Emu.Stop(); + Emu.Load(); + } + + void HandleCommand(wxCommandEvent& event) + { + switch(event.GetId()) + { + case DID_STOP_EMU: + m_btn_run->SetLabel("Run"); + break; + + case DID_PAUSE_EMU: + m_btn_run->SetLabel("Resume"); + break; + + case DID_START_EMU: + case DID_RESUME_EMU: + m_btn_run->SetLabel("Pause"); + break; + } + + UpdateUI(); + event.Skip(); + } +}; + +DebuggerPanel::DebuggerPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(400, 600), wxTAB_TRAVERSAL) +{ + m_aui_mgr.SetManagedWindow(this); + + m_nb = new wxAuiNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxAUI_NB_TOP | wxAUI_NB_TAB_SPLIT | + wxAUI_NB_TAB_EXTERNAL_MOVE | wxAUI_NB_SCROLL_BUTTONS | + wxAUI_NB_WINDOWLIST_BUTTON | wxAUI_NB_TAB_MOVE | wxNO_BORDER); + + m_aui_mgr.AddPane(new DbgEmuPanel(this), wxAuiPaneInfo().Top()); + m_aui_mgr.AddPane(m_nb, wxAuiPaneInfo().Center().CaptionVisible(false).CloseButton().MaximizeButton()); + m_aui_mgr.Update(); + + m_app_connector.Connect(wxEVT_DBG_COMMAND, wxCommandEventHandler(DebuggerPanel::HandleCommand), (wxObject*)0, this); +} + +DebuggerPanel::~DebuggerPanel() +{ + m_aui_mgr.UnInit(); +} + +void DebuggerPanel::UpdateUI() +{ +} + +void DebuggerPanel::HandleCommand(wxCommandEvent& event) +{ + PPCThread* thr = (PPCThread*)event.GetClientData(); + + switch(event.GetId()) + { + case DID_CREATE_THREAD: + m_nb->AddPage(new InterpreterDisAsmFrame(m_nb, thr), thr->GetFName()); + break; + + case DID_REMOVE_THREAD: + for(uint i=0; iGetPageCount(); ++i) + { + InterpreterDisAsmFrame* page = (InterpreterDisAsmFrame*)m_nb->GetPage(i); + + if(page->CPU.GetId() == thr->GetId()) + { + m_nb->DeletePage(i); + break; + } + } + break; + } + + event.Skip(); +} \ No newline at end of file diff --git a/rpcs3/Gui/Debugger.h b/rpcs3/Gui/Debugger.h new file mode 100644 index 0000000000..e127642937 --- /dev/null +++ b/rpcs3/Gui/Debugger.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include "wx/aui/aui.h" + +class DebuggerPanel : public wxPanel +{ + wxAuiManager m_aui_mgr; + wxAuiNotebook* m_nb; + AppConnector m_app_connector; + +public: + DebuggerPanel(wxWindow* parent); + ~DebuggerPanel(); + + void UpdateUI(); + void HandleCommand(wxCommandEvent& event); +}; \ No newline at end of file diff --git a/rpcs3/Gui/DisAsmFrame.cpp b/rpcs3/Gui/DisAsmFrame.cpp index defba175e8..808ec657f6 100644 --- a/rpcs3/Gui/DisAsmFrame.cpp +++ b/rpcs3/Gui/DisAsmFrame.cpp @@ -54,7 +54,7 @@ DisAsmFrame::DisAsmFrame(PPCThread& cpu) Connect( wxEVT_SIZE, wxSizeEventHandler(DisAsmFrame::OnResize) ); - wxGetApp().Connect(m_disasm_list->GetId(), wxEVT_MOUSEWHEEL, wxMouseEventHandler(DisAsmFrame::MouseWheel), (wxObject*)0, this); + m_app_connector.Connect(m_disasm_list->GetId(), wxEVT_MOUSEWHEEL, wxMouseEventHandler(DisAsmFrame::MouseWheel), (wxObject*)0, this); Connect(b_prev.GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DisAsmFrame::Prev)); Connect(b_next.GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(DisAsmFrame::Next)); @@ -211,7 +211,7 @@ struct WaitDumperThread : public ThreadBase ~WaitDumperThread() { - free((bool*)done); + delete (bool*)done; done = NULL; } @@ -272,11 +272,11 @@ struct WaitDumperThread : public ThreadBase for(uint sh=0; shLoadInfo()) { delete l_elf64; @@ -323,7 +323,7 @@ void DisAsmFrame::Dump(wxCommandEvent& WXUNUSED(event)) case CLASS_ELF32: ElfType64 = false; - l_elf32 = new ELF32Loader(Emu.m_path); + l_elf32 = new ELF32Loader(f_elf); if(!l_elf32->LoadInfo()) { delete l_elf32; diff --git a/rpcs3/Gui/DisAsmFrame.h b/rpcs3/Gui/DisAsmFrame.h index 82290e2427..a863b4af13 100644 --- a/rpcs3/Gui/DisAsmFrame.h +++ b/rpcs3/Gui/DisAsmFrame.h @@ -5,6 +5,7 @@ class DisAsmFrame : public wxFrame static const uint LINES_OPCODES = 40; u32 count; + AppConnector m_app_connector; wxListView* m_disasm_list; PPCThread& CPU; diff --git a/rpcs3/Gui/FrameBase.h b/rpcs3/Gui/FrameBase.h index 582c14086a..e6abb14287 100644 --- a/rpcs3/Gui/FrameBase.h +++ b/rpcs3/Gui/FrameBase.h @@ -5,6 +5,7 @@ class FrameBase : public wxFrame protected: IniEntry m_ini; WindowInfo m_default_info; + bool m_is_skip_resize; FrameBase( wxWindow* parent, @@ -13,9 +14,11 @@ protected: const wxString& ininame = wxEmptyString, wxSize defsize = wxDefaultSize, wxPoint defposition = wxDefaultPosition, - long style = wxDEFAULT_FRAME_STYLE) + long style = wxDEFAULT_FRAME_STYLE, + bool is_skip_resize = false) : wxFrame(parent, id, framename, defposition, defsize, style) , m_default_info(defsize, defposition) + , m_is_skip_resize(is_skip_resize) { m_ini.Init(ininame.IsEmpty() ? framename : ininame, "GuiSettings"); LoadInfo(); @@ -51,7 +54,7 @@ protected: void OnResize(wxSizeEvent& event) { m_ini.SetValue(WindowInfo(GetSize(), m_ini.GetValue().position)); - //event.Skip(); + if(m_is_skip_resize) event.Skip(); } void OnClose(wxCloseEvent& event) diff --git a/rpcs3/Gui/GameViewer.cpp b/rpcs3/Gui/GameViewer.cpp index 04d4caf4e1..c559a41820 100644 --- a/rpcs3/Gui/GameViewer.cpp +++ b/rpcs3/Gui/GameViewer.cpp @@ -3,21 +3,14 @@ #include "Loader/PSF.h" static const wxString m_class_name = "GameViewer"; -GameViewer::GameViewer(wxWindow* parent) : wxPanel(parent) +GameViewer::GameViewer(wxWindow* parent) : wxListView(parent) { - wxBoxSizer& s_panel( *new wxBoxSizer(wxVERTICAL) ); - - m_game_list = new wxListView(this); - s_panel.Add(m_game_list); - - SetSizerAndFit( &s_panel ); - LoadSettings(); - m_columns.Show(m_game_list); + m_columns.Show(this); m_path = wxGetCwd(); //TODO - Connect(m_game_list->GetId(), wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(GameViewer::DClick)); + Connect(GetId(), wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(GameViewer::DClick)); Refresh(); } @@ -25,13 +18,11 @@ GameViewer::GameViewer(wxWindow* parent) : wxPanel(parent) GameViewer::~GameViewer() { SaveSettings(); - m_game_list->Destroy(); } void GameViewer::DoResize(wxSize size) { SetSize(size); - m_game_list->SetSize(size); } void GameViewer::LoadGames() @@ -63,7 +54,8 @@ void GameViewer::LoadPSF() { const wxString& path = m_games[i] + "\\" + "PARAM.SFO"; if(!wxFileExists(path)) continue; - PSFLoader psf(path); + vfsLocalFile f(path); + PSFLoader psf(f); if(!psf.Load(false)) continue; psf.m_info.root = m_games[i]; m_game_data.Add(new GameInfo(psf.m_info)); @@ -74,7 +66,7 @@ void GameViewer::LoadPSF() void GameViewer::ShowData() { - m_columns.ShowData(m_game_list); + m_columns.ShowData(this); } void GameViewer::Refresh() @@ -86,7 +78,7 @@ void GameViewer::Refresh() void GameViewer::SaveSettings() { - m_columns.LoadSave(false, m_class_name, m_game_list); + m_columns.LoadSave(false, m_class_name, this); } void GameViewer::LoadSettings() @@ -96,10 +88,10 @@ void GameViewer::LoadSettings() void GameViewer::DClick(wxListEvent& event) { - long i = m_game_list->GetFirstSelected(); + long i = GetFirstSelected(); if(i < 0) return; - const wxString& path = m_game_data[i].root + "\\" + "USRDIR" + "\\" + "BOOT.BIN"; + const wxString& path = m_path + "\\" + m_game_data[i].root + "\\" + "USRDIR" + "\\" + "BOOT.BIN"; if(!wxFileExists(path)) { ConLog.Error("Boot error: elf not found! [%s]", path); diff --git a/rpcs3/Gui/GameViewer.h b/rpcs3/Gui/GameViewer.h index eb9d957a0d..17dfa9f776 100644 --- a/rpcs3/Gui/GameViewer.h +++ b/rpcs3/Gui/GameViewer.h @@ -214,9 +214,8 @@ public: } }; -class GameViewer : public wxPanel +class GameViewer : public wxListView { - wxListView* m_game_list; wxString m_path; wxArrayString m_games; ArrayF m_game_data; diff --git a/rpcs3/Gui/InterpreterDisAsm.cpp b/rpcs3/Gui/InterpreterDisAsm.cpp index 242c9c6e10..b8e18e8e98 100644 --- a/rpcs3/Gui/InterpreterDisAsm.cpp +++ b/rpcs3/Gui/InterpreterDisAsm.cpp @@ -10,13 +10,11 @@ u32 FixPc(const u32 pc) return pc - ((show_lines/2)*4); } -InterpreterDisAsmFrame::InterpreterDisAsmFrame(const wxString& title, PPCThread* cpu) - : FrameBase(NULL, wxID_ANY, title, "InterpreterDisAsmFrame", wxSize(500, 700)) +InterpreterDisAsmFrame::InterpreterDisAsmFrame(wxWindow* parent, PPCThread* cpu) + : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(500, 700), wxTAB_TRAVERSAL) , ThreadBase(false, "DisAsmFrame Thread") - , m_main_panel(*new wxPanel(this)) , CPU(*cpu) , PC(0) - , m_exec(false) { if(CPU.IsSPU()) { @@ -34,14 +32,14 @@ InterpreterDisAsmFrame::InterpreterDisAsmFrame(const wxString& title, PPCThread* wxBoxSizer& s_p_main = *new wxBoxSizer(wxVERTICAL); wxBoxSizer& s_b_main = *new wxBoxSizer(wxHORIZONTAL); - m_list = new wxListView(&m_main_panel); + m_list = new wxListView(this); - wxButton& b_go_to_addr = *new wxButton(&m_main_panel, wxID_ANY, "Go To Address"); - wxButton& b_go_to_pc = *new wxButton(&m_main_panel, wxID_ANY, "Go To PC"); + wxButton& b_go_to_addr = *new wxButton(this, wxID_ANY, "Go To Address"); + wxButton& b_go_to_pc = *new wxButton(this, wxID_ANY, "Go To PC"); - m_btn_step = new wxButton(&m_main_panel, wxID_ANY, "Step"); - m_btn_run = new wxButton(&m_main_panel, wxID_ANY, "Run"); - m_btn_pause = new wxButton(&m_main_panel, wxID_ANY, "Pause"); + m_btn_step = new wxButton(this, wxID_ANY, "Step"); + m_btn_run = new wxButton(this, wxID_ANY, "Run"); + m_btn_pause = new wxButton(this, wxID_ANY, "Pause"); s_b_main.Add(&b_go_to_addr, wxSizerFlags().Border(wxALL, 5)); s_b_main.Add(&b_go_to_pc, wxSizerFlags().Border(wxALL, 5)); @@ -49,7 +47,7 @@ InterpreterDisAsmFrame::InterpreterDisAsmFrame(const wxString& title, PPCThread* s_b_main.Add(m_btn_run, wxSizerFlags().Border(wxALL, 5)); s_b_main.Add(m_btn_pause, wxSizerFlags().Border(wxALL, 5)); - m_regs = new wxTextCtrl(&m_main_panel, wxID_ANY, wxEmptyString, + m_regs = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_DONTWRAP|wxNO_BORDER|wxTE_RICH2); m_regs->SetMinSize(wxSize(495, 100)); m_regs->SetEditable(false); @@ -58,25 +56,14 @@ InterpreterDisAsmFrame::InterpreterDisAsmFrame(const wxString& title, PPCThread* m_regs->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); wxBoxSizer& s_w_list = *new wxBoxSizer(wxHORIZONTAL); - s_w_list.Add(m_list); - s_w_list.AddSpacer(5); - s_w_list.Add(m_regs); + s_w_list.Add(m_list, 2, wxEXPAND | wxLEFT | wxDOWN, 5); + s_w_list.Add(m_regs, 1, wxEXPAND | wxRIGHT | wxDOWN, 5); - s_p_main.AddSpacer(5); - s_p_main.Add(&s_b_main); - s_p_main.AddSpacer(5); - s_p_main.Add(&s_w_list); - s_p_main.AddSpacer(5); + s_p_main.Add(&s_b_main, 0, wxEXPAND | wxLEFT | wxRIGHT, 5); + s_p_main.Add(&s_w_list, 1, wxEXPAND | wxDOWN, 5); - wxBoxSizer& s_p_main_h = *new wxBoxSizer(wxVERTICAL); - s_p_main_h.AddSpacer(5); - s_p_main_h.Add(&s_p_main); - s_p_main_h.AddSpacer(5); - m_main_panel.SetSizerAndFit(&s_p_main_h); - - wxBoxSizer& s_main = *new wxBoxSizer(wxVERTICAL); - s_main.Add(&m_main_panel); - SetSizerAndFit(&s_main); + SetSizer(&s_p_main); + Layout(); m_list->InsertColumn(0, "ASM"); @@ -92,14 +79,12 @@ InterpreterDisAsmFrame::InterpreterDisAsmFrame(const wxString& title, PPCThread* Connect(m_btn_run->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(InterpreterDisAsmFrame::DoRun)); Connect(m_btn_pause->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(InterpreterDisAsmFrame::DoPause)); Connect(m_list->GetId(), wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(InterpreterDisAsmFrame::DClick)); - Connect(wxEVT_SIZE, wxSizeEventHandler(InterpreterDisAsmFrame::OnResize)); - wxGetApp().Connect(m_list->GetId(), wxEVT_MOUSEWHEEL, wxMouseEventHandler(InterpreterDisAsmFrame::MouseWheel), (wxObject*)0, this); - wxGetApp().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(InterpreterDisAsmFrame::OnKeyDown), (wxObject*)0, this); + //Connect(wxEVT_SIZE, wxSizeEventHandler(InterpreterDisAsmFrame::OnResize)); + m_app_connector.Connect(m_list->GetId(), wxEVT_MOUSEWHEEL, wxMouseEventHandler(InterpreterDisAsmFrame::MouseWheel), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(InterpreterDisAsmFrame::OnKeyDown), (wxObject*)0, this); - m_btn_pause->Disable(); - //ShowPc(Loader.entry); + m_app_connector.Connect(wxEVT_DBG_COMMAND, wxCommandEventHandler(InterpreterDisAsmFrame::HandleCommand), (wxObject*)0, this); WriteRegs(); - ThreadBase::Start(); Load(BreakPointsDBName); } @@ -171,7 +156,7 @@ void InterpreterDisAsmFrame::Load(const wxString& path) void InterpreterDisAsmFrame::OnKeyDown(wxKeyEvent& event) { - if(wxGetActiveWindow() != this) + if(wxGetActiveWindow() != wxGetTopLevelParent(this)) { event.Skip(); return; @@ -231,7 +216,7 @@ void InterpreterDisAsmFrame::ShowAddr(const u64 addr) wxColour colour; - if(PC == CPU.PC) + if((!CPU.IsRunned() || !Emu.IsRunned()) && PC == CPU.PC) { colour = wxColour("Green"); } @@ -252,7 +237,6 @@ void InterpreterDisAsmFrame::ShowAddr(const u64 addr) m_list->SetItemBackgroundColour( i, colour ); } - while(remove_markedPC.GetCount()) { u32 mpc = remove_markedPC[0]; @@ -271,6 +255,7 @@ void InterpreterDisAsmFrame::ShowAddr(const u64 addr) markedPC.RemoveAt(mpc); } + m_list->SetColumnWidth(0, -1); m_list->Thaw(); } @@ -282,6 +267,41 @@ void InterpreterDisAsmFrame::WriteRegs() m_regs->Thaw(); } +void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) +{ + PPCThread* thr = (PPCThread*)event.GetClientData(); + event.Skip(); + + if(!thr || thr->GetId() != CPU.GetId()) return; + + switch(event.GetId()) + { + case DID_EXEC_THREAD: + case DID_REMOVE_THREAD: + case DID_RESUME_THREAD: + m_btn_step->Disable(); + m_btn_run->Disable(); + m_btn_pause->Enable(); + break; + + case DID_START_THREAD: + case DID_CREATE_THREAD: + case DID_PAUSE_THREAD: + case DID_STOP_THREAD: + m_btn_step->Enable(!Emu.IsReady()); + m_btn_run->Enable(!Emu.IsReady()); + m_btn_pause->Disable(); + + DoUpdate(); + break; + + case DID_STOP_EMU: + case DID_PAUSE_EMU: + DoUpdate(); + break; + } +} + void InterpreterDisAsmFrame::OnUpdate(wxCommandEvent& event) { //WriteRegs(); @@ -324,29 +344,21 @@ void InterpreterDisAsmFrame::Show_PC(wxCommandEvent& WXUNUSED(event)) extern bool dump_enable; void InterpreterDisAsmFrame::DoRun(wxCommandEvent& WXUNUSED(event)) { - m_exec = true; - /* - bool dump_status = dump_enable; - if(Emu.IsPaused()) Emu.Run(); - while(Emu.IsRunned()) - { - CPU.Exec(); - if(IsBreakPoint(CPU.PC) || dump_status != dump_enable) break; - } + if(CPU.IsPaused()) CPU.Resume(); + if(Emu.IsPaused()) Emu.Resume(); - DoUpdate(); - */ + CPU.Exec(); + //ThreadBase::Start(); } void InterpreterDisAsmFrame::DoPause(wxCommandEvent& WXUNUSED(event)) { - Emu.Pause(); + CPU.Pause(); } void InterpreterDisAsmFrame::DoStep(wxCommandEvent& WXUNUSED(event)) { - CPU.Exec(); - DoUpdate(); + ThreadBase::Start(); } void InterpreterDisAsmFrame::DClick(wxListEvent& event) @@ -356,7 +368,7 @@ void InterpreterDisAsmFrame::DClick(wxListEvent& event) const u64 start_pc = PC - show_lines*4; const u64 pc = start_pc + i*4; - //ConLog.Write("pc=0x%x", pc); + //ConLog.Write("pc=0x%llx", pc); if(!Memory.IsGoodAddr(pc, 4)) return; @@ -371,37 +383,12 @@ void InterpreterDisAsmFrame::DClick(wxListEvent& event) ShowAddr(start_pc); } - -void InterpreterDisAsmFrame::OnResize(wxSizeEvent& event) -{ - const wxSize& size( GetClientSize() ); - - wxTextCtrl& regs = *m_regs; - wxListView& list = *m_list; - - const int list_size = size.GetWidth() * 0.75; - const int regs_size = size.GetWidth() * 0.25 - 5; - list.SetMinSize(wxSize(list_size, size.GetHeight()-55)); - regs.SetMinSize(wxSize(regs_size, size.GetHeight()-55)); - m_main_panel.SetSize( size ); - m_main_panel.GetSizer()->RecalcSizes(); - - list.SetColumnWidth(0, list_size-8); - - event.Skip(); -} void InterpreterDisAsmFrame::MouseWheel(wxMouseEvent& event) { const int value = (event.m_wheelRotation / event.m_wheelDelta); - if(event.ControlDown()) - { - ShowAddr( PC - (show_lines * (value + 1)) * 4); - } - else - { - ShowAddr( PC - (show_lines + value) * 4 ); - } + + ShowAddr( PC - (event.ControlDown() ? show_lines * (value + 1) : show_lines + value) * 4); event.Skip(); } @@ -440,31 +427,30 @@ bool InterpreterDisAsmFrame::RemoveBreakPoint(u64 pc) void InterpreterDisAsmFrame::Task() { - while(!TestDestroy()) + wxGetApp().SendDbgCommand(DID_RESUME_THREAD, &CPU); + + bool dump_status = dump_enable; + + //CPU.InitTls(); + + try { - Sleep(1); - - if(!m_exec) continue; - - m_btn_step->Disable(); - m_btn_run->Disable(); - m_btn_pause->Enable(); - - bool dump_status = dump_enable; - if(Emu.IsPaused()) Emu.Resume(); - - while(Emu.IsRunned() && !TestDestroy()) + do { - CPU.Exec(); - if(IsBreakPoint(CPU.PC) || dump_status != dump_enable) break; + CPU.ExecOnce(); } - - DoUpdate(); - - m_btn_step->Enable(); - m_btn_run->Enable(); - m_btn_pause->Disable(); - - m_exec = false; + while(CPU.IsRunned() && Emu.IsRunned() && !TestDestroy() && !IsBreakPoint(CPU.PC) && dump_status == dump_enable); } + catch(const wxString& e) + { + ConLog.Error(e); + } + catch(...) + { + ConLog.Error("Unhandled exception."); + } + + //CPU.FreeTls(); + + wxGetApp().SendDbgCommand(DID_PAUSE_THREAD, &CPU); } \ No newline at end of file diff --git a/rpcs3/Gui/InterpreterDisAsm.h b/rpcs3/Gui/InterpreterDisAsm.h index bae01e49eb..2d2fc2f420 100644 --- a/rpcs3/Gui/InterpreterDisAsm.h +++ b/rpcs3/Gui/InterpreterDisAsm.h @@ -6,12 +6,10 @@ #include "Emu/Cell/SPUDisAsm.h" class InterpreterDisAsmFrame - : public FrameBase + : public wxPanel , public ThreadBase { wxListView* m_list; - wxPanel& m_main_panel; - PPCThread& CPU; DisAsm* disasm; Decoder* decoder; u64 PC; @@ -22,20 +20,24 @@ class InterpreterDisAsmFrame wxButton* m_btn_step; wxButton* m_btn_run; wxButton* m_btn_pause; - volatile bool m_exec; + AppConnector m_app_connector; public: - InterpreterDisAsmFrame(const wxString& title, PPCThread* cpu); + PPCThread& CPU; + +public: + InterpreterDisAsmFrame(wxWindow* parent, PPCThread* cpu); ~InterpreterDisAsmFrame(); void Save(const wxString& path); void Load(const wxString& path); - virtual void OnKeyDown(wxKeyEvent& event); + void OnKeyDown(wxKeyEvent& event); void DoUpdate(); void ShowAddr(const u64 addr); void WriteRegs(); + void HandleCommand(wxCommandEvent& event); void OnUpdate(wxCommandEvent& event); void Show_Val(wxCommandEvent& event); void Show_PC(wxCommandEvent& event); @@ -44,7 +46,6 @@ public: void DoStep(wxCommandEvent& event); void DClick(wxListEvent& event); - void OnResize(wxSizeEvent& event); void MouseWheel(wxMouseEvent& event); bool IsBreakPoint(u64 pc); void AddBreakPoint(u64 pc); diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index 14d38d741e..8c264de5c4 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -5,6 +5,8 @@ #include "Emu/System.h" #include "Ini.h" #include "svnrev.h" +#include "Emu/GS/sysutil_video.h" +#include BEGIN_EVENT_TABLE(MainFrame, FrameBase) EVT_CLOSE(MainFrame::OnQuit) @@ -17,10 +19,21 @@ enum IDs id_boot_game, id_sys_pause, id_sys_stop, + id_sys_send_exit, id_config_emu, + id_update_dbg, }; -MainFrame::MainFrame() : FrameBase(NULL, wxID_ANY, "", "MainFrame", wxSize(280, 180)) +wxString GetPaneName() +{ + static int pane_num = 0; + + return wxString::Format("Pane_%d", pane_num++); +} + +MainFrame::MainFrame() + : FrameBase(NULL, wxID_ANY, "", "MainFrame", wxSize(280, 180)) + , m_aui_mgr(this) { SetLabel(wxString::Format(_PRGNAME_ " " _PRGVER_ " r%d" SVN_MOD " (" SVN_DATE ")", SVN_REV)); wxMenuBar& menubar(*new wxMenuBar()); @@ -39,40 +52,55 @@ MainFrame::MainFrame() : FrameBase(NULL, wxID_ANY, "", "MainFrame", wxSize(280, //menu_boot.Append(id_boot_self, "Boot Self"); menu_sys.Append(id_sys_pause, "Pause"); - menu_sys.Append(id_sys_stop, "Stop"); + menu_sys.Append(id_sys_stop, "Stop\tCtrl + S"); + menu_sys.AppendSeparator(); + menu_sys.Append(id_sys_send_exit, "Send exit cmd"); menu_conf.Append(id_config_emu, "Settings"); SetMenuBar(&menubar); - wxBoxSizer& s_panel( *new wxBoxSizer(wxHORIZONTAL) ); - m_game_viewer = new GameViewer(this); - s_panel.Add( m_game_viewer, wxSizerFlags().Expand() ); - - SetSizerAndFit( &s_panel ); - - Connect(wxEVT_SIZE, wxSizeEventHandler(MainFrame::OnResize)); - + AddPane(m_game_viewer, "Game List", wxAUI_DOCK_BOTTOM); + Connect( id_boot_game, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::BootGame) ); Connect( id_boot_elf, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::BootElf) ); Connect( id_boot_self, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::BootSelf) ); Connect( id_sys_pause, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::Pause) ); Connect( id_sys_stop, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::Stop) ); + Connect( id_sys_send_exit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::SendExit) ); + Connect( id_update_dbg, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::UpdateUI) ); Connect( id_config_emu, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::Config) ); - wxGetApp().Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainFrame::OnKeyDown), (wxObject*)0, this); + m_app_connector.Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainFrame::OnKeyDown), (wxObject*)0, this); UpdateUI(); - - (new CompilerELF(NULL))->Show(); } -void MainFrame::OnResize(wxSizeEvent& event) +MainFrame::~MainFrame() { - m_game_viewer->DoResize(GetClientSize()); - event.Skip(); + m_aui_mgr.UnInit(); +} + +void MainFrame::AddPane(wxWindow* wind, const wxString& caption, int flags) +{ + m_aui_mgr.AddPane(wind, wxAuiPaneInfo().Name(GetPaneName()).Caption(caption).Direction(flags).CloseButton(false).MaximizeButton()); +} + +void MainFrame::DoSettings(bool load) +{ + IniEntry ini; + ini.Init("Settings", "MainFrameAui"); + + if(load) + { + m_aui_mgr.LoadPerspective(ini.LoadValue(m_aui_mgr.SavePerspective())); + } + else + { + ini.SaveValue(m_aui_mgr.SavePerspective()); + } } void MainFrame::BootGame(wxCommandEvent& WXUNUSED(event)) @@ -85,7 +113,7 @@ void MainFrame::BootGame(wxCommandEvent& WXUNUSED(event)) stoped = true; } - wxDirDialog ctrl( this, L"Select game folder", wxEmptyString); + wxDirDialog ctrl(this, L"Select game folder", wxEmptyString); if(ctrl.ShowModal() == wxID_CANCEL) { @@ -141,7 +169,7 @@ void MainFrame::BootGame(wxCommandEvent& WXUNUSED(event)) return; } - Emu.Run(); + Emu.Load(); ConLog.Write("Game: boot done."); return; @@ -174,7 +202,7 @@ void MainFrame::BootElf(wxCommandEvent& WXUNUSED(event)) Emu.Stop(); Emu.SetElf(ctrl.GetPath()); - Emu.Run(); + Emu.Load(); ConLog.Write("Elf: boot done."); } @@ -203,14 +231,18 @@ void MainFrame::BootSelf(wxCommandEvent& WXUNUSED(event)) Emu.Stop(); Emu.SetSelf(ctrl.GetPath()); - Emu.Run(); + Emu.Load(); ConLog.Write("SELF: boot done."); } void MainFrame::Pause(wxCommandEvent& WXUNUSED(event)) { - if(Emu.IsPaused()) + if(Emu.IsReady()) + { + Emu.Run(); + } + else if(Emu.IsPaused()) { Emu.Resume(); } @@ -225,6 +257,11 @@ void MainFrame::Stop(wxCommandEvent& WXUNUSED(event)) Emu.Stop(); } +void MainFrame::SendExit(wxCommandEvent& event) +{ + Emu.GetCallbackManager().m_exit_callback.Handle(0x0101, 0); +} + void MainFrame::Config(wxCommandEvent& WXUNUSED(event)) { //TODO @@ -237,27 +274,41 @@ void MainFrame::Config(wxCommandEvent& WXUNUSED(event)) paused = true; } - wxDialog* diag = new wxDialog(this, wxID_ANY, "Settings", wxDefaultPosition); + wxDialog diag(this, wxID_ANY, "Settings", wxDefaultPosition); wxBoxSizer* s_panel(new wxBoxSizer(wxVERTICAL)); - wxStaticBoxSizer* s_round_cpu( new wxStaticBoxSizer( wxVERTICAL, diag, _("CPU") ) ); - wxStaticBoxSizer* s_round_cpu_decoder( new wxStaticBoxSizer( wxVERTICAL, diag, _("Decoder") ) ); + wxStaticBoxSizer* s_round_cpu( new wxStaticBoxSizer( wxVERTICAL, &diag, _("CPU") ) ); + wxStaticBoxSizer* s_round_cpu_decoder( new wxStaticBoxSizer( wxVERTICAL, &diag, _("Decoder") ) ); - wxStaticBoxSizer* s_round_gs( new wxStaticBoxSizer( wxHORIZONTAL, diag, _("GS") ) ); - wxStaticBoxSizer* s_round_gs_render( new wxStaticBoxSizer( wxVERTICAL, diag, _("Render") ) ); + wxStaticBoxSizer* s_round_gs( new wxStaticBoxSizer( wxVERTICAL, &diag, _("GS") ) ); + wxStaticBoxSizer* s_round_gs_render( new wxStaticBoxSizer( wxVERTICAL, &diag, _("Render") ) ); + wxStaticBoxSizer* s_round_gs_res( new wxStaticBoxSizer( wxVERTICAL, &diag, _("Default resolution") ) ); + wxStaticBoxSizer* s_round_gs_aspect( new wxStaticBoxSizer( wxVERTICAL, &diag, _("Default aspect ratio") ) ); - wxStaticBoxSizer* s_round_pad( new wxStaticBoxSizer( wxHORIZONTAL, diag, _("Pad") ) ); - wxStaticBoxSizer* s_round_pad_handler( new wxStaticBoxSizer( wxVERTICAL, diag, _("Handler") ) ); + wxStaticBoxSizer* s_round_pad( new wxStaticBoxSizer( wxVERTICAL, &diag, _("Pad") ) ); + wxStaticBoxSizer* s_round_pad_handler( new wxStaticBoxSizer( wxVERTICAL, &diag, _("Handler") ) ); - wxComboBox* cbox_cpu_decoder = new wxComboBox(diag, wxID_ANY); - wxComboBox* cbox_gs_render = new wxComboBox(diag, wxID_ANY); - wxComboBox* cbox_pad_handler = new wxComboBox(diag, wxID_ANY); + wxComboBox* cbox_cpu_decoder = new wxComboBox(&diag, wxID_ANY); + wxComboBox* cbox_gs_render = new wxComboBox(&diag, wxID_ANY); + wxComboBox* cbox_gs_resolution = new wxComboBox(&diag, wxID_ANY); + wxComboBox* cbox_gs_aspect = new wxComboBox(&diag, wxID_ANY); + wxComboBox* cbox_pad_handler = new wxComboBox(&diag, wxID_ANY); + + wxCheckBox* chbox_gs_vsync = new wxCheckBox(&diag, wxID_ANY, "VSync"); //cbox_cpu_decoder->Append("DisAsm"); cbox_cpu_decoder->Append("Interpreter & DisAsm"); cbox_cpu_decoder->Append("Interpreter"); + for(int i=1; iAppend(wxString::Format("%dx%d", ResolutionTable[i].width, ResolutionTable[i].height)); + } + + cbox_gs_aspect->Append("4:3"); + cbox_gs_aspect->Append("16:9"); + cbox_gs_render->Append("Null"); cbox_gs_render->Append("OpenGL"); //cbox_gs_render->Append("Software"); @@ -266,50 +317,65 @@ void MainFrame::Config(wxCommandEvent& WXUNUSED(event)) cbox_pad_handler->Append("Windows"); //cbox_pad_handler->Append("DirectInput"); + chbox_gs_vsync->SetValue(Ini.GSVSyncEnable.GetValue()); + cbox_cpu_decoder->SetSelection(Ini.CPUDecoderMode.GetValue() ? Ini.CPUDecoderMode.GetValue() - 1 : 0); cbox_gs_render->SetSelection(Ini.GSRenderMode.GetValue()); + cbox_gs_resolution->SetSelection(ResolutionIdToNum(Ini.GSResolution.GetValue()) - 1); + cbox_gs_aspect->SetSelection(Ini.GSAspectRatio.GetValue() - 1); cbox_pad_handler->SetSelection(Ini.PadHandlerMode.GetValue()); - s_round_cpu_decoder->Add(cbox_cpu_decoder); - s_round_cpu->Add(s_round_cpu_decoder); + s_round_cpu_decoder->Add(cbox_cpu_decoder, wxSizerFlags().Border(wxALL, 5).Expand()); + s_round_cpu->Add(s_round_cpu_decoder, wxSizerFlags().Border(wxALL, 5).Expand()); - s_round_gs_render->Add(cbox_gs_render); - s_round_gs->Add(s_round_gs_render); + s_round_gs_render->Add(cbox_gs_render, wxSizerFlags().Border(wxALL, 5).Expand()); + s_round_gs_res->Add(cbox_gs_resolution, wxSizerFlags().Border(wxALL, 5).Expand()); + s_round_gs_aspect->Add(cbox_gs_aspect, wxSizerFlags().Border(wxALL, 5).Expand()); + s_round_gs->Add(s_round_gs_render, wxSizerFlags().Border(wxALL, 5).Expand()); + s_round_gs->Add(s_round_gs_res, wxSizerFlags().Border(wxALL, 5).Expand()); + s_round_gs->Add(s_round_gs_aspect, wxSizerFlags().Border(wxALL, 5).Expand()); + s_round_gs->Add(chbox_gs_vsync, wxSizerFlags().Border(wxALL, 5)); - s_round_pad_handler->Add(cbox_pad_handler); - s_round_pad->Add(s_round_pad_handler); + s_round_pad_handler->Add(cbox_pad_handler, wxSizerFlags().Border(wxALL, 5).Expand()); + s_round_pad->Add(s_round_pad_handler, wxSizerFlags().Border(wxALL, 5).Expand()); wxBoxSizer* s_b_panel(new wxBoxSizer(wxHORIZONTAL)); - s_b_panel->Add(new wxButton(diag, wxID_OK)); - s_b_panel->AddSpacer(5); - s_b_panel->Add(new wxButton(diag, wxID_CANCEL)); + s_b_panel->Add(new wxButton(&diag, wxID_OK), wxSizerFlags().Border(wxALL, 5).Center()); + s_b_panel->Add(new wxButton(&diag, wxID_CANCEL), wxSizerFlags().Border(wxALL, 5).Center()); - s_panel->Add(s_round_cpu); - s_panel->AddSpacer(5); - s_panel->Add(s_round_gs); - s_panel->AddSpacer(5); - s_panel->Add(s_round_pad); - s_panel->AddSpacer(8); - s_panel->Add(s_b_panel, wxRIGHT); + //wxBoxSizer* s_conf_panel(new wxBoxSizer(wxHORIZONTAL)); - diag->SetSizerAndFit( s_panel ); + s_panel->Add(s_round_cpu, wxSizerFlags().Border(wxALL, 5).Expand()); + s_panel->Add(s_round_gs, wxSizerFlags().Border(wxALL, 5).Expand()); + s_panel->Add(s_round_pad, wxSizerFlags().Border(wxALL, 5).Expand()); + s_panel->Add(s_b_panel, wxSizerFlags().Border(wxALL, 8).Expand()); + + diag.SetSizerAndFit( s_panel ); - if(diag->ShowModal() == wxID_OK) + if(diag.ShowModal() == wxID_OK) { Ini.CPUDecoderMode.SetValue(cbox_cpu_decoder->GetSelection() + 1); Ini.GSRenderMode.SetValue(cbox_gs_render->GetSelection()); + Ini.GSResolution.SetValue(ResolutionNumToId(cbox_gs_resolution->GetSelection() + 1)); + Ini.GSAspectRatio.SetValue(cbox_gs_aspect->GetSelection() + 1); + Ini.GSVSyncEnable.SetValue(chbox_gs_vsync->GetValue()); Ini.PadHandlerMode.SetValue(cbox_pad_handler->GetSelection()); + Ini.Save(); } - delete diag; - if(paused) Emu.Resume(); } +void MainFrame::UpdateUI(wxCommandEvent& WXUNUSED(event)) +{ + wxGetApp().m_debugger_frame->UpdateUI(); +} + void MainFrame::OnQuit(wxCloseEvent& event) { + DoSettings(false); TheApp->Exit(); } @@ -356,13 +422,14 @@ void MakeSaveState(wxFile& f) void MainFrame::OnKeyDown(wxKeyEvent& event) { - if(wxGetActiveWindow() == this && event.ControlDown()) + if(wxGetActiveWindow() /*== this*/ && event.ControlDown()) { switch(event.GetKeyCode()) { - case 'C': case 'c': if(Emu.IsPaused()) Emu.Resume(); return; + case 'C': case 'c': if(Emu.IsPaused()) Emu.Resume(); else if(Emu.IsReady()) Emu.Run(); return; + case 'P': case 'p': if(Emu.IsRunned()) Emu.Pause(); return; case 'S': case 's': if(!Emu.IsStopped()) Emu.Stop(); return; - case 'R': case 'r': if(Emu.m_path.Len()) {Emu.Stop(); Emu.Run();} return; + case 'R': case 'r': if(!Emu.m_path.IsEmpty()) {Emu.Stop(); Emu.Run();} return; } } @@ -374,17 +441,16 @@ void MainFrame::UpdateUI() wxMenuBar& menubar( *GetMenuBar() ); wxMenuItem& pause = *menubar.FindItem( id_sys_pause ); wxMenuItem& stop = *menubar.FindItem( id_sys_stop ); + wxMenuItem& send_exit = *menubar.FindItem( id_sys_send_exit ); - if(Emu.IsRunned()) - { - pause.SetText("Pause"); - pause.Enable(); - stop.Enable(); - } - else - { - pause.SetText("Resume"); - pause.Enable(Emu.IsPaused()); - stop.Enable(Emu.IsPaused()); - } + pause.SetText(Emu.IsRunned() ? "Pause\tCtrl + P" : Emu.IsReady() ? "Start\tCtrl + C" : "Resume\tCtrl + C"); + pause.Enable(!Emu.IsStopped()); + stop.Enable(!Emu.IsStopped()); + //send_exit.Enable(false); + send_exit.Enable(!Emu.IsStopped() && Emu.GetCallbackManager().m_exit_callback.m_callbacks.GetCount()); + + m_aui_mgr.Update(); + + wxCommandEvent refit( wxEVT_COMMAND_MENU_SELECTED, id_update_dbg ); + GetEventHandler()->AddPendingEvent( refit ); } \ No newline at end of file diff --git a/rpcs3/Gui/MainFrame.h b/rpcs3/Gui/MainFrame.h index 1461618d61..acd7072708 100644 --- a/rpcs3/Gui/MainFrame.h +++ b/rpcs3/Gui/MainFrame.h @@ -1,27 +1,35 @@ #pragma once #include "GameViewer.h" +#include "wx/aui/aui.h" class MainFrame : public FrameBase { GameViewer* m_game_viewer; + wxAuiManager m_aui_mgr; + AppConnector m_app_connector; public: MainFrame(); + ~MainFrame(); + + void AddPane(wxWindow* wind, const wxString& caption, int flags); + void DoSettings(bool load); private: - virtual void OnQuit(wxCloseEvent& event); - virtual void OnResize(wxSizeEvent& event); + void OnQuit(wxCloseEvent& event); - virtual void BootGame(wxCommandEvent& event); - virtual void BootElf(wxCommandEvent& event); - virtual void BootSelf(wxCommandEvent& event); - virtual void Pause(wxCommandEvent& event); - virtual void Stop(wxCommandEvent& event); - virtual void Config(wxCommandEvent& event); - virtual void OnKeyDown(wxKeyEvent& event); + void BootGame(wxCommandEvent& event); + void BootElf(wxCommandEvent& event); + void BootSelf(wxCommandEvent& event); + void Pause(wxCommandEvent& event); + void Stop(wxCommandEvent& event); + void SendExit(wxCommandEvent& event); + void Config(wxCommandEvent& event); + void UpdateUI(wxCommandEvent& event); + void OnKeyDown(wxKeyEvent& event); public: - virtual void UpdateUI(); + void UpdateUI(); private: DECLARE_EVENT_TABLE() diff --git a/rpcs3/Gui/Plugins.h b/rpcs3/Gui/Plugins.h new file mode 100644 index 0000000000..0cffa91ad2 --- /dev/null +++ b/rpcs3/Gui/Plugins.h @@ -0,0 +1,362 @@ +#pragma once + +#define LOAD_SYMBOL(x) if(m_dll.HasSymbol(#x)) x = (_##x)m_dll.GetSymbol(#x) +struct Ps3EmuPlugin +{ + enum PS3EMULIB_TYPE + { + LIB_PAD = (1 << 0), + LIB_KB = (1 << 1), + LIB_MOUSE = (1 << 2), + LIB_FS = (1 << 3), + }; + + typedef u32 (*_Ps3EmuLibGetType)(); + typedef u32 (*_Ps3EmuLibGetVersion)(); + typedef const char* (*_Ps3EmuLibGetName)(); + typedef const char* (*_Ps3EmuLibGetFullName)(); + typedef void (*_Ps3EmuLibSetSettingsPath)(const char* path); + typedef void (*_Ps3EmuLibSetLogPath)(const char* path); + typedef const char* (*_Ps3EmuLibGetSettingsName)(); + typedef const char* (*_Ps3EmuLibGetLogName)(); + typedef void (*_Ps3EmuLibConfigure)(PS3EMULIB_TYPE type); + typedef void (*_Ps3EmuLibAbout)(); + typedef u32 (*_Ps3EmuLibTest)(PS3EMULIB_TYPE type); + + _Ps3EmuLibGetType Ps3EmuLibGetType; + _Ps3EmuLibGetVersion Ps3EmuLibGetVersion; + _Ps3EmuLibGetName Ps3EmuLibGetName; + _Ps3EmuLibGetFullName Ps3EmuLibGetFullName; + _Ps3EmuLibSetSettingsPath Ps3EmuLibSetSettingsPath; + _Ps3EmuLibSetLogPath Ps3EmuLibSetLogPath; + _Ps3EmuLibGetSettingsName Ps3EmuLibGetSettingsName; + _Ps3EmuLibGetLogName Ps3EmuLibGetLogName; + _Ps3EmuLibConfigure Ps3EmuLibConfigure; + _Ps3EmuLibAbout Ps3EmuLibAbout; + _Ps3EmuLibTest Ps3EmuLibTest; + + wxDynamicLibrary m_dll; + + Ps3EmuPlugin() + { + Reset(); + } + + Ps3EmuPlugin(const wxString& path) + { + Load(path); + } + + virtual ~Ps3EmuPlugin() throw() + { + Unload(); + } + + wxString FormatVersion() + { + if(!Ps3EmuLibGetVersion) return wxEmptyString; + + const u32 v = Ps3EmuLibGetVersion(); + + const u8 v0 = v >> 24; + const u8 v1 = v >> 16; + const u8 v2 = v >> 8; + const u8 v3 = v; + + if(!v2 && !v3) return wxString::Format("%d.%d", v0, v1); + if(!v3) return wxString::Format("%d.%d.%d", v0, v1, v2); + + return wxString::Format("%d.%d.%d.%d", v0, v1, v2, v3); + } + + void Load(const wxString& path) + { + if(m_dll.Load(path)) + { + Init(); + } + else + { + Reset(); + } + } + + void Unload() + { + Reset(); + + if(m_dll.IsLoaded()) m_dll.Unload(); + } + + virtual bool Test() + { + return + m_dll.IsLoaded() && + Ps3EmuLibGetType && + Ps3EmuLibGetVersion && + Ps3EmuLibGetName && + Ps3EmuLibGetFullName && + Ps3EmuLibSetSettingsPath && + Ps3EmuLibSetLogPath && + Ps3EmuLibGetSettingsName && + Ps3EmuLibGetLogName && + Ps3EmuLibConfigure && + Ps3EmuLibAbout && + Ps3EmuLibTest; + } + +protected: + virtual void Init() + { + LOAD_SYMBOL(Ps3EmuLibGetType); + LOAD_SYMBOL(Ps3EmuLibGetVersion); + LOAD_SYMBOL(Ps3EmuLibGetName); + LOAD_SYMBOL(Ps3EmuLibGetFullName); + LOAD_SYMBOL(Ps3EmuLibSetSettingsPath); + LOAD_SYMBOL(Ps3EmuLibSetLogPath); + LOAD_SYMBOL(Ps3EmuLibGetSettingsName); + LOAD_SYMBOL(Ps3EmuLibGetLogName); + LOAD_SYMBOL(Ps3EmuLibConfigure); + LOAD_SYMBOL(Ps3EmuLibAbout); + LOAD_SYMBOL(Ps3EmuLibTest); + } + + virtual void Reset() + { + Ps3EmuLibGetType = nullptr; + Ps3EmuLibGetVersion = nullptr; + Ps3EmuLibGetName = nullptr; + Ps3EmuLibGetFullName = nullptr; + Ps3EmuLibSetSettingsPath = nullptr; + Ps3EmuLibSetLogPath = nullptr; + Ps3EmuLibGetSettingsName = nullptr; + Ps3EmuLibGetLogName = nullptr; + Ps3EmuLibConfigure = nullptr; + Ps3EmuLibAbout = nullptr; + Ps3EmuLibTest = nullptr; + } +}; + +struct Ps3EmuPluginPAD : public Ps3EmuPlugin +{ + typedef int (*_PadInit)(u32 max_connect); + typedef int (*_PadEnd)(); + typedef int (*_PadClearBuf)(u32 port_no); + typedef int (*_PadGetData)(u32 port_no, void* data); + typedef int (*_PadGetDataExtra)(u32 port_no, u32* device_type, void* data); + typedef int (*_PadSetActDirect)(u32 port_no, void* param); + typedef int (*_PadGetInfo)(void* info); + typedef int (*_PadGetInfo2)(void* info); + typedef int (*_PadSetPortSetting)(u32 port_no, u32 port_setting); + typedef int (*_PadLddRegisterController)(); + typedef int (*_PadLddUnregisterController)(int handle); + typedef int (*_PadLddDataInsert)(int handle, void* data); + typedef int (*_PadLddGetPortNo)(int handle); + typedef int (*_PadPeriphGetInfo)(void* info); + typedef int (*_PadPeriphGetData)(u32 port_no, void* data); + typedef int (*_PadInfoPressMode)(u32 port_no); + typedef int (*_PadSetPressMode)(u32 port_no, u8 mode); + typedef int (*_PadInfoSensorMode)(u32 port_no); + typedef int (*_PadSetSensorMode)(u32 port_no, u8 mode); + typedef int (*_PadGetRawData)(u32 port_no, void* data); + typedef int (*_PadDbgLddRegisterController)(u32 device_capability); + typedef int (*_PadDbgLddSetDataInsertMode)(int handle, u8 mode); + typedef int (*_PadDbgPeriphRegisterDevice)(u16 vid, u16 pid, u32 pclass_type, u32 pclass_profile, void* mapping); + typedef int (*_PadDbgGetData)(u32 port_no, void* data); + + _PadInit PadInit; + _PadEnd PadEnd; + _PadClearBuf PadClearBuf; + _PadGetData PadGetData; + _PadGetDataExtra PadGetDataExtra; + _PadSetActDirect PadSetActDirect; + _PadGetInfo PadGetInfo; + _PadGetInfo2 PadGetInfo2; + _PadSetPortSetting PadSetPortSetting; + _PadLddRegisterController PadLddRegisterController; + _PadLddUnregisterController PadLddUnregisterController; + _PadLddDataInsert PadLddDataInsert; + _PadLddGetPortNo PadLddGetPortNo; + _PadPeriphGetInfo PadPeriphGetInfo; + _PadPeriphGetData PadPeriphGetData; + _PadInfoPressMode PadInfoPressMode; + _PadSetPressMode PadSetPressMode; + _PadInfoSensorMode PadInfoSensorMode; + _PadSetSensorMode PadSetSensorMode; + _PadGetRawData PadGetRawData; + _PadDbgLddRegisterController PadDbgLddRegisterController; + _PadDbgLddSetDataInsertMode PadDbgLddSetDataInsertMode; + _PadDbgPeriphRegisterDevice PadDbgPeriphRegisterDevice; + _PadDbgGetData PadDbgGetData; + + Ps3EmuPluginPAD() + { + Reset(); + } + + Ps3EmuPluginPAD(const wxString& path) + { + Load(path); + } + + virtual bool Test() + { + return Ps3EmuPlugin::Test() && + PadInit && + PadEnd && + PadClearBuf && + PadGetData && + PadGetDataExtra && + PadSetActDirect && + PadGetInfo && + PadGetInfo2 && + PadSetPortSetting && + PadLddRegisterController && + PadLddUnregisterController && + PadLddDataInsert && + PadLddGetPortNo && + PadPeriphGetInfo && + PadPeriphGetData && + PadInfoPressMode && + PadSetPressMode && + PadInfoSensorMode && + PadSetSensorMode && + PadGetRawData && + PadDbgLddRegisterController && + PadDbgLddSetDataInsertMode && + PadDbgPeriphRegisterDevice && + PadDbgGetData; + } + +protected: + virtual void Init() + { + LOAD_SYMBOL(PadInit); + LOAD_SYMBOL(PadEnd); + LOAD_SYMBOL(PadClearBuf); + LOAD_SYMBOL(PadGetData); + LOAD_SYMBOL(PadGetDataExtra); + LOAD_SYMBOL(PadSetActDirect); + LOAD_SYMBOL(PadGetInfo); + LOAD_SYMBOL(PadGetInfo2); + LOAD_SYMBOL(PadSetPortSetting); + LOAD_SYMBOL(PadLddRegisterController); + LOAD_SYMBOL(PadLddUnregisterController); + LOAD_SYMBOL(PadLddDataInsert); + LOAD_SYMBOL(PadLddGetPortNo); + LOAD_SYMBOL(PadPeriphGetInfo); + LOAD_SYMBOL(PadPeriphGetData); + LOAD_SYMBOL(PadInfoPressMode); + LOAD_SYMBOL(PadSetPressMode); + LOAD_SYMBOL(PadInfoSensorMode); + LOAD_SYMBOL(PadSetSensorMode); + LOAD_SYMBOL(PadGetRawData); + LOAD_SYMBOL(PadDbgLddRegisterController); + LOAD_SYMBOL(PadDbgLddSetDataInsertMode); + LOAD_SYMBOL(PadDbgPeriphRegisterDevice); + LOAD_SYMBOL(PadDbgGetData); + + Ps3EmuPlugin::Init(); + } + + virtual void Reset() + { + PadInit = nullptr; + PadEnd = nullptr; + PadClearBuf = nullptr; + PadGetData = nullptr; + PadGetDataExtra = nullptr; + PadSetActDirect = nullptr; + PadGetInfo = nullptr; + PadGetInfo2 = nullptr; + PadSetPortSetting = nullptr; + PadLddRegisterController = nullptr; + PadLddUnregisterController = nullptr; + PadLddDataInsert = nullptr; + PadLddGetPortNo = nullptr; + PadPeriphGetInfo = nullptr; + PadPeriphGetData = nullptr; + PadInfoPressMode = nullptr; + PadSetPressMode = nullptr; + PadInfoSensorMode = nullptr; + PadSetSensorMode = nullptr; + PadGetRawData = nullptr; + PadDbgLddRegisterController = nullptr; + PadDbgLddSetDataInsertMode = nullptr; + PadDbgPeriphRegisterDevice = nullptr; + PadDbgGetData = nullptr; + + Ps3EmuPlugin::Reset(); + } +}; + +struct PluginsManager +{ + struct PluginInfo + { + wxString path; + wxString name; + u32 type; + }; + + Array m_plugins; + ArrayF m_pad_plugins; + + void Load(const wxString& path) + { + if(!wxDirExists(path)) return; + + m_plugins.ClearD(); + wxDir dir(path); + + wxArrayString res; + wxDir::GetAllFiles(path, &res, "*.dll", wxDIR_FILES); + + for(u32 i=0; iAdd(new wxStaticText(parent, wxID_ANY, name), wxSizerFlags().Border(wxRIGHT, 5).Center().Left()); + s_panel->Add(cb = new wxComboBox(parent, wxID_ANY), wxSizerFlags().Center().Right()); + + return s_panel; + } + + void Dialog() + { + wxDialog dial(nullptr, wxID_ANY, "Select plugins...", wxDefaultPosition); + + wxBoxSizer& s_panel(*new wxBoxSizer(wxVERTICAL)); + + wxComboBox* cbox_pad_plugins; + s_panel.Add(GetNewComboBox(&dial, "Pad", cbox_pad_plugins), wxSizerFlags().Border(wxALL, 5).Expand()); + + for(u32 i=0; iAppend(m_plugins[i].name + " (" + wxFileName(m_plugins[i].path).GetName() + ")"); + } + } + + if(cbox_pad_plugins->GetCount()) cbox_pad_plugins->Select(0); + + dial.SetSizerAndFit(&s_panel); + dial.ShowModal(); + } +}; diff --git a/rpcs3/Ini.h b/rpcs3/Ini.h index da9e777323..829db83c46 100644 --- a/rpcs3/Ini.h +++ b/rpcs3/Ini.h @@ -55,7 +55,7 @@ template struct IniEntry : public Ini m_Config->SetPath(path); } - void SetValue(const T value) + void SetValue(const T& value) { m_value = value; } @@ -65,17 +65,22 @@ template struct IniEntry : public Ini return m_value; } - T LoadValue(const T defvalue) + T LoadValue(const T& defvalue) { return Ini::Load(m_key, defvalue); } + void SaveValue(const T& value) + { + Ini::Save(m_key, value); + } + void Save() { Ini::Save(m_key, m_value); } - T Load(const T defvalue) + T Load(const T& defvalue) { return (m_value = Ini::Load(m_key, defvalue)); } @@ -89,20 +94,36 @@ private: public: IniEntry CPUDecoderMode; IniEntry GSRenderMode; + IniEntry GSResolution; + IniEntry GSAspectRatio; + IniEntry GSVSyncEnable; IniEntry PadHandlerMode; public: Inis() : DefPath("EmuSettings") { - CPUDecoderMode.Init("DecoderMode", DefPath + "\\" + "CPU"); - GSRenderMode.Init("RenderMode", DefPath + "\\" + "GS"); - PadHandlerMode.Init("HandlerMode", DefPath + "\\" + "Pad"); + wxString path; + + path = DefPath + "\\" + "CPU"; + CPUDecoderMode.Init("DecoderMode", path); + + path = DefPath + "\\" + "GS"; + GSRenderMode.Init("RenderMode", path); + GSResolution.Init("Resolution", path); + GSAspectRatio.Init("AspectRatio", path); + GSVSyncEnable.Init("VSyncEnable", path); + + path = DefPath + "\\" + "Pad"; + PadHandlerMode.Init("HandlerMode", path); } void Load() { CPUDecoderMode.Load(2); GSRenderMode.Load(0); + GSResolution.Load(4); + GSAspectRatio.Load(1); + GSVSyncEnable.Load(false); PadHandlerMode.Load(0); } @@ -110,6 +131,9 @@ public: { CPUDecoderMode.Save(); GSRenderMode.Save(); + GSResolution.Save(); + GSAspectRatio.Save(); + GSVSyncEnable.Save(); PadHandlerMode.Save(); } }; diff --git a/rpcs3/Loader/ELF.cpp b/rpcs3/Loader/ELF.cpp index 3d5d1678f2..d967e4583e 100644 --- a/rpcs3/Loader/ELF.cpp +++ b/rpcs3/Loader/ELF.cpp @@ -2,17 +2,10 @@ #include "Loader.h" #include "ELF.h" -ELFLoader::ELFLoader(wxFile& f) +ELFLoader::ELFLoader(vfsStream& f) : elf_f(f) , LoaderBase() - , loader(NULL) -{ -} - -ELFLoader::ELFLoader(const wxString& path) - : elf_f(*new wxFile(path)) - , LoaderBase() - , loader(NULL) + , loader(nullptr) { } @@ -32,19 +25,21 @@ bool ELFLoader::LoadInfo() if(!loader || !loader->LoadInfo()) return false; + entry = loader->GetEntry(); + machine = loader->GetMachine(); + return true; } -bool ELFLoader::LoadData() +bool ELFLoader::LoadData(u64 offset) { - if(!loader || !loader->LoadData()) return false; - entry = loader->GetEntry(); - machine = loader->GetMachine(); + if(!loader || !loader->LoadData(offset)) return false; return true; } bool ELFLoader::Close() { - safe_delete(loader); + delete loader; + loader = nullptr; return elf_f.Close(); } diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index d68fa0de95..90d89e31f4 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -1,6 +1,7 @@ #pragma once #include "ELF64.h" #include "ELF32.h" +#include "Emu/FS/vfsStream.h" enum ElfClass { @@ -18,7 +19,7 @@ struct Elf_Ehdr { } - virtual void Load(wxFile& f) + virtual void Load(vfsStream& f) { e_magic = Read32(f); e_class = Read8(f); @@ -40,17 +41,16 @@ struct Elf_Ehdr class ELFLoader : public LoaderBase { - wxFile& elf_f; + vfsStream& elf_f; LoaderBase* loader; public: Elf_Ehdr ehdr; - ELFLoader(wxFile& f); - ELFLoader(const wxString& path); + ELFLoader(vfsStream& f); ~ELFLoader() {Close();} virtual bool LoadInfo(); - virtual bool LoadData(); + virtual bool LoadData(u64 offset = 0); virtual bool Close(); }; \ No newline at end of file diff --git a/rpcs3/Loader/ELF32.cpp b/rpcs3/Loader/ELF32.cpp index 3d45ff9602..514ed3df17 100644 --- a/rpcs3/Loader/ELF32.cpp +++ b/rpcs3/Loader/ELF32.cpp @@ -1,18 +1,12 @@ #include "stdafx.h" #include "ELF32.h" -ELF32Loader::ELF32Loader(wxFile& f) +ELF32Loader::ELF32Loader(vfsStream& f) : elf32_f(f) , LoaderBase() { } -ELF32Loader::ELF32Loader(const wxString& path) - : elf32_f(*new wxFile(path)) - , LoaderBase() -{ -} - bool ELF32Loader::LoadInfo() { if(!elf32_f.IsOpened()) return false; @@ -24,13 +18,13 @@ bool ELF32Loader::LoadInfo() return true; } -bool ELF32Loader::LoadData() +bool ELF32Loader::LoadData(u64 offset) { if(!elf32_f.IsOpened()) return false; - if(!LoadEhdrData()) return false; - if(!LoadPhdrData()) return false; - if(!LoadShdrData()) return false; + if(!LoadEhdrData(offset)) return false; + if(!LoadPhdrData(offset)) return false; + if(!LoadShdrData(offset)) return false; return true; } @@ -81,9 +75,9 @@ bool ELF32Loader::LoadPhdrInfo() elf32_f.Seek(ehdr.e_phoff); for(uint i=0; iLoad(elf32_f); + phdr_arr.Move(phdr); } return true; @@ -94,9 +88,9 @@ bool ELF32Loader::LoadShdrInfo() elf32_f.Seek(ehdr.e_shoff); for(u32 i=0; iLoad(elf32_f); + shdr_arr.Move(shdr); } if(ehdr.e_shstrndx >= shdr_arr.GetCount()) @@ -123,7 +117,7 @@ bool ELF32Loader::LoadShdrInfo() return true; } -bool ELF32Loader::LoadEhdrData() +bool ELF32Loader::LoadEhdrData(u64 offset) { #ifdef LOADER_DEBUG ConLog.SkipLn(); @@ -133,7 +127,7 @@ bool ELF32Loader::LoadEhdrData() return true; } -bool ELF32Loader::LoadPhdrData() +bool ELF32Loader::LoadPhdrData(u64 offset) { for(u32 i=0; i shdr_arr; Array phdr_arr; - ELF32Loader(wxFile& f); - ELF32Loader(const wxString& path); + ELF32Loader(vfsStream& f); ~ELF32Loader() {Close();} virtual bool LoadInfo(); - virtual bool LoadData(); + virtual bool LoadData(u64 offset); virtual bool Close(); private: bool LoadEhdrInfo(); bool LoadPhdrInfo(); bool LoadShdrInfo(); - bool LoadEhdrData(); - bool LoadPhdrData(); - bool LoadShdrData(); + + bool LoadEhdrData(u64 offset); + bool LoadPhdrData(u64 offset); + bool LoadShdrData(u64 offset); }; \ No newline at end of file diff --git a/rpcs3/Loader/ELF64.cpp b/rpcs3/Loader/ELF64.cpp index a5463f445c..817d115026 100644 --- a/rpcs3/Loader/ELF64.cpp +++ b/rpcs3/Loader/ELF64.cpp @@ -1,18 +1,14 @@ #include "stdafx.h" #include "ELF64.h" +#include "Gui/CompilerELF.h" +using namespace PPU_opcodes; -ELF64Loader::ELF64Loader(wxFile& f) +ELF64Loader::ELF64Loader(vfsStream& f) : elf64_f(f) , LoaderBase() { } -ELF64Loader::ELF64Loader(const wxString& path) - : elf64_f(*new wxFile(path)) - , LoaderBase() -{ -} - bool ELF64Loader::LoadInfo() { if(!elf64_f.IsOpened()) return false; @@ -24,13 +20,13 @@ bool ELF64Loader::LoadInfo() return true; } -bool ELF64Loader::LoadData() +bool ELF64Loader::LoadData(u64 offset) { if(!elf64_f.IsOpened()) return false; - if(!LoadEhdrData()) return false; - if(!LoadPhdrData()) return false; - if(!LoadShdrData()) return false; + if(!LoadEhdrData(offset)) return false; + if(!LoadPhdrData(offset)) return false; + if(!LoadShdrData(offset)) return false; return true; } @@ -40,9 +36,9 @@ bool ELF64Loader::Close() return elf64_f.Close(); } -bool ELF64Loader::LoadEhdrInfo() +bool ELF64Loader::LoadEhdrInfo(s64 offset) { - elf64_f.Seek(0); + elf64_f.Seek(offset < 0 ? 0 : offset); ehdr.Load(elf64_f); if(!ehdr.CheckMagic()) return false; @@ -82,7 +78,7 @@ bool ELF64Loader::LoadEhdrInfo() return true; } -bool ELF64Loader::LoadPhdrInfo() +bool ELF64Loader::LoadPhdrInfo(s64 offset) { phdr_arr.Clear(); @@ -92,18 +88,19 @@ bool ELF64Loader::LoadPhdrInfo() return false; } - elf64_f.Seek(ehdr.e_phoff); + elf64_f.Seek(offset < 0 ? ehdr.e_phoff : offset); + for(u32 i=0; iLoad(elf64_f); + phdr_arr.Move(phdr); } return true; } -bool ELF64Loader::LoadShdrInfo() +bool ELF64Loader::LoadShdrInfo(s64 offset) { shdr_arr.Clear(); shdr_name_arr.Clear(); @@ -113,12 +110,12 @@ bool ELF64Loader::LoadShdrInfo() return false; } - elf64_f.Seek(ehdr.e_shoff); + elf64_f.Seek(offset < 0 ? ehdr.e_shoff : offset); for(u32 i=0; iLoad(elf64_f); + shdr_arr.Move(shdr); } if(ehdr.e_shstrndx >= shdr_arr.GetCount()) @@ -129,7 +126,7 @@ bool ELF64Loader::LoadShdrInfo() for(u32 i=0; iSetLoaded(); + } + else + { + ConLog.Warning("Unknown module '%s'", module_name); + } + #ifdef LOADER_DEBUG ConLog.SkipLn(); ConLog.Write("*** size: 0x%x", stub.s_size); @@ -281,21 +294,41 @@ bool ELF64Loader::LoadPhdrData() ConLog.Write("*** unk0: 0x%x", stub.s_unk0); ConLog.Write("*** unk1: 0x%x", stub.s_unk1); ConLog.Write("*** imports: %d", stub.s_imports); - ConLog.Write("*** module name: %s [0x%x]", Memory.ReadString(stub.s_modulename), stub.s_modulename); + ConLog.Write("*** module name: %s [0x%x]", module_name, stub.s_modulename); ConLog.Write("*** nid: 0x%x", stub.s_nid); ConLog.Write("*** text: 0x%x", stub.s_text); #endif + static const u32 section = 4 * 3; + u64 tbl = Memory.MainMem.Alloc(stub.s_imports * 4 * 2); + u64 dst = Memory.MainMem.Alloc(stub.s_imports * section); for(u32 i=0; iLoad(nid)) + { + ConLog.Warning("Unknown function 0x%08x in '%s' module", nid, module_name); + } + } #ifdef LOADER_DEBUG ConLog.Write("import %d:", i+1); - ConLog.Write("*** nid: 0x%x", nid); - ConLog.Write("*** text: 0x%x", text); + ConLog.Write("*** nid: 0x%x (0x%x)", nid, stub.s_nid + i*4); + ConLog.Write("*** text: 0x%x (0x%x)", text, stub.s_text + i*4); #endif - Memory.MemFlags.Add(text, stub.s_text + i*4, nid); + Memory.Write32(stub.s_text + i*4, tbl + i*8); + + mem32_t out_tbl(tbl + i*8); + out_tbl += dst + i*section; + out_tbl += nid; + + mem32_t out_dst(dst + i*section); + out_dst += ToOpcode(G_1f) | SetField(OR, 21, 30) | ToRA(11) | ToRS(2) | ToRB(2) | ToRC(0); + out_dst += ToOpcode(SC) | ToSYS(2); + out_dst += ToOpcode(G_13) | SetField(BCLR, 21, 30) | ToBO(0x10 | 0x04) | ToBI(0) | ToLK(0); } } #ifdef LOADER_DEBUG @@ -313,7 +346,7 @@ bool ELF64Loader::LoadPhdrData() return true; } -bool ELF64Loader::LoadShdrData() +bool ELF64Loader::LoadShdrData(u64 offset) { u64 max_addr = 0; @@ -341,12 +374,12 @@ bool ELF64Loader::LoadShdrData() const u64 addr = shdr.sh_addr; const u64 size = shdr.sh_size; - if(size == 0 || !Memory.IsGoodAddr(addr, size)) continue; + if(size == 0 || !Memory.IsGoodAddr(offset + addr, size)) continue; switch(shdr.sh_type) { case SHT_NOBITS: - memset(&Memory[addr], 0, size); + memset(&Memory[offset + addr], 0, size); break; case SHT_PROGBITS: diff --git a/rpcs3/Loader/ELF64.h b/rpcs3/Loader/ELF64.h index 73dcbe8749..0f3d0bcb95 100644 --- a/rpcs3/Loader/ELF64.h +++ b/rpcs3/Loader/ELF64.h @@ -23,7 +23,7 @@ struct Elf64_Ehdr u16 e_shnum; u16 e_shstrndx; - void Load(wxFile& f) + void Load(vfsStream& f) { e_magic = Read32(f); e_class = Read8(f); @@ -88,7 +88,7 @@ struct Elf64_Shdr u64 sh_addralign; u64 sh_entsize; - void Load(wxFile& f) + void Load(vfsStream& f) { sh_name = Read32(f); sh_type = Read32(f); @@ -130,7 +130,7 @@ struct Elf64_Phdr u64 p_memsz; u64 p_align; - void Load(wxFile& f) + void Load(vfsStream& f) { p_type = Read32(f); p_flags = Read32(f); @@ -159,7 +159,7 @@ struct Elf64_Phdr class ELF64Loader : public LoaderBase { - wxFile& elf64_f; + vfsStream& elf64_f; public: Elf64_Ehdr ehdr; @@ -167,19 +167,21 @@ public: Array shdr_arr; Array phdr_arr; - ELF64Loader(wxFile& f); - ELF64Loader(const wxString& path); + ELF64Loader(vfsStream& f); ~ELF64Loader() {Close();} virtual bool LoadInfo(); - virtual bool LoadData(); + virtual bool LoadData(u64 offset = 0); virtual bool Close(); + bool LoadEhdrInfo(s64 offset=-1); + bool LoadPhdrInfo(s64 offset=-1); + bool LoadShdrInfo(s64 offset=-1); + private: - bool LoadEhdrInfo(); - bool LoadPhdrInfo(); - bool LoadShdrInfo(); - bool LoadEhdrData(); - bool LoadPhdrData(); - bool LoadShdrData(); + bool LoadEhdrData(u64 offset); + bool LoadPhdrData(u64 offset); + bool LoadShdrData(u64 offset); + + //bool LoadImports(); }; \ No newline at end of file diff --git a/rpcs3/Loader/Loader.cpp b/rpcs3/Loader/Loader.cpp index 81160c10ad..929216a9ca 100644 --- a/rpcs3/Loader/Loader.cpp +++ b/rpcs3/Loader/Loader.cpp @@ -3,37 +3,7 @@ #include "ELF.h" #include "SELF.h" #include "PSF.h" - -u8 Read8(wxFile& f) -{ - u8 ret; - f.Read(&ret, sizeof(u8)); - return ret; -} - -u16 Read16(wxFile& f) -{ - const u8 c0 = Read8(f); - const u8 c1 = Read8(f); - - return ((u16)c0 << 8) | (u16)c1; -} - -u32 Read32(wxFile& f) -{ - const u16 c0 = Read16(f); - const u16 c1 = Read16(f); - - return ((u32)c0 << 16) | (u32)c1; -} - -u64 Read64(wxFile& f) -{ - const u32 c0 = Read32(f); - const u32 c1 = Read32(f); - - return ((u64)c0 << 32) | (u64)c1; -} +#include "Emu/FS/vfsLocalFile.h" const wxString Ehdr_DataToString(const u8 data) { @@ -121,64 +91,61 @@ const wxString Phdr_TypeToString(const u32 type) return wxString::Format("Unknown (%x)", type); } -Loader::Loader() : f(NULL) +Loader::Loader() : m_stream(nullptr) { } -Loader::Loader(const wxString& path) : f(new wxFile(path)) +Loader::Loader(vfsFileBase& stream) : m_stream(&stream) { } -void Loader::Open(const wxString& path) +void Loader::Open(vfsFileBase& stream) { - m_path = path; - f = new wxFile(path); -} - -void Loader::Open(wxFile& _f, const wxString& path) -{ - m_path = path; - f = &_f; + m_stream = &stream; } LoaderBase* Loader::SearchLoader() { - if(!f) return NULL; + if(!m_stream) return nullptr; - LoaderBase* l = NULL; + LoaderBase* l; - if((l=new ELFLoader(*f))->LoadInfo()) return l; - safe_delete(l); - if((l=new SELFLoader(*f))->LoadInfo()) return l; - safe_delete(l); - return NULL; + if((l=new ELFLoader(*m_stream))->LoadInfo()) return l; + delete l; + + if((l=new SELFLoader(*m_stream))->LoadInfo()) return l; + delete l; + + return nullptr; } bool Loader::Load() { - LoaderBase* l = SearchLoader(); + static const u64 spu_offset = 0x10000; + + ScopedPtr l = SearchLoader(); + if(!l) { ConLog.Error("Unknown file type"); return false; } - if(!l->LoadData()) + if(!l->LoadData(l->GetMachine() == MACHINE_SPU ? spu_offset : 0)) { ConLog.Error("Broken file"); - safe_delete(l); return false; } machine = l->GetMachine(); - entry = l->GetEntry(); - safe_delete(l); + entry = l->GetMachine() == MACHINE_SPU ? l->GetEntry() + spu_offset : l->GetEntry(); - const wxString& root = wxFileName(wxFileName(m_path).GetPath()).GetPath(); + const wxString& root = wxFileName(wxFileName(m_stream->GetPath()).GetPath()).GetPath(); const wxString& psf_path = root + "\\" + "PARAM.SFO"; if(wxFileExists(psf_path)) { - PSFLoader psf_l(psf_path); + vfsLocalFile f(psf_path); + PSFLoader psf_l(f); if(psf_l.Load()) { CurGameInfo = psf_l.m_info; @@ -188,13 +155,4 @@ bool Loader::Load() } return true; -} - -Loader::~Loader() -{ - if(f) - { - f->Close(); - f = NULL; - } } \ No newline at end of file diff --git a/rpcs3/Loader/Loader.h b/rpcs3/Loader/Loader.h index 7f2c04d41f..034c53dc29 100644 --- a/rpcs3/Loader/Loader.h +++ b/rpcs3/Loader/Loader.h @@ -1,4 +1,5 @@ #pragma once +#include "Emu/FS/vfsFileBase.h" #ifdef _DEBUG #define LOADER_DEBUG @@ -13,7 +14,7 @@ enum Elf_Machine enum ShdrType { - SHT_NULL, + SHT_NULL, SHT_PROGBITS, SHT_SYMTAB, SHT_STRTAB, @@ -35,10 +36,27 @@ enum ShdrFlag SHF_MASKPROC = 0xf0000000, }; -u8 Read8 (wxFile& f); -u16 Read16(wxFile& f); -u32 Read32(wxFile& f); -u64 Read64(wxFile& f); +__forceinline static u8 Read8(vfsStream& f) +{ + u8 ret; + f.Read(&ret, sizeof(u8)); + return ret; +} + +__forceinline static u16 Read16(vfsStream& f) +{ + return ((u16)Read8(f) << 8) | (u16)Read8(f); +} + +__forceinline static u32 Read32(vfsStream& f) +{ + return (Read16(f) << 16) | Read16(f); +} + +__forceinline static u64 Read64(vfsStream& f) +{ + return ((u64)Read32(f) << 32) | (u64)Read32(f); +} const wxString Ehdr_DataToString(const u8 data); const wxString Ehdr_TypeToString(const u16 type); @@ -111,24 +129,22 @@ protected: } public: - virtual bool LoadInfo(){return false;}; - virtual bool LoadData(){return false;}; - virtual bool Close(){return false;}; + virtual bool LoadInfo() { return false; } + virtual bool LoadData(u64 offset = 0) { return false; } Elf_Machine GetMachine() { return machine; } u32 GetEntry() { return entry; } }; class Loader : public LoaderBase { - wxFile* f; - wxString m_path; + vfsFileBase* m_stream; public: Loader(); - Loader(const wxString& path); + Loader(vfsFileBase& stream); + void Open(const wxString& path); - void Open(wxFile& f, const wxString& path); - ~Loader(); + void Open(vfsFileBase& stream); bool Load(); diff --git a/rpcs3/Loader/PSF.cpp b/rpcs3/Loader/PSF.cpp index 177d4bbe1c..7e7c1a2ac9 100644 --- a/rpcs3/Loader/PSF.cpp +++ b/rpcs3/Loader/PSF.cpp @@ -1,11 +1,7 @@ #include "stdafx.h" #include "PSF.h" -PSFLoader::PSFLoader(wxFile& f) : psf_f(f) -{ -} - -PSFLoader::PSFLoader(const wxString& path) : psf_f(*new wxFile(path)) +PSFLoader::PSFLoader(vfsStream& f) : psf_f(f) { } @@ -80,7 +76,7 @@ bool PSFLoader::LoadKeyTable() struct PsfHelper { - static wxString ReadString(wxFile& f, const u32 size) + static wxString ReadString(vfsStream& f, const u32 size) { wxString ret = wxEmptyString; @@ -92,7 +88,7 @@ struct PsfHelper return ret; } - static wxString ReadString(wxFile& f) + static wxString ReadString(vfsStream& f) { wxString ret = wxEmptyString; @@ -106,14 +102,14 @@ struct PsfHelper return ret; } - static char ReadChar(wxFile& f) + static char ReadChar(vfsStream& f) { char c; f.Read(&c, 1); return c; } - static char ReadCharNN(wxFile& f) + static char ReadCharNN(vfsStream& f) { char c; while(!f.Eof()) @@ -125,7 +121,7 @@ struct PsfHelper return c; } - static void GoToNN(wxFile& f) + static void GoToNN(vfsStream& f) { while(!f.Eof()) { diff --git a/rpcs3/Loader/PSF.h b/rpcs3/Loader/PSF.h index 6359fbdf1d..c665734408 100644 --- a/rpcs3/Loader/PSF.h +++ b/rpcs3/Loader/PSF.h @@ -23,12 +23,11 @@ struct PsfDefTbl class PSFLoader { - wxFile& psf_f; + vfsStream& psf_f; bool m_show_log; public: - PSFLoader(wxFile& f); - PSFLoader(const wxString& path); + PSFLoader(vfsStream& f); wxArrayString m_table; GameInfo m_info; diff --git a/rpcs3/Loader/SELF.cpp b/rpcs3/Loader/SELF.cpp index 2064f787fd..842d74263a 100644 --- a/rpcs3/Loader/SELF.cpp +++ b/rpcs3/Loader/SELF.cpp @@ -1,18 +1,13 @@ #include "stdafx.h" #include "SELF.h" +#include "ELF64.h" -SELFLoader::SELFLoader(wxFile& f) +SELFLoader::SELFLoader(vfsStream& f) : self_f(f) , LoaderBase() { } -SELFLoader::SELFLoader(const wxString& path) - : self_f(*new wxFile(path)) - , LoaderBase() -{ -} - bool SELFLoader::LoadInfo() { if(!self_f.IsOpened()) return false; @@ -24,12 +19,26 @@ bool SELFLoader::LoadInfo() return true; } -bool SELFLoader::LoadData() +bool SELFLoader::LoadData(u64 offset) { if(!self_f.IsOpened()) return false; sce_hdr.Show(); self_hdr.Show(); + + ELF64Loader l(self_f); + if( !l.LoadEhdrInfo(self_hdr.se_elfoff) || + !l.LoadPhdrInfo(self_hdr.se_phdroff) || + !l.LoadShdrInfo(self_hdr.se_shdroff) || + !l.LoadData(offset) ) + { + ConLog.Error("Broken SELF file."); + + return false; + } + + return true; + ConLog.Error("Boot SELF not supported yet!"); return false; } \ No newline at end of file diff --git a/rpcs3/Loader/SELF.h b/rpcs3/Loader/SELF.h index c68e45861f..63ac82f90f 100644 --- a/rpcs3/Loader/SELF.h +++ b/rpcs3/Loader/SELF.h @@ -11,7 +11,7 @@ struct SceHeader u64 se_hsize; u64 se_esize; - void Load(wxFile& f) + void Load(vfsStream& f) { se_magic = Read32(f); se_hver = Read32(f); @@ -50,7 +50,7 @@ struct SelfHeader u64 se_controlsize; u64 pad; - void Load(wxFile& f) + void Load(vfsStream& f) { se_htype = Read64(f); se_appinfooff = Read64(f); @@ -80,16 +80,14 @@ struct SelfHeader class SELFLoader : public LoaderBase { - wxFile& self_f; + vfsStream& self_f; SceHeader sce_hdr; SelfHeader self_hdr; public: - SELFLoader(wxFile& f); - SELFLoader(const wxString& path); - ~SELFLoader() {Close();} + SELFLoader(vfsStream& f); virtual bool LoadInfo(); - virtual bool LoadData(); + virtual bool LoadData(u64 offset = 0); }; \ No newline at end of file diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index f285cfb2e9..f716e8b532 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -3,6 +3,9 @@ #include "Ini.h" #include "Emu/System.h" #include +#include "Gui/CompilerELF.h" + +const wxEventType wxEVT_DBG_COMMAND = wxNewEventType(); IMPLEMENT_APP(Rpcs3App) Rpcs3App* TheApp; @@ -15,13 +18,20 @@ bool Rpcs3App::OnInit() Ini.Load(); - ConLogFrame = new LogFrame(); - ConLogFrame->Show(); - m_MainFrame = new MainFrame(); + SetTopWindow(m_MainFrame); Emu.Init(); + + (new CompilerELF(m_MainFrame))->Show(); + m_debugger_frame = new DebuggerPanel(m_MainFrame); + ConLogFrame = new LogFrame(m_MainFrame); + + m_MainFrame->AddPane(ConLogFrame, "Log", wxAUI_DOCK_BOTTOM); + m_MainFrame->AddPane(m_debugger_frame, "Debugger", wxAUI_DOCK_RIGHT); + //ConLogFrame->Show(); m_MainFrame->Show(); + m_MainFrame->DoSettings(true); return true; } @@ -35,6 +45,13 @@ void Rpcs3App::Exit() wxApp::Exit(); } +void Rpcs3App::SendDbgCommand(DbgCommand id, PPCThread* thr) +{ + wxCommandEvent event(wxEVT_DBG_COMMAND, id); + event.SetClientData(thr); + AddPendingEvent(event); +} + /* CPUThread& GetCPU(const u8 core) { diff --git a/rpcs3/rpcs3.h b/rpcs3/rpcs3.h index a62ec80c0c..c44142c5ff 100644 --- a/rpcs3/rpcs3.h +++ b/rpcs3/rpcs3.h @@ -1,22 +1,52 @@ #pragma once #include "Gui/MainFrame.h" +#include "Gui/Debugger.h" template T min(const T a, const T b) { return a < b ? a : b; } template T max(const T a, const T b) { return a > b ? a : b; } -#define re(val) MemoryBase::Reverse(val) +//#define re(val) MemoryBase::Reverse(val) #define re64(val) MemoryBase::Reverse64(val) #define re32(val) MemoryBase::Reverse32(val) #define re16(val) MemoryBase::Reverse16(val) +template T re(const T val) { return MemoryBase::Reverse(val); } +template void re(T1& dst, const T2 val) { dst = MemoryBase::Reverse(val); } + + +extern const wxEventType wxEVT_DBG_COMMAND; + +enum DbgCommand +{ + DID_FIRST_COMMAND = 0x500, + + DID_START_EMU, + DID_STOP_EMU, + DID_PAUSE_EMU, + DID_RESUME_EMU, + DID_CREATE_THREAD, + DID_REMOVE_THREAD, + DID_RENAME_THREAD, + DID_START_THREAD, + DID_STOP_THREAD, + DID_PAUSE_THREAD, + DID_RESUME_THREAD, + DID_EXEC_THREAD, + + DID_LAST_COMMAND, +}; + class Rpcs3App : public wxApp { public: MainFrame* m_MainFrame; + DebuggerPanel* m_debugger_frame; virtual bool OnInit(); virtual void Exit(); + + void SendDbgCommand(DbgCommand id, PPCThread* thr=nullptr); }; DECLARE_APP(Rpcs3App) diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index a7afadc24c..7611efb926 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -186,11 +186,18 @@ + + + + + + + @@ -203,23 +210,34 @@ + + + + + + + + + + + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 5c81bf7fd5..b4d5f92feb 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -38,6 +38,12 @@ {9bd88f78-8528-48f3-b9e0-78e06476b04d} + + {fd7cea02-e77f-41b1-8b61-f78e7d280e04} + + + {dfd581c4-aed0-4229-bb30-7ee5816049e1} + @@ -187,6 +193,60 @@ Utilities + + Emu\SysCalls + + + Emu\SysCalls\lv2 + + + Emu\SysCalls + + + Emu\FS + + + Emu\FS + + + Emu\FS + + + Emu\FS + + + Emu\FS + + + Emu\SysCalls\lv2 + + + Emu\FS + + + Emu\SysCalls\lv2 + + + Emu\SysCalls\lv2 + + + Emu\SysCalls\Modules + + + Emu\SysCalls\Modules + + + Emu\SysCalls\Modules + + + Emu\SysCalls\Modules + + + Gui + + + rpcs3 + diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index b3b249bfe5..35052a2feb 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -17,17 +17,17 @@ #include -#define uint unsigned int +typedef unsigned int uint; -#define u8 unsigned __int8 -#define u16 unsigned __int16 -#define u32 unsigned __int32 -#define u64 unsigned __int64 +typedef unsigned __int8 u8; +typedef unsigned __int16 u16; +typedef unsigned __int32 u32; +typedef unsigned __int64 u64; -#define s8 signed __int8 -#define s16 signed __int16 -#define s32 signed __int32 -#define s64 signed __int64 +typedef signed __int8 s8; +typedef signed __int16 s16; +typedef signed __int32 s32; +typedef signed __int64 s64; union u128 { @@ -174,13 +174,14 @@ static void safe_realloc(T* ptr, uint new_size) ptr = (T*)((ptr == NULL) ? malloc(new_size * sizeof(T)) : realloc(ptr, new_size * sizeof(T))); } -#define safe_delete(x) {free(x);(x)=NULL;} +#define safe_delete(x) {free(x);(x)=nullptr;} enum Status { Runned, Paused, Stopped, + Ready, }; #include "Utilities/Thread.h" @@ -188,11 +189,19 @@ enum Status #include "Utilities/Timer.h" #include "Utilities/IdManager.h" +#include "AppConnector.h" + #include "Ini.h" #include "Gui/FrameBase.h" #include "Gui/ConLog.h" #include "Emu/System.h" #include "Emu/Memory/Memory.h" +#include "Emu/Cell/PPUThread.h" + +#include "Emu/FS/vfsFileBase.h" +#include "Emu/FS/vfsLocalFile.h" +#include "Emu/FS/vfsStream.h" +#include "Emu/FS/vfsStreamMemory.h" #include "rpcs3.h" #define _PRGNAME_ "RPCS3"