From 9bf8f2eec551b43ce5b433c7da70ae57d2305c4f Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sun, 8 Dec 2013 03:12:37 +0400 Subject: [PATCH 01/16] SPU Improvement Some instructions implemented with SSE intrinsics --- rpcs3/Emu/Cell/SPUInterpreter.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index 23832209e3..850d426d22 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -79,17 +79,13 @@ private: } void SF(u32 rt, u32 ra, u32 rb) { - CPU.GPR[rt]._u32[0] = CPU.GPR[rb]._u32[0] - CPU.GPR[ra]._u32[0]; - CPU.GPR[rt]._u32[1] = CPU.GPR[rb]._u32[1] - CPU.GPR[ra]._u32[1]; - CPU.GPR[rt]._u32[2] = CPU.GPR[rb]._u32[2] - CPU.GPR[ra]._u32[2]; - CPU.GPR[rt]._u32[3] = CPU.GPR[rb]._u32[3] - CPU.GPR[ra]._u32[3]; + //(SSE2) PSUBD - Subtract Packed Integers + CPU.GPR[rt]._m128i = _mm_sub_epi32(CPU.GPR[rb]._m128i, CPU.GPR[ra]._m128i); } void OR(u32 rt, u32 ra, u32 rb) { - CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] | CPU.GPR[rb]._u32[0]; - CPU.GPR[rt]._u32[1] = CPU.GPR[ra]._u32[1] | CPU.GPR[rb]._u32[1]; - CPU.GPR[rt]._u32[2] = CPU.GPR[ra]._u32[2] | CPU.GPR[rb]._u32[2]; - CPU.GPR[rt]._u32[3] = CPU.GPR[ra]._u32[3] | CPU.GPR[rb]._u32[3]; + //(SSE2) POR - Bitwise Logical Or + CPU.GPR[rt]._m128i = _mm_or_si128(CPU.GPR[ra]._m128i, CPU.GPR[rb]._m128i); } void BG(u32 rt, u32 ra, u32 rb) { @@ -100,20 +96,24 @@ private: } void SFH(u32 rt, u32 ra, u32 rb) { - for (int h = 0; h < 8; h++) - CPU.GPR[rt]._u16[h] = CPU.GPR[rb]._u16[h] - CPU.GPR[ra]._u16[h]; + //(SSE2) PSUBW - Subtract Packed Words + CPU.GPR[rt]._m128i = _mm_sub_epi16(CPU.GPR[rb]._m128i, CPU.GPR[ra]._m128i); } void NOR(u32 rt, u32 ra, u32 rb) { - CPU.GPR[rt]._u32[0] = ~(CPU.GPR[ra]._u32[0] | CPU.GPR[rb]._u32[0]); - CPU.GPR[rt]._u32[1] = ~(CPU.GPR[ra]._u32[1] | CPU.GPR[rb]._u32[1]); - CPU.GPR[rt]._u32[2] = ~(CPU.GPR[ra]._u32[2] | CPU.GPR[rb]._u32[2]); - CPU.GPR[rt]._u32[3] = ~(CPU.GPR[ra]._u32[3] | CPU.GPR[rb]._u32[3]); + //(SSE2) POR - Bitwise Logical Or + //(SSE2) PXOR - Logical Exclusive Or + const __u32x4 InvMask = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; + CPU.GPR[rt]._m128i = _mm_xor_si128(_mm_or_si128(CPU.GPR[ra]._m128i, CPU.GPR[rb]._m128i), InvMask.m128i); } void ABSDB(u32 rt, u32 ra, u32 rb) { - for (int b = 0; b < 16; b++) - CPU.GPR[rt]._u8[b] = CPU.GPR[rb]._u8[b] > CPU.GPR[ra]._u8[b] ? CPU.GPR[rb]._u8[b] - CPU.GPR[ra]._u8[b] : CPU.GPR[ra]._u8[b] - CPU.GPR[rb]._u8[b]; + //(SSE2) PMINUB - Minimum of Packed Unsigned Byte Integers + //(SSE2) PMAXUB - Maximum of Packed Unsigned Byte Integers + //(SSE2) PSUBB - Subtract Packed Bytes + const __m128i _a = CPU.GPR[ra]._m128i; + const __m128i _b = CPU.GPR[rb]._m128i; + CPU.GPR[rt]._m128i = _mm_sub_epi8(_mm_max_epu8(_a, _b), _mm_min_epu8(_a, _b)); } void ROT(u32 rt, u32 ra, u32 rb) { From 34ac4688e6bac6235ac0026a9aae655e042ab632 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sun, 8 Dec 2013 18:03:51 +0400 Subject: [PATCH 02/16] MFC Minor changes --- rpcs3/Emu/Cell/MFC.h | 12 ++++++++++ rpcs3/Emu/Cell/SPUThread.h | 46 ++++++++++++++------------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/rpcs3/Emu/Cell/MFC.h b/rpcs3/Emu/Cell/MFC.h index c928078c46..c15947cbc7 100644 --- a/rpcs3/Emu/Cell/MFC.h +++ b/rpcs3/Emu/Cell/MFC.h @@ -5,6 +5,18 @@ enum { MFC_PUT_CMD = 0x20, MFC_GET_CMD = 0x40, + MFC_PUTL_CMD = 0x24, + MFC_GETL_CMD = 0x44, + MFC_GETLLAR_CMD = 0xD0, + MFC_PUTLLC_CMD = 0xB4, + MFC_PUTLLUC_CMD = 0xB0, + MFC_PUTQLLUC_CMD= 0xB8, + MFC_SNDSIG_CMD = 0xA0, + MFC_BARRIER_CMD = 0xC0, + MFC_EIEIO_CMD = 0xC8, + MFC_SYNC_CMD = 0xCC, + MFC_BARRIER_MASK= 0x01, + MFC_FENCE_MASK = 0x02, MFC_MASK_CMD = 0xffff, }; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 020882af20..1745fc37b6 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -358,7 +358,7 @@ public: u32 cmd = MFC.CMDStatus.GetValue(); u16 op = cmd & MFC_MASK_CMD; - switch(op & (MFC_PUT_CMD | MFC_GET_CMD)) + switch(op & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) { case MFC_PUT_CMD: case MFC_GET_CMD: @@ -391,34 +391,6 @@ public: { switch(ch) { - case SPU_RdEventStat: //Read event status with mask applied - case SPU_WrEventMask: //Write event mask - case SPU_WrEventAck: //Write end of event processing - case SPU_RdSigNotify1: //Signal notification 1 - case SPU_RdSigNotify2: //Signal notification 2 - case SPU_WrDec: //Write decrementer count - case SPU_RdDec: //Read decrementer count - case SPU_RdEventMask: //Read event mask - case SPU_RdMachStat: //Read SPU run status - case SPU_WrSRR0: //Write SPU machine state save/restore register 0 (SRR0) - case SPU_RdSRR0: //Read SPU machine state save/restore register 0 (SRR0) - case MFC_WrMSSyncReq: //Write multisource synchronization request - case MFC_RdTagMask: //Read tag mask - case MFC_LSA: //Write local memory address command parameter - case MFC_EAH: //Write high order DMA effective address command parameter - case MFC_EAL: //Write low order DMA effective address command parameter - case MFC_Size: //Write DMA transfer size command parameter - case MFC_TagID: //Write tag identifier command parameter - case MFC_Cmd: //Write and enqueue DMA command with associated class ID - case MFC_WrTagMask: //Write tag mask - case MFC_WrTagUpdate: //Write request for conditional or unconditional tag status update - case MFC_RdTagStat: //Read tag status with mask applied - case MFC_RdListStallStat: //Read DMA list stall-and-notify status - case MFC_WrListStallAck: //Write DMA list stall-and-notify acknowledge - case MFC_RdAtomicStat: //Read completion status of last completed immediate MFC atomic update command - ConLog.Error("%s error: unimplemented channel (%s).", __FUNCTION__, spu_ch_name[ch]); - break; - case SPU_WrOutMbox: return SPU.Out_MBox.GetFreeCount(); @@ -428,6 +400,22 @@ public: case SPU_WrOutIntrMbox: return 0;//return SPU.OutIntr_Mbox.GetFreeCount(); + case MFC_LSA: + return MFC.LSA.max_count; + + case MFC_EAH: + return MFC.EAH.max_count; + + case MFC_EAL: + return MFC.EAL.max_count; + + case MFC_Size: + case MFC_TagID: + return MFC.Size_Tag.max_count; + + case MFC_Cmd: + return MFC.CMDStatus.max_count; + default: ConLog.Error("%s error: unknown/illegal channel (%d [%s]).", __FUNCTION__, ch, spu_ch_name[ch]); break; From e8d2245a0b1b4dd645070052b7f36ddf68a8001e Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sun, 8 Dec 2013 21:44:52 +0400 Subject: [PATCH 03/16] Revert "SPU Improvement" This reverts commit 9bf8f2eec551b43ce5b433c7da70ae57d2305c4f. --- rpcs3/Emu/Cell/SPUInterpreter.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index 5acd50b0d4..e71eb5a7f0 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -79,13 +79,17 @@ private: } void SF(u32 rt, u32 ra, u32 rb) { - //(SSE2) PSUBD - Subtract Packed Integers - CPU.GPR[rt]._m128i = _mm_sub_epi32(CPU.GPR[rb]._m128i, CPU.GPR[ra]._m128i); + CPU.GPR[rt]._u32[0] = CPU.GPR[rb]._u32[0] - CPU.GPR[ra]._u32[0]; + CPU.GPR[rt]._u32[1] = CPU.GPR[rb]._u32[1] - CPU.GPR[ra]._u32[1]; + CPU.GPR[rt]._u32[2] = CPU.GPR[rb]._u32[2] - CPU.GPR[ra]._u32[2]; + CPU.GPR[rt]._u32[3] = CPU.GPR[rb]._u32[3] - CPU.GPR[ra]._u32[3]; } void OR(u32 rt, u32 ra, u32 rb) { - //(SSE2) POR - Bitwise Logical Or - CPU.GPR[rt]._m128i = _mm_or_si128(CPU.GPR[ra]._m128i, CPU.GPR[rb]._m128i); + CPU.GPR[rt]._u32[0] = CPU.GPR[ra]._u32[0] | CPU.GPR[rb]._u32[0]; + CPU.GPR[rt]._u32[1] = CPU.GPR[ra]._u32[1] | CPU.GPR[rb]._u32[1]; + CPU.GPR[rt]._u32[2] = CPU.GPR[ra]._u32[2] | CPU.GPR[rb]._u32[2]; + CPU.GPR[rt]._u32[3] = CPU.GPR[ra]._u32[3] | CPU.GPR[rb]._u32[3]; } void BG(u32 rt, u32 ra, u32 rb) { @@ -96,24 +100,20 @@ private: } void SFH(u32 rt, u32 ra, u32 rb) { - //(SSE2) PSUBW - Subtract Packed Words - CPU.GPR[rt]._m128i = _mm_sub_epi16(CPU.GPR[rb]._m128i, CPU.GPR[ra]._m128i); + for (int h = 0; h < 8; h++) + CPU.GPR[rt]._u16[h] = CPU.GPR[rb]._u16[h] - CPU.GPR[ra]._u16[h]; } void NOR(u32 rt, u32 ra, u32 rb) { - //(SSE2) POR - Bitwise Logical Or - //(SSE2) PXOR - Logical Exclusive Or - const __u32x4 InvMask = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; - CPU.GPR[rt]._m128i = _mm_xor_si128(_mm_or_si128(CPU.GPR[ra]._m128i, CPU.GPR[rb]._m128i), InvMask.m128i); + CPU.GPR[rt]._u32[0] = ~(CPU.GPR[ra]._u32[0] | CPU.GPR[rb]._u32[0]); + CPU.GPR[rt]._u32[1] = ~(CPU.GPR[ra]._u32[1] | CPU.GPR[rb]._u32[1]); + CPU.GPR[rt]._u32[2] = ~(CPU.GPR[ra]._u32[2] | CPU.GPR[rb]._u32[2]); + CPU.GPR[rt]._u32[3] = ~(CPU.GPR[ra]._u32[3] | CPU.GPR[rb]._u32[3]); } void ABSDB(u32 rt, u32 ra, u32 rb) { - //(SSE2) PMINUB - Minimum of Packed Unsigned Byte Integers - //(SSE2) PMAXUB - Maximum of Packed Unsigned Byte Integers - //(SSE2) PSUBB - Subtract Packed Bytes - const __m128i _a = CPU.GPR[ra]._m128i; - const __m128i _b = CPU.GPR[rb]._m128i; - CPU.GPR[rt]._m128i = _mm_sub_epi8(_mm_max_epu8(_a, _b), _mm_min_epu8(_a, _b)); + for (int b = 0; b < 16; b++) + CPU.GPR[rt]._u8[b] = CPU.GPR[rb]._u8[b] > CPU.GPR[ra]._u8[b] ? CPU.GPR[rb]._u8[b] - CPU.GPR[ra]._u8[b] : CPU.GPR[ra]._u8[b] - CPU.GPR[rb]._u8[b]; } void ROT(u32 rt, u32 ra, u32 rb) { From c9fc99c388c9361600ef8dc8c4e024b61add94e0 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Wed, 11 Dec 2013 02:58:11 +0400 Subject: [PATCH 04/16] Basic DMA, MFC, channel changes Added new consts, thread-safe locks for critical resources, removed some warnings. Modified git-version.h --- rpcs3/Emu/Cell/MFC.h | 60 ++++++++++++++++++------------ rpcs3/Emu/Cell/RawSPUThread.cpp | 29 +++++++-------- rpcs3/Emu/Cell/SPUThread.cpp | 5 ++- rpcs3/Emu/Cell/SPUThread.h | 66 ++++++++++++++++++--------------- rpcs3/git-version.h | 4 +- 5 files changed, 93 insertions(+), 71 deletions(-) diff --git a/rpcs3/Emu/Cell/MFC.h b/rpcs3/Emu/Cell/MFC.h index c15947cbc7..54700c3285 100644 --- a/rpcs3/Emu/Cell/MFC.h +++ b/rpcs3/Emu/Cell/MFC.h @@ -3,21 +3,22 @@ enum { - MFC_PUT_CMD = 0x20, - MFC_GET_CMD = 0x40, - MFC_PUTL_CMD = 0x24, - MFC_GETL_CMD = 0x44, - MFC_GETLLAR_CMD = 0xD0, - MFC_PUTLLC_CMD = 0xB4, - MFC_PUTLLUC_CMD = 0xB0, - MFC_PUTQLLUC_CMD= 0xB8, - MFC_SNDSIG_CMD = 0xA0, - MFC_BARRIER_CMD = 0xC0, - MFC_EIEIO_CMD = 0xC8, - MFC_SYNC_CMD = 0xCC, - MFC_BARRIER_MASK= 0x01, - MFC_FENCE_MASK = 0x02, - MFC_MASK_CMD = 0xffff, + MFC_PUT_CMD = 0x20, MFC_PUTB_CMD = 0x21, MFC_PUTF_CMD = 0x22, + MFC_GET_CMD = 0x40, MFC_GETB_CMD = 0x41, MFC_GETF_CMD = 0x42, + MFC_PUTL_CMD = 0x24, MFC_PUTLB_CMD = 0x25, MFC_PUTLF_CMD = 0x26, + MFC_GETL_CMD = 0x44, MFC_GETLB_CMD = 0x45, MFC_GETLF_CMD = 0x46, + MFC_GETLLAR_CMD = 0xD0, + MFC_PUTLLC_CMD = 0xB4, + MFC_PUTLLUC_CMD = 0xB0, + MFC_PUTQLLUC_CMD = 0xB8, + MFC_SNDSIG_CMD = 0xA0, MFC_SNDSIGB_CMD = 0xA1, MFC_SNDSIGF_CMD = 0xA2, + MFC_BARRIER_CMD = 0xC0, + MFC_EIEIO_CMD = 0xC8, + MFC_SYNC_CMD = 0xCC, + + MFC_BARRIER_MASK = 0x01, + MFC_FENCE_MASK = 0x02, + MFC_MASK_CMD = 0xffff, }; enum @@ -142,11 +143,13 @@ public: struct DMAC { - DMAC_Queue queue[MFC_SPU_MAX_QUEUE_SPACE]; - DMAC_Proxy proxy[MFC_PPU_MAX_QUEUE_SPACE]; + //DMAC_Queue queue[MFC_SPU_MAX_QUEUE_SPACE]; //not used yet + DMAC_Proxy proxy[MFC_PPU_MAX_QUEUE_SPACE+MFC_SPU_MAX_QUEUE_SPACE]; //temporarily 24 u64 ls_offset; - std::atomic queue_pos; - std::atomic proxy_pos; + u32 queue_pos; + u32 proxy_pos; + u32 queue_lock; + u32 proxy_lock; u32 Cmd(u32 cmd, u32 tag, u32 lsa, u64 ea, u32 size) { @@ -160,31 +163,42 @@ struct DMAC return MFC_PPU_DMA_QUEUE_FULL; } - DMAC_Proxy& p = proxy[proxy_pos++]; + while (_InterlockedCompareExchange(&proxy_lock, 1, 0)); + DMAC_Proxy& p = proxy[proxy_pos]; p.cmd = cmd; p.tag = tag; p.lsa = lsa; p.ea = ea; p.size = size; + proxy_pos++; + proxy_lock = 0; return MFC_PPU_DMA_CMD_ENQUEUE_SUCCESSFUL; } + void ClearCmd() + { + while (_InterlockedCompareExchange(&proxy_lock, 1, 0)); + memcpy(proxy, proxy + 1, --proxy_pos * sizeof(DMAC_Proxy)); + proxy_lock = 0; //release lock + } + void DoCmd() { if(proxy_pos) { - DMAC_Proxy p = proxy[0]; - memcpy(proxy, proxy + 1, --proxy_pos * sizeof(DMAC_Proxy)); + const DMAC_Proxy& p = proxy[0]; - switch(p.cmd) + switch(p.cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) //barrier/fence ignored { case MFC_PUT_CMD: memcpy(Memory + p.ea, Memory + ls_offset + p.lsa, p.size); + ClearCmd(); break; case MFC_GET_CMD: memcpy(Memory + ls_offset + p.lsa, Memory + p.ea, p.size); + ClearCmd(); break; default: diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index ee0cf6c4b3..17d0512701 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -57,12 +57,12 @@ bool RawSPUThread::Read32(const u64 addr, u32* value) u32 offset = addr - GetStartAddr() - RAW_SPU_PROB_OFFSET; switch(offset) { - case MFC_LSA_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_LSA)", m_index); *value = MFC.LSA.GetValue(); break; - case MFC_EAH_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_EAH)", m_index); *value = MFC.EAH.GetValue(); break; - case MFC_EAL_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_EAL)", m_index); *value = MFC.EAL.GetValue(); break; - case MFC_Size_Tag_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_Size_Tag)", m_index); *value = MFC.Size_Tag.GetValue(); break; - case MFC_CMDStatus_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_CMDStatus)", m_index); *value = MFC.CMDStatus.GetValue(); break; - case MFC_QStatus_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_QStatus)", m_index); *value = MFC.QStatus.GetValue(); break; + case MFC_LSA_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_LSA)", m_index); *value = MFC2.LSA.GetValue(); break; + case MFC_EAH_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_EAH)", m_index); *value = MFC2.EAH.GetValue(); break; + case MFC_EAL_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_EAL)", m_index); *value = MFC2.EAL.GetValue(); break; + case MFC_Size_Tag_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_Size_Tag)", m_index); *value = MFC2.Size_Tag.GetValue(); break; + case MFC_CMDStatus_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_CMDStatus)", m_index); *value = MFC2.CMDStatus.GetValue(); break; + case MFC_QStatus_offs: *value = MFC2.QStatus.GetValue(); break; case Prxy_QueryType_offs: ConLog.Warning("RawSPUThread[%d]: Read32(Prxy_QueryType)", m_index); *value = Prxy.QueryType.GetValue(); break; case Prxy_QueryMask_offs: ConLog.Warning("RawSPUThread[%d]: Read32(Prxy_QueryMask)", m_index); *value = Prxy.QueryMask.GetValue(); break; case Prxy_TagStatus_offs: ConLog.Warning("RawSPUThread[%d]: Read32(Prxy_TagStatus)", m_index); *value = Prxy.TagStatus.GetValue(); break; @@ -151,16 +151,15 @@ bool RawSPUThread::Write32(const u64 addr, const u32 value) switch(offset) { - case MFC_LSA_offs: ConLog.Warning("RawSPUThread[%d]: Write32(MFC_LSA, 0x%x)", m_index, value); MFC.LSA.SetValue(value); break; - case MFC_EAH_offs: ConLog.Warning("RawSPUThread[%d]: Write32(MFC_EAH, 0x%x)", m_index, value); MFC.EAH.SetValue(value); break; - case MFC_EAL_offs: ConLog.Warning("RawSPUThread[%d]: Write32(MFC_EAL, 0x%x)", m_index, value); MFC.EAL.SetValue(value); break; - case MFC_Size_Tag_offs: ConLog.Warning("RawSPUThread[%d]: Write32(MFC_Size_Tag, 0x%x)", m_index, value); MFC.Size_Tag.SetValue(value); break; + case MFC_LSA_offs: MFC2.LSA.SetValue(value); break; + case MFC_EAH_offs: MFC2.EAH.SetValue(value); break; + case MFC_EAL_offs: MFC2.EAL.SetValue(value); break; + case MFC_Size_Tag_offs: MFC2.Size_Tag.SetValue(value); break; case MFC_CMDStatus_offs: - ConLog.Warning("RawSPUThread[%d]: Write32(MFC_CMDStatus, 0x%x)", m_index, value); - MFC.CMDStatus.SetValue(value); - DoMfcCmd(); + MFC2.CMDStatus.SetValue(value); + EnqMfcCmd(MFC2); break; - case MFC_QStatus_offs: ConLog.Warning("RawSPUThread[%d]: Write32(MFC_QStatus, 0x%x)", m_index, value); MFC.QStatus.SetValue(value); break; + case MFC_QStatus_offs: ConLog.Warning("RawSPUThread[%d]: Write32(MFC_QStatus, 0x%x)", m_index, value); MFC2.QStatus.SetValue(value); break; case Prxy_QueryType_offs: { ConLog.Warning("RawSPUThread[%d]: Write32(Prxy_QueryType, 0x%x)", m_index, value); @@ -178,7 +177,7 @@ bool RawSPUThread::Write32(const u64 addr, const u32 value) } Prxy.QueryType.SetValue(0); - MFC.QStatus.SetValue(Prxy.QueryMask.GetValue()); + MFC2.QStatus.SetValue(Prxy.QueryMask.GetValue()); } break; case Prxy_QueryMask_offs: ConLog.Warning("RawSPUThread[%d]: Write32(Prxy_QueryMask, 0x%x)", m_index, value); Prxy.QueryMask.SetValue(value); break; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 2faacb49bd..34e24b5471 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -46,11 +46,14 @@ void SPUThread::InitRegs() dmac.ls_offset = m_offset; dmac.proxy_pos = 0; dmac.queue_pos = 0; + dmac.proxy_lock = 0; + dmac.queue_lock = 0; SPU.RunCntl.SetValue(SPU_RUNCNTL_STOP); SPU.Status.SetValue(SPU_STATUS_RUNNING); Prxy.QueryType.SetValue(0); - MFC.CMDStatus.SetValue(0); + MFC1.CMDStatus.SetValue(0); + MFC2.CMDStatus.SetValue(0); //PC = SPU.NPC.GetValue(); } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 1745fc37b6..106306c181 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -265,6 +265,7 @@ public: private: u32 m_value[max_count]; u32 m_index; + u32 m_lock; public: @@ -276,19 +277,32 @@ public: void Init() { m_index = 0; + m_lock = 0; //simple lock } __forceinline bool Pop(u32& res) { - if(!m_index) return false; + while (_InterlockedCompareExchange(&m_lock, 1, 0)); + if(!m_index) + { + m_lock = 0; //release lock + return false; + } res = m_value[--m_index]; + m_lock = 0; return true; } __forceinline bool Push(u32 value) { - if(m_index >= max_count) return false; + while (_InterlockedCompareExchange(&m_lock, 1, 0)); + if(m_index >= max_count) + { + m_lock = 0; //release lock + return false; + } m_value[m_index++] = value; + m_lock = 0; return true; } @@ -313,7 +327,7 @@ public: } }; - struct + struct MFCReg { Channel<1> LSA; Channel<1> EAH; @@ -321,7 +335,7 @@ public: Channel<1> Size_Tag; Channel<1> CMDStatus; Channel<1> QStatus; - } MFC; + } MFC1, MFC2; struct { @@ -353,9 +367,9 @@ public: DMAC dmac; - void DoMfcCmd() + void EnqMfcCmd(MFCReg& MFCArgs) { - u32 cmd = MFC.CMDStatus.GetValue(); + u32 cmd = MFCArgs.CMDStatus.GetValue(); u16 op = cmd & MFC_MASK_CMD; switch(op & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) @@ -363,21 +377,13 @@ public: 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(); + u32 lsa = MFCArgs.LSA.GetValue(); + u64 ea = (u64)MFCArgs.EAL.GetValue() | ((u64)MFCArgs.EAH.GetValue() << 32); + u32 size_tag = MFCArgs.Size_Tag.GetValue(); u16 tag = (u16)size_tag; u16 size = size_tag >> 16; - ConLog.Warning("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.Warning("*** cmd = 0x%x", cmd); - ConLog.SkipLn(); - - MFC.CMDStatus.SetValue(dmac.Cmd(cmd, tag, lsa, ea, size)); + MFCArgs.CMDStatus.SetValue(dmac.Cmd(cmd, tag, lsa, ea, size)); } break; @@ -401,20 +407,20 @@ public: return 0;//return SPU.OutIntr_Mbox.GetFreeCount(); case MFC_LSA: - return MFC.LSA.max_count; + return MFC1.LSA.max_count; case MFC_EAH: - return MFC.EAH.max_count; + return MFC1.EAH.max_count; case MFC_EAL: - return MFC.EAL.max_count; + return MFC1.EAL.max_count; case MFC_Size: case MFC_TagID: - return MFC.Size_Tag.max_count; + return MFC1.Size_Tag.max_count; case MFC_Cmd: - return MFC.CMDStatus.max_count; + return MFC1.CMDStatus.max_count; default: ConLog.Error("%s error: unknown/illegal channel (%d [%s]).", __FUNCTION__, ch, spu_ch_name[ch]); @@ -455,28 +461,28 @@ public: break; case MFC_LSA: - MFC.LSA.SetValue(v); + MFC1.LSA.SetValue(v); break; case MFC_EAH: - MFC.EAH.SetValue(v); + MFC1.EAH.SetValue(v); break; case MFC_EAL: - MFC.EAL.SetValue(v); + MFC1.EAL.SetValue(v); break; case MFC_Size: - MFC.Size_Tag.SetValue((MFC.Size_Tag.GetValue() & 0xffff) | (v << 16)); + MFC1.Size_Tag.SetValue((MFC1.Size_Tag.GetValue() & 0xffff) | (v << 16)); break; case MFC_TagID: - MFC.Size_Tag.SetValue((MFC.Size_Tag.GetValue() & ~0xffff) | (v & 0xffff)); + MFC1.Size_Tag.SetValue((MFC1.Size_Tag.GetValue() & ~0xffff) | (v & 0xffff)); break; case MFC_Cmd: - MFC.CMDStatus.SetValue(v); - DoMfcCmd(); + MFC1.CMDStatus.SetValue(v); + EnqMfcCmd(MFC1); break; default: diff --git a/rpcs3/git-version.h b/rpcs3/git-version.h index 07a3d127dd..f770897486 100644 --- a/rpcs3/git-version.h +++ b/rpcs3/git-version.h @@ -1,6 +1,6 @@ // This is a generated file. -#define RPCS3_GIT_VERSION "d83a9b1" +#define RPCS3_GIT_VERSION "unknown" // If you don't want this file to update/recompile, change to 1. -#define RPCS3_GIT_VERSION_NO_UPDATE 0 +#define RPCS3_GIT_VERSION_NO_UPDATE 1 From ee137323aa4f635e93318860768451bae3486aab Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Fri, 13 Dec 2013 05:35:28 +0400 Subject: [PATCH 05/16] MBox, some improvements Some changes reverted (warnings), multi-thread safety fixed, MBox MMIO modified --- rpcs3/Emu/Cell/MFC.h | 9 +++-- rpcs3/Emu/Cell/RawSPUThread.cpp | 15 +++++++-- rpcs3/Emu/Cell/SPUThread.h | 60 +++++++++++++++++++++++---------- 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/rpcs3/Emu/Cell/MFC.h b/rpcs3/Emu/Cell/MFC.h index 54700c3285..9f63247e1a 100644 --- a/rpcs3/Emu/Cell/MFC.h +++ b/rpcs3/Emu/Cell/MFC.h @@ -163,14 +163,17 @@ struct DMAC return MFC_PPU_DMA_QUEUE_FULL; } - while (_InterlockedCompareExchange(&proxy_lock, 1, 0)); + while (_InterlockedExchange(&proxy_lock, 1)); + _mm_lfence(); DMAC_Proxy& p = proxy[proxy_pos]; p.cmd = cmd; p.tag = tag; p.lsa = lsa; p.ea = ea; p.size = size; + _mm_sfence(); //for DoCmd() proxy_pos++; + _mm_sfence(); proxy_lock = 0; return MFC_PPU_DMA_CMD_ENQUEUE_SUCCESSFUL; @@ -178,8 +181,10 @@ struct DMAC void ClearCmd() { - while (_InterlockedCompareExchange(&proxy_lock, 1, 0)); + while (_InterlockedExchange(&proxy_lock, 1)); + _mm_lfence(); memcpy(proxy, proxy + 1, --proxy_pos * sizeof(DMAC_Proxy)); + _mm_sfence(); proxy_lock = 0; //release lock } diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 17d0512701..248321e99b 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -62,11 +62,17 @@ bool RawSPUThread::Read32(const u64 addr, u32* value) case MFC_EAL_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_EAL)", m_index); *value = MFC2.EAL.GetValue(); break; case MFC_Size_Tag_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_Size_Tag)", m_index); *value = MFC2.Size_Tag.GetValue(); break; case MFC_CMDStatus_offs: ConLog.Warning("RawSPUThread[%d]: Read32(MFC_CMDStatus)", m_index); *value = MFC2.CMDStatus.GetValue(); break; - case MFC_QStatus_offs: *value = MFC2.QStatus.GetValue(); break; + case MFC_QStatus_offs: + ConLog.Warning("RawSPUThread[%d]: Read32(MFC_QStatus)", m_index); + *value = MFC2.QStatus.GetValue(); + break; case Prxy_QueryType_offs: ConLog.Warning("RawSPUThread[%d]: Read32(Prxy_QueryType)", m_index); *value = Prxy.QueryType.GetValue(); break; case Prxy_QueryMask_offs: ConLog.Warning("RawSPUThread[%d]: Read32(Prxy_QueryMask)", m_index); *value = Prxy.QueryMask.GetValue(); break; case Prxy_TagStatus_offs: ConLog.Warning("RawSPUThread[%d]: Read32(Prxy_TagStatus)", m_index); *value = Prxy.TagStatus.GetValue(); break; - case SPU_Out_MBox_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_Out_MBox)", m_index); while(!SPU.Out_MBox.Pop(*value) && !Emu.IsStopped()) Sleep(1); break; + case SPU_Out_MBox_offs: + ConLog.Warning("RawSPUThread[%d]: Read32(SPU_Out_MBox)", m_index); + SPU.Out_MBox.PopUncond(*value); //if Out_MBox is empty yet, the result will be undefined + break; case SPU_In_MBox_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_In_MBox)", m_index); while(!SPU.In_MBox.Pop(*value) && !Emu.IsStopped()) Sleep(1); break; case SPU_MBox_Status_offs: //ConLog.Warning("RawSPUThread[%d]: Read32(SPU_MBox_Status)", m_index); //SPU.MBox_Status.SetValue(SPU.Out_MBox.GetCount() ? SPU.MBox_Status.GetValue() | 1 : SPU.MBox_Status.GetValue() & ~1); @@ -183,7 +189,10 @@ bool RawSPUThread::Write32(const u64 addr, const u32 value) case Prxy_QueryMask_offs: ConLog.Warning("RawSPUThread[%d]: Write32(Prxy_QueryMask, 0x%x)", m_index, value); Prxy.QueryMask.SetValue(value); break; case Prxy_TagStatus_offs: ConLog.Warning("RawSPUThread[%d]: Write32(Prxy_TagStatus, 0x%x)", m_index, value); Prxy.TagStatus.SetValue(value); break; case SPU_Out_MBox_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_Out_MBox, 0x%x)", m_index, value); while(!SPU.Out_MBox.Push(value) && !Emu.IsStopped()) Sleep(1); break; - case SPU_In_MBox_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_In_MBox, 0x%x)", m_index, value); while(!SPU.In_MBox.Push(value) && !Emu.IsStopped()) Sleep(1); break; + case SPU_In_MBox_offs: + ConLog.Warning("RawSPUThread[%d]: Write32(SPU_In_MBox, 0x%x)", m_index, value); + SPU.In_MBox.PushUncond(value); //if In_MBox is already full, the last message will be overwritten + break; case SPU_MBox_Status_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_MBox_Status, 0x%x)", m_index, value); SPU.MBox_Status.SetValue(value); break; case SPU_RunCntl_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_RunCntl, 0x%x)", m_index, value); SPU.RunCntl.SetValue(value); break; case SPU_Status_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_Status, 0x%x)", m_index, value); SPU.Status.SetValue(value); break; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 106306c181..7e123062e8 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -282,37 +282,69 @@ public: __forceinline bool Pop(u32& res) { - while (_InterlockedCompareExchange(&m_lock, 1, 0)); + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); if(!m_index) { m_lock = 0; //release lock return false; } res = m_value[--m_index]; + _mm_sfence(); m_lock = 0; return true; } __forceinline bool Push(u32 value) { - while (_InterlockedCompareExchange(&m_lock, 1, 0)); + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); if(m_index >= max_count) { m_lock = 0; //release lock return false; } m_value[m_index++] = value; + _mm_sfence(); m_lock = 0; return true; } + __forceinline void PushUncond(u32 value) + { + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); + if(m_index >= max_count) + m_value[max_count-1] = value; //last message is overwritten + else + m_value[m_index++] = value; + _mm_sfence(); + m_lock = 0; + } + + __forceinline void PopUncond(u32& res) + { + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); + if(!m_index) + res = 0; //result is undefined + else + res = m_value[--m_index]; + _mm_sfence(); + m_lock = 0; + } + u32 GetCount() const { + while (m_lock); + _mm_lfence(); return m_index; } u32 GetFreeCount() const { + while (m_lock); + _mm_lfence(); return max_count - m_index; } @@ -383,6 +415,14 @@ public: u16 tag = (u16)size_tag; u16 size = size_tag >> 16; + ConLog.Warning("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.Warning("*** cmd = 0x%x", cmd); + ConLog.SkipLn(); + MFCArgs.CMDStatus.SetValue(dmac.Cmd(cmd, tag, lsa, ea, size)); } break; @@ -406,22 +446,6 @@ public: case SPU_WrOutIntrMbox: return 0;//return SPU.OutIntr_Mbox.GetFreeCount(); - case MFC_LSA: - return MFC1.LSA.max_count; - - case MFC_EAH: - return MFC1.EAH.max_count; - - case MFC_EAL: - return MFC1.EAL.max_count; - - case MFC_Size: - case MFC_TagID: - return MFC1.Size_Tag.max_count; - - case MFC_Cmd: - return MFC1.CMDStatus.max_count; - default: ConLog.Error("%s error: unknown/illegal channel (%d [%s]).", __FUNCTION__, ch, spu_ch_name[ch]); break; From eed6a7ffe3356f1770a9e33aa1f156da4e125992 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sat, 14 Dec 2013 04:15:20 +0400 Subject: [PATCH 06/16] STBUX implemented Missed PPU opcode + replaced some non-unicode non-ASCII characters in PPPInstrTable.h --- rpcs3/Emu/Cell/PPUDisAsm.h | 4 ++++ rpcs3/Emu/Cell/PPUInstrTable.h | 7 ++++--- rpcs3/Emu/Cell/PPUInterpreter.h | 6 ++++++ rpcs3/Emu/Cell/PPUOpcodes.h | 2 ++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUDisAsm.h b/rpcs3/Emu/Cell/PPUDisAsm.h index 7aea473b42..501cc669a6 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.h +++ b/rpcs3/Emu/Cell/PPUDisAsm.h @@ -1399,6 +1399,10 @@ private: { DisAsm_R3("stdux", rs, ra, rb); } + void STBUX(u32 rs, u32 ra, u32 rb) + { + DisAsm_R3("stbux", rs, ra, rb); + } void STVEWX(u32 vs, u32 ra, u32 rb) { DisAsm_V1_R2("stvewx", vs, ra, rb); diff --git a/rpcs3/Emu/Cell/PPUInstrTable.h b/rpcs3/Emu/Cell/PPUInstrTable.h index ea524f4827..4dc5c9d132 100644 --- a/rpcs3/Emu/Cell/PPUInstrTable.h +++ b/rpcs3/Emu/Cell/PPUInstrTable.h @@ -78,7 +78,7 @@ namespace PPU_instr static CodeField<11, 15> BI; //Immediate field specifying a 14-bit signed two's complement branch displacement that is concatenated on the - //right with ‘00’ and sign-extended to 64 bits. + //right with '00' and sign-extended to 64 bits. static CodeFieldSigned<16, 31> BD(FIELD_BRANCH); // @@ -179,9 +179,9 @@ namespace PPU_instr Record bit. 0 Does not update the condition register (CR). 1 Updates the CR to reflect the result of the operation. - For integer instructions, CR bits [0–2] are set to reflect the result as a signed quantity and CR bit [3] + For integer instructions, CR bits [0-2] are set to reflect the result as a signed quantity and CR bit [3] receives a copy of the summary overflow bit, XER[SO]. The result as an unsigned quantity or a bit - string can be deduced from the EQ bit. For floating-point instructions, CR bits [4–7] are set to reflect + string can be deduced from the EQ bit. For floating-point instructions, CR bits [4-7] are set to reflect floating-point exception, floating-point enabled exception, floating-point invalid operation exception, and floating-point overflow exception. */ @@ -482,6 +482,7 @@ namespace PPU_instr /*0x097*/bind_instr(g1f_list, STWX, RS, RA, RB); /*0x0a7*/bind_instr(g1f_list, STVEHX, VS, RA, RB); /*0x0b5*/bind_instr(g1f_list, STDUX, RS, RA, RB); + /*0x0f7*/bind_instr(g1f_list, STBUX, RS, RA, RB); /*0x0c7*/bind_instr(g1f_list, STVEWX, VS, RA, RB); /*0x0ca*/bind_instr(g1f_list, ADDZE, RD, RA, OE, RC); /*0x0d6*/bind_instr(g1f_list, STDCX_, RS, RA, RB); diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index b6b54d4302..b35e23ebc1 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -2650,6 +2650,12 @@ private: Memory.Write64(addr, CPU.GPR[rs]); CPU.GPR[ra] = addr; } + void STBUX(u32 rs, u32 ra, u32 rb) + { + const u64 addr = CPU.GPR[ra] + CPU.GPR[rb]; + Memory.Write8(addr, CPU.GPR[rs]); + CPU.GPR[ra] = addr; + } void STVEWX(u32 vs, u32 ra, u32 rb) { const u64 addr = (ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]) & ~3ULL; diff --git a/rpcs3/Emu/Cell/PPUOpcodes.h b/rpcs3/Emu/Cell/PPUOpcodes.h index 2f10abcfa1..b74e82ab04 100644 --- a/rpcs3/Emu/Cell/PPUOpcodes.h +++ b/rpcs3/Emu/Cell/PPUOpcodes.h @@ -297,6 +297,7 @@ namespace PPU_opcodes STWX = 0x097, STVEHX = 0x0a7, //Store Vector Element Halfword Indexed STDUX = 0x0b5, + STBUX = 0x0f7, STVEWX = 0x0c7, //Store Vector Element Word Indexed ADDZE = 0x0ca, STDCX_ = 0x0d6, @@ -678,6 +679,7 @@ public: virtual void STWX(u32 rs, u32 ra, u32 rb) = 0; virtual void STVEHX(u32 vs, u32 ra, u32 rb) = 0; virtual void STDUX(u32 rs, u32 ra, u32 rb) = 0; + virtual void STBUX(u32 rs, u32 ra, u32 rb) = 0; virtual void STVEWX(u32 vs, u32 ra, u32 rb) = 0; virtual void ADDZE(u32 rd, u32 ra, u32 oe, bool rc) = 0; virtual void STDCX_(u32 rs, u32 ra, u32 rb) = 0; From c3d2cd344f8042d2cb9b9dc0966748752e5e4d19 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sun, 15 Dec 2013 01:59:37 +0400 Subject: [PATCH 07/16] Win32 fix & elf loading fix Fixed compilation error for x86 version. Fixed issue with ELF loading. --- rpcs3/Emu/Cell/MFC.h | 4 ++-- rpcs3/Emu/Cell/SPUThread.h | 2 +- rpcs3/Emu/FS/vfsDevice.cpp | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/MFC.h b/rpcs3/Emu/Cell/MFC.h index 9f63247e1a..4523131b46 100644 --- a/rpcs3/Emu/Cell/MFC.h +++ b/rpcs3/Emu/Cell/MFC.h @@ -148,8 +148,8 @@ struct DMAC u64 ls_offset; u32 queue_pos; u32 proxy_pos; - u32 queue_lock; - u32 proxy_lock; + long queue_lock; + long proxy_lock; u32 Cmd(u32 cmd, u32 tag, u32 lsa, u64 ea, u32 size) { diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 7e123062e8..7dc71b74f1 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -265,7 +265,7 @@ public: private: u32 m_value[max_count]; u32 m_index; - u32 m_lock; + long m_lock; public: diff --git a/rpcs3/Emu/FS/vfsDevice.cpp b/rpcs3/Emu/FS/vfsDevice.cpp index 570e60dd52..d7fbc464a1 100644 --- a/rpcs3/Emu/FS/vfsDevice.cpp +++ b/rpcs3/Emu/FS/vfsDevice.cpp @@ -30,7 +30,11 @@ u32 vfsDevice::CmpPs3Path(const wxString& ps3_path) for(u32 i=0; i Date: Thu, 19 Dec 2013 02:30:06 +0400 Subject: [PATCH 08/16] Notification of NI flag change Probably useless. --- rpcs3/Emu/Cell/PPUInterpreter.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index b35e23ebc1..e2e8118895 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -3377,6 +3377,7 @@ private: void MTFSB1(u32 crbd, bool rc) { u64 mask = (1ULL << crbd); + if ((crbd == 29) && !CPU.FPSCR.NI) ConLog.Warning("Non-IEEE mode enabled"); CPU.FPSCR.FPSCR |= mask; if(rc) UNIMPLEMENTED(); @@ -3390,6 +3391,7 @@ private: void MTFSB0(u32 crbd, bool rc) { u64 mask = (1ULL << crbd); + if ((crbd == 29) && !CPU.FPSCR.NI) ConLog.Warning("Non-IEEE mode disabled"); CPU.FPSCR.FPSCR &= ~mask; if(rc) UNIMPLEMENTED(); @@ -3400,10 +3402,12 @@ private: if(i) { + if ((crfd == 29) && !CPU.FPSCR.NI) ConLog.Warning("Non-IEEE mode enabled"); CPU.FPSCR.FPSCR |= mask; } else { + if ((crfd == 29) && CPU.FPSCR.NI) ConLog.Warning("Non-IEEE mode disabled"); CPU.FPSCR.FPSCR &= ~mask; } @@ -3422,7 +3426,15 @@ private: if(flm & (1 << i)) mask |= 0xf << (i * 4); } + const u32 oldNI = CPU.FPSCR.NI; CPU.FPSCR.FPSCR = (CPU.FPSCR.FPSCR & ~mask) | ((u32&)CPU.FPR[frb] & mask); + if (CPU.FPSCR.NI != oldNI) + { + if (oldNI) + ConLog.Warning("Non-IEEE mode disabled"); + else + ConLog.Warning("Non-IEEE mode enabled"); + } if(rc) UNK("mtfsf."); } void FCMPU(u32 crfd, u32 fra, u32 frb) From c9abf559be4fecf68a07bc3c29f5d45e8325bd16 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sun, 22 Dec 2013 21:40:50 +0400 Subject: [PATCH 09/16] SPU Improvements Implemented SNR channels and syscalls, fixed sys_spu_thread_initialize (args' passing, SPU image copying for each thread) --- rpcs3/Emu/Cell/RawSPUThread.cpp | 8 +- rpcs3/Emu/Cell/SPUInterpreter.h | 8 +- rpcs3/Emu/Cell/SPUThread.cpp | 2 + rpcs3/Emu/Cell/SPUThread.h | 107 +++++++++++++++------- rpcs3/Emu/SysCalls/SysCalls.cpp | 4 +- rpcs3/Emu/SysCalls/SysCalls.h | 3 + rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 112 +++++++++++++++++++---- rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h | 1 + 8 files changed, 184 insertions(+), 61 deletions(-) diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 248321e99b..006c7b28d7 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -82,8 +82,8 @@ bool RawSPUThread::Read32(const u64 addr, u32* value) case SPU_RunCntl_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_RunCntl)", m_index); *value = SPU.RunCntl.GetValue(); break; case SPU_Status_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_Status)", m_index); *value = SPU.Status.GetValue(); break; case SPU_NPC_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_NPC)", m_index); *value = SPU.NPC.GetValue(); break; - case SPU_RdSigNotify1_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_RdSigNotify1)", m_index); *value = SPU.RdSigNotify1.GetValue(); break; - case SPU_RdSigNotify2_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_RdSigNotify2)", m_index); *value = SPU.RdSigNotify2.GetValue(); break; + case SPU_RdSigNotify1_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_RdSigNotify1)", m_index); *value = SPU.SNR[0].GetValue(); break; + case SPU_RdSigNotify2_offs: ConLog.Warning("RawSPUThread[%d]: Read32(SPU_RdSigNotify2)", m_index); *value = SPU.SNR[1].GetValue(); break; default: ConLog.Error("RawSPUThread[%d]: Read32(0x%x)", m_index, offset); @@ -197,8 +197,8 @@ bool RawSPUThread::Write32(const u64 addr, const u32 value) case SPU_RunCntl_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_RunCntl, 0x%x)", m_index, value); SPU.RunCntl.SetValue(value); break; case SPU_Status_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_Status, 0x%x)", m_index, value); SPU.Status.SetValue(value); break; case SPU_NPC_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_NPC, 0x%x)", m_index, value); SPU.NPC.SetValue(value); break; - case SPU_RdSigNotify1_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_RdSigNotify1, 0x%x)", m_index, value); SPU.RdSigNotify1.SetValue(value); break; - case SPU_RdSigNotify2_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_RdSigNotify2, 0x%x)", m_index, value); SPU.RdSigNotify2.SetValue(value); break; + case SPU_RdSigNotify1_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_RdSigNotify1, 0x%x)", m_index, value); SPU.SNR[0].SetValue(value); break; + case SPU_RdSigNotify2_offs: ConLog.Warning("RawSPUThread[%d]: Write32(SPU_RdSigNotify2, 0x%x)", m_index, value); SPU.SNR[1].SetValue(value); break; default: ConLog.Error("RawSPUThread[%d]: Write32(0x%x, 0x%x)", m_index, offset, value); diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index e71eb5a7f0..f4e88eccdc 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -35,24 +35,24 @@ private: if(code & 0x2000) { CPU.SetExitStatus(code & 0xfff); - CPU.Stop(); } else { ConLog.Warning("STOP: 0x%x", code); - Emu.Pause(); + //Emu.Pause(); } + CPU.Stop(); } void LNOP() { } void SYNC(u32 Cbit) { - //UNIMPLEMENTED(); + _mm_mfence(); } void DSYNC() { - //UNIMPLEMENTED(); + _mm_mfence(); } void MFSPR(u32 rt, u32 sa) { diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 34e24b5471..76732b7f58 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -43,6 +43,8 @@ void SPUThread::InitRegs() GPR[5]._u64[1] = m_args[2]; GPR[6]._u64[1] = m_args[3]; + cfg.Reset(); + dmac.ls_offset = m_offset; dmac.proxy_pos = 0; dmac.queue_pos = 0; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 7dc71b74f1..de0b0f17a8 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -249,12 +249,30 @@ union SPU_SPR_hdr } }; +union SPU_SNRConfig_hdr +{ + u64 value; + + SPU_SNRConfig_hdr() {} + + wxString ToString() const + { + return wxString::Format("%01x", value); + } + + void Reset() + { + memset(this, 0, sizeof(*this)); + } +}; + class SPUThread : public PPCThread { public: SPU_GPR_hdr GPR[128]; //General-Purpose Register SPU_SPR_hdr SPR[128]; //Special-Purpose Registers FPSCR FPSCR; + SPU_SNRConfig_hdr cfg; //Signal Notification Registers Configuration (OR-mode enabled: 0x1 for SNR1, 0x2 for SNR2) template class Channel @@ -322,6 +340,18 @@ public: m_lock = 0; } + __forceinline void PushUncond_OR(u32 value) + { + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); + if(m_index >= max_count) + m_value[max_count-1] |= value; //last message is logically ORed + else + m_value[m_index++] = value; + _mm_sfence(); + m_lock = 0; + } + __forceinline void PopUncond(u32& res) { while (_InterlockedExchange(&m_lock, 1)); @@ -385,8 +415,7 @@ public: Channel<1> RunCntl; Channel<1> Status; Channel<1> NPC; - Channel<1> RdSigNotify1; - Channel<1> RdSigNotify2; + Channel<1> SNR[2]; } SPU; u32 LSA; @@ -404,31 +433,29 @@ public: u32 cmd = MFCArgs.CMDStatus.GetValue(); u16 op = cmd & MFC_MASK_CMD; + u32 lsa = MFCArgs.LSA.GetValue(); + u64 ea = (u64)MFCArgs.EAL.GetValue() | ((u64)MFCArgs.EAH.GetValue() << 32); + u32 size_tag = MFCArgs.Size_Tag.GetValue(); + u16 tag = (u16)size_tag; + u16 size = size_tag >> 16; + switch(op & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) { case MFC_PUT_CMD: case MFC_GET_CMD: { - u32 lsa = MFCArgs.LSA.GetValue(); - u64 ea = (u64)MFCArgs.EAL.GetValue() | ((u64)MFCArgs.EAH.GetValue() << 32); - u32 size_tag = MFCArgs.Size_Tag.GetValue(); - u16 tag = (u16)size_tag; - u16 size = size_tag >> 16; - - ConLog.Warning("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.Warning("*** cmd = 0x%x", cmd); - ConLog.SkipLn(); - + ConLog.Warning("DMA %s%s%s: lsa = 0x%x, ea = 0x%llx, tag = 0x%x, size = 0x%x, cmd = 0x%x", + op & MFC_PUT_CMD ? "PUT" : "GET", + op & MFC_BARRIER_MASK ? "B" : "", + op & MFC_FENCE_MASK ? "F" : "", + lsa, ea, tag, size, cmd); MFCArgs.CMDStatus.SetValue(dmac.Cmd(cmd, tag, lsa, ea, size)); } break; default: - ConLog.Error("Unknown MFC cmd. (opcode=0x%x, cmd=0x%x)", op, cmd); + ConLog.Error("Unknown MFC cmd. (opcode=0x%x, cmd=0x%x, lsa = 0x%x, ea = 0x%llx, tag = 0x%x, size = 0x%x)", + op, cmd, lsa, ea, tag, size); break; } } @@ -446,6 +473,15 @@ public: case SPU_WrOutIntrMbox: return 0;//return SPU.OutIntr_Mbox.GetFreeCount(); + case MFC_RdTagStat: + return Prxy.TagStatus.GetCount(); + + case SPU_RdSigNotify1: + return SPU.SNR[0].GetCount(); + + case SPU_RdSigNotify2: + return SPU.SNR[1].GetCount(); + default: ConLog.Error("%s error: unknown/illegal channel (%d [%s]).", __FUNCTION__, ch, spu_ch_name[ch]); break; @@ -458,30 +494,26 @@ public: { const u32 v = r._u32[3]; - ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); - switch(ch) { case SPU_WrOutIntrMbox: - while(!SPU.OutIntr_Mbox.Push(v) && !Emu.IsStopped()) - { - Sleep(1); - } + ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); + while (!SPU.OutIntr_Mbox.Push(v) && !Emu.IsStopped()) _mm_pause(); break; case SPU_WrOutMbox: - while(!SPU.Out_MBox.Push(v) && !Emu.IsStopped()) - { - Sleep(1); - } + ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); + while (!SPU.Out_MBox.Push(v) && !Emu.IsStopped()) _mm_pause(); break; case MFC_WrTagMask: + //ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); Prxy.QueryMask.SetValue(v); break; case MFC_WrTagUpdate: - Prxy.TagStatus.SetValue(Prxy.QueryMask.GetValue()); + //ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); + Prxy.TagStatus.PushUncond(Prxy.QueryMask.GetValue()); break; case MFC_LSA: @@ -523,19 +555,30 @@ public: switch(ch) { case SPU_RdInMbox: - if(!SPU.In_MBox.Pop(v)) v = 0; + while (!SPU.In_MBox.Pop(v) && !Emu.IsStopped()) _mm_pause(); + ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; case MFC_RdTagStat: - v = Prxy.TagStatus.GetValue(); + while (dmac.proxy_pos) dmac.DoCmd(); //probably incompatible with MFC lists + while (!Prxy.TagStatus.Pop(v) && !Emu.IsStopped()) _mm_pause(); + //ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); + break; + + case SPU_RdSigNotify1: + while (!SPU.SNR[0].Pop(v) && !Emu.IsStopped()) _mm_pause(); + ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); + break; + + case SPU_RdSigNotify2: + while (!SPU.SNR[1].Pop(v) && !Emu.IsStopped()) _mm_pause(); + //ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; default: ConLog.Error("%s error: unknown/illegal channel (%d [%s]).", __FUNCTION__, ch, spu_ch_name[ch]); break; } - - ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); } bool IsGoodLSA(const u32 lsa) const { return Memory.IsGoodAddr(lsa + m_offset) && lsa < 0x40000; } diff --git a/rpcs3/Emu/SysCalls/SysCalls.cpp b/rpcs3/Emu/SysCalls/SysCalls.cpp index 686e5e78b7..959fd93dd1 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.cpp +++ b/rpcs3/Emu/SysCalls/SysCalls.cpp @@ -97,8 +97,8 @@ static func_caller* sc_table[1024] = null_func, null_func, null_func, null_func, bind_func(sys_spu_initialize), //169 bind_func(sys_spu_thread_group_create), bind_func(sys_spu_thread_set_argument), bind_func(sys_spu_thread_initialize), bind_func(sys_spu_thread_group_start), null_func, //174 null_func, null_func, null_func, null_func, null_func, //179 - null_func, bind_func(sys_spu_thread_write_ls), bind_func(sys_spu_thread_read_ls), null_func, null_func, //184 - null_func, null_func, null_func, null_func, null_func, //189 + null_func, bind_func(sys_spu_thread_write_ls), bind_func(sys_spu_thread_read_ls), null_func, bind_func(sys_spu_thread_write_snr), //184 + null_func, null_func, bind_func(sys_spu_thread_set_spu_cfg), bind_func(sys_spu_thread_get_spu_cfg), null_func, //189 bind_func(sys_spu_thread_write_spu_mb), bind_func(sys_spu_thread_connect_event), null_func, null_func, null_func, //194 null_func, null_func, null_func, null_func, null_func, //199 null_func, null_func, null_func, null_func, null_func, //204 diff --git a/rpcs3/Emu/SysCalls/SysCalls.h b/rpcs3/Emu/SysCalls/SysCalls.h index 7dfb90e6cf..02e481cf6c 100644 --- a/rpcs3/Emu/SysCalls/SysCalls.h +++ b/rpcs3/Emu/SysCalls/SysCalls.h @@ -292,6 +292,9 @@ 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, mem64_t value, u32 type); extern int sys_spu_thread_write_spu_mb(u32 id, u32 value); +extern int sys_spu_thread_set_spu_cfg(u32 id, u64 value); +extern int sys_spu_thread_get_spu_cfg(u32 id, mem64_t value); +extern int sys_spu_thread_write_snr(u32 id, u32 number, u32 value); //sys_time extern int sys_time_get_timezone(mem32_t timezone, mem32_t summertime); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index b888915c28..23e84922bb 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -21,12 +21,14 @@ struct SpuGroupInfo }; u64 g_spu_offset = 0; +u64 g_spu_alloc_size = 0; u32 LoadSpuImage(vfsStream& stream) { ELFLoader l(stream); l.LoadInfo(); - g_spu_offset = Memory.MainMem.Alloc(0xFFFFED - stream.GetSize()); + g_spu_alloc_size = 0xFFFFED - stream.GetSize(); + g_spu_offset = Memory.MainMem.Alloc(g_spu_alloc_size); l.LoadData(g_spu_offset); return g_spu_offset + l.GetEntry(); @@ -56,6 +58,7 @@ int sys_spu_image_open(mem_ptr_t img, u32 path_addr) img->entry_point = entry; img->segs_addr = 0x0; img->nsegs = 0; + img->_img_offs = g_spu_offset; return CELL_OK; } @@ -93,37 +96,42 @@ int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t< return CELL_EBUSY; } - u32 entry = img->entry_point; + u32 ls_entry = img->entry_point - img->_img_offs; std::string name = Memory.ReadString(attr->name_addr, attr->name_len).mb_str(); u64 a1 = arg->arg1; u64 a2 = arg->arg2; u64 a3 = arg->arg3; u64 a4 = arg->arg4; + CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_SPU); + //copy SPU image: + g_spu_alloc_size = 256 * 1024; + g_spu_offset = Memory.MainMem.Alloc(g_spu_alloc_size); + memcpy(Memory + g_spu_offset, Memory + img->_img_offs, g_spu_alloc_size); + //initialize from new place: + new_thread.SetOffset(g_spu_offset); + new_thread.SetEntry(ls_entry); + new_thread.SetName(name); + new_thread.SetArg(0, a1); + new_thread.SetArg(1, a2); + new_thread.SetArg(2, a3); + new_thread.SetArg(3, a4); + new_thread.Run(); + + thread = new_thread.GetId(); + + group_info.threads[spu_num] = &new_thread; + ConLog.Write("New SPU Thread:"); - ConLog.Write("entry = 0x%x", entry); + ConLog.Write("ls_entry = 0x%x", ls_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.Write("ls_offset = 0x%x", ((SPUThread&)new_thread).dmac.ls_offset); ConLog.SkipLn(); - CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_SPU); - new_thread.SetOffset(g_spu_offset); - new_thread.SetEntry(entry - g_spu_offset); - new_thread.SetName(name); - new_thread.Run(); - new_thread.Pause(); - new_thread.SetArg(0, a1); - new_thread.SetArg(1, a2); - new_thread.SetArg(2, a3); - new_thread.SetArg(3, a4); - - thread = new_thread.GetId(); - - group_info.threads[spu_num] = &new_thread; - return CELL_OK; } @@ -164,7 +172,7 @@ int sys_spu_thread_group_start(u32 id) ID& id_data = Emu.GetIdManager().GetIDData(id); SpuGroupInfo& group_info = *(SpuGroupInfo*)id_data.m_data; - Emu.Pause(); + //Emu.Pause(); for(int i=0; iGetType() != CPU_THREAD_SPU && thr->GetType() != CPU_THREAD_RAW_SPU)) + { + return CELL_ESRCH; + } + + if (value > 3) + { + return CELL_EINVAL; + } + + (*(SPUThread*)thr).cfg.value = value; + + return CELL_OK; +} + +//188 +int sys_spu_thread_get_spu_cfg(u32 id, mem64_t value) +{ + sc_spu.Warning("sys_spu_thread_get_spu_cfg(id=0x%x, value_addr=0x%x)", id, value.GetAddr()); + + CPUThread* thr = Emu.GetCPU().GetThread(id); + + if(!thr || (thr->GetType() != CPU_THREAD_SPU && thr->GetType() != CPU_THREAD_RAW_SPU)) + { + return CELL_ESRCH; + } + + value = (*(SPUThread*)thr).cfg.value; + + return CELL_OK; +} + +//184 +int sys_spu_thread_write_snr(u32 id, u32 number, u32 value) +{ + CPUThread* thr = Emu.GetCPU().GetThread(id); + + if(!thr || (thr->GetType() != CPU_THREAD_SPU && thr->GetType() != CPU_THREAD_RAW_SPU)) + { + return CELL_ESRCH; + } + + if (number > 1) + { + return CELL_EINVAL; + } + + if ((*(SPUThread*)thr).cfg.value & (1< entry_point; be_t segs_addr; be_t nsegs; + u64 _img_offs; }; From 37b5b49f58809e092b76e4b4a8dea04657f7c78f Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Sun, 22 Dec 2013 22:16:57 +0400 Subject: [PATCH 10/16] Fast fix Previous commit was wrong --- rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 10 ++++------ rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index 23e84922bb..e7993cd422 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -58,7 +58,6 @@ int sys_spu_image_open(mem_ptr_t img, u32 path_addr) img->entry_point = entry; img->segs_addr = 0x0; img->nsegs = 0; - img->_img_offs = g_spu_offset; return CELL_OK; } @@ -96,7 +95,7 @@ int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t< return CELL_EBUSY; } - u32 ls_entry = img->entry_point - img->_img_offs; + u32 ls_entry = img->entry_point - g_spu_offset; std::string name = Memory.ReadString(attr->name_addr, attr->name_len).mb_str(); u64 a1 = arg->arg1; u64 a2 = arg->arg2; @@ -105,11 +104,10 @@ int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t< CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_SPU); //copy SPU image: - g_spu_alloc_size = 256 * 1024; - g_spu_offset = Memory.MainMem.Alloc(g_spu_alloc_size); - memcpy(Memory + g_spu_offset, Memory + img->_img_offs, g_spu_alloc_size); + u32 spu_offset = Memory.MainMem.Alloc(256 * 1024); + memcpy(Memory + spu_offset, Memory + g_spu_offset, 256 * 1024); //initialize from new place: - new_thread.SetOffset(g_spu_offset); + new_thread.SetOffset(spu_offset); new_thread.SetEntry(ls_entry); new_thread.SetName(name); new_thread.SetArg(0, a1); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h index eed7d1eb15..4d6515a5f2 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h @@ -29,5 +29,4 @@ struct sys_spu_image be_t entry_point; be_t segs_addr; be_t nsegs; - u64 _img_offs; }; From ba8108d59500907b7a4a704c86bb9c2e69ba6ecb Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Mon, 23 Dec 2013 15:57:09 +0400 Subject: [PATCH 11/16] SPU Fixes Mandelbrot set does work now and its modified version included in TEST12345 folder. --- .../USRDIR/mandelbrot_set_no_pads.self | Bin 0 -> 171644 bytes .../TEST12345/USRDIR/mandelbrot_set_spu.self | Bin 0 -> 5032 bytes rpcs3/Emu/Cell/SPUInterpreter.h | 20 +++++------------- rpcs3/Emu/Cell/SPUThread.h | 4 ++-- 4 files changed, 7 insertions(+), 17 deletions(-) create mode 100644 bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_no_pads.self create mode 100644 bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_spu.self diff --git a/bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_no_pads.self b/bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_no_pads.self new file mode 100644 index 0000000000000000000000000000000000000000..ab6694184ecf78b308944a2c1dfeaa1e43f2314a GIT binary patch literal 171644 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 literal 0 HcmV?d00001 diff --git a/bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_spu.self b/bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_spu.self new file mode 100644 index 0000000000000000000000000000000000000000..c2899f38b7ca4a5fb93e02aabc093847aec570da GIT binary patch literal 5032 zcmd^CeQ;A%7QZifX_GXN(iV8I!6t21ia@sQO9k1L_Y#VT6exwtldZglHrPsOYg;0? z&g3QO2OljI`Cd?qjtYMAvCg>24grdQu3Em7RX);L#X34WIy$qnyFm7LU-Ak%&d&PJ z{2biNsUGax0*B@DHf0#ZOM_&w;g20zpn<-^1Ouz6=kb>44t!qb)+bl&r8 z!cHP8&YRJYGcNWykuVKI{#F%p|(Umy~n_<+YuH{3{^}uHe6_T#x zw3=)d(v=&(lrDr)({e-EL-rjz(P{f$ub0RPLj_@4IfpK+#7^iGN3V$weud2%$HH%$ z5X(!!IKq?73Qs;&cdtkxCQ%`+qMZcODv#+zh0)WoqbE%ViZDGE9gW{(7w_Ohqh9pL z$`C#@;&~c3a1Zzy&g;r$mN1ujB_0<08|ON2h@aqqUJ^o?oDj|&Aw)7q3!!1#0@waL zJaFy3EFnB>w-8D)V7q?4!QvetPU=-Xvmv7W;eF87JpFIbs?1 z`}`9f?>_Cksa){b?=N@W6hHFVLk~J{l8-(1rx!YJL@#OMP${|K)v6ICe zz!kg_yCU?vu3Cs|yjAE-&ssVBWt)-gwfctVaptUTb~3%&lYMxE5S!TL5#n2fSlY(Q z)BoIB8IN!AWJj|+*^1SZ9m}M93wvo9M1NR}$6JM1N*izo&XJy(8Tl|&I_mwsbgX!% zbl$tm#-t7{$v!zsdhvaI>?O8OYJWe)bZ5zG3t=Zp5lslmJ|)mk)nHA zxx(Y8xD3Nd>G`p<2X^cFq{!G3DYEpmbY@9!$9KP+CC&X(l+IQkm(JF_!{s~g<*ZeQ zrTva$UlcsoCv9{bl_I{W(gzi9w|^r?rP*H=NgqBtMfz~hTinFO2RX}?0%`r_yQSAJ zyZc?XzR>KQZ%UCx2FbgZm7J({HB3<<1+^e?%KrVzk8U=UwbI9Y_S+v_OQU^Kk!yyS;f@Awb;FZ zYgga4J`$T~edOvZ7~g|_uJp#@Nr)M7R}@LFojfSL*0EoDZN?i??~Xr6z1t5+z1#L( zcz@L93!U3HU$BhaY#hJ!W#jnadp~=(W5Z`J&)8tRYwSy(tvR_tI=%OK>2&RL(&=%# z1Iy<1fU*P2O4nMe)5lnsSf4SDUBBL#+h&q-dOMuN*kLESZfR?KmyI=Ur1zjO7*(v5 zox?MN>$V}EBSOrTB`WEejvk|a<(uXmD_?kY^r|gpoNs*vOI&&`rL&#W=~-eYgd2Hm zgm6U1aSKay2V$YcH6u$#V%|1Kk7)#Gi=)S!1=_?16_OEr#lkDQGI-_bE+K3hCWKQq zaO|TCq~WREG>$}EHKn|=W2X=rvb`|)0)bCG^&>vhUCIX^S2)x;H}0Doci4^l>&Crx zbM)(sUS9@xE@s2iyd(fTDS!xquHg#G?4tskl$;$#VXDN*kXmuY=T@JYxPC81p9 zu?Xx$u&-v&8eWkqBq43}0Bg`4S(zPk}8x zAACTealro$Ua7i+S1NZ-jgn`l9w)mnubjKDcFe&UuVx?kV$R10Q@*xk1cmLn%Hq*A zDYYYMe7VY^?Z~&SLVp_a#JCkchJ!vz`uP;R1-!B#lPezBd|XU_d7qWfp z&`SkH=-ck-8H)c}jQYmYb*g*vGyd1W^|Nm z3w|_iZd}n))8zM6YjWahUbVc%w*-AlQ=+fYM)Mn2%qX8(OkB0~wJpTe;$Mm1r2xd$ z)KKkf@ex;5OG8sLaaAWfmA9lBoT`T9%l-AxuWxAayWm!?_bvCsOO-$1Z)&b>sQ-yX z_z$dK(EI=;zc1+U2j3AY13V{4(?j)V=(k2bNNY6LXuAuf=|wRV2{G;gPivYUotxA% z)evGD0RAa^cPI5SpwokIAN!B=XdINLf~Y-x;=74{LlMmvhJ>X*XwyNO9^~fLz(1u| za+@CPo4bLjAFXNQGBD_T>emkHf%$#FTGR9vfL<^Rp1+Few=$ageN64q+@)AON8sfC znbo}lPM)utJ5-;pVXy%w3?(_hX@f!xfGt15^gDjAK|XX4U;s?_3~MygxLFKne9$pd zI~3FS=)F@LOXA;RkcTM}?Xej!K|IiJM6ZKBjdu>P0v$1l9|BKp$@$2Kv?v(VKGhFE zpRQZ;*9r_c7|&C{bbr#3AXM)KOtYge#zmw4{|LE=J|A*w?@yp8C_N`3KaR0_elSAM z?=;MuLN6ufpTW2fg#UP|y^FAC2Tpe+?0to`CO(tCLyP}$`t%C9%eG+G#+jKO!&(OG$z)^loEX${s!eZ3pHL% zU&j94KEE1M^PrxW^k0i{lul1#oJS(9)Ocli+Jd=d^7J{yvP@*T_!giQe7Uc--c{Au zNaRHY_dM>aZIPEYG|8y+jdlJORPe9`3qF9Y6u#lGwYgV`U2JZ>g47|(!8n}-4EAvPTVs$jvRj zCNQh~b#$(3s?pcvTTYjdU)|(;TwV&~Uxgs>Y$es{I>Bgaz+a6==qsj~k*gcz#Hxs_ nzJnULUDsb Date: Mon, 23 Dec 2013 17:20:16 +0400 Subject: [PATCH 12/16] Files removed They don't work from this folder --- .../USRDIR/mandelbrot_set_no_pads.self | Bin 171644 -> 0 bytes .../TEST12345/USRDIR/mandelbrot_set_spu.self | Bin 5032 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_no_pads.self delete mode 100644 bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_spu.self diff --git a/bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_no_pads.self b/bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_no_pads.self deleted file mode 100644 index ab6694184ecf78b308944a2c1dfeaa1e43f2314a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171644 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 diff --git a/bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_spu.self b/bin/dev_hdd0/game/TEST12345/USRDIR/mandelbrot_set_spu.self deleted file mode 100644 index c2899f38b7ca4a5fb93e02aabc093847aec570da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5032 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 nzJnULUDsb Date: Tue, 24 Dec 2013 15:10:55 +0400 Subject: [PATCH 13/16] Fixed x86 build Fixed crashes --- rpcs3/Emu/Cell/SPUInterpreter.h | 31 ++++++++++++------------ rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 6 ++--- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index f6b7e9266f..01b7fb8866 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -7,12 +7,12 @@ #define UNIMPLEMENTED() UNK(__FUNCTION__) -typedef union _CRT_ALIGN(16) __u32x4 { - unsigned __int32 _u32[4]; +/* typedef union _CRT_ALIGN(16) __u32x4 { + u32 _u32[4]; __m128i m128i; __m128 m128; __m128d m128d; - } __u32x4; + } __u32x4; */ class SPUInterpreter : public SPUOpcodes { @@ -378,17 +378,16 @@ private: } void FREST(u32 rt, u32 ra) { - //(SSE) RCPPS - Compute Reciprocals of Packed Single-Precision Floating-Point Values - //rt = approximate(1/ra) - CPU.GPR[rt]._m128 = _mm_rcp_ps(CPU.GPR[ra]._m128); + //CPU.GPR[rt]._m128 = _mm_rcp_ps(CPU.GPR[ra]._m128); + for (int i = 0; i < 4; i++) + CPU.GPR[rt]._f[i] = 1 / CPU.GPR[ra]._f[i]; } void FRSQEST(u32 rt, u32 ra) { - //(SSE) RSQRTPS - Compute Reciprocals of Square Roots of Packed Single-Precision Floating-Point Values - //rt = approximate(1/sqrt(abs(ra))) - //abs(ra) === ra & FloatAbsMask - const __u32x4 FloatAbsMask = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; - CPU.GPR[rt]._m128 = _mm_rsqrt_ps(_mm_and_ps(CPU.GPR[ra]._m128, FloatAbsMask.m128)); + //const __u32x4 FloatAbsMask = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; + //CPU.GPR[rt]._m128 = _mm_rsqrt_ps(_mm_and_ps(CPU.GPR[ra]._m128, FloatAbsMask.m128)); + for (int i = 0; i < 4; i++) + CPU.GPR[rt]._f[i] = 1 / sqrt(abs(CPU.GPR[ra]._f[i])); } void LQX(u32 rt, u32 ra, u32 rb) { @@ -999,9 +998,10 @@ private: exp = 255; CPU.GPR[rt]._u32[i] = (CPU.GPR[ra]._u32[i] & 0x807fffff) | (exp << 23); + + CPU.GPR[rt]._u32[i] = (u32)CPU.GPR[rt]._f[i]; //trunc } - //(SSE2) CVTTPS2DQ - Convert with Truncation Packed Single FP to Packed Dword Int - CPU.GPR[rt]._m128i = _mm_cvttps_epi32(CPU.GPR[rt]._m128); + //CPU.GPR[rt]._m128i = _mm_cvttps_epi32(CPU.GPR[rt]._m128); } void CFLTU(u32 rt, u32 ra, s32 i8) { @@ -1028,11 +1028,12 @@ private: } void CSFLT(u32 rt, u32 ra, s32 i8) { - //(SSE2) CVTDQ2PS - Convert Packed Dword Integers to Packed Single-Precision FP Values - CPU.GPR[rt]._m128 = _mm_cvtepi32_ps(CPU.GPR[ra]._m128i); + //CPU.GPR[rt]._m128 = _mm_cvtepi32_ps(CPU.GPR[ra]._m128i); const u32 scale = 155 - (i8 & 0xff); //unsigned immediate for (int i = 0; i < 4; i++) { + CPU.GPR[rt]._f[i] = (s32)CPU.GPR[ra]._i32[i]; + u32 exp = ((CPU.GPR[rt]._u32[i] >> 23) & 0xff) - scale; if (exp > 255) //< 0 diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index e7993cd422..92be21e5c3 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -37,7 +37,7 @@ u32 LoadSpuImage(vfsStream& stream) //156 int sys_spu_image_open(mem_ptr_t img, u32 path_addr) { - const std::string& path = Memory.ReadString(path_addr).mb_str(); + const wxString path = Memory.ReadString(path_addr).mb_str(); sc_spu.Warning("sys_spu_image_open(img_addr=0x%x, path_addr=0x%x [%s])", img.GetAddr(), path_addr, path); if(!img.IsGood() || !Memory.IsGoodAddr(path_addr)) @@ -122,7 +122,7 @@ int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t< ConLog.Write("New SPU Thread:"); ConLog.Write("ls_entry = 0x%x", ls_entry); - ConLog.Write("name = %s", name); + ConLog.Write("name = %s", wxString(name)); ConLog.Write("a1 = 0x%x", a1); ConLog.Write("a2 = 0x%x", a2); ConLog.Write("a3 = 0x%x", a3); @@ -196,7 +196,7 @@ int sys_spu_thread_group_create(mem32_t id, u32 num, int prio, mem_ptr_ttype.ToLE()); ConLog.Write("*** attr.option.ct=%d", attr->option.ct.ToLE()); - const std::string& name = Memory.ReadString(attr->name_addr, attr->name_len).mb_str(); + const wxString name = Memory.ReadString(attr->name_addr, attr->name_len).mb_str(); ConLog.Write("*** name='%s'", name); id = Emu.GetIdManager().GetNewID(wxString::Format("sys_spu_thread_group '%s'", name), new SpuGroupInfo(*attr)); From 35257f5e94cd861c766a409e8460e3cd7865fb9c Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Tue, 24 Dec 2013 21:34:51 +0400 Subject: [PATCH 14/16] SPU Improvements Implemented sys_spu_image_import, original Mandelbrot PKG should work now. --- rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp | 46 +++++++++++++++++--- rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 1 + rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h | 10 +++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp index 424b0139e1..52a1d66755 100644 --- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp @@ -13,31 +13,31 @@ void sys_initialize_tls() s64 sys_process_atexitspawn() { sysPrxForUser.Log("sys_process_atexitspawn()"); - return 0; + return CELL_OK; } s64 sys_process_at_Exitspawn() { sysPrxForUser.Log("sys_process_at_Exitspawn"); - return 0; + return CELL_OK; } 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; + return CELL_OK; } s64 sys_prx_register_library(u32 lib_addr) { sysPrxForUser.Error("sys_prx_register_library(lib_addr=0x%x)", lib_addr); - return 0; + return CELL_OK; } s64 sys_prx_exitspawn_with_level() { sysPrxForUser.Log("sys_prx_exitspawn_with_level()"); - return 0; + return CELL_OK; } s64 sys_strlen(u32 addr) @@ -47,6 +47,38 @@ s64 sys_strlen(u32 addr) return str.Len(); } +int sys_spu_elf_get_information(u32 elf_img, mem32_t entry, mem32_t nseg) +{ + sysPrxForUser.Warning("sys_spu_elf_get_information(elf_img=0x%x, entry_addr=0x%x, nseg_addr=0x%x", elf_img, entry.GetAddr(), nseg.GetAddr()); + return CELL_OK; +} + +int sys_spu_elf_get_segments(u32 elf_img, mem_ptr_t segments, int nseg) +{ + sysPrxForUser.Warning("sys_spu_elf_get_segments(elf_img=0x%x, segments_addr=0x%x, nseg=0x%x)", elf_img, segments.GetAddr(), nseg); + return CELL_OK; +} + +int sys_spu_image_import(mem_ptr_t img, u64 src, u32 type) +{ + sysPrxForUser.Warning("sys_spu_image_import(img=0x%x, src=0x%x, type=0x%x)", img.GetAddr(), src, type); + + if(!img.IsGood() || !Memory.IsGoodAddr(src)) + { + return CELL_EFAULT; + } + + vfsStreamMemory f(src); + u32 entry = LoadSpuImage(f); + + img->type = 1; + img->entry_point = entry; + img->segs_addr = 0x0; + img->nsegs = 0; + + return CELL_OK; +} + void sysPrxForUser_init() { sysPrxForUser.AddFunc(0x744680a2, sys_initialize_tls); @@ -82,4 +114,8 @@ void sysPrxForUser_init() sysPrxForUser.AddFunc(0xb257540b, sys_mmapper_allocate_memory); sysPrxForUser.AddFunc(0xdc578057, sys_mmapper_map_memory); + + sysPrxForUser.AddFunc(0x1ed454ce, sys_spu_elf_get_information); + sysPrxForUser.AddFunc(0xdb6b3250, sys_spu_elf_get_segments); + sysPrxForUser.AddFunc(0xebe5f72f, sys_spu_image_import); } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index 92be21e5c3..bb5fe07953 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 "SC_SPU_Thread.h" #include "Emu/SysCalls/SysCalls.h" +#include "Emu/SysCalls/SC_FUNC.h" #include "Loader/ELF.h" #include "Emu/Cell/RawSPUThread.h" diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h index 4d6515a5f2..c4f670c28a 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.h @@ -1,5 +1,7 @@ #pragma once +u32 LoadSpuImage(vfsStream& stream); + struct sys_spu_thread_group_attribute { be_t name_len; @@ -30,3 +32,11 @@ struct sys_spu_image be_t segs_addr; be_t nsegs; }; + +struct sys_spu_segment +{ + be_t type; + be_t ls_start; + be_t size; + be_t src; +}; From 1ab5ef9dd7f5606a6933c8e36db68b92758ca598 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Wed, 25 Dec 2013 21:28:10 +0400 Subject: [PATCH 15/16] SPU Implemented sys_raw_spu_load, sys_raw_spu_image_load. Minor review of MFC, channels. --- rpcs3/Emu/Cell/MFC.h | 41 +++-- rpcs3/Emu/Cell/SPUThread.h | 184 +++++++++++++------ rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp | 43 ++++- rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp | 17 +- 4 files changed, 206 insertions(+), 79 deletions(-) diff --git a/rpcs3/Emu/Cell/MFC.h b/rpcs3/Emu/Cell/MFC.h index 4523131b46..839987cd68 100644 --- a/rpcs3/Emu/Cell/MFC.h +++ b/rpcs3/Emu/Cell/MFC.h @@ -151,6 +151,27 @@ struct DMAC long queue_lock; long proxy_lock; + bool ProcessCmd(u32 cmd, u32 tag, u32 lsa, u64 ea, u32 size) + { + //returns true if the command should be deleted from the queue + if (cmd & (MFC_BARRIER_MASK | MFC_FENCE_MASK)) _mm_mfence(); + + switch(cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) + { + case MFC_PUT_CMD: + memcpy(Memory + ea, Memory + ls_offset + lsa, size); + return true; + + case MFC_GET_CMD: + memcpy(Memory + ls_offset + lsa, Memory + ea, size); + return true; + + default: + ConLog.Error("Unknown DMA cmd."); + return true; + } + } + u32 Cmd(u32 cmd, u32 tag, u32 lsa, u64 ea, u32 size) { if(!Memory.IsGoodAddr(ls_offset + lsa, size) || !Memory.IsGoodAddr(ea, size)) @@ -163,7 +184,7 @@ struct DMAC return MFC_PPU_DMA_QUEUE_FULL; } - while (_InterlockedExchange(&proxy_lock, 1)); + /* while (_InterlockedExchange(&proxy_lock, 1)); _mm_lfence(); DMAC_Proxy& p = proxy[proxy_pos]; p.cmd = cmd; @@ -174,7 +195,8 @@ struct DMAC _mm_sfence(); //for DoCmd() proxy_pos++; _mm_sfence(); - proxy_lock = 0; + proxy_lock = 0; */ + ProcessCmd(cmd, tag, lsa, ea, size); return MFC_PPU_DMA_CMD_ENQUEUE_SUCCESSFUL; } @@ -193,22 +215,9 @@ struct DMAC if(proxy_pos) { const DMAC_Proxy& p = proxy[0]; - - switch(p.cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) //barrier/fence ignored + if (ProcessCmd(p.cmd, p.tag, p.lsa, p.ea, p.size)) { - case MFC_PUT_CMD: - memcpy(Memory + p.ea, Memory + ls_offset + p.lsa, p.size); ClearCmd(); - break; - - case MFC_GET_CMD: - memcpy(Memory + ls_offset + p.lsa, Memory + p.ea, p.size); - ClearCmd(); - break; - - default: - ConLog.Error("Unknown DMA cmd."); - break; } } } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index e434b2b2cc..bcd9391f7a 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -279,10 +279,20 @@ public: { public: static const size_t max_count = _max_count; +#ifdef _M_X64 + static const bool x86 = false; +#else + static const bool x86 = true; +#endif private: - u32 m_value[max_count]; - u32 m_index; + union _CRT_ALIGN(8) { + struct { + u32 m_index; + u32 m_value[max_count]; + }; + u64 m_indval; + }; long m_lock; public: @@ -300,81 +310,149 @@ public: __forceinline bool Pop(u32& res) { - while (_InterlockedExchange(&m_lock, 1)); - _mm_lfence(); - if(!m_index) + if (max_count > 1 || x86) { - m_lock = 0; //release lock - return false; + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); + if(!m_index) + { + m_lock = 0; //release lock + return false; + } + res = m_value[--m_index]; + m_value[m_index] = 0; + _mm_sfence(); + m_lock = 0; + return true; + } + else + { //lock-free + if(!m_index) + return false; + else + { + res = (m_indval >> 32); + m_indval = 0; + return true; + } } - res = m_value[--m_index]; - _mm_sfence(); - m_lock = 0; - return true; } __forceinline bool Push(u32 value) { - while (_InterlockedExchange(&m_lock, 1)); - _mm_lfence(); - if(m_index >= max_count) + if (max_count > 1 || x86) { - m_lock = 0; //release lock - return false; + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); + if(m_index >= max_count) + { + m_lock = 0; //release lock + return false; + } + m_value[m_index++] = value; + _mm_sfence(); + m_lock = 0; + return true; + } + else + { //lock-free + if(m_index) + return false; + else + { + m_indval = ((u64)value << 32) | 1; + return true; + } } - m_value[m_index++] = value; - _mm_sfence(); - m_lock = 0; - return true; } __forceinline void PushUncond(u32 value) { - while (_InterlockedExchange(&m_lock, 1)); - _mm_lfence(); - if(m_index >= max_count) - m_value[max_count-1] = value; //last message is overwritten + if (max_count > 1 || x86) + { + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); + if(m_index >= max_count) + m_value[max_count-1] = value; //last message is overwritten + else + m_value[m_index++] = value; + _mm_sfence(); + m_lock = 0; + } else - m_value[m_index++] = value; - _mm_sfence(); - m_lock = 0; + { //lock-free + m_indval = ((u64)value << 32) | 1; + } } __forceinline void PushUncond_OR(u32 value) { - while (_InterlockedExchange(&m_lock, 1)); - _mm_lfence(); - if(m_index >= max_count) - m_value[max_count-1] |= value; //last message is logically ORed + if (max_count > 1 || x86) + { + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); + if(m_index >= max_count) + m_value[max_count-1] |= value; //last message is logically ORed + else + m_value[m_index++] = value; + _mm_sfence(); + m_lock = 0; + } else - m_value[m_index++] = value; - _mm_sfence(); - m_lock = 0; + { +#ifdef _M_X64 + _InterlockedOr64((volatile __int64*)m_indval, ((u64)value << 32) | 1); +#else + ConLog.Error("PushUncond_OR(): no code compiled"); +#endif + } } __forceinline void PopUncond(u32& res) { - while (_InterlockedExchange(&m_lock, 1)); - _mm_lfence(); - if(!m_index) - res = 0; //result is undefined + if (max_count > 1 || x86) + { + while (_InterlockedExchange(&m_lock, 1)); + _mm_lfence(); + if(!m_index) + res = 0; //result is undefined + else + { + res = m_value[--m_index]; + m_value[m_index] = 0; + } + _mm_sfence(); + m_lock = 0; + } else - res = m_value[--m_index]; - _mm_sfence(); - m_lock = 0; + { //lock-free + if(!m_index) + res = 0; + else + { + res = (m_indval >> 32); + m_indval = 0; + } + } } u32 GetCount() const { - while (m_lock); - _mm_lfence(); + if (max_count > 1 || x86) + { + while (m_lock); + _mm_lfence(); + } return m_index; } u32 GetFreeCount() const { - while (m_lock); - _mm_lfence(); + if (max_count > 1 || x86) + { + while (m_lock); + _mm_lfence(); + } return max_count - m_index; } @@ -471,6 +549,7 @@ public: return SPU.In_MBox.GetCount(); case SPU_WrOutIntrMbox: + ConLog.Warning("GetChannelCount(%s) = 0", spu_ch_name[ch]); return 0;//return SPU.OutIntr_Mbox.GetFreeCount(); case MFC_RdTagStat: @@ -498,12 +577,12 @@ public: { case SPU_WrOutIntrMbox: ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); - while (!SPU.OutIntr_Mbox.Push(v) && !Emu.IsStopped()) _mm_pause(); + if (!SPU.OutIntr_Mbox.Push(v)) do _mm_pause(); while (!SPU.OutIntr_Mbox.Push(v) && !Emu.IsStopped()); break; case SPU_WrOutMbox: ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); - while (!SPU.Out_MBox.Push(v) && !Emu.IsStopped()) _mm_pause(); + if (!SPU.Out_MBox.Push(v)) do _mm_pause(); while (!SPU.Out_MBox.Push(v) && !Emu.IsStopped()); break; case MFC_WrTagMask: @@ -555,23 +634,22 @@ public: switch(ch) { case SPU_RdInMbox: - while (!SPU.In_MBox.Pop(v) && !Emu.IsStopped()) _mm_pause(); + if (!SPU.In_MBox.Pop(v)) do _mm_pause(); while (!SPU.In_MBox.Pop(v) && !Emu.IsStopped()); ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; case MFC_RdTagStat: - while (dmac.proxy_pos) dmac.DoCmd(); //probably incompatible with MFC lists - while (!Prxy.TagStatus.Pop(v) && !Emu.IsStopped()) _mm_pause(); + if (!Prxy.TagStatus.Pop(v)) do _mm_pause(); while (!Prxy.TagStatus.Pop(v) && !Emu.IsStopped()); //ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; case SPU_RdSigNotify1: - while (!SPU.SNR[0].Pop(v) && !Emu.IsStopped()) _mm_pause(); - ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); + if (!SPU.SNR[0].Pop(v)) do _mm_pause(); while (!SPU.SNR[0].Pop(v) && !Emu.IsStopped()); + //ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; case SPU_RdSigNotify2: - while (!SPU.SNR[1].Pop(v) && !Emu.IsStopped()) _mm_pause(); + if (!SPU.SNR[1].Pop(v)) do _mm_pause(); while (!SPU.SNR[1].Pop(v) && !Emu.IsStopped()); //ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp index 52a1d66755..e81f99e51b 100644 --- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp @@ -1,6 +1,9 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" +#include "Emu/SysCalls/lv2/SC_SPU_Thread.h" +#include "Loader/ELF.h" +#include "Emu/Cell/RawSPUThread.h" void sysPrxForUser_init(); Module sysPrxForUser("sysPrxForUser", sysPrxForUser_init); @@ -59,7 +62,7 @@ int sys_spu_elf_get_segments(u32 elf_img, mem_ptr_t segments, i return CELL_OK; } -int sys_spu_image_import(mem_ptr_t img, u64 src, u32 type) +int sys_spu_image_import(mem_ptr_t img, u32 src, u32 type) { sysPrxForUser.Warning("sys_spu_image_import(img=0x%x, src=0x%x, type=0x%x)", img.GetAddr(), src, type); @@ -79,6 +82,41 @@ int sys_spu_image_import(mem_ptr_t img, u64 src, u32 type) return CELL_OK; } +int sys_raw_spu_load(int id, u32 path_addr, mem32_t entry) +{ + const wxString path = Memory.ReadString(path_addr).mb_str(); + sysPrxForUser.Warning("sys_raw_spu_load(id=0x%x, path=0x%x [%s], entry_addr=0x%x)", + id, path_addr, path, entry.GetAddr()); + + vfsFile f(path.c_str()); + if(!f.IsOpened()) + { + sysPrxForUser.Error("sys_raw_spu_load error: '%s' not found!", path); + return CELL_ENOENT; + } + + ELFLoader l(f); + l.LoadInfo(); + l.LoadData(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id); + + entry = l.GetEntry(); + + return CELL_OK; +} + +extern u64 g_last_spu_offset; + +int sys_raw_spu_image_load(int id, mem_ptr_t img) +{ + sysPrxForUser.Warning("sys_raw_spu_image_load(id=0x%x, img_addr=0x%x)", id, img.GetAddr()); + + memcpy(Memory + RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id, Memory + g_last_spu_offset, 256 * 1024); + Memory.Write32(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id + RAW_SPU_PROB_OFFSET + SPU_NPC_offs, + img->entry_point - g_last_spu_offset); + + return CELL_OK; +} + void sysPrxForUser_init() { sysPrxForUser.AddFunc(0x744680a2, sys_initialize_tls); @@ -118,4 +156,7 @@ void sysPrxForUser_init() sysPrxForUser.AddFunc(0x1ed454ce, sys_spu_elf_get_information); sysPrxForUser.AddFunc(0xdb6b3250, sys_spu_elf_get_segments); sysPrxForUser.AddFunc(0xebe5f72f, sys_spu_image_import); + + sysPrxForUser.AddFunc(0x893305fa, sys_raw_spu_load); + sysPrxForUser.AddFunc(0xb995662e, sys_raw_spu_image_load); } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index bb5fe07953..a7fb066862 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -21,18 +21,17 @@ struct SpuGroupInfo } }; -u64 g_spu_offset = 0; -u64 g_spu_alloc_size = 0; +u64 g_last_spu_offset = 0; u32 LoadSpuImage(vfsStream& stream) { ELFLoader l(stream); l.LoadInfo(); - g_spu_alloc_size = 0xFFFFED - stream.GetSize(); - g_spu_offset = Memory.MainMem.Alloc(g_spu_alloc_size); - l.LoadData(g_spu_offset); + u32 alloc_size = 0xFFFFED - stream.GetSize(); + g_last_spu_offset = Memory.MainMem.Alloc(alloc_size); + l.LoadData(g_last_spu_offset); - return g_spu_offset + l.GetEntry(); + return g_last_spu_offset + l.GetEntry(); } //156 @@ -96,7 +95,7 @@ int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t< return CELL_EBUSY; } - u32 ls_entry = img->entry_point - g_spu_offset; + u32 ls_entry = img->entry_point - g_last_spu_offset; std::string name = Memory.ReadString(attr->name_addr, attr->name_len).mb_str(); u64 a1 = arg->arg1; u64 a2 = arg->arg2; @@ -106,7 +105,7 @@ int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t< CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_SPU); //copy SPU image: u32 spu_offset = Memory.MainMem.Alloc(256 * 1024); - memcpy(Memory + spu_offset, Memory + g_spu_offset, 256 * 1024); + memcpy(Memory + spu_offset, Memory + g_last_spu_offset, 256 * 1024); //initialize from new place: new_thread.SetOffset(spu_offset); new_thread.SetEntry(ls_entry); @@ -396,7 +395,7 @@ int sys_spu_thread_write_snr(u32 id, u32 number, u32 value) return CELL_EINVAL; } - if ((*(SPUThread*)thr).cfg.value & (1< Date: Fri, 27 Dec 2013 15:35:08 +0400 Subject: [PATCH 16/16] SPU LQX Hack removed, Critical Section for m_status removed. --- rpcs3/Emu/Cell/SPUInterpreter.h | 6 ------ rpcs3/Emu/Cell/SPUThread.h | 18 +++++++++--------- rpcs3/Emu/System.cpp | 9 +-------- rpcs3/Emu/System.h | 14 +++++++------- 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index 01b7fb8866..1dcb845bd5 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -393,12 +393,6 @@ private: { u32 a = CPU.GPR[ra]._u32[3], b = CPU.GPR[rb]._u32[3]; - if(b & 0xf) - { - ConLog.Warning("LQX HACK (a[0x%x] + b[0x%x(0x%x)])", a, b << 3, b); - b <<= 3; - } - u32 lsa = (a + b) & 0x3fff0; if(!CPU.IsGoodLSA(lsa)) diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index bcd9391f7a..d881fc580c 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -288,12 +288,12 @@ public: private: union _CRT_ALIGN(8) { struct { - u32 m_index; + volatile u32 m_index; u32 m_value[max_count]; }; - u64 m_indval; + volatile u64 m_indval; }; - long m_lock; + volatile long m_lock; public: @@ -577,12 +577,12 @@ public: { case SPU_WrOutIntrMbox: ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); - if (!SPU.OutIntr_Mbox.Push(v)) do _mm_pause(); while (!SPU.OutIntr_Mbox.Push(v) && !Emu.IsStopped()); + while (!SPU.OutIntr_Mbox.Push(v) && !Emu.IsStopped()) Sleep(1); break; case SPU_WrOutMbox: ConLog.Warning("%s: %s = 0x%x", __FUNCTION__, spu_ch_name[ch], v); - if (!SPU.Out_MBox.Push(v)) do _mm_pause(); while (!SPU.Out_MBox.Push(v) && !Emu.IsStopped()); + while (!SPU.Out_MBox.Push(v) && !Emu.IsStopped()) Sleep(1); break; case MFC_WrTagMask: @@ -634,22 +634,22 @@ public: switch(ch) { case SPU_RdInMbox: - if (!SPU.In_MBox.Pop(v)) do _mm_pause(); while (!SPU.In_MBox.Pop(v) && !Emu.IsStopped()); + while (!SPU.In_MBox.Pop(v) && !Emu.IsStopped()) Sleep(1); ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; case MFC_RdTagStat: - if (!Prxy.TagStatus.Pop(v)) do _mm_pause(); while (!Prxy.TagStatus.Pop(v) && !Emu.IsStopped()); + while (!Prxy.TagStatus.Pop(v) && !Emu.IsStopped()) Sleep(1); //ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; case SPU_RdSigNotify1: - if (!SPU.SNR[0].Pop(v)) do _mm_pause(); while (!SPU.SNR[0].Pop(v) && !Emu.IsStopped()); + while (!SPU.SNR[0].Pop(v) && !Emu.IsStopped()) Sleep(1); //ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; case SPU_RdSigNotify2: - if (!SPU.SNR[1].Pop(v)) do _mm_pause(); while (!SPU.SNR[1].Pop(v) && !Emu.IsStopped()); + while (!SPU.SNR[1].Pop(v) && !Emu.IsStopped()) Sleep(1); //ConLog.Warning("%s: 0x%x = %s", __FUNCTION__, v, spu_ch_name[ch]); break; diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 368ac13a8e..3e79a860be 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -354,7 +354,6 @@ void Emulator::Load() thread.Run(); - wxCriticalSectionLocker lock(m_cs_status); m_status = Ready; #ifndef QT_UI wxGetApp().SendDbgCommand(DID_READY_EMU); @@ -379,7 +378,6 @@ void Emulator::Run() wxGetApp().SendDbgCommand(DID_START_EMU); #endif - wxCriticalSectionLocker lock(m_cs_status); //ConLog.Write("run..."); m_status = Running; @@ -403,7 +401,6 @@ void Emulator::Pause() wxGetApp().SendDbgCommand(DID_PAUSE_EMU); #endif - wxCriticalSectionLocker lock(m_cs_status); m_status = Paused; #ifndef QT_UI wxGetApp().SendDbgCommand(DID_PAUSED_EMU); @@ -418,7 +415,6 @@ void Emulator::Resume() wxGetApp().SendDbgCommand(DID_RESUME_EMU); #endif - wxCriticalSectionLocker lock(m_cs_status); m_status = Running; CheckStatus(); @@ -436,10 +432,7 @@ void Emulator::Stop() #ifndef QT_UI wxGetApp().SendDbgCommand(DID_STOP_EMU); #endif - { - wxCriticalSectionLocker lock(m_cs_status); - m_status = Stopped; - } + m_status = Stopped; m_rsx_callback = 0; diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 04da0a30e8..78b9146937 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -1,5 +1,6 @@ #pragma once +#include #include "Gui/MemoryViewer.h" #include "Emu/CPU/CPUThreadManager.h" #include "Emu/Io/Pad.h" @@ -64,9 +65,8 @@ class Emulator InterpreterDisAsm, Interpreter, }; - - mutable wxCriticalSection m_cs_status; - Status m_status; + + volatile uint m_status; uint m_mode; u32 m_rsx_callback; @@ -159,10 +159,10 @@ public: void SavePoints(const std::string& path); void LoadPoints(const std::string& path); - __forceinline bool IsRunning() const { wxCriticalSectionLocker lock(m_cs_status); return m_status == Running; } - __forceinline bool IsPaused() const { wxCriticalSectionLocker lock(m_cs_status); return m_status == Paused; } - __forceinline bool IsStopped() const { wxCriticalSectionLocker lock(m_cs_status); return m_status == Stopped; } - __forceinline bool IsReady() const { wxCriticalSectionLocker lock(m_cs_status); return m_status == Ready; } + __forceinline bool IsRunning() const { return m_status == Running; } + __forceinline bool IsPaused() const { return m_status == Paused; } + __forceinline bool IsStopped() const { return m_status == Stopped; } + __forceinline bool IsReady() const { return m_status == Ready; } }; extern Emulator Emu; \ No newline at end of file