diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index be38544d72..257cb157cf 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -3044,8 +3044,8 @@ void PPUTranslator::DIVW(ppu_opcode_t op) { const auto a = GetGpr(op.ra, 32); const auto b = GetGpr(op.rb, 32); - const auto o = m_ir->CreateOr(IsZero(b), m_ir->CreateAnd(m_ir->CreateICmpEQ(a, m_ir->getInt32(1 << 31)), IsOnes(b))); - const auto result = m_ir->CreateSDiv(a, m_ir->CreateSelect(o, m_ir->getInt32(1 << 31), b)); + const auto o = m_ir->CreateOr(IsZero(b), m_ir->CreateAnd(m_ir->CreateICmpEQ(a, m_ir->getInt32(INT32_MIN)), IsOnes(b))); + const auto result = m_ir->CreateSDiv(a, m_ir->CreateSelect(o, m_ir->getInt32(INT32_MIN), b)); SetGpr(op.rd, m_ir->CreateSelect(o, m_ir->getInt32(0), result)); if (op.rc) SetCrField(0, GetUndef(), GetUndef(), GetUndef()); if (op.oe) SetOverflow(o); diff --git a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp index d36921902d..aacd06ec1e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp @@ -95,7 +95,6 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm sys_event_flag.trace("sys_event_flag_wait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x, timeout=0x%llx)", id, bitptn, mode, result, timeout); // Fix function arguments for external access - // TODO: Avoid using registers ppu.gpr[3] = -1; ppu.gpr[4] = bitptn; ppu.gpr[5] = mode; diff --git a/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp b/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp index cfc12e2b4e..e92fee2116 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_lwcond.cpp @@ -140,7 +140,7 @@ error_code _sys_lwcond_signal(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u3 { verify(HERE), !mutex->signaled; std::lock_guard lock(mutex->mutex); - mutex->sq.emplace_back(result); + verify(HERE), mutex->add_waiter(result); } else { @@ -228,7 +228,7 @@ error_code _sys_lwcond_signal_all(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id { verify(HERE), !mutex->signaled; std::lock_guard lock(mutex->mutex); - mutex->sq.emplace_back(cpu); + verify(HERE), mutex->add_waiter(cpu); } else { @@ -283,6 +283,23 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id return; } + // Try to increment lwmutex's lwcond's waiters count + if (!mutex->lwcond_waiters.fetch_op([](s32& val) + { + if (val == INT32_MIN) + { + return false; + } + + val++; + return true; + }).second) + { + // Failed - lwmutex was detroyed and all waiters have quit + mutex.reset(); + return; + } + std::lock_guard lock(cond.mutex); // Add a waiter @@ -343,6 +360,12 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id } } + if (--mutex->lwcond_waiters == INT32_MIN) + { + // Notify the thread destroying lwmutex on last waiter + mutex->lwcond_waiters.notify_all(); + } + // Return cause return not_an_error(ppu.gpr[3]); } diff --git a/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp b/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp index 1ee7f26915..ca4ecf898f 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_lwmutex.cpp @@ -40,26 +40,54 @@ error_code _sys_lwmutex_destroy(ppu_thread& ppu, u32 lwmutex_id) sys_lwmutex.warning("_sys_lwmutex_destroy(lwmutex_id=0x%x)", lwmutex_id); - const auto mutex = idm::withdraw(lwmutex_id, [&](lv2_lwmutex& mutex) -> CellError - { - std::lock_guard lock(mutex.mutex); - - if (!mutex.sq.empty()) - { - return CELL_EBUSY; - } - - return {}; - }); + auto mutex = idm::get(lwmutex_id); if (!mutex) { return CELL_ESRCH; } - if (mutex.ret) + while (true) { - return mutex.ret; + if (std::scoped_lock lock(mutex->mutex); mutex->sq.empty()) + { + // Set "destroyed" bit + if (mutex->lwcond_waiters.fetch_or(INT32_MIN) & 0x7fff'ffff) + { + // Deschedule if waiters were found + lv2_obj::sleep(ppu); + } + } + else + { + return CELL_EBUSY; + } + + // Wait for all lwcond waiters to quit + if (const s32 old = mutex->lwcond_waiters; old & 0x7fff'ffff) + { + if (old > 0) + { + // Sleep queue is no longer empty + // Was set to positive value to announce it + continue; + } + + mutex->lwcond_waiters.wait(old); + + if (ppu.is_stopped()) + { + return 0; + } + } + + break; + } + + if (!idm::remove_verify(lwmutex_id, std::move(mutex))) + { + // Other thread has destroyed the lwmutex earlier + return CELL_ESRCH; } return CELL_OK; @@ -95,7 +123,7 @@ error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout) if (old) { - if (old == (1 << 31)) + if (old == INT32_MIN) { ppu.gpr[3] = CELL_EBUSY; } @@ -103,7 +131,12 @@ error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout) return true; } - mutex.sq.emplace_back(&ppu); + if (!mutex.add_waiter(&ppu)) + { + ppu.gpr[3] = CELL_ESRCH; + return true; + } + mutex.sleep(ppu, timeout); return false; }); @@ -229,7 +262,7 @@ error_code _sys_lwmutex_unlock2(ppu_thread& ppu, u32 lwmutex_id) return; } - mutex.signaled |= 1 << 31; + mutex.signaled |= INT32_MIN; }); if (!mutex) diff --git a/rpcs3/Emu/Cell/lv2/sys_lwmutex.h b/rpcs3/Emu/Cell/lv2/sys_lwmutex.h index 818887293b..15a23fdbf6 100644 --- a/rpcs3/Emu/Cell/lv2/sys_lwmutex.h +++ b/rpcs3/Emu/Cell/lv2/sys_lwmutex.h @@ -62,6 +62,7 @@ struct lv2_lwmutex final : lv2_obj shared_mutex mutex; atomic_t signaled{0}; std::deque sq; + atomic_t lwcond_waiters{0}; lv2_lwmutex(u32 protocol, vm::ptr control, u64 name) : protocol(protocol) @@ -69,6 +70,38 @@ struct lv2_lwmutex final : lv2_obj , name(std::bit_cast>(name)) { } + + // Try to add a waiter + bool add_waiter(cpu_thread* cpu) + { + if (const auto old = lwcond_waiters.fetch_op([](s32& val) + { + if (val + 0u <= INT32_MIN + 0u) + { + // Value was either positive or INT32_MIN + return false; + } + + // lwmutex was set to be destroyed, but there are lwcond waiters + // Turn off the "destroying" bit as we are adding an lwmutex waiter + val &= 0x7fff'ffff; + return true; + }).first; old != INT32_MIN) + { + sq.emplace_back(cpu); + + if (old < 0) + { + // Notify lwmutex destroyer (may cause EBUSY to be returned for it) + lwcond_waiters.notify_all(); + } + + return true; + } + + // Failed - lwmutex was set to be destroyed and all lwcond waiters quit + return false; + } }; // Aux diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 898290f961..94c4c56783 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1883,7 +1883,7 @@ namespace rsx const s32 default_frequency_mask = (1 << 8); const s32 swap_storage_mask = (1 << 29); const s32 volatile_storage_mask = (1 << 30); - const s32 modulo_op_frequency_mask = (1 << 31); + const s32 modulo_op_frequency_mask = (INT32_MIN); const u32 modulo_mask = rsx::method_registers.frequency_divider_operation_mask(); const auto max_index = (first_vertex + vertex_count) - 1; diff --git a/rpcs3/Emu/RSX/gcm_enums.h b/rpcs3/Emu/RSX/gcm_enums.h index 38ece3f98b..d717083d8e 100644 --- a/rpcs3/Emu/RSX/gcm_enums.h +++ b/rpcs3/Emu/RSX/gcm_enums.h @@ -600,7 +600,7 @@ enum CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_U = 1 << 28, CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_V = 1 << 29, CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_P = 1 << 30, - CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_Q = 1 << 31, + CELL_GCM_TEXTURE_CYLINDRICAL_WRAP_ENABLE_TEX7_Q = 1u << 31, CELL_GCM_COLOR_MASK_B = 1 << 0, CELL_GCM_COLOR_MASK_G = 1 << 8,