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 zcmb<-^>JfjVoYOz0VW1f1_lO3Fqf0}0hnfBZ~)UB;W=R1fx&`-gTaA8mO%!r)`5W) zEDj?ex?!d;FtAU~087DWh&&@h0s|XZoC8cUFmR&L44w-hA`2kyfazmkU;&GBz~sR^ z+yp`&)IQSmvBBL*l0J3@xI3+Sz>ecYqhaz4j2po63=A9zh){zH5k)a3FfcH|RkBak z0JAyJXjnWVXqF#}|0mXPFfIGR$k5=yz`Qeofq}t;A$%i-?RH7YeQG-NU`)FiWS0qKMCSwL)y z1O^7MeTx4lg6(8G)BD1aK?7tL$R3b=Aa%&@0+|WI9*mh^!8DWdRWQx?HWExTY1l&O z60kTHvl>Bm{(i_%@q>|Jg7^RbwH}NqfnYss$2~#x3r2n*OS-+TliwD~oQK-8Yyavfb!f683?TKK0jtm9A8yOmYA7q&D z`ys=O--!$rzZWue{9t6*P*X2mQ8P!mqGp$NMa@0qirTr!6^%&^ER`$_3YBgQ3N??d zD=JkP6zXR`u9&=2ub>TU)FOAZD`OD_hF`uz?L4V(-N z)^iwGCU7$_IL!I~02J=^v6n0AT`yPE|M^{E83oZh`6UCx=VM)rY$uqdr#*9#dsULOR<`wR<3h6SLw^I$t!3o;KB{~l~73!&_kABz7WY3LwB zLGz&o15i2wrKb)#s2}bzq%=QjF!5ku(5Td8P_W=;V3{D#zyOMqAC3$fHJr@L8ax?T z{2EvkL1s4AGcZ(UGAJ}^GBD_Og2LecnFgi?3s87C-eVAGNNTY0U}kIux!r<+fpLZ$ z0|PjX9%Lv0rBjd}Kxq}^X5_rmz|`OXH3Jk6HVsV;E*?z(T|wqFBsF*-g{e*hQ-cpk zZ3QQTg5x{}8C-6~m!ClC7!(#2Eer}Z&B_%G@{sUwoX4P1!OWoG!BkKRvK!>~3H6}7 z&%6xeewf=mm8$jU(4KqmIehAKw3qa;p%x6%rILJ@{ z4ijbuh59GID;(!Bgn-Ro%Dx4hp4g7w1=*qB42m-bix-Rx1@#Y@DqcTi=&0fTS5}+J z1hQ{|g(Aa*T5XR2kbRb#|NmDi^FILT2ig6CkzoO-90chBsRN0F)PdySWe(I`jmiu> zp!DH5kHMmWnSn#61>$#o1_maFc?=Hvtqcr~Z45FM`xz7}l^GO3@lgNtcLm5F4)eg} zDo(o`X8nKS{r~>~u>X=lW!jO02*3S5L5|-r+@H+AquYoE(|0FVWyl!Ne0E*k!3mIm- zKFF}(^+Sdg7K#iTK>o8}WcXlxfRSMWBSV1)1A|HhD}#au<2+SRe$a3J|G)n6?+S-` z3;HjvE@=7(n6RFz?@w`UC$8YB)bt)GBxc zfWj$Mng5^n|9=l)=|sPof$;|@KQJ&b{C>#Lk-+f(|8G!zS}$Fp&&DD2UP!p(oPLh9c=vo>V~%e51JP>giQX+z%cnQ1B(Y! zPAJHopfI#(U|;>gvaC|oW{}1waMGK39_y7M277#uolrQi@@xLReEc<)aZvhooX1#EA<3X%-N3+5uY9_~gK-+Toiw?Tp`@ltx&maE z^)3cR52kEKkl!p4845t@YjPt)LH&}~72f}UNrK`X6z&a)pmZn=sk;*y3hGr(S5&)# z(rZ3AZ8kG96zI=jV3_R4P~td`k)v7yYA^HQit67WbyE+5?3g^Cfu*KWx}u?;fx&tw z$nLD8AaRhr^~tv@K=w|a4{9g;oC~+Ro`J!GNgA9c>sbzi^;WBb%*+Sp6_DE|+cU6$ z{N=%v1+H%z>KJ$?&tu>K`Ne}N3)~*CNMK-`Jdc6r1tY@+P#S*C$k6awk)Z<|myQe* zUMDilc-_bVYR|2BeUM?p>xT?GEEE|IfZ`UU-h*vFxPCznb5MAL!W|ao4)gv^0Oc?5 z|NlCgnHmDT|Nr~(L-D^MDBgcT%Jc`IGNz)OLE$$e!vs(~{cdE~@cSUcf!_}qPW)D6 znDN_@VZrZ2h84dTGVB1Q6L32b)Lz`74JvbZ>K^Z|@c#dItNsiIM#p)KNAzbfFgea+ z3IT;bC{7*bF;-YF0H=Qz4~9LEx?cuTZ`5!zSAf$?9RtqCIj+)$a!pwO_7fv0{6 z`xc7>3=9?zK<&+aS3&t3n$ET~$S^QWNMtAhxpzV$LxJNw#xJO8Yw|--Jc8l?HEm@) z1&L#(t?zr`@e58{lBOVapfm-FPf+^uV9W)(9h9b;9T^JrXMx>YQXK_NTh5m&JQycu zg7jggtBhQbI4E6#!V8hEzPrNh1*a>Cy&!e?l{~)tK`N3iV z1H)uFNc!^r|9vYcZM|S*0N3%a9x`;iW`vgeiVQPeJ2EVIoyf4_btA)u*9#eTygtZq z;Ppd>6BddL7eHytB7uR$dI5u@2g9zJV0&@ImBYM$5sgU=0ibvS#TO{9Kye3&uf3|U z_;Q^0F9hUH@BhCig4_g36Uc4?rRCQ|xXXG0gOm6Fzk8aU!0EULnyz^olNuswl^HHr zv@rhA>W&AiP72FI8pfb(Ui-F}}HUrB6P6mOu*$hSpyBQb`axyR+T+P67P?JI7 z05=2AK~DyQw>?o-Z`USOyF0mFf(vWUDW=hz%hZ7f#>2%29^t)3>*`<7qYq z0`&*oq*yBc|L{hU!RU_yhg^+<|GI|6{~s)p8Mr|41#-iW#Qz^oF#iAWhk7K>mB9%TNK5Z)9qS0EKU;r-E6igMgJ~1_MhCgO`Y<0)t3xLX-$7+(CT~ zQ2w{fW?-Db$-v;m@c)CQ7XzdIDh4ixdH+v<%mJkr5UszMkTHLk>s}NIgg# z#0J@WL79OE6t)+f8DuUbgTo6JwgS*F1&1dr9AR+)3Ug2xgUmX>)KKuY)j;bF8-o$Z z+_$M3S_hpP65brs0L6jLTSWnli_Q!dZ<#nie3rLMB(y+r_ohVx#20wu!~x=ig4y|Yt%Ay%r3NY&7c+3Yaq>{Pu$V#TZI6M<1!o3{i_HugZ@Lv!E;KVR zyv=q{xtPo#0&8EupC`{{IOF~QUn(>l87I$WxbV7>p#ao=n83-v0%}u$+7O_62xLFJ zulbOnf^h#T8C);o?q4N?>ug-|u(}CU*EBC`D6wf^1hvN=B(yLxg4#YF3}N8%6~qRW zdmap7;Pz#sQbWW+r-q7yO$`l}jtmV4lNxINC?re(m5T?Q8e$flWVi-Wzu+aqt%?II z3Ktz2W>mZc)1dK>1t%C87SymYSAf#;Peq0WClwhMSTHh7_^aSI2V_^Rx_8CJMuwgr zj0^=A8yV(+()u3;4~e&HnKr#mVEXiiA!*lJ-2|(f%>x^4AJ0r6jYskV=SWqk~$Vk1_lBum1rXxe) zMn{Gf9t>$Pe|~uF$gtscBEt@lKVLU89C*DDT%SIG<{!xT+d_s8XgqCbXliHykH0lC zG&IyQ-0@(@fsI#nOsHje0u?(63de?~h8|E~4=T15BvuU?->8BrSb~kMN3FLm**wYWif8eo> z-wzolG(Q5BCyWPfA7otc{{Y8?+YcF6{C^-Y;ddirMT0zJLd_oSirWhrXMoyczY`e? zelKKf_ltJt8qK23QN(~8bVt;5H zNNSkyM)8Nnfu@FvH_0C~-n4wssBeB;ao|wH0_%kgAFLA@4M6Ru>S_jsKMOP@y#N0S zJ)qRk@W+8;0;vCVAgN)4_y3_Y+?*G9GxH$XM{Ykr6U(xR9~o^+Cpt*AE#doL! zHPkaESR^y>Sj=ZM0JWPu*fv^%#J&Ijv(Rq=jhQhm1C>F)4>DBze#p>p8#LB5Ul}|e zU9(Rc+#h$G!(ik6|NF!G$Lw4FKj4_){r~$_P-C}-yV=UkT^^YOb(Kl{z3B6L&h10O^&ms9B?3 z0UH0V)x0HAzv6X;#Y+a3idPH@mg)ch=yNbIRAw_M)Ur8OG-@$0fb0VGCu%jNL~1pe zL_q%Q-~{(wL1WZD`W+0yjVcTrjx!iy9Op8$)M_S)gla38g(?VG>31@4)-Xhg=yNhK z)G!!{)G)Absa5zT;y9Nf#&HHiL5fQGY1|i^IJC zKHmTTn}Ey&^{oh*>oE8K8VkAq|6eEmzwx^9{{v8Xyk7YK#p{FrKfHeU{|6}lfWi_~ z2Z79MRBAZ$Tk-!1Sa>(gWjGU3qhJOK_ur2HFMz@v6fX@d3=GXG3@jlG0#*}JKy7&T zEj5`8dbKu2h%h~B3=dOKeQn9fz*5P@pkSHt{|_iUi3rPx<|hn1jV%luweG(}^t%`s z^tl*>9p^AKIL>5PQLCecBV2=^;ra>`t__Ye7;2EhwZP&z1EVDegMt&o{}PL142=5A zz~SoAz{0@tTJish39^hOuO0tifQ1=yczOT-n**{J)DFQO_6WNj=Kk-n_WA$c`Tzq% zz1!uATANSsu&ICk8y+^u;iAtDNozGvt%*r%F8b{Z!j3Z-S{&yxbcCE#Fbg>#V5Q#% zN@HFk`fQ*yriC09DULH35|F|o1QZUS@Ni=I-(Y=@fe{=Y`@rE5GT8;3pNI&PKM|n3 z1)7ht_F-V8gMSko=Q5-a;a>;G84NZ^{xu-vU+@3_1)zReV(s()-_Z8UKNo~w7?`m7 zg*Hw<2i(Sh_IW{V-q!~i8bD+Apf(7ooCJj*Xxzb}?f(r> zd1*bDv0?IEFmCZ+3IX?xK;!kGHUvoA`~Qc#jkb&p7PX8$vrjTGOrFcwG5IcIiw6T_ zjetchW3LCpHgJ1>@?FN7$$uFuCf{Y8F!?Uyl*xA)3nt%XEb;#TKL=zNMD3PZP+bi& z&x0Wy+-Gj6Wo(%Iml0&YB1jJAhsl3o^*BQXx<4!+WBv?~G5>#ipnT{Yz`r#=6#qR0 zg)3+*;P*pD2~ZvUgOSm|f|G&6Qj_UM4Ks7aM0-Yu3Gxhgem62Q{9ee&@vD*Hz^{c2 zCw?7dxbW*C!;RmJ3=e)QGQ9Zh$nfEJBEyg02N?xC7%aeRFB<9@9jqG}8bIT}9t`H- z^%9_V7$|HfFJx$#ypW;AgDDui_Q9foK?EG03qkGF_q~mZ3=NG=4HrOdtl29WglDg0 zV3@s*(({iXRl=7 znZ1&MclJsKzS%1o_-C(V5SYD^L2&j;2BF48hK3F<28K>82F6Y<2BwZz49qjQ85m}2 zGceBNW?-7}nt{1fi-EP1i-E1<6$5(*7X!--ZU&Z_+6=5Sxf$4IyatPLbZRkhc5*Rr zb-ZHWp25w)F;km?b0#+f*NoQ;+#OsDJe^t$yq#POd>yYC_-Al4@XXX^;GN0Mz&GPH z1Ahk>gHWdygFq)2gWwEq1|bUu2H}RJhAT6*83bl>GYC##WN7hVPy@H+K;hSr$k1T5 zkfFe;k)Z?R@y zb%!@sSTrzbg2cW5zf)~UWat3p5sL*3njTC{;66#CQp1hLM1~GfTc^{Lfw7a5fvMvq zG>p6vVbsCNz|!f-z}m^lz}E4SfgK!1-bi7@$-vR+$-vpk$-vd|5*kL{NMXduz|-l; zz}v~mz}N8-8b;noVZ_NG(CNt_*vZKt)bWx*cm_9vz)Wuj!I|6)LNi`72zPKYh;(`~ zh<0)^h;_VV5TC)#ATraNL3AcJgV>DM4B{P}3=*B543eFk3{o908Kh@$Gf2$zW{{l8 z%^)@7HG^~qCxc9B*qb$;qJD@sdHQ z<0OOfOm7B-26@JgncNJDHR8+_GhQ<&%{a}V+`-A9-s#Dp(#gr7+VPS>Z3Z`k`b=*I zm6_ZOsxw|QsDZ{CCNE@|(P$2CGiyv<$S?;qr#69+p=a_!h9#31GOU@rkYR=6JeEI| zmeBbG@Eln+IF2VbGR#4p#|V%G#l1x$!;I>^3<{GQ8D@ay6ukex2?h00K=Ij-$S}i$ zi2*X+lgKawG=~A2&+uRj0hcY!j0`jMXMpF(<~Yt{=>gA?9b{Ng&wRK7G-l|*STz@9 zNA)cR1@IhMBEy2ojSNd5^JM-IIneyC^JU09StG*&@Bgo_BH6#dgYiFjd=5NMmdLOG zGEWu^o|^@^Z?Yo85|H0KnEb)xUtoVYU#_S-vKdqdFna%g3hwtp(?2vF`-98BhDL@0 zP#Ug3{JElGAw!Ax{|Df)$A(6R5{m=|2k-ySUxL(t`Y<4QP@iLhJY$RpV=&nN4fPCn zKzRm~cRU!PA$@yBh5}I8WOa~X!UTDS2a^{vOqjfoVTuQnA9$<>Dep}1{{QM_Lm~sH zZ_NPCJB)sd|l^Py2CNfMI%{vT=otz9x9WTN8LtzFtgThR22F01&3`#RzgT)Bt znGKT{GE^YtnHp%GnKF4H!;3I@L>E4>e@0iBr;SW@=Ty7C@(ZKGE_kGOpW6_#td+tdC0H7^W z`OSmL2Ua#z)T88?N8tPc%`?#S>kZC_p!@(z)1Y+~pgiII|IR~@zd(HN|M$UbpP>3c zVZX(L$s0W0IC&w%22eip{(m1l7YXY3HzYDFn83)e#DmEj+&+21$Y}7Yk>S9rg$yS^ zZK_uX87{nf$Z+E|Bg2E&iVQDaJ2HHDoyhRxbt5Cg>xGOQuMaW`yne_iVWG&V;MV}2 z*SX{U|2IoBQv--DX;cEy|1=={e=|Vt0*ym}>;;vX9!%a~|AXo;Q2hofV?CI>S;78k zeq7Yz}{}@cw@foQ6Plf&2>brx$ok9@Iwh zU^)semq70E{{R01l)eI@LG?6LPm2eW7kK_3ss|h|AT=O2H6${?+yid^fZXE!|9=Ze z9ugn-z;(Fy|Nk&^H$ctZ;=$wvF8{s%{}1`0`1c~H4QJE9AYhZgAn?19Nx-_1q2&i7 zlf$ovj4ysCGI9J~$RzP=A>)l-2N@syW@P;ETaod{Z$~DE-v^l#em`W=@L=);uU!DS ztEO7I0;!GG0xDxYm^{JdAIN@-1_nWpe?aXM@BdGqHYzf-G&(gr0gaV`+G_Qww=1A+ zEsEM}jfo5`1lwpx?K4n&4cz`9t&PUtsl~w4$;H6i@rr@3=~2U#8Qcu~77PpmGqo9b zW^yy|&Unqh2b#a0zzAwr$%5k@6iy9^3@z4;;JJhj4<--rTnotW(0E`3x6v5P!Ru8$ z*ye-RCxPl955@w>`WDbSnAXSO@plilc~e1SVI7GKOF;7&9&GcDg80pc8X^`dG88NT z&1WP))@DwSW4JNVj^T>qJO&Z)8Yaz)`z$pX7(PH6_n;I8W;jV>qor*Kj8(nAuJX! z1T-Wvlz1@y0H+U&1q}WmenV5k3lByg@EVDReGFIhTfyxb{|Sr?6^`?m4_K=H|KISB zF(71xfK@2WeF_eGp=t(3pmmHDOBobGjxiWjJZDe{NG`Nod4PQ_@ zx59(T9b7jz)-eWHDl+sm$1&Uhjg>dsF-CyK<0da;D5+KP2DKL$K;v4ISAy4?Fw9_N z=mD)&oxGBPXYxu0mdPs_I3^}C%$UK*&^LP_LvOuQc~X-I1L;=y3Mh<0 z`2p0o^I&oT=flYh8EU-$e{l8w|KOtc|HlhK>!K_Y{{MHJ#}M&?k;wtHM&s2&#v88= zGCly;#Sa-@yk=zl@LG}a$7@F>hS!Nq9IqRh1YR#>l6ZZPN#XTFCJhTkCIe7C49d5l zd<0t84~l#5|9=9!|3B<)sAHUAbpVupA2~KOHT(gM(SXv62ir{W_`XE~1G@*?4DkL5 z@Bfc^o0%GNnie&bbVz{5%NZQ!u`jVmWH=IbpUuvLZN^einro0_I0G63cbvx}0W!;? zk69P!L9J3_Oi$$YI1Y1s+B$42+;~ zpg4>Wb0jVxyFm5dL^V(tJ>7{EMk~Gl-#$8_j&VWw1GaCFaN#cKkYHW~VKW#s+A*8~ z$J0Cp8Bkr)kjcPek<7qYk<7lp`~S16NM@}Hf527*(&sRb+X0jhK;`j5MMkes1-RJ} z9TLo|L25wz7Y@iVrhww=;G#x`Hw+mXGbET-Uyx%=2~~iY<2aAorbB{x4G#5MGbEVT zK-EL@0JlPi1oK*`dL5A4EfN@*8#NhNKz?wT$L#@XUwHq2CJV9`-@+o`zBe28#xUj~)z$;C(at^Fiwf z*mgAkW845`zi5tQI0IRW$zD?J%%A{D@1XqM{HWmsXn#qgGBnMrK+A>g40<)stSf5% zT33L|xKM5ZtJ<_Ek=m3fkuT+-vi3S`pd!dDN8{6B*1A>nSsZHZ7R5Y{87ia!u$V2#|Ak@SU7f6NJHFf z?ZChQa&LXi<%;^q%i#60jnxb+pmp=1*$QT$He7A?Bk=qVvKwo~8T4xYBfHZ{q*l`k zWuDSu9?Kj3xeUypy;#+-)=>wZ}kV=s1tTq`H$q0o1;Mr0f4rKd9Y3J1l5h8I^AL3{}&!?6K;a|puEsA zk8!pKgFLvO4w478Asy!ZSOAjqV9){2?|c8hvDf?m&ASjWGjMyl(T;HjC_RR1vj3}8 zW`8izj&X+f|67%yx_R;suv-`$=KV;4>H)8f2eCnO958c1?Fvwv0#r|f+7+)JGQI${ zEkJDzV%rv=`~q5U4%(+un*pmsnPByUO|7jLto}3rm1&N17&HjgpN{kXG=RzzP@8mu z9Jp*@scBQLsF|w_o_7VsM`JPr3#cqWq#?+BJgIpJ6z?FnHzqUifZU4gHc(#!Qon=B z3id6aI(`Nx0}E(A-{Kf}U2u&>3j?G65^#Ep@nEt7?-vDyC&-N!a^N*}T(zl8@U^js z^u^F)nav>L#PI(OsBE*i$G`&euQn?KW5r1Zg&CkddovjVEK~pg4|yqI74ksA3^eav z%gH2Cvr@aFHj{ao2Ll5~t!HqBrRV?uwHaO_wHiSp;IdAWeM@cjClQc)L2VLHAKHoG z{|!)iW^s%`M1MJWE$$qPrT_nf>PC=%v|Aa39p?TI&~9d6ah%I4p%0?xvEFHrU|<2I z%SJnf3o;B0Cu*1(uYuP5PLN}`;5hHs50JPGgX{Cg3I-O?e$s}dMxF$QO~tjEk=JT9 z8N@<07u$i-X{8FY!bA%O77xa~;Bgu6|G)M$)G>nkc#IA67;9>(q(o}wDv5y79;i>| z{r`5R_y0dC4b==Bps;M1&A?MriJ`{T`~R;W1k@;c|Nor=8UXbE|F;Abr!yp&;q{!u zyq_{qv5m-eoWs1I3LtT4p7{u>*E|@sz-uEx^_0WBp8+0h{U1U3xY2@vhwhEIx6E#&P*Dv^2F!?b9&*aApEEDP&H%xA3-~g2mwYDD9YBgVp)M|0%g0&-9F zO9q9>oeVq^I2kw`=KYug;#)BM|17*C-;n9X!v3>~^r5of&v0J2S9MPG;Z$je`;4cJO##V;$oLnANnIJH?F8TVvJ}Xq(_O=*5GR&V6G2xn8y?W zD(~R!1H}51C7?Xqyr|(st&J6Yok-0SYjAl5iYrjx26?RrC@&MUh6dh0NrRWI36Qoo zlS-`$tZZeHfRwFF0?6eTr0jx}TcC0PRIfSAV|)YZk3jn-C7?0^+Q#wl{{LMAl;%P0 zMDPD!L1QwEGbET-!rIyn^S;c0%EQ~vj`O~p2)oZ_2gwr*Jsxa5;QgzhwkoI{ZFPWw z5oE9R0|v$kb&N|K=6wN;d4kMc4Qn?$%=-drKZ3;8gepM#hYs_;ynxyTZ|^$H`w9wM zkh*o=|KaQv-v2*cfaLGbB@p_PhX-3vC@61w|NmlOk@^3>_x~?DK<#VP{=luHpn3-6 z))^Aa>%9MeIna>Q$l}2m2;MUWlA9sHym5vEsE_=Wp&_Y}!-FvpJZ20k_dsQyttJB# zsLTVEftH$W3qWPxL_3BHAbC)ESc|h9+yS=3`~TN1pfm-KXLudT-~)+g1{*{?Gmub+ z{*CeefBvBN|F0pCF!{gd08_&o(Abvu|F*8NqfQ_^rqQ-Y@stk)Z*!FBrTh7`7j{LK?h3qh==L90%(LMuv)N1_jVsmfs5* zWqko4^1XYxH1z2%f(I%~?0hXMl>Cg6k@f`5<#a zdow+l48iT*$?@R5D+j8X85CYCGE}@~WGDc&PhUGSG`xoHea5!$8PvuH*$pyt@_x{s zTm!Hjps~@(`$2m>SA*y68l4&yK=Ul1eY2o_vY_bfgX>3m1{Tox5ooM^f;Fm6gbI|Iy&E#@xeb z0G>y}+{5_iE7Ck-fd}IQaJ~bFA9Rnb2ctEur(+-`6>xev}u@O_M_ATv$Cbv1Y& zV?6`QWbpihK6pN*A*oSivLZu)2cr)-k6JH;>`MUIGg*AUZvBM zLA8^UL9OE@gZd0^29=rK45~A^8PsOHW>D|oWYFyNWYCzw&7f()z@RnLn?VCIA8;6) zPeE-vP#l2bDts@Sod=UHI4Bwl|{r?wZ^Fao19az8pbp>c2P5u7Y z6%%9`SSH9aFx1}w?Zao-0bYjynoneg?QahQ&v9e!L)He*YlHF?yqpK`Z~w6qHUHiL z&u@X(Ak;H3K-M4_fcHOFA7)Shl?xtB+Ti`c;5}-L3=^szgUj6+uR!}jIGC3a+GpGh zspC9gd;UP-1qwfG;Q*>@LE#0eKWkW+D?nik3UBz{^hSmapfCr8D`>5f<2;5J){YD% z4GS3x!1G>6d$g-985F$#|5yn+V*xZb2I_xM9z)u*4ciOY&;&~3 z|0O_XLepyv#9U2qeGHnb1*KWge31u}CU`yxl-50%mVoyq4ouj8*To<9nqdEc+`WJlVH|$Icq%;gO57fN?ufqbR zU#NND`ApfUv{ z4>~)-dLctYLsO#x=b|p{r}TVc=?{lP~pLN4P5S2voa`v;tRCS&4W=3yl)Y7wgo5+Oa`q1U@Hh&{UQqiNWbb5fU+vvlxZU8s+9mMXR#5%~wTr94eKj@k+ypp{!~1HA;IS-3 zU(F6YHjllprU;II)V|ugP}IKKMR55&+E>H6?|?>qwQb<~0aTuY%UDH*8c1JF9vqjH z_0?>^c@JM*kA997?!MY)a9so{dqHUu(pQrQ_y0lp95OC56P$iPYM^ajX!$G;UWX5A z_oDaJK>No)ZLS}R{}W;7FTl<~157V3Gru4bx{cuz>1NP@Bv9 z|7*`iTLu=3S_U3a8|wul!v>Jr$+e*U^~}pa=YoRl1Dy#9I*-Bo|8Illq(+4XrbZcy zY6gJ`(hNM_|2Ki#$sqqXR5J)n-pwH5!6XNc--c=imdU%pVjLb!vfz3cWCzI1$-5a? z!2U>NsDPOdTK5HNdxFyi$gJ5Qvt+^R|G@RcYz6@jrWxS%$QG!3$~Hjxusvl98kHJN zpkdqy8XL8K`Tq~-OwQ`p3<{0fpf#-Ea5bsaW>B#H|NjqYe>J>q>5XVxg65VW=aK&Z z?=bKG74TRo(wVp*H^cS?g7^N-Q?3A=iK0D^fgQXb5OTiR0?^q=HG8!yYVH_UfcDOU z&PW5b2O7A+bEx8$nhY_Z`o6xC!NGAZgA0gVeS}S+dMTSibsK2^dSNAKZUDC54ivwj zvvNRt?LgrOnuA2%JJ_hrzymse4elqTb5=p=%W>ZS1vUu{iZJsMk{SNvGY@&(xd!CN zhYS@UKZ3#(9d861LQ2@B#S)>L(Kr8{{VswtVpVRB%`xgzR+zoqr1QhXv@Y zVb~eTg#EAq>W2xS@oTUjK<5KN&OB@Yg$XErKzqnRY2oL=|Df|y1weD~pfU5G5C2!_ zH%PGmV*KCmOYwilFUS8AekJ~&@vHIwf?o^&ulRNF|At==|L^$C`2WCf&>nTrest)$ z0H8hWpnd5R6902J&SQC@-yj|7IFIFleuH$J<2;rd`VG>tj`LV9=r>5mIL>1^q2C}K z?KqF+KnSaw9cbP@gw06~G>0D|d4R9cnL!{#;)q@YGXqP#rEx_=GJ{CHgmy)v9D@W% zeZxNnnb1NvyHGVZy9o>b^MppZ+11-0u4q>L&ofE!zetGe0lpAHC%F)YBYGhZ4{?)K^cgsGqaBqGsvp zidv5Dikf4)D{7vfuF#(^9a+DZxuT}UyP`fSx}rWIxT1cJbVbcl=ZczR))kZYF|bSk zon08mz%xOPL14l@28jvx7-S~@V_=v(k3qz79$St6d{CIPmFUk0g*97_{(K2`$9XJA z^yh=)WRL!Qa9nI@kb}*Eu!LAEFfdG3{LeC3@jpYe439)A z-0T|V83gJznU{r#9N??>U|tr&4+;O~{R|8Z^BGh^ zt=;TGZJgv9;~97+F#b;nH9El8P|u(d%HSl|@Si~f6!s1K85F`dy4f|{XOOAYyj?MY z@xM-8(Bq1b1}C}j%?J3xH#o_K?>(Xy8slacD(Pl7f$_gjU66N0qvHRBkOW9PPHz0K zFb`^3nt#V(5@0npHP69ra0oeeL~nBA zf0vM52l(nO!E!D&$G~zPAxn?wh0H#{S04oy_o-P577qw%0j2E&eD&2}@sOGpuy_P0 zE!Gt`SJX|ZuBh8uTv2x|xuSlKcSTKdcZL2W28O!g*A;bBm@DeGMpx8b3$Cd9?p#qX zVO;^rXZ2Cq74_B1;B+>ro`GS~d5F3 zQL7nUQN!R|0Xlnmq89j!xeE>Q3=)vOKSM+Pf=?pVD;N|SI2ag0xUlIB2I~dQnNQRJ zk7fI4cQP=7){BDX@j>UGINo8ksg1Qlb_WXqcYw|bywKeEpQSmDfd{l+tYP7QhWc3# z94wYH7));b&oW`*e+I|7e_nv*>p*HiYcd??G0K4Q322QAXpKO99g|3nv=R8+!bWWd z$h`hMh8GRo3?fkf-2s(h`t4wMU#Pe95~+f!@DB%0N~F@3L7|qDc^N1#g3flVbYW1aS3h0hc!wpT;x&W9!MzL&ps;V$ zW?-qc@e--jU{J8kWMHxMWZ(d;Spda}r6vQ<#T5(;2jm$TKw)-)n}Oi~GXui~E(V5! zOBfg~u4G^Uhdo1s-2@H>22gt-1jOcMU;xJ_h|L8#yBsXe368@S&^jcGV+;oMvostW z=l=Wif{{Vs<-z~pIlq?=|5vH8Nn90D>=*hqlBI{-sTH|IH>gpsH8trBm z!ghc!gxN_hH2nZyXo{0usK)_5P@MqE^Pv1*!|=MIHv4f!Er)hRt=H{}nq$%xHP4kR zYK}2iEZ}4isCgb;u`rW?r{5uuSA+-~iP{q3I5Kp>7U(A*>F1 zq1lJ*LcI>zg)khl)9+wlsLi&nsP!_gsA15q*qF({vw@RAV527kpX0n=Q$YQ##yAF< z+PEN*T9+UZixvifN>>I2OD+Z$i&G37pm?r0#-IRN2V?2Qz!SpmW(UqUp!~ydL=Td8 ztSdnA2+BX9nhkanRiW_(%3mP$Xlmi=G#D5f*r53lq!vXTBtL@9Q-j7co_q^drv+Lk z%pg(Upy1#*_rH$!|DRiG?H)nm<|Km%D1Jh2x!J+vNW{&q-jjJ*BPRn-!%GH%&|){c z&>%OvP(C-ikT-62Au3k`RY3uQjQ7b@l?7xLr)U&t>f zxsam=_(E<%$`V+7g34u3c?&8}YZw++)X!O6QD1$yqBeVWMg8906}4WID{7KASJZM8 zSAgn)nwHBIHP4ePYL=d^0F}4(bC@gYtD`IG_XbzgBs*8sv{+ZvEH$o}+{wT&VI_kI z=spC{ex(VS3_KG$83Y>SVCB`171WMXV&W52?h%U1iA2OBsQd-_1saE-Gs13IJ1{VU z;>r8}uLGd@6Hs~T{r~q8&>5$owj=D^%f?Iw9#Gi;Z&xGr6+mZUg4&!lkoG6|evpQw zMhj&99&9NSq3w9|b2Gtrj)2bg2A!`7-Y*W)yG*+RG}lzC>RsW%mJHs%hs_>~1_9U| zB8ABP4i5%K@SfWm29`A*Y$;&<77PNQGhZ1%Ytm~#XNq?UfX)q9lihmuY`pk=*|F8`@bH%j-LV5 z-}@Li>gT@z`y1{j(3vivxkTvsa8UapcVB?|8};COP$v97$WTx-PZ_j!gbCDMY_w-! zvEXLlsBiE9_311?=giuG&aBJ^or}v5FtMJ2WAaG`mI<6-KZ5%($o(LYJ&pGG`*EQ2 zX+d^*|Np-P-bcgkCdS5i1`e>BxENTVZZfF!W>82!slZ^t#Q?gSg9Ee=0@g1h%PsMs z{V)I55O51i!+)q-*ck+%ZV9P)!JuIAoPonu0df|a1v>+WZ2|*JqNePW#B4?e(E1e6 z82RKA3@o6sU;-Bd%fu|o!VcsP@BjZh2)LJs_>`SM=lCSTP0T8T&`kw{&rMu8gV0SS z)OJ&C1A`i9z8AbM475J%Aj1aOJqw_D-`@)vR)E)(f$m^}o~;jB$1t%Gbe016KCBB3 zEDRzIj~O^>SeQgW`*a$u85o*(Gq8kOiTw(-d+|?Cih&{gfQCs;!zU4kNz5{!J>RvO zN+J#N&^=7*ULuy73^JfGA&0pP9-#8gVG^?nC|`rx@`bhe3Bm#Mm@2 zG*lm8P^f1;T#?Yi-2gg+5mfF}tOT9C$iQOh%HRO%Gfe1U0N+ytYI8P1_D?i4yky{L zILW{Rx=U&z=q{tz3>*_qgUe@uMotEihL;Qy4JR37CU7$dOyp(|neduHV!~&7d*iG=ok9CxcBRCxb!5O9qpMlMEIUxEX9Fax)lA zc+Frk;WUFqqdfyh!z%_BOHBrsMlJ@9#w-RNP*{QbC7`+k)<&NQYMXE|2sCCfh%|CB zNHn}+keR^AATTkLL1ZE)gT#cF3^EN|3<`}|3@VLW3>pou7<49ZGAK;UWKfyN$)GXe zC4)}GD+Y@OE(V*%ECz!{E(VhcoD4P-GZ_pfax$1qc*$S^3U7<&3@xBAbz=B;BK&}b zL2U$+h~p#%A5c7mJ4}$O_GVB>VBl`BWM^Oj?fC$?vC@-4p^}?H0d%IlB?kiwsGST7 zM<<5=FTyjGgHWOr7ow%rh7n3T7%Y6wXX!D4OZWP~5@Jz|yJCz}lJ4 zz}D%`z&?YKp=72aL+Q*!hO(KC4CNi{3>=;644j?m3|yV=4BRsq87gKfGE~k?WT=|y z$WYzM&cNTn&cM^D&cNH5&cHW=k)dX$B17%WM25PVj12W242AKa+R$N3YI+#95oEy72({XyTUtF%_`d%6hM4SZn1{&bT+$iuOAGS z+2Rg044}T~>59~B29{I~1{O;l2961=3@j5<85kzoGjL3FV_=!c25vVT0Ikmjt4L2(6J&kbrzgWAZTISa>m3?iU&Z!I-{zt-+# z&~uo}VDf^IfwZtoYP6};6z{0vjL-w`i)V-u0hPUe4J--_42&@h3=Df17}x_C7}z%a z|NnmrF_?jY^$G(6%O99p1_rQMKVW=X(;#<)&i{qX(f#`Xsuw|hD$u$T3wF>Lfro>` zT=4#G9*YJBHvDPl--d>N3>={H%!YwM#L^wqN09pis(-*`pFIPQB?AKkxcrl4V3@$g zzyO-No2bRW(pbm90qR>=fa;rn3>*_V!R5UGXsrp7`vNF*p8(i>FTi!Jh^0EzeVPmk z6F3+c8tWN&KxfoW%wS+?0J)EofdRu^o(v)rJsEf=dNK%j|Nncyx`n}@(FU|{oM(sQ zJjN8u6ov%Qd>ANQI?iJZv2or7@O)qfwRt zv`39$VkQI2L{D(M@HEIW2sG9*h&0ABNHoeK)z6*`5)(NYWEx}{6dG$8R2pL$G#X`* z>Ss>|jftELIt_mrY#M7BOd4bv3>sq@EGA}x>u7_Co(vWfUNYE#!rbCDgU5t_pmOL> zjP)D_1M9sE0|436`D-LU=-jsqT$dqM67x!Gaf{{Zj*{~v(;mcSrlt->%NxmxZIvKmLw9iZ?u zPN~0s1%UkpK4Tm*Ef1u`vwMunx(-NjgAZx zE+1r=Q1hI*B6$v%LIWeighUQ5h69`o3<<}WryN|#z;dvYf#YC31H-|129|^N3>+5~ z89FY2?&?{{P;#-6q2eMVL(RoRhK7rd3@sNPGW1M%%)l`bbpO_3@IDHT33nM7CdM+b zOzvi2XkN%rVClkOW6{EpFnKit3uvt&s1G$En}MTYAw$K4)eJlh2N`N6oMsScc*xKI znj4?+nn9$Ik)dTGH-ki@BSXhTZw8qLMur|xe-qTt1C8s0`@@+G91~VD@Ju+#ATZ%2 zxUC>D(UU>u1tTNFYej|?pnGJ%?cqd*9k6?3ULRz*@cJRcjnfAi9-My2@WMio;e#LO zyhJ;2{}-_?8?#NkrZEmw7ySe8n+L@?xLpgo>zaW<61t8SeLNiVUP$Pjkf62d&2kJJ zptuH=^EEe(D=aM-8bD(gptSPqAVa~ghYS_qaSrHRk)Zah<6NecYHkLF1@#Ojp#EjO z?BR+82F5AY5)2%mHdZwUgF>f00}DtFbdQ~_=D)WfxdaBr9#B54=7O|!I_())LE{ZD zH3PJr#%CkwJd`S$PAGFYIX(%P<_`~&%mC*z&Hb}C;Q)9Ygq;rP`v@N za{@a9L#I81rVRsw1Xy14-`fNRCXR{f3@o6wRcAeehP5n%LuWh#L%3ywT!%aZV`#R9 zNvAynQ>d4NUI;^i+JgTKW}tbpS}*2`nrsFW#cT%oTFu~!(Ch}engU|~#2krUOgh%G3^5Zm7VLFb0O&WNGj` zR&6$OMTa~CbFJp-ibM{k9SO&n7!sDsc3AIaXqX_zz%qFs1INU229}B93=9)yGjL3} z&A>3Bnt=s$-ou0);J)F7R|gpiUV+BaL1X{Wdoc<1>p|@div|XT22P~C3Yd9f2`K%7 z&UOHe$AR($D6N9_T!8w}&B+Wb7Ca0Jpmbc{aKizdmO<$lG+u{vH!3I}fX3`Vb2#8W z<95is={0526^?V694zkr{}1Xnhul15SKE?YQEdV4Lkm=Mu3A#f5Smii4C+67iCDHU zNYpSK5(zoRW>>+$pj0`RL7`URM1XCA15c>tk2+8sg3ikVg?XserhOsD5AcE30f5T( z#B6bqL@x=B@HuRDi5wClHP3ffShg@ISei31g8JJNSQ&UgeeVgZ3>@{xyhJ89Gq6l_ zW?+~&pMhh-Uj~+mvf%mu4Eu)oKh_Hv7?S5mh*@{dUV!bfKKUQimRwWK zuxd#)XJ`t@FSVM7Ky$WAHJm2`LKuG3f%cP!$ZpyfzA{3i*487Se&vY((0Gx!6?uciM zVF752p}C%c#ryxiC*dnW=Rzfc##CAAJ3($_*bu&8f)prhQWzY-VUxfhAp%-|4H}25 zU&*`-6n-H2^aGHvv%bd&Ix7Qo@8KJe9OzC8(AiNI4Ul_t@wi0<;g-KsK;;6&4-8EB z)BnFaAbsBd|7`*3v6#!i2+{`{>m{IX2}mEr@Bd|VR2UdQ=gK%XF@VMd z^{tTm&mG(Tuc$d;Tmf1$<2dX89MD~oJsb>-Ahk8z>?Qi#3=D8R;CsOT->HYt;Jae# z+d=K?|DgM?47~sUpU{v3T93-00H5;&?QMk2IfCv%t^lt)-K$;EaG!w#H2({7!)A$Y78mir&P@nvl%ih*cdn_x-+m$RA&IsvrS&X zz%nrdTn>Wn{;hzmGeucrN_cMa2*^#K^Mv8^z>eTE1U7)$rJy|upz#t5b_M~^I%rUT z95zq<`yj)F-wzpPfc6qJKVc97`3p1_1zA7KXi^*hNdz=*59*(VuV(mXA;Z838b_^R zh^_#Q|1Ria5J=SIcrt+lTt_j0`g04p7)$~#wt$g&kc?=(m=u+%$W2G1FT z;>uy}{{^6NZv6Rxfv4d&s7>|11iYTwk)Z&zRvX-AYh-8ut*3r{kYU2>hYT}7bLQZ7 znlb}u3<^B%jwe5FftdRbG?xsy#}h5BfzGo6t#1a+IfM4^fYKUhpPQvN0|zJ_g8a~Y zmjQGS(t;YfS0dq^H3}6h3<{t+BvF-xp)!L(!QwOnM+XaoP@ud4WRi5(0#G>Ees4Gx>2doP}JE}%0UCT4=`Q4Nqj55`pRz6D(K z_)s)Ie1fg};|Nt?uYrUMBdGmXV11vVfRI{;dH-V?vl%pMlcPcF#~B(u87vyJ zAZ;hm+);xUsO)1=1g$>-o$KK+@4rr?Jp=fDLYUd0Z~&bh268jVoQ7@&j)WEl77vDI z@SY;jedD#7zd-f9LSrVXU7$HMcz(dN3)I#H-K&5IpG?qNH%R!P+SLT!%j*6A-yPEY z^X~>ITtQ>{g#FV5KFh%S|Gy=mIK-YllEHiH@TZSt@E&^n=_6SY)Gr&}>7x<6mxn&- zqXE1xiMHvZ0X!FkmOcc3F)|eVQe>$3<;c+RE0Lk&S0lrOUke##{5r_6;MYTj6~7r7 zHvCp(*zwzu;lS@ih7-RV87}-@$Z+HLL52svA2PfEjn8;6CW7~tHYPPLf$X1QU~rtn zkYT~ezz9l?j-@@r7R!2u zBMplhH(16qTxn8jTw^KEu)*R#!vTx?3>TVEXXMx%&&adco{?{YJR`#lc}9U5 z@{Ft#_A~65u%BVir2h;jCdD&KSkyDhcrcWJ(-g$d*5LhJn10p)@13@gXPnTe)VRgs zKBG$0p~e=A{fs6cKU&OZbOHI%qMlI!B%5q$;seg@e<^JMv2My83RE6n)IL1 z2GkCpSkLGIYM)PxXY`rC%)l^Fo-qS-e#nIXj5(mOfywfWEtBgRD>lb7)@-(CteYXv z*f2w$v2>z6qsBygMx9Cj87(HrGZsv`&)6|(3#gsS*i+ZMxWZyS!wS&ZIvz|H!RJX> z$TP4sH8plv%x9FSkY!M?G+_{#1Ur4g?i%td}i%bRqP?%b;WN@(VWC*bO z!XPrqp7F$l`HVk6X=p+{;~!8OnzWyZ0i1^F8Q*}@(0(QfP*_i%&m;p1>&fy=CX?ft zG&b8a>1?{sq&GpHNnwUOlfevmCe?}dj5j9QGv1k8&-h}}dqRTqeWMDDq&s z06quJLY|?d(VoG_vV=hd6po3S3`~>eGZaj;XYiT)pCMynE#y2bi}{QvK;i1abU_pp zE|dR*?qK@;2h?_lq|-u3ywo#FfYMP-igbksLlO8+7tmfr52gfgz67ZU?Gc_N$H3sh zPz64l(W0Kg!u$XKGhn&JuPY|Wg5|(Ai zskf+SJmLNSzXg=&3_-v9rD&XH$=*3VQbe`COBwc#&+qrvBU(6;=I2H&%Q zS^gG)@5g}T8#8bigY!)ta=y_3`(gN$zxj|fngD8Rnu7PYV)|Jf+&>r@d zJq^xJnB{LSSR9g$Ou+ZMfx{G~{8a_7hZ(-*?PkQ*_}-)yj7V0MAZUr_%WTK+1+<_v!&GF1F(WN7%ckfGz(L52yx9x}}M&B(9- zJg*3vPh7}w;`c#@3y?WUh7X|eDUf}R^B7`4X%KZ!KBLDAMurcs5*aF9H8OzjFn_g> zq2tv-h6%48GR%0*$glu5m-ZUVJeq|f!vj!1A8GIYmgYl^E1HrTBP{9|)=a7cwP~}! zeafb$#x)joj53qvF|3$W$0*^!kOfZvmUWCBjY^Fzpgji7OpO7So{)Na(mX~9P@Ohe zmVsfS9V2KSki&xk65pWny_=O9J1XQE6f7+nL_mA8C(DD>F|bUw1J#eY;Iv>-$9QH^ z9pecPhAeQO!qSex0=ze`5^l!{m>rYt!0S+0JQ#AoXCYYHF%(!Iy(> zVLTXez-M@P|NnOY4R%WbXgmTm_gSm#T``#x3rJn+jz&)g2Fp4ofrXq5 zER$Us7-rWosZMrgV3;7sBr!RLfnj1Dlgwl{28M}oOd6A27#KFnG3iW>WnkFwk4bNG z9FxNAIwpe&_ZV+X*vEKh!aT+o6Y3b>Oy0-%WAZ%4Ka=B_Oswk|Pk_=vbtZ#CLned8 zq)Y||Q2GO{2Laue)cl8m0W`koIFF$QG>+}T7!0m^KxZ3RF#P`u+6QUr`Tu`?7lVVt zT;?0K3je_ON!DK~(t)jdWBt9^_v)^Us~i#r+EKz%pR{!`Fg z^n#ZRES7bQ5tea`2@|9k7-rmKES(?@ibuwb38J8QWXzc$$-ppCj zFl?U3ST{kAv0%nM#)ipuj2e^U7lh`7kINuc zP#YS1Trx4>kIO)C`;WZ1d=2jFSis{l6?}GMha95-D6Bzc4>asydwOLoKxGuuW$+n0 z4Rwqn9t`KeW&NhdnU^BdFCmu|};C`vqKE@LbOpOts zu@9KvLE{k6GGqbhoa5$N1_r2GTOehKKREA#+>Ks_IL>9@f!T*M4C+B);0GT6ftF1= zpl~FrELr@z!u$UVa9a-%2Fc*^7}Or|U|7QYnFRWhnV1Tr3ApQ!3ri=d^R{IzwK?@#_l? zV@Uk|^C7`s3f}+!#(>V~0kz3M=?!!jJZR4yY+l3r|F=MpJa|kRbiPgk1Ngpp(DAFW=2;`tOu=)2F)q|p8^`^Kz4ugqs9ZE`5@5T5GWlq z$iwcpV{ouYVBqjzPzUE9Xr31U$xlAazyca~1G#rXJp%*i%p6eofcAO9>}^PDJmSG{ z0(^EA^ju;1K39-^jmZoQptV#XT>@61HP@g$)S&$UptFB!wV-DdAlfT3AiF^M7G?*? zK9K)9pl3rk%wtdhsR4y2ObtjqOq~a#B)E+h{#w8abZ1X}!w(0Cx&JSK&d#aTM%d^5 z|No3erN#rG_yvU%$gQAw2Ca=j+#|st0UAqgNNPMWgA>$eKMwAjgZdjDjFRBI1#%0> z9U%K*VFGd+)Z9eaIb9x%Uf_DqgHaMZ&NLyBp#XHgp7;O1b3y7o7(K!951yZ0$j|{b z4}4x1Xsqv-_`K3b;J!+O93*`@%=@1Ja-RpI2RL2{&C?>n$^+6DT>QGi zgUJWHuN$e4hcZVBnyUn*d(he)ME{Cm3MkB)PcjIA_6a)9V}RCs9^kRN>eFPF_xC~J z;|@Oi5K=BO5MA$q(;Ak07Z7g#{{ZA>P+Jw0S3&6#bf*pI-W!l!Q2GR=D^PxdwgGrN zn7qOBx}ZGd!88jTPM~rT6y~5Z*Zcp!Gf+ObeDwbRZx4KJ#zBS&)`uA)et_nK62bfG zXMomYG{`ZCfcn9pv=2&epgaXC13_mfSTr-lXg4tMJI-Zj0j=M$NMNw{V0Z^E7p?y@ zlz`S2LgjZ@H!$#n)_6^x&maNPJ9|F^3rKGAe(-t$pV|8uSSHV7keIxWL8PIYfdO=u zf(N4;xIGWfM~Mt5=ii~PacGzaZYR|=)Pd*+;4y3*W`f&EM41U*XY~M99y(Aztlon2 zCUwHf72HlD%3mm9bst=YVGk=;a2ZLInc%Rx2R<(pv}X|>R-o~b8t@qjpfhjJ{B~q0 z0Il}{r6<@rAJF^{XuJiKPCXc1!R`U=+k})kj3%IUZVfdI9M%c{|5~5?|EJbYO2m5a z|No$VX7fvUJ06;+UBG28sC|Mc@4##02$y&NLHQYSzb&cx8RR~Yd#&&N{{t%Lz{gXc!2guHzqaiv0z}}pJ@+1he2YZJ%h-^8u0q74X=q<-_rmN z`@7(=8cIEgnp6;C3y@OlaF6 z5mZ-!$_UUo9-wlgfvJ&*_;m)CQRK(3GdMqzAHPoE{7;?uJq^wukT7-x_kWPX*l{ic z7323dbp6Nw4Vdxk2%i5%wi^+@x542}IDTEhZ41id*D(>4){x@YfwcVV2u_dW$FBpp zEk~XBJqaFzg2b-_xc`G3#*T9tLXg81w8w0MJc9%%@4)h~_y3!)whjq-`6hTy6f~y^ zjq4Inyg=Mx-wFy}WOpFq`X=~Hb!0bT&&$r>bPF;QnwKH_EkNh}L)V#ukO7?)(ah8czPAjtRvol1-I9TUzv&UU>|(5EK3oAB>jR%9p~%nyTb~Y^U!E8T z8c$>3nOMglFk>Er$V7PtiHUU#924sqL}pfl#&$n}+hL9J3=-NM4E&(-q(0$xg~MF% zegKw+MU5Bg8$f5QF(`oK>oaeI*0_WACWzEgW4=$cg<&D0kwHRaSpzlgyA%J{0O8Dq{eYB;{xru4E&(>8_2yA zi(v*#}YsS{q$!{RnjD9N5gs^C9Me*QGxM-$w-k#!1r;0!ruG;-#ztnA6Iz)|N8>e<^}cRL2228$sRl}15PJQEnxi?HOOg45YR(Rb9>{%TNbv@(hp&R$x}bCb%2%ND0?H?#G~>ZE6+Au$a)$>~6L_tY z2U8O`y?OutpYcQS-$!U&`~kEVFo4cF{k@RkM}30= z!|z0f7rzfOGW>qX$N}nOf$ns0oX1oHDmN@z7$htX{QnPbk0>&%nZU?!V4@0PJ}zK*@5~N;C=-o!xBjTVM+nJ2h=W7WSB64 zkzvV1MTQ083UIZc{cQ(8YQg1SBEuT*|5w1}-wQ@Yh5E?FP3F>P)&ik*^FqZ-BA8^=#!jOT1fAT|y1t53Ne#kHZ zDg1gs>peY~&Vu{@4RaV6EEO2|VfrR2GIY#-$WSu*A*4?Q3Uko;bfEDCs6KG{->AsY zVbRD?()^HNfp#YYKPZnjG%}Qc{Ly@np~Z0?LqP*0!xD=`h6xbAF#Le|1>_cpe;7_c z{89`~Ul6}An1IX%nF)$lklmnoa-7TH15TR?4E&&TML_NZ-B;oL|DOv;J;-dwIsebp zKloGu(p$szub|eOsiHP3BEW+C|NjtX{(qqK>oE8K9?*V9@Bf!z>35Cw1JL;2O@zY@^;Ado(YKZ?Np4~U-sHlVx# z)dLCtWCjJ0UWd8=c|iIa5*a3V|GxxoZ+QRzI|Y;uL2iVmMFUXyfx-@&z9xA8zXsNLu)=yVgAAzN1)bRe8nXi3wb<+g zse8e7Ea+Yx1_u6SrN%v={R6ObJ3w=|6YC)LEVRz$0JTRz^(?5~ommU2YhQaNFff45 zY>?3IX5a_)?duo3#$MNUGw|21d<{zXpnZBsKXuGh5%3*2O8gkh9L*2%mu|8=$?oA*2fhd3`f9sQ-IXL)PTmVYHe>J>acov9d-b8 zRtxAZMQDERc-_b_0o#3wAb)}K1IR3p8*BDz1XQO%)-yutnp5C5G58!4&^?WgkU4yO zcQj(DW9E4O|Mv$J<{nIz;I(7$J}+qh3ap>o$j|`p>po2?0l5)$-Y3Wm@Bd$KLg-ght@krXG&?n3v0z}Z2c^LY@}T>mUx58)AE&y)W$hko>y>PRENG6cFk72-plnI{v?)L6$+nq82h<1#)jgEwnA>a=8MMjt_#< zK1dxX9b4K${bkF*QmcuSmTMtt+57*0A5gf0_P|-lg2t=!rc#Grj+x1?Ly<|Nn2m_k%&t4f+kcYoJydyeG{2|KCzjoYwDoUE#sN5AOdX;*~)J zWIyPfB1n9GVSK^JumBX!lm9R747i>LUwH@ewQ$ujt^9t~;%FGDKKQGQ?QV zWGJ!v$xs10x7A_+Lx@Ek1B>+m27V8QeX#nBAC$*Ip_0KOyS6yAZSVr-J%`j0{yv7!*Kp z;=yno+*U%$zfY>aGAPt7UR~kA@EANU)O?3QLVF&hKCgSQ8xjXH3@o5FxZ@l~29R9c zONe+QXy3gItiJ@BQ)74lmVdgt!f`IMPBmz6_|hL}b>?C4_y_3DvsVilDqca)#Rc89 z`5Jl$+H3Ut&kiyic#U)i8YtWx=KXsBN-H4$gW?2qjuptips<`E3-Q0hy#E}{j~Xv{ z|Nk!oR?7+sE2aWa`BVdP*D>U}{17<*d;kA;2DKgmrLX@-y#N1Qpg$XwmY62g&;9^P zV;;%%4E#-ZU}=nL3Mh?*pF~e%9uAIk7+;{KF>de}E2wX$KMfQ|3_1-m3=(xuc2|JX z56C|abD(L=nL(in9Phiqb5|gBAT^G28K1z?7$`m_$S{b^5NBYR9K*me`41?KF*4L@ zZV~~l&w!<|Np~RO;yCYL3oM@N9z)`?p$1YH)h$_F0Xm=1gJCzgJqxoBkr$ew`nTo+}K#OYRvXBEj zMgU5u9!y2xyFj64`at!7=ZY-qQ1pP^2P!{3n2NyjXAt-N+5&cm41)wH{et^KH6VW< z0oVW52@DLNx&##Wp!xz7|FCcetE~mA1((f}?=nbCu4NFJT>-kU`@aUnEkzGO{Sl<} zDgX(;BjEB96!#$WLHPt^J}9qvFtvcg0;(VEe=K%_>u`{MQ2!cKwt(t3P!fAEQ9Oi zT2Q^L1-8$FVG+1JXMLL?!aABE#(FkGiM2FRy<7`gm#`h|9*~>ib+R3U#N>YrBC~%n zFif<8*2zCWb@D}U*@9Xp-vy_sMmq)xP}vD?uQ^V1G#2__}bD3pe|a>GDC`iVX_?q%jAEc@{%c~ zR&$pKXzmSEMu6gb(mzP~<~WbR2Nn*XJ__gzAW)hCmlaDvc5Q{!|8kJD4Km+xE|W#A z<|{;H^_B)}}$v+tsK=)UAFx&>W!y0Xn;sDkz0@cY4pm1hn0L2fe zT?8tl8*3O?8bEb2G#(u0GC=A{aNWmv15{ps>%L_S3ZQZbR33RS?FQHV6qVz1LFG8K zPWFVP6L9}&D)>HF25@+Dg4CeZ$*!QZ!x#g)iyPBjyTExGTqoBtuz=SPK>g)77g8rz z?`Kc|`KkWNZ_wFL3?59|!DSLszYAQqg4$A`bN~t?P`Ln#KhRyVpm7`zhArT+R;03n zqgjT5rPcx|{`NrPkCHH|*F0STN?)LK21;L`bOuXf4)gvl@c#ez2`J1!V^pB^2e9-7 za;pc!W^f*brDsqYbC?U#^Zx>*{`jYXsFVIc>!d$>8ul@;fWo$V8G8M*6FmOs{r_(P z$S&{y`@#7IR3C!WdoZwo*Xw}Nz4!k=4xqc*z5oBo0hI$t;RG#X_k-K?;64Xvyz&o& z$OIV%2~gV~=3a0a4qj8S4?LF*DwjZI(L_522~c^2SgWA|?)yXfgBsv_`#^Sr&N&2! z15*xIKk7US6S$uODjz(UvcdHy#J#_-K=r`Z4qk1qffag#^{Zx>B7K)I5FKA2{v8F~H>@SeK2U9uN zz3{#_q;Cc~d#t1RP~#m?ebT(B@d0SPUL&}ko)1)D;X3zKzArLfXh{b=0}ZB!270{GQe@#u&D7v zLsR1$&>bcqb0)t8?R%{S*V$mT3=E77NsTWi|7G9+%{h56ECBn>`~Uwv2*0a=`?Vnb z9!zE6_J5;N;{#CN9ds@RsGr}!)cC{#bfz390|!_ggC#gmg2EJgc&dTNKf!95O2O#_ z9-gqV4Cs0{(77+oiyD7`!?mdqbcf>taJbsS!!-tPxE}so0V-cS7*~PYV$iS!o!h+t zWIt$}0~EgC@kF$7j_OYI@NHs%gl`%oO)hHUX=rNVKn>p-aGRz1Q4<4b+_oX93Dl;C zhVMLZ9}YEqRl#Kg$SodBCEzinMy19-sNvhhfEvE$;4yC8;j0Q>{|q$~+@?W3XRJA? z33M($KWGjYG+zVCL%$C)YygetgU0niWBTAZTB(bH$)Beo+4&RA*T*GL(SJ zV@8IOhDA*>&@nx5Ki@Kup~P`6lSBg}Lkpyz51O}SyrB>7=QCY^_VZ_e`}vIwEs%ab z(;3`t3Z?+$KA89azhM7^@;7LX65P*cgv^D&>xo8&5^$ShF@pkRu5=wZ?KUtnbb!== z>Udb20(4h8$j_4>GIUrfg51v7Qmgq0wU6J(&|B{|^DpIe^>(iU*LrAp0ky#KFHa-v9sAfZXQ&|KAFbepvW};=+Sr4W$2v6bBA- z|2263NACaqT?6U={aw<)$WQ`uKiJKnab~C=AZ@Ho;QACaM+~yZ`~P-unFu<=8Z;*t zlFVS{{r}HGP zNZkp!w;JpZrc`iQgWON^{{IUco*?r%jLh8zdvmm_Pz z^BRb9WHYoq@E5e+pa4>i{D=hQ5A5ZLK6u>;D9k;W;=$zw%w33bWHWgE10>JNg4Q;*NCG&$A5e-XGp4KmY% zDFNJ`^k7P01eNO$H_3p@D$sd8&^8Xr+ITlS(Q_wnA(3#1gJ`8j{E2y2~!6*W*zZ;X9rhxXigZJ~zVF&=N5df7Npfi>{7)8Kk z5O^IT8D~I)`h5tqgu!DPpmf%()C4}SY_dJ*YzIwnnF#7ndjEe9Ui%8_n}XU!uydxo z|33hSY15;o9E*B}BbN3I8=4L^&9RuzaG;T?X@T{Ah6~p78Ln8@Gi+)24jHRkaGin0 zvYz3^g6#}!mhlW9Ce$;WnE+aIdY|FJr27mjCe}0j0PO*s7|-wrwC7@?JfjF`Z}5cw zj2xh|xi;Kq{@OrFnhXR-~%l3#K!$Sq87DN9GcZ(0Feof8XJE6eXI!v&I|GYlJmZpv zrlu8^@{Ajr9yP^S{Ab)^J)dz3sJ^t=&v?Pwib2HUKH~|{Ic5{~Gu{Ek!-V;aUqI)2 zO{iyl0Y1;`KH~wXpKgHCx#@UsgtgN#G$G#sGR{iTL#qT0r%Y))WC5DX){6c zHAp=uOeTTnPjn%EtY`cIS`P@81D!MW7c{q`3m%&S_2Hmqfx`}B79?MzsRy@xp=XwX z_uhcQX%fUNaNityW=;x7AN0&J@IHOeIffn#DquC>b8H}fk^rAG24Ty1FsOk20+R=| zgF)pCv>ioaImi#5kHcRM@`L-(ptu3W3n)%NaRXZOO-#I?mVUDSE(cFS(j8Id;7M@5jkt2~B&2@>J3Eo+a_}TL+(CJb@^VlCTz{dKgNoodgOpv6 zd<`mt5apl-#E)3ZK@IR)0z^5e01i8dS&)1UHVa;EYJk@pU@r$Xz-@E<<)A!R4Xm8A zsAohg2j#(jfysl)L8QIlhM=`{pgH(ojSK~#wR0%*^GIvvK=bpUd4142JJ31_&>R%# zoOV!pM?32)0z7xv$WQ=U(*i03L2I8#S@(prr+Wve478|cGV%Vu0Gzv6*5<%)Rbd!pK*f4 ze#R*l^BEUd)H5!z-p{zfdOqV8@Ht}gj3>b7h}AP*fS)5~&-e^{j+i{-4e&W)^^AAG z=ZM8Kz5t&iCeQc=e2&|1OrFoU zXL3B_7wdS&j;5xjh-y#h`A3~d=ZIA^C^XMzV1SN$LeIWo2e;v>yD`r~z?wJqbHC~tB|MnUf!A0x)Pv6bY6hFpsMM5WZO^D+Ezf9R^`Fti>OP}`)qX}7>v$aJ ze$_KZSjIC#&i%U2SOz|OsvdgoS3LCGFL~&>U;m-!enI;cGww4sOtxp#m>kcjGg+R| z0(8#tr2C8>llC+EOps@6v5seyAU^Eb!08)%*fRy-Jf8y`_N?IbkK~2@d`LL}4|_>) z9_f&WoclHTKEn!V*dxyUs%Kc?!E_c}Up3T&&i!fvn*j-Xh7H#83Zw+%Hi76m;gAJR{`Xum6lZ;B&v`L;I!o83iVT z`lRs;dnU^>oB*AzJ?TEfjY<0%?o5zp6tRwHSV4T)vq0)Mtm&TN1pcsR2G@n;h5cM` zezSmwy#%=ZZvn0k-ZDb!hy~#Cgwpy4c7B)l|NkZk_TM!KHu5=QY~V8AYCmH~15*>Y ze*!ya3e+bB)sK$z7;u~;#thE8l$|rh1YRc(+Q$YiOI{OsmfB*_Ibw6bq@Uj%wIbuxU zI)SoqU<8lxL&AadbHu#=|JU&T|6j#wKiXMgaJT=vgLYOJcpr@iBO|yhrOfRN;Pwl| z?f){WiHt+<|Nm^f|NpIko>2on1I+vXHx(!!dS=Zx5m0*%be{^WT?Jw{IyD^ujj=Q) zH63`V#lQeM*T_`!@P*pm^ij zQgN6;!Grl3xQqnNFEu1J9q?ef2X3cTFJVxqNM%p}?Zx+C&<2l_fcDUMFpGinJZLPU zhC#xhhJml3hVz4k4rq^k6GIIoeR6#Pt7Z5HE{~g&nx`~3HJt#RaoQlwz%YT4p}>Rb z9=Kcq?VkblgS`K50@stEvJjMq9Otp^fSBTQmW4!<|<1bhoq}PM# zE_e{Ul@1|CxNho=HXx9`mZ^uc}sIsvjE6GMA+Q|=OIMc zt%KBa%?u2Xa)9{(*gl4zU~!OM52jm?HV8`Cse#KHuv%sz@YpDkZUfJMHYYXjXl`mc zU;$b)4lko_g3A^pw}JhSh*zc`VEY(;g2h34J(zBS*9D-aIW=%u16Iq-4Gs$eZhHe( z%kUH2CunYJKG1xqc@L-!1v+;EQhwe9=VOH1)`Iw}C6uOBjMSST_Yfa*kO znFU%S%izKA16*%3CpB+qZfg1giYG)I-vIjq;np?a_AruLr$GGs9V`yg>%nvbJO_pv zjw;}`r1$^7p!=H(VEP^A{mucc8}neg2hN9}d(9m0amq9#G8BN)(1dEpy(%6|*THEV zG$!Nye+@X^z5o9<0I5Tm^-BY67Beq6eM8OC0I5MU3*6p?n6(;Qj(Gq7`39s8Vb)Iv zh*^B#atLZx0!R(2S=YerVyIc*`q=ybPjo+m*R*&r^MKtAF^dCq?gN@1uR-D)+;(3L zt}ng+|84=8#h1!5K_xP1XN3rqUC z3LbNSng#B2dH??lx-GMB9z6dKwyy?cAIl4neT_`bEe(&F zJ3xMFIMm$JsMOrhnAB`A(VBr_Vlo3mqXz>+13LplqZ$Ll;#817CS$9FkJ$d<7jq*>uB%)-!nkxA9(*i2X4E7?F7dG^8`@3YD{Vd-{AyGQy_nW z?y~}=tJ!}UG(4Csz-g(0so4hP|AwSyhU!SrI_*nfvtelrve!!v+}48Fbr#%ygxLkM z$8jEm4Tx@ZYPJCNPeJ<$LH2>{ogmA=0;*dl#xgL>kOkWd&a(|n%_dNLIXsvyg3}P1 zo!~mk`~P>)S=2J#|IdQ!MXBE&~I|kD$A5J(%sli`nqGJ?T?D5g zSi1|-|B?l_1-<`&^|44`Nb&xE23!_{%>;)(;~!8SfabXn)I667Ub|@_&AJ=BcWFg7)jxLE0{f3>QFqz&w~PfZN%iv|+uFp~PY)_@4O~&^>hxj0{)6dKbU0 zm>|QD;K5J_o*w|21qw@$oCiZKxGn?b8PIxr52g#?`V2MS$bjp4@BiOEK+^CTXr0Eu z04W<813+oGQK{LXF{xPx6xI`M7$D_hG6Sf-x!}Qc9^B3Xxf8W)Y~JI+bROLHMNPvp z;5z38BkAoNh#OCX*FS>891@RPKyGYKYTDAk)a(MwXOn9n?gW*o9!%%J=?v42O+P%C z&V%z1svD)jZFKMdUsS9cK>K-r|AElI1VDR&z5oBNfY86LfX@B({{Jfm!v8k~#D}E2 zFHb;h@Be=mK=l66fzbb6KcqJgPtN5i7#IiNi^4Nc86Kx28JaR?74 zPjGz=nz!~~GKQoHoN7J5?uMu}0*5_rb3Jx~$}gx|aJt5$7TjKgss*>3@u&sI4^%C< zZo#7#Ty8_vg6l6lYQbq7sutXT!>!gGR&KF)FhR~Q!J`&j9)k9zfcs)hSHblFi0%Eq z4bo13{JX+~@dLQL2dis-T;aiV2GU+>2A!kV1`f-5h&pgv0d$THSS|Qm?=z6H72FnV z1BWG4EjUkt)q(HgI|GhisNLZ4W~e%F*#J@pX`jCVxACBI;Bw9T|NkeT{fOTG|K9=G z4H_Q`Or3mGavWf*9`VgtlnNEuKI%FnG}|AY1+!uH5nC^Ad{^`+qZ^)U5= z{m=l`-vSOxV)R=iFld0>;Qjv?xIa*nAYB2@+o1a%8P&mNl4uUt-)QE5%Mcvq!1siD z|8D`8CFo%W@^_C}_3LvqDq?RQh8dN5M<4E!UL~uMYwX6V@P2T^T z!D#~ByatdwGXn!iFESfBY(Zht3_AO&K^c6e6KI|Zbk39GJO&T&ouc&&3ZS|U)L#be zb8NI_-~gRlQL~hN3&@?=_L?^DWnciA;{e@b3W{6M{pjF+^h1UYj5vng0S`Kh0CX1t z$W7k=|Hn4(XOIDz<2a8&0kjttG=JW_5|nP5z-?fVoWs2TFPis*=93|IPLOAi!J-~q zR>IVS+DITbO^^rQ4Y2^W=N05WP*@>{4=C+gBrx!R?w$vQ3&?Gt`zdPnX;*mvKLXDG zpgPcT9)n1OEQ7-2#|#`u_h2lTP|KhI_dDpUD^&l(@4f)#Q_xyF(0v;}7#SvbFuVZQ z=Z#D)Ye4O{`hUMGEE5 zmw}RnUD349~!Q7sz-G@%Jf!?nMLHJsG^-@)@{p ztA5I$(5MQ!M+NE!P6oyatS~=3gS1_p85BV0pn3oQ(+N5c5LCVsd(I$Y{`9{AbkFn( z55||^K8g4L{~wxnGBDKCNrTT0fZsX6@C@9a0j-g+K)bJvs5^%CdsuidJO!_@A~lRa z<%IYD{|TV7)q}|b?0u(x?=Y7Eyg$tQe;us+TL8+}uc7Bg!SXdI zPglkQ|t0<^SJCEe00L%>VzP zcl7k&Ok1F{{TZHs+fO8i8@NqU3vMey!mt)R-Uh2nu=xpmhffx0E$IIOXxiZMV1(o? zNcfw9(+To9bf9ntl`o*M2bDFA_2Bye7(j90IFG>r7Vdb$9DL`_Yb^VsKz4fn|D}r* zzBfSehj9-Y!(&MP#~!{l_`~-RxSd3D_=4|2tAW+?6R^1-DLfkx;rR?)r-S_F!DI?< zC(<`Of7~S;o{zw8#F1VJgy%zWn2;Bq)!?*_tIoh4o{+o+2~QJn|KW$?e@0Nd=652) zjUSAR0u~Gm;=dai9@MljSb*+i`n{0h#e`-C4iNwML52@HEDQ|4A2R%KY+`f?S;}Uo z-^#$?aF5L=q?^48l#U(dG3A7GvhM-yy>Xbw=+eBXR`30wY7mL`8-c z4~8k=bO_oH2`WQjd(A<6(n0&R89s0LfW8Le3;;bcF6P=K`H28sXqD zmj$vys#G#JyU)UpMX zFG1r282j;C9^=`MUkt8;8kkx(pzfn(xd1vpxM5KXXuSe|LsQEEP?-YqHz*9ierK`* z*AXum83kS^GTe9#3U3bPWv>@9ym)<(;lt~P3_mOs8NqGG=0z)N%q6e#~>A;Rlv$bYz&&z{s$m1|ELQH6VG5g$xrM z=d!q1H!}2K55FRCnFt9#WpLb47JfzG^)E=_w*=%4$GOZ4aD?BInTiY(Kxc$|Fj<1@ zh~}i0J>Wg63mMjc`im1484gTX$gszQVG_9i)TqdC04Z$NK*MHD!=jcw(7os(AUiD+ z8P+(?WwrpFX%0Kn!eK6h1xU_}Mbpg4@I%42%Ylb1;}wAm?B(O@W?+ zu?KPv2D1#Vb1)p{Fo+<|31Z*?on3%>F2*i!eFAC|L)UfcfX^~-gq&p#_FE&v8q{+! zvLNGMj11s&)E(zC?Lj&h19bNLp2-gx4p=He{0KUi0dyt;(5`VP~0JDuVJcLkpUn3qaxR{r`^(C~h6* z{c`}tAt-)8>84SUVGS(4L2*5S5fVS%|NnvJ2TQ#Fmw?MU@BhC+bB`0e|CfNr%Dn&o ziD(3$%m3#@!$QzG7r$B>7J|-P|GlSC5k&v5ftU_OeWyI5a=9u@Bfhd#6j!EpnPyz@&5nU26RtBNHT+k_y3*X@mf$`_x}H< z2NX6QOiJLk0_uMF?chEgDE~nA!z+U8Pw)SKwt&PyYhAqme*o{12bI?zObXz6eOOwC z?!jN<{l6HT*Fj~m2jgAvxEaJwMR5BOi+XTb3Cc^LGzdy}p!5d{V-F_CS z7m$4(ObTHCdoU@0>mrDM?mPsIhr;^|2N@cg54EfT^}8+P7%V{J0~MSM3ZT9?$P7?l zyn~a0p@xCI1l%W>#~?F7j=^Vw9D~CIIR=*ratt1z{xc};f!jE97*;gaGRT1136Ap^ zu2d#7D1gg)X7IU%3=Z=cR6H0MGC=oGg4BTI9p^E)G(hD+=lwz275^VJFf(vK?EHTP z)J}o2Pk`Dz$od`T{XYWI<9LrD1(e1?;~1bh4t#CGu)V~6y4bWZ!us>xPWF|ib&-XD*RAvCp`Eg8)Wsvdy|Gxwt z=CJXY1O^sRc!S11K;iHGA9DT=*xWkliu%95!DDES^B8!%|1SaO8AuvqVzf+PU;~Y> zOps^b0o@M(8kYgN9du6V)3Aj&L132aA0ifpG=SzQ|orANd-13<5N+1YSo>-~*7E77Ywc-v3{>HpnpWSSm1xH_T?>0EyQycvyhqeS$P79eoC!>3#qt zPWl+hzYtKogT`1udckHmGBkkhTmYptTz5Gj`g%9OWBnlidN8R&#y^@F7(r`aQPu$~ zwY)%IAL}@ep{7x(s{{rIt5H{2J8tvi}_#m0C5Rc?dRc`~*}6LCkvz?oXqS9V5czI=HNd zgozrsPHRZQG;azs1B>_nm*Dm^9<#vpB-AYM`VaVh77rO_fcroR44}J%DnNJs{XWPr z0d$`QZ2S;($At&OWpErd$TRTN-~LdJhAm_FM+_@FfCP4s#g`LRuKC8tNH1 zJQ$od&2m;IgxN71r^l892)z(A~KVkhy!Pc{-qSgqVF%ps)eC2V_6UO_2R- z7r}F4;5$7C+~onP<0eAJo>HtAFlbmbFf8!?|F5Ufsa3;*i-Dtpn?a#d8+wM~JO+d2 zM=hXraV#E8qTuw_u&C9dp{dmbG^YVd50JAXwZVJ$z-qx~N46SFe#O8rgBw&%Lhcs_ zooxuJ_d)LVV7LHo6GGYzS5|_`QA-5|Mo|2L?hJybEzn)FpgTgVr7J*dZ9w*c(h?|r z{f6F|QZrY%!u$VRaGMfpHh50ngJC|nUDF^78E36;kYI3}#|T|pdjTB(pfgdyJ8ltETFq{LH#@rhH|hQ^yf3MS^sBXbeQ)av@VIkgW){5 zO$SO#pmC7bgwm5|aK%JtP#N;?3^scm=Q2ou?1S7@PzH`0(4E{Mw}8Yzavlt2;CXj& zyR=@q0yNIxcrac9_l;oXj0eLxaCrrqr$tFCpu2S*g3sA50GC4x!E+uP zI#?MPz-^5A$`$$z3|gQ$Ajf%3(6#E!2Kw_E?5!6tFj_Z&!hu1=gMq<8e=Y-?^ z$9W7U^$jl!>W_S`0PPW|f5~7`!_2+~vQOau9S?>w@cc1My~Di!S3qh(e)C{}oQnz? zM*#Jm9Op4!vHs0a((Ke~Q}2AaqIo~au14@081Nl+HPRLJa~VMQV(bBx3l8%b4%E+m z0J;;6r+)4OQ2LFjz60rJdN4GC!=TX~62IWQB@ekOmCjJ062i#YyXJB!h%aj1N z1ANvp=&l3U-C#^R8ul}=fcgS8OFsmF&J+fXeSy!lU`hn9F94+l(EUT8czN9ju9v}g z(!XBF(1Cu}a*ZrAbWY>H2k7o5i)se&{RfV7nG8UF0p;QPrJ#GGm>583eS_@sU}Oh% z;Tb?>y~Di!3ZT7L)%&1#NFDiH0cvA-FvVtq$_Yf@7IKG_MLp#19mlzhFTiHEJgxxU z`Q^b71$H~g4WPaZXzwp54}kI+${F7K8NhdRfcyyd9}#!zcCa!qg4$#b^Zp({j!zGU z2JpPNg)~D2C|*Eu0t$bSy`VU0Sk!6*9Xl!kiCco>h2a4rUNjsW<}yTp}J@#lAybcj)LwV__qd>u0d^n z4~Am!`UOzh2AxRUk8~QekU@m_`Q&6$M1to2Rv9*!Sw~GKUyz&xB|2%r~c1xaCuO}c>%U(2zpN- zO9Z4`KLuX9)4ZtFqxn#)3#fk$s*50N-dBO^5YRd{Q2cuTuLF-Gfzm&y9qc%d^$x_0 zlVEX>UJs^~;4xIt_!4+csWO9t_y2FT;STH;ptR^Xk5K?Jw#NkB+j0`z_h>rQ8qvI{ zyG7gXDW*43Q^$1_O32h&P$ z9t4$dAUA-@X7B%Be?r{~E~A?kwZ=3ZYWV`X1E_f`*gmk?2)k;*Yd;WnF)x7VKLHj8 z>GNP(2_B&tPe7jcrZZDU<1oN{apbP2dSyP3yRkh;PloY z!60BUouQ=Ofw=-yPlD7=kYEt-V9^5ieJv^>_i=*q2WZ_Q;=JEz@OTx-ZJ_yUNcouo z4!0MKOb=cuGDU#G>6If>!mC843{W5IRU=cutA$JzuMRRbyn4vg@tToo!fQpQ8Lu6g z7Q9YmTJgG(X~XMEm-DWDI2ytp5j|UkA+-gXTv;G|0|IrPc&c zUjc5fEdv8+tu5&6WDlmLV7G$pm4n)A2e$W%2h%ce9||1KsjxAMPk$ln(-(pF#en@- zD_sHdpW{3R0}u_h6MSwNC_Z3*ogfEY8_xncy9tt(mx1dKRDXS%>HYt&3B;a-;JgdA z2XgL?OGOrgLc^n0k%mL95)F%5Wg3-QIU18%c_wN>*Nd|=Ff_0+Ff^(%Fid_09zOz& z9SKZiX5g6om_ei=snx}UX(^;1a|F6q5?r=?QuO}+dkLr=0;)S4=KTS!0~YZ9zYtt@ zf!yFYk0GO?m_Y&LM=M80g$Ab91WWn@yx$&>bg%%tP7dzQ5~w>988?8^9o(Iz3<{G>7#Jphfx2_? z>I(1wlfZU^?oEQYb1@`8GBP>TCC^54XAQVK0gVfT?4SG%=FY|7{u0EU72t7I@BhEn z!1rU+GblLB`z2$M$WY+HA`f-z|NoBn7$JK*>KPO!)G{!D@~H>YVsKjz#P_7lOuw8k1U68l74p>#{px>#|!}K=}u>))r=FH8jp4W`EH2 z{{I`aHk-ry|9p6z3>rHH)k`2VEf^UlG%B?!)YM2c5vQnU}|~Lu&6Zv)c0&?Y6ag{1nS#+Fv)_`706AX@SbE3(i;OFZvd~gWIP6* z7XitG(wX=FYH*t!6qlfK$NPT_*sqZK;Sku}P<`Nh4blf*4+?6ZAO2i1$rfxrcuWMM z?kKqb0#XO^3&hN5uozSx>`#z9$WHJ7Rp34^C{IAug2M;g|EZC#0H0q3vg`5h3W#5# z!Q=U$^*JCjy#N1?0MVeb9pqm~`JM#sM?&ob&vk&+fcA$n90Z3SR1WM{=-uw%HWTQs zK#>eY~c}WWyc7V=Fs+p!-QL|OMqIx>0y>Jj* z_joW^g8Sdre;6XHV;Df=JTag)0BCK!MLFm$BR}x?DQJEg)Sd*jD?#JUlkYQ#%)ZOO zFhPnzV)iWtmdUq3eWVO<_*GOeD1g#9Xq~uJz(u(hj|P$K=-ND9bw;6wG>o0f1C(D zbA!x#?m%t@wXK`?GqBX`{SW|h1E>!Kx`P%Jm!PuWgDDt1U+ewx%iO2 z6)3HN`ZkVp86v=K0G7iQ^{S^UJQ#ezaSUqDL*-?#-J1(K2Lj{{P<=SLoe{&GePEiF!+G$U6{EJbN{dL{{Q~~s9)Yt1HFrkVLv$hK;?-C<00_4 z3`ic7RzZ6}AYmL2P8%Tqg2bU>;Pw}`bO}m}AUA^2CFtBP(Eb;^Y4RY$0nBu{51b!8 z7|g+ShV>nW2x}XLlKR_~BvRk{19T5!L;Ze@0MOVq9C z0pR`zNMEBJg9u1ZO}$3|I1kk^u+&;hf#*>`>2^XLq%ZIN|KFPiIR+NgFa`BH|E&R; z1GC$4E+YfTE>J%d)He5E@B+shNDasy$GMCwwbn@YMM3ZT0*#k~+JPW-HTAc^ZgQN< zC{oiRB?5{kkeML&docKe`+y*IaJTRE2msw*=s1_*NUf$8!tGGAL2ibHo5S3HXKMCq zL;IEgB)tFsJ5s~UTmcJvhdKW`K^YKw$w2XGr<*w*xeO4$7CHd;#(c zOb%lHe;ZI7fWo7uo(UW_AUh!8;s7f*>+?M0G;V)k-%W`yOA;B_d>>u z-wzopek(G1{7z(y_ zW#Z&tpf*7ZxW8Dxn|+J5BEt&M{W73^fS~m5{r|^Q&^)g7LWUmDeJ!A}1+*q<0wco` z(7Gg0-wl+%EE*YQAa3^sw+Wh?T5XyiGAsbs-3+SIFk)n{;>3i20d-Vtnf0|O((?kI3N0J*zi z7Xt&x4;J9|*t@wPw)g)+aNG6;BjbeEii{qwL17A7gOkt+&{_wOe?fN#fbI|g-ygt` z15N|zYyLp_t}&@K2eMv`Q2LJGFM+;5l}1ye@eS9mDhn z*FmUZ5CLunH8iyrG%RW@0gVTFFbRS42WT7<5nrFc?S4>vIn4VP0y-xV6sDkc9iX@c z#j(TOf1t6oGoUq)pfV8TH}IUp;@9B2t{`#i0q(!S>JZ0y{}n*%Jz(pSKE8UT@U= z3o?)H!LS`NZh~b!F?hXuW^@H;t_tUT40zr4Hc0utMJeUN)bt%Z59*lp% zWeOn+R;fErga_Asq0lXY~$XEd{hroMQCW7-mDE>k13CDR15}-aAxEun{DV2fO zA5Go`Du?R9X{!DzKByc5rCpG`2a_kb ze+4d!7Q)J+Ot3$}Wzk#)2T+~>r3u9NKbEp+?hglWx&n{?BbP<*L0Jhr{tqsT4u3`p zyHIc)idh!D5`?6$TyWTvR2IzzwOjtr0mUn#t_TJDzkcoy2Et`g1~?DVuPky0mwk}3 z=m&T#f%3A*9h_dkaSbo`L%?lg)Ut>dvStl5M%nVX0#bLp1<%ca(xL;bESmen0bCX> zeGM-6h%1Yn!ReH=vd94(-{h4=+2FbyQWpIJx0w)S(I#+xh;1!C@nz8_@R$H-EQ++U zh#Ne<1S(HF7=M848b~>_72GERiNnhxa9;NYhd)#ec&rqp22vIs2getrEcy;E8&Tsp z2)suYS{5w?&zUS_3yA7|_7f zT48;JLE_gzMuA@s86|!@G8+6&WVHC*$msBUA*07{Mn;86iy0UiUBK&vKzGqGG_o*& z_66{FrZ9+r>iLPz3_KH=83bk~Gl)#IXAlAP`6ntfaLlx1U;z1{8PpG&ty}?Wqt#4T zuBiUTpx`)g2pQuJQy~B z!xOZ22{IqRFe8Lnz{-Qs5%1QC;$Ixv}WM2 z-uwT5V+{jGt(})hZCw<2O$q26;^vo-{YR5ugZJ@)#%MwIfyY1a?~z*%9y0{lWqt4e zABTDWp=Wcg2dBXojEn`Z7BX_YI>-oKYxn9Qqr_`QMupdkj2f>U84X@1GFrTDWOR7F zkkRAyLB@dB4;dpY6d4mh?JQ8=98@lX>S@Qhi~^v(hs8YD94W&MQ27gLgV(bjuJB;6 z1=q(QJrMN_b80P-`a96MQRHxSnD=ju^+ARL(3l^%Z2?Y?wczn4Q2V^v6})H89@1WX zk_#$hz5l0!+p?7StJxmf582Nk0&*j)ui^dwe+sDo2=W)mPawZ}FxY_W6PUjo=Kj|K zjZ?tF0yM4x@~^|(e_y=+|33m<`yv2ZFM)OK3*#q98p68v1#+J{(%P2>&^S9}?TZ#T zFM9v~zXw!5Lc$^qT$Y2o{aK*9qZw>K>;6FfxYwXH ze~Ju{wFQX`9k|y0O|%Ei`Tnl}ty64HYPGSx%OC;j(}U8Z2SWn5TnCjU7Pa7gwGyB< zKd5a2iuXp)+Kc7@(AtO!kho^Qod|L}$lYM~gWL>re4YVda?PynkOn>DHnSo@c@L(Sq7Ppt*1l21`i&Cl9%o+|mx@euf0l-V0FQ36w7; z&xeeIfZPUl7m?wLI@SmZqkn(A|NjqwqyzAo?jj(2Kz2aJ0RArkg(=K!p!oG*u-FS~ z?}PjR%3~n+In4c^0!phE`#|yb54>g`k{4XS`#(YTlLzBE@R;Ba#ead|^vuXuP*VY( z$2a+{$XM~)k+I?TLdF@t4>B(Jb&xUP*F(mP--(PJzZ)4R{C>!|;s+z+0q}amo!S+k zwCw%A4cw*&r%T8=CnBJH&@c}&r{ewpC+p-L3=E)qm@PONBr3HT6e_qF6f9OU$TV~_ z2sCg(!WW!wRxz+xJ!CinT6YVId(fDh_x~41!TtG0h80!^87)BVW^f-+5xh6#z(hrc zJ)rh8sE)2XvKiX{vjzA6KxwvlA;TU}n)P6a1^g3ECRaNIC?g3Sf_5yXf22jo8wCP*Cu@(XC3!h<0e z+y(;o3+6L8fc)b)mx%{tzQa7m2vDEZaSr1IP+P!Z9%Bb+ycFg>4<=7=y9WO_Kn%DH z#B6&r6(EfRya452&^Q2i-4p1(Vfdak#xo!};>G}Mz-t#E?d~^_J}|{&0M_8~B2YLY z{OtiQ15n%DOyKqnD6Xv^{QvJb=buIWf=?AS-2Vz{Gnp!CH6sE-?fL(C|NjT-zr*+V zfYU2zzu$al8hQE~RIYP?#}ZO*L)-5U!D}r*>BMo~zXH@a@BqgjC|xw{U|;~H5lCHr zXCa8~{XZUD7rtO*Jn-ruW5TP4jNtVHuNfH&UMn(Iymn-4c%8`D@w$<5!s~^MGhQEL zT=4oK;|dE!MsWFu7_0gVx>v~q&0XO3Imlh0y2N4LUl-85?4U3J?co8Hf1onPVeVf6 z@BjZ#U?1Nw2hZOjm8UMCz5@R74HHPe2jxy+(EL9`3HI@gzaJoDEPp3}_LO@6|GNNG z-$2^mdmv*Pj~Ns^7~g>FM%1`=1E*g|SjK_NJV<}!EqF|!`3G!lWEnW^QPv+>3hq0D z@*YY35ytQ5GWFHvdbBqZoy^OP6h^0yh6&Z>rX*!@Bh)@@`c>83ps8e^$KHvMI)mPsGZ|Dmq`WW zUQqu76i*%u`rx&DAUz;?(0)nBxr`6c$`kNjR!}*{u@B*#{U|qescoP z;p49xL&5bYmb&o=qHbgYw{4+y;|uVe>+rgf3B0$HxVq64-2R2sjZeYzyp)egnSlGh zsO6<2xC}$78ySCt_h*2@tsXSbKZl8>{vShtWy1gekiF6j0+6)(r=|Y&?+Q>HfWjXX z4>fgOBA~U+pm~FuZY2>|oPqMY2ZJ%#evtY`MTR{f^|kgM0gyf33xx-;DXg%%!1E9Q*$P2f?^E9Bm;4tsc5752ypfm&? zhkFXy(SsgtWTu*}2D=G6A zjNifS2#}vV7@zC})z_df@L*gBZa0F&vDeY&ka<#YeZCMpuKz>v-$8JnE0IwGw2lT; zNB>r2xg4m=(f0&WX{_RM-PJ^_!FH2;RBnK|J8 zJ7sBRHhAq5>1pOGc+U^WogR#jz~dyKbnn3kx$6le4jN~sEIpKi^A7&>@UoM#^l;OY z{Pb}8B4&Cxe-x4)g28E-y!2oUZofmqb1t|~4+>9+xk2Fk2}uu+!0i`AdYA>ykCdf{ znc#T>($m9d@LUqeogR!2!1XND{ouJykT^6w4B>eKO>mnJR{!9hCzt`L`(@z!t)cS- zir_j0l%AKW{!WJU^=)Bi8)U&9i0Cu8BYg|&k;XQpaSW)K*t;%=aM^5kP5DE zKzRanp5Tltq)hb(*Qb>Di?n$H6>xqBg$1acLHRtvEl3)|Hc!C#0o*pnK2IP7Zd-xI zNWpa|_{=juaQTcnPhbX~>jQ<~B=9`JOmN!;qz84LU@ADxqRkUP);}F&DER%5p#n56 z2RcvAB7uR?gJCMTzG_rz1MOX61kGzrJ_*{pI~6dXVHn_O~GMLH5=l@mU&`+DZ`oe|JFZEJ1Nr(ZZtO{r}&BABz7Tg4=G2 z3@@PLD-6FK89w}OWMufgkdfo}Lq>@ojEn~0`?KaKSNvvVc#+~O|n3J;dW z;5HCw?zevbYw+GS&_351&Iho4Z8f0s$8jFp3CP^gWN_KjoYXGSe5kDily4FDqZEVt z-xVng3ZS~q`#O}6+y+|rGmo_atd?Oixb4xL)UMKesI3NMKEiE9;5I+lZI3~AxP!+D z5pH9K+;PV+2`moM>%mk6?uVec4Lnu|wvS~wI6o3_8}!bkN#ONH%}MPV&4=1rK;|R4 z4O|W(+y)sVM7WI!dau+(aJYcRb&+y00!U;^DI0Wt$D4$|wv zQ~(~MKutS)z~dwkx2*ujIexdXK;wM^c#U~;Q@cs?p|&+3^Fe3pLiUp8gWH-2x4D4* zkK{H9uzd{uU~!OM52k$ZxHFpD!0n_LjEn}a85thD29+nEHJy$OA6_Ri{CM5S$nbh0 zBggB5i~_G8GD=t|GJ?;z2Ddrqv1)+hhgA$5hS2z#;=z;;o`*+`pWWcJ=KcR4_}&?q zJ00fzReW>A) ztOekD6>1h_UsWxG0+LzaeL)bjoWS#z-v599K{e|)=)RKz50<&$GrAyVvF3o(pt=ik zPZ`uKaC-6n|62s44&lc?ko$^Q7J}P)dx+4?0+&@#v%vFp-v9rA)gjFKI|1Uy z<=`?3YSs#neQ0KZ+xQT(9Krd;`~Tk%kUAu@9ze{3lo?R75Md9$V<;CKH&C;{eGc#c zf0sb^p=E;O0%RtrZGqT_)^?=Pscj4D{UB{M9!$C5elcwS86@xS1pC|j|1Xf40^a{K zAno5~$Qd4v^O!?GerslGOKD(gy8^naqFRMPVX`db?ve>|p#8=<;5H`cu8M}Fww4Lt zeVhzeAbSdboq_BP%m9}Q2)8m_0o^+SHFw8kY53hb3?2+V;JgP)(;f_c;5OXkeGD8E z>OlKK+e$o`a=`6)@R%I*o=NZjJHU08_y3;`5c|@>b7n~PH6Z!p0(O67gX;^ZKYGyo z0opTd;{87zyiNqkTm_K1jZST6z~xG{bOk7UK=Cz0mO%q_mkj8R7Y~+|;Jn_z)b<4w zW{|QJa&I1LnXm&KN8bN`f%b#Tc>hlar$w-RnV`MAj7z}wCAHmYU}`%6vI`W3Ap2l< z=78Eo9xMyNdA1>`ZHf2)U!XgNQoR4Cfzu+`Ebv}d#uo5BB#^khF5F$_~c zV`Ys=ZD&AZZ=muS6pje_5YF2 z@Ou}%|8E1=|K9(9902X{^#1=h!FmB${Sxp0KMO!?@BjZqKy*V=TaWku9}!S?i}(NU z8?3>1wfyG+?E&=u4_=eT;=y-TIy`iaH2Gq_6$1USy@c1tHUJ=H# z;PSr#w^~TOpTGbz7rgc#k6Q3JCe&Q8zwoF9=Xt1FaN7fqT5uZ?suoL7Ce=YZMGpgFkz6TJWbUjiCi2Z=$=ft2UqKB6KxT|mtNi-XjG%mI~&-v1TB zVo-T-xOo5n9|Bs_(~#620qVcl>M}4iJZev_7aMdwm`yGN1L(W}3q^(ki{uF`pmYE-M@N-`0hBgs8k8#>n-~NV8W6v6ddO;Bs6d_Fo4dR4B=+531MLW z2U`2-Fpp6Kv_2Hu-521!ObHAOmYVoDg(=sXri4~AB7T?{&>5p*Ua=sZSH9Kp_G1jP@?{)Q|D2Dsn78Cd)pSQKA4GH5i~ zgZ=$uB6#2Qe;csc3><3h7+5Sd|Nj7mkN5w7HJ~^I#U&^nL2(9-%SMI*(Akrqc0lud z28Qq>0#=~;6HpvC?+4|5Ua)&XWq)oef$O21;)r_rcue!O#M( zdqD23Kk%=hhVxTJt%64YD7}H!I)c(lt@giy&};_KIdUA1^B6iB;~6KPX%wzBY*$X-|tGTH?!$O{s1GF!$RvYSuxeVa47|9Kl z6KX)^)PK;O2%s^7M36b4b1p${0fhy~Js>-f-Gl5FkXax*p?=Z?`N{kLe;v@;2+;hX z_y2!0ki!}juAs06%O@}}S|%{xabaL!^8Pm+TIT!#_jNp2uI<}nypANcdm zu=jz}e*;r{LH%6t+Hs8rruGsjTc&}j9dr&D=*|=YuzE%=Sih0UgP{@Jck}-LACw+B zJQ%sa^{@B;|2gnDdC1Vw{HVPK6fe*`#{e0_VUR((kH^OQ|G!p{xWl~vZ$M*A4N2_< zp!4Xx|NqNvNNO+f{{Jr(lxD!`1bY8XLsC0N_<+XPqf{Ffhyk ziM^0x@Br;Gu3_*J`60*P;V_q>2DCqTK^+52^%DjK(0Qdb$?PVuePkf_)c=j{Q|Y02$=^m1EkJ_p#j{+2B`;yGuRCe!RM+qfXX=7olB7P z*#I7^Ye;Hu0L5KR-M@l{r1lo?|NkmMes`S1(9)39-r@cKUnZ1a)2P(m(~!vk-dp7` z542yM!K9&{!3Hcpk3j>pCKl8_@c#eL6eQ*_=l>IsJSa@S{CWRD=Zr9UFw}$F)P&p# z(gzyngwidbv;j&ZpmYJsyP$LeYVU&53aG6MN~6~M8Js;B>cM@4#(D-B=oz0PkbK9I z1WGFo^BCSV$V2Xrah%5>FhQOHymxwnJOk*QG?B)527&qp(3)k29S#2(5?;zPcvQS* zP^e!K;ovZbL8ewCBH)EQLqu&xbOk6q{j6v3_#w{_QSp*N;e|XyKn;UOK>Y#-(7JG* z`V}7FbO>^v2ZIZ^jR+2ly9`S}_JGW;^kz_aY0nU0>BYcksmZ`-!NI@?Qs2Frf#HWd zgU7^p27#aU3=tj-b>K1t6mFn$4^;kx;u#dT$Yn0bOpv?1|Nk$j%w$jioo@;@ZzTf* z=nQ7qeUu<`KxTl=0;LnsJ!>4y%V6mQq{n)}|NkBgwcxfm=nM&X9DwR&koq~w;Qaxh zu=il71Fz!&t;+|cNe>2F@E9X#{Q!ht3m!)V+q;mVqP`h)e--mGP#prZv$laj?T6xj zL-3gk_0ko;6B$;3`qRG|875dWGnjzNuwM@uI({oM%=isDGsKZ$0qD#T@OdNq88oaO zG89fpJQ!-hZQDlBdC+q~cX=>QX{cwAsZ~A^V11H-Sx=6E!C?}^ z9?-d%jr9yVp#8rNa~K1_`{NfxfbZR`-o>C$^_W2c)OV|Qyuk8#Xn zTmz0*P?^riPyniw^pjUO!|wVWG$X zJ~s=Lwqf;=!<_#%pmUBubFKQ#|NlA6``@BHmx0OgE+Y@f9T2;l!Sx5&y>}VDG}<%h zDAqABgZy9L;1N(i2OLH|^?ObPG|Xp^0oC30`(6ZC?`2>Hjm3K~@PX&>YHB};G|XoZ zsBUFY0JX3^`QH?%z2G?@h`o#kAa$Vd2DJg8>68hi z4s>St;+3GjdNp`H8x%gEumOb;IpG7c2O9PQ-v9r#fWix84=8Pe%JL2JAiE%Y@6qjg z&B!nToWB$qW`N^^KwLoTYCW*KEs_}+z5oAXsjZ2w@c#dAORX(9JOx1M$5NAlxl)-y zVWK^Qj0XcdxNit@C#VjsVR!{fCjyT5{zugBdkxa-19PtjLlvk=%i#V0?-_Vr1f`kz z$`zn;5Y(Olt+xV|ji5XUYO{JURKe1}0BC-)zMBD5j?DpuIp}QT`c;O8y^zi2j4~9zcSQ@CT2jyja?LYnb3|tQL z{+|JrC86hAo% z(^wBmNB;wUDE@Z@*s!80L@u}){=tyVxW5$ zLs}TjKzF;<-Ry5i$7}Pg-fWkDS{(wgS=zLa( zc?=xZ2N)PZYC&yT$GQI(=+9$da-7Q$P|eJsQ0>B^0NOXvlQU^}=G7JI><}fgX zG{ECEA*6vdD?FYB7Ox%K#zY{ykKOw0FGBihKARQ3?1M+;K(rHbt1!z*NqII zJJwdbKFF})^+Sdo7K#i9K>b2!T<-vx1v18Pc7gLCD8GQ}Y>-{xFl}TgfYp7lwEgQKL&2|y450iBNx!i4?8q=}!ao0T>_Xw}AJ)rqdPv;@j#E&W z}1Sfy&VOmka^*^L{uu%>ADO z=BM1Q0O#BJ4+1KyGxH_rC^YPY44%U%UYM!Goa$TpriR zYzP3QQBc^y(kQm@s#&mm8Yn%3!}%dY1t>p&;ue&4JQy6oZ6}cb9q0U?0wx_~?{Nhrk1&AU4AKLdhp)Ag2ymSDpTqhjs6P460I~~|&Oq*h*$Hwd$WDCj1i1_3 zCXgS!|9=AK5%2&11samt!T0TIure@Iw6G~SHZiDZurhEsG%%>ttYlu+!OXx=qr$-8 z+r*#%s?(ep{(q=RmZ|`mRl^{qQ^U+~p@M-uK?8DTxJC^p^Rk)+8UbK^3JMO$`atG7 zHZbg|ZPpU0>|s~2I8a|-)1h2Z$>E|?ajZe7lA}SV;ur&8B?ki^#9TI*xhgdb8V-&P z3<4DkUL2}e%AVlCz+eEfr-I?dArA%y6$@sDh8hMY9gw^R1A~BL0|SQz1A{;XySz%x za*Y6xyIz3&!r)Xuw}cs~zy8Hi4l8WR3;{1BYV+gN;Qp1BVmC|0^}h z2@H-6|5t#*#RNKkC?8OrX28*Tte^|6IaQt}9z+jmTw)+iCJ=jhb1_p~321d|cU{IY6iUW{6 zH7}W$^|Ji`Z?W|M|GC`%|2r}K|Kh>GU{b@Nq*Kwxs!);1s!+4c!@;42(Wj6$XWu zEesMbT^IyjvM_Lf{P%;KLF1=3gTl{l28o~E3<5v7A%1meWe5P70n!gL$KuF;22#za zVKCAGxyP~be~Gp#1G~da2APUwFAmk51cmMYpP;b)@%sNyP}t}%Wnch>r|&!lg`Ok- z|IaxM4znMiu(UY9z)|zuAOIAm9Y?@%@*f=Lr@?W;2)g&dgMmT9(gl>xAmOL8oPoii z?f(f_T!F$*X8{AFV;f_SjsgP{DBScHFfcpLV^~v}#-5=a(IFlbn`Fc^T+#1C!;j-T2L0zXcJ!%yKSH~8LO1Bb@H zTV86UaBx~yg0%j@Y01r;w1}%!iy(R{oD);KeZV+EL#{Eew=0y`033c@spcD z;m2!;88r+^I@(Ja^d0B^FY*5WH$_90f#0F=Z%f7UhfJWb0J+bSg`vS>>HiNeTNpSj zS{MW>Ub#N0Y;#knVQ|u!+0DRT@xo1^=45b1#RE5{ncNHvm0fO36SWywE87k+P2gr= zoaoKKx#2VeuVprafD^<24WM(rA^G56LB+C%OrW^3X!-xaGW-7rustpe94}iK7%W-- zH&i;fDa`a{;IHg-eNx#Dv9pHFyQ0$L5Yvp;3=EZ?ZcGzSGq6@RxG`>PbyYD zWUBQ<_0NkVQ2%r_KdCt00`|{_(+qr-9W4qqFM}&88(Ki_XQ(*d0`d>r1a1bV39lKr zp#Iq5{r`6lG`w!W!V4u{S{OK9vM?}!{88D^qOh@>fv=LS`AH=^$S#mSj4LX;T9`I~ z;)MZX$3$-iE|5J=4F7j{|NqmVy_7-RVeX#_ir3ET_<6SWyQCU!G0Oyp+ZnQ)qc#ryyNEg=04bAMfU z>B7KZafA_6p1uHu_sbRr0fae5IupGaSSs7V<}iTtfX(7&;IU|7keG0qL11DxgNS7| zg9OM-(7jW?6aU}%{qX;f-;Mtt{9gF~#qWdvKY->|L3fVTtkAA-oX5DMzF7fu*ThEYAFYVcGNlK_v%+ zLdA0i1xx$?KP>D2f3S@I{{pms`UM9AgZ2Oa4_+*1V5n?iPyp!%oh?%7#h~z#je!BQ zt_GBst?&JR@Pm_q;m2wQhM!yv3_l(-FgP*%pQAmGfgN<;q@~^eAEBBr{-L;`=l>7u z|Nnnfb}}ebzh+RdzW4vf3y>MyV7*VY=P@ul%w=e>wEO=jRP)0=6umtE|69EI|Igyg z|G$VA{xE|2%U{I)Kbq3Yh8?;-&{z|Y^{r?Blu1EJB+$Gl9Pes#Y+Z(AKc*jive2ygX)4a+O42; z_iu)!>i-{>4h#(FX|GmGNu)LlC5(SqdNFVyrN0L?EkPnR$w4CC|Nnv9_5kE3ko!P> z0{IQ*uUfTVBDHC^;OFju+-Px(K>^u~AUFT?W?%rj71B-tx$^|boi%O0L~5#yL>%V+ zeF1ha0~YscD2dc&pt+X=-MvmCHOWpQp#9#p_D1M#`e1R4L7>r-fuWI;fu-Rk14jcV z1J6Wn28IdT3_KIL8CWK~2DcGGZem7qQwS)XT7un#ng?q!-So$jgMkG!XFLIthe3M< zb5P5qzZXE`R-p=zIdAYiH{GE2AL|TITw5PzU<9Sp@Kfw{)=wB1^}85YKxx%t#sB}H zeC9ZhJwv~Xfz5Fq*9ZMB1}4XO>^7iu1RC=Jt*vpG%K*w(4?uAQiYv@``b1Vd)zle@ z)XX;$0mYZY+&>PK#FO{`e-W^J<2a8=2No6%^H>u=`KSKR?~2;F$rTRs*ymXE{QqZB z|NkE-Y(f&)>p)=x%5T;u{{LIR$-uCI8`L*v(6H3`|I0%D|1a1%A)s+)>l6Qf)v9<` zfac35a4|4w&ts4W&2fPC#zD)~2k2pBDf|D2#ozxQEbjh)VZE2QdI2;a3(5lSe*-&Y!@OWL$C`nqKKWsBjDcgJCj-MmP6nn0FBw=Ca5AuM^akgDwvF5jptdm!sQ%Y( zWncuAQ#EO~L>%UR2i1T7AZ7Otl=dH%viqMU2LltVOmSlP|G@kI&kZ2AI?iLhqujU-ok{LKaeQ1zB zko~oR8{#h{KY{9UMu)lI&RDAc{|Bkd(fop0m;bZ$LifuL6G(Z%=wZ?G|G!23|NkKO zgk(X>)Gh`V>l6R~gZu%hXIQR4>KV2_ka~t?0Vw|ruJZHO4@)fVYU1+FmnERRebuKK z6f9Q!|7(4if!X>A12ZiC;c`p=|FOQuz+(L$QLlUd|1klQE?AC$(}kRKg~PlrF_t<1 z|69oa|BqDe{D+o1?4WQ3l{+)I7#N`C4o3R0l>PtD;_v@I7I**u0{H=+m+k-mv#kIB z$1?u^FHl}y0Lsh%|NmHkB`+@o<>ki=3JV~4`NsxO9l4r;VIvm0$uD#8_@$W`zx-Z+(=T(Neu0!rD1Pz&|2v}sB>tFz5p+*a_yhKR)<+xQ?N$bU(EM6GqAvXaD$_vy0OW9~RRh(f zzmV%vqT8f3_3*ls*MW4Owh9H9lF~=Wd*p* zYhe(uNM;ZKjVFQH&&cg*a68%?Jca;kOM}WTkb4Ed?gjTl(Av&x@7%Wx(|FK~H{|80Qz5jneYG7?d z@BhDLEOY+7l=uH1Z{Ty8kac}iU~2|IWddx?fCobqc-w5AfYz6iP&4mRHj zQVUw!<-rgMp8qGL-sUX>gQfZe6;ND*;@x2$!wZ{U1_m1j21!em2@IAA3>*mzX}qd4f(NM;fmMCqqN6mUqR3)1Yx= z1_e-lu1#~iW}9u3U*E;RU~&3${DhOBKJWi0p!MxGYZ(|UH7Dp;x=&ECWSPKV@pOU) zXn#y&CPNLVzMOD^fnj1gXxw@N*j*rbkee)>C+IY0FfcT#GceRe#meG zG(TChO}hd#4_!BZafQ_a1_o;f1_scc5zzXFx|-P))(i}czZWu`nAphh0_<)^h7TSL z3?2>H3=G!QpgrT<;B||jwd0^Ud{7v8FmQwC>g)GPiGc3Tt&e&oqCXFOzc7pS90nHa zd*J(YUFsP=7(n*%?ttzw{(lEFW>wwIpwMW^z);`d0XlDp^Zp;v zpAD)r8Ef=JL2JMmZ7iz({|B{`L1E~@a38!@7L>L@Wd&#rYC{17_Ui!#M(YFyMr#KK>-q$Aw=tA}{0MSegCqlkz9<7jeJ99%h8R#A2IM}+ zSqwZN|IYSgU})%OU;yPm4~8W095TdBoZvNQAag)r1Cnp(1g&360!^Nm4g z8-va+2CW?dtsANVts?=SbIM?2%>Z5texRP2eamY_hKkpW307j`RK}G)RHcH|Q?% z{~@3}HlYd}ULGC{!r*m0pm_@K|Nko>@uCD?BM;T902*h3++)tb0oGdw*4xmK)V>6i zM;n^jSHQ{_4~7Zg{YRj(r6H+(4RYB6uTK^-RDjl-c`yir_rW$VYG2d9%)rp7%)ru^ z%)rs;%)m22mVslUEd$TQS_XlMu?!*&%nSmJ$_yfn$qW*W&I~dWWEmtT+A_#YtYuJ` z7|Wp2z|5e~sLY_!n9QKj=**xq0kZGVWTGvD&cs>SEyJ9NwG0a;#xg9y;?5NlZ5h@~tYz3RF_vKq z7I%W|J2KIhVb8=`h6CvCoFL0^Vxld>nTfRw7beOwT=8HC0q<$U;*J{=Z5i%NtYvsG zF_z&87I(auXv^?sVlBgmiLne{K=Fm_mWi>9EE8lIeoVAw_%pGVkpbimSiFJaaH1_E z&%|0rfr+w=A`@g8IXoC5z~_EgFfcGTG_~)UPz%1xD8QnYQDKHG!xZoT_rT?y_y3dN zvIVqf9kk!lqL#71`#+?-fwd<6V%_}%WmVfVc!g46K^PnB4MSeAjx0Ec^w2B5Wn z4)YlHSl?lAwg%Nb4jc^7IjTFM3h+H3YZ~JjI6^fS?0X^4-~sC6hiWF+)o_941}|6C zFhq&e#=Q~&6_6miHr6wwY_x~33+I`9pCMuLeFl!n_Zb90_Z?2Y&!98;K7+*M`wRv@ z>lp%m)`QlK2Y~kyg6e(HS~u_-J0k8TcbM}(#C}2j|Kz>kb(tOw^$Z*y3^%~*G9i9F zQ5V8sVOPV+Tmf1`>;3<~1*ksqV7LKp|AEVzdkh+&{Tm?l$x)#8;(rePRt6@}x<7|` z|4XbLI2gSD{{ycX^kDFhMOp_M0cvmA=7QT>43^mw!1vxx$OO-$f!jskJ%Sue%PbZ% zsDR3ZlX1<-!OiJG8w+K@X>K;;*x-hifu0&u;h)P4Yx9{z3o)yVMS z*FuILzYa1oG-fiefZ8>Uo(vpTjSK~ioD4ix3mMiloMaGbSjiyK@R9*^Zw1G1Mn-|A zmkbuooD86|#XFhh7}VYGVGafkm10Dg$zd~ zFJw3|xsl<_&u0z(669UW-> zICvfG-`^FWH0(H!;Z8#$!-VRk3O zAtT3YMn-|xii{Gk9T^o~Co*chZe%ogy^zu3^+865*AE#zEEE|7K=EJy@?QbS{k00e zDnR=-YMB2OBqTF{>R-^g0SutM%b@nR#nK6Dy#N2b0$$UWAze|^rCd?N&0YdJw;eQA z={S!epo4{hp@xOM1k^qU?UAc*i2#L*Nv&sag$HO|*(?T)9!>_v8V2?fP#ICr5CLkN zzUg6RU<8>3G6&Qqt<{hS0Ig#Lxeu}ijy<7fsa6GOeO*n8G-yw(ObsXVGO&HBkab~> zO$-H~HDMM9Kx>nfbZXd?bUFs3-5A#%xeKmVu`?o`Dg>uhoRyj{+LUuvB1l zsp0gn(C-BI#U9kgvX|)3Wzcn;$IzlbmqFWc9z#vVJO=PSE>K$$G`f|575LvI^860*!gs%e-&^?dJy7=dkj`aUO$*Pfs2|cf`b(g?2G(Z+ z#R0Z{lg(KM28&~$vgO|acw4%Wp`c<8g92zg22?hJ_LP9eX+ZnlKy7T$+9MAJ56B*# zdH?@g?`Lpu0PW2Hox{rD0p4E$Zu>Mc6oB^t;@F4SXwM)6+6NB0lL|Br1G?7+H2-9o z`TswtzE0#=z!<8qqpnud!6Jmg!%n}Gfw3}+K_P)N090;WXpm>n0JTLxeg}o+#Cisq z*{8t$hzighD2?_E9H2dQpu3s8|Nno2;Xly6m3rw4xc?4<`)vy#dsf`ReOS;L%+~uD zR1p4i2Q`I2YoHf^_Knp)X5Ru@PY>G11`cEB-cnF{Z?t1jfcx(OxD9gvwD;12A%W4- z^Z&ol%pG;L*$ft;njUug-3*Kt3=p^>2klx`c6+RuR69-w&-Q2y{>2m_B{G%zw0fWior=0JJQaUKIG zZ!>__FoDj)vruFJ@6QCec|syX!9+!d643fHcz8Wz=m5og^J4}UQ2GX?h5Gp#4xsb_ z+7k;(BcS@a2713MXze>_9aICjPj?x-mK;>}fzBlWt$zodLk&8sWuhGe3n-m{_U3`Y z4-}5*VF+p`gYq@Ftqk3B@p~ad2duqZGfTMwls7?c0J+6FnZd!jnZXCN4+CT_sNZU_ zfI$I#-;Mj_iplpGJUkd6?Xi$d4jX-DQ2XxxzgkW9EsnDo1Zrfx^gveE1Uf$-d0!Ezz2i8K;Y>p!!wyh9H8e8v zcrY-*_W(J}V`u^GMRJ_S5EK5I!=}-ZVMhZaBTubXlt}&k4*{V50BAqbL`6{hkdX(} zJ_Fq`2s-l!vn)b0os!f%D|wuJ1Aa3Yd)4R zGcbYJ4)gy1sNs|dsNv=-s7z*10Of}tivJlwaR;eW7(w^HG5l6!IPu$&;ll4kh8w>d z86Nyz$nfI#L52^%A2R#^jpKmw;s!>BDo_}J>XHqN3>7sql`BB)Z0kmb0&7Qx5>VaY zIFHe#X8(-I#n^y)B^A)rx_p#Uc4Fz^48$%>$Q^8Xyr9QF%vo0Q=MG~Hf!oe0{a3f}+v;Ppa= z7q1U8e0cqk;fIAHBe;KKy^x^>l!rj>0_CB4xZ7O7^$|G#G(+-_HZy}fC=Y?$S!?43 z-g~ov6L$XW{}#uY|97CdZ3Q;7LGJZnZ~^b*#BcupEugbxK;eiSt{^vi|Nrj;sn@`9 z?f{BAQ2n}4nSlWm&I>^8u{{ht20QC_a z=P-zX&Y1v}o1pXnN>ea4$Xrl50);)uT+q6Hkl7Y33``rC85k^*!RstPz}nJ~b-D~b zA*(rTYFVO0>YE)vd-iNV=78GkATw&6qeMXFSaSUTXX*9-UnM7l0%(6WC{97^w?J!0 zLGcH23rHVG56BH5J&yDKzku7%35pkp{mANT5#_&LL4N2`cKs3B=fR4L?(mH658z`(`XJ*tdX5ZrdF9gyCYS93l z9|qc6#Zi-@5daHA_?W38c--T_0%nF3P+kFzn}W`|0gaP_@+PPa4<0*pWM}}7r5qx(JL~U(#+&~acrcs>ez4xi#!(&}f!tkO8Hs7xNiBK>bNjT@OkpAp5=l|8un1%is*^KUK3Z zC?JVzHq2)b0L6d9eg++o+WNhZK;z+ppf)O~{|&n9y(29b%%3_25)86+kuGZ;)%W{~-r%n+&c8}M8LgbnJm7l6VMRBwRxRD$-i$ z4%Ggvxo%uhu^-gWV2o*KYJUKltF&0akkP%LfvI^ugAM3hm&M>Sj!uEw+ZIv`&Y-aX z(7lhAQQ&rQ%1?WS0MNM&v-dMFO`gwSGr69@Wnw&o0_ePm3GxgHpz~fp`{P09PfU4cpa|5S({ErXw^U$|0-bG?!N8DcnZO`c!@yt?!f>;$ zhJoQi4QGT!xB{C&4TE<@2q*hK(3l*k&*wOg(V<5UZdQ^$1Gv9z;xLQB1+=aLq&9(p zfmy#3yidshqz_%a!@U1jAZppRBrq^2K+OGrLw^RiuX+Zwr+LAA28QaRptNuUviCKC z-DE>O0}CjvINo7+0@e>rYYy|kcQsxBg)3-ZDk=LHEfN?sJQ$9^=RzjbGq^a;W0(Re zFFhE_VRIE1K<0wX2bm4p7YRyhg!a7FJ%FC;bBDoX(|_>Z5+9HmpgPX`|90@0vj@Xb z@EjB<-zP9IFoE*9_y7MU2z7tvdjJ2gg2eCjU^vUA29FwshyYN(uYQ4t!*9_3W6&HrXr5{=15f?R2nWYGjBgf#&Miw|U~j zZUpr$;r$`Bv%x^&1PVK7I0=Bl0~B7cuyUOD?*^z02iXmBLrs?gsJ=C+X#t&8l?@&n zhntbfU<0ZfYqh;AKy@Q1j5pddq`<=2ao)c~Lt9t_!F|AX8QH8%w`?*Ix@P=CFKnMnj>eyy^X2+04SFa@n*mqEV^+NpmLy z185&MsNH8#%ivOf>NBWM$T6Xwfyev*-v-dxZXOH=z-M%V(gvuWs>zbBm{8Ba;W+RA z1yHWn8O&eFrI-S z)B)1&a-7HDU?Iui3`z@@l?)o7x(Jj$C)P74fW~sbYVWY_^}D zf!ax+wFk8h9u}ZB8b}Rjd=nI(pte)3GI)FkeD^IV-GRnALF1aBHn0c70dO4vDpNt` zg4D=jtmVTdq8%8&A~C&3Fja>p#(ZN@TEON0H|IE z<&#=Xi-1~(hyYO7f$H&}@eC25@w58X7NB#Uet^OU6!xHfNT6`_{{L^w%X$Wn`jhNi zK<0ts0hC^A7%V_-0}n`CfXZ^P{yDIC1l@fMDqBI}QGbs~1k{cIrR5L?NSoMk-ro-v zI~klo=ktKl9H>3${r?Mi9Sx{02`YcUZJ-KJe}mDYW}ZR-NDZh>2~zL<|1-Fa3u-%p z+Kr$(2PzIeYXsbO-0?~T(tiBg0y>)}5j=PCNd&xJN}fRgR8E1<|CDDCfXD@jfW$y; z8t?!Aen7&KfhUoHfdy0-z|yeA0tO~X8cqYBfeb3!8{-)gKxGdotU>7+5#9j-wG2q% zEe{TFlls*GpuU?9DE))o<_T)wIH9-=5f^`+fYf>a|No(}o&if<__L&bwTFYl+&^;? z85kHrZuS2EX9A?n{O<(3j)sm`g2v)NV{hR4cp*at=xiELT>?H^=il#&>KCB1EFwVX zf7O7}fW-sQ{056%!t)7CwF(xXx|qpf9>b1M1(p>3h6KwOH) z^>ZCSavK)fGo&PXB``GBL&gn5H5qI`_JQ+YJ%hx?cm{@v@(c-}um{zD6Xh91K;<>K zjK*`uCCDDonXnD=3_PGSuN?pWuc>8ruJHc90o;}cmA{~}(_!BKEg*k*|NkFT>xr$5 z4~CZU|9cvh+FziQ@p23<^($UiOx_P33+4f}lOSdMF7WwRAUi;1JfwVQFaX(8!~94D zw6-(RlL1tx^MKMDs1BJ>51!KomGPjlq&Fb7pfQL>rS>`Rgbs@uy=BD-!)qfd4X9hKZ##TVB*n~I-dmOUL9!$2GF?IFwquR_C-7VZsITuhk1?d7sr`%h|Ht65{MtGxk=pnu5s*C~b0KvKLxA-w21bxO zL2d@Q)BFEp@S01I8$fOYxd)^Nn|mDE{+EE}6&;%xt~j*)-vO#$Ky6u2dl{65s%t^* zbpxyFD1rfY(VhGb+sLc$k z7r^B-wBL;F{&UB9{|)MY|E{Q&bgpn{`*#Q2=C1!F0t!>mxTp93N8ok@XpJK1j7yMz zJs9-BVGXhiWDnRr==cEGJ{zZ8kw3+>ZvG!Bhb{_YdT5*!mW@{m{9TMs~<~_n@*$zk`9HvyFirG=Bi)HZeGNfQ;AQW8BgS8gn}UTF=SQ5}E+3rx-*+9Q7spBM@~)6-!dLj|Y~bG*l-gJi!C$bL|N1Ica+i(?Fo`fdF0 z9q%&gfZP$v$zfBg$plWLpgl{W-YlRw9tOvIj6Agt>?WZ($0285&G{b@@{&O>)Pc>e zR-L)R`~QCrXdH2D_{YEia(itj*zXJ*?HSl==1K&B&NT(K^}uCpGyZdgA>qil1mq7; z{RdY9$n!uk0C|B3*5JU0ty=^ z2SyE$T*ynKOW`f)W zN+%#SAun0%Kx#I~Gq71S{Qn2C7jz~ySTD5S0@4f92l5Zdd|d7aopo>XmVv?I`Ghy1 z`!gDqI(RxV!F{R{(6|H0Zs@q}8yFun-vsI>faZ=sb4uWRmLOeWk;vG9JP-97I`8!B zAtQJ_(QidY3Gf)y9pj1#jEoI6pm8Z#<_Zr6LGaou&>RwIZ6|CV5tJUo6CTusFerf1 zG^67@h9mmmIA(mK-vA!dnWEpoz~VTMVa^Gr4uKnt3s17uEyl`b1SbOn$SvS?K1v-5NcDxkS}TZMPop!EkQ7#Th! zYHBe2QDB>6!QdcMsmXrKB9WnE0wcqYpNb3zJQ((Z&%n~3$H3??mthKco%8|*MyrPm zE36qAW>_mSTzJvQ&;dI4^Tk4j2_X3w2N`yN)@Q$X2-Xi?qX^#b!_1%n8aD;m53?I& zuLr|kaC!cskzvJ)g$x_2l^GOX9AudB;vvHV4~7HadbfVw3x)(>pKG({D z!3AU%$PAEvkRDLn)o`+Jah&)6#!p3t51@DhssFoxZO#cU1_qFRklw$&306)F|Bu`R zuRZziP}9b~1r#q9i3|@w@#MjD5RyL&!1vWAGD^VjtUbtR@arMNkKc@p450gLk@9B&B7gFN&l991 ze}d|G(7Hdg{26cnoM=LHVowGk-FH+gzYDhm=1nKymCi4<&y-IKjy9089RCn83)e z;in?Q4iAQ%;Q9$m{#;-UIuBTp;lPVVh6ZTH0CNMHI{8VJ`FDU?Mub3_74b7^n|g-vAoNXWpRS4(dBF=+rWR+pQq~ zDL~Bs{~u;P)UTkuzl@-e^ky&s zt<44b7i_i*Z0#ndSq}65?*O^oVFrVN1>^sO`j_lmY7@LfI=C4OK<)>(QRXs%!=nEA zZ}2{CkeOir%w_su$^QR8DBNlp*tdYftuvFs0Mxey-D?3l53A9YfdO=0od*MhjK#zM z3AO4>A|0Fz;JHnQc}zW^^V%&l{{IJ^vtFCPzQu{*|DB1h3=E($*Wv*K;|#|C6Li`b z7#!Oe|JWR`cwF=PUqQmLK1-1Owdw}bYBl)^YPCHALKP$|K%obC}DpBD_Js3gq6}hF2n>F=ddOLFv+Z;r|J>^OGw; z=URfwvJQ3z2J1NtjG*!h6h4+c3@jkGgZet4b1$467-B$n*RZ`3scdFYs9HXS=luuofdT37R0pj&WMB;8maqt6;IOIHWG?}&D+2im)aG|$_`e3cwiuFr zQbFxZ{RReC(0)tMy60Xk2F6--_7eSe&^#iehkiR~9WJAT#RCTIP;E9lkY7SGIcz{{ z39+SN$9W7Dp$rlhus)g%1M64Nm}qYn17iZiga4MC|1W^_gXWiU=r?hg`Tqb&JxDJ| zEl3|oEl3Y+Jrg32{=WiBcMx|nENEb5V9;*^-95x4GJzG`#}cS@fvnL6-CG4+Yrx~! z#$YgE76U^)sQ<(G|G;a-|0iBM{=Wb|GYoWg7}~jEpmUqO|Np-LT4M@If1tKdZ90=k zr#2{$Ffh*4W-tJqdsxfvTmedV-v9qj0p)e?|9=8NVG5e3ujOF4;Qjx9g2TVR72f~< zW>CKm!3w@dgpxMO38oGQP#Y4hjgl|`eFz_q1Z-2hE{l??Y?=pSO>_53w0M-wkb}kl2UV22Kl*Ha@OC z1P{DD2Wq2$;vUq-0L4FQAL1G4;pJGVeiA1#-xr2(EMVjGiYvr?7a^S(7g{8pgjnnd3Delc+hw&V($Yx zXt@moX#H>feE8mn3h3Sk&>jkH$9W7lkoG=+=D-ht=H&>@m4WQE#=7r;4YHRJb>G7T z@VP}#+VA4m^aJ&ti+3x@yyL3DNTv3{7z`ZeF%fi5b$LvIC`@j*j{{f5HAU{Cu{=Why4{CGPYI|3J z+^FBcpy4==!J>m3d`}&yuc7HM?|*|O7x=vM3XmR)1O^UJxO)Hp_XNa;vTuOeGobaE zAiW`+ynpl=7~)aGoXH4j-_RAuS^q)x>x>RA(0n1deOv)`e}u!le>*_uY9%ByFnRy~ zw*h2NLNWt0XbmXHozOS|#R14ao(}W=-LcGIU<94Lf+UaR-+y~RXLX~=BkP}oMZN^; zo)Cw5|7x(vS3>0@9p?RuvE&AY(Z3MTng%^K2GDvU!C#CF1-~2_8h$NgnDOf%!-C(5 z3@3gmGF1FZWa#+S$N)Oi0kjW(!*4}~9lsqJ4*YIpxbS-+!;Rku86Nz8$nc`(nsG(V zRON~q(4P1L>57_3$`unELE{HZ%YHC2g4Z&D+RPK!7#NzAIubzTSa&l6LwG0epT02pYH4cAUq!$GU+*TVI-i!EpwI2xy%I z*w3>WR2=3ql;}@lU;xd-fy6-cBvA&27mSPypuT%!Bg2nZ(Ea+a6rtxDI5IT6N@VDG z)yOd6)k20DuMRRSc=eEB#cM`}4X+g$cD#0EIPf}=;l%4kh6}G3GTeB5km14WhYT+) z6d68%#&sQLGPdZ?W)N_k$EZ{NgaNWI=yTIMgmD?sP=fcF#ae_he^s3WCf2ZI7=Pcg{-RtexRQ>c%V2mpm!y(fD~z47S^ zkX<#BH?P$zc!_khFfi2p+g;Hh$iPtdcy~oeCx=Zv>){I690#ON3RwfZB7|APAY3m6>d{X0;f$-bqw_D2Ay&D_DspaDwTwFwacp#GHO9EJwa z*_<8>^T2HjP#Fy`pC>Wg0QJ$W75@L9AjZG|iVMei|5_%01?`dfR|9fC=sq`)+btF_ z2*APzbS45Qd_ZC5{r^8hy#uJvaU!68CWC|Hoc}Xw7;aCiZFo?`0AK*>`9 z;PgRmo;m}nTR?dWoTor*kwAHB7Wn)W9{pM1JXN7T5j9Uu`~uEX0`X>Vk>SK^M+Wd7hu4h^ zSob$L%w+7)p9RiW2B7!_-HQZHBcF-PSD&Hz%Jp&uXpc?Pp^lu2?FYnZS)pkAFJpys48XiilIbZ$`f9R`IC z8Ss6;2B7lU>Hz~IXsn{ysiVN|z$DK)juQ;E*4`CK*Hk8h)=JdJUapuZ0nUR7HS!y# zf%eeVFo+e@a54mha7tLzDtH9caQ*<5C5)gru9H8(;4qItz`B}&r8=8I0d%$~=*+fG zEe44Y1_d*4dSLJp0f~d$1F|3Nra24@p!Fga3mC*|TG&gx|IY%iivaCWuaT7q0Hrg~ zm;%VmT8CGlJw6eR6aE)~%eRkCVAFKA^e%BsWQ3#)xUyThD###@+TNT{s7&hfgENIGyZR>k@rBv2^aP_L5~lR zzd>&3;9!sdxg%6l!3-2Wp!yUP4viHI43^yg|AXB>hk*eSA2Y!F^oVh%!@U17_}m3b zCyw(NJ*u-96ciO0*h94itm^-Ji8O%Q{t*TfxEL5fVOh)R5CC!m=xiW|xeNu>nUHx# zkbZ}G3<z)0MI%j{mG#8 zz#srp5878h`6>g0O~NEk(D_pJ5B?Q^&X-C^R+$VsUkY@#6lfhLG3QIw)JsgON&Y1Q zI%ldTS*)NYK?1a|{R8OCDNw%z)Qo-~~{xL9u?&1Ws!Jy|c zfcm&^^jkoEErur+4Gh|iNgd!a19VP5sLl`d8CM0Z8h^An`TqjQoO(8#=GZvQ{J#Ta7DzA1ERa5sdXO0) zy|tQ{_Ji)vvwFb52z9?f4V#ll{guxZHUF$DCP;wt?7s$3o&e=H@Be>tK<0z|@BRP3 zOwBQH`{@Fx%?6qO{3r55@!vzxn8ojfj2xhKpP)OWekC&O_|?d8;MYQi6Td*^?n8zf zzZn@G{B~sc@H>&=$L~f)hTjhvC4Mk68vIrS?YCqU0PUTv0qy^~Zd_6G&AP(-|6cG| z5vUzgzw32H{pQ!;y}_V8`c?}WN~{_gW>_;aRBT{msG9(xYw8#LhRzK#%m9@q)`|=T z;551S|Nron0#=}LBG6j1=9dft&7BM~)$Ky4E67{^lv1<*a5-v94qg2rsX(H7Xmrx`OhOeglKQ<2;6vnq}-Ipm|Qvo&wO>DOJlE6skHH z6hQL>9t`Wi?rvDfPy*_=gYLX=n9I;oKO1xh^8X0X*+`(ZM<73Y|DOsDs|H4flKKRY zznMUL#cDwNjX-;dDmeKcfX1Og=GH7_F9Ep;WG}Lv9Vfx}jQj_yImTWBvd@EI?MYA^ zz}(ExFT0+Tx#Wa^(XTmbEB^Zx($3-W$*&>r$e zH3kOI9`oOc4B&fGem62SfbQ{z?Li0ad9S%+Tme}R!}&dg*0twozsKkI{o!(GOn1@bVru{eO;CIfRDivegHLcasFwv9yv zbmu)G_c`8U{!^R4Zc^!aTnS{~MtcS!$2p84p$_mhc@hic8AL)j9rQvN4%vbBi*1l+ z5D9gFt;_pw0gMmRO-m` z2DW{m{V)^cL3?Tb?E!@es4Q-jXJDxBc;VnM=ieXez5oA%;uaJpp!h`&7l(QO-Ym3d z5D3*2-c?)Up$9sPI0U}mfLQ}%Kd2r7*$;{{(0RKc^`N$6sFv`qjrI%zwKV}yeb6)^ z4@(n_9L@V7@e8us`~N=~l(^>v-#yC%x-S=LedrIgcrF0V(}Mg}s}8y6p_3ClhYwoE zCJ~yzW(TVK>N^-1^w~k_j)4a}f6vaqV8O+}7{bY72l8*I_SIcAT+9`rn!JBd;s})I z5o=Rz9A^EWL#jSdT@5B6C@Be>IKxTmK_5S}?1GLuxly4m8F}?w%bx3~t z3%YX`>^ATJe_5b@hTd!c=L=}QH{<~LuGaq!^Zq;mg&Sy`8kDX;@c=RhR4;k||8oL# zw+cwC)>;aDr#VQCWdef$C{2UZfYSHIdIlj-`UcfkpnFeWH!?K5UI@M?e!}aA3^Obg z85V%rRTdBa{|BWVSa{cWc!2g?fbW$G%?5?_|No$H2Bo9=2Jl%|6omJ`Gmf+VpYi_x ze+H;52CZlF{=XG`ZXIYZ3TXcTB;EYp04mQxR;{E@33#eTI z@&o8>rADWY8c;h8w&pw}Tf-zILBgU@i-EzC3$)fQN~FG1f}w`NNu+|ALBU}zXrHz< zcugbvx^x|7@O<|&m|2cZ3<3?w3@#oF*5EaB=<3O|I}+3eKsO7I-Qc@n(bdD(us1Rk zG&gnBfY#7k$bioq?f{)_4qm?w+6ODkTw#5mp#n7LW&IztC)gT%E*Ymi~_-T3<gYl2 zk?EKKV?)*~Pt*amV?pYm{2Uk`w6>}Nw1;4>as}w_J5b&Mt;MRjXIud~i>^@?RL(LU zX<%bu=uBf^2g!rVR?r?sP-I>Q7c zU#qDEn%`monGIS`ztI!qZiW-}tr89na~Td;HZU+C)jKMnIUmrNI>9ALA9_>sXSgi~x6!hZ}V_01X%4tE%IKy{LSJE;A}paAkK$e$p0 zAp6nb-+u*=TS0CExfOI59Vp&G>Okf9L|O2e;0mC->Ol8oY^-Bo1Kn8%iZjsKe((SP zT|%|k?7aW~djWH!_y2zlp`I*uAUA@{1DP?=j)7r=90MCDenDoz%mBp&C=Nj9w1D)3 z`U{|azo7jJuyEMG0`{{4=&oIm8jxFIa$8tHdkPqMK;ZE!(b@((DiLFFwt92*!IL2(D$3*zt(w1%7E4{}-x zX+TO#EGTKo0+gOX>A_(h0|#gwRIRF!NUZ~V3CL`aIH;ZmrK6di;Bevqg&!zQ)i6kd z`?wDC7z&_k^7tL^GPHox3*5~NCHid)OpbRMax8lo81;L==_LW=50D=~b|Aam@g9Q< zcppm!Bu_ZZ`EL`_3Ejt{0^Y|0UjGN${{SizY9MFuSAfa`P?-aY3lD}C@cLTN{0^wR z0PX*(a!P>rHh|XZf%d3aCj4)()cpSobnd0&9fp)z z2S3o7#b2QIat%Xt1*nYxG7EIJ56BPR|Nq;BdM>uJVEF$4Bo4KM31kQ8JW!BdaQVag z|Gz1qHZo+a<^K%Oc|V|YY(Z|fI5`2lHw475Xkh`L;Q}gC!1Hx4C+2|G1bYAf^QBR# zV+O2zhS!A;89G37LZG@3dG9G`?<**sHQF=i)c^Zk0lGuoaSp>A>}NtSv{*VMAe{%n z$bmQyf}y9eo`J)9KWN`6xGi_0VLt;8XrC8o-)_xn4agY=OWC(ftY;9ISkIs{v7W)e zgFzd@D7|F?ksz%YRUe?Y_6$6r{p%Cz!Tq;AAbCrLCI*lluy6qFk0uniNc)NnKyh1**jEfX zD}%w}K>!n|d`H?-yaAM+K=nq#^92mGnidu{4AvEpGqo5pkoFjdfa4mx#~2*fb1WDZ zfWjHP_M_H;xdIfwAU}Zi^n=2k$T-`Bw3m1aX#KkP{}ON;4-{q~|0C@sF2Nn{@cqOc zjY=I0km64UJ^m(x&t&@V0*RkG@Y*eqU5NN$0PPWH0PQIT#SfM}#R8Cc`p*E{Ph69& zToIbUUI)sv4)YjJfbNWcslvcezY<)oC)g&`*C%Gz*H>n-^23VN~I)BN;aTbF?4>JQ}uL=XBK4@QUg9n4-y#H@Nc^(wD_|8fT zO@Q6~`hNlVY`? zrI5n4!q%a_{$&aSL!wuGeWe%su~e`6dJwxdA;6+GGq|FLAxNZ#!{djJ1~@D=ekL<8 zyi8%>v`A*)z^2Br>Hh=J+5phKxuCd%mFu9sK*eFmd4fpca06t2Bh-Em1_ptN$qWo2 ze_14h{1t%VuUbt8XgK`8fy15_Q2!M^hS|u_(A?Cq1T>~#gW zt=GW72&!AW|5t*>kaGoYFL2w zc7w`dSosZW&w={sp!S>vBg2Uw2N_OSru_d8_4kFJj0~W?UJrgcGQ5DEp$akKp3=bwJGCY~w$jAY@YZScyf%E_W5C;B#pgA*8dFVKgA*Ftfln7|wxy6G2|3QmF zE0!`OfW|mLd;P237!)=pg7$hH1(%@<6B!CX`a%;}>@qkRR2}F3-_Rionqy|TP{GXq zzyi&D@LHYy4+22vkvh(00kx^^8WkBiK>4ty7IYpZQ%rMG#|BWGS?^(BtgpXaF(HwW zV*?|jzywA{h6#)ee*W z!=M1#;}4ok2F=ZSFl+(OJ=MrV=8r=d1gt=F<)E_&K=YRR4Gdb2^O!y~CUtB9k7Fk? z@<7fV=dfwa2F=$+Va)r1&YbmNU$ick&y-DkA|d<9T2~42G56s!ryTo z6W9)r9?MwBIq{XA3<~<)3>h;R8HH-&gDW~UL3Ia{gMK$dnd3aB0{w1=9LIS~d-S^* zd_eAUoX2#fgOO24zkva?&hr9v4z~=nCJUrrzkva?h7)u~akAq)h7A1%hIGey3<>%T z3@MKD7$Wo=7*auYgT~o9_AoG5Br+VRS28Gb&5;~a(p&{}rTSet$W zL$brXzXedcQyk{~O@XWr`x^r?Z=oWifMo)EBghS)`V*8^Kx@b zkUb##JQ$e4=NW^-#$nF?8K8Ra1t@PMgYN2K)OhX4XaFjoUMDhIyl!N4c)gI(VaDc)9bUv>4|GxzwHpm>1Ui}7!499s4 z5A+)t${gnX1?`XXw3t}tqh>CBjk0$b1WDbQfgy^D?n$nRK_qUK+O2p(x}Mrp!x}eLPH|M zlWNd?8Jr9ZKO7k}K>fi6PX-ph1{TE^jtm;0ICY%&*9BD0PDo^U;{E^Eg$0a^0=1e9 z7ZMm4SU_&^gT74Gh7sbC|*FC1L9$8yOb-0-d`FTOSEKyLlnQ3s9f72DCP^UAY3Z z7Y8(#>o}LOpfRaqPpGEot{Tn=Jy1CgZdY@H>US0e1_mYp1_s6e`eF7HsCI$)FieU& zIMK`j>1FakH5(*`jzM;T#5hA37+5P97(jd&28n~jKr}LDbzoosna%P7VLnR+gbfmh zsb%u1d%#}OSkIsXY72nMU~s*l{Yb=un}HEje}T>qpY@UfG%u;+!LR~6-V9E^cNoBD ze1p!l2DQmxYeGTgFD2_jYmw}>dcdHHWb zuYu2cW$2V=2!f9Lr8v&xf!41)AiI*S6+q`2GJwo2vsM6~(P09rkF6EJ#j^LSkJ8yHF)=kXZmH!xH=&g0QgtYqL&jAUR@v}9lk&E~KP^%AfG&0kn% zGYD9EF$h>{G6*P0g4V!Bi7c#V2&k9y5~<(EBvKz2BvL(vL80z5dkJX%sqQ0tiIoHR z{2ov_qA)|0fdQm`QapnWs5}Mv!@8P*$vTRG#ae=a1LUX4_rY#YnS7tYX7YUoi^=yH zOh9)}InLua01dMppmu=cJkC3yvf6PT*8-6L9OrTE2~B{XL89HDGmD>TtH*!j&ry+fYKqj57Pizn^gclZwjQ= zaURzj(485eHu;2l1`|-a2IV;qhNYn9I0HzH!=FDJAZhc@8jzi!I~5w38FVZf7%D*L zpw+N3iGbVy>T`k9Sr+)LMU9`}bGjCQ*A#=~s+NGx^;!a6uLw0a0%~qZr!qs3rRM*6 zpmJNgfkDDyF3TQJ`UQ>GHOMn~fb0R;0XLr;EH5Bo2g@s5pgfi3IFBm;WEVUv)fpH-^)jf82j!O^@(keoV(q{n07_pF zvlx6pX~^0EDX%yn2RzDr1rz=dsH`@(FtfB%iRqf#;1mYz_`DPw?PbMPYEP{_|I?nm^#yaZ%f5@7hUmEaylK{yxpmlJw zzJmA5GlJwmXI4Y<6eJ%x%=`BNcK)#AJk~!@w^?IzoA>{J4?yXuqKrWSk*1hI9;n-OvAG?4xK5S@&3z-nbzv%q56XLv zbAC>-uKxcYvX=121yCG<%OeF?xH2yRh3jN{1|3k@3QjkB88{}(Gq`~K2`yvGFw+bR zsD4UyoX2tlG{zjlzyjJw2rAoLKz1O~35x~PKUmX=_y7L}ptyj>1E`PK0?Esapgqy) zj`J8nV=p<5^H`sN?sWu}Z=m=A)vvW_K_ayp-W8y<588_dDmNN97+64M2RJXpGw66Q zEC8fm87f|^KTBqs3Fc&;m1vR4w zYDPz6QpW+@?L@|k|NnpMpkoFG#t@Jg0|O(597r65@yUVYVHo80>T3)NmYNLwp_&Y4 zp!?cEX$<6lO9cjj8it5-HDaFUy#N2!AztnG3+QSsI2i;$ZLtaLkURiN7c~qX=RkIV z;tFI3XiN-LUOLQUSYoXJ+MDtH1;`x2Zu_$aG!ELp%-{j418TFPL3;(ZfX-Z+n8Lu& zsKvnY1AG_nHwjR@!_{d)+dC=@42@X~ETH=_y#Igwf=>-7O?dzRdI7XY2V9$z{~YIh6VRW{!0$NkYXB&3Oy19+6N<=xF|$n> z7$$?-In|*3l>a+G>w!RhK~R0<{r`IeqOHIf04noAZ3R$%1)bf1Fz43V=P`iVb6KD>A0h2F22fwN3fi{>^|L{J%N%IjF*wZoJEoFKTkl*0IX%2_y3<)Kx>>q zXFT1I2i5n#Hz54=TL6!nzJTlmjd6kO^Zx%$CsctIR0lIS&SQ81wbKe)IqLoY#~p-! z7+%2q0}0n(KR|H+KFn(enwMjNxc~E&4tWN4C>wkik@x>z@O=&5|G(yd@-Aph z5>&>+>R-^_cTjoO=nZabg51rp(UGBG10zF`2g3}cvi;*1NV)v+Lqk1-i}(MpGKhS` zFbC8onk@>+XWsw+sDRQYsGU~Bzz&ViZz-Vi3T7|J&X?JswD@U915?M5hNO-optisF z{~qu;Yu^9AfZMIe>R*7{s$X}2%tpl7H=YIZ3~UfTf4TydD}nI8BDGl75f$O-y0nfQnl*Ff*t$Dl=$+ z#GzsepgWi+urN4)#z$fEH?Z|MmI=&GE({Dzps^ZI{Qz3m?{JSn1~l&i>i2`zZ6MFT zcrYY^&kmgY7_`<0eDB^39ToWc!@`8&Ls6=MGxI>rd}YubJ`XGtm^DCiI3Rn#VO1v$-a8KJ2Rh6Hm3x1eg63UnnAx|y z0PRUx$gtz}LCAiU*AE#^SST`F0L8lpLjt(I1m*w9he7%7|Aill{|VWxNx*JRBJBpZ zm(lD-Sr=xRz-$llBk}Q*AV@^~IFM#$Jh;Dv7C$KN1g-h8Okg(AZUCQizs2D$g8|46 zaNN{OS3uLT1t|YO(=pn*taxylij-!xplSAk2SYsgoE=bF2Dt%8I@ST%M|?QNfzKBL znTIDGYmsOs_?{C2X6lh>Ciu*4G&2jqc>_AX3krLSgA4_*u(wQLHUiC4qsM{6JqDYX ziVOvywJo4CVqoi!Kx3DXybuc>FT@@PvEVbC@Wlbh9hM2qDlm6A%w-S(`4yH19PTkF zAmt0tx_h)V5DT8q1m}mx;JgSbTfly`RAjgSDmz0O*or`RXo1=uKNdpQN4KzV0j&W7 z&2d^LFe`%kV-^b;z-K9f%2{!Vx> z#DLR3lDkfT_H%>Qc>hrRm-zc3e-j%SSAgQ@2P5MK$3u)34ap1w zpmUZj6ByD!;h_gwpFfF_1+*`s(H?XUfd}Z0x;>zC=Peo;DnM=r)iIU{%xWO@;P9)H z2JPhr-CjT_+9o;Bit$%LImuiS`T}AiG{LGHw8sp^c4T>;R>W z*9RFVfcCGxe#kh(LXi=?Pr`#CdL_7=In2NSHYb501Ee3c?-67MNI%FNkY2bs4;g2G z%t0~(T>gR58%SRQ0|PA2*LW~QgU{(gl7rP@3E=SHU;?e{FZ{ib0bH+w)(3*p2Pphu zVGRlk$9oJ1K;aK^8%UpJ0<*fqJ%$OOum*)OOdK>10}2O+c?=;S|A53T6Btrq?1p;K zne`E^AU}ir<-rgEo|6TYYmoc|zO$tQv=$E>X3%;XlCPq`>q|i9gX$QVnV`Jn{r~?F z4~8gk{Ri?ZNDL_)R(LQ(f%89-94s7qeklG^1f{dziHtjbH!>dhy^!(5?}LmNem`Wq z@q>}^189GsMI%FrWdgG*$Uh+egTlvQ9z%z9Bg2Y@M1~S;(E6BU_AQ`wHuXziSD>W1 zdT^TadBMm~0U84Yr905Mp&ks3IUq3*AGA*sWDZO}C`{4K0PPI{-D3n&2MT|X8jyQD z7$U*(59$|#%C{Gcj34TgZ&$o-WIXUXk#R>2=zi{nj3-_nWW4bDA>$1TMaBo9@T|TE zo@)g8OC4qw$d4es7L5!AAhTh91?{nPnD>8%OdTUnNHT+!2SXUR9SvH4Vvzva7aj(_ zhXA~WV;;j5kUr4rOk|ibQIVl%1|!1)P=2&%WB}a*m;!Piv3>=G zJ817AXfKTiLl}5(P(va^0cto#fXCrLX#^COpt)2?-T7|=#6SNpfb92R2nDYz0^NE2 zL-9YjT!pnsL1i~a9n!+S1y?-+ns0`uduUn*#TO`kJQ%{k^JO6Sprz*v9t`2&^Bq9q zpg6}-my_7$2?zThWFDTnoWwRy7(g#mSSBz| z0;LVGJ+;ym)&D_vF*2BVFffA3?}C|H44`=jF3`FH^Z)Otp#LhmC1 z#V@uyNkHc=YlH6EyUQ>IlpjH3mlaDH6f6!hfXWjkxcflm$vuV(&5aB-AoIay6LAlT z2SX^h{b$_(zGwcx{{LSCz7}#J z_+Fh2pzs0h-v)(I&0g&ahq(;kb4?uYF)Y9mUdkZ%z|w#NsQy|A4zCS0i@|kF2WY<{ zC@x@Y9U*CqVU2z>X#R)60TlkAwc?<)7@#~2^CQ?y(B1P(UspK(`@g}1AsD=d3A8p2 z>h?KMy`cL~g8gQOP3cCW3`7m=m7=pq62ZayFETpjW@n8rBujc`YgW}Em z|9=Y)hG1|T1W7$CJY^c0I&N4hF#H9TrL~&O%Pb-HDl&rZbp)AVae{>b)YhA*!N4*x zgMniL2Mfmp4hDt^94sKrQmf@8Qk!LjJRZg}VI>34gx3r_&IfHQel59Op8KG%9u60JQ z8U_nX4+f@M&{}dY1}lp<{|{`;U|_J+VqmFEXnqo^#$W+j%UIuL;ZVWJehhTpOAV)Y zh2vZXmzopC6&A-B9O~N*9DZsrSp3vrF!-s#pz%|KLE)zcgTzk_27#X%3>-f}>!RoU zzXDpjY{ACB@e_1b0vmHheUE{IrkeIPR(<}#?DtA(j^n8$Djv?mNy?t{)h2elh3Rx&7n?&$%oqXzBQ2ASbFk3q(R zfnfni9$5}_7c^)ejztRtBWRBfsICCH8Dt*lOmWb?ZlMbB_3bOb>)Ro2{sTG_-C-U> z4roocMGFH<#mWzhv|AaNLH2>%jII`@4s=Hk%*_t-{)c$~|K9_0SA7TQOfATr0+|UiA9TOc3wZ_(%S;AF zP+zxx0cgJfgM$al;zE!fP#FlihZCgt1?b*HO$Npv@(diHdvd_*$w7O}2-KGbl}(_&0q9523j8oD(_%x1T7dCm?nVd82%ps`5P1tpl}0?ErY_q zgJlsXD9oUCg6{l7^RG9!-DZ)@c%xC7fuX)r0dyy!2xzRihLcI8z5&b!?E~ikn=$YI z9Z-0J!pee$fy2^)fiZ-E-3~Nf3-XI)0=Q0C6tAv#L#I-vUzKz|0^}-^lFJJ&m;tEH#;+`zsj? zEMyrtKyCrK!(kqS3uxb4eZvQ^e?k)&tQzVWctCfRgYPe8U|@ugXW+OaSHGKq-C^GU z8!&Sm=KkN~{r~?7kiCxc{vT-IWMFC31dXpViPYM81Xy(bzfj*I0h(K10=oAX z-28nBa)0w41{P4?IeYa&M9kC8jImuK}t7#ArssO$x!61AAyA>#$9p*78 zSjeEnzX0f*H0YTVCgA;+kg;vZzIsqQ2Gn1M-tz_9mjWs;Kyw1&3n1=tocI4keFxYb z0wE6|{&1Z4e@mkc0|#iYL45;wPyLcc8wQ>RP6m$p156d@?nI7nP}>s}#}+LAe}MMu zgW?f1hf=doy8?9nD=2S)>R?dZHos&5oegpm6qex+*sVZ$3$$M<`~ia%Xp9cDr`%x{ zgGN2W2hjd>1&etM0**5nBtUrOhW`$8|DOq8`J)aL{+5{x?4a_+QWJEJMT9}Er-uP(E~>s0l&AkM(Vxp8 z51KQ#kOiIF@}mxv2f%u;sCStAzoep?L7_hWas{Yg>A}G80d%LF!@d78pfk@M@BI$} z#YF=rgFt=54^X<3Y2XBr&!T|i=P~>N znGITV?*0FlFVy^$26+aV`i2(_4)^{=G}<$;geovtfz$K7e;%-PK@Ru++0@$oLMgw% z`R(366_6dEwl*Xmfc*bY05m=e&Ib)(J_G2!Td?|h|E7S-Q84@7-xr{~4q`k0`yb=| z|L+Y;&;JaeUhNjOngIqDoF5oM7}~39H5m+Qy_iHSGY>F?X1DLD)r>Hx^@;&DOc2_OIL(&Fj$3hzWArz$sq4ImmvXM*E9GSfXoBC5p?eoGlL3f zy)5Ydllm4=8OvA$N-vF?3>@{~J(dhI^=%#upmNr69%DkIHbfk>uMkuggYp2V4h-cy zWCyD6KoD2+(O`tOZ1+4VD800NA85Kb1xq;6^Q}+^S z)MVfR)v2KlKk5_~Fi3*Tah&&m0mxoh`Ci}P07}1mYPmEFJQx^6(8QO7ddgRYdU4o* z)*yk+)%MT>nFBJzVc!2Mp!=FZ`PtHeLEbi@fx&U^{{&F{fZRw*9D)3Y98a|jhU~f+f#ORsfk6ONj)TJ~mEEppfp$d=3zJAAQ^%89RS)nz-4m4=coIQ()F^m~Okie^ z0Ii#{U|=wq2wJ}Ys@F5Xdp%`9ac055pkR^ApaLqFL2a+l1PKdJ->8O}Nd%MzCU7!v zOyFeT0rfjT>OuL-g8BaskeuT@1{KiW8c==$grV(dg6xTK~dss}K<}frCLLQ8OZ^lwJj}@84rN$1GP~>bq~y550>eO_WwNw32@syfq?J;#qz2B{&=k+nDG(YP2fYeqN0j1Ly+zbMs@pOlI3=tO9 zj0Zq&_h6ZJ7tz*bYIwoO@Bws|DCitfQ2YMXLxvfz85tJ5R%BT5+L2+y>qLefuNxT- zyk5v~;`Kp>3!pPZLFa~o;-vXd#}m*xk(YK15|)|_3ZQ!)9Op1dyx?Y#0NLv}?|%;{ z-hRk2NYt%ydIsumSsh?t@L-v?6Xb@5y9^Sbb4fsFeK^izIAO7y@kXO915eXk#xfcq*Oi1rI8eJC(62!wF5+tqqHiPU#FfbNbjX<%hwsd>R9 zQr{i`s`FFo+d%huF~n4QGAJ}=GO$3}te|s_7(D9RLHCt2IDpC}(A^23G!9eaIFCUi zG=Y6jXeI-!{lrn*nGgWlw^-ld;Q(6S?J)QMfrh0F93c0C&Z=#UXJB~!51bYXYQ27m zSnp?0uvo>w0;-EFjxw--$1tBUuv8plP_SfYU;&jEmKh8z;PW-K8CXE|mPIE63&+^w7#J*LA$NE<&SlU5?bQO+XBAxx3Ly8q%w%AA!O0*1s)IpeFQ9YVK<*{N z`~nXKJMb7FylvIU&`?pspz!-3!vxUSCTRW))QpdGG)KB|v3=qf*C{hD=Z#{#yYL+l34rpt(zM zxPr!O7rzFTH3i^s1)T+YkYNI7?=+|%2?^uhyr3`!g`Kqn0|U71Tgsqdaex6FcJ+&2 zgWHIp@f}bcfZDjAG8oj(ZQjqgr8%B)L-TybHQxV!bArMIBtCf`1IOez28PM=!1r!% z0M+@#oC^n<7YD7M`R&NCprVRF;kP2g4A5QVpmV2xH8K?ZTF6lG>mWnJuZIjBzZn@O z{7z(8@w<^>18AMk?}H2nem`V50ltT03h1r?h8v)A7<6}|_y1p-AU`_HV?NRJlYs%$ z=Czh&P_csCy)6Kmv(VoInlE8Y2=A1zs5|h$pniv!NZkVli|W9~i*#tqDpyj3Usu+EIO_66`X`{Xckr6Dx)#lITvPa^f5 zHyA*3DIm3=yVOAUSA)(1sRNx`#mS&hBgb6fIEg_7)TafvXB#3wXQVrT+PL}+44|}py4>_zYUt*u^>5^-H!AAcYxv;WFKb!_~{F3M}XEkf!Y#{jSM$lH8K={ z(&?*(&^tj6GBmt;$k6eckzvAXMTQx#9T^t9PGnf|x{(2tuEFc0kk>|m!o%_2{|T_L zb(r^Wh4oKH9nhSl^#bsoDFOX?4DyiqU51!OO9qzu1s@C==7Y}41)VqNVFBtNg7PaU z+(BWE2yZ41koll-0+4?|`OW+PCr^-n9p?R)ftl+t@Bf_1_UNp>PkN}zK zIFIoPOt1IYnBOtrH|9`m(RllV{ zk^!_|26QfMi}gzC>JfMD*btZ#><2|N3jWv+`1ll_cYLh|p4G*|3V_~z9GL4C9jp!Ti=$gR~!KxM-hRgilf@BL!|g#&1e6SUtPlCGeA z__x@f0rJwMX3|1h&fYV{Mmq<;+A(8rS1&5j$$`y`x8DAjWr~o>f z_Xfm`3ZQT~Q_%_WAH=PUd+P6h29IIE#z7qC{r5q&`!lr9_csLO2S`48`w-+F$AAAz zy#N2T0hK4-|Nnya>oPdZ`@08pPH6K@#x>1588_6=d*R?X_pe05PX-ClJ(QsPo-B4k zWj|%{R2E+GWYif(7E6s_1^zK{)MRheaHL%pBtcZ9-LPnfY(x$G|yq+0Htk*e}8pA zVO>A_gM;JW-y6LD|LXzeDR5b<_}>szc4IB86&Yq=E3ZN2GpL>GIFIo|MFoQbs4Y;> ze7M5<{})I)n8$d-`q%#-4gVMf>f3)ffcDfo&Sl(F{fa>Ww1=WH;r}B@zF}Me8vARI zW84y2!(auMl^* z22_86&Nl?rVIX_))nnfOKbe5S4-}4|Hg{+OgH3Wb1LFkHeywUqIC}s8x2JyPkAV92 z7Y>eh|En~}F^GWA(QkOc;5d)%N~qrvD5a^>ab#gyT>B z+#R6(v=6}LtUWBffXCTD>4gE3UJAi$E9<9&^+bTu3uxTCemW=~83Z7CV~2y|+>8e!K9ZA9FtBn@Oc0K^CY|zbvy=i zwh8Lk2SWsC?1PeVkNO7GaSza#$HZs`nTCH1ETNtPRy7Ym=Yo9_(P03um13~uWYn1; z%^)yAnnA>&iQxz69L<^s(G?Tq7+5C1WMG*5n}K7ZG=s$C-ylEy{R3LJ(CEs*)6mKw zQ9l<{Mzii{U}fN_Z)b3Dy!*EWR2GBoznkdEz%zlHfdf=_fzCH?Ol6P>%>><_@JYl{ zlTim0-;i-ma6ER@w=013yMXF5$has-oLyj|CxZy+jC;_Xkf4631=ZJcAo72nfaF2(GJ%^xWTGd7g!lj7J|I7X z#=k&u*a#XEo5#Qd9`j;gkgV?lohit`0~*5uok3Q~&7go7;{weeu&RK@xIp7uH5pza z71>~U&{`nS+{_tJ{;Y41aB#f)Cu8z!^zi(%2ILm+|G!Q&dNA-btYDC+pA8OEg$51= z4p16{x?2b2Zjis>;o1sHhreqYD{+M@s7z$7fS94-;CT1<3(y=lBy7QQUl3vY+XND} zzmI_IXpn@4?P}z({k;O@26)&?!ov2~4~QFocR=N#;ri{xy$YP~=CSN)n8)}4oZjxSfX^MYPGDdJ-Ma<4zZz7A z)-nWx)AfXv3=9(%GjL2yW{{Y$n1Kh}Zo9`)Q`4?o0csCH(;!b{CIbU#T(*&ufu;E+ z189Fa=zO~?p!O!He!ysRa(*6UTAq1)KG(h^N)OTBe)Ky@tWyj0M6V4$+l zf`LKamH{*l%n(9qSqSRGz{lwqAj(1T+;Rqk!31UonZ`HtI5g?BDLHB7PXq6 zL_qcKWMu{c%S=Wc$ELqGKxsw4fk7TLb`C2KLKuG3DLODn)~^ZxjdSBD4?txW3lFS3 zsMYWSj~{~W+5&|aXiNtZ-X0E)cYj1oSPe~w%#d{G{r_(TQrl^teggxu!@S=QYHc5h zRIg%yoO2JYBPY)Tt*?UAkrT5(ZPOnK4RQ<|pmy8je+(j^b17r!}+O+5j%Pa;FOD+ZhP?&(q zYp6e-fXYl5A2fbg%k2z0-|h@(jgaHr?==-C85AIP{Q}+fE70f-YNI$fIL`f^0Ls^( zy3=tk0|zJ#fDVBPVPFH_rQvw*y921r4-%jJnt@}2G=s#%Xh^$fqBN)u`d(qd#UNsF z3f=9D8DzTM1m<>77)%78mHcyt1;hV;jdlzypz=9}A;PX!lfh!59Vnc?g6dKMh(Etg zLGot-C{Dcpf4c!Q2Xx0FD89Y_|5^eyj{#!d37B~d-v7Ux0j*n`Ajcr%{r}q>m^x4! zGlpRYlDe;;{3zi4|Hl{5*zcr!3>=g0fzEUNb)zAv;}58;^#M#0e_7Lz)CoG5{)Y!c26&Bz_x~>oKz;+I z1CW0l=KcKvN;jxt{}y=v|5*VVCj=21`4}8=!fP`eqMM+nfh1H|LiOXuJ+oPB_f_ z|HAwKFCL4d3?de+3<9WbVG!_OV0Z#@FFENJ6i<*i{c-?gE;Q}#fyM_5Y8v>$0typQ zS^?d~1=8#N|JMW5c=@dW5(lUIc2K(ik^w41ai{w)9-wqTVKoECWMu}4iOvihptX?` znL%m(X96_tNS^ypI6a4j%VMRqb zg97Mo-I^-t3ebJ6;CpxhvxQ>+jC|KD(nfunw&fkVw{;|hnlEG3On3_SH6p!syR zBh8OGo;1iaDAa@RH&DB^hJoRSr4@q; zC@t4>Gl_)Dg8FZ-M5<>pC^X+>;HbXMpwJ)<>-&Sse+CIq-fBqd6tQRlrO8zAUX=Pd z9~hc{gU*ZLYysWF;5d&n1&2P+e1L!lLn?T0N&Os9Uxl@%K?>Ao{o&wvmsvz!o3g|DQB^|NmFgSOtpP2nUC` zOmiT4oOOy-0s~_WXMjOnLV$tQEKrzn*wiL4TmY>r1(lz*nG6?dH6tuQc|TNP*|%Cv z7K2dj`xdpDF$SRX)IznsSJi5U7=YXos{MZtOuSO-|09qXNUm;ymq^$i35)tIk3_0> zFeo%zFmO~`BgX-#O%9I(6-XQ;gU@2BpL+rt2fR<}XM)0&_Xr{mQ1lr<^?}!x*Uts1 zWq*Q*1I`)XIQYT9F?l5ePyN0J0rfLKi6oz5U zLB>i!ZJqFcFa9-GF^Fi-1NB`wGqmS3@P{9eu&Dn68XMqr0F@JsR-p9w0cmW2lLtID z@alI(^$P}t2~rH;J19Zr#^ekJj)~q39FsE{cqYGQ;IRg!k6jELlV^eIh`)atZZWV} zh%pF*<_SaO7|a~!F)M)9+0_4IxByD)ptcUE&UKi_oMBzXr~?|G0*#q~(iNy344Rj6 zoX2JW>nDNgd{`f;HZ!`S{y2jHNDpYADy)PDhwhtLBS44|`$zyk9p!;pzSVrwh3Lw_;$3VK@M3i&=rjYk&1LS}{n}$~gpp z@_hZuPa>7-3<{vRtV9L|#@dujDaU@Be>n8l=GMVi_Fg{phGwV7Opy#i&!m=>aK! zwL#@?j6tXZsQhIy0NpDHD}QUVgG53VK;6rOVV+<@EgZj=)A5haV^BGV&0rhL) zeP>uYmI0l!EMQe5iJp#`FF@P{O2^DSsOgwP1*PBI9pK=2mpP--gF&GImX1##r(uT+wW+oEB~r}=Da(=%Ffb>-W?-Bk z!@vUS>w(7anrA`g6+mN+;j>@-Yqnw#(Vh*ebJ(|l$E2+oRKgQLZ5mL$!w%Xf$q1=; zZXne=>=EF4r|WS=bqBQG(PmHp*E<@}dPkFiXL2_K&*WbW9FwI$b4h2w?~0lO3>KhqN5`gLUqI=RdTH?%BrSr^X8AP(RGxy`ztDD=45TiP2A|jE{r_7J zR9pfo4&Gbl{r_7I$lrAdD?sk80M)JD|9@zB|G#w?v>)4olfe*l7H$p0Cy^Q%4~zOa zpmnfceH!f=d=8F(hLgZd4>4J`gLh*-!n2!QV2ZG`soL3dAk zFt9@UO6f%R@hd=ff$o$8j~)Dl_P;^vz`zW#N@Yn%(y*+5$q!whh2ZM-Z27>@7 zt-!_$9OnIh0CF!9{|Y2OSvWY}Wv~Ic71ICuxdBOB1tI|W#GMSTsVKO@d z$3%7piOJwJ@l&QzspAFceiG1G29^noF`#(JVPFub;rv%ntHM-K)8Y|O)AFyNriFb= zMGNZ^ixw6J(7s1K@Yx7|b(%qE@z4JM-*Mh2p9U$$HH}u#bl(Z?KdgcFA2u}aVvvBe zPdIukelgwv-OcDYi@~Fjoq?g^6@x;cmRs~`i2|extGaR3<8d`7))woZi#^IhJ)_0lJH<){!sA}az8mp%yAy` zgNEk}9H4n?&{zVf{{ZUa)U$x*L(k@d+Q5y;3@o5Ex%CgCL@e$9pR}z1e=t;&9efWe zsLiXu$siEI#co%x9R$u-^$jP$Zm8e+x}wpFaf{VX295?P#vPWL3=W`jV4@YIKQ+;c zafij8{}({^f%>=ADWJ6jS2u<&U)N^?xlT8B{>+ z2T*^&;s68tL{D%VpJ8$*0}E&!=%ihY4?yv4$-%&4@tlFNzT<}jsNL!~=l>i1c2N3c z)2W{cYF{xlfX*wc`FkQDUV_HC8Eio9)`=AiERJ(oWwhHF z_(A1Qc!PvR{Y!A&06M1|S~omEt{WhIlGnd0Kx2>avS<|p$HWRq|7h|L1|E>V8)kv# zTh8>t#sM7We&9iFN7cOiBvPrypa8mu9K9Vi;TKXn$_>tP@Ka0G4-!NZD3q;J)k}yBmS{na338s zw)?wcGBiwoE9fOU2;*|3?N? z+yN>MK3mHB{|_5bJpeWbJ`eo!i}(Lu;Px-5Oh%OZza+f>e+QSNAismkIEQ(^61@L^ z@qo5-Sz;IhVC`HM=-A}HMk@xE7zPI<@$caA2=Lfe188jPR|ozv-Cr4?@*h-YP3C6c zn83~;G0`2lp8C?!z{S7=8e@TsCHzqV-4EZF15a}u14sS* z6QH$iu)bad#Oxq&{Z~K#2C`fXR1SO=1!zr6_&(U2Ib_`v)a?mSb>O|_^|L{JNmd&R zDbPBLPa^fR!D}i6KxqKkC``^| z;Fzqe+e#dG*I=yiW~fWgEF1{u&84tNg#0B8>X66zej z1cSf?2~hpd5CSUWY7Rt$+OXhpr^!1QI3`LkNKD=VY7_pdfW~2lMGHfK2SWg~{rj^8 zDxL!s2k%w){{Qm`$UIn?*>DonCPK`Q*C3afY|Is)I~76u2*CZn1`E&_g9Cg%ObyF? z7`)8<<}!IT0|Usd6K=rV`tuAZy)*_A z7$jhI(1aTxzyGWO)uWSVG2WRV#rOoYUlCL*B{W_F;4<0T2442%;%<0Z2oYXCrIdH+9>=>7kt1IP{V^!E}q{nc}U>ue2p z8dFD2V~+EfB*1Go*qJLTULeLL>l+S0)@MjD2td*oXiom~0sQIf^8!%W08U>W3>*{W z86;q3#DxE_@OUu+RF0sg1JF9Y&neJ!kOCR1Nv|G!xp z;~01v?lDNz&j*cLFgyXx?}GXwknye0;PEfaaf@GP8tY(l{+H0!+JNd1h7O1sps|Zj z7Z76?V7U~;*u}4w`fkwJ#iupku?soq7#zyj#it1|?@dIj~JKp_xW^y+qT~GLroUWgpfW`}A z?#~L;?);epaxY|j^5X=MTOobbe=VT3CpD0>&jNoRWLWSUbOsS4L&0xF2GAY>%(IB* zD_4N_;yTV@XwYt95OAE!U;{dP2eiNFH6ueo#S#XE*NO}k&~CTcP; zg4QB|^n%)MpmTF-7_=)uYZF1|kAvm47+5NfgZF|9fYuo`x-+nZdV|)ZMu65ZJJi}- z5~;0;2ncmx|5xi7B?6ik0l5QY7HIyUzJa;IS`*wJ2A!9~0vbC7k41Aaur#p4<`f@* z_F{Ed?_%IU4i|@c3^AZJnV>c3pn3=tp0GJn@Bb(6>dyd&oj^4Ug91p-`~T&=ko!j% z1VH?16;S{4^2+)JuPeO&|6{R!z@T8M@PC2#|G#gb2WZc4bsK|1eZvEXMm7ePP$>qh z+L%ispmudA17s~0sLfwLC&0n+4g-$|11G2(zyRu-fcnCqcm|gr_x`^CrEQA^3<3^w z8F;|ynw7zxBUAE>OB-6ad8m$StsVs9p}<+r#Aj|9GTD4Fg9-Jt&<2 ztAO_QK*k^#YCz-IAbr($A!@<#*Z#NyWDe*YC-48qWfANLwje)(%yFFe?~aAk|3CFd zKUdVxV{ib8Im~6?s9|NU0G;s%Hs>*DUkr~ZC~m-Jv^=ix{(teH_y2zjK>J^6eldyE zw^}$j%=uqa8>1x>%E0~)RKC|dwFm&E%Y>H~GL{5)T%$ZNTd`SwP~|M;R1A``kk08LS+d7=y= z|G@ziU!ZsZ?Y(uJ$>37K%Af#B$KLa9H> zwV?hkF16nO|4aa>!7cx@q^3a`e3vRHO@qo|&^dxo|MGy!d(b#%%@XYj@BepifY$JU z@-J*pE=XMiXrELHu+7+NPK|?qh0zqbi%<^D~{s=M$6b2x_!`uNnix3p1 zptb;LKOne0fU7M~FJ0mN|4^a!JSGK)xeR}rojO444OkQy{ywO00L?Wpys(gG;Hdxo z8@&G<6mFn+1%(}Wj6#;(u72+=5l~oJ+d}SAthQ%RuvozO0hH#f_cEUF{=XUA*RxLrJJ z3?IPz-xERSvoa=t>idaW4B#>F=0%+i4U7yGAq=T@&3hRb>Kk5w%7%_crOp=69zW1I zq81AnEJ0`cz073b=+0!Y0G%QK(vv~pB`1T#i~S|6@>joyc$k92SiX7hW%9xbgZR!-Llk8D3Z@ zGJF7qL->LPVjwlI8yPN4kYi8*>4T{Q>qAmo!^{j?C#C=@w>=n`!1wlo@&+gzL3zXb z|96xIz%ogFpHKxZE?SJbL{S9t&b1@7Nq(_fP! zT>(-LGY4db_y41?eJc%E%8`j6ch$6l&zEn2+sDFO;r;)6E3_P zPKI#M9XU=6|8Icy(SzLnL-D^O$e!Ph;61`Kem6qXE$B|ohYUM@Ffv>K`5APMA*ejE zOklnPnm?+s<*opg?+s0zJ=PByz~@w2EMSnVfA+hgc`j&77INMv=$`2pH4Flvvr)bO z{|^PV!zxl36hP`gc?^~>nv*&wfcO@mGg?z_SAgVUaooHb_4siam27_BhP@zo4P1a|TEbq}E~q zgF4I(5Z{9(^ew220kOUReFV3iZE_hHERrX#0i|1LxCTPQwE?tFv!+?O0^D~5t<6ki z=m4K%3+-2e&$0!bS2h!LR%IzT|5nE^D1h$l2JK@9-JJ`m9~d1PWElh+Z9!wm9u5w3 z7#kMIg7?jGf!Zb^46wUm7`AvYFoMT(LFHhrl|%sOjB4wXpgAOl8I86K0-!c#gDist zsC`ta%%D*Jk|Cge-Vaclc!l?WMzEhic7gV(gXYQWqdtj%+%rLzLBMg|{}k_kpTK22 z$b83n|3g4>-v5ii^Ru9{fkFLGP~IoD+y}Ye`@cH)>;sVdKyCx?Zv?rk<#7ebZ6J4o z-0Lv+e?s+B28H^lUn1WB8Nl;FAh&|T47}G%`E&)yoycM5Fz??9P*{a9u!8Qr1dWqt zK-~<>(+5Cq1?`sw)l;B43Y5RWe!y|&t@nQiuwGCYIL!H10Wq%#>>f}Uc>n+R$NS$$ zaG3^D6?uD{9yc zENVE7D{9@1L~6AIe$*x~lvJ?3I8@W%gk6r|#i1Hj1_#Fm1{Dnk1`bEi7(g-uhZDp9 z4WRpyHCP!qYMYrv9Gm{Xu~@^vSlf{BLxYup0pwoCCWac2duyAMD}0+66hLK=We)>m zB@3HE2M+^-h5`da%>o4nkeovs$eiQ~kh~KEQ0ND+)7i55^N&3y~{~hNs+yTeMJcbSW^Vt76%w@2tbzleGqs8Djhv7}5Qs*4d zy-=Vze9&E0_|8A8sgn}XZ)X4JIQRbnu$gli-dH66{~zkWZl~Y;|4gVmxX&a|^TfJh zgFIwEv%?&Q7nTeR3>*G4Fl>}(VB09qz_d{weD=cxPzM%th92mAL(si9p!5g2`v&As z&^;0!43L&pS0h5}I8 z_9Bs?0Cb)o=zby(22NH`e1pu#W;XmRNP=nqoRWSs11RlZ0H>LG3`_LqF`%dY2aQUd zD?oR&fzHmmAkV-6PCw4zbBzno(<3D9Ga#q^6PDn#&sL}3{r_bGCqqN12ZO>!dj+-)PUkv@xE6p@y5`LJd2+3203U=!~3DZx%aHI#pn25UAC(GpJ2qFsRM81D{0* zS|bT(D~FM zFWKyBnVl;vHSHTfdj&yfnSshlOV9t`LFaCH|Nnmi6h9#Qz5oBeas%qd8;bvbfWrC( z%qXl z19qPSgG;T0k68^vgat^gBm6^kKYgf|7b{L@Bpo4Z+^&NVbjpSP^)2isZx~zw9infHd{-imLu>I z=nS0-1_q^?*Fhqnvs^*v(}Da|@qj_8mV-&8R?|tOQh`Bfq7DPdzZDaB7&t(Fwm8PX z=;Xj4Qqy2!P{Zv3os;Iu+r_Nr0gG8}ws%DZ1N(#8cqtLk7-X$mKtTO!28SO!44`uw7(w&0pmhYb zT3#ZrAO8OVs_!i|4=_#CVPF8oFDP6<;nS$d-~wuEH!w1UfWoFW!6Tr7ks$ySKJ}du z4h;_(EI?sX>);SDF`0n}RPWYz1UP`|GKV<~A3*)0T8Dri@H1D{pl4EP^8c$%@CL8- z2kqN(oX4=DF`0n}q_#Gj;lc!F@LprkJ$a!0jtZbO23j);3P;dhSJ2r{p!AKFt|94$ z;s2XjO$+c@=@y*-AAsBix<3TuuUdu>gBmsuSU%tZ-FNH6@c)eh7btuK3~CcV>$d~I z;akIH5in61;trU8kQ=@K|IY#W5i~FEIOjiTZXI;LfCZ?%0J?J=6ds`PX*kGW0S!A3 zP+J5Po;3^x0ga9fE{%x{4mHnN%s}DXsK^ik3e#H8AQ8}>o=Q&!B~X1?^T12Qf`P%I zGMj+~bf8GIb)G4zDLWd9d_K*0WLN>x2dlqdoMc$w{r^uQ z$PAEKAafiW|L*~fX}@S>c=2K(!-p3K846xJWT^1||Hl&44+6;{_bFcE=ub2s>f8ak zgU|Z^|NkKSKx1p5Hp7d1j0P|MF;3=V z5l~wUoGua>E;K4KT&U>gUjk~6S!y0(_DvM3P5c&(3};ho%Z@s z=YbFg*18{!3^yvX|E4+2Wr(SeU*HFt@2`+&=JEdj0X#kfx*J1(9s@%H1A`1GFM-CT z8|1)z&^jbg8)(DthYSTLpyTAQbLn0qok>?|zre3DYt^CZ2mDJwcfBChAt{wviWdZ|Rr~*TsV-rJ32-hJy%LE4YT7?*c@Rtk% zpm{n=(3$F7Od=r+Om(&P%oX7enCik`bJ^8*F*t-~|F8>x;QJ3;FM#^S3{E1junA$< z5l~ytTmcFnkUK!(19A&;_=Iqs1f81>8Z!ddAD})VI6W!;Zv>s64LVaB)K>)EYYggp zg4%#CdMOd>#|(_1y@H^&E2uyA`XNIFsJ#lZ7gUCU#_B+3 zhjfcuH9B>k0gc;*G_Y5Nbh3Q|nH$o=ZlTl3z@X2P@1XsL z4cQC~AotWafX_k~u}J>^59C+x|F6JfOxAlDc%W{9owEYE^K1h#bH@!_44}DghWgt~ zBA|Op^yf3!SpNr|Ir4u^t^FtP+0&qN_Ce*kFaRAvC(*91Dh=b^pIL-v9qQ;4-VZn}MPEB?ALwFYtes>RAj5 z)u45yRSclB6&PUQ1hT{X|4ZOkw^YaN0sKa&5;vrRGslf%saUo4of`Gdw*wHp}w z9YE(wBrq_5?mB^~t7%pUut}KYY0<*Ko{+3Exu*MHK@E$FB_ks1cEf*NjyfDn*B z6+8lJIu!yy^H>&38Q5zU{41zQm6%qOtR+&DELH%zD;YGt2eKPBzX_Vt1kW>r#$)$s zgYP^9&3AhL|1Vh^_aeYHVUnk%7h`c^w#wvM_kRVJ%#67;hd%_=YDG)~oq?<0#+Y5J zDOOPb+#?|TfrLekJVQYJgMffqc_)!t(4LHph-tOiP9nA0Vgh9M zJC4zBW6W}#%K)M?LF*OtyFuxhA*6l@`{~$L;{E?WqlNtcf1vw6LFFQ-+yc#? zg8TxSKgD*Rh<-N%6KH->zZ-N8$^SJV^`J3Ikb1B_=zRvz@&$Ic5`1i}rjC6JsJsM~ zm7qIEK;@?Q|9@ve>B?aqg9fP0QvZ*A3#e=a$%D#9P~RSEUIQqMLFcqOg4!-nbs#k$ zwIDShwV<*Tqz)Dr`t1zQ27li$3f*Zs2sOoWGDcwrvcU774nd? ze?ey%)znFY<}A2C?LzPWLf|>Cnr!KchD3&f`neJej`tWuK=m7_F4J#d&V~8yK`f z>w!V-YOtN4yxjV@!h?a^7gYD4+XV{C=A_O$FBlmvfZE2d85tU0gZu`%cf^rl!s|qa z8Lt}|7Q9}_u;TSWh7GSDGVHKWWH=8ts+=!|&KxtKNotSjnw zzpikc%UIEv)cGJ(Q*;;h_6*Yj1_s6m`e9ZH1_qW73=FI)3=FI_5PRAFFfg!fU|?YT z!oa|$01<<^iLC;a4-#kVVPIgJ!@$7W!@$4_qK_~zu>N3RVCP|AVAo+_V9&wS1JVb= z>?;@;7`HGmFy3HbU=m{m_;CbW)&zM0;NkB7?@k2 z>>UgY%m)}4m>)1Ou=s%5Ce?Ke3NvIG7%dnWm}cB%V3;5S9!LHFN{g==85%(6>Vxty z=xlvb&ed<2&j7khN+hI(!v-`?4r&{FFmQnPh*i{q*X7NrSpZI}u8#BAB^pu~6hP$; zNG)jI+Jk`|yho-%hJgX39@O6fxy}3k|CEOL3_R7z3<@AKtB)}#)T^_XRC|Ed+b}SB z|NmJEQt$o$zYR!T#WMy4P`OyMoV^6B9-+>+19TpB9fN{?1A~FXy#G9)@wFMU;I<`0 z!+r*thI$4O(E1tBU9X@r4W!@u|Btyg41up}vi}t%Ftp0oFf)MLNj2RJ0kSF#DK(uE z0iZQk4s-s4_UWW}|NkEWioE7ST?`Z0L1zD3(lDPv2Iek{76wK%w=sMF|FIF`hkq5I za0Iy@q#mrc1$_55=-vy)=0lxtpk@Yu?#ymT0naUi$^~w4JEMVt0TiAfcY83ff!D-A z%~e2zJ?oN&6b2onuxIxE{|~%hr}?4WRm#Q{?IGkX93 z{uN@!-#s8bpgWe3?9ymRVNgM`i_!c4KM7EHHr6wUR6k}=0EKC7)<0025FD=cT?_%T zHVi5Coe=?`GkP88ux_YVU@xh@4vsh1>M+pR=FC$p7BDb@+*1#|o44-S?h25*>P~D1 zty4*ax(Q@|0I2*1wSOJvF>o{_b^ZaF1uB2*p6;&jU|+XF>H3$W7k=|HOd8w;>s{ zx90y1P#9NyhlEMRG;o+)0flSBdf(X zbXFM1jvAJK1vL!{0U<5?|3LaZ7?{9&Bq4f#o&m*ELo#Tu-Y=fIBcOcuP14$#fvKiK zx}rLSL7_tmd`~ljsb*Dj`M!M0gY>Pnu5+oXD{jC zhP0*X8$j*m-xq8X($Z>br9=|5)6#1a{uO}wB%t+7waTErp;!SZZGgri9OnLBQLFH; zz+x!_W348toa5a8FF^Xd|Nm~W-uwT5{a#So2Cb9*FtcXPj{uOlpnh)+t5gLj%xV^B z1c27fS}gtlKV&KYKZT`?d7ymb{r{sOC=Yo5{|PFiEWH2!y3>#XnsfT`rXhvF!TbNO z1W=fQ%1H13Uk+L{FffDK>!7d$g_Q>b!$w%y&>;s7D^NIt_AZ+=q%hck?%qPu-)PYQ zp5JFg(hpwC1By#fd_m2ycAWS32&^o0ocDJJxZG=Cu=ZdO0iWps%DbR`YK??M0H}Tj zsYTS)-*;HgVMy`*|0}?PfkC&Xh2a9IT=f3`>jk_I^^jo(Xbm@bEDUsq$3lh<(AXEK zUj=T@)=5{??AERTwQE6rEKr#Yy5que9)pebVFtz;8TKupGxj|g7<54WNYHsVpm=bY z%MbyIZ_pYlkY11)kY2}m3Ak9tv^ENADO`O*dX(2?W9CN z=7G$0ocn)6jhsgS=-v~MdQe{nbniW=?*qOM1xsHIbT1o7Jxm=~Um`;VNH3^e1)KYY z^^ss>lAy6j@BeqeXJcao#v8vG87Jt^Wk~$_kkR88BV)iXMaGCmXypuZN5s zzZDs0{B~qq@Vk+5!|#KP2Yx?fJOR45VPYfW3vk#lGJZH=&A@O&kpXn>myG^g21n3( zkOpZ67jRmb!)V|*k5Qnaj6neuMxgxR!N3XjD`@OizZpCRvj>#M9p*9$fZWwk&7c9= zZ=gSyAr&;{tUs3_4P>VNT!wT|K4@s_k^r46*3i@?W5K|{49c6}HQ5Uom?uaxn0WuY z2_Bn!!N~Zbv61n`%ZH2}pfGyH$QV#Np;6(LB4flWN5+I#iHsSq8W{^-Eo7{Cb&#>) z)kDUP*Nlu4UMn)rcr8fZsNfp0bWB3 zT5kt6X9vU_=9<%ox-?EGGF1F;{J`KihaHr5S9t%w4-T`1iVOv{n(QVv3?e^dHA|-_ zFl_Av-)*YNFs)WIub>ukPtl)R1$L8-iVOviIA(hhs=xw1=L2-#bf^M@RSg@v$p%J- z0?@pr!(0Y~nqv$XUNkbSut;QNc(IUSL(MTzyW}zWjE4qBMuwM)j2sgf8CLvMWY_?T zZ&3PioW}+_U&YN*fq~6&9ve@+CzFW&ECylF_@}i4gRm7NL&=Loh7~Uw88%oxWY}59 z+FVilr@F%X{}XV(Mt>Fq!~X{y6F_&-&0u8M;V_pm0OV(oT97)2xr`2W3-;{+v6Hqc zFjy%#NLFoSKW5d)FvBX5QNYTPQNdb~p~30_gTWt$A99w83=gcM{{PUE0@ZQ;>zWTT zJaCxEFr)qe`x>6<#Vb zYJkB}=P%+zLJXgjnlvO9ci&&^k~{2GAYJ0Spdv zn2ywUvV+$DS=MT5f#*O#?f}(U`V9;w4s)57)M{vn)Ml`kfa)Mnod%Kv-Tes4Z?)=L zkabO(|Nn#JLF*7e_9LrvocI4l%`1z5`mP57`W+0Gj`NsXz-yl7FhzjE2h^8ndeo)T zXwBea-N2w|t-zoNstc_TFeo|BVGywHWMHhH$!=nOlYtSOKN%S+{x^W~Cv(gRX$A(+ zn(-Tq3>6j)|Nnx+BQ?_l6}oX5BY0a=h~=z=9Lx7tlE7|No${snuj&=6Hv( z1LPKvn`XUaU~pmp-4XXk#Zm*N?_U89eJ(J4vsc3O{c~xwX3zkYv6c)B72y2g$-oHO zQ!z1`K?7um1s4P3Oil)d#%c!8T^t&g*$hk*s~L2>|Nk^V(i?%U*ZcqfBTbLG6ukfc zIRo;qr8)zn_y0cvusX_86EqJ0^9?A>9OwL*Q^UzzVe#_+|C$$|z43oII<*-XK>dDD zdFK89Pf4d1l&=LE1Ni3x%2$xJkAIf@QQ!ywrDgB`KfZwMsyS+00S#LgNVxt}P+(wS z29?bgi3}e=WqAk#=v-lj3$VHlRL4Q;wL8#qo#DevMTQ@svKmr8|I`4LW1u+j{{L?W zXv_+fHoX7;D?x6PdNA-!1(oAC+N2TSc9-J+M9^Fas0|Mq2L|0qrr*Ml?>LX~kA4fN zEye`8n$9ar9^jCq$yCoXr8C)z5 zFi3z36>yl&V^Fa;z#v|mcL^U1c25(0qD;b{m5@XpeTy0roAmicBJ%nG6N`-3*{Q(8PiZR6ek8 z0p06Wn+RS*W-x;rbf)5eiH1esxDv2bU=RSs-3!nh3Kw&Q!@Pe1HJOkwbeQwcrPhJr zg7^P_Hqf}`fW|Ec=qy91m;h7^)DGPN6@#Y%(AuOQivJ#h($(*Si~^uCzWFeN%ddwF zH$e5u??y(3-wPQzel;>2__dJX#4pes1S7+P---+`emgRJ_?^h`&LnV|D-85^t` z85*n=7#!+mFRlRD>u{IR0yOUjIya$0ib28ZAVZ1O0tN;T28J06WEdDgXVg?Z29L*q z=ID4p?G(pb3=8V#e_*J;{~^HfF2jdLTLzsPc~Bbxd>)g^q`eG29t@12Bo11`3SI*M z>We$xVaS;XQd{jM0@{}a>QjLB(|G?o3*ILPvJYeyD6VVmy+o|%Fff7YN01)Jdkh^8 zHyK(M%wb@t0?liJ=e*p(^Mjy%4Jd#5%P=rF{Q2)=&G7$!{pY3TMylP}P@MRKKWVrE~k>SB>MTQrz9T`5nPGtD;x{;CL^+HCD z*9RE|UO!}%uux=F0H@sn*O03T0qWGDcYI}OYXJ~gwH zD;gFv=rkrX7=YSwjm``v3zZ>xqcNGmB2<&T3Y140of&LEbyA};g9B(T88j9F5(kxG zpfx$5d0x<%1SqWPGZ_LL<}vVq_E|a3W%vQQGuLr0vq1BH1_RJ|f#Y1p0}cBbOrYWp z&HEWFAmU6S4f`2vAmWTV&HEV~pyCilD?TY56E)(s!3<}jP3<~x8UW3|L9M%UI6dLUr6hLEm4fPBv_4{4~ zfYzS;aAeQ`m4OYO3@m;PEQ+8#a{986wI>tm8B`qSF&t@-XYi z&KVSr;P7)~C;){as5}LY$$-XSVB-{^F$z!^HaB%yfbUXA^0&jxe+JYFQ-^u~SAg6E z3PJ z|7Qcp9I*a+1{F};BgZ?o_;#H4PXv^X8toZOkj!a-irFBEwZPgm6F~j`1q>_}jSMTm zV{ZRWgo5g6Q2Pe7ZV!}JKW{fom442c}|^_3j#$5J`!>p|?A z0}d87C!H&57@S0ExIKP=+8++{7K}#QS;KM zqE^AdqE-{CkHO;ysQm#d2Rs-U3mj%K=GZXQ*S~aT;7nksudjI#1X_=OEQO)I9>lNZ zX0WLBWUv68Ar9IjQ0v89QOm(xQJdiK1GJw|Ux9%^N1K7cvFZN~&^RY(Pi9REdkJU` z4Ac*)Z((5YorkEWJQx_hIL>1D0BW0p)FP|XX8@PsPoUwk4`d#w&H>#;3ks9QWJs8# zCe+v0W;$4a+-Iv3!z_jZTZQ`i7G?&PM3A|!m?~;t5sky31N8guU0_{E(Wh_pkvjbacS^cp9BU5P@Ms)f4u)~2hWXw%6O#x z);yqn$&T|FLO@~-OyB%t2~q}O5rgD6ZtC=9&+Z3C~30hO1qvJsU2k?R;(z5(Zz z|Nnm0KVaZM&nswZu6qAJ=nL`_X#G(Qi}p3pT3pb%T{TM?uhq0F1Wf1z)jtdh;Ix(@ zT~X7eTv5Z#UIJ>Lf#Sv_BGL?m~5l*`b;oHehj0=8BE#3=9i17#J3CLG*yd zvzaS4Ld8NDIBa0{!t8UH`+oswY*f2}LEd35Ly09T1Czy51~yAq1{RRrp!p?`yFvG_ zg=$tPfbuKMFCf2#YW}DTVW?05?eW^E1+||IWIt3KWuaAbUXJ2?|$7u-)u- zpmBB(A0!^qz@PxCr@{8HLG}+>vN5oL!rA-(za^l47ATCDa51of;>Tg$zX_mz#KIH? z22lS1w7>HAL52cQUJOlsS_fM5Sd+kR0$K;D-@yJKv?mmFzaZ$|D3E@~xeOklwCMf+ zFSu_8QVSAyn9HC88mj{3i4Ah#^)M4a?gFiOe2up6abZ0JLuf+RKTG95OrX8Bpt?PQ z!TteAJ!pR*Xno~II|c@rK9Cs}iVWcOl^{Dn<}XxXU_d(OS_QfW5T~6BSm1kqZ!BPe zq(9JoBnwy=*g)X}@&oAZX^=Uf{u5{|E=auAJ-A|F7RbG9b+u{H6(O7~cA#_IK5lUlZ|HY2 zxYzGzH*uWD2)biE(QzIti+(3){)xq9(I%Zh6fCebC??%>KHUEW-%~Uw=yWy zWJ`&F+Q^_jqxZl4;C>&-{suV)m)e>r5zsx-Ap0iBF<5}?0`2js)r=AWjdxjOGBC~t zox?wer3X}YgVwcJD>E>H#sop?Kyy3}b6Gq<`;)8_87e^QIl$w#3mCXO7+ApdP_4}^ zk;Mxcia`ErU}Pu(g$Zcv3Zx%oj^o__XY}VVFgna-VF0i1naB9U;Vy$ibpwM!J?LCE zjnfsNJ_^VVi|7CUgU;KodCk5Bw2r>EDoUhNn}Gu~H!xERJm&YH-tv)1gB*iKqaB0E zLOF&2P@AuY;Y7d$IR+chm`N>k%*1gXqXy_sWze0;#N3w*N<$#G>(BrH$6*c==-vtN zd><&D9p?R0sLy6MX^aDn>qIa(&SkCujYWdayqp-v;Nm!k`ADV$LwXJOzk=FKrixn4 zh=5wne+8kM3?|l`45{A#|9-HZ%fMJ)&u&sbm))d(#p?=CyTM^DlS2Lc7oc&X9}RL0 zCg3)89fJm_?_ZP5Bm#-kz2LpRAiF_+2H9C7|019^^Op!HPC;?&Fqc^Z6u01b)nrfr zrJ=?;1{;tbP}&29)5JIihlzElVdOCH-vek`(P^|}umJ5bX^dmAX{=*#0Ii(|^XnLV zz;}E$L@+qa`M05lAtC@4rW52C48URR$gtpbBEt%BTcVL+!|R0%J6<1TIPm%*!wCyT zh6|vu1)V<)8mG0kgrqOfm=P!)gYGaztAoqjZ2l8`mZInpOo*MxW|1yE@ zwJ>c;MP`HBgOXe>TP*{QX z2!PB1g)ivr^Jb?mAJBcb-v57psF7he@&5m(0c02G>{y36e{U?1V+er7FKp}$bQU={ zzUPDHfEak{U;VC_{Exu}6#uoc0RiAN(8a(2N>uEUYHTf!445X|ZMijUE1G0fh%F z931EU?g6cH0?iTYw}9u=L1V$L4)cD0!AujsAArgs(7GQB1_oVFSp>?HApd&*|6PD= zuEV@P4WK>y6YD_p)qhHmO)YB^3;837~qmh9SbtGMj-N)K7Dq^Zx-T5C3$5 zwsjmB7(jc5I+H=|Mh3>(1oo23Otu72eO{{>U}ovXz+TJYT>;Xk-Nj(i>bwWr3L6kj&%(%3}@5Og>ta{T7xD$mW6O$}L)cAJlJQnCN(y zA)(Q!E1;&6eM?A#fEDO2Z_vKBS}%`v`dthxHQWrKyXjQ)yBIhe?lG8v>_#^K$4ZD_ z(ZxacO@sX6{eKJiZdF>k;YB8PH-PqNLEYdA>IWg8O&C(gW(V3Ytv`<;0KQlHA$Tm} z0C>#$j&ViJY~>2ac?>z>2iWau_G?#YH!$#k_8NKr-wa+C(U{Z~62d8lW9>WxXw5ii z{WmBHmoP9eT=@V0{~ct^z`$6*z`z*5zyMOqaKTcQfjLx@!3x?2U;*7T2HF=5N>iXU zs<5+`aP5okOl4rONM&H@RApeS%s%*}gB8?1Y<^O~;QFLiGrFQPm4O-5uB*+2-7olm zN%#S_GSFOx<2;6j`UUJIwVJoU_eD6&`QHI*JAn2DdjJ1l12vnYgB5J{u})P6rV57U zW1X%HER~wB$3Sg9kolEf2cLlYB^|5`Y?ayU$G~=HdslR-GO*Tqf!)AfsoDIbRx`K) z<`(b&|4ZtBgU)dM-_XGd_5%~h4`98Tp!4qjhjgk!#KHD!LdJAz7}!l}I6>w9e-qIC zH8xo^(;Je&p^qWERG7K5s|2KmBE1)rOhj|P(pneJ{FM#3)dCaP!8MMy+4|s1dOdeDR zgVcc1qkc1Z?r00>92JP#-{AAWy#N2-0_B6(_<`FXhcLtqb#{w#IXK;e+ zYNi>S49qh)89-vJGdLO8W^gjF&){U>n8C@wIfIjdYX&C+_Y6)3o*A4ByfZi%_-1f2 z@Xz365SYQqAUK1QL1+digYXPa29X(@45BkQ8N_CAGKkOMWRRG_$sjp{lR;_*Cxi41 zP6nA7oD8xvI2q(-a5Bix;ABvk!O5UFgOfpN1}B5^3{D1>8JrBNGdLO4W^gj7&){Ux zn8C@QIfIiyYX&ES_6$x2of(`Ax-&Q#^k#4}=+EF}Fqpx~U^s)5!Dt32gYgVb29p__ z45l+U8O&yIGMLZcWU!dQ$zVBylfh~RCxi72P6nG9oD8-zI2r6_a5C7>;AC)^!O7q_ zgOkB&rYD2X3{D2;nVt+TGd&qxXL>TY&Gck&pXtfqG1HU5bEYSQ*Gx|a@0pnlzB4l! z{AOk{_|ME_2$-445I8fFA!ueML-5Q@hLD+=452eK8Ny~}GK9~}WQdrV$q+d+lObwm zCPVbhOoo`5nGCTrGa2G$W-`Rj%w$NInaPkiGm{}{W+p@O%uI%q8JrA-Gcy@-W@a*^ z&dg*;o0-XwJ~NXcV`e5p=FCinteKe%*)up9a%XTdpos+DA75qz2TF1*IpD8L)Fe>zfrEKxcr03Y%XC z85aCzWPsdj?8tBcd?x5Yh6kW?K!1VGhiGKD@OvS{jT(DyaGwg)&jR(u!2PUk+7%Pn zI2CGc8do^XVcgL0jMJfkmC2{R!NS2|F5?2L2Mi3=Cm9q#=Q)7Z%Y<;U*@bGe8R>)W zr<})lM8BJ%!Eqjwf_^u{0Z<=IyOTlRaW3PM#-y%@5KdhCj2I0V7??X47?}4kFfdCn zFffCL)+LBqs4rN4p1C<|Nmb4Ql6m#6t*vJ zb9!j6VA=4po}r^!ok5|N(-}NJWU2A{Am~gm(42p*Ci|9JF6W9`buW<#oD3YGI@;3X z_rXw41+yBq$V(u3(E8pQc2BMP8Q`;VMH+r{Iy6W@;>L0Aznqte3=A*083bOP{=ebX z>;F4IY2-Ec{{yeJ|DSm6{r|%2?Eg1jcmIFzdiDPouTTH~@cQ-tAGL|m;Pc_U|E~qF zyQ^SeTkz7FGoXgS^HL2vlgI>VP6yCg85LY?3$$0UY^e1Nu5g?SIxk1<3CJ&?^agVC z&v=Fkkh?+qgeE%AEC$93hJ#N!wHTNxvzwn(YPvoFjbnCjF))ML0-(CLhQSfpj5Xc6ITNamK*zKH z|F{X7w*>8dw7Abu03PH1|LtIdJY<{?k}m#MfXafG)nI!WLFGXWLnN}95f;;!52oO1EJXpX0_SuTWZw<8s_NLpgx0L3L*T44gE6_9zL^a9T3(DeI4o?!ww zpM%ry9~u4W3=EF*7$d4)GAPvj-Cfb#$qC+52fD|=VgaW{{q)Bb-v8Eu=Pf4h=2Y*HxP!tG z6!zZ#fA^s2nF8b|xV`0XI*EFtLm`ih{o;qOL<1E4i4pgp#r{m-ENwAE7?6n;NsxM8t?GXpf9 zUAK63g+&7c185H}Xpb&v@9nRH3=?2`ZhtE>tN`uV1?|oC{vQsW+ia*}0`L6<)$8@q zmn$YnF$MgPVoGqF$CLtU3s}rz3IVwrlm|Q*?7?eHKx%8sq$_Gb>u2UM*)%XSuvjKD zuryjR#ndZA1WdHybg;h3zzMon0h~wwT?E?&+G7dYL!sZmkO~@4bOg=G&*uc6$pJFI z+62@_{czD@KBouFPVfI8es+j}`n*f5Zu?Tm#&y0!}OyEl`)|CW{?=@3~`WOy#Id+1?_(at^JrF!|4Ju-(lYWCHgZ!`GVn2!xRRF>S_js zYGwun&{^#DskcG*cd*ngTU`P2qxb)BPdj8dLH+puj`J8pLKwhv$xM#(m;@Z=GM>?I zU|iAp&P$;21M9~Oj37V%kl~B~`5D>24)gwl_e@$eGE{*4;{E@9 z=L<%L3XogtnVCdD?g!&npDhXQ>ah5 zT>*+;@Bg0~LF0X(I0wZwMC?N*Jg%jf0zm7$9Op5VG)!Y)0F?=#c&(YPBm#<8ixvhJ zNZfwq1htL5|Nnae8i#S1_s<377K;Q14v>35Vj#DG&Ng$L_rC^+F!-ew5b-c>-QPcwh$9ey+STHd7fy}Y^ z$LIqZo41r>5U_x(eUovV#o#j`je)^pDT4s$PE^p{sLl=yE;TG#BA{~yL1!9PvN0%t z&N2dxi-FeS>UT0Q>T@zMIn4WihvB6n4+rQ>zZZ-= z1{;+bJvJ~idc9O+y3oMLV>3~ifoB3U1B>_n|0bY*2q?^cDE@W)-N>}y_d=!>KNy)V z{65IE;rBzP9iTb;9|sv`)a=o&0JRMr?l9|EA7EevjqQQP>^eOe7%a8^{|$|2vjd%b zpxwYQ*>NsYL1R)^4DNFZKz#ON4v-24B_m5qrECHmbg z8^GpD&3>J;Bw(|>5@BjZkfXe{6|Nm>a*|&iD z67>rr7=AD^R5;xER{)yd0+kP-FaY@-Xlxu5Ulz#>OklGdnLzWP49NYmmst!m zK;x32HE1BWJI?!O0h*)n{{PQFe;xy)!`;72tndB*XYuO)v+&apcUf?Q&V2>Vo3n2L zxe0t`uNM>OehLwgyIy)RXgJROo70%ol@Rh0*BA=Z83qO>(3r^+1_q`N3=GUH|NsB> z0bvFPW>DEC17&M4FffD4K^q1JW)G-X00RSa0R#BF67U$z3^W|)0gb`#Kem-qiaU!Zxd!~6fU z(ie>2J7Oky|NnCb68?V*!cR(ASZe3}C#_tkP zx$#mHydI_kH0KMk^Y;OWzki2-{H;HqK^~MgLGA?QjTfAd@B!_a)t=8F?>Lw7g#LWc zo=L_h`tuni9p?R(0j+m*nDe^=G_C|SGsi;y{|5aANI7RA|GQAX0aVWY`cT8kz6IoF z(Af>%|9?w>{0H)<_x~qHE#&`CK++Qk)w2gx&wm*U`TrLX`TsWqayWw4L_*x~>qRX? zfCZF&r-mc=5|n+RhRY!U%02=T2aka;z{NJya0J5CE&-ht4plp&h9d}+RwsD>|J75= z5MTxsZ>VJmG4uZatE84844Mvq<johETmOUh7XDNOl{dd! z>Q^~1fZF&DbAFkC+A-e$f9b%)9OwR0fQm`L#pe71opZzB{r?vW$ZSKkhzzCJRmk`uFw1bPf#2xgW8Vz^BJaq*5~QZXHamM_cNe| zf#C+&D?hG)+~NKI z_X3dLi0!99-27w455+$RL2c4s51D@aW@Kgn^`)&D85({aWcu*Cky)W;nsNoGEnoG7 zL7|@ca0RGM|2vUc;s+yhz;8unj^B>V0>2kBYy3XQZ1DRbvxWD++2Fo;10zFA-R;ew zJ+2In^O!;!%{Xm9@eT^R3F6>z643{R=RD9|fz!cvOE;Qvn&@|e#t@lg^gBU$>;H>d zZAjhWO=U=agb)Y;9YC|+|g2&TMAZ5Y-J)m#^&3C>K(zj)Jqu&5JqnJ@ae?F&x^*sg_>;DV_j`tWsYNUUOfbKqO;0E{i zL4AM=pgTZ8ZUeV*G(lt20#>zkk3>M_C+IwnS}QLRWOEug83ZP9GcY*(`@f|@i~)9E zj00%Ssr&`vW?I^Q9&O189!ZhT(-{0t15vXr8kkbRYUW zh9fT+nFC%OWcu*xA=3{~Tjn(*Gs9~|W{%g6%mS|ynI&E~GAq1Z$gJ`DAhW^ihs+ih zip&n6whrk0)N1hkp7;O00QJRFq$@yYx`FmBfbs#T&se<-H0JR+vDWSpc)!|&Oi+L4 z-wP1`1vdi&Xuh*XmI*Ze&H(D4gUYvtGSE2H{gp5?>sb$1Ae#v~OWyncXI{{pF=)?T zqdAiY$Pbp93>qMRfZSih!MtpuIRgjCA0U4b@(X1A`0o=?I5vngd30(rFhcX41vuZ` z`@032Hwr*|p(Zdgw0Qsj+X4z(XqmADlukhN-=OnHKxck~&V%})`1Km2rg$(I!RN9Z<}tK@#-%`gbH{lM z6&e8S|T-waA0Og^CXXFnJj!2SY_K~9ioV40}M2^wQ(1fAglvd>b1f#C-u z=Zc?-oS-wtRbMc2DNL?o@_FsZ`2ZBwuM;_6yl&+D@OmNVkJkse7+yc*;;>NU5&)$W z3kLB1cLB=;1{TnnD4;XgX2>!y&5&hao*@esW1S((z&1mcfqjN71IG+m2F@9>3|up0 z8MtT2GVsihW#FA5%fL57mVtkUEQ7!dSq8xwvJ65qWEq5K$TEn`kYx~^A|eugag3^>IZvJ6TyWEqra$TFzR zkY!MvAM8%iuTDmcf6fEknReTZX`ywhTctZ5e`R+A@U9v}FjLY0D5c)0QE8 zrY%FnOk0M?nYIj3Gi@27XWBBv%(P{QooUMuH`A6Oex@x$!c1F+#F@4XNi%I3l4sg7 zq|CHsNCl;fnYIi$ptLbVmZ5N_EkoK&TZZ(RwhS3FZ5c9W+A?I#v}MSiAuA%D0!93=9p@Odc<- zm=ZvJD!91`ULqj7UOO^C);~kf&IIQtEazu}!lHqN$)nQ}UN-4K$|eRKl(I>RfdzEW z5yqH-oh$tNA-E5)08~yv%A6mzjkcUI$n_JX z{$!}BRbb!JnA8;zswrRvs;g?V8P->_{GT6@Jo@W|i~MV=GC9eFPNPUN}qyOHO?OGn7s{ND?CUi@I>UGV!L&xhX+d49ai zVi1_v$U9>~B9F_2Mjnp_ZUzR>xm=(%20y$QG%OlWgvZ^d;~HRqz`0<}uN8StymsWd@H&y_#_L9&2d@|Mym)<( z=fmrVJU>nzL9PfsfWBCrx|$z zPAl?8oOa|*IGxCwak`PW;PgV?iqi*q8%{su?XXbfg`O1+t%H&3Tu}WAu9It}E5PMV zm2?GYOxZdSbXOY}#}7s>1rG*>j7CMyEugiGj`J8iK>6v#UIvEbCk#xWwSk~B&Z^xQ z6hP^<{`lt#9Q*#*9p^E`fa}_M3<03E;12f~DnRu(Xm7Ch|38%o_LHwKYZ)XOWWaSZ zLk&Yz1xA0!p8rAke*r7UdH*v&dmcdbFsO_J)x)4V7!(({>S6EyJ>a#(ki7H$;LU^l z8*V=2-*JnP|G+Ip{u8$x`7hi`mmP-+l&GXw-p6AZaWGH z+)fmbxZNnAaC@PE#_fXw2DcvySo}Q5ukiCBzs4^{euH0%{1(3)`5k^G@_YPhka5BW2GGx8VwR^+ev?a1HoJCVQRcO(CV-wXL?fYSc&gZvAAKjdEl%F90( z1pT+L6EEbs~Sq>qhLCAzQxExnoMseYIISqaaoSNp;B=yZ#OX!>h0_ZK zG)^BBFgX2Cz`{aNz@bJlNCet8WCoql{DP711t<iXZ@rUA9$oUG2oG*SbavA(~wmo6cYaoJr1)N zTEKZ8+;{p>3F2?e63Hr4VlwC{<#8FR)EjTOkiNtmuFzLXkg$1?G1I9#o%Jm zz#s)af3Ssp3yQrl4DkK!AhSSaIjC#`=>?}Fc~D*O-@*C;=FXZBQeUMAw^+PTR3q>yQxdxDO<7eW{gKPpfAF@f@Vq{aerO2jn z%aP6CRwA3ltwuJ7TMOAdZXIL`xb=`N;x;2&!fi#ijN6WE1-BE~DsDHjHQZjv)^YnF z+l1Q>*=GDa$lCDpA#2AkM%D?x6j^8da%5fbE0J}@uSV7lzZSCY_;rx=z^{j_Cw?=s zUihuZdgHev>x18ktS^2yvVQozko5;BZ~s2X#_;qgcOuNSiZczuwK z;q^l{j*|!31WrC=lQ_l5rf^D;P2-d!o586>Hj7h@Y!0UuvU!|3$QE$wAzQ?0Mz)00 zifkFD9oY&_C$d$XZe(jXy^yWr^g*@>rysJ-uux=M0MB!cYzshP2PzX$+Z(JN-v2)a zg4!FPy!fJ#)dQ3lLHX|oBWr;7zb;i!AM`~ctHX;%)&TGSf3|}1o#Q>wxx8)QH4rZn zSv??fpZ_BA9@_@cz5;0a3A}lbP2uK4HjP`1YzDU!*(`23vN_yJWb?Sy$QE#GAzQ?) zgKP=69U_xP6dq!R?1^D}Ekio$&J^>x^HF ztP6fAvaa~$$hzTIBI}M{jjRWLEo43M>mcieUk_Pt{AOf*@LQ4f#cxN}55E&xfBbG_ zWB9$0jRO=0zYnqr{C>zL0Zl&#ek8IM{AgsYKuSLiKMt~XOl)M^@gtGd1C)LyB(gf7 zry)={g6f?gjI0TuF#D;N8aGG7n+VJ8a zYsbrjtP@^7WS#Mfk#)f zzIg4(`r&mV>yOusYz(g#vT?jV$R_amA)Ca>gKP>XAF^qjVq`NorO0M+%8||CR3e+l zsYbSdQw!N5P90=RIQ5V%<1`~%!D&Udiqnp44W|>?I!-sTO*p-fZN}+?Yzt05WLsgO z$hHBNZihnp0i^?QI&lQuL(HH6OHV%*vNlX;WDNkNA5fZ@u#hzbo~{^KBS7g1)XsUC z$m#(~BcT4l#6(sHP#Oj0Q&9UI)K~ES|8^r%{)_;nC6IZbbm#rA4Ln8!GNVzEHK2i! zHRMGjYXT@O{orPh_^AzAWAT#_BnGN0UnH_dfXX(|`E1~J%sqw+pfppf>|Fs$FE2o8 zWfG&p3*@vikx}8rLRN4af3T+)@BiVLKap!+xm+kT}+6$TDaz1+acz)|1H0J`H` z0JN{#aSm9Vp(dGq3#gqGa+1RalomkyX?`zcm|?w=f$_H^Lqq*s3kL9B8ql30iVUFp zLO^EIW*`V{M8B)R(1gyaA6^2J54f_}vK;!TY z(F`o0dtDl&85rt!zODeZXF=zF!_LeJU&&zuYEyvD%&4{XuBeyt0PUqKXtW3K)n%9< z&%iLzj)B2pCPR$%Uk1kT1p-#}vwkpu?t226Uunmn;4t@p2gv+}eGDvM`#G7HHOymR z0ND+<=YfC~$R3bBhdKWvK>F(c{;u%;{~t7daNso~LjmYcbWr{K+L56Fv`+`u9v%32 zlp=V1<3h740}JR*m2k};b-x}mR9L_K{~vTO3#h$?JWd4~p90;x0y>wfX1a2PwJQUM z;~WMH(48w4a~Kq=V;L0c?bx@}KV#nlx(fw#?iKjF<)yDHKy5DX|NneJW;)FI|G@g@ z|No%5L(o~x;hF+g)zS&KDZ0kf+ig@Fa+Hc-0^;nA&v^f@1>YOckODfJ{r>?_egV1r_d$jQl_vUnf%glugGeC2Ypz%-8dGrm*3<99Jn2O{$CF{MQ^z+ve zv|h5Jib0{FhCu+dKN~a`0+z1^%P$3q!^Emn85ArIg2v?j_BJXq6oAeE0F~|7%5~5l zLr~ccE4x5x8CHH-C^BpSrD?dkA2O`4e$2oLO2aUBgZu$n-wGP51@+Ak`~R=G4WM%L5dj2LnTZr5ytYXuJzF z4=^zieBLGK&P$dFj0`29a<_*2L;xtC)HirIIL=}C0jkGq?Yu;+PcpE8?hydl0UGCU zoX2pZp`L*S6gH0Y{@-bk2HlU!03HJajel9lF|gDyGX&Iw_fIgWfX1n6#6kN=|Jv5G z90t$zS0uA3c>n+T6tun{a{k5tlC(zwbp!g)^{-+;`{|!NP%&&(G9lseFCj17S zr;-TXivYT}?ZEGc;Ild#emgQO_}$2`0dyt{IF9!r#c_pn1*nYyDu=2;alBupYAWGztO1H+YX=4ftApTsqazyRz-tLW^IZmw=HN9YAhrrL&RIZfBthq1fYvxn z%w%8zotrlKB?CvJ9RmaS{LGgOEZ{pn;rD1T9BHV-6VLL{cy71=vgZOgo~1zX{5upi zp1=PEyL%33-@yMEa6Ip4;HXw%Pyoj>=q{_puR(rHgv9BeK+rwg*y0p)#y4oKdaX8t z1?b#rho=9aGd{4o-(lW=7tlEzuMRRayn4vc0gB7lj0_WAD>BS@4LzIVHI8#RYQX;Z z9{|s{2N^C@%mdF~et_puM}`gHIH{7Z0Oey)-T>v{->^KM$gl&nmj#r!!SzbHbcNLc zP#7?R?_;-CU|<04aRB8}56~4>QVa}M*BBV<8zMku(w+LQ#}yN+7#P6uW9ubiy^?{Y zdLM%V$WD-5;Cxtzh#w1C29|1jHii0ThJgAx0iZbXsRr+b{&^I%wx*(gzfnkCagMh_e299bLXncY4 z<#K->*#^Ml&lp!T-+|Nrp) z?V!2`3K0^Vt4+Ij2{~_b( zp#1R=^}c9OIS(=u9Bv008bDzN3O{hT)k%ZSq63{Nv;b7sfx-&ZW&q9Ug64Ta?t+CG zENnr0DZKxGp4hyoE5o9efy0A=cPl7tn*TB=fci+FGXbh!Gbl80Gw{?md;pyz3>s^H zfh-2P&&EQYK?F2b(fpEu0i+LfM|<;M1{Dz7@gBnx%UA{#&>4A++6+A4eiP{I{&@@q zmhlWCm7WX=Fnx~q7%HG<3xM<0oc|XPcK^QuQrp1I0A7~_aW4sM8Ch>bHfU_L0K6^&v=<0!7C8RB{}+Py|60g0D1iI~+W!iQYjC(TGBkMq z{|CNf8Po;=*$1)*6z`xo2erAt>J73`0Bps~UK;CsPAdc6PV!|&Ar=>?eq z(hob=(N-b;!4F1;4WRlIbQUn|3}8?i2d8yV8b89oF;Rs<05nhP{r}_H=A^C+>)pud zoEv=KMT0anoy#zYOa_lJaD(p_0-v!6x(5j9JYU!uw4l8-Ab+5xbFBSJP+A131*JvM z91SRqgVLk-|9n|cS^}kO&^=?`|Nn#6IDp~_lr}+rhQ-Yf#s7>TGk!HPEcmsMVa0Do zh6BGqcZ4`HO!$?^Fyq%jh7G?!_pK^2ocQg?aN&0%!;RmK3=e)UWO(uWAj5~>4?$}_ z83h_aeT5R~ikhj)6`*qxL1n4q9ELNY$xQ#id8Z7NM;NDsCNMZb;)Tti`3nONs4f7F zxqfAK9D;d=P+o%+~hEi#ROzu z!xRRds->WHdS3%UcVc-kF!Y2bK-zW=^H^7a>I#q_9Okh^G?Xy#G%_===y!nXG=>Dw z9XO!-%|U1B)wer1ILu-2so}5^3E}!t7wQRJ8=~wb()^Nv!?K=%1H5jBn}MM*n}MZ% zDuaXLoc||4XKU3rfZWOa!QvGIBgh=bIsf;7)>yt^WE23+jlEK2=y(NMC&R(K>{TMe zj8}~e3tlZ`Sn=v0!-iK68Fsv8WH|6zk>SK^M}`Zp6B%y2Ze)1ydLhG$*9RFsyne{= z!$Ogf0hI2;P6}9AYBF$u$CD0!uJHb!1G%H7nSlW`M>io6bY}m*6QH{}K=w}F4_Zr; z1I|yNc%6{QPy-r^oxGTVr9lMT28PBj^9NA;g5s@VB?E(gD>!^~tPe6&fZ98-xUD_{ z4(E%=>OgVZ{Ez`W-{&}wm7)3*gF=H41516w1_p$SDEETQxO9T=qx2rTiW5$JLni}YHfarfZ8h6t3l@#Fere=55Z|{H3LIK zCj$p)ysse+F-HjsQ^z^~7u0K>uE6F_kQ*K5{ZFVWW>A3nu`ZK&87L2d?m-LX6tJqz z`h{38YvcvqM{eoGzyexl3v!#21A{}Yc@TKr80T*bg*_4@yR&>84qAh%S5?(=5fV*UF6|5{@&k*1Rj991_N z6ddL;=y)(Nq=df|uxgND0NumG01AK5cq3>G5#&e5d5kTf`-MSaZ@v5fe~=qMVGkaE zRXq*5Q*KNBEGChLIXL2A?*ApV*1JSNX$rok%gRe+(n(MnVKk_Fy&E+48q$1)fd{m% z4puHX&ifNnwHMT;VUPf=bw-nO0F`SXw}8ekz5jps2#W8Tdg%&KdjQn$@CMy|DZ;>0 zSs$ka?mvh#S5*IGQ>a%yUE%%z_0Hz)3@o7Y0UhW4{sJ0@ss7Eb0NO)t)yPl+?E?wa z-~C)sKj(#m!<^q2YGfaQ?+pl%`%wq#H-?`SumYI{3a?soNI7J&pMe9EC%yl_ehTkn zwlOGx#&Es=zrF|>yQ!ZF>JR^40UE;r?f-z3PyYj~=Yj4%`9B5Zr-m&IJfQW;-v2*= z?n7~~P5`fM-qUEzz|tVazygYUP}-Ov#lQl(3&8t-2IP!AM}~?9ke@vn6hLPKgUaTK zAq+gJ*$oV!^4t6WFAvaNkDzkd`~T-skUVHVdPo-2KhR!=Pz8o7kn)(}Op{Vqh4=s8 zUz)x!@HB@ruz>E`a-8?;1gM{Z5=K?GE9z%~?q2w{rbhCTNQm^0y6{YB`3wygP<&X_ zGq5y(`tk`9;C<`f|KHYv;-#XNO#w7k25Q%O|9_@L1VHh85obU&?~oD;CwDHYYP^?K?yB zZUz-dnqn}i-p`-_8sn&McmS#=MLZZ7O<;V7xr`d1dpkjDLFydl{eRKCn?V7@cbvyK z0VD>#d*Jc!iUxTGkqOeEvils8QuL9L4|64%*sJ;WrZ;<;)8{`>S zKyw0(_6!^otr%Du!f4SO4TUbU*luM9`QEsJ#Rl za|6XGBtN{k3W}fl=EoJF`IHG#;Pb8zfX65s876?*QLmxniO6GZpf)im9K8R3TMEwK zb)fwH?*_uHjNoxKV%+LD?|+0v9Ro{)3<4>d9@0L=}7`uE=dU&VsXL#U{M?zeA#Tmcz7e)bm>wxBU%P&k9!VI2wTCq6H1 zkYr%7s6_FfWPQr*3UC~O^Up2H(fi0O~7*B=i3RnE^5rqz{z0 zL2YM{eo%kZgLxPD9t~pjg7kp(GBPxHFz?nR*3O2et_H9^Xg|k;c^5B8FDRWk++zT( zp9Z@X@#Zj> z0o-o}h3RYLafX8o4aj{+kefl_Z6U`XQ2`o1XntJb!MtN5l6!5!S4vob>ov_!A~P}> z3>xGZ0zhpy(AlMv_b~*3(wqnLj=3;BpuW1}y#FoU|C1nlsgk-{U}0;K%n$*I)1AFA z_1MEVDHr75MkRz^kUk3r1}0GWT4XXrOyFcF@L=ANO28a&K1B{UP+tx-UI5Bhps@x} z9R~_OPavR8v5WBa1gt^ag-v1QP*@NKrFbe}i zg9`%-sJ#Z8{|1F8Xf7Qz-wh9=hYTIfkGfhwX$#y3o}*mR(A3od?hDs5lz_qy6kgu{ z|AG6z7WE7gAiF)7w^hRIb-2f{pfR2y0CfI?eiwNBJR;Ov!UD9PzE<;>NCzi_0Vv!- zdl(eB7+7kxe~EzBm{?{qSb*H;!N4E^s+%mc88|?D6rpNEKzA`ZG5mi4GIwG;LjcHb z59V#)@&TMz=Rwka{bBYk_|m=i{{-+I^B^~Y%mJMd1-iGcHv5;z#8?K<`T|fNgb`%^ z3~mO4Mq363P(Km0?;MgBeu+%(WH9ky-nta#XUBQ}JG}q@Ux6HspfqIxTI+ofJSVuo zgL&&!kb3R;49uYOD53KL3qW&g*y1?+rGy12JwojKgcNT7PgvB!!hI_v$V{;R?=i$Q z)G<_m*r2&DP+js;k)hxPGXqEc0?-}Z3=$p;3=SZ1n3&@{27`uV$QeJNwg<>fAhSJ~ zx15BT>p1U!jQ9UI@H$0M|G-j{K>*~Y`o|u6-v9q!fsONk?&^U18G4roXxs;s*F2cF z+yt2i^1I_)1_fMx1D%-J3W{;gX=$# zdC>Av#ruCO_-+uC@LUBt&+7k$256ZHF6)vRBrIAO1VD8gs0{RA-dqR@N03<#_ZSo! zlDc}l{{;ww+zT4R1J$XZu!58ku^8oF^P;X6P`H8ew?!?31Sl_q%0W=R_F&$$6>j!C zh7HYg87d%iXbcmq?=T2h|79=$jVW0FN6D8C4V(-tpz~y)`P2Y3&Il@FL445M4QT!o zG-De?W5s z|H0?WfZRSomZ8Ffc@sGQgUU>Nc^ZJE zf`tq_K=Wx68yRkZ>n=uy2Oi9uBtc;V%8#J%1I26o&CeAc%o`g);^6q0$8e+hA47$n zJOhK{BnBH$U;d>dL%|DfQ2Gl1jW>hZMbZFFMV5yP0Bm!!OgZ3kU?s5U`SF4vhzyNX+ zDC|M!2Y~W?4FmfY>uS&#%_$MbSqz{vFqp09FtC93se#Vla-0OdhmFx;E<=k&C4+MM2c07fH$#r00(3SMC?A2+I67^q0GZPOmBXMk4NBV+6&Y$kcPUvgFbGUk zWGDdLNjL$tmw3N+MZ;1ChDL1$2~ay4R~m)3hZ#Z`*lY9~7}y-sq_VLglYs?vp78I33X1IWCZJM3F({#%Ju|7K7CpVM>CxS}zWL8jt4g92y` zkHrE8wtBbA74;gYD?sU|KJzwWe;ZO7YII~M0NuObIFF&HF_EFB);YK$Gy$F$LH9a> z&;5s$N8mJ_$gl#}96V^O6qKiH6&NgFdD?N_|2>ur46LBL;~-}nFere=NI>^WfW`wH z=PKz zYg9nzh=9rkQ2F5f?;3bq1l)gy_CG-V4p5&1l%Hyq*|#9K8Ia1NM$p|)pgt|AA6s+Z zxT0nsxZF`dG8;Ts3@UR#VF6l)Wx>F}W~l&PSIjv1KDaH-GSQxa2h_I(myrhH;7*<4YxMZ2PAyEb@E<5wa>2l(7;=y}(_7Bb9$pMecM@A^0N3~bOj z*)_$|kh8BrXMa>lgT|8$LFEs~jSlk|_cTj0aD+3keFL>k9Of~lG+$=m0gq!YWGDc| zGw946$9s$_;in}mCMz-&SZXr}Ojcwl0kwB0gYH(h@)DU0s!uc-SSoG*KLU-@f$D>B z2KIj-GePMHJicwi{tqt%YhdVPJsWeGHnzsoAb2V!_QI02&*s zk!RmhcVu$~Xnj&`)ek+;p29|L28KpY1_{uZ9VlJYW->2p@Ip=(Y<3{OInMj<(!j_7 zs-HL<=P`iBKubVpg*eP*kbt={gyBP72!nv#1V)CE#%cy0Q2GX)>j_#X4I1|Woz>xR zk1+z&mT;WM09t>=0tyq**eWPoz-u0)8CV?t{d)r1&yIZmPN*{fKMw|m1dv}r{r?)N zA9@oQ8EU-$|F39tWGDgUIZ$5^lz%~W=R|7;jtSBX3=^vvcqT?OusGahPzZHk-vbFl zhBXbs3>+cQFm?F%53~=J@dYCzc&+-YM23!6(6Pc-3mIm-I>@l#)kB6AuNfIOyjEn` z@!FB$!0SYY6R#T?F1%jIaO3qsh6k@7GQ6-*WcUE8ZyOaEz~|gJ-eUmoA$PpTpi|!o znp^rW04npq^M{!XEDhbDy7e-6UL739|Nead^_ju-zBFjg?tek312o?6{ZD|p8SDp# ze}A`t`qj&X}c8n_q)YWbWielao}2w`CR_DhlB1Sr0KIWk=MmB?`8S0lrN zU!XbcMn;9-4;d|fK4jSO>mb92Uk@36{AOfi_^rsu@!OG6;CCXU#P5ZS8ov)R8q~%F zSNuq1m;kzm19X1mkB1BkK=a+ThTaw6ybLM}E*MwTe6j|$c|h~R922=11nMVsFgVU* zX{evv06L>5r=ggErC|anzAl2-oPxp!bau~+g$xrcA22X9yE5>2Ffg(-?19ajGB<$I zVLf;bl{uk?{a-=N1Ez`^28)22<^Kvo82JCyyws|w)wBozm6f&X3>G@?Gt9!8gX7Gq}CLa=4;-H3I{vuBj-^qrmG#Mv2#r zj0&$8GHSd&$Y}8TA)|$bA|v>mBv6_Hr6EvSa+u3-29&NG=lsNDQp0sRrzRg%cmEC1pUc1u^^-yP0%)3^#~=YZTN5j$8@zCTAm{&Aecu%U)^m56l$16w`FFAqTHVo#7^;PL+d&!m2G0jMn~11@{|KxNNa@OTTT>~Wa)8&qd8 zO#TBZ|Nn7>K4!D?{{N2w+|dy#N0N`JV}1hsiMTfbK5x{{L5}zS#iQ237o($WQ@3Gkqbr z-3B=~9eG`PBf|mEnpV*H>7ezipf#1JL5(ob?^ymEl2TFqu^Zv(x(&Geq zP+I&SP(K&EK1&7^77f!tY4H?zT^uAl{x^X1Oqvf$i~m*X+dW|MmH69{q2M=i+XFi8 z03LJrZ4KVnR~r`$8GE>ETmkC))iXPQ{FMN@OA%E5gVyXf%mv$tl>Qk+K<=Ad$G|W_ zj)7-#90Loe%>i=9Ya;HAcbvr_)6fAM-#7`LZ-e@!2WHOXc?=vNvq13!8ehP52R&%) zufElS0W_!on~|a5H)szd^1S{*2Jl|S-=MuZJG3imK>HbYYJ=~10o@e_9&@Reu9(;e zDzllEf%e5X%wtdirF+oaJGej82J-(&xc?pJF`6{rW8kU&%b=h;`~P3inWfe*83aJ> zY3u+0e}LN4^{?2sIL>1@QEM9{(wND>5}LriCo~gM#_4e~Fw`c1`V9Xs)Tgj-scvIX zsCH*iaGb;-;n??oPoo6`L#+a&t_6)-)z${+)$C*v0oRvH85E%X2GF_Sh`ZvE+JO4a zpf<(-630mlK8=kGI}(x^7(x49zU{pA0pK_&g3iZvd3;K>eh~I!HbS&9{TXx%wG{LUlSc ztTVyuiV%$BIpm9#n zm>+1&3pC#4{oe&VhYE5#s9tcK#}MMdzy|KGgT}8xV?q`S7y>}!te~_9+P?wXyW#!s z3wYhIMLl?qegW7WIMyIHFX~#*(A2dAJRbG`5qO<9Xzj80{|Df+ZM5exF*M3DfX+-+ z0PTqe-JJsJi-7Ngt_77v_rZ7Cg2wk9{{2t!U=Rb(V}RV^0MF(AWCGSEojc>{|5af|Nn!|F7f`q7kr-^$n72sypo`C zvWBEC@Oc!Vc@)^*Qt$r{I6-otaS-G&5O~-^=0ZX1Fe)tV84N&kCH2i74xsRLoWmes zA@5*N54uxs4ugS3^8XDW`@H` zU;&REg6I3g(iLp( z5Oij(4THNM=uGGYhEQkFS+FS#&e|`v84MB_v>EDGg4Py+!im8E)OG}g6=>|-`+pB? z92PVNT>%;s0@)7=r$%iC@VtiO9EKRsm=$zfLc#G4L&ijH@VX2PJr>Cf1`VL`lw<}4 zsQKV=SkOE@sIBQR@Ba)B1}50NLJw$Teazf@*0fcjqt+`hfXPy&iaP+bUG za|H?u^fBT_Wr*JuI2jnh`}8>(7$-0@h%{<3u+(?IU;u^3L@fr;Ie zNM_K0nFTTzwqC%4K?uAT0+B{`f#-8UYCIS?!Fx6=lK)=-@j+&Q!XI3Aa4;5W?%$`F({pY>`Y+z|6iY(fuW{TxuT{8oQ`ZjWthV(28D!{|NrYd z6~J?A4M|-qIza2j8$fn4guv_pg#j#0Ikf$M)4Zr_1<34%rmi)hyazfT)cgN^MsS)= z_*Vd1rvVCMP~U&1EvV1(e+wwBgW84O|L=g)Kg@iP*&sVWVP?_r|34^RD_U3-y#N0< zfbtoke9(OD3XmT``4N;CVeSQu4I<2c46b+3%O#Niu;~SjyMomwGjM>)F;EcNL3z z@EShweOcJnFE!LNfXD5;|K9_zSplUhhk4*VtQ_9|ZNYYe#6b12<2?ookb2O*P*7Nd z))RXFzYibN0qr|-n8zRjTI&KjLl&A>TRa%}!Fx~;e%TJb8w=_ea9RY_L7=%0&^@ z7|@ttVYVm04wW2bq3NI9R%*=wE~w{p!7%hPTm%9`2Z{1 zK7kjPMg-0lf!;jn?}fvZnu1Mg|}{{IJbW;qr;0#@Gt z|Ie^!0M9E-fakwN@Od-`8kHG%z~_!OGEAsd^9TU-bwPO(bcPS8Yyp+M){P7wtY7~B zKVc!m2T*+tIXm>%S|h{Gl_u4Lo8Am7(sVpgXBT(n<2*_>iz#`B51uDsEz}L14tYcW}y8Y;QsPMh7}X! z7(nZU4}iiCbj}7SEJ5jfq8$SZ$Zn83K<)zjVKIXO$Q=&z{?~xoJ0Nk8-QNHIx4_q< zFJxE&8s`Jq1!~8F&LRTsEe4%k^n($+H*v=AMur96|G$9y6b-Tr5{jH17q?udspF&4JAF{{IC$X5;<;yDunzHG}F-Muq}V7=zA!vYyQl0E+MW zr@uk#c?_#F85BTi2(&i3{@CXV(B2>K|9@>^b&$h7h8mDuLsQolkUVH^)%*WnS(v=z zJ+OH!-v2*<+vN>RT{{{Ub?pJgFGvr_O`!D<9t>>YaYB%vz5jpK1i8)o|7Y+XSx|g? z|6dQj&jHkq_h4WJ_Z>VKSix-}&^QvfZL0Y1;x9*r1HVA?&WsEnelap^_@&6O<5wcX ziC>Kj7k({dxbf>D!;9aF3_pH5GBW&5WaRkW$SCl8A*00agW$O_jhao`72vy>zgbs+ z?gO0I$Y=ph(~OLuJIWJYFfuw+B(p0tHZoegVr19=isx5~3_Cz$AFmu44!lZaIPt2H z;lith3^!gKWO(rEA;XK;j0_)MD>D3e?a0XRI+2m%bt9v|>xGOGuMaXRyne{2VWG%q z07@(REevcHYzzuk%l=;g&Gmrxq=Wh~pgs(&?*i(#)Gr0KwV6af`@=wE$lm|!!QlkT zf1tTFt44+yp#1}&IU&$o9Y~GULxv9T|9=weRZfG?`$j?QW)W)u7*e43gBXCq6*Q)U5w@E^eUyJP zRVx`l^LjOtI~i0azhsb@e3C(CQYLuMmWKELzkfjMR3Lkqet`B4f##)Fc>n)*Bb4p!D-a7hWdK`#+`m zAw!R)0s}K>ey359VFf5XS)cs>-~0c6n+8UPIrW{OJ97Ukfa1yF-@gMErI0?f_y70c zvIi7a4Vj>_l@Z)O#TC~M^Zs*y)Pm*|y#N0Ptv}TOxwT;-Lk-An-v7UU%t&Bh3}JwT zx8uBjKR|bSHzYDFsbXPJs7aFu0Nqs!@%yKn;PpD__arIsKk)wl|A3_e0~08{d;k9{ z;K9HIu17r>n5KftQi%R{;4>*a7?{B8_8?{LTSz(s?>$-t)&pu6g3CNd1`SYO7_?@% z!5MXrf(HX5xZZ({4S>c2Kw|?SweWdgXT%(s_kRPVI_MpEFQoT>1MuE`s5`)8ppbH8 zCAiOsZnpRTkKp|Qpz)1HWys!8&^XaVW$<`Q04NSXdykRx9NIXS38=oEz|6p4y_dlN zRG&iZTMnKdfUw_!$3i?97{P7>odW^7uWJG~gNXP4|7T!sYIN#4VyXNebe_)wkiCsb zT?asGR6urv!U9xYH@sqCuwMNCK_fQ<18B^xF{$eW$WCw>zW8;;#7qW@37rfK6IL>? zOyp$XnDCN;=LaW)f%pGK;CO(#2kZ_H1_p4t({5$}pJ4!M6T{YkS*rZM;CPoI24uel zXkTVMg92#%F({vc);WR1!F4oreiAg+i@eqwBi`?5gy2|(>q0IS7ekN1B)@c3qOlQA2YCk_6&g194K9b z>PC=%y#E{kmrI~>7c_1IIm6K%%CuNKLdmJUmNf^DtNpMc|RJ`m^es3$V?JB*|Lz0l4{%xY*9vz2C)TymATvPu4_qfOH3mT3vKO2-L4ABs-yRfa zAh+PKGX!GiHt-x6#7;|aJpzt*&^SKYdT%s4Lm+l;1G^coouIkIB^m*s@C2Ph0BTo( z{O$cu7hJZ2>;v^BK<)*lF0QsR&nZW{_2bdbcYeGTs3Yr508VgXaGJ{{`JYXX*L>e|;ANDD5q%Rrpr`Qdh(21>O${8fUN7 z{8vza-^0TD-#o~;&wNn3i$MppCk8av3Z2hp!L_Cl62}(c@Up03U;&qBe>Z{4ZBRMn z{ns23|Hx$!+Md{2&)|xQ-k>({{}@EMybxShgU$T^0h}i+l0j#~g6Bpig2X{-)%))z za2<*st~k_!-HM?$;s1Y7x(1!kj7-Dpfrp^8G{JisCV=J^8sr&rKx0jyeLtXcxEeDV zaE|HfH~;?++A{_Udr;i~%KxA`7?HCk5^?PF`^aA|3=z-M z^c(*F2d#&$fASkNH}3#)FGw6){n`Ble2(t~T=Gym@$FH8>jmdi=s9Je@Q3G9Xr5?z z%m6;40y!^$!UD8D3p~D54?UCN9)m%nGJ^nUydC6shdB%i`YiwdJI-S8vCRJeAGE&} zw2!O40ep{GN5edZ9MCyU4)YjJSTFehAC%|o7lZfdAkK+HORJ5}3;`D0|Nl2KGlWcF zXJB}##Q>fc{Hevj@G^se0c6k53w#gOy1j^FU@zOvi1O z!IO{l=YrmuR$MP_UF| zh=GI^Xl>DdhS~cVVj5!@3@qdsVjFE31R84?6ee;qFihlTV3@!K8sq%;pi!wY1~lK? z=+v0-V1B7$#~n2rS@WVA;UUAhJ+{fo-EU#9YwXP2e&awCBqa z&l;?K+7+NRec-fJBV7UN*E-H+aPeSZ*io6xpx`i*fdjM_7&JCnfB!>(_dhl899yF` zhq(+jjW!G_)vG{fo-!yP;qlv5plKA5YX_OK>@U0t>HF9 z2q-@5=ll|R!NI@)N?VQU3=B04K_Z~DUVm^v+&@vBfuYtbO5~>-0|Pj{*)k}A+CeC3 z%@$HWfYL8=daIXX0Hw8pMr&vq!?U&uv|kt$w$^h%_u(>#fZPU3Ycn#zW{{NLKz)nG zq(&d)_7eCU{J;9(aZ2>M8jpJLo-1_q$ZciNS`lde2e-K!nHqgS?T7}Z#tcyT25QfH z|J4VNU!a?VJa!Krqd&;70o2|G)kC0mAn3jUP~8o>Zvb?*4EQ{mL(5|durz0IuqS#DwBN}3?}O^n1J%2r33?q z#SaDnOA7`O%L)bw%LoP!3kimZi98G<6L=UxCcI%#nW)2HFfoV0WTFp4z(gJffeCLI z6dIEnb3kJ|ljRt4Cde^FfWwUud=4;p4GY86qayF@V>;O{`-Om>9<(GEt5}VZuKKl?nG43?}SjFahtsfbP8j?Yo$e#lSEj zoq=IODg(oWYzBr284L^)(ja}|#-zp)P@e@nZm7+`Fwq{|X4wPUe^VcOxuQBAbmrc_ zJK!#*A?7v_5T0Q0aTtq%$NWkCk3s!@%}#rJl77A1F4@Z4;m-X0*gV<0%-)5YmG{c zpnDJ|G%z!Wfcgla^aUylKyG%J%V5!v3|jvw2}vJHjXt398gLl>)q<^k0w%VP?-bTOY8l28F<|!sBA?t1H6tD)Mo(oZ;;J^)!hW#rvtLb`@aOZ z&j>ObaZkN~@rC$jE&? zq;ZZ6#5l(U@VG4~ZF&E{51#Y>q4;khBzzbhKx09_6B#W)bq+|)Z$(Cp-wPQ%elRi? zfa;fD4;dwXGcqdtc4Rd8eULHW_d~`A&|V(zzgr=B)P_L?TzBqa(3l{@paUvfK=W0T z7cvxh|AVXp2CX9ntw-1IhL%&@42%;vVdYd0cr5`)pZC8W@O)D9qs9r%PK`aFx~h2> zdOg+yZoh)$K;;i;uLe{vE2vBa)nTA>V7&i2fX9hIc^Fhifyy?}T^A6w-Qaby%?}w0 zpz#A8a&9Ci$+fdoMT+zwAH|% zfTUIe*LYZi41)&9&ogQm3>@b%)`Y9F*@4;?4)YidUNABiOxVM~^6DX@1UPLlGAg`Q zWYl==$Y}67kAV4e#jVMp~#p3D(jHaj0%qWZ3)Ov;Qe~@{v83G zo$dWM4IBrcumzp1UQr8fLrt+@WGDfZ!Jx2iderDsApx!1Y8eNM}ac9Ed;0ot!u4O$cMcOiJ51r%GZ6&7d`$bHQzb7N|IQ>=Be+ z(9@jbTm~Lk{NPM``$1{16Py>JX_@%(Rq#31J&^X^lK=l5=KcQyDg(U#-v)<`_y7NI zKym5)|Gy8Ewt(Mp;RxD4&hVk(FoOcQx>ena;HHl{H!fXWomo)-v0QIFo?JZCr zx_dde4*38cr&45C@!AoxhURr5!;aUD3WH|BqAj5^%4;gM)C^9?%rSFDnh8##b zWuDR?4H>s`yvJMsT{p}>Gm)WTq9enUiHQv0wa(uEIKb^;Q2!Hj|Apf{#yyRR3=?V- zB7SH$FfcmIWp2>#{{PSVHK=dO9MR~=Fr|KefP=$a#s|rN8TgwU874r+$(RFd7*?$c z_54wnz_4m{jVyy$2*Zy$$bC6Xdn{@h7(w?mgTwo83OGN3!dM@C?+JrO10%x}&^=1p z4GjE_bD4I4`eL9p4WM-{-v30v?E{b+sQdx#xeWXuKTST!&;atwL3*RV2IILu|> z0htG4gZ$fYkYR%LLxzHy14bg2_6&>`vltRUXE}l9NI?Ds^*2FgT0CR`?GG2JfAJf% zH@u`l7IHr}XuPN)7O_469KUlIL_q7f9Og1KfcC+G`YWI@SD4=%=Kej=kjT*B{Z}5G z-$3_!dc2%P_Mq$5z+In4Wa2NX`AaCMx^$OCdCD9l0OJrjJl-hUR* z+8)q(G7k3`moz3q;sCVnNV^*pj|^+-S26@NEM%BaKUV{^_NNC_wmQyZr~%1?!X9)d zCp^5EnJa1%g`A;RMCkA_CjJo{e{P@H@J zV+EIypnMfl3p!i)j|C`xy#N0(fW(U&c#Q|hJc~qznvi4$EAPKa;P#65|37mecR2nz zgCqxT*MRb-_y0ejJJaC#1r!D#yFukXD9kJtFfe-m69m^e-3u8CKxxAJ|KB$t8Wd)r zI0fke`2!R-ji5b!oJ=D1uNeYB@eV47Js22FK;odY(;H?piYJG;3<41U%7WL?fZXW)za2cc1`enHr@?b&7WK&G zLjrgX5L7;R|33vDKZ1nE?<-I};Po$bYMU!>%9Np0f#NfZQlP+g8Pcz z|4)MFW4-_X4*{=>Z2A8mbngZ79TuRwFW`4wfbPUYyYB*g=UA;YcyIB>m$v+qw z1svuvfX)tOfZWT#zNN8|k>eF;ohs;FhJy?nz-u5$xR0UHnL!1#7YkIkINoE>XjEn} zL8@24XN`jE6D|gZ8J(bh`u~oGxsdr7$9c@4vIIPb;dqZZz#@^M0USmO4E!O<3}&G9 zhZYMMI3_AGOaavovkx*9fYP=1zuh2Z;BwGXk)Z<=pN?~xW>`Bi6oAscg%ks$WdZ|7 zg*a%Al!3uoo*@BrpPJP|h6<2=P#%PpkBzm^@{xnN!f`H>2WZS2l$SyGiG$jLpt2Wq zhfxkDx^GQyaFl@9p^G0f!W!xkf8!p zCWF#Cs64KJpb-E%qtI~@g8@h!6b_Da8NqFUhPw9|^)I7D zQ1eGDxNQe&Z|Khi*J(PSya1~|!Ttr^N!Y@m0BW~YgVyx@;{~@LLFyp!$e5u$A6kck z+60LV6DB`|l;0rzpmWS>HBX6v>Su6VGcYhrR)o}fj`RNMfcymt!-hoEIJWEZ`c+B6Fjzoq6P};9aW-syn#{wSz0F}egJrrxe z=KTN11M-{0y#F>Je;~UHo1a^8WvC0>YdaaJYE?|NEeR{$tSkaqu1l@BhcZacfb7TpmV) z%Scdsd;dQQavB3v4-2R~Lxd5mtQ7~hmmp5I*qlyeOKw~}N zd;DPMkv?QN0dfQKd8Dv;RmXV@0v&P;vEVv-9)knu{1edGboGxxB0~kt-yz8i7NE2XO1Bdg8G2?Wg3GZE?|<9C zb#((HLk}pOK=KdM3}`yx0JS%)V;K^xLFaNNGIT)8y&eljNcq!f#}EN(3qspD6(BZ9 zZ^J=`4p18qbXF{=Z3vnt0L?9d+Jp7#r$OhW$biatc$tSXF9~XogWLMdj`Nr#z-8b( zh6s?qLGA>((P8etJq?Kr72bbE!EFann1aqg1%)dpOhM=Ufx|VCVFfr`8yPmdUdXWH z^+ARMuOBj;uux>U0P0iBsAGt2RAd1Ar(q#O0VuyX&Sh*tDo;&7VxX`DNtlHbhb89pCbZ1#snJw0i81h8lwV*6X;$NNLVp&G&C}R*DitP z5kY5;djI19_h&)sKx!Q4GHSrf%tlDsm<(DQt;kT}IG5o@t>!HeP@57Ix1cmJ8JZRx z=l%Tx3O`U7H6${0fYOEIT!sahW#wjYTODSfr6Snef?Ca2h;nivBz<}R|GNOx&PK$+ zzYX61|J8ubsDqV*pnT;0|L+%&n;ho-Zvoj4G7}U>Aax*jg3|uPM1~G%Ip+fs2i>m* zGTZzA|AhKZP}%ff15qFSTLY?(K=V2li3}x>c54K9Jt8O$z5oC70L8!eUt#c>bKd{| zT|iO?UT5k3|DQqqe$bwxe+i&+1?FF9Td~6XuP}J+ETsIZ0hg1Y^yK~jAh_&B?MsG% z$J0RZ>iz!!c7;SMd3_3mG~Wfx}`R0}tq&X6tEszAKFl>}( zNCT~Zn;FlLF@cSNVZuHJfeG^%L?+ZRC`{f5oiF;wU@}3TA*Z2^!J+}Qey$mO4i0EN z!;Fd=28HH74B)#3LH#+%*iJdPJ&j{LaS6^b9q@Q!Ik-&^N~@r}2^t>)wF?Q2CxX_p zdjAywuNMK`hXRiCzun+E3mmq81;FtK+V2ZquYq*89{O4h*w~Zz|No#hUMTAZCxGVj z=P@j(v}RCf-VHx906L~s22P_OH7sUU~puAfNPRpS2 zam0An32>Oe=HP!EWaI$N!8folz{tqAP?3>m10zFK10y5HkB5u`6Brpv z8WO$NsS4hHA{_7jVYk|50nN#d*mnDL+%n=sLjAJL7stQ zqdf!90&WH_(E6wi@(g?v?HL3Xa51nf)M8-UAkQGO(VjsJG*=BOM?mWXK=az*`i{W5 z0MMLeLmfj-1?Zl}6j1p71NT=!b*#g@|0$4mBSQn|E*g+J@4w%9L2V?cybI{O7|U3O zn9XqvX`AgBQYOnWBs755nRJ2M>KO|d8LB{gIUDXVBs3;6lz`5{T`0$pw%{K_3dkP| z7#S)+b9Ec!8F(hvGjL3dXJFYV&mgwpKZD4G`=E0v{_B9w;sV(X?(6^ki)8=5BcQX# z8*Ld3EMysC8*3Q^CVDe4OyFQ(0Ivsz?3)Y(&xb(od2duR6&`E^ix4id*- z!ToZiIIf{`93KG3u_HqRXwNA)j&Yo=1giTT<}rZU{-FEW6+q`wfx@xAm3@o%-{0VN z5;PtYK;jmS3sf~Uc1Ea&de@7ZvAmg?U^ZxCy zt_JP1`nRFc3L2kP81dNwtB*iut%K?tSiJGsmkUdaCvo0zGum8YN7tO$57ZrfZPx9&_E=d2a4mM}o3T~5- zSpTFn+M%vn$6x;xfXCt}tAFw#^#ob<4<~r82T}jFfZK!My7_-Gq+UarZ#e`W)6;MH z{~uHif%f?$mph+V2Q;2y{TsYTP6M=0 z9JF>D+Vg!iKGbn(^Q~v$|k7t6^K;@Tc&tu>RwZ%a9VN8%^5Sby*z%V(Mfo1Yt z1_@A`yWaD11!zoVvMmF6EEUvGtZ(>N06HHIbhca#^CR$ma^U^)ko(0zb38LZ=U9RE z%7e}t0F`Z^^^>4|^Ps*NXsw{bJO)^uBm*8-uV!FzoXf}patqiU%%J=8D6>a<4ro08 z|C|O{29f$X;5`r=pmmlY`@rW&%x7R|kY!*2-Q(mik6{ORoy}i%aNE4r8a!{p59)7B zkY$hnxoh%Y29X(@pu1Zsa~EWMKElCq&Od|t)1dMBe;W1weuKwT8fqC>y#N3E13uUB z|34Qn|1TSKuJ2z6s0{S}>kZytN*5LH_05o4bVLyY&jFsSd z%nP8g$X5p$3SPn1XEHK0fZAlBHHM(Ig|DG&3SXnGCrrM>z~6MAK}3HJ*j^Ttd@X~L zuLDry#seJ2pu2MPr!g>C?}p}Uc?Jp4nHA7{YXHvI%nS-uM;R2n|Na1vwKd2yNI>M7 zNzkU-fO4=rNMtJNO@~beK$C7Eoqn!&QljaZUBV|I6PK@<{SPg z)L;K3(y*UFq`noDCKyxd_h|%x_V+;MhZzK*^=Sbp>_L74`3L0xnmR8L@Yt9<14~UE zlSoaAlnBUdSXg-f{R7TZAU%!t3?g7Xdp!a``Vrxw1rHCfn>ZO5z;2nY;ZXN)H)u|l z15|H-(y8OTe>Xs8f#L&X7RU@xoYXXb5~gUb|4AX zD$u^;2ByX*puU~=-`ZAC9}3it_x{TWc0X*bBB;&-tyct@0qVy>&d1CG=M9M4cY@~x zKx^5d?FsNYI{xljez#iS1LpH zVj%kkv~RaDsYwLlpC$p&-fGa;3TTZWC_F3b7!*KZ>NuCd23FpK>c{{z?Gc>Dt%pTuPb9yjBQb3EZ!1x|O6@Pmvo z;IbPMet*I7k1hP5X-@=}_R!KDJ~x2J4j^v$3tr1izzyKJc{1F9C+;f2Wefp(!Eu8- z?ke%dKWKasRDOW=mP2U<5RFu3fY&R7*3g31Dud=>Kw}=woeUf`OSCIMWTKN110R|S`L52gM`@6vXI?#FL zRnirp^Q;}`F;sxg%dtAhP-OLxp}?Avp`<=VBLK7ywASvCNW(7%iK@d4;4?Hp=ih+F z!K&F96hQX?c5pHids=WUhgZp@(F`Rno4-6Iz|Nn!^^aYFz zMH}Q97(ij?Fzf#c&>UH{GK0dagW&xz9iaGm&B!nTHeZHs?h!N(3i7*tI|BpAPYY8R z*g*R-L3=XcVUftN0v-mSaDj#a=+3g5pVpx9(Gt)&>BdBcDo~#S6xWXP7_NZQhT}Yj zGvK!Re(efSIKaXMbRT`gF9wd9{SN{_aSfVN1ce1?Uyt`cBXB!Hzxn?^P+He%W?;}~ z{{P=`4g-(FOa=wVHii)Ey$p=?|Gh-&XTD&lmj->lXKJbwo6e}Wn$llL=-fa~5&@Ol1t*O-IC)BEom@O}>O|NqzgQ2e(M_d-UC-v=2Tem?}SCr|iwkdfinLq?9@jEn-m8yO8i<%##d zIPlne^P?u0W~C+v-20c}!0jodb>A)^yS)F!fy-;q_$p}axA$Km@Yp{nU4z01)D{Dc z$vVzs1g}l^{uc{wCpFqb?(8OV{}Sk)42D++85v$ZWCY!D1=_!q@S2fP;I$$nWWBW` zqr&S%Mvd2vj0Ue4GFrSo$msC;A)|+dB4Yrk902WO0_g|sV*;50-p9nqC;;Baq{t`% z-pAy~r~sPN0+|8Y%LLL7n!AGTXG$P)Ka&Q`evsYx_cJwlfaVe`H5nKuBK9+_LEFy+ zI{QYz`>zFfO#~=hLFeXIR52(tDmD3l@>~N`QvfKOn+`QuRQ&+0`EClCXvM%Vc@_f; zs0{^amurFBk>3A|!0`o2$KLK^Z|@ct_TX`ee^u9$R-fx-LFSFnBF|Nox= zsqy~*{{`~iQP5sY?|;$YG=|=O##9#r%3cgu)Zy5l(nR8!9MRzZ26ztv=6(eN`%|L8 zYhu9b8|RUDCdZ#I;I_F1{7jB~@R$sF`%~h;{V-5j<^6vxxV;W4!$4&UC_F*pkaIZl z!0U<7!xD#Da61xREvQTdnG0&qfYR#^Mur6qOieMM`C#wA<>2-@sEy$L=QB91gZ%?N zQvuXI0p(**odGff`s&eq07`mOH{ox>e=rnTM8I;aI?N!j&0Mgup4ycTx z%Gn=(uS4Rx8MJre|Np^$_Qzrxp8WyZF9VG~{P*O*&S*g0SD)j-!0-gL9tPA_0_|}J zjg3OiXb=OZnTFpC0S&jI`|5WygoMB3u+g8#!0>{dfuVkmmq;TUWPd$)Z@s3I$PdsN zAE^us6V;%5>s`Tn>l+#|_ttBG<}hJruheie1k^C_73eT9Fx1F{&aeEy;Mn*-gT(#y zOrSIBLGH7TVqn|=*p9E0L{aJ=iU|=ReW9>N zGgW}@psLLd2>1<}qibZC@Edfe?L&qYpt1JfjtmW;`MjDn+Tb+;HGi!uz~{TkF)-9x zdRK(j^Z%=5cCK)o$Iw#W0a`P_P*ZcuxT0boouW2f9zgaUR10$5{*oH7U{+j%^HQ8s)(APz)9skn=rjTbM-ZB_$Z@ z=Oi#poXfy4p$@zT1Ju8n0a`2hf|20>cs(TAeNmuw1fY31@R`*I85V%%-e7ZXAUi5K z85BTi9@@W|&`=9HCy@<24-1mFXa@IlDy$zcFoEm<-2(--N0FfdJl6-x19giczzF2-V1=+k&V?1 zDzGvhbZ!bLeb(>w5&@m>4J!LU`;l;*%cB81mj_gjfXYhbGQEBt1E@UjfS%36w1B*` zc}ObDL1`J3&Oz%Z24cL$0dl@PQR6M>bA64<3^}0kxu!w60(71zXg&`#uLqj@!!_?0 zvW&w9G^e4@z`y_+#|F)lI?Q8m0nJl@`VFAD3Q+qKG(QJgF9l6=9faoRKxxi#79$5} zj0|*siuF9u{?&gwL46SGItHGabm>tSU}@nh_SCA@ESYN z{ut0WSG@y!322Ys?5Lv@_U68g94}y0G-4C8?+DVj&TL3UTApCz*4Ekpa3e@LFed$&i{ai4QS5# zAj5{m_6%%`p^8~Ab7qD)GxOLo#A{KJdWo9Du4Cm7#KkNbZVF%1k^WtU~rtn zkOE3G^)8nys*f@#fX1U7W-`cl|Gfq7&w=(=)-Ze$0f~e9L?C~I?}UNQOB3U7m>$qQ zoty0$*g$$V%QLWm!VMgL1lAh3s zxu7tupKQPYs&~Qn%r9h^Q6a*h0J>|s;uM3zZ$*Y3za1G4{7z&z@w<`X!taF)H$p4< z|5+z9aDc)dbO-&fhYTygch5g$cmcj6a*lFEt*v*(L{Q!A9b8ehltH1cc`>MNE&!c5 zSl{`Ap@#ECfIb80JRgP_hnWluKxGH0d~UeSz|im;bZ2A$xGxEc6UR9WUm9*PFo5o( zZ;)ePusFuRRKp-8GT|0v?J&swAh&|-sr3%70NDjPTM2Y8dc89H7E13$U(n6K05Tiq zwncv!*cRPkU}=^Cr5%58nFMktsO+q3SzJ+(3R>&H-~e)K1v7&}4a187(0*3OISeM= ze{X=-e}Mc0l85-^mk7vjF#mw=$Ff#nVDkR|-(j*01H-023~Zb3fab{m8zANXLQo$Z zG^Pn2+l02iLHPqbwpj;S%fzq%w5A%AzrlHQE_fWarxCQDWdQ>ND4&DYQ#EQs_LIZg zgh+crz~@djKduPj;{5~4)1bDATxE}y^WwT z2ymTC#J$3x_A02(0L=@6<^)>~L)O%R$`Vk03feygIupiXAwvmh{XS^j{su;d3h#ej zu=0^SO7}@p!5!E7l6b-A1`QeRBO%|ZG18A%tewN{%d*Cz5 z(8oL*otiAb>QK(N`;Y1NgA4_rG8we~1+*pvR1Sjn1UbxOFaWs?)HVkB!~4%Y#QGO( zYe7Kgj{Sq&(~hd|E_kj791d9YVL$7tsRWdkKxH?mylzx#ia|TC3(HwoiQqA8$k_2+ z@On8=zJryYu(4yT_9?;b1Fz9(OlpcjIG9XyAR#Sd7{ z97_PVSs;G610KsH!4EL|u=#-lapqV&c)bR?A4ocn>L8;4?lY;fAma^q&!ln!&%5G3 zlgbfXE`ai+_dh4_TB+toO%2UXO*NqTN6a&+oFHdU$uofW3OUYW41nl$1kXi*<|RSp zkN2N%;4vFeeg~y>(EbC^{t&2I@Oll*GpQWG^HDg~?cc$_Zr=gCrVYBzkdia0G(hbm zcpD3;UDN{g6?Sptp#aRYHG21$WYS2)C4}831yDb7IH2XVvdrJ6I32IDm8U9 zIyLn)GBr)WdnVOCQSf{fC@#JKIfBd733UuIlm9V5)*}A-0bbvQIJ3$gTpr;+v&tT< z2RxSvorePLi3in%-hacv=?oOEuMRQ_fWi|LuAsB4hzV27bE{&(>%YAJ{{x@z1fCE5 z^BuhQ2viPCx(k{Qy#bydg``nC@Eknm8CLdScTu+A!VaANNuG}cpJC+)UaJXOzlhk= zHv!x)2IVo(dUfyr|DQnFE>PM4RK|nKdeAxxa9IyOCy>;6SUZUS$+{cC7BXH=>OPaj zuPeO&TmzS<7Vvr4KyV!anxFFia}69O;4&P~SbiDCfca zJpw5su%8DLN8mh|E8w#XxMdbafJ6gn7_xtcN2lmBLSUz1M0_k|Gx!pPk_!in4HMaK%4Vmik^b%9FY4!Yq&t? zX@KUILFc!Sbso&0OW?IG_|Ag?odvTX5wwqUKiF>2{1IsE6SRgEd_K$ne*%y_z5gAc zXC#2nkpsC2G*$vy)Pi&ti~}glAm<*4gZGy}&w`NwjjdOM<^=!l2lo>o_WTimo(02! z&?g42J3(hNK+kc3?k{Qpk2j(2VG#xIPXfiQ_y2lGT?siaMgmk1!@?1~uLyMB+n3?f*yAG6|z5h3W+X^5zAmaN5xIgCoef^gv}H zXl)}E&UlFf^?w`&1FtSe~z5B@V={+x%^e-Fq!<0Y<<(lcKE z?uYcLz~v;wengh*gT99_eOB`rklXhpkG(~{=lF+^8IL>%60{0nl z^v?;L@nQ%*6A09g1+`H?{U^}6DbSi+d}l_3<~_XsoQBk2@O|~J;5rL@#><~m;P?ZN zVTeupycmMhD9Fv^pYdV<@jt13AJ7>ue@>zK-v#1-@EI@0;Ixd| zA8Z1L32a_=@SX9}lmc2y*XY!g0a^>;b*+WH4=5k3#48G z?F)mgT_W#{mlfbMyM|_6qz&$8;i!uUobjRsE_JTo(7p%Ix!T0m zKS*c15L5qXfybyQtA8{h^#ob<&tb@T0lfZ&j9-H5=Ks2odJW}_mm2VyFc|0OgYKTJ zlCHowKR=y>^Yb$q6hLcxEw~w&z-xuG7#J3>#D0c8=-hMA9yZX~=aihE4;miuUVi0J3qfbzxDq=>!+aeI+;B{XQbE92c1`g zaelr7c>D);UJc&!^I5@dWYD-SXxzea9>)3k3m{_=3<}`$^Z#xI? zQO?hw0!kYowV<;%LE-B-mrCd7|J?~rFQBm^P@IAKxul+-uie1F=y;c54|(V3gU;vq z>rlTMbcV=Z3(#5h-v9q5fX3HA;Q<c6x84qBf#)t4gU-uO zWSHXp|L-4Ie1PHtl*U2s1)rDi$WT!OIxqhf+Ijho3>Ap;@`J$f4L>jc?;UXe88qMH zFz^2jkl7%+y#N250_uZ;+y{ymXy3KL`_Ddbe*@%3@BixHc}+ZL2yVN8 z$^-BJ+2C-4gvakEP(9%J8c^6E#{M8<+TQ6F9i|pE2M5vv!l3n6AU7P29+6*`>T=M8nYmSfdQ1a9Og0TfX*vg zP|v^y>VHAq%mQ*V$h{zUg4_#oD@blbJp1Xx%T&FQ9cdAb)`T1M>?gTwwAragZ9YKS6ii;tHRbT2#eGSHkfXiW`l zJq>cWf#M#NA3$lr`=0`&J(>Vs`v|@t5wyqIaUKH)s6XxfPaZs`4~tWfA7Fk&HxpzY z$Xw964^Y|yn1KPDM?rf-<}pC;ERzS<$2j%?7vNkUavyYWlpI;@ zU$Eb?tfxYnOYQ-Uy+F<-QUaIrka8Dt1|`-r4h}Ljz}Cou${x_#J<#~3<2(lBGY;6{ zXB@~euz>bLfy}LtXHamM#Sj9n8~$tu&wqulBcrWZz=Jz`$Oc?Ikin4zeb1 z@;(NU4cy>73*I*eN>iXXM88k3{>kqO@4t7zW2WB!|1W{J-4hvhfX=T5-*xqn;l>X} zh7aKM2il8SBV7R+gZl+Kr-YGV!Eex=SdI)EelKJ=0lEtdwEnPWhc@_J3-B7mzZb!M z1*^vljQZ0-WAuyypnWKyy$yB$cUOSU(6@GEDDhxm-~pXW;W&%Ir1}VhLceWtHfbI+T{`(HRjs$G?94QgdzJu!73<{2O|DOPv_v#_T4A41|uNfH@fX)_ywH=A+ ze^?|iFj*g9VD$d;9Xw6}3Kx)DL2d`RannPFrb!POYBoIt-8J+-0kl86F{x<_Xn!_n zZO0Erh6eC`&wsXp(>nUvvqq(+Eub}rp!|zRFSzYQre1Jc6Q5r6d=ARvpghpP)GPw> zD`?FoC~QG#9aJZQ^VMGi@OWlJJ%a#D4k`{#uMPF!`>+^5dZ6Oq{c4~x7__buWRCYA z$oY-9@-y6B&^i|87LdDOc7n`-nhz<@Fx>+lN5tYD$Q%WzPLK!hVFlj}i(LNUj*l(i z_8Y!1#jO`|rvRCH!F^jY^@8&`KE23kQAe490i1pn{~i2&kkJ5s7jz<{#P3G%*(w^p zA2M2i?t=!U6~`t9f#%2X^XE-K_bXc@FfhJWWaI$dmkci75*fjFCclQ>cMQHaS&`8J zl=dwW7zDikd;zZ?0_9my{DRN<0G(^E)U*Y3$58`QvjphegodVOnd)c;g~>Jy43qyr z&-eh3$v*?PoxT6w0QVh0>Bak>6gc0(&-ehVy9kc^`US5mKywVFx)o$DXq;uTEogn( zM)2AJaM}M~8eFe~<()5Ac>lQoE|)-e7$b)*?zFKHyw(X{9O2Om_A@@cjZRHl8km|D zKy4t{z2C_0LW=W?zaKJc{B~qy_?^he@w<^x;P*mCiQfkq6@D->I)MBKj_<|rbN4~_ ze>zwsFtELLWMp`q$jAYX??y&}*9#dXULRysc>R!3!$OhK02eFw$)#5x9s$@>@}cT|Jp@d-G*LG_*YpY!1KUElDy z0#qivU}PlPJ)krNDw8J1f#P!mcw8UU*7N=^#R+O7VOfj51yrtq!;*`EVImhu-+FM{ z5k35o+nJy?=5J^_1C(bPn3^>}VF_w~VlQL0!DD<4^$a|q`~b2OQr2mM=U+i_2Xf~` zJFxw*_y^x{3~Eb&;{a(-KfFAJw&#$_d9XXMyFm-mro?cA7P$NexdCK9!7?0bX9H8S z2`HYycGpW+G%7V)fYKw(Zcw`);ZH4?|Iz9gEwKMF%XBSp8fd6z0N=lZWDdA(0&*)T zojA!p<)1o!V2pv9X7{&>>_mqVC!qb7KK1k>G`;5Dw8?$!kRAMEbM zuPY$#UJGt-gZd?)b|Z=L40bmxtU=)e$}5mGr3v2SfazupSp0+Tw?wMXHNf$Y>1GXZ z{SR_8$h~;N7rY+>J_f+lY(spy(1681TDs7H#Xq`R!SRp9t>80rKyF2(i#6c01a~b#4+Grup2Hpobml<@v z0(d_ok>@c~*E1-9#&AG=B+!_p_y2|9ejd`MIHn-sJn_R~r0&*Ah{NMwyGatZZE1vTr8sr#QkmJX3?!Of8|Nni! zeHb|giJJOH;B%85=Q65bp0B(Zl3wK)B;aAU+amyU_mksXhAXw2nBfPq3l?7vbN_{a z&ZPvet7m4efW@`LoPQFabm;x(6C|JPV-WHF|BnUaR&ZKT_z?gy!(r~rHQF&i&c+9stEmMZrv;e{vG>0Y$bFzVuBnHdMGY#qK;y*Te;0wrWI^$V z9!?H(|K0Ka|9=H~{R^&tY8e!e%X3g21S-#Q*F(zSHXdf#s0{8CVwUI1;Jn;Whh7gU zgUf$Vc{9-#+~>eoSA)wLQ2AotQ2!rvhD-9C|Nnj`GIZ3pJa7P&GoZ2rRQ7<%67ab& zpz{i5L+@n(ogY;t4Z7PCbZ(PG{cO;EZ46J6>loNUcQVw^djPrv(+1Snhn`E#6p{?; zqrU?8F+goE(As6|U(hq!Wf>$uc}=^4f!%Q~;~P-;gVy;&?p`oG3?h@~GO&R1S$+58if~O18_?M+p!*kUHKRnpd8-j}XE5?P zFB>=+80r&1XSUt~-^Wk`IkWWu==>M-Gh0Dp3l8%bra;Ot1{v*626l@o@cH)!Aopxw zW?%rh17sf9K1YUz8s=M|IpPVRb6j9%gX{;Hjq9EV@Z2D5Z8-cc3(#Gm|F+a{gU&ku z-D~l0Mg7SrP##;LJsT8m|8p8Z_chE$y04*=0W_wh04iTW{fWZCKEqpWr(Y{{Oqs zFqZ*zXM+fs|7R(?A^@>>33&ViG(K#>z`(xImVpIv zH$w`z?F4GWd;bpv_n*A~2ZHwxg4)L3|M|f60(_6IBX}%xLjyB|NX;(o3ecIpH4F?t zKzn!V8x$PC>o-7SXP`Tn>!mBe=Wf+XSAfm}aG1;R2AaMMKx^ATb<1la_6Ik}Gl+oH zg37S^Iwq0o6b1$F|9`;YgIr(f&tqT&l_iez81{h9i?*x<-$%giIG14wC~sD~uqf1I zONoHa&H#tk-;?0=y&$!qd}$#IO6Lp_;h7vZlj|8oKzDG1(mbe~2IV1A+zfKB_n+tB z^3D7Ie;&}8uDHwvm9vfYpmg#-2ILkbcT0q1a@c_KHpmT|Jwf*p{EvXD1)Xc@2pVJf ztq9)VeW6jAL89iSaRumn5>UPc-CYJdp9JGPorerNKz%6C8LptQIMBUVHGixtESMqX z3usQMzTpJuj`JlTvn-=PVZ-FnSjQj|n!sUG!^I>5k!SL$?|$J>9RWUfqNDzAL_q!g z8z6l%>Kh*XsI`v>0Nt_WFqdIY{rm?GHS!Swb$=NGz;nS59#??IbTgwRR2f4#x&i@9GIo^L>fzvWb4(29@x&I65>u*;eyUTIz{~U<= zXW%q!yGHKz?U&3xB~fZpZW(se$-mS(j${W4fih*Fn=~fK;2)DfcowS4vuq}80u$( z+x0b|umFVvXf6U2CZMoJ4)<^s4jWM1gW?=qj)U83N5J)eqaA}pjqEQGke@;rIBY=X z)G$PW?gbI4k$)uuiaU@vNUUb=EgWg+2)K_8c1IK>{y_c%gYZP}qXP$6@Z@DWJLG z`ne}ScknC$$<^BaLd|!aQ6i3W8Gqmov%}yz78L%Vum_nNzJS996wZ!w88={$vqRu< zFz^5WW`M%1x`jajlt;b)e}#@e{`~+F_x|$;++GHyH}C&{kJR0MB~m}_1n54YGa$Eu z?OMrU1M&|j>~N-)RM=fChrsm@$gQ9>0dg-WO;~{L&S6l3rvR% zeODCttg?d)8)`u9{2kgApmq=Ve6mXE3h?Ky5IKN<^LowZj^u86+(25n_$j3@qU+ z95$e~NKL1bNPX*L@O{nRe-A+76WSlJtc29bjkOFc;U_t4KzSZq9^ZxJby%JTxd~Jj zfXW3>z6PJyrN}_=yu12;heR6og6=s2m3vGo_4lFU8Q^n58LmLvY$xhxXgJi%9|nzQ zn1I^bj`RNaAp6sCE~88M6Al|lKUPTul+Hlu6XZYdzxyHM8L|u#jke(N4ECD&9sw4T zpmqhrn~+Wpn+bEVwz)2l>o1T#?>~>g^;)AfxUX&iYIB3!3<^`wxsRay0?Mn7a~WRL z%vS=ZOHg_Q#VaCB?gQ^T1nGmjeIMNI6GB#U*i6`q>2_FnIn4cc1v;K^2GmEv6hLVd zbk?f(|KH%c4b;X5-xoqezofbbI)>Kr7<`|K_y70cx*t&v{NDm9PeJ`CP&xy-p#gae zt@Uw*_uoC>yI4W$U}`|_1l6gaz7Z&|gZBS`?o0=rUj%D|f$lQ_yYnCeXx?=Ov<%pQ ze5Mg7EI?%@a(Fn-{r3gb$HX$OV*{E;`HeKcLU>$92;2w59M=&7&-;M-VyN>jg5dr! z=D3a^xc(t!TnC!Ys5pMS9y~8Fe8!La;O#5;*d9&CkGsKh`bcS?i1FigNLeEX9{1u0 zA;U4_dv!gk;hx9I(~cy+>V5d&*B?Dz71Xr1yM)I_;C+pd=c3{R2e_+2CoeO zm0j>O1`Q{~`0>AspfQSHjSLq+bKe#W39&Jc_?Xq!2%_r-Mdgap}TYeW=#Xb%0iCG{(a$IV(hp_JAoMb7!1OxIW7GiY0jY7A$Kc~Qi=_e7W@=Ps zP(ZAGWo2nphMbj&Sog~E0en_{CS>ia<2)7*(0W$TT2RnB&js=fY~h(KcJ&P&3=Z=c zjx^MP)PdHhv2cLQZ+Oh0(3lQekIEu}#e5dfx$_`9(D$b-$i%)s1+*U3aUSyuP`zcX zz`!^|j)4W?w|_st{iy#}z-0w2TpZ^8djoD${J#ttzl4Ub!>oS~5MlK1LAU~&9Vm=I zcOF!GGAMx366oFo&|C-14##;+JTP+|=P^kj!kbA2VHT4LOg$vLnIk~qfg0Y-J21n8 zc?l>iKz@h%Qvo@=7uGYdh1as!)z6Lq%@H0zvLmLU0v7fR4p{6ez+zVnvRw}In4W;l z2ZbwW&KVZR7P1VSp`bYK1g#Zh^dZ4c1uS;*pxN1g%}$Vg7K{wbL1XH*)*b=%vltvK zH5m+MYBMl6%wgaF#R(#zA#Q|t+6_Qq2kkTXb8Q9ul$9c>*K=##e|0}4?WU8puj0gx-=KlxU zD^uUF0o2bFXkcV0ftbNKBOw`d?miXtZ_YoHkOUUH8g6FLd>5Pd|Gx@o{1&Jgc-G?o z`a@XF9}gnbOdvwdgZgfx&U^zl6ybLF4oPVnFcGt z5Ov%k@R~8u9f9?8LG=K`4kY(QAeB+zF$vW2kns(sI~i{vyVGGFBRtMw<&)I{23@Rf zb-7#ts-r<}2G!SC+zM{9fZPhIS3&LtE@HOU?iPLHXMI z{~rxVd2|-sH}d}f8(Qa`0k<{1|9@Wtkv{`2L%jcg?*Z8h$v^+V=k+Eox%P49}5{C{5Z() z0<`!7bZ4K%LWUQh^$ZpV7#J)X86H?XVE=5%2)?U*0_Y5qT08HGiJ)_={(l9ZQvyD} zKAE{9B$Is)Xb%bK9thCgG>wT21^V6W+4|fJE{<~)PD!9f!kOQIlCUzXMPRsBZK>pD=_JYjt{{MfEWi~?!5`PWS zI!;Q~?lO_KcK0N>O$3T_Xu8e-&6zeTH9LUX!VOH#I-s%2W~HVrptZZ;^_&>%cXvb5 z*55KdznFFDWG%&UB64TdqL)c<~Kp>hrR!_fufrM6c672 zK7hw9!SdksyGx++)BmqfDNLD33)AD^F&(5ZO#y`|C=8)t>H-Q=P*{S(6yzV&_0T)P zeFE>l2jTwm{`(f(=Rpn6CE&GxXyHk;TfyPg{I~+L9=Zjb{~_V|4m`gFmIsIDV(^?M zXg&16gy%8vdbip*$i&~LBjJb_?#j{ zcrF5;Pe;Y@WFakm9tDpB;0aG3{NcGBoG!ip?t}Zw`|oS;7%^&iE(DLgqlG8YZUu)I zJbgBS_vS*v^9{I;2Fruf=R)w@+rWkAVM6;K!1tWu&hJ~n>C*e}Ubw%!|GosT;Xn<~ z1>m(h)CtdqpP>0JNO-;ikAot@b3Szb5vBZ0{QZz|#&1T(gx`vc8NVGF3w|duR{U;c zZ1}y9vE%na#tAuW~Fgx89U8KC~dYe&X{*NKc3uNxT~ zUN2~_G1Sy?@_s=ymf%ij%fciRAw$80I);Smy$~}j8W~Q2#?&XzV~FwoTMQnz1({pFC=N$}l3 zbCp4BoiA80Fj#@s5;(*$l4)YjFIv5#Qp#7GRhD1gN zNSlQ53uga?aSxWZ_6p>Ff#W>JJsT1k*}#1QMn;zK1{S-;jf`yd4G%#5+ys#Q2@DKO zAiH5S$bQfozJ-a5Y>kSH44`&5XpInPP5j0>Xxom6xlJUuyMWw|)=zN3;&uZv`w34# z@i~K$k;PJ)fdxmHTYLY11(^epXJ9}L^Zy(mKVybD!x50X(fVQ~Slpd}8s-c=$YH*K z5gy(RpzsE*bs(ah556~h0wW^>XpJ(+f1o}Hh(A-2k;VJ}f6$mAf$@QV;BW!y@&5n6 z1Q8z$b3pz=jSq$cnEql|LuPz{=gC3w0g4MyzZjJEt1}oBpy}V*`~OQs`e%>^mk-|m z|APB_X#NDb3*=9HeWD&{c;f3@W&BY5_YpiE&d2~Nrxj{g4FY~WWH?bHz*kUHE?uD` z03MTJuwYAF03pA^>!Uu|8zpUjQ`k?>LWX33$$2l0gDKUoP$?QulIq z1!!GIqa_0iXnif{zC_U6GH9(3XkHmKzYJQ_0Gj{z{yQIB1~*nRNHj>c30GW1FPq0j$~k|54&7p zY4ZOc$R6;Tw1o^MmI(~(6BaVGOiX0x0NrI-w|jMkl_TVi632NA5%p76FjyaCC~=&} zYy$EdNH1vbPL14$fErf50+3pZ1_p+Zr3^N8&XX&umN6()v4hr{UIVuwL1%@6+){u0 zGsy28&4~;ppfxJi4Gau5VoV}6QVan#a(o3I3=9UKH0S+y4lAf`w@7ASte+&iW`Ok@{*c5zyVk)wK)?4L2BgKzpn~br@)Us`ua7;C6xaW02k-4)t?>i8##p zzXs$k*qJAgvz8Bl>$>Kh3?lV&A2>M7VcY{cL&@RaKOOAx$xwqFpLMTySAgc7YdCj- z)*JCOR>Ia4;#vza7hFF#NHRz?T0+-x$bt4EFmN>eWMBcsA!xlEDDG-CcZp2i35iQk zJicILRH#a3P((QD_%7+YSE? zMTQ%%9T^_HPGorTx{=|->xB$IULRy+c>R!(!$Oe}e2xpq-=K5_T5DGS_eVhef)_ua z`*{{X*Qh+HzyC_4PPw}RwC^4i=idL%gZEw`%~kHvpU=S9P{|9#_sK3u70`iad z|MTEJ0KzZ-=79VHiW|pyj5*l+5~|E$16p?lS__Tv%N+0;RG42t{szS-$Pb{iZ$bG5 zl+HnS%);}GJYPZm91RChzHyw(u%c#A%umP>X0NqVk ze-fN`1R50?N(ow1C37MxG&{{uJ08 zjz&j@5>VMOfsvsFRCa*U9k@K;1dYZ10q^61y2S-l|A6w3_n&>>v*#KXGPHp96oLE< ziaU_mAb*4650pod;|>yU|71Yn@BL>Nc$@`d?q416|Nk<;W<1_q0W!mJE`tncjWEb9 zwVG1k{WwtlD;g?6W92PQ9X}A0pv$ec?9uO7C1j3(lSGg{v6Obb1V@Jj0_zXi3}yMeP#`b z3?-mESby|$1t<-B{}TrHqdqPuNN}hczuxJ!Rv<%FDw)pK7jgBp!5rNS2qKAodd*OufS=egA+D~ z%;*9-n+v24ln&9=fbK4<|IZ`>I!B~_&I|CIZrB3ooanv(0UeSIETHp5Ao0O8qeGm5 zrRp(k-kPbVp%OL+$rR8a4zbhW-hT;DIHI=0m_ThA1Jv`Nm@3F?hcSKt<%1a_3@p|X z42+;~28FlPf&c$O;SZ`0YZGpXfYK8pE&aQ)FbX`s%v9eH0P-hj572@7E{K@pynkCj z>aG7H(x1b;|4+RC|C>|)|B%Q6DFy~mTLqL>EA1E*7TPf|fabs*=P`iq7IvJ+P=Fd% zi~^`(#V7#|D`o};$5{*&u&@H@2c-*3q;%K_V$Wcxui<_rQqOs~qJo=20Tg$Z3Jmq2 z`B3lw|3W}1l)1GhnK(-}br!sz(7~=l=;Hw=TX1jW6)}0EQM&oT2s! z7(iqDXmQ4{02EG$`DzBA4V4UR;C>jWjyk|%x6qP-4HPyG^ZsjCEnr~u{{MH0_x~;6 zb}@38IL!O^!TUe7kNzLprk4bjvH!Qg-FolO8Bkn<+s+~k3^PqYW!*m>@BdrCz5oABfwq_Nw5k7t=EgwnZai)3zjHukf%l($@K~?+|34w1I0BWIp!^GJ zqeA7uWj?69@&0!cyw@M34wToR>Nr684ODl4`~WH=y#N2+0#^TL3%HK;{{K4wzHaUz zc+bs(1{MY$&|Jrlg$x&d9Avm*k;rhNhK(82kG}yb(?Iuy{yxai0GcBK&3AbJ{{^mR zLH+5P1ik{tISdXH{^(}aII01;R19nV*%P+Mg?fUI`aLXpg9wmK2YBo zq!*+HbpJ2NOwb;8tY)E}YXI4+kMI)*X@2?%9>YcRlLW|qnAL$!Or}MWL8Lm0L7{pM zgFyA(WryZ9kP5oo*!WEaS7AUhg{jo;OTc@l8_O9Os(vsi zEMCY^RDbJp1!(RQ)L(1VX5awTJMg(lr2Bxu>8nP%0<x`BbI<00b_&>id!^B6#5 zR0lxvj`KJ*Fv~_R6U=c1E(O%Gk>kULM8;zl4;UC{JY+nw;2`6%@c%4!AqPO~r$GH< z=8ljDkh<0Zw2wcL5o#aD15Eq4=V02$-GOG`643evkbR)?Vgn=NF_66$@(i3ICqZ_4 zFgVU(7SNvqx~H0N3#1*yEzoGrz+jQcaHWHh@kl+(;fh8_#uHWQ3<@(C8D4ZSGM)+F z%L2NGSZ1LjN1%R>LAVXudM|cR zy@uvCc9b?>f080N07#Yuj%7P6C87|Fu$Z(-ClR*NM9~v1MPjo6Wp4q_2aH?S;!v#?KpQ*@j zVj?5Mk%^8BXFz$N;UL3_nx&Am4^H>TP}4mqKlnAUD86t6pGRHOsmRa(ii-t|3{9ZC z0896v`~b>#j`RMqfZT{wP9T*_EGs~16g3a9g2olm;)Hb#a`-sTV<|w+1E4$(O=BRl zPe95%7Vx?-G_$d+fnnu9Gn->WBI7x5dytXwOamk10g%~_^H?r4FfyKiuo*ymDi{(N z7#Klo1we5RTK~2%k?~lgBIAJ>VxV&v87E9MV_?`=1{s%srO3$f%8^mvRU)Irt42nJ zR|^?6UL9mKc=eFc;x!|q!)rxGkJpZj0k0DoBVIQ$CcIwAnDP1`W5Mf(j1?A&j17Jb z3<{w14odr=G!F`M@BhESYf7Q)574$Fo^~NI>%4x?LC@Eyw5JOJ6g8_G890A_Ao2oZ4#MAz0U&>)rdQ4)2fl&aI-=J*(0%a~Imbo5I17!0Z=CRKK#T6`kKw$?;e~`EXwE-Av5dL8xU>`e{ zbi$63P8{ZOJiuljavQ*54lB6a1GNJj<}n@tl{<;q3{0T2+(7+ZZ2DpDb(q7Dp+5(7 z{s_mK1~~?X1a6qV-w7~3djJ0pUcWhkk@1B0|KC3l;=d=r%m%ebKM_&`ir1& zahUTx#ryxS9OA=-6U&-oPP8@098+NSg6)L1$3S+2+yS!NVGhe5P`rEp|E~f~zj)H- zk1epW%wgVd7w`YSL2W@$pAwIrU!b%|K+V@Pgzf(2fSg{v|NpRo)?a&&>#q$588*#$ z$glxR-L

A;Sh}-L+#PBg2-7jtpy{b=M9O>Mqcn01GHQI~5r~ZMH+Oy327MTL36c zLG!MG_y3;`c*?F97Kw}py#Fr+uUi4t1)w^@Vc!2ch`h~!W$h>fD9q97IR=#Wx5K>u zkoAs?AoKqdvDWwh8B1vf9#FplG`DTh$auhFA>#>)0}PB74;bXV|DOf-Pe6XF&6ci! z3c_E6@;DpFooN1ID?lw%Sg&A9&yYL{Zo^fOV4nqM8Of%CW?u<5`@#}f z?7;a4v`+xFolc+}V|{^XC+ih7J9Dtv3G!nYv<>Gtk7)vuKS6C5X#a_s1+y+-#!^l& zqm&bl^O(-yPN&Q)i1f%b0c1XE9m|BJf5y~7P95twj|qG((hNq%BMATfFY*5WAF_U( zjkyBUH}d}f1=@#W^w6Kf!0b4W(E)$@Jq2EigqnW;fX2gF9Op5HK+IsC0V+>G?Ol-F zp!P7R-D{D^FlPoM11MdcY4l`(^#xaKRAgAyn8n*(U@6TYfFp16c>g~M-sc1ITcat104RTg)=7i%=tNTn0q_4`HlUW9pWmSJe`F9a zAG{A2xB0(CAm!Ks@LC@4|9`>eSv z>Q7J|3#vasVdnk+&l?aQ!u|^?CqeZNo_6hD(AYdk4W9bz&jXPAK>qjs|E+?syFP*T zmJu-XQwU*mzU{GYU|@#W`x7k=zY|ecf0F^FXK*@BWISMz$j}1n-_3Z)Fk{0(hFOiC z7=61*4GS4&R3@+=o2ke!VPYb~l!=TCJ<$4b0=RzUVqgHLW8}V_7Xu3@eZ%^8orw$` zpt(h8TJ8eH1tKkfg6vCy=i~1MsA=(+0UCb-C|`r>AZ+R1`~NY>`X4z422dP<(h?}$ zd;kAPM0?`f12ns45RQ|dHAJX6L0HW%3Fy7ajK3WjE`a9sKnuSu9T?ax85r1qD>9t; zoyc(GcO%1t-wzpn{Cda$TC;NC_dtlW}G78kNXav+q@fGMvF)%nzVl=Q+{r}IJk)Z%IMh}}S za+t@sqekXO0BGHXjvNC+{SF2-$0mk9miGVuSv_RvK~>A}#qkp3o%$V&3J#ALd^{K! zFF@95RDtIXEtnY?>l-dOINV{pQQz?PxDZf(;6s4^ECvS0dyES}^Wcv67-xX? z)Ys1et>Iu20k7ZK$-o0THwI!}1!6qBUb@2Z-~TzF{UV_GThN?<_y2j|F;`IfKoVOE zS}XE`kx`(rk&&bRG5eNR4;gm6W`ykXdacNCqIN=~!fQu{3$GIyZoF<}c<_25!;9Ak z89uyz$ne8Lk&yw^ZwC3<`~QC#P&j%2|M#Mikzt7iBf|vk4hHrbE@sfU9_X9|nZ`th z1@&`&Fo4fsddUP@_n-k%15)camytuii-7?&uL_>)X9x(WVc{zP^|L@_6O!aZlnwibuIV6z%Aaku7874T){nygm$S}eC|G%3077YgL zg$yONnb8%F^B7w|c7ehMk5at z|5!llaJ>Kj{Zl{d0|RJI(Q)3tDfP2HI9NaU|KD-WzYgmMpn0u-4fO{uRn*VB-~ivB z1KM+E&;QS1?!Od}TJU*Bjtmn(_h5Mc|MvhC2B7&pmRYW>j?hcftd|D z+X=J}4s<6I_}raDh6&(vI_9ITlK`DH245#p1)gIAg?9rZ!vc_5pmh?KiJ)+2-~ip} z4qYd)0CaW?C>}w11G+9D!8(C~!TJCLgZ69&22dP0&SjWW1KP_yPXn}WiKAwpb_F=? z&->Q`Uel2PThsAMq<$qR9vC2NIzVT`F?fm8%)2F0vrhwZ-Vif`0_vKMO7K1aP&|Ul zT~J(t)`Nl0@BoDgXigRC))p*o1Dt>V?FG+K{80Qa2r7$yJ!Dt`TATNS zkrA@CVj;tf--!$-em62)_>HuV0=~8ay6(dJZy$IaG{}DMzq`R}$R?CC7);*IpyU15 z6})}}6b}=`89163Gw{@uN>@w}XOO7g!=NxhoIwDz$7X^!gNXOv9B_N7?#u29(0VZI z>kK;8zZn!jYm&YHOab?EKxwExlX+Q9CU|~?$Af`E24qf$3KDHVu&!ocgxy71KSxRgv@W6H9|HrZjI0Lb zZ8p#v@GkJ#lHf7Sx&O~t&t_l@Z|1NGNoKI|{xcaop9@;20SY5feF3_AWdb8Z3FzJx z$9XJ!K;a77w_|k>e7}l>^_>6zt@ko8f%JgxW3K-%1wMNiZ0{2Wh5FRn6;;PT_I85L zoq*X3GJmo;gTMw^28K>k1|{uI(0NPDCz_p_BdX)U`^>@nn~?4I{+kWH7sQ$w8ZI*! z7~tU|d$^)L<}y57VD2L(T%hg)r$w0iP{U;sI30of2y!( zA@L!D7A^+$QBooxe}T@U1&7UINZ3?^%;?#-b6Kth`70M`N7!)78lPoafP z6(Y?rO{lsJ2^(mf5FIvqbY*`Xx}SH4d}e< z4dM(upm|@AxfTZ*YBn!qXaboFGIKLz9vCFn;QhA^y#Bw_oIwMmAGCjRgE#{pXpd}% zD1$O+{g8G8L)`>%1{qMj0*X5j8>Fs7ltHo6ltH1hltIa19)kc>EtA7sh6@c$%_$Zo z3#rGjdfBY2G)Xq^=$Ypgm%8Dv2Ag4#dLp$sCRI~kip86>>_|FLN(1+7*2EmNz- zUZUN_!00&lzfNridx>@j==`z&3Zb3?Rw3LH7N9kRjZV!8p!Qv@hL;HF+(6J8#SjJo zs~UzN&^n|aP%~H@=l(Yd$%N^X5&`Sc@)7~*$JF*ia6+E65Qc4qhkNP!72(2uU1VCp45ZfbVfY z5(k&%-v4>PXQF}Z_x|@2Y&N9c+XgPvVE1JGI><1grcAm5eD@Xfo-B-eu@V_}fbY>d z$Z)~?Z~I438y0Nt-!0(#-$3o431titleaO5O#a5eG5H$)DA3~H}{+A5%X zq~K-9J*0XERQ@wFC^YL2)ytrAc@YrXgXf9wAYOdvgu^B4@k_1$a+1yJ4v+YP#J>*(i- zYKYz7wgs#V0ogHGjDZ7m#<%xhO>mesH#308L3YR8u+X?EnGpOOx177P4@*~L2&~QPjcNmr+g$wB3F&Warr3n)5pmdLP@0g_m z0~6?;T2T0a?j4&f#=tYtj6ndrb{w>?n~`Aw=pM4yi3~elH!>V}y^!I=>w^pzUO!~G zVWG(I0MsV}xeK)41w#J^&%=8E`wmWn4P^`*VE@AN)KTz0w1zUsoiT{Ka}-=Qd;jMI zw_U+${NFcleFMomn`J?1AKqp>$glvEcR*`#L2X2E+Yz+>7PKyxkzvDcXd4mUjs%^7 z^pN3&_g`ypzX=@1CDIiW#27dxh%tyv5MvN%Ud&+N{kH`?w&neIBY5lzbhm={-z0E) zwr*x%te(#RUQ_G+rwg3dL3JVMtTac^THBY*%c@s0D1h37pt@uRGXn!?Zvv=III)a@ z!~3rlc<*tuGJ}Kn-xly5M^Ky8`|k#DdaJg8wuP7vgZ81przgX{#|(O~ru+*ZV__bsJFK>h=bJ%Pfs-t}?? z!v1>jIr32Z!FS?K{sw6;djGWq=b2_@1{d$YO_1`hRJy|Z?|N`v2AK(R7pSiR%EyR2 zoCsc1NMx9mfZR`7nAL&f66)qN)+k}t0a=4MhXJ(D2z>7hXqI9XOvr?LX0I!k_~>6KnD}29eFS3=GzZkhOQ+pmq9x8^Lu@)prI3&{^c( zf7j)L@)Bqb9;h7!YCnPc|DZKybD} zfe+kPS;#O0v<@6x7Ll+P92BMwxT~96k+Y==J{r zNc~@iUjIYx`1bzK0^UCiw(s9Z@P1oJS+x#aHs~nB_6_`N1g(pNty6@rd1PdC_^rt3 z0bV!R$d~|H0|{HZ2wGDKx@QNx#~5*MoyQB%TFV3m7TCHEaM+N#_9KBo!uwAvxGf9H zJK!`lkBOtAj6tEPsc8$yKcG7dtrjx$fc6j9EnZzQX&M8=*Z`Z+;9aN9)0TPw&5Lz-mEfX?p*SZ3U$}P@V*x@e68yd;e(# z@9_nVeZb4nPUdCRFBud->(9~4QBa#?VlAjFH3g@c$p;x~njbSbfbOLC{#yr*g9(WY zHQs+$gUhW6i3|;(@g?vYOo(BBr<^aKtRnX@&4Zp_IIN>gU*Cn29fTUp!8n{N&hvVvS~Fq{aY|H z6j>@V6jVqtC|EEuv{YI$C{#!=D9nDyP_X$RL($}e3@w`vGIVS{$k0{Y4q4Y)uW}la z|JV*!c>k>ehgYLHG_T1r@IdUU1(!XLJ>09nX%uQ^7qYpaHKHK%>e&ugfXoNwKkvU) z;PtPdyatNb4YCY;-hUFnWh{8Vy)>vD|91|kE(fhcnlu-*C)*Js|M+)>_rKNPJ{nkl z@#+fiKMCNyhoC*#;I{O?_uw(3hFbJ`e=j(E8fww=*j{j7wxJe1kL?B5kKX?o!Epq# z&->pyaJl9E|9=N)ti0AOxT4XiIisdsxdOEB^T$HQfM3x4oS^-ipnadeLHjqMdpp7V zIUCXTa!ygM=sL(4P%G_R(e#ipq@t8TVPYer1ZZ692P30G%>nk3&MXENQ2(oei-BQg zBKY3d3Ws@2A7&~tRDsf#r2+%Lbvpxx^#cY0Yej|*>xB$G)(i{`){YFIvG)Zt8X0;f zG&0QbU|{e7jUzeCW5@uFA%e;Z*xLApM1~$v_<_b|t6d>uv!FQh{yPWUmjLBahj|Q7 z!vAyFG&C~IscBFE@56JP`+oyy?8p003;66Qkoh2a$9ex3)ZbSEj|+kJu|dY*%)o6j zr2Rh`;Js{#40AwtSz>cjO>z``KVJA#1{?3c_aX7f$glu3E(vpgqas63t>!0@$p;zc zfW|#%b22c1+yUCd45dNmtzpp<1>U0rQV+gsNs(azs7$hc{Qp1bzOW693~dt_85%Y) zGPGDGGW0;lFQNP27-v{_Gcbb2III~N8mfLVDCmhZFo5u()lc0EUoX7B_ ze$Fis@BjZ3KxI3~K2TZfFzNpSE6^Rj|Nq};RAg8%>na1oq^}GtApb-6SM+%Qu>l}vzHMv-t^H%z051O*GVB1&UA!W3Z)c+w0|V&JdQdrm?@oE||Nkv!9Av1PsmM_6 z{r|5({o&6Q-hacvbtcFU)}Z~jwYMw0|Npy!?0)b6{~bVS3Y2%D=|ljO&b|NGg4^Jr zaV_tE%fV$VQe9d2Yaye>FGog&Ux|zwzZw}0ejQ|V`1O#{<2NH?z;8vyh~JKk3BMB= zGk!NR7W`hwSn>NHW5e%Ozzask1)y^|UO6%!$0Eby4qrt0% zj25pBGCI6^$msE!kul)4B4fmBN5+KLiHsSq8yO2;FJ!EEeUP!?^+U!E3q{5WpuUbp z0)veApE|JLLFp8fc0hZ$D@qs?8kL%JKzC+ZB{H;t>cJ+^eWp##G80W07$$##oZV5E zJiEgCPXRa|djG8e_xV8QdwBm{0zR8$LOFxPeaY99LgH z4BV~(-2)9869o6=r-9F(i>bT6xuSmF;|fq452Y{v_(uS!p8`#nI-vWot6_clBMb`Q zJOl1emw@YUus=b41L$4ZkiL8^xI6~c-Jr05_F0kp@>4)@Uv(F$FMki(mxtWZj=e9x z^fk0EUktAQpmsz01>&&2yd=2K1nYQ&M4c>=NpY!eKfZLv+^YK9KM9|stpflt_WewI zsGo^B-#!)`rl2#TLFWvB>PI@AZx6Zu4>WgIFAcgAtD*ka?+Vbmh5BX(2GD(f77PsR zpffQ*=VO5G1^f-Vixf1s2O7Jnmab^-U=RV_4G7-5C+A!NN^4uK7+5M97!(}mG3=1&Q=U;)hugUYa4%}XMaXCt4fz;FN@pG4mKS>Fk|<8Id?|m71KEx#v$GHp(YFZwNfcEHzv~bvf+HByvBf$5zM1$Mi zAbp^_Hb8E#kptaV%HRW@%asP*k8ufn?;fcA1)6t$!AP3hLE(VQEbssSw}9N%Sk1r! zau>*b;5l>9969`Mizx8e0J<9;=KfCr-JMax%v|C9rw)9c259~tPo5X3CHqc`aIhQb zljmoE%U8mPWZIH<1!>OV9lH5XK5 zGAMxhHK6`b15>jFs80dv7lHa1;Jy*4uXK>nfl%LQLMH>uBu@s0iRBCo6T}%9W^8BB z0NoP-9^V7E%~HU5!~1VGc>D|8KKnZZ+&7#2ok0h*uEzVX5_qf*R8NA})I3I7Q}YkH zrsnWx&>9;N@4uelbrja+3_8|}85lupWgz2yg^+ouxeN^Toy@4~Y5p-NbjUFv|Zfaat@eGO~WHop#dUh^^3-WCRh`lYX- z>uJ)!`2c1wDEz_eY2;DX(_8?rr-^~iAvHe+%^|`2V%~o}z~vTrJq;*akk`}5AFcr1 zzXt9jDl!zo+*eOTxIorzq=M@~nER|z`iS}9c{H?eLFx-J@2Qq$PymGud_4_nxIos^ z#4v#Oz=PxDF(hmtV>+pja7XS7nKOuh`a+;}6VUZEYryMiP{Ri92JgRaZ$V``sE-Kh zL+c~2rvde!<1WMc&xrLjM1&2*eJS9+KK8K51Lrxkuz|;!<2gO+A+ zh7BZMQo#8aYWE57dKw1?2Jm{Ccm@X0_%vw!Kqu&~5zsib9D_ooErY_$M23QmjtoVU zA2O6|e#lU<`5{9UXl)HB>_KBcpfl7UW68;o{*@^McuWX1rVNtuElazn+Fs2D%0YZ9R<~I394Ur$JtiGXtt0Z4Hea zxUUQ9TYLZU1dmIA#+9fsri8SvMg+R9CIY&y#ss>qMg+FT$8qle6r}Yr5hSdSF@Wl0 zbe#L|0b*^=zbhoH%~=CWi{AhLg4bzy|NjH&%m4qK1L|ja|GN$u|HoKkvlKigg1N?K zDP;T~V~x#H@c2LG8XM>x-~V2M+l?UiK;pz5-0laBU4X|j{!IesX^_14|Nn2m^uKH1 z{02HZ>xbgMgP`$--v=2DK;v%~2@DLs6B#9bH!>>xe#mI?12Wdg@!OG6;P*mC4e!7G zVEdXMHFGp4HJ5H{SpMSAfsDO#sbR^@7_sq?&7yz+mG2 zClfs92`*FSF=T+wu>sv<2RiQ^bnZK7uREwsQ60me01DU1`x#hf$TMhw+yL?`qzw=a z&WoV90?iM5|Gx}&52!Ei{l^X5#%N$_HmSe=xdOB&e)4bddItvYzrA3)tN$@5fbPB3 z?*xyp^H^|#_s+9G!mJk@2cWR>{^JPFr{4enZ2`Fjv^E1|=VZ`)^&ho|pfVq#rw2Ti z2iEg%0(h-RSA32V9SWV4p`6VP}!gZJMa@Yp9fo*5Y#K>j24{B#^?s05LQy20T@j@giOlnx#T0Qmtl_XJ8u zp!O|jE(dh(H7MM{>Bx#fVfKCojmh&FSU~3&gVT`nEBFC_X`F&wBsA2zC$X zoNDhs&ft2ffvMRBGYxfv=PU82p)T-PJSe=p|JZ}`z4!lr8$fP?ryY? zb%EUy4i_zZ{tgekC$h{Ay%s__dI!sWIFNsAk&4{51DRQC^CW9QR+7^R5;B0YoI>~ zw14@p#mht{k49~Ho(11o=>0zp+=j7kWN3htJ8j^xrKU&CH4RBk2b#Bo@?aWxjSJ{b z07!jn1#TOG%34r6$Z?)@3q)@#SRAAVlo!1Jr-An-z~a?$9-|F%TsqEU^nk^o<1EGi zcpTpQ(*x53N$1>H&L82`pFtfoM^ z0u*MTu!DzNB0~{4+!`4Q5aAXA?gxSFu}EZCv6zve2wE2k;7Y#@j0^`X6&cn**GhoS z%UlDR%K_z0P(K>9x5QyCj|nWVInHCs0jI4RSe?aG0ZUVkvzS`oY3d$ROJ^Mei$18X z0H1HltOE*r)boLuLHn4|>LKO~)cncx1v!5%-p9ZeF3)0DKjVdi<6S0|&;&?&a{!&+ zR}QO>u$`604BCf}ZWr2lB}`{P=7Z8KD9yv_KF~Qe;VW6}>Otph+-2NAyq!#-GiT83 zWI{Rr$YCB+1~xlEX&;g9nG`V7eGzzE5RvW~LO|&lRL8*T8*m$;l7R(uZZ#-9gZEuZ zGO&Q+ck@aH29P|ybd4y_7-K+jhgzO7W?+`Bj3MNeXAFNpVUAkA{{`=__WmCWo}Wfe za}M+VP5{*(-v494{UccVbC~rPk^cVu1*bn)Tsh7Y766$6Y8N@q6Z!(v<2Z})4lK<& z&SSh|b%22ZbS67!e=$TYgH8Rk2GAX-2KCb#0;*+M6hQ4iP#FZ;X9^lG_5NQ1o=*df zA%pf`gU&4msR89JP`+}U%L^(CLF=;lXDTwRnZU?!V4@~J_Kza2CbPf(7 zHAI|m@K*-ZrU2Fbpms&`O9qC?55a3*!SxZSUD=okDks2gEFKGP2F3}T4A6dcEqD)^ z^+JXUL|@?pxZm#m|1)Ua*AwsmU)D4xGCVqyl(y;{Bp4j#{hfoz zTMSsvi(_KIlDFOv%3Gkdg+H?x7+z{X#$kSHfX-`UyaP&Kpfm-l(LcW4+#_=7a51WH0q>b1xn5F~Zx0 zkaCp~yblkxTxC3hnSU5JAm<;)d5i_fLYVh=J z{@I}B=idyV{0yoa8#5VraP>W`5*d0xV|Z1ykTrLpwE>|03Euz3!0kE^8x$ts{P3Tx z5;PV9YwtSD<9q|EPeJ7XERG!KG3@}y3!-mw1Qs8TvzRWx%e{L{7hrlI>6Qh{9eAu* z$_Q4JG6Hm71#-Ft)dBGOZ7y>Nk{txhR}vS2yOlm+c9Q;$zj5q%Su4$Qtu8aR!@;>rnw!A;Izata{RW15&^`!|yd~0kIknlFL@Kix6gr`ERiL{$5M>J!_-tU* zvITn|j=Zvk34E72YPs?^091#2{|^ASb&=Dr!@R#Ppt8aHe*k#A4VIQ2X8pB*r{#Zt zEkJ7rKw~S8^B8Oz7@^_P0Kbz494?^r0;(TCVFMlK2KE1VKy49_UQl1f(h)Ka;5e79 z1J-YHn8y$SGT(6?n@Oc7gF>e!14FGgdx?HGD16xz8bN0aK4h3+b?yIueKAlwl68l6 z2LnGSZXD;beL#*AkiGQ{5e$y=Shs-YPhoi*)YtO~0J#IyXL6YP=|V#y!y4~D z+~EG7_y11^aQO}9FHk$c@!r2LVe?t+z+<)d{=FbRKQMyM)I)1yGNvHcQx5YO1+e9Z z@CBeV&3`aB-1}G2(8#a=kvAC5fb&N3>vuYp?T{{xjLAan546Ti-&>iG?- zw+ZO^*8y#R;xqRFsGaTo#}7OY1S+4r|NqWGvkT@=Jm&m{^|$e;`K&?MJ)a9e=?+vk zfz#wXmL=eP4WH*>Spsg4H-P3&SP^`oI)AL{KJ+ff$GY#Ioan-{OjEpUyb15tp z84Ez;MBucg$Ot}@59AM!9~Qp^^(7fX5bbI{eibu5igQGSpy25c5g9Lir;4ts!2T+*-bEEfv9`HH}(3r6I|2IVR7eB0M zbYxiL{r_hLxZF=B+zd zaue8o1_p)+jgYd@`~TN7;JY6=Icz|8l7jk9Aag)|2KAe~|37H}-RB2tbAsmdKye4^ zlX?Hu002 z0&5>Q&SRJY>K}Oj{|H{M;W&@E0<>ns`~MT9KK@$+NP6%9ry=kEzeBwLdw}n(s14_f*|34gnsR!SwA_v*q z>@bfZz;PC~v&Gp!=gOkx4>kc%`h(3+JIrH+oNF(~zyNC7faa({{si@{Kz&D0e+Zmb z6d6u{!W0y?h;aRo;Qjy81>`*B{oflr=Y*Pnyufoyp!yV4mx9-fBr_3ILY4=RfY)WNTbI7|2)xZezEQ~Xjv4O>s}Iu+Eg^#u3v5Mj%R<-Q+A zENciD$y`Ig2u|;ywl^e98UCP!sSIghnhIW9h#aPt;QiDd;JERv|wPE`~$j&_weQl z?>{DgL4DvxrRJ7Kr{<1Ersf{dIT($Z3=E)i0va_K7$(Rv@JzI25P+OlVFS*?p!s+2 zzZu}PJfW6BX7XPK2~b>k|B(mp?*^3z)qfcjCVyvO0QI@O|GR zhVcIX>x%b($QnrR|G#d8FJQ9+g$t-{<2a8&!Xl9|2az}2!0{B`0GV3^$%DcRNgjOe z0jOL=5(BR<1m!&>F>v1nlt#S&Wq|i;g6sszgTfY+KfV9Ef$J6UUblMb3Q#?wzvTaa zhk5@mfa)Iaf7`+4g7ks(c>i|=uL%a5`Tsw7Z7ig2cZKYK{r^7)q!u*a;{7+J6%u#< zAArRY8CHPohSU-x0jt){$WXXszh)g$yfxEM#Q(agdRt0yO5d zkdXnj&eGx_BggMVh8e#b85aCL$gtrDBg2K-nBa=v4;gkq?svQao>%W+WMrwI{kQ^@ zW}tTpF+lP%BRH;5+jvZ%dw9_5FeV@5I?Qn%spQSX6je#p=Q>NkVW z3IW|M!@$4+ZUiaz8x7R zfYQS2M1~o!LH%J4=4G!JGOT!gkYU5?hYUL`6d4YH)(qFrzQEu(@Bb3ex=7GiDrk=? z$XxILA>grZL?8KoLxT*1ghe%EkFDceh8+!%wIiUrgV`PDGSqo-7UGU)#1$$t?0Z9(@sfW#*-GIDtT{|D(CGcq!O?tpHnVPL_n z@864tM#P=PAp5=lDuByWWV;>a{_pVq{~zvVkeMKNIn4d<E(Xv!7$x4ne}UI&LGot`xSao?`0pYppZrQ>IAN8@ zu;AB0h6g_uG8X(e$Oyjw(qbWF0k~{9$XM~Kk>SFxg`j(;89{r*9DY4yc=4N&;lpo5 zh9AEj8A0pWIes^S*RMTF14EQ6+V(0zS1NM=_+%x1vS2V=o9zRQ9(zRPfALn31x)a;rD zM#ch=*^cv=Uog3AHW`2!8243N9jL>U+w7#X&J)HuvzSO67g0-b}U+@#-MM zgI5n3Uc6>x`0!eh;m2!7Muyjkj2y2U83kT1WR!S)kWu0FLq-h?MMeWq9)qT9P`v_P z-!*}eu>f>t3h3@sO9ck-+3KKuG8+{cnm~JHCMq)2Y*b`uX!r+RlZ9AcRO9{E09?md zFfi~pDl%*VjosHbcreU<$j~vNkzvLJMus_{J5C+vF?dv}FerfTdH}6W^8WV@>?cHi z2H(9m$5N4@1JtK;oXZRzw*{F8aWBJ+8J!FajgAaEEJ6LcE(UhTxlG`>w(!?1cJ=>$ zSAg5{4G|8Ga~KssZGDhBXnTJNsJ(BQ&A!^Y|%LyNT|!xHO6h7QpANgfOgJ=RAU7(sVzgZANp%cFm9!R;<2_k->uo>QNF zyP{E%VFt(#V7n6;I%>1ML~1Qx1c3UdmYNI(^;x&U_dhofaj7k1`D+_8Tfcys@8v*TWoA>Vu$jvbSSuiqm zfZPBoccJ5wx*)e(Ix=*C#vB_H8Rj63OLo*EjZ1>eocxes21p&~o&b=!)(>HE&yWL( z^XkV83KJL^KzA&cfWq7Yd@h|76kp!||0RIV;|JX% z0}3l>84kK*9#n>d^di>AGcfSaXoUC^ln24(G<^NA4Y-X5DeL3FV?7|Vy#N2d13Jr0 zzm0+6hvMHxP~G`sA=81M2N^&7e8~9Y7b6qHuZ2t+zaBDK{AOfw`0dCP@OvRs#xF%C zj$e*U0>2WOBz`qADf~LfWbj*&$>Vn-Q^fB^ri9-InF@YCWU8nQ2(IXQ$aJ7)u5v}w zLZ%}%@2x9pC7dg2^}H*7Br@KpjS8-q*vQlYs&jrYGEH!t$FQJLok0cEW^IP74=VBg z8wDQA#lJo%65Ow}Ze%C{>Gl2_1#Y*3_ev!-b2R@3#iJ#7UIno(nGZaE2&otN!Dr$? z^g_-z0JS?o<%{=!D{vdcgMlFh&20_dez&w&q!k#0|Th8 z0NwWt+5^?7&7gp*FAGvzlOSDDcVu(L1V+XSHA2jwv1I{^M20uG^fU-Ch}2JFaB!T% zsZpyiA)tnV;fM80(7h@@931BIZ-BHJ`TpqlF)%>$9{WZ`MvV!Kj5ZS$87&;=vF~X# z1Mkgf0ojXhZ$<|rLyPr7h8dlT46PuwpgXT-Dl$}dFfz1(+LWD&4DB-*A?JdD{L-k+ zfV1D;{E%UdMIxgLs5}9+F+uH1@Bf}Cb&5D@^tJJmko_hs4vuq~m*|7{n{Y$-n{c0j>^I@@ zvCL**bYl2_qJf`50^}BlxlC{ByBHkmyBGo-<}f`76=eOSKas%!w0BE?A|r$2Je~&7 z*|~N9c2_vgV@Rml&!7-4%VMV|%D~__iK(D59{FrJW`;(4^t0ud-hlcNptI$|nOVSR z%WdWat*r&E(PJ_I-8}#@-(lYWE%nzwSAgo-8X<-Y;hZdXp!3qL8Wx6Q zJBhKUF&5e1jCUGs(f!SM0_JbbGxZpKkoQ45%=^EhzWjCtC>%ib7bvVJ@H0qYhBu=Q zYIrkRV1_p%_`W~v;mz;{IlL_r!R;^2k1P%Nh=vxW}jqWc*V%X@Jf-1 zoB zJ()zj|Nq|8z{q$3Tz-Jo%1bdwSX#mQ(`+uy7Z^k)Eo7Jiij!Fn89;Ti4rsrcbt1zA zP~Ut3XbS-a^B+t-4l;?o+TIfo_J6m2up^PIh!(JWeyh$sQv)earoBRg6cKLNt_oz_Jwe; z*wwN)SD@Jox)TlE-Yu{&4~3R>8{uUgXA8(bb)dTmUQP%A)pOuD1?_j8#A(wQhwLZL z2aR^<McnEfEP zc>nhW=K+vjgc}UNZA_3Gy#IfH)0oJ}VyVdR0n{IIoXhk9)W-nT_mDM7-|m3KLGq4s zS^mJ}Ky$6&ygBz9xUB?D>p0iWfy#H#dO2&5Jq--}ptUxja-iCpLBUFafua5rLqMlB z1A_(U|NkKjYq7~Ti|@6ZbCBSPBF3==@<)zY4U(P9>)90Bb!sbTRF0k3PF1#0goi8SnD5DEXw zVFNl}u4cbRKuyalkqY?zX1w6}36MFUx*Svbwc;6 zNHMU4pX9Kad<(J<3nafulYzm~l7X>On?a$28!~SV3CsTswbjuTwU*u$=wab7_umii z|6co-*t=xZrDEGfbfa1*i|Ia;*iVP^}-C^Ee5%2$>LE~jM7KyMn8}pn7MurU* zjo>j6cE`EQM?ig0(D*52{FVzmR|RV4f!uAG$f)5sm$?Bk4jUyh`5~hPDEuLF_Fo)8 zY1R9W9(XS>C{4rCvg6#p77#ft$Qci_7(jOjsnmKIiFp72vZ8^J;f(b{h8-Y#K;hH8 zkYPvjLxvNmasN{Rcid}$%OCW(M}*B+n_4d|kX<$njEvy5>W=fiYJmI(Yr}%<1*Jof ze?WC6D9jxvF`IzuG1U5trKC|Ay^di?fYo2<yfvZ)(JuD?oP@TY51tdjJ3K z0#W6+pr$G}iiCS3uUV-g4Vi9u%~)U_rA~&iU{JmscROy?_4&uUGW`|NaOl zKY`L1s6MK{&m;mWGrj*?hoY93AD)2nBB-ncrDITf1C^DIbD2^=Wh1DZ1f_rPzZ{Tr zHs&)hI?iKS1J+XoE0>sDK=}l8Hds{(i-PxG4#*yNDFz9Ud7yIAaV`@Na`_1An}E_4 zB%gdp0J+oqk2-j~4;1!}^FDy)ARL-^F@W~_^1$+C12nHX%>9$$!N8!>n8>IBayQ7l zT21u)_u&BOymgSr5Ugo zs82ATfdSMGahUgA!TbOB8Y}QUpWmSL*FT{3SKj}B>ohAeeDMDN6LhB%BPjiO|Nq+n zN*@sRM^HPD0Tf2~`wCx|faVcF=aJ)A_iIa|9Rv6*lLkh{2Q@5^J_zoJUINGr@BfhVt3h{9fcT&`Ab9);Ja4WB z9$Ny9>4M}yXRd%~u$sf4!TUPA|Eqz=ZbA3iVUYvx-3Rr1L2};z|MNg;37qHu;5<9Z zl@;WV=0{C8nv*YA!kNS29FJb)(m+6{taFu0y^slQHMB#*EHd; zL!2PzUx4m2`28I`7VZ82-w{x_g6x@W2U-_#2fT&|k{+GF`2>{4y#Kj@-2+k!KI5lW zx&rDJa2^58CI3+Tw-Ge1{(B*#1xQ`ZW9y2a2N@3he8_O(7bC-kUy2MjemOEc_?^h8 z@%td71LzL#ntR3-zaKJs{9t5E0Nq{uE0N*FuSSLszZNq5_;rwx;nzb(j^B)o0>2d* zC4M_HD*SF_H1Pia7joWEk#vPcBEyWTKn8{S4X-QeRJtn~ix?Oz4>AHoQkC@Ao2MSJdXfkgUSd{`@#F)MQ|Dd&BZ~}tV;tU!xYOzh6Yes=Qvkz188m$ zR1aG^GBiThzD@zHeRZ5Gm{RG4y|-~~Wy{|rEJh`4)%{{yJaiCBxtzh+@Q185x!1L(XgP(3_Do`EGa zfyK_!mVvXr!GpnZE+dEjYzAeAxqLZR-x(Mi6B(vhDl#;H!WYv1WoW3?JR}0TlLQvd zkp41*4oD5i|6p~A3=Or}P<3egc))q~zZ|$MhxyNO9!~-&O@RFBIFBa>DLquc(u2c2 zo(h2$cqozu|BFag$oa+u3A!+Hq=BdBd%y#$hez5ks9_s@{hC}=($G_ML?qiw;+ z&;W|721bSk=v*-a$bXiO3=N<;U{KtH;uNH&(UGB{R_hgbtsqFvj`{u?RbZA3X(kp;*AZoo)Rr>v7ww(sl1y{Vm^VnTR1yCMyocr$%33f+d z@hkfN_7Ax12BjItd8`p6*e8O;J_h8lb(qJx1*?6TEOyq_42-pw9s!`W6*H|E7_6f} z>E!PR&{|CI|B~Rk9v1Hov;OhGqA32>PSbHBr^|1NO%|NHL(x}(=&4&w$;T@A{AGeBoZ$TP5j!X8qtG9rZo;~JPb zj+OiM8H9TTYEhL#VQz~l3$~9*3XP^aJnhC9(X2f2n z;>ZJtxEBYe0THzh5+p!IL!K`fJnQ)6hLNy$~H)u&4uNB0B(@G(aI`rl>PIL^SJgPmsPO7 z!s7h|&I?)qIWGuuR?yEW-hU2)``?Yq3_76n3+jV_{0s7{_kUqnTFybZ|Fa63`!z7j zT5ix9dNlWQ{Xs7O9OrRg zP+zU#0|RJ{I3uLL#s*#!4caHYP?4bs+#i_7umQ=9A&7P@7x+wX)cTPJG+&J7Rvz$t zF`{1JGQr|j@cDzJxs??>&xPz(hj|~b%#MbP30V7s+8cu4ebC^LGcYs^PsskP}>-!2Q*e|iM+0qkzo#^%w>oGne8}_A)t~IecwKVO~XTm25X7` z|Mevp7#wFYL?G;DIs>vB*^DPGeG-F|4%`8yTd&2n)w<~f5iKb03;8C z?fx8tS_XaAKuTW(+5(KAb5PLI7bE)lQaae$0?@u4gX26#1`_N8?YTp@4`u(a!#svF zSnb16|9k)cuz_%y`w=aSJ`hn?eI%k@dH)AeSN&)3{{I1TE&#l){C5L2UjAD^@&+$> zzQz0hJII{Ge_rs|u=oFW4ut(d)EZwDmr@`Bqxh`jsV1nNHU zTn{4ee%J6|V2Ef`WN5JT1eF2rW`NkBJb`WpsNQg#`zOZx|DS|L&|T0B3=E*L8ql~6 z@_3EI+;<_~|Nn1*l?jgXzFj~{Lpb)vve#g?TiH?0%ygK?S_8|6u(22r-%^1g6*T4s zI$IQao+zmNt^nO%kGt$<0M9WYmxqq?7`7m}GXha|W8KU90n@!~caZBDhj}dUb{Lv_ zT}gKDpa0-=gzR32d0#>2uB3wE8&Plafalp@{&1YfQUOZ?j`LVzVDauai=_Y&KP&~% z@iX|@b)c|7sAV_+Q|oY#;Q-8RNE?D_4`vKW6L`%KqF!TaL9W-(`hC!T9XGff zth=!pX&mc6G+%L{wSWGHK+-oCSP#tY4zvDyz}^1uzehL&WbdQHJZ1;b*r5jl;}k^r z5ZlLt?CC|V%bBpWI~dX09gK63{R}Dx$?M}WV(CvZqVy*n<}uoU%m?KUP`w3Df0%td z&^|K~`gn|>eO%~ed<<&3XSjgPZcyJ6+c*a({d)iB1dsKj#x<;u#{nMyfW;GNT_q&0 zIly`lagEx?s~0<5;r;(p2rLY||9=C`tAfXJz5jm&@B0OXjrae*@N&ib|GyN}a{b>4 zRQ`WZAD2MC;V&`!*g)k60dxKl(VzM^p&^l>!Ta}N@R*_Z|L+b6|9&N+j{8D{JHGIM z(yjOZUp)}|w+XCoVhtXr`m+NPA8g1 zM?`-52Rb7QeE&6Qt_V7=(%}93065;Twb?;^2qd+0yni16&yR!VBEA1=^!8(zlpxT~6p}LMip?Wcc0(hO~LdF1#gNzaN$3Iti|9=FYqpeA0 z-(u0oPy+H3WM3IOcHY~X!T9gK`2ptc)yUupnk{*4jKoERf$90ffc zo1ms+Mve`MjA7t(%*Yth!N?c`H9Mdokud^dHn#Q>BbNG+0i}L)n8yGfLz}?J7}3DU z7yvTYaUR2x21dpRC|jUG3c4p%3cM$^q`u(<19-10BLirk1&EyhRRca766AMKS+p>b zF|1LMF<^!a1Ir6WMumws3=A7<7}!8#aIYB|PP|rRxbWJM;l}Gkh6k@38D6|z$nfFy zL53f%A2KqWe#ppSp~xrz8fUxx12i`Fe+?*oOkiXT@c#er2h4pA^Zplj|NoyrIM4k9 z&BG8X`#(U}kf?b7QwO(oK~X2Ndt#|NlP$l}FzH%D{0?`?RbK9!J2R zmX*Nc2*jo32jKNo7-{*>5Ac{iA}uq6`Jng&rG?S7JXF)N%0f`xghv?G%Y^{oxMSoa{2Orl*@|X zu^xC@{`U{uHbYIz1>iO@XkYRV#eWZfEM!dh^^oDlZ$^d(zZDr?{B~sc@H>&=$M1!V z9KQ}ST=?C{$ng6hqrmTnj1o0Bj4PTI8B=P0Syyx?GA7i@I#*PbGbl`KWYhq)SAH-u z8Z@wh&I)Hqf|iLB8dDe;sudU%W+yU~G%RE&0j*h`tjJJNv6TH-qc#Ii#d8*g8dmL! zN)9#!(Ee=D`WI~HF1}!71fQMO*vP2y>LB!-hKCF{Kzr$4Gcr7Qt;q1=wIjoa*NF^2 zUNCNoKQywrg@g>*_5M5$oz3 z62WJ-z}NhP#)M&OhCyKuS~u+d=N7oV2GR>zGt969q^>cMVF&8kW5}6qpf$}PH-pzZ zf!1IC`wMQnHd-)9fc8x}%w>vcU}QK^-^t)mf50QaVJ?$Tg9IoYuzu2S0PmMA0qu9I zue}Z4CktvzhR3kj=}Ch3$?~A?*JEb zK4^azX#W;y4*|$;){Tg8X>Nq2G0IjWtny+F#hk*%X2gq!PN&i=X;g;Q~%e&~?4Dw}bBqX7T>_8$9Od{r?Xveb%6* zPZ2!na{}1R1q=*`^vMnGUnA1z-!q_iz>+?vSTusy*|9s!{hQIy$k5>Z|8D}wy`XjH zps;b6_xFYO|9{}UK%lcCh+pUZuLQQvyS@doejJw{y#N0_fNJNz9`FDEZV<2k-waS$ z3(6PZ`~l7zzrf?2;IM&)xA(tayrA*{Tn_(}0ngEa+R_g5{)6W`z5hY(RRoy@TlWmA zo4x=20H22f*7yJa38K~}Bi~I5zS~v;ydI(XQPY9uq~;pX8f47%$r6zL8uE~P5JCIf zA^k*g@cB-N^~q+!RZRTj+p9qlZu1eKjfGVJ{$1& zCvg1-@`K|%h8|G=xlyUP0dzMh_-w#>=?c&trPWr@vjJ@w7$)z5oDB$B1N;QMCbrS3 zxu=n-xuxMzbI0WU3?ktB>oOP^8rZ>i@(N6lXW*Gw&%ojR=MC7sp!Lt*e}jldGK0p#M)*t@Hv2>JcWCIj2KuCIF0;11zpSj4}1m{sBbx02DG+$J-Cj7 z#G@#<{{v1(|0KcvdT3gE0;20d_m}=Y2VV0GTH6e58yp1n<9>tgyj{rH@be+Vj$e!n z2Yw|o-1yZ9YAb-wa$}VEk;t&(=Rt-IzZ4lx{Bi`JS@q)AL52^%9y0v+4N8lkJC;Ff z=Rx=99%MB5agbp_%@pN|CPu~<(0L`@iHr@klFk*i#@-bmw@z$i^nkV{0vgj8R6t`K zlOKZCB8!6e0pV{Oi-7Zs^+ARbL|uIWoZo6HAa!*$AA^GTzpvo-yhS2ILt_O4187XE zQJX;kSKFgnlR*J#ZdluK9;=6b1B0&PJXQz&1_lGid8`4}4h#&9i3~F=6&b*5nZfm` zB0~#oeiypN_5^4>CP3}`QTZ985ofK20p94wl2D&*2cR6WT#az10(X@R)@L&dtNFsENI|_pOKMJ zKbgUyE*N}9M#xJ?h6N2=3<4nx@VdW7zX5bs28)mWBnF21I!GI!N}fR>^e>B@o&cx~ zz*vJ?*D)1f)^$waF?~>77y1~|2H5x#-UhI-3i|&aWInhJaQkxw=-$~z3kHVT!^L>l zjMtl@UP&!T}rrz({}p{{Iph4-KN z;57))@`I!9szdj$+94qSf!deV%nS-uPr&PX8LQPG z>1negLlI~#{^WxU6`Kz-)NDS;P}lGURAxK@xAPV-G88#ZVkl{hVNikZQ_W&v0NwTH zIFI2&qYe5VMF#MEA2^IK_SiG%SnvJ+AJj&5oX21Qx;aGLO`k!P-`HQgj)RrmkD)k@IZ@4psMJbM5CyQa|w zde%hqV^DnJ*-P~ww8ohcbj~Me+^bH7P#fSsd|w#hGcW#50fhx9o)2CVnL1VK=n`iJ6wMsRy!A)~_&Mn-`~ zH3os-jtmz<6pmvEO!wJxStmcD^F5t0@g^UiM z{c1ILj6vybkA4FOXxzRAR1Y*PYCcoT=3D`4BUv;sIC%e+2G5;LKFClqdm%$Xbqa&R z3lQDLpiu43qEN4Kx}qWzb`IG$@Oa(}@S0hM6Ci)Qc4WBlI+5W9C||yAWO(p;A;XK; z2N^!Re#r2{LXnXHG)@t!z+MF%KZyxdfZV(0IFDfu)IB#)-6I8F%j^CBe*sJ_=)8BR z+7oDM!Q*`1|Npzd)H=>%aDl45fTk8)2YLVhF9DT5f+i0hI|0o%K;^HX$%DtyLH#MH z{2erTa6Rh%|KAS!$Dt&+txH@Se)$SYXS9t&32?iWxHyFLA&84Z32-|XcN|K9`*XPC z5Yl(V9fuO&{SP#X!wt}UcqLSU4Sd!ss9y?AOAk=fk~nyrleqZ&3@&>}jZdh(Ptfdz z`u~3iEI)zv`9am9HYt|3RE5~ABw^9 zKMRssFtuoTS`3>1|J{I^g_dW+} z!Tozgo^4@3lLzm61;sH9;t)@sZDFZpW8Z>^PZ4nWPh5Py2hSyw8lR9f+`@rsuQ1gA zSo7=~G_~NdGa~ZrA2fa7bq+{*_63?ecs$Vi|9@5Ffta@+%m4H`3~OWe&Odsh8w>a86NynWO(t*k>SIyM1~)~8W|aW zf#-}E1%5L!DtQ0j2X6a-#tA`VY@ogCpuPQ|ef*8J3=ESM84lnZa{;LVjb(x6h;WU| z)K}cD0IgeX`N6x{kYiw}zx%mjaUw%e^%cnecJSPO38=3M>UVHrIpdrc%Q_Wav~?<+ z;CrUP>r~_zSURK_SgHe%%rAhOKLIrEhPsxM7j$15+Bz5>@EL}%^=FRrI3qSxF@W}l zGlK38X{d$m$7ctxKLNET9p|wpG&eFF2z$V)WBGuA1JpJK&0B!#S<43u0*><-L~0lq zF4XYe(3izFjy^M;Iamt zd7c33*RXI{DMI(<+k(eGAa|eqTLc-yT>QEMG=2`6=koqH5!?G|mRy#lpxi1=L?~oXZB89|DcHGcbVepaSp5Z&YMB09sQE+K+E_l0hEf z7seY1zc4;Q_=WKU%r6f27(c+&L)H~DS762q3+Q|ow0L1z0qTb#;)U4(vR zpmBl)^BLGc=@XhZt0pQklz{g3fX;tuxWT{xIzOf12Lnr;>*NaWzmPjZIv5!W!j`iA zX-I0}0ribR=hw^tjr0ELsRXr8A>&cZEMX5|>xmfzI%L4(`39hPt)H{O!C?*qkM$e| zM$lP5puORsJtLs;M(9{&L#^g55o-m+UKr?@C6;pmpz6@h1pwcR1wPZ9$pREssP{K9 zVcCnpWI)dSO^)*zZ-Bya1}GoN!|u5HX8_)-^6w6KtQ!`;4)gx0)G;?#c>lWtzC#5T z*ABD(NxSIuM1~))8W|a0Eo9_)b&yfu)k8*!*Nlt`uN4_J zUOO@xyiR1ac-_e8@OmMm$LoWP0k0o2Mp!5^CV={`@Gzan`bD1~CE-(w`lz%bK}fyMj(p8${;WKHy+1EBG| z#!Lo0_Z9tpf*gOy=^7S?p!jpV_fI7BF6`Wge;i2pJfs0$&NH%r(lBZ{OnA+p<2;5l z$oU<#j{vlH-{Ich1*qpeFl+#y_n_QefjIA>2fh3Y0r>}X&mK5lsd3&zHE8VV4-xy3 zej}|%|8D{sv-SRc3%rL1p_twHq#C@;)zWGDdD7tsBnRiJtU)Rt-Z!N7ppo;d^V=OVAm##loR zPwOn;F*Ve*&MJY$tt@D6oq?Rzts5A0$#d&za2K5<+-@x?@=uUU<|KC=C!XI=`llT8`Hi&rtE`S>E z%vjoQ%%D9DXzc`M@LUZd-kBqi!!kU9#crWJNtA{$q#O0FgVQnC4m$U0gi19HlVT;HBB*N zIe&v0<@^nYdCVms`$1(stj%Ppz)%57S0Hgvdmpr~4z!nUVW^tjnW4>0q*C9 zuVk?U%|C$Z&w9{#yz_qS0kIutG4Sa3GYB}$`+<}mn84)>YJOl!!AuWK0pz3y$9aq= zK>h>u4?t~tP`-fH*<#-Rzi2?(T$jP_hNTmSd7qy^+FX~xbIsoWKVO0D3F?QAnS2ic zndSZe+X0Z-ps@4)|671?ee#Kjb?To$^#OEG)$a_1`G3Iq7dc&f|Nr!Zu${khKz$`p z8^-(pchI@O1lGy_dO_ICKQO=IY0LaBfrQ~Da6ifW{~ypjB@CeO2E~E*zl-3u0_a?P zsC{_M`lA5~2aw-k@dzr%AZ@IF;QjpG|NnsMCIWu@lK?8Cy#N0N)eRuEc%V$L~gl1I?=$7%Hk66n-l*toRLD@A{D8#*c-J3O^2l?p0x^ z0PU**@1L2YTrqhebpOns&)~VI>eYDm&;0oW&J*Bu+MxL%I|c>Ny|UHK3<{vNoE8fi z6)X-iYFIqrV5oobyTbcF~_?N5k-8qBR4<#%lOJngy>F8CJY@WZ3XJkzvQ{Mur2g7c!i9eURb8>xT?C zEEE|Yfch+xe}MPUYyjnv3E;7-e}7bC{0v z7*xFfegV&qf#MIecF_CZMR1w|$31)xjWc*0@rUAnL2z1r$nfF^BO?PWE&qDRu;RBP z!-3z43@3g!GF$+yl>(<_(7l0-3>$tcGVJ&bxnt1a$3aF5@Ln(}=Zfa#3<9<0-W4^V zG!5Gm_U8k5O)uW{Re#=t*I-#QGJx01Rm(FdRD;rWGlN3)a`?V51B-)<7WF4U`@&v> z$67J>AKU@wTd?23^T8LuWn2d%qX{TIK-04UB+W5WvM=e{rt&|UQ)bD{gfpy!S=U`fvhKy%8-_o{)%<&e^|2{b*MH7YV1K+-cK z19EzP^^jr3Yet3*uN4_~ymn+b@H&y<#Op?e3$GV4+<1ME;lb;N3@xJPP2Vse?Nlfhrsa)T2uJ%95_v(rfCOon}9xP`Ym`48-JR910EA0 zK25&>&oyGE=}X}L7TE9LG<_C42Q`|eZ-e&K4B<5W4!qYN9IxOseHxsmP}8(6cpjZT zY5Em-4i|r#ehHq-AU;h$eF+*v#!S6=G^)&qg zQC5M=>7(E@g_@?#!FwX$eM3-R54~>)>IZ`QhoHXUYX*jjDh35`zYx@41ob76`-YOv z70t&P1mOKccw71jcnt^s^!x<8ju3RVg7rd%hU$6-1yJ9x`Z#2rKCFKT=^H)(kDGw@ zkbwMCzyEcG_rD|HbpaiW3=NRI2#h`TN7=W4?lc0O!C(Qp*Z0pk@cK!3U$IWQ0@}8Q z^%Z@v%yDF3jvX7bKx)E?~Q}7@~#= zboUI%|De4#@V=rHw6AEzz_783w7w!Jyz5VX2A%Do3z|EhXaR0#L(VXT^c~+=KV)bG zojX!%$8Z5E-vDzz=qzCG|NlYbiiG-#GYF5L{JVhYPd-JYf6$o>{|;eE|0Z-w{||{s z{|`w>|97#Z|AXl1e+OpzKTRn8W0})utiX~+Q;^fB<2*(O9O>T&X{_W2mb?XuceK7G zBg%NP<2;4~8xk4y8W6Igwz~BtSh|#+ym#)>gNogGksX}yZ`@p1f4O%&A>Q;9eVE0pL?Jw z4hHLk3>AnyY%{^_ZczDe-N-Np?1z8*!08{P7_{FUG$smKF9ljhGl7v|%f>{81rrk) zHhBNL06yyhw5A3$?g$$9aG1xu1GFE#Vm5;UsP3sxxedN2qcM{K-+tdI`t!l=6#=a^ z0NqVp-|)l1aV~=Z$X@T?d%*kbKzke<=CParjcI}01agn}zy09#ZlHTWY7-)UXg7fF z7iMj+e$BuL(pwGL7scTH?>zYI4Uk(w?gX8w+Q7)L#WIm$0r(8-1_sa_b_+oFAAV< zEEY0OusFy#qkhHf3h)2d!D$Mu&0ORC=O|*lZZW9su?M^^vVoDIq`u(>Xk1I8fsvsG zlutnQxAuHc{4#J@KluN@gOPCxR6L|1k#PbfUKz2JyUaeAiHtKKW@8)EW5RNN8xwfH4s84z21x7(jjpt#@6R$T+Q0k#WKd zNl?3kF=3)51H;BjNV|iSGu1)q$x@MF0Vqv5&Sj_orE3ry?l_kL9A;p# z1O`S>82}RZ{{IJZ)*vI}1n>X<4nY0DG(iH~egU1g3U0$#W->5=!rT%?@9!&(jtmRD z|NmLh2(=$%KFEHUy`X)epnJF)6B+R4PlvgGQb2q2VBzU7@81FM|Nl0C$`n}Ihu`h@ z53()*Q6BsO-DkN45eAGE4U7yIEE*a1G$b-?0ELC)T*jD?WDXn9Sr;{#QX-%`{6O`v z_n#Ty`4mtXg4BV^6vw%Y8lXG;Kz;$0C6f;#$_nrQe+*FE6wf45?E>06dJu@rs?2v9quMah6Rpu8EV2kIcy+q3KE&@i0r0cYY4Xq z{}OS2-JdO>u=f7F4ZL?8R4#k}|5XBs&Y7$8TNSp z|F;Ap{;va3*WQMlGgJ?~C+pu1@EjGmo`TP1Xo1hm0@ugQkCE1V?f|zH!D|2BhV}{ZFL=FEb#ub0z78eAkQFSeV>74F(X3} zC|!c;iRSwZEFd>E`~{ySsk?X|0|Uq&kefjE!|VpF9|fDK2wC3@y2s1=&k9{o-2+-b z0NR5LT6^X=k2xTmi^Xn%90Nmr7lVW2T;?T~T%h}+92gwuGS2|*m#jbixx#~ifv3I! zB*q|Pna#jty%(}ad;up!pTpe$PpbDYC|ExLyNlf-k)Z|Z7KRCo40AR=WayaukYR@R zzvJM(2PltNCo*(^%4LUnj1!t4GRy$&KX#bMU{n9##}CjxcSu`+nE|vP88pU-XbT(% zr%jL$gpIhBEuF?T?}gLK@;C3WP4@llJ zkztABT*fVkHU{X9PKGU@vJh0xHY{YQsa9c70Nu${mByw3I_Id$odJBtQB?L`II+jf?`X7cxq`KFFx>`XQr+g(9N?XnqLn z=j7WJAU`?IW!M4oGx+S6*2fjze2Vw}fVmmue(yh%VC}9sFn2r51)pQTr7@9ViTD41J`Ifw9U!%! zxCO|e_w#k#{k`%3fa5-XH5emLkDhu z{0#y516LS0%mtm}{{H|d-FyGu04@hXY25q&pD&<130jkZS>|m9=Pi)iKz@X-?d|aX zoegg5)q~eUTm`2WkY4Zq|8GFg^C$%Q`}ae}jvosdFZ_JS$nlGjQQ#Nou0ck|fZvLY z5kC?cUi<{zL+Hq;@GFr~<5wf2!LNmk7QYTMI{bRb=<(Z;G2wS2W5(}B#)97q87qDt zWNi2WI*V(Daz*1qCY}wT^SA_@E1DD;uS`^AxHHj_;X$_|;|1us9y37Y^AASG1xV+5 z)Oi272tG#~|5({Y@Z6g9L53PcpQr~ikC@2t;RPeZ2avyOWSJ|x|7`^4*B1{NK2+}o z-)Xo6bnZoCIs?P+g$zGHdrlfTp?lIfnU^&n-IEB?^CFSq2S_bw-w7^tpmiyh3JeBT zjSNLr3mFQmAZO3>&uC=m0j*8jkjQXoLnA}mghqxr8yXpkCNwfMY-nU?nZU@f0JMIm z;Rgdp-ICRiy(ej)GbI***FRQCGbk*2$WR2jW2n=bfkD5MK^wGoKU0%Iw^EZ$!BX@8 z|4K9d2i|{Tz-42jBLis7fsO?OLn`PFh|Wv~21`YT6*CwaDnadsnTZS)GZ-1FKy^x^ zBEt$$-fMWs@Su^A;ZAM3lL%-{J7kSNGYd#RNU!5u0r0sbjgAZt8Xhv-sm-zis{^gq z1+{-2<}%#?)&CZZ3_YMbg&gM!^wc*rI5^JbzXRff#6k8oFf#Oj&K;{^P!a*jJIv)j zfG%I(&=62RN5R2yu2@Ztj6y)||LThReg@E4CLEx%AZi*Eet`Oz4s#{8fZExW{}>b; z=1FjX>OhMIhEz}<_WlzM4m(KMF8HPXgG2zx-ujjw4vup~-hj+#c*w+3-(cb3FjwRR zh^^la+Lt2yqN9n{=80~Bn8)w{VxDLZ zC_EwNiEOBE`2eyLe5W5M>>z4{R)E+LHG*Fn7#a3hCo-%6t>*@XQ*$B%=x+1{7Ksc! zFtLV2h8|E}wP<9hsICH?Nze${OI0sj(P@Q#4h*9N_#7B9*f}ta9H4VxKw)5gkf8(O zZoUs7cSGu0eg^9n21ZbsQ{4jDkK_F>4%`-j)wPcE7$!6@GAyuEgt)0@zL$ta3b;=v z0E*X|`AlGb!$O8RAa{bw(VG1l0ibhcy#FNp1hqdv`a%8ynE~==qas63t>!OKIj;js zhn9{EJT=i4+u&bQ$`qTk2B06LQb zv_`keoLjD42cO`3=9hx8E$~;L;YsZxC^6; z<+=a=L3TLI`@f<7=I08_0}KqHGz=;?KzRd{hGFS>(?f;>-hU#%?Q%$ZWOCi{pkwO`X5V82F5zE=87sC1_jVLg;j^acbe){J!Md+`?`T<-83Y(AgTGG6S@y2X@{DDEz?ZSM-7M z|GymX|Nr)Y)FGY0aijj`4%k^Y5}@-kpl990fc)k-iD5>SGR9dqJ&np3=kUPK;;3_; zTmg;CL$J66wSyezF+k3{F*_w<_3!_GtCOJp2>%*Daq9j5?;H}&`&w$KF^78|bpBot& zPJr@x?Td6bxT>r;f5_<9On^trVc3mg3_+zJccJ&@-vq7!2r5j0R8N$0+>7StKTjbcAA{O)pnME!(=pT$&c_`fzk%Wi6G;e$e*~=DeD4|9f)+w!~#mEpt`ex15_tM&aH8r$F`x-sYRgDi$MX@Uk9~CLG4~p zo3}Fya$g;2|I$pzJrivc6&V^fDl)XxrOmE@)CtqUYpg(d8IopLT|oI5eg>K|gGwX( z95i;&eOzeq#Xbk*4{YfMlAl?=G(2QDV#&b3P$S7)QTrT}zpUW-Ya$~P3#eXO^pN2Y zsBQ%1H&7c9bPinY&*}>AKY^^EHV!<`7{KerHK6#Yw>@1^C*O=X15S{E0dxi&-g@yM zL&N5S3@w`v!s^9o;4yZH{~2)QnFm<%4ESCnY<>slnZ58c;AB8!E}%2J(9U~%Q-8Ap zS_d+`0o_SUaUJ-0H~u=%;FO3Zs1D?0V1$)%-v9sgkdSvk_hW$a&KA)4Xx$USbs)%( zptyqO9T`x%#+`Q<(b{i}S1|Jq;{i~b!<}~+T|jnV%R8Vp9Vm~0&K;P*0X{!21zf)z zgts46Q1dRM1{V88K=v;{YCkS?WVo>reoivO15o=1WEZIY2x=dL+UL;mgC6hS(cm>< zp#BLc&w}JYdoN}xGW3A@trHa)=0Mf1@ctbQp0j%Kkm1D(P=CIWaYoH!>x!2L85uz5 zTD=6_M*=!;PMv{e_HqV>SB#7TuM`<2UO6%!$c-6>g@Mx;k#Pce%<3T%3#k2u8ixN;KerfFhczI?}GRL{|fN4$&NtN12?oy1nT#DGqCtIuqeK8WYF;b|L*}@E$rMnZHU@h zP4xEDKNC><1l1i3cO2$@XYv03JqA>dqscvRnD_mT_x~TDJBk>+|9{^>yj|a)z|}(A zYPi}x-v7TH0i`cgyFumiFAnejzY;)mTHgPE^T5p`=+9pQpfLmQ|L?zm`jVjjV|_z~ zgTq|rH=r{fKxbPz&ieqGf0<*E$j|{AyR3iDBmx@0_5K$CA9DreXGl5u!3UJ)K>a~b z+Jf~78xk4jfYgA-UqSOVpmG+}r}X~g10K%;=>e$+>4WKk_9G#E#D|bRqWAyzZ$N!h z@Bi;hKz&>9-wPq*dW{ToKyC;1eI4h1kxEdJDY27F*d}uW%Z)c8dcPk5&Q|`5cx$eZlKp5#t?ie}MEfCNj*?ZwKu$W(A!)f-xpjlN=?|u#kzx zqJhBxl19IQ$0b2&6qG(e=@gbmL3bp1Fff74!Dc^ro(su-P}&8Lw}8qPkU2H;yhLi! zl|;a0E+a!v%{(TNniMM$lrfimurU{ZNP2$Aa0lXkmI#oW(8rW&l0Sj;-$A(lE4Yo} z!N9Nql!rr-87#bi&jPo-y#K%TK@tP+>+t^nhXXY3400nPyqRx+_>k~^s{)b-=>wMs z@bLBlhZDlTZ~lPvAi|qD2fKSg;mv^L-Y?+!K9E}U@Wx&yyqSXHo^~b?(3qF^KX0`7 zfBgeyCMX?%!+*XPIK1KEKOal@!^gb*!0kbhUP$;em4M8{4F6wf>E|jt24R7jfmcLiiNW_W?eTancLoq=6l6eyh`srv&e z`#K=uISo9|;{E>*Xr2^YMnT=r0U6W%gswM*I6HowL4^HO@O~BV|G&WN0121-KS1NV zh;kLaHp3m99}sEy`3;a;pzZ~o<?w%4A1ayW@EYNFCf<1}_os|NmDY`3<}V)%*W*haZZN^VfbX zWLy9`Q>{^rK?HRE8t4uc(3xqlbJ#%Vvw_ZG`vtmp2l*_vnaUN-iHu7?=dg7$7I)HwZ8|&SO#tIRH6J z(_tQiN5}!TZ~6_Ou~4QhRtFh6ZYVM`ym-jSP@BfSrD0J^3h2B7iw1@qP+Jc)=kNXJ z2)G{bU|@8xOke=bkBvzQX#W<6kFfSM(OkY|tqo4fb_w?B#uM%Mos zxIpGO&i(%Y5?+iUh;U>SfrWwNJSG7wVc-A@1MfeF!S(*+M242xjtmXe3&3XwH&kRZ zD1g=sfY#(xvoR=CJ!es`cH1mqvbd4F$s|Nnag$xkc@KQZpX;->(ZpFnW|NgId2ZUdcp zh@@8oi(U(uUhhAL!1bp0|9>AqcK=b}0N=6bFz-KTP1qU`-~0dHAE3T5B5nMCTdrJy`U5V9^Wp|34LQc>Mo+1B*Tvm_F}6 z2f^oGdjJ3X1&cZxgt`OZ^&sB=|DM63&IhIr;pYS3J_u+I0gL(&Eb77aoA>{}IYX>` znu6(<{owW?3FT7{7Bj%-HjJS1sQ?xR-hcLi(?9v;(_Qd5{~#!z8eo1xYnPbDxr zz5nb1w{vlnPdP~H!0X^~lutD@|BiE5Y(VW3(A^~9HUp%6qGQ1TX`j?^GB1O*O{{`2+a{o} zxS_}hs)yNM9Ap%*N@O_Du&8Cm#6*TUn-4N{Og_jk19S$cMFWF|_n%#0|AWTRYAU5G z9Og1|fa-4e+M|OECDsfK4Au<{3|0>rIy@K{8bI^ipgsp^Z5*U;HxJwoL9~k*0ziH5 zx(B-}z-#ro8Q4MXT+muJ@LIKQ26oVzJdix7kFhzE0dy}AsPEqaS}zT9A4nc7$H~9| zS|?gR^987#Sy5{{OJp-6LlH=Baw9{@=0=8!&5aCIpt%pwdL!7n0MOdis$&ca4ZlG5 z-PD5H-OUFXW>`ODXjz=dP*i{519+c^L25u_yU6|rt;GVh5kM6% zX#ExJd@ax!VH+43DnN5jAbUXbPj$yOgV$Yo|EUG%J&<{eA42S_Z#G*9R- zk8urX92~sX^ge?Ec&#ZTL&MC6Sk{_?_E9WgVDSDo2fX$SDV#y$Q%Gw~86o3AjSL;2 zwR@oTk)UxTQ2(R;>F)~fKbhe58pvxA9p^IK0EIcof3=!XB9jlIt~ISZ0$FPcI-e1= z*0kdwBSXVOMi$WeQqaAc9gGbB7lP6-C;J2M|Nh|e9d*5FH@Ln-U2obAcE<}w#se=8 zGAe+~e)*75;}s*L0jRC{N|Djxl_R6Wt3*bRSB;DTuNE>!ygJC3@aiFB#%o5#g4c?S z6|Wr`8(t?ecD!z6obY-f?*^Y;c)`6c2OsCowQKBr?nZjmtUC zV*rl}St>%}0hIO|6&X4pYxG$ZK;zI=t_%vGxbgmz0ZyBs{G#2>z;40F&;e2p$}6CF zf#n5I{D9ItXpM*W|Gy_d;Te+5VCDV09o+u|r8DpUe^-Fow4grE3`U0k-v58^fr`%n znGZ4>WCzUrkW3C6SU%g(3ErpC02)64jW2-X5S->Z!RwzVnSsSpnSrH&nLz^DH|A(mW?-pQW>5gd%@4)@ji7S&_d|vX>&FZ% zjY%zAelRjjXkcpD@cSS`!31UohKb1x44}2^6?F^>ATu22GI)UU3up{urac481bGID ziS`U4;4zcmd%^882-^|7H{b;$!-Urd846xMWT*h8DGNo029Vti^$aZDza7DQ{Xy&c zq2n+--hWqt*Oh_eMNL>B^@9phKX5gtwW-kGyfsSMbUKktM&EWWD zW?%rNV^F`ff(x|IlEJ1?i$S5*IT&;=80>Denno=KgUSpB1<={?HQWpVAoUJ&8B#hj zAnS}k;|ZWV+fmQJ3OauflqW%U&X5P~(PEeYN|&HC2}+wFH-Y45)HASx)Wg(()T65d z-vgP+fajdB|1CPo3=E+B1=_O$+AD&*H|6(2MhV#770`Z9$0i1W=EV#QpnVgdwQQh0 z02T=hOs}Eq`$6u*wZ`8ffkDFicRhF>4CGf(8^UoOLkVcys4=OP!-A2api!xn2Na&o zPAxYoq(OIHwhBzFf!ujHQJH}OwD!UKcOiH#uF%@T%&dIkZQ98?@!#x>M4h@gpcg35sgrj{F^JO(ny z`?n3a&5SFb!QJ(cp#tPCm|H;Zf|(662V(Db$oM#hd$xniA58ab2iJcf_khd*yWt^2 z1#;f_q4+-$6c4`>87e^cO8j51nn9rXF@u5kpE7Vd^Zv6K>_*U87WIEY?V?cdS$Wov85lwPen8>k z{ksOd#{sm)vnE@*0(2Hb{YmC!)hihkK4YXFIzLR+wDBXbe zi+V6H$bj^9$TEQUFM-D5L2GJ3_D!f}5b*w^1MaUiKW1?8{!<2yL+?L}!0ra=gW3l! zvr+A<2G5<4XrD-<6@yOqN(Kh+Kc(P0%=^zGu)AmPX5eYGVlaWqf%jN@|5*eMi`lyw zcwd0dlyhV#cnyj_4(4UA8yOm2FJ$O=eGps@&ahBqSOCg9pmgp1_c=HY8>%5`9dsTC zSp4@^@Ocgm)sS?KBo0ac)sS?KBo0ph-hY3B>jzMt_x}4V6O{JB;qrGeI3K{$7ATI9 z(^GQ`14nIbbOk7#fzmrD%~j8W&)f5Z&ieqRM^L((z|9~r*Oq~0qBesF=x$T*KSAKR zphkNJ2~c_k?MtX%@VWxr?viIpmhA2i14khVNd{>1)BSV^xO2n z`2o?6{lBF_jzIzxKA<)i$o_^p1{P5G*SlPKyIwHy#=4Ym|Vvo z0&){5y@K3FjN2OP7+8?Qz!9={DF7Bfps)soB`A(S5e{kx);BAF+KD0)m>DE~Bb5W7 zI0MDk+-e3EP~0`QFo=NmY}U+E2JZ{6XaLviYe4Y{3I~wiy#MKf`@x8?U`PSA??Ckt z%%7n4MuRMagvC4t7P#14Nce#I7WJ-|E4=?`f#-ffdSPms>ls*Tt&!#y=QD_q6gKq? zBK6%5931B`e5t<^5n$2q|Gz%ytc-aK0-!wXIPd=s(0)Xaoi#PLL_qdB&SjWT(;_7T z@*gPvK<@MYqYo~7K;v7Uh>H-i1B?Og#XdqL)a z^n>~D?t4qsJY8DUos`W=E#R{oKy^8|tU{JV% z?3ru_3U6cZm;%J_Gr{QqR6cqCoeOpkNG)id97IFi0$vLO+D8dmGYD!ofYQrP*!|+r zd&GZ((nlhr#P3E%g__yQ6*YUbD?n-G_d!Mj@SWt0i~%)wjVoaHk^frA@ZuNRz2n~h z|AOP9rbxO1G=^vSkfESXrMsf`adkyw5#-+b`hwdP_46KARL=yT<&5(V@D3T+Ju?g; z4V9oi1ZW?O5P1I+>Y4F!ptVcLXZ$cY%$0Kit+VRL1nC3qaS%$GQNzFr?o-Gxuyn{W zuvBkFx(lilqz-Jp2Phw+nlF0=?VJn-hq@*ALH7n0faE}G9Ov?Z&r$)&A@*T02Q(Tn zFo4F08Z#MqaNYk3THj#R$j}3-7iKgvOsFZAu9(osuwVis!xGT=9B51fRQ`DXQwR6+ zk;-|O21bS@mWd1#Amygmk;-fa(0Ck!r6a?{8QkD|aF&4TW5>B-6_uXgJxq+Cat>rZ zsC=_Jz`y{z2MRP+igZr_Qd~0CV1~8G9L%s5=|GK3CKph@5FD5CkhlbuPvCRb;PEJU z1B=<9Go{heiAVsd*@6pD;;|lfr2e+J2p7)1JKz;ZUT2xu$`&0oS_knM7qC%ge9vRD`a8yoq-WFR%ofnFaeYfL2Vw;eK@t6heSa8WI=TpC_O^b zDg*4UstI6qi3}5Jv!UwH@2aYS3k43^mpj6bs&7+z{HFo5cXpBfAd9qAzRL3y1Ye1F0WP(GK3l_7uc zz{1dB-d|9jfZkW|cMoz}MGAAIbjl2_n^EJJ^9E-8a)S5HAj$-$JIL`1DswQ?Dbo?m zbjpc-X9t7BTu$(v9Y|*PU{9w^2N3C$=>U>{d>m&nrC_lOOWDbZR(3M!Ao~N9j-csA z-{CG(43ZrL(kUnUIo}Kpb2Az|#~%3CM2^C~1nZ z0W*#mbI40m4B&PkDE))dmG}R@;CpI7_d9^ZCde=_%(P)(@&5l8Im`)!2M1^$BU--Y z0Pkaj#k0da4hB5oVc|NjfBGeP?X@!VJOZ$(W4dr1v9bHxmK23GI?|3u(v_upR+s{{sy zumkYB-FP5(yRq+q`PXqC`w3W{a-7G01$NJz<1F?EFmoK|u|I(5W03G*VDtdF4-|h^ z-~ayy#T&vbRvCm@te~+Y27Pe-&F}$cmcu;;wEGEJu$(u@mVlY&*nE)FxZ^yQ1IYQQ zL!NTr1T8VtElOj1$18sT6$q=ASXRL%=-sk;{!^+h_w6f4cxE){!4)N zAvw$er+WiXUYjAszyOMWSeSw9TX?&S2}`?-1xvlej8ZQ-&SOeJ2@6?RdCd^g0I$oL zu$-C7d;v3@nfIWEGm{98GSUO?&wETBNdED0oW=MCRF0sQ^~|7i5YfV&xdhcN#x2PH z0OcV>8Oitr$qoW#0yCC83c3FXkw+OTK<0zeGH84d)((Q&XX$vCu>@2`I?iP%A;Dhs z^V%33<}!oNYeU$}bOM{b$Yldk`o&TnGhk`kF?5iVenDq+fcy{2!=U^OT1y4ugVHld z43Vb)hJeID;RIU$0A@qVsyAS^JgD6I2Ra9jKw0&_2d%8?fv2H=e-9v)Qzo!5cbvzq zfhbeBO%UZ1_Y9a`$9dc{!V(~5oWnc@0hl=s^B6cleFcykL47A!TOE`Kz5nTe`;nmj z?#qV^JL(>@Z+XSYaNw09!wFD7`jsQYg;$9TH(oU|Jb1N`;l->)n*YyY3OF;9H^$ijX4)eax z@%|$MUPsZW%%B76AA$S|Di6Va1>e!jmjJ3;Q0sI-P&)~&ZV@~{UYW?p04gsa<>7Z8 zL^!?gK@BHfEcL0t4$S&gU2Ny<0Iunt=fv-T@4Zknud80+8LP;mr@)4~G`s{4>Z4ZywM& zAPkW3eg`^BfB{tRc>jM3O6$-z@mtWoi6!8^H0)d)MTQBWzBE!ln!)iNV?kXVdx`!$ z1`s<1DJ%)K7eII4q4|gT3UXLF%wr0{6?XLv9~d0&F@W})E`aUXbePK|V10yv5i~}G zSl=TGo>xN}BMJeH5rNL70*w(_FfxGq{|$@`6KWL@W9F8Q43K-lLFc)3L(c_rWSCH^ z1>VB~-rF(xAj1OCm=QScBp`9;Fz@{XP+A7n=b$i!^sV0R0f|BS8t*{+YoUGkcL$(l z6P~`t`xmhM;PCIAi~bA-22lUPaUKI`j}$z99Op7`0mY3~0^&|?SUKh}mx0Gxf`JjF z540A^(vhJAq~&f2pL?UclJ(joXd0tWIrrzfb0g}**lNH1>`1BoWR-w-v3m<^NHU7Kiz@p z2e}1w7cYkXx&M#gzw`J13Us$R%=@3<{r~?2ke%MY6Crbuji57W|2{#^o8JHbZXiCr zGGfVpj5AQvD`N((^a|=HInHCWA;CUSU5u7L7*YCT4)Yi}u-S*)uJ!)^26Ue+fjaUv zXnc{78c-XUkQ%h`eG>vr-+0!YpW`N8_i%%?Rhc$(~_+$m0kH7#rvkDZK2!H+i1IgFd!D}O6Vd60F-v>y(z7CmN zhR&}z%=-5P9zOs6JqbsQ`+?e6;C=%r{6Xe}=9NHU=Qxk?2h3c@c}y&bux1i~n{|%~ z?XGYZ@VUFFd6^Z`g zygJNdaRHeR$|G3vEogiP)MsTUZG4BN1~WaeWT4sWfz4jzdJvJWm?JRL6*KxhMxZgI z5c1L$6L@?QOS%$(q^qmo_42UzcbNB&1(L3=g4;x}wBRu7?+C%Fnbm=gU30!}phVw6QzZ)b5O0S6i$}gmQ(SOB2 z(-Xcp1mA~@Y0h63P~1Y&*>6~S!DG%Jv@x#VpfZP08Uvl_MnKIkBF5={JD|2Be}m7~ z!#`g3y9MMwkiQXO_XBpP0v>;U0F`M3-25JVz5#yw-%mhw^EdF^-ULz5*zgZ<+(N?s zI}!JTeFM!EfZT+~-S3EKH-9I>Z{Pla!VTiKKLsE*s1El2{~a{G0&)Ys{sP(<;GZSX z^odVT4pa>uzx}xbT3-n2qk{U4ko6^^;JO}p9NYV!D0odQXiby%-wNSGcM1}{y4>J4!-@k3)T>)C_I2nFdUPi}$NY@yxZQ{nw56nxeUXf4mBkDm~}QI+T1)N%ZxOFtEO0WK?*~$Z+DdBIJIB*NzM~UMDgT`1Mh*)_Mgh>;=-WR)`SU;QzU=_-|NkKQfsru+=4Q~{e=s*Y z%=;hX{r^9x-$o#B{3Bv){@(-W-WnC}zop=HjiB@kS{v*AF9>|D4LEK8|NjKkPV)YH z1U!EBLy^{LyAZq|1Ap2s1g{e$E^S|j_$`%v3+RqZke?v;T)Kht5F%{{g88602Bi=1 z-0*1H9tLT<2(teNbUzUIPCtL}{xeWI0Ie~Fr0s*?^@Fre+xg%-?eM4VJn$M^;?njN zh~F^MwmYP5I{di;Qf~W!^VDeCrcSwidC-*G1>k+ch;rK(d|w%A+TJe<+8+bkbAe?~ zj0pLAV&uVPCQbLm{QVE^CxiB!fcBpdv+o3BPt4!n;JrJDxgR3-#QgpNZtKu{PYjm$ z5gubTgZaL3e=Vc?Wu5_ z$DIPc=O}@J0kprt`%gN!{sZ56RLj5s>gRy&JOb^5{^7`=0Xmxm`Oc%pS_THtehtul zN4V60#yM*Wr7NnxV%wv@bp}-LqK@70fyNim##8vfXU!tUw7I4rkKJ@gF|breAemnQ zT0afi1HlDer+{icFK7%3&3xVksQm>ljTu$YF>@&fr14meC7^x)B#b$h)X6kggf+10 zSUE6o=r^!{`Uwj94IB(s4h#aIbzLRvI{(<_lTy;SV7ND>I=>@kb;cEgpH(-VV zFKEmF-Hi&UZse>$4+FT_aQ}0H=RnZ>kEOrFi`HM_binR^P8Wp#IbA?!ome$6a9Jxb zFo4dLahS&jI&YQ1DiN_)1GJ93~1ihGLd28 z?3Lg?8R)(}e#g0NKR{ss>XU=U2ta0o=G(0 zK0_Ii&X_m|hcS}?X8f@{!0eNT&!@R%XJs6O&Dad((;PE{<28Nk- z44`%L;Q3gH{6FwLxD&wl;{JoJufsFO^uGZf*Z=-WfabJ8?g6bwfvvM~oXh;AR`Zq! zC_jMKVu9idoX_X7y#cLb$F_bWTmceqpnZT4GnoY{JsA{G*KaWWfv?}F{{LTJ8Z;ls zGy%5$!f`IM3A(+I^&3nRK;<4N%%JB?F@na%!0G276L=3FsNMkQQSccvOyD&2f|2pT z%ZH2}b%p~!dvG zTQ%g4D+cesAHnlHNM&IN$WP?2M|uU`-vLT5lNUnPBZ1P1_y0dcjEn!i0v#8{Gj{q5 zv`&k_xYKX&9y@5BcmeXS_isD!T3=9jfX257+w*S;EG>fK6}0vO z1!9muPxlV<{)!;VwZEXdk_|v<7gUyc|NpB4&yVw1Wb}nVd4eqfRF3ISV&DSZ%>t_L z9OwO%(VxV?;V_Ri1}etk{r@Ki$c@Np50VZd7#!yP0o^GPfwBMB7Buen4ZH>fT^~5z z&i$j{!NAM{O4F8^pfw^tA?!qkiJ-7Rt{WZZ{s7%CdIRQu$9dn8*WUR!&SK30m7}O_ z0b=%eu$myJZ_qqeEo5&W<~$j^O@nQ3A7Q&3=CND>nGY&kKxqd&7A()e0<}-qVcs{; zJ?IReJ*n0cAnWTK=dm<^{DoRSu!7bvq16wp0p!&WEa0>Zsvo@ne*@<&klVcfe*>*K zVE~N>d;k9mnP-s)#pl-?u=}Mz>~9A^`^O#Uu}IX(d&#^8% zX=6LgV}1e(FHl;7rE^f)*Y9QkokvrFR7b^t%m;-lXw4<2-5p##1!0UNI@#g*i4|x9|a+>u1|M3GX zoW1}5I)geN``ZB2R|M53-v57|fSlW40zUsA8ov>s^R=25wSdllUI9M0p<244p{Z4% z>IQ?tq&<+cwLoX+)iWQi@cyj^o^J%rMb)z$uJHc94ZKDPbPlTbZvjYOzUFqt%ej{#jDF3^z3P~!dT zJ$SqcY9@G29Hb7k#u`L}+Ww$6AE;m8{r4NV{SP`P=7-{cMex|!LPpTJ^dEj4WYmDq zhc9F}@tcuh!EZ-~4Zjl^b~LYMV5q2OQ25=*aNxHh!;0Sr87};O$Z!KZ4=&|gQ45+2 zpQBtcc_Bjq#Gj$yxytI*;IlYT(>6COZHI!#$-#3f3mFQk?HClQ_cJI|H!~=J&e*Y7 z$f#g(kWs_p0S80mNo?oenaHTxsK}@=Lz;nQqBR4<%xch_IKzk6j0_82D>AGA_4!^qGHiIA$gtye zBg28$3mHzlKFDz4^+Sdm7K#iHKz+K&KR|7#|KK%56Brp4y#N1)tX*Mb)PT9yVc!1` zn0uk?`$6kW3FMi7pf(R7HBS)p;bGvjg+TEME)V}1L*gHlm*Df^OTlv=KNSB9LesXv z4@O3Y9|svN;A#6I!;4=J8CLvuWH|6Uk>Lb*tp_A+U--R{;l^*!S$B#IJANNzcmPY^ z&C3}CK=&-yfYLa8y+;su?j8TUA>>XNYet3!Yej~FYIz0)a5`^hP^exGU-MyLagfoX z{sd^v$4l_}&X{XH?7{gK?DxZ;L1&VI*M@X3GMa$W2sC{gK++&ZYd%b&>D#1(k-m_XCFX`>>e0c!eYWO((E zVa01kh7I8Kt;n$BwIjoU*NF@#UNwG!8ir5H*d@ z2k#T7Pa5|Hx7YBeaUaO~52DNQ$B^H4o>4b;I(|CX`DJ~eDa_v$029z zf#VsR#ywBt#~*l7S$&*A0kp;f z*58EmHSdDeg3jp#`KNyW>k99G>frS-9gGYO`V9=aj`JAT)E{Nv0$TqBTEl8_kfFl+ zj{*3sV0hoNP8xLAFK9hBtnZ0s+@BH4zBxveJ(rI27)^-idtzIU4LVB&z3+)~HW#R` ziPrZ7t;a_6JtY|!zDG5Rakz&vL=S8 z{NO-De!y~$8Y40J;Q(5GI08#s;C%>?w9W`h>kW(ybj=UiptGpw{$BzLb5L4_ggXNV zD35G}F+aG1&&fyR2SsqZA2mNr z0gvg`HZZ9DQ2Zwd8aw;_knzQjg-ixNL3eyHG8X(&WUTlFx&thcvEx@G*BjXS7m=PlrLt`3)3aB2M{E(r< z`;P;7JQ06hasc;ftrZzcAmg77;PKDqLoI)rnOYqh)7=nBwT*!a%%p!>|M85kJ#I~hRp&?1?d47!z?Yzmf||NmE7@IUbWeH!f7Mn?wFm>=j&n^f&C z2L8@W1_nz-h8Z&$845vZa%Lh!!3;)*BG4KG(0yj0dG*@DNg|+i29PyNOcOx*KzbbK z@;*V92hHz;!qH(aQx0g3-hz>#M!S=N-*GPQk@|)e4i0m9Pc$?#^nlKzsc+ce;4oL@ zh5j@K2GAaRhj}6apz(T(28Pu7qn|6hf1d)!IVijx=Ls*U|0)px(p%s1!@+Tma1F$a z1r82#g%k9rfbQKD>gi}?C;*uO(qGfUUgG`x6u1omF+=b}{Z)t=*8dn7A?664fSA+a z;4oKkh5kg4IRZE8TRu2A&JoxHkptahCa?gsw$EW6Kj_}9HP#CmWR(=ia?)Bdn!2J)9evn^4Ws^5! z9u9N=eF52T2}(P#bCnnwYQSa2L53dCJ%LsV3=CEc3=E(Owo#8k0(9rI!(1kt8j(%V zdnG&^>K=FmILu+vXy9WI2w`CTq~E~c;5d(=M!$iP!EqkX2GALp^>LRg9Op44RMj&m zgvqkm=?OD1I8I`G&=}94(wGH1znICV(VoEsw4N7n_Yo8LJP^>i#bL~Bb`!W57-n&T z*5`rd4;dGL&L9Pu?=bKGmik+tE5Pe88yN~f`M2sGg90dR!qREI#pw#~-zUK34J4g1 zC)Aos1cWoN+11BiuCRQ`z*uYLT~W7lbwyPzgTe$xMvrP`a5;l>0FzNpSkeqb` z10zV@Vc!2I^|vnsfX~93$7oQ`z+O^6|8a#50|SF&6GH^ZosN?jR#YW3C^R}F#{mQA zY#p>XU;v*T4sugC)J?OYZmMs1435_tdFF~*%UdGWasU5YYceo;|NnOa6gSof7#Jb( z^G^hH|0gJZK2F&!z z4qj6N3M)`r1+9~^3i|&aWG3i-XteqV=ec+w`)V>E>BVspI|pVwae~frM2ja*8C+?n z{=nx7$9ZfAK;>?Y95X1+0$_e~oWzEf57`!A`il*Gb~cK?4FCTJnFsP$-T4gxbz7@M zKzR_B_dxm10$#@)0hf1>e8aQ^l^3eIWo8 z#*jSnUjx(*faVdTbi~Smnm<@hV5TFMEwHeL=Z`7>|AWi}r6Wik!d?Yx+knn^Zd7XR z0M&z_GR|=l%K=dM!OKyUa*G{wZVFmBvWLLzLN7}p`GKXN?h+{7P7(Pl-V(98_y51uN(M$y z{R;|D@BjY}kdQWIplK7dSB9b9g<#rjsK31dIc>^-(h@XnhQP`=PS7+#>6uY#HW83kZz60<$SumV(Pg6sgbXI?5YI)K`r zpfxR^^(?bbGB8ZaWMJ|Bdl}s4u}EZ?0+aLpdl|g`{ROB$*U0$eMIxgEXg>JmLB@pI z35^ObA2MdVVq`3MrN~(E%8{|*RU%`@t478NuNE@Scy*9*!K;UiD_%1)Zg{Q8xZ|}W zgOsrIL>ANP$R1lPCp6eVpp2pnlk1@LV5E4{HAgai<%|4U-cY z!2K8R{~vQO)~SQ;YWn~F2FQKhzlFee5Q4*?K>>Qt_NN$#93OZN5+vvS|C0yEe~$Cm zRO6Gc0;=ag?M-}X)BFFo8_+XH3#wK!D1gcgNWH^o0xBbM)H|TDB($=U-2|4$ z(aZ082XH^=iPcsHMyqT8|AXosP=1H>k3Ktq>;#orpgxl0B!&>w@`M4D&(Q2*0H0+7 zDwna8CnBJ{3Nqhu-lr!Hlm2f3@j>YmB<3*h{|E2?pU%M2H{m?^A2gl;ZnxtP#~+|E zYfzgVoR+=+|IGlU6;M2Z%CY)SOd{1!Kxu3rc)bTAjeP)(vrhn}6;L{>ndc=^Q=}vU zz8jj6p{8aYlSoasK?(0RECf6n(3sY&TV_;WtUpIxZ_gpV5>0MDuGPh?HOaq3s#GEOfJ6M525}!F1Kdu8KT-DVfb${5AMY|Ce&Ys@YeL0*AY%OBeM3+& z9Z=lXFfjap-a`)_zj2)R&IKe6QjZ$vd%$O*Aj0AuXg>?+uB0Bvc`QDlxWJ6JC~&-i z`~n(B@&1#V3R+|8{r{f_iXB^+L_lHg{dW&|{Q|;{w;>>NVQ~x^r>RL4{cnO|_cxf`yTJ3}AiF_j zJfyAs8k{fD&1pzv=mDt#-Tw!=!yeSu2FLU7?clHg*$pZWK>A?eh&_M4)&Yfq_iqmH z+HvpyuLYod@OZWN|9@AYeDHmR-v9qbK-R&tg4<3IH!?(2)G{bEJ!-jOoyf2Oybitw zvJM_JhdSvm1HA7_`wR+=_Gs(iL3ddufbPN70FTFl#xJ~o-38w>>;3;F10)T+x&ZM5JNS-3@Bcq6 zAbf7{U5OAiKfw2sLfrt)3n2GR`U6^@eF*GV$auO&D<~g<#?!t3af18TU_1Znfb%zK z4AuMp{}Z5a2gNbCu4nKP@&5lq1QaJAaZtRV#fbqZT|?6JZf^6pgEA=4;dpsXFN0~GA^n4 zZe7vs$hZKsm!sx}afQQNCY^?33c=3>tp*D?uOT(hpJD|1y77YwJ zp!o#r2cR@-0`{{91EYgw0)vY8|NnE4%vgZMj4N2o0OuVK28KT;n_5A8n;HK%CNMb6 zVUhuj;X~cSadJ`X43NG58#5UE?|yD_n8#pXslfQ<08{G%kT_`k!h?Z&l&78|rp+A>_(_t=C!O2CfE5P!1KxQ!|z|DHVzyLK%1R>8L12%W> z|8IX38H}v|GjM^-ah&`A0mOZbA&78f6oG|-<2)t-EMc$%76#sbjKTSLaw0>^Y)6KM z>IDo6FBUR1RAe(KRI4y3fW{!I*%%b+p0k(Ko!DFf8tbZl#Q@sNQ1CO6p~CzBV{q8L zU}OZ(%e*?sDDdhbqXeiA_!@LBoFb#fYezqIUyKulFA#aM|zu|KA6Y-G3A~z~|?I#)!QC|62p%d;f>*iASW3|8Kni z|9yhR+#4`+k=*16anrvESkzy@q8{810*zrK{J_wIMehMjy@pW#|5E{n$N#^eJyz&> zY7b1G_a8&ZJ?Q`ce!*h?79@4xvfTUs-!oX$9f7Gs_}LI#Zh`J_!lM2R7WLrvG3b2x zp;bP$@nHJJ0NidSzIM$QOFx z{t*%7(*u}Z?>~Crc|_`zPi-79a}jRRg_VC;%BMC4Eb75)@rWp&K48%c^*`S7=?zSu z_a9wWNZEu=KHHp6;GS2w1knzDU zM@EBRiHsJ%Ky9Ukj2^!ZG6wu+WK8&-$XHQP#Guf0knzbcMMjNZ4;dqVD>7#Mc4RF0 z-N@MRdm&@T?}LmJYOWYpbSE-CsQGGLQ7a7D^9O1(Pi$md0dA);GHyUxH&fvKM;5&Q z75};!S@8M+(B3GJS>AtS8$sa+x<9^233`A0Mf7zuvf#c9=n}CFo4=!)(+rvd3r1r87kmw78DsOK>Y%c z8IZLt3@1QxAT^G2SysU0pyo3;%w<>slD9s{P~kY2WkU5G28DY0!xh#F3_9rR7#!y^ z)PUD@NWs>1Fyw&Nb<`C95~_=Wv0?`W8di zLabz90F9|T&SUJcw*3DeWIkx_p#Jt}P*^d5q6&1!G0OZQ{&f#wkD=lH5)$4h>mICbiCD}2|8EVxOXuGSP&|OgSZ9LV^H0P3 z|GyH@*#h4G|GsIogRXlx{23G{IP)9&x`#T#YaVilAIJTB0Tf1{cmsu#!@PfYK>e3+d@g73`bDYCj*wbT?Y8vZWu=TWM~0}3&WX;cLDvwlZ3gVw(P|6PDFS78rYul*~fgOh;~T84n~Igfq=LxjV;Ur($Z7?`~O z|9SzMLwB6V*a13I(fj|e1pQf{HRenjb#=2VzJ#x zI{p{KpfKql1Hj;^FZeUfW{`hf2{-8^^i7#2)G{! zs`I`7eg)5uLB{taz~jOoGeF~eAR06-1%@4xBb zeiTTL_y7MZp!Adbw&3=Mu&=e28EhC#-Mz&N56psG(J^ht;kT( zu&7OBgcz(e9|9=;lTF`zE zs9G5`wcx!7-v9qgK;;F{lKXd8zLx}f$radD^s?f(%Ihw{+zA6#)L5AHj7|Hl=F^5FI4xZ_YBe7_%! z;&1~rA6^MnU;~d2gVy#!(~=HqT9N~oTg1iZCvZ7JYJ5WNH9)f$>i_>8u>1tdvrx5Y zc~1^J&*A<5e+*Lo4uI)%oX0Q$st+xH%Ynx~Kw|_@d9-{e3(Nm3NM@-()uQETS!n); z=Sj3YD+?~SLGcOIkCta;!SgoW|NqUPL!Py%WP|h@A?2ar^;1_e5$OBgG3S zj?l|p4^;QaK+8X@<*o&qTIl#E);#NgrWRb@AeFl&X!6kf56`nMsPfX_d1ItJ>w_i_ zUPlg!V;aOEzC0UH%f`M1QIAT4%YWkH^BuTtL27(L({Ko?y;4yBW6iS`XlkM5AHh89 zfu;{Uc7l{=P0-|_^T7YHzdmU4;B^m>Jo^`VHxpC9 z4@M@19|xHtejj9F0PXet)yTLZ`~XYRuZ4^|K;sI(K{9efT#91KInbTdjfxC=KzHF*dNL?j zCNMC9)Pc+f&0|;3VNd|=p$4_@LG$R1HsC#GBDn6fZ+^%yMSngxTtV~XXCUh}emFSJ zWe@=Q!TZ;9aJvVzM$KU!*B_9XFn@XfT?zJcV5Gcbbmg4Rue z{O|pDCb-{$vY)LL8TNSpT>%ag zP`rWM4{}$tBEz0)YX${SzuA(3fuTd1fuV+ry+pqY)L-U!P;2QV0=lyhbUrPpziY|C zzz80{<76)Z?Nf4`$7E688Ngtf&A{j|@Bahu|Nk$*@&jmpJv2|$c>iGsuW`YjC)mO5 zXzN6V57v$hHK4U1)v-u>>Yx0suvo|xU~!NsqJG8e3h)2(!2LGN^CCWi%N)?YMo<`d z|9uWF7r=XK8*YI5JD@dlpgrIW4)YiUwC6D}*5CMC0SXUmhX4ON7@0yq<$}XJhKPnl zCg{Cq3^t%K%|>Mgg~m+SUMemu=eKg9oZsp&kKG59&L=Q3fzA?SoWaNx(!t0CyZa1e zKJ4x@Ear23!90VL;{hlhVCShi%wvO`Gs?&m0b0{NgOMqwfsqM3kLx&(V@U%e6XG5; zG3dT~F$M71Syms;FV`2nb=||2IRFfdS-y(4BaliAO2m znlLcTECKDeXHt0O$hhEDB4~`BdD*K*#tp9)GVXYFknzB)hm0p)GcsOyt;l%ewIkz$ z*NKcTUNjTM$(-=X02Bdu0pg#+=R*b=*fstX0MIyrjSbAJEE*v3T_L7$`vFr=v>g>8I6t%TflDWV&Dg*KaiVh zH9x`6g9oKU$9arTpnFsoU{eDc2dn?jBm&y2RzK?lgTp+=7}y!uj`#kTfZ`Km4rHG^ zEIqTZfWikgJ+pw$*h4$pfd%a@XXZ0A5}Ba)_@SibkcLDiXj*38g2iks_c5@b-N(RO zGb52H7M_mbd5_rymX00n{aXSu8||#x63l$UoIuW5wV-pt9p^D!0jCK@rVvovg5#Ls z|9?=N*H-=#0p~wX28J4V8TiizRBnRGKv3BUDi0mzF@W~ssnj<>%0|a||G;-PSpSEX zpA76JkT?OAnScM(|9>RXY0ba@G9NV0Qd!HO&>;)iV_M${DvKEfK+v!PeyRK zpvEU7=uBL+_+;Dxc0Z^LYX;Syj5&nklQ9Go=4fVv&g4Zm8@#UrRyH}#W0b*&PiD}W z>kJjJ_;j2HVng!)Y=0~$kIrGJui^Y8Qs4c!qJon_0hI176Bz1eW`f3l|4M-Jg9Rf4 zI6pKnGAz&rsb}~kQs4QwqJkT$UV)(=RIbm|2I=|p1Yzg@37|0S-V1ME`8dvEID+X% z2GBjSXntgPf*O7dJ;>qLsmK&szkt026o>T<9t;li{;PoQ(D45MCk2$IK=pw4|9{Xk z6__Ht|NjHkA)x*A_|60Y@6*A5H|#$Xg!%tV;C{XLy9AV`L2{sTY(e(VRAdVA{{I)W zKaByz_x}F}rZ>X-KQZUm{s-Md4stWTvt@~J^KT-~e*XOel!m zf{Bg{8@&JjY-nI)I0H5hv|r;slL%;ky7%9Ha6cGP4}DtzYD-xvBDH}P8TK?VGQ6>9 zWVq3g2x%vR(r!Z|!wt~7ThQJOP&$L`-{=9K-2yTLRDXfY0jUMGlR=j10MGPwQarse+AW#Sp3lkUI&Np$CoD{ zH-PL1xdY~g21bS%7L5!oX#Qxy^hYMZq57uXPpK}yM}3k7$~2AcLA?A{&f@F2ZQ!i!1E2w zPN4P1i~=b04fPBPlj9f|CeMS;H-OfY{yGggcM3G$0A6n#4Q@|D*7|M*kBQgsd0jDi zCUm~x_g`61`=ok4p7qAR|A6~X;Pm)w130~U|Nm3~3J0jU;JrYgIfluxpf$wPKwUD( zx!%A3fY;H1`fT2R?}F#k!RGvB0Jn=kX29kcK<#fRA2z3O2bA8t|N4RVQ((-ef%pD= z1m`L6T*u!#;QRqLAGRMnlL1fJ^i>ABF2wN1LM8$5U1^Mr6Hx9;TgbQrejgg>J~h~# zXrO!4Kz;9rj3+?rXqy$8M8Ic5qy8?9P z0ch_n`aNf$yK6x5jg5>qUO6%*faWVech*4XLCLzerqKdzOyKt~NdM<0xc`7UCcp_D z6ZriL(*J3MjtTt!1MX~3%x|n484AGbQowU$kaZTI^bU%D$9XIZ z!2SK-zaZ%aq~`A}NdIsy1B0ak1NhvPnTiZ^CNMH=n5f9G#&I6g0?>L_(0+wlm*5Jk zgA6kowZQ#we0|ah;59=F7#OPmLC)Fs{@Vo}cSe*i44}P9puHD!K;;9dY-mVinA5<> zFvX&gp#d(|$N;|U0~C*-{R!T`+re&cU}V?;Qe&yeFbAZ!QITN|+TMgUpmsUPJdira zxy(0^{MRsnkzvY2MTQBD7LcPSW?YagDPfI?tWbXwx5w< zgLNYVJWQG!8Rj%UWLN_&-&(3W85BVFSKBfubb#7`lNb^}XIvo8wqR1hoR?;T?|ltv zhPY!!C(IpaXIso+UesZxPa490|WTJsoC2Z7$%#8)+@{akA-{x{}TgBo7M{%wt&(msQyDr zpZLa!AZw*j(q}tb{f9Gsp0Q|T*a1pYpmf*J$gl%cmLk$;3wVx~*z^feAo=*)PEZ|#Sfj>p0V)n|LwNuH z{RI@3)(aRILGk7=@2`*d|GzVcpXdMk06wSR0@)9aYa9x6j}n?)KfM3{gYV(3C#e4) z3&@S2`%WNp?!UozCxQIasLjA&smZ`NQJaBbB51A0@2}u}GT#4xi!>xMGJOB=dK8|LO&|JCMxZF@cfc%tS?o6W;&-F94|r?Sb(A zdj-5M7TN{~0EvU*#{2hYa2oXf|Njn13=&Rjz-wYb{+$#GYPa-)+eb+4kQWWf4B#=J zj${U&nq>A8(7b;~G6OG+-H^;6QcbdExNek)h#tB16aTMurK$4>BzH{g7b=XgsoJmv%)>wRA%WbwA9R(5&@ra)dJZM zS+Nwf7rYfb&IMB6XvZJ|RzKe(0HoJ(E`tZke#m(YBB1*61tY@&&>YokM}~&iiVPK? zcF*fXhK|>b3=>{2WSH^#Aj5*!4;fZiC^BpSg&Qc%gVz2u$T6^h?60Y15&_u_au2Lr z^#0unUVn?t9S(E<*MQDL0om#O>k6do1do&aXQ;`Pu4qhZOZlPre2!__d!O3--?VJza1F`ekU?Y z{BC4a_`Q%(_DnGn`e*~X3Frk)# zXYyYL4oE*}1-N{#U+}sDbk`24ZUvbOD#Ip&?%w;=0giugIrirhczp<1-uZHc_pjyP z_yv`fjZSST;BaSVV3_C(NmD-*{}+P7`1eAFiXWhRYYs9r{C>#L0SeCsrnUnhb3kjJ z8;_P=0cnDF`_L&0m1nHvit#R;el14^Td{R6unQ;LW-k|uzUS{kBpTXn( z`v7>}AC%U-f4^M`@+WGUu^7B23a!i_+N~gS5oJaTc+CW)%yo-st{6q17BDjo8WB|91et_;wS_p1GHT*sZF7sx1|K0~4>je4H z`}b?GA3$kqLN$ZLhiWm+j4q8CXE~e}Uuj_grwEu|CYe2;S!c z8V~t(1U%LS>WkEOGA{$QBOK;2@PO{51nKLLXJD}2%fJX~D}nkXP7MFwOsEE%4-T{D z#S8)kXu3?1Nd^dQ58*AE$H zSST`p*V}{27Vp0XkUm8t2KN7*ZMs@A(4n?Sk#&N@T1+-ploak#PekUeW3T3(!6Tiv$K%*!}^q9~|-TA+Shb zNb&x480;rd{DR6p(EU!J`{J6Jz;*QvQ2jSqnt@^RZb%!$`*%FJUbB#9;ILlEaHK+# zL7^grLBV1HLkOt7S-lI?MlJ!D=b-WiJl6ROvIg7x_a1OL1FB=Zf4>Bm(bfwY3MSMs zBvkK(m|@Y#Z~_!IljkwSc>gW|w_!l$g7!Fp?C}0w0!|YUbLWG{!$IfYLF=Clq=o?~ zEJ1bIWI0efumQYg7M#xhyaJDFgXI^$uJHaf4_r2a@(@ydZT#)XsPO9{Bgbz>MuFdo zj1s>S88v=4G8+6|$Y}BVAfv0!RpVO$+ zb_djM0Ld%S<|0{5Lk@$CKkIXK?H`TRD6 z$mHJ)Jd=Ne@?k7^j;HxC0}H564yp^if6oNhnbwOL7(r(hgUWU9UkAW#S5W%|BcK0c zQ0S0jU;xd_V&?N$aM{)Tm_Yzx{tR&0f@VHAzDY43l+R OCb}kn#V7+ZCWYhslit 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 FKLE$p^mzaP delta 51242 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%0D9ym!GhHMWWP~szbeM%zru)Ql zm5LqdlcN;5D*ybf{^1jBp;atO#*ux2NS&A`AS0(C5uW?)+|{Yor{t`Mka3NnCM r3c9W*V7hP|hZzsE6m(5A$6|)*7acgnr{~0R$TBl4S5NPW<4^zqyu!Me diff --git a/bin/make_fself.cmd b/bin/make_fself.cmd new file mode 100644 index 0000000000..34cee52250 --- /dev/null +++ b/bin/make_fself.cmd @@ -0,0 +1 @@ +make_fself compiled.elf EBOOT.BIN \ No newline at end of file diff --git a/rpcs3.sln b/rpcs3.sln index 492db8046e..7cbbc25353 100644 --- a/rpcs3.sln +++ b/rpcs3.sln @@ -2,60 +2,60 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rpcs3", "rpcs3\rpcs3.vcxproj", "{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}" ProjectSection(ProjectDependencies) = postProject - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34} = {2E26269D-8BFC-1A93-FD6E-E0CA16399B34} - {DB839A56-59B6-6430-99A9-51D08C6E33FD} = {DB839A56-59B6-6430-99A9-51D08C6E33FD} - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30} = {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30} - {0366B8AE-9D7B-A940-458C-A6B131A12AC8} = {0366B8AE-9D7B-A940-458C-A6B131A12AC8} - {B87F7202-6FA6-FC01-14FE-E92CE882F85B} = {B87F7202-6FA6-FC01-14FE-E92CE882F85B} - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214} = {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214} - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171} = {506026B2-9FD9-7F39-6E6E-55E0C2C8C171} - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495} = {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495} - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99} = {E1973C7F-F026-4596-C19D-6C8FBFAD2C99} - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA} = {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA} - {54994EB2-0674-8592-CF85-8E201FE680E2} = {54994EB2-0674-8592-CF85-8E201FE680E2} + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04} = {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04} + {156E1310-DA46-F664-E3C1-EE90B4CD92AE} = {156E1310-DA46-F664-E3C1-EE90B4CD92AE} + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF} = {90A67BD5-253F-CD50-BB9E-7002DA7D35CF} + {032DB7F5-755A-DF9E-5CBE-553BF92898EC} = {032DB7F5-755A-DF9E-5CBE-553BF92898EC} + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85} = {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85} + {7C123413-10B1-F29F-EEE5-B57A36827C70} = {7C123413-10B1-F29F-EEE5-B57A36827C70} + {F8EC63A1-40C4-541B-911E-3679CCA1132D} = {F8EC63A1-40C4-541B-911E-3679CCA1132D} + {CF3420B3-B31E-FDD0-4897-648FD5125566} = {CF3420B3-B31E-FDD0-4897-648FD5125566} + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6} = {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6} + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B} = {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B} + {350261DE-995E-5363-B942-85B09B015F2B} = {350261DE-995E-5363-B942-85B09B015F2B} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wxWidgets", "wxWidgets", "{5812E712-6213-4372-B095-9EB9BAA1F2DF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "adv", "wxWidgets\build\msw\wx_adv.vcxproj", "{3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "adv", "wxWidgets\build\msw\wx_adv.vcxproj", "{7C123413-10B1-F29F-EEE5-B57A36827C70}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aui", "wxWidgets\build\msw\wx_aui.vcxproj", "{7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aui", "wxWidgets\build\msw\wx_aui.vcxproj", "{0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "wxWidgets\build\msw\wx_base.vcxproj", "{2E26269D-8BFC-1A93-FD6E-E0CA16399B34}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "wxWidgets\build\msw\wx_base.vcxproj", "{7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "wxWidgets\build\msw\wx_core.vcxproj", "{E1973C7F-F026-4596-C19D-6C8FBFAD2C99}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "wxWidgets\build\msw\wx_core.vcxproj", "{8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dbgrid", "wxWidgets\build\msw\wx_dbgrid.vcxproj", "{13138455-15D7-0F02-57C4-083FE663B876}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dbgrid", "wxWidgets\build\msw\wx_dbgrid.vcxproj", "{16CD4B8D-C653-6AFD-426A-D10A768C1CE5}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gl", "wxWidgets\build\msw\wx_gl.vcxproj", "{B87F7202-6FA6-FC01-14FE-E92CE882F85B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gl", "wxWidgets\build\msw\wx_gl.vcxproj", "{A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "html", "wxWidgets\build\msw\wx_html.vcxproj", "{B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "html", "wxWidgets\build\msw\wx_html.vcxproj", "{CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "media", "wxWidgets\build\msw\wx_media.vcxproj", "{03F61B23-19D0-F1E9-18D0-51504E42CC12}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "media", "wxWidgets\build\msw\wx_media.vcxproj", "{28365CAD-FDA4-E41A-7C13-FD86AC470A0D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "net", "wxWidgets\build\msw\wx_net.vcxproj", "{73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "net", "wxWidgets\build\msw\wx_net.vcxproj", "{995774A7-FA69-E03D-1759-A4C33AE7FF7C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "odbc", "wxWidgets\build\msw\wx_odbc.vcxproj", "{A0EBB666-81D6-8FDE-6279-E42B597CE104}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "odbc", "wxWidgets\build\msw\wx_odbc.vcxproj", "{EE9EE1EA-E48D-7B28-2825-6F014802A43B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qa", "wxWidgets\build\msw\wx_qa.vcxproj", "{6A708533-20DB-79A7-1DB4-B8DC76D5A105}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qa", "wxWidgets\build\msw\wx_qa.vcxproj", "{D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "richtext", "wxWidgets\build\msw\wx_richtext.vcxproj", "{5CEF975B-7E93-DFD0-C929-38EFBFCF640A}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "richtext", "wxWidgets\build\msw\wx_richtext.vcxproj", "{DCF03EA6-9806-338B-3B07-60273468D4A9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxexpat", "wxWidgets\build\msw\wx_wxexpat.vcxproj", "{FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxexpat", "wxWidgets\build\msw\wx_wxexpat.vcxproj", "{90A67BD5-253F-CD50-BB9E-7002DA7D35CF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxjpeg", "wxWidgets\build\msw\wx_wxjpeg.vcxproj", "{DB839A56-59B6-6430-99A9-51D08C6E33FD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxjpeg", "wxWidgets\build\msw\wx_wxjpeg.vcxproj", "{156E1310-DA46-F664-E3C1-EE90B4CD92AE}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxpng", "wxWidgets\build\msw\wx_wxpng.vcxproj", "{B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxpng", "wxWidgets\build\msw\wx_wxpng.vcxproj", "{CF3420B3-B31E-FDD0-4897-648FD5125566}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxregex", "wxWidgets\build\msw\wx_wxregex.vcxproj", "{0366B8AE-9D7B-A940-458C-A6B131A12AC8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxregex", "wxWidgets\build\msw\wx_wxregex.vcxproj", "{032DB7F5-755A-DF9E-5CBE-553BF92898EC}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxtiff", "wxWidgets\build\msw\wx_wxtiff.vcxproj", "{506026B2-9FD9-7F39-6E6E-55E0C2C8C171}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxtiff", "wxWidgets\build\msw\wx_wxtiff.vcxproj", "{F8EC63A1-40C4-541B-911E-3679CCA1132D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxzlib", "wxWidgets\build\msw\wx_wxzlib.vcxproj", "{54994EB2-0674-8592-CF85-8E201FE680E2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wxzlib", "wxWidgets\build\msw\wx_wxzlib.vcxproj", "{350261DE-995E-5363-B942-85B09B015F2B}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xml", "wxWidgets\build\msw\wx_xml.vcxproj", "{D047D758-2B7F-D39A-B387-01064479E2DA}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xml", "wxWidgets\build\msw\wx_xml.vcxproj", "{503E4C3C-5D61-7928-7745-526C6EE2646F}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xrc", "wxWidgets\build\msw\wx_xrc.vcxproj", "{F0D3A651-E58B-6E40-75DA-0882C2B5F987}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xrc", "wxWidgets\build\msw\wx_xrc.vcxproj", "{EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -73,190 +73,190 @@ Global {70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Release|Win32.Build.0 = Release|Win32 {70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Release|x64.ActiveCfg = Release|x64 {70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Release|x64.Build.0 = Release|x64 - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}.Debug|Win32.ActiveCfg = Debug|Win32 - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}.Debug|Win32.Build.0 = Debug|Win32 - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}.Debug|x64.ActiveCfg = Debug|x64 - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}.Debug|x64.Build.0 = Debug|x64 - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}.Release|Win32.ActiveCfg = Release|Win32 - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}.Release|Win32.Build.0 = Release|Win32 - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}.Release|x64.ActiveCfg = Release|x64 - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214}.Release|x64.Build.0 = Release|x64 - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}.Debug|Win32.ActiveCfg = Debug|Win32 - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}.Debug|Win32.Build.0 = Debug|Win32 - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}.Debug|x64.ActiveCfg = Debug|x64 - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}.Debug|x64.Build.0 = Debug|x64 - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}.Release|Win32.ActiveCfg = Release|Win32 - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}.Release|Win32.Build.0 = Release|Win32 - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}.Release|x64.ActiveCfg = Release|x64 - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA}.Release|x64.Build.0 = Release|x64 - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34}.Debug|Win32.ActiveCfg = Debug|Win32 - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34}.Debug|Win32.Build.0 = Debug|Win32 - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34}.Debug|x64.ActiveCfg = Debug|x64 - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34}.Debug|x64.Build.0 = Debug|x64 - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34}.Release|Win32.ActiveCfg = Release|Win32 - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34}.Release|Win32.Build.0 = Release|Win32 - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34}.Release|x64.ActiveCfg = Release|x64 - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34}.Release|x64.Build.0 = Release|x64 - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99}.Debug|Win32.ActiveCfg = Debug|Win32 - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99}.Debug|Win32.Build.0 = Debug|Win32 - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99}.Debug|x64.ActiveCfg = Debug|x64 - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99}.Debug|x64.Build.0 = Debug|x64 - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99}.Release|Win32.ActiveCfg = Release|Win32 - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99}.Release|Win32.Build.0 = Release|Win32 - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99}.Release|x64.ActiveCfg = Release|x64 - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99}.Release|x64.Build.0 = Release|x64 - {13138455-15D7-0F02-57C4-083FE663B876}.Debug|Win32.ActiveCfg = Debug|Win32 - {13138455-15D7-0F02-57C4-083FE663B876}.Debug|Win32.Build.0 = Debug|Win32 - {13138455-15D7-0F02-57C4-083FE663B876}.Debug|x64.ActiveCfg = Debug|x64 - {13138455-15D7-0F02-57C4-083FE663B876}.Debug|x64.Build.0 = Debug|x64 - {13138455-15D7-0F02-57C4-083FE663B876}.Release|Win32.ActiveCfg = Release|Win32 - {13138455-15D7-0F02-57C4-083FE663B876}.Release|Win32.Build.0 = Release|Win32 - {13138455-15D7-0F02-57C4-083FE663B876}.Release|x64.ActiveCfg = Release|x64 - {13138455-15D7-0F02-57C4-083FE663B876}.Release|x64.Build.0 = Release|x64 - {B87F7202-6FA6-FC01-14FE-E92CE882F85B}.Debug|Win32.ActiveCfg = Debug|Win32 - {B87F7202-6FA6-FC01-14FE-E92CE882F85B}.Debug|Win32.Build.0 = Debug|Win32 - {B87F7202-6FA6-FC01-14FE-E92CE882F85B}.Debug|x64.ActiveCfg = Debug|x64 - {B87F7202-6FA6-FC01-14FE-E92CE882F85B}.Debug|x64.Build.0 = Debug|x64 - {B87F7202-6FA6-FC01-14FE-E92CE882F85B}.Release|Win32.ActiveCfg = Release|Win32 - {B87F7202-6FA6-FC01-14FE-E92CE882F85B}.Release|Win32.Build.0 = Release|Win32 - {B87F7202-6FA6-FC01-14FE-E92CE882F85B}.Release|x64.ActiveCfg = Release|x64 - {B87F7202-6FA6-FC01-14FE-E92CE882F85B}.Release|x64.Build.0 = Release|x64 - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}.Debug|Win32.ActiveCfg = Debug|Win32 - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}.Debug|Win32.Build.0 = Debug|Win32 - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}.Debug|x64.ActiveCfg = Debug|x64 - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}.Debug|x64.Build.0 = Debug|x64 - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}.Release|Win32.ActiveCfg = Release|Win32 - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}.Release|Win32.Build.0 = Release|Win32 - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}.Release|x64.ActiveCfg = Release|x64 - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37}.Release|x64.Build.0 = Release|x64 - {03F61B23-19D0-F1E9-18D0-51504E42CC12}.Debug|Win32.ActiveCfg = Debug|Win32 - {03F61B23-19D0-F1E9-18D0-51504E42CC12}.Debug|Win32.Build.0 = Debug|Win32 - {03F61B23-19D0-F1E9-18D0-51504E42CC12}.Debug|x64.ActiveCfg = Debug|x64 - {03F61B23-19D0-F1E9-18D0-51504E42CC12}.Debug|x64.Build.0 = Debug|x64 - {03F61B23-19D0-F1E9-18D0-51504E42CC12}.Release|Win32.ActiveCfg = Release|Win32 - {03F61B23-19D0-F1E9-18D0-51504E42CC12}.Release|Win32.Build.0 = Release|Win32 - {03F61B23-19D0-F1E9-18D0-51504E42CC12}.Release|x64.ActiveCfg = Release|x64 - {03F61B23-19D0-F1E9-18D0-51504E42CC12}.Release|x64.Build.0 = Release|x64 - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}.Debug|Win32.ActiveCfg = Debug|Win32 - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}.Debug|Win32.Build.0 = Debug|Win32 - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}.Debug|x64.ActiveCfg = Debug|x64 - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}.Debug|x64.Build.0 = Debug|x64 - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}.Release|Win32.ActiveCfg = Release|Win32 - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}.Release|Win32.Build.0 = Release|Win32 - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}.Release|x64.ActiveCfg = Release|x64 - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78}.Release|x64.Build.0 = Release|x64 - {A0EBB666-81D6-8FDE-6279-E42B597CE104}.Debug|Win32.ActiveCfg = Debug|Win32 - {A0EBB666-81D6-8FDE-6279-E42B597CE104}.Debug|Win32.Build.0 = Debug|Win32 - {A0EBB666-81D6-8FDE-6279-E42B597CE104}.Debug|x64.ActiveCfg = Debug|x64 - {A0EBB666-81D6-8FDE-6279-E42B597CE104}.Debug|x64.Build.0 = Debug|x64 - {A0EBB666-81D6-8FDE-6279-E42B597CE104}.Release|Win32.ActiveCfg = Release|Win32 - {A0EBB666-81D6-8FDE-6279-E42B597CE104}.Release|Win32.Build.0 = Release|Win32 - {A0EBB666-81D6-8FDE-6279-E42B597CE104}.Release|x64.ActiveCfg = Release|x64 - {A0EBB666-81D6-8FDE-6279-E42B597CE104}.Release|x64.Build.0 = Release|x64 - {6A708533-20DB-79A7-1DB4-B8DC76D5A105}.Debug|Win32.ActiveCfg = Debug|Win32 - {6A708533-20DB-79A7-1DB4-B8DC76D5A105}.Debug|Win32.Build.0 = Debug|Win32 - {6A708533-20DB-79A7-1DB4-B8DC76D5A105}.Debug|x64.ActiveCfg = Debug|x64 - {6A708533-20DB-79A7-1DB4-B8DC76D5A105}.Debug|x64.Build.0 = Debug|x64 - {6A708533-20DB-79A7-1DB4-B8DC76D5A105}.Release|Win32.ActiveCfg = Release|Win32 - {6A708533-20DB-79A7-1DB4-B8DC76D5A105}.Release|Win32.Build.0 = Release|Win32 - {6A708533-20DB-79A7-1DB4-B8DC76D5A105}.Release|x64.ActiveCfg = Release|x64 - {6A708533-20DB-79A7-1DB4-B8DC76D5A105}.Release|x64.Build.0 = Release|x64 - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A}.Debug|Win32.ActiveCfg = Debug|Win32 - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A}.Debug|Win32.Build.0 = Debug|Win32 - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A}.Debug|x64.ActiveCfg = Debug|x64 - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A}.Debug|x64.Build.0 = Debug|x64 - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A}.Release|Win32.ActiveCfg = Release|Win32 - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A}.Release|Win32.Build.0 = Release|Win32 - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A}.Release|x64.ActiveCfg = Release|x64 - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A}.Release|x64.Build.0 = Release|x64 - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}.Debug|Win32.ActiveCfg = Debug|Win32 - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}.Debug|Win32.Build.0 = Debug|Win32 - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}.Debug|x64.ActiveCfg = Debug|x64 - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}.Debug|x64.Build.0 = Debug|x64 - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}.Release|Win32.ActiveCfg = Release|Win32 - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}.Release|Win32.Build.0 = Release|Win32 - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}.Release|x64.ActiveCfg = Release|x64 - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30}.Release|x64.Build.0 = Release|x64 - {DB839A56-59B6-6430-99A9-51D08C6E33FD}.Debug|Win32.ActiveCfg = Debug|Win32 - {DB839A56-59B6-6430-99A9-51D08C6E33FD}.Debug|Win32.Build.0 = Debug|Win32 - {DB839A56-59B6-6430-99A9-51D08C6E33FD}.Debug|x64.ActiveCfg = Debug|x64 - {DB839A56-59B6-6430-99A9-51D08C6E33FD}.Debug|x64.Build.0 = Debug|x64 - {DB839A56-59B6-6430-99A9-51D08C6E33FD}.Release|Win32.ActiveCfg = Release|Win32 - {DB839A56-59B6-6430-99A9-51D08C6E33FD}.Release|Win32.Build.0 = Release|Win32 - {DB839A56-59B6-6430-99A9-51D08C6E33FD}.Release|x64.ActiveCfg = Release|x64 - {DB839A56-59B6-6430-99A9-51D08C6E33FD}.Release|x64.Build.0 = Release|x64 - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}.Debug|Win32.ActiveCfg = Debug|Win32 - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}.Debug|Win32.Build.0 = Debug|Win32 - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}.Debug|x64.ActiveCfg = Debug|x64 - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}.Debug|x64.Build.0 = Debug|x64 - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}.Release|Win32.ActiveCfg = Release|Win32 - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}.Release|Win32.Build.0 = Release|Win32 - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}.Release|x64.ActiveCfg = Release|x64 - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495}.Release|x64.Build.0 = Release|x64 - {0366B8AE-9D7B-A940-458C-A6B131A12AC8}.Debug|Win32.ActiveCfg = Debug|Win32 - {0366B8AE-9D7B-A940-458C-A6B131A12AC8}.Debug|Win32.Build.0 = Debug|Win32 - {0366B8AE-9D7B-A940-458C-A6B131A12AC8}.Debug|x64.ActiveCfg = Debug|x64 - {0366B8AE-9D7B-A940-458C-A6B131A12AC8}.Debug|x64.Build.0 = Debug|x64 - {0366B8AE-9D7B-A940-458C-A6B131A12AC8}.Release|Win32.ActiveCfg = Release|Win32 - {0366B8AE-9D7B-A940-458C-A6B131A12AC8}.Release|Win32.Build.0 = Release|Win32 - {0366B8AE-9D7B-A940-458C-A6B131A12AC8}.Release|x64.ActiveCfg = Release|x64 - {0366B8AE-9D7B-A940-458C-A6B131A12AC8}.Release|x64.Build.0 = Release|x64 - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171}.Debug|Win32.ActiveCfg = Debug|Win32 - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171}.Debug|Win32.Build.0 = Debug|Win32 - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171}.Debug|x64.ActiveCfg = Debug|x64 - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171}.Debug|x64.Build.0 = Debug|x64 - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171}.Release|Win32.ActiveCfg = Release|Win32 - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171}.Release|Win32.Build.0 = Release|Win32 - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171}.Release|x64.ActiveCfg = Release|x64 - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171}.Release|x64.Build.0 = Release|x64 - {54994EB2-0674-8592-CF85-8E201FE680E2}.Debug|Win32.ActiveCfg = Debug|Win32 - {54994EB2-0674-8592-CF85-8E201FE680E2}.Debug|Win32.Build.0 = Debug|Win32 - {54994EB2-0674-8592-CF85-8E201FE680E2}.Debug|x64.ActiveCfg = Debug|x64 - {54994EB2-0674-8592-CF85-8E201FE680E2}.Debug|x64.Build.0 = Debug|x64 - {54994EB2-0674-8592-CF85-8E201FE680E2}.Release|Win32.ActiveCfg = Release|Win32 - {54994EB2-0674-8592-CF85-8E201FE680E2}.Release|Win32.Build.0 = Release|Win32 - {54994EB2-0674-8592-CF85-8E201FE680E2}.Release|x64.ActiveCfg = Release|x64 - {54994EB2-0674-8592-CF85-8E201FE680E2}.Release|x64.Build.0 = Release|x64 - {D047D758-2B7F-D39A-B387-01064479E2DA}.Debug|Win32.ActiveCfg = Debug|Win32 - {D047D758-2B7F-D39A-B387-01064479E2DA}.Debug|Win32.Build.0 = Debug|Win32 - {D047D758-2B7F-D39A-B387-01064479E2DA}.Debug|x64.ActiveCfg = Debug|x64 - {D047D758-2B7F-D39A-B387-01064479E2DA}.Debug|x64.Build.0 = Debug|x64 - {D047D758-2B7F-D39A-B387-01064479E2DA}.Release|Win32.ActiveCfg = Release|Win32 - {D047D758-2B7F-D39A-B387-01064479E2DA}.Release|Win32.Build.0 = Release|Win32 - {D047D758-2B7F-D39A-B387-01064479E2DA}.Release|x64.ActiveCfg = Release|x64 - {D047D758-2B7F-D39A-B387-01064479E2DA}.Release|x64.Build.0 = Release|x64 - {F0D3A651-E58B-6E40-75DA-0882C2B5F987}.Debug|Win32.ActiveCfg = Debug|Win32 - {F0D3A651-E58B-6E40-75DA-0882C2B5F987}.Debug|Win32.Build.0 = Debug|Win32 - {F0D3A651-E58B-6E40-75DA-0882C2B5F987}.Debug|x64.ActiveCfg = Debug|x64 - {F0D3A651-E58B-6E40-75DA-0882C2B5F987}.Debug|x64.Build.0 = Debug|x64 - {F0D3A651-E58B-6E40-75DA-0882C2B5F987}.Release|Win32.ActiveCfg = Release|Win32 - {F0D3A651-E58B-6E40-75DA-0882C2B5F987}.Release|Win32.Build.0 = Release|Win32 - {F0D3A651-E58B-6E40-75DA-0882C2B5F987}.Release|x64.ActiveCfg = Release|x64 - {F0D3A651-E58B-6E40-75DA-0882C2B5F987}.Release|x64.Build.0 = Release|x64 + {7C123413-10B1-F29F-EEE5-B57A36827C70}.Debug|Win32.ActiveCfg = Debug|Win32 + {7C123413-10B1-F29F-EEE5-B57A36827C70}.Debug|Win32.Build.0 = Debug|Win32 + {7C123413-10B1-F29F-EEE5-B57A36827C70}.Debug|x64.ActiveCfg = Debug|x64 + {7C123413-10B1-F29F-EEE5-B57A36827C70}.Debug|x64.Build.0 = Debug|x64 + {7C123413-10B1-F29F-EEE5-B57A36827C70}.Release|Win32.ActiveCfg = Release|Win32 + {7C123413-10B1-F29F-EEE5-B57A36827C70}.Release|Win32.Build.0 = Release|Win32 + {7C123413-10B1-F29F-EEE5-B57A36827C70}.Release|x64.ActiveCfg = Release|x64 + {7C123413-10B1-F29F-EEE5-B57A36827C70}.Release|x64.Build.0 = Release|x64 + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}.Debug|Win32.ActiveCfg = Debug|Win32 + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}.Debug|Win32.Build.0 = Debug|Win32 + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}.Debug|x64.ActiveCfg = Debug|x64 + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}.Debug|x64.Build.0 = Debug|x64 + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}.Release|Win32.ActiveCfg = Release|Win32 + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}.Release|Win32.Build.0 = Release|Win32 + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}.Release|x64.ActiveCfg = Release|x64 + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B}.Release|x64.Build.0 = Release|x64 + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}.Debug|Win32.ActiveCfg = Debug|Win32 + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}.Debug|Win32.Build.0 = Debug|Win32 + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}.Debug|x64.ActiveCfg = Debug|x64 + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}.Debug|x64.Build.0 = Debug|x64 + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}.Release|Win32.ActiveCfg = Release|Win32 + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}.Release|Win32.Build.0 = Release|Win32 + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}.Release|x64.ActiveCfg = Release|x64 + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04}.Release|x64.Build.0 = Release|x64 + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}.Debug|Win32.ActiveCfg = Debug|Win32 + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}.Debug|Win32.Build.0 = Debug|Win32 + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}.Debug|x64.ActiveCfg = Debug|x64 + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}.Debug|x64.Build.0 = Debug|x64 + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}.Release|Win32.ActiveCfg = Release|Win32 + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}.Release|Win32.Build.0 = Release|Win32 + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}.Release|x64.ActiveCfg = Release|x64 + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6}.Release|x64.Build.0 = Release|x64 + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5}.Debug|Win32.ActiveCfg = Debug|Win32 + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5}.Debug|Win32.Build.0 = Debug|Win32 + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5}.Debug|x64.ActiveCfg = Debug|x64 + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5}.Debug|x64.Build.0 = Debug|x64 + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5}.Release|Win32.ActiveCfg = Release|Win32 + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5}.Release|Win32.Build.0 = Release|Win32 + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5}.Release|x64.ActiveCfg = Release|x64 + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5}.Release|x64.Build.0 = Release|x64 + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}.Debug|Win32.ActiveCfg = Debug|Win32 + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}.Debug|Win32.Build.0 = Debug|Win32 + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}.Debug|x64.ActiveCfg = Debug|x64 + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}.Debug|x64.Build.0 = Debug|x64 + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}.Release|Win32.ActiveCfg = Release|Win32 + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}.Release|Win32.Build.0 = Release|Win32 + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}.Release|x64.ActiveCfg = Release|x64 + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85}.Release|x64.Build.0 = Release|x64 + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}.Debug|Win32.ActiveCfg = Debug|Win32 + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}.Debug|Win32.Build.0 = Debug|Win32 + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}.Debug|x64.ActiveCfg = Debug|x64 + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}.Debug|x64.Build.0 = Debug|x64 + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}.Release|Win32.ActiveCfg = Release|Win32 + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}.Release|Win32.Build.0 = Release|Win32 + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}.Release|x64.ActiveCfg = Release|x64 + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8}.Release|x64.Build.0 = Release|x64 + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D}.Debug|Win32.ActiveCfg = Debug|Win32 + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D}.Debug|Win32.Build.0 = Debug|Win32 + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D}.Debug|x64.ActiveCfg = Debug|x64 + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D}.Debug|x64.Build.0 = Debug|x64 + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D}.Release|Win32.ActiveCfg = Release|Win32 + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D}.Release|Win32.Build.0 = Release|Win32 + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D}.Release|x64.ActiveCfg = Release|x64 + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D}.Release|x64.Build.0 = Release|x64 + {995774A7-FA69-E03D-1759-A4C33AE7FF7C}.Debug|Win32.ActiveCfg = Debug|Win32 + {995774A7-FA69-E03D-1759-A4C33AE7FF7C}.Debug|Win32.Build.0 = Debug|Win32 + {995774A7-FA69-E03D-1759-A4C33AE7FF7C}.Debug|x64.ActiveCfg = Debug|x64 + {995774A7-FA69-E03D-1759-A4C33AE7FF7C}.Debug|x64.Build.0 = Debug|x64 + {995774A7-FA69-E03D-1759-A4C33AE7FF7C}.Release|Win32.ActiveCfg = Release|Win32 + {995774A7-FA69-E03D-1759-A4C33AE7FF7C}.Release|Win32.Build.0 = Release|Win32 + {995774A7-FA69-E03D-1759-A4C33AE7FF7C}.Release|x64.ActiveCfg = Release|x64 + {995774A7-FA69-E03D-1759-A4C33AE7FF7C}.Release|x64.Build.0 = Release|x64 + {EE9EE1EA-E48D-7B28-2825-6F014802A43B}.Debug|Win32.ActiveCfg = Debug|Win32 + {EE9EE1EA-E48D-7B28-2825-6F014802A43B}.Debug|Win32.Build.0 = Debug|Win32 + {EE9EE1EA-E48D-7B28-2825-6F014802A43B}.Debug|x64.ActiveCfg = Debug|x64 + {EE9EE1EA-E48D-7B28-2825-6F014802A43B}.Debug|x64.Build.0 = Debug|x64 + {EE9EE1EA-E48D-7B28-2825-6F014802A43B}.Release|Win32.ActiveCfg = Release|Win32 + {EE9EE1EA-E48D-7B28-2825-6F014802A43B}.Release|Win32.Build.0 = Release|Win32 + {EE9EE1EA-E48D-7B28-2825-6F014802A43B}.Release|x64.ActiveCfg = Release|x64 + {EE9EE1EA-E48D-7B28-2825-6F014802A43B}.Release|x64.Build.0 = Release|x64 + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}.Debug|Win32.ActiveCfg = Debug|Win32 + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}.Debug|Win32.Build.0 = Debug|Win32 + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}.Debug|x64.ActiveCfg = Debug|x64 + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}.Debug|x64.Build.0 = Debug|x64 + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}.Release|Win32.ActiveCfg = Release|Win32 + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}.Release|Win32.Build.0 = Release|Win32 + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}.Release|x64.ActiveCfg = Release|x64 + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B}.Release|x64.Build.0 = Release|x64 + {DCF03EA6-9806-338B-3B07-60273468D4A9}.Debug|Win32.ActiveCfg = Debug|Win32 + {DCF03EA6-9806-338B-3B07-60273468D4A9}.Debug|Win32.Build.0 = Debug|Win32 + {DCF03EA6-9806-338B-3B07-60273468D4A9}.Debug|x64.ActiveCfg = Debug|x64 + {DCF03EA6-9806-338B-3B07-60273468D4A9}.Debug|x64.Build.0 = Debug|x64 + {DCF03EA6-9806-338B-3B07-60273468D4A9}.Release|Win32.ActiveCfg = Release|Win32 + {DCF03EA6-9806-338B-3B07-60273468D4A9}.Release|Win32.Build.0 = Release|Win32 + {DCF03EA6-9806-338B-3B07-60273468D4A9}.Release|x64.ActiveCfg = Release|x64 + {DCF03EA6-9806-338B-3B07-60273468D4A9}.Release|x64.Build.0 = Release|x64 + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF}.Debug|Win32.ActiveCfg = Debug|Win32 + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF}.Debug|Win32.Build.0 = Debug|Win32 + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF}.Debug|x64.ActiveCfg = Debug|x64 + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF}.Debug|x64.Build.0 = Debug|x64 + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF}.Release|Win32.ActiveCfg = Release|Win32 + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF}.Release|Win32.Build.0 = Release|Win32 + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF}.Release|x64.ActiveCfg = Release|x64 + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF}.Release|x64.Build.0 = Release|x64 + {156E1310-DA46-F664-E3C1-EE90B4CD92AE}.Debug|Win32.ActiveCfg = Debug|Win32 + {156E1310-DA46-F664-E3C1-EE90B4CD92AE}.Debug|Win32.Build.0 = Debug|Win32 + {156E1310-DA46-F664-E3C1-EE90B4CD92AE}.Debug|x64.ActiveCfg = Debug|x64 + {156E1310-DA46-F664-E3C1-EE90B4CD92AE}.Debug|x64.Build.0 = Debug|x64 + {156E1310-DA46-F664-E3C1-EE90B4CD92AE}.Release|Win32.ActiveCfg = Release|Win32 + {156E1310-DA46-F664-E3C1-EE90B4CD92AE}.Release|Win32.Build.0 = Release|Win32 + {156E1310-DA46-F664-E3C1-EE90B4CD92AE}.Release|x64.ActiveCfg = Release|x64 + {156E1310-DA46-F664-E3C1-EE90B4CD92AE}.Release|x64.Build.0 = Release|x64 + {CF3420B3-B31E-FDD0-4897-648FD5125566}.Debug|Win32.ActiveCfg = Debug|Win32 + {CF3420B3-B31E-FDD0-4897-648FD5125566}.Debug|Win32.Build.0 = Debug|Win32 + {CF3420B3-B31E-FDD0-4897-648FD5125566}.Debug|x64.ActiveCfg = Debug|x64 + {CF3420B3-B31E-FDD0-4897-648FD5125566}.Debug|x64.Build.0 = Debug|x64 + {CF3420B3-B31E-FDD0-4897-648FD5125566}.Release|Win32.ActiveCfg = Release|Win32 + {CF3420B3-B31E-FDD0-4897-648FD5125566}.Release|Win32.Build.0 = Release|Win32 + {CF3420B3-B31E-FDD0-4897-648FD5125566}.Release|x64.ActiveCfg = Release|x64 + {CF3420B3-B31E-FDD0-4897-648FD5125566}.Release|x64.Build.0 = Release|x64 + {032DB7F5-755A-DF9E-5CBE-553BF92898EC}.Debug|Win32.ActiveCfg = Debug|Win32 + {032DB7F5-755A-DF9E-5CBE-553BF92898EC}.Debug|Win32.Build.0 = Debug|Win32 + {032DB7F5-755A-DF9E-5CBE-553BF92898EC}.Debug|x64.ActiveCfg = Debug|x64 + {032DB7F5-755A-DF9E-5CBE-553BF92898EC}.Debug|x64.Build.0 = Debug|x64 + {032DB7F5-755A-DF9E-5CBE-553BF92898EC}.Release|Win32.ActiveCfg = Release|Win32 + {032DB7F5-755A-DF9E-5CBE-553BF92898EC}.Release|Win32.Build.0 = Release|Win32 + {032DB7F5-755A-DF9E-5CBE-553BF92898EC}.Release|x64.ActiveCfg = Release|x64 + {032DB7F5-755A-DF9E-5CBE-553BF92898EC}.Release|x64.Build.0 = Release|x64 + {F8EC63A1-40C4-541B-911E-3679CCA1132D}.Debug|Win32.ActiveCfg = Debug|Win32 + {F8EC63A1-40C4-541B-911E-3679CCA1132D}.Debug|Win32.Build.0 = Debug|Win32 + {F8EC63A1-40C4-541B-911E-3679CCA1132D}.Debug|x64.ActiveCfg = Debug|x64 + {F8EC63A1-40C4-541B-911E-3679CCA1132D}.Debug|x64.Build.0 = Debug|x64 + {F8EC63A1-40C4-541B-911E-3679CCA1132D}.Release|Win32.ActiveCfg = Release|Win32 + {F8EC63A1-40C4-541B-911E-3679CCA1132D}.Release|Win32.Build.0 = Release|Win32 + {F8EC63A1-40C4-541B-911E-3679CCA1132D}.Release|x64.ActiveCfg = Release|x64 + {F8EC63A1-40C4-541B-911E-3679CCA1132D}.Release|x64.Build.0 = Release|x64 + {350261DE-995E-5363-B942-85B09B015F2B}.Debug|Win32.ActiveCfg = Debug|Win32 + {350261DE-995E-5363-B942-85B09B015F2B}.Debug|Win32.Build.0 = Debug|Win32 + {350261DE-995E-5363-B942-85B09B015F2B}.Debug|x64.ActiveCfg = Debug|x64 + {350261DE-995E-5363-B942-85B09B015F2B}.Debug|x64.Build.0 = Debug|x64 + {350261DE-995E-5363-B942-85B09B015F2B}.Release|Win32.ActiveCfg = Release|Win32 + {350261DE-995E-5363-B942-85B09B015F2B}.Release|Win32.Build.0 = Release|Win32 + {350261DE-995E-5363-B942-85B09B015F2B}.Release|x64.ActiveCfg = Release|x64 + {350261DE-995E-5363-B942-85B09B015F2B}.Release|x64.Build.0 = Release|x64 + {503E4C3C-5D61-7928-7745-526C6EE2646F}.Debug|Win32.ActiveCfg = Debug|Win32 + {503E4C3C-5D61-7928-7745-526C6EE2646F}.Debug|Win32.Build.0 = Debug|Win32 + {503E4C3C-5D61-7928-7745-526C6EE2646F}.Debug|x64.ActiveCfg = Debug|x64 + {503E4C3C-5D61-7928-7745-526C6EE2646F}.Debug|x64.Build.0 = Debug|x64 + {503E4C3C-5D61-7928-7745-526C6EE2646F}.Release|Win32.ActiveCfg = Release|Win32 + {503E4C3C-5D61-7928-7745-526C6EE2646F}.Release|Win32.Build.0 = Release|Win32 + {503E4C3C-5D61-7928-7745-526C6EE2646F}.Release|x64.ActiveCfg = Release|x64 + {503E4C3C-5D61-7928-7745-526C6EE2646F}.Release|x64.Build.0 = Release|x64 + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}.Debug|Win32.ActiveCfg = Debug|Win32 + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}.Debug|Win32.Build.0 = Debug|Win32 + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}.Debug|x64.ActiveCfg = Debug|x64 + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}.Debug|x64.Build.0 = Debug|x64 + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}.Release|Win32.ActiveCfg = Release|Win32 + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}.Release|Win32.Build.0 = Release|Win32 + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}.Release|x64.ActiveCfg = Release|x64 + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {3B3A5736-72C8-C9BB-FC7C-8B3B096D0214} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {7B39DA9E-3F58-7A74-C4A9-E84F163B0FBA} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {2E26269D-8BFC-1A93-FD6E-E0CA16399B34} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {E1973C7F-F026-4596-C19D-6C8FBFAD2C99} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {13138455-15D7-0F02-57C4-083FE663B876} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {B87F7202-6FA6-FC01-14FE-E92CE882F85B} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {B3E441F3-C4A3-7816-FAAE-2E7B0CA1AD37} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {03F61B23-19D0-F1E9-18D0-51504E42CC12} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {73AF98D9-3335-CAFC-E7A9-9A20BA14EE78} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {A0EBB666-81D6-8FDE-6279-E42B597CE104} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {6A708533-20DB-79A7-1DB4-B8DC76D5A105} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {5CEF975B-7E93-DFD0-C929-38EFBFCF640A} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {FBFC91AB-EA5A-EF88-57EF-12A42BE15C30} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {DB839A56-59B6-6430-99A9-51D08C6E33FD} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {B52A4FC7-1A2C-4959-ACFD-8FF0E0AC7495} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {0366B8AE-9D7B-A940-458C-A6B131A12AC8} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {506026B2-9FD9-7F39-6E6E-55E0C2C8C171} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {54994EB2-0674-8592-CF85-8E201FE680E2} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {D047D758-2B7F-D39A-B387-01064479E2DA} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} - {F0D3A651-E58B-6E40-75DA-0882C2B5F987} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {7C123413-10B1-F29F-EEE5-B57A36827C70} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {0C1DDDB4-F751-0E8E-982C-A6B14886FC7B} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {7F6DB432-0C72-FFB1-DE82-D450DAD5CF04} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {8A8F7ACD-0DB7-90B1-F4F8-F31D60255FD6} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {16CD4B8D-C653-6AFD-426A-D10A768C1CE5} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {A66455C9-DAD3-9BB7-13E6-08E6E33D9B85} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {CABB82BE-DFB1-E556-0AC3-5FC4D6933AE8} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {28365CAD-FDA4-E41A-7C13-FD86AC470A0D} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {995774A7-FA69-E03D-1759-A4C33AE7FF7C} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {EE9EE1EA-E48D-7B28-2825-6F014802A43B} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {D78B0119-3CA8-C0A4-E1C2-4D646A68AC5B} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {DCF03EA6-9806-338B-3B07-60273468D4A9} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {90A67BD5-253F-CD50-BB9E-7002DA7D35CF} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {156E1310-DA46-F664-E3C1-EE90B4CD92AE} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {CF3420B3-B31E-FDD0-4897-648FD5125566} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {032DB7F5-755A-DF9E-5CBE-553BF92898EC} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {F8EC63A1-40C4-541B-911E-3679CCA1132D} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {350261DE-995E-5363-B942-85B09B015F2B} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {503E4C3C-5D61-7928-7745-526C6EE2646F} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} + {EB4E73BF-E88A-8B43-D654-EE1A4D88D10E} = {5812E712-6213-4372-B095-9EB9BAA1F2DF} EndGlobalSection EndGlobal diff --git a/rpcs3/AppConnector.cpp b/rpcs3/AppConnector.cpp new file mode 100644 index 0000000000..b38ccd67c8 --- /dev/null +++ b/rpcs3/AppConnector.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "AppConnector.h" + +AppConnector::~AppConnector() +{ + for(uint i=0; i 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"