From 6d7d3acb43d4808575e359ff86a04fe647814daa Mon Sep 17 00:00:00 2001 From: DH Date: Sat, 6 Jul 2013 02:49:38 +0300 Subject: [PATCH 01/13] - Fixed loading modules in debug mode. - Improved Lwmutex. - Implemented events syscalls. - Fixed SPU local storage. --- Utilities/Array.h | 55 +++ rpcs3/Emu/Cell/PPCDisAsm.h | 19 +- rpcs3/Emu/Cell/PPCThread.cpp | 50 +- rpcs3/Emu/Cell/PPCThread.h | 21 + rpcs3/Emu/Cell/PPCThreadManager.cpp | 9 +- rpcs3/Emu/Cell/PPUDisAsm.h | 4 +- rpcs3/Emu/Cell/PPUInstrTable.h | 2 +- rpcs3/Emu/Cell/PPUInterpreter.h | 114 +++-- rpcs3/Emu/Cell/PPUThread.cpp | 14 +- rpcs3/Emu/Cell/PPUThread.h | 24 + rpcs3/Emu/Cell/SPUDecoder.h | 6 - rpcs3/Emu/Cell/SPUDisAsm.h | 451 ++++++++++--------- rpcs3/Emu/Cell/SPUInterpreter.h | 74 +-- rpcs3/Emu/Cell/SPUOpcodes.h | 4 +- rpcs3/Emu/Cell/SPUThread.cpp | 2 - rpcs3/Emu/Cell/SPUThread.h | 130 +++--- rpcs3/Emu/Memory/Memory.cpp | 8 + rpcs3/Emu/SysCalls/FuncList.cpp | 4 +- rpcs3/Emu/SysCalls/Modules.cpp | 235 ++++++++++ rpcs3/Emu/SysCalls/Modules.h | 76 ++++ rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp | 42 +- rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp | 59 +-- rpcs3/Emu/SysCalls/Modules/sys_fs.cpp | 38 +- rpcs3/Emu/SysCalls/Modules/sys_io.cpp | 26 +- rpcs3/Emu/SysCalls/SysCalls.cpp | 198 ++------ rpcs3/Emu/SysCalls/SysCalls.h | 212 +-------- rpcs3/Emu/SysCalls/lv2/SC_Event.cpp | 137 ++++++ rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp | 65 +-- rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp | 46 +- rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 282 +++++++++++- rpcs3/Emu/System.cpp | 24 +- rpcs3/Emu/System.h | 15 + rpcs3/Emu/event.h | 35 ++ rpcs3/Gui/InterpreterDisAsm.cpp | 5 +- rpcs3/Loader/ELF32.cpp | 21 + rpcs3/Loader/ELF64.cpp | 20 + rpcs3/Loader/Loader.h | 6 + rpcs3/rpcs3.vcxproj | 2 + rpcs3/rpcs3.vcxproj.filters | 6 + 39 files changed, 1639 insertions(+), 902 deletions(-) create mode 100644 rpcs3/Emu/SysCalls/Modules.cpp create mode 100644 rpcs3/Emu/SysCalls/Modules.h create mode 100644 rpcs3/Emu/SysCalls/lv2/SC_Event.cpp create mode 100644 rpcs3/Emu/event.h diff --git a/Utilities/Array.h b/Utilities/Array.h index 45961bd7cd..c672ef4fe6 100644 --- a/Utilities/Array.h +++ b/Utilities/Array.h @@ -209,6 +209,61 @@ template struct Stack : public Array } }; +template class SizedStack +{ + T m_ptr[size]; + uint m_count; + +public: + SizedStack() + { + Clear(); + } + + ~SizedStack() + { + Clear(); + } + + void Clear() + { + m_count = 0; + } + + bool Pop(T& dst) + { + if(!m_count) + return false; + + dst = m_ptr[--m_count]; + return true; + } + + bool Push(const T& src) + { + if(m_count + 1 > size) + return false; + + m_ptr[m_count++] = src; + return true; + } + + size_t GetFreeCount() const + { + return size - m_count; + } + + size_t GetCount() const + { + return m_count; + } + + size_t GetMaxCount() const + { + return size; + } +}; + template class ArrayF { u32 m_count; diff --git a/rpcs3/Emu/Cell/PPCDisAsm.h b/rpcs3/Emu/Cell/PPCDisAsm.h index 0c8ee7eb3b..c3ca64fa25 100644 --- a/rpcs3/Emu/Cell/PPCDisAsm.h +++ b/rpcs3/Emu/Cell/PPCDisAsm.h @@ -23,7 +23,7 @@ protected: { case DumpMode: { - wxString mem = wxString::Format("\t%x:\t", dump_pc); + wxString mem = wxString::Format("\t%08llx:\t", dump_pc); for(u8 i=0; i < 4; ++i) { mem += wxString::Format("%02x", Memory.Read8(dump_pc + i)); @@ -36,14 +36,11 @@ protected: case InterpreterMode: { - wxString mem = wxString::Format("[%x] ", dump_pc); - for(u8 i=0; i < 4; ++i) - { - mem += wxString::Format("%02x", Memory.Read8(dump_pc + i)); - if(i < 3) mem += " "; - } - - last_opcode = mem + ": " + value; + last_opcode = wxString::Format("[%08llx] %02x %02x %02x %02x: %s", dump_pc, + Memory.Read8(offset + dump_pc), + Memory.Read8(offset + dump_pc + 1), + Memory.Read8(offset + dump_pc + 2), + Memory.Read8(offset + dump_pc + 3), value); } break; @@ -55,12 +52,14 @@ protected: public: wxString last_opcode; - uint dump_pc; + u64 dump_pc; + u64 offset; protected: PPC_DisAsm(PPCThread& cpu, DisAsmModes mode = NormalMode) : m_mode(mode) , disasm_frame(NULL) + , offset(0) { if(m_mode != NormalMode) return; diff --git a/rpcs3/Emu/Cell/PPCThread.cpp b/rpcs3/Emu/Cell/PPCThread.cpp index 8124a58115..e58f5a7d41 100644 --- a/rpcs3/Emu/Cell/PPCThread.cpp +++ b/rpcs3/Emu/Cell/PPCThread.cpp @@ -17,6 +17,8 @@ PPCThread::PPCThread(PPCThreadType type) , stack_addr(0) , m_prio(0) , m_offset(0) + , m_sync_wait(false) + , m_wait_thread_id(-1) { } @@ -39,6 +41,9 @@ void PPCThread::Reset() { CloseStack(); + m_sync_wait = 0; + m_wait_thread_id = -1; + SetPc(0); cycle = 0; @@ -92,6 +97,36 @@ void PPCThread::SetName(const wxString& name) m_name = name; } +void PPCThread::Wait(bool wait) +{ + wxCriticalSectionLocker lock(m_cs_sync); + m_sync_wait = wait; +} + +void PPCThread::Wait(const PPCThread& thr) +{ + wxCriticalSectionLocker lock(m_cs_sync); + m_wait_thread_id = thr.GetId(); + m_sync_wait = true; +} + +bool PPCThread::Sync() +{ + wxCriticalSectionLocker lock(m_cs_sync); + + return m_sync_wait; +} + +int PPCThread::ThreadStatus() +{ + if(Emu.IsStopped()) return PPCThread_Stopped; + if(TestDestroy()) return PPCThread_Break; + if(Emu.IsPaused() || Sync()) + return PPCThread_Sleeping; + + return PPCThread_Running; +} + void PPCThread::NextBranchPc() { SetPc(nPC); @@ -127,9 +162,9 @@ void PPCThread::SetEntry(const u64 pc) void PPCThread::SetBranch(const u64 pc) { - if(!Memory.IsGoodAddr(pc)) + if(!Memory.IsGoodAddr(m_offset + pc)) { - ConLog.Error("%s branch error: bad address 0x%llx #pc: 0x%llx", GetFName(), pc, PC); + ConLog.Error("%s branch error: bad address 0x%llx #pc: 0x%llx", GetFName(), m_offset+ pc, m_offset + PC); Emu.Pause(); } nPC = pc; @@ -255,9 +290,16 @@ void PPCThread::Task() } } - while(!Emu.IsStopped() && !TestDestroy()) + while(true) { - if(Emu.IsPaused()) + int status = ThreadStatus(); + + if(status == PPCThread_Stopped || status == PPCThread_Break) + { + break; + } + + if(status == PPCThread_Sleeping) { Sleep(1); continue; diff --git a/rpcs3/Emu/Cell/PPCThread.h b/rpcs3/Emu/Cell/PPCThread.h index 14abf1f17b..edcfcd43cf 100644 --- a/rpcs3/Emu/Cell/PPCThread.h +++ b/rpcs3/Emu/Cell/PPCThread.h @@ -8,6 +8,16 @@ enum PPCThreadType PPC_THREAD_SPU, }; +enum PPCThreadStatus +{ + PPCThread_Ready, + PPCThread_Running, + PPCThread_Paused, + PPCThread_Stopped, + PPCThread_Sleeping, + PPCThread_Break, +}; + class PPCThread : public ThreadBase { protected: @@ -43,6 +53,7 @@ public: void SetName(const wxString& name); void SetPrio(const u64 prio) { m_prio = prio; } void SetOffset(const u64 offset) { m_offset = offset; } + u64 GetOffset() { return m_offset; } u64 GetPrio() const { return m_prio; } wxString GetName() const { return m_name; } @@ -88,6 +99,16 @@ protected: public: ~PPCThread(); + u32 m_wait_thread_id; + + wxCriticalSection m_cs_sync; + bool m_sync_wait; + void Wait(bool wait); + void Wait(const PPCThread& thr); + bool Sync(); + + int ThreadStatus(); + void NextPc(); void NextBranchPc(); void PrevPc(); diff --git a/rpcs3/Emu/Cell/PPCThreadManager.cpp b/rpcs3/Emu/Cell/PPCThreadManager.cpp index dde13c534f..82ae53473d 100644 --- a/rpcs3/Emu/Cell/PPCThreadManager.cpp +++ b/rpcs3/Emu/Cell/PPCThreadManager.cpp @@ -35,14 +35,19 @@ void PPCThreadManager::RemoveThread(const u32 id) { for(u32 i=0; i(crfd, l ? CPU.GPR[ra] : (u32)CPU.GPR[ra], uimm16); + CPU.UpdateCRnU(l, crfd, CPU.GPR[ra], uimm16); } void CMPI(u32 crfd, u32 l, u32 ra, s32 simm16) { - CPU.UpdateCRn(crfd, l ? CPU.GPR[ra] : (s32)CPU.GPR[ra], simm16); + CPU.UpdateCRnS(l, crfd, CPU.GPR[ra], simm16); } void ADDIC(u32 rd, u32 ra, s32 simm16) { @@ -2172,12 +2172,12 @@ private: void ANDI_(u32 ra, u32 rs, u32 uimm16) { CPU.GPR[ra] = CPU.GPR[rs] & uimm16; - CPU.UpdateCR0(CPU.GPR[ra]); + CPU.UpdateCR0(CPU.GPR[ra]); } void ANDIS_(u32 ra, u32 rs, u32 uimm16) { CPU.GPR[ra] = CPU.GPR[rs] & (uimm16 << 16); - CPU.UpdateCR0(CPU.GPR[ra]); + CPU.UpdateCR0(CPU.GPR[ra]); } void RLDICL(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { @@ -2202,7 +2202,7 @@ private: } void CMP(u32 crfd, u32 l, u32 ra, u32 rb) { - CPU.UpdateCRn(crfd, l ? CPU.GPR[ra] : (s32)CPU.GPR[ra], l ? CPU.GPR[rb] : (s32)CPU.GPR[rb]); + CPU.UpdateCRnS(l, crfd, CPU.GPR[ra], CPU.GPR[rb]); } void TW(u32 to, u32 ra, u32 rb) { @@ -2256,7 +2256,7 @@ private: { const s64 RA = CPU.GPR[ra]; const s64 RB = CPU.GPR[rb]; - CPU.GPR[rd] = RB - RA; + CPU.GPR[rd] = ~RA + RB + 1; CPU.XER.CA = CPU.IsCarry(RA, RB); if(oe) UNK("subfco"); if(rc) CPU.UpdateCR0(CPU.GPR[rd]); @@ -2334,9 +2334,9 @@ private: CPU.reserve = true; CPU.GPR[rd] = Memory.Read32(addr); } - void LDX(u32 ra, u32 rs, u32 rb) + void LDX(u32 rd, u32 ra, u32 rb) { - CPU.GPR[ra] = Memory.Read64(rs ? CPU.GPR[rs] + CPU.GPR[rb] : CPU.GPR[rb]); + CPU.GPR[rd] = Memory.Read64(ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]); } void LWZX(u32 rd, u32 ra, u32 rb) { @@ -2362,11 +2362,16 @@ private: CPU.GPR[ra] = i; - if(rc) CPU.UpdateCR0(CPU.GPR[ra]); + if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } void SLD(u32 ra, u32 rs, u32 rb, bool rc) { - CPU.GPR[ra] = CPU.GPR[rb] & 0x40 ? 0 : CPU.GPR[rs] << (CPU.GPR[rb] & 0x3f); + const u32 n = CPU.GPR[rb] & 0x3f; + const u64 r = rotl64(CPU.GPR[rs], n); + const u64 m = (CPU.GPR[rb] & 0x30) ? 0 : rotate_mask[0][63 - n]; + + CPU.GPR[ra] = r & m; + if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } void AND(u32 ra, u32 rs, u32 rb, bool rc) @@ -2376,7 +2381,7 @@ private: } void CMPL(u32 crfd, u32 l, u32 ra, u32 rb) { - CPU.UpdateCRn(crfd, l ? CPU.GPR[ra] : (u32)CPU.GPR[ra], l ? CPU.GPR[rb] : (u32)CPU.GPR[rb]); + CPU.UpdateCRnU(l, crfd, CPU.GPR[ra], CPU.GPR[rb]); } void LVSR(u32 vd, u32 ra, u32 rb) { @@ -2570,21 +2575,23 @@ private: void STWCX_(u32 rs, u32 ra, u32 rb) { CPU.SetCR(0, CPU.XER.SO ? CR_SO : 0); - if(!CPU.reserve) return; - - const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; - - if(addr == CPU.reserve_addr) + + if(CPU.reserve) { - Memory.Write32(addr, CPU.GPR[rs]); - CPU.SetCR_EQ(0, true); - } - else - { - static const bool u = 0; - if(u) Memory.Write32(addr, CPU.GPR[rs]); - CPU.SetCR_EQ(0, u); - CPU.reserve = false; + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + + if(addr == CPU.reserve_addr) + { + Memory.Write32(addr, CPU.GPR[rs]); + CPU.SetCR_EQ(0, true); + } + else + { + static const bool u = 0; + if(u) Memory.Write32(addr, CPU.GPR[rs]); + CPU.SetCR_EQ(0, u); + CPU.reserve = false; + } } } void STWX(u32 rs, u32 ra, u32 rb) @@ -2621,21 +2628,22 @@ private: void STDCX_(u32 rs, u32 ra, u32 rb) { CPU.SetCR(0, CPU.XER.SO ? CR_SO : 0); - if(!CPU.reserve) return; - - const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; - - if(addr == CPU.reserve_addr) + if(!CPU.reserve) { - Memory.Write64(addr, CPU.GPR[rs]); - CPU.SetCR_EQ(0, true); - } - else - { - static const bool u = 0; - if(u) Memory.Write64(addr, CPU.GPR[rs]); - CPU.SetCR_EQ(0, u); - CPU.reserve = false; + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + + if(addr == CPU.reserve_addr) + { + Memory.Write64(addr, CPU.GPR[rs]); + CPU.SetCR_EQ(0, true); + } + else + { + static const bool u = 0; + if(u) Memory.Write64(addr, CPU.GPR[rs]); + CPU.SetCR_EQ(0, u); + CPU.reserve = false; + } } } void STBX(u32 rs, u32 ra, u32 rb) @@ -2824,10 +2832,10 @@ private: const s64 RA = CPU.GPR[ra]; const s64 RB = CPU.GPR[rb]; - if (RB == 0 || ((u64)RA == 0x8000000000000000 && RB == -1)) + if (RB == 0 || ((u64)RA == (1ULL << 63) && RB == -1)) { if(oe) UNK("divdo"); - CPU.GPR[rd] = (((u64)RA & 0x8000000000000000) && RB == 0) ? -1 : 0; + CPU.GPR[rd] = (((u64)RA & (1ULL << 63)) && RB == 0) ? -1 : 0; } else { @@ -2841,10 +2849,10 @@ private: const s32 RA = CPU.GPR[ra]; const s32 RB = CPU.GPR[rb]; - if (RB == 0 || ((u32)RA == 0x80000000 && RB == -1)) + if (RB == 0 || ((u32)RA == (1 << 31) && RB == -1)) { if(oe) UNK("divwo"); - CPU.GPR[rd] = (((u32)RA & 0x80000000) && RB == 0) ? -1 : 0; + CPU.GPR[rd] = (((u32)RA & (1 << 31)) && RB == 0) ? -1 : 0; } else { @@ -2871,12 +2879,20 @@ private: } void SRW(u32 ra, u32 rs, u32 rb, bool rc) { - CPU.GPR[ra] = CPU.GPR[rb] & 0x20 ? 0 : (u32)CPU.GPR[rs] >> (CPU.GPR[rb] & 0x1f); + u32 n = CPU.GPR[rb] & 0x1f; + u64 r = rotl32((u32)CPU.GPR[rs], 64 - n); + u64 m = CPU.GPR[rb] & 0x20 ? 0 : rotate_mask[32 + n][63]; + CPU.GPR[ra] = r & m; + if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } void SRD(u32 ra, u32 rs, u32 rb, bool rc) { - CPU.GPR[ra] = CPU.GPR[rb] & 0x40 ? 0 : CPU.GPR[rs] >> (CPU.GPR[rb] & 0x3f); + u32 n = CPU.GPR[rb] & 0x3f; + u64 r = rotl64(CPU.GPR[rs], 64 - n); + u64 m = CPU.GPR[rb] & 0x40 ? 0 : rotate_mask[n][63]; + CPU.GPR[ra] = r & m; + if(rc) CPU.UpdateCR0(CPU.GPR[ra]); } void LVRX(u32 vd, u32 ra, u32 rb) @@ -2940,7 +2956,7 @@ private: CPU.GPR[rd] = (u16&)Memory[ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]]; } void SRAW(u32 ra, u32 rs, u32 rb, bool rc) - { + { s32 RS = CPU.GPR[rs]; s32 RB = CPU.GPR[rb]; CPU.GPR[ra] = RS >> RB; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 0b8eb74234..0bb661060f 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -60,13 +60,13 @@ void PPUThread::AddArgv(const wxString& arg) void PPUThread::InitRegs() { - const u32 entry = Memory.Read32(PC); - const u32 rtoc = Memory.Read32(PC + 4); + const u32 pc = Memory.Read32(entry); + const u32 rtoc = Memory.Read32(entry + 4); ConLog.Write("entry = 0x%x", entry); ConLog.Write("rtoc = 0x%x", rtoc); - SetPc(entry); + SetPc(pc); u64 argc = m_arg; u64 argv = 0; @@ -110,20 +110,18 @@ void PPUThread::InitRegs() { GPR[3] = argc; GPR[4] = argv; - GPR[5] = argv ? argv - 0xc - 4 * argc : 0; //unk + GPR[5] = argv ? argv + 0xc + 4 * argc : 0; //unk } else { GPR[3] = m_arg; } - GPR[0] = entry; - //GPR[7] = 0x80d90; + GPR[0] = pc; GPR[8] = entry; - //GPR[10] = 0x131700; GPR[11] = 0x80; GPR[12] = Emu.GetMallocPageSize(); - GPR[13] = Memory.MainMem.Alloc(0x10000) + 0x7060; + GPR[13] = Memory.PRXMem.Alloc(0x10000) + 0x7060 - 8; GPR[28] = GPR[4]; GPR[29] = GPR[3]; GPR[31] = GPR[5]; diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 7e1c150632..dc543f0a9e 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -652,6 +652,30 @@ public: SetCR_SO(n, XER.SO); } + void UpdateCRnU(const u8 l, const u8 n, const u64 a, const u64 b) + { + if(l) + { + UpdateCRn(n, a, b); + } + else + { + UpdateCRn(n, a, b); + } + } + + void UpdateCRnS(const u8 l, const u8 n, const u64 a, const u64 b) + { + if(l) + { + UpdateCRn(n, a, b); + } + else + { + UpdateCRn(n, a, b); + } + } + template void UpdateCR0(const T val) { UpdateCRn(0, val, 0); diff --git a/rpcs3/Emu/Cell/SPUDecoder.h b/rpcs3/Emu/Cell/SPUDecoder.h index eac3dae07a..97af2abb3a 100644 --- a/rpcs3/Emu/Cell/SPUDecoder.h +++ b/rpcs3/Emu/Cell/SPUDecoder.h @@ -24,9 +24,3 @@ public: (*SPU_instr::rrr_list)(m_op, code); } }; - -#undef START_OPCODES_GROUP_ -#undef START_OPCODES_GROUP -#undef ADD_OPCODE -#undef ADD_NULL_OPCODE -#undef END_OPCODES_GROUP \ No newline at end of file diff --git a/rpcs3/Emu/Cell/SPUDisAsm.h b/rpcs3/Emu/Cell/SPUDisAsm.h index d8a94307b5..c82b9d7a2e 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.h +++ b/rpcs3/Emu/Cell/SPUDisAsm.h @@ -46,813 +46,861 @@ private: } private: + wxString& FixOp(wxString& op) + { + op.Append(' ', max(10 - (int)op.Len(), 0)); + return op; + } + void DisAsm(const char* op) + { + Write(op); + } + void DisAsm(wxString op, u32 a1) + { + Write(wxString::Format("%s 0x%x", FixOp(op), a1)); + } + void DisAsm(wxString op, const char* a1) + { + Write(wxString::Format("%s %s", FixOp(op), a1)); + } + void DisAsm(wxString op, const char* a1, const char* a2) + { + Write(wxString::Format("%s %s,%s", FixOp(op), a1, a2)); + } + void DisAsm(wxString op, int a1, const char* a2) + { + Write(wxString::Format("%s 0x%x,%s", FixOp(op), a1, a2)); + } + void DisAsm(wxString op, const char* a1, int a2) + { + Write(wxString::Format("%s %s,0x%x", FixOp(op), a1, a2)); + } + void DisAsm(wxString op, int a1, int a2) + { + Write(wxString::Format("%s 0x%x,0x%x", FixOp(op), a1, a2)); + } + void DisAsm(wxString op, const char* a1, const char* a2, const char* a3) + { + Write(wxString::Format("%s %s,%s,%s", FixOp(op), a1, a2, a3)); + } + void DisAsm(wxString op, const char* a1, int a2, const char* a3) + { + Write(wxString::Format("%s %s,0x%x(%s)", FixOp(op), a1, a2, a3)); + } + void DisAsm(wxString op, const char* a1, const char* a2, int a3) + { + Write(wxString::Format("%s %s,%s,0x%x", FixOp(op), a1, a2, a3)); + } + void DisAsm(wxString op, const char* a1, const char* a2, const char* a3, const char* a4) + { + Write(wxString::Format("%s %s,%s,%s,%s", FixOp(op), a1, a2, a3, a4)); + } //0 - 10 void STOP(u32 code) { - Write(wxString::Format("stop 0x%x", code)); + DisAsm("stop", code); } void LNOP() { - Write("lnop"); + DisAsm("lnop"); } void SYNC(u32 Cbit) { - Write(wxString::Format("sync %d", Cbit)); + DisAsm("sync", Cbit); } void DSYNC() { - Write("dsync"); + DisAsm("dsync"); } void MFSPR(u32 rt, u32 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 ? + DisAsm("mfspr", spu_reg_name[rt], spu_reg_name[sa]); // Are SPR mapped on the GPR or are there 128 additional registers ? } void RDCH(u32 rt, u32 ra) { - Write(wxString::Format("rdch %s,%s", spu_reg_name[rt], spu_ch_name[ra])); + DisAsm("rdch", spu_reg_name[rt], spu_ch_name[ra]); } void RCHCNT(u32 rt, u32 ra) { - Write(wxString::Format("rchcnt %s,%s", spu_reg_name[rt], spu_ch_name[ra])); + DisAsm("rchcnt", spu_reg_name[rt], spu_ch_name[ra]); } void SF(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("sf %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("sf", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void OR(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("or %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("or", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void BG(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("bg %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("bg", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void SFH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("sfh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("sfh", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void NOR(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("nor %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("nor", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ABSDB(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("absdb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("absdb", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rot %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rot", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTM(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotm %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotm", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTMA(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotma %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotma", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void SHL(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("shl %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("shl", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("roth %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("roth", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTHM(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rothm %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rothm", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTMAH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotmah %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotmah", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void SHLH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("shlh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("shlh", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("roti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("roti", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTMI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rotmi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rotmi", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTMAI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rotmai %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rotmai", spu_reg_name[rt], spu_reg_name[ra], i7); } void SHLI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("shli %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("shli", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTHI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rothi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rothi", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTHMI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rothmi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rothmi", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTMAHI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rotmahi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rotmahi", spu_reg_name[rt], spu_reg_name[ra], i7); } void SHLHI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("shlhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("shlhi", spu_reg_name[rt], spu_reg_name[ra], i7); } void A(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("a %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("a", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void AND(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("and %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("and", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CG(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("cg %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("cg", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void AH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("ah %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("ah", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void NAND(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("nand %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("nand", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void AVGB(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("avgb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("avgb", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MTSPR(u32 rt, u32 sa) { - Write(wxString::Format("mtspr %s,%s", spu_reg_name[rt], spu_reg_name[sa])); + DisAsm("mtspr", spu_reg_name[rt], spu_reg_name[sa]); } void WRCH(u32 ra, u32 rt) { - Write(wxString::Format("wrch %s,%s", spu_ch_name[ra], spu_reg_name[rt])); + DisAsm("wrch", spu_ch_name[ra], spu_reg_name[rt]); } void BIZ(u32 rt, u32 ra) { - Write(wxString::Format("biz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("biz", spu_reg_name[rt], spu_reg_name[ra]); } void BINZ(u32 rt, u32 ra) { - Write(wxString::Format("binz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("binz", spu_reg_name[rt], spu_reg_name[ra]); } void BIHZ(u32 rt, u32 ra) { - Write(wxString::Format("bihz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("bihz", spu_reg_name[rt], spu_reg_name[ra]); } void BIHNZ(u32 rt, u32 ra) { - Write(wxString::Format("bihnz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("bihnz", spu_reg_name[rt], spu_reg_name[ra]); } void STOPD(u32 rc, u32 ra, u32 rb) { - Write(wxString::Format("bihnz %s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("bihnz", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb]); } void STQX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("stqx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("stqx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void BI(u32 ra) { - Write(wxString::Format("bi %s", spu_reg_name[ra])); + DisAsm("bi", spu_reg_name[ra]); } void BISL(u32 rt, u32 ra) { - Write(wxString::Format("bisl %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("bisl", spu_reg_name[rt], spu_reg_name[ra]); } void IRET(u32 ra) { - Write(wxString::Format("iret %s", spu_reg_name[ra])); + DisAsm("iret", spu_reg_name[ra]); } void BISLED(u32 rt, u32 ra) { - Write(wxString::Format("bisled %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("bisled", spu_reg_name[rt], spu_reg_name[ra]); } void HBR(u32 p, u32 ro, u32 ra) { - Write(wxString::Format("hbr 0x%x,%s", DisAsmBranchTarget(ro), spu_reg_name[ra])); + DisAsm("hbr", DisAsmBranchTarget(ro), spu_reg_name[ra]); } void GB(u32 rt, u32 ra) { - Write(wxString::Format("gb %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("gb", spu_reg_name[rt], spu_reg_name[ra]); } void GBH(u32 rt, u32 ra) { - Write(wxString::Format("gbh %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("gbh", spu_reg_name[rt], spu_reg_name[ra]); } void GBB(u32 rt, u32 ra) { - Write(wxString::Format("gbb %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("gbb", spu_reg_name[rt], spu_reg_name[ra]); } void FSM(u32 rt, u32 ra) { - Write(wxString::Format("fsm %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("fsm", spu_reg_name[rt], spu_reg_name[ra]); } void FSMH(u32 rt, u32 ra) { - Write(wxString::Format("fsmh %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("fsmh", spu_reg_name[rt], spu_reg_name[ra]); } void FSMB(u32 rt, u32 ra) { - Write(wxString::Format("fsmb %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("fsmb", spu_reg_name[rt], spu_reg_name[ra]); } void FREST(u32 rt, u32 ra) { - Write(wxString::Format("frest %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("frest", spu_reg_name[rt], spu_reg_name[ra]); } void FRSQEST(u32 rt, u32 ra) { - Write(wxString::Format("frsqest %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("frsqest", spu_reg_name[rt], spu_reg_name[ra]); } void LQX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("lqx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("lqx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTQBYBI(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotqbybi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotqbybi", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTQMBYBI(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotqmbybi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotqmbybi", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void SHLQBYBI(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("shlqbybi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("shlqbybi", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CBX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("cbx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("cbx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CHX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("chx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("chx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CWX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("cwx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("cwx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CDX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("cdx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("cdx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTQBI(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotqbi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotqbi", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTQMBI(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotqmbi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotqmbi", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void SHLQBI(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("shlqbi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("shlqbi", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTQBY(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotqby %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotqby", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ROTQMBY(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("rotqmby %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("rotqmby", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void SHLQBY(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("shlqby %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("shlqby", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ORX(u32 rt, u32 ra) { - Write(wxString::Format("orx %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("orx", spu_reg_name[rt], spu_reg_name[ra]); } void CBD(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("cbd %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("cbd", spu_reg_name[rt], spu_reg_name[ra], i7); } void CHD(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("chd %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("chd", spu_reg_name[rt], spu_reg_name[ra], i7); } void CWD(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("cwd %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("cwd", spu_reg_name[rt], spu_reg_name[ra], i7); } void CDD(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("cdd %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("cdd", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTQBII(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rotqbii %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rotqbii", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTQMBII(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rotqmbii %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rotqmbii", spu_reg_name[rt], spu_reg_name[ra], i7); } void SHLQBII(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("shlqbii %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("shlqbii", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTQBYI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rotqbyi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rotqbyi", spu_reg_name[rt], spu_reg_name[ra], i7); } void ROTQMBYI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("rotqmbyi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("rotqmbyi", spu_reg_name[rt], spu_reg_name[ra], i7); } void SHLQBYI(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("shlqbyi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("shlqbyi", spu_reg_name[rt], spu_reg_name[ra], i7); } void NOP(u32 rt) { - Write(wxString::Format("nop %s", spu_reg_name[rt])); + DisAsm("nop", spu_reg_name[rt]); } void CGT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("cgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("cgt", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void XOR(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("xor %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("xor", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CGTH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("cgth %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("cgth", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void EQV(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("eqv %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("eqv", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CGTB(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("cgtb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("cgtb", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void SUMB(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("sumb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("sumb", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void HGT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("hgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("hgt", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CLZ(u32 rt, u32 ra) { - Write(wxString::Format("clz %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("clz", spu_reg_name[rt], spu_reg_name[ra]); } void XSWD(u32 rt, u32 ra) { - Write(wxString::Format("xswd %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("xswd", spu_reg_name[rt], spu_reg_name[ra]); } void XSHW(u32 rt, u32 ra) { - Write(wxString::Format("xshw %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("xshw", spu_reg_name[rt], spu_reg_name[ra]); } void CNTB(u32 rt, u32 ra) { - Write(wxString::Format("cntb %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("cntb", spu_reg_name[rt], spu_reg_name[ra]); } void XSBH(u32 rt, u32 ra) { - Write(wxString::Format("xsbh %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("xsbh", spu_reg_name[rt], spu_reg_name[ra]); } void CLGT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("clgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("clgt", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ANDC(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("andc %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("andc", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void FCGT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("fcgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("fcgt", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFCGT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfcgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfcgt", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void FA(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("fa %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("fa", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void FS(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("fs %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("fs", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void FM(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("fm %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("fm", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CLGTH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("clgth %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("clgth", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ORC(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("orc %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("orc", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void FCMGT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("fcmgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("fcmgt", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFCMGT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfcmgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfcmgt", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFA(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfa %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfa", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFS(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfs %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfs", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFM(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfm %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfm", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CLGTB(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("clgtb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("clgtb", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void HLGT(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("hlgt %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("hlgt", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFMA(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfma %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfma", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFMS(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfms %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfms", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFNMS(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfnms %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfnms", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFNMA(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfnma %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfnma", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CEQ(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("ceq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("ceq", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MPYHHU(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("mpyhhu %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("mpyhhu", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void ADDX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("addx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("addx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void SFX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("sfx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("sfx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CGX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("cgx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("cgx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void BGX(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("bgx %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("bgx", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MPYHHA(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("mpyhha %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("mpyhha", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MPYHHAU(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("mpyhhau %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("mpyhhau", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void FSCRRD(u32 rt) { - Write(wxString::Format("fscrrd %s", spu_reg_name[rt])); + DisAsm("fscrrd", spu_reg_name[rt]); } void FESD(u32 rt, u32 ra) { - Write(wxString::Format("fesd %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("fesd", spu_reg_name[rt], spu_reg_name[ra]); } void FRDS(u32 rt, u32 ra) { - Write(wxString::Format("frds %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("frds", spu_reg_name[rt], spu_reg_name[ra]); } void FSCRWR(u32 rt, u32 ra) { - Write(wxString::Format("fscrwr %s,%s", spu_reg_name[rt], spu_reg_name[ra])); + DisAsm("fscrwr", spu_reg_name[rt], spu_reg_name[ra]); } void DFTSV(u32 rt, u32 ra, s32 i7) { - Write(wxString::Format("dftsv %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i7)); + DisAsm("dftsv", spu_reg_name[rt], spu_reg_name[ra], i7); } void FCEQ(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("fceq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("fceq", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFCEQ(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfceq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfceq", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MPY(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("mpy %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("mpy", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MPYH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("mpyh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("mpyh", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MPYHH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("mpyhh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("mpyhh", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MPYS(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("mpys %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("mpys", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CEQH(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("ceqh %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("ceqh", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void FCMEQ(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("fcmeq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("fcmeq", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void DFCMEQ(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("dfcmeq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("dfcmeq", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void MPYU(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("mpyu %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("mpyu", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void CEQB(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("ceqb %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("ceqb", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void FI(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("fi %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("fi", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } void HEQ(u32 rt, u32 ra, u32 rb) { - Write(wxString::Format("heq %s,%s,%s", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb])); + DisAsm("heq", spu_reg_name[rt], spu_reg_name[ra], spu_reg_name[rb]); } //0 - 9 void CFLTS(u32 rt, u32 ra, s32 i8) { - Write(wxString::Format("cflts %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i8)); + DisAsm("cflts", spu_reg_name[rt], spu_reg_name[ra], i8); } void CFLTU(u32 rt, u32 ra, s32 i8) { - Write(wxString::Format("cfltu %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i8)); + DisAsm("cfltu", spu_reg_name[rt], spu_reg_name[ra], i8); } void CSFLT(u32 rt, u32 ra, s32 i8) { - Write(wxString::Format("csflt %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i8)); + DisAsm("csflt", spu_reg_name[rt], spu_reg_name[ra], i8); } void CUFLT(u32 rt, u32 ra, s32 i8) { - Write(wxString::Format("cuflt %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i8)); + DisAsm("cuflt", spu_reg_name[rt], spu_reg_name[ra], i8); } //0 - 8 void BRZ(u32 rt, s32 i16) { - Write(wxString::Format("brz %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("brz", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void STQA(u32 rt, s32 i16) { - Write(wxString::Format("stqa %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("stqa", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void BRNZ(u32 rt, s32 i16) { - Write(wxString::Format("brnz %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("brnz", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void BRHZ(u32 rt, s32 i16) { - Write(wxString::Format("brhz %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("brhz", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void BRHNZ(u32 rt, s32 i16) { - Write(wxString::Format("brhnz %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("brhnz", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void STQR(u32 rt, s32 i16) { - Write(wxString::Format("stqr %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("stqr", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void BRA(s32 i16) { - Write(wxString::Format("bra 0x%x", DisAsmBranchTarget(i16))); + DisAsm("bra", DisAsmBranchTarget(i16)); } void LQA(u32 rt, s32 i16) { - Write(wxString::Format("lqa %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("lqa", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void BRASL(u32 rt, s32 i16) { - Write(wxString::Format("brasl %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("brasl", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void BR(s32 i16) { - Write(wxString::Format("br 0x%x", DisAsmBranchTarget(i16))); + DisAsm("br", DisAsmBranchTarget(i16)); } void FSMBI(u32 rt, s32 i16) { - Write(wxString::Format("fsmbi %s,%d", spu_reg_name[rt], i16)); + DisAsm("fsmbi", spu_reg_name[rt], i16); } void BRSL(u32 rt, s32 i16) { - Write(wxString::Format("brsl %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("brsl", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void LQR(u32 rt, s32 i16) { - Write(wxString::Format("lqr %s,0x%x", spu_reg_name[rt], DisAsmBranchTarget(i16))); + DisAsm("lqr", spu_reg_name[rt], DisAsmBranchTarget(i16)); } void IL(u32 rt, s32 i16) { - Write(wxString::Format("il %s,%d", spu_reg_name[rt], i16)); + DisAsm("il", spu_reg_name[rt], i16); } void ILHU(u32 rt, s32 i16) { - Write(wxString::Format("ilhu %s,%d", spu_reg_name[rt], i16)); + DisAsm("ilhu", spu_reg_name[rt], i16); } void ILH(u32 rt, s32 i16) { - Write(wxString::Format("ilh %s,%d", spu_reg_name[rt], i16)); + DisAsm("ilh", spu_reg_name[rt], i16); } void IOHL(u32 rt, s32 i16) { - Write(wxString::Format("iolh %s,%d", spu_reg_name[rt], i16)); + DisAsm("iolh", spu_reg_name[rt], i16); } - //0 - 7 void ORI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("ori %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("ori", spu_reg_name[rt], spu_reg_name[ra], i10); } void ORHI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("orhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("orhi", spu_reg_name[rt], spu_reg_name[ra], i10); } void ORBI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("orbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("orbi", spu_reg_name[rt], spu_reg_name[ra], i10); } void SFI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("sfi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("sfi", spu_reg_name[rt], spu_reg_name[ra], i10); } void SFHI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("sfhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("sfhi", spu_reg_name[rt], spu_reg_name[ra], i10); } void ANDI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("andi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("andi", spu_reg_name[rt], spu_reg_name[ra], i10); } void ANDHI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("andhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("andhi", spu_reg_name[rt], spu_reg_name[ra], i10); } void ANDBI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("andbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("andbi", spu_reg_name[rt], spu_reg_name[ra], i10); } void AI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("ai %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("ai", spu_reg_name[rt], spu_reg_name[ra], i10); } void AHI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("ahi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("ahi", spu_reg_name[rt], spu_reg_name[ra], i10); } void STQD(u32 rt, s32 i10, u32 ra) { - Write(wxString::Format("stqd %s,%d(%s)", spu_reg_name[rt], i10, spu_reg_name[ra])); + DisAsm("stqd", spu_reg_name[rt], i10, spu_reg_name[ra]); } void LQD(u32 rt, s32 i10, u32 ra) { - Write(wxString::Format("lqd %s,%d(%s)", spu_reg_name[rt], i10, spu_reg_name[ra])); + DisAsm("lqd", spu_reg_name[rt], i10, spu_reg_name[ra]); } void XORI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("xori %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("xori", spu_reg_name[rt], spu_reg_name[ra], i10); } void XORHI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("xorhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("xorhi", spu_reg_name[rt], spu_reg_name[ra], i10); } void XORBI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("xorbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("xorbi", spu_reg_name[rt], spu_reg_name[ra], i10); } void CGTI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("cgti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("cgti", spu_reg_name[rt], spu_reg_name[ra], i10); } void CGTHI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("cgthi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("cgthi", spu_reg_name[rt], spu_reg_name[ra], i10); } void CGTBI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("cgtbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("cgtbi", spu_reg_name[rt], spu_reg_name[ra], i10); } void HGTI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("hgti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("hgti", spu_reg_name[rt], spu_reg_name[ra], i10); } void CLGTI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("clgti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("clgti", spu_reg_name[rt], spu_reg_name[ra], i10); } void CLGTHI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("clgthi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("clgthi", spu_reg_name[rt], spu_reg_name[ra], i10); } void CLGTBI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("clgtbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("clgtbi", spu_reg_name[rt], spu_reg_name[ra], i10); } void HLGTI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("hlgti %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("hlgti", spu_reg_name[rt], spu_reg_name[ra], i10); } void MPYI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("mpyi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("mpyi", spu_reg_name[rt], spu_reg_name[ra], i10); } void MPYUI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("mpyui %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("mpyui", spu_reg_name[rt], spu_reg_name[ra], i10); } void CEQI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("ceqi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("ceqi", spu_reg_name[rt], spu_reg_name[ra], i10); } void CEQHI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("ceqhi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("ceqhi", spu_reg_name[rt], spu_reg_name[ra], i10); } void CEQBI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("ceqbi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("ceqbi", spu_reg_name[rt], spu_reg_name[ra], i10); } void HEQI(u32 rt, u32 ra, s32 i10) { - Write(wxString::Format("heqi %s,%s,%d", spu_reg_name[rt], spu_reg_name[ra], i10)); + DisAsm("heqi", spu_reg_name[rt], spu_reg_name[ra], i10); } //0 - 6 void HBRA(s32 ro, s32 i16) { - Write(wxString::Format("hbra 0x%x,0x%x", DisAsmBranchTarget(ro), DisAsmBranchTarget(i16))); + DisAsm("hbra", DisAsmBranchTarget(ro), DisAsmBranchTarget(i16)); } void HBRR(s32 ro, s32 i16) { - Write(wxString::Format("hbrr 0x%x,0x%x", DisAsmBranchTarget(ro), DisAsmBranchTarget(i16))); + DisAsm("hbrr", DisAsmBranchTarget(ro), DisAsmBranchTarget(i16)); } void ILA(u32 rt, s32 i18) { - Write(wxString::Format("ila %s,%d", spu_reg_name[rt], i18)); + DisAsm("ila", spu_reg_name[rt], i18); } //0 - 3 void SELB(u32 rc, u32 ra, u32 rb, u32 rt) { - Write(wxString::Format("selb %s,%s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt])); + DisAsm("selb", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt]); } void SHUFB(u32 rc, u32 ra, u32 rb, u32 rt) { - Write(wxString::Format("shufb %s,%s,%s,%s", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt])); + DisAsm("shufb", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt]); } void MPYA(u32 rc, u32 ra, u32 rb, u32 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])); + DisAsm("mpya", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt]); } void FNMS(u32 rc, u32 ra, u32 rb, u32 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])); + DisAsm("fnms", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt]); } void FMA(u32 rc, u32 ra, u32 rb, u32 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])); + DisAsm("fma", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt]); } void FMS(u32 rc, u32 ra, u32 rb, u32 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])); + DisAsm("fms", spu_reg_name[rc], spu_reg_name[ra], spu_reg_name[rb], spu_reg_name[rt]); } void UNK(u32 code, u32 opcode, u32 gcode) @@ -860,6 +908,3 @@ private: Write(wxString::Format("Unknown/Illegal opcode! (0x%08x, 0x%x, 0x%x)", code, opcode, gcode)); } }; - -#undef START_OPCODES_GROUP -#undef END_OPCODES_GROUP \ No newline at end of file diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index 39952916ee..dcbc7fba8d 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -27,18 +27,18 @@ private: //0 - 10 void STOP(u32 code) { - Emu.Pause(); + CPU.Pause(); } void LNOP() { } void SYNC(u32 Cbit) { - UNIMPLEMENTED(); + //UNIMPLEMENTED(); } void DSYNC() { - UNIMPLEMENTED(); + //UNIMPLEMENTED(); } void MFSPR(u32 rt, u32 sa) { @@ -51,7 +51,7 @@ private: void RCHCNT(u32 rt, u32 ra) { CPU.GPR[rt].Reset(); - CPU.GPR[rt]._u32[0] = CPU.GetChannelCount(ra); + CPU.GPR[rt]._u32[3] = CPU.GetChannelCount(ra); } void SF(u32 rt, u32 ra, u32 rb) { @@ -280,16 +280,15 @@ private: } void STQX(u32 rt, u32 ra, u32 rb) { - CPU.LSA = CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]; - CPU.WriteLSA128(CPU.GPR[rt]._u128); + CPU.WriteLS128(CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0], CPU.GPR[rt]._u128); } void BI(u32 ra) { - CPU.SetBranch(CPU.GPR[ra]._u32[0] & 0xfffffffc); + CPU.SetBranch(CPU.GPR[ra]._u32[3] & 0xfffffffc); } void BISL(u32 rt, u32 ra) { - CPU.SetBranch(CPU.GPR[ra]._u32[0] & 0xfffffffc); + CPU.SetBranch(CPU.GPR[ra]._u32[3] & 0xfffffffc); CPU.GPR[rt].Reset(); CPU.GPR[rt]._u32[0] = CPU.PC + 4; } @@ -353,8 +352,7 @@ private: } void LQX(u32 rt, u32 ra, u32 rb) { - CPU.LSA = CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]; - CPU.GPR[rt]._u128 = CPU.ReadLSA128(); + CPU.GPR[rt]._u128 = CPU.ReadLS128(CPU.GPR[ra]._u32[0] + CPU.GPR[rb]._u32[0]); } void ROTQBYBI(u32 rt, u32 ra, u32 rb) { @@ -870,8 +868,6 @@ private: UNIMPLEMENTED(); } - - //0 - 9 void CFLTS(u32 rt, u32 ra, s32 i8) { @@ -890,35 +886,31 @@ private: UNIMPLEMENTED(); } - - //0 - 8 void BRZ(u32 rt, s32 i16) { - if(!CPU.GPR[rt]._u32[0]) CPU.SetBranch(branchTarget(CPU.PC, i16)); + if(!CPU.GPR[rt]._u32[3]) CPU.SetBranch(branchTarget(CPU.PC, i16)); } void STQA(u32 rt, s32 i16) { - CPU.LSA = i16 << 2; - CPU.WriteLSA128(CPU.GPR[rt]._u128); + CPU.WriteLS128(i16 << 2, CPU.GPR[rt]._u128); } void BRNZ(u32 rt, s32 i16) { - if(CPU.GPR[rt]._u32[0] != 0) + if(CPU.GPR[rt]._u32[3] != 0) CPU.SetBranch(branchTarget(CPU.PC, i16)); } void BRHZ(u32 rt, s32 i16) { - if(!CPU.GPR[rt]._u16[0]) CPU.SetBranch(branchTarget(CPU.PC, i16)); + if(!CPU.GPR[rt]._u16[7]) CPU.SetBranch(branchTarget(CPU.PC, i16)); } void BRHNZ(u32 rt, s32 i16) { - if(CPU.GPR[rt]._u16[0]) CPU.SetBranch(branchTarget(CPU.PC, i16)); + if(CPU.GPR[rt]._u16[7]) CPU.SetBranch(branchTarget(CPU.PC, i16)); } void STQR(u32 rt, s32 i16) { - CPU.LSA = branchTarget(CPU.PC, i16); - CPU.WriteLSA128(CPU.GPR[rt]._u128); + CPU.WriteLS128(branchTarget(CPU.PC, i16), CPU.GPR[rt]._u128); } void BRA(s32 i16) { @@ -926,14 +918,7 @@ private: } void LQA(u32 rt, s32 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(); + CPU.GPR[rt]._u128 = CPU.ReadLS128(i16 << 2); } void BRASL(u32 rt, s32 i16) { @@ -964,19 +949,12 @@ private: void BRSL(u32 rt, s32 i16) { CPU.GPR[rt].Reset(); - CPU.GPR[rt]._u32[0] = CPU.PC + 4; + CPU.GPR[rt]._u32[3] = CPU.PC + 4; CPU.SetBranch(branchTarget(CPU.PC, i16)); } void LQR(u32 rt, s32 i16) { - CPU.LSA = branchTarget(CPU.PC, i16); - if(!Memory.IsGoodAddr(CPU.LSA)) - { - ConLog.Warning("LQR: Bad addr: 0x%x", CPU.LSA); - return; - } - - CPU.GPR[rt]._u128 = CPU.ReadLSA128(); + CPU.GPR[rt]._u128 = CPU.ReadLS128(branchTarget(CPU.PC, i16)); } void IL(u32 rt, s32 i16) { @@ -988,7 +966,7 @@ private: void ILHU(u32 rt, s32 i16) { for (int w = 0; w < 4; w++) - CPU.GPR[rt]._u16[w*2] = i16; + CPU.GPR[rt]._u16[w*2 + 1] = i16; } void ILH(u32 rt, s32 i16) { @@ -1061,13 +1039,11 @@ private: } void STQD(u32 rt, s32 i10, u32 ra) { - CPU.LSA = branchTarget(0, i10 + CPU.GPR[ra]._u32[0]); - CPU.WriteLSA128(CPU.GPR[rt]._u128); + CPU.WriteLS128(CPU.GPR[ra]._u32[3] + i10, CPU.GPR[rt]._u128); } void LQD(u32 rt, s32 i10, u32 ra) { - CPU.LSA = branchTarget(0, i10 + CPU.GPR[ra]._u32[0]); - CPU.GPR[rt]._u128 = CPU.ReadLSA128(); + CPU.GPR[rt]._u128 = CPU.ReadLS128(CPU.GPR[ra]._u32[3] + i10); } void XORI(u32 rt, u32 ra, s32 i10) { @@ -1167,10 +1143,6 @@ private: } void HBRR(s32 ro, s32 i16) { - UNIMPLEMENTED(); - //CHECK ME - //CPU.GPR[0]._u64[0] = branchTarget(CPU.PC, i16); - //CPU.SetBranch(branchTarget(CPU.PC, ro)); } void ILA(u32 rt, s32 i18) { @@ -1189,12 +1161,6 @@ private: ( CPU.GPR[rc]._u32[i] & CPU.GPR[rb]._u32[i]) | (~CPU.GPR[rc]._u32[i] & CPU.GPR[ra]._u32[i]); } - /* - CPU.GPR[rt] = _mm_or_si128( - _mm_and_si128(CPU.GPR[rc], CPU.GPR[rb]), - _mm_andnot_si128(CPU.GPR[rc], CPU.GPR[ra]) - ); - */ } void SHUFB(u32 rc, u32 ra, u32 rb, u32 rt) { diff --git a/rpcs3/Emu/Cell/SPUOpcodes.h b/rpcs3/Emu/Cell/SPUOpcodes.h index 1b56e9281b..e3bff38d3e 100644 --- a/rpcs3/Emu/Cell/SPUOpcodes.h +++ b/rpcs3/Emu/Cell/SPUOpcodes.h @@ -231,8 +231,8 @@ class SPU_Opcodes public: static u32 branchTarget(const u64 pc, const s32 imm) { - return (pc + ((imm << 2) & ~0x3)) & 0x3fff0; - } + return (pc + (imm << 2)) & 0x3fffc; + } virtual void Exit()=0; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index fcac328f20..ad636cacb3 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -26,8 +26,6 @@ void SPUThread::DoReset() { //reset regs for(u32 i=0; i<128; ++i) GPR[i].Reset(); - - LSA = 0; } void SPUThread::InitRegs() diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 54e46e85f6..e713e5c8e0 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -1,29 +1,30 @@ #pragma once #include "PPCThread.h" +#include "Emu/event.h" static const wxString spu_reg_name[128] = { - "$LR", "$SP", "$3", "$4", "$5", "$6", "$7", "$8", - "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$16", - "$17", "$18", "$19", "$20", "$21", "$22", "$23", "$24", - "$25", "$26", "$27", "$28", "$29", "$30", "$31", "$32", - "$33", "$34", "$35", "$36", "$37", "$38", "$39", "$40", - "$41", "$42", "$43", "$44", "$45", "$46", "$47", "$48", - "$49", "$50", "$51", "$52", "$53", "$54", "$55", "$56", - "$57", "$58", "$59", "$60", "$61", "$62", "$63", "$64", - "$65", "$66", "$67", "$68", "$69", "$70", "$71", "$72", - "$73", "$74", "$75", "$76", "$77", "$78", "$79", "$80", - "$81", "$82", "$83", "$84", "$85", "$86", "$87", "$88", - "$89", "$90", "$91", "$92", "$93", "$94", "$95", "$96", - "$97", "$98", "$99", "$100", "$101", "$102", "$103", "$104", - "$105", "$106", "$107", "$108", "$109", "$110", "$111", "$112", - "$113", "$114", "$115", "$116", "$117", "$118", "$119", "$120", - "$121", "$122", "$123", "$124", "$125", "$126", "$127", + "$LR", "$SP", "$2", "$3", "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31", + "$32", "$33", "$34", "$35", "$36", "$37", "$38", "$39", + "$40", "$41", "$42", "$43", "$44", "$45", "$46", "$47", + "$48", "$49", "$50", "$51", "$52", "$53", "$54", "$55", + "$56", "$57", "$58", "$59", "$60", "$61", "$62", "$63", + "$64", "$65", "$66", "$67", "$68", "$69", "$70", "$71", + "$72", "$73", "$74", "$75", "$76", "$77", "$78", "$79", + "$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87", + "$88", "$89", "$90", "$91", "$92", "$93", "$94", "$95", + "$96", "$97", "$98", "$99", "$100", "$101", "$102", "$103", + "$104", "$105", "$106", "$107", "$108", "$109", "$110", "$111", + "$112", "$113", "$114", "$115", "$116", "$117", "$118", "$119", + "$120", "$121", "$122", "$123", "$124", "$125", "$126", "$127", }; static const wxString spu_ch_name[128] = { - "$SPU_RdEventStat", "$SPU_WrEventMask", "$SPU_RdSigNotify1", + "$SPU_RdEventStat", "$SPU_WrEventMask", "$SPU_WrEventAck", "$SPU_RdSigNotify1", "$SPU_RdSigNotify2", "$ch5", "$ch6", "$SPU_WrDec", "$SPU_RdDec", "$MFC_WrMSSyncReq", "$ch10", "$SPU_RdEventMask", "$MFC_RdTagMask", "$SPU_RdMachStat", "$SPU_WrSRR0", "$SPU_RdSRR0", "$MFC_LSA", "$MFC_EAH", "$MFC_EAL", "$MFC_Size", @@ -82,8 +83,6 @@ enum MFCchannels union SPU_GPR_hdr { - //__m128i _m128i; - u128 _u128; s128 _i128; u64 _u64[2]; @@ -96,19 +95,6 @@ union SPU_GPR_hdr s8 _i8[16]; SPU_GPR_hdr() {} - /* - SPU_GPR_hdr(const __m128i val){_u128._u64[0] = val.m128i_u64[0]; _u128._u64[1] = val.m128i_u64[1];} - SPU_GPR_hdr(const u128 val) { _u128 = val; } - SPU_GPR_hdr(const u64 val) { Reset(); _u64[0] = val; } - SPU_GPR_hdr(const u32 val) { Reset(); _u32[0] = val; } - SPU_GPR_hdr(const u16 val) { Reset(); _u16[0] = val; } - SPU_GPR_hdr(const u8 val) { Reset(); _u8[0] = val; } - SPU_GPR_hdr(const s128 val) { _i128 = val; } - SPU_GPR_hdr(const s64 val) { Reset(); _i64[0] = val; } - SPU_GPR_hdr(const s32 val) { Reset(); _i32[0] = val; } - SPU_GPR_hdr(const s16 val) { Reset(); _i16[0] = val; } - SPU_GPR_hdr(const s8 val) { Reset(); _i8[0] = val; } - */ wxString ToString() const { @@ -119,41 +105,17 @@ union SPU_GPR_hdr { memset(this, 0, sizeof(*this)); } - - //operator __m128i() { __m128i ret; ret.m128i_u64[0]=_u128._u64[0]; ret.m128i_u64[1]=_u128._u64[1]; return ret; } - /* - SPU_GPR_hdr operator ^ (__m128i right) { return _mm_xor_si128(*this, right); } - SPU_GPR_hdr operator | (__m128i right) { return _mm_or_si128 (*this, right); } - SPU_GPR_hdr operator & (__m128i right) { return _mm_and_si128(*this, right); } - SPU_GPR_hdr operator << (int right) { return _mm_slli_epi32(*this, right); } - SPU_GPR_hdr operator << (__m128i right) { return _mm_sll_epi32(*this, right); } - SPU_GPR_hdr operator >> (int right) { return _mm_srai_epi32(*this, right); } - SPU_GPR_hdr operator >> (__m128i right) { return _mm_sra_epi32(*this, right); } - - SPU_GPR_hdr operator | (__m128i right) { return _mm_or_si128 (*this, right); } - SPU_GPR_hdr operator & (__m128i right) { return _mm_and_si128(*this, right); } - SPU_GPR_hdr operator << (int right) { return _mm_slli_epi32(*this, right); } - SPU_GPR_hdr operator << (__m128i right) { return _mm_sll_epi32(*this, right); } - SPU_GPR_hdr operator >> (int right) { return _mm_srai_epi32(*this, right); } - SPU_GPR_hdr operator >> (__m128i right) { return _mm_sra_epi32(*this, right); } - - SPU_GPR_hdr operator ^= (__m128i right) { return *this = *this ^ right; } - SPU_GPR_hdr operator |= (__m128i right) { return *this = *this | right; } - SPU_GPR_hdr operator &= (__m128i right) { return *this = *this & right; } - SPU_GPR_hdr operator <<= (int right) { return *this = *this << right; } - SPU_GPR_hdr operator <<= (__m128i right){ return *this = *this << right; } - SPU_GPR_hdr operator >>= (int right) { return *this = *this >> right; } - SPU_GPR_hdr operator >>= (__m128i right){ return *this = *this >> right; } - */ }; class SPUThread : public PPCThread { public: SPU_GPR_hdr GPR[128]; //General-Purpose Register - Stack Mbox; + SizedStack OutMbox; + SizedStack OutIntrMbox; + SizedStack InMbox; - u32 LSA; //local storage address + u32 LSA; union { @@ -165,11 +127,14 @@ public: { switch(ch) { + case SPU_WrOutMbox: + return OutMbox.GetFreeCount(); + case SPU_RdInMbox: - return 1; + return InMbox.GetCount(); case SPU_WrOutIntrMbox: - return 0; + return 0;//return OutIntrMbox.GetFreeCount(); default: ConLog.Error("%s error: unknown/illegal channel (%d).", __FUNCTION__, ch); @@ -181,12 +146,24 @@ public: void WriteChannel(u32 ch, const SPU_GPR_hdr& r) { - const u32 v = r._u32[0]; + const u32 v = r._u32[3]; switch(ch) { case SPU_WrOutIntrMbox: - Mbox.Push(v); + ConLog.Warning("SPU_WrOutIntrMbox = 0x%x", v); + if(!OutIntrMbox.Push(v)) + { + ConLog.Warning("Not enought free rooms."); + } + break; + + case SPU_WrOutMbox: + ConLog.Warning("SPU_WrOutMbox = 0x%x", v); + if(!OutMbox.Push(v)) + { + ConLog.Warning("Not enought free rooms."); + } break; default: @@ -198,12 +175,12 @@ public: void ReadChannel(SPU_GPR_hdr& r, u32 ch) { r.Reset(); - u32& v = r._u32[0]; + u32& v = r._u32[3]; switch(ch) { case SPU_RdInMbox: - v = Mbox.Pop(); + if(!InMbox.Pop(v)) v = 0; break; default: @@ -212,17 +189,18 @@ public: } } - u8 ReadLSA8 () { return Memory.Read8 (LSA + m_offset); } - u16 ReadLSA16 () { return Memory.Read16 (LSA + m_offset); } - u32 ReadLSA32 () { return Memory.Read32 (LSA + m_offset); } - u64 ReadLSA64 () { return Memory.Read64 (LSA + m_offset); } - u128 ReadLSA128() { return Memory.Read128(LSA + m_offset); } + bool IsGoodLSA(const u32 lsa) const { return Memory.IsGoodAddr(lsa); } + u8 ReadLS8 (const u32 lsa) const { return Memory.Read8 (lsa + (m_offset & 0x3fffc)); } + u16 ReadLS16 (const u32 lsa) const { return Memory.Read16 (lsa + m_offset); } + u32 ReadLS32 (const u32 lsa) const { return Memory.Read32 (lsa + m_offset); } + u64 ReadLS64 (const u32 lsa) const { return Memory.Read64 (lsa + m_offset); } + u128 ReadLS128(const u32 lsa) const { return Memory.Read128(lsa + m_offset); } - void WriteLSA8 (const u8& data) { Memory.Write8 (LSA + m_offset, data); } - void WriteLSA16 (const u16& data) { Memory.Write16 (LSA + m_offset, data); } - void WriteLSA32 (const u32& data) { Memory.Write32 (LSA + m_offset, data); } - void WriteLSA64 (const u64& data) { Memory.Write64 (LSA + m_offset, data); } - void WriteLSA128(const u128& data) { Memory.Write128(LSA + m_offset, data); } + void WriteLS8 (const u32 lsa, const u8& data) const { Memory.Write8 (lsa + m_offset, data); } + void WriteLS16 (const u32 lsa, const u16& data) const { Memory.Write16 (lsa + m_offset, data); } + void WriteLS32 (const u32 lsa, const u32& data) const { Memory.Write32 (lsa + m_offset, data); } + void WriteLS64 (const u32 lsa, const u64& data) const { Memory.Write64 (lsa + m_offset, data); } + void WriteLS128(const u32 lsa, const u128& data) const { Memory.Write128(lsa + m_offset, data); } public: SPUThread(); diff --git a/rpcs3/Emu/Memory/Memory.cpp b/rpcs3/Emu/Memory/Memory.cpp index 0c08be11b7..466566c921 100644 --- a/rpcs3/Emu/Memory/Memory.cpp +++ b/rpcs3/Emu/Memory/Memory.cpp @@ -529,6 +529,8 @@ bool MemoryBase::Write128NN(u64 addr, const u128 data) u8 MemoryBase::Read8(u64 addr) { + if(enable_log && addr >= 0xd0010a84) + ConLog.Warning("Read8 from block: [%08llx]", addr); MemoryBlock& mem = GetMemByAddr(addr); if(mem.IsNULL()) { @@ -540,6 +542,8 @@ u8 MemoryBase::Read8(u64 addr) u16 MemoryBase::Read16(u64 addr) { + if(enable_log && addr >= 0xd0010a84) + ConLog.Warning("Read16 from block: [%08llx]", addr); MemoryBlock& mem = GetMemByAddr(addr); if(mem.IsNULL()) { @@ -551,6 +555,8 @@ u16 MemoryBase::Read16(u64 addr) u32 MemoryBase::Read32(u64 addr) { + if(enable_log && addr >= 0xd0010a84) + ConLog.Warning("Read32 from block: [%08llx]", addr); MemoryBlock& mem = GetMemByAddr(addr); if(mem.IsNULL()) { @@ -562,6 +568,8 @@ u32 MemoryBase::Read32(u64 addr) u64 MemoryBase::Read64(u64 addr) { + if(enable_log && addr >= 0xd0010a84) + ConLog.Warning("Read64 from block: [%08llx]", addr); MemoryBlock& mem = GetMemByAddr(addr); if(mem.IsNULL()) { diff --git a/rpcs3/Emu/SysCalls/FuncList.cpp b/rpcs3/Emu/SysCalls/FuncList.cpp index 5103b69c45..1fe792894f 100644 --- a/rpcs3/Emu/SysCalls/FuncList.cpp +++ b/rpcs3/Emu/SysCalls/FuncList.cpp @@ -3583,7 +3583,7 @@ s64 SysCalls::DoFunc(const u32 id) case 0x2f85c0ef: return sys_lwmutex_create(SC_ARGS_2);//FUNC_LOG_ERROR("TODO: sys_lwmutex_create"); case 0x3172759d: FUNC_LOG_ERROR("TODO: sys_game_get_temperature"); case 0x318f17e1: FUNC_LOG_ERROR("TODO: _sys_memalign"); - case 0x350d454e: return sys_ppu_thread_get_id();//FUNC_LOG_ERROR("TODO: sys_ppu_thread_get_id"); + case 0x350d454e: return sys_ppu_thread_get_id(SC_ARGS_1);//FUNC_LOG_ERROR("TODO: sys_ppu_thread_get_id"); case 0x35168520: return sys_heap_malloc(SC_ARGS_2); //FUNC_LOG_ERROR("TODO: _sys_heap_malloc"); case 0x3bd53c7b: FUNC_LOG_ERROR("TODO: _sys_memchr"); case 0x3dd4a957: FUNC_LOG_ERROR("TODO: sys_ppu_thread_register_atexit"); @@ -3647,7 +3647,7 @@ s64 SysCalls::DoFunc(const u32 id) case 0xa285139d: FUNC_LOG_ERROR("TODO: sys_spinlock_lock"); case 0xa2c7ba64: FUNC_LOG_ERROR("TODO: sys_prx_exitspawn_with_level"); case 0xa330ad84: FUNC_LOG_ERROR("TODO: sys_prx_load_module_on_memcontainer_by_fd"); - case 0xa3e3be68: FUNC_LOG_ERROR("TODO: sys_ppu_thread_once"); + case 0xa3e3be68: sys_ppu_thread_once(SC_ARGS_2); return SC_ARGS_1;//FUNC_LOG_ERROR("TODO: sys_ppu_thread_once"); case 0xa5d06bf0: FUNC_LOG_ERROR("TODO: sys_prx_get_module_list"); case 0xaa6d9bff: FUNC_LOG_ERROR("TODO: sys_prx_load_module_on_memcontainer"); case 0xac6fc404: FUNC_LOG_ERROR("TODO: sys_ppu_thread_unregister_atexit"); diff --git a/rpcs3/Emu/SysCalls/Modules.cpp b/rpcs3/Emu/SysCalls/Modules.cpp new file mode 100644 index 0000000000..de9bc167af --- /dev/null +++ b/rpcs3/Emu/SysCalls/Modules.cpp @@ -0,0 +1,235 @@ +#include "stdafx.h" +#include "SysCalls.h" +#include "SC_FUNC.h" + +Module* g_modules[50]; +uint g_modules_count = 0; +ArrayF g_modules_funcs_list; + +bool IsLoadedFunc(u32 id) +{ + for(u32 i=0; iSetLoaded(false); + } + + g_modules_funcs_list.Clear(); +} + +Module* GetModuleByName(const wxString& name) +{ + for(u32 i=0; iGetName().Cmp(name) == 0) + { + return g_modules[i]; + } + } + + return nullptr; +} + +Module* GetModuleById(u16 id) +{ + for(u32 i=0; iGetID() == id) + { + return g_modules[i]; + } + } + + return nullptr; +} + +Module::Module(const char* name, u16 id, void (*init)()) + : m_is_loaded(false) + , m_name(name) + , m_id(id) +{ + g_modules[g_modules_count++] = this; + init(); +} + +void Module::Load() +{ + for(u32 i=0; i bool Module::CheckId(u32 id, T*& data) +{ + ID id_data; + + if(!CheckId(id, id_data)) return false; + + data = (T*)id_data.m_data; + + return true; +} + +u32 Module::GetNewId(void* data, u8 flags) +{ + return Emu.GetIdManager().GetNewID(GetName(), data, flags); +} diff --git a/rpcs3/Emu/SysCalls/Modules.h b/rpcs3/Emu/SysCalls/Modules.h new file mode 100644 index 0000000000..38143bdd68 --- /dev/null +++ b/rpcs3/Emu/SysCalls/Modules.h @@ -0,0 +1,76 @@ +#pragma once + +#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 (*init)()); + + void Load(); + void UnLoad(); + bool Load(u32 id); + bool UnLoad(u32 id); + + void SetLoaded(bool loaded = true); + bool IsLoaded() const; + + u16 GetID() const; + wxString GetName() const; + +public: + void Log(const u32 id, wxString fmt, ...); + void Log(wxString fmt, ...); + + void Warning(const u32 id, wxString fmt, ...); + void Warning(wxString fmt, ...); + + void Error(const u32 id, wxString fmt, ...); + void Error(wxString fmt, ...); + + bool CheckId(u32 id) const; + + bool CheckId(u32 id, ID& _id) const; + + template bool CheckId(u32 id, T*& data); + + u32 GetNewId(void* data = nullptr, u8 flags = 0); + __forceinline void Module::AddFunc(u32 id, func_caller* func) + { + m_funcs_list.Move(new ModuleFunc(id, func)); + } +}; + +bool IsLoadedFunc(u32 id); +bool CallFunc(u32 id); +bool UnloadFunc(u32 id); +void UnloadModules(); +Module* GetModuleByName(const wxString& name); +Module* GetModuleById(u16 id); diff --git a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp index ac33410632..c367afe2df 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp @@ -2,7 +2,8 @@ #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" -Module cellGcmSys("cellGcmSys", 0x0010); +void cellGcmSys_init(); +Module cellGcmSys("cellGcmSys", 0x0010, cellGcmSys_init); s64 cellGcmFunc15() { @@ -16,25 +17,22 @@ s64 cellGcmSetFlipCommandWithWaitLabel() return 0; } -struct _cellGcmSys_init +void cellGcmSys_init() { - _cellGcmSys_init() - { - cellGcmSys.AddFunc(0x055bd74d, bind_func(cellGcmGetTiledPitchSize)); - cellGcmSys.AddFunc(0x15bae46b, bind_func(cellGcmInit)); - cellGcmSys.AddFunc(0x21397818, bind_func(cellGcmFlush)); - cellGcmSys.AddFunc(0x21ac3697, bind_func(cellGcmAddressToOffset)); - cellGcmSys.AddFunc(0x3a33c1fd, bind_func(cellGcmFunc15)); - cellGcmSys.AddFunc(0x4ae8d215, bind_func(cellGcmSetFlipMode)); - cellGcmSys.AddFunc(0x5e2ee0f0, bind_func(cellGcmGetDefaultCommandWordSize)); - cellGcmSys.AddFunc(0x72a577ce, bind_func(cellGcmGetFlipStatus)); - cellGcmSys.AddFunc(0x8cdf8c70, bind_func(cellGcmGetDefaultSegmentWordSize)); - cellGcmSys.AddFunc(0x9ba451e4, bind_func(cellGcmSetDefaultFifoSize)); - cellGcmSys.AddFunc(0xa53d12ae, bind_func(cellGcmSetDisplayBuffer)); - cellGcmSys.AddFunc(0xa547adde, bind_func(cellGcmGetControlRegister)); - cellGcmSys.AddFunc(0xb2e761d4, bind_func(cellGcmResetFlipStatus)); - cellGcmSys.AddFunc(0xd8f88e1a, bind_func(cellGcmSetFlipCommandWithWaitLabel)); - cellGcmSys.AddFunc(0xe315a0b2, bind_func(cellGcmGetConfiguration)); - cellGcmSys.AddFunc(0x9dc04436, bind_func(cellGcmBindZcull)); - } -} _cellGcmSys_init; + cellGcmSys.AddFunc(0x055bd74d, bind_func(cellGcmGetTiledPitchSize)); + cellGcmSys.AddFunc(0x15bae46b, bind_func(cellGcmInit)); + cellGcmSys.AddFunc(0x21397818, bind_func(cellGcmFlush)); + cellGcmSys.AddFunc(0x21ac3697, bind_func(cellGcmAddressToOffset)); + cellGcmSys.AddFunc(0x3a33c1fd, bind_func(cellGcmFunc15)); + cellGcmSys.AddFunc(0x4ae8d215, bind_func(cellGcmSetFlipMode)); + cellGcmSys.AddFunc(0x5e2ee0f0, bind_func(cellGcmGetDefaultCommandWordSize)); + cellGcmSys.AddFunc(0x72a577ce, bind_func(cellGcmGetFlipStatus)); + cellGcmSys.AddFunc(0x8cdf8c70, bind_func(cellGcmGetDefaultSegmentWordSize)); + cellGcmSys.AddFunc(0x9ba451e4, bind_func(cellGcmSetDefaultFifoSize)); + cellGcmSys.AddFunc(0xa53d12ae, bind_func(cellGcmSetDisplayBuffer)); + cellGcmSys.AddFunc(0xa547adde, bind_func(cellGcmGetControlRegister)); + cellGcmSys.AddFunc(0xb2e761d4, bind_func(cellGcmResetFlipStatus)); + cellGcmSys.AddFunc(0xd8f88e1a, bind_func(cellGcmSetFlipCommandWithWaitLabel)); + cellGcmSys.AddFunc(0xe315a0b2, bind_func(cellGcmGetConfiguration)); + cellGcmSys.AddFunc(0x9dc04436, bind_func(cellGcmBindZcull)); +} diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp index 2d8102dda6..512de006aa 100644 --- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp @@ -2,7 +2,8 @@ #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" -Module sysPrxForUser("sysPrxForUser", -1); +void sysPrxForUser_init(); +Module sysPrxForUser("sysPrxForUser", -1, sysPrxForUser_init); void sys_initialize_tls() { @@ -21,6 +22,12 @@ s64 sys_process_at_Exitspawn() return 0; } +int sys_spu_printf_initialize(int a1, int a2, int a3, int a4, int a5) +{ + sysPrxForUser.Warning("sys_spu_printf_initialize(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)", a1, a2, a3, a4, a5); + return 0; +} + s64 sys_prx_register_library() { sysPrxForUser.Error("sys_prx_register_library()"); @@ -40,36 +47,36 @@ s64 sys_strlen(u32 addr) return str.Len(); } -struct _sysPrxForUser_init +void sysPrxForUser_init() { - _sysPrxForUser_init() - { - sysPrxForUser.AddFunc(0x744680a2, bind_func(sys_initialize_tls)); + sysPrxForUser.AddFunc(0x744680a2, bind_func(sys_initialize_tls)); + + sysPrxForUser.AddFunc(0x2f85c0ef, bind_func(sys_lwmutex_create)); + sysPrxForUser.AddFunc(0xc3476d0c, bind_func(sys_lwmutex_destroy)); + sysPrxForUser.AddFunc(0x1573dc3f, bind_func(sys_lwmutex_lock)); + sysPrxForUser.AddFunc(0xaeb78725, bind_func(sys_lwmutex_trylock)); + sysPrxForUser.AddFunc(0x1bc200f4, bind_func(sys_lwmutex_unlock)); - sysPrxForUser.AddFunc(0x2f85c0ef, bind_func(sys_lwmutex_create)); - sysPrxForUser.AddFunc(0xc3476d0c, bind_func(sys_lwmutex_destroy)); - sysPrxForUser.AddFunc(0x1573dc3f, bind_func(sys_lwmutex_lock)); - sysPrxForUser.AddFunc(0xaeb78725, bind_func(sys_lwmutex_trylock)); - sysPrxForUser.AddFunc(0x1bc200f4, bind_func(sys_lwmutex_unlock)); + sysPrxForUser.AddFunc(0x8461e528, bind_func(sys_time_get_system_time)); - sysPrxForUser.AddFunc(0x8461e528, bind_func(sys_time_get_system_time)); + sysPrxForUser.AddFunc(0xe6f2c1e7, bind_func(sys_process_exit)); + sysPrxForUser.AddFunc(0x2c847572, bind_func(sys_process_atexitspawn)); + sysPrxForUser.AddFunc(0x96328741, bind_func(sys_process_at_Exitspawn)); - sysPrxForUser.AddFunc(0xe6f2c1e7, bind_func(sys_process_exit)); - sysPrxForUser.AddFunc(0x2c847572, bind_func(sys_process_atexitspawn)); - sysPrxForUser.AddFunc(0x96328741, bind_func(sys_process_at_Exitspawn)); + sysPrxForUser.AddFunc(0x24a1ea07, bind_func(sys_ppu_thread_create)); + sysPrxForUser.AddFunc(0x350d454e, bind_func(sys_ppu_thread_get_id)); + sysPrxForUser.AddFunc(0xaff080a4, bind_func(sys_ppu_thread_exit)); + sysPrxForUser.AddFunc(0xa3e3be68, bind_func(sys_ppu_thread_once)); - sysPrxForUser.AddFunc(0x24a1ea07, bind_func(sys_ppu_thread_create)); - sysPrxForUser.AddFunc(0x350d454e, bind_func(sys_ppu_thread_get_id)); - sysPrxForUser.AddFunc(0xaff080a4, bind_func(sys_ppu_thread_exit)); + sysPrxForUser.AddFunc(0x45fe2fce, bind_func(sys_spu_printf_initialize)); - sysPrxForUser.AddFunc(0x42b23552, bind_func(sys_prx_register_library)); - sysPrxForUser.AddFunc(0xa2c7ba64, bind_func(sys_prx_exitspawn_with_level)); + sysPrxForUser.AddFunc(0x42b23552, bind_func(sys_prx_register_library)); + sysPrxForUser.AddFunc(0xa2c7ba64, bind_func(sys_prx_exitspawn_with_level)); - sysPrxForUser.AddFunc(0x2d36462b, bind_func(sys_strlen)); + sysPrxForUser.AddFunc(0x2d36462b, bind_func(sys_strlen)); - sysPrxForUser.AddFunc(0x35168520, bind_func(sys_heap_malloc)); - //sysPrxForUser.AddFunc(0xaede4b03, bind_func(sys_heap_free>); - //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 + sysPrxForUser.AddFunc(0x35168520, bind_func(sys_heap_malloc)); + //sysPrxForUser.AddFunc(0xaede4b03, bind_func(sys_heap_free)); + //sysPrxForUser.AddFunc(0x8a561d92, bind_func(sys_heap_delete_heap)); + sysPrxForUser.AddFunc(0xb2fcf2c8, bind_func(sys_heap_create_heap)); +} diff --git a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp index 6c0c9f0633..c73d87ebdd 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp @@ -2,25 +2,23 @@ #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" -Module sys_fs("sys_fs", 0x000e); +void sys_fs_init(); +Module sys_fs("sys_fs", 0x000e, sys_fs_init); -struct _sys_fs_init +void 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 + 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)); +} diff --git a/rpcs3/Emu/SysCalls/Modules/sys_io.cpp b/rpcs3/Emu/SysCalls/Modules/sys_io.cpp index 7bea344fdb..c4654036c0 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_io.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_io.cpp @@ -2,19 +2,17 @@ #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" -Module sys_io("sys_io", 0x0017); +void sys_io_init(); +Module sys_io("sys_io", 0x0017, sys_io_init); -struct _sys_io_init +void 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 + 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)); +} diff --git a/rpcs3/Emu/SysCalls/SysCalls.cpp b/rpcs3/Emu/SysCalls/SysCalls.cpp index b81c86d8c2..b3250416d5 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.cpp +++ b/rpcs3/Emu/SysCalls/SysCalls.cpp @@ -1,137 +1,7 @@ #include "stdafx.h" #include "SysCalls.h" +#include "Modules.h" #include "SC_FUNC.h" -#include - -ArrayF g_modules_funcs_list; -ArrayF g_modules_list; - -bool IsLoadedFunc(u32 id) -{ - for(u32 i=0; i 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); +extern bool enable_log; class SysCallBase //Module { @@ -178,22 +24,24 @@ public: void Log(const u32 id, wxString fmt, ...) { -#ifdef SYSCALLS_DEBUG + if(enable_log) + { 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 + if(enable_log) + { 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, ...) @@ -259,40 +107,18 @@ public: } }; -/* -static bool CmpPath(const wxString& path1, const wxString& path2) -{ - return path1.Len() >= path2.Len() && path1(0, path2.Len()).CmpNoCase(path2) == 0; -} - -static wxString GetWinPath(const wxString& path) -{ - if(!CmpPath(path, "/") && CmpPath(path(1, 1), ":")) return path; - - wxString ppath = wxFileName(Emu.m_path).GetPath() + '/' + wxFileName(path).GetFullName(); - if(wxFileExists(ppath)) return ppath; - - if (CmpPath(path, "/dev_hdd0/") || - CmpPath(path, "/dev_hdd1/") || - CmpPath(path, "/dev_bdvd/") || - CmpPath(path, "/dev_usb001/") || - CmpPath(path, "/ps3_home/") || - CmpPath(path, "/app_home/") || - CmpPath(path, "/dev_flash/") || - CmpPath(path, "/dev_flash2/") || - CmpPath(path, "/dev_flash3/") - ) return wxGetCwd() + 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_event +extern int sys_event_queue_create(u32 equeue_id_addr, u32 attr_addr, u64 event_queue_key, int size); +extern int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout); +extern int sys_event_port_create(u32 eport_id_addr, int port_type, u64 name); +extern int sys_event_port_connect_local(u32 event_port_id, u32 event_queue_id); + //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); @@ -333,7 +159,9 @@ 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(); +extern void sys_ppu_thread_once(u32 once_ctrl_addr, u32 entry); +extern int sys_ppu_thread_get_id(const u32 id_addr); +extern int sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, u32 spup_addr); //memory extern int sys_memory_container_create(u32 cid_addr, u32 yield_size); @@ -421,10 +249,16 @@ extern int sys_heap_create_heap(const u32 heap_addr, const u32 start_addr, const extern int sys_heap_malloc(const u32 heap_addr, const u32 size); //sys_spu +extern int sys_spu_image_open(u32 img_addr, u32 path_addr); +extern int sys_spu_thread_initialize(u32 thread_addr, u32 group, u32 spu_num, u32 img_addr, u32 attr_addr, u32 arg_addr); +extern int sys_spu_thread_group_start(u32 id); extern int sys_spu_thread_group_create(u64 id_addr, u32 num, int prio, u64 attr_addr); extern int sys_spu_thread_create(u64 thread_id_addr, u64 entry_addr, u64 arg, int prio, u32 stacksize, u64 flags, u64 threadname_addr); extern int sys_raw_spu_create(u32 id_addr, u32 attr_addr); extern int sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu); +extern int sys_spu_thread_write_ls(u32 id, u32 address, u64 value, u32 type); +extern int sys_spu_thread_read_ls(u32 id, u32 address, u32 value_addr, u32 type); +extern int sys_spu_thread_write_spu_mb(u32 id, u32 value); //sys_time extern int sys_time_get_current_time(u32 sec_addr, u32 nsec_addr); @@ -469,7 +303,7 @@ protected: ~SysCalls(); public: - s64 DoSyscall(u32 code); + void DoSyscall(u32 code); s64 DoFunc(const u32 id); }; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp new file mode 100644 index 0000000000..16a405c0b7 --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp @@ -0,0 +1,137 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" +#include "Emu/Cell/SPUThread.h" +#include "Emu/event.h" + +SysCallBase sys_event("sys_event"); + +//128 +int sys_event_queue_create(u32 equeue_id_addr, u32 attr_addr, u64 event_queue_key, int size) +{ + sys_event.Warning("sys_event_queue_create(equeue_id_addr=0x%x, attr_addr=0x%x, event_queue_key=0x%llx, size=%d)", + equeue_id_addr, attr_addr, event_queue_key, size); + + if(size <= 0 || size > 127) + { + return CELL_EINVAL; + } + + if(!Memory.IsGoodAddr(equeue_id_addr, 4) || !Memory.IsGoodAddr(attr_addr, sizeof(sys_event_queue_attr))) + { + return CELL_EFAULT; + } + + auto& attr = (sys_event_queue_attr&)Memory[attr_addr]; + sys_event.Warning("name = %s", attr.name); + sys_event.Warning("type = %d", re(attr.type)); + EventQueue* equeue = new EventQueue(); + equeue->size = size; + equeue->pos = 0; + equeue->type = re(attr.type); + strncpy(equeue->name, attr.name, 8); + Memory.Write32(equeue_id_addr, sys_event.GetNewId(equeue)); + + return CELL_OK; +} + +int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout) +{ + sys_event.Warning("sys_event_queue_receive(equeue_id=0x%x, event_addr=0x%x, timeout=0x%x)", + equeue_id, event_addr, timeout); + + if(!sys_event.CheckId(equeue_id)) + { + return CELL_ESRCH; + } + + PPCThread* thr = GetCurrentPPCThread(); + + while(true) + { + int status = thr->ThreadStatus(); + if(status == PPCThread_Stopped) + { + return CELL_ECANCELED; + } + + EventQueue* equeue = (EventQueue*)Emu.GetIdManager().GetIDData(equeue_id).m_data; + for(int i=0; ipos; ++i) + { + if(!equeue->ports[i]->has_data && equeue->ports[i]->thread) + { + SPUThread* thr = (SPUThread*)equeue->ports[i]->thread; + if(thr->OutIntrMbox.GetCount()) + { + u32 val; + thr->OutIntrMbox.Pop(val); + if(!thr->OutMbox.Pop(val)) val = 0; + equeue->ports[i]->data1 = val; + equeue->ports[i]->data2 = 0; + equeue->ports[i]->data3 = 0; + equeue->ports[i]->has_data = true; + } + } + } + + bool has_data = false; + for(int i=0; ipos; i++) + { + if(equeue->ports[i]->has_data) + { + has_data = true; + auto res = (sys_event_data&)Memory[event_addr]; + + re(res.source, equeue->ports[i]->name); + re(res.data1, equeue->ports[i]->data1); + re(res.data2, equeue->ports[i]->data2); + re(res.data3, equeue->ports[i]->data3); + + equeue->ports[i]->has_data = false; + break; + } + } + + if(has_data) + break; + + Sleep(1); + } + + return CELL_OK; +} + +int sys_event_port_create(u32 eport_id_addr, int port_type, u64 name) +{ + sys_event.Warning("sys_event_port_create(eport_id_addr=0x%x, port_type=0x%x, name=0x%llx)", + eport_id_addr, port_type, name); + + if(!Memory.IsGoodAddr(eport_id_addr, 4)) + { + return CELL_EFAULT; + } + + EventPort* eport = new EventPort(); + u32 id = sys_event.GetNewId(eport); + eport->has_data = false; + eport->name = name ? name : id; + Memory.Write32(eport_id_addr, id); + + return CELL_OK; +} + +int sys_event_port_connect_local(u32 event_port_id, u32 event_queue_id) +{ + sys_event.Warning("sys_event_port_connect_local(event_port_id=0x%x, event_queue_id=0x%x)", + event_port_id, event_queue_id); + + if(!sys_event.CheckId(event_port_id) || !sys_event.CheckId(event_queue_id)) + { + return CELL_ESRCH; + } + + EventPort* eport = (EventPort*)Emu.GetIdManager().GetIDData(event_port_id).m_data; + EventQueue* equeue = (EventQueue*)Emu.GetIdManager().GetIDData(event_queue_id).m_data; + equeue->ports[equeue->pos++] = eport; + + return CELL_OK; +} \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp index 99dba6cfca..8177530f34 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp @@ -33,26 +33,15 @@ SysCallBase sc_lwmutex("sys_wmutex"); int sys_lwmutex_create(u64 lwmutex_addr, u64 lwmutex_attr_addr) { + if(!Memory.IsGoodAddr(lwmutex_addr, 4) || !Memory.IsGoodAddr(lwmutex_attr_addr)) + { + return CELL_EFAULT; + } + lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; + lmtx.lock_var.all_info = 0; lwmutex_attr& lmtx_attr = (lwmutex_attr&)Memory[lwmutex_attr_addr]; - //sc_lwmutex.Warning("sys_lwmutex_create(lwmutex_addr = 0x%llx, lwmutex_attr_addr = 0x%llx)", lwmutex_addr, lwmutex_attr_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]", 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", re(lmtx_attr.attr_protocol)); - ConLog.Write("*** attr_recursive: 0x%x", re(lmtx_attr.attr_recursive)); - ConLog.Write("*** name: %s", lmtx_attr.name); - */ return CELL_OK; } @@ -60,39 +49,46 @@ int sys_lwmutex_destroy(u64 lwmutex_addr) { //sc_lwmutex.Warning("sys_lwmutex_destroy(lwmutex_addr = 0x%llx)", lwmutex_addr); - lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; - Emu.GetIdManager().RemoveID(lmtx.attribute); + //lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; + //Emu.GetIdManager().RemoveID(lmtx.attribute); return CELL_OK; } int sys_lwmutex_lock(u64 lwmutex_addr, u64 timeout) { - //sc_lwmutex.Warning("sys_lwmutex_lock(lwmutex_addr = 0x%llx, timeout = 0x%llx)", lwmutex_addr, timeout); - lwmutex& lmtx = (lwmutex&)Memory[lwmutex_addr]; + PPCThread& thr = GetCurrentPPUThread(); + + if(thr.GetId() == re(lmtx.lock_var.info.owner)) + { + re(lmtx.recursive_count, re(lmtx.recursive_count) + 1); + return CELL_OK; + } + if(!lmtx.lock_var.info.owner) { re(lmtx.lock_var.info.owner, GetCurrentPPUThread().GetId()); + re(lmtx.recursive_count, 1); } 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); + thr.Wait(true); + re(lmtx.lock_var.info.waiter, thr.GetId()); } else { - return -1; + ConLog.Warning("lwmutex has waiter!"); + return CELL_EBUSY; } - + return CELL_OK; } int sys_lwmutex_trylock(u64 lwmutex_addr) { //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; @@ -105,8 +101,21 @@ int sys_lwmutex_unlock(u64 lwmutex_addr) //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; + + re(lmtx.recursive_count, re(lmtx.recursive_count) - 1); + + if(!lmtx.recursive_count) + { + if(lmtx.lock_var.info.owner = lmtx.lock_var.info.waiter) + { + lmtx.lock_var.info.waiter = 0; + PPCThread* thr = Emu.GetCPU().GetThread(lmtx.lock_var.info.owner); + if(thr) + { + thr->Wait(false); + } + } + } return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp index eddf0ec52e..f9f39c4bbd 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp @@ -3,7 +3,12 @@ extern Module sysPrxForUser; -#define PPU_THREAD_ID_INVALID 0xFFFFFFFFU +static const u32 PPU_THREAD_ID_INVALID = 0xFFFFFFFFU; +enum +{ + SYS_PPU_THREAD_ONCE_INIT, + SYS_PPU_THREAD_DONE_INIT, +}; int sys_ppu_thread_exit(int errorcode) { @@ -15,9 +20,14 @@ int sys_ppu_thread_exit(int errorcode) int sys_ppu_thread_yield() { - sysPrxForUser.Log("sys_ppu_thread_yield()"); - wxThread::Yield(); + sysPrxForUser.Log("sys_ppu_thread_yield()"); + enable_log = !enable_log; + dump_enable = !dump_enable; + if(!enable_log) + { + Emu.Pause(); + } return CELL_OK; } @@ -110,7 +120,10 @@ int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u32 arg, int prio, u32 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; + if(!Memory.IsGoodAddr(entry) || !Memory.IsGoodAddr(thread_id_addr) || !Memory.IsGoodAddr(threadname_addr)) + { + return CELL_EFAULT; + } PPCThread& new_thread = Emu.GetCPU().AddThread(true); @@ -127,9 +140,28 @@ int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u32 arg, int prio, u32 return CELL_OK; } -int sys_ppu_thread_get_id() +void sys_ppu_thread_once(u32 once_ctrl_addr, u32 entry) { - sysPrxForUser.Log("sys_ppu_thread_get_id()"); + sysPrxForUser.Warning("sys_ppu_thread_once(once_ctrl_addr=0x%x, entry=0x%x)", once_ctrl_addr, entry); - return GetCurrentPPUThread().GetId(); + if(Memory.IsGoodAddr(once_ctrl_addr, 4) && Memory.Read32(once_ctrl_addr) == SYS_PPU_THREAD_ONCE_INIT) + { + Memory.Write32(once_ctrl_addr, SYS_PPU_THREAD_DONE_INIT); + + PPCThread& new_thread = Emu.GetCPU().AddThread(true); + new_thread.SetEntry(entry); + ((PPUThread&)new_thread).LR = Emu.GetPPUThreadExit(); + new_thread.Run(); + new_thread.Exec(); + + GetCurrentPPUThread().Wait(new_thread); + } +} + +int sys_ppu_thread_get_id(const u32 id_addr) +{ + sysPrxForUser.Log("sys_ppu_thread_get_id(id_addr=0x%x)", id_addr); + + Memory.Write32(id_addr, GetCurrentPPUThread().GetId()); + return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index 7e3ba1b5d5..241aaddcc8 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" #include "Loader/ELF.h" +#include "Emu/Cell/SPUThread.h" SysCallBase sc_spu("sys_spu"); @@ -12,6 +13,21 @@ struct sys_spu_thread_group_attribute union{u32 ct;} option; }; +struct sys_spu_thread_attribute +{ + u32 name_addr; + u32 name_len; + u32 option; +}; + +struct sys_spu_thread_argument +{ + u64 arg1; + u64 arg2; + u64 arg3; + u64 arg4; +}; + struct sys_spu_image { u32 type; @@ -20,25 +36,149 @@ struct sys_spu_image int nsegs; }; -u32 LoadImage(vfsStream& stream) +struct SpuGroupInfo +{ + PPCThread* threads[10]; + sys_spu_thread_group_attribute& attr; + + SpuGroupInfo(sys_spu_thread_group_attribute& attr) : attr(attr) + { + memset(threads, 0, sizeof(PPCThread*) * 10); + } +}; + +u64 g_spu_offset = 0; + +u32 LoadSpuImage(vfsStream& stream) { ELFLoader l(stream); l.LoadInfo(); - l.LoadData(Memory.MainMem.Alloc(stream.GetSize())); + g_spu_offset = Memory.MainMem.Alloc(0xFFFFED - stream.GetSize()); + l.LoadData(g_spu_offset); - return l.GetEntry(); + return g_spu_offset + l.GetEntry(); } - int sys_spu_image_open(u32 img_addr, u32 path_addr) - { +//156 +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); + if(!Memory.IsGoodAddr(img_addr, sizeof(sys_spu_image)) || !Memory.IsGoodAddr(path_addr)) + { + return CELL_EFAULT; + } + + vfsStream* stream = Emu.GetVFS().Open(path, vfsRead); + + if(!stream || !stream->IsOpened()) + { + sc_spu.Error("'%s' not found!", path); + delete stream; + + return CELL_ENOENT; + } + + u32 entry = LoadSpuImage(*stream); + delete stream; + + sys_spu_image& ret = (sys_spu_image&)Memory[img_addr]; + re(ret.type, 1); + re(ret.entry_point, entry); + re(ret.segs_addr, 0x0); + re(ret.nsegs, 0); return CELL_OK; - } +} + +//172 +int sys_spu_thread_initialize(u32 thread_addr, u32 group, u32 spu_num, u32 img_addr, u32 attr_addr, u32 arg_addr) +{ + sc_spu.Warning("sys_spu_thread_initialize(thread_addr=0x%x, group=0x%x, spu_num=%d, img_addr=0x%x, attr_addr=0x%x, arg_addr=0x%x)", + thread_addr, group, spu_num, img_addr, attr_addr, arg_addr); + + if(!Emu.GetIdManager().CheckID(group)) + { + return CELL_ESRCH; + } + + if( + !Memory.IsGoodAddr(img_addr, sizeof(sys_spu_image)) || + !Memory.IsGoodAddr(attr_addr, sizeof(sys_spu_thread_attribute)) || + !Memory.IsGoodAddr(arg_addr, sizeof(sys_spu_thread_argument))) + { + return CELL_EFAULT; + } + + sys_spu_image& img = (sys_spu_image&)Memory[img_addr]; + sys_spu_thread_attribute& attr = (sys_spu_thread_attribute&)Memory[attr_addr]; + sys_spu_thread_argument& arg = (sys_spu_thread_argument&)Memory[arg_addr]; + + if(!Memory.IsGoodAddr(re(attr.name_addr), re(attr.name_len))) + { + return CELL_EFAULT; + } + + u32 entry = re(img.entry_point); + wxString name = Memory.ReadString(re(attr.name_addr), re(attr.name_len)); + u64 a1 = re(arg.arg1); + u64 a2 = re(arg.arg2); + u64 a3 = re(arg.arg3); + u64 a4 = re(arg.arg4); + + ConLog.Write("New SPU Thread:"); + ConLog.Write("entry = 0x%x", entry); + ConLog.Write("name = %s", name); + ConLog.Write("a1 = 0x%x", a1); + ConLog.Write("a2 = 0x%x", a2); + ConLog.Write("a3 = 0x%x", a3); + ConLog.Write("a4 = 0x%x", a4); + ConLog.SkipLn(); + + PPCThread& new_thread = Emu.GetCPU().AddThread(false); + new_thread.SetOffset(g_spu_offset); + new_thread.SetEntry(entry - g_spu_offset); + new_thread.SetName(name); + SPU_GPR_hdr* GPR = ((SPUThread&)new_thread).GPR; + new_thread.Run(); + new_thread.Pause(); + GPR[3]._u64[1] = a1; + GPR[4]._u64[1] = a2; + GPR[5]._u64[1] = a3; + GPR[6]._u64[1] = a4; + + ID& id = Emu.GetIdManager().GetIDData(group); + SpuGroupInfo& group_info = *(SpuGroupInfo*)id.m_data; + group_info.threads[spu_num] = &new_thread; + + return CELL_OK; +} + +//173 +int sys_spu_thread_group_start(u32 id) +{ + sc_spu.Warning("sys_spu_thread_group_start(id=0x%x)", id); + + if(!Emu.GetIdManager().CheckID(id)) + { + return CELL_ESRCH; + } + + ID& id_data = Emu.GetIdManager().GetIDData(id); + SpuGroupInfo& group_info = *(SpuGroupInfo*)id_data.m_data; + + Emu.Pause(); + for(int i=0; i<10; i++) + { + if(group_info.threads[i]) + { + group_info.threads[i]->Exec(); + } + } + + return CELL_OK; +} //170 int sys_spu_thread_group_create(u64 id_addr, u32 num, int prio, u64 attr_addr) @@ -49,7 +189,7 @@ int sys_spu_thread_group_create(u64 id_addr, u32 num, int prio, u64 attr_addr) ConLog.Write("*** prio=%d", prio); ConLog.Write("*** attr_addr=0x%llx", attr_addr); - sys_spu_thread_group_attribute& attr = *new sys_spu_thread_group_attribute(*(sys_spu_thread_group_attribute*)&Memory[attr_addr]); + sys_spu_thread_group_attribute& attr = (sys_spu_thread_group_attribute&)Memory[attr_addr]; ConLog.Write("*** attr.name_len=%d", re(attr.name_len)); ConLog.Write("*** attr.name_addr=0x%x", re(attr.name_addr)); @@ -60,7 +200,7 @@ int sys_spu_thread_group_create(u64 id_addr, u32 num, int prio, u64 attr_addr) ConLog.Write("*** name='%s'", name); Memory.Write32(id_addr, - Emu.GetIdManager().GetNewID(wxString::Format("sys_spu_thread_group %s", name), &attr, 0)); + Emu.GetIdManager().GetNewID(wxString::Format("sys_spu_thread_group '%s'", name), new SpuGroupInfo(attr))); return CELL_OK; } @@ -76,10 +216,10 @@ int sys_raw_spu_create(u32 id_addr, u32 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); + Memory.Write32(id_addr, Emu.GetCPU().GetThreadNumById(false, new_thread.GetId())); + Memory.Write32(0xe0043014, 0); return CELL_OK; } @@ -88,10 +228,126 @@ int sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu) { sc_spu.Warning("sys_spu_initialize(max_usable_spu=%d, max_raw_spu=%d)", max_usable_spu, max_raw_spu); + if(max_raw_spu > 5) + { + return CELL_EINVAL; + } + if(!Memory.InitSpuRawMem(max_raw_spu)) { + //30010780; + //e0043004 - offset + //e004300c - entry return CELL_UNKNOWN_ERROR; } + //enable_log = true; + //dump_enable = true; + + return CELL_OK; +} + +//181 +int sys_spu_thread_write_ls(u32 id, u32 address, u64 value, u32 type) +{ + sc_spu.Warning("sys_spu_thread_write_ls(id=0x%x, address=0x%x, value=0x%llx, type=0x%x)", + id, address, value, type); + + PPCThread* thr = Emu.GetCPU().GetThread(id); + + if(!thr || !thr->IsSPU()) + { + return CELL_ESRCH; + } + + (*(SPUThread*)thr).WriteLS64(address, value); + + return CELL_OK; +} + +//182 +int sys_spu_thread_read_ls(u32 id, u32 address, u32 value_addr, u32 type) +{ + sc_spu.Warning("sys_spu_thread_read_ls(id=0x%x, address=0x%x, value_addr=0x%x, type=0x%x)", + id, address, value_addr, type); + + PPCThread* thr = Emu.GetCPU().GetThread(id); + + if(!thr || !thr->IsSPU()) + { + return CELL_ESRCH; + } + + if(!(*(SPUThread*)thr).IsGoodLSA(address)) + { + return CELL_EFAULT; + } + + Memory.Write64(value_addr, (*(SPUThread*)thr).ReadLS64(address)); + + return CELL_OK; +} + +//190 +int sys_spu_thread_write_spu_mb(u32 id, u32 value) +{ + sc_spu.Warning("sys_spu_thread_write_spu_mb(id=0x%x, value=0x%x)", id, value); + + PPCThread* thr = Emu.GetCPU().GetThread(id); + + if(!thr || !thr->IsSPU()) + { + return CELL_ESRCH; + } + + if(!(*(SPUThread*)thr).InMbox.Push(value)) + { + ConLog.Warning("sys_spu_thread_write_spu_mb(id=0x%x, value=0x%x): used all mbox items."); + return CELL_EBUSY; //? + } + + return CELL_OK; +} + +extern SysCallBase sys_event; + +int sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, u32 spup_addr) +{ + sc_spu.Warning("sys_spu_thread_group_connect_event_all_threads(id=0x%x, eq=0x%x, req=0x%llx, spup_addr=0x%x)", + id, eq, req, spup_addr); + + if(!Emu.GetIdManager().CheckID(id) || !sys_event.CheckId(eq)) + { + return CELL_ESRCH; + } + + if(!req) + { + return CELL_EINVAL; + } + + SpuGroupInfo* group = (SpuGroupInfo*)Emu.GetIdManager().GetIDData(id).m_data; + EventQueue* equeue = (EventQueue*)Emu.GetIdManager().GetIDData(eq).m_data; + + for(int i=0; i<10; ++i) + { + if(group->threads[i]) + { + bool finded_port = false; + for(int j=0; jpos; ++j) + { + if(!equeue->ports[j]->thread) + { + finded_port = true; + equeue->ports[j]->thread = group->threads[i]; + } + } + + if(!finded_port) + { + return CELL_EISCONN; + } + } + } return CELL_OK; } \ No newline at end of file diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 812e50e05c..9d62bfeef7 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -12,7 +12,10 @@ using namespace PPU_instr; static const wxString& BreakPointsDBName = "BreakPoints.dat"; static const u16 bpdb_version = 0x1000; -//SysCalls SysCallsManager; +ModuleInitializer::ModuleInitializer() +{ + Emu.AddModuleInit(this); +} Emulator::Emulator() : m_status(Stopped) @@ -24,6 +27,11 @@ Emulator::Emulator() void Emulator::Init() { + while(m_modules_init.GetCount()) + { + m_modules_init[0].Init(); + m_modules_init.RemoveAt(0); + } //if(m_memory_viewer) m_memory_viewer->Close(); //m_memory_viewer = new MemoryViewerPanel(wxGetApp().m_MainFrame); } @@ -108,7 +116,19 @@ void Emulator::Load() LoadPoints(BreakPointsDBName); PPCThread& thread = GetCPU().AddThread(l.GetMachine() == MACHINE_PPC64); - thread.SetEntry(l.GetEntry()); + if(l.GetMachine() == MACHINE_SPU) + { + ConLog.Write("offset = 0x%llx", Memory.MainMem.GetStartAddr()); + ConLog.Write("max addr = 0x%x", l.GetMaxAddr()); + thread.SetOffset(Memory.MainMem.GetStartAddr()); + Memory.MainMem.Alloc(Memory.MainMem.GetStartAddr() + l.GetMaxAddr(), 0xFFFFED - l.GetMaxAddr()); + thread.SetEntry(l.GetEntry() - Memory.MainMem.GetStartAddr()); + } + else + { + thread.SetEntry(l.GetEntry()); + } + thread.SetArg(thread.GetId()); Memory.StackMem.Alloc(0x1000); thread.InitStack(); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 837fe2322f..8719e5fa73 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -8,6 +8,7 @@ #include "Emu/DbgConsole.h" #include "Loader/Loader.h" #include "SysCalls/Callback.h" +#include "SysCalls/Modules.h" struct EmuInfo { @@ -45,6 +46,14 @@ public: u64 GetTLSMemsz() const { return tls_memsz; } }; +class ModuleInitializer +{ +public: + ModuleInitializer(); + + virtual void Init() = 0; +}; + class Emulator { enum Mode @@ -62,6 +71,7 @@ class Emulator u32 m_ppu_thr_exit; MemoryViewerPanel* m_memory_viewer; //ArrayF m_cpu_threads; + ArrayF m_modules_init; Array m_break_points; Array m_marked_points; @@ -93,6 +103,11 @@ public: VFS& GetVFS() { return m_vfs; } Array& GetBreakPoints() { return m_break_points; } Array& GetMarkedPoints() { return m_marked_points; } + + void AddModuleInit(ModuleInitializer* m) + { + m_modules_init.Add(m); + } void SetTLSData(const u64 addr, const u64 filesz, const u64 memsz) { diff --git a/rpcs3/Emu/event.h b/rpcs3/Emu/event.h new file mode 100644 index 0000000000..57580c9975 --- /dev/null +++ b/rpcs3/Emu/event.h @@ -0,0 +1,35 @@ +#pragma once + +struct sys_event_queue_attr +{ + u32 attr_protocol; + int type; + char name[8]; +}; + +struct sys_event_data +{ + u64 source; + u64 data1; + u64 data2; + u64 data3; +}; + +struct EventPort +{ + u64 name; + u64 data1; + u64 data2; + u64 data3; + bool has_data; + PPCThread* thread; +}; + +struct EventQueue +{ + EventPort* ports[127]; + int size; + int pos; + int type; + char name[8]; +}; \ No newline at end of file diff --git a/rpcs3/Gui/InterpreterDisAsm.cpp b/rpcs3/Gui/InterpreterDisAsm.cpp index bdf9a1e4de..a0126bdb66 100644 --- a/rpcs3/Gui/InterpreterDisAsm.cpp +++ b/rpcs3/Gui/InterpreterDisAsm.cpp @@ -164,16 +164,17 @@ void InterpreterDisAsmFrame::ShowAddr(const u64 addr) { PC = addr; m_list->Freeze(); + disasm->offset = CPU.GetOffset(); for(uint i=0; iSetItem(i, 0, wxString::Format("[%08llx] illegal address", PC)); continue; } disasm->dump_pc = PC; - decoder->Decode(Memory.Read32(PC)); + decoder->Decode(Memory.Read32(CPU.GetOffset() + PC)); if(IsBreakPoint(PC)) { diff --git a/rpcs3/Loader/ELF32.cpp b/rpcs3/Loader/ELF32.cpp index 514ed3df17..7dc5e4b0a9 100644 --- a/rpcs3/Loader/ELF32.cpp +++ b/rpcs3/Loader/ELF32.cpp @@ -133,6 +133,16 @@ bool ELF32Loader::LoadPhdrData(u64 offset) { phdr_arr[i].Show(); + if(phdr_arr[i].p_vaddr < min_addr) + { + min_addr = phdr_arr[i].p_vaddr; + } + + if(phdr_arr[i].p_vaddr + phdr_arr[i].p_memsz > max_addr) + { + max_addr = phdr_arr[i].p_vaddr + phdr_arr[i].p_memsz; + } + if(phdr_arr[i].p_type == 0x00000001) //LOAD { if(phdr_arr[i].p_vaddr != phdr_arr[i].p_paddr) @@ -161,6 +171,7 @@ bool ELF32Loader::LoadShdrData(u64 offset) for(u32 i=0; i max_addr) + { + max_addr = shdr.sh_addr + shdr.sh_size; + } + //const s64 addr = shdr.sh_addr; //const s64 size = shdr.sh_size; //MemoryBlock* mem = nullptr; diff --git a/rpcs3/Loader/ELF64.cpp b/rpcs3/Loader/ELF64.cpp index e12b58f923..ef6fe0dd13 100644 --- a/rpcs3/Loader/ELF64.cpp +++ b/rpcs3/Loader/ELF64.cpp @@ -207,6 +207,16 @@ bool ELF64Loader::LoadPhdrData(u64 offset) { phdr_arr[i].Show(); + if(phdr_arr[i].p_vaddr < min_addr) + { + min_addr = phdr_arr[i].p_vaddr; + } + + if(phdr_arr[i].p_vaddr + phdr_arr[i].p_memsz > max_addr) + { + max_addr = phdr_arr[i].p_vaddr + phdr_arr[i].p_memsz; + } + if(phdr_arr[i].p_vaddr != phdr_arr[i].p_paddr) { ConLog.Warning @@ -403,6 +413,16 @@ bool ELF64Loader::LoadShdrData(u64 offset) { Elf64_Shdr& shdr = shdr_arr[i]; + if(shdr.sh_addr < min_addr) + { + min_addr = shdr.sh_addr; + } + + if(shdr.sh_addr + shdr.sh_size > max_addr) + { + max_addr = shdr.sh_addr + shdr.sh_size; + } + if(i < shdr_name_arr.GetCount()) { const wxString& name = shdr_name_arr[i]; diff --git a/rpcs3/Loader/Loader.h b/rpcs3/Loader/Loader.h index d96d5b15b6..febec7c85c 100644 --- a/rpcs3/Loader/Loader.h +++ b/rpcs3/Loader/Loader.h @@ -144,11 +144,15 @@ class LoaderBase { protected: u32 entry; + u32 min_addr; + u32 max_addr; Elf_Machine machine; LoaderBase() : machine(MACHINE_Unknown) , entry(0) + , min_addr(0) + , max_addr(0) { } @@ -157,6 +161,8 @@ public: virtual bool LoadData(u64 offset = 0) { return false; } Elf_Machine GetMachine() { return machine; } u32 GetEntry() { return entry; } + u32 GetMinAddr() { return min_addr; } + u32 GetMaxAddr() { return min_addr; } }; class Loader : public LoaderBase diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 50c21ec9bf..74ef6e59f7 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -224,6 +224,7 @@ + @@ -240,6 +241,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index df23b4814f..b74d4d565f 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -250,6 +250,12 @@ Emu\CPU + + Emu\SysCalls\lv2 + + + Emu\SysCalls + From fb57bb9c4e1cca401c093cb53c379ad892883f7b Mon Sep 17 00:00:00 2001 From: DH Date: Mon, 8 Jul 2013 16:24:46 +0300 Subject: [PATCH 02/13] - Implemented MTCRF instruction. - Implemented sys_spu_thread_set_argument syscall. - Improved Debugger. --- rpcs3/Emu/Cell/PPCThread.cpp | 20 ++- rpcs3/Emu/Cell/PPCThread.h | 10 +- rpcs3/Emu/Cell/PPUDisAsm.h | 19 +- rpcs3/Emu/Cell/PPUInstrTable.h | 52 ++---- rpcs3/Emu/Cell/PPUInterpreter.h | 98 +++++++--- rpcs3/Emu/Cell/PPUOpcodes.h | 10 +- rpcs3/Emu/Cell/PPUThread.cpp | 55 ++++-- rpcs3/Emu/Cell/PPUThread.h | 17 +- rpcs3/Emu/Cell/SPUThread.cpp | 6 +- rpcs3/Emu/GS/GL/GLGSRender.cpp | 14 +- rpcs3/Emu/SysCalls/SysCalls.cpp | 3 +- rpcs3/Emu/SysCalls/SysCalls.h | 1 + rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp | 27 ++- rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp | 2 +- rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 83 +++++++-- rpcs3/Emu/System.cpp | 42 ++--- rpcs3/Gui/Debugger.cpp | 36 +--- rpcs3/Gui/Debugger.h | 3 - rpcs3/Gui/InterpreterDisAsm.cpp | 220 +++++++++++++++-------- rpcs3/Gui/InterpreterDisAsm.h | 9 +- rpcs3/Loader/SELF.cpp | 5 +- 21 files changed, 456 insertions(+), 276 deletions(-) diff --git a/rpcs3/Emu/Cell/PPCThread.cpp b/rpcs3/Emu/Cell/PPCThread.cpp index e58f5a7d41..22cfb54bab 100644 --- a/rpcs3/Emu/Cell/PPCThread.cpp +++ b/rpcs3/Emu/Cell/PPCThread.cpp @@ -11,7 +11,6 @@ PPCThread::PPCThread(PPCThreadType type) : ThreadBase(true, "PPCThread") , m_type(type) , DisAsmFrame(NULL) - , m_arg(0) , m_dec(NULL) , stack_size(0) , stack_addr(0) @@ -43,6 +42,7 @@ void PPCThread::Reset() m_sync_wait = 0; m_wait_thread_id = -1; + memset(m_args, 0, sizeof(u64) * 4); SetPc(0); cycle = 0; @@ -88,8 +88,6 @@ void PPCThread::CloseStack() void PPCThread::SetId(const u32 id) { m_id = id; - ID& thread = Emu.GetIdManager().GetIDData(m_id); - thread.m_name = GetName(); } void PPCThread::SetName(const wxString& name) @@ -119,10 +117,20 @@ bool PPCThread::Sync() int PPCThread::ThreadStatus() { - if(Emu.IsStopped()) return PPCThread_Stopped; - if(TestDestroy()) return PPCThread_Break; + if(Emu.IsStopped()) + { + return PPCThread_Stopped; + } + + if(TestDestroy()) + { + return PPCThread_Break; + } + if(Emu.IsPaused() || Sync()) + { return PPCThread_Sleeping; + } return PPCThread_Running; } @@ -186,7 +194,7 @@ void PPCThread::SetError(const u32 error) wxArrayString PPCThread::ErrorToString(const u32 error) { wxArrayString earr; - earr.Clear(); + if(error == 0) return earr; earr.Add("Unknown error"); diff --git a/rpcs3/Emu/Cell/PPCThread.h b/rpcs3/Emu/Cell/PPCThread.h index edcfcd43cf..76bd87fdb1 100644 --- a/rpcs3/Emu/Cell/PPCThread.h +++ b/rpcs3/Emu/Cell/PPCThread.h @@ -27,12 +27,13 @@ protected: wxWindow* DisAsmFrame; u32 m_id; PPCThreadType m_type; - u64 m_arg; + u64 m_args[4]; u64 m_prio; bool m_joinable; bool m_joining; Array argv_addr; u64 m_offset; + u32 m_exit_status; public: u64 stack_size; @@ -47,14 +48,17 @@ public: virtual u64 GetStackAddr() const { return stack_addr; } virtual u64 GetStackSize() const { return stack_size; } virtual u64 GetFreeStackSize() const=0; - void SetArg(const u64 arg) { m_arg = arg; } + void SetArg(const uint pos, const u64 arg) { assert(pos < 4); m_args[pos] = arg; } + void SetId(const u32 id); void SetName(const wxString& name); void SetPrio(const u64 prio) { m_prio = prio; } void SetOffset(const u64 offset) { m_offset = offset; } - u64 GetOffset() { return m_offset; } + void SetExitStatus(const u32 status) { m_exit_status = status; } + u64 GetOffset() const { return m_offset; } + u32 GetExitStatus() const { return m_exit_status; } u64 GetPrio() const { return m_prio; } wxString GetName() const { return m_name; } wxString GetFName() const diff --git a/rpcs3/Emu/Cell/PPUDisAsm.h b/rpcs3/Emu/Cell/PPUDisAsm.h index 1080415b1e..adf038e2a7 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.h +++ b/rpcs3/Emu/Cell/PPUDisAsm.h @@ -274,9 +274,9 @@ private: { DisAsm_V2("vlogefp", vd, vb); } - void VMADDFP(u32 vd, u32 va, u32 vb, u32 vc) + void VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) { - DisAsm_V4("vmaddfp", vd, va, vb, vc); + DisAsm_V4("vmaddfp", vd, va, vc, vb); } void VMAXFP(u32 vd, u32 va, u32 vb) { @@ -1166,9 +1166,16 @@ private: { DisAsm_R3_OE_RC("adde", rd, ra, rb, oe, rc); } - void MTOCRF(u32 crm, u32 rs) + void MTOCRF(u32 l, u32 crm, u32 rs) { - DisAsm_INT1_R1("mtocrf", crm, rs); + if(l) + { + DisAsm_INT1_R1("mtocrf", crm, rs); + } + else + { + DisAsm_INT1_R1("mtcrf", crm, rs); + } } void STDX(u32 rs, u32 ra, u32 rb) { @@ -1284,10 +1291,6 @@ private: { DisAsm_V1_R2("lvxl", vd, ra, rb); } - void ABS(u32 rd, u32 ra, u32 oe, bool rc) - { - DisAsm_R2_OE_RC("abs", rd, ra, oe, rc); - } void MFTB(u32 rd, u32 spr) { const u32 n = (spr >> 5) | ((spr & 0x1f) << 5); diff --git a/rpcs3/Emu/Cell/PPUInstrTable.h b/rpcs3/Emu/Cell/PPUInstrTable.h index 2de84a6b1e..245af24fd3 100644 --- a/rpcs3/Emu/Cell/PPUInstrTable.h +++ b/rpcs3/Emu/Cell/PPUInstrTable.h @@ -125,8 +125,10 @@ namespace PPU_instr */ static CodeField<30> AA; + static CodeFieldSignedOffset<6, 29, 2> LI(FIELD_BRANCH); + // - static CodeFieldSigned<6, 31> LL(FIELD_BRANCH); + static CodeFieldSignedOffset<6, 29, 2> LL(FIELD_BRANCH); /* Link bit. 0 Does not update the link register (LR). @@ -177,36 +179,7 @@ namespace PPU_instr static CodeFieldSigned<16, 31> D; // - struct : public CodeFieldSigned<16, 31> - { - static __forceinline u32 decode(u32 data) - { - int res = sign((data & mask) >> shift); - if(res < 0) return res - 1; - return res; - } - - static __forceinline void encode(u32& data, u32 value) - { - if((s32)value < 0) - { - value++; - } - - data &= ~mask; - data |= (value << shift) & mask; - } - - virtual u32 operator ()(u32 data) const - { - return decode(data); - } - - virtual void operator ()(u32& data, u32 value) const - { - return encode(data, value); - } - } static DS; + static CodeFieldSignedOffset<16, 29, 2> DS; //This immediate field is used to specify a 16-bit signed integer static CodeFieldSigned<16, 31> simm16; @@ -269,7 +242,7 @@ namespace PPU_instr bind_instr(main_list, ADDIS, RD, RA, simm16); bind_instr(main_list, BC, BO, BI, BD, AA, LK); bind_instr(main_list, SC, SYS); - bind_instr(main_list, B, LL, AA, LK); + bind_instr(main_list, B, LI, AA, LK); bind_instr(main_list, RLWIMI, RA, RS, SH, MB, ME, RC); bind_instr(main_list, RLWINM, RA, RS, SH, MB, ME, RC); bind_instr(main_list, RLWNM, RA, RS, RB, MB, ME, RC); @@ -302,7 +275,7 @@ namespace PPU_instr bind_instr(main_list, STFD, FRS, RA, D); bind_instr(main_list, STFDU, FRS, RA, D); - bind_instr(g04_list, VMADDFP, VD, VA, VB, VC); + bind_instr(g04_list, VMADDFP, VD, VA, VC, VB); bind_instr(g04_list, VMHADDSHS, VD, VA, VB, VC); bind_instr(g04_list, VMHRADDSHS, VD, VA, VB, VC); bind_instr(g04_list, VMLADDUHM, VD, VA, VB, VC); @@ -515,7 +488,7 @@ namespace PPU_instr /*0x087*/bind_instr(g1f_list, STVEBX, VS, RA, RB); /*0x088*/bind_instr(g1f_list, SUBFE, RD, RA, RB, OE, RC); /*0x08a*/bind_instr(g1f_list, ADDE, RD, RA, RB, OE, RC); - /*0x090*/bind_instr(g1f_list, MTOCRF, CRM, RS); + /*0x090*/bind_instr(g1f_list, MTOCRF, L_11, CRM, RS); /*0x095*/bind_instr(g1f_list, STDX, RS, RA, RB); /*0x096*/bind_instr(g1f_list, STWCX_, RS, RA, RB); /*0x097*/bind_instr(g1f_list, STWX, RS, RA, RB); @@ -541,7 +514,6 @@ namespace PPU_instr /*0x156*/bind_instr(g1f_list, DST, RA, RB, STRM, L_6); /*0x157*/bind_instr(g1f_list, LHAX, RD, RA, RB); /*0x167*/bind_instr(g1f_list, LVXL, VD, RA, RB); - /*0x168*/bind_instr(g1f_list, ABS, RD, RA, OE, RC); /*0x173*/bind_instr(g1f_list, MFTB, RD, SPR); /*0x176*/bind_instr(g1f_list, DSTST, RA, RB, STRM, L_6); /*0x177*/bind_instr(g1f_list, LHAUX, RD, RA, RB); @@ -554,7 +526,7 @@ namespace PPU_instr /*0x1d3*/bind_instr(g1f_list, MTSPR, SPR, RS); /*0x1d6*///DCBI /*0x1dc*/bind_instr(g1f_list, NAND, RA, RS, RB, RC); - /*0x1e7*/bind_instr(g1f_list, STVXL, RS, RA, RB); + /*0x1e7*/bind_instr(g1f_list, STVXL, VS, RA, RB); /*0x1e9*/bind_instr(g1f_list, DIVD, RD, RA, RB, OE, RC); /*0x1eb*/bind_instr(g1f_list, DIVW, RD, RA, RB, OE, RC); /*0x207*/bind_instr(g1f_list, LVLX, VD, RA, RB); @@ -568,9 +540,9 @@ namespace PPU_instr /*0x257*/bind_instr(g1f_list, LFDX, FRD, RA, RB); /*0x277*/bind_instr(g1f_list, LFDUX, FRD, RA, RB); /*0x287*/bind_instr(g1f_list, STVLX, VS, RA, RB); - /*0x297*/bind_instr(g1f_list, STFSX, RS, RA, RB); + /*0x297*/bind_instr(g1f_list, STFSX, FRS, RA, RB); /*0x2a7*/bind_instr(g1f_list, STVRX, VS, RA, RB); - /*0x2d7*/bind_instr(g1f_list, STFDX, RS, RA, RB); + /*0x2d7*/bind_instr(g1f_list, STFDX, FRS, RA, RB); /*0x307*/bind_instr(g1f_list, LVLXL, VD, RA, RB); /*0x316*/bind_instr(g1f_list, LHBRX, RD, RA, RB); /*0x318*/bind_instr(g1f_list, SRAW, RA, RS, RB, RC); @@ -588,7 +560,7 @@ namespace PPU_instr /*0x3d6*///ICBI /*0x3f6*/bind_instr(g1f_list, DCBZ, RA, RB); - bind_instr(g3a_list, LD, RD, RA, D); + bind_instr(g3a_list, LD, RD, RA, DS); bind_instr(g3a_list, LDU, RD, RA, DS); bind_instr(g3b_list, FDIVS, FRD, FRA, FRB, RC); @@ -602,7 +574,7 @@ namespace PPU_instr bind_instr(g3b_list, FNMSUBS, FRD, FRA, FRC, FRB, RC); bind_instr(g3b_list, FNMADDS, FRD, FRA, FRC, FRB, RC); - bind_instr(g3e_list, STD, RS, RA, D); + bind_instr(g3e_list, STD, RS, RA, DS); bind_instr(g3e_list, STDU, RS, RA, DS); bind_instr(g3f_list, FSEL, FRD, FRA, FRC, FRB, RC); diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index 950251da06..b30e4632a0 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -97,18 +97,12 @@ private: const u8 bo1 = (bo & 0x08) ? 1 : 0; const u8 bo2 = (bo & 0x04) ? 1 : 0; const u8 bo3 = (bo & 0x02) ? 1 : 0; - const u8 bo4 = (bo & 0x01) ? 1 : 0; if(!bo2) --CPU.CTR; const u8 ctr_ok = bo2 | ((CPU.CTR != 0) ^ bo3); const u8 cond_ok = bo0 | (CPU.IsCR(bi) ^ (~bo1 & 0x1)); - //if(bo1) CPU.SetCR(bi, bo4 ? 1 : 0); - //if(bo1) return !bo4; - - //ConLog.Write("bo0: 0x%x, bo1: 0x%x, bo2: 0x%x, bo3: 0x%x", bo0, bo1, bo2, bo3); - return ctr_ok && cond_ok; } @@ -805,7 +799,7 @@ private: CPU.VPR[vd]._f[w] = log(CPU.VPR[vb]._f[w]) / log(2.0f); } } - void VMADDFP(u32 vd, u32 va, u32 vb, u32 vc) + void VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) { for (uint w = 0; w < 4; w++) { @@ -2549,24 +2543,38 @@ private: if(rc) CPU.UpdateCR0(CPU.GPR[rd]); if(oe) UNK("addeo"); } - void MTOCRF(u32 crm, u32 rs) + void MTOCRF(u32 l, u32 crm, u32 rs) { - u32 n = 0, count = 0; - for(u32 i=0; i<8; ++i) + if(l) { - if(crm & (1 << i)) + u32 n = 0, count = 0; + for(u32 i=0; i<8; ++i) { - n = i; - count++; + if(crm & (1 << i)) + { + n = i; + count++; + } + } + + if(count == 1) + { + //CR[4*n : 4*n+3] = RS[32+4*n : 32+4*n+3]; + CPU.SetCR(n, (CPU.GPR[rs] >> (4*n)) & 0xf); + } + else + CPU.CR.CR = 0; + } + else + { + for(u32 i=0; i<8; ++i) + { + if(crm & (1 << i)) + { + CPU.SetCR(i, CPU.GPR[rs] & (0xf << i)); + } } } - - if(count == 1) - { - //CR[4*n : 4*n+3] = RS[32+4*n : 32+4*n+3]; - CPU.SetCR(n, (CPU.GPR[rs] >> (4*n)) & 0xf); - } - else CPU.CR.CR = 0; } void STDX(u32 rs, u32 ra, u32 rb) { @@ -2584,6 +2592,7 @@ private: { Memory.Write32(addr, CPU.GPR[rs]); CPU.SetCR_EQ(0, true); + CPU.reserve = false; } else { @@ -2732,12 +2741,6 @@ private: { CPU.VPR[vd]._u128 = Memory.Read128((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~0xfULL); } - void ABS(u32 rd, u32 ra, u32 oe, bool rc) - { - CPU.GPR[rd] = abs((s64)CPU.GPR[ra]); - if(oe) UNK("abso"); - if(rc) CPU.UpdateCR0(CPU.GPR[rd]); - } void MFTB(u32 rd, u32 spr) { const u32 n = (spr >> 5) | ((spr & 0x1f) << 5); @@ -3175,8 +3178,41 @@ private: } void FDIVS(u32 frd, u32 fra, u32 frb, bool rc) { - if(CPU.FPR[frb] == 0.0) CPU.SetFPSCRException(FPSCR_ZX); - CPU.FPR[frd] = static_cast(CPU.FPR[fra] / CPU.FPR[frb]); + if(FPRdouble::IsNaN(CPU.FPR[fra])) + { + CPU.FPR[frd] = CPU.FPR[fra]; + } + else if(FPRdouble::IsNaN(CPU.FPR[frb])) + { + CPU.FPR[frd] = CPU.FPR[frb]; + } + else + { + if(CPU.FPR[frb] == 0.0) + { + if(CPU.FPR[fra] == 0.0) + { + CPU.FPSCR.VXZDZ = true; + CPU.FPR[frd] = FPR_NAN; + } + else + { + CPU.FPR[frd] = (float)(CPU.FPR[fra] / CPU.FPR[frb]); + } + + CPU.FPSCR.ZX = true; + } + else if(FPRdouble::IsINF(CPU.FPR[fra]) && FPRdouble::IsINF(CPU.FPR[frb])) + { + CPU.FPSCR.VXIDI = true; + CPU.FPR[frd] = FPR_NAN; + } + else + { + CPU.FPR[frd] = (float)(CPU.FPR[fra] / CPU.FPR[frb]); + } + } + CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fdivs.");//CPU.UpdateCR1(CPU.FPR[frd]); } @@ -3194,13 +3230,17 @@ private: } void FSQRTS(u32 frd, u32 frb, bool rc) { - CPU.FPR[frd] = static_cast(sqrt((float)CPU.FPR[frb])); + CPU.FPR[frd] = static_cast(sqrt(CPU.FPR[frb])); + CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); if(rc) UNK("fsqrts.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FRES(u32 frd, u32 frb, bool rc) { if(CPU.FPR[frb] == 0.0) CPU.SetFPSCRException(FPSCR_ZX); CPU.FPR[frd] = static_cast(1.0f/CPU.FPR[frb]); + CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FI = 0; + CPU.FPSCR.FR = 0; if(rc) UNK("fres.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FMULS(u32 frd, u32 fra, u32 frc, bool rc) diff --git a/rpcs3/Emu/Cell/PPUOpcodes.h b/rpcs3/Emu/Cell/PPUOpcodes.h index bd25e4025a..52806e9241 100644 --- a/rpcs3/Emu/Cell/PPUOpcodes.h +++ b/rpcs3/Emu/Cell/PPUOpcodes.h @@ -318,7 +318,6 @@ namespace PPU_opcodes 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, @@ -436,8 +435,8 @@ public: static u64 branchTarget(const u64 pc, const u64 imm) { - return pc + (imm & ~0x3); - } + return pc + (imm & ~0x3ULL); + } virtual void NULL_OP() = 0; virtual void NOP() = 0; @@ -498,7 +497,7 @@ public: virtual void VCTUXS(u32 vd, u32 uimm5, u32 vb) = 0; virtual void VEXPTEFP(u32 vd, u32 vb) = 0; virtual void VLOGEFP(u32 vd, u32 vb) = 0; - virtual void VMADDFP(u32 vd, u32 va, u32 vb, u32 vc) = 0; + virtual void VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) = 0; virtual void VMAXFP(u32 vd, u32 va, u32 vb) = 0; virtual void VMAXSB(u32 vd, u32 va, u32 vb) = 0; virtual void VMAXSH(u32 vd, u32 va, u32 vb) = 0; @@ -675,7 +674,7 @@ public: virtual void STVEBX(u32 vs, u32 ra, u32 rb) = 0; virtual void SUBFE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) = 0; virtual void ADDE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) = 0; - virtual void MTOCRF(u32 crm, u32 rs) = 0; + virtual void MTOCRF(u32 l, u32 crm, u32 rs) = 0; virtual void STDX(u32 rs, u32 ra, u32 rb) = 0; virtual void STWCX_(u32 rs, u32 ra, u32 rb) = 0; virtual void STWX(u32 rs, u32 ra, u32 rb) = 0; @@ -701,7 +700,6 @@ public: virtual void DST(u32 ra, u32 rb, u32 strm, u32 t) = 0; virtual void LHAX(u32 rd, u32 ra, u32 rb) = 0; virtual void LVXL(u32 vd, u32 ra, u32 rb) = 0; - virtual void ABS(u32 rd, u32 ra, u32 oe, bool rc) = 0; virtual void MFTB(u32 rd, u32 spr) = 0; virtual void DSTST(u32 ra, u32 rb, u32 strm, u32 t) = 0; virtual void LHAUX(u32 rd, u32 ra, u32 rb) = 0; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 0bb661060f..cc0b7eecfb 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -67,19 +67,6 @@ void PPUThread::InitRegs() ConLog.Write("rtoc = 0x%x", rtoc); SetPc(pc); - - u64 argc = m_arg; - u64 argv = 0; - - if(argv_addr.GetCount()) - { - argc = argv_addr.GetCount(); - stack_point -= 0xc + 4 * argc; - argv = stack_point; - - mem64_t argv_list(argv); - for(int i=0; i 220) { @@ -193,7 +214,7 @@ void PPUThread::DoCode(const s32 code) bool FPRdouble::IsINF(PPCdouble d) { - return wxFinite(d) ? 1 : 0; + return d.GetType() == FPR_INF; } bool FPRdouble::IsNaN(PPCdouble d) diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index dc543f0a9e..571565a2ff 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -18,6 +18,18 @@ enum CR_SO = 0x1, }; +enum +{ + PPU_THREAD_STATUS_IDLE = (1 << 0), + PPU_THREAD_STATUS_RUNNABLE = (1 << 1), + PPU_THREAD_STATUS_ONPROC = (1 << 2), + PPU_THREAD_STATUS_SLEEP = (1 << 3), + PPU_THREAD_STATUS_STOP = (1 << 4), + PPU_THREAD_STATUS_ZOMBIE = (1 << 5), + PPU_THREAD_STATUS_DELETED = (1 << 6), + PPU_THREAD_STATUS_UNKNOWN = (1 << 7), +}; + enum FPSCR_EXP { FPSCR_FX = 0x80000000, @@ -302,6 +314,9 @@ enum FPRType FPR_ND = 0x18, }; +static const u64 FPR_NAN_I = 0x7FF8000000000000ULL; +static const double& FPR_NAN = (double&)FPR_NAN_I; + struct PPCdouble { union @@ -707,7 +722,7 @@ public: void SetFPSCR_FI(const u32 val) { if(val) SetFPSCRException(FPSCR_XX); - FPSCR.FI = val; + FPSCR.FI = val; } virtual wxString RegsToString() diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index ad636cacb3..212f25b39e 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -30,7 +30,11 @@ void SPUThread::DoReset() void SPUThread::InitRegs() { - GPR[1]._u64[0] = stack_point; + //GPR[1]._u64[0] = stack_point; + GPR[3]._u64[1] = m_args[0]; + GPR[4]._u64[1] = m_args[1]; + GPR[5]._u64[1] = m_args[2]; + GPR[6]._u64[1] = m_args[3]; } u64 SPUThread::GetFreeStackSize() const diff --git a/rpcs3/Emu/GS/GL/GLGSRender.cpp b/rpcs3/Emu/GS/GL/GLGSRender.cpp index 335f834fb7..c2da9042b6 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.cpp +++ b/rpcs3/Emu/GS/GL/GLGSRender.cpp @@ -461,7 +461,7 @@ void GLGSRender::InitVertexData() void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 count) { #if CMD_DEBUG - wxString debug = getMethodName(cmd); + wxString debug = GetMethodName(cmd); debug += "("; for(u32 i=0; i> 16; + u32 offset = address - sa; + //Memory.Write16(map_offset_addr + map_offset_pos + 0, ea); + //Memory.Write16(map_offset_addr + map_offset_pos + 2, offset); + //map_offset_pos += 4; + + Memory.Write32(offset_addr, offset); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp index f9f39c4bbd..ccdd66c444 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp @@ -129,7 +129,7 @@ int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u32 arg, int prio, u32 Memory.Write32(thread_id_addr, new_thread.GetId()); new_thread.SetEntry(entry); - new_thread.SetArg(arg); + new_thread.SetArg(0, arg); new_thread.SetPrio(prio); new_thread.stack_size = stacksize; //new_thread.flags = flags; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index 241aaddcc8..84e4470f13 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -36,14 +36,16 @@ struct sys_spu_image int nsegs; }; +static const u32 g_spu_group_thr_count = 255; + struct SpuGroupInfo { - PPCThread* threads[10]; + PPCThread* threads[g_spu_group_thr_count]; sys_spu_thread_group_attribute& attr; SpuGroupInfo(sys_spu_thread_group_attribute& attr) : attr(attr) { - memset(threads, 0, sizeof(PPCThread*) * 10); + memset(threads, 0, sizeof(PPCThread*) * g_spu_group_thr_count); } }; @@ -74,7 +76,7 @@ int sys_spu_image_open(u32 img_addr, u32 path_addr) if(!stream || !stream->IsOpened()) { - sc_spu.Error("'%s' not found!", path); + sc_spu.Error("sys_spu_image_open error: '%s' not found!", path); delete stream; return CELL_ENOENT; @@ -83,7 +85,7 @@ int sys_spu_image_open(u32 img_addr, u32 path_addr) u32 entry = LoadSpuImage(*stream); delete stream; - sys_spu_image& ret = (sys_spu_image&)Memory[img_addr]; + auto& ret = (sys_spu_image&)Memory[img_addr]; re(ret.type, 1); re(ret.entry_point, entry); re(ret.segs_addr, 0x0); @@ -103,6 +105,8 @@ int sys_spu_thread_initialize(u32 thread_addr, u32 group, u32 spu_num, u32 img_a return CELL_ESRCH; } + SpuGroupInfo& group_info = *(SpuGroupInfo*)Emu.GetIdManager().GetIDData(group).m_data; + if( !Memory.IsGoodAddr(img_addr, sizeof(sys_spu_image)) || !Memory.IsGoodAddr(attr_addr, sizeof(sys_spu_thread_attribute)) || @@ -111,15 +115,25 @@ int sys_spu_thread_initialize(u32 thread_addr, u32 group, u32 spu_num, u32 img_a return CELL_EFAULT; } - sys_spu_image& img = (sys_spu_image&)Memory[img_addr]; - sys_spu_thread_attribute& attr = (sys_spu_thread_attribute&)Memory[attr_addr]; - sys_spu_thread_argument& arg = (sys_spu_thread_argument&)Memory[arg_addr]; + auto& img = (sys_spu_image&)Memory[img_addr]; + auto& attr = (sys_spu_thread_attribute&)Memory[attr_addr]; + auto& arg = (sys_spu_thread_argument&)Memory[arg_addr]; if(!Memory.IsGoodAddr(re(attr.name_addr), re(attr.name_len))) { return CELL_EFAULT; } + + if(spu_num >= g_spu_group_thr_count) + { + return CELL_EINVAL; + } + if(group_info.threads[spu_num]) + { + return CELL_EBUSY; + } + u32 entry = re(img.entry_point); wxString name = Memory.ReadString(re(attr.name_addr), re(attr.name_len)); u64 a1 = re(arg.arg1); @@ -140,21 +154,43 @@ int sys_spu_thread_initialize(u32 thread_addr, u32 group, u32 spu_num, u32 img_a new_thread.SetOffset(g_spu_offset); new_thread.SetEntry(entry - g_spu_offset); new_thread.SetName(name); - SPU_GPR_hdr* GPR = ((SPUThread&)new_thread).GPR; new_thread.Run(); new_thread.Pause(); - GPR[3]._u64[1] = a1; - GPR[4]._u64[1] = a2; - GPR[5]._u64[1] = a3; - GPR[6]._u64[1] = a4; + new_thread.SetArg(0, a1); + new_thread.SetArg(1, a2); + new_thread.SetArg(2, a3); + new_thread.SetArg(3, a4); - ID& id = Emu.GetIdManager().GetIDData(group); - SpuGroupInfo& group_info = *(SpuGroupInfo*)id.m_data; group_info.threads[spu_num] = &new_thread; return CELL_OK; } +//166 +int sys_spu_thread_set_argument(u32 id, u32 arg_addr) +{ + sc_spu.Warning("sys_spu_thread_set_argument(id=0x%x, arg_addr=0x%x)", id, arg_addr); + PPCThread* thr = Emu.GetCPU().GetThread(id); + + if(!thr || !thr->IsSPU()) + { + return CELL_ESRCH; + } + + if(!Memory.IsGoodAddr(arg_addr, sizeof(sys_spu_thread_argument))) + { + return CELL_EFAULT; + } + + auto& arg = (sys_spu_thread_argument&)Memory[arg_addr]; + thr->SetArg(0, re(arg.arg1)); + thr->SetArg(1, re(arg.arg2)); + thr->SetArg(2, re(arg.arg3)); + thr->SetArg(3, re(arg.arg4)); + + return CELL_OK; +} + //173 int sys_spu_thread_group_start(u32 id) { @@ -169,7 +205,7 @@ int sys_spu_thread_group_start(u32 id) SpuGroupInfo& group_info = *(SpuGroupInfo*)id_data.m_data; Emu.Pause(); - for(int i=0; i<10; i++) + for(int i=0; i 63) + { + return CELL_EINVAL; + } + + return CELL_OK; +} + //160 int sys_raw_spu_create(u32 id_addr, u32 attr_addr) { @@ -329,7 +380,7 @@ int sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, u32 SpuGroupInfo* group = (SpuGroupInfo*)Emu.GetIdManager().GetIDData(id).m_data; EventQueue* equeue = (EventQueue*)Emu.GetIdManager().GetIDData(eq).m_data; - for(int i=0; i<10; ++i) + for(int i=0; ithreads[i]) { diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 9d62bfeef7..9bc0229754 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -85,8 +85,6 @@ void Emulator::Load() Memory.Init(); GetInfo().Reset(); - Memory.Write64(Memory.PRXMem.Alloc(8), 0xDEADBEEFABADCAFE); - bool is_error; vfsLocalFile f(m_path); Loader l(f); @@ -127,28 +125,26 @@ void Emulator::Load() else { thread.SetEntry(l.GetEntry()); + Memory.StackMem.Alloc(0x1000); + thread.InitStack(); + thread.AddArgv(m_path); + //thread.AddArgv("-emu"); + + 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 += ADDI(11, 0, 0x3ff); + callback_data += SC(2); + callback_data += BCLR(0x10 | 0x04, 0, 0, 0); + + m_ppu_thr_exit = Memory.MainMem.Alloc(4 * 3); + + mem32_t ppu_thr_exit_data(m_ppu_thr_exit); + ppu_thr_exit_data += ADDI(11, 0, 41); + ppu_thr_exit_data += SC(2); + ppu_thr_exit_data += BCLR(0x10 | 0x04, 0, 0, 0); } - - thread.SetArg(thread.GetId()); - Memory.StackMem.Alloc(0x1000); - thread.InitStack(); - thread.AddArgv(m_path); - //thread.AddArgv("-emu"); - - 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 += ADDI(11, 0, 0x3ff); - callback_data += SC(2); - callback_data += BCLR(0x10 | 0x04, 0, 0, 0); - - m_ppu_thr_exit = Memory.MainMem.Alloc(4 * 3); - - mem32_t ppu_thr_exit_data(m_ppu_thr_exit); - ppu_thr_exit_data += ADDI(11, 0, 41); - ppu_thr_exit_data += SC(2); - ppu_thr_exit_data += BCLR(0x10 | 0x04, 0, 0, 0); thread.Run(); diff --git a/rpcs3/Gui/Debugger.cpp b/rpcs3/Gui/Debugger.cpp index 31bb005d06..a237bd705b 100644 --- a/rpcs3/Gui/Debugger.cpp +++ b/rpcs3/Gui/Debugger.cpp @@ -99,16 +99,9 @@ DebuggerPanel::DebuggerPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY, wxDef { 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.AddPane(new InterpreterDisAsmFrame(this), 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() @@ -119,30 +112,3 @@ DebuggerPanel::~DebuggerPanel() 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 index e127642937..7f48f0c623 100644 --- a/rpcs3/Gui/Debugger.h +++ b/rpcs3/Gui/Debugger.h @@ -5,13 +5,10 @@ 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/InterpreterDisAsm.cpp b/rpcs3/Gui/InterpreterDisAsm.cpp index a0126bdb66..9b5d9d029d 100644 --- a/rpcs3/Gui/InterpreterDisAsm.cpp +++ b/rpcs3/Gui/InterpreterDisAsm.cpp @@ -3,48 +3,39 @@ //static const int show_lines = 30; -u32 InterpreterDisAsmFrame::CentrePc(const u32 pc) const +u64 InterpreterDisAsmFrame::CentrePc(const u64 pc) const { return pc - ((m_item_count / 2) * 4); } -InterpreterDisAsmFrame::InterpreterDisAsmFrame(wxWindow* parent, PPCThread* cpu) +InterpreterDisAsmFrame::InterpreterDisAsmFrame(wxWindow* parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(500, 700), wxTAB_TRAVERSAL) , ThreadBase(false, "DisAsmFrame Thread") - , CPU(*cpu) , PC(0) + , CPU(nullptr) , m_item_count(30) + , decoder(nullptr) + , disasm(nullptr) { - if(CPU.IsSPU()) - { - SPU_DisAsm& dis_asm = *new SPU_DisAsm(CPU, InterpreterMode); - decoder = new SPU_Decoder(dis_asm); - disasm = &dis_asm; - } - else - { - PPU_DisAsm& dis_asm = *new PPU_DisAsm(CPU, InterpreterMode); - decoder = new PPU_Decoder(dis_asm); - disasm = &dis_asm; - } - wxBoxSizer& s_p_main = *new wxBoxSizer(wxVERTICAL); wxBoxSizer& s_b_main = *new wxBoxSizer(wxHORIZONTAL); m_list = new wxListView(this); + m_choice_units = new wxChoice(this, wxID_ANY); 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(this, wxID_ANY, "Step"); - m_btn_run = new wxButton(this, wxID_ANY, "Run"); - m_btn_pause = new wxButton(this, 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)); - s_b_main.Add(m_btn_step, wxSizerFlags().Border(wxALL, 5)); - s_b_main.Add(m_btn_run, wxSizerFlags().Border(wxALL, 5)); - s_b_main.Add(m_btn_pause, wxSizerFlags().Border(wxALL, 5)); + s_b_main.Add(&b_go_to_addr, wxSizerFlags().Border(wxALL, 5)); + s_b_main.Add(&b_go_to_pc, wxSizerFlags().Border(wxALL, 5)); + s_b_main.Add(m_btn_step, wxSizerFlags().Border(wxALL, 5)); + s_b_main.Add(m_btn_run, wxSizerFlags().Border(wxALL, 5)); + s_b_main.Add(m_btn_pause, wxSizerFlags().Border(wxALL, 5)); + s_b_main.Add(m_choice_units, wxSizerFlags().Border(wxALL, 5)); m_regs = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_DONTWRAP|wxNO_BORDER|wxTE_RICH2); @@ -77,12 +68,14 @@ InterpreterDisAsmFrame::InterpreterDisAsmFrame(wxWindow* parent, PPCThread* cpu) 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(m_choice_units->GetId(),wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(InterpreterDisAsmFrame::OnSelectUnit)); 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_app_connector.Connect(wxEVT_DBG_COMMAND, wxCommandEventHandler(InterpreterDisAsmFrame::HandleCommand), (wxObject*)0, this); - WriteRegs(); + + ShowAddr(CentrePc(PC)); + UpdateUnitList(); } InterpreterDisAsmFrame::~InterpreterDisAsmFrame() @@ -90,6 +83,48 @@ InterpreterDisAsmFrame::~InterpreterDisAsmFrame() ThreadBase::Stop(); } +void InterpreterDisAsmFrame::UpdateUnitList() +{ + m_choice_units->Freeze(); + m_choice_units->Clear(); + auto& thrs = Emu.GetCPU().GetThreads(); + + for(uint i=0; iAppend(thrs[i].GetFName(), &thrs[i]); + } + + m_choice_units->Thaw(); +} + +void InterpreterDisAsmFrame::OnSelectUnit(wxCommandEvent& event) +{ + CPU = (PPCThread*)event.GetClientData(); + + delete decoder; + //delete disasm; + decoder = nullptr; + disasm = nullptr; + + if(CPU) + { + if(CPU->IsSPU()) + { + SPU_DisAsm& dis_asm = *new SPU_DisAsm(*CPU, InterpreterMode); + decoder = new SPU_Decoder(dis_asm); + disasm = &dis_asm; + } + else + { + PPU_DisAsm& dis_asm = *new PPU_DisAsm(*CPU, InterpreterMode); + decoder = new PPU_Decoder(dis_asm); + disasm = &dis_asm; + } + } + + DoUpdate(); +} + void InterpreterDisAsmFrame::OnKeyDown(wxKeyEvent& event) { if(wxGetActiveWindow() != wxGetTopLevelParent(this)) @@ -164,48 +199,59 @@ void InterpreterDisAsmFrame::ShowAddr(const u64 addr) { PC = addr; m_list->Freeze(); - disasm->offset = CPU.GetOffset(); - for(uint i=0; iSetItem(i, 0, wxString::Format("[%08llx] illegal address", PC)); - continue; } - - disasm->dump_pc = PC; - decoder->Decode(Memory.Read32(CPU.GetOffset() + PC)); - - if(IsBreakPoint(PC)) + } + else + { + disasm->offset = CPU->GetOffset(); + for(uint i=0; iSetItem(i, 0, ">>> " + disasm->last_opcode); - } - else - { - m_list->SetItem(i, 0, " " + disasm->last_opcode); - } - - wxColour colour; - - if((!CPU.IsRunned() || !Emu.IsRunned()) && PC == CPU.PC) - { - colour = wxColour("Green"); - } - else - { - colour = wxColour("White"); - - for(u32 i=0; iGetOffset() + PC, 4)) { - if(Emu.GetMarkedPoints()[i] == PC) + m_list->SetItem(i, 0, wxString::Format("[%08llx] illegal address", PC)); + continue; + } + + disasm->dump_pc = PC; + decoder->Decode(Memory.Read32(CPU->GetOffset() + PC)); + + if(IsBreakPoint(PC)) + { + m_list->SetItem(i, 0, ">>> " + disasm->last_opcode); + } + else + { + m_list->SetItem(i, 0, " " + disasm->last_opcode); + } + + wxColour colour; + + if((!CPU->IsRunned() || !Emu.IsRunned()) && PC == CPU->PC) + { + colour = wxColour("Green"); + } + else + { + colour = wxColour("White"); + + for(u32 i=0; iSetItemBackgroundColour( i, colour ); + m_list->SetItemBackgroundColour( i, colour ); + } } while(remove_markedPC.GetCount()) @@ -234,7 +280,7 @@ void InterpreterDisAsmFrame::WriteRegs() { m_regs->Freeze(); m_regs->Clear(); - m_regs->WriteText(CPU.RegsToString()); + if(CPU) m_regs->WriteText(CPU->RegsToString()); m_regs->Thaw(); } @@ -253,7 +299,7 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) break; } } - else if(thr->GetId() == CPU.GetId()) + else if(CPU && thr->GetId() == CPU->GetId()) { switch(event.GetId()) { @@ -266,7 +312,6 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) DoUpdate(); break; - case DID_START_THREAD: case DID_EXEC_THREAD: case DID_RESUME_THREAD: @@ -285,6 +330,36 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) m_btn_run->Disable(); m_btn_step->Disable(); m_btn_pause->Disable(); + + if(event.GetId() == DID_REMOVE_THREAD) + { + m_choice_units->SetSelection(-1); + wxCommandEvent event; + event.SetInt(-1); + //event.SetClientData(nullptr); + OnSelectUnit(event); + UpdateUnitList(); + } + + DoUpdate(); + break; + } + } + else + { + switch(event.GetId()) + { + case DID_CREATE_THREAD: + UpdateUnitList(); + if(m_choice_units->GetSelection() == -1) + { + m_choice_units->SetSelection(0); + wxCommandEvent event; + event.SetInt(0); + event.SetClientData(&Emu.GetCPU().GetThreads()[0]); + OnSelectUnit(event); + DoUpdate(); + } break; } } @@ -313,11 +388,11 @@ void InterpreterDisAsmFrame::Show_Val(wxCommandEvent& WXUNUSED(event)) diag->SetSizerAndFit( s_panel ); - p_pc->SetLabel(wxString::Format("%llx", CPU.PC)); + if(CPU) p_pc->SetLabel(wxString::Format("%llx", CPU->PC)); if(diag->ShowModal() == wxID_OK) { - u64 pc = CPU.PC; + u64 pc = CPU ? CPU->PC : 0x0; sscanf(p_pc->GetLabel(), "%llx", &pc); remove_markedPC.AddCpy(Emu.GetMarkedPoints().AddCpy(pc)); ShowAddr(CentrePc(pc)); @@ -326,17 +401,19 @@ void InterpreterDisAsmFrame::Show_Val(wxCommandEvent& WXUNUSED(event)) void InterpreterDisAsmFrame::Show_PC(wxCommandEvent& WXUNUSED(event)) { - ShowAddr(CentrePc(CPU.PC)); + if(CPU) ShowAddr(CentrePc(CPU->PC)); } extern bool dump_enable; void InterpreterDisAsmFrame::DoRun(wxCommandEvent& WXUNUSED(event)) { - if(CPU.IsPaused()) CPU.Resume(); + if(!CPU) return; + + if(CPU->IsPaused()) CPU->Resume(); if(!Emu.IsPaused()) { - CPU.Exec(); + CPU->Exec(); } //ThreadBase::Start(); @@ -344,7 +421,7 @@ void InterpreterDisAsmFrame::DoRun(wxCommandEvent& WXUNUSED(event)) void InterpreterDisAsmFrame::DoPause(wxCommandEvent& WXUNUSED(event)) { - CPU.Pause(); + if(CPU) CPU->Pause(); } void InterpreterDisAsmFrame::DoStep(wxCommandEvent& WXUNUSED(event)) @@ -418,7 +495,8 @@ bool InterpreterDisAsmFrame::RemoveBreakPoint(u64 pc) void InterpreterDisAsmFrame::Task() { - wxGetApp().SendDbgCommand(DID_RESUME_THREAD, &CPU); + if(!CPU) return; + wxGetApp().SendDbgCommand(DID_RESUME_THREAD, CPU); bool dump_status = dump_enable; @@ -428,9 +506,9 @@ void InterpreterDisAsmFrame::Task() { do { - CPU.ExecOnce(); + CPU->ExecOnce(); } - while(CPU.IsRunned() && Emu.IsRunned() && !TestDestroy() && !IsBreakPoint(CPU.PC) && dump_status == dump_enable); + while(CPU->IsRunned() && Emu.IsRunned() && !TestDestroy() && !IsBreakPoint(CPU->PC) && dump_status == dump_enable); } catch(const wxString& e) { @@ -443,5 +521,5 @@ void InterpreterDisAsmFrame::Task() //CPU.FreeTls(); - wxGetApp().SendDbgCommand(DID_PAUSE_THREAD, &CPU); + 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 57d3846fcb..b86db3ab2a 100644 --- a/rpcs3/Gui/InterpreterDisAsm.h +++ b/rpcs3/Gui/InterpreterDisAsm.h @@ -20,16 +20,19 @@ class InterpreterDisAsmFrame wxButton* m_btn_pause; AppConnector m_app_connector; u32 m_item_count; + wxChoice* m_choice_units; public: - PPCThread& CPU; + PPCThread* CPU; public: - InterpreterDisAsmFrame(wxWindow* parent, PPCThread* cpu); + InterpreterDisAsmFrame(wxWindow* parent); ~InterpreterDisAsmFrame(); - u32 CentrePc(const u32 pc) const; + void UpdateUnitList(); + u64 CentrePc(const u64 pc) const; + void OnSelectUnit(wxCommandEvent& event); void OnKeyDown(wxKeyEvent& event); void OnResize(wxSizeEvent& event); void DoUpdate(); diff --git a/rpcs3/Loader/SELF.cpp b/rpcs3/Loader/SELF.cpp index 842d74263a..e6f522252d 100644 --- a/rpcs3/Loader/SELF.cpp +++ b/rpcs3/Loader/SELF.cpp @@ -30,13 +30,16 @@ bool SELFLoader::LoadData(u64 offset) if( !l.LoadEhdrInfo(self_hdr.se_elfoff) || !l.LoadPhdrInfo(self_hdr.se_phdroff) || !l.LoadShdrInfo(self_hdr.se_shdroff) || - !l.LoadData(offset) ) + !l.LoadData(self_hdr.se_appinfooff) ) { ConLog.Error("Broken SELF file."); return false; } + machine = l.GetMachine(); + entry = l.GetEntry(); + return true; ConLog.Error("Boot SELF not supported yet!"); From 027a31bd844a2d417488f4552ba9cb0ca3c2c9a0 Mon Sep 17 00:00:00 2001 From: DH Date: Thu, 11 Jul 2013 17:28:10 +0300 Subject: [PATCH 03/13] - Fixed PPU G_3f_0 Decoder. --- rpcs3/Emu/Cell/PPUInstrTable.h | 24 ++++++--------------- rpcs3/Emu/Cell/PPUInterpreter.h | 38 +++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUInstrTable.h b/rpcs3/Emu/Cell/PPUInstrTable.h index 245af24fd3..75506c6510 100644 --- a/rpcs3/Emu/Cell/PPUInstrTable.h +++ b/rpcs3/Emu/Cell/PPUInstrTable.h @@ -70,18 +70,6 @@ namespace PPU_instr //This field is used to specify a bit in the CR, or in the FPSCR, as the destination of the result of an instruction static CodeField<6, 10> CRBD(FIELD_R_CR); - - // - static CodeField<6, 10> BT; - - // - static CodeField<11, 15> BA; - - // - static CodeField<16, 20> BB; - - // - static CodeField<6, 10> BF; //This field is used to specify options for the branch conditional instructions static CodeField<6, 10> BO; @@ -158,7 +146,7 @@ namespace PPU_instr static CodeField<6, 10> FRS; // - static CodeField<7, 14> FLM; + static CodeField<7, 14> FM; //This field is used to specify an FPR as a source static CodeField<11, 15> FRA(FIELD_R_FPR); @@ -602,12 +590,12 @@ namespace PPU_instr bind_instr(g3f_0_list, FCTID, FRD, FRB, RC); bind_instr(g3f_0_list, FCTIDZ, FRD, FRB, RC); - bind_instr(g3f_0_list, MTFSB1, BT, RC); - bind_instr(g3f_0_list, MCRFS, BF, BFA); - bind_instr(g3f_0_list, MTFSB0, BT, RC); - bind_instr(g3f_0_list, MTFSFI, CRFD, I, RC); + bind_instr(g3f_0_list, MTFSB1, CRBD, RC); + bind_instr(g3f_0_list, MCRFS, CRFD, CRFS); + bind_instr(g3f_0_list, MTFSB0, CRBD, RC); + bind_instr(g3f_0_list, MTFSFI, CRBD, I, RC); bind_instr(g3f_0_list, MFFS, FRD, RC); - bind_instr(g3f_0_list, MTFSF, FLM, FRB, RC); + bind_instr(g3f_0_list, MTFSF, FM, FRB, RC); #undef bind_instr }; \ No newline at end of file diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index b30e4632a0..d234a5ea48 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -3286,26 +3286,42 @@ private: Memory.Write64(addr, CPU.GPR[rs]); CPU.GPR[ra] = addr; } - void MTFSB1(u32 bt, bool rc) + void MTFSB1(u32 crbd, bool rc) { - UNIMPLEMENTED(); + u64 mask = (1ULL << crbd); + CPU.FPSCR.FPSCR |= mask; + if(rc) UNIMPLEMENTED(); } - void MCRFS(u32 bf, u32 bfa) + void MCRFS(u32 crbd, u32 crbs) { - UNIMPLEMENTED(); + u64 mask = (1ULL << crbd); + CPU.CR.CR &= ~mask; + CPU.CR.CR |= CPU.FPSCR.FPSCR & mask; } - void MTFSB0(u32 bt, bool rc) + void MTFSB0(u32 crbd, bool rc) { - UNIMPLEMENTED(); + u64 mask = (1ULL << crbd); + CPU.FPSCR.FPSCR &= ~mask; + if(rc) UNIMPLEMENTED(); } void MTFSFI(u32 crfd, u32 i, bool rc) { - UNIMPLEMENTED(); + u64 mask = (1ULL << crfd); + + if(i) + { + CPU.FPSCR.FPSCR |= mask; + } + else + { + CPU.FPSCR.FPSCR &= ~mask; + } + if(rc) UNIMPLEMENTED(); } void MFFS(u32 frd, bool rc) { (u64&)CPU.FPR[frd] = CPU.FPSCR.FPSCR; - if(rc) UNK("mffs."); + if(rc) UNIMPLEMENTED(); } void MTFSF(u32 flm, u32 frb, bool rc) { @@ -3493,17 +3509,17 @@ private: CPU.FPR[frd] = CPU.FPR[fra] * CPU.FPR[frc]; CPU.FPSCR.FI = 0; CPU.FPSCR.FR = 0; - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fmul.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FRSQRTE(u32 frd, u32 frb, bool rc) { - UNIMPLEMENTED(); + //if(CPU.FPR[frb]. } void FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] * CPU.FPR[frc] - CPU.FPR[frb]; - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fmsub.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) From 559852a8fc2da59e91025f4709590df030282236 Mon Sep 17 00:00:00 2001 From: DH Date: Fri, 12 Jul 2013 15:42:17 +0300 Subject: [PATCH 04/13] - Implemented RAW SPU. - Implemented memory mapping. --- Utilities/Thread.h | 4 + rpcs3/Emu/Cell/PPCThread.cpp | 1 + rpcs3/Emu/Cell/PPCThread.h | 14 +- rpcs3/Emu/Cell/PPCThreadManager.cpp | 23 +- rpcs3/Emu/Cell/PPCThreadManager.h | 4 +- rpcs3/Emu/Cell/PPUInstrTable.h | 2 +- rpcs3/Emu/Cell/PPUInterpreter.h | 95 ++++---- rpcs3/Emu/Cell/PPUThread.cpp | 6 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 151 ++++++++++++ rpcs3/Emu/Cell/RawSPUThread.h | 66 +++++ rpcs3/Emu/Cell/SPUInterpreter.h | 6 +- rpcs3/Emu/Cell/SPUThread.cpp | 4 +- rpcs3/Emu/Cell/SPUThread.h | 121 ++++++++- rpcs3/Emu/GS/GL/GLGSRender.cpp | 30 ++- rpcs3/Emu/GS/GL/GLProcTable.tbl | 3 +- rpcs3/Emu/Memory/Memory.cpp | 66 ++++- rpcs3/Emu/Memory/Memory.h | 43 +++- rpcs3/Emu/Memory/MemoryBlock.h | 26 ++ rpcs3/Emu/SysCalls/Callback.cpp | 18 +- rpcs3/Emu/SysCalls/Modules.cpp | 243 ++++++++++++++++++- rpcs3/Emu/SysCalls/Modules.h | 7 +- rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp | 2 +- rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp | 95 ++++++++ rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp | 2 +- rpcs3/Emu/SysCalls/Modules/sys_fs.cpp | 2 +- rpcs3/Emu/SysCalls/Modules/sys_io.cpp | 2 +- rpcs3/Emu/SysCalls/SysCalls.cpp | 2 +- rpcs3/Emu/SysCalls/SysCalls.h | 2 +- rpcs3/Emu/SysCalls/lv2/SC_Event.cpp | 38 ++- rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp | 9 +- rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp | 4 +- rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp | 42 +++- rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp | 17 +- rpcs3/Emu/SysCalls/lv2/SC_Process.cpp | 1 - rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 25 +- rpcs3/Emu/System.cpp | 2 +- rpcs3/Gui/DisAsmFrame.cpp | 4 +- rpcs3/Gui/InterpreterDisAsm.cpp | 2 +- rpcs3/Loader/ELF32.cpp | 74 ++++-- rpcs3/Loader/ELF32.h | 34 +++ rpcs3/Loader/ELF64.cpp | 30 ++- rpcs3/rpcs3.vcxproj | 2 + rpcs3/rpcs3.vcxproj.filters | 6 + 43 files changed, 1106 insertions(+), 224 deletions(-) create mode 100644 rpcs3/Emu/Cell/RawSPUThread.cpp create mode 100644 rpcs3/Emu/Cell/RawSPUThread.h create mode 100644 rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 38f6807aeb..e2c2a7e3a1 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -1,5 +1,9 @@ #pragma once #include "Array.h" +#include +#include +#include +#include class ThreadExec; diff --git a/rpcs3/Emu/Cell/PPCThread.cpp b/rpcs3/Emu/Cell/PPCThread.cpp index 22cfb54bab..54e5c4ef28 100644 --- a/rpcs3/Emu/Cell/PPCThread.cpp +++ b/rpcs3/Emu/Cell/PPCThread.cpp @@ -273,6 +273,7 @@ void PPCThread::Exec() { wxGetApp().SendDbgCommand(DID_EXEC_THREAD, this); ThreadBase::Start(); + //std::thread thr(std::bind(std::mem_fn(&PPCThread::Task), this)); } void PPCThread::ExecOnce() diff --git a/rpcs3/Emu/Cell/PPCThread.h b/rpcs3/Emu/Cell/PPCThread.h index 76bd87fdb1..c16bb90c18 100644 --- a/rpcs3/Emu/Cell/PPCThread.h +++ b/rpcs3/Emu/Cell/PPCThread.h @@ -6,6 +6,7 @@ enum PPCThreadType { PPC_THREAD_PPU, PPC_THREAD_SPU, + PPC_THREAD_RAW_SPU, }; enum PPCThreadStatus @@ -77,6 +78,7 @@ public: { case PPC_THREAD_PPU: return "PPU"; case PPC_THREAD_SPU: return "SPU"; + case PPC_THREAD_RAW_SPU: return "RawSPU"; } return "Unknown"; @@ -111,6 +113,15 @@ public: void Wait(const PPCThread& thr); bool Sync(); + template + void WaitFor(T func) + { + while(func(ThreadStatus())) + { + Sleep(1); + } + } + int ThreadStatus(); void NextPc(); @@ -125,7 +136,6 @@ public: static wxArrayString ErrorToString(const u32 error); wxArrayString ErrorToString() { return ErrorToString(m_error); } - bool IsSPU() const { return m_type == PPC_THREAD_SPU; } bool IsOk() const { return m_error == 0; } bool IsRunned() const { return m_status == Runned; } bool IsPaused() const { return m_status == Paused; } @@ -138,6 +148,7 @@ public: u32 GetError() const { return m_error; } u32 GetId() const { return m_id; } + PPCThreadType GetType() const { return m_type; } void Reset(); void Close(); @@ -160,6 +171,7 @@ protected: virtual void DoResume()=0; virtual void DoStop()=0; +public: virtual void Task(); private: diff --git a/rpcs3/Emu/Cell/PPCThreadManager.cpp b/rpcs3/Emu/Cell/PPCThreadManager.cpp index 82ae53473d..eba18e18bb 100644 --- a/rpcs3/Emu/Cell/PPCThreadManager.cpp +++ b/rpcs3/Emu/Cell/PPCThreadManager.cpp @@ -2,6 +2,7 @@ #include "PPCThreadManager.h" #include "PPUThread.h" #include "SPUThread.h" +#include "RawSPUThread.h" PPCThreadManager::PPCThreadManager() { @@ -17,13 +18,19 @@ void PPCThreadManager::Close() while(m_threads.GetCount()) RemoveThread(m_threads[0].GetId()); } -PPCThread& PPCThreadManager::AddThread(bool isPPU) +PPCThread& PPCThreadManager::AddThread(PPCThreadType type) { - PPCThread* new_thread = isPPU ? (PPCThread*)new PPUThread() : (PPCThread*)new SPUThread(); - - new_thread->SetId(Emu.GetIdManager().GetNewID( - wxString::Format("%s Thread", isPPU ? "PPU" : "SPU"), new_thread, 0) - ); + PPCThread* new_thread; + char* name; + switch(type) + { + case PPC_THREAD_PPU: new_thread = new PPUThread(); name = "PPU"; break; + case PPC_THREAD_SPU: new_thread = new SPUThread(); name = "SPU"; break; + case PPC_THREAD_RAW_SPU: new_thread = new RawSPUThread(); name = "RawSPU"; break; + default: assert(0); + } + + new_thread->SetId(Emu.GetIdManager().GetNewID(wxString::Format("%s Thread", name), new_thread)); m_threads.Add(new_thread); wxGetApp().SendDbgCommand(DID_CREATE_THREAD, new_thread); @@ -54,14 +61,14 @@ void PPCThreadManager::RemoveThread(const u32 id) Emu.CheckStatus(); } -s32 PPCThreadManager::GetThreadNumById(bool isPPU, u32 id) +s32 PPCThreadManager::GetThreadNumById(PPCThreadType type, u32 id) { s32 num = 0; for(u32 i=0; i& GetThreads() { return m_threads; } - s32 GetThreadNumById(bool isPPU, u32 id); + s32 GetThreadNumById(PPCThreadType type, u32 id); PPCThread* GetThread(u32 id); //IdManager& GetIDs() {return m_threads_id;} diff --git a/rpcs3/Emu/Cell/PPUInstrTable.h b/rpcs3/Emu/Cell/PPUInstrTable.h index 75506c6510..08e110c6f4 100644 --- a/rpcs3/Emu/Cell/PPUInstrTable.h +++ b/rpcs3/Emu/Cell/PPUInstrTable.h @@ -593,7 +593,7 @@ namespace PPU_instr bind_instr(g3f_0_list, MTFSB1, CRBD, RC); bind_instr(g3f_0_list, MCRFS, CRFD, CRFS); bind_instr(g3f_0_list, MTFSB0, CRBD, RC); - bind_instr(g3f_0_list, MTFSFI, CRBD, I, RC); + bind_instr(g3f_0_list, MTFSFI, CRFD, I, RC); bind_instr(g3f_0_list, MFFS, FRD, RC); bind_instr(g3f_0_list, MTFSF, FM, FRB, RC); diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index d234a5ea48..86d012636c 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -2080,48 +2080,48 @@ private: CPU.SetBranch(branchTarget(0, CPU.LR)); if(lk) CPU.LR = CPU.PC + 4; } - void CRNOR(u32 bt, u32 ba, u32 bb) + void CRNOR(u32 crbd, u32 crba, u32 crbb) { - const u8 v = 1 ^ (CPU.IsCR(ba) | CPU.IsCR(bb)); - CPU.SetCRBit2(bt, v & 0x1); + const u8 v = 1 ^ (CPU.IsCR(crba) | CPU.IsCR(crbb)); + CPU.SetCRBit2(crbd, v & 0x1); } - void CRANDC(u32 bt, u32 ba, u32 bb) + void CRANDC(u32 crbd, u32 crba, u32 crbb) { - const u8 v = CPU.IsCR(ba) & (1 ^ CPU.IsCR(bb)); - CPU.SetCRBit2(bt, v & 0x1); + const u8 v = CPU.IsCR(crba) & (1 ^ CPU.IsCR(crbb)); + CPU.SetCRBit2(crbd, v & 0x1); } void ISYNC() { } - void CRXOR(u32 bt, u32 ba, u32 bb) + void CRXOR(u32 crbd, u32 crba, u32 crbb) { - const u8 v = CPU.IsCR(ba) ^ CPU.IsCR(bb); - CPU.SetCRBit2(bt, v & 0x1); + const u8 v = CPU.IsCR(crba) ^ CPU.IsCR(crbb); + CPU.SetCRBit2(crbd, v & 0x1); } - void CRNAND(u32 bt, u32 ba, u32 bb) + void CRNAND(u32 crbd, u32 crba, u32 crbb) { - const u8 v = 1 ^ (CPU.IsCR(ba) & CPU.IsCR(bb)); - CPU.SetCRBit2(bt, v & 0x1); + const u8 v = 1 ^ (CPU.IsCR(crba) & CPU.IsCR(crbb)); + CPU.SetCRBit2(crbd, v & 0x1); } - void CRAND(u32 bt, u32 ba, u32 bb) + void CRAND(u32 crbd, u32 crba, u32 crbb) { - const u8 v = CPU.IsCR(ba) & CPU.IsCR(bb); - CPU.SetCRBit2(bt, v & 0x1); + const u8 v = CPU.IsCR(crba) & CPU.IsCR(crbb); + CPU.SetCRBit2(crbd, v & 0x1); } - void CREQV(u32 bt, u32 ba, u32 bb) + void CREQV(u32 crbd, u32 crba, u32 crbb) { - const u8 v = 1 ^ (CPU.IsCR(ba) ^ CPU.IsCR(bb)); - CPU.SetCRBit2(bt, v & 0x1); + const u8 v = 1 ^ (CPU.IsCR(crba) ^ CPU.IsCR(crbb)); + CPU.SetCRBit2(crbd, v & 0x1); } - void CRORC(u32 bt, u32 ba, u32 bb) + void CRORC(u32 crbd, u32 crba, u32 crbb) { - const u8 v = CPU.IsCR(ba) | (1 ^ CPU.IsCR(bb)); - CPU.SetCRBit2(bt, v & 0x1); + const u8 v = CPU.IsCR(crba) | (1 ^ CPU.IsCR(crbb)); + CPU.SetCRBit2(crbd, v & 0x1); } - void CROR(u32 bt, u32 ba, u32 bb) + void CROR(u32 crbd, u32 crba, u32 crbb) { - const u8 v = CPU.IsCR(ba) | CPU.IsCR(bb); - CPU.SetCRBit2(bt, v & 0x1); + const u8 v = CPU.IsCR(crba) | CPU.IsCR(crbb); + CPU.SetCRBit2(crbd, v & 0x1); } void BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) { @@ -2166,7 +2166,7 @@ private: void ANDI_(u32 ra, u32 rs, u32 uimm16) { CPU.GPR[ra] = CPU.GPR[rs] & uimm16; - CPU.UpdateCR0(CPU.GPR[ra]); + CPU.UpdateCR0(CPU.GPR[ra]); } void ANDIS_(u32 ra, u32 rs, u32 uimm16) { @@ -2934,7 +2934,7 @@ private: } void STFSX(u32 frs, u32 ra, u32 rb) { - Memory.Write32((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), PPCdouble(CPU.FPR[frs]).To32()); + Memory.Write32((ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]), CPU.FPR[frs].To32()); } void STVRX(u32 vs, u32 ra, u32 rb) { @@ -3147,12 +3147,12 @@ private: } void STFS(u32 frs, u32 ra, s32 d) { - Memory.Write32(ra ? CPU.GPR[ra] + d : d, PPCdouble(CPU.FPR[frs]).To32()); + Memory.Write32(ra ? CPU.GPR[ra] + d : d, CPU.FPR[frs].To32()); } void STFSU(u32 frs, u32 ra, s32 d) { const u64 addr = CPU.GPR[ra] + d; - Memory.Write32(addr, PPCdouble(CPU.FPR[frs]).To32()); + Memory.Write32(addr, CPU.FPR[frs].To32()); CPU.GPR[ra] = addr; } void STFD(u32 frs, u32 ra, s32 d) @@ -3213,32 +3213,32 @@ private: } } - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fdivs.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FSUBS(u32 frd, u32 fra, u32 frb, bool rc) { CPU.FPR[frd] = static_cast(CPU.FPR[fra] - CPU.FPR[frb]); - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fsubs.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FADDS(u32 frd, u32 fra, u32 frb, bool rc) { CPU.FPR[frd] = static_cast(CPU.FPR[fra] + CPU.FPR[frb]); - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fadds.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FSQRTS(u32 frd, u32 frb, bool rc) { CPU.FPR[frd] = static_cast(sqrt(CPU.FPR[frb])); - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fsqrts.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FRES(u32 frd, u32 frb, bool rc) { if(CPU.FPR[frb] == 0.0) CPU.SetFPSCRException(FPSCR_ZX); CPU.FPR[frd] = static_cast(1.0f/CPU.FPR[frb]); - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); CPU.FPSCR.FI = 0; CPU.FPSCR.FR = 0; if(rc) UNK("fres.");//CPU.UpdateCR1(CPU.FPR[frd]); @@ -3248,31 +3248,31 @@ private: CPU.FPR[frd] = static_cast(CPU.FPR[fra] * CPU.FPR[frc]); CPU.FPSCR.FI = 0; CPU.FPSCR.FR = 0; - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fmuls.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FMADDS(u32 frd, u32 fra, u32 frc, u32 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(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fmadds.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FMSUBS(u32 frd, u32 fra, u32 frc, u32 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(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fmsubs.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FNMSUBS(u32 frd, u32 fra, u32 frc, u32 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(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fnmsubs.");////CPU.UpdateCR1(CPU.FPR[frd]); } void FNMADDS(u32 frd, u32 fra, u32 frc, u32 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(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fnmadds.");//CPU.UpdateCR1(CPU.FPR[frd]); } void STD(u32 rs, u32 ra, s32 d) @@ -3290,6 +3290,7 @@ private: { u64 mask = (1ULL << crbd); CPU.FPSCR.FPSCR |= mask; + if(rc) UNIMPLEMENTED(); } void MCRFS(u32 crbd, u32 crbs) @@ -3302,11 +3303,12 @@ private: { u64 mask = (1ULL << crbd); CPU.FPSCR.FPSCR &= ~mask; + if(rc) UNIMPLEMENTED(); } void MTFSFI(u32 crfd, u32 i, bool rc) { - u64 mask = (1ULL << crfd); + u64 mask = (0x1ULL << crfd); if(i) { @@ -3316,6 +3318,7 @@ private: { CPU.FPSCR.FPSCR &= ~mask; } + if(rc) UNIMPLEMENTED(); } void MFFS(u32 frd, bool rc) @@ -3478,25 +3481,25 @@ private: } CPU.FPR[frd] = CPU.FPR[fra] / CPU.FPR[frb]; - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fdiv.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FSUB(u32 frd, u32 fra, u32 frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] - CPU.FPR[frb]; - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fsub.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FADD(u32 frd, u32 fra, u32 frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] + CPU.FPR[frb]; - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fadd.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FSQRT(u32 frd, u32 frb, bool rc) { CPU.FPR[frd] = sqrt(CPU.FPR[frb]); - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fsqrt.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FSEL(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) @@ -3525,19 +3528,19 @@ private: void FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { CPU.FPR[frd] = CPU.FPR[fra] * CPU.FPR[frc] + CPU.FPR[frb]; - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fmadd.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FNMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { CPU.FPR[frd] = -(CPU.FPR[fra] * CPU.FPR[frc] - CPU.FPR[frb]); - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fnmsub.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FNMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { CPU.FPR[frd] = -(CPU.FPR[fra] * CPU.FPR[frc] + CPU.FPR[frb]); - CPU.FPSCR.FPRF = PPCdouble(CPU.FPR[frd]).GetType(); + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fnmadd.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FCMPO(u32 crfd, u32 fra, u32 frb) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index cc0b7eecfb..e3cbffe359 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -3,14 +3,14 @@ #include "Emu/Cell/PPUDecoder.h" #include "Emu/Cell/PPUInterpreter.h" #include "Emu/Cell/PPUDisAsm.h" - +#include extern gcmInfo gcm_info; PPUThread& GetCurrentPPUThread() { PPCThread* thread = GetCurrentPPCThread(); - if(!thread || thread->IsSPU()) throw wxString("GetCurrentPPUThread: bad thread"); + if(!thread || thread->GetType() != PPC_THREAD_PPU) throw wxString("GetCurrentPPUThread: bad thread"); return *(PPUThread*)thread; } @@ -68,7 +68,7 @@ void PPUThread::InitRegs() SetPc(pc); - const s32 thread_num = Emu.GetCPU().GetThreadNumById(!IsSPU(), GetId()); + const s32 thread_num = Emu.GetCPU().GetThreadNumById(GetType(), GetId()); if(thread_num < 0) { diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp new file mode 100644 index 0000000000..e10f97d6dc --- /dev/null +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -0,0 +1,151 @@ +#include "stdafx.h" +#include "Emu/Cell/RawSPUThread.h" + +RawSPUThread::RawSPUThread(PPCThreadType type) : SPUThread(type) +{ + Reset(); +} + +RawSPUThread::~RawSPUThread() +{ +} + +void RawSPUThread::InitRegs() +{ + GPR[3]._u64[1] = m_args[0]; + GPR[4]._u64[1] = m_args[1]; + GPR[5]._u64[1] = m_args[2]; + GPR[6]._u64[1] = m_args[3]; + + u32 num = Emu.GetCPU().GetThreadNumById(GetType(), GetId()); + + m_offset = RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * num + RAW_SPU_LS_OFFSET; + MFC_LSA.SetAddr(GetRawSPURegAddrByNum(num, MFC_LSA_offs)); + MFC_EAH.SetAddr(GetRawSPURegAddrByNum(num, MFC_EAH_offs)); + MFC_EAL.SetAddr(GetRawSPURegAddrByNum(num, MFC_EAL_offs)); + MFC_Size_Tag.SetAddr(GetRawSPURegAddrByNum(num, MFC_Size_Tag_offs)); + MFC_CMDStatus.SetAddr(GetRawSPURegAddrByNum(num, MFC_CMDStatus_offs)); + MFC_QStatus.SetAddr(GetRawSPURegAddrByNum(num, MFC_QStatus_offs)); + Prxy_QueryType.SetAddr(GetRawSPURegAddrByNum(num, Prxy_QueryType_offs)); + Prxy_QueryMask.SetAddr(GetRawSPURegAddrByNum(num, Prxy_QueryMask_offs)); + Prxy_TagStatus.SetAddr(GetRawSPURegAddrByNum(num, Prxy_TagStatus_offs)); + SPU_Out_MBox.SetAddr(GetRawSPURegAddrByNum(num, SPU_Out_MBox_offs)); + SPU_In_MBox.SetAddr(GetRawSPURegAddrByNum(num, SPU_In_MBox_offs)); + SPU_MBox_Status.SetAddr(GetRawSPURegAddrByNum(num, SPU_MBox_Status_offs)); + SPU_RunCntl.SetAddr(GetRawSPURegAddrByNum(num, SPU_RunCntl_offs)); + SPU_Status.SetAddr(GetRawSPURegAddrByNum(num, SPU_Status_offs)); + SPU_NPC.SetAddr(GetRawSPURegAddrByNum(num, SPU_NPC_offs)); + SPU_RdSigNotify1.SetAddr(GetRawSPURegAddrByNum(num, SPU_RdSigNotify1_offs)); + SPU_RdSigNotify2.SetAddr(GetRawSPURegAddrByNum(num, SPU_RdSigNotify2_offs)); + + SPU_RunCntl.SetValue(SPU_RUNCNTL_STOP); + SPU_Status.SetValue(SPU_STATUS_RUNNING); + Prxy_QueryType.SetValue(0); + MFC_CMDStatus.SetValue(0); + PC = SPU_NPC.GetValue(); +} + +void RawSPUThread::Task() +{ + ConLog.Write("%s enter", PPCThread::GetFName()); + + const Array& bp = Emu.GetBreakPoints(); + + try + { + for(uint i=0; i> 16; + ConLog.Warning("RawSPU DMA GET:"); + ConLog.Warning("*** lsa = 0x%x", lsa); + ConLog.Warning("*** ea = 0x%llx", ea); + ConLog.Warning("*** tag = 0x%x", tag); + ConLog.Warning("*** size = 0x%x", size); + ConLog.SkipLn(); + memcpy(Memory + m_offset + lsa, Memory + ea, size); + } + + if(Prxy_QueryType.GetValue() == 2) + { + Prxy_QueryType.SetValue(0); + u32 mask = Prxy_QueryMask.GetValue(); + // + MFC_QStatus.SetValue(mask); + } + + if(SPU_RunCntl.GetValue() != SPU_RUNCNTL_RUNNABLE) + { + if(!is_last_paused) + { + if(is_last_paused = SPU_RunCntl.GetValue() != SPU_RUNCNTL_RUNNABLE) + { + SPU_NPC.SetValue(PC); + SPU_Status.SetValue(SPU_STATUS_WAITING_FOR_CHANNEL); + } + } + + Sleep(1); + continue; + } + + if(is_last_paused) + { + is_last_paused = false; + PC = SPU_NPC.GetValue(); + SPU_Status.SetValue(SPU_STATUS_RUNNING); + } + + DoCode(Memory.Read32(m_offset + PC)); + NextPc(); + + for(uint i=0; iIsSPU()) throw wxString("GetCurrentSPUThread: bad thread"); + if(!thread || thread->GetType() == PPC_THREAD_PPU) throw wxString("GetCurrentSPUThread: bad thread"); return *(SPUThread*)thread; } -SPUThread::SPUThread() : PPCThread(PPC_THREAD_SPU) +SPUThread::SPUThread(PPCThreadType type) : PPCThread(type) { Reset(); } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index e713e5c8e0..c311dfef28 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -81,6 +81,22 @@ enum MFCchannels MFC_RdAtomicStat = 27, //Read completion status of last completed immediate MFC atomic update command }; +enum +{ + SPU_RUNCNTL_STOP = 0, + SPU_RUNCNTL_RUNNABLE = 1, +}; + +enum +{ + SPU_STATUS_STOPPED = 0x0, + SPU_STATUS_RUNNING = 0x1, + SPU_STATUS_STOPPED_BY_STOP = 0x2, + SPU_STATUS_STOPPED_BY_HALT = 0x4, + SPU_STATUS_WAITING_FOR_CHANNEL = 0x8, + SPU_STATUS_SINGLE_STEP = 0x10, +}; + union SPU_GPR_hdr { u128 _u128; @@ -111,10 +127,91 @@ class SPUThread : public PPCThread { public: SPU_GPR_hdr GPR[128]; //General-Purpose Register - SizedStack OutMbox; - SizedStack OutIntrMbox; - SizedStack InMbox; + template + class SPUReg + { + u64 m_addr; + u32 m_pos; + + public: + static const size_t max_count = _max_count; + static const size_t size = max_count * 4; + + SPUReg() + { + Init(); + } + + void Init() + { + m_pos = 0; + } + + void SetAddr(u64 addr) + { + m_addr = addr; + } + + u64 GetAddr() const + { + return m_addr; + } + + __forceinline bool Pop(u32& res) + { + if(!m_pos) return false; + res = Memory.Read32(m_addr + m_pos--); + return true; + } + + __forceinline bool Push(u32 value) + { + if(m_pos >= max_count) return false; + Memory.Write32(m_addr + m_pos++, value); + return true; + } + + u32 GetCount() const + { + return m_pos; + } + + u32 GetFreeCount() const + { + return max_count - m_pos; + } + + void SetValue(u32 value) + { + Memory.Write32(m_addr, value); + } + + u32 GetValue() const + { + return Memory.Read32(m_addr); + } + }; + + SPUReg<1> MFC_LSA; + SPUReg<1> MFC_EAH; + SPUReg<1> MFC_EAL; + SPUReg<1> MFC_Size_Tag; + SPUReg<1> MFC_CMDStatus; + SPUReg<1> MFC_QStatus; + SPUReg<1> Prxy_QueryType; + SPUReg<1> Prxy_QueryMask; + SPUReg<1> Prxy_TagStatus; + SPUReg<1> SPU_Out_MBox; + SPUReg<4> SPU_In_MBox; + SPUReg<1> SPU_MBox_Status; + SPUReg<1> SPU_RunCntl; + SPUReg<1> SPU_Status; + SPUReg<1> SPU_NPC; + SPUReg<1> SPU_RdSigNotify1; + SPUReg<1> SPU_RdSigNotify2; + + SizedStack SPU_OutIntr_Mbox; u32 LSA; union @@ -128,13 +225,13 @@ public: switch(ch) { case SPU_WrOutMbox: - return OutMbox.GetFreeCount(); + return SPU_Out_MBox.GetFreeCount(); case SPU_RdInMbox: - return InMbox.GetCount(); + return SPU_In_MBox.GetCount(); case SPU_WrOutIntrMbox: - return 0;//return OutIntrMbox.GetFreeCount(); + return 0;//return SPU_OutIntr_Mbox.GetFreeCount(); default: ConLog.Error("%s error: unknown/illegal channel (%d).", __FUNCTION__, ch); @@ -152,7 +249,7 @@ public: { case SPU_WrOutIntrMbox: ConLog.Warning("SPU_WrOutIntrMbox = 0x%x", v); - if(!OutIntrMbox.Push(v)) + if(!SPU_OutIntr_Mbox.Push(v)) { ConLog.Warning("Not enought free rooms."); } @@ -160,10 +257,11 @@ public: case SPU_WrOutMbox: ConLog.Warning("SPU_WrOutMbox = 0x%x", v); - if(!OutMbox.Push(v)) + if(!SPU_Out_MBox.Push(v)) { ConLog.Warning("Not enought free rooms."); } + SPU_Status.SetValue((SPU_Status.GetValue() & ~0xff) | 1); break; default: @@ -180,7 +278,8 @@ public: switch(ch) { case SPU_RdInMbox: - if(!InMbox.Pop(v)) v = 0; + if(!SPU_In_MBox.Pop(v)) v = 0; + SPU_Status.SetValue((SPU_Status.GetValue() & ~0xff00) | (SPU_In_MBox.GetCount() << 8)); break; default: @@ -203,7 +302,7 @@ public: void WriteLS128(const u32 lsa, const u128& data) const { Memory.Write128(lsa + m_offset, data); } public: - SPUThread(); + SPUThread(PPCThreadType type = PPC_THREAD_SPU); ~SPUThread(); virtual wxString RegsToString() @@ -224,7 +323,7 @@ protected: virtual void DoResume(); virtual void DoStop(); -private: +protected: virtual void DoCode(const s32 code); }; diff --git a/rpcs3/Emu/GS/GL/GLGSRender.cpp b/rpcs3/Emu/GS/GL/GLGSRender.cpp index c2da9042b6..a2da8e5a7b 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.cpp +++ b/rpcs3/Emu/GS/GL/GLGSRender.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "GLGSRender.h" - -#define CMD_DEBUG 0 +#include "Emu/Cell/PPCInstrTable.h" +#define CMD_DEBUG 1 #define DUMP_VERTEX_DATA 1 #if CMD_DEBUG @@ -132,7 +132,9 @@ void GLRSXThread::Task() { wxCriticalSectionLocker lock(p.m_cs_main); - if(p.m_ctrl->get == p.m_ctrl->put || !Emu.IsRunned()) + const u32 get = re(p.m_ctrl->get); + const u32 put = re(p.m_ctrl->put); + if(put <= get || !Emu.IsRunned()) { SemaphorePostAndWait(p.m_sem_flush); @@ -170,23 +172,25 @@ void GLRSXThread::Task() continue; } - const u32 get = re(p.m_ctrl->get); + ConLog.Write("addr = 0x%x", p.m_ioAddress + get); const u32 cmd = Memory.Read32(p.m_ioAddress + get); const u32 count = (cmd >> 18) & 0x7ff; - + //if(cmd == 0) continue; if(cmd & CELL_GCM_METHOD_FLAG_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); + addr &= ~0x1000; + //0x30101000 + 0x80bf000 = 0x80be000 + ConLog.Warning("rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", addr, p.m_ioAddress + get, cmd, get, put); + re(p.m_ctrl->get, addr); continue; } if(cmd & CELL_GCM_METHOD_FLAG_CALL) { call_stack.Push(get + 4); u32 addr = cmd & ~CELL_GCM_METHOD_FLAG_CALL; + ConLog.Warning("rsx call(0x%x) #0x%x - 0x%x", addr, cmd, get); p.m_ctrl->get = re32(addr); - ConLog.Warning("rsx call(0x%x)", addr); continue; } if(cmd & CELL_GCM_METHOD_FLAG_RETURN) @@ -606,6 +610,14 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c //Enable(args[0] ? true : false, GL_DEPTH_CLAMP); break; + case NV4097_SET_ALPHA_FUNC: + glAlphaFunc(args[0], args[1]); + break; + + case NV4097_SET_CULL_FACE: + glCullFace(args[0]); + break; + case NV4097_SET_VIEWPORT_VERTICAL: { m_set_viewport_vertical = true; @@ -1134,7 +1146,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c for(u32 i=0; i= GetStartAddr() && addr < GetEndAddr(); + return mem && addr >= GetStartAddr() && addr < GetEndAddr(); } __forceinline const u8 MemoryBlock::FastRead8(const u64 addr) const @@ -466,24 +466,52 @@ u8* DynamicMemoryBlock::GetMem(u64 addr) const return nullptr; } +#define DEBUG_RAWSPU_MEM 1 //MemoryBase void MemoryBase::Write8(u64 addr, const u8 data) { +#if DEBUG_RAWSPU_MEM + if(SpuRawMem.IsMyAddress(addr)) + { + ConLog.Warning("Write8(addr=0x%llx,data=0x%x)", addr, data); + } +#endif GetMemByAddr(addr).Write8(addr, data); } void MemoryBase::Write16(u64 addr, const u16 data) { +#if DEBUG_RAWSPU_MEM + if(SpuRawMem.IsMyAddress(addr)) + { + ConLog.Warning("Write16(addr=0x%llx,data=0x%x)", addr, data); + } +#endif + GetMemByAddr(addr).Write16(addr, data); } void MemoryBase::Write32(u64 addr, const u32 data) { +#if DEBUG_RAWSPU_MEM + if(SpuRawMem.IsMyAddress(addr)) + { + ConLog.Warning("Write32(addr=0x%llx,data=0x%x)", addr, data); + } +#endif + GetMemByAddr(addr).Write32(addr, data); } void MemoryBase::Write64(u64 addr, const u64 data) { +#if DEBUG_RAWSPU_MEM + if(SpuRawMem.IsMyAddress(addr)) + { + ConLog.Warning("Write64(addr=0x%llx,data=0x%llx)", addr, data); + } +#endif + GetMemByAddr(addr).Write64(addr, data); } @@ -529,8 +557,13 @@ bool MemoryBase::Write128NN(u64 addr, const u128 data) u8 MemoryBase::Read8(u64 addr) { - if(enable_log && addr >= 0xd0010a84) - ConLog.Warning("Read8 from block: [%08llx]", addr); +#if DEBUG_RAWSPU_MEM + if(SpuRawMem.IsMyAddress(addr)) + { + ConLog.Warning("Read8(addr=0x%llx)", addr); + } +#endif + MemoryBlock& mem = GetMemByAddr(addr); if(mem.IsNULL()) { @@ -542,8 +575,13 @@ u8 MemoryBase::Read8(u64 addr) u16 MemoryBase::Read16(u64 addr) { - if(enable_log && addr >= 0xd0010a84) - ConLog.Warning("Read16 from block: [%08llx]", addr); +#if DEBUG_RAWSPU_MEM + if(SpuRawMem.IsMyAddress(addr)) + { + ConLog.Warning("Read16(addr=0x%llx)", addr); + } +#endif + MemoryBlock& mem = GetMemByAddr(addr); if(mem.IsNULL()) { @@ -555,8 +593,13 @@ u16 MemoryBase::Read16(u64 addr) u32 MemoryBase::Read32(u64 addr) { - if(enable_log && addr >= 0xd0010a84) - ConLog.Warning("Read32 from block: [%08llx]", addr); +#if DEBUG_RAWSPU_MEM + if(SpuRawMem.IsMyAddress(addr) && !(GetCurrentPPCThread() && GetCurrentPPCThread()->GetType() == PPC_THREAD_RAW_SPU)) + { + ConLog.Warning("Read32(addr=0x%llx)", addr); + } +#endif + MemoryBlock& mem = GetMemByAddr(addr); if(mem.IsNULL()) { @@ -568,8 +611,13 @@ u32 MemoryBase::Read32(u64 addr) u64 MemoryBase::Read64(u64 addr) { - if(enable_log && addr >= 0xd0010a84) - ConLog.Warning("Read64 from block: [%08llx]", addr); +#if DEBUG_RAWSPU_MEM + if(SpuRawMem.IsMyAddress(addr)) + { + ConLog.Warning("Read64(addr=0x%llx)", addr); + } +#endif + MemoryBlock& mem = GetMemByAddr(addr); if(mem.IsNULL()) { diff --git a/rpcs3/Emu/Memory/Memory.h b/rpcs3/Emu/Memory/Memory.h index 5fdcaa0b8f..0d56b45863 100644 --- a/rpcs3/Emu/Memory/Memory.h +++ b/rpcs3/Emu/Memory/Memory.h @@ -63,16 +63,16 @@ public: */ } - 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); }; + template static __forceinline u64 ReverseData(u64 val); + template<> static __forceinline u64 ReverseData<1>(u64 val) { return val; } + template<> static __forceinline u64 ReverseData<2>(u64 val) { return Reverse16(val); } + template<> static __forceinline u64 ReverseData<4>(u64 val) { return Reverse32(val); } + template<> static __forceinline u64 ReverseData<8>(u64 val) { return Reverse64(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); }; + template static __forceinline T Reverse(T val) + { + return (T)ReverseData(val); + }; MemoryBlock& GetMemByNum(const u8 num) { @@ -328,6 +328,31 @@ public: return PRXMem.Free(addr); } + bool Map(const u64 dst_addr, const u64 src_addr, const u32 size) + { + if(IsGoodAddr(dst_addr) || !IsGoodAddr(src_addr)) + { + return false; + } + + MemoryBlocks.Add((new MemoryMirror())->SetRange(GetMemFromAddr(src_addr), dst_addr, size)); + return true; + } + + bool Unmap(const u64 addr) + { + for(uint i=0; iWait(new_thread); } Callback2::Callback2(u32 slot, u64 addr, u64 userdata) : Callback(slot, addr) diff --git a/rpcs3/Emu/SysCalls/Modules.cpp b/rpcs3/Emu/SysCalls/Modules.cpp index de9bc167af..a32b6d4ac4 100644 --- a/rpcs3/Emu/SysCalls/Modules.cpp +++ b/rpcs3/Emu/SysCalls/Modules.cpp @@ -2,10 +2,131 @@ #include "SysCalls.h" #include "SC_FUNC.h" -Module* g_modules[50]; -uint g_modules_count = 0; +Module* g_modules[3][0xff] = {0}; +uint g_max_module_id = 0; +uint g_module_2_count = 0; ArrayF g_modules_funcs_list; +struct ModuleInfo +{ + u32 id; + char* name; +} +static const g_module_list[] = +{ + {0x0000, "sys_net"}, + {0x0001, "sys_http"}, + {0x0002, "cellHttpUtil"}, + {0x0003, "cellSsl"}, + {0x0004, "cellHttps"}, + {0x0005, "cellVdec"}, + {0x0006, "cellAdec"}, + {0x0007, "cellDmux"}, + {0x0008, "cellVpost"}, + {0x0009, "cellRtc"}, + {0x000a, "cellSpurs"}, + {0x000b, "cellOvis"}, + {0x000c, "cellSheap"}, + {0x000d, "sys_sync"}, + {0x000e, "sys_fs"}, + {0x000f, "sys_jpgdec"}, + {0x0010, "cellGcmSys"}, + {0x0011, "cellAudio"}, + {0x0012, "cellPamf"}, + {0x0013, "cellAtrac"}, + {0x0014, "cellNetCtl"}, + {0x0015, "cellSysutil"}, + {0x0016, "sceNp"}, + {0x0017, "sys_io"}, + {0x0018, "cellPngDec"}, + {0x0019, "cellFont"}, + {0x001a, "cellFontFt"}, + {0x001b, "cellFreetype"}, + {0x001c, "cellUsbd"}, + {0x001d, "cellSail"}, + {0x001e, "cellL10n"}, + {0x001f, "cellResc"}, + {0x0020, "cellDaisy"}, + {0x0021, "cellKey2char"}, + {0x0022, "cellMic"}, + {0x0023, "cellCamera"}, + {0x0024, "cellVdecMpeg2"}, + {0x0025, "cellVdecAvc"}, + {0x0026, "cellAdecLpcm"}, + {0x0027, "cellAdecAc3"}, + {0x0028, "cellAdecAtx"}, + {0x0029, "cellAdecAt3"}, + {0x002a, "cellDmuxPamf"}, + {0x002e, "cellLv2dbg"}, + {0x0030, "cellUsbpspcm"}, + {0x0031, "cellAvconfExt"}, + {0x0032, "cellSysutilUserinfo"}, + {0x0033, "cellSysutilSavedata"}, + {0x0034, "cellSubdisplay"}, + {0x0035, "cellSysutilRec"}, + {0x0036, "cellVideoExport"}, + {0x0037, "cellSysutilGameExec"}, + {0x0038, "sceNp2"}, + {0x0039, "cellSysutilAp"}, + {0x003a, "cellSysutilNpClans"}, + {0x003b, "cellSysutilOskExt"}, + {0x003c, "cellVdecDivx"}, + {0x003d, "cellJpgEnc"}, + {0x003e, "cellSysutilGame"}, + {0x003f, "cellBgdl"}, + {0x0040, "cellFreetypeTT"}, + {0x0041, "cellSysutilVideoUpload"}, + {0x0042, "cellSysutilSysconfExt"}, + {0x0043, "cellFiber"}, + {0x0044, "cellSysutilNpCommerce2"}, + {0x0045, "cellSysutilNpTus"}, + {0x0046, "cellVoice"}, + {0x0047, "cellAdecCelp8"}, + {0x0048, "cellCelp8Enc"}, + {0x0049, "cellSysutilLicenseArea"}, + {0x004a, "cellSysutilMusic2"}, + {0x004e, "cellSysutilScreenshot"}, + {0x004f, "cellSysutilMusicDecode"}, + {0x0050, "cellSysutilSpursJq"}, + {0x0052, "cellPngEnc"}, + {0x0053, "cellSysutilMusicDecode2"}, + {0x0055, "cellSync2"}, + {0x0056, "cellSysutilNpUtil"}, + {0x0057, "cellRudp"}, + {0x0059, "cellSysutilNpSns"}, + {0x005a, "cellGem"}, + {0xf00a, "cellScelpEnc"}, + {0xf010, "cellGifDec"}, + {0xf019, "cellAdecCelp"}, + {0xf01b, "cellAdecM2bc"}, + {0xf01d, "cellAdecM4aac"}, + {0xf01e, "cellAdecMp3"}, + {0xf023, "cellImejp"}, + {0xf028, "cellSysutilMusic"}, + {0xf029, "cellPhotoExport"}, + {0xf02a, "cellPrint"}, + {0xf02b, "cellPhotoImport"}, + {0xf02c, "cellMusicExport"}, + {0xf02e, "cellPhotoDecode"}, + {0xf02f, "cellSysutilSearch"}, + {0xf030, "cellSysutilAvchat2"}, + {0xf034, "cellSailRec"}, + {0xf035, "sceNpTrophy"}, + {0xf053, "cellAdecAt3multi"}, + {0xf054, "cellLibatrac3multi"}, +}; + +struct _InitNullModules +{ + _InitNullModules() + { + for(auto& m : g_module_list) + { + new Module(m.id, m.name); + } + } +} InitNullModules; + bool IsLoadedFunc(u32 id) { for(u32 i=0; iSetLoaded(false); + if(g_modules[0][i]) + { + g_modules[0][i]->SetLoaded(false); + } + + if(g_modules[1][i]) + { + g_modules[1][i]->SetLoaded(false); + } + + if(g_modules[2][i]) + { + g_modules[2][i]->SetLoaded(false); + } } g_modules_funcs_list.Clear(); @@ -61,11 +195,21 @@ void UnloadModules() Module* GetModuleByName(const wxString& name) { - for(u32 i=0; iGetName().Cmp(name) == 0) + if(g_modules[0][i] && g_modules[0][i]->GetName().Cmp(name) == 0) { - return g_modules[i]; + return g_modules[0][i]; + } + + if(g_modules[1][i] && g_modules[1][i]->GetName().Cmp(name) == 0) + { + return g_modules[1][i]; + } + + if(g_modules[2][i] && g_modules[2][i]->GetName().Cmp(name) == 0) + { + return g_modules[2][i]; } } @@ -74,24 +218,92 @@ Module* GetModuleByName(const wxString& name) Module* GetModuleById(u16 id) { - for(u32 i=0; iGetID() == id) + if(g_modules[0][i] && g_modules[0][i]->GetID() == id) { - return g_modules[i]; + return g_modules[0][i]; + } + + if(g_modules[1][i] && g_modules[1][i]->GetID() == id) + { + return g_modules[1][i]; } } return nullptr; } -Module::Module(const char* name, u16 id, void (*init)()) +void SetModule(int id, Module* module, bool with_data) +{ + if(id != 0xffff) + { + if(u16((u8)id + 1) > g_max_module_id) + { + g_max_module_id = u16((u8)id + 1); + } + + int index; + switch(id >> 8) + { + case 0x00: index = 0; break; + case 0xf0: index = 1; break; + default: assert(0); return; + } + + if(g_modules[index][(u8)id]) + { + if(with_data) + { + module->SetName(g_modules[index][(u8)id]->GetName()); + delete g_modules[index][(u8)id]; + g_modules[index][(u8)id] = module; + } + else + { + g_modules[index][(u8)id]->SetName(module->GetName()); + delete module; + } + } + else + { + g_modules[index][(u8)id] = module; + } + } + else + { + g_modules[2][g_module_2_count++] = module; + + if(g_module_2_count > g_max_module_id) + { + g_max_module_id = g_module_2_count; + } + } +} + +Module::Module(u16 id, const char* name) : m_is_loaded(false) , m_name(name) , m_id(id) { - g_modules[g_modules_count++] = this; - init(); + SetModule(m_id, this, false); +} + +Module::Module(const char* name, void (*init)()) + : m_is_loaded(false) + , m_name(name) + , m_id(-1) +{ + SetModule(m_id, this, init != nullptr); + if(init) init(); +} + +Module::Module(u16 id, void (*init)()) + : m_is_loaded(false) + , m_id(id) +{ + SetModule(m_id, this, init != nullptr); + if(init) init(); } void Module::Load() @@ -154,6 +366,11 @@ wxString Module::GetName() const return m_name; } +void Module::SetName(const wxString& name) +{ + m_name = name; +} + void Module::Log(const u32 id, wxString fmt, ...) { if(enable_log) diff --git a/rpcs3/Emu/SysCalls/Modules.h b/rpcs3/Emu/SysCalls/Modules.h index 38143bdd68..d3678e4ea0 100644 --- a/rpcs3/Emu/SysCalls/Modules.h +++ b/rpcs3/Emu/SysCalls/Modules.h @@ -25,14 +25,16 @@ struct ModuleFunc class Module { - const char* m_name; + wxString m_name; const u16 m_id; bool m_is_loaded; public: Array m_funcs_list; - Module(const char* name, u16 id, void (*init)()); + Module(u16 id, const char* name); + Module(const char* name, void (*init)()); + Module(u16 id, void (*init)()); void Load(); void UnLoad(); @@ -44,6 +46,7 @@ public: u16 GetID() const; wxString GetName() const; + void SetName(const wxString& name); public: void Log(const u32 id, wxString fmt, ...); diff --git a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp index c367afe2df..ae7eb0746e 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp @@ -3,7 +3,7 @@ #include "Emu/SysCalls/SC_FUNC.h" void cellGcmSys_init(); -Module cellGcmSys("cellGcmSys", 0x0010, cellGcmSys_init); +Module cellGcmSys(0x0010, cellGcmSys_init); s64 cellGcmFunc15() { diff --git a/rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp b/rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp new file mode 100644 index 0000000000..fb7bb2e3c7 --- /dev/null +++ b/rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp @@ -0,0 +1,95 @@ +#include "stdafx.h" +#include "Emu/SysCalls/Modules.h" +#include "Emu/SysCalls/SC_FUNC.h" + +void cellSysmodule_init(); +Module cellSysmodule("cellSysmodule", cellSysmodule_init); + +enum +{ + CELL_SYSMODULE_LOADED = CELL_OK, + CELL_SYSMODULE_ERROR_DUPLICATED = 0x80012001, + CELL_SYSMODULE_ERROR_UNKNOWN = 0x80012002, + CELL_SYSMODULE_ERROR_UNLOADED = 0x80012003, + CELL_SYSMODULE_ERROR_INVALID_MEMCONTAINER = 0x80012004, + CELL_SYSMODULE_ERROR_FATAL = 0x800120ff, +}; + +int cellSysmoduleInitialize() +{ + cellSysmodule.Log("cellSysmoduleInitialize()"); + return CELL_OK; +} + +int cellSysmoduleFinalize() +{ + cellSysmodule.Log("cellSysmoduleFinalize()"); + return CELL_OK; +} + +int cellSysmoduleSetMemcontainer(u32 ct_id) +{ + cellSysmodule.Warning("TODO: cellSysmoduleSetMemcontainer(ct_id=0x%x)", ct_id); + return CELL_OK; +} + +int cellSysmoduleLoadModule(u16 id) +{ + cellSysmodule.Warning("cellSysmoduleLoadModule(id=0x%04x)", id); + Module* m = GetModuleById(id); + + if(!m) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + if(m->IsLoaded()) + { + return CELL_SYSMODULE_ERROR_DUPLICATED; + } + + m->Load(); + return CELL_OK; +} + +int cellSysmoduleUnloadModule(u16 id) +{ + cellSysmodule.Warning("cellSysmoduleUnloadModule(id=0x%04x)", id); + Module* m = GetModuleById(id); + + if(!m) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + if(!m->IsLoaded()) + { + return CELL_SYSMODULE_ERROR_UNLOADED; + } + + m->UnLoad(); + return CELL_OK; +} + +int cellSysmoduleIsLoaded(u16 id) +{ + cellSysmodule.Warning("cellSysmoduleIsLoaded(id=0x%04x)", id); + Module* m = GetModuleById(id); + + if(!m) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + return m->IsLoaded() ? CELL_SYSMODULE_LOADED : CELL_SYSMODULE_ERROR_UNLOADED; +} + +void cellSysmodule_init() +{ + cellSysmodule.AddFunc(0x63ff6ff9, bind_func(cellSysmoduleInitialize)); + cellSysmodule.AddFunc(0x96c07adf, bind_func(cellSysmoduleFinalize)); + cellSysmodule.AddFunc(0xa193143c, bind_func(cellSysmoduleSetMemcontainer)); + cellSysmodule.AddFunc(0x32267a31, bind_func(cellSysmoduleLoadModule)); + cellSysmodule.AddFunc(0x112a5ee9, bind_func(cellSysmoduleUnloadModule)); + cellSysmodule.AddFunc(0x5a59e258, bind_func(cellSysmoduleIsLoaded)); +} diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp index 512de006aa..42a0f50e17 100644 --- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp @@ -3,7 +3,7 @@ #include "Emu/SysCalls/SC_FUNC.h" void sysPrxForUser_init(); -Module sysPrxForUser("sysPrxForUser", -1, sysPrxForUser_init); +Module sysPrxForUser("sysPrxForUser", sysPrxForUser_init); void sys_initialize_tls() { diff --git a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp index c73d87ebdd..56bd3ccc9c 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp @@ -3,7 +3,7 @@ #include "Emu/SysCalls/SC_FUNC.h" void sys_fs_init(); -Module sys_fs("sys_fs", 0x000e, sys_fs_init); +Module sys_fs(0x000e, sys_fs_init); void sys_fs_init() { diff --git a/rpcs3/Emu/SysCalls/Modules/sys_io.cpp b/rpcs3/Emu/SysCalls/Modules/sys_io.cpp index c4654036c0..f01c950d8d 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_io.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_io.cpp @@ -3,7 +3,7 @@ #include "Emu/SysCalls/SC_FUNC.h" void sys_io_init(); -Module sys_io("sys_io", 0x0017, sys_io_init); +Module sys_io(0x0017, sys_io_init); void sys_io_init() { diff --git a/rpcs3/Emu/SysCalls/SysCalls.cpp b/rpcs3/Emu/SysCalls/SysCalls.cpp index bdb3078aed..6adafdddf0 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.cpp +++ b/rpcs3/Emu/SysCalls/SysCalls.cpp @@ -247,7 +247,7 @@ void SysCalls::DoSyscall(u32 code) //timer case 141: case 142: - Sleep(SC_ARGS_1 / (1000 * 1000)); + std::this_thread::sleep_for(std::chrono::nanoseconds(SC_ARGS_1)); RESULT(0); return; diff --git a/rpcs3/Emu/SysCalls/SysCalls.h b/rpcs3/Emu/SysCalls/SysCalls.h index 2534b5e97d..554f8992e8 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.h +++ b/rpcs3/Emu/SysCalls/SysCalls.h @@ -222,7 +222,7 @@ 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 cellGcmGetLabelAddress(u8 index); extern u32 cellGcmGetControlRegister(); 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); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp index 16a405c0b7..fe0cf1de46 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp @@ -44,14 +44,13 @@ int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout) return CELL_ESRCH; } - PPCThread* thr = GetCurrentPPCThread(); - - while(true) + int result; + auto queue_receive = [&](int status) -> bool { - int status = thr->ThreadStatus(); if(status == PPCThread_Stopped) { - return CELL_ECANCELED; + result = CELL_ECANCELED; + return false; } EventQueue* equeue = (EventQueue*)Emu.GetIdManager().GetIDData(equeue_id).m_data; @@ -60,11 +59,11 @@ int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout) if(!equeue->ports[i]->has_data && equeue->ports[i]->thread) { SPUThread* thr = (SPUThread*)equeue->ports[i]->thread; - if(thr->OutIntrMbox.GetCount()) + if(thr->SPU_OutIntr_Mbox.GetCount()) { u32 val; - thr->OutIntrMbox.Pop(val); - if(!thr->OutMbox.Pop(val)) val = 0; + thr->SPU_OutIntr_Mbox.Pop(val); + if(!thr->SPU_Out_MBox.Pop(val)) val = 0; equeue->ports[i]->data1 = val; equeue->ports[i]->data2 = 0; equeue->ports[i]->data3 = 0; @@ -73,29 +72,28 @@ int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout) } } - bool has_data = false; for(int i=0; ipos; i++) { if(equeue->ports[i]->has_data) { - has_data = true; - auto res = (sys_event_data&)Memory[event_addr]; + auto dst = (sys_event_data&)Memory[event_addr]; - re(res.source, equeue->ports[i]->name); - re(res.data1, equeue->ports[i]->data1); - re(res.data2, equeue->ports[i]->data2); - re(res.data3, equeue->ports[i]->data3); + re(dst.source, equeue->ports[i]->name); + re(dst.data1, equeue->ports[i]->data1); + re(dst.data2, equeue->ports[i]->data2); + re(dst.data3, equeue->ports[i]->data3); equeue->ports[i]->has_data = false; - break; + + result = CELL_OK; + return false; } } - if(has_data) - break; + return true; + }; - Sleep(1); - } + GetCurrentPPUThread().WaitFor(queue_receive); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp b/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp index 55e88107ef..ae886b8474 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp @@ -199,9 +199,14 @@ int cellFsWrite(u32 fd, u32 buf_addr, u64 nbytes, u32 nwrite_addr) ID id; if(!sys_fs.CheckId(fd, id)) return CELL_ESRCH; vfsStream& file = *(vfsStream*)id.m_data; + if(Memory.IsGoodAddr(buf_addr) && !Memory.IsGoodAddr(buf_addr, nbytes)) + { + MemoryBlock& block = Memory.GetMemByAddr(buf_addr); + nbytes = block.GetSize() - (buf_addr - block.GetStartAddr()); + } - Memory.Write64NN(nwrite_addr, file.Write(Memory.GetMemFromAddr(buf_addr), nbytes)); - + int count = nbytes ? file.Write(Memory.GetMemFromAddr(buf_addr), nbytes) : 0; + Memory.Write64NN(nwrite_addr, count); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp index c6d0e5e3fc..fb2461e7c0 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp @@ -133,9 +133,9 @@ int cellGcmSetDisplayBuffer(u32 id, u32 offset, u32 pitch, u32 width, u32 height return CELL_OK; } -u32 cellGcmGetLabelAddress(u32 index) +u32 cellGcmGetLabelAddress(u8 index) { - cellGcmSys.Log("cellGcmGetLabelAddress(index=%d)", index); + cellGcmSys.Error("cellGcmGetLabelAddress(index=%d)", index); return Memory.RSXCMDMem.GetStartAddr() + 0x10 * index; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp index 44981568ac..e7e2c41fe3 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp @@ -162,15 +162,51 @@ enum SYS_MEMORY_PAGE_SIZE_64K = 0x200, }; +struct MemoryContainerInfo +{ + u64 addr; + u32 size; + + MemoryContainerInfo(u64 addr, u32 size) + : addr(addr) + , size(size) + { + } +}; + int sys_memory_container_create(u32 cid_addr, u32 yield_size) { - sc_mem.Warning("TODO: sys_memory_container_create(cid_addr=0x%x,yield_size=0x%x)", cid_addr, yield_size); + sc_mem.Warning("sys_memory_container_create(cid_addr=0x%x,yield_size=0x%x)", cid_addr, yield_size); + + if(!Memory.IsGoodAddr(cid_addr, 4)) + { + return CELL_EFAULT; + } + + u64 addr = Memory.Alloc(yield_size, 1); + + if(!addr) + { + return CELL_ENOMEM; + } + + Memory.Write32(cid_addr, sc_mem.GetNewId(new MemoryContainerInfo(addr, yield_size))); return CELL_OK; } int sys_memory_container_destroy(u32 cid) { - sc_mem.Warning("TODO: sys_memory_container_destroy(cid=0x%x)", cid); + sc_mem.Warning("sys_memory_container_destroy(cid=0x%x)", cid); + + MemoryContainerInfo* ct; + + if(!sc_mem.CheckId(cid, ct)) + { + return CELL_ESRCH; + } + + Memory.Free(ct->addr); + Emu.GetIdManager().RemoveID(cid); return CELL_OK; } @@ -230,7 +266,7 @@ int sys_mmapper_allocate_address(u32 size, u64 flags, u32 alignment, u32 alloc_a { 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)); + Memory.Write32(alloc_addr, Memory.Alloc(size, alignment)); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp index ccdd66c444..3601a5205f 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp @@ -21,20 +21,17 @@ int sys_ppu_thread_exit(int errorcode) int sys_ppu_thread_yield() { sysPrxForUser.Log("sys_ppu_thread_yield()"); - enable_log = !enable_log; - dump_enable = !dump_enable; - - if(!enable_log) - { - Emu.Pause(); - } 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); + sysPrxForUser.Warning("sys_ppu_thread_join(thread_id=%d, vptr_addr=0x%x)", thread_id, vptr_addr); + PPCThread* thr = Emu.GetCPU().GetThread(thread_id); + if(!thr) return CELL_ESRCH; + + GetCurrentPPUThread().Wait(*thr); return CELL_OK; } @@ -125,7 +122,7 @@ int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u32 arg, int prio, u32 return CELL_EFAULT; } - PPCThread& new_thread = Emu.GetCPU().AddThread(true); + PPCThread& new_thread = Emu.GetCPU().AddThread(PPC_THREAD_PPU); Memory.Write32(thread_id_addr, new_thread.GetId()); new_thread.SetEntry(entry); @@ -148,7 +145,7 @@ void sys_ppu_thread_once(u32 once_ctrl_addr, u32 entry) { Memory.Write32(once_ctrl_addr, SYS_PPU_THREAD_DONE_INIT); - PPCThread& new_thread = Emu.GetCPU().AddThread(true); + PPCThread& new_thread = Emu.GetCPU().AddThread(PPC_THREAD_PPU); new_thread.SetEntry(entry); ((PPUThread&)new_thread).LR = Emu.GetPPUThreadExit(); new_thread.Run(); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp index 8f0a127121..69ca0d4561 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp @@ -37,7 +37,6 @@ int SysCalls::lv2ProcessWaitForChild(PPUThread& CPU) int SysCalls::lv2ProcessGetStatus(PPUThread& CPU) { ConLog.Warning("lv2ProcessGetStatus"); - if(CPU.IsSPU()) return CELL_UNKNOWN_ERROR; //Memory.Write32(CPU.GPR[4], GetPPUThreadStatus(CPU)); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index 84e4470f13..81d3925fc3 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -150,7 +150,7 @@ int sys_spu_thread_initialize(u32 thread_addr, u32 group, u32 spu_num, u32 img_a ConLog.Write("a4 = 0x%x", a4); ConLog.SkipLn(); - PPCThread& new_thread = Emu.GetCPU().AddThread(false); + PPCThread& new_thread = Emu.GetCPU().AddThread(PPC_THREAD_SPU); new_thread.SetOffset(g_spu_offset); new_thread.SetEntry(entry - g_spu_offset); new_thread.SetName(name); @@ -172,7 +172,7 @@ int sys_spu_thread_set_argument(u32 id, u32 arg_addr) sc_spu.Warning("sys_spu_thread_set_argument(id=0x%x, arg_addr=0x%x)", id, arg_addr); PPCThread* thr = Emu.GetCPU().GetThread(id); - if(!thr || !thr->IsSPU()) + if(!thr || thr->GetType() == PPC_THREAD_PPU) { return CELL_ESRCH; } @@ -268,9 +268,11 @@ int sys_raw_spu_create(u32 id_addr, u32 attr_addr) sc_spu.Warning("sys_raw_spu_create(id_addr=0x%x, attr_addr=0x%x)", id_addr, attr_addr); //Emu.GetIdManager().GetNewID("sys_raw_spu", new u32(attr_addr)); - PPCThread& new_thread = Emu.GetCPU().AddThread(false); - Memory.Write32(id_addr, Emu.GetCPU().GetThreadNumById(false, new_thread.GetId())); - Memory.Write32(0xe0043014, 0); + PPCThread& new_thread = Emu.GetCPU().AddThread(PPC_THREAD_RAW_SPU); + Memory.Write32(id_addr, Emu.GetCPU().GetThreadNumById(PPC_THREAD_RAW_SPU, new_thread.GetId())); + new_thread.Run(); + new_thread.Exec(); + return CELL_OK; } @@ -286,10 +288,7 @@ int sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu) if(!Memory.InitSpuRawMem(max_raw_spu)) { - //30010780; - //e0043004 - offset - //e004300c - entry - return CELL_UNKNOWN_ERROR; + return CELL_ENOMEM; } //enable_log = true; @@ -306,7 +305,7 @@ int sys_spu_thread_write_ls(u32 id, u32 address, u64 value, u32 type) PPCThread* thr = Emu.GetCPU().GetThread(id); - if(!thr || !thr->IsSPU()) + if(!thr || thr->GetType() == PPC_THREAD_PPU) { return CELL_ESRCH; } @@ -324,7 +323,7 @@ int sys_spu_thread_read_ls(u32 id, u32 address, u32 value_addr, u32 type) PPCThread* thr = Emu.GetCPU().GetThread(id); - if(!thr || !thr->IsSPU()) + if(!thr || thr->GetType() == PPC_THREAD_PPU) { return CELL_ESRCH; } @@ -346,12 +345,12 @@ int sys_spu_thread_write_spu_mb(u32 id, u32 value) PPCThread* thr = Emu.GetCPU().GetThread(id); - if(!thr || !thr->IsSPU()) + if(!thr || !thr->GetType() == PPC_THREAD_PPU) { return CELL_ESRCH; } - if(!(*(SPUThread*)thr).InMbox.Push(value)) + if(!(*(SPUThread*)thr).SPU_In_MBox.Push(value)) { ConLog.Warning("sys_spu_thread_write_spu_mb(id=0x%x, value=0x%x): used all mbox items."); return CELL_EBUSY; //? diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 9bc0229754..76ad909a66 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -112,7 +112,7 @@ void Emulator::Load() } LoadPoints(BreakPointsDBName); - PPCThread& thread = GetCPU().AddThread(l.GetMachine() == MACHINE_PPC64); + PPCThread& thread = GetCPU().AddThread(l.GetMachine() == MACHINE_PPC64 ? PPC_THREAD_PPU : PPC_THREAD_SPU); if(l.GetMachine() == MACHINE_SPU) { diff --git a/rpcs3/Gui/DisAsmFrame.cpp b/rpcs3/Gui/DisAsmFrame.cpp index 5feadd3490..4fc2a446d7 100644 --- a/rpcs3/Gui/DisAsmFrame.cpp +++ b/rpcs3/Gui/DisAsmFrame.cpp @@ -135,7 +135,7 @@ public: *done = false; - if(Emu.GetCPU().GetThreads()[0].IsSPU()) + if(Emu.GetCPU().GetThreads()[0].GetType() != PPC_THREAD_PPU) { SPU_DisAsm& dis_asm = *new SPU_DisAsm(*(PPCThread*)NULL, DumpMode); decoder = new SPU_Decoder(dis_asm); @@ -341,7 +341,7 @@ void DisAsmFrame::Dump(wxCommandEvent& WXUNUSED(event)) PPC_DisAsm* disasm; PPC_Decoder* decoder; - if(Emu.GetCPU().GetThreads()[0].IsSPU()) + if(Emu.GetCPU().GetThreads()[0].GetType() != PPC_THREAD_PPU) { SPU_DisAsm& dis_asm = *new SPU_DisAsm(*(PPCThread*)NULL, DumpMode); decoder = new SPU_Decoder(dis_asm); diff --git a/rpcs3/Gui/InterpreterDisAsm.cpp b/rpcs3/Gui/InterpreterDisAsm.cpp index 9b5d9d029d..d137359a70 100644 --- a/rpcs3/Gui/InterpreterDisAsm.cpp +++ b/rpcs3/Gui/InterpreterDisAsm.cpp @@ -108,7 +108,7 @@ void InterpreterDisAsmFrame::OnSelectUnit(wxCommandEvent& event) if(CPU) { - if(CPU->IsSPU()) + if(CPU->GetType() != PPC_THREAD_PPU) { SPU_DisAsm& dis_asm = *new SPU_DisAsm(*CPU, InterpreterMode); decoder = new SPU_Decoder(dis_asm); diff --git a/rpcs3/Loader/ELF32.cpp b/rpcs3/Loader/ELF32.cpp index 7dc5e4b0a9..99951b8515 100644 --- a/rpcs3/Loader/ELF32.cpp +++ b/rpcs3/Loader/ELF32.cpp @@ -133,18 +133,18 @@ bool ELF32Loader::LoadPhdrData(u64 offset) { phdr_arr[i].Show(); - if(phdr_arr[i].p_vaddr < min_addr) - { - min_addr = phdr_arr[i].p_vaddr; - } - - if(phdr_arr[i].p_vaddr + phdr_arr[i].p_memsz > max_addr) - { - max_addr = phdr_arr[i].p_vaddr + phdr_arr[i].p_memsz; - } - if(phdr_arr[i].p_type == 0x00000001) //LOAD { + if(phdr_arr[i].p_vaddr < min_addr) + { + min_addr = phdr_arr[i].p_vaddr; + } + + if(phdr_arr[i].p_vaddr + phdr_arr[i].p_memsz > max_addr) + { + max_addr = phdr_arr[i].p_vaddr + phdr_arr[i].p_memsz; + } + if(phdr_arr[i].p_vaddr != phdr_arr[i].p_paddr) { ConLog.Warning @@ -158,6 +158,40 @@ bool ELF32Loader::LoadPhdrData(u64 offset) elf32_f.Seek(phdr_arr[i].p_offset); elf32_f.Read(&Memory[phdr_arr[i].p_vaddr + offset], phdr_arr[i].p_filesz); } + else if(phdr_arr[i].p_type == 0x00000004) + { + elf32_f.Seek(phdr_arr[i].p_offset); + Elf32_Note note; + note.Load(elf32_f); + + if(note.type != 1) + { + ConLog.Error("ELF32: Bad NOTE type (%d)", note.type); + break; + } + + if(note.namesz != sizeof(note.name)) + { + ConLog.Error("ELF32: Bad NOTE namesz (%d)", note.namesz); + break; + } + + if(note.descsz < sizeof(note.desc)) + { + ConLog.Error("ELF32: Bad NOTE descsz (%d)", note.descsz); + break; + } + + //if(note.desc.flags) + //{ + // ConLog.Error("ELF32: Bad NOTE flags (0x%x)", note.desc.flags); + // break; + //} + + ConLog.Warning("name = %s", note.name); + ConLog.Warning("ls_size = %d", note.desc.ls_size); + ConLog.Warning("stack_size = %d", note.desc.stack_size); + } #ifdef LOADER_DEBUG ConLog.SkipLn(); #endif @@ -177,6 +211,11 @@ bool ELF32Loader::LoadShdrData(u64 offset) shdr.Show(); ConLog.SkipLn(); #endif + if((shdr.sh_type == SHT_RELA) || (shdr.sh_type == SHT_REL)) + { + ConLog.Error("ELF32 ERROR: Relocation"); + continue; + } if((shdr.sh_flags & SHF_ALLOC) != SHF_ALLOC) continue; if(shdr.sh_addr < min_addr) @@ -188,21 +227,6 @@ bool ELF32Loader::LoadShdrData(u64 offset) { max_addr = shdr.sh_addr + shdr.sh_size; } - - //const s64 addr = shdr.sh_addr; - //const s64 size = shdr.sh_size; - //MemoryBlock* mem = nullptr; - - /* - switch(shdr.sh_type) - { - case SHT_NOBITS: - if(size == 0) continue; - memset(&Memory[addr + offset], 0, size); - case SHT_PROGBITS: - break; - } - */ } //TODO diff --git a/rpcs3/Loader/ELF32.h b/rpcs3/Loader/ELF32.h index dc13b78ef7..5657eb7d27 100644 --- a/rpcs3/Loader/ELF32.h +++ b/rpcs3/Loader/ELF32.h @@ -75,6 +75,40 @@ struct Elf32_Ehdr u32 GetEntry() const { return e_entry; } }; +struct Elf32_Desc +{ + u32 revision; + u32 ls_size; + u32 stack_size; + u32 flags; + + void Load(vfsStream& f) + { + revision = Read32(f); + ls_size = Read32(f); + stack_size = Read32(f); + flags = Read32(f); + } +}; + +struct Elf32_Note +{ + u32 namesz; + u32 descsz; + u32 type; + u8 name[8]; + Elf32_Desc desc; + + void Load(vfsStream& f) + { + namesz = Read32(f); + descsz = Read32(f); + type = Read32(f); + f.Read(name, 8); + desc.Load(f); + } +}; + struct Elf32_Shdr { u32 sh_name; diff --git a/rpcs3/Loader/ELF64.cpp b/rpcs3/Loader/ELF64.cpp index ef6fe0dd13..c49eb4b384 100644 --- a/rpcs3/Loader/ELF64.cpp +++ b/rpcs3/Loader/ELF64.cpp @@ -413,16 +413,6 @@ bool ELF64Loader::LoadShdrData(u64 offset) { Elf64_Shdr& shdr = shdr_arr[i]; - if(shdr.sh_addr < min_addr) - { - min_addr = shdr.sh_addr; - } - - if(shdr.sh_addr + shdr.sh_size > max_addr) - { - max_addr = shdr.sh_addr + shdr.sh_size; - } - if(i < shdr_name_arr.GetCount()) { const wxString& name = shdr_name_arr[i]; @@ -445,6 +435,22 @@ bool ELF64Loader::LoadShdrData(u64 offset) if(size == 0 || !Memory.IsGoodAddr(offset + addr, size)) continue; + if(shdr.sh_addr < min_addr) + { + min_addr = shdr.sh_addr; + } + + if(shdr.sh_addr + shdr.sh_size > max_addr) + { + max_addr = shdr.sh_addr + shdr.sh_size; + } + + if((shdr.sh_type == SHT_RELA) || (shdr.sh_type == SHT_REL)) + { + ConLog.Error("ELF64 ERROR: Relocation"); + continue; + } + switch(shdr.sh_type) { case SHT_NOBITS: @@ -457,6 +463,10 @@ bool ELF64Loader::LoadShdrData(u64 offset) elf64_f.Read(&Memory[addr], shdr.sh_size); */ break; + + case SHT_RELA: + ConLog.Warning("ELF64: RELA"); + break; } } diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 74ef6e59f7..271eebaa2a 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -201,6 +201,7 @@ + @@ -243,6 +244,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index b74d4d565f..b9bee6158c 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -256,6 +256,12 @@ Emu\SysCalls + + Emu\SysCalls\Modules + + + Emu\CPU + From 81e874c9e28af6d458d1db85b7ad825aca79ef13 Mon Sep 17 00:00:00 2001 From: DH Date: Sat, 3 Aug 2013 12:40:03 +0300 Subject: [PATCH 05/13] - Implemented HDD manager. - Implemented VFS manager. - Implemented MFC. - Fixed ELF Compiler. - Improved HLE Func binder. --- Utilities/Array.h | 110 ++- rpcs3/Emu/Cell/MFC.cpp | 2 + rpcs3/Emu/Cell/MFC.h | 252 ++++++ rpcs3/Emu/Cell/PPUProgramCompiler.cpp | 40 +- rpcs3/Emu/Cell/PPUProgramCompiler.h | 8 +- rpcs3/Emu/Cell/RawSPUThread.cpp | 84 +- rpcs3/Emu/Cell/RawSPUThread.h | 17 +- rpcs3/Emu/Cell/SPUInterpreter.h | 4 +- rpcs3/Emu/Cell/SPUThread.h | 105 +-- rpcs3/Emu/FS/VFS.cpp | 116 ++- rpcs3/Emu/FS/VFS.h | 29 +- rpcs3/Emu/FS/vfsDevice.cpp | 15 + rpcs3/Emu/FS/vfsDevice.h | 34 +- rpcs3/Emu/FS/vfsFileBase.cpp | 2 +- rpcs3/Emu/FS/vfsFileBase.h | 2 +- rpcs3/Emu/FS/vfsLocalFile.cpp | 2 +- rpcs3/Emu/GS/GL/GLGSRender.cpp | 2 +- rpcs3/Emu/HDD/HDD.cpp | 3 + rpcs3/Emu/HDD/HDD.h | 864 +++++++++++++++++++ rpcs3/Emu/Memory/Memory.h | 1 + rpcs3/Emu/Memory/MemoryBlock.h | 2 +- rpcs3/Emu/SysCalls/FuncList.cpp | 2 +- rpcs3/Emu/SysCalls/Modules.cpp | 32 +- rpcs3/Emu/SysCalls/Modules.h | 6 +- rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp | 35 +- rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp | 12 +- rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp | 44 +- rpcs3/Emu/SysCalls/Modules/sys_fs.cpp | 28 +- rpcs3/Emu/SysCalls/Modules/sys_io.cpp | 16 +- rpcs3/Emu/SysCalls/SysCalls.cpp | 2 +- rpcs3/Emu/SysCalls/SysCalls.h | 3 + rpcs3/Emu/SysCalls/lv2/SC_Event.cpp | 2 +- rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp | 27 +- rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp | 173 +--- rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 2 +- rpcs3/Emu/System.cpp | 11 +- rpcs3/Gui/MainFrame.cpp | 40 +- rpcs3/Gui/MainFrame.h | 2 + rpcs3/Gui/TextInputDialog.cpp | 37 + rpcs3/Gui/TextInputDialog.h | 13 + rpcs3/Gui/VFSManager.cpp | 219 +++++ rpcs3/Gui/VFSManager.h | 39 + rpcs3/Gui/VHDDManager.cpp | 551 ++++++++++++ rpcs3/Gui/VHDDManager.h | 87 ++ rpcs3/Ini.cpp | 7 +- rpcs3/Loader/ELF32.cpp | 15 +- rpcs3/Loader/ELF32.h | 16 +- rpcs3/Loader/ELF64.cpp | 2 +- rpcs3/rpcs3.cpp | 10 +- rpcs3/rpcs3.h | 2 +- rpcs3/rpcs3.vcxproj | 5 + rpcs3/rpcs3.vcxproj.filters | 18 + 52 files changed, 2684 insertions(+), 468 deletions(-) create mode 100644 rpcs3/Emu/Cell/MFC.cpp create mode 100644 rpcs3/Emu/Cell/MFC.h create mode 100644 rpcs3/Emu/HDD/HDD.cpp create mode 100644 rpcs3/Emu/HDD/HDD.h create mode 100644 rpcs3/Gui/TextInputDialog.cpp create mode 100644 rpcs3/Gui/TextInputDialog.h create mode 100644 rpcs3/Gui/VFSManager.cpp create mode 100644 rpcs3/Gui/VFSManager.h create mode 100644 rpcs3/Gui/VHDDManager.cpp create mode 100644 rpcs3/Gui/VHDDManager.h diff --git a/Utilities/Array.h b/Utilities/Array.h index c672ef4fe6..02c80417c8 100644 --- a/Utilities/Array.h +++ b/Utilities/Array.h @@ -2,6 +2,7 @@ template class Array { +protected: u32 m_count; T* m_array; @@ -120,7 +121,13 @@ public: u32 count = m_count; m_count = 0; for(u32 i=0; i= count) return; + if(m_count >= count) return; - _InsertRoomEnd(count - GetCount()); + _InsertRoomEnd(count - m_count); - if(memzero) memset(m_array + GetCount(), 0, count - GetCount()); + if(memzero) memset(m_array + m_count - count, 0, sizeof(T) * (m_count - count)); } void Reserve(const u32 count) @@ -162,6 +169,7 @@ public: } inline T* GetPtr() { return m_array; } + inline const T* GetPtr() const { return m_array; } T& operator[](u32 num) const { return m_array[num]; } @@ -185,6 +193,96 @@ protected: } }; +class ArrayString : public Array +{ +public: + ArrayString() : Array() + { + } + + ArrayString(const wxString& value) : Array() + { + *this = value; + } + + ArrayString(const char* value) : Array() + { + *this = value; + } + + virtual u32 GetCount() const + { + return m_array ? strlen(m_array) : 0; + } + + virtual void SetCount(const u32 count, bool memzero = true) + { + if(m_count && count < m_count - 1) + { + m_array[count] = '\0'; + } + else + { + Array::SetCount(count + 1, memzero); + } + } + + ArrayString& operator = (const char* right) + { + Clear(); + + if(right) + { + size_t len = strlen(right); + + if(len) + { + SetCount(len); + memcpy(m_array, right, len * sizeof(char)); + m_array[len] = '\0'; + } + } + + return *this; + } + + ArrayString& operator = (const ArrayString& right) + { + Clear(); + + if(size_t len = right.GetCount()) + { + SetCount(len); + memcpy(m_array, right.GetPtr(), len * sizeof(char)); + m_array[len] = '\0'; + } + + return *this; + } + + ArrayString& operator = (const wxString& right) + { + Clear(); + + if(size_t len = right.Len()) + { + SetCount(len); + memcpy(m_array, right.c_str(), len * sizeof(char)); + m_array[len] = '\0'; + } + + return *this; + } + + ArrayString* Clone() const + { + ArrayString* new_array = new ArrayString(); + (*new_array) = m_array; + + return new_array; + } +}; + template struct Stack : public Array { Stack() : Array() diff --git a/rpcs3/Emu/Cell/MFC.cpp b/rpcs3/Emu/Cell/MFC.cpp new file mode 100644 index 0000000000..f63341b79c --- /dev/null +++ b/rpcs3/Emu/Cell/MFC.cpp @@ -0,0 +1,2 @@ +#include "stdafx.h" +#include "MFC.h" diff --git a/rpcs3/Emu/Cell/MFC.h b/rpcs3/Emu/Cell/MFC.h new file mode 100644 index 0000000000..0ab2b35fee --- /dev/null +++ b/rpcs3/Emu/Cell/MFC.h @@ -0,0 +1,252 @@ +#pragma once +#include + +enum +{ + MFC_PUT_CMD = 0x20, + MFC_GET_CMD = 0x40, + MFC_MASK_CMD = 0xffff, +}; + +enum +{ + MFC_SPU_TO_PPU_MAILBOX_STATUS_MASK = 0x000000FF, + MFC_SPU_TO_PPU_MAILBOX_STATUS_SHIFT = 0x0, + MFC_PPU_TO_SPU_MAILBOX_STATUS_MASK = 0x0000FF00, + MFC_PPU_TO_SPU_MAILBOX_STATUS_SHIFT = 0x8, + MFC_PPU_TO_SPU_MAILBOX_MAX = 0x4, + MFC_SPU_TO_PPU_INT_MAILBOX_STATUS_MASK = 0x00FF0000, + MFC_SPU_TO_PPU_INT_MAILBOX_STATUS_SHIFT = 0x10, +}; + +enum +{ + MFC_PPU_DMA_CMD_ENQUEUE_SUCCESSFUL = 0x00, + MFC_PPU_DMA_CMD_SEQUENCE_ERROR = 0x01, + MFC_PPU_DMA_QUEUE_FULL = 0x02, +}; + +enum +{ + MFC_PPU_MAX_QUEUE_SPACE = 0x08, + MFC_SPU_MAX_QUEUE_SPACE = 0x10, +}; + +struct DMAC_Queue +{ + bool is_valid; + u64 ea; + u32 lsa; + u16 size; + u32 op; + u8 tag; + u8 rt; + u16 list_addr; + u16 list_size; + u32 dep_state; + u32 cmd; + u32 dep_type; +}; + +struct DMAC_Proxy +{ + u64 ea; + u32 lsa; + u16 size; + u32 op; + u8 tag; + u8 rt; + u16 list_addr; + u16 list_size; + u32 dep_state; + u32 cmd; + u32 dep_type; +}; + +template +class SPUReg +{ + u64 m_addr; + u32 m_pos; + +public: + static const size_t max_count = _max_count; + static const size_t size = max_count * 4; + + SPUReg() + { + Init(); + } + + void Init() + { + m_pos = 0; + } + + void SetAddr(u64 addr) + { + m_addr = addr; + } + + u64 GetAddr() const + { + return m_addr; + } + + __forceinline bool Pop(u32& res) + { + if(!m_pos) return false; + res = Memory.Read32(m_addr + m_pos--); + return true; + } + + __forceinline bool Push(u32 value) + { + if(m_pos >= max_count) return false; + Memory.Write32(m_addr + m_pos++, value); + return true; + } + + u32 GetCount() const + { + return m_pos; + } + + u32 GetFreeCount() const + { + return max_count - m_pos; + } + + void SetValue(u32 value) + { + Memory.Write32(m_addr, value); + } + + u32 GetValue() const + { + return Memory.Read32(m_addr); + } +}; + +struct DMAC +{ + DMAC_Queue queue[MFC_SPU_MAX_QUEUE_SPACE]; + DMAC_Proxy proxy[MFC_PPU_MAX_QUEUE_SPACE]; + u64 ls_offset; + std::atomic queue_pos; + std::atomic proxy_pos; + + u32 Cmd(u32 cmd, u32 tag, u32 lsa, u64 ea, u32 size) + { + if(!Memory.IsGoodAddr(ls_offset + lsa, size) || !Memory.IsGoodAddr(ea, size)) + { + return MFC_PPU_DMA_CMD_SEQUENCE_ERROR; + } + + if(proxy_pos >= MFC_PPU_MAX_QUEUE_SPACE) + { + return MFC_PPU_DMA_QUEUE_FULL; + } + + DMAC_Proxy& p = proxy[proxy_pos++]; + p.cmd = cmd; + p.tag = tag; + p.lsa = lsa; + p.ea = ea; + p.size = size; + + return MFC_PPU_DMA_CMD_ENQUEUE_SUCCESSFUL; + } + + void DoCmd() + { + if(proxy_pos) + { + DMAC_Proxy p = proxy[0]; + memcpy(proxy, proxy + 1, proxy_pos--); + + switch(p.cmd) + { + case MFC_PUT_CMD: + memcpy(Memory + ls_offset + p.lsa, Memory + p.ea, p.size); + break; + + case MFC_GET_CMD: + memcpy(Memory + p.ea, Memory + ls_offset + p.lsa, p.size); + break; + + default: + ConLog.Error("Unknown DMA cmd."); + break; + } + } + } +}; + +struct MFC +{ + SPUReg<1> MFC_LSA; + SPUReg<1> MFC_EAH; + SPUReg<1> MFC_EAL; + SPUReg<1> MFC_Size_Tag; + SPUReg<1> MFC_CMDStatus; + SPUReg<1> MFC_QStatus; + SPUReg<1> Prxy_QueryType; + SPUReg<1> Prxy_QueryMask; + SPUReg<1> Prxy_TagStatus; + SPUReg<1> SPU_Out_MBox; + SPUReg<4> SPU_In_MBox; + SPUReg<1> SPU_MBox_Status; + SPUReg<1> SPU_RunCntl; + SPUReg<1> SPU_Status; + SPUReg<1> SPU_NPC; + SPUReg<1> SPU_RdSigNotify1; + SPUReg<1> SPU_RdSigNotify2; + + DMAC dmac; + + void Handle() + { + u32 cmd = MFC_CMDStatus.GetValue(); + + if(cmd) + { + u16 op = cmd & MFC_MASK_CMD; + + switch(op) + { + case MFC_PUT_CMD: + case MFC_GET_CMD: + { + u32 lsa = MFC_LSA.GetValue(); + u64 ea = (u64)MFC_EAL.GetValue() | ((u64)MFC_EAH.GetValue() << 32); + u32 size_tag = MFC_Size_Tag.GetValue(); + u16 tag = (u16)size_tag; + u16 size = size_tag >> 16; + + ConLog.Warning("RawSPU DMA %s:", op == MFC_PUT_CMD ? "PUT" : "GET"); + ConLog.Warning("*** lsa = 0x%x", lsa); + ConLog.Warning("*** ea = 0x%llx", ea); + ConLog.Warning("*** tag = 0x%x", tag); + ConLog.Warning("*** size = 0x%x", size); + ConLog.SkipLn(); + + MFC_CMDStatus.SetValue(dmac.Cmd(cmd, tag, lsa, ea, size)); + } + break; + + default: + ConLog.Error("Unknown MFC cmd."); + break; + } + } + + if(Prxy_QueryType.GetValue() == 2) + { + Prxy_QueryType.SetValue(0); + u32 mask = Prxy_QueryMask.GetValue(); + // + MFC_QStatus.SetValue(mask); + } + } +}; \ No newline at end of file diff --git a/rpcs3/Emu/Cell/PPUProgramCompiler.cpp b/rpcs3/Emu/Cell/PPUProgramCompiler.cpp index 8733ab4a36..c645a2cf7f 100644 --- a/rpcs3/Emu/Cell/PPUProgramCompiler.cpp +++ b/rpcs3/Emu/Cell/PPUProgramCompiler.cpp @@ -72,7 +72,7 @@ SectionInfo::SectionInfo(const wxString& _name) : name(_name) shdr.sh_offset = section_offs; shdr.sh_name = section_name_offs; - section_name_offs += name.Len() + 1; + section_name_offs += name.GetCount() + 1; } void SectionInfo::SetDataSize(u32 size, u32 align) @@ -107,11 +107,11 @@ SectionInfo::~SectionInfo() for(u32 i=section_num + 1; i= 0) return m_text_addr + m_branches[i].m_pos * 4; return m_branches[i].m_addr; @@ -664,7 +664,7 @@ CompilePPUProgram::Branch& CompilePPUProgram::GetBranch(const wxString& name) { for(u32 i=0; i> 16)); //oris r12,r12,addr>>16 - Write32(f, ToOpcode(LWZ) | ToRD(12) | ToRA(12) | ToIMM16(addr)); //lwz r12,addr&0xffff(r12) - Write32(f, 0xf8410028); //std r2,40(r1) - Write32(f, ToOpcode(LWZ) | ToRD(0) | ToRA(12) | ToIMM16(0)); //lwz r0,0(r12) - Write32(f, ToOpcode(LWZ) | ToRD(2) | ToRA(12) | ToIMM16(4)); //lwz r2,4(r12) - Write32(f, 0x7c0903a6); //mtctr r0 - Write32(f, 0x4e800420); //bctr - */ } f.Seek(s_lib_stub_top.sh_offset); diff --git a/rpcs3/Emu/Cell/PPUProgramCompiler.h b/rpcs3/Emu/Cell/PPUProgramCompiler.h index b86d4f7a3e..23b92e43d6 100644 --- a/rpcs3/Emu/Cell/PPUProgramCompiler.h +++ b/rpcs3/Emu/Cell/PPUProgramCompiler.h @@ -19,7 +19,7 @@ enum ArgType struct Arg { - wxString string; + ArrayString string; u32 value; ArgType type; @@ -34,7 +34,7 @@ struct Arg struct SectionInfo { Elf64_Shdr shdr; - wxString name; + ArrayString name; Array code; u32 section_num; @@ -62,7 +62,7 @@ class CompilePPUProgram { struct Branch { - wxString m_name; + ArrayString m_name; s32 m_pos; s32 m_id; s32 m_addr; @@ -101,7 +101,7 @@ class CompilePPUProgram struct SpData { - wxString m_data; + ArrayString m_data; u32 m_addr; SpData(const wxString& data, u32 addr) diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index e10f97d6dc..370550cea7 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -20,29 +20,32 @@ void RawSPUThread::InitRegs() u32 num = Emu.GetCPU().GetThreadNumById(GetType(), GetId()); m_offset = RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * num + RAW_SPU_LS_OFFSET; - MFC_LSA.SetAddr(GetRawSPURegAddrByNum(num, MFC_LSA_offs)); - MFC_EAH.SetAddr(GetRawSPURegAddrByNum(num, MFC_EAH_offs)); - MFC_EAL.SetAddr(GetRawSPURegAddrByNum(num, MFC_EAL_offs)); - MFC_Size_Tag.SetAddr(GetRawSPURegAddrByNum(num, MFC_Size_Tag_offs)); - MFC_CMDStatus.SetAddr(GetRawSPURegAddrByNum(num, MFC_CMDStatus_offs)); - MFC_QStatus.SetAddr(GetRawSPURegAddrByNum(num, MFC_QStatus_offs)); - Prxy_QueryType.SetAddr(GetRawSPURegAddrByNum(num, Prxy_QueryType_offs)); - Prxy_QueryMask.SetAddr(GetRawSPURegAddrByNum(num, Prxy_QueryMask_offs)); - Prxy_TagStatus.SetAddr(GetRawSPURegAddrByNum(num, Prxy_TagStatus_offs)); - SPU_Out_MBox.SetAddr(GetRawSPURegAddrByNum(num, SPU_Out_MBox_offs)); - SPU_In_MBox.SetAddr(GetRawSPURegAddrByNum(num, SPU_In_MBox_offs)); - SPU_MBox_Status.SetAddr(GetRawSPURegAddrByNum(num, SPU_MBox_Status_offs)); - SPU_RunCntl.SetAddr(GetRawSPURegAddrByNum(num, SPU_RunCntl_offs)); - SPU_Status.SetAddr(GetRawSPURegAddrByNum(num, SPU_Status_offs)); - SPU_NPC.SetAddr(GetRawSPURegAddrByNum(num, SPU_NPC_offs)); - SPU_RdSigNotify1.SetAddr(GetRawSPURegAddrByNum(num, SPU_RdSigNotify1_offs)); - SPU_RdSigNotify2.SetAddr(GetRawSPURegAddrByNum(num, SPU_RdSigNotify2_offs)); + mfc.dmac.ls_offset = m_offset; + mfc.dmac.proxy_pos = 0; + mfc.dmac.queue_pos = 0; + mfc.MFC_LSA.SetAddr(GetRawSPURegAddrByNum(num, MFC_LSA_offs)); + mfc.MFC_EAH.SetAddr(GetRawSPURegAddrByNum(num, MFC_EAH_offs)); + mfc.MFC_EAL.SetAddr(GetRawSPURegAddrByNum(num, MFC_EAL_offs)); + mfc.MFC_Size_Tag.SetAddr(GetRawSPURegAddrByNum(num, MFC_Size_Tag_offs)); + mfc.MFC_CMDStatus.SetAddr(GetRawSPURegAddrByNum(num, MFC_CMDStatus_offs)); + mfc.MFC_QStatus.SetAddr(GetRawSPURegAddrByNum(num, MFC_QStatus_offs)); + mfc.Prxy_QueryType.SetAddr(GetRawSPURegAddrByNum(num, Prxy_QueryType_offs)); + mfc.Prxy_QueryMask.SetAddr(GetRawSPURegAddrByNum(num, Prxy_QueryMask_offs)); + mfc.Prxy_TagStatus.SetAddr(GetRawSPURegAddrByNum(num, Prxy_TagStatus_offs)); + mfc.SPU_Out_MBox.SetAddr(GetRawSPURegAddrByNum(num, SPU_Out_MBox_offs)); + mfc.SPU_In_MBox.SetAddr(GetRawSPURegAddrByNum(num, SPU_In_MBox_offs)); + mfc.SPU_MBox_Status.SetAddr(GetRawSPURegAddrByNum(num, SPU_MBox_Status_offs)); + mfc.SPU_RunCntl.SetAddr(GetRawSPURegAddrByNum(num, SPU_RunCntl_offs)); + mfc.SPU_Status.SetAddr(GetRawSPURegAddrByNum(num, SPU_Status_offs)); + mfc.SPU_NPC.SetAddr(GetRawSPURegAddrByNum(num, SPU_NPC_offs)); + mfc.SPU_RdSigNotify1.SetAddr(GetRawSPURegAddrByNum(num, SPU_RdSigNotify1_offs)); + mfc.SPU_RdSigNotify2.SetAddr(GetRawSPURegAddrByNum(num, SPU_RdSigNotify2_offs)); - SPU_RunCntl.SetValue(SPU_RUNCNTL_STOP); - SPU_Status.SetValue(SPU_STATUS_RUNNING); - Prxy_QueryType.SetValue(0); - MFC_CMDStatus.SetValue(0); - PC = SPU_NPC.GetValue(); + mfc.SPU_RunCntl.SetValue(SPU_RUNCNTL_STOP); + mfc.SPU_Status.SetValue(SPU_STATUS_RUNNING); + mfc.Prxy_QueryType.SetValue(0); + mfc.MFC_CMDStatus.SetValue(0); + PC = mfc.SPU_NPC.GetValue(); } void RawSPUThread::Task() @@ -78,39 +81,16 @@ void RawSPUThread::Task() continue; } - if(MFC_CMDStatus.GetValue() == 0x40) - { - MFC_CMDStatus.SetValue(0); - u32 lsa = MFC_LSA.GetValue(); - u64 ea = (u64)MFC_EAL.GetValue() | ((u64)MFC_EAH.GetValue() << 32); - u32 size_tag = MFC_Size_Tag.GetValue(); - u16 tag = (u16)size_tag; - u16 size = size_tag >> 16; - ConLog.Warning("RawSPU DMA GET:"); - ConLog.Warning("*** lsa = 0x%x", lsa); - ConLog.Warning("*** ea = 0x%llx", ea); - ConLog.Warning("*** tag = 0x%x", tag); - ConLog.Warning("*** size = 0x%x", size); - ConLog.SkipLn(); - memcpy(Memory + m_offset + lsa, Memory + ea, size); - } + mfc.Handle(); - if(Prxy_QueryType.GetValue() == 2) - { - Prxy_QueryType.SetValue(0); - u32 mask = Prxy_QueryMask.GetValue(); - // - MFC_QStatus.SetValue(mask); - } - - if(SPU_RunCntl.GetValue() != SPU_RUNCNTL_RUNNABLE) + if(mfc.SPU_RunCntl.GetValue() != SPU_RUNCNTL_RUNNABLE) { if(!is_last_paused) { - if(is_last_paused = SPU_RunCntl.GetValue() != SPU_RUNCNTL_RUNNABLE) + if(is_last_paused = mfc.SPU_RunCntl.GetValue() != SPU_RUNCNTL_RUNNABLE) { - SPU_NPC.SetValue(PC); - SPU_Status.SetValue(SPU_STATUS_WAITING_FOR_CHANNEL); + mfc.SPU_NPC.SetValue(PC); + mfc.SPU_Status.SetValue(SPU_STATUS_WAITING_FOR_CHANNEL); } } @@ -121,8 +101,8 @@ void RawSPUThread::Task() if(is_last_paused) { is_last_paused = false; - PC = SPU_NPC.GetValue(); - SPU_Status.SetValue(SPU_STATUS_RUNNING); + PC = mfc.SPU_NPC.GetValue(); + mfc.SPU_Status.SetValue(SPU_STATUS_RUNNING); } DoCode(Memory.Read32(m_offset + PC)); diff --git a/rpcs3/Emu/Cell/RawSPUThread.h b/rpcs3/Emu/Cell/RawSPUThread.h index d95bb702c6..829f87878f 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.h +++ b/rpcs3/Emu/Cell/RawSPUThread.h @@ -49,12 +49,17 @@ public: RawSPUThread(PPCThreadType type = PPC_THREAD_RAW_SPU); ~RawSPUThread(); - virtual wxString RegsToString() - { - wxString ret = PPCThread::RegsToString(); - for(uint i=0; i<128; ++i) ret += wxString::Format("GPR[%d] = 0x%s\n", i, GPR[i].ToString()); - return ret; - } + virtual u8 ReadLS8 (const u32 lsa) const { return Memory.Read8 (lsa + (m_offset & 0x3fffc)); } + virtual u16 ReadLS16 (const u32 lsa) const { return Memory.Read16 (lsa + m_offset); } + virtual u32 ReadLS32 (const u32 lsa) const { return Memory.Read32 (lsa + m_offset); } + virtual u64 ReadLS64 (const u32 lsa) const { return Memory.Read64 (lsa + m_offset); } + virtual u128 ReadLS128(const u32 lsa) const { return Memory.Read128(lsa + m_offset); } + + virtual void WriteLS8 (const u32 lsa, const u8& data) const { Memory.Write8 (lsa + m_offset, data); } + virtual void WriteLS16 (const u32 lsa, const u16& data) const { Memory.Write16 (lsa + m_offset, data); } + virtual void WriteLS32 (const u32 lsa, const u32& data) const { Memory.Write32 (lsa + m_offset, data); } + virtual void WriteLS64 (const u32 lsa, const u64& data) const { Memory.Write64 (lsa + m_offset, data); } + virtual void WriteLS128(const u32 lsa, const u128& data) const { Memory.Write128(lsa + m_offset, data); } public: virtual void InitRegs(); diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index 293f5a32a1..d34c42512f 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -1166,7 +1166,7 @@ private: (~CPU.GPR[rc]._u32[i] & CPU.GPR[ra]._u32[i]); } } - void SHUFB(u32 rc, u32 ra, u32 rb, u32 rt) + void SHUFB(u32 rt, u32 ra, u32 rb, u32 rc) { ConLog.Warning("SHUFB"); } @@ -1175,7 +1175,7 @@ private: 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]; } - void FNMS(u32 rc, u32 ra, u32 rb, u32 rt) + void FNMS(u32 rt, u32 ra, u32 rb, u32 rc) { UNIMPLEMENTED(); } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index c311dfef28..26775a6ec9 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -1,6 +1,7 @@ #pragma once #include "PPCThread.h" #include "Emu/event.h" +#include "MFC.h" static const wxString spu_reg_name[128] = { @@ -127,72 +128,7 @@ class SPUThread : public PPCThread { public: SPU_GPR_hdr GPR[128]; //General-Purpose Register - - template - class SPUReg - { - u64 m_addr; - u32 m_pos; - - public: - static const size_t max_count = _max_count; - static const size_t size = max_count * 4; - - SPUReg() - { - Init(); - } - - void Init() - { - m_pos = 0; - } - - void SetAddr(u64 addr) - { - m_addr = addr; - } - - u64 GetAddr() const - { - return m_addr; - } - - __forceinline bool Pop(u32& res) - { - if(!m_pos) return false; - res = Memory.Read32(m_addr + m_pos--); - return true; - } - - __forceinline bool Push(u32 value) - { - if(m_pos >= max_count) return false; - Memory.Write32(m_addr + m_pos++, value); - return true; - } - - u32 GetCount() const - { - return m_pos; - } - - u32 GetFreeCount() const - { - return max_count - m_pos; - } - - void SetValue(u32 value) - { - Memory.Write32(m_addr, value); - } - - u32 GetValue() const - { - return Memory.Read32(m_addr); - } - }; - + /* SPUReg<1> MFC_LSA; SPUReg<1> MFC_EAH; SPUReg<1> MFC_EAL; @@ -210,9 +146,10 @@ public: SPUReg<1> SPU_NPC; SPUReg<1> SPU_RdSigNotify1; SPUReg<1> SPU_RdSigNotify2; - + */ SizedStack SPU_OutIntr_Mbox; u32 LSA; + MFC mfc; union { @@ -225,10 +162,10 @@ public: switch(ch) { case SPU_WrOutMbox: - return SPU_Out_MBox.GetFreeCount(); + return mfc.SPU_Out_MBox.GetFreeCount(); case SPU_RdInMbox: - return SPU_In_MBox.GetCount(); + return mfc.SPU_In_MBox.GetCount(); case SPU_WrOutIntrMbox: return 0;//return SPU_OutIntr_Mbox.GetFreeCount(); @@ -257,11 +194,11 @@ public: case SPU_WrOutMbox: ConLog.Warning("SPU_WrOutMbox = 0x%x", v); - if(!SPU_Out_MBox.Push(v)) + if(!mfc.SPU_Out_MBox.Push(v)) { ConLog.Warning("Not enought free rooms."); } - SPU_Status.SetValue((SPU_Status.GetValue() & ~0xff) | 1); + mfc.SPU_Status.SetValue((mfc.SPU_Status.GetValue() & ~0xff) | 1); break; default: @@ -278,8 +215,8 @@ public: switch(ch) { case SPU_RdInMbox: - if(!SPU_In_MBox.Pop(v)) v = 0; - SPU_Status.SetValue((SPU_Status.GetValue() & ~0xff00) | (SPU_In_MBox.GetCount() << 8)); + if(!mfc.SPU_In_MBox.Pop(v)) v = 0; + mfc.SPU_Status.SetValue((mfc.SPU_Status.GetValue() & ~0xff00) | (mfc.SPU_In_MBox.GetCount() << 8)); break; default: @@ -288,18 +225,18 @@ public: } } - bool IsGoodLSA(const u32 lsa) const { return Memory.IsGoodAddr(lsa); } - u8 ReadLS8 (const u32 lsa) const { return Memory.Read8 (lsa + (m_offset & 0x3fffc)); } - u16 ReadLS16 (const u32 lsa) const { return Memory.Read16 (lsa + m_offset); } - u32 ReadLS32 (const u32 lsa) const { return Memory.Read32 (lsa + m_offset); } - u64 ReadLS64 (const u32 lsa) const { return Memory.Read64 (lsa + m_offset); } - u128 ReadLS128(const u32 lsa) const { return Memory.Read128(lsa + m_offset); } + bool IsGoodLSA(const u32 lsa) const { return Memory.IsGoodAddr(lsa + m_offset); } + virtual u8 ReadLS8 (const u32 lsa) const { return Memory.Read8 (lsa + (m_offset & 0x3fffc)); } + virtual u16 ReadLS16 (const u32 lsa) const { return Memory.Read16 (lsa + m_offset); } + virtual u32 ReadLS32 (const u32 lsa) const { return Memory.Read32 (lsa + m_offset); } + virtual u64 ReadLS64 (const u32 lsa) const { return Memory.Read64 (lsa + m_offset); } + virtual u128 ReadLS128(const u32 lsa) const { return Memory.Read128(lsa + m_offset); } - void WriteLS8 (const u32 lsa, const u8& data) const { Memory.Write8 (lsa + m_offset, data); } - void WriteLS16 (const u32 lsa, const u16& data) const { Memory.Write16 (lsa + m_offset, data); } - void WriteLS32 (const u32 lsa, const u32& data) const { Memory.Write32 (lsa + m_offset, data); } - void WriteLS64 (const u32 lsa, const u64& data) const { Memory.Write64 (lsa + m_offset, data); } - void WriteLS128(const u32 lsa, const u128& data) const { Memory.Write128(lsa + m_offset, data); } + virtual void WriteLS8 (const u32 lsa, const u8& data) const { Memory.Write8 (lsa + m_offset, data); } + virtual void WriteLS16 (const u32 lsa, const u16& data) const { Memory.Write16 (lsa + m_offset, data); } + virtual void WriteLS32 (const u32 lsa, const u32& data) const { Memory.Write32 (lsa + m_offset, data); } + virtual void WriteLS64 (const u32 lsa, const u64& data) const { Memory.Write64 (lsa + m_offset, data); } + virtual void WriteLS128(const u32 lsa, const u128& data) const { Memory.Write128(lsa + m_offset, data); } public: SPUThread(PPCThreadType type = PPC_THREAD_SPU); diff --git a/rpcs3/Emu/FS/VFS.cpp b/rpcs3/Emu/FS/VFS.cpp index 3aa909ffd6..86e1e87b7d 100644 --- a/rpcs3/Emu/FS/VFS.cpp +++ b/rpcs3/Emu/FS/VFS.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "VFS.h" +#include "Emu\HDD\HDD.h" int sort_devices(const void* _a, const void* _b) { @@ -31,13 +32,23 @@ void VFS::UnMount(const wxString& ps3_path) { if(!m_devices[i].GetPs3Path().Cmp(ps3_path)) { - m_devices.RemoveAt(i); + delete &m_devices[i]; + m_devices.RemoveFAt(i); return; } } } +void VFS::UnMountAll() +{ + for(u32 i=0; i entries; + SaveLoadDevices(entries, true); + + for(uint i=0; i& res, bool is_load) +{ + IniEntry entries_count; + entries_count.Init("count", "VFSManager"); + + int count; + if(is_load) + { + count = entries_count.LoadValue(count); + + if(!count) + { + int idx; + idx = res.Move(new VFSManagerEntry()); + res[idx].path = "$(EmulatorDir)\\dev_hdd0\\"; + res[idx].mount = "/dev_hdd0/"; + res[idx].device = vfsDevice_LocalFile; + + idx = res.Move(new VFSManagerEntry()); + res[idx].path = "$(GameDir)"; + res[idx].mount = ""; + res[idx].device = vfsDevice_LocalFile; + + idx = res.Move(new VFSManagerEntry()); + res[idx].path = "$(GameDir)"; + res[idx].mount = "/"; + res[idx].device = vfsDevice_LocalFile; + + idx = res.Move(new VFSManagerEntry()); + res[idx].path = "$(GameDir)"; + res[idx].mount = "/app_home/"; + res[idx].device = vfsDevice_LocalFile; + return; + } + + res.SetCount(count); + } + else + { + count = res.GetCount(); + entries_count.SaveValue(res.GetCount()); + } + + for(int i=0; i entry_path; + IniEntry entry_device_path; + IniEntry entry_mount; + IniEntry entry_device; + + entry_path.Init(wxString::Format("path[%d]", i), "VFSManager"); + entry_device_path.Init(wxString::Format("device_path[%d]", i), "VFSManager"); + entry_mount.Init(wxString::Format("mount[%d]", i), "VFSManager"); + entry_device.Init(wxString::Format("device[%d]", i), "VFSManager"); + + if(is_load) + { + new (res + i) VFSManagerEntry(); + res[i].path = entry_path.LoadValue(wxEmptyString); + res[i].device_path = entry_device_path.LoadValue(wxEmptyString); + res[i].mount = entry_mount.LoadValue(wxEmptyString); + res[i].device = (vfsDeviceType)entry_device.LoadValue(vfsDevice_LocalFile); + } + else + { + entry_path.SaveValue(res[i].path.GetPtr()); + entry_device_path.SaveValue(res[i].device_path.GetPtr()); + entry_mount.SaveValue(res[i].mount.GetPtr()); + entry_device.SaveValue(res[i].device); + } + } +} \ No newline at end of file diff --git a/rpcs3/Emu/FS/VFS.h b/rpcs3/Emu/FS/VFS.h index 75e0f2dc49..798e951142 100644 --- a/rpcs3/Emu/FS/VFS.h +++ b/rpcs3/Emu/FS/VFS.h @@ -1,15 +1,42 @@ #pragma once #include "vfsDevice.h" +enum vfsDeviceType +{ + vfsDevice_LocalFile, + vfsDevice_HDD, +}; + +static const char* vfsDeviceTypeNames[] = +{ + "Local", + "HDD", +}; + +struct VFSManagerEntry +{ + ArrayString device_path; + ArrayString path; + ArrayString mount; + vfsDeviceType device; + + VFSManagerEntry() : device(vfsDevice_LocalFile) + { + } +}; + struct VFS { ArrayF m_devices; - void Mount(const wxString& ps3_path, const wxString& local_path, vfsDevice* device); void UnMount(const wxString& ps3_path); + void UnMountAll(); 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); + + void Init(const wxString& path); + void SaveLoadDevices(Array& res, bool is_load); }; \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsDevice.cpp b/rpcs3/Emu/FS/vfsDevice.cpp index 1560fa647b..2a05018c11 100644 --- a/rpcs3/Emu/FS/vfsDevice.cpp +++ b/rpcs3/Emu/FS/vfsDevice.cpp @@ -198,4 +198,19 @@ wxString vfsDevice::GetPs3Path(const wxString& l, const wxString& r) if(r.IsEmpty()) return GetPs3Path(l); return GetPs3Path(l + '/' + r, false); +} + +void vfsDevice::Lock() const +{ + m_mtx_lock.lock(); +} + +void vfsDevice::Unlock() const +{ + m_mtx_lock.unlock(); +} + +bool vfsDevice::TryLock() const +{ + return m_mtx_lock.try_lock(); } \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsDevice.h b/rpcs3/Emu/FS/vfsDevice.h index 47e7269dc5..a0545b113b 100644 --- a/rpcs3/Emu/FS/vfsDevice.h +++ b/rpcs3/Emu/FS/vfsDevice.h @@ -1,19 +1,23 @@ #pragma once #include "vfsStream.h" +#include enum vfsOpenMode { - vfsRead, - vfsWrite, - vfsReadWrite, - vfsWriteExcl, - vfsWriteAppend, + vfsRead = 0x1, + vfsWrite = 0x2, + vfsExcl = 0x4, + vfsAppend = 0x8, + vfsReadWrite = vfsRead | vfsWrite, + vfsWriteExcl = vfsWrite | vfsExcl, + vfsWriteAppend = vfsWrite | vfsAppend, }; class vfsDevice : public vfsStream { wxString m_ps3_path; wxString m_local_path; + mutable std::mutex m_mtx_lock; public: vfsDevice(const wxString& ps3_path, const wxString& local_path); @@ -36,4 +40,24 @@ public: static wxString GetWinPath(const wxString& l, const wxString& r); static wxString GetPs3Path(const wxString& p, bool is_dir = true); static wxString GetPs3Path(const wxString& l, const wxString& r); + + void Lock() const; + void Unlock() const; + bool TryLock() const; +}; + +class vfsDeviceLocker +{ + vfsDevice& m_device; + +public: + vfsDeviceLocker(vfsDevice& device) : m_device(device) + { + m_device.Lock(); + } + + ~vfsDeviceLocker() + { + m_device.Unlock(); + } }; \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsFileBase.cpp b/rpcs3/Emu/FS/vfsFileBase.cpp index 5606491d37..ed4afc0b1b 100644 --- a/rpcs3/Emu/FS/vfsFileBase.cpp +++ b/rpcs3/Emu/FS/vfsFileBase.cpp @@ -40,4 +40,4 @@ wxString vfsFileBase::GetPath() const 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 index 0b33913dc2..da776df4ec 100644 --- a/rpcs3/Emu/FS/vfsFileBase.h +++ b/rpcs3/Emu/FS/vfsFileBase.h @@ -21,4 +21,4 @@ public: */ 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 index 244cb68e8a..ec30d24ed0 100644 --- a/rpcs3/Emu/FS/vfsLocalFile.cpp +++ b/rpcs3/Emu/FS/vfsLocalFile.cpp @@ -92,4 +92,4 @@ u64 vfsLocalFile::Tell() const bool vfsLocalFile::IsOpened() const { return m_file.IsOpened() && vfsFileBase::IsOpened(); -} \ No newline at end of file +} diff --git a/rpcs3/Emu/GS/GL/GLGSRender.cpp b/rpcs3/Emu/GS/GL/GLGSRender.cpp index a2da8e5a7b..4f1cd9973a 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.cpp +++ b/rpcs3/Emu/GS/GL/GLGSRender.cpp @@ -1375,5 +1375,5 @@ void GLGSRender::Init() void GLGSRender::CloseOpenGL() { - Reset(); + //Reset(); } \ No newline at end of file diff --git a/rpcs3/Emu/HDD/HDD.cpp b/rpcs3/Emu/HDD/HDD.cpp new file mode 100644 index 0000000000..00f20832bd --- /dev/null +++ b/rpcs3/Emu/HDD/HDD.cpp @@ -0,0 +1,3 @@ +#include "stdafx.h" +#include "HDD.h" + diff --git a/rpcs3/Emu/HDD/HDD.h b/rpcs3/Emu/HDD/HDD.h new file mode 100644 index 0000000000..58438a6a4e --- /dev/null +++ b/rpcs3/Emu/HDD/HDD.h @@ -0,0 +1,864 @@ +#pragma once +#include "Emu/FS/vfsDevice.h" + +static const u64 g_hdd_magic = *(u64*)"PS3eHDD\0"; +static const u16 g_hdd_version = 0x0001; + +struct vfsHDD_Block +{ + struct + { + u64 is_used : 1; + u64 next_block : 63; + }; +} static const g_null_block = {0}, g_used_block = {1}; + +struct vfsHDD_Hdr : public vfsHDD_Block +{ + u64 magic; + u16 version; + u64 block_count; + u32 block_size; +}; + +enum vfsHDD_EntryType : u8 +{ + vfsHDD_Entry_Dir = 0, + vfsHDD_Entry_File = 1, + vfsHDD_Entry_Link = 2, +}; + +struct vfsHDD_Entry : public vfsHDD_Block +{ + u64 data_block; + vfsOpenMode access; + vfsHDD_EntryType type; + u64 size; + u64 ctime; + u64 mtime; + u64 atime; +}; + +class vfsHDDManager +{ +public: + static void CreateBlock(vfsHDD_Block& block) + { + block.is_used = true; + block.next_block = 0; + } + + static void CreateEntry(vfsHDD_Entry& entry) + { + memset(&entry, 0, sizeof(vfsHDD_Entry)); + u64 ctime = time(nullptr); + entry.atime = ctime; + entry.ctime = ctime; + entry.mtime = ctime; + entry.access = vfsReadWrite; + CreateBlock(entry); + } + + static void CreateHDD(const wxString& path, u64 size, u64 block_size) + { + wxFile f(path, wxFile::write); + + static const u64 cur_dir_block = 1; + + vfsHDD_Hdr hdr; + CreateBlock(hdr); + hdr.next_block = cur_dir_block; + hdr.magic = g_hdd_magic; + hdr.version = g_hdd_version; + hdr.block_count = (size + block_size) / block_size; + hdr.block_size = block_size; + f.Write(&hdr, sizeof(vfsHDD_Hdr)); + + { + vfsHDD_Entry entry; + CreateEntry(entry); + entry.type = vfsHDD_Entry_Dir; + entry.data_block = hdr.next_block; + entry.next_block = 0; + + f.Seek(cur_dir_block * hdr.block_size); + f.Write(&entry, sizeof(vfsHDD_Entry)); + f.Write("."); + } + + u8 null = 0; + f.Seek(hdr.block_count * hdr.block_size - sizeof(null)); + f.Write(&null, sizeof(null)); + } + + void Format() + { + } + + void AppendEntry(vfsHDD_Entry entry) + { + } +}; + + +class vfsHDDFile +{ + u64 m_info_block; + vfsHDD_Entry m_info; + const vfsHDD_Hdr& m_hdd_info; + vfsLocalFile& m_hdd; + u32 m_position; + u64 m_cur_block; + + bool goto_block(u64 n) + { + vfsHDD_Block block_info; + + if(m_info.data_block >= m_hdd_info.block_count) + { + return false; + } + + m_hdd.Seek(m_info.data_block * m_hdd_info.block_size); + block_info.next_block = m_info.data_block; + + for(u64 i=0; i= m_hdd_info.block_count) + { + return false; + } + + m_hdd.Seek(block_info.next_block * m_hdd_info.block_size); + m_hdd.Read(&block_info, sizeof(vfsHDD_Block)); + } + + return true; + } + + void RemoveBlocks(u64 start_block) + { + vfsHDD_Block block_info; + block_info.next_block = start_block; + + while(block_info.next_block && block_info.is_used) + { + u64 offset = block_info.next_block * m_hdd_info.block_size; + + ReadBlock(offset, block_info); + WriteBlock(offset, g_null_block); + } + } + + void WriteBlock(u64 block, const vfsHDD_Block& data) + { + m_hdd.Seek(block * m_hdd_info.block_size); + m_hdd.Write(&data, sizeof(vfsHDD_Block)); + } + + void ReadBlock(u64 block, vfsHDD_Block& data) + { + m_hdd.Seek(block * m_hdd_info.block_size); + m_hdd.Read(&data, sizeof(vfsHDD_Block)); + } + + void WriteEntry(u64 block, const vfsHDD_Entry& data) + { + m_hdd.Seek(block * m_hdd_info.block_size); + m_hdd.Write(&data, sizeof(vfsHDD_Entry)); + } + + void ReadEntry(u64 block, vfsHDD_Entry& data) + { + m_hdd.Seek(block * m_hdd_info.block_size); + m_hdd.Read(&data, sizeof(vfsHDD_Entry)); + } + + void ReadEntry(u64 block, vfsHDD_Entry& data, wxString& name) + { + m_hdd.Seek(block * m_hdd_info.block_size); + m_hdd.Read(&data, sizeof(vfsHDD_Entry)); + m_hdd.Read(wxStringBuffer(name, GetMaxNameLen()), GetMaxNameLen()); + } + + void ReadEntry(u64 block, wxString& name) + { + m_hdd.Seek(block * m_hdd_info.block_size + sizeof(vfsHDD_Entry)); + m_hdd.Read(wxStringBuffer(name, GetMaxNameLen()), GetMaxNameLen()); + } + + void WriteEntry(u64 block, const vfsHDD_Entry& data, const wxString& name) + { + m_hdd.Seek(block * m_hdd_info.block_size); + m_hdd.Write(&data, sizeof(vfsHDD_Entry)); + m_hdd.Write(name.c_str(), min(GetMaxNameLen() - 1, name.Len() + 1)); + } + + __forceinline u32 GetMaxNameLen() const + { + return m_hdd_info.block_size - sizeof(vfsHDD_Entry); + } + +public: + vfsHDDFile(vfsLocalFile& hdd, const vfsHDD_Hdr& hdd_info) + : m_hdd(hdd) + , m_hdd_info(hdd_info) + { + } + + ~vfsHDDFile() + { + } + + void Open(u64 info_block) + { + m_info_block = info_block; + ReadEntry(m_info_block, m_info); + m_position = 0; + m_cur_block = m_info.data_block; + } + + u64 FindFreeBlock() + { + vfsHDD_Block block_info; + + for(u64 i = 0; i(block_size - m_position, size); + + vfsHDD_Block cur_block_info; + m_hdd.Seek(m_cur_block * m_hdd_info.block_size); + m_hdd.Read(&cur_block_info, sizeof(vfsHDD_Block)); + m_hdd.Seek(m_cur_block * m_hdd_info.block_size + sizeof(vfsHDD_Block) + m_position); + m_hdd.Read(dst, rsize); + size -= rsize; + m_position += rsize; + if(!size) + { + return rsize; + } + + u64 offset = rsize; + + for(; size; size -= rsize, offset += rsize) + { + if(!cur_block_info.is_used || !cur_block_info.next_block || cur_block_info.next_block >= m_hdd_info.block_count) + { + return offset; + } + + m_cur_block = cur_block_info.next_block; + rsize = min(block_size, size); + + m_hdd.Seek(cur_block_info.next_block * m_hdd_info.block_size); + m_hdd.Read(&cur_block_info, sizeof(vfsHDD_Block)); + + if(m_hdd.Read((u8*)dst + offset, rsize) != rsize) + { + return offset; + } + } + + m_position = rsize; + + return offset; + } + + u64 Write(const void* src, u64 size) + { + if(!size) + return 0; + + vfsDeviceLocker lock(m_hdd); + + const u32 block_size = m_hdd_info.block_size - sizeof(vfsHDD_Block); + + if(!m_cur_block) + { + if(!m_info.data_block) + { + u64 new_block = FindFreeBlock(); + + if(!new_block) + { + return 0; + } + + WriteBlock(new_block, g_used_block); + m_info.data_block = new_block; + m_info.size = 0; + SaveInfo(); + } + + m_cur_block = m_info.data_block; + m_position = 0; + } + + u64 wsize = min(block_size - m_position, size); + + vfsHDD_Block block_info; + ReadBlock(m_cur_block, block_info); + + if(wsize) + { + m_hdd.Seek(m_cur_block * m_hdd_info.block_size + sizeof(vfsHDD_Block) + m_position); + m_hdd.Write(src, wsize); + size -= wsize; + m_info.size += wsize; + m_position += wsize; + SaveInfo(); + + if(!size) + return wsize; + } + + u64 last_block = m_cur_block; + block_info.is_used = true; + u64 offset = wsize; + + for(; size; size -= wsize, offset += wsize, m_info.size += wsize) + { + u64 new_block = FindFreeBlock(); + + if(!new_block) + { + m_position = 0; + SaveInfo(); + return offset; + } + + m_cur_block = new_block; + wsize = min(block_size, size); + + block_info.next_block = m_cur_block; + m_hdd.Seek(last_block * m_hdd_info.block_size); + if(m_hdd.Write(&block_info, sizeof(vfsHDD_Block)) != sizeof(vfsHDD_Block)) + { + m_position = 0; + SaveInfo(); + return offset; + } + + block_info.next_block = 0; + m_hdd.Seek(m_cur_block * m_hdd_info.block_size); + if(m_hdd.Write(&block_info, sizeof(vfsHDD_Block)) != sizeof(vfsHDD_Block)) + { + m_position = 0; + SaveInfo(); + return offset; + } + if((m_position = m_hdd.Write((u8*)src + offset, wsize)) != wsize) + { + m_info.size += wsize; + SaveInfo(); + return offset; + } + + last_block = m_cur_block; + } + + SaveInfo(); + m_position = wsize; + return offset; + } + + bool Eof() const + { + return m_info.size <= (m_cur_block * m_hdd_info.block_size + m_position); + } +}; + +class vfsHDD : public vfsFileBase +{ + vfsHDD_Hdr m_hdd_info; + vfsHDD_Entry m_cur_dir; + u64 m_cur_dir_block; + vfsHDDFile m_file; + vfsLocalFile m_hdd_file; + const wxString& m_hdd_path; + +public: + vfsHDD(const wxString& hdd_path) + : m_hdd_file(hdd_path, vfsReadWrite) + , m_file(m_hdd_file, m_hdd_info) + , m_hdd_path(hdd_path) + { + m_hdd_file.Read(&m_hdd_info, sizeof(vfsHDD_Hdr)); + m_cur_dir_block = m_hdd_info.next_block; + if(!m_hdd_info.block_size) + { + ConLog.Error("Bad block size!"); + m_hdd_info.block_size = 2048; + } + m_hdd_file.Seek(m_cur_dir_block * m_hdd_info.block_size); + m_hdd_file.Read(&m_cur_dir, sizeof(vfsHDD_Entry)); + } + + virtual vfsDevice* GetNew() + { + return new vfsHDD(m_hdd_path); + } + + __forceinline u32 GetMaxNameLen() const + { + return m_hdd_info.block_size - sizeof(vfsHDD_Entry); + } + + bool SearchEntry(const wxString& name, u64& entry_block, u64* parent_block = nullptr) + { + u64 last_block = 0; + u64 block = m_cur_dir_block; + vfsHDD_Entry entry; + wxString buf; + + while(block) + { + ReadEntry(block, entry, buf); + + if(name.CmpNoCase(buf) == 0) + { + entry_block = block; + if(parent_block) + *parent_block = last_block; + + return true; + } + + last_block = block; + block = entry.is_used ? entry.next_block : 0ULL; + } + + return false; + } + + int OpenDir(const wxString& name) + { + ConLog.Warning("OpenDir(%s)", name); + u64 entry_block; + if(!SearchEntry(name, entry_block)) + return -1; + + m_hdd_file.Seek(entry_block * m_hdd_info.block_size); + vfsHDD_Entry entry; + m_hdd_file.Read(&entry, sizeof(vfsHDD_Entry)); + if(entry.type == vfsHDD_Entry_File) + return 1; + + m_cur_dir_block = entry.data_block; + ReadEntry(m_cur_dir_block, m_cur_dir); + + return 0; + } + + bool Rename(const wxString& from, const wxString& to) + { + u64 entry_block; + if(!SearchEntry(from, entry_block)) + { + return false; + } + + vfsHDD_Entry entry; + ReadEntry(entry_block, entry); + WriteEntry(entry_block, entry, to); + + return true; + } + + u64 FindFreeBlock() + { + vfsHDD_Block block_info; + + for(u64 i = 0; i(GetMaxNameLen() - 1, name.Len() + 1)); + } + + bool Create(vfsHDD_EntryType type, const wxString& name) + { + if(HasEntry(name)) + { + return false; + } + + u64 new_block = FindFreeBlock(); + + ConLog.Write("CREATION ENTRY AT 0x%llx", new_block); + if(!new_block) + { + return false; + } + + WriteBlock(new_block, g_used_block); + + { + vfsHDD_Entry new_entry; + vfsHDDManager::CreateEntry(new_entry); + new_entry.next_block = 0; + new_entry.type = type; + + if(type == vfsHDD_Entry_Dir) + { + u64 block_cur = FindFreeBlock(); + + if(!block_cur) + { + return false; + } + + WriteBlock(block_cur, g_used_block); + + u64 block_last = FindFreeBlock(); + + if(!block_last) + { + return false; + } + + WriteBlock(block_last, g_used_block); + + vfsHDD_Entry entry_cur, entry_last; + vfsHDDManager::CreateEntry(entry_cur); + vfsHDDManager::CreateEntry(entry_last); + + entry_cur.type = vfsHDD_Entry_Dir; + entry_cur.data_block = block_cur; + entry_cur.next_block = block_last; + + entry_last.type = vfsHDD_Entry_Dir; + entry_last.data_block = m_cur_dir_block; + entry_last.next_block = 0; + + new_entry.data_block = block_cur; + + WriteEntry(block_cur, entry_cur, "."); + WriteEntry(block_last, entry_last, ".."); + } + + WriteEntry(new_block, new_entry, name); + } + + { + u64 block = m_cur_dir_block; + + vfsHDD_Block tmp; + while(block) + { + ReadBlock(block, tmp); + + if(!tmp.next_block) + break; + + block = tmp.next_block; + } + + tmp.next_block = new_block; + WriteBlock(block, tmp); + } + + return true; + } + + bool GetFirstEntry(u64& block, vfsHDD_Entry& entry, wxString& name) + { + if(!m_cur_dir_block) + { + return false; + } + + ReadEntry(m_cur_dir_block, entry, name); + block = entry.is_used ? entry.next_block : 0; + + return true; + } + + bool GetNextEntry(u64& block, vfsHDD_Entry& entry, wxString& name) + { + if(!block) + { + return false; + } + + ReadEntry(block, entry, name); + + block = entry.is_used ? entry.next_block : 0; + return true; + } + + virtual bool Open(const wxString& path, vfsOpenMode mode = vfsRead) + { + const char* s = path.c_str(); + u64 from = 0; + u64 pos = 0; + u64 file_pos = -1; + + do + { + if(s[pos] == '\\' || s[pos] == '\0') + { + if(file_pos != -1) + { + return false; + } + + if(from != -1) + { + if(pos - from > 1) + { + int res = OpenDir(wxString(s + from, pos)); + if(res == -1) + { + return false; + } + + if(res == 1) + { + file_pos = from; + } + } + + from = pos; + } + else + { + from = pos; + } + } + } + while(s[pos++] != '\0'); + + if(file_pos == -1) + { + return false; + } + + u64 file_block; + if(!SearchEntry(wxString(s + file_pos), file_block)) + { + return false; + } + + ConLog.Write("ENTRY FINDED AT 0x%llx", file_block); + m_file.Open(file_block); + + return vfsFileBase::Open(path, mode); + } + + bool HasEntry(const wxString& name) + { + u64 file_block; + if(!SearchEntry(name, file_block)) + { + return false; + } + + return true; + } + + void RemoveBlocksDir(u64 start_block) + { + wxString name; + u64 block = start_block; + vfsHDD_Entry entry; + + while(block) + { + ReadEntry(block, entry, name); + WriteBlock(block, g_null_block); + + if(entry.type == vfsHDD_Entry_Dir && name != "." && name != "..") + { + ConLog.Warning("removing sub folder '%s'", name); + RemoveBlocksDir(entry.data_block); + } + else if(entry.type == vfsHDD_Entry_File) + { + RemoveBlocksFile(entry.data_block); + } + + block = entry.next_block; + } + } + + void RemoveBlocksFile(u64 start_block) + { + u64 block = start_block; + vfsHDD_Block block_data; + + while(block) + { + ReadBlock(block, block_data); + WriteBlock(block, g_null_block); + + block = block_data.next_block; + } + } + + bool RemoveEntry(const wxString& name) + { + u64 entry_block, parent_entry; + if(!SearchEntry(name, entry_block, &parent_entry)) + { + return false; + } + + vfsHDD_Entry entry; + ReadEntry(entry_block, entry); + if(entry.type == vfsHDD_Entry_Dir) + { + RemoveBlocksDir(entry.data_block); + } + else if(entry.type == vfsHDD_Entry_File) + { + RemoveBlocksFile(entry.data_block); + } + + if(parent_entry) + { + u64 next = entry.next_block; + ReadEntry(parent_entry, entry); + entry.next_block = next; + WriteEntry(parent_entry, entry); + } + WriteBlock(entry_block, g_null_block); + return true; + } + + virtual bool Create(const wxString& path) + { + return false; + } + + virtual u32 Write(const void* src, u32 size) + { + return vfsFileBase::Write(src, m_file.Write(src, size)); + } + + virtual u32 Read(void* dst, u32 size) + { + return vfsFileBase::Read(dst, m_file.Read(dst, size)); + } + + virtual u64 Seek(s64 offset, vfsSeekMode mode = vfsSeekSet) + { + switch(mode) + { + case vfsSeekCur: + m_file.Seek(Tell() + offset); + break; + + case vfsSeekSet: + m_file.Seek(offset); + break; + + case vfsSeekEnd: + m_file.Seek(m_file.GetSize() + offset); + break; + } + + return vfsFileBase::Seek(offset, mode); + } + + virtual bool Eof() + { + return m_file.Eof(); + } + + virtual u64 GetSize() + { + return m_file.GetSize(); + } +}; \ No newline at end of file diff --git a/rpcs3/Emu/Memory/Memory.h b/rpcs3/Emu/Memory/Memory.h index 0d56b45863..312ae36c91 100644 --- a/rpcs3/Emu/Memory/Memory.h +++ b/rpcs3/Emu/Memory/Memory.h @@ -336,6 +336,7 @@ public: } MemoryBlocks.Add((new MemoryMirror())->SetRange(GetMemFromAddr(src_addr), dst_addr, size)); + ConLog.Warning("memory mapped 0x%llx to 0x%llx size=0x%x", src_addr, dst_addr, size); return true; } diff --git a/rpcs3/Emu/Memory/MemoryBlock.h b/rpcs3/Emu/Memory/MemoryBlock.h index 05cfbc1f18..63687f4a38 100644 --- a/rpcs3/Emu/Memory/MemoryBlock.h +++ b/rpcs3/Emu/Memory/MemoryBlock.h @@ -36,7 +36,7 @@ protected: public: MemoryBlock(); - ~MemoryBlock(); + virtual ~MemoryBlock(); private: void Init(); diff --git a/rpcs3/Emu/SysCalls/FuncList.cpp b/rpcs3/Emu/SysCalls/FuncList.cpp index 1fe792894f..0dbc923703 100644 --- a/rpcs3/Emu/SysCalls/FuncList.cpp +++ b/rpcs3/Emu/SysCalls/FuncList.cpp @@ -318,7 +318,7 @@ s64 SysCalls::DoFunc(const u32 id) case 0x5f909b17: FUNC_LOG_ERROR("TODO: _cellGcmFunc1"); case 0x626e8518: FUNC_LOG_ERROR("TODO: cellGcmMapEaIoAddressWithFlags"); case 0x63387071: FUNC_LOG_ERROR("TODO: cellGcmGetLastFlipTime"); - case 0x63441cb4: FUNC_LOG_ERROR("TODO: cellGcmMapEaIoAddress"); + case 0x63441cb4: return cellGcmMapEaIoAddress(SC_ARGS_3); //FUNC_LOG_ERROR("TODO: cellGcmMapEaIoAddress"); case 0x657571f7: FUNC_LOG_ERROR("TODO: cellGcmGetTileInfo"); case 0x661fe266: FUNC_LOG_ERROR("TODO: _cellGcmFunc12"); case 0x688b8ac9: FUNC_LOG_ERROR("TODO: _cellGcmFunc38"); diff --git a/rpcs3/Emu/SysCalls/Modules.cpp b/rpcs3/Emu/SysCalls/Modules.cpp index a32b6d4ac4..9e3c98bb2f 100644 --- a/rpcs3/Emu/SysCalls/Modules.cpp +++ b/rpcs3/Emu/SysCalls/Modules.cpp @@ -65,51 +65,51 @@ static const g_module_list[] = {0x0034, "cellSubdisplay"}, {0x0035, "cellSysutilRec"}, {0x0036, "cellVideoExport"}, - {0x0037, "cellSysutilGameExec"}, + {0x0037, "cellGameExec"}, {0x0038, "sceNp2"}, {0x0039, "cellSysutilAp"}, {0x003a, "cellSysutilNpClans"}, {0x003b, "cellSysutilOskExt"}, {0x003c, "cellVdecDivx"}, {0x003d, "cellJpgEnc"}, - {0x003e, "cellSysutilGame"}, + {0x003e, "cellGame"}, {0x003f, "cellBgdl"}, {0x0040, "cellFreetypeTT"}, {0x0041, "cellSysutilVideoUpload"}, {0x0042, "cellSysutilSysconfExt"}, {0x0043, "cellFiber"}, - {0x0044, "cellSysutilNpCommerce2"}, - {0x0045, "cellSysutilNpTus"}, + {0x0044, "cellNpCommerce2"}, + {0x0045, "cellNpTus"}, {0x0046, "cellVoice"}, {0x0047, "cellAdecCelp8"}, {0x0048, "cellCelp8Enc"}, - {0x0049, "cellSysutilLicenseArea"}, - {0x004a, "cellSysutilMusic2"}, - {0x004e, "cellSysutilScreenshot"}, - {0x004f, "cellSysutilMusicDecode"}, - {0x0050, "cellSysutilSpursJq"}, + {0x0049, "cellLicenseArea"}, + {0x004a, "cellMusic2"}, + {0x004e, "cellScreenshot"}, + {0x004f, "cellMusicDecode"}, + {0x0050, "cellSpursJq"}, {0x0052, "cellPngEnc"}, - {0x0053, "cellSysutilMusicDecode2"}, + {0x0053, "cellMusicDecode2"}, {0x0055, "cellSync2"}, - {0x0056, "cellSysutilNpUtil"}, + {0x0056, "cellNpUtil"}, {0x0057, "cellRudp"}, - {0x0059, "cellSysutilNpSns"}, + {0x0059, "cellNpSns"}, {0x005a, "cellGem"}, - {0xf00a, "cellScelpEnc"}, + {0xf00a, "cellCelpEnc"}, {0xf010, "cellGifDec"}, {0xf019, "cellAdecCelp"}, {0xf01b, "cellAdecM2bc"}, {0xf01d, "cellAdecM4aac"}, {0xf01e, "cellAdecMp3"}, {0xf023, "cellImejp"}, - {0xf028, "cellSysutilMusic"}, + {0xf028, "cellMusic"}, {0xf029, "cellPhotoExport"}, {0xf02a, "cellPrint"}, {0xf02b, "cellPhotoImport"}, {0xf02c, "cellMusicExport"}, {0xf02e, "cellPhotoDecode"}, - {0xf02f, "cellSysutilSearch"}, - {0xf030, "cellSysutilAvchat2"}, + {0xf02f, "cellSearch"}, + {0xf030, "cellAvchat2"}, {0xf034, "cellSailRec"}, {0xf035, "sceNpTrophy"}, {0xf053, "cellAdecAt3multi"}, diff --git a/rpcs3/Emu/SysCalls/Modules.h b/rpcs3/Emu/SysCalls/Modules.h index d3678e4ea0..b6b98b0e68 100644 --- a/rpcs3/Emu/SysCalls/Modules.h +++ b/rpcs3/Emu/SysCalls/Modules.h @@ -65,9 +65,11 @@ public: template bool CheckId(u32 id, T*& data); u32 GetNewId(void* data = nullptr, u8 flags = 0); - __forceinline void Module::AddFunc(u32 id, func_caller* func) + + template + __forceinline void AddFunc(u32 id, T func) { - m_funcs_list.Move(new ModuleFunc(id, func)); + m_funcs_list.Move(new ModuleFunc(id, bind_func(func))); } }; diff --git a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp index ae7eb0746e..ba4990e08b 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp @@ -19,20 +19,23 @@ s64 cellGcmSetFlipCommandWithWaitLabel() void cellGcmSys_init() { - cellGcmSys.AddFunc(0x055bd74d, bind_func(cellGcmGetTiledPitchSize)); - cellGcmSys.AddFunc(0x15bae46b, bind_func(cellGcmInit)); - cellGcmSys.AddFunc(0x21397818, bind_func(cellGcmFlush)); - cellGcmSys.AddFunc(0x21ac3697, bind_func(cellGcmAddressToOffset)); - cellGcmSys.AddFunc(0x3a33c1fd, bind_func(cellGcmFunc15)); - cellGcmSys.AddFunc(0x4ae8d215, bind_func(cellGcmSetFlipMode)); - cellGcmSys.AddFunc(0x5e2ee0f0, bind_func(cellGcmGetDefaultCommandWordSize)); - cellGcmSys.AddFunc(0x72a577ce, bind_func(cellGcmGetFlipStatus)); - cellGcmSys.AddFunc(0x8cdf8c70, bind_func(cellGcmGetDefaultSegmentWordSize)); - cellGcmSys.AddFunc(0x9ba451e4, bind_func(cellGcmSetDefaultFifoSize)); - cellGcmSys.AddFunc(0xa53d12ae, bind_func(cellGcmSetDisplayBuffer)); - cellGcmSys.AddFunc(0xa547adde, bind_func(cellGcmGetControlRegister)); - cellGcmSys.AddFunc(0xb2e761d4, bind_func(cellGcmResetFlipStatus)); - cellGcmSys.AddFunc(0xd8f88e1a, bind_func(cellGcmSetFlipCommandWithWaitLabel)); - cellGcmSys.AddFunc(0xe315a0b2, bind_func(cellGcmGetConfiguration)); - cellGcmSys.AddFunc(0x9dc04436, bind_func(cellGcmBindZcull)); + cellGcmSys.AddFunc(0x055bd74d, cellGcmGetTiledPitchSize); + cellGcmSys.AddFunc(0x15bae46b, cellGcmInit); + cellGcmSys.AddFunc(0x21397818, cellGcmFlush); + cellGcmSys.AddFunc(0x21ac3697, cellGcmAddressToOffset); + cellGcmSys.AddFunc(0x3a33c1fd, cellGcmFunc15); + cellGcmSys.AddFunc(0x4ae8d215, cellGcmSetFlipMode); + cellGcmSys.AddFunc(0x5e2ee0f0, cellGcmGetDefaultCommandWordSize); + cellGcmSys.AddFunc(0x72a577ce, cellGcmGetFlipStatus); + cellGcmSys.AddFunc(0x8cdf8c70, cellGcmGetDefaultSegmentWordSize); + cellGcmSys.AddFunc(0x9ba451e4, cellGcmSetDefaultFifoSize); + cellGcmSys.AddFunc(0xa53d12ae, cellGcmSetDisplayBuffer); + cellGcmSys.AddFunc(0xa547adde, cellGcmGetControlRegister); + cellGcmSys.AddFunc(0xb2e761d4, cellGcmResetFlipStatus); + cellGcmSys.AddFunc(0xd8f88e1a, cellGcmSetFlipCommandWithWaitLabel); + cellGcmSys.AddFunc(0xe315a0b2, cellGcmGetConfiguration); + cellGcmSys.AddFunc(0x9dc04436, cellGcmBindZcull); + + cellGcmSys.AddFunc(0xd9b7653e, cellGcmUnbindTile); + cellGcmSys.AddFunc(0xa75640e8, cellGcmUnbindZcull); } diff --git a/rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp b/rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp index fb7bb2e3c7..3f18a17da7 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSysmodule.cpp @@ -86,10 +86,10 @@ int cellSysmoduleIsLoaded(u16 id) void cellSysmodule_init() { - cellSysmodule.AddFunc(0x63ff6ff9, bind_func(cellSysmoduleInitialize)); - cellSysmodule.AddFunc(0x96c07adf, bind_func(cellSysmoduleFinalize)); - cellSysmodule.AddFunc(0xa193143c, bind_func(cellSysmoduleSetMemcontainer)); - cellSysmodule.AddFunc(0x32267a31, bind_func(cellSysmoduleLoadModule)); - cellSysmodule.AddFunc(0x112a5ee9, bind_func(cellSysmoduleUnloadModule)); - cellSysmodule.AddFunc(0x5a59e258, bind_func(cellSysmoduleIsLoaded)); + cellSysmodule.AddFunc(0x63ff6ff9, cellSysmoduleInitialize); + cellSysmodule.AddFunc(0x96c07adf, cellSysmoduleFinalize); + cellSysmodule.AddFunc(0xa193143c, cellSysmoduleSetMemcontainer); + cellSysmodule.AddFunc(0x32267a31, cellSysmoduleLoadModule); + cellSysmodule.AddFunc(0x112a5ee9, cellSysmoduleUnloadModule); + cellSysmodule.AddFunc(0x5a59e258, cellSysmoduleIsLoaded); } diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp index 42a0f50e17..ab26b6d773 100644 --- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp @@ -49,34 +49,34 @@ s64 sys_strlen(u32 addr) void sysPrxForUser_init() { - sysPrxForUser.AddFunc(0x744680a2, bind_func(sys_initialize_tls)); + sysPrxForUser.AddFunc(0x744680a2, sys_initialize_tls); - sysPrxForUser.AddFunc(0x2f85c0ef, bind_func(sys_lwmutex_create)); - sysPrxForUser.AddFunc(0xc3476d0c, bind_func(sys_lwmutex_destroy)); - sysPrxForUser.AddFunc(0x1573dc3f, bind_func(sys_lwmutex_lock)); - sysPrxForUser.AddFunc(0xaeb78725, bind_func(sys_lwmutex_trylock)); - sysPrxForUser.AddFunc(0x1bc200f4, bind_func(sys_lwmutex_unlock)); + sysPrxForUser.AddFunc(0x2f85c0ef, sys_lwmutex_create); + sysPrxForUser.AddFunc(0xc3476d0c, sys_lwmutex_destroy); + sysPrxForUser.AddFunc(0x1573dc3f, sys_lwmutex_lock); + sysPrxForUser.AddFunc(0xaeb78725, sys_lwmutex_trylock); + sysPrxForUser.AddFunc(0x1bc200f4, sys_lwmutex_unlock); - sysPrxForUser.AddFunc(0x8461e528, bind_func(sys_time_get_system_time)); + sysPrxForUser.AddFunc(0x8461e528, sys_time_get_system_time); - sysPrxForUser.AddFunc(0xe6f2c1e7, bind_func(sys_process_exit)); - sysPrxForUser.AddFunc(0x2c847572, bind_func(sys_process_atexitspawn)); - sysPrxForUser.AddFunc(0x96328741, bind_func(sys_process_at_Exitspawn)); + sysPrxForUser.AddFunc(0xe6f2c1e7, sys_process_exit); + sysPrxForUser.AddFunc(0x2c847572, sys_process_atexitspawn); + sysPrxForUser.AddFunc(0x96328741, sys_process_at_Exitspawn); - sysPrxForUser.AddFunc(0x24a1ea07, bind_func(sys_ppu_thread_create)); - sysPrxForUser.AddFunc(0x350d454e, bind_func(sys_ppu_thread_get_id)); - sysPrxForUser.AddFunc(0xaff080a4, bind_func(sys_ppu_thread_exit)); - sysPrxForUser.AddFunc(0xa3e3be68, bind_func(sys_ppu_thread_once)); + sysPrxForUser.AddFunc(0x24a1ea07, sys_ppu_thread_create); + sysPrxForUser.AddFunc(0x350d454e, sys_ppu_thread_get_id); + sysPrxForUser.AddFunc(0xaff080a4, sys_ppu_thread_exit); + sysPrxForUser.AddFunc(0xa3e3be68, sys_ppu_thread_once); - sysPrxForUser.AddFunc(0x45fe2fce, bind_func(sys_spu_printf_initialize)); + sysPrxForUser.AddFunc(0x45fe2fce, sys_spu_printf_initialize); - sysPrxForUser.AddFunc(0x42b23552, bind_func(sys_prx_register_library)); - sysPrxForUser.AddFunc(0xa2c7ba64, bind_func(sys_prx_exitspawn_with_level)); + sysPrxForUser.AddFunc(0x42b23552, sys_prx_register_library); + sysPrxForUser.AddFunc(0xa2c7ba64, sys_prx_exitspawn_with_level); - sysPrxForUser.AddFunc(0x2d36462b, bind_func(sys_strlen)); + sysPrxForUser.AddFunc(0x2d36462b, sys_strlen); - sysPrxForUser.AddFunc(0x35168520, bind_func(sys_heap_malloc)); - //sysPrxForUser.AddFunc(0xaede4b03, bind_func(sys_heap_free)); - //sysPrxForUser.AddFunc(0x8a561d92, bind_func(sys_heap_delete_heap)); - sysPrxForUser.AddFunc(0xb2fcf2c8, bind_func(sys_heap_create_heap)); + sysPrxForUser.AddFunc(0x35168520, sys_heap_malloc); + //sysPrxForUser.AddFunc(0xaede4b03, sys_heap_free); + //sysPrxForUser.AddFunc(0x8a561d92, sys_heap_delete_heap); + sysPrxForUser.AddFunc(0xb2fcf2c8, sys_heap_create_heap); } diff --git a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp index 56bd3ccc9c..c4d4736cff 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp @@ -7,18 +7,18 @@ Module sys_fs(0x000e, sys_fs_init); void 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.AddFunc(0x718bf5f8, cellFsOpen); + sys_fs.AddFunc(0x4d5ff8e2, cellFsRead); + sys_fs.AddFunc(0xecdcf2ab, cellFsWrite); + sys_fs.AddFunc(0x2cb51f0d, cellFsClose); + sys_fs.AddFunc(0x3f61245c, cellFsOpendir); + sys_fs.AddFunc(0x5c74903d, cellFsReaddir); + sys_fs.AddFunc(0xff42dcc3, cellFsClosedir); + sys_fs.AddFunc(0x7de6dced, cellFsStat); + sys_fs.AddFunc(0xef3efa34, cellFsFstat); + sys_fs.AddFunc(0xba901fe6, cellFsMkdir); + sys_fs.AddFunc(0xf12eecc8, cellFsRename); + sys_fs.AddFunc(0x2796fdf3, cellFsRmdir); + sys_fs.AddFunc(0x7f4677a8, cellFsUnlink); + sys_fs.AddFunc(0xa397d042, cellFsLseek); } diff --git a/rpcs3/Emu/SysCalls/Modules/sys_io.cpp b/rpcs3/Emu/SysCalls/Modules/sys_io.cpp index f01c950d8d..385f5c99d1 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_io.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_io.cpp @@ -7,12 +7,12 @@ Module sys_io(0x0017, sys_io_init); void 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.AddFunc(0x1cf98800, cellPadInit); + sys_io.AddFunc(0x4d9b75d5, cellPadEnd); + sys_io.AddFunc(0x0d5f2c14, cellPadClearBuf); + sys_io.AddFunc(0x8b72cda1, cellPadGetData); + sys_io.AddFunc(0x6bc09c61, cellPadGetDataExtra); + sys_io.AddFunc(0xf65544ee, cellPadSetActDirect); + sys_io.AddFunc(0xa703a51d, cellPadGetInfo2); + sys_io.AddFunc(0x578e3c98, cellPadSetPortSetting); } diff --git a/rpcs3/Emu/SysCalls/SysCalls.cpp b/rpcs3/Emu/SysCalls/SysCalls.cpp index 6adafdddf0..2692ccc9b9 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.cpp +++ b/rpcs3/Emu/SysCalls/SysCalls.cpp @@ -14,7 +14,7 @@ static func_caller* sc_table[1024] = null_func, null_func, null_func, null_func, null_func, //34 null_func, null_func, null_func, null_func, null_func, //39 null_func, bind_func(sys_ppu_thread_exit), null_func, bind_func(sys_ppu_thread_yield), bind_func(sys_ppu_thread_join), //44 - bind_func(sys_ppu_thread_detach), bind_func(sys_ppu_thread_detach), bind_func(sys_ppu_thread_get_join_state), bind_func(sys_ppu_thread_set_priority), bind_func(sys_ppu_thread_get_priority), //49 + bind_func(sys_ppu_thread_detach), bind_func(sys_ppu_thread_get_join_state), bind_func(sys_ppu_thread_set_priority), bind_func(sys_ppu_thread_get_priority), bind_func(sys_ppu_thread_get_stack_information), //49 bind_func(sys_ppu_thread_stop), bind_func(sys_ppu_thread_restart), bind_func(sys_ppu_thread_create), null_func, null_func, //54 null_func, null_func, null_func, null_func, null_func, //59 null_func, null_func, null_func, null_func, null_func, //64 diff --git a/rpcs3/Emu/SysCalls/SysCalls.h b/rpcs3/Emu/SysCalls/SysCalls.h index 554f8992e8..8553cc0d86 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.h +++ b/rpcs3/Emu/SysCalls/SysCalls.h @@ -235,6 +235,9 @@ extern int cellGcmSetFlipMode(u32 mode); extern u32 cellGcmGetDefaultCommandWordSize(); extern u32 cellGcmGetDefaultSegmentWordSize(); extern int cellGcmSetDefaultFifoSize(u32 bufferSize, u32 segmentSize); +extern int cellGcmMapEaIoAddress(const u32 ea, const u32 io, const u32 size); +extern int cellGcmUnbindTile(u8 index); +extern int cellGcmUnbindZcull(u8 index); //sys_tty extern int sys_tty_read(u32 ch, u64 buf_addr, u32 len, u64 preadlen_addr); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp index fe0cf1de46..29b5c28aaf 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp @@ -63,7 +63,7 @@ int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout) { u32 val; thr->SPU_OutIntr_Mbox.Pop(val); - if(!thr->SPU_Out_MBox.Pop(val)) val = 0; + if(!thr->mfc.SPU_Out_MBox.Pop(val)) val = 0; equeue->ports[i]->data1 = val; equeue->ports[i]->data2 = 0; equeue->ports[i]->data3 = 0; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp index fb2461e7c0..b18323c1a1 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp @@ -135,7 +135,7 @@ int cellGcmSetDisplayBuffer(u32 id, u32 offset, u32 pitch, u32 width, u32 height u32 cellGcmGetLabelAddress(u8 index) { - cellGcmSys.Error("cellGcmGetLabelAddress(index=%d)", index); + cellGcmSys.Log("cellGcmGetLabelAddress(index=%d)", index); return Memory.RSXCMDMem.GetStartAddr() + 0x10 * index; } @@ -234,3 +234,28 @@ int cellGcmSetDefaultFifoSize(u32 bufferSize, u32 segmentSize) cellGcmSys.Warning("cellGcmSetDefaultFifoSize(bufferSize=0x%x, segmentSize=0x%x)", bufferSize, segmentSize); return CELL_OK; } + +int cellGcmMapEaIoAddress(const u32 ea, const u32 io, const u32 size) +{ + cellGcmSys.Warning("cellGcmMapEaIoAddress(ea=0x%x, io=0x%x, size=0x%x)", ea, io, size); + Memory.Map(io, ea, size); + return CELL_OK; +} + +int cellGcmUnbindTile(u8 index) +{ + cellGcmSys.Warning("cellGcmUnbindTile(index=%d)", index); + if(index >= 15) + return CELL_EINVAL; + + return CELL_OK; +} + +int cellGcmUnbindZcull(u8 index) +{ + cellGcmSys.Warning("cellGcmUnbindZcull(index=%d)", index); + if(index >= 8) + return CELL_EINVAL; + + return CELL_OK; +} \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp index e7e2c41fe3..65e98f195c 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp @@ -1,158 +1,5 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" -/* -struct MemContiner -{ - u8* continer; - u32 num; - - void Create(u32 size) - { - continer = new u8[size]; - } - - void Delete() - { - if(continer != NULL) free(continer); - } - - ~MemContiner() - { - Delete(); - } -}; - -class MemContiners : private SysCallBase -{ - SysCallsArraysList continers; - -public: - MemContiners() : SysCallBase("MemContainers") - { - } - - u64 AddContiner(const u64 size) - { - const u64 id = continers.Add(); - bool error; - MemContiner& data = *continers.GetDataById(id, &error); - if(error) - { - ConLog.Error("%s error: id [%d] is not found!", module_name, id); - return 0; - } - - data.Create(size); - - return id; - } - - void DeleteContiner(const u64 id) - { - bool error; - MemContiner& data = *continers.GetDataById(id, &error); - if(error) - { - ConLog.Error("%s error: id [%d] is not found!", module_name, id); - return; - } - data.Delete(); - continers.RemoveById(id); - } -}; - -MemContiners continers; -*/ -/* -int SysCalls::lv2MemContinerCreate(PPUThread& CPU) -{ - u64& continer = CPU.GPR[3]; - u32 size = CPU.GPR[4]; - - ConLog.Warning("lv2MemContinerCreate[size: 0x%x]", size); - //continer = continers.AddContiner(size); - return 0; -} - -int SysCalls::lv2MemContinerDestroy(PPUThread& CPU) -{ - u32 container = CPU.GPR[3]; - ConLog.Warning("lv2MemContinerDestroy[container: 0x%x]", container); - //continers.DeleteContiner(container); - return 0; -}*/ -/* -static const u32 max_user_mem = 0x0d500000; //100mb -u32 free_user_mem = max_user_mem; -static u64 addr_user_mem = 0; - -struct MemoryInfo -{ - u32 free_user_mem; - u32 aviable_user_mem; -}; - -enum -{ - SYS_MEMORY_PAGE_SIZE_1M = 0x400, - SYS_MEMORY_PAGE_SIZE_64K = 0x200, -}; - -int SysCalls::sys_memory_allocate(u32 size, u64 flags, u64 alloc_addr) -{ - //int sys_memory_allocate(size_t size, uint64_t flags, sys_addr_t * alloc_addr); - - const u64 size = CPU.GPR[3]; - const u64 flags = CPU.GPR[4]; - const u64 alloc_addr = CPU.GPR[5]; - - ConLog.Write("lv2MemAllocate: size: 0x%llx, flags: 0x%llx, alloc_addr: 0x%llx", size, flags, alloc_addr); - - //u32 addr = 0; - switch(flags) - { - case SYS_MEMORY_PAGE_SIZE_1M: - if(size & 0xfffff) return CELL_EALIGN; - //addr = Memory.Alloc(size, 0x100000); - break; - - case SYS_MEMORY_PAGE_SIZE_64K: - if(size & 0xffff) return CELL_EALIGN; - //addr = Memory.Alloc(size, 0x10000); - break; - - default: return CELL_EINVAL; - } - - u32 num = Memory.MemoryBlocks.GetCount(); - Memory.MemoryBlocks.Add(new MemoryBlock()); - Memory.MemoryBlocks[num].SetRange(Memory.MemoryBlocks[num - 1].GetEndAddr(), size); - - Memory.Write32(alloc_addr, Memory.MemoryBlocks[num].GetStartAddr()); - ConLog.Write("Test..."); - Memory.Write32(Memory.MemoryBlocks[num].GetStartAddr(), 0xfff); - if(Memory.Read32(Memory.MemoryBlocks[num].GetStartAddr()) != 0xfff) - { - ConLog.Write("Test faild"); - } - else - { - ConLog.Write("Test OK"); - Memory.Write32(Memory.MemoryBlocks[num].GetStartAddr(), 0x0); - } - - return CELL_OK; -} - -int SysCalls::sys_memory_get_user_memory_size(PPUThread& CPU) -{ - ConLog.Write("lv2MemGetUserMemorySize: r3=0x%llx", CPU.GPR[3]); - //int sys_memory_get_user_memory_size(sys_memory_info_t * mem_info); - MemoryInfo& memoryinfo = *(MemoryInfo*)Memory.GetMemFromAddr(CPU.GPR[3]); - memoryinfo.aviable_user_mem = Memory.Reverse32(free_user_mem); - memoryinfo.free_user_mem = Memory.Reverse32(free_user_mem); - return CELL_OK; -}*/ SysCallBase sc_mem("memory"); @@ -248,11 +95,13 @@ int sys_memory_free(u32 start_addr) struct mmapper_info { + u64 addr; u32 size; u32 flags; - mmapper_info(u32 _size, u32 _flags) - : size(_size) + mmapper_info(u64 _addr, u32 _size, u32 _flags) + : addr(_addr) + , size(_size) , flags(_flags) { } @@ -266,7 +115,7 @@ int sys_mmapper_allocate_address(u32 size, u64 flags, u32 alignment, u32 alloc_a { 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, alignment)); + Memory.Write32(alloc_addr, Memory.Alloc(size, 4)); return CELL_OK; } @@ -276,7 +125,13 @@ 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))); + + u64 addr = Memory.Alloc(size, 1); + + if(!addr) + return CELL_ENOMEM; + + Memory.Write32(mem_id_addr, sc_mem.GetNewId(new mmapper_info(addr, size, flags))); return CELL_OK; } @@ -288,6 +143,10 @@ int sys_mmapper_map_memory(u32 start_addr, u32 mem_id, u64 flags) mmapper_info* info; if(!sc_mem.CheckId(mem_id, info)) return CELL_ESRCH; + if(!Memory.Map(start_addr, info->addr, info->size)) + { + sc_mem.Error("sys_mmapper_map_memory failed!"); + } //Memory.MemoryBlocks.Add((new MemoryBlock())->SetRange(start_addr, info->size)); return CELL_OK; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index 81d3925fc3..5ecad4f566 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -350,7 +350,7 @@ int sys_spu_thread_write_spu_mb(u32 id, u32 value) return CELL_ESRCH; } - if(!(*(SPUThread*)thr).SPU_In_MBox.Push(value)) + if(!(*(SPUThread*)thr).mfc.SPU_In_MBox.Push(value)) { ConLog.Warning("sys_spu_thread_write_spu_mb(id=0x%x, value=0x%x): used all mbox items."); return CELL_EBUSY; //? diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 76ad909a66..eed635de4b 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -174,10 +174,11 @@ void Emulator::Run() //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()); + m_vfs.Init(m_path); + //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()); ConLog.SkipLn(); ConLog.Write("Mount info:"); @@ -245,6 +246,8 @@ void Emulator::Stop() m_break_points.Clear(); m_marked_points.Clear(); + m_vfs.UnMountAll(); + GetGSManager().Close(); GetCPU().Close(); //SysCallsManager.Close(); diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index 0a6b1c435f..0825fecdc8 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -5,6 +5,8 @@ #include "Emu/System.h" #include "Ini.h" #include "Emu/GS/sysutil_video.h" +#include "Gui/VHDDManager.h" +#include "Gui/VFSManager.h" #include BEGIN_EVENT_TABLE(MainFrame, FrameBase) @@ -20,6 +22,8 @@ enum IDs id_sys_stop, id_sys_send_exit, id_config_emu, + id_config_vfs_manager, + id_config_vhdd_manager, id_update_dbg, }; @@ -31,7 +35,7 @@ wxString GetPaneName() } MainFrame::MainFrame() - : FrameBase(NULL, wxID_ANY, "", "MainFrame", wxSize(280, 180)) + : FrameBase(NULL, wxID_ANY, "", "MainFrame", wxSize(800, 600)) , m_aui_mgr(this) { SetLabel(wxString::Format(_PRGNAME_ " " _PRGVER_)); @@ -56,22 +60,29 @@ MainFrame::MainFrame() menu_sys.Append(id_sys_send_exit, "Send exit cmd")->Enable(false); menu_conf.Append(id_config_emu, "Settings"); + menu_conf.AppendSeparator(); + menu_conf.Append(id_config_vfs_manager, "Virtual File System Manager"); + menu_conf.Append(id_config_vhdd_manager, "Virtual HDD Manager"); SetMenuBar(&menubar); m_game_viewer = new GameViewer(this); 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_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_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_config_emu, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::Config) ); + Connect( id_config_vfs_manager, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::ConfigVFS) ); + Connect( id_config_vhdd_manager,wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::ConfigVHDD) ); + + Connect( id_update_dbg, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::UpdateUI) ); - Connect( id_config_emu, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::Config) ); m_app_connector.Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainFrame::OnKeyDown), (wxObject*)0, this); m_app_connector.Connect(wxEVT_DBG_COMMAND, wxCommandEventHandler(MainFrame::UpdateUI), (wxObject*)0, this); } @@ -83,6 +94,7 @@ MainFrame::~MainFrame() void MainFrame::AddPane(wxWindow* wind, const wxString& caption, int flags) { + wind->SetSize(-1, 300); m_aui_mgr.AddPane(wind, wxAuiPaneInfo().Name(GetPaneName()).Caption(caption).Direction(flags).CloseButton(false).MaximizeButton()); } @@ -366,6 +378,16 @@ void MainFrame::Config(wxCommandEvent& WXUNUSED(event)) if(paused) Emu.Resume(); } +void MainFrame::ConfigVFS(wxCommandEvent& WXUNUSED(event)) +{ + VFSManagerDialog(this).ShowModal(); +} + +void MainFrame::ConfigVHDD(wxCommandEvent& WXUNUSED(event)) +{ + VHDDManagerDialog(this).ShowModal(); +} + void MainFrame::UpdateUI(wxCommandEvent& event) { event.Skip(); diff --git a/rpcs3/Gui/MainFrame.h b/rpcs3/Gui/MainFrame.h index b7e479a324..955a160fda 100644 --- a/rpcs3/Gui/MainFrame.h +++ b/rpcs3/Gui/MainFrame.h @@ -25,6 +25,8 @@ private: void Stop(wxCommandEvent& event); void SendExit(wxCommandEvent& event); void Config(wxCommandEvent& event); + void ConfigVFS(wxCommandEvent& event); + void ConfigVHDD(wxCommandEvent& event); void UpdateUI(wxCommandEvent& event); void OnKeyDown(wxKeyEvent& event); diff --git a/rpcs3/Gui/TextInputDialog.cpp b/rpcs3/Gui/TextInputDialog.cpp new file mode 100644 index 0000000000..619bcf57c4 --- /dev/null +++ b/rpcs3/Gui/TextInputDialog.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "TextInputDialog.h" + +TextInputDialog::TextInputDialog(wxWindow* parent, const wxString& defvalue) + : wxDialog(parent, wxID_ANY, "Input text", wxDefaultPosition) +{ + m_tctrl_text = new wxTextCtrl(this, wxID_ANY, defvalue); + + wxBoxSizer& s_text(*new wxBoxSizer(wxVERTICAL)); + s_text.Add(m_tctrl_text, 1, wxEXPAND); + + wxBoxSizer& s_btns(*new wxBoxSizer(wxHORIZONTAL)); + s_btns.Add(new wxButton(this, wxID_OK)); + s_btns.AddSpacer(30); + s_btns.Add(new wxButton(this, wxID_CANCEL)); + + wxBoxSizer& s_main(*new wxBoxSizer(wxVERTICAL)); + s_main.Add(&s_text, 1, wxEXPAND | wxUP | wxLEFT | wxRIGHT, 5); + s_main.AddSpacer(30); + s_main.Add(&s_btns, 0, wxCENTER | wxDOWN | wxLEFT | wxRIGHT, 5); + + SetSizerAndFit(&s_main); + SetSize(250, -1); + + Connect(wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(TextInputDialog::OnOk)); +} + +void TextInputDialog::OnOk(wxCommandEvent& event) +{ + m_result = m_tctrl_text->GetValue(); + EndModal(wxID_OK); +} + +wxString& TextInputDialog::GetResult() +{ + return m_result; +} \ No newline at end of file diff --git a/rpcs3/Gui/TextInputDialog.h b/rpcs3/Gui/TextInputDialog.h new file mode 100644 index 0000000000..cb4cf0ab52 --- /dev/null +++ b/rpcs3/Gui/TextInputDialog.h @@ -0,0 +1,13 @@ +#pragma once + +class TextInputDialog : public wxDialog +{ + wxTextCtrl* m_tctrl_text; + wxString m_result; + +public: + TextInputDialog(wxWindow* parent, const wxString& defvalue = wxEmptyString); + void OnOk(wxCommandEvent& event); + + wxString& GetResult(); +}; \ No newline at end of file diff --git a/rpcs3/Gui/VFSManager.cpp b/rpcs3/Gui/VFSManager.cpp new file mode 100644 index 0000000000..cfd82c446d --- /dev/null +++ b/rpcs3/Gui/VFSManager.cpp @@ -0,0 +1,219 @@ +#include "stdafx.h" +#include "VFSManager.h" + +VFSEntrySettingsDialog::VFSEntrySettingsDialog(wxWindow* parent, VFSManagerEntry& entry) + : wxDialog(parent, wxID_ANY, "Mount configuration", wxDefaultPosition) + , m_entry(entry) +{ + m_tctrl_dev_path = new wxTextCtrl(this, wxID_ANY); + m_btn_select_dev_path = new wxButton(this, wxID_ANY, "..."); + m_tctrl_path = new wxTextCtrl(this, wxID_ANY); + m_btn_select_path = new wxButton(this, wxID_ANY, "..."); + m_tctrl_mount = new wxTextCtrl(this, wxID_ANY); + m_ch_type = new wxChoice(this, wxID_ANY); + + wxBoxSizer& s_type(*new wxBoxSizer(wxHORIZONTAL)); + s_type.Add(m_ch_type, 1, wxEXPAND); + + wxBoxSizer& s_dev_path(*new wxBoxSizer(wxHORIZONTAL)); + s_dev_path.Add(m_tctrl_dev_path, 1, wxEXPAND); + s_dev_path.Add(m_btn_select_dev_path, 0, wxLEFT, 5); + + wxBoxSizer& s_path(*new wxBoxSizer(wxHORIZONTAL)); + s_path.Add(m_tctrl_path, 1, wxEXPAND); + s_path.Add(m_btn_select_path, 0, wxLEFT, 5); + + wxBoxSizer& s_mount(*new wxBoxSizer(wxHORIZONTAL)); + s_mount.Add(m_tctrl_mount, 1, wxEXPAND); + + wxBoxSizer& s_btns(*new wxBoxSizer(wxHORIZONTAL)); + s_btns.Add(new wxButton(this, wxID_OK)); + s_btns.AddSpacer(30); + s_btns.Add(new wxButton(this, wxID_CANCEL)); + + wxBoxSizer& s_main(*new wxBoxSizer(wxVERTICAL)); + s_main.Add(&s_type, 1, wxEXPAND | wxALL, 10); + s_main.Add(&s_dev_path, 1, wxEXPAND | wxALL, 10); + s_main.Add(&s_path, 1, wxEXPAND | wxALL, 10); + s_main.Add(&s_mount, 1, wxEXPAND | wxALL, 10); + s_main.AddSpacer(10); + s_main.Add(&s_btns, 0, wxALL | wxCENTER, 10); + + SetSizerAndFit(&s_main); + + SetSize(350, -1); + + for(const auto i : vfsDeviceTypeNames) + { + m_ch_type->Append(i); + } + + Connect(m_ch_type->GetId(), wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(VFSEntrySettingsDialog::OnSelectType)); + Connect(m_btn_select_path->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(VFSEntrySettingsDialog::OnSelectPath)); + Connect(m_btn_select_dev_path->GetId(), wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(VFSEntrySettingsDialog::OnSelectDevPath)); + Connect(wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(VFSEntrySettingsDialog::OnOk)); + + m_tctrl_dev_path->SetValue(m_entry.device_path.GetPtr()); + m_tctrl_path->SetValue(m_entry.path.GetPtr()); + m_tctrl_mount->SetValue(m_entry.mount.GetPtr()); + m_ch_type->SetSelection(m_entry.device); + + OnSelectType(wxCommandEvent()); +} + +void VFSEntrySettingsDialog::OnSelectType(wxCommandEvent& event) +{ + m_btn_select_path->Enable(m_ch_type->GetSelection() == vfsDevice_LocalFile); + m_tctrl_dev_path->Enable(m_ch_type->GetSelection() != vfsDevice_LocalFile); + m_btn_select_dev_path->Enable(m_ch_type->GetSelection() != vfsDevice_LocalFile); +} + +void VFSEntrySettingsDialog::OnSelectPath(wxCommandEvent& event) +{ + wxDirDialog ctrl(this, "Select path", wxGetCwd()); + + if(ctrl.ShowModal() == wxID_CANCEL) + { + return; + } + + m_tctrl_path->SetValue(ctrl.GetPath()); +} + +void VFSEntrySettingsDialog::OnSelectDevPath(wxCommandEvent& event) +{ + wxFileDialog ctrl(this, "Select device"); + + if(ctrl.ShowModal() == wxID_CANCEL) + { + return; + } + + m_tctrl_dev_path->SetValue(ctrl.GetPath()); +} + +void VFSEntrySettingsDialog::OnOk(wxCommandEvent& event) +{ + m_entry.device_path = m_tctrl_dev_path->GetValue(); + m_entry.path = m_tctrl_path->GetValue(); + m_entry.mount = m_tctrl_mount->GetValue(); + m_entry.device = (vfsDeviceType)m_ch_type->GetSelection(); + + EndModal(wxID_OK); +} + +enum +{ + id_add, + id_remove, + id_config, +}; + +VFSManagerDialog::VFSManagerDialog(wxWindow* parent) + : wxDialog(parent, wxID_ANY, "Virtual File System Manager", wxDefaultPosition) +{ + m_list = new wxListView(this); + + wxBoxSizer& s_main(*new wxBoxSizer(wxVERTICAL)); + s_main.Add(m_list, 1, wxEXPAND); + + SetSizerAndFit(&s_main); + SetSize(800, 600); + + m_list->InsertColumn(0, "Path"); + m_list->InsertColumn(1, "Device path"); + m_list->InsertColumn(2, "Path to Device"); + m_list->InsertColumn(3, "Device"); + + Connect(m_list->GetId(), wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxCommandEventHandler(VFSManagerDialog::OnEntryConfig)); + Connect(m_list->GetId(), wxEVT_COMMAND_RIGHT_CLICK, wxCommandEventHandler(VFSManagerDialog::OnRightClick)); + Connect(id_add, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VFSManagerDialog::OnAdd)); + Connect(id_remove, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VFSManagerDialog::OnRemove)); + Connect(id_config, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VFSManagerDialog::OnEntryConfig)); + Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(VFSManagerDialog::OnClose)); + + LoadEntries(); + UpdateList(); +} + +void VFSManagerDialog::UpdateList() +{ + m_list->Freeze(); + m_list->DeleteAllItems(); + for(uint i=0; iInsertItem(i, m_entries[i].mount.GetPtr()); + m_list->SetItem(i, 1, m_entries[i].path.GetPtr()); + m_list->SetItem(i, 2, m_entries[i].device_path.GetPtr()); + m_list->SetItem(i, 3, vfsDeviceTypeNames[m_entries[i].device]); + } + m_list->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER); + m_list->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER); + m_list->SetColumnWidth(2, wxLIST_AUTOSIZE_USEHEADER); + m_list->SetColumnWidth(3, wxLIST_AUTOSIZE_USEHEADER); + m_list->Thaw(); +} + +void VFSManagerDialog::OnEntryConfig(wxCommandEvent& event) +{ + int idx = m_list->GetFirstSelected(); + if(idx != wxNOT_FOUND) + { + VFSEntrySettingsDialog(this, m_entries[idx]).ShowModal(); + UpdateList(); + } +} + +void VFSManagerDialog::OnRightClick(wxCommandEvent& event) +{ + wxMenu* menu = new wxMenu(); + int idx = m_list->GetFirstSelected(); + + menu->Append(id_add, "Add"); + menu->Append(id_remove, "Remove")->Enable(idx != wxNOT_FOUND); + menu->AppendSeparator(); + menu->Append(id_config, "Config")->Enable(idx != wxNOT_FOUND); + + PopupMenu( menu ); +} + +void VFSManagerDialog::OnAdd(wxCommandEvent& event) +{ + u32 idx = m_entries.Move(new VFSManagerEntry()); + UpdateList(); + + for(int i=0; iGetItemCount(); ++i) + { + m_list->SetItemState(i, i == idx ? wxLIST_STATE_SELECTED : ~wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); + } + + OnEntryConfig(wxCommandEvent()); +} + +void VFSManagerDialog::OnRemove(wxCommandEvent& event) +{ + for(int sel = m_list->GetNextSelected(-1), offs = 0; sel != wxNOT_FOUND; sel = m_list->GetNextSelected(sel), --offs) + { + m_entries.RemoveAt(sel + offs); + } + + UpdateList(); +} + +void VFSManagerDialog::OnClose(wxCloseEvent& event) +{ + SaveEntries(); + event.Skip(); +} + +void VFSManagerDialog::LoadEntries() +{ + m_entries.Clear(); + + Emu.GetVFS().SaveLoadDevices(m_entries, true); +} + +void VFSManagerDialog::SaveEntries() +{ + Emu.GetVFS().SaveLoadDevices(m_entries, false); +} \ No newline at end of file diff --git a/rpcs3/Gui/VFSManager.h b/rpcs3/Gui/VFSManager.h new file mode 100644 index 0000000000..e46f4911af --- /dev/null +++ b/rpcs3/Gui/VFSManager.h @@ -0,0 +1,39 @@ +#pragma once + +class VFSEntrySettingsDialog : public wxDialog +{ + wxTextCtrl* m_tctrl_dev_path; + wxButton* m_btn_select_dev_path; + wxTextCtrl* m_tctrl_path; + wxButton* m_btn_select_path; + wxTextCtrl* m_tctrl_mount; + wxChoice* m_ch_type; + VFSManagerEntry& m_entry; + +public: + VFSEntrySettingsDialog(wxWindow* parent, VFSManagerEntry& entry); + void OnSelectType(wxCommandEvent& event); + void OnSelectPath(wxCommandEvent& event); + void OnSelectDevPath(wxCommandEvent& event); + void OnOk(wxCommandEvent& event); +}; + +class VFSManagerDialog : public wxDialog +{ + wxListView* m_list; + Array m_entries; + +public: + VFSManagerDialog(wxWindow* parent); + + void UpdateList(); + + void OnEntryConfig(wxCommandEvent& event); + void OnRightClick(wxCommandEvent& event); + void OnAdd(wxCommandEvent& event); + void OnRemove(wxCommandEvent& event); + + void OnClose(wxCloseEvent& event); + void LoadEntries(); + void SaveEntries(); +}; \ No newline at end of file diff --git a/rpcs3/Gui/VHDDManager.cpp b/rpcs3/Gui/VHDDManager.cpp new file mode 100644 index 0000000000..367b144c6f --- /dev/null +++ b/rpcs3/Gui/VHDDManager.cpp @@ -0,0 +1,551 @@ +#include "stdafx.h" +#include "VHDDManager.h" +#include "TextInputDialog.h" +#include + +VHDDListDropTarget::VHDDListDropTarget(wxListView* parent) : m_parent(parent) +{ + SetDataObject(new wxDataObjectSimple(wxDF_PRIVATE)); +} + +wxDragResult VHDDListDropTarget::OnDragOver(wxCoord x, wxCoord y, wxDragResult def) +{ + int flags = 0; + int indx = m_parent->HitTest(wxPoint(x, y), flags); + for(int i=0; iGetItemCount(); ++i) + { + m_parent->SetItemState(i, i == indx ? wxLIST_STATE_SELECTED : ~wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); + } + + return def; +} + +void VHDDListDropTarget::OnLeave() +{ + for(int i=0; iGetItemCount(); ++i) + { + m_parent->SetItemState(i, ~wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); + } +} + +wxDragResult VHDDListDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult def) +{ + int flags = 0; + int dst_indx = m_parent->HitTest(wxPoint(x, y), flags); + ConLog.Write("OnData(%d -> %d)", m_src_indx, dst_indx); + return def; +} + +enum +{ + id_open = 0x777, + id_rename, + id_remove, + id_create_dir, + id_create_file, + id_import, + id_export, + id_create_hdd, + id_add_hdd +}; + +VHDDExplorer::VHDDExplorer(wxWindow* parent, const wxString& hdd_path) : wxDialog(parent, wxID_ANY, "Virtual HDD Explorer", wxDefaultPosition) +{ + m_list = new wxListView(this); + m_drop_target = new VHDDListDropTarget(m_list); + + m_list->SetDropTarget(m_drop_target); + m_list->DragAcceptFiles(true); + + wxBoxSizer& s_main(*new wxBoxSizer(wxVERTICAL)); + s_main.Add(m_list, 1, wxEXPAND | wxALL, 5); + SetSizerAndFit(&s_main); + + SetSize(800, 600); + m_list->InsertColumn(0, "Name"); + m_list->InsertColumn(1, "Type"); + m_list->InsertColumn(2, "Size"); + m_list->InsertColumn(3, "Creation time"); + + m_hdd = new vfsHDD(hdd_path); + UpdateList(); + Connect(m_list->GetId(), wxEVT_COMMAND_LIST_BEGIN_DRAG, wxListEventHandler(VHDDExplorer::OnListDrag)); + Connect(m_list->GetId(), wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(VHDDExplorer::DClick)); + Connect(m_list->GetId(), wxEVT_COMMAND_RIGHT_CLICK, wxCommandEventHandler(VHDDExplorer::OnContextMenu)); + m_list->Connect(wxEVT_DROP_FILES, wxDropFilesEventHandler(VHDDExplorer::OnDropFiles), (wxObject*)0, this); + + Connect(id_open, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDExplorer::OnOpen)); + Connect(id_rename, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDExplorer::OnRename)); + Connect(id_remove, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDExplorer::OnRemove)); + Connect(id_create_dir, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDExplorer::OnCreateDir)); + Connect(id_create_file, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDExplorer::OnCreateFile)); + Connect(id_import, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDExplorer::OnImport)); + Connect(id_export, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDExplorer::OnExport)); +} + +void VHDDExplorer::UpdateList() +{ + m_list->Freeze(); + m_list->DeleteAllItems(); + m_entries.Clear(); + m_names.Clear(); + + u64 block; + vfsHDD_Entry entry; + wxString name; + + for(bool is_ok = m_hdd->GetFirstEntry(block, entry, name); is_ok; is_ok = m_hdd->GetNextEntry(block, entry, name)) + { + int item = m_list->GetItemCount(); + m_list->InsertItem(item, name); + m_list->SetItem(item, 1, entry.type == vfsHDD_Entry_Dir ? "Dir" : "File"); + m_list->SetItem(item, 2, wxString::Format("%lld", entry.size)); + m_list->SetItem(item, 3, wxDateTime().Set(time_t(entry.ctime)).Format()); + m_entries.AddCpy(entry); + m_names.Add(name); + } + + m_list->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER); + m_list->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER); + m_list->SetColumnWidth(2, wxLIST_AUTOSIZE_USEHEADER); + m_list->SetColumnWidth(3, wxLIST_AUTOSIZE_USEHEADER); + m_list->Thaw(); +} + +void VHDDExplorer::Import(const wxString& path, const wxString& to) +{ + if(!m_hdd->Create(vfsHDD_Entry_File, to)) + { + wxMessageBox("IMPORT ERROR: file creation error."); + return; + } + + if(!m_hdd->Open(to, vfsWrite)) + { + wxMessageBox("IMPORT ERROR: file open error."); + return; + } + + wxFile f(path); + char buf[256]; + + while(!f.Eof()) + { + m_hdd->Write(buf, f.Read(buf, 256)); + } + + m_hdd->Close(); +} + +void VHDDExplorer::Export(const wxString& path, const wxString& to) +{ + if(!m_hdd->Open(path)) + { + wxMessageBox(wxString::Format("EXPORT ERROR: file open error. (%s)", path)); + return; + } + + wxFile f(to, wxFile::write); + char buf[256]; + + while(u64 size = m_hdd->Read(buf, 256)) + { + f.Write(buf, size); + } +} + +void VHDDExplorer::OpenDir(int sel) +{ + if(sel == wxNOT_FOUND) + { + return; + } + + if(m_entries[sel].type == vfsHDD_Entry_Dir) + { + m_hdd->OpenDir(m_names[sel]); + UpdateList(); + } +} + +void VHDDExplorer::OnListDrag(wxListEvent& event) +{ + m_drop_target->SetSrcIndx(event.GetIndex()); + wxDataObjectSimple obj(wxDF_PRIVATE); + wxDropSource drag(obj, m_list); + drag.DoDragDrop(wxDrag_AllowMove); +} + +void VHDDExplorer::OnDropFiles(wxDropFilesEvent& event) +{ + int count = event.GetNumberOfFiles(); + wxString* dropped = event.GetFiles(); + + wxBusyCursor busyCursor; + wxWindowDisabler disabler; + wxBusyInfo busyInfo("Adding files, wait please..."); + + + for(int i=0; iGetFirstSelected(); + + menu->Append(id_open, "Open")->Enable(idx != wxNOT_FOUND && m_entries[idx].type == vfsHDD_Entry_Dir); + menu->Append(id_rename, "Rename")->Enable(idx != wxNOT_FOUND && m_names[idx] != "." && m_names[idx] != ".."); + menu->Append(id_remove, "Remove")->Enable(idx != wxNOT_FOUND && m_names[idx] != "." && m_names[idx] != ".."); + menu->AppendSeparator(); + menu->Append(id_create_dir, "Create dir"); + menu->Append(id_create_file, "Create file"); + menu->AppendSeparator(); + menu->Append(id_import, "Import"); + menu->Append(id_export, "Export")->Enable(idx != wxNOT_FOUND); + + PopupMenu( menu ); +} + +void VHDDExplorer::OnOpen(wxCommandEvent& event) +{ + m_hdd->OpenDir(m_names[m_list->GetFirstSelected()]); + UpdateList(); +} + +void VHDDExplorer::OnRename(wxCommandEvent& event) +{ + TextInputDialog dial(this, m_names[m_list->GetFirstSelected()]); + + if(dial.ShowModal() == wxID_OK) + { + m_hdd->Rename(m_names[m_list->GetFirstSelected()], dial.GetResult()); + UpdateList(); + } +} + +void VHDDExplorer::OnRemove(wxCommandEvent& event) +{ + for(int sel = m_list->GetNextSelected(-1); sel != wxNOT_FOUND; sel = m_list->GetNextSelected(sel)) + { + m_hdd->RemoveEntry(m_names[sel]); + } + + UpdateList(); +} + +void VHDDExplorer::OnCreateDir(wxCommandEvent& event) +{ + int i = 1; + static const wxString& fmt = "New Dir (%d)"; + while(m_hdd->HasEntry(wxString::Format(fmt, i))) i++; + + m_hdd->Create(vfsHDD_Entry_Dir, wxString::Format(fmt, i)); + UpdateList(); +} + +void VHDDExplorer::OnCreateFile(wxCommandEvent& event) +{ + int i = 1; + static const wxString& fmt = "New File (%d)"; + while(m_hdd->HasEntry(wxString::Format(fmt, i))) i++; + + m_hdd->Create(vfsHDD_Entry_File, wxString::Format(fmt, i)); + UpdateList(); +} + +void VHDDExplorer::OnImport(wxCommandEvent& event) +{ + wxFileDialog ctrl(this, "Select import files", wxEmptyString, wxEmptyString, wxFileSelectorDefaultWildcardStr, + wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE); + + if(ctrl.ShowModal() == wxID_CANCEL) + { + return; + } + + wxArrayString paths; + ctrl.GetPaths(paths); + for(size_t i=0; iGetSelectedItemCount() > 1) + { + wxDirDialog ctrl(this, "Select export folder", wxGetCwd()); + + if(ctrl.ShowModal() == wxID_CANCEL) + { + return; + } + + for(int sel = m_list->GetNextSelected(-1); sel != wxNOT_FOUND; sel = m_list->GetNextSelected(sel)) + { + Export(m_names[sel], ctrl.GetPath() + '\\' + m_names[sel]); + } + } + else + { + int sel = m_list->GetFirstSelected(); + wxFileDialog ctrl(this, "Select export file", wxEmptyString, m_names[sel], wxFileSelectorDefaultWildcardStr, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + + if(ctrl.ShowModal() == wxID_CANCEL) + { + return; + } + + Export(m_names[sel], ctrl.GetPath()); + } + + UpdateList(); +} + +VHDDSetInfoDialog::VHDDSetInfoDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, "HDD Settings", wxDefaultPosition) +{ + m_spin_size = new wxSpinCtrl(this); + m_ch_type = new wxChoice(this, wxID_ANY); + m_spin_block_size = new wxSpinCtrl(this); + + wxBoxSizer& s_sinf(*new wxBoxSizer(wxHORIZONTAL)); + s_sinf.Add(m_spin_size, wxSizerFlags().Border(wxALL, 5).Expand()); + s_sinf.Add(m_ch_type, wxSizerFlags().Border(wxALL, 5).Expand()); + + wxBoxSizer& s_binf(*new wxBoxSizer(wxHORIZONTAL)); + s_binf.Add(m_spin_block_size, wxSizerFlags().Border(wxALL, 5).Expand()); + + wxBoxSizer& s_btns(*new wxBoxSizer(wxHORIZONTAL)); + s_btns.Add(new wxButton(this, wxID_OK), wxSizerFlags().Align(wxALIGN_LEFT).Border(wxALL, 5)); + s_btns.Add(new wxButton(this, wxID_CANCEL), wxSizerFlags().Align(wxALIGN_RIGHT).Border(wxALL, 5)); + + wxBoxSizer& s_main(*new wxBoxSizer(wxVERTICAL)); + s_main.Add(&s_sinf, wxSizerFlags().Align(wxALIGN_TOP).Expand()); + s_main.Add(&s_binf, wxSizerFlags().Align(wxALIGN_TOP).Expand()); + s_main.Add(&s_btns, wxSizerFlags().Align(wxALIGN_BOTTOM).Expand()); + SetSizerAndFit(&s_main); + + m_ch_type->Append("B"); + m_ch_type->Append("KB"); + m_ch_type->Append("MB"); + m_ch_type->Append("GB"); + + m_spin_size->SetMin(1); + m_spin_size->SetMax(0x7fffffff); + m_spin_size->SetValue(64); + m_ch_type->SetSelection(3); + m_spin_block_size->SetMin(64); + m_spin_block_size->SetMax(0x7fffffff); + m_spin_block_size->SetValue(2048); + Connect(wxID_OK, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(VHDDSetInfoDialog::OnOk)); +} + +void VHDDSetInfoDialog::OnOk(wxCommandEvent& event) +{ + m_res_size = m_spin_size->GetValue(); + + for(int i=0; iGetSelection(); ++i) + { + m_res_size *= 1024; + } + + m_res_block_size = m_spin_block_size->GetValue(); + + EndModal(wxID_OK); +} + +void VHDDSetInfoDialog::GetResult(u64& size, u64& block_size) +{ + size = m_res_size; + block_size = m_res_block_size; +} + +VHDDManagerDialog::VHDDManagerDialog(wxWindow* parent) + : wxDialog(parent, wxID_ANY, "Virtual HDD Manager", wxDefaultPosition) +{ + m_list = new wxListView(this); + + wxBoxSizer& s_main(*new wxBoxSizer(wxVERTICAL)); + s_main.Add(m_list, 1, wxEXPAND | wxALL, 5); + + SetSizerAndFit(&s_main); + SetSize(800, 600); + + m_list->InsertColumn(0, "Path"); + //m_list->InsertColumn(1, "Size"); + //m_list->InsertColumn(2, "Block size"); + Connect(m_list->GetId(), wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(VHDDManagerDialog::DClick)); + Connect(m_list->GetId(), wxEVT_COMMAND_RIGHT_CLICK, wxCommandEventHandler(VHDDManagerDialog::OnContextMenu)); + + Connect(id_add_hdd, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDManagerDialog::AddHDD)); + Connect(id_open, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDManagerDialog::OnOpen)); + Connect(id_remove, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDManagerDialog::OnRemove)); + Connect(id_create_hdd, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(VHDDManagerDialog::OnCreateHDD)); + Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(VHDDManagerDialog::OnClose)); + LoadPathes(); + UpdateList(); +} + +void VHDDManagerDialog::UpdateList() +{ + m_list->Freeze(); + m_list->DeleteAllItems(); + + for(size_t i=0; iInsertItem(i, m_pathes[i]); + } + + m_list->SetColumnWidth(0, wxLIST_AUTOSIZE_USEHEADER); + //m_list->SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER); + //m_list->SetColumnWidth(2, wxLIST_AUTOSIZE_USEHEADER); + + m_list->Thaw(); +} + +void VHDDManagerDialog::Open(int sel) +{ + VHDDExplorer dial(this, m_pathes[sel]); + dial.ShowModal(); +} + +void VHDDManagerDialog::DClick(wxListEvent& event) +{ + Open(event.GetIndex()); +} + +void VHDDManagerDialog::AddHDD(wxCommandEvent& event) +{ + wxFileDialog ctrl(this, "Select HDDs", wxEmptyString, wxEmptyString, "Virtual HDD (*.hdd) | *.hdd", + wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE); + + if(ctrl.ShowModal() == wxID_CANCEL) + { + return; + } + + wxArrayString pathes; + ctrl.GetPaths(pathes); + for(size_t i=0; iGetFirstSelected(); + menu->Append(id_open, "Open")->Enable(idx != wxNOT_FOUND); + menu->Append(id_remove, "Remove")->Enable(idx != wxNOT_FOUND); + menu->AppendSeparator(); + menu->Append(id_add_hdd, "Add"); + menu->Append(id_create_hdd, "Create"); + PopupMenu(menu); +} + +void VHDDManagerDialog::OnOpen(wxCommandEvent& event) +{ + int idx = m_list->GetFirstSelected(); + if(idx >= 0) Open(idx); +} + +void VHDDManagerDialog::OnRemove(wxCommandEvent& event) +{ + for(int sel = m_list->GetNextSelected(-1), offs = 0; sel != wxNOT_FOUND; sel = m_list->GetNextSelected(sel), --offs) + { + m_pathes.RemoveAt(sel + offs); + } + + UpdateList(); +} + +void VHDDManagerDialog::OnCreateHDD(wxCommandEvent& event) +{ + wxFileDialog ctrl(this, "Select HDD path", wxEmptyString, "new_hdd.hdd", "Virtual HDD (*.hdd) | *.hdd", + wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + + if(ctrl.ShowModal() == wxID_CANCEL) + { + return; + } + + VHDDSetInfoDialog dial(this); + + if(dial.ShowModal() == wxID_OK) + { + u64 size, bsize; + dial.GetResult(size, bsize); + vfsHDDManager::CreateHDD(ctrl.GetPath(), size, bsize); + m_pathes.AddCpy(ctrl.GetPath()); + UpdateList(); + } +} + +void VHDDManagerDialog::OnClose(wxCloseEvent& event) +{ + SavePathes(); + event.Skip(); +} + +void VHDDManagerDialog::LoadPathes() +{ + IniEntry path_count; + path_count.Init("path_count", "HDDManager"); + int count = 0; + count = path_count.LoadValue(count); + + m_pathes.SetCount(count); + + for(size_t i=0; i path_entry; + path_entry.Init(wxString::Format("path[%d]", i), "HDDManager"); + new (m_pathes + i) wxString(path_entry.LoadValue(wxEmptyString)); + } +} + +void VHDDManagerDialog::SavePathes() +{ + IniEntry path_count; + path_count.Init("path_count", "HDDManager"); + path_count.SaveValue(m_pathes.GetCount()); + + for(size_t i=0; i path_entry; + path_entry.Init(wxString::Format("path[%d]", i), "HDDManager"); + path_entry.SaveValue(m_pathes[i]); + } +} \ No newline at end of file diff --git a/rpcs3/Gui/VHDDManager.h b/rpcs3/Gui/VHDDManager.h new file mode 100644 index 0000000000..744760f27e --- /dev/null +++ b/rpcs3/Gui/VHDDManager.h @@ -0,0 +1,87 @@ +#pragma once +#include +#include "Emu/HDD/HDD.h" + +class VHDDListDropTarget : public wxDropTarget +{ + wxListView* m_parent; + int m_src_indx; + +public: + VHDDListDropTarget(wxListView* parent); + virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def); + virtual void OnLeave(); + virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def); + void SetSrcIndx(int src_indx) + { + m_src_indx = src_indx; + } +}; + +class VHDDExplorer : public wxDialog +{ + Array m_entries; + wxArrayString m_names; + wxListView* m_list; + vfsHDD* m_hdd; + VHDDListDropTarget* m_drop_target; + +public: + VHDDExplorer(wxWindow* parent, const wxString& hdd_path); + + void UpdateList(); + void Import(const wxString& path, const wxString& to); + void Export(const wxString& path, const wxString& to); + + void OnListDrag(wxListEvent& event); + void OnDropFiles(wxDropFilesEvent& event); + void OpenDir(int sel); + void DClick(wxListEvent& event); + void OnContextMenu(wxCommandEvent& event); + void OnOpen(wxCommandEvent& event); + void OnRename(wxCommandEvent& event); + void OnRemove(wxCommandEvent& event); + void OnCreateDir(wxCommandEvent& event); + void OnCreateFile(wxCommandEvent& event); + void OnImport(wxCommandEvent& event); + void OnExport(wxCommandEvent& event); +}; + +class VHDDSetInfoDialog : public wxDialog +{ + wxSpinCtrl* m_spin_size; + wxChoice* m_ch_type; + wxSpinCtrl* m_spin_block_size; + + u64 m_res_size; + u64 m_res_block_size; +public: + VHDDSetInfoDialog(wxWindow* parent); + + void OnOk(wxCommandEvent& event); + + void GetResult(u64& size, u64& block_size); +}; + +class VHDDManagerDialog : public wxDialog +{ + Array m_pathes; + wxListView* m_list; + +public: + VHDDManagerDialog(wxWindow* parent); + + void UpdateList(); + + void Open(int sel); + void DClick(wxListEvent& event); + void AddHDD(wxCommandEvent& event); + void OnContextMenu(wxCommandEvent& event); + void OnOpen(wxCommandEvent& event); + void OnRemove(wxCommandEvent& event); + void OnCreateHDD(wxCommandEvent& event); + + void OnClose(wxCloseEvent& event); + void LoadPathes(); + void SavePathes(); +}; diff --git a/rpcs3/Ini.cpp b/rpcs3/Ini.cpp index d141b5bc4a..05a4ed6fb8 100644 --- a/rpcs3/Ini.cpp +++ b/rpcs3/Ini.cpp @@ -7,7 +7,12 @@ Inis Ini; static bool StringToBool(const wxString str) { - if(!str.CmpNoCase("enable") || !str.CmpNoCase("e") || !str.CmpNoCase("1")) + if( + !str.CmpNoCase("enable") || + !str.CmpNoCase("e") || + !str.CmpNoCase("1") || + !str.CmpNoCase("true") || + !str.CmpNoCase("t") ) { return true; } diff --git a/rpcs3/Loader/ELF32.cpp b/rpcs3/Loader/ELF32.cpp index 99951b8515..74bb8a3112 100644 --- a/rpcs3/Loader/ELF32.cpp +++ b/rpcs3/Loader/ELF32.cpp @@ -176,7 +176,7 @@ bool ELF32Loader::LoadPhdrData(u64 offset) break; } - if(note.descsz < sizeof(note.desc)) + if(note.descsz != sizeof(note.desc) && note.descsz != 32) { ConLog.Error("ELF32: Bad NOTE descsz (%d)", note.descsz); break; @@ -188,9 +188,16 @@ bool ELF32Loader::LoadPhdrData(u64 offset) // break; //} - ConLog.Warning("name = %s", note.name); - ConLog.Warning("ls_size = %d", note.desc.ls_size); - ConLog.Warning("stack_size = %d", note.desc.stack_size); + if(note.descsz == sizeof(note.desc)) + { + ConLog.Warning("name = %s", note.name); + ConLog.Warning("ls_size = %d", note.desc.ls_size); + ConLog.Warning("stack_size = %d", note.desc.stack_size); + } + else + { + ConLog.Warning("desc = '%s'", note.desc_text); + } } #ifdef LOADER_DEBUG ConLog.SkipLn(); diff --git a/rpcs3/Loader/ELF32.h b/rpcs3/Loader/ELF32.h index 5657eb7d27..586d5f4bb9 100644 --- a/rpcs3/Loader/ELF32.h +++ b/rpcs3/Loader/ELF32.h @@ -97,7 +97,11 @@ struct Elf32_Note u32 descsz; u32 type; u8 name[8]; - Elf32_Desc desc; + union + { + Elf32_Desc desc; + char desc_text[32]; + }; void Load(vfsStream& f) { @@ -105,7 +109,15 @@ struct Elf32_Note descsz = Read32(f); type = Read32(f); f.Read(name, 8); - desc.Load(f); + + if(descsz == 32) + { + f.Read(desc_text, descsz); + } + else + { + desc.Load(f); + } } }; diff --git a/rpcs3/Loader/ELF64.cpp b/rpcs3/Loader/ELF64.cpp index c49eb4b384..70f89523aa 100644 --- a/rpcs3/Loader/ELF64.cpp +++ b/rpcs3/Loader/ELF64.cpp @@ -339,7 +339,7 @@ bool ELF64Loader::LoadPhdrData(u64 offset) Module* module = GetModuleByName(module_name); if(module) { - module->SetLoaded(); + //module->SetLoaded(); } else { diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index 8222acffc0..f716e8b532 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -22,15 +22,7 @@ bool Rpcs3App::OnInit() SetTopWindow(m_MainFrame); Emu.Init(); - try - { - (new CompilerELF(m_MainFrame))->Show(); - } - catch(...) - { - ConLog.Warning("CompilerELF is broken."); - } - + (new CompilerELF(m_MainFrame))->Show(); m_debugger_frame = new DebuggerPanel(m_MainFrame); ConLogFrame = new LogFrame(m_MainFrame); diff --git a/rpcs3/rpcs3.h b/rpcs3/rpcs3.h index 4f3a4649d4..533118cb92 100644 --- a/rpcs3/rpcs3.h +++ b/rpcs3/rpcs3.h @@ -57,7 +57,7 @@ public: MainFrame* m_MainFrame; DebuggerPanel* m_debugger_frame; - virtual bool OnInit(); + virtual bool OnInit(); virtual void Exit(); void SendDbgCommand(DbgCommand id, PPCThread* thr=nullptr); diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 271eebaa2a..f65f9194e6 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -197,6 +197,7 @@ + @@ -220,6 +221,7 @@ + @@ -258,6 +260,9 @@ + + + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index b9bee6158c..728fc0fe06 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -44,6 +44,9 @@ {dfd581c4-aed0-4229-bb30-7ee5816049e1} + + {718bc358-b7ef-4988-8547-2148d14bb08b} + @@ -262,6 +265,21 @@ Emu\CPU + + Emu\CPU + + + Emu\HDD + + + Gui + + + Gui + + + Gui + From f42d4b65723a47c3466b9fa6766228f8f521dd9d Mon Sep 17 00:00:00 2001 From: DH Date: Sun, 11 Aug 2013 00:56:24 +0300 Subject: [PATCH 06/13] OpenGL Renderer fixes: - Fixed Vertex & Fragment Shader Decompilers. - Fixed Transform Constants loading. --- Utilities/Array.h | 2 +- rpcs3/Emu/Cell/PPCDecoder.h | 25 +++-- rpcs3/Emu/Cell/PPUInterpreter.h | 2 +- rpcs3/Emu/GS/GL/FragmentProgram.cpp | 94 +++++++++++----- rpcs3/Emu/GS/GL/FragmentProgram.h | 3 +- rpcs3/Emu/GS/GL/GLGSRender.cpp | 125 ++++++++++++--------- rpcs3/Emu/GS/GL/GLGSRender.h | 78 +++++++++---- rpcs3/Emu/GS/GL/Program.cpp | 15 ++- rpcs3/Emu/GS/GL/Program.h | 1 + rpcs3/Emu/GS/GL/ProgramBuffer.cpp | 34 ++++-- rpcs3/Emu/GS/GL/ProgramBuffer.h | 4 +- rpcs3/Emu/GS/GL/ShaderParam.h | 50 +++++++-- rpcs3/Emu/GS/GL/VertexProgram.cpp | 166 +++++++++++++++++----------- rpcs3/Emu/GS/GL/VertexProgram.h | 62 ++++++----- rpcs3/Emu/SysCalls/FuncList.cpp | 2 +- 15 files changed, 429 insertions(+), 234 deletions(-) diff --git a/Utilities/Array.h b/Utilities/Array.h index 02c80417c8..0f7df002de 100644 --- a/Utilities/Array.h +++ b/Utilities/Array.h @@ -141,7 +141,7 @@ public: virtual void SetCount(const u32 count, bool memzero = true) { if(m_count >= count) return; - + _InsertRoomEnd(count - m_count); if(memzero) memset(m_array + m_count - count, 0, sizeof(T) * (m_count - count)); diff --git a/rpcs3/Emu/Cell/PPCDecoder.h b/rpcs3/Emu/Cell/PPCDecoder.h index 053a30c2d1..81e24087dd 100644 --- a/rpcs3/Emu/Cell/PPCDecoder.h +++ b/rpcs3/Emu/Cell/PPCDecoder.h @@ -381,7 +381,11 @@ public: , m_parent(nullptr) , m_opcode(-1) { - memset(m_instrs, 0, sizeof(InstrCaller*) * count); + for(int i=0; i*) * count); } @@ -413,6 +417,14 @@ public: void set_error_func(InstrCaller* error_func) { + for(int i=0; i* instr = m_instrs[entry]; - - if(instr) - { - (*instr)(op, code); - } - else if(m_error_func) - { - (*m_error_func)(op, code); - } + (*m_instrs[entry])(op, code); } virtual void operator ()(TO* op, u32 code) const diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index 86d012636c..9125b4a43a 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -3564,7 +3564,7 @@ private: } void FNEG(u32 frd, u32 frb, bool rc) { - CPU.FPR[frd] = -CPU.FPR[frb]; + CPU.FPR[frd] = ((u64&)CPU.FPR[frb]) ^ (1ULL << 63); if(rc) UNK("fneg.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FMR(u32 frd, u32 frb, bool rc) diff --git a/rpcs3/Emu/GS/GL/FragmentProgram.cpp b/rpcs3/Emu/GS/GL/FragmentProgram.cpp index a2678419ff..5ae2e41e9a 100644 --- a/rpcs3/Emu/GS/GL/FragmentProgram.cpp +++ b/rpcs3/Emu/GS/GL/FragmentProgram.cpp @@ -3,10 +3,38 @@ void FragmentDecompilerThread::AddCode(wxString code) { - if(!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_le) return; - if(!src0.exec_if_eq || !src0.exec_if_gr || !src0.exec_if_le) + if(!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) return; + + wxString cond; + + if(src0.exec_if_gr) { - ConLog.Error("Bad cond! eq: %d gr: %d le: %d", src0.exec_if_eq, src0.exec_if_gr, src0.exec_if_le); + cond = ">"; + } + else if(src0.exec_if_lt) + { + cond = "<"; + } + else if(src0.exec_if_eq) + { + cond = "="; + } + + if(src0.exec_if_eq) + { + cond += "="; + } + else + { + if(src0.exec_if_gr && src0.exec_if_lt) + { + cond = "!="; + } + } + + if(cond) + { + ConLog.Error("cond! [eq: %d gr: %d lt: %d] (%s)", src0.exec_if_eq, src0.exec_if_gr, src0.exec_if_lt, cond); Emu.Pause(); return; } @@ -29,7 +57,7 @@ void FragmentDecompilerThread::AddCode(wxString code) } } - code = AddReg(dst.dest_reg) + GetMask() + " = " + code + GetMask(); + code = AddReg(dst.dest_reg, dst.fp16) + GetMask() + " = " + code + GetMask(); main += "\t" + code + ";\n"; } @@ -46,20 +74,29 @@ wxString FragmentDecompilerThread::GetMask() return ret.IsEmpty() || ret == "xyzw" ? wxEmptyString : ("." + ret); } -wxString FragmentDecompilerThread::AddReg(u32 index) +wxString FragmentDecompilerThread::AddReg(u32 index, int fp16) { //if(!index) return "gl_FragColor"; - return m_parr.AddParam(index ? PARAM_NONE : PARAM_OUT, "vec4", wxString::Format("r%d", index), index ? -1 : 0); + return m_parr.AddParam(index ? PARAM_NONE : PARAM_OUT, "vec4", + wxString::Format((fp16 ? "h%d" : "r%d"), index), (index || fp16) ? -1 : 0); } wxString FragmentDecompilerThread::AddConst() { - return m_parr.AddParam(PARAM_CONST, "vec4", wxString::Format("fc%d", m_const_index++)); + mem32_t data(m_addr + m_size + m_offset); + + m_offset += 4 * 4; + u32 x = GetData(data[0]); + u32 y = GetData(data[1]); + u32 z = GetData(data[2]); + u32 w = GetData(data[3]); + return m_parr.AddParam("vec4", wxString::Format("fc%d", m_const_index++), + wxString::Format("vec4(%f, %f, %f, %f)", (float&)x, (float&)y, (float&)z, (float&)w)); } wxString FragmentDecompilerThread::AddTex() { - return m_parr.AddParam(PARAM_CONST, "sampler2D", wxString::Format("tex_%d", dst.tex_num)); + return m_parr.AddParam(PARAM_UNIFORM, "sampler2D", wxString::Format("tex%d", dst.tex_num)); } template wxString FragmentDecompilerThread::GetSRC(T src) @@ -69,7 +106,7 @@ template wxString FragmentDecompilerThread::GetSRC(T src) switch(src.reg_type) { case 0: //tmp - ret += AddReg(src.tmp_reg_index); + ret += AddReg(src.tmp_reg_index, src.fp16); break; case 1: //input @@ -102,7 +139,6 @@ template wxString FragmentDecompilerThread::GetSRC(T src) break; case 2: //const - ConLog.Write("reg index = %d", src.tmp_reg_index); ret += AddConst(); break; @@ -157,6 +193,8 @@ void FragmentDecompilerThread::Task() src1.HEX = GetData(data[2]); src2.HEX = GetData(data[3]); + m_offset = 4 * 4; + switch(dst.opcode) { case 0x00: break; //NOP @@ -166,24 +204,24 @@ void FragmentDecompilerThread::Task() case 0x04: AddCode("(" + GetSRC(src0) + " * " + GetSRC(src1) + " + " + GetSRC(src2) + ")"); break; //MAD case 0x05: AddCode("dot(" + GetSRC(src0) + ".xyz, " + GetSRC(src1) + ".xyz)"); break; // DP3 case 0x06: AddCode("dot(" + GetSRC(src0) + ", " + GetSRC(src1) + ")"); break; // DP4 - //case 0x07: break; // DST + case 0x07: AddCode("distance(" + GetSRC(src0) + ", " + GetSRC(src1) + ")"); break; // DST case 0x08: AddCode("min(" + GetSRC(src0) + ", " + GetSRC(src1) + ")"); break; // MIN case 0x09: AddCode("max(" + GetSRC(src0) + ", " + GetSRC(src1) + ")"); break; // MAX - //case 0x0a: break; // SLT - //case 0x0b: break; // SGE - //case 0x0c: break; // SLE - //case 0x0d: break; // SGT - //case 0x0e: break; // SNE - //case 0x0f: break; // SEQ + case 0x0a: AddCode("vec4(lessThan(" + GetSRC(src0) + ", " + GetSRC(src1) + "))"); break; // SLT + case 0x0b: AddCode("vec4(greaterThanEqual(" + GetSRC(src0) + ", " + GetSRC(src1) + "))"); break; // SGE + case 0x0c: AddCode("vec4(lessThanEqual(" + GetSRC(src0) + ", " + GetSRC(src1) + "))"); break; // SLE + case 0x0d: AddCode("vec4(greaterThan(" + GetSRC(src0) + ", " + GetSRC(src1) + "))"); break; // SGT + case 0x0e: AddCode("vec4(notEqual(" + GetSRC(src0) + ", " + GetSRC(src1) + "))"); break; // SNE + case 0x0f: AddCode("vec4(equal(" + GetSRC(src0) + ", " + GetSRC(src1) + "))"); break; // SEQ - //case 0x10: break; // FRC - //case 0x11: break; // FLR + case 0x10: AddCode("fract(" + GetSRC(src0) + ")"); break; // FRC + case 0x11: AddCode("floor(" + GetSRC(src0) + ")"); break; // FLR //case 0x12: break; // KIL //case 0x13: break; // PK4 //case 0x14: break; // UP4 case 0x15: AddCode("ddx(" + GetSRC(src0) + ")"); break; // DDX case 0x16: AddCode("ddy(" + GetSRC(src0) + ")"); break; // DDY - case 0x17: AddCode("texture(" + AddTex() + ", (" + GetSRC(src0) + ").xy)"); break; //TEX + case 0x17: AddCode("texture(" + AddTex() + ", " + GetSRC(src0) + ".xy)"); break; //TEX //case 0x18: break; // TXP //case 0x19: break; // TXD case 0x1a: AddCode("1 / (" + GetSRC(src0) + ")"); break; // RCP @@ -199,7 +237,7 @@ void FragmentDecompilerThread::Task() case 0x23: AddCode("sin(" + GetSRC(src0) + ")"); break; // SIN //case 0x24: break; // PK2 //case 0x25: break; // UP2 - //case 0x26: break; // POW + case 0x26: AddCode("pow(" + GetSRC(src0) + ", " + GetSRC(src1) +")"); break; // POW //case 0x27: break; // PKB //case 0x28: break; // UPB //case 0x29: break; // PK16 @@ -218,23 +256,23 @@ void FragmentDecompilerThread::Task() //case 0x37: break; // TIMESWTEX //case 0x38: break; // DP2 //case 0x39: break; // NRM - //case 0x3a: break; // DIV - //case 0x3b: break; // DIVSQ + case 0x3a: AddCode("(" + GetSRC(src0) + " / " + GetSRC(src1) + ")"); break; // DIV + case 0x3b: AddCode("(" + GetSRC(src0) + " / " + GetSRC(src1) + ")"); break; // DIVSQ //case 0x3c: break; // LIF - //case 0x3d: break; // FENCT - //case 0x3e: break; // FENCB + case 0x3d: break; // FENCT + case 0x3e: break; // FENCB default: - ConLog.Error("Unknown opcode 0x%x", dst.opcode); + ConLog.Error("Unknown opcode 0x%x (inst %d)", dst.opcode, m_size / (4 * 4)); Emu.Pause(); break; } - m_size += 4 * 4; + m_size += m_offset; if(dst.end) break; - data.SetOffset(4 * 4); + data.SetOffset(m_offset); } m_shader = BuildCode(); diff --git a/rpcs3/Emu/GS/GL/FragmentProgram.h b/rpcs3/Emu/GS/GL/FragmentProgram.h index e3758cd303..09955cbc95 100644 --- a/rpcs3/Emu/GS/GL/FragmentProgram.h +++ b/rpcs3/Emu/GS/GL/FragmentProgram.h @@ -100,6 +100,7 @@ struct FragmentDecompilerThread : public ThreadBase u32 m_addr; u32& m_size; u32 m_const_index; + u32 m_offset; FragmentDecompilerThread(wxString& shader, ParamArray& parr, u32 addr, u32& size) : ThreadBase(false, "Fragment Shader Decompiler Thread") @@ -115,7 +116,7 @@ struct FragmentDecompilerThread : public ThreadBase wxString GetMask(); void AddCode(wxString code); - wxString AddReg(u32 index); + wxString AddReg(u32 index, int fp16); wxString AddConst(); wxString AddTex(); diff --git a/rpcs3/Emu/GS/GL/GLGSRender.cpp b/rpcs3/Emu/GS/GL/GLGSRender.cpp index 4f1cd9973a..686f7f099f 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 "Emu/Cell/PPCInstrTable.h" -#define CMD_DEBUG 1 -#define DUMP_VERTEX_DATA 1 +#define CMD_DEBUG 0 +#define DUMP_VERTEX_DATA 0 #if CMD_DEBUG #define CMD_LOG ConLog.Write @@ -75,8 +75,8 @@ void GLGSFrame::SetViewport(int x, int y, u32 w, u32 h) } GLGSRender::GLGSRender() - : m_frame(NULL) - , m_rsx_thread(NULL) + : m_frame(nullptr) + , m_rsx_thread(nullptr) , m_fp_buf_num(-1) , m_vp_buf_num(-1) { @@ -122,6 +122,12 @@ void GLRSXThread::Task() glEnable(GL_TEXTURE_2D); glSwapInterval(Ini.GSVSyncEnable.GetValue() ? 1 : 0); + for(u32 i=0; iGetTexture(i); + tex.Create(); + } + bool draw = true; u32 drawed = 0; u32 skipped = 0; @@ -172,7 +178,7 @@ void GLRSXThread::Task() continue; } - ConLog.Write("addr = 0x%x", p.m_ioAddress + get); + //ConLog.Write("addr = 0x%x", p.m_ioAddress + get); const u32 cmd = Memory.Read32(p.m_ioAddress + get); const u32 count = (cmd >> 18) & 0x7ff; //if(cmd == 0) continue; @@ -251,6 +257,10 @@ void GLGSRender::Init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddre m_localAddress = localAddress; m_ctrl = (CellGcmControl*)Memory.GetMemFromAddr(m_ctrlAddress); + m_cur_vertex_prog = nullptr; + m_cur_shader_prog = nullptr; + m_cur_shader_prog_num = 0; + (m_rsx_thread = new GLRSXThread(this))->Start(); } @@ -309,7 +319,7 @@ void GLGSRender::EnableVertexData(bool indexed_draw) wxFile dump("VertexDataArray.dump", wxFile::write); #endif - for(u32 i=0; i<16; ++i) + for(u32 i=0; iconstants4.GetCount(); ++i) + for(u32 i=0; iconstants4[i]; + const TransformConstant& c = m_transform_constants[i]; const wxString& name = wxString::Format("vc%d", c.id); - //const int l = glGetUniformLocation(m_program.id, name); const int l = m_program.GetLocation(name); checkForGlError("glGetUniformLocation " + name); @@ -499,7 +508,9 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c 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)); + u32 tex_addr = GetAddress(offset, location); + ConLog.Warning("texture addr = 0x%x", tex_addr); + tex.SetOffset(tex_addr); tex.SetFormat(cubemap, dimension, format, mipmap); } break; @@ -810,10 +821,11 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case NV4097_SET_COLOR_CLEAR_VALUE: { - const u8 a = (args[0] >> 24) & 0xff; - const u8 r = (args[0] >> 16) & 0xff; - const u8 g = (args[0] >> 8) & 0xff; - const u8 b = args[0] & 0xff; + const u32 color = args[0]; + const u8 a = (color >> 24) & 0xff; + const u8 r = (color >> 16) & 0xff; + const u8 g = (color >> 8) & 0xff; + const u8 b = color & 0xff; CMD_LOG("a=%d, r=%d, g=%d, b=%d", a, r, g, b); glClearColor( (float)r / 255.0f, @@ -825,9 +837,9 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case NV4097_SET_SHADER_PROGRAM: { - m_shader_prog.Delete(); - m_shader_prog.addr = GetAddress(args[0] & ~0x3, (args[0] & 0x3) - 1); - //m_program.Delete(); + m_cur_shader_prog = &m_shader_progs[m_cur_shader_prog_num++]; + m_cur_shader_prog->Delete(); + m_cur_shader_prog->addr = GetAddress(args[0] & ~0x3, (args[0] & 0x3) - 1); } break; @@ -850,13 +862,14 @@ 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_cur_vertex_prog = &m_vertex_progs[args[0]]; m_cur_vertex_prog->Delete(); if(count == 2) { const u32 start = args[1]; + if(start) + ConLog.Warning("NV4097_SET_TRANSFORM_PROGRAM_LOAD: start = %d", start); } } break; @@ -899,12 +912,6 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; } - if(!m_cur_vertex_prog) - { - ConLog.Warning("NV4097_SET_TRANSFORM_CONSTANT_LOAD: m_cur_vertex_prog == NULL"); - break; - } - for(u32 id = args[0], i = 1; iconstants4.AddCpy(c); + m_transform_constants.AddCpy(c); } } break; @@ -1154,13 +1156,20 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c bool GLGSRender::LoadProgram() { - m_fp_buf_num = m_prog_buffer.SearchFp(m_shader_prog); + if(!m_cur_shader_prog) + { + ConLog.Warning("LoadProgram: m_cur_shader_prog == NULL"); - if(m_fp_buf_num == -1) m_shader_prog.Decompile(); + return false; + } + + m_fp_buf_num = m_prog_buffer.SearchFp(*m_cur_shader_prog); + + if(m_fp_buf_num == -1) m_cur_shader_prog->Decompile(); if(!m_cur_vertex_prog) { - ConLog.Warning("NV4097_SET_BEGIN_END: m_cur_vertex_prog == NULL"); + ConLog.Warning("LoadProgram: m_cur_vertex_prog == NULL"); return false; } @@ -1182,11 +1191,11 @@ bool GLGSRender::LoadProgram() if(m_fp_buf_num == -1) { ConLog.Warning("FP not found in buffer!"); - m_shader_prog.Wait(); - m_shader_prog.Compile(); + m_cur_shader_prog->Wait(); + m_cur_shader_prog->Compile(); wxFile f(wxGetCwd() + "/FragmentProgram.txt", wxFile::write); - f.Write(m_shader_prog.shader); + f.Write(m_cur_shader_prog->shader); } if(m_vp_buf_num == -1) @@ -1203,10 +1212,14 @@ bool GLGSRender::LoadProgram() m_program.id = m_prog_buffer.GetProg(m_fp_buf_num, m_vp_buf_num); } - if(!m_program.id) + 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(); + } + else + { + m_program.Create(m_cur_vertex_prog->id, m_cur_shader_prog->id); + m_prog_buffer.Add(m_program, *m_cur_shader_prog, *m_cur_vertex_prog); m_program.Use(); @@ -1270,7 +1283,12 @@ void GLGSRender::ExecCMD() glDepthRangef(m_clip_min, m_clip_max); } - for(u32 i=0; i<16; ++i) + if(m_indexed_array.m_count && m_draw_array_count) + { + ConLog.Warning("m_indexed_array.m_count && draw_array_count"); + } + + for(u32 i=0; iGetTexture(i); if(!tex.IsEnabled()) continue; @@ -1280,17 +1298,11 @@ void GLGSRender::ExecCMD() 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); @@ -1347,13 +1359,20 @@ void GLGSRender::ExecCMD() void GLGSRender::Reset() { - m_program.Delete(); - //m_program.id = 0; - m_shader_prog.id = 0; + m_program.UnUse(); + + if(m_cur_shader_prog) + m_cur_shader_prog->id = 0; + if(m_cur_vertex_prog) m_cur_vertex_prog->id = 0; - memset(m_vertex_data, 0, sizeof(VertexData) * 16); + m_cur_shader_prog_num = 0; + m_transform_constants.Clear(); + for(uint i=0; i glGenTextures"); + Bind(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + } + void SetRect(const u32 width, const u32 height) { m_width = width; @@ -68,21 +82,8 @@ public: void Init() { - if(!m_id) - { - glGenTextures(1, &m_id); - checkForGlError("GLTexture::Init() -> glGenTextures"); - glBindTexture(GL_TEXTURE_2D, m_id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - } - else - { - glBindTexture(GL_TEXTURE_2D, m_id); - } - + Bind(); + ConLog.Warning("texture addr = 0x%x, width = %d, height = %d", m_offset, m_width, m_height); //TODO: safe init checkForGlError("GLTexture::Init() -> glBindTexture"); @@ -106,18 +107,17 @@ public: default: ConLog.Error("Init tex error: Bad tex format (0x%x)", m_format); break; } - //glBindTexture(GL_TEXTURE_2D, 0); + //Unbind(); } void Save(const wxString& name) { 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); + Bind(); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, alldata); u8* data = new u8[m_width * m_height * 3]; u8* alpha = new u8[m_width * m_height]; @@ -133,7 +133,6 @@ public: *dst_a++ = *src++; } - ConLog.Write("end"); wxImage out; out.Create(m_width, m_height, data, alpha); out.SaveFile(name, wxBITMAP_TYPE_PNG); @@ -160,10 +159,34 @@ public: glBindTexture(GL_TEXTURE_2D, m_id); } + void Unbind() + { + glBindTexture(GL_TEXTURE_2D, 0); + } + void Enable(bool enable) { m_enabled = enable; } bool IsEnabled() const { return m_enabled; } }; +struct TransformConstant +{ + u32 id; + float x, y, z, w; + + TransformConstant() + { + } + + TransformConstant(u32 id, float x, float y, float z, float w) + : id(id) + , x(x) + , y(y) + , z(z) + , w(w) + { + } +}; + struct IndexArrayData { Array m_data; @@ -228,16 +251,25 @@ class GLGSRender , public GSRender , public ExecRSXCMDdata { +public: + static const uint m_vertex_count = 16; + static const uint m_fragment_count = 16; + static const uint m_textures_count = 16; + private: GLRSXThread* m_rsx_thread; IndexArrayData m_indexed_array; - ShaderProgram m_shader_prog; - VertexData m_vertex_data[16]; + ShaderProgram m_shader_progs[m_fragment_count]; + ShaderProgram* m_cur_shader_prog; + int m_cur_shader_prog_num; + VertexData m_vertex_data[m_vertex_count]; Array m_vdata; - VertexProgram m_vertex_progs[16]; + VertexProgram m_vertex_progs[m_vertex_count]; VertexProgram* m_cur_vertex_prog; + Array m_transform_constants; + Program m_program; int m_fp_buf_num; int m_vp_buf_num; diff --git a/rpcs3/Emu/GS/GL/Program.cpp b/rpcs3/Emu/GS/GL/Program.cpp index bc90e28628..9d74358cc0 100644 --- a/rpcs3/Emu/GS/GL/Program.cpp +++ b/rpcs3/Emu/GS/GL/Program.cpp @@ -19,7 +19,9 @@ int Program::GetLocation(const wxString& name) u32 pos = m_locations.Move(new Location()); m_locations[pos].name = name; - return m_locations[pos].loc = glGetUniformLocation(id, name); + m_locations[pos].loc = glGetUniformLocation(id, name); + checkForGlError(wxString::Format("glGetUniformLocation(0x%x, %s)", id, name)); + return m_locations[pos].loc; } bool Program::IsCreated() const @@ -72,6 +74,11 @@ void Program::Create(const u32 vp, const u32 fp) } } +void Program::UnUse() +{ + id = 0; +} + void Program::Use() { glUseProgram(id); @@ -80,8 +87,10 @@ void Program::Use() void Program::SetTex(u32 index) { - glUniform1i(GetLocation(wxString::Format("tex_%d", index)), index); - checkForGlError(wxString::Format("SetTex(%d)", index)); + int loc = GetLocation(wxString::Format("tex%d", index)); + checkForGlError(wxString::Format("GetLocation(tex%d)", index)); + glUniform1i(loc, index); + checkForGlError(wxString::Format("SetTex(%d - %d)", index, loc)); } void Program::Delete() diff --git a/rpcs3/Emu/GS/GL/Program.h b/rpcs3/Emu/GS/GL/Program.h index 4a376414b6..71fb597ff6 100644 --- a/rpcs3/Emu/GS/GL/Program.h +++ b/rpcs3/Emu/GS/GL/Program.h @@ -22,6 +22,7 @@ public: bool IsCreated() const; void Create(const u32 vp, const u32 fp); void Use(); + void UnUse(); void SetTex(u32 index); void Delete(); }; \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/ProgramBuffer.cpp b/rpcs3/Emu/GS/GL/ProgramBuffer.cpp index ee6a292e73..859bad2bce 100644 --- a/rpcs3/Emu/GS/GL/ProgramBuffer.cpp +++ b/rpcs3/Emu/GS/GL/ProgramBuffer.cpp @@ -8,7 +8,7 @@ int ProgramBuffer::SearchFp(ShaderProgram& fp) if(memcmp(&m_buf[i].fp_data[0], &Memory[fp.addr], m_buf[i].fp_data.GetCount()) != 0) continue; fp.id = m_buf[i].fp_id; - fp.shader = m_buf[i].fp_shader; + fp.shader = m_buf[i].fp_shader.GetPtr(); return i; } @@ -21,10 +21,10 @@ int ProgramBuffer::SearchVp(VertexProgram& vp) for(u32 i=0; i fp_data; Array vp_data; - wxString fp_shader; - wxString vp_shader; + ArrayString fp_shader; + ArrayString vp_shader; }; struct ProgramBuffer diff --git a/rpcs3/Emu/GS/GL/ShaderParam.h b/rpcs3/Emu/GS/GL/ShaderParam.h index f90ba43acc..553d4278a6 100644 --- a/rpcs3/Emu/GS/GL/ShaderParam.h +++ b/rpcs3/Emu/GS/GL/ShaderParam.h @@ -5,18 +5,21 @@ enum ParamFlag { PARAM_IN, PARAM_OUT, + PARAM_UNIFORM, PARAM_CONST, PARAM_NONE, }; struct ParamItem { - wxString name; - wxString location; + ArrayString name; + ArrayString location; + ArrayString value; - ParamItem(const wxString& _name, int _location) + ParamItem(const wxString& _name, int _location, const wxString& _value = wxEmptyString) : name(_name) , location(_location > -1 ? wxString::Format("layout (location = %d) ", _location) : "") + , value(_value) { } }; @@ -24,7 +27,7 @@ struct ParamItem struct ParamType { const ParamFlag flag; - wxString type; + ArrayString type; Array items; ParamType(const ParamFlag _flag, const wxString& _type) @@ -37,7 +40,7 @@ struct ParamType { for(u32 i=0; iSearchName(name)) t->items.Move(new ParamItem(name, -1, value)); + } + else + { + const u32 num = params.GetCount(); + params.Move(new ParamType(PARAM_CONST, type)); + params[num].items.Move(new ParamItem(name, -1, value)); + } + + return name; + } + wxString AddParam(const ParamFlag flag, wxString type, const wxString& name, int location = -1) { type = GetParamFlag(flag) + type; diff --git a/rpcs3/Emu/GS/GL/VertexProgram.cpp b/rpcs3/Emu/GS/GL/VertexProgram.cpp index 9938ad184f..7a9bcfcfb4 100644 --- a/rpcs3/Emu/GS/GL/VertexProgram.cpp +++ b/rpcs3/Emu/GS/GL/VertexProgram.cpp @@ -1,28 +1,36 @@ #include "stdafx.h" #include "VertexProgram.h" -wxString VertexDecompilerThread::GetVecMask() +wxString VertexDecompilerThread::GetMask(bool is_sca) { wxString ret = wxEmptyString; - if(d3.vec_writemask_x) ret += "x"; - if(d3.vec_writemask_y) ret += "y"; - if(d3.vec_writemask_z) ret += "z"; - if(d3.vec_writemask_w) ret += "w"; + if(is_sca) + { + 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"; + } + else + { + if(d3.vec_writemask_x) ret += "x"; + if(d3.vec_writemask_y) ret += "y"; + if(d3.vec_writemask_z) ret += "z"; + if(d3.vec_writemask_w) ret += "w"; + } return ret.IsEmpty() || ret == "xyzw" ? wxEmptyString : ("." + ret); } +wxString VertexDecompilerThread::GetVecMask() +{ + return GetMask(false); +} + 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); + return GetMask(true); } wxString VertexDecompilerThread::GetDST(bool isSca) @@ -38,26 +46,25 @@ wxString VertexDecompilerThread::GetDST(bool isSca) }; wxString ret = wxEmptyString; - u32 dst = isSca ? d3.sca_dst : d3.dst; - switch(dst) + switch(isSca ? 0x1f : d3.dst) { case 0x0: case 0x6: - ret += reg_table[dst]; + ret += reg_table[d3.dst]; break; case 0x1f: - ret += m_parr.AddParam(PARAM_NONE, "vec4", wxString::Format("tmp%d", d0.dst_tmp)); + ret += m_parr.AddParam(PARAM_NONE, "vec4", wxString::Format("tmp%d", isSca ? d3.sca_dst_tmp : d0.dst_tmp)); break; default: - if(dst < WXSIZEOF(reg_table)) + if(d3.dst < WXSIZEOF(reg_table)) { - ret += m_parr.AddParam(PARAM_OUT, "vec4", reg_table[dst]); + ret += m_parr.AddParam(PARAM_OUT, "vec4", reg_table[d3.dst]); } else { - ConLog.Error("Bad dst reg num: %d", dst); + ConLog.Error("Bad dst reg num: %d", d3.dst); ret += m_parr.AddParam(PARAM_OUT, "vec4", "unk"); } break; @@ -97,7 +104,7 @@ wxString VertexDecompilerThread::GetSRC(const u32 n, bool isSca) } break; case 3: //const - ret += m_parr.AddParam(PARAM_CONST, "vec4", wxString::Format("vc%d", d1.const_src)); + ret += m_parr.AddParam(PARAM_UNIFORM, "vec4", wxString::Format("vc%d", d1.const_src)); break; default: @@ -106,8 +113,7 @@ wxString VertexDecompilerThread::GetSRC(const u32 n, bool isSca) break; } - static const char f[4] = {'x', 'z', 'w', 'y'}; - static const char fSca[4] = {'x', 'z', 'y', 'w'}; + static const char f[4] = {'x', 'y', 'z', 'w'}; wxString swizzle = wxEmptyString; @@ -117,7 +123,7 @@ wxString VertexDecompilerThread::GetSRC(const u32 n, bool isSca) assert(src[n].swz_z == src[n].swz_w); assert(src[n].swz_x == src[n].swz_z); - ret += "." + fSca[src[n].swz_x]; + ret += "." + f[src[n].swz_x]; } else { @@ -144,34 +150,50 @@ wxString VertexDecompilerThread::GetSRC(const u32 n, bool isSca) return ret; } -void VertexDecompilerThread::AddVecCode(wxString code, bool src_mask) +void VertexDecompilerThread::AddCode(bool is_sca, wxString code, bool src_mask) { if(d0.cond == 0) return; - if(d0.cond != 7) + enum { - ConLog.Error("Bad cond! %d", d0.cond); - Emu.Pause(); - return; + lt = 0x1, + eq = 0x2, + gt = 0x4, + }; + + wxString cond; + + if(d0.cond != (lt | gt | eq)) + { + if(d0.cond & (lt | gt)) + { + cond = "!="; + } + else + { + if(d0.cond & lt) cond = "<"; + else if(d0.cond & gt) cond = ">"; + else if(d0.cond & eq) cond = "="; + + if(d0.cond & eq) cond += "="; + } + + ConLog.Error("cond! %d (%d %s %d %d)", d0.cond, d0.dst_tmp, cond, d1.input_src, d1.const_src); + cond = wxString::Format("if(tmp%d.x %s 0) ", d0.dst_tmp, cond); } - code = GetDST() + GetVecMask() + " = " + (src_mask ? code + GetVecMask() : code); + code = cond + GetDST(is_sca) + GetMask(is_sca) + " = " + (src_mask ? code + GetMask(is_sca) : code); main += "\t" + code + ";\n"; } -void VertexDecompilerThread::AddScaCode(wxString code, bool src_mask) +void VertexDecompilerThread::AddVecCode(const wxString& code, bool src_mask) { - if(d0.cond == 0) return; - if(d0.cond != 7) - { - ConLog.Error("Bad cond! %d", d0.cond); - Emu.Pause(); - return; - } + AddCode(false, code, src_mask); +} - code = GetDST(true) + GetScaMask() + " = " + (src_mask ? code + GetScaMask() : code); - - main += "\t" + code + ";\n"; +void VertexDecompilerThread::AddScaCode(const wxString& code, bool src_mask) +{ + AddCode(true, code, src_mask); } wxString VertexDecompilerThread::BuildCode() @@ -201,16 +223,18 @@ void VertexDecompilerThread::Task() d2.HEX = m_data[i++]; d3.HEX = m_data[i++]; - src[0].HEX = d2.src0l | (d1.src0h << 9); - src[1].HEX = d2.src1; - src[2].HEX = d3.src2l | (d2.src2h << 11); + src[0].src0l = d2.src0l; + src[0].src0h = d1.src0h; + src[1].src1 = d2.src1; + src[2].src2l = d3.src2l; + src[2].src2h = d2.src2h; switch(d1.sca_opcode) { case 0x00: break; // NOP case 0x01: AddScaCode(GetSRC(2, true)); break; // MOV case 0x02: AddScaCode("1 / (" + GetSRC(2, true) + ")"); break; // RCP - case 0x03: AddScaCode("clamp(1 / (" + GetSRC(2, true) + "), 5.42101e-20, 1.884467e19"); break; // RCC + case 0x03: AddScaCode("clamp(1 / (" + GetSRC(2, true) + "), 5.42101e-20, 1.884467e19)"); break; // RCC case 0x04: AddScaCode("inversesqrt(" + GetSRC(2, true) + ")"); break; // RSQ case 0x05: AddScaCode("exp(" + GetSRC(2, true) + ")"); break; // EXP case 0x06: AddScaCode("log(" + GetSRC(2, true) + ")"); break; // LOG @@ -229,7 +253,6 @@ void VertexDecompilerThread::Task() //case 0x13: break; // PSH : works differently (PSH o[1].x A0;) //case 0x14: break; // POP : works differently (POP o[1].x;) - default: ConLog.Error("Unknown vp sca_opcode 0x%x", d1.sca_opcode); Emu.Pause(); @@ -241,7 +264,7 @@ void VertexDecompilerThread::Task() case 0x00: break; //NOP case 0x01: AddVecCode(GetSRC(0)); break; //MOV case 0x02: AddVecCode("(" + GetSRC(0) + " * " + GetSRC(1) + ")"); break; //MUL - case 0x03: AddVecCode("(" + GetSRC(0) + " + " + GetSRC(1) + ")"); break; //ADD + case 0x03: AddVecCode("(" + GetSRC(0) + " + " + GetSRC(2) + ")"); break; //ADD case 0x04: AddVecCode("(" + GetSRC(0) + " * " + GetSRC(1) + " + " + GetSRC(2) + ")"); break; //MAD case 0x05: AddVecCode("dot(" + GetSRC(0) + ".xyz, " + GetSRC(1) + ".xyz)", false); break; //DP3 case 0x06: AddVecCode("(dot(" + GetSRC(0) + ".xyz, " + GetSRC(1) + ".xyz) + " + GetSRC(1) + ".w)", false); break; //DPH @@ -249,18 +272,17 @@ void VertexDecompilerThread::Task() case 0x08: AddVecCode("distance(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //DST case 0x09: AddVecCode("min(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //MIN case 0x0a: AddVecCode("max(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //MAX - case 0x0b: AddVecCode("lessThan(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //SLT - case 0x0c: AddVecCode("greaterThanEqual(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //SGE + case 0x0b: AddVecCode("vec4(lessThan(" + GetSRC(0) + ", " + GetSRC(1) + "))"); break; //SLT + case 0x0c: AddVecCode("vec4(greaterThanEqual(" + GetSRC(0) + ", " + GetSRC(1) + "))"); break; //SGE case 0x0e: AddVecCode("fract(" + GetSRC(0) + ")"); break; //FRC case 0x0f: AddVecCode("floor(" + GetSRC(0) + ")"); break; //FLR - case 0x10: AddVecCode("equal(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //SEQ - case 0x11: AddVecCode("vec4(0.0f, 0.0f, 0.0f, 0.0f)"); break; //SFL - case 0x12: AddVecCode("greaterThan(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //SGT - case 0x13: AddVecCode("lessThanEqual(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //SLE - case 0x14: AddVecCode("notEqual(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //SNE - case 0x15: AddVecCode("vec4(1.0f, 1.0f, 1.0f, 1.0f)"); break; //STR - case 0x16: AddVecCode("sign(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //SSG - + case 0x10: AddVecCode("vec4(equal(" + GetSRC(0) + ", " + GetSRC(1) + "))"); break; //SEQ + //case 0x11: AddVecCode("vec4(0.0f, 0.0f, 0.0f, 0.0f)"); break; //SFL + case 0x12: AddVecCode("vec4(greaterThan(" + GetSRC(0) + ", " + GetSRC(1) + "))"); break; //SGT + case 0x13: AddVecCode("vec4(lessThanEqual(" + GetSRC(0) + ", " + GetSRC(1) + "))"); break; //SLE + case 0x14: AddVecCode("vec4(notEqual(" + GetSRC(0) + ", " + GetSRC(1) + "))"); break; //SNE + //case 0x15: AddVecCode("vec4(1.0f, 1.0f, 1.0f, 1.0f)"); break; //STR + case 0x16: AddVecCode("sign(" + GetSRC(0) + ")"); break; //SSG default: ConLog.Error("Unknown vp opcode 0x%x", d1.vec_opcode); @@ -276,7 +298,7 @@ void VertexDecompilerThread::Task() } VertexProgram::VertexProgram() - : m_decompiler_thread(NULL) + : m_decompiler_thread(nullptr) , id(0) { } @@ -358,13 +380,7 @@ void VertexProgram::Compile() void VertexProgram::Delete() { data.Clear(); - for(u32 i=0; i constants4; - Array data; ParamArray parr; @@ -174,11 +187,10 @@ struct VertexData Array data; - VertexData() { memset(this, 0, sizeof(*this)); } - ~VertexData() { data.Clear(); } + VertexData(); + void Reset(); bool IsEnabled() { return size > 0; } - void Load(u32 start, u32 count); u32 GetTypeSize(); diff --git a/rpcs3/Emu/SysCalls/FuncList.cpp b/rpcs3/Emu/SysCalls/FuncList.cpp index 0dbc923703..83557bc3e3 100644 --- a/rpcs3/Emu/SysCalls/FuncList.cpp +++ b/rpcs3/Emu/SysCalls/FuncList.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "SysCalls.h" -#define FUNC_LOG_ERROR(x) ConLog.Error(x); return SC_ARGS_1 +#define FUNC_LOG_ERROR(x) ConLog.Error(x); return 0 s64 SysCalls::DoFunc(const u32 id) { switch(id) From 0aff0499608f4cca6746e832d20d3b679449c39c Mon Sep 17 00:00:00 2001 From: DH Date: Sat, 17 Aug 2013 01:22:26 +0300 Subject: [PATCH 07/13] - Improved Vertex & Fragment Shader Decompilers. - Fixed some FPR instructions. - Implemented more GCM syscalls. - Fixed callbacks alert. --- rpcs3/Emu/Cell/PPUInterpreter.h | 160 +++++++++++++++--- rpcs3/Emu/Cell/PPUThread.cpp | 12 +- rpcs3/Emu/Cell/PPUThread.h | 10 +- rpcs3/Emu/FS/VFS.cpp | 2 +- rpcs3/Emu/GS/GL/FragmentProgram.cpp | 94 ++++++---- rpcs3/Emu/GS/GL/FragmentProgram.h | 2 +- rpcs3/Emu/GS/GL/GLBuffers.cpp | 7 + rpcs3/Emu/GS/GL/GLBuffers.h | 1 + rpcs3/Emu/GS/GL/GLGSRender.cpp | 52 +++++- rpcs3/Emu/GS/GL/GLGSRender.h | 53 +++++- rpcs3/Emu/GS/GL/GLProcTable.tbl | 3 + rpcs3/Emu/GS/GL/Program.cpp | 8 +- rpcs3/Emu/GS/GL/VertexProgram.cpp | 12 +- rpcs3/Emu/GS/GSRender.h | 2 + rpcs3/Emu/SysCalls/Callback.cpp | 37 +++- rpcs3/Emu/SysCalls/Callback.h | 26 ++- rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp | 3 +- rpcs3/Emu/SysCalls/SysCalls.h | 2 + rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp | 18 ++ rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp | 10 +- .../Emu/SysCalls/lv2/SC_SysUtil_MsgDialog.cpp | 2 +- 21 files changed, 410 insertions(+), 106 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index 9125b4a43a..acc50fb558 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -3236,11 +3236,58 @@ private: } void FRES(u32 frd, u32 frb, bool rc) { - if(CPU.FPR[frb] == 0.0) CPU.SetFPSCRException(FPSCR_ZX); - CPU.FPR[frd] = static_cast(1.0f/CPU.FPR[frb]); - CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); - CPU.FPSCR.FI = 0; - CPU.FPSCR.FR = 0; + double res; + + if(_fpclass(CPU.FPR[frb]) >= _FPCLASS_NZ) + { + res = static_cast(1.0 / CPU.FPR[frb]); + if(FPRdouble::IsINF(res) && CPU.FPR[frb] != 0.0) + { + if(res > 0.0) + { + (u64&)res = 0x47EFFFFFE0000000ULL; + } + else + { + (u64&)res = 0xC7EFFFFFE0000000ULL; + } + } + } + else + { + u64 v = CPU.FPR[frb]; + + if(v == 0ULL) + { + v = 0x7FF0000000000000ULL; + } + else if(v == 0x8000000000000000ULL) + { + v = 0xFFF0000000000000ULL; + } + else if(FPRdouble::IsNaN(CPU.FPR[frb])) + { + v = 0x7FF8000000000000ULL; + } + else if(CPU.FPR[frb] < 0.0) + { + v = 0x8000000000000000ULL; + } + else + { + v = 0ULL; + } + + res = (double&)v; + } + + if(CPU.FPR[frb] == 0.0) + { + CPU.SetFPSCRException(FPSCR_ZX); + } + + CPU.FPR[frd] = res; + if(rc) UNK("fres.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FMULS(u32 frd, u32 fra, u32 frc, bool rc) @@ -3339,7 +3386,9 @@ private: } void FCMPU(u32 crfd, u32 fra, u32 frb) { - if((CPU.FPSCR.FPRF = FPRdouble::Cmp(CPU.FPR[fra], CPU.FPR[frb])) == 1) + int cmp_res = FPRdouble::Cmp(CPU.FPR[fra], CPU.FPR[frb]); + + if(cmp_res == CR_SO) { if(FPRdouble::IsSNaN(CPU.FPR[fra]) || FPRdouble::IsSNaN(CPU.FPR[frb])) { @@ -3347,7 +3396,8 @@ private: } } - CPU.SetCR(crfd, CPU.FPSCR.FPRF); + CPU.FPSCR.FPRF = cmp_res; + CPU.SetCR(crfd, cmp_res); } void FRSP(u32 frd, u32 frb, bool rc) { @@ -3467,20 +3517,44 @@ private: } void FDIV(u32 frd, u32 fra, u32 frb, bool rc) { - if(FPRdouble::IsINF(CPU.FPR[fra]) == 0.0 && CPU.FPR[frb] == 0.0) + double res; + + if(FPRdouble::IsNaN(CPU.FPR[fra])) { - CPU.FPSCR.VXZDZ = 1; + res = CPU.FPR[fra]; } - else if(FPRdouble::IsINF(CPU.FPR[fra]) && FPRdouble::IsINF(CPU.FPR[frb])) + else if(FPRdouble::IsNaN(CPU.FPR[frb])) { - CPU.FPSCR.VXIDI = 1; + res = CPU.FPR[frb]; } - else if(CPU.FPR[fra] != 0.0 && CPU.FPR[frb] == 0.0) + else { - CPU.SetFPSCRException(FPSCR_ZX); + if(CPU.FPR[frb] == 0.0) + { + if(CPU.FPR[fra] == 0.0) + { + CPU.FPSCR.VXZDZ = 1; + res = FPR_NAN; + } + else + { + res = CPU.FPR[fra] / CPU.FPR[frb]; + } + + CPU.SetFPSCRException(FPSCR_ZX); + } + else if(FPRdouble::IsINF(CPU.FPR[fra]) && FPRdouble::IsINF(CPU.FPR[frb])) + { + CPU.FPSCR.VXIDI = 1; + res = FPR_NAN; + } + else + { + res = CPU.FPR[fra] / CPU.FPR[frb]; + } } - CPU.FPR[frd] = CPU.FPR[fra] / CPU.FPR[frb]; + CPU.FPR[frd] = res; CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fdiv.");//CPU.UpdateCR1(CPU.FPR[frd]); } @@ -3509,15 +3583,31 @@ private: } void FMUL(u32 frd, u32 fra, u32 frc, bool rc) { - CPU.FPR[frd] = CPU.FPR[fra] * CPU.FPR[frc]; - CPU.FPSCR.FI = 0; - CPU.FPSCR.FR = 0; - CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); + if((FPRdouble::IsINF(CPU.FPR[fra]) && CPU.FPR[frc] == 0.0) || (FPRdouble::IsINF(CPU.FPR[frc]) && CPU.FPR[fra] == 0.0)) + { + CPU.SetFPSCRException(FPSCR_VXIMZ); + CPU.FPR[frd] = FPR_NAN; + CPU.FPSCR.FI = 0; + CPU.FPSCR.FR = 0; + CPU.FPSCR.FPRF = FPR_QNAN; + } + else + { + if(FPRdouble::IsSNaN(CPU.FPR[fra]) || FPRdouble::IsSNaN(CPU.FPR[frc])) + { + CPU.SetFPSCRException(FPSCR_VXSNAN); + } + + CPU.FPR[frd] = CPU.FPR[fra] * CPU.FPR[frc]; + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); + } + if(rc) UNK("fmul.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FRSQRTE(u32 frd, u32 frb, bool rc) { - //if(CPU.FPR[frb]. + UNIMPLEMENTED(); + //CPU.FPR[frd] = 1.0f / (float)sqrt(CPU.FPR[frb]); } void FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { @@ -3545,14 +3635,16 @@ private: } void FCMPO(u32 crfd, u32 fra, u32 frb) { - if((CPU.FPSCR.FPRF = FPRdouble::Cmp(CPU.FPR[fra], CPU.FPR[frb])) == 1) + int cmp_res = FPRdouble::Cmp(CPU.FPR[fra], CPU.FPR[frb]); + + if(cmp_res == CR_SO) { if(FPRdouble::IsSNaN(CPU.FPR[fra]) || FPRdouble::IsSNaN(CPU.FPR[frb])) { CPU.SetFPSCRException(FPSCR_VXSNAN); if(!CPU.FPSCR.VE) CPU.SetFPSCRException(FPSCR_VXVC); } - else if(FPRdouble::IsQNaN(CPU.FPR[fra]) || FPRdouble::IsQNaN(CPU.FPR[frb])) + else { CPU.SetFPSCRException(FPSCR_VXVC); } @@ -3560,11 +3652,12 @@ private: CPU.FPSCR.FX = 1; } - CPU.SetCR(crfd, CPU.FPSCR.FPRF); + CPU.FPSCR.FPRF = cmp_res; + CPU.SetCR(crfd, cmp_res); } void FNEG(u32 frd, u32 frb, bool rc) { - CPU.FPR[frd] = ((u64&)CPU.FPR[frb]) ^ (1ULL << 63); + CPU.FPR[frd] = -CPU.FPR[frb]; if(rc) UNK("fneg.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FMR(u32 frd, u32 frb, bool rc) @@ -3574,7 +3667,7 @@ private: } void FNABS(u32 frd, u32 frb, bool rc) { - (u64&)CPU.FPR[frd] = (u64&)CPU.FPR[frb] | 0x8000000000000000ULL; + CPU.FPR[frd] = -fabs(CPU.FPR[frb]); if(rc) UNK("fnabs.");//CPU.UpdateCR1(CPU.FPR[frd]); } void FABS(u32 frd, u32 frb, bool rc) @@ -3685,7 +3778,24 @@ private: } void FCFID(u32 frd, u32 frb, bool rc) { - CPU.FPR[frd] = (double)(u64&)CPU.FPR[frb]; + s64 bi = (s64&)CPU.FPR[frb]; + double bf = (double)bi; + s64 bfi = (s64)bf; + + if(bi == bfi) + { + CPU.SetFPSCR_FI(0); + CPU.FPSCR.FR = 0; + } + else + { + CPU.SetFPSCR_FI(1); + CPU.FPSCR.FR = abs(bfi) > abs(bi); + } + + CPU.FPR[frd] = bf; + + CPU.FPSCR.FPRF = CPU.FPR[frd].GetType(); if(rc) UNK("fcfid.");//CPU.UpdateCR1(CPU.FPR[frd]); } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index e3cbffe359..54717b8fd6 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -214,7 +214,7 @@ void PPUThread::DoCode(const s32 code) bool FPRdouble::IsINF(PPCdouble d) { - return d.GetType() == FPR_INF; + return ((u64&)d & 0x7FFFFFFFFFFFFFFFULL) == 0x7FF0000000000000ULL; } bool FPRdouble::IsNaN(PPCdouble d) @@ -224,12 +224,18 @@ bool FPRdouble::IsNaN(PPCdouble d) bool FPRdouble::IsQNaN(PPCdouble d) { - return d.GetType() == FPR_QNAN; + return + ((u64&)d & 0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && + ((u64&)d & 0x0007FFFFFFFFFFFULL) == 0ULL && + ((u64&)d & 0x000800000000000ULL) != 0ULL; } bool FPRdouble::IsSNaN(PPCdouble d) { - return d.GetType() == FPR_SNAN; + return + ((u64&)d & 0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && + ((u64&)d & 0x000FFFFFFFFFFFFFULL) != 0ULL && + ((u64&)d & 0x0008000000000000ULL) == 0ULL; } int FPRdouble::Cmp(PPCdouble a, PPCdouble b) diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 571565a2ff..6954c31412 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -298,11 +298,11 @@ union VSCRhdr enum FPRType { - FPR_NORM, - FPR_ZERO, - FPR_SNAN, + //FPR_NORM, + //FPR_ZERO, + //FPR_SNAN, //FPR_QNAN, - FPR_INF, + //FPR_INF, FPR_PZ = 0x2, FPR_PN = 0x4, FPR_PINF = 0x5, @@ -357,7 +357,7 @@ struct PPCdouble switch(fpc) { - case _FPCLASS_SNAN: return FPR_SNAN; + case _FPCLASS_SNAN:// return FPR_SNAN; case _FPCLASS_QNAN: return FPR_QNAN; case _FPCLASS_NINF: return FPR_NINF; case _FPCLASS_NN: return FPR_NN; diff --git a/rpcs3/Emu/FS/VFS.cpp b/rpcs3/Emu/FS/VFS.cpp index 86e1e87b7d..0a162f9f2c 100644 --- a/rpcs3/Emu/FS/VFS.cpp +++ b/rpcs3/Emu/FS/VFS.cpp @@ -136,7 +136,7 @@ void VFS::SaveLoadDevices(Array& res, bool is_load) IniEntry entries_count; entries_count.Init("count", "VFSManager"); - int count; + int count = 0; if(is_load) { count = entries_count.LoadValue(count); diff --git a/rpcs3/Emu/GS/GL/FragmentProgram.cpp b/rpcs3/Emu/GS/GL/FragmentProgram.cpp index 5ae2e41e9a..bd30fb2a45 100644 --- a/rpcs3/Emu/GS/GL/FragmentProgram.cpp +++ b/rpcs3/Emu/GS/GL/FragmentProgram.cpp @@ -5,34 +5,37 @@ void FragmentDecompilerThread::AddCode(wxString code) { if(!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) return; - wxString cond; + wxString cond = wxEmptyString; - if(src0.exec_if_gr) + if(!src0.exec_if_gr || !src0.exec_if_lt || !src0.exec_if_eq) { - cond = ">"; - } - else if(src0.exec_if_lt) - { - cond = "<"; - } - else if(src0.exec_if_eq) - { - cond = "="; - } - - if(src0.exec_if_eq) - { - cond += "="; - } - else - { - if(src0.exec_if_gr && src0.exec_if_lt) + if(src0.exec_if_gr) { - cond = "!="; + cond = ">"; + } + else if(src0.exec_if_lt) + { + cond = "<"; + } + else if(src0.exec_if_eq) + { + cond = "="; + } + + if(src0.exec_if_eq) + { + cond += "="; + } + else + { + if(src0.exec_if_gr && src0.exec_if_lt) + { + cond = "!="; + } } } - if(cond) + if(cond.Len()) { ConLog.Error("cond! [eq: %d gr: %d lt: %d] (%s)", src0.exec_if_eq, src0.exec_if_gr, src0.exec_if_lt, cond); Emu.Pause(); @@ -43,12 +46,12 @@ void FragmentDecompilerThread::AddCode(wxString code) { switch(src1.scale) { - case 1: code = "(" + code + ") * 2"; break; - case 2: code = "(" + code + ") * 4"; break; - case 3: code = "(" + code + ") * 8"; break; - case 5: code = "(" + code + ") / 2"; break; - case 6: code = "(" + code + ") / 4"; break; - case 7: code = "(" + code + ") / 8"; break; + case 1: code = "(" + code + " * 2)"; break; + case 2: code = "(" + code + " * 4)"; break; + case 3: code = "(" + code + " * 8)"; break; + case 5: code = "(" + code + " / 2)"; break; + case 6: code = "(" + code + " / 4)"; break; + case 7: code = "(" + code + " / 8)"; break; default: ConLog.Error("Bad scale: %d", src1.scale); @@ -57,6 +60,12 @@ void FragmentDecompilerThread::AddCode(wxString code) } } + if(dst.fp16) + { + //HACK! TODO: fp16 -> fp32 + code = "/*" + code + "*/ vec4(1.0, 1.0, 1.0, 1.0)"; + } + code = AddReg(dst.dest_reg, dst.fp16) + GetMask() + " = " + code + GetMask(); main += "\t" + code + ";\n"; @@ -66,18 +75,24 @@ wxString FragmentDecompilerThread::GetMask() { wxString ret = wxEmptyString; - if(dst.mask_x) ret += 'x'; - if(dst.mask_y) ret += 'y'; - if(dst.mask_z) ret += 'z'; - if(dst.mask_w) ret += 'w'; + static const char dst_mask[2][4] = + { + {'x', 'y', 'z', 'w'}, + {'r', 'g', 'b', 'a'} + }; - return ret.IsEmpty() || ret == "xyzw" ? wxEmptyString : ("." + ret); + if(dst.mask_x) ret += dst_mask[dst.dest_reg == 0][0]; + if(dst.mask_y) ret += dst_mask[dst.dest_reg == 0][1]; + if(dst.mask_z) ret += dst_mask[dst.dest_reg == 0][2]; + if(dst.mask_w) ret += dst_mask[dst.dest_reg == 0][3]; + + return ret.IsEmpty() || strncmp(ret, dst_mask[dst.dest_reg == 0], 4) == 0 ? wxEmptyString : ("." + ret); } wxString FragmentDecompilerThread::AddReg(u32 index, int fp16) { //if(!index) return "gl_FragColor"; - return m_parr.AddParam(index ? PARAM_NONE : PARAM_OUT, "vec4", + return m_parr.AddParam((index || fp16) ? PARAM_NONE : PARAM_OUT, "vec4", wxString::Format((fp16 ? "h%d" : "r%d"), index), (index || fp16) ? -1 : 0); } @@ -103,6 +118,8 @@ template wxString FragmentDecompilerThread::GetSRC(T src) { wxString ret = wxEmptyString; + bool is_color = src.reg_type == 0 || (src.reg_type == 1 && dst.src_attr_reg_num >= 1 && dst.src_attr_reg_num <= 3); + switch(src.reg_type) { case 0: //tmp @@ -148,14 +165,17 @@ template wxString FragmentDecompilerThread::GetSRC(T src) break; } - static const char f[4] = {'x', 'y', 'z', 'w'}; + static const char f_pos[4] = {'x', 'y', 'z', 'w'}; + static const char f_col[4] = {'r', 'g', 'b', 'a'}; + const char *f = is_color ? f_col : f_pos; + wxString swizzle = wxEmptyString; swizzle += f[src.swizzle_x]; swizzle += f[src.swizzle_y]; swizzle += f[src.swizzle_z]; swizzle += f[src.swizzle_w]; - if(swizzle != "xyzw") ret += "." + swizzle; + if(strncmp(swizzle, f, 4) != 0) ret += "." + swizzle; if(src.abs) ret = "abs(" + ret + ")"; if(src.neg) ret = "-" + ret; @@ -280,7 +300,7 @@ void FragmentDecompilerThread::Task() } ShaderProgram::ShaderProgram() - : m_decompiler_thread(NULL) + : m_decompiler_thread(nullptr) , id(0) { } diff --git a/rpcs3/Emu/GS/GL/FragmentProgram.h b/rpcs3/Emu/GS/GL/FragmentProgram.h index 09955cbc95..54a0b6b784 100644 --- a/rpcs3/Emu/GS/GL/FragmentProgram.h +++ b/rpcs3/Emu/GS/GL/FragmentProgram.h @@ -41,7 +41,7 @@ struct FragmentDecompilerThread : public ThreadBase u32 swizzle_z : 2; u32 swizzle_w : 2; u32 neg : 1; - u32 exec_if_le : 1; + u32 exec_if_lt : 1; u32 exec_if_eq : 1; u32 exec_if_gr : 1; u32 cond_swizzle_x : 2; diff --git a/rpcs3/Emu/GS/GL/GLBuffers.cpp b/rpcs3/Emu/GS/GL/GLBuffers.cpp index 8d8e96957b..ce788fdd23 100644 --- a/rpcs3/Emu/GS/GL/GLBuffers.cpp +++ b/rpcs3/Emu/GS/GL/GLBuffers.cpp @@ -36,6 +36,7 @@ void GLBufferObject::Delete() void GLBufferObject::Bind(u32 type, u32 num) { + assert(num < m_id.GetCount()); glBindBuffer(type, m_id[num]); } @@ -105,10 +106,16 @@ void GLvao::Bind() const glBindVertexArray(m_id); } +void GLvao::Unbind() +{ + glBindVertexArray(0); +} + void GLvao::Delete() { if(!IsCreated()) return; + Unbind(); glDeleteVertexArrays(1, &m_id); m_id = 0; } diff --git a/rpcs3/Emu/GS/GL/GLBuffers.h b/rpcs3/Emu/GS/GL/GLBuffers.h index e42a7b4078..946184067e 100644 --- a/rpcs3/Emu/GS/GL/GLBuffers.h +++ b/rpcs3/Emu/GS/GL/GLBuffers.h @@ -43,6 +43,7 @@ public: void Create(); void Bind() const; + static void Unbind(); 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 686f7f099f..d6adabd82e 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.cpp +++ b/rpcs3/Emu/GS/GL/GLGSRender.cpp @@ -1,8 +1,9 @@ #include "stdafx.h" #include "GLGSRender.h" #include "Emu/Cell/PPCInstrTable.h" + #define CMD_DEBUG 0 -#define DUMP_VERTEX_DATA 0 +#define DUMP_VERTEX_DATA 1 #if CMD_DEBUG #define CMD_LOG ConLog.Write @@ -12,9 +13,8 @@ gcmBuffer gcmBuffers[2]; -void checkForGlError(const char* situation) +void printGlError(GLenum err, const char* situation) { - GLenum err = glGetError(); if(err != GL_NO_ERROR) { ConLog.Error("%s: opengl error 0x%04x", situation, err); @@ -22,6 +22,11 @@ void checkForGlError(const char* situation) } } +void checkForGlError(const char* situation) +{ + printGlError(glGetError(), situation); +} + #if 0 #define checkForGlError(x) /*x*/ #endif @@ -168,7 +173,15 @@ void GLRSXThread::Task() } } - if(draw) p.m_frame->Flip(); + if(draw) + { + p.m_frame->Flip(); + if(p.m_flip_handler) + { + p.m_flip_handler.Handle(1, 0, 0); + p.m_flip_handler.Branch(false); + } + } p.m_flip_status = 0; if(SemaphorePostAndWait(p.m_sem_flip)) continue; @@ -279,7 +292,7 @@ void GLGSRender::Close() } if(m_frame->IsShown()) m_frame->Hide(); - m_ctrl = NULL; + m_ctrl = nullptr; } void GLGSRender::EnableVertexData(bool indexed_draw) @@ -425,6 +438,7 @@ void GLGSRender::DisableVertexData() m_vertex_data[i].data.Clear(); glDisableVertexAttribArray(i); } + m_vao.Unbind(); } void GLGSRender::LoadVertexData(u32 first, u32 count) @@ -509,7 +523,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c index, offset, location, cubemap, dimension, format, mipmap); u32 tex_addr = GetAddress(offset, location); - ConLog.Warning("texture addr = 0x%x", tex_addr); + //ConLog.Warning("texture addr = 0x%x", tex_addr); tex.SetOffset(tex_addr); tex.SetFormat(cubemap, dimension, format, mipmap); } @@ -1141,6 +1155,28 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case NV4097_SET_SCULL_CONTROL: break; + case NV4097_GET_REPORT: + { + u32 a0 = args[0]; + u8 type = a0 >> 24; + u32 offset = a0 & 0xffffff; + + u64 data; + switch(type) + { + case 1: + data = std::chrono::steady_clock::now().time_since_epoch().count(); + break; + + default: + ConLog.Error("NV4097_GET_REPORT: bad type %d", type); + break; + } + + Memory.Write64(m_localAddress + offset, data); + } + break; + default: { wxString log = GetMethodName(cmd); @@ -1293,8 +1329,9 @@ void GLGSRender::ExecCMD() GLTexture& tex = m_frame->GetTexture(i); if(!tex.IsEnabled()) continue; - glActiveTexture(GL_TEXTURE0_ARB + i); + glActiveTexture(GL_TEXTURE0 + i); checkForGlError("glActiveTexture"); + tex.Create(); tex.Bind(); checkForGlError("tex.Bind"); m_program.SetTex(i); @@ -1342,6 +1379,7 @@ void GLGSRender::ExecCMD() EnableVertexData(); InitVertexData(); m_vao.Bind(); + glDrawArrays(m_draw_mode, 0, m_draw_array_count); checkForGlError("glDrawArrays"); DisableVertexData(); diff --git a/rpcs3/Emu/GS/GL/GLGSRender.h b/rpcs3/Emu/GS/GL/GLGSRender.h index 22def2cbd4..1736ea74f2 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.h +++ b/rpcs3/Emu/GS/GL/GLGSRender.h @@ -10,6 +10,7 @@ #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "gl.lib") +void printGlError(GLenum err, const char* situation); void checkForGlError(const char* situation); class GLTexture @@ -40,6 +41,11 @@ public: void Create() { + if(m_id) + { + Delete(); + } + if(!m_id) { glGenTextures(1, &m_id); @@ -83,24 +89,27 @@ public: void Init() { Bind(); - ConLog.Warning("texture addr = 0x%x, width = %d, height = %d", m_offset, m_width, m_height); + //ConLog.Warning("texture addr = 0x%x, width = %d, height = %d", m_offset, m_width, m_height); //TODO: safe init checkForGlError("GLTexture::Init() -> glBindTexture"); switch(m_format & ~(0x20 | 0x40)) { case 0x81: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RED, GL_UNSIGNED_BYTE, Memory.GetMemFromAddr(m_offset)); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_BLUE, GL_UNSIGNED_BYTE, Memory.GetMemFromAddr(m_offset)); checkForGlError("GLTexture::Init() -> glTexImage2D"); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_BLUE); + checkForGlError("GLTexture::Init() -> glTexParameteri"); break; 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)); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, Memory.GetMemFromAddr(m_offset)); checkForGlError("GLTexture::Init() -> glTexImage2D"); break; @@ -112,13 +121,31 @@ public: void Save(const wxString& name) { - if(!m_id || !m_offset) return; + if(!m_id || !m_offset || !m_width || !m_height) return; u32* alldata = new u32[m_width * m_height]; Bind(); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, alldata); + switch(m_format & ~(0x20 | 0x40)) + { + case 0x81: + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, alldata); + break; + + case 0x85: + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, alldata); + break; + + default: + delete[] alldata; + return; + } + + { + wxFile f(name + ".raw", wxFile::write); + f.Write(alldata, m_width * m_height * 4); + } u8* data = new u8[m_width * m_height * 3]; u8* alpha = new u8[m_width * m_height]; @@ -164,6 +191,14 @@ public: glBindTexture(GL_TEXTURE_2D, 0); } + void Delete() + { + if(m_id) + { + glDeleteTextures(1, &m_id); + m_id = 0; + } + } void Enable(bool enable) { m_enabled = enable; } bool IsEnabled() const { return m_enabled; } }; diff --git a/rpcs3/Emu/GS/GL/GLProcTable.tbl b/rpcs3/Emu/GS/GL/GLProcTable.tbl index 90a8ac7b65..9d76816541 100644 --- a/rpcs3/Emu/GS/GL/GLProcTable.tbl +++ b/rpcs3/Emu/GS/GL/GLProcTable.tbl @@ -40,6 +40,9 @@ OPENGL_PROC(PFNGLDEPTHRANGEFPROC, DepthRangef); OPENGL_PROC(PFNGLUNIFORM1IPROC, Uniform1i); OPENGL_PROC(PFNGLUNIFORM1FPROC, Uniform1f); OPENGL_PROC(PFNGLUNIFORM4FPROC, Uniform4f); +OPENGL_PROC(PFNGLPROGRAMUNIFORM1IPROC, ProgramUniform1i); +OPENGL_PROC(PFNGLPROGRAMUNIFORM1FPROC, ProgramUniform1f); +OPENGL_PROC(PFNGLPROGRAMUNIFORM4FPROC, ProgramUniform4f); OPENGL_PROC(PFNGLUNIFORMMATRIX4FVPROC, UniformMatrix4fv); OPENGL_PROC(PFNGLUSEPROGRAMPROC, UseProgram); OPENGL_PROC2(PFNWGLSWAPINTERVALEXTPROC, SwapInterval, wglSwapIntervalEXT); diff --git a/rpcs3/Emu/GS/GL/Program.cpp b/rpcs3/Emu/GS/GL/Program.cpp index 9d74358cc0..530031abcd 100644 --- a/rpcs3/Emu/GS/GL/Program.cpp +++ b/rpcs3/Emu/GS/GL/Program.cpp @@ -89,8 +89,12 @@ void Program::SetTex(u32 index) { int loc = GetLocation(wxString::Format("tex%d", index)); checkForGlError(wxString::Format("GetLocation(tex%d)", index)); - glUniform1i(loc, index); - checkForGlError(wxString::Format("SetTex(%d - %d)", index, loc)); + glProgramUniform1i(id, loc, index); + GLenum err = glGetError(); + if(err != 0x502) + { + printGlError(err, wxString::Format("SetTex(%d - %d - %d)", id, index, loc)); + } } void Program::Delete() diff --git a/rpcs3/Emu/GS/GL/VertexProgram.cpp b/rpcs3/Emu/GS/GL/VertexProgram.cpp index 7a9bcfcfb4..a5048f61ea 100644 --- a/rpcs3/Emu/GS/GL/VertexProgram.cpp +++ b/rpcs3/Emu/GS/GL/VertexProgram.cpp @@ -181,7 +181,14 @@ void VertexDecompilerThread::AddCode(bool is_sca, wxString code, bool src_mask) cond = wxString::Format("if(tmp%d.x %s 0) ", d0.dst_tmp, cond); } - code = cond + GetDST(is_sca) + GetMask(is_sca) + " = " + (src_mask ? code + GetMask(is_sca) : code); + wxString value = src_mask ? code + GetMask(is_sca) : code; + + if(d0.staturate) + { + value = "clamp(" + value + ", 0.0, 1.0)"; + } + + code = cond + GetDST(is_sca) + GetMask(is_sca) + " = " + value; main += "\t" + code + ";\n"; } @@ -209,7 +216,7 @@ wxString VertexDecompilerThread::BuildCode() "#version 330\n" "\n" "%s\n" - "void main()\n{\n%s}\n"; + "void main()\n{\n\tgl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);\n%s}\n"; return wxString::Format(prot, p, main); } @@ -294,6 +301,7 @@ void VertexDecompilerThread::Task() } m_shader = BuildCode(); + main = wxEmptyString; } diff --git a/rpcs3/Emu/GS/GSRender.h b/rpcs3/Emu/GS/GSRender.h index 86c05ece7b..aa08b9cdb7 100644 --- a/rpcs3/Emu/GS/GSRender.h +++ b/rpcs3/Emu/GS/GSRender.h @@ -1,5 +1,6 @@ #pragma once #include "Emu/GS/GCM.h" +#include "Emu/SysCalls/Callback.h" enum Method { @@ -48,6 +49,7 @@ struct GSRender int m_flip_status; int m_flip_mode; volatile bool m_draw; + Callback m_flip_handler; GSRender(); diff --git a/rpcs3/Emu/SysCalls/Callback.cpp b/rpcs3/Emu/SysCalls/Callback.cpp index 4d456c19a5..62c2857016 100644 --- a/rpcs3/Emu/SysCalls/Callback.cpp +++ b/rpcs3/Emu/SysCalls/Callback.cpp @@ -13,6 +13,31 @@ Callback::Callback(u32 slot, u64 addr) { } +u32 Callback::GetSlot() const +{ + return m_slot; +} + +u64 Callback::GetAddr() const +{ + return m_addr; +} + +void Callback::SetSlot(u32 slot) +{ + m_slot = slot; +} + +void Callback::SetAddr(u64 addr) +{ + m_addr = addr; +} + +bool Callback::HasData() const +{ + return m_has_data; +} + void Callback::Handle(u64 _a1, u64 _a2, u64 _a3) { a1 = _a1; @@ -21,13 +46,13 @@ void Callback::Handle(u64 _a1, u64 _a2, u64 _a3) m_has_data = true; } -void Callback::Branch() +void Callback::Branch(bool wait) { m_has_data = false; PPCThread& new_thread = Emu.GetCPU().AddThread(PPC_THREAD_PPU); - new_thread.SetPc(m_addr); + new_thread.SetEntry(m_addr); new_thread.SetPrio(1001); new_thread.stack_size = 0x10000; new_thread.SetName("Callback"); @@ -40,7 +65,13 @@ void Callback::Branch() new_thread.Exec(); - GetCurrentPPCThread()->Wait(new_thread); + if(wait) + GetCurrentPPCThread()->Wait(new_thread); +} + +Callback::operator bool() const +{ + return GetAddr() != 0; } Callback2::Callback2(u32 slot, u64 addr, u64 userdata) : Callback(slot, addr) diff --git a/rpcs3/Emu/SysCalls/Callback.h b/rpcs3/Emu/SysCalls/Callback.h index fe720ab18e..49395f3527 100644 --- a/rpcs3/Emu/SysCalls/Callback.h +++ b/rpcs3/Emu/SysCalls/Callback.h @@ -1,19 +1,29 @@ #pragma once -struct Callback +class Callback { +protected: u64 m_addr; u32 m_slot; + bool m_has_data; + +public: u64 a1; u64 a2; u64 a3; - bool m_has_data; + u32 GetSlot() const; + u64 GetAddr() const; + void SetSlot(u32 slot); + void SetAddr(u64 addr); + bool HasData() const; - Callback(u32 slot, u64 addr); - void Handle(u64 a1, u64 a2, u64 a3); - void Branch(); + Callback(u32 slot = 0, u64 addr = 0); + void Handle(u64 a1 = 0, u64 a2 = 0, u64 a3 = 0); + void Branch(bool wait); + + operator bool() const; }; struct Callback2 : public Callback @@ -46,7 +56,7 @@ struct Callbacks { for(u32 i=0; i Date: Sat, 17 Aug 2013 19:23:03 +0300 Subject: [PATCH 08/13] - Fixed sys_ppu_thread_exit. - Disabled some dbg tools. --- rpcs3/Emu/Cell/PPCDecoder.h | 14 +-- rpcs3/Emu/Cell/PPCInstrTable.h | 9 +- rpcs3/Emu/Cell/PPCThread.cpp | 10 +-- rpcs3/Emu/Cell/PPCThreadManager.cpp | 7 +- rpcs3/Emu/Cell/PPUInstrTable.h | 2 + rpcs3/Emu/Cell/PPUOpcodes.h | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 2 + rpcs3/Emu/GS/GL/GLGSRender.cpp | 5 +- rpcs3/Emu/SysCalls/Modules.cpp | 29 ++++--- rpcs3/Emu/SysCalls/Modules.h | 5 +- rpcs3/Emu/SysCalls/SysCalls.cpp | 106 ++++++++++++----------- rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp | 6 +- rpcs3/Gui/Debugger.cpp | 7 +- rpcs3/Gui/InterpreterDisAsm.cpp | 58 ++++++++----- rpcs3/Loader/ELF64.cpp | 2 +- rpcs3/rpcs3.h | 1 + 16 files changed, 152 insertions(+), 113 deletions(-) diff --git a/rpcs3/Emu/Cell/PPCDecoder.h b/rpcs3/Emu/Cell/PPCDecoder.h index 81e24087dd..68e7d6da72 100644 --- a/rpcs3/Emu/Cell/PPCDecoder.h +++ b/rpcs3/Emu/Cell/PPCDecoder.h @@ -1,6 +1,6 @@ #pragma once #include "PPCInstrTable.h" -#pragma warning( disable : 4800 4554 ) +#pragma warning( disable : 4800 ) template class InstrCaller @@ -485,21 +485,21 @@ static InstrList* connect_list(InstrList* parent, InstrL } template -static InstrList<1 << (to - from + 1), TO>* new_list(const CodeField& func, InstrCaller* error_func = nullptr) +static InstrList<1 << CodeField::size, TO>* new_list(const CodeField& func, InstrCaller* error_func = nullptr) { - return new InstrList<1 << (to - from + 1), TO>(func, error_func); + return new InstrList<1 << CodeField::size, TO>(func, error_func); } template -static InstrList<1 << (to - from + 1), TO>* new_list(InstrList* parent, int opcode, const CodeField& func, InstrCaller* error_func = nullptr) +static InstrList<1 << CodeField::size, TO>* new_list(InstrList* parent, int opcode, const CodeField& func, InstrCaller* error_func = nullptr) { - return connect_list(parent, new InstrList<1 << (to - from + 1), TO>(func, error_func), opcode); + return connect_list(parent, new InstrList<1 << CodeField::size, TO>(func, error_func), opcode); } template -static InstrList<1 << (to - from + 1), TO>* new_list(InstrList* parent, const CodeField& func, InstrCaller* error_func = nullptr) +static InstrList<1 << CodeField::size, TO>* new_list(InstrList* parent, const CodeField& func, InstrCaller* error_func = nullptr) { - return connect_list(parent, new InstrList<1 << (to - from + 1), TO>(func, error_func)); + return connect_list(parent, new InstrList<1 << CodeField::size, TO>(func, error_func)); } template diff --git a/rpcs3/Emu/Cell/PPCInstrTable.h b/rpcs3/Emu/Cell/PPCInstrTable.h index 76328a8b36..97f701b02d 100644 --- a/rpcs3/Emu/Cell/PPCInstrTable.h +++ b/rpcs3/Emu/Cell/PPCInstrTable.h @@ -1,10 +1,12 @@ #pragma once -template __forceinline static T sign(const T value) +template __forceinline static T sign(const T value) { - if(value & (1 << (size - 1))) + static_assert(size > 0 && size < sizeof(T) * 8, "Bad size"); + static const T sub_value = T(1) << size; + if(value & (T(1) << (size - 1))) { - return value - (1 << size); + return value - sub_value; } return value; @@ -52,6 +54,7 @@ public: { } + static const u32 size = to - from + 1; static const u32 shift = 31 - to; static const u32 mask = ((1ULL << ((to - from) + 1)) - 1) << shift; diff --git a/rpcs3/Emu/Cell/PPCThread.cpp b/rpcs3/Emu/Cell/PPCThread.cpp index 54e5c4ef28..4f63d6832b 100644 --- a/rpcs3/Emu/Cell/PPCThread.cpp +++ b/rpcs3/Emu/Cell/PPCThread.cpp @@ -10,8 +10,8 @@ PPCThread* GetCurrentPPCThread() PPCThread::PPCThread(PPCThreadType type) : ThreadBase(true, "PPCThread") , m_type(type) - , DisAsmFrame(NULL) - , m_dec(NULL) + , DisAsmFrame(nullptr) + , m_dec(nullptr) , stack_size(0) , stack_addr(0) , m_prio(0) @@ -28,12 +28,13 @@ PPCThread::~PPCThread() void PPCThread::Close() { - Stop(); if(DisAsmFrame) { DisAsmFrame->Close(); DisAsmFrame = nullptr; } + + Stop(); } void PPCThread::Reset() @@ -260,12 +261,11 @@ void PPCThread::Stop() wxGetApp().SendDbgCommand(DID_STOP_THREAD, this); m_status = Stopped; + ThreadBase::Stop(); Reset(); DoStop(); Emu.CheckStatus(); - ThreadBase::Stop(); - wxGetApp().SendDbgCommand(DID_STOPED_THREAD, this); } diff --git a/rpcs3/Emu/Cell/PPCThreadManager.cpp b/rpcs3/Emu/Cell/PPCThreadManager.cpp index eba18e18bb..d94375e3ed 100644 --- a/rpcs3/Emu/Cell/PPCThreadManager.cpp +++ b/rpcs3/Emu/Cell/PPCThreadManager.cpp @@ -50,10 +50,11 @@ 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(); - delete &m_threads[i]; + PPCThread* thr = &m_threads[i]; m_threads.RemoveFAt(i); + wxGetApp().SendDbgCommand(DID_REMOVE_THREAD, thr); + thr->Close(); + delete thr; i--; } diff --git a/rpcs3/Emu/Cell/PPUInstrTable.h b/rpcs3/Emu/Cell/PPUInstrTable.h index 08e110c6f4..45c2081de3 100644 --- a/rpcs3/Emu/Cell/PPUInstrTable.h +++ b/rpcs3/Emu/Cell/PPUInstrTable.h @@ -541,7 +541,9 @@ namespace PPU_instr /*0x33a*/bind_instr(g1f_list, SRADI1, RA, RS, sh, RC); /*0x33b*/bind_instr(g1f_list, SRADI2, RA, RS, sh, RC); /*0x356*/bind_instr(g1f_list, EIEIO); + /*0x387*/bind_instr(g1f_list, STVLXL, VS, RA, RB); /*0x39a*/bind_instr(g1f_list, EXTSH, RA, RS, RC); + /*0x387*/bind_instr(g1f_list, STVRXL, VS, RA, RB); /*0x3ba*/bind_instr(g1f_list, EXTSB, RA, RS, RC); /*0x3d7*/bind_instr(g1f_list, STFIWX, FRS, RA, RB); /*0x3da*/bind_instr(g1f_list, EXTSW, RA, RS, RC); diff --git a/rpcs3/Emu/Cell/PPUOpcodes.h b/rpcs3/Emu/Cell/PPUOpcodes.h index 52806e9241..d7d3a086be 100644 --- a/rpcs3/Emu/Cell/PPUOpcodes.h +++ b/rpcs3/Emu/Cell/PPUOpcodes.h @@ -739,7 +739,7 @@ public: virtual void SRADI1(u32 ra, u32 rs, u32 sh, bool rc) = 0; virtual void SRADI2(u32 ra, u32 rs, u32 sh, bool rc) = 0; virtual void EIEIO() = 0; - virtual void STVLXL(u32 sd, u32 ra, u32 rb) = 0; + virtual void STVLXL(u32 vs, u32 ra, u32 rb) = 0; virtual void EXTSH(u32 ra, u32 rs, bool rc) = 0; virtual void STVRXL(u32 sd, u32 ra, u32 rb) = 0; virtual void EXTSB(u32 ra, u32 rs, bool rc) = 0; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 54717b8fd6..618966d7c5 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -178,6 +178,7 @@ bool dump_enable = false; void PPUThread::DoCode(const s32 code) { +#ifdef _DEBUG static bool is_last_enabled = false; if(dump_enable) @@ -208,6 +209,7 @@ void PPUThread::DoCode(const s32 code) cycle = 0; TB++; } +#endif m_dec->Decode(code); } diff --git a/rpcs3/Emu/GS/GL/GLGSRender.cpp b/rpcs3/Emu/GS/GL/GLGSRender.cpp index d6adabd82e..60215814a4 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.cpp +++ b/rpcs3/Emu/GS/GL/GLGSRender.cpp @@ -3,7 +3,7 @@ #include "Emu/Cell/PPCInstrTable.h" #define CMD_DEBUG 0 -#define DUMP_VERTEX_DATA 1 +#define DUMP_VERTEX_DATA 0 #if CMD_DEBUG #define CMD_LOG ConLog.Write @@ -199,7 +199,6 @@ void GLRSXThread::Task() { u32 addr = cmd & ~(CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT); addr &= ~0x1000; - //0x30101000 + 0x80bf000 = 0x80be000 ConLog.Warning("rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", addr, p.m_ioAddress + get, cmd, get, put); re(p.m_ctrl->get, addr); continue; @@ -1337,7 +1336,7 @@ void GLGSRender::ExecCMD() m_program.SetTex(i); tex.Init(); checkForGlError("tex.Init"); - tex.Save(); + //tex.Save(); } if(m_indexed_array.m_count) diff --git a/rpcs3/Emu/SysCalls/Modules.cpp b/rpcs3/Emu/SysCalls/Modules.cpp index 9e3c98bb2f..e8a5021980 100644 --- a/rpcs3/Emu/SysCalls/Modules.cpp +++ b/rpcs3/Emu/SysCalls/Modules.cpp @@ -140,19 +140,13 @@ bool IsLoadedFunc(u32 id) return false; } -bool CallFunc(u32 id) +bool CallFunc(u32 num) { - for(u32 i=0; i= g_modules_funcs_list.GetCount()) + return false; - return true; - } - } - - return false; + (*g_modules_funcs_list[num - 1024].func)(); + return true; } bool UnloadFunc(u32 id) @@ -170,6 +164,19 @@ bool UnloadFunc(u32 id) return false; } +u32 GetFuncNumById(u32 id) +{ + for(u32 i=0; iSetLabel("Pause"); break; + + case DID_EXIT_THR_SYSCALL: + Emu.GetCPU().RemoveThread(((PPCThread*)event.GetClientData())->GetId()); + break; } UpdateUI(); - event.Skip(); } }; diff --git a/rpcs3/Gui/InterpreterDisAsm.cpp b/rpcs3/Gui/InterpreterDisAsm.cpp index d137359a70..fd61e070ac 100644 --- a/rpcs3/Gui/InterpreterDisAsm.cpp +++ b/rpcs3/Gui/InterpreterDisAsm.cpp @@ -278,9 +278,17 @@ void InterpreterDisAsmFrame::ShowAddr(const u64 addr) void InterpreterDisAsmFrame::WriteRegs() { + if(!CPU) + { + m_regs->Clear(); + return; + } + + const wxString data = CPU->RegsToString(); + m_regs->Freeze(); m_regs->Clear(); - if(CPU) m_regs->WriteText(CPU->RegsToString()); + m_regs->WriteText(data); m_regs->Thaw(); } @@ -293,9 +301,9 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) { switch(event.GetId()) { - case DID_STOP_EMU: - case DID_PAUSE_EMU: - DoUpdate(); + case DID_STOPED_EMU: + case DID_PAUSED_EMU: + //DoUpdate(); break; } } @@ -304,12 +312,16 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) switch(event.GetId()) { case DID_PAUSE_THREAD: + m_btn_run->Disable(); + m_btn_step->Disable(); + m_btn_pause->Disable(); + break; + + case DID_PAUSED_THREAD: m_btn_run->Enable(); m_btn_step->Enable(); m_btn_pause->Disable(); - - case DID_CREATE_THREAD: - DoUpdate(); + //DoUpdate(); break; case DID_START_THREAD: @@ -318,11 +330,6 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) m_btn_run->Disable(); m_btn_step->Disable(); m_btn_pause->Enable(); - - if(event.GetId() == DID_START_THREAD) - { - DoUpdate(); - } break; case DID_REMOVE_THREAD: @@ -333,15 +340,14 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) if(event.GetId() == DID_REMOVE_THREAD) { - m_choice_units->SetSelection(-1); - wxCommandEvent event; - event.SetInt(-1); + //m_choice_units->SetSelection(-1); + //wxCommandEvent event; + //event.SetInt(-1); //event.SetClientData(nullptr); - OnSelectUnit(event); + //OnSelectUnit(event); UpdateUnitList(); + //DoUpdate(); } - - DoUpdate(); break; } } @@ -353,14 +359,17 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) UpdateUnitList(); if(m_choice_units->GetSelection() == -1) { - m_choice_units->SetSelection(0); - wxCommandEvent event; - event.SetInt(0); - event.SetClientData(&Emu.GetCPU().GetThreads()[0]); - OnSelectUnit(event); - DoUpdate(); + //m_choice_units->SetSelection(0); + //wxCommandEvent event; + //event.SetInt(0); + //event.SetClientData(&Emu.GetCPU().GetThreads()[0]); + //OnSelectUnit(event); } break; + + case DID_REMOVED_THREAD: + UpdateUnitList(); + break; } } } @@ -421,6 +430,7 @@ void InterpreterDisAsmFrame::DoRun(wxCommandEvent& WXUNUSED(event)) void InterpreterDisAsmFrame::DoPause(wxCommandEvent& WXUNUSED(event)) { + //DoUpdate(); if(CPU) CPU->Pause(); } diff --git a/rpcs3/Loader/ELF64.cpp b/rpcs3/Loader/ELF64.cpp index 70f89523aa..f6ff6d3b8a 100644 --- a/rpcs3/Loader/ELF64.cpp +++ b/rpcs3/Loader/ELF64.cpp @@ -382,7 +382,7 @@ bool ELF64Loader::LoadPhdrData(u64 offset) mem32_t out_tbl(tbl + i*8); out_tbl += dst + i*section; - out_tbl += nid; + out_tbl += GetFuncNumById(nid); mem32_t out_dst(dst + i*section); out_dst += OR(11, 2, 2, 0); diff --git a/rpcs3/rpcs3.h b/rpcs3/rpcs3.h index 533118cb92..6d1ff5911a 100644 --- a/rpcs3/rpcs3.h +++ b/rpcs3/rpcs3.h @@ -47,6 +47,7 @@ enum DbgCommand DID_EXEC_THREAD, DID_REGISTRED_CALLBACK, DID_UNREGISTRED_CALLBACK, + DID_EXIT_THR_SYSCALL, DID_LAST_COMMAND, }; From 234e174b7d28c133fc089fe2d73d255a2ec4fec6 Mon Sep 17 00:00:00 2001 From: DH Date: Mon, 19 Aug 2013 02:06:11 +0300 Subject: [PATCH 09/13] - Implemented send open system menu cmd. - Added cellSysutil module. - Improved OpenGL renderer. - Added cube & hello world homebrews. - Implemented more GCM syscalls. --- Utilities/Thread.h | 2 +- bin/dev_hdd0/game/TEST12345/USRDIR/cube.elf | Bin 0 -> 356532 bytes .../game/TEST12345/USRDIR/hello_world.elf | Bin 0 -> 343036 bytes rpcs3/Emu/Cell/PPCThread.cpp | 15 +- rpcs3/Emu/Cell/PPCThread.h | 1 + rpcs3/Emu/Cell/PPCThreadManager.cpp | 19 +- rpcs3/Emu/Cell/PPCThreadManager.h | 4 + rpcs3/Emu/Cell/PPUThread.cpp | 9 +- rpcs3/Emu/GS/GL/FragmentProgram.cpp | 26 +- rpcs3/Emu/GS/GL/FragmentProgram.h | 1 + rpcs3/Emu/GS/GL/GLGSRender.cpp | 220 +++++++++++++--- rpcs3/Emu/GS/GL/GLGSRender.h | 10 +- rpcs3/Emu/GS/GSManager.h | 7 - rpcs3/Emu/GS/GSRender.h | 4 + rpcs3/Emu/GS/RSXThread.h | 82 ++++++ rpcs3/Emu/SysCalls/Callback.cpp | 1 - rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp | 74 ++++++ rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp | 240 ++++++++++++++++++ rpcs3/Emu/SysCalls/SysCalls.h | 2 + rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp | 22 +- rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp | 3 +- rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp | 2 +- rpcs3/Emu/System.cpp | 3 +- rpcs3/Gui/InterpreterDisAsm.cpp | 3 + rpcs3/Gui/MainFrame.cpp | 31 ++- rpcs3/Gui/MainFrame.h | 2 + rpcs3/rpcs3.vcxproj | 1 + rpcs3/rpcs3.vcxproj.filters | 3 + 28 files changed, 702 insertions(+), 85 deletions(-) create mode 100644 bin/dev_hdd0/game/TEST12345/USRDIR/cube.elf create mode 100644 bin/dev_hdd0/game/TEST12345/USRDIR/hello_world.elf create mode 100644 rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp diff --git a/Utilities/Thread.h b/Utilities/Thread.h index e2c2a7e3a1..f98c22107b 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -63,7 +63,7 @@ public: m_parent = nullptr; - //if(wait) + if(wait) { Delete(); //wxCriticalSectionLocker lock(m_wait_for_exit); diff --git a/bin/dev_hdd0/game/TEST12345/USRDIR/cube.elf b/bin/dev_hdd0/game/TEST12345/USRDIR/cube.elf new file mode 100644 index 0000000000000000000000000000000000000000..020b9b4ec2076f2eb28d207db5e33e9a06d37d0b GIT binary patch literal 356532 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%0D;SY~5uxca%rXEIvq+o0a4HAK4h!BVb34v%3 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 literal 0 HcmV?d00001 diff --git a/bin/dev_hdd0/game/TEST12345/USRDIR/hello_world.elf b/bin/dev_hdd0/game/TEST12345/USRDIR/hello_world.elf new file mode 100644 index 0000000000000000000000000000000000000000..ecc3ccb41a9328728b946670dafa4899adbee6b0 GIT binary patch literal 343036 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 literal 0 HcmV?d00001 diff --git a/rpcs3/Emu/Cell/PPCThread.cpp b/rpcs3/Emu/Cell/PPCThread.cpp index 4f63d6832b..45ba83d0ec 100644 --- a/rpcs3/Emu/Cell/PPCThread.cpp +++ b/rpcs3/Emu/Cell/PPCThread.cpp @@ -18,6 +18,7 @@ PPCThread::PPCThread(PPCThreadType type) , m_offset(0) , m_sync_wait(false) , m_wait_thread_id(-1) + , m_free_data(false) { } @@ -28,6 +29,11 @@ PPCThread::~PPCThread() void PPCThread::Close() { + if(IsAlive()) + { + m_free_data = true; + } + if(DisAsmFrame) { DisAsmFrame->Close(); @@ -261,7 +267,7 @@ void PPCThread::Stop() wxGetApp().SendDbgCommand(DID_STOP_THREAD, this); m_status = Stopped; - ThreadBase::Stop(); + ThreadBase::Stop(false); Reset(); DoStop(); Emu.CheckStatus(); @@ -284,7 +290,7 @@ void PPCThread::ExecOnce() void PPCThread::Task() { - ConLog.Write("%s enter", PPCThread::GetFName()); + //ConLog.Write("%s enter", PPCThread::GetFName()); const Array& bp = Emu.GetBreakPoints(); @@ -336,5 +342,8 @@ void PPCThread::Task() ConLog.Error("Exception: %s", e); } - ConLog.Write("%s leave", PPCThread::GetFName()); + //ConLog.Write("%s leave", PPCThread::GetFName()); + + if(m_free_data) + free(this); } diff --git a/rpcs3/Emu/Cell/PPCThread.h b/rpcs3/Emu/Cell/PPCThread.h index c16bb90c18..6fd153bbce 100644 --- a/rpcs3/Emu/Cell/PPCThread.h +++ b/rpcs3/Emu/Cell/PPCThread.h @@ -35,6 +35,7 @@ protected: Array argv_addr; u64 m_offset; u32 m_exit_status; + bool m_free_data; public: u64 stack_size; diff --git a/rpcs3/Emu/Cell/PPCThreadManager.cpp b/rpcs3/Emu/Cell/PPCThreadManager.cpp index d94375e3ed..37580c518e 100644 --- a/rpcs3/Emu/Cell/PPCThreadManager.cpp +++ b/rpcs3/Emu/Cell/PPCThreadManager.cpp @@ -20,6 +20,8 @@ void PPCThreadManager::Close() PPCThread& PPCThreadManager::AddThread(PPCThreadType type) { + std::lock_guard lock(m_mtx_thread); + PPCThread* new_thread; char* name; switch(type) @@ -40,6 +42,8 @@ PPCThread& PPCThreadManager::AddThread(PPCThreadType type) void PPCThreadManager::RemoveThread(const u32 id) { + std::lock_guard lock(m_mtx_thread); + for(u32 i=0; iClose(); - delete thr; + if(thr->IsAlive()) + { + thr->Close(); + } + else + { + thr->Close(); + delete thr; + } + + + m_threads.RemoveFAt(i); i--; } diff --git a/rpcs3/Emu/Cell/PPCThreadManager.h b/rpcs3/Emu/Cell/PPCThreadManager.h index 8775ab9b54..593e1fdba8 100644 --- a/rpcs3/Emu/Cell/PPCThreadManager.h +++ b/rpcs3/Emu/Cell/PPCThreadManager.h @@ -7,6 +7,9 @@ class PPCThreadManager //ArrayF m_ppu_threads; //ArrayF m_spu_threads; ArrayF m_threads; + std::mutex m_mtx_thread; + wxSemaphore m_sem_task; + Stack m_delete_threads; public: PPCThreadManager(); @@ -23,4 +26,5 @@ public: //IdManager& GetIDs() {return m_threads_id;} void Exec(); + void Task(); }; \ No newline at end of file diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 618966d7c5..548d8ca9ba 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -63,8 +63,8 @@ void PPUThread::InitRegs() const u32 pc = Memory.Read32(entry); const u32 rtoc = Memory.Read32(entry + 4); - ConLog.Write("entry = 0x%x", entry); - ConLog.Write("rtoc = 0x%x", rtoc); + //ConLog.Write("entry = 0x%x", entry); + //ConLog.Write("rtoc = 0x%x", rtoc); SetPc(pc); @@ -132,9 +132,11 @@ void PPUThread::InitRegs() GPR[29] = GPR[3]; GPR[31] = GPR[5]; + LR = Emu.GetPPUThreadExit(); CTR = PC; CR.CR = 0x22000082; VSCR.NJ = 1; + TB = 0; } u64 PPUThread::GetFreeStackSize() const @@ -202,14 +204,13 @@ void PPUThread::DoCode(const s32 code) { is_last_enabled = false; } - +#endif if(++cycle > 220) { cycle = 0; TB++; } -#endif m_dec->Decode(code); } diff --git a/rpcs3/Emu/GS/GL/FragmentProgram.cpp b/rpcs3/Emu/GS/GL/FragmentProgram.cpp index bd30fb2a45..712c7a435d 100644 --- a/rpcs3/Emu/GS/GL/FragmentProgram.cpp +++ b/rpcs3/Emu/GS/GL/FragmentProgram.cpp @@ -37,9 +37,17 @@ void FragmentDecompilerThread::AddCode(wxString code) if(cond.Len()) { - ConLog.Error("cond! [eq: %d gr: %d lt: %d] (%s)", src0.exec_if_eq, src0.exec_if_gr, src0.exec_if_lt, cond); - Emu.Pause(); - return; + + static const char f[4] = {'x', 'y', 'z', 'w'}; + wxString swizzle = wxEmptyString; + swizzle += f[src0.cond_swizzle_x]; + swizzle += f[src0.cond_swizzle_y]; + swizzle += f[src0.cond_swizzle_z]; + swizzle += f[src0.cond_swizzle_w]; + cond = wxString::Format("if(rc.%s %s 0.0) ", swizzle, cond); + //ConLog.Error("cond! [eq: %d gr: %d lt: %d] (%s)", src0.exec_if_eq, src0.exec_if_gr, src0.exec_if_lt, cond); + //Emu.Pause(); + //return; } if(src1.scale) @@ -60,13 +68,12 @@ void FragmentDecompilerThread::AddCode(wxString code) } } - if(dst.fp16) + if(dst.saturate) { - //HACK! TODO: fp16 -> fp32 - code = "/*" + code + "*/ vec4(1.0, 1.0, 1.0, 1.0)"; + code = "clamp(" + code + ", 0.0, 1.0)"; } - code = AddReg(dst.dest_reg, dst.fp16) + GetMask() + " = " + code + GetMask(); + code = cond + (dst.set_cond ? AddCond(dst.fp16) : AddReg(dst.dest_reg, dst.fp16)) + GetMask() + " = " + code + GetMask(); main += "\t" + code + ";\n"; } @@ -96,6 +103,11 @@ wxString FragmentDecompilerThread::AddReg(u32 index, int fp16) wxString::Format((fp16 ? "h%d" : "r%d"), index), (index || fp16) ? -1 : 0); } +wxString FragmentDecompilerThread::AddCond(int fp16) +{ + return m_parr.AddParam(PARAM_NONE , "vec4", (fp16 ? "hc" : "rc"), -1); +} + wxString FragmentDecompilerThread::AddConst() { mem32_t data(m_addr + m_size + m_offset); diff --git a/rpcs3/Emu/GS/GL/FragmentProgram.h b/rpcs3/Emu/GS/GL/FragmentProgram.h index 54a0b6b784..bcf6885b5d 100644 --- a/rpcs3/Emu/GS/GL/FragmentProgram.h +++ b/rpcs3/Emu/GS/GL/FragmentProgram.h @@ -117,6 +117,7 @@ struct FragmentDecompilerThread : public ThreadBase void AddCode(wxString code); wxString AddReg(u32 index, int fp16); + wxString AddCond(int fp16); wxString AddConst(); wxString AddTex(); diff --git a/rpcs3/Emu/GS/GL/GLGSRender.cpp b/rpcs3/Emu/GS/GL/GLGSRender.cpp index 60215814a4..e56ca4320d 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.cpp +++ b/rpcs3/Emu/GS/GL/GLGSRender.cpp @@ -11,7 +11,7 @@ #define CMD_LOG(...) #endif -gcmBuffer gcmBuffers[2]; +gcmBuffer gcmBuffers[8]; void printGlError(GLenum err, const char* situation) { @@ -176,14 +176,16 @@ void GLRSXThread::Task() if(draw) { p.m_frame->Flip(); - if(p.m_flip_handler) - { - p.m_flip_handler.Handle(1, 0, 0); - p.m_flip_handler.Branch(false); - } } + p.m_gcm_current_buffer = ++p.m_gcm_current_buffer % p.m_gcm_buffers_count; p.m_flip_status = 0; + if(p.m_flip_handler) + { + p.m_flip_handler.Handle(1, 0, 0); + p.m_flip_handler.Branch(false); + } + if(SemaphorePostAndWait(p.m_sem_flip)) continue; } @@ -198,7 +200,7 @@ void GLRSXThread::Task() if(cmd & CELL_GCM_METHOD_FLAG_JUMP) { u32 addr = cmd & ~(CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT); - addr &= ~0x1000; + addr -= 0x1000; ConLog.Warning("rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", addr, p.m_ioAddress + get, cmd, get, put); re(p.m_ctrl->get, addr); continue; @@ -621,17 +623,14 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case NV4097_SET_ALPHA_TEST_ENABLE: m_set_alpha_test = args[0] ? true : false; - //Enable(args[0] ? true : false, GL_ALPHA_TEST); break; case NV4097_SET_BLEND_ENABLE: m_set_blend = args[0] ? true : false; - //Enable(args[0] ? true : false, GL_BLEND); break; case NV4097_SET_DEPTH_BOUNDS_TEST_ENABLE: m_set_depth_bounds_test = args[0] ? true : false; - //Enable(args[0] ? true : false, GL_DEPTH_CLAMP); break; case NV4097_SET_ALPHA_FUNC: @@ -680,47 +679,51 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c m_clip_max = (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: m_set_depth_func = true; m_depth_func = args[0]; - //glDepthFunc(m_depth_func); break; case NV4097_SET_DEPTH_TEST_ENABLE: m_depth_test_enable = args[0] ? true : false; - //Enable(args[0] ? true : false, GL_DEPTH_TEST); break; case NV4097_SET_FRONT_POLYGON_MODE: - glPolygonMode(GL_FRONT, args[0]); + m_set_front_polygon_mode = true; + m_front_polygon_mode = args[0]; + //glPolygonMode(GL_FRONT, args[0]); break; case NV4097_CLEAR_SURFACE: { - const u32 mask = args[0]; - GLbitfield f = 0; - if (mask & 0x1) f |= GL_DEPTH_BUFFER_BIT; - if (mask & 0x2) f |= GL_STENCIL_BUFFER_BIT; - if (mask & 0x10) f |= GL_COLOR_BUFFER_BIT; - glClear(f); + m_set_clear_surface = true; + m_clear_surface_mask = args[0]; } break; case NV4097_SET_BLEND_FUNC_SFACTOR: { - const u16 src_rgb = args[0] & 0xffff; - const u16 dst_rgb = args[0] >> 16; - const u16 src_alpha = args[1] & 0xffff; - const u16 dst_alpha = args[1] >> 16; - CMD_LOG("src_rgb=0x%x, dst_rgb=0x%x, src_alpha=0x%x, dst_alpha=0x%x", - src_rgb, dst_rgb, src_alpha, dst_alpha); + m_set_blend_sfactor = true; + m_blend_sfactor_rgb = args[0] & 0xffff; + m_blend_sfactor_alpha = args[0] >> 16; - glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); + if(count >= 2) + { + m_set_blend_dfactor = true; + m_blend_dfactor_rgb = args[1] & 0xffff; + m_blend_dfactor_alpha = args[1] >> 16; + } + } + break; + + case NV4097_SET_BLEND_FUNC_DFACTOR: + { + m_set_blend_dfactor = true; + m_blend_dfactor_rgb = args[0] & 0xffff; + m_blend_dfactor_alpha = args[0] >> 16; } break; @@ -822,12 +825,20 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c if(args[0]) { //begin + if(Emu.GetCallbackManager().m_exit_callback.m_callbacks.GetCount()) + { + //Emu.GetCallbackManager().m_exit_callback.Handle(0x0121, 0); + } m_draw_mode = args[0] - 1; } else { //end ExecCMD(); + if(Emu.GetCallbackManager().m_exit_callback.m_callbacks.GetCount()) + { + //Emu.GetCallbackManager().m_exit_callback.Handle(0x0122, 0); + } } } break; @@ -941,19 +952,66 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_LOGIC_OP_ENABLE: - Enable(args[0] ? true : false, GL_LOGIC_OP); + m_set_logic_op = args[0] ? true : false; break; case NV4097_SET_CULL_FACE_ENABLE: - Enable(args[0] ? true : false, GL_CULL_FACE); + m_set_cull_face = args[0] ? true : false; break; case NV4097_SET_DITHER_ENABLE: - Enable(args[0] ? true : false, GL_DITHER); + m_set_dither = args[0] ? true : false; break; case NV4097_SET_STENCIL_TEST_ENABLE: - Enable(args[0] ? true : false, GL_STENCIL_TEST); + m_set_stencil_test = args[0] ? true : false; + break; + + case NV4097_SET_STENCIL_MASK: + m_set_stencil_mask = true; + m_stencil_mask = args[0]; + break; + + case NV4097_SET_STENCIL_FUNC: + m_set_stencil_func = true; + m_stencil_func = args[0]; + if(count >= 2) + { + m_set_stencil_func_ref = true; + m_stencil_func_ref = args[1]; + + if(count >= 3) + { + m_set_stencil_func_mask = true; + m_stencil_func_mask = args[2]; + } + } + break; + + case NV4097_SET_STENCIL_FUNC_REF: + m_set_stencil_func_ref = true; + m_stencil_func_ref = args[0]; + break; + + case NV4097_SET_STENCIL_FUNC_MASK: + m_set_stencil_func_mask = true; + m_stencil_func_mask = args[0]; + break; + + case NV4097_SET_STENCIL_OP_FAIL: + m_set_stencil_fail = true; + m_stencil_fail = args[0]; + if(count >= 2) + { + m_set_stencil_zfail = true; + m_stencil_zfail = args[1]; + + if(count >= 3) + { + m_set_stencil_zpass = true; + m_stencil_zpass = args[2]; + } + } break; case NV4097_SET_TWO_SIDED_STENCIL_TEST_ENABLE: @@ -984,8 +1042,11 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_BLEND_COLOR: - glBlendColor(args[0] & 0xff, (args[0] >> 8) & 0xff, - (args[0] >> 16) & 0xff, (args[0] >> 24) & 0xff); + m_set_blend_color = true; + m_blend_color_r = args[0] & 0xff; + m_blend_color_g = (args[0] >> 8) & 0xff; + m_blend_color_b = (args[0] >> 16) & 0xff; + m_blend_color_a = (args[0] >> 24) & 0xff; break; case NV4097_SET_BLEND_COLOR2: @@ -993,8 +1054,9 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_BLEND_EQUATION: - glBlendEquationSeparate(args[0] & 0xffff, args[0] >> 16); - //glBlendEquation + m_set_blend_equation = true; + m_blend_equation_rgb = args[0] & 0xffff; + m_blend_equation_alpha = args[0] >> 16; break; case NV4097_SET_REDUCE_DST_COLOR: @@ -1002,7 +1064,8 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_DEPTH_MASK: - glDepthMask(args[0]); + m_set_depth_mask = true; + m_depth_mask = args[0]; break; case NV4097_SET_SCISSOR_VERTICAL: @@ -1025,10 +1088,6 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c 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; @@ -1148,6 +1207,20 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } break; + case NV4097_SET_LINE_SMOOTH_ENABLE: + m_set_line_smooth = args[0] ? true : false; + break; + + case NV4097_SET_LINE_WIDTH: + m_set_line_width = true; + m_line_width = args[0]; + break; + + case NV4097_SET_SHADE_MODE: + m_set_shade_mode = true; + m_shade_mode = args[0]; + break; + case NV4097_SET_ZSTENCIL_CLEAR_VALUE: case NV4097_SET_ZCULL_CONTROL0: case NV4097_SET_ZCULL_CONTROL1: @@ -1165,6 +1238,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c { case 1: data = std::chrono::steady_clock::now().time_since_epoch().count(); + data *= 1000000; break; default: @@ -1285,6 +1359,15 @@ void GLGSRender::ExecCMD() { if(LoadProgram()) { + if(m_set_clear_surface) + { + GLbitfield f = 0; + if (m_clear_surface_mask & 0x1) f |= GL_DEPTH_BUFFER_BIT; + if (m_clear_surface_mask & 0x2) f |= GL_STENCIL_BUFFER_BIT; + if (m_clear_surface_mask & 0x10) f |= GL_COLOR_BUFFER_BIT; + glClear(f); + } + if(m_set_color_mask) { glColorMask(m_color_mask_r, m_color_mask_g, m_color_mask_b, m_color_mask_a); @@ -1303,10 +1386,45 @@ void GLGSRender::ExecCMD() glScissor(m_scissor_x, m_scissor_y, m_scissor_w, m_scissor_h); } + if(m_set_front_polygon_mode) + { + glPolygonMode(GL_FRONT, m_front_polygon_mode); + } + 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); + Enable(m_set_logic_op, GL_LOGIC_OP); + Enable(m_set_cull_face, GL_CULL_FACE); + Enable(m_set_dither, GL_DITHER); + Enable(m_set_stencil_test, GL_STENCIL_TEST); + Enable(m_set_line_smooth, GL_LINE_SMOOTH); + + if(m_set_stencil_mask) + { + glStencilMask(m_stencil_mask); + } + + if(m_set_stencil_func && m_set_stencil_func_ref && m_set_stencil_func_mask) + { + glStencilFunc(m_stencil_func, m_stencil_func_ref, m_stencil_func_mask); + } + + if(m_set_stencil_fail && m_set_stencil_zfail && m_set_stencil_zpass) + { + glStencilOp(m_stencil_fail, m_stencil_zfail, m_stencil_zpass); + } + + if(m_set_shade_mode) + { + glShadeModel(m_shade_mode); + } + + if(m_set_depth_mask) + { + glDepthMask(m_depth_mask); + } if(m_set_depth_func) { @@ -1318,6 +1436,26 @@ void GLGSRender::ExecCMD() glDepthRangef(m_clip_min, m_clip_max); } + if(m_set_line_width) + { + glLineWidth(m_line_width / 255.f); + } + + if(m_set_blend_equation) + { + glBlendEquationSeparate(m_blend_equation_rgb, m_blend_equation_alpha); + } + + if(m_set_blend_sfactor && m_set_blend_dfactor) + { + glBlendFuncSeparate(m_blend_sfactor_rgb, m_blend_dfactor_rgb, m_blend_sfactor_alpha, m_blend_dfactor_alpha); + } + + if(m_set_blend_color) + { + glBlendColor(m_blend_color_r, m_blend_color_g, m_blend_color_b, m_blend_color_a); + } + if(m_indexed_array.m_count && m_draw_array_count) { ConLog.Warning("m_indexed_array.m_count && draw_array_count"); diff --git a/rpcs3/Emu/GS/GL/GLGSRender.h b/rpcs3/Emu/GS/GL/GLGSRender.h index 1736ea74f2..dad0efcb84 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.h +++ b/rpcs3/Emu/GS/GL/GLGSRender.h @@ -113,9 +113,17 @@ public: checkForGlError("GLTexture::Init() -> glTexImage2D"); break; + case 0x94://FIXME + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, m_width, m_height, 0, GL_RED, GL_SHORT, Memory.GetMemFromAddr(m_offset)); + checkForGlError("GLTexture::Init() -> glTexImage2D"); + break; + default: ConLog.Error("Init tex error: Bad tex format (0x%x)", m_format); break; } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //Unbind(); } @@ -269,8 +277,6 @@ private: virtual void OnSize(wxSizeEvent& event); }; -extern gcmBuffer gcmBuffers[2]; - struct GLRSXThread : public ThreadBase { wxWindow* m_parent; diff --git a/rpcs3/Emu/GS/GSManager.h b/rpcs3/Emu/GS/GSManager.h index bac7ce4233..7ab4fda396 100644 --- a/rpcs3/Emu/GS/GSManager.h +++ b/rpcs3/Emu/GS/GSManager.h @@ -38,15 +38,8 @@ struct gcmBuffer u32 pitch; u32 width; u32 height; - bool update; - - gcmBuffer() : update(false) - { - } }; -extern gcmBuffer gcmBuffers[2]; - class GSManager { GSInfo m_info; diff --git a/rpcs3/Emu/GS/GSRender.h b/rpcs3/Emu/GS/GSRender.h index aa08b9cdb7..d32220163f 100644 --- a/rpcs3/Emu/GS/GSRender.h +++ b/rpcs3/Emu/GS/GSRender.h @@ -51,6 +51,10 @@ struct GSRender volatile bool m_draw; Callback m_flip_handler; + u32 m_gcm_buffers_addr; + u32 m_gcm_buffers_count; + u32 m_gcm_current_buffer; + GSRender(); virtual void Init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddress, const u32 localAddress)=0; diff --git a/rpcs3/Emu/GS/RSXThread.h b/rpcs3/Emu/GS/RSXThread.h index 9cf1e9c458..4b877f5990 100644 --- a/rpcs3/Emu/GS/RSXThread.h +++ b/rpcs3/Emu/GS/RSXThread.h @@ -36,6 +36,67 @@ protected: u16 m_scissor_w; u16 m_scissor_h; + bool m_set_front_polygon_mode; + u32 m_front_polygon_mode; + + bool m_set_clear_surface; + u32 m_clear_surface_mask; + + bool m_set_blend_sfactor; + u16 m_blend_sfactor_rgb; + u16 m_blend_sfactor_alpha; + + bool m_set_blend_dfactor; + u16 m_blend_dfactor_rgb; + u16 m_blend_dfactor_alpha; + + bool m_set_logic_op; + bool m_set_cull_face; + bool m_set_dither; + bool m_set_stencil_test; + + bool m_set_stencil_mask; + u32 m_stencil_mask; + + bool m_set_stencil_func; + u32 m_stencil_func; + + bool m_set_stencil_func_ref; + u32 m_stencil_func_ref; + + bool m_set_stencil_func_mask; + u32 m_stencil_func_mask; + + bool m_set_stencil_fail; + u32 m_stencil_fail; + + bool m_set_stencil_zfail; + u32 m_stencil_zfail; + + bool m_set_stencil_zpass; + u32 m_stencil_zpass; + + bool m_set_blend_equation; + u16 m_blend_equation_rgb; + u16 m_blend_equation_alpha; + + bool m_set_depth_mask; + u32 m_depth_mask; + + bool m_set_line_smooth; + + bool m_set_line_width; + u32 m_line_width; + + bool m_set_shade_mode; + u32 m_shade_mode; + + bool m_set_blend_color; + u8 m_blend_color_r; + u8 m_blend_color_g; + u8 m_blend_color_b; + u8 m_blend_color_a; + public: ExecRSXCMDdata() { @@ -55,6 +116,27 @@ public: m_set_viewport_vertical = false; m_set_scissor_horizontal = false; m_set_scissor_vertical = false; + m_set_front_polygon_mode = false; + m_set_clear_surface = false; + m_set_blend_sfactor = false; + m_set_blend_dfactor = false; + m_set_logic_op = false; + m_set_cull_face = false; + m_set_dither = false; + m_set_stencil_test = false; + m_set_stencil_mask = false; + m_set_stencil_func = false; + m_set_stencil_func_ref = false; + m_set_stencil_func_mask = false; + m_set_stencil_fail = false; + m_set_stencil_zfail = false; + m_set_stencil_zpass = false; + m_set_blend_equation = false; + m_set_depth_mask = false; + m_set_line_smooth = false; + m_set_line_width = false; + m_set_shade_mode = false; + m_set_blend_color = false; } virtual void ExecCMD()=0; diff --git a/rpcs3/Emu/SysCalls/Callback.cpp b/rpcs3/Emu/SysCalls/Callback.cpp index 62c2857016..def71a0e83 100644 --- a/rpcs3/Emu/SysCalls/Callback.cpp +++ b/rpcs3/Emu/SysCalls/Callback.cpp @@ -60,7 +60,6 @@ void Callback::Branch(bool wait) new_thread.SetArg(0, a1); new_thread.SetArg(1, a2); new_thread.SetArg(2, a3); - ((PPUThread&)new_thread).LR = Emu.GetPPUThreadExit(); new_thread.Run(); new_thread.Exec(); diff --git a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp index c497e25c6c..6ee40bfdd6 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" +#include "Emu/GS/GCM.h" void cellGcmSys_init(); Module cellGcmSys(0x0010, cellGcmSys_init); @@ -17,6 +18,68 @@ s64 cellGcmSetFlipCommandWithWaitLabel() return 0; } +int cellGcmInitCursor() +{ + UNIMPLEMENTED_FUNC(cellGcmSys); + return CELL_OK; +} + +int cellGcmSetCursorPosition(s32 x, s32 y) +{ + UNIMPLEMENTED_FUNC(cellGcmSys); + return CELL_OK; +} + +int cellGcmSetCursorDisable() +{ + UNIMPLEMENTED_FUNC(cellGcmSys); + return CELL_OK; +} + +int cellGcmSetVBlankHandler() +{ + UNIMPLEMENTED_FUNC(cellGcmSys); + return CELL_OK; +} + +int cellGcmUpdateCursor() +{ + UNIMPLEMENTED_FUNC(cellGcmSys); + return CELL_OK; +} + +int cellGcmSetCursorEnable() +{ + UNIMPLEMENTED_FUNC(cellGcmSys); + return CELL_OK; +} + +int cellGcmSetCursorImageOffset(u32 offset) +{ + UNIMPLEMENTED_FUNC(cellGcmSys); + return CELL_OK; +} + +u32 cellGcmGetDisplayInfo() +{ + cellGcmSys.Warning("cellGcmGetDisplayInfo() = 0x%x", Emu.GetGSManager().GetRender().m_gcm_buffers_addr); + return Emu.GetGSManager().GetRender().m_gcm_buffers_addr; +} + +int cellGcmGetCurrentDisplayBufferId(u32 id_addr) +{ + cellGcmSys.Warning("cellGcmGetCurrentDisplayBufferId(id_addr=0x%x)", id_addr); + + if(!Memory.IsGoodAddr(id_addr)) + { + return CELL_EFAULT; + } + + Memory.Write32(id_addr, Emu.GetGSManager().GetRender().m_gcm_current_buffer); + + return CELL_OK; +} + void cellGcmSys_init() { cellGcmSys.AddFunc(0x055bd74d, cellGcmGetTiledPitchSize); @@ -39,4 +102,15 @@ void cellGcmSys_init() cellGcmSys.AddFunc(0xd9b7653e, cellGcmUnbindTile); cellGcmSys.AddFunc(0xa75640e8, cellGcmUnbindZcull); cellGcmSys.AddFunc(0xa41ef7e8, cellGcmSetFlipHandler); + cellGcmSys.AddFunc(0xa114ec67, cellGcmMapMainMemory); + cellGcmSys.AddFunc(0xf80196c1, cellGcmGetLabelAddress); + cellGcmSys.AddFunc(0x107bf3a1, cellGcmInitCursor); + cellGcmSys.AddFunc(0x1a0de550, cellGcmSetCursorPosition); + cellGcmSys.AddFunc(0x69c6cc82, cellGcmSetCursorDisable); + cellGcmSys.AddFunc(0xa91b0402, cellGcmSetVBlankHandler); + cellGcmSys.AddFunc(0xbd2fa0a7, cellGcmUpdateCursor); + cellGcmSys.AddFunc(0xc47d0812, cellGcmSetCursorEnable); + cellGcmSys.AddFunc(0xf9bfdc72, cellGcmSetCursorImageOffset); + cellGcmSys.AddFunc(0x0e6b0dae, cellGcmGetDisplayInfo); + cellGcmSys.AddFunc(0x93806525, cellGcmGetCurrentDisplayBufferId); } diff --git a/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp b/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp new file mode 100644 index 0000000000..1602f4fc52 --- /dev/null +++ b/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp @@ -0,0 +1,240 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" +#include "Emu/SysCalls/SC_FUNC.h" + +enum +{ + CELL_SYSUTIL_SYSTEMPARAM_ID_LANG = 0x0111, + CELL_SYSUTIL_SYSTEMPARAM_ID_ENTER_BUTTON_ASSIGN = 0x0112, + CELL_SYSUTIL_SYSTEMPARAM_ID_DATE_FORMAT = 0x0114, + CELL_SYSUTIL_SYSTEMPARAM_ID_TIME_FORMAT = 0x0115, + CELL_SYSUTIL_SYSTEMPARAM_ID_TIMEZONE = 0x0116, + CELL_SYSUTIL_SYSTEMPARAM_ID_SUMMERTIME = 0x0117, + CELL_SYSUTIL_SYSTEMPARAM_ID_GAME_PARENTAL_LEVEL = 0x0121, + CELL_SYSUTIL_SYSTEMPARAM_ID_GAME_PARENTAL_LEVEL0_RESTRICT = 0x0123, + CELL_SYSUTIL_SYSTEMPARAM_ID_CURRENT_USER_HAS_NP_ACCOUNT = 0x0141, + CELL_SYSUTIL_SYSTEMPARAM_ID_CAMERA_PLFREQ = 0x0151, + CELL_SYSUTIL_SYSTEMPARAM_ID_PAD_RUMBLE = 0x0152, + CELL_SYSUTIL_SYSTEMPARAM_ID_KEYBOARD_TYPE = 0x0153, + CELL_SYSUTIL_SYSTEMPARAM_ID_JAPANESE_KEYBOARD_ENTRY_METHOD = 0x0154, + CELL_SYSUTIL_SYSTEMPARAM_ID_CHINESE_KEYBOARD_ENTRY_METHOD = 0x0155, + CELL_SYSUTIL_SYSTEMPARAM_ID_PAD_AUTOOFF = 0x0156, +}; + +enum +{ + CELL_SYSUTIL_LANG_JAPANESE = 0, + CELL_SYSUTIL_LANG_ENGLISH_US = 1, + CELL_SYSUTIL_LANG_FRENCH = 2, + CELL_SYSUTIL_LANG_SPANISH = 3, + CELL_SYSUTIL_LANG_GERMAN = 4, + CELL_SYSUTIL_LANG_ITALIAN = 5, + CELL_SYSUTIL_LANG_DUTCH = 6, + CELL_SYSUTIL_LANG_PORTUGUESE_PT = 7, + CELL_SYSUTIL_LANG_RUSSIAN = 8, + CELL_SYSUTIL_LANG_KOREAN = 9, + CELL_SYSUTIL_LANG_CHINESE_T = 10, + CELL_SYSUTIL_LANG_CHINESE_S = 11, + CELL_SYSUTIL_LANG_FINNISH = 12, + CELL_SYSUTIL_LANG_SWEDISH = 13, + CELL_SYSUTIL_LANG_DANISH = 14, + CELL_SYSUTIL_LANG_NORWEGIAN = 15, + CELL_SYSUTIL_LANG_POLISH = 16, + CELL_SYSUTIL_LANG_PORTUGUESE_BR = 17, + CELL_SYSUTIL_LANG_ENGLISH_GB = 18, +}; + +enum +{ + CELL_SYSUTIL_ENTER_BUTTON_ASSIGN_CIRCLE = 0, + CELL_SYSUTIL_ENTER_BUTTON_ASSIGN_CROSS = 1, +}; + +enum +{ + CELL_SYSUTIL_DATE_FMT_YYYYMMDD = 0, + CELL_SYSUTIL_DATE_FMT_DDMMYYYY = 1, + CELL_SYSUTIL_DATE_FMT_MMDDYYYY = 2, +}; + +enum +{ + CELL_SYSUTIL_TIME_FMT_CLOCK12 = 0, + CELL_SYSUTIL_TIME_FMT_CLOCK24 = 1, +}; + +enum +{ + CELL_SYSUTIL_GAME_PARENTAL_OFF = 0, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL01 = 1, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL02 = 2, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL03 = 3, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL04 = 4, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL05 = 5, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL06 = 6, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL07 = 7, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL08 = 8, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL09 = 9, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL10 = 10, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL11 = 11, +}; + +enum +{ + CELL_SYSUTIL_GAME_PARENTAL_LEVEL0_RESTRICT_OFF = 0, + CELL_SYSUTIL_GAME_PARENTAL_LEVEL0_RESTRICT_ON = 1, +}; + +enum +{ + CELL_SYSUTIL_CAMERA_PLFREQ_DISABLED = 0, + CELL_SYSUTIL_CAMERA_PLFREQ_50HZ = 1, + CELL_SYSUTIL_CAMERA_PLFREQ_60HZ = 2, + CELL_SYSUTIL_CAMERA_PLFREQ_DEVCIE_DEPEND = 4, +}; + +enum +{ + CELL_SYSUTIL_PAD_RUMBLE_OFF = 0, + CELL_SYSUTIL_PAD_RUMBLE_ON = 1, +}; + +enum +{ + CELL_KB_MAPPING_101, + CELL_KB_MAPPING_106, + CELL_KB_MAPPING_106_KANA, + CELL_KB_MAPPING_GERMAN_GERMANY, + CELL_KB_MAPPING_SPANISH_SPAIN, + CELL_KB_MAPPING_FRENCH_FRANCE, + CELL_KB_MAPPING_ITALIAN_ITALY, + CELL_KB_MAPPING_DUTCH_NETHERLANDS, + CELL_KB_MAPPING_PORTUGUESE_PORTUGAL, + CELL_KB_MAPPING_RUSSIAN_RUSSIA, + CELL_KB_MAPPING_ENGLISH_UK, + CELL_KB_MAPPING_KOREAN_KOREA, + CELL_KB_MAPPING_NORWEGIAN_NORWAY, + CELL_KB_MAPPING_FINNISH_FINLAND, + CELL_KB_MAPPING_DANISH_DENMARK, + CELL_KB_MAPPING_SWEDISH_SWEDEN, + CELL_KB_MAPPING_CHINESE_TRADITIONAL, + CELL_KB_MAPPING_CHINESE_SIMPLIFIED, + CELL_KB_MAPPING_SWISS_FRENCH_SWITZERLAND, + CELL_KB_MAPPING_SWISS_GERMAN_SWITZERLAND, + CELL_KB_MAPPING_CANADIAN_FRENCH_CANADA, + CELL_KB_MAPPING_BELGIAN_BELGIUM, + CELL_KB_MAPPING_POLISH_POLAND, + CELL_KB_MAPPING_PORTUGUESE_BRAZIL, +}; + +void cellSysutil_init(); +Module cellSysutil(0x0015, cellSysutil_init); + +void cellVideoOutGetDeviceInfo() +{ + UNIMPLEMENTED_FUNC(cellSysutil); +} + +int cellSysutilGetSystemParamInt(int id, u32 value_addr) +{ + cellSysutil.Log("cellSysutilGetSystemParamInt(id=0x%x, value_addr=0x%x)", id, value_addr); + + if(!Memory.IsGoodAddr(value_addr)) + { + return CELL_EFAULT; + } + + switch(id) + { + case CELL_SYSUTIL_SYSTEMPARAM_ID_LANG: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_LANG"); + Memory.Write32(value_addr, CELL_SYSUTIL_LANG_ENGLISH_US); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_ENTER_BUTTON_ASSIGN: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_ENTER_BUTTON_ASSIGN"); + Memory.Write32(value_addr, CELL_SYSUTIL_ENTER_BUTTON_ASSIGN_CROSS); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_DATE_FORMAT: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_DATE_FORMAT"); + Memory.Write32(value_addr, CELL_SYSUTIL_DATE_FMT_DDMMYYYY); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_TIME_FORMAT: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_TIME_FORMAT"); + Memory.Write32(value_addr, CELL_SYSUTIL_TIME_FMT_CLOCK24); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_TIMEZONE: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_TIMEZONE"); + Memory.Write32(value_addr, 3); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_SUMMERTIME: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_SUMMERTIME"); + Memory.Write32(value_addr, 1); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_GAME_PARENTAL_LEVEL: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_GAME_PARENTAL_LEVEL"); + Memory.Write32(value_addr, CELL_SYSUTIL_GAME_PARENTAL_OFF); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_GAME_PARENTAL_LEVEL0_RESTRICT: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_GAME_PARENTAL_LEVEL0_RESTRICT"); + Memory.Write32(value_addr, CELL_SYSUTIL_GAME_PARENTAL_LEVEL0_RESTRICT_OFF); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_CURRENT_USER_HAS_NP_ACCOUNT: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_CURRENT_USER_HAS_NP_ACCOUNT"); + Memory.Write32(value_addr, 0); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_CAMERA_PLFREQ: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_CAMERA_PLFREQ"); + Memory.Write32(value_addr, CELL_SYSUTIL_CAMERA_PLFREQ_DISABLED); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_PAD_RUMBLE: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_PAD_RUMBLE"); + Memory.Write32(value_addr, CELL_SYSUTIL_PAD_RUMBLE_OFF); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_KEYBOARD_TYPE: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_KEYBOARD_TYPE"); + Memory.Write32(value_addr, 0); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_JAPANESE_KEYBOARD_ENTRY_METHOD: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_JAPANESE_KEYBOARD_ENTRY_METHOD"); + Memory.Write32(value_addr, 0); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_CHINESE_KEYBOARD_ENTRY_METHOD: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_CHINESE_KEYBOARD_ENTRY_METHOD"); + Memory.Write32(value_addr, 0); + break; + + case CELL_SYSUTIL_SYSTEMPARAM_ID_PAD_AUTOOFF: + cellSysutil.Warning("cellSysutilGetSystemParamInt: CELL_SYSUTIL_SYSTEMPARAM_ID_PAD_AUTOOFF"); + Memory.Write32(value_addr, 0); + break; + + default: + return CELL_EINVAL; + } + + return CELL_OK; +} + +void cellSysutil_init() +{ + cellSysutil.AddFunc(0x0bae8772, cellVideoOutConfigure); + cellSysutil.AddFunc(0x189a74da, cellSysutilCheckCallback); + cellSysutil.AddFunc(0x1e930eef, cellVideoOutGetDeviceInfo); + cellSysutil.AddFunc(0x40e895d3, cellSysutilGetSystemParamInt); + cellSysutil.AddFunc(0x887572d5, cellVideoOutGetState); + cellSysutil.AddFunc(0x9d98afa0, cellSysutilRegisterCallback); + cellSysutil.AddFunc(0xe558748d, cellVideoOutGetResolution); +} \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/SysCalls.h b/rpcs3/Emu/SysCalls/SysCalls.h index 2a2d60a446..70e6ae446b 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.h +++ b/rpcs3/Emu/SysCalls/SysCalls.h @@ -271,6 +271,8 @@ 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 UNIMPLEMENTED_FUNC(module) module.Error("Unimplemented function: "__FUNCTION__) + #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] diff --git a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp index 0eaab5d5b3..5f834dd9e3 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp @@ -62,6 +62,10 @@ int cellGcmInit(u32 context_addr, u32 cmdSize, u32 ioSize, u32 ioAddress) ctrl.get = 0; ctrl.ref = -1; + Emu.GetGSManager().GetRender().m_gcm_buffers_addr = Memory.Alloc(sizeof(gcmBuffer) * 8, sizeof(gcmBuffer)); + Emu.GetGSManager().GetRender().m_gcm_buffers_count = 0; + Emu.GetGSManager().GetRender().m_gcm_current_buffer = 0; + Emu.GetGSManager().GetRender().Init(ctx_begin, ctx_size, gcm_info.control_addr, local_addr); return CELL_OK; @@ -122,13 +126,19 @@ int cellGcmSetDisplayBuffer(u32 id, u32 offset, u32 pitch, u32 width, u32 height { 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; + if(id > 7) return CELL_EINVAL; - gcmBuffers[id].offset = offset; - gcmBuffers[id].pitch = pitch; - gcmBuffers[id].width = width; - gcmBuffers[id].height = height; - gcmBuffers[id].update = true; + gcmBuffer* buffers = (gcmBuffer*)Memory.GetMemFromAddr(Emu.GetGSManager().GetRender().m_gcm_buffers_addr); + + buffers[id].offset = re(offset); + buffers[id].pitch = re(pitch); + buffers[id].width = re(width); + buffers[id].height = re(height); + + if(id + 1 > Emu.GetGSManager().GetRender().m_gcm_buffers_count) + { + Emu.GetGSManager().GetRender().m_gcm_buffers_count = id + 1; + } return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp index b1c7dc4a98..fddb80a40e 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp @@ -23,7 +23,7 @@ int sys_ppu_thread_exit(int errorcode) PPUThread& thr = GetCurrentPPUThread(); thr.SetExitStatus(errorcode); - wxGetApp().SendDbgCommand(DID_EXIT_THR_SYSCALL, &thr); + Emu.GetCPU().RemoveThread(thr.GetId()); return CELL_OK; } @@ -157,7 +157,6 @@ void sys_ppu_thread_once(u32 once_ctrl_addr, u32 entry) PPCThread& new_thread = Emu.GetCPU().AddThread(PPC_THREAD_PPU); new_thread.SetEntry(entry); - ((PPUThread&)new_thread).LR = Emu.GetPPUThreadExit(); new_thread.Run(); new_thread.Exec(); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp index 4af1e69c9a..4a223aeab0 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SysUtil.cpp @@ -201,4 +201,4 @@ int cellSysutilUnregisterCallback(int slot) wxGetApp().SendDbgCommand(DID_UNREGISTRED_CALLBACK); return CELL_OK; -} \ No newline at end of file +} diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index eed635de4b..a202c5813b 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -138,9 +138,10 @@ void Emulator::Load() callback_data += SC(2); callback_data += BCLR(0x10 | 0x04, 0, 0, 0); - m_ppu_thr_exit = Memory.MainMem.Alloc(4 * 3); + m_ppu_thr_exit = Memory.MainMem.Alloc(4 * 4); mem32_t ppu_thr_exit_data(m_ppu_thr_exit); + ppu_thr_exit_data += ADDI(3, 0, 0); ppu_thr_exit_data += ADDI(11, 0, 41); ppu_thr_exit_data += SC(2); ppu_thr_exit_data += BCLR(0x10 | 0x04, 0, 0, 0); diff --git a/rpcs3/Gui/InterpreterDisAsm.cpp b/rpcs3/Gui/InterpreterDisAsm.cpp index fd61e070ac..1fe944d2a5 100644 --- a/rpcs3/Gui/InterpreterDisAsm.cpp +++ b/rpcs3/Gui/InterpreterDisAsm.cpp @@ -302,6 +302,9 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) switch(event.GetId()) { case DID_STOPED_EMU: + UpdateUnitList(); + break; + case DID_PAUSED_EMU: //DoUpdate(); break; diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index 0825fecdc8..393cea6820 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -20,6 +20,7 @@ enum IDs id_boot_game, id_sys_pause, id_sys_stop, + id_sys_send_open_menu, id_sys_send_exit, id_config_emu, id_config_vfs_manager, @@ -35,8 +36,9 @@ wxString GetPaneName() } MainFrame::MainFrame() - : FrameBase(NULL, wxID_ANY, "", "MainFrame", wxSize(800, 600)) + : FrameBase(nullptr, wxID_ANY, "", "MainFrame", wxSize(800, 600)) , m_aui_mgr(this) + , m_sys_menu_opened(false) { SetLabel(wxString::Format(_PRGNAME_ " " _PRGVER_)); wxMenuBar& menubar(*new wxMenuBar()); @@ -57,6 +59,7 @@ MainFrame::MainFrame() menu_sys.Append(id_sys_pause, "Pause")->Enable(false); menu_sys.Append(id_sys_stop, "Stop\tCtrl + S")->Enable(false); menu_sys.AppendSeparator(); + menu_sys.Append(id_sys_send_open_menu, "Send open system menu cmd")->Enable(false); menu_sys.Append(id_sys_send_exit, "Send exit cmd")->Enable(false); menu_conf.Append(id_config_emu, "Settings"); @@ -75,6 +78,7 @@ MainFrame::MainFrame() 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_open_menu, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::SendOpenCloseSysMenu) ); Connect( id_sys_send_exit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::SendExit) ); Connect( id_config_emu, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::Config) ); @@ -272,6 +276,13 @@ void MainFrame::SendExit(wxCommandEvent& event) Emu.GetCallbackManager().m_exit_callback.Handle(0x0101, 0); } +void MainFrame::SendOpenCloseSysMenu(wxCommandEvent& event) +{ + Emu.GetCallbackManager().m_exit_callback.Handle(m_sys_menu_opened ? 0x0132 : 0x0131, 0); + m_sys_menu_opened = !m_sys_menu_opened; + UpdateUI(wxCommandEvent()); +} + void MainFrame::Config(wxCommandEvent& WXUNUSED(event)) { //TODO @@ -410,6 +421,7 @@ void MainFrame::UpdateUI(wxCommandEvent& event) is_runned = false; is_stopped = true; is_ready = false; + m_sys_menu_opened = false; break; case DID_PAUSE_EMU: @@ -432,11 +444,14 @@ void MainFrame::UpdateUI(wxCommandEvent& event) is_ready = true; break; - default: + case DID_REGISTRED_CALLBACK: is_runned = Emu.IsRunned(); is_stopped = Emu.IsStopped(); is_ready = Emu.IsReady(); break; + + default: + return; } } else @@ -449,15 +464,19 @@ void MainFrame::UpdateUI(wxCommandEvent& event) 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 ); - + wxMenuItem& send_exit = *menubar.FindItem( id_sys_send_exit ); + wxMenuItem& send_open_menu = *menubar.FindItem( id_sys_send_open_menu ); pause.SetText(is_runned ? "Pause\tCtrl + P" : is_ready ? "Start\tCtrl + C" : "Resume\tCtrl + C"); pause.Enable(!is_stopped); stop.Enable(!is_stopped); //send_exit.Enable(false); - send_exit.Enable(!is_stopped && Emu.GetCallbackManager().m_exit_callback.m_callbacks.GetCount()); + bool enable_commands = !is_stopped && Emu.GetCallbackManager().m_exit_callback.m_callbacks.GetCount(); - m_aui_mgr.Update(); + send_open_menu.SetText(wxString::Format("Send %s system menu cmd", m_sys_menu_opened ? "close" : "open")); + send_open_menu.Enable(enable_commands); + send_exit.Enable(enable_commands); + + //m_aui_mgr.Update(); //wxCommandEvent refit( wxEVT_COMMAND_MENU_SELECTED, id_update_dbg ); //GetEventHandler()->AddPendingEvent( refit ); diff --git a/rpcs3/Gui/MainFrame.h b/rpcs3/Gui/MainFrame.h index 955a160fda..43909bcd76 100644 --- a/rpcs3/Gui/MainFrame.h +++ b/rpcs3/Gui/MainFrame.h @@ -7,6 +7,7 @@ class MainFrame : public FrameBase GameViewer* m_game_viewer; wxAuiManager m_aui_mgr; AppConnector m_app_connector; + bool m_sys_menu_opened; public: MainFrame(); @@ -24,6 +25,7 @@ private: void Pause(wxCommandEvent& event); void Stop(wxCommandEvent& event); void SendExit(wxCommandEvent& event); + void SendOpenCloseSysMenu(wxCommandEvent& event); void Config(wxCommandEvent& event); void ConfigVFS(wxCommandEvent& event); void ConfigVHDD(wxCommandEvent& event); diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index f65f9194e6..162773f50a 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -247,6 +247,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 728fc0fe06..b57aac8b3f 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -280,6 +280,9 @@ Gui + + Emu\SysCalls\Modules + From f83aa9d5aeb192c1983ba1b34d4284db0f522f4f Mon Sep 17 00:00:00 2001 From: DH Date: Mon, 26 Aug 2013 17:18:59 +0300 Subject: [PATCH 10/13] OpenGL renderer: - Improved Vertex & Fragment Shader Decompilers. - Implemented fp uniform loader. - Implemented DXT1 & DXT2 textures decompression. - Implemented draft cellResc module. - Updated glext. PPU Interpreter: - Fixed VSPLTW, VNMSUBFP, VMRGLW, VMRGLH, VMRGLB, VMRGHW, VMRGHH, VMRGHB instructions. cellFs: - Fixed cellFsStat syscall. --- GL/glext.h | 2014 +++++++++++++++++++-- Utilities/Array.h | 14 +- rpcs3/Emu/Cell/MFC.h | 2 +- rpcs3/Emu/Cell/PPUDisAsm.h | 4 +- rpcs3/Emu/Cell/PPUInstrTable.h | 2 +- rpcs3/Emu/Cell/PPUInterpreter.h | 36 +- rpcs3/Emu/Cell/PPUOpcodes.h | 2 +- rpcs3/Emu/GS/GL/FragmentProgram.cpp | 116 +- rpcs3/Emu/GS/GL/FragmentProgram.h | 43 +- rpcs3/Emu/GS/GL/GLBuffers.cpp | 126 ++ rpcs3/Emu/GS/GL/GLBuffers.h | 41 + rpcs3/Emu/GS/GL/GLGSRender.cpp | 878 +++++++-- rpcs3/Emu/GS/GL/GLGSRender.h | 231 ++- rpcs3/Emu/GS/GL/GLProcTable.tbl | 51 +- rpcs3/Emu/GS/GL/Program.cpp | 12 +- rpcs3/Emu/GS/GL/ShaderParam.h | 13 +- rpcs3/Emu/GS/GL/VertexProgram.cpp | 71 +- rpcs3/Emu/GS/GL/VertexProgram.h | 4 +- rpcs3/Emu/GS/GSManager.cpp | 2 +- rpcs3/Emu/GS/GSRender.cpp | 9 +- rpcs3/Emu/GS/GSRender.h | 112 +- rpcs3/Emu/GS/Null/NullGSRender.h | 2 +- rpcs3/Emu/GS/RSXThread.h | 139 +- rpcs3/Emu/Io/Pad.cpp | 4 +- rpcs3/Emu/Io/PadHandler.h | 15 +- rpcs3/Emu/Memory/MemoryBlock.h | 19 +- rpcs3/Emu/SysCalls/FuncList.cpp | 28 +- rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp | 453 ++++- rpcs3/Emu/SysCalls/Modules/cellResc.cpp | 39 + rpcs3/Emu/SysCalls/SysCalls.cpp | 2 +- rpcs3/Emu/SysCalls/SysCalls.h | 24 +- rpcs3/Emu/SysCalls/lv2/SC_Event.cpp | 34 +- rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp | 12 +- rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp | 266 +-- rpcs3/Emu/SysCalls/lv2/SC_Pad.cpp | 6 + rpcs3/Emu/SysCalls/lv2/SC_Process.cpp | 4 + rpcs3/Emu/System.cpp | 13 +- rpcs3/Emu/event.h | 4 + rpcs3/Gui/InterpreterDisAsm.cpp | 5 +- rpcs3/Loader/ELF64.cpp | 4 +- rpcs3/rpcs3.vcxproj | 1 + rpcs3/rpcs3.vcxproj.filters | 3 + 42 files changed, 4015 insertions(+), 845 deletions(-) create mode 100644 rpcs3/Emu/SysCalls/Modules/cellResc.cpp diff --git a/GL/glext.h b/GL/glext.h index af58da28e8..f0999b8acb 100644 --- a/GL/glext.h +++ b/GL/glext.h @@ -6,7 +6,14 @@ extern "C" { #endif /* -** Copyright (c) 2007-2011 The Khronos Group Inc. +** THIS FILE IS OBSOLETE. Please migrate away from using the +** ".spec" files and the headers generated from them to the +** XML Registry and headers generated from that. See +** http://www.opengl.org/registry/api/README.txt +** for more information. +** +** +** Copyright (c) 2007-2013 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the @@ -29,9 +36,9 @@ extern "C" { */ /* Header file version number, required by OpenGL ABI for Linux */ -/* glext.h last updated $Date: 2011-10-02 22:22:16 -0700 (Sun, 02 Oct 2011) $ */ +/* glext.h last updated $Date: 2013-06-13 02:52:31 -0700 (Thu, 13 Jun 2013) $ */ /* Current version at http://www.opengl.org/registry/ */ -#define GL_GLEXT_VERSION 73 +#define GL_GLEXT_VERSION 87 /* Function declaration macros - to move into glplatform.h */ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) @@ -88,9 +95,6 @@ extern "C" { #define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 #define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E -#endif - -#ifndef GL_VERSION_1_2_DEPRECATED #define GL_RESCALE_NORMAL 0x803A #define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 #define GL_SINGLE_COLOR 0x81F9 @@ -110,9 +114,6 @@ extern "C" { #define GL_BLEND_EQUATION 0x8009 #define GL_FUNC_SUBTRACT 0x800A #define GL_FUNC_REVERSE_SUBTRACT 0x800B -#endif - -#ifndef GL_ARB_imaging_DEPRECATED #define GL_CONVOLUTION_1D 0x8010 #define GL_CONVOLUTION_2D 0x8011 #define GL_SEPARABLE_2D 0x8012 @@ -239,9 +240,6 @@ extern "C" { #define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 #define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 #define GL_CLAMP_TO_BORDER 0x812D -#endif - -#ifndef GL_VERSION_1_3_DEPRECATED #define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 #define GL_MAX_TEXTURE_UNITS 0x84E2 #define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 @@ -298,9 +296,6 @@ extern "C" { #define GL_TEXTURE_DEPTH_SIZE 0x884A #define GL_TEXTURE_COMPARE_MODE 0x884C #define GL_TEXTURE_COMPARE_FUNC 0x884D -#endif - -#ifndef GL_VERSION_1_4_DEPRECATED #define GL_POINT_SIZE_MIN 0x8126 #define GL_POINT_SIZE_MAX 0x8127 #define GL_POINT_DISTANCE_ATTENUATION 0x8129 @@ -354,9 +349,7 @@ extern "C" { #define GL_DYNAMIC_READ 0x88E9 #define GL_DYNAMIC_COPY 0x88EA #define GL_SAMPLES_PASSED 0x8914 -#endif - -#ifndef GL_VERSION_1_5_DEPRECATED +#define GL_SRC1_ALPHA 0x8589 #define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 #define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 #define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 @@ -378,7 +371,6 @@ extern "C" { #define GL_SRC1_RGB 0x8581 #define GL_SRC2_RGB 0x8582 #define GL_SRC0_ALPHA 0x8588 -#define GL_SRC1_ALPHA 0x8589 #define GL_SRC2_ALPHA 0x858A #endif @@ -463,9 +455,6 @@ extern "C" { #define GL_STENCIL_BACK_REF 0x8CA3 #define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 #define GL_STENCIL_BACK_WRITEMASK 0x8CA5 -#endif - -#ifndef GL_VERSION_2_0_DEPRECATED #define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 #define GL_POINT_SPRITE 0x8861 #define GL_COORD_REPLACE 0x8862 @@ -489,9 +478,6 @@ extern "C" { #define GL_SRGB8_ALPHA8 0x8C43 #define GL_COMPRESSED_SRGB 0x8C48 #define GL_COMPRESSED_SRGB_ALPHA 0x8C49 -#endif - -#ifndef GL_VERSION_2_1_DEPRECATED #define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F #define GL_SLUMINANCE_ALPHA 0x8C44 #define GL_SLUMINANCE8_ALPHA8 0x8C45 @@ -518,7 +504,7 @@ extern "C" { #define GL_CONTEXT_FLAGS 0x821E #define GL_COMPRESSED_RED 0x8225 #define GL_COMPRESSED_RG 0x8226 -#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 #define GL_RGBA32F 0x8814 #define GL_RGB32F 0x8815 #define GL_RGBA16F 0x881A @@ -726,9 +712,6 @@ extern "C" { /* reuse GL_RG32UI */ /* Reuse tokens from ARB_vertex_array_object */ /* reuse GL_VERTEX_ARRAY_BINDING */ -#endif - -#ifndef GL_VERSION_3_0_DEPRECATED #define GL_CLAMP_VERTEX_COLOR 0x891A #define GL_CLAMP_FRAGMENT_COLOR 0x891B #define GL_ALPHA_INTEGER 0x8D97 @@ -749,7 +732,6 @@ extern "C" { #define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B #define GL_TEXTURE_BINDING_BUFFER 0x8C2C #define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D -#define GL_TEXTURE_BUFFER_FORMAT 0x8C2E #define GL_TEXTURE_RECTANGLE 0x84F5 #define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 #define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 @@ -935,7 +917,6 @@ extern "C" { /* reuse GL_MIN_FRAGMENT_INTERPOLATION_OFFSET */ /* reuse GL_MAX_FRAGMENT_INTERPOLATION_OFFSET */ /* reuse GL_FRAGMENT_INTERPOLATION_OFFSET_BITS */ -/* reuse GL_MAX_VERTEX_STREAMS */ /* Reuse tokens from ARB_gpu_shader_fp64 */ /* reuse GL_DOUBLE_VEC2 */ /* reuse GL_DOUBLE_VEC3 */ @@ -1015,10 +996,12 @@ extern "C" { /* reuse GL_MEDIUM_INT */ /* reuse GL_HIGH_INT */ /* reuse GL_SHADER_COMPILER */ +/* reuse GL_SHADER_BINARY_FORMATS */ /* reuse GL_NUM_SHADER_BINARY_FORMATS */ /* reuse GL_MAX_VERTEX_UNIFORM_VECTORS */ /* reuse GL_MAX_VARYING_VECTORS */ /* reuse GL_MAX_FRAGMENT_UNIFORM_VECTORS */ +/* reuse GL_RGB565 */ /* Reuse tokens from ARB_get_program_binary */ /* reuse GL_PROGRAM_BINARY_RETRIEVABLE_HINT */ /* reuse GL_PROGRAM_BINARY_LENGTH */ @@ -1163,6 +1146,290 @@ extern "C" { /* reuse GL_TEXTURE_IMMUTABLE_FORMAT */ #endif +#ifndef GL_VERSION_4_3 +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +/* Reuse tokens from ARB_arrays_of_arrays (none, GLSL only) */ +/* Reuse tokens from ARB_fragment_layer_viewport (none, GLSL only) */ +/* Reuse tokens from ARB_shader_image_size (none, GLSL only) */ +/* Reuse tokens from ARB_ES3_compatibility */ +/* reuse GL_COMPRESSED_RGB8_ETC2 */ +/* reuse GL_COMPRESSED_SRGB8_ETC2 */ +/* reuse GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 */ +/* reuse GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 */ +/* reuse GL_COMPRESSED_RGBA8_ETC2_EAC */ +/* reuse GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC */ +/* reuse GL_COMPRESSED_R11_EAC */ +/* reuse GL_COMPRESSED_SIGNED_R11_EAC */ +/* reuse GL_COMPRESSED_RG11_EAC */ +/* reuse GL_COMPRESSED_SIGNED_RG11_EAC */ +/* reuse GL_PRIMITIVE_RESTART_FIXED_INDEX */ +/* reuse GL_ANY_SAMPLES_PASSED_CONSERVATIVE */ +/* reuse GL_MAX_ELEMENT_INDEX */ +/* Reuse tokens from ARB_clear_buffer_object (none) */ +/* Reuse tokens from ARB_compute_shader */ +/* reuse GL_COMPUTE_SHADER */ +/* reuse GL_MAX_COMPUTE_UNIFORM_BLOCKS */ +/* reuse GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS */ +/* reuse GL_MAX_COMPUTE_IMAGE_UNIFORMS */ +/* reuse GL_MAX_COMPUTE_SHARED_MEMORY_SIZE */ +/* reuse GL_MAX_COMPUTE_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_MAX_COMPUTE_ATOMIC_COUNTERS */ +/* reuse GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_COMPUTE_LOCAL_INVOCATIONS */ +/* reuse GL_MAX_COMPUTE_WORK_GROUP_COUNT */ +/* reuse GL_MAX_COMPUTE_WORK_GROUP_SIZE */ +/* reuse GL_COMPUTE_LOCAL_WORK_SIZE */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER */ +/* reuse GL_DISPATCH_INDIRECT_BUFFER */ +/* reuse GL_DISPATCH_INDIRECT_BUFFER_BINDING */ +/* Reuse tokens from ARB_copy_image (none) */ +/* Reuse tokens from KHR_debug */ +/* reuse GL_DEBUG_OUTPUT_SYNCHRONOUS */ +/* reuse GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH */ +/* reuse GL_DEBUG_CALLBACK_FUNCTION */ +/* reuse GL_DEBUG_CALLBACK_USER_PARAM */ +/* reuse GL_DEBUG_SOURCE_API */ +/* reuse GL_DEBUG_SOURCE_WINDOW_SYSTEM */ +/* reuse GL_DEBUG_SOURCE_SHADER_COMPILER */ +/* reuse GL_DEBUG_SOURCE_THIRD_PARTY */ +/* reuse GL_DEBUG_SOURCE_APPLICATION */ +/* reuse GL_DEBUG_SOURCE_OTHER */ +/* reuse GL_DEBUG_TYPE_ERROR */ +/* reuse GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR */ +/* reuse GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR */ +/* reuse GL_DEBUG_TYPE_PORTABILITY */ +/* reuse GL_DEBUG_TYPE_PERFORMANCE */ +/* reuse GL_DEBUG_TYPE_OTHER */ +/* reuse GL_MAX_DEBUG_MESSAGE_LENGTH */ +/* reuse GL_MAX_DEBUG_LOGGED_MESSAGES */ +/* reuse GL_DEBUG_LOGGED_MESSAGES */ +/* reuse GL_DEBUG_SEVERITY_HIGH */ +/* reuse GL_DEBUG_SEVERITY_MEDIUM */ +/* reuse GL_DEBUG_SEVERITY_LOW */ +/* reuse GL_DEBUG_TYPE_MARKER */ +/* reuse GL_DEBUG_TYPE_PUSH_GROUP */ +/* reuse GL_DEBUG_TYPE_POP_GROUP */ +/* reuse GL_DEBUG_SEVERITY_NOTIFICATION */ +/* reuse GL_MAX_DEBUG_GROUP_STACK_DEPTH */ +/* reuse GL_DEBUG_GROUP_STACK_DEPTH */ +/* reuse GL_BUFFER */ +/* reuse GL_SHADER */ +/* reuse GL_PROGRAM */ +/* reuse GL_QUERY */ +/* reuse GL_PROGRAM_PIPELINE */ +/* reuse GL_SAMPLER */ +/* reuse GL_DISPLAY_LIST */ +/* reuse GL_MAX_LABEL_LENGTH */ +/* reuse GL_DEBUG_OUTPUT */ +/* reuse GL_CONTEXT_FLAG_DEBUG_BIT */ +/* reuse GL_STACK_UNDERFLOW */ +/* reuse GL_STACK_OVERFLOW */ +/* Reuse tokens from ARB_explicit_uniform_location */ +/* reuse GL_MAX_UNIFORM_LOCATIONS */ +/* Reuse tokens from ARB_framebuffer_no_attachments */ +/* reuse GL_FRAMEBUFFER_DEFAULT_WIDTH */ +/* reuse GL_FRAMEBUFFER_DEFAULT_HEIGHT */ +/* reuse GL_FRAMEBUFFER_DEFAULT_LAYERS */ +/* reuse GL_FRAMEBUFFER_DEFAULT_SAMPLES */ +/* reuse GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS */ +/* reuse GL_MAX_FRAMEBUFFER_WIDTH */ +/* reuse GL_MAX_FRAMEBUFFER_HEIGHT */ +/* reuse GL_MAX_FRAMEBUFFER_LAYERS */ +/* reuse GL_MAX_FRAMEBUFFER_SAMPLES */ +/* Reuse tokens from ARB_internalformat_query2 */ +/* reuse GL_INTERNALFORMAT_SUPPORTED */ +/* reuse GL_INTERNALFORMAT_PREFERRED */ +/* reuse GL_INTERNALFORMAT_RED_SIZE */ +/* reuse GL_INTERNALFORMAT_GREEN_SIZE */ +/* reuse GL_INTERNALFORMAT_BLUE_SIZE */ +/* reuse GL_INTERNALFORMAT_ALPHA_SIZE */ +/* reuse GL_INTERNALFORMAT_DEPTH_SIZE */ +/* reuse GL_INTERNALFORMAT_STENCIL_SIZE */ +/* reuse GL_INTERNALFORMAT_SHARED_SIZE */ +/* reuse GL_INTERNALFORMAT_RED_TYPE */ +/* reuse GL_INTERNALFORMAT_GREEN_TYPE */ +/* reuse GL_INTERNALFORMAT_BLUE_TYPE */ +/* reuse GL_INTERNALFORMAT_ALPHA_TYPE */ +/* reuse GL_INTERNALFORMAT_DEPTH_TYPE */ +/* reuse GL_INTERNALFORMAT_STENCIL_TYPE */ +/* reuse GL_MAX_WIDTH */ +/* reuse GL_MAX_HEIGHT */ +/* reuse GL_MAX_DEPTH */ +/* reuse GL_MAX_LAYERS */ +/* reuse GL_MAX_COMBINED_DIMENSIONS */ +/* reuse GL_COLOR_COMPONENTS */ +/* reuse GL_DEPTH_COMPONENTS */ +/* reuse GL_STENCIL_COMPONENTS */ +/* reuse GL_COLOR_RENDERABLE */ +/* reuse GL_DEPTH_RENDERABLE */ +/* reuse GL_STENCIL_RENDERABLE */ +/* reuse GL_FRAMEBUFFER_RENDERABLE */ +/* reuse GL_FRAMEBUFFER_RENDERABLE_LAYERED */ +/* reuse GL_FRAMEBUFFER_BLEND */ +/* reuse GL_READ_PIXELS */ +/* reuse GL_READ_PIXELS_FORMAT */ +/* reuse GL_READ_PIXELS_TYPE */ +/* reuse GL_TEXTURE_IMAGE_FORMAT */ +/* reuse GL_TEXTURE_IMAGE_TYPE */ +/* reuse GL_GET_TEXTURE_IMAGE_FORMAT */ +/* reuse GL_GET_TEXTURE_IMAGE_TYPE */ +/* reuse GL_MIPMAP */ +/* reuse GL_MANUAL_GENERATE_MIPMAP */ +/* reuse GL_AUTO_GENERATE_MIPMAP */ +/* reuse GL_COLOR_ENCODING */ +/* reuse GL_SRGB_READ */ +/* reuse GL_SRGB_WRITE */ +/* reuse GL_FILTER */ +/* reuse GL_VERTEX_TEXTURE */ +/* reuse GL_TESS_CONTROL_TEXTURE */ +/* reuse GL_TESS_EVALUATION_TEXTURE */ +/* reuse GL_GEOMETRY_TEXTURE */ +/* reuse GL_FRAGMENT_TEXTURE */ +/* reuse GL_COMPUTE_TEXTURE */ +/* reuse GL_TEXTURE_SHADOW */ +/* reuse GL_TEXTURE_GATHER */ +/* reuse GL_TEXTURE_GATHER_SHADOW */ +/* reuse GL_SHADER_IMAGE_LOAD */ +/* reuse GL_SHADER_IMAGE_STORE */ +/* reuse GL_SHADER_IMAGE_ATOMIC */ +/* reuse GL_IMAGE_TEXEL_SIZE */ +/* reuse GL_IMAGE_COMPATIBILITY_CLASS */ +/* reuse GL_IMAGE_PIXEL_FORMAT */ +/* reuse GL_IMAGE_PIXEL_TYPE */ +/* reuse GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST */ +/* reuse GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST */ +/* reuse GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE */ +/* reuse GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE */ +/* reuse GL_TEXTURE_COMPRESSED_BLOCK_WIDTH */ +/* reuse GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT */ +/* reuse GL_TEXTURE_COMPRESSED_BLOCK_SIZE */ +/* reuse GL_CLEAR_BUFFER */ +/* reuse GL_TEXTURE_VIEW */ +/* reuse GL_VIEW_COMPATIBILITY_CLASS */ +/* reuse GL_FULL_SUPPORT */ +/* reuse GL_CAVEAT_SUPPORT */ +/* reuse GL_IMAGE_CLASS_4_X_32 */ +/* reuse GL_IMAGE_CLASS_2_X_32 */ +/* reuse GL_IMAGE_CLASS_1_X_32 */ +/* reuse GL_IMAGE_CLASS_4_X_16 */ +/* reuse GL_IMAGE_CLASS_2_X_16 */ +/* reuse GL_IMAGE_CLASS_1_X_16 */ +/* reuse GL_IMAGE_CLASS_4_X_8 */ +/* reuse GL_IMAGE_CLASS_2_X_8 */ +/* reuse GL_IMAGE_CLASS_1_X_8 */ +/* reuse GL_IMAGE_CLASS_11_11_10 */ +/* reuse GL_IMAGE_CLASS_10_10_10_2 */ +/* reuse GL_VIEW_CLASS_128_BITS */ +/* reuse GL_VIEW_CLASS_96_BITS */ +/* reuse GL_VIEW_CLASS_64_BITS */ +/* reuse GL_VIEW_CLASS_48_BITS */ +/* reuse GL_VIEW_CLASS_32_BITS */ +/* reuse GL_VIEW_CLASS_24_BITS */ +/* reuse GL_VIEW_CLASS_16_BITS */ +/* reuse GL_VIEW_CLASS_8_BITS */ +/* reuse GL_VIEW_CLASS_S3TC_DXT1_RGB */ +/* reuse GL_VIEW_CLASS_S3TC_DXT1_RGBA */ +/* reuse GL_VIEW_CLASS_S3TC_DXT3_RGBA */ +/* reuse GL_VIEW_CLASS_S3TC_DXT5_RGBA */ +/* reuse GL_VIEW_CLASS_RGTC1_RED */ +/* reuse GL_VIEW_CLASS_RGTC2_RG */ +/* reuse GL_VIEW_CLASS_BPTC_UNORM */ +/* reuse GL_VIEW_CLASS_BPTC_FLOAT */ +/* Reuse tokens from ARB_invalidate_subdata (none) */ +/* Reuse tokens from ARB_multi_draw_indirect (none) */ +/* Reuse tokens from ARB_program_interface_query */ +/* reuse GL_UNIFORM */ +/* reuse GL_UNIFORM_BLOCK */ +/* reuse GL_PROGRAM_INPUT */ +/* reuse GL_PROGRAM_OUTPUT */ +/* reuse GL_BUFFER_VARIABLE */ +/* reuse GL_SHADER_STORAGE_BLOCK */ +/* reuse GL_VERTEX_SUBROUTINE */ +/* reuse GL_TESS_CONTROL_SUBROUTINE */ +/* reuse GL_TESS_EVALUATION_SUBROUTINE */ +/* reuse GL_GEOMETRY_SUBROUTINE */ +/* reuse GL_FRAGMENT_SUBROUTINE */ +/* reuse GL_COMPUTE_SUBROUTINE */ +/* reuse GL_VERTEX_SUBROUTINE_UNIFORM */ +/* reuse GL_TESS_CONTROL_SUBROUTINE_UNIFORM */ +/* reuse GL_TESS_EVALUATION_SUBROUTINE_UNIFORM */ +/* reuse GL_GEOMETRY_SUBROUTINE_UNIFORM */ +/* reuse GL_FRAGMENT_SUBROUTINE_UNIFORM */ +/* reuse GL_COMPUTE_SUBROUTINE_UNIFORM */ +/* reuse GL_TRANSFORM_FEEDBACK_VARYING */ +/* reuse GL_ACTIVE_RESOURCES */ +/* reuse GL_MAX_NAME_LENGTH */ +/* reuse GL_MAX_NUM_ACTIVE_VARIABLES */ +/* reuse GL_MAX_NUM_COMPATIBLE_SUBROUTINES */ +/* reuse GL_NAME_LENGTH */ +/* reuse GL_TYPE */ +/* reuse GL_ARRAY_SIZE */ +/* reuse GL_OFFSET */ +/* reuse GL_BLOCK_INDEX */ +/* reuse GL_ARRAY_STRIDE */ +/* reuse GL_MATRIX_STRIDE */ +/* reuse GL_IS_ROW_MAJOR */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_INDEX */ +/* reuse GL_BUFFER_BINDING */ +/* reuse GL_BUFFER_DATA_SIZE */ +/* reuse GL_NUM_ACTIVE_VARIABLES */ +/* reuse GL_ACTIVE_VARIABLES */ +/* reuse GL_REFERENCED_BY_VERTEX_SHADER */ +/* reuse GL_REFERENCED_BY_TESS_CONTROL_SHADER */ +/* reuse GL_REFERENCED_BY_TESS_EVALUATION_SHADER */ +/* reuse GL_REFERENCED_BY_GEOMETRY_SHADER */ +/* reuse GL_REFERENCED_BY_FRAGMENT_SHADER */ +/* reuse GL_REFERENCED_BY_COMPUTE_SHADER */ +/* reuse GL_TOP_LEVEL_ARRAY_SIZE */ +/* reuse GL_TOP_LEVEL_ARRAY_STRIDE */ +/* reuse GL_LOCATION */ +/* reuse GL_LOCATION_INDEX */ +/* reuse GL_IS_PER_PATCH */ +/* Reuse tokens from ARB_robust_buffer_access_behavior (none) */ +/* Reuse tokens from ARB_shader_storage_buffer_object */ +/* reuse GL_SHADER_STORAGE_BUFFER */ +/* reuse GL_SHADER_STORAGE_BUFFER_BINDING */ +/* reuse GL_SHADER_STORAGE_BUFFER_START */ +/* reuse GL_SHADER_STORAGE_BUFFER_SIZE */ +/* reuse GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS */ +/* reuse GL_MAX_SHADER_STORAGE_BLOCK_SIZE */ +/* reuse GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT */ +/* reuse GL_SHADER_STORAGE_BARRIER_BIT */ +/* reuse GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES */ +/* Reuse tokens from ARB_stencil_texturing */ +/* reuse GL_DEPTH_STENCIL_TEXTURE_MODE */ +/* Reuse tokens from ARB_texture_buffer_range */ +/* reuse GL_TEXTURE_BUFFER_OFFSET */ +/* reuse GL_TEXTURE_BUFFER_SIZE */ +/* reuse GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT */ +/* Reuse tokens from ARB_texture_query_levels (none) */ +/* Reuse tokens from ARB_texture_storage_multisample (none) */ +/* Reuse tokens from ARB_texture_view */ +/* reuse GL_TEXTURE_VIEW_MIN_LEVEL */ +/* reuse GL_TEXTURE_VIEW_NUM_LEVELS */ +/* reuse GL_TEXTURE_VIEW_MIN_LAYER */ +/* reuse GL_TEXTURE_VIEW_NUM_LAYERS */ +/* reuse GL_TEXTURE_IMMUTABLE_LEVELS */ +/* Reuse tokens from ARB_vertex_attrib_binding */ +/* reuse GL_VERTEX_ATTRIB_BINDING */ +/* reuse GL_VERTEX_ATTRIB_RELATIVE_OFFSET */ +/* reuse GL_VERTEX_BINDING_DIVISOR */ +/* reuse GL_VERTEX_BINDING_OFFSET */ +/* reuse GL_VERTEX_BINDING_STRIDE */ +/* reuse GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET */ +/* reuse GL_MAX_VERTEX_ATTRIB_BINDINGS */ +#endif + #ifndef GL_ARB_multitexture #define GL_TEXTURE0_ARB 0x84C0 #define GL_TEXTURE1_ARB 0x84C1 @@ -1692,7 +1959,7 @@ extern "C" { #define GL_TEXTURE_DEPTH_TYPE 0x8C16 #define GL_UNSIGNED_NORMALIZED 0x8C17 #define GL_FRAMEBUFFER_BINDING 0x8CA6 -#define GL_DRAW_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 #define GL_RENDERBUFFER_BINDING 0x8CA7 #define GL_READ_FRAMEBUFFER 0x8CA8 #define GL_DRAW_FRAMEBUFFER 0x8CA9 @@ -1745,9 +2012,6 @@ extern "C" { #define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #define GL_MAX_SAMPLES 0x8D57 -#endif - -#ifndef GL_ARB_framebuffer_object_DEPRECATED #define GL_INDEX 0x8222 #define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 #define GL_TEXTURE_INTENSITY_TYPE 0x8C15 @@ -1882,7 +2146,9 @@ extern "C" { #endif #ifndef GL_ARB_copy_buffer +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 #define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 #define GL_COPY_WRITE_BUFFER 0x8F37 #endif @@ -1977,6 +2243,7 @@ extern "C" { #ifndef GL_ARB_texture_gather #define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E #define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F #endif #ifndef GL_ARB_texture_query_lod @@ -2131,7 +2398,9 @@ extern "C" { #ifndef GL_ARB_transform_feedback2 #define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 #define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 #define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 #define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 #endif @@ -2152,10 +2421,12 @@ extern "C" { #define GL_MEDIUM_INT 0x8DF4 #define GL_HIGH_INT 0x8DF5 #define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 #define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB #define GL_MAX_VARYING_VECTORS 0x8DFC #define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 #endif #ifndef GL_ARB_get_program_binary @@ -2393,6 +2664,386 @@ extern "C" { #define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F #endif +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif + +#ifndef GL_KHR_debug +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_DISPLAY_LIST 0x82E7 +/* DISPLAY_LIST used in compatibility profile only */ +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +/* reuse GL_STACK_UNDERFLOW */ +/* reuse GL_STACK_OVERFLOW */ +#endif + +#ifndef GL_ARB_arrays_of_arrays +#endif + +#ifndef GL_ARB_clear_buffer_object +#endif + +#ifndef GL_ARB_compute_shader +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_LOCAL_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_LOCAL_WORK_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#endif + +#ifndef GL_ARB_copy_image +#endif + +#ifndef GL_ARB_texture_view +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#endif + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#endif + +#ifndef GL_ARB_robustness_isolation +#endif + +#ifndef GL_ARB_ES3_compatibility +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#endif + +#ifndef GL_ARB_explicit_uniform_location +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#endif + +#ifndef GL_ARB_fragment_layer_viewport +#endif + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#endif + +#ifndef GL_ARB_internalformat_query2 +/* reuse GL_IMAGE_FORMAT_COMPATIBILITY_TYPE */ +/* reuse GL_NUM_SAMPLE_COUNTS */ +/* reuse GL_RENDERBUFFER */ +/* reuse GL_SAMPLES */ +/* reuse GL_TEXTURE_1D */ +/* reuse GL_TEXTURE_1D_ARRAY */ +/* reuse GL_TEXTURE_2D */ +/* reuse GL_TEXTURE_2D_ARRAY */ +/* reuse GL_TEXTURE_3D */ +/* reuse GL_TEXTURE_CUBE_MAP */ +/* reuse GL_TEXTURE_CUBE_MAP_ARRAY */ +/* reuse GL_TEXTURE_RECTANGLE */ +/* reuse GL_TEXTURE_BUFFER */ +/* reuse GL_TEXTURE_2D_MULTISAMPLE */ +/* reuse GL_TEXTURE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_TEXTURE_COMPRESSED */ +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_SRGB_DECODE_ARB 0x8299 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#endif + +#ifndef GL_ARB_invalidate_subdata +#endif + +#ifndef GL_ARB_multi_draw_indirect +#endif + +#ifndef GL_ARB_program_interface_query +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +/* reuse GL_ATOMIC_COUNTER_BUFFER */ +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +/* reuse GL_NUM_COMPATIBLE_SUBROUTINES */ +/* reuse GL_COMPATIBLE_SUBROUTINES */ +#endif + +#ifndef GL_ARB_robust_buffer_access_behavior +#endif + +#ifndef GL_ARB_shader_image_size +#endif + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +/* reuse GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS */ +#endif + +#ifndef GL_ARB_stencil_texturing +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#endif + +#ifndef GL_ARB_texture_buffer_range +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#endif + +#ifndef GL_ARB_texture_query_levels +#endif + +#ifndef GL_ARB_texture_storage_multisample +#endif + #ifndef GL_EXT_abgr #define GL_ABGR_EXT 0x8000 #endif @@ -2831,12 +3482,9 @@ extern "C" { #ifndef GL_SGIX_tag_sample_buffer #endif -#ifndef GL_FfdMaskSGIX +#ifndef GL_SGIX_polynomial_ffd #define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 #define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 -#endif - -#ifndef GL_SGIX_polynomial_ffd #define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 #define GL_TEXTURE_DEFORMATION_SGIX 0x8195 #define GL_DEFORMATIONS_MASK_SGIX 0x8196 @@ -2908,6 +3556,8 @@ extern "C" { #define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B #define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C #define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 #define GL_COLOR3_BIT_PGI 0x00010000 #define GL_COLOR4_BIT_PGI 0x00020000 #define GL_EDGEFLAG_BIT_PGI 0x00040000 @@ -2924,8 +3574,6 @@ extern "C" { #define GL_TEXCOORD2_BIT_PGI 0x20000000 #define GL_TEXCOORD3_BIT_PGI 0x40000000 #define GL_TEXCOORD4_BIT_PGI 0x80000000 -#define GL_VERTEX23_BIT_PGI 0x00000004 -#define GL_VERTEX4_BIT_PGI 0x00000008 #endif #ifndef GL_PGI_misc_hints @@ -3094,16 +3742,6 @@ extern "C" { #define GL_ALPHA_MAX_SGIX 0x8321 #endif -#ifndef GL_SGIX_impact_pixel_texture -#define GL_PIXEL_TEX_GEN_Q_CEILING_SGIX 0x8184 -#define GL_PIXEL_TEX_GEN_Q_ROUND_SGIX 0x8185 -#define GL_PIXEL_TEX_GEN_Q_FLOOR_SGIX 0x8186 -#define GL_PIXEL_TEX_GEN_ALPHA_REPLACE_SGIX 0x8187 -#define GL_PIXEL_TEX_GEN_ALPHA_NO_REPLACE_SGIX 0x8188 -#define GL_PIXEL_TEX_GEN_ALPHA_LS_SGIX 0x8189 -#define GL_PIXEL_TEX_GEN_ALPHA_MS_SGIX 0x818A -#endif - #ifndef GL_EXT_bgra #define GL_BGR_EXT 0x80E0 #define GL_BGRA_EXT 0x80E1 @@ -3251,11 +3889,6 @@ extern "C" { #define GL_TRANSFORM_HINT_APPLE 0x85B1 #endif -#ifndef GL_SGIX_fog_scale -#define GL_FOG_SCALE_SGIX 0x81FC -#define GL_FOG_SCALE_VALUE_SGIX 0x81FD -#endif - #ifndef GL_SUNX_constant_data #define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 #define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 @@ -3362,12 +3995,12 @@ extern "C" { #endif #ifndef GL_EXT_vertex_weighting -#define GL_MODELVIEW0_STACK_DEPTH_EXT GL_MODELVIEW_STACK_DEPTH +#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 #define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 -#define GL_MODELVIEW0_MATRIX_EXT GL_MODELVIEW_MATRIX +#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 #define GL_MODELVIEW1_MATRIX_EXT 0x8506 #define GL_VERTEX_WEIGHTING_EXT 0x8509 -#define GL_MODELVIEW0_EXT GL_MODELVIEW +#define GL_MODELVIEW0_EXT 0x1700 #define GL_MODELVIEW1_EXT 0x850A #define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B #define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C @@ -3528,12 +4161,6 @@ extern "C" { #define GL_YCRCBA_SGIX 0x8319 #endif -#ifndef GL_SGI_depth_pass_instrument -#define GL_DEPTH_PASS_INSTRUMENT_SGIX 0x8310 -#define GL_DEPTH_PASS_INSTRUMENT_COUNTERS_SGIX 0x8311 -#define GL_DEPTH_PASS_INSTRUMENT_MAX_SGIX 0x8312 -#endif - #ifndef GL_3DFX_texture_compression_FXT1 #define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 #define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 @@ -3617,6 +4244,11 @@ extern "C" { #define GL_FENCE_CONDITION_NV 0x84F4 #endif +#ifndef GL_IBM_static_data +#define GL_ALL_STATIC_DATA_IBM 103060 +#define GL_STATIC_VERTEX_ARRAY_IBM 103061 +#endif + #ifndef GL_IBM_texture_mirrored_repeat #define GL_MIRRORED_REPEAT_IBM 0x8370 #endif @@ -3680,11 +4312,11 @@ extern "C" { #define GL_SHADER_OPERATION_NV 0x86DF #define GL_CULL_MODES_NV 0x86E0 #define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1 #define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2 #define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 -#define GL_OFFSET_TEXTURE_2D_MATRIX_NV GL_OFFSET_TEXTURE_MATRIX_NV -#define GL_OFFSET_TEXTURE_2D_SCALE_NV GL_OFFSET_TEXTURE_SCALE_NV -#define GL_OFFSET_TEXTURE_2D_BIAS_NV GL_OFFSET_TEXTURE_BIAS_NV +#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3 #define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 #define GL_CONST_EYE_NV 0x86E5 #define GL_PASS_THROUGH_NV 0x86E6 @@ -4249,6 +4881,8 @@ extern "C" { #define GL_RGB4_S3TC 0x83A1 #define GL_RGBA_S3TC 0x83A2 #define GL_RGBA4_S3TC 0x83A3 +#define GL_RGBA_DXT5_S3TC 0x83A4 +#define GL_RGBA4_DXT5_S3TC 0x83A5 #endif #ifndef GL_ATI_draw_buffers @@ -4272,7 +4906,7 @@ extern "C" { #endif #ifndef GL_ATI_pixel_format_float -#define GL_TYPE_RGBA_FLOAT_ATI 0x8820 +#define GL_RGBA_FLOAT_MODE_ATI 0x8820 #define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 #endif @@ -4362,11 +4996,37 @@ extern "C" { #ifndef GL_ATI_vertex_attrib_array_object #endif +#ifndef GL_OES_byte_coordinates +#endif + +#ifndef GL_OES_fixed_point +#define GL_FIXED_OES 0x140C +#endif + +#ifndef GL_OES_single_precision +#endif + +#ifndef GL_OES_compressed_paletted_texture +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif + #ifndef GL_OES_read_format #define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A #define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B #endif +#ifndef GL_OES_query_matrix +#endif + #ifndef GL_EXT_depth_bounds_test #define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 #define GL_DEPTH_BOUNDS_EXT 0x8891 @@ -4511,7 +5171,7 @@ extern "C" { #ifndef GL_EXT_framebuffer_blit #define GL_READ_FRAMEBUFFER_EXT 0x8CA8 #define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 -#define GL_DRAW_FRAMEBUFFER_BINDING_EXT GL_FRAMEBUFFER_BINDING_EXT +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 #define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA #endif @@ -4732,7 +5392,7 @@ extern "C" { #define GL_PRIMITIVES_GENERATED_NV 0x8C87 #define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 #define GL_RASTERIZER_DISCARD_NV 0x8C89 -#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_ATTRIBS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B #define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C #define GL_SEPARATE_ATTRIBS_NV 0x8C8D @@ -4899,7 +5559,7 @@ extern "C" { #ifndef GL_AMD_texture_texture4 #endif -#ifndef GL_AMD_vertex_shader_tesselator +#ifndef GL_AMD_vertex_shader_tessellator #define GL_SAMPLER_BUFFER_AMD 0x9001 #define GL_INT_SAMPLER_BUFFER_AMD 0x9002 #define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 @@ -5236,8 +5896,8 @@ extern "C" { #endif #ifndef GL_NV_multisample_coverage -#define GL_COVERAGE_SAMPLES_NV 0x80A9 #define GL_COLOR_SAMPLES_NV 0x8E20 +/* reuse GL_SAMPLES_ARB */ #endif #ifndef GL_AMD_name_gen_delete @@ -5249,6 +5909,7 @@ extern "C" { #endif #ifndef GL_AMD_debug_output +#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143 #define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 #define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 #define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 @@ -5311,6 +5972,212 @@ extern "C" { #define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB #endif +#ifndef GL_NV_path_rendering +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +/* reuse GL_PRIMARY_COLOR */ +/* reuse GL_PRIMARY_COLOR_NV */ +/* reuse GL_SECONDARY_COLOR_NV */ +#endif + +#ifndef GL_AMD_pinned_memory +#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 +#endif + +#ifndef GL_AMD_stencil_operation_extended +#define GL_SET_AMD 0x874A +#define GL_REPLACE_VALUE_AMD 0x874B +#define GL_STENCIL_OP_VALUE_AMD 0x874C +#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D +#endif + +#ifndef GL_AMD_vertex_shader_viewport_index +#endif + +#ifndef GL_AMD_vertex_shader_layer +#endif + +#ifndef GL_NV_bindless_texture +#endif + +#ifndef GL_NV_shader_atomic_float +#endif + +#ifndef GL_AMD_query_buffer_object +#define GL_QUERY_BUFFER_AMD 0x9192 +#define GL_QUERY_BUFFER_BINDING_AMD 0x9193 +#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194 +#endif + +#ifndef GL_NV_compute_program5 +#define GL_COMPUTE_PROGRAM_NV 0x90FB +#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC +#endif + +#ifndef GL_NV_shader_storage_buffer_object +#endif + +#ifndef GL_NV_shader_atomic_counters +#endif + +#ifndef GL_NV_deep_texture3D +#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0 +#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1 +#endif + +#ifndef GL_NVX_conditional_render +#endif + +#ifndef GL_AMD_sparse_texture +#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A +#define GL_MIN_SPARSE_LEVEL_AMD 0x919B +#define GL_MIN_LOD_WARNING_AMD 0x919C +#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001 +#endif + +#ifndef GL_AMD_shader_trinary_minmax +#endif + +#ifndef GL_INTEL_map_texture +#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF +#define GL_LAYOUT_DEFAULT_INTEL 0 +#define GL_LAYOUT_LINEAR_INTEL 1 +#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2 +#endif + +#ifndef GL_NV_draw_texture +#endif + /*************************************************************/ @@ -5410,31 +6277,28 @@ typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLen typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); #endif +#ifndef GL_KHR_debug +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); +#endif + #ifndef GL_NV_vdpau_interop typedef GLintptr GLvdpauSurfaceNV; #endif +#ifndef GL_OES_fixed_point +/* GLint must be 32 bits, a relatively safe assumption on modern CPUs */ +typedef GLint GLfixed; +#endif + #ifndef GL_VERSION_1_2 #define GL_VERSION_1_2 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); GLAPI void APIENTRY glBlendEquation (GLenum mode); GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -#endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); -typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); -typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); -typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); -typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); -typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); -#endif - -#ifndef GL_VERSION_1_2_DEPRECATED -#define GL_VERSION_1_2_DEPRECATED 1 -#ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glColorTable (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); GLAPI void APIENTRY glColorTableParameterfv (GLenum target, GLenum pname, const GLfloat *params); GLAPI void APIENTRY glColorTableParameteriv (GLenum target, GLenum pname, const GLint *params); @@ -5468,6 +6332,12 @@ GLAPI void APIENTRY glMinmax (GLenum target, GLenum internalformat, GLboolean si GLAPI void APIENTRY glResetHistogram (GLenum target); GLAPI void APIENTRY glResetMinmax (GLenum target); #endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); @@ -5506,7 +6376,7 @@ typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); #define GL_VERSION_1_3 1 #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glActiveTexture (GLenum texture); -GLAPI void APIENTRY glSampleCoverage (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); @@ -5514,21 +6384,6 @@ GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, GLvoid *img); -#endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); -typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLclampf value, GLboolean invert); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); -typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); -typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img); -#endif - -#ifndef GL_VERSION_1_3_DEPRECATED -#define GL_VERSION_1_3_DEPRECATED 1 -#ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glClientActiveTexture (GLenum texture); GLAPI void APIENTRY glMultiTexCoord1d (GLenum target, GLdouble s); GLAPI void APIENTRY glMultiTexCoord1dv (GLenum target, const GLdouble *v); @@ -5567,6 +6422,15 @@ GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *m); GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *m); GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m); #endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img); typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); @@ -5610,25 +6474,12 @@ typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); #define GL_VERSION_1_4 1 #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); -GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei drawcount); GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); -#endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); -typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount); -typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); -typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); -typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); -typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); -#endif - -#ifndef GL_VERSION_1_4_DEPRECATED -#define GL_VERSION_1_4_DEPRECATED 1 -#ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glFogCoordf (GLfloat coord); GLAPI void APIENTRY glFogCoordfv (const GLfloat *coord); GLAPI void APIENTRY glFogCoordd (GLdouble coord); @@ -5668,6 +6519,13 @@ GLAPI void APIENTRY glWindowPos3iv (const GLint *v); GLAPI void APIENTRY glWindowPos3s (GLshort x, GLshort y, GLshort z); GLAPI void APIENTRY glWindowPos3sv (const GLshort *v); #endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); @@ -5789,7 +6647,7 @@ GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoi GLAPI GLboolean APIENTRY glIsProgram (GLuint program); GLAPI GLboolean APIENTRY glIsShader (GLuint shader); GLAPI void APIENTRY glLinkProgram (GLuint program); -GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar* const *string, const GLint *length); GLAPI void APIENTRY glUseProgram (GLuint program); GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); @@ -5883,7 +6741,7 @@ typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); -typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* const *string, const GLint *length); typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); @@ -5979,7 +6837,7 @@ GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); GLAPI void APIENTRY glEndTransformFeedback (void); GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); -GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar* const *varyings, GLenum bufferMode); GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); @@ -6038,7 +6896,7 @@ typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); -typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar* *varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar* const *varyings, GLenum bufferMode); typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); @@ -6094,13 +6952,13 @@ typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint ind /* ARB_copy_buffer */ /* ARB_uniform_buffer_object */ #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei primcount); -GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instancecount); GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instancecount); typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); #endif @@ -6155,13 +7013,13 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divi /* ARB_transform_feedback2 */ /* ARB_transform_feedback3 */ #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMinSampleShading (GLclampf value); +GLAPI void APIENTRY glMinSampleShading (GLfloat value); GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLclampf value); +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); @@ -6195,6 +7053,33 @@ typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, /* ARB_texture_storage */ #endif +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +/* OpenGL 4.3 reuses entry points from these extensions: */ +/* ARB_arrays_of_arrays (no entry points, GLSL only) */ +/* ARB_fragment_layer_viewport (no entry points, GLSL only) */ +/* ARB_shader_image_size (no entry points, GLSL only) */ +/* ARB_ES3_compatibility (no entry points) */ +/* ARB_clear_buffer_object */ +/* ARB_compute_shader */ +/* ARB_copy_image */ +/* KHR_debug (includes ARB_debug_output commands promoted to KHR without suffixes) */ +/* ARB_explicit_uniform_location (no entry points) */ +/* ARB_framebuffer_no_attachments */ +/* ARB_internalformat_query2 */ +/* ARB_invalidate_subdata */ +/* ARB_multi_draw_indirect */ +/* ARB_program_interface_query */ +/* ARB_robust_buffer_access_behavior (no entry points) */ +/* ARB_shader_storage_buffer_object */ +/* ARB_stencil_texturing (no entry points) */ +/* ARB_texture_buffer_range */ +/* ARB_texture_query_levels (no entry points) */ +/* ARB_texture_storage_multisample */ +/* ARB_texture_view */ +/* ARB_vertex_attrib_binding */ +#endif + #ifndef GL_ARB_multitexture #define GL_ARB_multitexture 1 #ifdef GL_GLEXT_PROTOTYPES @@ -6286,9 +7171,9 @@ typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); #ifndef GL_ARB_multisample #define GL_ARB_multisample 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glSampleCoverageARB (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSampleCoverageARB (GLfloat value, GLboolean invert); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLfloat value, GLboolean invert); #endif #ifndef GL_ARB_texture_env_add @@ -6907,7 +7792,7 @@ typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); #ifndef GL_ARB_uniform_buffer_object #define GL_ARB_uniform_buffer_object 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar* const *uniformNames, GLuint *uniformIndices); GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); @@ -6915,7 +7800,7 @@ GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlo GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar* *uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar* const *uniformNames, GLuint *uniformIndices); typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); @@ -6949,13 +7834,13 @@ typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum w #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); -GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount, GLint basevertex); -GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount, const GLint *basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instancecount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei drawcount, const GLint *basevertex); #endif /* GL_GLEXT_PROTOTYPES */ typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount, GLint basevertex); -typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* *indices, GLsizei primcount, const GLint *basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei drawcount, const GLint *basevertex); #endif #ifndef GL_ARB_fragment_coord_conventions @@ -7029,9 +7914,9 @@ typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcR #ifndef GL_ARB_sample_shading #define GL_ARB_sample_shading 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glMinSampleShadingARB (GLclampf value); +GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLclampf value); +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); #endif #ifndef GL_ARB_texture_cube_map_array @@ -7358,14 +8243,14 @@ typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index GLAPI void APIENTRY glReleaseShaderCompiler (void); GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const GLvoid *binary, GLsizei length); GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); -GLAPI void APIENTRY glDepthRangef (GLclampf n, GLclampf f); -GLAPI void APIENTRY glClearDepthf (GLclampf d); +GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); +GLAPI void APIENTRY glClearDepthf (GLfloat d); #endif /* GL_GLEXT_PROTOTYPES */ typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const GLvoid *binary, GLsizei length); typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); -typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLclampf n, GLclampf f); -typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLclampf d); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); #endif #ifndef GL_ARB_get_program_binary @@ -7385,7 +8270,7 @@ typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pnam #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); -GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar* *strings); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar* const *strings); GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); @@ -7446,7 +8331,7 @@ GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSiz #endif /* GL_GLEXT_PROTOTYPES */ typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); -typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar* *strings); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar* const *strings); typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); @@ -7541,8 +8426,8 @@ GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); -GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLclampd *v); -GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLclampd n, GLclampd f); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); #endif /* GL_GLEXT_PROTOTYPES */ @@ -7552,8 +8437,8 @@ typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); -typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLclampd *v); -typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLclampd n, GLclampd f); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); #endif @@ -7633,13 +8518,13 @@ typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint locati #ifndef GL_ARB_base_instance #define GL_ARB_base_instance 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei primcount, GLuint baseinstance); -GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount, GLuint baseinstance); -GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount, GLint basevertex, GLuint baseinstance); +GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount, GLuint baseinstance); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount, GLuint baseinstance); -typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount, GLint basevertex, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); #endif #ifndef GL_ARB_shading_language_420pack @@ -7649,11 +8534,11 @@ typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (G #ifndef GL_ARB_transform_feedback_instanced #define GL_ARB_transform_feedback_instanced 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei primcount); -GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei primcount); +GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); +GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei primcount); -typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); #endif #ifndef GL_ARB_compressed_texture_pixel_storage @@ -7716,6 +8601,242 @@ typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum ta typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); #endif +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI void APIENTRY glPopDebugGroup (void); +GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, GLsizeiptr offset, GLsizeiptr size, const void *data); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, GLsizeiptr offset, GLsizeiptr size, const void *data); +#endif + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); +#endif + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +#endif + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +#endif + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +#endif + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +#endif + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +#endif + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); +GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); +GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +#endif + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +#endif + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +#endif + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +#endif + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +#endif + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +#endif + #ifndef GL_EXT_abgr #define GL_EXT_abgr 1 #endif @@ -7723,9 +8844,9 @@ typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum ta #ifndef GL_EXT_blend_color #define GL_EXT_blend_color 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glBlendColorEXT (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +GLAPI void APIENTRY glBlendColorEXT (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); #endif #ifndef GL_EXT_polygon_offset @@ -8044,8 +9165,9 @@ typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); #define GL_SGIX_pixel_tiles 1 #endif -#ifndef GL_SGIX_texture_select -#define GL_SGIX_texture_select 1 +#ifndef GL_SGIS_texture_select +#define GL_SGIS_texture_select 1 +/* This used to be SGIX prefix, which was an error in the header */ #endif #ifndef GL_SGIX_sprite @@ -8457,11 +9579,15 @@ GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum target, GLenum pname, GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum target, GLenum pname, GLfloat param); GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum target, GLenum pname, const GLint *params); GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTransformParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTransformParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); #endif /* GL_GLEXT_PROTOTYPES */ typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); #endif #ifndef GL_EXT_pixel_transform_color_table @@ -8813,11 +9939,11 @@ typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLen #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glVertexWeightfEXT (GLfloat weight); GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *weight); -GLAPI void APIENTRY glVertexWeightPointerEXT (GLsizei size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glVertexWeightPointerEXT (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); #endif /* GL_GLEXT_PROTOTYPES */ typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); -typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLsizei size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); #endif #ifndef GL_NV_light_max_exponent @@ -8944,6 +10070,10 @@ typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); #endif +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#endif + #ifndef GL_IBM_cull_vertex #define GL_IBM_cull_vertex 1 #endif @@ -9082,6 +10212,18 @@ typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); #endif +#ifndef GL_IBM_static_data +#define GL_IBM_static_data 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushStaticDataIBM (GLenum target); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC) (GLenum target); +#endif + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_IBM_texture_mirrored_repeat 1 +#endif + #ifndef GL_NV_evaluators #define GL_NV_evaluators 1 #ifdef GL_GLEXT_PROTOTYPES @@ -9766,15 +10908,15 @@ typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs) /* Some NV_fragment_program entry points are shared with ARB_vertex_program. */ #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); #endif /* GL_GLEXT_PROTOTYPES */ typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); -typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); @@ -9881,10 +11023,10 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, c #ifndef GL_NV_pixel_data_range #define GL_NV_pixel_data_range 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, GLvoid *pointer); +GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, const GLvoid *pointer); GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum target); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, GLvoid *pointer); +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, const GLvoid *pointer); typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); #endif @@ -9938,10 +11080,304 @@ typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); #endif +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiTexCoord1bOES (GLenum texture, GLbyte s); +GLAPI void APIENTRY glMultiTexCoord1bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord2bOES (GLenum texture, GLbyte s, GLbyte t); +GLAPI void APIENTRY glMultiTexCoord2bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord3bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glMultiTexCoord3bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord4bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glMultiTexCoord4bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glTexCoord1bOES (GLbyte s); +GLAPI void APIENTRY glTexCoord1bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord2bOES (GLbyte s, GLbyte t); +GLAPI void APIENTRY glTexCoord2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord3bOES (GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glTexCoord3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord4bOES (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glTexCoord4bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex2bOES (GLbyte x); +GLAPI void APIENTRY glVertex2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex3bOES (GLbyte x, GLbyte y); +GLAPI void APIENTRY glVertex3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex4bOES (GLbyte x, GLbyte y, GLbyte z); +GLAPI void APIENTRY glVertex4bvOES (const GLbyte *coords); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC) (GLenum texture, GLbyte s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC) (GLenum texture, GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC) (GLbyte s); +typedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC) (GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC) (GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC) (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX2BOESPROC) (GLbyte x); +typedef void (APIENTRYP PFNGLVERTEX2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX3BOESPROC) (GLbyte x, GLbyte y); +typedef void (APIENTRYP PFNGLVERTEX3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX4BOESPROC) (GLbyte x, GLbyte y, GLbyte z); +typedef void (APIENTRYP PFNGLVERTEX4BVOESPROC) (const GLbyte *coords); +#endif + +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAccumxOES (GLenum op, GLfixed value); +GLAPI void APIENTRY glAlphaFuncxOES (GLenum func, GLfixed ref); +GLAPI void APIENTRY glBitmapxOES (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +GLAPI void APIENTRY glBlendColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearAccumxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearDepthxOES (GLfixed depth); +GLAPI void APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); +GLAPI void APIENTRY glColor3xOES (GLfixed red, GLfixed green, GLfixed blue); +GLAPI void APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glColor3xvOES (const GLfixed *components); +GLAPI void APIENTRY glColor4xvOES (const GLfixed *components); +GLAPI void APIENTRY glConvolutionParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glConvolutionParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glDepthRangexOES (GLfixed n, GLfixed f); +GLAPI void APIENTRY glEvalCoord1xOES (GLfixed u); +GLAPI void APIENTRY glEvalCoord2xOES (GLfixed u, GLfixed v); +GLAPI void APIENTRY glEvalCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glEvalCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glFeedbackBufferxOES (GLsizei n, GLenum type, const GLfixed *buffer); +GLAPI void APIENTRY glFogxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glFogxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glFrustumxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glGetClipPlanexOES (GLenum plane, GLfixed *equation); +GLAPI void APIENTRY glGetConvolutionParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetHistogramParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetLightxOES (GLenum light, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetMapxvOES (GLenum target, GLenum query, GLfixed *v); +GLAPI void APIENTRY glGetMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glGetPixelMapxv (GLenum map, GLint size, GLfixed *values); +GLAPI void APIENTRY glGetTexEnvxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexLevelParameterxvOES (GLenum target, GLint level, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glIndexxOES (GLfixed component); +GLAPI void APIENTRY glIndexxvOES (const GLfixed *component); +GLAPI void APIENTRY glLightModelxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glLineWidthxOES (GLfixed width); +GLAPI void APIENTRY glLoadMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glLoadTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMap1xOES (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +GLAPI void APIENTRY glMap2xOES (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +GLAPI void APIENTRY glMapGrid1xOES (GLint n, GLfixed u1, GLfixed u2); +GLAPI void APIENTRY glMapGrid2xOES (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +GLAPI void APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glMultMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord1xOES (GLenum texture, GLfixed s); +GLAPI void APIENTRY glMultiTexCoord2xOES (GLenum texture, GLfixed s, GLfixed t); +GLAPI void APIENTRY glMultiTexCoord3xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glMultiTexCoord4xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glMultiTexCoord1xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord2xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord3xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord4xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); +GLAPI void APIENTRY glNormal3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glOrthoxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glPassThroughxOES (GLfixed token); +GLAPI void APIENTRY glPixelMapx (GLenum map, GLint size, const GLfixed *values); +GLAPI void APIENTRY glPixelStorex (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelTransferxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelZoomxOES (GLfixed xfactor, GLfixed yfactor); +GLAPI void APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glPointSizexOES (GLfixed size); +GLAPI void APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); +GLAPI void APIENTRY glPrioritizeTexturesxOES (GLsizei n, const GLuint *textures, const GLfixed *priorities); +GLAPI void APIENTRY glRasterPos2xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glRasterPos3xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glRasterPos4xOES (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +GLAPI void APIENTRY glRasterPos2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRectxOES (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +GLAPI void APIENTRY glRectxvOES (const GLfixed *v1, const GLfixed *v2); +GLAPI void APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glSampleCoverageOES (GLfixed value, GLboolean invert); +GLAPI void APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glTexCoord1xOES (GLfixed s); +GLAPI void APIENTRY glTexCoord2xOES (GLfixed s, GLfixed t); +GLAPI void APIENTRY glTexCoord3xOES (GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glTexCoord4xOES (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glTexCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glVertex2xOES (GLfixed x); +GLAPI void APIENTRY glVertex3xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glVertex4xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glVertex2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex4xvOES (const GLfixed *coords); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACCUMXOESPROC) (GLenum op, GLfixed value); +typedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLfixed ref); +typedef void (APIENTRYP PFNGLBITMAPXOESPROC) (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +typedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLfixed depth); +typedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); +typedef void (APIENTRYP PFNGLCOLOR3XOESPROC) (GLfixed red, GLfixed green, GLfixed blue); +typedef void (APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCOLOR3XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCOLOR4XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC) (GLfixed u); +typedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC) (GLfixed u, GLfixed v); +typedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC) (GLsizei n, GLenum type, const GLfixed *buffer); +typedef void (APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum plane, GLfixed *equation); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETMAPXVOESPROC) (GLenum target, GLenum query, GLfixed *v); +typedef void (APIENTRYP PFNGLGETMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC) (GLenum map, GLint size, GLfixed *values); +typedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC) (GLenum target, GLint level, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLINDEXXOESPROC) (GLfixed component); +typedef void (APIENTRYP PFNGLINDEXXVOESPROC) (const GLfixed *component); +typedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); +typedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMAP1XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +typedef void (APIENTRYP PFNGLMAP2XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +typedef void (APIENTRYP PFNGLMAPGRID1XOESPROC) (GLint n, GLfixed u1, GLfixed u2); +typedef void (APIENTRYP PFNGLMAPGRID2XOESPROC) (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +typedef void (APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC) (GLenum texture, GLfixed s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC) (GLenum texture, GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (APIENTRYP PFNGLNORMAL3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLORTHOXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC) (GLfixed token); +typedef void (APIENTRYP PFNGLPIXELMAPXPROC) (GLenum map, GLint size, const GLfixed *values); +typedef void (APIENTRYP PFNGLPIXELSTOREXPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC) (GLfixed xfactor, GLfixed yfactor); +typedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC) (GLsizei n, const GLuint *textures, const GLfixed *priorities); +typedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +typedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRECTXOESPROC) (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +typedef void (APIENTRYP PFNGLRECTXVOESPROC) (const GLfixed *v1, const GLfixed *v2); +typedef void (APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEOESPROC) (GLfixed value, GLboolean invert); +typedef void (APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC) (GLfixed s); +typedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC) (GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC) (GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC) (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLVERTEX2XOESPROC) (GLfixed x); +typedef void (APIENTRYP PFNGLVERTEX3XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLVERTEX4XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLVERTEX2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX4XVOESPROC) (const GLfixed *coords); +#endif + +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangefOES (GLclampf n, GLclampf f); +GLAPI void APIENTRY glFrustumfOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +GLAPI void APIENTRY glOrthofOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +GLAPI void APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); +GLAPI void APIENTRY glClearDepthfOES (GLclampf depth); +GLAPI void APIENTRY glGetClipPlanefOES (GLenum plane, GLfloat *equation); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f); +typedef void (APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLORTHOFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); +typedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); +typedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum plane, GLfloat *equation); +#endif + +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#endif + #ifndef GL_OES_read_format #define GL_OES_read_format 1 #endif +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLbitfield APIENTRY glQueryMatrixxOES (GLfixed *mantissa, GLint *exponent); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed *mantissa, GLint *exponent); +#endif + #ifndef GL_EXT_depth_bounds_test #define GL_EXT_depth_bounds_test 1 #ifdef GL_GLEXT_PROTOTYPES @@ -10317,13 +11753,13 @@ typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLen #ifndef GL_NV_parameter_buffer_object #define GL_NV_parameter_buffer_object 1 #ifdef GL_GLEXT_PROTOTYPES -GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLfloat *params); -GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLint *params); -GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); #endif /* GL_GLEXT_PROTOTYPES */ -typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLfloat *params); -typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLint *params); -typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint buffer, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); #endif #ifndef GL_EXT_draw_buffers2 @@ -10954,8 +12390,8 @@ typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, #define GL_AMD_texture_texture4 1 #endif -#ifndef GL_AMD_vertex_shader_tesselator -#define GL_AMD_vertex_shader_tesselator 1 +#ifndef GL_AMD_vertex_shader_tessellator +#define GL_AMD_vertex_shader_tessellator 1 #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glTessellationFactorAMD (GLfloat factor); GLAPI void APIENTRY glTessellationModeAMD (GLenum mode); @@ -11393,7 +12829,7 @@ typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei #ifdef GL_GLEXT_PROTOTYPES GLAPI void APIENTRY glVDPAUInitNV (const GLvoid *vdpDevice, const GLvoid *getProcAddress); GLAPI void APIENTRY glVDPAUFiniNV (void); -GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (const GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterOutputSurfaceNV (GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); GLAPI void APIENTRY glVDPAUIsSurfaceNV (GLvdpauSurfaceNV surface); GLAPI void APIENTRY glVDPAUUnregisterSurfaceNV (GLvdpauSurfaceNV surface); @@ -11404,7 +12840,7 @@ GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSur #endif /* GL_GLEXT_PROTOTYPES */ typedef void (APIENTRYP PFNGLVDPAUINITNVPROC) (const GLvoid *vdpDevice, const GLvoid *getProcAddress); typedef void (APIENTRYP PFNGLVDPAUFININVPROC) (void); -typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (const GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) (GLvoid *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); typedef void (APIENTRYP PFNGLVDPAUISSURFACENVPROC) (GLvdpauSurfaceNV surface); typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC) (GLvdpauSurfaceNV surface); @@ -11478,6 +12914,230 @@ typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC) (GLenum mode, GLe #define GL_EXT_framebuffer_multisample_blit_scaled 1 #endif +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); +GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); +GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); +GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const GLvoid *coords); +GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const GLvoid *coords); +GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const GLvoid *coords); +GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const GLvoid *coords); +GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const GLvoid *pathString); +GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const GLvoid *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const GLvoid *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const GLvoid *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); +GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); +GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); +GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); +GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); +GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); +GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); +GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); +GLAPI void APIENTRY glPathColorGenNV (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +GLAPI void APIENTRY glPathTexGenNV (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +GLAPI void APIENTRY glPathFogGenNV (GLenum genMode); +GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); +GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); +GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); +GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +GLAPI void APIENTRY glGetPathColorGenivNV (GLenum color, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathColorGenfvNV (GLenum color, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathTexGenivNV (GLenum texCoordSet, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathTexGenfvNV (GLenum texCoordSet, GLenum pname, GLfloat *value); +GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); +GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); +GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); +GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const GLvoid *coords); +typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const GLvoid *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const GLvoid *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const GLvoid *coords); +typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const GLvoid *pathString); +typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const GLvoid *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const GLvoid *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const GLvoid *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const GLvoid *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +#endif + +#ifndef GL_AMD_pinned_memory +#define GL_AMD_pinned_memory 1 +#endif + +#ifndef GL_AMD_stencil_operation_extended +#define GL_AMD_stencil_operation_extended 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpValueAMD (GLenum face, GLuint value); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC) (GLenum face, GLuint value); +#endif + +#ifndef GL_AMD_vertex_shader_viewport_index +#define GL_AMD_vertex_shader_viewport_index 1 +#endif + +#ifndef GL_AMD_vertex_shader_layer +#define GL_AMD_vertex_shader_layer 1 +#endif + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); +#endif + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif + +#ifndef GL_AMD_query_buffer_object +#define GL_AMD_query_buffer_object 1 +#endif + +#ifndef GL_NV_compute_program5 +#define GL_NV_compute_program5 1 +#endif + +#ifndef GL_NV_shader_storage_buffer_object +#define GL_NV_shader_storage_buffer_object 1 +#endif + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif + +#ifndef GL_NV_deep_texture3D +#define GL_NV_deep_texture3D 1 +#endif + +#ifndef GL_NVX_conditional_render +#define GL_NVX_conditional_render 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNVX (GLuint id); +GLAPI void APIENTRY glEndConditionalRenderNVX (void); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC) (void); +#endif + +#ifndef GL_AMD_sparse_texture +#define GL_AMD_sparse_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexStorageSparseAMD (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +GLAPI void APIENTRY glTextureStorageSparseAMD (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +typedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC) (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#endif + +#ifndef GL_AMD_shader_trinary_minmax +#define GL_AMD_shader_trinary_minmax 1 +#endif + +#ifndef GL_INTEL_map_texture +#define GL_INTEL_map_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSyncTextureINTEL (GLuint texture); +GLAPI void APIENTRY glUnmapTexture2DINTEL (GLuint texture, GLint level); +GLAPI GLvoid* APIENTRY glMapTexture2DINTEL (GLuint texture, GLint level, GLbitfield access, const GLint *stride, const GLenum *layout); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level); +typedef GLvoid* (APIENTRYP PFNGLMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level, GLbitfield access, const GLint *stride, const GLenum *layout); +#endif + +#ifndef GL_NV_draw_texture +#define GL_NV_draw_texture 1 +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawTextureNV (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#endif /* GL_GLEXT_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC) (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#endif + #ifdef __cplusplus } diff --git a/Utilities/Array.h b/Utilities/Array.h index 0f7df002de..11439694e9 100644 --- a/Utilities/Array.h +++ b/Utilities/Array.h @@ -88,11 +88,14 @@ public: return m_count - 1; } - inline bool AddCpy(const u32 pos, const T* data, u64 count = 1) + inline bool AddCpy(const u32 pos, const T* data, u32 count = 1) { if(!InsertRoom(pos, count)) return false; - memcpy(m_array + pos, data, sizeof(T) * count); + for(u32 i=0; i wxString FragmentDecompilerThread::GetSRC(T src) { wxString ret = wxEmptyString; - bool is_color = src.reg_type == 0 || (src.reg_type == 1 && dst.src_attr_reg_num >= 1 && dst.src_attr_reg_num <= 3); - switch(src.reg_type) { case 0: //tmp @@ -177,9 +180,7 @@ template wxString FragmentDecompilerThread::GetSRC(T src) break; } - static const char f_pos[4] = {'x', 'y', 'z', 'w'}; - static const char f_col[4] = {'r', 'g', 'b', 'a'}; - const char *f = is_color ? f_col : f_pos; + static const char f[4] = {'x', 'y', 'z', 'w'}; wxString swizzle = wxEmptyString; swizzle += f[src.swizzle_x]; @@ -199,6 +200,11 @@ wxString FragmentDecompilerThread::BuildCode() { wxString p = wxEmptyString; + if(!m_parr.HasParam(PARAM_OUT, "vec4", "r0") && m_parr.HasParam(PARAM_NONE, "vec4", "h0")) + { + main += "\t" + m_parr.AddParam(PARAM_OUT, "vec4", "r0", 0) + " = " + "h0;\n"; + } + for(u32 i=0; i m_id; + +public: + GLrbo(); + ~GLrbo(); + + void Create(u32 count = 1); + void Bind(u32 num = 0) const; + void Storage(u32 format, u32 width, u32 height); + static void Unbind(); + void Delete(); + bool IsCreated() const; + u32 GetId(u32 num) const; +}; + +class GLfbo +{ +protected: + GLuint m_id; + GLuint m_type; + +public: + GLfbo(); + ~GLfbo(); + + void Create(); + void Bind(u32 type = GL_FRAMEBUFFER, int id = -1); + void Texture1D(u32 attachment, u32 texture, int level = 0); + void Texture2D(u32 attachment, u32 texture, int level = 0); + void Texture3D(u32 attachment, u32 texture, int zoffset = 0, int level = 0); + void Renderbuffer(u32 attachment, u32 renderbuffer); + void Blit(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, u32 mask, u32 filter); + void Unbind(); + static void Unbind(u32 type); + 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 e56ca4320d..b7d15d2323 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.cpp +++ b/rpcs3/Emu/GS/GL/GLGSRender.cpp @@ -3,7 +3,7 @@ #include "Emu/Cell/PPCInstrTable.h" #define CMD_DEBUG 0 -#define DUMP_VERTEX_DATA 0 +#define DUMP_VERTEX_DATA 1 #if CMD_DEBUG #define CMD_LOG ConLog.Write @@ -32,7 +32,7 @@ void checkForGlError(const char* situation) #endif GLGSFrame::GLGSFrame() - : GSFrame(NULL, "GSFrame[OpenGL]") + : GSFrame(nullptr, "GSFrame[OpenGL]") , m_frames(0) { canvas = new wxGLCanvas(this, wxID_ANY, NULL); @@ -80,7 +80,8 @@ void GLGSFrame::SetViewport(int x, int y, u32 w, u32 h) } GLGSRender::GLGSRender() - : m_frame(nullptr) + : GSRender() + , m_frame(nullptr) , m_rsx_thread(nullptr) , m_fp_buf_num(-1) , m_vp_buf_num(-1) @@ -125,14 +126,9 @@ void GLRSXThread::Task() InitProcTable(); glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); glSwapInterval(Ini.GSVSyncEnable.GetValue() ? 1 : 0); - for(u32 i=0; iGetTexture(i); - tex.Create(); - } - bool draw = true; u32 drawed = 0; u32 skipped = 0; @@ -145,49 +141,10 @@ void GLRSXThread::Task() const u32 get = re(p.m_ctrl->get); const u32 put = re(p.m_ctrl->put); - if(put <= get || !Emu.IsRunned()) + if(put == get || !Emu.IsRunned()) { - SemaphorePostAndWait(p.m_sem_flush); - - if(p.m_draw) - { - p.m_draw = false; - - 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_gcm_current_buffer = ++p.m_gcm_current_buffer % p.m_gcm_buffers_count; - p.m_flip_status = 0; - if(p.m_flip_handler) - { - p.m_flip_handler.Handle(1, 0, 0); - p.m_flip_handler.Branch(false); - } - - if(SemaphorePostAndWait(p.m_sem_flip)) continue; - } + if(put == get) + SemaphorePostAndWait(p.m_sem_flush); Sleep(1); continue; @@ -197,27 +154,29 @@ void GLRSXThread::Task() const u32 cmd = Memory.Read32(p.m_ioAddress + get); const u32 count = (cmd >> 18) & 0x7ff; //if(cmd == 0) continue; + if(cmd & CELL_GCM_METHOD_FLAG_JUMP) { u32 addr = cmd & ~(CELL_GCM_METHOD_FLAG_JUMP | CELL_GCM_METHOD_FLAG_NON_INCREMENT); - addr -= 0x1000; - ConLog.Warning("rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", addr, p.m_ioAddress + get, cmd, get, put); + //ConLog.Warning("rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", addr, p.m_ioAddress + get, cmd, get, put); re(p.m_ctrl->get, addr); continue; } if(cmd & CELL_GCM_METHOD_FLAG_CALL) { call_stack.Push(get + 4); - u32 addr = cmd & ~CELL_GCM_METHOD_FLAG_CALL; - ConLog.Warning("rsx call(0x%x) #0x%x - 0x%x", addr, cmd, get); - p.m_ctrl->get = re32(addr); + u32 offs = cmd & ~CELL_GCM_METHOD_FLAG_CALL; + u32 addr = p.m_ioAddress + offs; + //ConLog.Warning("rsx call(0x%x) #0x%x - 0x%x - 0x%x", offs, addr, cmd, get); + p.m_ctrl->get = re32(offs); continue; } - if(cmd & CELL_GCM_METHOD_FLAG_RETURN) + if(cmd == CELL_GCM_METHOD_FLAG_RETURN) { - u32 addr = call_stack.Pop(); - p.m_ctrl->get = re32(addr); - ConLog.Warning("rsx return(0x%x)", addr); + //ConLog.Warning("rsx return!"); + u32 get = call_stack.Pop(); + //ConLog.Warning("rsx return(0x%x)", get); + p.m_ctrl->get = re32(get); continue; } if(cmd & CELL_GCM_METHOD_FLAG_NON_INCREMENT) @@ -262,13 +221,15 @@ void GLGSRender::Init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddre m_draw_frames = 1; m_skip_frames = 0; + m_width = 720; + m_height = 576; m_frame->Show(); m_ioAddress = ioAddress; m_ioSize = ioSize; m_ctrlAddress = ctrlAddress; - m_localAddress = localAddress; + m_local_mem_addr = localAddress; m_ctrl = (CellGcmControl*)Memory.GetMemFromAddr(m_ctrlAddress); m_cur_vertex_prog = nullptr; @@ -298,18 +259,18 @@ void GLGSRender::Close() void GLGSRender::EnableVertexData(bool indexed_draw) { - Array offset_list; + static u32 offset_list[16]; u32 cur_offset = 0; for(u32 i=0; i<16; ++i) { - offset_list.AddCpy(cur_offset); + offset_list[i] = cur_offset; - if(!m_vertex_data[i].IsEnabled()) continue; + if(!m_vertex_data[i].IsEnabled() || !m_vertex_data[i].addr) continue; cur_offset += m_vertex_data[i].data.GetCount(); const u32 pos = m_vdata.GetCount(); - m_vdata.SetCount(pos + m_vertex_data[i].data.GetCount()); + m_vdata.InsertRoomEnd(m_vertex_data[i].data.GetCount()); memcpy(&m_vdata[pos], &m_vertex_data[i].data[0], m_vertex_data[i].data.GetCount()); } @@ -419,13 +380,49 @@ void GLGSRender::EnableVertexData(bool indexed_draw) if(m_vertex_data[i].type >= 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]; + if(!m_vertex_data[i].addr) + { + switch(m_vertex_data[i].type) + { + case 5: + case 1: + switch(m_vertex_data[i].size) + { + case 1: glVertexAttrib1s(i, (GLshort&)m_vertex_data[i].data[0]); break; + case 2: glVertexAttrib2sv(i, (GLshort*)&m_vertex_data[i].data[0]); break; + case 3: glVertexAttrib3sv(i, (GLshort*)&m_vertex_data[i].data[0]); break; + case 4: glVertexAttrib4sv(i, (GLshort*)&m_vertex_data[i].data[0]); break; + } + break; - glEnableVertexAttribArray(i); - checkForGlError("glEnableVertexAttribArray"); - glVertexAttribPointer(i, m_vertex_data[i].size, gltype, normalized, 0, (void*)offset_list[i]); - checkForGlError("glVertexAttribPointer"); + case 2: + switch(m_vertex_data[i].size) + { + case 1: glVertexAttrib1f(i, (GLfloat&)m_vertex_data[i].data[0]); break; + case 2: glVertexAttrib2fv(i, (GLfloat*)&m_vertex_data[i].data[0]); break; + case 3: glVertexAttrib3fv(i, (GLfloat*)&m_vertex_data[i].data[0]); break; + case 4: glVertexAttrib4fv(i, (GLfloat*)&m_vertex_data[i].data[0]); break; + } + break; + + case 6: + case 4: + glVertexAttrib4ubv(i, (GLubyte*)&m_vertex_data[i].data[0]); + break; + } + + checkForGlError("glVertexAttrib"); + } + else + { + 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"); + } } } } @@ -435,9 +432,10 @@ void GLGSRender::DisableVertexData() m_vdata.Clear(); for(u32 i=0; ioffset; + if(id < 32) + id = 32; + + const wxString name = wxString::Format("fc%u", id); + 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); + glUniform4f(l, c.x, c.y, c.z, c.w); + checkForGlError("glUniform4f " + name + wxString::Format(" %d [%f %f %f %f]", l, c.x, c.y, c.z, c.w)); } } @@ -496,14 +520,72 @@ 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 u32 semaphore_offset = 0; u32 index = 0; //static u32 draw_array_count = 0; switch(cmd) { + case 0x3fead: + //if(cmd == 0xfeadffff) + { + //if(p.m_draw) + { + //p.m_draw = false; + /* + if(m_skip_frames) + { + if(!draw) + { + if(skipped++ >= m_skip_frames) + { + skipped = 0; + draw = true; + } + } + else + { + if(drawed++ >= m_draw_frames) + { + drawed = 0; + draw = false; + } + } + }*/ + + //if(draw) + { + //if(m_frame->GetClientSize() != wxSize(m_viewport_w, m_viewport_h)) + // m_frame->SetClientSize(m_viewport_w, m_viewport_h); + + if(m_fbo.IsCreated()) + { + m_fbo.Bind(GL_READ_FRAMEBUFFER); + m_fbo.Bind(GL_DRAW_FRAMEBUFFER, 0); + m_fbo.Blit( + 0, 0, m_width, m_height, + 0, 0, m_width, m_height, + GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + m_fbo.Bind(); + } + + m_frame->Flip(); + } + + m_gcm_current_buffer = args[0]; + + m_flip_status = 0; + if(m_flip_handler) + { + m_flip_handler.Handle(1, 0, 0); + m_flip_handler.Branch(false); + } + + SemaphorePostAndWait(m_sem_flip); + } + } + break; + case NV4097_NO_OPERATION: break; @@ -515,16 +597,22 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c { GLTexture& tex = m_frame->GetTexture(index); const u32 offset = args[0]; - const u8 location = args[1] & 0x3 - 1; - const bool cubemap = (args[1] >> 2) & 0x1; - const u8 dimension = (args[1] >> 4) & 0xf; - const u8 format = (args[1] >> 8) & 0xff; - const u16 mipmap = (args[1] >> 16) & 0xffff; + u32 a1 = args[1]; + u8 location = (a1 & 0x3) - 1; + const bool cubemap = (a1 >> 2) & 0x1; + const u8 dimension = (a1 >> 4) & 0xf; + const u8 format = (a1 >> 8) & 0xff; + const u16 mipmap = (a1 >> 16) & 0xffff; 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); + if(location == 2) + { + ConLog.Error("Bad texture location."); + location = 1; + } u32 tex_addr = GetAddress(offset, location); - //ConLog.Warning("texture addr = 0x%x", tex_addr); + //ConLog.Warning("texture addr = 0x%x #offset = 0x%x, location=%d", tex_addr, offset, location); tex.SetOffset(tex_addr); tex.SetFormat(cubemap, dimension, format, mipmap); } @@ -533,10 +621,15 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case_16(NV4097_SET_TEXTURE_CONTROL0, 0x20): { GLTexture& tex = m_frame->GetTexture(index); - tex.Enable(args[0] >> 31 ? true : false); + u32 a0 = args[0]; + bool enable = a0 >> 31 ? true : false; + u16 minlod = (a0 >> 19) & 0xfff; + u16 maxlod = (a0 >> 7) & 0xfff; + u8 maxaniso = (a0 >> 2) & 0x7; + tex.SetControl0(enable, minlod, maxlod, maxaniso); } break; - + case_16(NV4097_SET_VERTEX_DATA4UB_M, 4): { u32 v = args[0]; @@ -545,21 +638,73 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c u8 v2 = v >> 16; u8 v3 = v >> 24; - m_vertex_data[index].type = 7; + m_vertex_data[index].size = 4; + m_vertex_data[index].type = 4; 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); + //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_VERTEX_DATA2F_M, 8): + { + u32 a0 = args[0]; + u32 a1 = args[1]; + + float v0 = (float&)a0; + float v1 = (float&)a1; + + m_vertex_data[index].type = 2; + m_vertex_data[index].size = 2; + m_vertex_data[index].data.SetCount(sizeof(float) * 2); + (float&)m_vertex_data[index].data[sizeof(float)*0] = v0; + (float&)m_vertex_data[index].data[sizeof(float)*1] = v1; + + //ConLog.Warning("index = %d, v0 = %f, v1 = %f", index, v0, v1); + } + break; + + case_16(NV4097_SET_VERTEX_DATA4F_M, 16): + { + u32 a0 = args[0]; + u32 a1 = args[1]; + u32 a2 = args[2]; + u32 a3 = args[3]; + + float v0 = (float&)a0; + float v1 = (float&)a1; + float v2 = (float&)a2; + float v3 = (float&)a3; + + m_vertex_data[index].type = 2; + m_vertex_data[index].size = 4; + m_vertex_data[index].data.SetCount(sizeof(float) * 4); + (float&)m_vertex_data[index].data[sizeof(float)*0] = v0; + (float&)m_vertex_data[index].data[sizeof(float)*1] = v1; + (float&)m_vertex_data[index].data[sizeof(float)*2] = v2; + (float&)m_vertex_data[index].data[sizeof(float)*3] = v3; + + //ConLog.Warning("index = %d, v0 = %f, v1 = %f, v2 = %f, v3 = %f", index, v0, v1, v2, v3); } break; case_16(NV4097_SET_TEXTURE_CONTROL1, 0x20): - //TODO + { + GLTexture& tex = m_frame->GetTexture(index); + tex.SetControl1(args[0]); + } break; case_16(NV4097_SET_TEXTURE_CONTROL3, 4): - //TODO + { + GLTexture& tex = m_frame->GetTexture(index); + u32 a0 = args[0]; + u32 pitch = a0 & 0xFFFFF; + u16 depth = a0 >> 20; + tex.SetControl3(pitch, depth); + } break; case_16(NV4097_SET_TEXTURE_FILTER, 0x20): @@ -567,7 +712,21 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case_16(NV4097_SET_TEXTURE_ADDRESS, 0x20): - //TODO + { + GLTexture& tex = m_frame->GetTexture(index); + + u32 a0 = args[0]; + u8 wraps = a0 & 0xf; + u8 aniso_bias = (a0 >> 4) & 0xf; + u8 wrapt = (a0 >> 8) & 0xf; + u8 unsigned_remap = (a0 >> 12) & 0xf; + u8 wrapr = (a0 >> 16) & 0xf; + u8 gamma = (a0 >> 20) & 0xf; + u8 signed_remap = (a0 >> 24) & 0xf; + u8 zfunc = a0 >> 28; + + tex.SetAddress(wraps, wrapt, wrapr, unsigned_remap, zfunc, gamma, aniso_bias, signed_remap); + } break; case_16(NV4097_SET_TEX_COORD_CONTROL, 4): @@ -587,20 +746,81 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case NV4097_SET_SURFACE_FORMAT: { - const u32 color_format = args[0] & 0x1f; - const u32 depth_format = (args[0] >> 5) & 0x7; - const u32 type = (args[0] >> 8) & 0xf; - const u32 antialias = (args[0] >> 12) & 0xf; - const u32 width = (args[0] >> 16) & 0xff; - const u32 height = (args[0] >> 24) & 0xff; - const u32 pitch_a = args[1]; - const u32 offset_a = args[2]; - const u32 offset_z = args[3]; - const u32 offset_b = args[4]; - const u32 pitch_b = args[5]; + u32 a0 = args[0]; + m_set_surface_format = true; + m_surface_color_format = a0 & 0x1f; + m_surface_depth_format = (a0 >> 5) & 0x7; + m_surface_type = (a0 >> 8) & 0xf; + m_surface_antialias = (a0 >> 12) & 0xf; + m_surface_width = (a0 >> 16) & 0xff; + m_surface_height = (a0 >> 24) & 0xff; + m_surface_pitch_a = args[1]; + m_surface_offset_a = args[2]; + m_surface_offset_z = args[3]; + m_surface_offset_b = args[4]; + m_surface_pitch_b = args[5]; - CMD_LOG("color_format=%d, depth_format=%d, type=%d, antialias=%d, width=%d, height=%d, pitch_a=%d, offset_a=0x%x, offset_z=0x%x, offset_b=0x%x, pitch_b=%d", - color_format, depth_format, type, antialias, width, height, pitch_a, offset_a, offset_z, offset_b, pitch_b); + /* + ConLog.Write("surface color format: 0x%x", m_surface_color_format); + ConLog.Write("surface depth format: 0x%x", m_surface_depth_format); + ConLog.Write("surface type: 0x%x", m_surface_type); + ConLog.Write("surface antialias: 0x%x", m_surface_antialias); + ConLog.Write("surface width: 0x%x", m_surface_width); + ConLog.Write("surface height: 0x%x", m_surface_height); + ConLog.Write("surface pitch a: 0x%x", m_surface_pitch_a); + ConLog.Write("surface offset a: 0x%x", m_surface_offset_a); + ConLog.Write("surface offset z: 0x%x", m_surface_offset_z); + ConLog.Write("surface offset b: 0x%x", m_surface_offset_b); + ConLog.Write("surface pitch b: 0x%x", m_surface_pitch_b); + if(m_surface_offset_a && m_set_context_dma_color_a) + { + u32 surface_addr = GetAddress(m_surface_offset_a, (m_context_dma_color_a - 0xfeed0000)); + auto& surface = (const CellGcmSurface&)Memory[surface_addr]; + ConLog.Write("context_dma_color_a=0x%x", m_context_dma_color_a); + ConLog.Write("context a: color format: 0x%x", surface.color_format); + ConLog.Write("context a: depth format: 0x%x", surface.depth_format); + ConLog.Write("context a: type: 0x%x", surface.type); + ConLog.Write("context a: antialias: 0x%x", surface.antialias); + ConLog.Write("context a: width: 0x%x", surface.width); + ConLog.Write("context a: height: 0x%x", surface.height); + } + else if(m_surface_offset_z && m_set_context_dma_z) + { + u32 surface_addr = GetAddress(m_surface_offset_z, m_context_dma_z); + auto& surface = (const CellGcmSurface&)Memory[surface_addr]; + ConLog.Write("m_surface_offset_z=0x%x", m_surface_offset_z); + ConLog.Write("context x: color format: 0x%x", surface.color_format); + ConLog.Write("context z: depth format: 0x%x", surface.depth_format); + ConLog.Write("context z: type: 0x%x", surface.type); + ConLog.Write("context z: antialias: 0x%x", surface.antialias); + ConLog.Write("context z: width: 0x%x", surface.width); + ConLog.Write("context z: height: 0x%x", surface.height); + + m_width = surface.width; + m_height = surface.height; + } + */ + + if(0) + { + m_rbo.Create(2); + checkForGlError("m_rbo.Create"); + m_rbo.Bind(0); + m_rbo.Storage(GL_RGBA, m_width, m_height); + checkForGlError("m_rbo.Storage(GL_RGBA)"); + m_rbo.Bind(1); + m_rbo.Storage(GL_DEPTH_STENCIL, m_width, m_height); + checkForGlError("m_rbo.Storage(GL_DEPTH24_STENCIL8)"); + m_fbo.Create(); + checkForGlError("m_fbo.Create"); + m_fbo.Bind(); + m_fbo.Renderbuffer(GL_COLOR_ATTACHMENT0, m_rbo.GetId(0)); + checkForGlError("m_fbo.Renderbuffer(GL_COLOR_ATTACHMENT0)"); + m_fbo.Renderbuffer(GL_DEPTH_STENCIL_ATTACHMENT, m_rbo.GetId(1)); + checkForGlError("m_fbo.Renderbuffer(GL_DEPTH_STENCIL_ATTACHMENT)"); + } + //CMD_LOG("color_format=%d, depth_format=%d, type=%d, antialias=%d, width=%d, height=%d, pitch_a=%d, offset_a=0x%x, offset_z=0x%x, offset_b=0x%x, pitch_b=%d", + // color_format, depth_format, type, antialias, width, height, pitch_a, offset_a, offset_z, offset_b, pitch_b); } break; @@ -663,9 +883,6 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } 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; @@ -699,8 +916,23 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case NV4097_CLEAR_SURFACE: { - m_set_clear_surface = true; - m_clear_surface_mask = args[0]; + u32 a0 = args[0]; + GLbitfield f = 0; + if (a0 & 0x1) f |= GL_DEPTH_BUFFER_BIT; + if (a0 & 0x2) f |= GL_STENCIL_BUFFER_BIT; + if (a0 & 0xF0) f |= GL_COLOR_BUFFER_BIT; + glClear(f); + /* + if(m_set_clear_surface) + { + m_clear_surface_mask |= args[0]; + } + else + { + m_clear_surface_mask = args[0]; + m_set_clear_surface = true; + } + */ } break; @@ -731,23 +963,22 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c { const u32 addr = GetAddress(args[0] & 0x7fffffff, args[0] >> 31); CMD_LOG("num=%d, addr=0x%x", index, addr); - m_vertex_data[index].addr = addr; } break; case_16(NV4097_SET_VERTEX_DATA_ARRAY_FORMAT, 4): { - const u16 frequency = args[0] >> 16; - const u8 stride = (args[0] >> 8) & 0xff; - const u8 size = (args[0] >> 4) & 0xf; - const u8 type = args[0] & 0xf; + u32 a0 = args[0]; + const u16 frequency = a0 >> 16; + const u8 stride = (a0 >> 8) & 0xff; + const u8 size = (a0 >> 4) & 0xf; + const u8 type = a0 & 0xf; CMD_LOG("index=%d, frequency=%d, stride=%d, size=%d, type=%d", index, frequency, stride, size, type); VertexData& cv = m_vertex_data[index]; - cv.frequency = frequency; cv.stride = stride; cv.size = size; @@ -846,16 +1077,17 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c case NV4097_SET_COLOR_CLEAR_VALUE: { const u32 color = args[0]; - const u8 a = (color >> 24) & 0xff; - const u8 r = (color >> 16) & 0xff; - const u8 g = (color >> 8) & 0xff; - const u8 b = color & 0xff; - CMD_LOG("a=%d, r=%d, g=%d, b=%d", a, r, g, b); + //m_set_clear_color = true; + m_clear_color_a = (color >> 24) & 0xff; + m_clear_color_r = (color >> 16) & 0xff; + m_clear_color_g = (color >> 8) & 0xff; + m_clear_color_b = color & 0xff; + glClearColor( - (float)r / 255.0f, - (float)g / 255.0f, - (float)b / 255.0f, - (float)a / 255.0f); + m_clear_color_r / 255.0f, + m_clear_color_g / 255.0f, + m_clear_color_b / 255.0f, + m_clear_color_a / 255.0f); } break; @@ -863,7 +1095,9 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c { m_cur_shader_prog = &m_shader_progs[m_cur_shader_prog_num++]; m_cur_shader_prog->Delete(); - m_cur_shader_prog->addr = GetAddress(args[0] & ~0x3, (args[0] & 0x3) - 1); + u32 a0 = args[0]; + m_cur_shader_prog->offset = a0 & ~0x3; + m_cur_shader_prog->addr = GetAddress(m_cur_shader_prog->offset, (a0 & 0x3) - 1); } break; @@ -1015,7 +1249,54 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_TWO_SIDED_STENCIL_TEST_ENABLE: - if(args[0]) ConLog.Error("NV4097_SET_TWO_SIDED_STENCIL_TEST_ENABLE"); + m_set_two_sided_stencil_test_enable = args[0] ? true : false; + break; + + case NV4097_SET_BACK_STENCIL_MASK: + m_set_back_stencil_mask = true; + m_back_stencil_mask = args[0]; + break; + + case NV4097_SET_BACK_STENCIL_FUNC: + m_set_back_stencil_func = true; + m_back_stencil_func = args[0]; + if(count >= 2) + { + m_set_back_stencil_func_ref = true; + m_back_stencil_func_ref = args[1]; + + if(count >= 3) + { + m_set_back_stencil_func_mask = true; + m_back_stencil_func_mask = args[2]; + } + } + break; + + case NV4097_SET_BACK_STENCIL_FUNC_REF: + m_set_back_stencil_func_ref = true; + m_back_stencil_func_ref = args[0]; + break; + + case NV4097_SET_BACK_STENCIL_FUNC_MASK: + m_set_back_stencil_func_mask = true; + m_back_stencil_func_mask = args[0]; + break; + + case NV4097_SET_BACK_STENCIL_OP_FAIL: + m_set_stencil_fail = true; + m_stencil_fail = args[0]; + if(count >= 2) + { + m_set_back_stencil_zfail = true; + m_back_stencil_zfail = args[1]; + + if(count >= 3) + { + m_set_back_stencil_zpass = true; + m_back_stencil_zpass = args[2]; + } + } break; case NV4097_SET_POLY_OFFSET_FILL_ENABLE: @@ -1038,7 +1319,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_POLY_SMOOTH_ENABLE: - if(args[0]) ConLog.Error("NV4097_SET_POLY_SMOOTH_ENABLE"); + m_set_poly_smooth = args[0] ? true : false; break; case NV4097_SET_BLEND_COLOR: @@ -1099,19 +1380,36 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } break; - case NV4097_BACK_END_WRITE_SEMAPHORE_RELEASE: - { - //TODO - } - break; - case NV4097_SET_SEMAPHORE_OFFSET: case NV406E_SEMAPHORE_OFFSET: { - semaphore_offset = args[0]; + m_set_semaphore_offset = true; + m_semaphore_offset = args[0]; } break; + case NV4097_BACK_END_WRITE_SEMAPHORE_RELEASE: + { + if(m_set_semaphore_offset) + { + m_set_semaphore_offset = false; + u32 value = args[0]; + value = (value & 0xff00ff00) | ((value & 0xff) << 16) | ((value >> 16) & 0xff); + + Memory.Write32(Memory.RSXCMDMem.GetStartAddr() + m_semaphore_offset, value); + } + } + break; + + case NV406E_SEMAPHORE_RELEASE: + case NV4097_TEXTURE_READ_SEMAPHORE_RELEASE: + if(m_set_semaphore_offset) + { + m_set_semaphore_offset = false; + Memory.Write32(Memory.RSXCMDMem.GetStartAddr() + m_semaphore_offset, args[0]); + } + break; + case NV406E_SEMAPHORE_ACQUIRE: { //TODO @@ -1129,27 +1427,32 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c //TODO } break; + case NV4097_SET_CONTEXT_DMA_COLOR_A: { - //TODO + m_set_context_dma_color_a = true; + m_context_dma_color_a = args[0]; } break; case NV4097_SET_CONTEXT_DMA_COLOR_B: { - //TODO + m_set_context_dma_color_b = true; + m_context_dma_color_b = args[0]; } break; case NV4097_SET_CONTEXT_DMA_COLOR_C: { - //TODO + m_set_context_dma_color_c = true; + m_context_dma_color_c = args[0]; } break; case NV4097_SET_CONTEXT_DMA_ZETA: { - //TODO + m_set_context_dma_z = true; + m_context_dma_z = args[0]; } break; @@ -1183,9 +1486,30 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } break; + case NV4097_SET_SURFACE_CLIP_VERTICAL: + { + u32 a0 = args[0]; + m_set_surface_clip_vertical = true; + m_surface_clip_y = a0; + m_surface_clip_h = a0 >> 16; + } + break; + case NV4097_SET_SURFACE_CLIP_HORIZONTAL: { - //TODO + u32 a0 = args[0]; + + m_set_surface_clip_horizontal = true; + m_surface_clip_x = a0; + m_surface_clip_w = a0 >> 16; + + if(count >= 2) + { + u32 a1 = args[1]; + m_set_surface_clip_vertical = true; + m_surface_clip_y = a1; + m_surface_clip_h = a1 >> 16; + } } break; @@ -1242,11 +1566,159 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; default: + data = 0; ConLog.Error("NV4097_GET_REPORT: bad type %d", type); break; } - Memory.Write64(m_localAddress + offset, data); + Memory.Write64(m_local_mem_addr + offset, data); + } + break; + + case NV3062_SET_OFFSET_DESTIN: + m_dst_offset = args[0]; + break; + + case NV308A_COLOR: + { + TransformConstant c; + c.id = m_dst_offset; + + if(count >= 1) + { + u32 a = args[0]; + a = a << 16 | a >> 16; + c.x = (float&)a; + } + + if(count >= 2) + { + u32 a = args[1]; + a = a << 16 | a >> 16; + c.y = (float&)a; + } + + if(count >= 3) + { + u32 a = args[2]; + a = a << 16 | a >> 16; + c.z = (float&)a; + } + + if(count >= 4) + { + u32 a = args[3]; + a = a << 16 | a >> 16; + c.w = (float&)a; + } + + if(count >= 5) + { + ConLog.Warning("NV308A_COLOR: count = %d", count); + } + + //ConLog.Warning("NV308A_COLOR: [%d]: %f, %f, %f, %f", c.id, c.x, c.y, c.z, c.w); + m_fragment_constants.AddCpy(c); + } + break; + + case NV308A_POINT: + //TODO + break; + + case NV3062_SET_COLOR_FORMAT: + { + m_color_format = args[0]; + m_color_format_src_pitch = args[1]; + m_color_format_dst_pitch = args[1] >> 16; + } + break; + + case NV3089_SET_COLOR_CONVERSION: + { + m_color_conv = args[0]; + m_color_conv_fmt = args[1]; + m_color_conv_op = args[2]; + m_color_conv_in_x = args[3]; + m_color_conv_in_y = args[3] >> 16; + m_color_conv_in_w = args[4]; + m_color_conv_in_h = args[4] >> 16; + m_color_conv_out_x = args[5]; + m_color_conv_out_y = args[5] >> 16; + m_color_conv_out_w = args[6]; + m_color_conv_out_h = args[6] >> 16; + m_color_conv_dsdx = args[7]; + m_color_conv_dtdy = args[8]; + } + break; + + case NV3089_IMAGE_IN_SIZE: + { + u16 w = args[0]; + u16 h = args[0] >> 16; + u16 pitch = args[1]; + u8 origin = args[1] >> 16; + u8 inter = args[1] >> 24; + u32 offset = args[2]; + u16 u = args[3]; + u16 v = args[3] >> 16; + + u8* pixels_src = &Memory[GetAddress(offset, m_context_dma_img_src - 0xfeed0000)]; + u8* pixels_dst = &Memory[GetAddress(m_dst_offset, m_context_dma_img_dst - 0xfeed0000)]; + + for(u16 y=0; y> 4) & 0xf; + m_clip_plane_2 = (a0 >> 8) & 0xf; + m_clip_plane_3 = (a0 >> 12) & 0xf; + m_clip_plane_4 = (a0 >> 16) & 0xf; + m_clip_plane_5 = a0 >> 20; + } + break; + + case NV4097_SET_FOG_PARAMS: + { + m_set_fog_params = true; + u32 a0 = args[0]; + u32 a1 = args[1]; + m_fog_param0 = (float&)a0; + m_fog_param1 = (float&)a1; } break; @@ -1359,36 +1831,53 @@ void GLGSRender::ExecCMD() { if(LoadProgram()) { - if(m_set_clear_surface) + if(m_set_surface_clip_horizontal && m_set_surface_clip_vertical) { - GLbitfield f = 0; - if (m_clear_surface_mask & 0x1) f |= GL_DEPTH_BUFFER_BIT; - if (m_clear_surface_mask & 0x2) f |= GL_STENCIL_BUFFER_BIT; - if (m_clear_surface_mask & 0x10) f |= GL_COLOR_BUFFER_BIT; - glClear(f); + m_width = m_surface_clip_w; + m_height = m_surface_clip_h; + //ConLog.Write("width: %d, height: %d, x: %d, y: %d", m_width, m_height, m_surface_clip_x, m_surface_clip_y); } if(m_set_color_mask) { glColorMask(m_color_mask_r, m_color_mask_g, m_color_mask_b, m_color_mask_a); + checkForGlError("glColorMask"); } 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->GetClientSize() != 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); + glViewport(m_scissor_x, m_height-m_viewport_y-m_viewport_h, m_viewport_w, m_viewport_h); + checkForGlError("glViewport"); } if(m_set_scissor_horizontal && m_set_scissor_vertical) { - glScissor(m_scissor_x, m_scissor_y, m_scissor_w, m_scissor_h); + glScissor(m_scissor_x, m_height-m_scissor_y-m_scissor_h, m_scissor_w, m_scissor_h); + checkForGlError("glScissor"); + } + + if(m_set_clear_color) + { + glClearColor( + m_clear_color_r / 255.0f, + m_clear_color_g / 255.0f, + m_clear_color_b / 255.0f, + m_clear_color_a / 255.0f); + } + + if(m_set_clear_surface) + { + GLbitfield f = 0; + if (m_clear_surface_mask & 0x1) f |= GL_DEPTH_BUFFER_BIT; + if (m_clear_surface_mask & 0x2) f |= GL_STENCIL_BUFFER_BIT; + if (m_clear_surface_mask & 0xF0) f |= GL_COLOR_BUFFER_BIT; + glClear(f); } if(m_set_front_polygon_mode) { glPolygonMode(GL_FRONT, m_front_polygon_mode); + checkForGlError("glPolygonMode"); } Enable(m_depth_test_enable, GL_DEPTH_TEST); @@ -1400,60 +1889,117 @@ void GLGSRender::ExecCMD() Enable(m_set_dither, GL_DITHER); Enable(m_set_stencil_test, GL_STENCIL_TEST); Enable(m_set_line_smooth, GL_LINE_SMOOTH); + Enable(m_set_poly_smooth, GL_POLYGON_SMOOTH); + checkForGlError("glEnable"); - if(m_set_stencil_mask) + if(m_set_clip_plane) { - glStencilMask(m_stencil_mask); + Enable(m_clip_plane_0, GL_CLIP_PLANE0); + Enable(m_clip_plane_1, GL_CLIP_PLANE1); + Enable(m_clip_plane_2, GL_CLIP_PLANE2); + Enable(m_clip_plane_3, GL_CLIP_PLANE3); + Enable(m_clip_plane_4, GL_CLIP_PLANE4); + Enable(m_clip_plane_5, GL_CLIP_PLANE5); + + checkForGlError("m_set_clip_plane"); } - if(m_set_stencil_func && m_set_stencil_func_ref && m_set_stencil_func_mask) + if(m_set_two_sided_stencil_test_enable) { - glStencilFunc(m_stencil_func, m_stencil_func_ref, m_stencil_func_mask); + if(m_set_back_stencil_fail && m_set_back_stencil_zfail && m_set_back_stencil_zpass) + { + glStencilOpSeparate(GL_BACK, m_back_stencil_fail, m_back_stencil_zfail, m_back_stencil_zpass); + checkForGlError("glStencilOpSeparate(GL_BACK)"); + } + + if(m_set_back_stencil_mask) + { + glStencilMaskSeparate(GL_BACK, m_back_stencil_mask); + checkForGlError("glStencilMaskSeparate(GL_BACK)"); + } + + if(m_set_back_stencil_func && m_set_back_stencil_func_ref && m_set_back_stencil_func_mask) + { + glStencilFuncSeparate(GL_BACK, m_back_stencil_func, m_back_stencil_func_ref, m_back_stencil_func_mask); + checkForGlError("glStencilFuncSeparate(GL_BACK)"); + } } if(m_set_stencil_fail && m_set_stencil_zfail && m_set_stencil_zpass) { - glStencilOp(m_stencil_fail, m_stencil_zfail, m_stencil_zpass); + glStencilOpSeparate(GL_FRONT, m_stencil_fail, m_stencil_zfail, m_stencil_zpass); + checkForGlError("glStencilOpSeparate(GL_FRONT)"); + } + + if(m_set_stencil_mask) + { + glStencilMaskSeparate(GL_FRONT, m_stencil_mask); + checkForGlError("glStencilMaskSeparate(GL_FRONT)"); + } + + if(m_set_stencil_func && m_set_stencil_func_ref && m_set_stencil_func_mask) + { + glStencilFuncSeparate(GL_FRONT, m_stencil_func, m_stencil_func_ref, m_stencil_func_mask); + checkForGlError("glStencilFuncSeparate(GL_FRONT)"); } if(m_set_shade_mode) { glShadeModel(m_shade_mode); + checkForGlError("glShadeModel"); } if(m_set_depth_mask) { glDepthMask(m_depth_mask); + checkForGlError("glDepthMask"); } if(m_set_depth_func) { glDepthFunc(m_depth_func); + checkForGlError("glDepthFunc"); } if(m_set_clip) { glDepthRangef(m_clip_min, m_clip_max); + checkForGlError("glDepthRangef"); } if(m_set_line_width) { glLineWidth(m_line_width / 255.f); + checkForGlError("glLineWidth"); } if(m_set_blend_equation) { glBlendEquationSeparate(m_blend_equation_rgb, m_blend_equation_alpha); + checkForGlError("glBlendEquationSeparate"); } if(m_set_blend_sfactor && m_set_blend_dfactor) { glBlendFuncSeparate(m_blend_sfactor_rgb, m_blend_dfactor_rgb, m_blend_sfactor_alpha, m_blend_dfactor_alpha); + checkForGlError("glBlendFuncSeparate"); } if(m_set_blend_color) { glBlendColor(m_blend_color_r, m_blend_color_g, m_blend_color_b, m_blend_color_a); + checkForGlError("glBlendColor"); + } + + if(m_set_fog_mode) + { + glFogi(GL_FOG_MODE, m_fog_mode); + } + + if(m_set_fog_params) + { + glFogf(GL_FOG_START, m_fog_param0); + glFogf(GL_FOG_END, m_fog_param1); } if(m_indexed_array.m_count && m_draw_array_count) @@ -1477,28 +2023,29 @@ void GLGSRender::ExecCMD() //tex.Save(); } + m_vao.Bind(); 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)); + EnableVertexData(m_indexed_array.m_count ? true : false); - m_vao.Bind(); + InitVertexData(); + InitFragmentData(); + + if(m_indexed_array.m_count) + { 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; diff --git a/rpcs3/Emu/GS/GL/GLGSRender.h b/rpcs3/Emu/GS/GL/GLGSRender.h index dad0efcb84..805c694b3c 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.h +++ b/rpcs3/Emu/GS/GL/GLGSRender.h @@ -24,18 +24,39 @@ class GLTexture u8 m_dimension; u32 m_format; u16 m_mipmap; + + u32 m_pitch; + u16 m_depth; + + u16 m_minlod; + u16 m_maxlod; + u8 m_maxaniso; + + u8 m_wraps; + u8 m_wrapt; + u8 m_wrapr; + u8 m_unsigned_remap; + u8 m_zfunc; + u8 m_gamma; + u8 m_aniso_bias; + u8 m_signed_remap; + + u32 m_remap; public: GLTexture() - : m_width(0), m_height(0), - m_id(0), - m_offset(0), - m_enabled(false), + : m_width(0), m_height(0) + , m_id(0) + , m_offset(0) + , m_enabled(false) - m_cubemap(false), - m_dimension(0), - m_format(0), - m_mipmap(0) + , m_cubemap(false) + , m_dimension(0) + , m_format(0) + , m_mipmap(0) + , m_minlod(0) + , m_maxlod(1000) + , m_maxaniso(0) { } @@ -51,10 +72,11 @@ public: glGenTextures(1, &m_id); checkForGlError("GLTexture::Init() -> glGenTextures"); Bind(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); } } @@ -74,6 +96,37 @@ public: m_mipmap = mipmap; } + void SetAddress(u8 wraps, u8 wrapt, u8 wrapr, u8 unsigned_remap, u8 zfunc, u8 gamma, u8 aniso_bias, u8 signed_remap) + { + m_wraps = wraps; + m_wrapt = wrapt; + m_wrapr = wrapr; + m_unsigned_remap = unsigned_remap; + m_zfunc = zfunc; + m_gamma = gamma; + m_aniso_bias = aniso_bias; + m_signed_remap = signed_remap; + } + + void SetControl0(const bool enable, const u16 minlod, const u16 maxlod, const u8 maxaniso) + { + m_enabled = enable; + m_minlod = minlod; + m_maxlod = maxlod; + m_maxaniso = maxaniso; + } + + void SetControl1(u32 remap) + { + m_remap = remap; + } + + void SetControl3(u16 depth, u32 pitch) + { + m_depth = depth; + m_pitch = pitch; + } + u32 GetFormat() const { return m_format; } void SetOffset(const u32 offset) @@ -86,44 +139,163 @@ public: return wxSize(m_width, m_height); } + int GetGlWrap(int wrap) + { + switch(wrap) + { + case 1: return GL_REPEAT; + case 2: return GL_MIRRORED_REPEAT; + case 3: return GL_CLAMP_TO_EDGE; + case 4: return GL_TEXTURE_BORDER; + case 5: return GL_CLAMP_TO_EDGE;//GL_CLAMP; + //case 6: return GL_MIRROR_CLAMP_TO_EDGE_EXT; + } + + ConLog.Error("Texture wrap error: bad wrap (%d).", wrap); + return GL_REPEAT; + } + void Init() { Bind(); - //ConLog.Warning("texture addr = 0x%x, width = %d, height = %d", m_offset, m_width, m_height); + if(!Memory.IsGoodAddr(m_offset)) + { + ConLog.Error("Bad texture address=0x%x", m_offset); + return; + } + //ConLog.Warning("texture addr = 0x%x, width = %d, height = %d, max_aniso=%d, mipmap=%d, remap=0x%x, zfunc=0x%x, wraps=0x%x, wrapt=0x%x, wrapr=0x%x, minlod=0x%x, maxlod=0x%x", + // m_offset, m_width, m_height, m_maxaniso, m_mipmap, m_remap, m_zfunc, m_wraps, m_wrapt, m_wrapr, m_minlod, m_maxlod); //TODO: safe init checkForGlError("GLTexture::Init() -> glBindTexture"); - switch(m_format & ~(0x20 | 0x40)) + glPixelStorei(GL_PACK_ROW_LENGTH, m_pitch); + + int format = m_format & ~(0x20 | 0x40); + bool is_swizzled = (m_format & 0x20) == 0; + + char* pixels = (char*)Memory.GetMemFromAddr(m_offset); + + switch(format) { case 0x81: - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_BLUE, GL_UNSIGNED_BYTE, Memory.GetMemFromAddr(m_offset)); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_BLUE, GL_UNSIGNED_BYTE, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_BLUE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_BLUE); - + checkForGlError("GLTexture::Init() -> glTexParameteri"); break; case 0x85: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, Memory.GetMemFromAddr(m_offset)); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels); checkForGlError("GLTexture::Init() -> glTexImage2D"); break; - case 0x94://FIXME - glTexImage2D(GL_TEXTURE_2D, 0, GL_R16, m_width, m_height, 0, GL_RED, GL_SHORT, Memory.GetMemFromAddr(m_offset)); + case 0x86: + { + u32 size = ((m_width + 3) / 4) * ((m_height + 3) / 4) * 8; + + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, m_width, m_height, 0, size, pixels); + checkForGlError("GLTexture::Init() -> glCompressedTexImage2D"); + } + break; + + case 0x87: + { + u32 size = ((m_width + 3) / 4) * ((m_height + 3) / 4) * 16; + + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, m_width, m_height, 0, size, pixels); + checkForGlError("GLTexture::Init() -> glCompressedTexImage2D"); + } + break; + + case 0x88: + { + u32 size = ((m_width + 3) / 4) * ((m_height + 3) / 4) * 16; + + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, m_width, m_height, 0, size, pixels); + checkForGlError("GLTexture::Init() -> glCompressedTexImage2D"); + } + break; + + case 0x94: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RED, GL_SHORT, pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); checkForGlError("GLTexture::Init() -> glTexImage2D"); break; - default: ConLog.Error("Init tex error: Bad tex format (0x%x)", m_format); break; + case 0x9a: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_BGRA, GL_HALF_FLOAT, pixels); + checkForGlError("GLTexture::Init() -> glTexImage2D"); + break; + + case 0x9e: + { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels); + checkForGlError("GLTexture::Init() -> glTexImage2D"); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE); + } + break; + + default: ConLog.Error("Init tex error: Bad tex format (0x%x | 0x%x | 0x%x)", format, m_format & 0x20, m_format & 0x40); break; } + if(m_mipmap > 1) + { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + if(format != 0x81 && format != 0x94) + { + u8 remap_a = m_remap & 0x3; + u8 remap_r = (m_remap >> 2) & 0x3; + u8 remap_g = (m_remap >> 4) & 0x3; + u8 remap_b = (m_remap >> 6) & 0x3; + + static const int gl_remap[] = + { + GL_ALPHA, + GL_RED, + GL_GREEN, + GL_BLUE, + }; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, gl_remap[remap_a]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, gl_remap[remap_r]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, gl_remap[remap_g]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, gl_remap[remap_b]); + } + + static const int gl_tex_zfunc[] = + { + GL_NEVER, + GL_LESS, + GL_EQUAL, + GL_LEQUAL, + GL_GREATER, + GL_NOTEQUAL, + GL_GEQUAL, + GL_ALWAYS, + }; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GetGlWrap(m_wraps)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GetGlWrap(m_wrapt)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GetGlWrap(m_wrapr)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, gl_tex_zfunc[m_zfunc]); + + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, m_aniso_bias); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, m_minlod); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, m_maxlod); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, m_maxaniso); + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //Unbind(); } @@ -207,7 +379,7 @@ public: m_id = 0; } } - void Enable(bool enable) { m_enabled = enable; } + bool IsEnabled() const { return m_enabled; } }; @@ -217,6 +389,10 @@ struct TransformConstant float x, y, z, w; TransformConstant() + : x(0.0f) + , y(0.0f) + , z(0.0f) + , w(0.0f) { } @@ -310,6 +486,7 @@ private: VertexProgram m_vertex_progs[m_vertex_count]; VertexProgram* m_cur_vertex_prog; Array m_transform_constants; + Array m_fragment_constants; Program m_program; int m_fp_buf_num; @@ -318,8 +495,13 @@ private: int m_draw_mode; ProgramBuffer m_prog_buffer; + u32 m_width; + u32 m_height; + GLvao m_vao; GLvbo m_vbo; + GLrbo m_rbo; + GLfbo m_fbo; public: GLGSFrame* m_frame; @@ -334,6 +516,7 @@ private: void DisableVertexData(); void LoadVertexData(u32 first, u32 count); void InitVertexData(); + void InitFragmentData(); void Enable(bool enable, const u32 cap); virtual void Init(const u32 ioAddress, const u32 ioSize, const u32 ctrlAddress, const u32 localAddress); diff --git a/rpcs3/Emu/GS/GL/GLProcTable.tbl b/rpcs3/Emu/GS/GL/GLProcTable.tbl index 9d76816541..6aeb2c8be4 100644 --- a/rpcs3/Emu/GS/GL/GLProcTable.tbl +++ b/rpcs3/Emu/GS/GL/GLProcTable.tbl @@ -30,6 +30,17 @@ OPENGL_PROC(PFNGLBINDATTRIBLOCATIONPROC, BindAttribLocation); OPENGL_PROC(PFNGLGETUNIFORMLOCATIONPROC, GetUniformLocation); OPENGL_PROC(PFNGLGETPROGRAMIVPROC, GetProgramiv); OPENGL_PROC(PFNGLGETPROGRAMINFOLOGPROC, GetProgramInfoLog); +OPENGL_PROC(PFNGLVERTEXATTRIB4BVPROC, VertexAttrib4bv); +OPENGL_PROC(PFNGLVERTEXATTRIB4UBVPROC, VertexAttrib4ubv); +OPENGL_PROC(PFNGLVERTEXATTRIB1SPROC, VertexAttrib1s); +OPENGL_PROC(PFNGLVERTEXATTRIB2SVPROC, VertexAttrib2sv); +OPENGL_PROC(PFNGLVERTEXATTRIB3SVPROC, VertexAttrib3sv); +OPENGL_PROC(PFNGLVERTEXATTRIB4SVPROC, VertexAttrib4sv); +OPENGL_PROC(PFNGLVERTEXATTRIB4IVPROC, VertexAttrib4iv); +OPENGL_PROC(PFNGLVERTEXATTRIB1FPROC, VertexAttrib1f); +OPENGL_PROC(PFNGLVERTEXATTRIB2FVPROC, VertexAttrib2fv); +OPENGL_PROC(PFNGLVERTEXATTRIB3FVPROC, VertexAttrib3fv); +OPENGL_PROC(PFNGLVERTEXATTRIB4FVPROC, VertexAttrib4fv); OPENGL_PROC(PFNGLVERTEXATTRIBPOINTERPROC, VertexAttribPointer); OPENGL_PROC(PFNGLENABLEVERTEXATTRIBARRAYPROC, EnableVertexAttribArray); OPENGL_PROC(PFNGLDISABLEVERTEXATTRIBARRAYPROC, DisableVertexAttribArray); @@ -39,11 +50,49 @@ OPENGL_PROC(PFNGLDELETEVERTEXARRAYSPROC, DeleteVertexArrays); OPENGL_PROC(PFNGLDEPTHRANGEFPROC, DepthRangef); OPENGL_PROC(PFNGLUNIFORM1IPROC, Uniform1i); OPENGL_PROC(PFNGLUNIFORM1FPROC, Uniform1f); +OPENGL_PROC(PFNGLUNIFORM1UIPROC, Uniform1ui); +OPENGL_PROC(PFNGLUNIFORM1IVPROC, Uniform1iv); +OPENGL_PROC(PFNGLUNIFORM1FVPROC, Uniform1fv); +OPENGL_PROC(PFNGLUNIFORM1UIVPROC, Uniform1uiv); +OPENGL_PROC(PFNGLUNIFORM2IPROC, Uniform2i); +OPENGL_PROC(PFNGLUNIFORM2FPROC, Uniform2f); +OPENGL_PROC(PFNGLUNIFORM2UIPROC, Uniform2ui); +OPENGL_PROC(PFNGLUNIFORM2IVPROC, Uniform2iv); +OPENGL_PROC(PFNGLUNIFORM2FVPROC, Uniform2fv); +OPENGL_PROC(PFNGLUNIFORM2UIVPROC, Uniform2uiv); +OPENGL_PROC(PFNGLUNIFORM3IPROC, Uniform3i); +OPENGL_PROC(PFNGLUNIFORM3FPROC, Uniform3f); +OPENGL_PROC(PFNGLUNIFORM3UIPROC, Uniform3ui); +OPENGL_PROC(PFNGLUNIFORM3IVPROC, Uniform3iv); +OPENGL_PROC(PFNGLUNIFORM3FVPROC, Uniform3fv); +OPENGL_PROC(PFNGLUNIFORM3UIVPROC, Uniform3uiv); +OPENGL_PROC(PFNGLUNIFORM4FPROC, Uniform4i); OPENGL_PROC(PFNGLUNIFORM4FPROC, Uniform4f); +OPENGL_PROC(PFNGLUNIFORM4UIPROC, Uniform4ui); +OPENGL_PROC(PFNGLUNIFORM4IVPROC, Uniform4iv); +OPENGL_PROC(PFNGLUNIFORM4FVPROC, Uniform4fv); +OPENGL_PROC(PFNGLUNIFORM4UIVPROC, Uniform4uiv); OPENGL_PROC(PFNGLPROGRAMUNIFORM1IPROC, ProgramUniform1i); OPENGL_PROC(PFNGLPROGRAMUNIFORM1FPROC, ProgramUniform1f); OPENGL_PROC(PFNGLPROGRAMUNIFORM4FPROC, ProgramUniform4f); OPENGL_PROC(PFNGLUNIFORMMATRIX4FVPROC, UniformMatrix4fv); OPENGL_PROC(PFNGLUSEPROGRAMPROC, UseProgram); OPENGL_PROC2(PFNWGLSWAPINTERVALEXTPROC, SwapInterval, wglSwapIntervalEXT); -OPENGL_PROC2(PFNGLDEPTHBOUNDSEXTPROC, DepthBounds, glDepthBoundsEXT); \ No newline at end of file +OPENGL_PROC2(PFNGLDEPTHBOUNDSEXTPROC, DepthBounds, glDepthBoundsEXT); +OPENGL_PROC(PFNGLSTENCILOPSEPARATEPROC, StencilOpSeparate); +OPENGL_PROC(PFNGLSTENCILFUNCSEPARATEPROC, StencilFuncSeparate); +OPENGL_PROC(PFNGLSTENCILMASKSEPARATEPROC, StencilMaskSeparate); +OPENGL_PROC(PFNGLCOMPRESSEDTEXIMAGE2DPROC, CompressedTexImage2D); +OPENGL_PROC(PFNGLGENERATEMIPMAPPROC, GenerateMipmap); +OPENGL_PROC(PFNGLBINDRENDERBUFFERPROC, BindRenderbuffer); +OPENGL_PROC(PFNGLDELETERENDERBUFFERSPROC, DeleteRenderbuffers); +OPENGL_PROC(PFNGLGENRENDERBUFFERSPROC, GenRenderbuffers); +OPENGL_PROC(PFNGLRENDERBUFFERSTORAGEPROC, RenderbufferStorage); +OPENGL_PROC(PFNGLBINDFRAMEBUFFERPROC, BindFramebuffer); +OPENGL_PROC(PFNGLDELETEFRAMEBUFFERSPROC, DeleteFramebuffers); +OPENGL_PROC(PFNGLGENFRAMEBUFFERSPROC, GenFramebuffers); +OPENGL_PROC(PFNGLFRAMEBUFFERTEXTURE1DPROC, FramebufferTexture1D); +OPENGL_PROC(PFNGLFRAMEBUFFERTEXTURE2DPROC, FramebufferTexture2D); +OPENGL_PROC(PFNGLFRAMEBUFFERTEXTURE3DPROC, FramebufferTexture3D); +OPENGL_PROC(PFNGLFRAMEBUFFERRENDERBUFFERPROC, FramebufferRenderbuffer); +OPENGL_PROC(PFNGLBLITFRAMEBUFFERPROC, BlitFramebuffer); \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/Program.cpp b/rpcs3/Emu/GS/GL/Program.cpp index 530031abcd..4730f33805 100644 --- a/rpcs3/Emu/GS/GL/Program.cpp +++ b/rpcs3/Emu/GS/GL/Program.cpp @@ -77,6 +77,7 @@ void Program::Create(const u32 vp, const u32 fp) void Program::UnUse() { id = 0; + m_locations.Clear(); } void Program::Use() @@ -87,14 +88,10 @@ void Program::Use() void Program::SetTex(u32 index) { - int loc = GetLocation(wxString::Format("tex%d", index)); - checkForGlError(wxString::Format("GetLocation(tex%d)", index)); + int loc = GetLocation(wxString::Format("tex%u", index)); + checkForGlError(wxString::Format("GetLocation(tex%u)", index)); glProgramUniform1i(id, loc, index); - GLenum err = glGetError(); - if(err != 0x502) - { - printGlError(err, wxString::Format("SetTex(%d - %d - %d)", id, index, loc)); - } + checkForGlError(wxString::Format("SetTex(%u - %d - %d)", id, index, loc)); } void Program::Delete() @@ -102,4 +99,5 @@ void Program::Delete() if(!IsCreated()) return; glDeleteProgram(id); id = 0; + m_locations.Clear(); } \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/ShaderParam.h b/rpcs3/Emu/GS/GL/ShaderParam.h index 553d4278a6..c0f91e38fb 100644 --- a/rpcs3/Emu/GS/GL/ShaderParam.h +++ b/rpcs3/Emu/GS/GL/ShaderParam.h @@ -95,9 +95,16 @@ struct ParamArray return wxEmptyString; } - wxString AddParam(wxString type, const wxString& name, const wxString& value) + bool HasParam(const ParamFlag flag, wxString type, const wxString& name) { - type = GetParamFlag(PARAM_CONST) + type; + type = GetParamFlag(flag) + type; + ParamType* t = SearchParam(type); + return t && t->SearchName(name); + } + + wxString AddParam(const ParamFlag flag, wxString type, const wxString& name, const wxString& value) + { + type = GetParamFlag(flag) + type; ParamType* t = SearchParam(type); if(t) @@ -107,7 +114,7 @@ struct ParamArray else { const u32 num = params.GetCount(); - params.Move(new ParamType(PARAM_CONST, type)); + params.Move(new ParamType(flag, type)); params[num].items.Move(new ParamItem(name, -1, value)); } diff --git a/rpcs3/Emu/GS/GL/VertexProgram.cpp b/rpcs3/Emu/GS/GL/VertexProgram.cpp index a5048f61ea..83cad2e739 100644 --- a/rpcs3/Emu/GS/GL/VertexProgram.cpp +++ b/rpcs3/Emu/GS/GL/VertexProgram.cpp @@ -40,8 +40,8 @@ wxString VertexDecompilerThread::GetDST(bool isSca) "gl_Position", "col0", "col1", "bfc0", "bfc1", - "fogc", - "gl_Pointsize", + "gl_ClipDistance[%d]", + "gl_ClipDistance[%d]", "tc0", "tc1", "tc2", "tc3", "tc4", "tc5", "tc6", "tc7" }; @@ -49,12 +49,12 @@ wxString VertexDecompilerThread::GetDST(bool isSca) switch(isSca ? 0x1f : d3.dst) { - case 0x0: case 0x6: + case 0x0: case 0x5: case 0x6: ret += reg_table[d3.dst]; break; case 0x1f: - ret += m_parr.AddParam(PARAM_NONE, "vec4", wxString::Format("tmp%d", isSca ? d3.sca_dst_tmp : d0.dst_tmp)); + ret += m_parr.AddParam(PARAM_NONE, "vec4", wxString::Format("tmp%u", isSca ? d3.sca_dst_tmp : d0.dst_tmp)); break; default: @@ -90,7 +90,7 @@ wxString VertexDecompilerThread::GetSRC(const u32 n, bool isSca) switch(src[n].reg_type) { case 1: //temp - ret += m_parr.AddParam(PARAM_NONE, "vec4", wxString::Format("tmp%d", src[n].tmp_src)); + ret += m_parr.AddParam(PARAM_NONE, "vec4", wxString::Format("tmp%u", src[n].tmp_src)); break; case 2: //input if(d1.input_src < WXSIZEOF(reg_table)) @@ -104,18 +104,16 @@ wxString VertexDecompilerThread::GetSRC(const u32 n, bool isSca) } break; case 3: //const - ret += m_parr.AddParam(PARAM_UNIFORM, "vec4", wxString::Format("vc%d", d1.const_src)); + ret += m_parr.AddParam(PARAM_UNIFORM, "vec4", wxString::Format("vc%u", d1.const_src)); break; default: - ConLog.Error("Bad src%d reg type: %d", n, src[n].reg_type); + ConLog.Error("Bad src%u reg type: %d", n, src[n].reg_type); Emu.Pause(); break; } - static const char f[4] = {'x', 'y', 'z', 'w'}; - - wxString swizzle = wxEmptyString; + static const wxString f = "xyzw"; if (isSca) { @@ -123,16 +121,19 @@ wxString VertexDecompilerThread::GetSRC(const u32 n, bool isSca) assert(src[n].swz_z == src[n].swz_w); assert(src[n].swz_x == src[n].swz_z); - ret += "." + f[src[n].swz_x]; + ret += '.'; + ret += f[src[n].swz_x]; } else { + 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(swizzle != "xyzw") ret += "." + swizzle; + if(swizzle != f) ret += '.' + swizzle; } bool abs; @@ -164,7 +165,7 @@ void VertexDecompilerThread::AddCode(bool is_sca, wxString code, bool src_mask) if(d0.cond != (lt | gt | eq)) { - if(d0.cond & (lt | gt)) + if((d0.cond & (lt | gt)) == (lt | gt)) { cond = "!="; } @@ -177,7 +178,7 @@ void VertexDecompilerThread::AddCode(bool is_sca, wxString code, bool src_mask) if(d0.cond & eq) cond += "="; } - ConLog.Error("cond! %d (%d %s %d %d)", d0.cond, d0.dst_tmp, cond, d1.input_src, d1.const_src); + //ConLog.Error("cond! %d (%d %s %d %d)", d0.cond, d0.dst_tmp, cond, d1.input_src, d1.const_src); cond = wxString::Format("if(tmp%d.x %s 0) ", d0.dst_tmp, cond); } @@ -188,7 +189,28 @@ void VertexDecompilerThread::AddCode(bool is_sca, wxString code, bool src_mask) value = "clamp(" + value + ", 0.0, 1.0)"; } - code = cond + GetDST(is_sca) + GetMask(is_sca) + " = " + value; + wxString dest; + if(d3.dst == 5 || d3.dst == 6) + { + int num = d3.dst == 5 ? 0 : 3; + + if(d3.vec_writemask_x) + { + ConLog.Error("Bad clip mask."); + } + + //if(d3.vec_writemask_y) num += 0; + if(d3.vec_writemask_z) num += 1; + else if(d3.vec_writemask_w) num += 2; + + dest = wxString::Format(GetDST(is_sca), num); + } + else + { + dest = GetDST(is_sca) + GetMask(is_sca); + } + + code = cond + dest + " = " + value; main += "\t" + code + ";\n"; } @@ -198,9 +220,9 @@ void VertexDecompilerThread::AddVecCode(const wxString& code, bool src_mask) AddCode(false, code, src_mask); } -void VertexDecompilerThread::AddScaCode(const wxString& code, bool src_mask) +void VertexDecompilerThread::AddScaCode(const wxString& code) { - AddCode(true, code, src_mask); + AddCode(true, code, false); } wxString VertexDecompilerThread::BuildCode() @@ -273,10 +295,10 @@ void VertexDecompilerThread::Task() case 0x02: AddVecCode("(" + GetSRC(0) + " * " + GetSRC(1) + ")"); break; //MUL case 0x03: AddVecCode("(" + GetSRC(0) + " + " + GetSRC(2) + ")"); break; //ADD case 0x04: AddVecCode("(" + GetSRC(0) + " * " + GetSRC(1) + " + " + GetSRC(2) + ")"); break; //MAD - case 0x05: AddVecCode("dot(" + GetSRC(0) + ".xyz, " + GetSRC(1) + ".xyz)", false); break; //DP3 - case 0x06: AddVecCode("(dot(" + GetSRC(0) + ".xyz, " + GetSRC(1) + ".xyz) + " + GetSRC(1) + ".w)", false); break; //DPH - case 0x07: AddVecCode("dot(" + GetSRC(0) + ", " + GetSRC(1) + ")", false); break; //DP4 - case 0x08: AddVecCode("distance(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //DST + case 0x05: AddVecCode("vec4(dot(" + GetSRC(0) + ".xyz, " + GetSRC(1) + ".xyz)).xxxx"); break; //DP3 + case 0x06: AddVecCode("vec4(dot(vec4(" + GetSRC(0) + ".xyz, 1), " + GetSRC(1) + ")).xxxx"); break; //DPH + case 0x07: AddVecCode("vec4(dot(" + GetSRC(0) + ", " + GetSRC(1) + ")).xxxx"); break; //DP4 + case 0x08: AddVecCode("vec4(distance(" + GetSRC(0) + ", " + GetSRC(1) + ")).xxxx"); break; //DST case 0x09: AddVecCode("min(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //MIN case 0x0a: AddVecCode("max(" + GetSRC(0) + ", " + GetSRC(1) + ")"); break; //MAX case 0x0b: AddVecCode("vec4(lessThan(" + GetSRC(0) + ", " + GetSRC(1) + "))"); break; //SLT @@ -399,8 +421,7 @@ void VertexProgram::Delete() } VertexData::VertexData() - : index(0) - , frequency(0) + : frequency(0) , stride(0) , size(0) , type(0) @@ -411,7 +432,6 @@ VertexData::VertexData() void VertexData::Reset() { - index = 0; frequency = 0; stride = 0; size = 0; @@ -422,7 +442,10 @@ void VertexData::Reset() void VertexData::Load(u32 start, u32 count) { + if(!addr) return; + const u32 tsize = GetTypeSize(); + data.SetCount((start + count) * tsize * size); for(u32 i=start; i data; diff --git a/rpcs3/Emu/GS/GSManager.cpp b/rpcs3/Emu/GS/GSManager.cpp index 7f9b2f1e07..456aa9125e 100644 --- a/rpcs3/Emu/GS/GSManager.cpp +++ b/rpcs3/Emu/GS/GSManager.cpp @@ -8,7 +8,7 @@ BEGIN_EVENT_TABLE(GSFrame, wxFrame) EVT_SIZE(GSFrame::OnSize) END_EVENT_TABLE() -GSManager::GSManager() : m_render(NULL) +GSManager::GSManager() : m_render(nullptr) { } diff --git a/rpcs3/Emu/GS/GSRender.cpp b/rpcs3/Emu/GS/GSRender.cpp index 3055b66733..225ae860de 100644 --- a/rpcs3/Emu/GS/GSRender.cpp +++ b/rpcs3/Emu/GS/GSRender.cpp @@ -78,9 +78,10 @@ void GSFrame::SetSize(int width, int height) GSRender::GSRender() - : m_ctrl(NULL) + : m_ctrl(nullptr) , m_flip_status(0) , m_flip_mode(CELL_GCM_DISPLAY_VSYNC) + , m_main_mem_addr(0) { } @@ -88,9 +89,11 @@ u32 GSRender::GetAddress(u32 offset, u8 location) { switch(location) { - case CELL_GCM_LOCATION_LOCAL: return Memory.RSXFBMem.GetStartAddr() + offset; - case CELL_GCM_LOCATION_MAIN: return offset; + case CELL_GCM_LOCATION_LOCAL: return m_local_mem_addr + offset; + case CELL_GCM_LOCATION_MAIN: return m_main_mem_addr + offset; } + + ConLog.Error("GetAddress(offset=0x%x, location=0x%x", location); return 0; } diff --git a/rpcs3/Emu/GS/GSRender.h b/rpcs3/Emu/GS/GSRender.h index d32220163f..8c79740f2e 100644 --- a/rpcs3/Emu/GS/GSRender.h +++ b/rpcs3/Emu/GS/GSRender.h @@ -1,6 +1,7 @@ #pragma once #include "Emu/GS/GCM.h" #include "Emu/SysCalls/Callback.h" +#include "rpcs3.h" enum Method { @@ -39,9 +40,109 @@ private: DECLARE_EVENT_TABLE(); }; +struct CellGcmSurface +{ + u8 type; + u8 antialias; + + u8 color_format; + u8 color_target; + u8 color_location[4]; + u32 color_offset[4]; + u32 color_pitch[4]; + + u8 depth_format; + u8 depth_location; + u16 pad; + u32 depth_offset; + u32 depth_pitch; + + u16 width; + u16 height; + u16 x; + u16 y; +}; + +struct CellGcmReportData +{ + u64 timer; + u32 value; + u32 pad; +}; + +struct CellGcmZcullInfo +{ + u32 region; + u32 size; + u32 start; + u32 offset; + u32 status0; + u32 status1; +}; + +struct CellGcmTileInfo +{ + u32 tile; + u32 limit; + u32 pitch; + u32 format; +}; + +struct GcmZcullInfo +{ + u32 m_offset; + u32 m_width; + u32 m_height; + u32 m_cullStart; + u32 m_zFormat; + u32 m_aaFormat; + u32 m_zCullDir; + u32 m_zCullFormat; + u32 m_sFunc; + u32 m_sRef; + u32 m_sMask; + bool m_binded; + + GcmZcullInfo() + { + memset(this, 0, sizeof(*this)); + } +}; + +struct GcmTileInfo +{ + u8 m_location; + u32 m_offset; + u32 m_size; + u32 m_pitch; + u8 m_comp; + u16 m_base; + u8 m_bank; + bool m_binded; + + GcmTileInfo() + { + memset(this, 0, sizeof(*this)); + } + + CellGcmTileInfo Pack() + { + CellGcmTileInfo ret; + + re(ret.tile, (m_location + 1) | (m_bank << 4) | ((m_offset / 0x10000) << 16) | (m_location << 31)); + re(ret.limit, ((m_offset + m_size - 1) / 0x10000) << 16 | (m_location << 31)); + re(ret.pitch, (m_pitch / 0x100) << 8); + re(ret.format, m_base | ((m_base + ((m_size - 1) / 0x10000)) << 13) | (m_comp << 26) | (1 << 30)); + + return ret; + } +}; + +static const int g_tiles_count = 15; + struct GSRender { - u32 m_ioAddress, m_ioSize, m_ctrlAddress, m_localAddress; + u32 m_ioAddress, m_ioSize, m_ctrlAddress; CellGcmControl* m_ctrl; wxCriticalSection m_cs_main; wxSemaphore m_sem_flush; @@ -51,9 +152,18 @@ struct GSRender volatile bool m_draw; Callback m_flip_handler; + GcmTileInfo m_tiles[g_tiles_count]; + + u32 m_tiles_addr; + u32 m_zculls_addr; u32 m_gcm_buffers_addr; u32 m_gcm_buffers_count; u32 m_gcm_current_buffer; + u32 m_ctxt_addr; + u32 m_report_main_addr; + + u32 m_local_mem_addr, m_main_mem_addr; + Array m_main_mem_info; GSRender(); diff --git a/rpcs3/Emu/GS/Null/NullGSRender.h b/rpcs3/Emu/GS/Null/NullGSRender.h index d9c9443b1a..116e78cbbd 100644 --- a/rpcs3/Emu/GS/Null/NullGSRender.h +++ b/rpcs3/Emu/GS/Null/NullGSRender.h @@ -71,7 +71,7 @@ private: m_ioAddress = ioAddress; m_ctrlAddress = ctrlAddress; m_ioSize = ioSize; - m_localAddress = localAddress; + m_local_mem_addr = localAddress; m_ctrl = (CellGcmControl*)Memory.GetMemFromAddr(m_ctrlAddress); (m_rsx_thread = new NullRSXThread(this))->Start(); diff --git a/rpcs3/Emu/GS/RSXThread.h b/rpcs3/Emu/GS/RSXThread.h index 4b877f5990..28a61a0898 100644 --- a/rpcs3/Emu/GS/RSXThread.h +++ b/rpcs3/Emu/GS/RSXThread.h @@ -3,7 +3,7 @@ class ExecRSXCMDdata { -protected: +public: bool m_set_color_mask; bool m_color_mask_r; bool m_color_mask_g; @@ -21,6 +21,12 @@ protected: bool m_set_blend; bool m_set_depth_bounds_test; bool m_depth_test_enable; + bool m_set_logic_op; + bool m_set_cull_face; + bool m_set_dither; + bool m_set_stencil_test; + bool m_set_line_smooth; + bool m_set_poly_smooth; bool m_set_viewport_horizontal; bool m_set_viewport_vertical; @@ -50,11 +56,6 @@ protected: u16 m_blend_dfactor_rgb; u16 m_blend_dfactor_alpha; - bool m_set_logic_op; - bool m_set_cull_face; - bool m_set_dither; - bool m_set_stencil_test; - bool m_set_stencil_mask; u32 m_stencil_mask; @@ -76,6 +77,29 @@ protected: bool m_set_stencil_zpass; u32 m_stencil_zpass; + bool m_set_two_sided_stencil_test_enable; + + bool m_set_back_stencil_mask; + u32 m_back_stencil_mask; + + bool m_set_back_stencil_func; + u32 m_back_stencil_func; + + bool m_set_back_stencil_func_ref; + u32 m_back_stencil_func_ref; + + bool m_set_back_stencil_func_mask; + u32 m_back_stencil_func_mask; + + bool m_set_back_stencil_fail; + u32 m_back_stencil_fail; + + bool m_set_back_stencil_zfail; + u32 m_back_stencil_zfail; + + bool m_set_back_stencil_zpass; + u32 m_back_stencil_zpass; + bool m_set_blend_equation; u16 m_blend_equation_rgb; u16 m_blend_equation_alpha; @@ -83,8 +107,6 @@ protected: bool m_set_depth_mask; u32 m_depth_mask; - bool m_set_line_smooth; - bool m_set_line_width; u32 m_line_width; @@ -97,6 +119,85 @@ protected: u8 m_blend_color_b; u8 m_blend_color_a; + bool m_set_clear_color; + u8 m_clear_color_r; + u8 m_clear_color_g; + u8 m_clear_color_b; + u8 m_clear_color_a; + + u32 m_context_dma_img_src; + u32 m_context_dma_img_dst; + u32 m_dst_offset; + u32 m_color_format; + u16 m_color_format_src_pitch; + u16 m_color_format_dst_pitch; + + u32 m_color_conv; + u32 m_color_conv_fmt; + u32 m_color_conv_op; + u16 m_color_conv_in_x; + u16 m_color_conv_in_y; + u16 m_color_conv_in_w; + u16 m_color_conv_in_h; + u16 m_color_conv_out_x; + u16 m_color_conv_out_y; + u16 m_color_conv_out_w; + u16 m_color_conv_out_h; + u32 m_color_conv_dsdx; + u32 m_color_conv_dtdy; + + bool m_set_semaphore_offset; + u32 m_semaphore_offset; + + bool m_set_fog_mode; + u32 m_fog_mode; + + bool m_set_fog_params; + float m_fog_param0; + float m_fog_param1; + + bool m_set_clip_plane; + u32 m_clip_plane_0; + u32 m_clip_plane_1; + u32 m_clip_plane_2; + u32 m_clip_plane_3; + u32 m_clip_plane_4; + u32 m_clip_plane_5; + + bool m_set_surface_format; + u8 m_surface_color_format; + u8 m_surface_depth_format; + u8 m_surface_type; + u8 m_surface_antialias; + u8 m_surface_width; + u8 m_surface_height; + u32 m_surface_pitch_a; + u32 m_surface_offset_a; + u32 m_surface_offset_z; + u32 m_surface_offset_b; + u32 m_surface_pitch_b; + + bool m_set_context_dma_color_a; + u32 m_context_dma_color_a; + + bool m_set_context_dma_color_b; + u32 m_context_dma_color_b; + + bool m_set_context_dma_color_c; + u32 m_context_dma_color_c; + + bool m_set_context_dma_z; + u32 m_context_dma_z; + + bool m_set_surface_clip_horizontal; + u16 m_surface_clip_x; + u16 m_surface_clip_w; + bool m_set_surface_clip_vertical; + u16 m_surface_clip_y; + u16 m_surface_clip_h; + + u8 m_begin_end; + public: ExecRSXCMDdata() { @@ -131,12 +232,34 @@ public: m_set_stencil_fail = false; m_set_stencil_zfail = false; m_set_stencil_zpass = false; + m_set_two_sided_stencil_test_enable = false; + m_set_back_stencil_mask = false; + m_set_back_stencil_func = false; + m_set_back_stencil_func_ref = false; + m_set_back_stencil_func_mask = false; + m_set_back_stencil_fail = false; + m_set_back_stencil_zfail = false; + m_set_back_stencil_zpass = false; m_set_blend_equation = false; m_set_depth_mask = false; m_set_line_smooth = false; + m_set_poly_smooth = false; m_set_line_width = false; m_set_shade_mode = false; m_set_blend_color = false; + m_set_clear_color = false; + m_set_semaphore_offset = false; + m_set_fog_mode = false; + m_set_fog_params = false; + m_set_clip_plane = false; + m_set_surface_format = false; + m_set_context_dma_color_a = false; + m_set_context_dma_color_b = false; + m_set_context_dma_color_c = false; + m_set_context_dma_z = false; + m_set_surface_clip_horizontal = false; + m_set_surface_clip_vertical = false; + m_begin_end = 0; } virtual void ExecCMD()=0; diff --git a/rpcs3/Emu/Io/Pad.cpp b/rpcs3/Emu/Io/Pad.cpp index 743b0166d1..73f2d216ed 100644 --- a/rpcs3/Emu/Io/Pad.cpp +++ b/rpcs3/Emu/Io/Pad.cpp @@ -4,7 +4,7 @@ #include "Windows/WindowsPadHandler.h" PadManager::PadManager() - : m_pad_handler(NULL) + : m_pad_handler(nullptr) , m_inited(false) { } @@ -32,7 +32,7 @@ void PadManager::Init(const u32 max_connect) void PadManager::Close() { if(m_pad_handler) m_pad_handler->Close(); - m_pad_handler = NULL; + m_pad_handler = nullptr; m_inited = false; } \ No newline at end of file diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 30c7852d48..2060e01339 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -90,9 +90,11 @@ struct Button u32 m_keyCode; u32 m_outKeyCode; bool m_pressed; + bool m_flush; Button(u32 offset, u32 keyCode, u32 outKeyCode) : m_pressed(false) + , m_flush(false) , m_offset(offset) , m_keyCode(keyCode) , m_outKeyCode(outKeyCode) @@ -169,8 +171,8 @@ struct Pad struct PadInfo { u32 max_connect; - u32 now_connect; - u32 system_info; + u32 now_connect; + u32 system_info; }; class PadHandlerBase @@ -191,7 +193,14 @@ public: { Button& button = GetButtons(p).Get(b); if(button.m_keyCode != code) continue; - button.m_pressed = pressed; + if(button.m_pressed && !pressed) + { + button.m_flush = true; + } + else + { + button.m_pressed = pressed; + } } } } diff --git a/rpcs3/Emu/Memory/MemoryBlock.h b/rpcs3/Emu/Memory/MemoryBlock.h index 63687f4a38..94d6739090 100644 --- a/rpcs3/Emu/Memory/MemoryBlock.h +++ b/rpcs3/Emu/Memory/MemoryBlock.h @@ -1,14 +1,27 @@ #pragma once -struct MemBlockInfo +struct MemInfo { u64 addr; u32 size; + + MemInfo(u64 _addr, u32 _size) + : addr(_addr) + , size(_size) + { + } + + MemInfo() + { + } +}; + +struct MemBlockInfo : public MemInfo +{ void* mem; MemBlockInfo(u64 _addr, u32 _size) - : addr(_addr) - , size(_size) + : MemInfo(_addr, _size) , mem(malloc(_size)) { if(!mem) diff --git a/rpcs3/Emu/SysCalls/FuncList.cpp b/rpcs3/Emu/SysCalls/FuncList.cpp index 83557bc3e3..e80e866100 100644 --- a/rpcs3/Emu/SysCalls/FuncList.cpp +++ b/rpcs3/Emu/SysCalls/FuncList.cpp @@ -286,19 +286,19 @@ s64 SysCalls::DoFunc(const u32 id) case 0x72cc6cf7: FUNC_LOG_ERROR("TODO: cellGameGetHomeDataImportPath"); case 0x94e9f81d: FUNC_LOG_ERROR("TODO: cellGameGetHomeLaunchOptionPath"); case 0xf6acd0bc: FUNC_LOG_ERROR("TODO: cellGameGetBootGameInfo"); - case 0x055bd74d: return cellGcmGetTiledPitchSize(SC_ARGS_1); //FUNC_LOG_ERROR("TODO: cellGcmGetTiledPitchSize"); + case 0x055bd74d: FUNC_LOG_ERROR("TODO: cellGcmGetTiledPitchSize"); case 0x06edea9e: FUNC_LOG_ERROR("TODO: cellGcmSetUserHandler"); case 0x0a862772: FUNC_LOG_ERROR("TODO: cellGcmSetQueueHandler"); case 0x0b4b62d5: FUNC_LOG_ERROR("TODO: cellGcmSetPrepareFlip"); case 0x0e6b0dae: FUNC_LOG_ERROR("TODO: cellGcmGetDisplayInfo"); case 0x107bf3a1: FUNC_LOG_ERROR("TODO: cellGcmInitCursor"); - case 0x15bae46b: return cellGcmInit(SC_ARGS_4);//FUNC_LOG_ERROR("TODO: _cellGcmInitBody"); + case 0x15bae46b: FUNC_LOG_ERROR("TODO: _cellGcmInitBody"); case 0x172c3197: FUNC_LOG_ERROR("TODO: cellGcmSetDefaultCommandBufferAndSegmentWordSize"); case 0x1a0de550: FUNC_LOG_ERROR("TODO: cellGcmSetCursorPosition"); case 0x1bd633f8: FUNC_LOG_ERROR("TODO: _cellGcmFunc3"); case 0x1f61b3ff: FUNC_LOG_ERROR("TODO: cellGcmDumpGraphicsError"); - case 0x21397818: return cellGcmFlush(SC_ARGS_2); //FUNC_LOG_ERROR("TODO: _cellGcmSetFlipCommand"); - case 0x21ac3697: return cellGcmAddressToOffset(SC_ARGS_2); //FUNC_LOG_ERROR("TODO: cellGcmAddressToOffset"); + case 0x21397818: FUNC_LOG_ERROR("TODO: _cellGcmSetFlipCommand"); + case 0x21ac3697: FUNC_LOG_ERROR("TODO: cellGcmAddressToOffset"); case 0x21cee035: FUNC_LOG_ERROR("TODO: cellGcmGetNotifyDataAddress"); case 0x23ae55a3: FUNC_LOG_ERROR("TODO: cellGcmGetLastSecondVTime"); case 0x25b40ab4: FUNC_LOG_ERROR("TODO: cellGcmSortRemapEaIoAddress"); @@ -309,7 +309,7 @@ s64 SysCalls::DoFunc(const u32 id) case 0x3a33c1fd: FUNC_LOG_ERROR("TODO: _cellGcmFunc15"); case 0x3b9bd5bd: FUNC_LOG_ERROR("TODO: cellGcmUnreserveIoMapSize"); case 0x4524cccd: FUNC_LOG_ERROR("TODO: cellGcmBindTile"); - case 0x4ae8d215: return cellGcmSetFlipMode(SC_ARGS_1); //FUNC_LOG_ERROR("TODO: cellGcmSetFlipMode"); + case 0x4ae8d215: FUNC_LOG_ERROR("TODO: cellGcmSetFlipMode"); case 0x4d7ce993: FUNC_LOG_ERROR("TODO: cellGcmSetSecondVFrequency"); case 0x51c9d62b: FUNC_LOG_ERROR("TODO: cellGcmSetDebugOutputLevel"); case 0x527c6439: FUNC_LOG_ERROR("TODO: cellGcmTerminate"); @@ -318,13 +318,13 @@ s64 SysCalls::DoFunc(const u32 id) case 0x5f909b17: FUNC_LOG_ERROR("TODO: _cellGcmFunc1"); case 0x626e8518: FUNC_LOG_ERROR("TODO: cellGcmMapEaIoAddressWithFlags"); case 0x63387071: FUNC_LOG_ERROR("TODO: cellGcmGetLastFlipTime"); - case 0x63441cb4: return cellGcmMapEaIoAddress(SC_ARGS_3); //FUNC_LOG_ERROR("TODO: cellGcmMapEaIoAddress"); + case 0x63441cb4: FUNC_LOG_ERROR("TODO: cellGcmMapEaIoAddress"); case 0x657571f7: FUNC_LOG_ERROR("TODO: cellGcmGetTileInfo"); case 0x661fe266: FUNC_LOG_ERROR("TODO: _cellGcmFunc12"); case 0x688b8ac9: FUNC_LOG_ERROR("TODO: _cellGcmFunc38"); case 0x69c6cc82: FUNC_LOG_ERROR("TODO: cellGcmSetCursorDisable"); case 0x723bbc7e: FUNC_LOG_ERROR("TODO: cellGcmGetVBlankCount"); - case 0x72a577ce: return cellGcmGetFlipStatus(); //FUNC_LOG_ERROR("TODO: cellGcmGetFlipStatus"); + case 0x72a577ce: FUNC_LOG_ERROR("TODO: cellGcmGetFlipStatus"); case 0x7fc034bc: FUNC_LOG_ERROR("TODO: _cellGcmFunc4"); case 0x8572bce2: FUNC_LOG_ERROR("TODO: cellGcmGetReportDataAddressLocation"); case 0x8bde5ebf: FUNC_LOG_ERROR("TODO: cellGcmSetUserCommand"); @@ -336,17 +336,17 @@ s64 SysCalls::DoFunc(const u32 id) case 0x9a0159af: FUNC_LOG_ERROR("TODO: cellGcmGetReportDataAddress"); case 0x9ba451e4: FUNC_LOG_ERROR("TODO: cellGcmSetDefaultFifoSize"); case 0x9dc04436: FUNC_LOG_ERROR("TODO: cellGcmBindZcull"); - case 0xa114ec67: return cellGcmMapMainMemory(SC_ARGS_3); //FUNC_LOG_ERROR("TODO: cellGcmMapMainMemory"); + case 0xa114ec67: FUNC_LOG_ERROR("TODO: cellGcmMapMainMemory"); case 0xa41ef7e8: FUNC_LOG_ERROR("TODO: cellGcmSetFlipHandler"); case 0xa47c09ff: FUNC_LOG_ERROR("TODO: cellGcmSetFlipStatus"); - case 0xa53d12ae: return cellGcmSetDisplayBuffer(SC_ARGS_5); //FUNC_LOG_ERROR("TODO: cellGcmSetDisplayBuffer"); - case 0xa547adde: return cellGcmGetControlRegister();//FUNC_LOG_ERROR("TODO: cellGcmGetControlRegister"); + case 0xa53d12ae: FUNC_LOG_ERROR("TODO: cellGcmSetDisplayBuffer"); + case 0xa547adde: FUNC_LOG_ERROR("TODO: cellGcmGetControlRegister"); case 0xa6b180ac: FUNC_LOG_ERROR("TODO: cellGcmGetReportDataLocation"); case 0xa75640e8: FUNC_LOG_ERROR("TODO: cellGcmUnbindZcull"); case 0xa7ede268: FUNC_LOG_ERROR("TODO: cellGcmReserveIoMapSize"); case 0xa91b0402: FUNC_LOG_ERROR("TODO: cellGcmSetVBlankHandler"); case 0xacee8542: FUNC_LOG_ERROR("TODO: cellGcmSetFlipImmediate"); - case 0xb2e761d4: return cellGcmResetFlipStatus(); //FUNC_LOG_ERROR("TODO: cellGcmResetFlipStatus"); + case 0xb2e761d4: FUNC_LOG_ERROR("TODO: cellGcmResetFlipStatus"); case 0xbb42a9dd: FUNC_LOG_ERROR("TODO: _cellGcmFunc13"); case 0xbc982946: FUNC_LOG_ERROR("TODO: cellGcmSetDefaultCommandBuffer"); case 0xbd100dbc: FUNC_LOG_ERROR("TODO: cellGcmSetTileInfo"); @@ -356,7 +356,7 @@ s64 SysCalls::DoFunc(const u32 id) case 0xc8f3bd09: FUNC_LOG_ERROR("TODO: cellGcmGetCurrentField"); case 0xcaabd992: FUNC_LOG_ERROR("TODO: cellGcmInitDefaultFifoMode"); case 0xd01b570d: FUNC_LOG_ERROR("TODO: cellGcmSetGraphicsHandler"); - case 0xd0b1d189: cellGcmSetTile(SC_ARGS_8); return SC_ARGS_1;//FUNC_LOG_ERROR("TODO: cellGcmSetTile"); + case 0xd0b1d189: ConLog.Error("TODO: cellGcmSetTile"); return SC_ARGS_1; case 0xd34a420d: ConLog.Error("TODO: cellGcmSetZcull"); return SC_ARGS_1; case 0xd8f88e1a: FUNC_LOG_ERROR("TODO: _cellGcmSetFlipCommandWithWaitLabel"); case 0xd9a0a879: FUNC_LOG_ERROR("TODO: cellGcmGetZcullInfo"); @@ -366,10 +366,10 @@ s64 SysCalls::DoFunc(const u32 id) case 0xdc09357e: FUNC_LOG_ERROR("TODO: cellGcmSetFlip"); case 0xdc494430: FUNC_LOG_ERROR("TODO: cellGcmSetSecondVHandler"); case 0xdf6476bd: FUNC_LOG_ERROR("TODO: cellGcmSetWaitFlipUnsafe"); - case 0xe315a0b2: return cellGcmGetConfiguration(SC_ARGS_1); //FUNC_LOG_ERROR("TODO: cellGcmGetConfiguration"); + case 0xe315a0b2: FUNC_LOG_ERROR("TODO: cellGcmGetConfiguration"); case 0xe44874f3: FUNC_LOG_ERROR("TODO: cellGcmSysGetLastVBlankTime"); case 0xefd00f54: FUNC_LOG_ERROR("TODO: cellGcmUnmapEaIoAddress"); - case 0xf80196c1: return cellGcmGetLabelAddress(SC_ARGS_1); //FUNC_LOG_ERROR("TODO: cellGcmGetLabelAddress"); + case 0xf80196c1: FUNC_LOG_ERROR("TODO: cellGcmGetLabelAddress"); case 0xf9bfdc72: FUNC_LOG_ERROR("TODO: cellGcmSetCursorImageOffset"); case 0xfb81c03e: FUNC_LOG_ERROR("TODO: cellGcmGetMaxIoMapSize"); case 0xfce9e764: FUNC_LOG_ERROR("TODO: cellGcmInitSystemMode"); diff --git a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp index 6ee40bfdd6..fa8b4a5695 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp @@ -6,16 +6,351 @@ void cellGcmSys_init(); Module cellGcmSys(0x0010, cellGcmSys_init); +enum +{ + CELL_GCM_ERROR_FAILURE = 0x802100ff, + CELL_GCM_ERROR_INVALID_ENUM = 0x80210002, + CELL_GCM_ERROR_INVALID_VALUE = 0x80210003, + CELL_GCM_ERROR_INVALID_ALIGNMENT = 0x80210004, +}; + +CellGcmConfig current_config; +CellGcmContextData current_context; +gcmInfo gcm_info; +struct gcm_offset +{ + u16 ea; + u16 offset; +}; + +u32 map_offset_addr = 0; +u32 map_offset_pos = 0; + +int cellGcmMapMainMemory(u32 address, u32 size, u32 offset_addr) +{ + 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; + + Emu.GetGSManager().GetRender().m_main_mem_addr = Emu.GetGSManager().GetRender().m_ioAddress; + + u32 offset = address - Emu.GetGSManager().GetRender().m_main_mem_addr; + Emu.GetGSManager().GetRender().m_main_mem_info.AddCpy(MemInfo(address, size)); + + Memory.Write32(offset_addr, offset); + + 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(); + + map_offset_addr = 0; + map_offset_pos = 0; + current_config.ioSize = re32(ioSize); + current_config.ioAddress = re32(ioAddress); + current_config.localSize = re32(local_size); + current_config.localAddress = re32(local_addr); + current_config.memoryFrequency = re32(650000000); + current_config.coreFrequency = re32(500000000); + + Memory.RSXFBMem.Alloc(local_size); + Memory.RSXCMDMem.Alloc(cmdSize); + + u32 ctx_begin = ioAddress/* + 0x1000*/; + u32 ctx_size = 0x6ffc; + current_context.begin = re(ctx_begin); + current_context.end = re(ctx_begin + ctx_size); + current_context.current = current_context.begin; + current_context.callback = re32(Emu.GetRSXCallback() - 4); + + gcm_info.context_addr = Memory.MainMem.Alloc(0x1000); + gcm_info.control_addr = gcm_info.context_addr + 0x40; + + Memory.WriteData(gcm_info.context_addr, current_context); + Memory.Write32(context_addr, gcm_info.context_addr); + + CellGcmControl& ctrl = (CellGcmControl&)Memory[gcm_info.control_addr]; + ctrl.put = 0; + ctrl.get = 0; + ctrl.ref = -1; + + auto& render = Emu.GetGSManager().GetRender(); + render.m_ctxt_addr = context_addr; + render.m_gcm_buffers_addr = Memory.Alloc(sizeof(gcmBuffer) * 8, sizeof(gcmBuffer)); + render.m_zculls_addr = Memory.Alloc(sizeof(CellGcmZcullInfo) * 8, sizeof(CellGcmZcullInfo)); + render.m_tiles_addr = Memory.Alloc(sizeof(CellGcmTileInfo) * 15, sizeof(CellGcmTileInfo)); + render.m_gcm_buffers_count = 0; + render.m_gcm_current_buffer = 0; + render.m_main_mem_info.Clear(); + render.m_main_mem_addr = 0; + render.Init(ctx_begin, ctx_size, gcm_info.control_addr, local_addr); + + 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(u32 address, u32 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; + + if(!map_offset_addr) + { + map_offset_addr = Memory.Alloc(4*50, 4); + } + + u32 sa; + bool is_main_mem = false; + const auto& main_mem_info = Emu.GetGSManager().GetRender().m_main_mem_info; + for(u32 i=0; i= main_mem_info[i].addr && address < main_mem_info[i].addr + main_mem_info[i].size) + { + is_main_mem = true; + break; + } + } + + if(is_main_mem) + { + //main + sa = Emu.GetGSManager().GetRender().m_main_mem_addr; + //ConLog.Warning("cellGcmAddressToOffset: main memory: offset = 0x%x - 0x%x = 0x%x", address, sa, address - sa); + } + else if(Memory.RSXFBMem.IsMyAddress(address)) + { + //local + sa = Emu.GetGSManager().GetRender().m_local_mem_addr; + //ConLog.Warning("cellGcmAddressToOffset: local memory: offset = 0x%x - 0x%x = 0x%x", address, sa, address - sa); + } + else + { + //io + sa = Emu.GetGSManager().GetRender().m_ioAddress; + //ConLog.Warning("cellGcmAddressToOffset: io memory: offset = 0x%x - 0x%x = 0x%x", address, sa, address - sa); + } + + u32 offset = address - sa; + //Memory.Write16(map_offset_addr + map_offset_pos + 0, ea); + //Memory.Write16(map_offset_addr + map_offset_pos + 2, offset); + //map_offset_pos += 4; + + Memory.Write32(offset_addr, offset); + + return CELL_OK; +} + +int cellGcmSetDisplayBuffer(u32 id, u32 offset, u32 pitch, u32 width, u32 height) +{ + 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 > 7) return CELL_EINVAL; + + gcmBuffer* buffers = (gcmBuffer*)Memory.GetMemFromAddr(Emu.GetGSManager().GetRender().m_gcm_buffers_addr); + + buffers[id].offset = re(offset); + buffers[id].pitch = re(pitch); + buffers[id].width = re(width); + buffers[id].height = re(height); + + if(id + 1 > Emu.GetGSManager().GetRender().m_gcm_buffers_count) + { + Emu.GetGSManager().GetRender().m_gcm_buffers_count = id + 1; + } + + return CELL_OK; +} + +u32 cellGcmGetLabelAddress(u8 index) +{ + cellGcmSys.Log("cellGcmGetLabelAddress(index=%d)", index); + return Memory.RSXCMDMem.GetStartAddr() + 0x10 * index; +} + +u32 cellGcmGetControlRegister() +{ + cellGcmSys.Log("cellGcmGetControlRegister()"); + + return gcm_info.control_addr; +} + +int cellGcmSetPrepareFlip(u32 ctx, u32 id) +{ + cellGcmSys.Log("cellGcmSetPrepareFlip(ctx=0x%x, id=0x%x)", ctx, id); + + if(id >= 8) + { + return CELL_GCM_ERROR_FAILURE; + } + + GSLockCurrent gslock(GS_LOCK_WAIT_FLUSH); + CellGcmContextData& ctxt = (CellGcmContextData&)Memory[ctx]; + u32 current = re(ctxt.current); + u32 end = re(ctxt.end); + + if(current + 8 >= end) + { + ConLog.Warning("bad flip!"); + cellGcmCallback(ctx, current + 8 - end); + } + + current = re(ctxt.current); + Memory.Write32(current, 0x3fead | (1 << 18)); + Memory.Write32(current + 4, id); + re(ctxt.current, current + 8); + + if(ctx == gcm_info.context_addr) + { + CellGcmControl& ctrl = (CellGcmControl&)Memory[gcm_info.control_addr]; + re(ctrl.put, re(ctrl.put) + 8); + } + + return id; +} + +int cellGcmSetFlipCommand(u32 ctx, u32 id) +{ + return cellGcmSetPrepareFlip(ctx, id); +} + +int cellGcmSetZcull(u8 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: cellGcmSetZcull(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; +} + +int cellGcmBindZcull(u8 index) +{ + cellGcmSys.Warning("TODO: cellGcmBindZcull(index=%d)", index); + + return CELL_OK; +} + +u32 cellGcmGetZcullInfo() +{ + cellGcmSys.Warning("cellGcmGetZcullInfo()"); + return Emu.GetGSManager().GetRender().m_zculls_addr; +} + +int cellGcmGetFlipStatus() +{ + return Emu.GetGSManager().GetRender().m_flip_status; +} + +int cellGcmResetFlipStatus() +{ + Emu.GetGSManager().GetRender().m_flip_status = 1; + + return CELL_OK; +} + +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 + cellGcmSys.Warning("cellGcmGetTiledPitchSize(size=%d)", size); + + return size; +} + +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; +} + +int cellGcmMapEaIoAddress(const u32 ea, const u32 io, const u32 size) +{ + cellGcmSys.Warning("cellGcmMapEaIoAddress(ea=0x%x, io=0x%x, size=0x%x)", ea, io, size); + Memory.Map(io, ea, size); + Emu.GetGSManager().GetRender().m_report_main_addr = ea; + return CELL_OK; +} + +int cellGcmUnbindZcull(u8 index) +{ + cellGcmSys.Warning("cellGcmUnbindZcull(index=%d)", index); + if(index >= 8) + return CELL_EINVAL; + + return CELL_OK; +} + +u64 cellGcmGetTimeStamp(u32 index) +{ + cellGcmSys.Log("cellGcmGetTimeStamp(index=%d)", index); + return Memory.Read64(Memory.RSXFBMem.GetStartAddr() + index * 0x10); +} + +int cellGcmSetFlipHandler(u32 handler_addr) +{ + cellGcmSys.Warning("cellGcmSetFlipHandler(handler_addr=%d)", handler_addr); + if(!Memory.IsGoodAddr(handler_addr) && handler_addr != 0) + { + return CELL_EFAULT; + } + + Emu.GetGSManager().GetRender().m_flip_handler.SetAddr(handler_addr); + return 0; +} + s64 cellGcmFunc15() { cellGcmSys.Error("cellGcmFunc15()"); return 0; } -s64 cellGcmSetFlipCommandWithWaitLabel() +int cellGcmSetFlipCommandWithWaitLabel(u32 ctx, u32 id, u32 label_index, u32 label_value) { - cellGcmSys.Error("cellGcmSetFlipCommandWithWaitLabel()"); - return 0; + int res = cellGcmSetPrepareFlip(ctx, id); + Memory.Write32(Memory.RSXCMDMem.GetStartAddr() + 0x10 * label_index, label_value); + return res == CELL_GCM_ERROR_FAILURE ? CELL_GCM_ERROR_FAILURE : CELL_OK; } int cellGcmInitCursor() @@ -80,14 +415,114 @@ int cellGcmGetCurrentDisplayBufferId(u32 id_addr) return CELL_OK; } +void cellGcmSetDefaultCommandBuffer() +{ + cellGcmSys.Warning("cellGcmSetDefaultCommandBuffer()"); + Memory.Write32(Emu.GetGSManager().GetRender().m_ctxt_addr, gcm_info.context_addr); +} + +void cellGcmSetFlipStatus() +{ + cellGcmSys.Warning("cellGcmSetFlipStatus()"); + + Emu.GetGSManager().GetRender().m_flip_status = 0; +} + +int cellGcmSetTileInfo(u8 index, u8 location, u32 offset, u32 size, u32 pitch, u8 comp, u16 base, u8 bank) +{ + cellGcmSys.Warning("cellGcmSetTileInfo(index=%d, location=%d, offset=%d, size=%d, pitch=%d, comp=%d, base=%d, bank=%d)", + index, location, offset, size, pitch, comp, base, bank); + + if(index >= g_tiles_count || base >= 800 || bank >= 4) + { + return CELL_GCM_ERROR_INVALID_VALUE; + } + + if(offset & 0xffff || size & 0xffff || pitch & 0xf) + { + return CELL_GCM_ERROR_INVALID_ALIGNMENT; + } + + if(location >= 2 || (comp != 0 && (comp < 7 || comp > 12))) + { + return CELL_GCM_ERROR_INVALID_ENUM; + } + + if(comp) + { + cellGcmSys.Error("cellGcmSetTileInfo: bad comp! (%d)", comp); + } + + auto& tile = Emu.GetGSManager().GetRender().m_tiles[index]; + tile.m_location = location; + tile.m_offset = offset; + tile.m_size = size; + tile.m_pitch = pitch; + tile.m_comp = comp; + tile.m_base = base; + tile.m_bank = bank; + Memory.WriteData(Emu.GetGSManager().GetRender().m_tiles_addr + sizeof(CellGcmTileInfo) * index, tile.Pack()); + + return CELL_OK; +} + +int cellGcmBindTile(u8 index) +{ + cellGcmSys.Warning("cellGcmBindTile(index=%d)", index); + + if(index >= g_tiles_count) + { + return CELL_GCM_ERROR_INVALID_VALUE; + } + + auto& tile = Emu.GetGSManager().GetRender().m_tiles[index]; + tile.m_binded = true; + + return CELL_OK; +} + +int cellGcmUnbindTile(u8 index) +{ + cellGcmSys.Warning("cellGcmUnbindTile(index=%d)", index); + + if(index >= g_tiles_count) + { + return CELL_GCM_ERROR_INVALID_VALUE; + } + + auto& tile = Emu.GetGSManager().GetRender().m_tiles[index]; + tile.m_binded = false; + + return CELL_OK; +} + +u32 cellGcmGetTileInfo() +{ + cellGcmSys.Warning("cellGcmGetTileInfo()"); + return Emu.GetGSManager().GetRender().m_tiles_addr; +} + +int cellGcmInitDefaultFifoMode(int mode) +{ + cellGcmSys.Warning("cellGcmInitDefaultFifoMode(mode=%d)", mode); + return CELL_OK; +} + +u32 cellGcmGetReportDataAddressLocation(u8 location, u32 index) +{ + ConLog.Warning("cellGcmGetReportDataAddressLocation(location=%d, index=%d)", location, index); + return Emu.GetGSManager().GetRender().m_report_main_addr; +} + void cellGcmSys_init() { cellGcmSys.AddFunc(0x055bd74d, cellGcmGetTiledPitchSize); cellGcmSys.AddFunc(0x15bae46b, cellGcmInit); - cellGcmSys.AddFunc(0x21397818, cellGcmFlush); + cellGcmSys.AddFunc(0x21397818, cellGcmSetFlipCommand); cellGcmSys.AddFunc(0x21ac3697, cellGcmAddressToOffset); cellGcmSys.AddFunc(0x3a33c1fd, cellGcmFunc15); cellGcmSys.AddFunc(0x4ae8d215, cellGcmSetFlipMode); + cellGcmSys.AddFunc(0x63441cb4, cellGcmMapEaIoAddress); cellGcmSys.AddFunc(0x5e2ee0f0, cellGcmGetDefaultCommandWordSize); cellGcmSys.AddFunc(0x72a577ce, cellGcmGetFlipStatus); cellGcmSys.AddFunc(0x8cdf8c70, cellGcmGetDefaultSegmentWordSize); @@ -98,6 +533,8 @@ void cellGcmSys_init() cellGcmSys.AddFunc(0xd8f88e1a, cellGcmSetFlipCommandWithWaitLabel); cellGcmSys.AddFunc(0xe315a0b2, cellGcmGetConfiguration); cellGcmSys.AddFunc(0x9dc04436, cellGcmBindZcull); + cellGcmSys.AddFunc(0xd34a420d, cellGcmSetZcull); + cellGcmSys.AddFunc(0xd9a0a879, cellGcmGetZcullInfo); cellGcmSys.AddFunc(0x5a41c10f, cellGcmGetTimeStamp); cellGcmSys.AddFunc(0xd9b7653e, cellGcmUnbindTile); cellGcmSys.AddFunc(0xa75640e8, cellGcmUnbindZcull); @@ -113,4 +550,12 @@ void cellGcmSys_init() cellGcmSys.AddFunc(0xf9bfdc72, cellGcmSetCursorImageOffset); cellGcmSys.AddFunc(0x0e6b0dae, cellGcmGetDisplayInfo); cellGcmSys.AddFunc(0x93806525, cellGcmGetCurrentDisplayBufferId); + cellGcmSys.AddFunc(0xbc982946, cellGcmSetDefaultCommandBuffer); + cellGcmSys.AddFunc(0xa47c09ff, cellGcmSetFlipStatus); + cellGcmSys.AddFunc(0xbd100dbc, cellGcmSetTileInfo); + cellGcmSys.AddFunc(0x4524cccd, cellGcmBindTile); + cellGcmSys.AddFunc(0x0b4b62d5, cellGcmSetPrepareFlip); + cellGcmSys.AddFunc(0x657571f7, cellGcmGetTileInfo); + cellGcmSys.AddFunc(0xcaabd992, cellGcmInitDefaultFifoMode); + cellGcmSys.AddFunc(0x8572bce2, cellGcmGetReportDataAddressLocation); } diff --git a/rpcs3/Emu/SysCalls/Modules/cellResc.cpp b/rpcs3/Emu/SysCalls/Modules/cellResc.cpp new file mode 100644 index 0000000000..beaa904bad --- /dev/null +++ b/rpcs3/Emu/SysCalls/Modules/cellResc.cpp @@ -0,0 +1,39 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" +#include "Emu/SysCalls/SC_FUNC.h" +#include "Emu/GS/GCM.h" + +void cellRecs_init(); +Module cellRecs(0x001f, cellRecs_init); + +int cellRescSetConvertAndFlip(s32 indx) +{ + cellRecs.Log("cellRescSetConvertAndFlip(indx=0x%x)", indx); + Emu.GetGSManager().GetRender().Draw(); + + return CELL_OK; +} + +int cellRescSetWaitFlip() +{ + return CELL_OK; +} + +int cellRescSetFlipHandler(u32 handler_addr) +{ + cellRecs.Warning("cellRescSetFlipHandler(handler_addr=%d)", handler_addr); + if(!Memory.IsGoodAddr(handler_addr) && handler_addr != 0) + { + return CELL_EFAULT; + } + + Emu.GetGSManager().GetRender().m_flip_handler.SetAddr(handler_addr); + return 0; +} + +void cellRecs_init() +{ + cellRecs.AddFunc(0x25c107e6, cellRescSetConvertAndFlip); + cellRecs.AddFunc(0x0d3c22ce, cellRescSetWaitFlip); + cellRecs.AddFunc(0x2ea94661, cellRescSetFlipHandler); +} diff --git a/rpcs3/Emu/SysCalls/SysCalls.cpp b/rpcs3/Emu/SysCalls/SysCalls.cpp index bfeeed2425..e76bf979e3 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.cpp +++ b/rpcs3/Emu/SysCalls/SysCalls.cpp @@ -35,7 +35,7 @@ static func_caller* sc_table[1024] = null_func, null_func, null_func, null_func, null_func, //124 null_func, null_func, null_func, bind_func(sys_event_queue_create), null_func, //129 bind_func(sys_event_queue_receive), null_func, null_func, null_func, bind_func(sys_event_port_create), //134 - null_func, bind_func(sys_event_port_connect_local), null_func, null_func, null_func, //139 + null_func, bind_func(sys_event_port_connect_local), null_func, bind_func(sys_event_port_send), null_func, //139 null_func, null_func, null_func, null_func, null_func, //144 bind_func(sys_time_get_current_time), bind_func(sys_time_get_system_time), bind_func(sys_time_get_timebase_frequency), null_func, null_func, //149 null_func, null_func, null_func, null_func, null_func, //154 diff --git a/rpcs3/Emu/SysCalls/SysCalls.h b/rpcs3/Emu/SysCalls/SysCalls.h index 70e6ae446b..edbb9a6dae 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.h +++ b/rpcs3/Emu/SysCalls/SysCalls.h @@ -118,6 +118,7 @@ extern int sys_event_queue_create(u32 equeue_id_addr, u32 attr_addr, u64 event_q extern int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout); extern int sys_event_port_create(u32 eport_id_addr, int port_type, u64 name); extern int sys_event_port_connect_local(u32 event_port_id, u32 event_queue_id); +extern int sys_event_port_send(u32 event_port_id, u64 data1, u64 data2, u64 data3); //sys_semaphore extern int sys_semaphore_create(u32 sem_addr, u32 attr_addr, int initial_val, int max_val); @@ -216,30 +217,7 @@ extern int cellPadGetInfo2(u32 info_addr); extern int cellPadSetPortSetting(u32 port_no, u32 port_setting); //cellGcm -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(u8 index); -extern u32 cellGcmGetControlRegister(); -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(u32 size); -extern int cellGcmSetFlipMode(u32 mode); -extern u32 cellGcmGetDefaultCommandWordSize(); -extern u32 cellGcmGetDefaultSegmentWordSize(); -extern int cellGcmSetDefaultFifoSize(u32 bufferSize, u32 segmentSize); -extern int cellGcmMapEaIoAddress(const u32 ea, const u32 io, const u32 size); -extern int cellGcmUnbindTile(u8 index); -extern int cellGcmUnbindZcull(u8 index); -extern u64 cellGcmGetTimeStamp(u32 index); -extern int cellGcmSetFlipHandler(u32 handler_addr); //sys_tty extern int sys_tty_read(u32 ch, u64 buf_addr, u32 len, u64 preadlen_addr); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp index 29b5c28aaf..8aa6f3fa00 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp @@ -95,7 +95,7 @@ int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout) GetCurrentPPUThread().WaitFor(queue_receive); - return CELL_OK; + return result; } int sys_event_port_create(u32 eport_id_addr, int port_type, u64 name) @@ -110,6 +110,7 @@ int sys_event_port_create(u32 eport_id_addr, int port_type, u64 name) EventPort* eport = new EventPort(); u32 id = sys_event.GetNewId(eport); + eport->pos = 0; eport->has_data = false; eport->name = name ? name : id; Memory.Write32(eport_id_addr, id); @@ -130,6 +131,37 @@ int sys_event_port_connect_local(u32 event_port_id, u32 event_queue_id) EventPort* eport = (EventPort*)Emu.GetIdManager().GetIDData(event_port_id).m_data; EventQueue* equeue = (EventQueue*)Emu.GetIdManager().GetIDData(event_queue_id).m_data; equeue->ports[equeue->pos++] = eport; + eport->queue[eport->pos++] = equeue; + + return CELL_OK; +} + +int sys_event_port_send(u32 event_port_id, u64 data1, u64 data2, u64 data3) +{ + sys_event.Warning("sys_event_port_send(event_port_id=0x%x, data1=0x%llx, data2=0x%llx, data3=0x%llx)", + event_port_id, data1, data2, data3); + + if(!sys_event.CheckId(event_port_id)) + { + return CELL_ESRCH; + } + + EventPort* eport = (EventPort*)Emu.GetIdManager().GetIDData(event_port_id).m_data; + + if(!eport->pos) + { + return CELL_ENOTCONN; + } + + if(eport->has_data) + { + return CELL_EBUSY; + } + + eport->has_data = true; + eport->data1 = data1; + eport->data2 = data2; + eport->data3 = data3; 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 ae886b8474..a281671f61 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp @@ -247,7 +247,13 @@ int cellFsStat(const u32 path_addr, const u32 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; + vfsStream* f = Emu.GetVFS().Open(path, vfsRead); + if(!f || !f->IsOpened()) + { + sys_fs.Warning("cellFsFstat: '%s' not found.", path); + Emu.GetVFS().Close(f); + return CELL_ENOENT; + } Lv2FsStat stat; stat.st_mode = @@ -261,7 +267,7 @@ int cellFsStat(const u32 path_addr, const u32 sb_addr) stat.st_atime = 0; //TODO stat.st_mtime = 0; //TODO stat.st_ctime = 0; //TODO - stat.st_size = wxFile(path).Length(); + stat.st_size = f->GetSize(); stat.st_blksize = 4096; mem_class_t stat_c(sb_addr); @@ -274,6 +280,8 @@ int cellFsStat(const u32 path_addr, const u32 sb_addr) stat_c += stat.st_size; stat_c += stat.st_blksize; + Emu.GetVFS().Close(f); + return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp index 5f834dd9e3..707651a5e8 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp @@ -3,73 +3,7 @@ #include "Emu/GS/GCM.h" extern Module cellGcmSys; - -CellGcmConfig current_config; -CellGcmContextData current_context; -gcmInfo gcm_info; -struct gcm_offset -{ - u16 ea; - u16 offset; -}; - -u32 map_offset_addr = 0; -u32 map_offset_pos = 0; - -int cellGcmMapMainMemory(u32 address, u32 size, u32 offset_addr) -{ - 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(); - - map_offset_addr = 0; - map_offset_pos = 0; - current_config.ioSize = re32(ioSize); - current_config.ioAddress = re32(ioAddress); - current_config.localSize = re32(local_size); - current_config.localAddress = re32(local_addr); - current_config.memoryFrequency = re32(650000000); - current_config.coreFrequency = re32(500000000); - - Memory.RSXFBMem.Alloc(local_size); - Memory.RSXCMDMem.Alloc(cmdSize); - - u32 ctx_begin = ioAddress + 0x1000; - u32 ctx_size = 0x6ffc; - current_context.begin = re(ctx_begin); - current_context.end = re(ctx_begin + ctx_size); - current_context.current = current_context.begin; - current_context.callback = re32(Emu.GetRSXCallback() - 4); - - gcm_info.context_addr = Memory.MainMem.Alloc(0x1000); - gcm_info.control_addr = gcm_info.context_addr + 0x40; - - Memory.WriteData(gcm_info.context_addr, current_context); - Memory.Write32(context_addr, gcm_info.context_addr); - - CellGcmControl& ctrl = (CellGcmControl&)Memory[gcm_info.control_addr]; - ctrl.put = 0; - ctrl.get = 0; - ctrl.ref = -1; - - Emu.GetGSManager().GetRender().m_gcm_buffers_addr = Memory.Alloc(sizeof(gcmBuffer) * 8, sizeof(gcmBuffer)); - Emu.GetGSManager().GetRender().m_gcm_buffers_count = 0; - Emu.GetGSManager().GetRender().m_gcm_current_buffer = 0; - - Emu.GetGSManager().GetRender().Init(ctx_begin, ctx_size, gcm_info.control_addr, local_addr); - - return CELL_OK; -} +extern gcmInfo gcm_info; int cellGcmCallback(u32 context_addr, u32 count) { @@ -89,201 +23,3 @@ int cellGcmCallback(u32 context_addr, u32 count) 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(u32 address, u32 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; - - if(!map_offset_addr) - { - map_offset_addr = Memory.Alloc(4*50, 4); - } - - u32 sa = Memory.RSXFBMem.IsInMyRange(address) ? Memory.RSXFBMem.GetStartAddr() : re(current_context.begin); - u16 ea = (sa + address - sa) >> 16; - u32 offset = address - sa; - //Memory.Write16(map_offset_addr + map_offset_pos + 0, ea); - //Memory.Write16(map_offset_addr + map_offset_pos + 2, offset); - //map_offset_pos += 4; - - Memory.Write32(offset_addr, offset); - - return CELL_OK; -} - -int cellGcmSetDisplayBuffer(u32 id, u32 offset, u32 pitch, u32 width, u32 height) -{ - 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 > 7) return CELL_EINVAL; - - gcmBuffer* buffers = (gcmBuffer*)Memory.GetMemFromAddr(Emu.GetGSManager().GetRender().m_gcm_buffers_addr); - - buffers[id].offset = re(offset); - buffers[id].pitch = re(pitch); - buffers[id].width = re(width); - buffers[id].height = re(height); - - if(id + 1 > Emu.GetGSManager().GetRender().m_gcm_buffers_count) - { - Emu.GetGSManager().GetRender().m_gcm_buffers_count = id + 1; - } - - return CELL_OK; -} - -u32 cellGcmGetLabelAddress(u8 index) -{ - cellGcmSys.Log("cellGcmGetLabelAddress(index=%d)", index); - return Memory.RSXCMDMem.GetStartAddr() + 0x10 * index; -} - -u32 cellGcmGetControlRegister() -{ - cellGcmSys.Log("cellGcmGetControlRegister()"); - - return gcm_info.control_addr; -} - -int cellGcmFlush(u32 ctx, u32 id) -{ - cellGcmSys.Log("cellGcmFlush(ctx=0x%x, id=0x%x)", ctx, id); - if(id > 1) return CELL_EINVAL; - - Emu.GetGSManager().GetRender().Draw(); - - return CELL_OK; -} - -void cellGcmSetTile(u32 index, u32 location, u32 offset, u32 size, u32 pitch, u32 comp, u32 base, u32 bank) -{ - 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; -} - -int cellGcmGetFlipStatus() -{ - return Emu.GetGSManager().GetRender().m_flip_status; -} - -int cellGcmResetFlipStatus() -{ - Emu.GetGSManager().GetRender().m_flip_status = 1; - - return CELL_OK; -} - -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 - cellGcmSys.Warning("cellGcmGetTiledPitchSize(size=%d)", size); - - return size; -} - -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; -} - -int cellGcmMapEaIoAddress(const u32 ea, const u32 io, const u32 size) -{ - cellGcmSys.Warning("cellGcmMapEaIoAddress(ea=0x%x, io=0x%x, size=0x%x)", ea, io, size); - Memory.Map(io, ea, size); - return CELL_OK; -} - -int cellGcmUnbindTile(u8 index) -{ - cellGcmSys.Warning("cellGcmUnbindTile(index=%d)", index); - if(index >= 15) - return CELL_EINVAL; - - return CELL_OK; -} - -int cellGcmUnbindZcull(u8 index) -{ - cellGcmSys.Warning("cellGcmUnbindZcull(index=%d)", index); - if(index >= 8) - return CELL_EINVAL; - - return CELL_OK; -} - -u64 cellGcmGetTimeStamp(u32 index) -{ - cellGcmSys.Log("cellGcmGetTimeStamp(index=%d)", index); - return Memory.Read64(Memory.RSXFBMem.GetStartAddr() + index * 0x10); -} - -int cellGcmSetFlipHandler(u32 handler_addr) -{ - cellGcmSys.Warning("cellGcmSetFlipHandler(handler_addr=%d)", handler_addr); - if(!Memory.IsGoodAddr(handler_addr) && handler_addr != 0) - { - return CELL_EFAULT; - } - - Emu.GetGSManager().GetRender().m_flip_handler.SetAddr(handler_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 ca1761ed7b..15d9e7bb54 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Pad.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Pad.cpp @@ -87,6 +87,12 @@ int cellPadGetData(u32 port_no, u32 data_addr) case CELL_PAD_BTN_OFFSET_DIGITAL1: if(!(d1 & buttons[i].m_outKeyCode)){d1 |= buttons[i].m_outKeyCode; len++;} break; case CELL_PAD_BTN_OFFSET_DIGITAL2: if(!(d2 & buttons[i].m_outKeyCode)){d2 |= buttons[i].m_outKeyCode; len++;} break; } + + if(buttons[i].m_flush) + { + buttons[i].m_pressed = false; + buttons[i].m_flush = false; + } } data.len = re(len); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp index 69ca0d4561..14a1b70868 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp @@ -11,7 +11,11 @@ int sys_process_getpid() int sys_process_exit(int errorcode) { ConLog.Warning("sys_process_exit(%d)", errorcode); +#ifdef _DEBUG Emu.Pause(); +#else + Emu.Stop(); +#endif return CELL_OK; } diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index a202c5813b..f0155e534b 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -196,7 +196,14 @@ void Emulator::Run() //m_memory_viewer->ShowPC(); if(!m_dbg_console) + { m_dbg_console = new DbgConsole(); + } + else + { + GetDbgCon().Close(); + GetDbgCon().Clear(); + } GetGSManager().Init(); GetCallbackManager().Init(); @@ -260,12 +267,6 @@ void Emulator::Stop() CurGameInfo.Reset(); Memory.Close(); - if(m_dbg_console) - { - GetDbgCon().Close(); - GetDbgCon().Clear(); - } - //if(m_memory_viewer && m_memory_viewer->IsShown()) m_memory_viewer->Hide(); wxGetApp().SendDbgCommand(DID_STOPED_EMU); } diff --git a/rpcs3/Emu/event.h b/rpcs3/Emu/event.h index 57580c9975..c3f72157d7 100644 --- a/rpcs3/Emu/event.h +++ b/rpcs3/Emu/event.h @@ -15,6 +15,8 @@ struct sys_event_data u64 data3; }; +struct EventQueue; + struct EventPort { u64 name; @@ -23,6 +25,8 @@ struct EventPort u64 data3; bool has_data; PPCThread* thread; + EventQueue* queue[127]; + int pos; }; struct EventQueue diff --git a/rpcs3/Gui/InterpreterDisAsm.cpp b/rpcs3/Gui/InterpreterDisAsm.cpp index 1fe944d2a5..48464a661a 100644 --- a/rpcs3/Gui/InterpreterDisAsm.cpp +++ b/rpcs3/Gui/InterpreterDisAsm.cpp @@ -324,7 +324,7 @@ void InterpreterDisAsmFrame::HandleCommand(wxCommandEvent& event) m_btn_run->Enable(); m_btn_step->Enable(); m_btn_pause->Disable(); - //DoUpdate(); + DoUpdate(); break; case DID_START_THREAD: @@ -510,7 +510,7 @@ void InterpreterDisAsmFrame::Task() { if(!CPU) return; wxGetApp().SendDbgCommand(DID_RESUME_THREAD, CPU); - + wxGetApp().SendDbgCommand(DID_RESUMED_THREAD, CPU); bool dump_status = dump_enable; //CPU.InitTls(); @@ -535,4 +535,5 @@ void InterpreterDisAsmFrame::Task() //CPU.FreeTls(); wxGetApp().SendDbgCommand(DID_PAUSE_THREAD, CPU); + wxGetApp().SendDbgCommand(DID_PAUSED_THREAD, CPU); } \ No newline at end of file diff --git a/rpcs3/Loader/ELF64.cpp b/rpcs3/Loader/ELF64.cpp index f6ff6d3b8a..b1cf1ff8ef 100644 --- a/rpcs3/Loader/ELF64.cpp +++ b/rpcs3/Loader/ELF64.cpp @@ -155,8 +155,8 @@ bool ELF64Loader::LoadShdrInfo(s64 offset) shdr_name_arr.Clear(); if(ehdr.e_shoff == 0 && ehdr.e_shnum) { - ConLog.Error("LoadShdr64 error: Section header offset is null!"); - return false; + ConLog.Warning("LoadShdr64 error: Section header offset is null!"); + return true; } elf64_f.Seek(offset < 0 ? ehdr.e_shoff : offset); diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 162773f50a..09fce76d29 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -246,6 +246,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index b57aac8b3f..d9e380c208 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -283,6 +283,9 @@ Emu\SysCalls\Modules + + Emu\SysCalls\Modules + From 05687829d674b1af3242c0592d8208444c74c811 Mon Sep 17 00:00:00 2001 From: DH Date: Mon, 26 Aug 2013 17:53:12 +0300 Subject: [PATCH 11/13] - Version bump. --- rpcs3/stdafx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index 8633a2be22..b36785c9e7 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -206,4 +206,4 @@ enum Status #include "rpcs3.h" #define _PRGNAME_ "RPCS3" -#define _PRGVER_ "0.0.0.2" \ No newline at end of file +#define _PRGVER_ "0.0.0.3" \ No newline at end of file From a9fdcd959bb81044308761ceea398bf9ab67573a Mon Sep 17 00:00:00 2001 From: DH Date: Tue, 27 Aug 2013 18:18:01 +0300 Subject: [PATCH 12/13] OpenGL renderer: - Disabled dump vertex data. - Fixed fragment constants loader. - Fixed fbo creation. - Implemented texture filtering. --- .../TEST12345/USRDIR/handle_system_cmd.elf | Bin 0 -> 274528 bytes rpcs3/Emu/GS/GL/GLBuffers.cpp | 4 +- rpcs3/Emu/GS/GL/GLGSRender.cpp | 117 +++++++++++++++--- rpcs3/Emu/GS/GL/GLGSRender.h | 39 +++++- rpcs3/Emu/GS/GSRender.cpp | 1 + rpcs3/Emu/GS/RSXThread.h | 16 ++- 6 files changed, 153 insertions(+), 24 deletions(-) create mode 100644 bin/dev_hdd0/game/TEST12345/USRDIR/handle_system_cmd.elf diff --git a/bin/dev_hdd0/game/TEST12345/USRDIR/handle_system_cmd.elf b/bin/dev_hdd0/game/TEST12345/USRDIR/handle_system_cmd.elf new file mode 100644 index 0000000000000000000000000000000000000000..2937c9e4ba25fb0b39ef24e76362b47baf6f3e4f GIT binary patch literal 274528 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 - if(id < 32) - id = 32; + u32 id = c.id - m_cur_shader_prog->offset + 2 * 4 * 4; const wxString name = wxString::Format("fc%u", id); const int l = m_program.GetLocation(name); @@ -563,13 +561,15 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c m_fbo.Bind(GL_READ_FRAMEBUFFER); m_fbo.Bind(GL_DRAW_FRAMEBUFFER, 0); m_fbo.Blit( - 0, 0, m_width, m_height, - 0, 0, m_width, m_height, + m_surface_clip_x, m_surface_clip_y, m_surface_clip_x + m_surface_clip_w, m_surface_clip_y + m_surface_clip_h, + m_surface_clip_x, m_surface_clip_y, m_surface_clip_x + m_surface_clip_w, m_surface_clip_y + m_surface_clip_h, GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); m_fbo.Bind(); + checkForGlError("m_fbo.Blit"); } m_frame->Flip(); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } m_gcm_current_buffer = args[0]; @@ -708,7 +708,20 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case_16(NV4097_SET_TEXTURE_FILTER, 0x20): - //TODO + { + GLTexture& tex = m_frame->GetTexture(index); + u32 a0 = args[0]; + u16 bias = a0 & 0x1fff; + u8 conv = (a0 >> 13) & 0xf; + u8 min = (a0 >> 16) & 0x7; + u8 mag = (a0 >> 24) & 0x7; + u8 a_signed = (a0 >> 28) & 0x1; + u8 r_signed = (a0 >> 29) & 0x1; + u8 g_signed = (a0 >> 30) & 0x1; + u8 b_signed = (a0 >> 31) & 0x1; + + tex.SetFilter(bias, min, mag, conv, a_signed, r_signed, g_signed, b_signed); + } break; case_16(NV4097_SET_TEXTURE_ADDRESS, 0x20): @@ -801,7 +814,11 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c } */ - if(0) + gcmBuffer* buffers = (gcmBuffer*)Memory.GetMemFromAddr(m_gcm_buffers_addr); + m_width = re(buffers[m_gcm_current_buffer].width); + m_height = re(buffers[m_gcm_current_buffer].height); + + if(1) { m_rbo.Create(2); checkForGlError("m_rbo.Create"); @@ -809,7 +826,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c m_rbo.Storage(GL_RGBA, m_width, m_height); checkForGlError("m_rbo.Storage(GL_RGBA)"); m_rbo.Bind(1); - m_rbo.Storage(GL_DEPTH_STENCIL, m_width, m_height); + m_rbo.Storage(GL_DEPTH24_STENCIL8, m_width, m_height); checkForGlError("m_rbo.Storage(GL_DEPTH24_STENCIL8)"); m_fbo.Create(); checkForGlError("m_fbo.Create"); @@ -854,11 +871,24 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_ALPHA_FUNC: - glAlphaFunc(args[0], args[1]); + m_set_alpha_func = true; + m_alpha_func = args[0]; + + if(count >= 2) + { + m_set_alpha_ref = true; + m_alpha_ref = args[1]; + } + break; + + case NV4097_SET_ALPHA_REF: + m_set_alpha_ref = true; + m_alpha_ref = args[0]; break; case NV4097_SET_CULL_FACE: - glCullFace(args[0]); + m_set_cull_face = true; + m_cull_face = args[0]; break; case NV4097_SET_VIEWPORT_VERTICAL: @@ -922,6 +952,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c if (a0 & 0x2) f |= GL_STENCIL_BUFFER_BIT; if (a0 & 0xF0) f |= GL_COLOR_BUFFER_BIT; glClear(f); + checkForGlError("glClear"); /* if(m_set_clear_surface) { @@ -1088,6 +1119,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c m_clear_color_g / 255.0f, m_clear_color_b / 255.0f, m_clear_color_a / 255.0f); + checkForGlError("glClearColor"); } break; @@ -1190,7 +1222,7 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_CULL_FACE_ENABLE: - m_set_cull_face = args[0] ? true : false; + m_set_cull_face_enable = args[0] ? true : false; break; case NV4097_SET_DITHER_ENABLE: @@ -1546,9 +1578,46 @@ void GLGSRender::DoCmd(const u32 fcmd, const u32 cmd, mem32_t& args, const u32 c break; case NV4097_SET_ZSTENCIL_CLEAR_VALUE: + { + u32 clear_valuei = args[0]; + //double clear_valuef = (double)clear_valuei / 0xffffffff; + //glClearDepth(clear_valuef); + glClearStencil(clear_valuei); + glClear(GL_STENCIL_BUFFER_BIT); + } + break; + case NV4097_SET_ZCULL_CONTROL0: + { + m_set_depth_func = true; + m_depth_func = args[0] >> 4; + } + break; + case NV4097_SET_ZCULL_CONTROL1: + { + //TODO + } + break; + case NV4097_SET_SCULL_CONTROL: + { + u32 a0 = args[0]; + m_set_stencil_func = m_set_stencil_func_ref = m_set_stencil_func_mask = true; + + m_stencil_func = a0 & 0xffff; + m_stencil_func_ref = (a0 >> 16) & 0xff; + m_stencil_func_mask = (a0 >> 24) & 0xff; + } + break; + + case NV4097_SET_ZCULL_EN: + { + u32 a0 = args[0]; + + m_depth_test_enable = a0 & 0x1 ? true : false; + m_set_stencil_test = a0 & 0x2 ? true : false; + } break; case NV4097_GET_REPORT: @@ -1800,8 +1869,9 @@ bool GLGSRender::LoadProgram() else { m_program.Create(m_cur_vertex_prog->id, m_cur_shader_prog->id); + checkForGlError("m_program.Create"); m_prog_buffer.Add(m_program, *m_cur_shader_prog, *m_cur_vertex_prog); - + checkForGlError("m_prog_buffer.Add"); m_program.Use(); GLint r = GL_FALSE; @@ -1833,9 +1903,7 @@ void GLGSRender::ExecCMD() { if(m_set_surface_clip_horizontal && m_set_surface_clip_vertical) { - m_width = m_surface_clip_w; - m_height = m_surface_clip_h; - //ConLog.Write("width: %d, height: %d, x: %d, y: %d", m_width, m_height, m_surface_clip_x, m_surface_clip_y); + //ConLog.Write("surface clip width: %d, height: %d, x: %d, y: %d", m_width, m_height, m_surface_clip_x, m_surface_clip_y); } if(m_set_color_mask) @@ -1885,7 +1953,7 @@ void GLGSRender::ExecCMD() Enable(m_set_depth_bounds_test, GL_DEPTH_CLAMP); Enable(m_set_blend, GL_BLEND); Enable(m_set_logic_op, GL_LOGIC_OP); - Enable(m_set_cull_face, GL_CULL_FACE); + Enable(m_set_cull_face_enable, GL_CULL_FACE); Enable(m_set_dither, GL_DITHER); Enable(m_set_stencil_test, GL_STENCIL_TEST); Enable(m_set_line_smooth, GL_LINE_SMOOTH); @@ -1991,15 +2059,30 @@ void GLGSRender::ExecCMD() checkForGlError("glBlendColor"); } + if(m_set_cull_face) + { + glCullFace(m_cull_face); + checkForGlError("glCullFace"); + } + + if(m_set_alpha_func && m_set_alpha_ref) + { + glAlphaFunc(m_alpha_func, m_alpha_ref); + checkForGlError("glAlphaFunc"); + } + if(m_set_fog_mode) { glFogi(GL_FOG_MODE, m_fog_mode); + checkForGlError("glFogi(GL_FOG_MODE)"); } if(m_set_fog_params) { glFogf(GL_FOG_START, m_fog_param0); + checkForGlError("glFogf(GL_FOG_START)"); glFogf(GL_FOG_END, m_fog_param1); + checkForGlError("glFogf(GL_FOG_END)"); } if(m_indexed_array.m_count && m_draw_array_count) diff --git a/rpcs3/Emu/GS/GL/GLGSRender.h b/rpcs3/Emu/GS/GL/GLGSRender.h index 805c694b3c..0040935796 100644 --- a/rpcs3/Emu/GS/GL/GLGSRender.h +++ b/rpcs3/Emu/GS/GL/GLGSRender.h @@ -41,6 +41,15 @@ class GLTexture u8 m_aniso_bias; u8 m_signed_remap; + u16 m_bias; + u8 m_min_filter; + u8 m_mag_filter; + u8 m_conv; + u8 m_a_signed; + u8 m_r_signed; + u8 m_g_signed; + u8 m_b_signed; + u32 m_remap; public: @@ -127,6 +136,18 @@ public: m_pitch = pitch; } + void SetFilter(u16 bias, u8 min, u8 mag, u8 conv, u8 a_signed, u8 r_signed, u8 g_signed, u8 b_signed) + { + m_bias = bias; + m_min_filter = min; + m_mag_filter = mag; + m_conv = conv; + m_a_signed = a_signed; + m_r_signed = r_signed; + m_g_signed = g_signed; + m_b_signed = b_signed; + } + u32 GetFormat() const { return m_format; } void SetOffset(const u32 offset) @@ -289,13 +310,25 @@ public: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GetGlWrap(m_wrapr)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, gl_tex_zfunc[m_zfunc]); - glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, m_aniso_bias); + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, m_bias); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, m_minlod); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, m_maxlod); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, m_maxaniso); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + static const int gl_tex_filter[] = + { + GL_NEAREST, + GL_NEAREST, + GL_LINEAR, + GL_NEAREST_MIPMAP_NEAREST, + GL_LINEAR_MIPMAP_NEAREST, + GL_NEAREST_MIPMAP_LINEAR, + GL_LINEAR_MIPMAP_LINEAR, + GL_NEAREST, + }; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_tex_filter[m_min_filter]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_tex_filter[m_mag_filter]); //Unbind(); } diff --git a/rpcs3/Emu/GS/GSRender.cpp b/rpcs3/Emu/GS/GSRender.cpp index 225ae860de..75664a9a1a 100644 --- a/rpcs3/Emu/GS/GSRender.cpp +++ b/rpcs3/Emu/GS/GSRender.cpp @@ -94,6 +94,7 @@ u32 GSRender::GetAddress(u32 offset, u8 location) } ConLog.Error("GetAddress(offset=0x%x, location=0x%x", location); + assert(0); return 0; } diff --git a/rpcs3/Emu/GS/RSXThread.h b/rpcs3/Emu/GS/RSXThread.h index 28a61a0898..78dc31eae3 100644 --- a/rpcs3/Emu/GS/RSXThread.h +++ b/rpcs3/Emu/GS/RSXThread.h @@ -22,7 +22,7 @@ public: bool m_set_depth_bounds_test; bool m_depth_test_enable; bool m_set_logic_op; - bool m_set_cull_face; + bool m_set_cull_face_enable; bool m_set_dither; bool m_set_stencil_test; bool m_set_line_smooth; @@ -196,6 +196,15 @@ public: u16 m_surface_clip_y; u16 m_surface_clip_h; + bool m_set_cull_face; + u32 m_cull_face; + + bool m_set_alpha_func; + u32 m_alpha_func; + + bool m_set_alpha_ref; + u32 m_alpha_ref; + u8 m_begin_end; public: @@ -222,7 +231,7 @@ public: m_set_blend_sfactor = false; m_set_blend_dfactor = false; m_set_logic_op = false; - m_set_cull_face = false; + m_set_cull_face_enable = false; m_set_dither = false; m_set_stencil_test = false; m_set_stencil_mask = false; @@ -259,6 +268,9 @@ public: m_set_context_dma_z = false; m_set_surface_clip_horizontal = false; m_set_surface_clip_vertical = false; + m_set_cull_face = false; + m_set_alpha_func = false; + m_set_alpha_ref = false; m_begin_end = 0; } From 5cc488011f00dc635ecac9acfdf0aa323e6fa3b7 Mon Sep 17 00:00:00 2001 From: DH Date: Tue, 27 Aug 2013 18:22:04 +0300 Subject: [PATCH 13/13] - Version bump. --- rpcs3/stdafx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index b36785c9e7..bd6a9ff2fe 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -206,4 +206,4 @@ enum Status #include "rpcs3.h" #define _PRGNAME_ "RPCS3" -#define _PRGVER_ "0.0.0.3" \ No newline at end of file +#define _PRGVER_ "0.0.0.4" \ No newline at end of file