diff --git a/rpcs3/Emu/SysCalls/Modules/cellSync.cpp b/rpcs3/Emu/SysCalls/Modules/cellSync.cpp index 5778c67dc3..dfab32dbd5 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSync.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSync.cpp @@ -360,7 +360,7 @@ s32 cellSyncRwmTryWrite(mem_ptr_t rwm, u32 buffer_addr) s32 cellSyncQueueInitialize(mem_ptr_t queue, u32 buffer_addr, u32 size, u32 depth) { - cellSync->Todo("cellSyncQueueInitialize(queue_addr=0x%x, buffer_addr=0x%x, size=0x%x, depth=0x%x)", queue.GetAddr(), buffer_addr, size, depth); + cellSync->Log("cellSyncQueueInitialize(queue_addr=0x%x, buffer_addr=0x%x, size=0x%x, depth=0x%x)", queue.GetAddr(), buffer_addr, size, depth); if (!queue) { @@ -390,8 +390,69 @@ s32 cellSyncQueueInitialize(mem_ptr_t queue, u32 buffer_addr, u32 s32 cellSyncQueuePush(mem_ptr_t queue, u32 buffer_addr) { - cellSync->Todo("cellSyncQueuePush(queue_addr=0x%x, buffer_addr=0x%x)", queue.GetAddr(), buffer_addr); + cellSync->Log("cellSyncQueuePush(queue_addr=0x%x, buffer_addr=0x%x)", queue.GetAddr(), buffer_addr); + if (!queue || !buffer_addr) + { + return CELL_SYNC_ERROR_NULL_POINTER; + } + if (queue.GetAddr() % 32) + { + return CELL_SYNC_ERROR_ALIGN; + } + + const u32 size = (u32)queue->m_size; + const u32 depth = (u32)queue->m_depth; + if (((u32)queue->m_v1 & 0xffffff) > depth || ((u32)queue->m_v2 & 0xffffff) > depth) + { + cellSync->Error("cellSyncQueuePush(queue_addr=0x%x): m_depth limit broken", queue.GetAddr()); + Emu.Pause(); + } + + u32 position; + while (true) + { + const u64 old_data = queue->m_data(); + CellSyncQueue new_queue; + new_queue.m_data() = old_data; + + const u32 v1 = (u32)new_queue.m_v1; + const u32 v2 = (u32)new_queue.m_v2; + // prx: compare 5th u8 with zero (repeat if not zero) + // prx: compare (second u32 (u24) + first u8) with depth (repeat if greater or equal) + if ((v2 >> 24) || ((v2 & 0xffffff) + (v1 >> 24)) >= depth) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack + if (Emu.IsStopped()) + { + cellSync->Warning("cellSyncQueuePush(queue_addr=0x%x) aborted", queue.GetAddr()); + return CELL_OK; + } + continue; + } + + // prx: extract first u32 (u24) (-> position), calculate (position + 1) % depth, insert it back + // prx: insert 1 in 5th u8 + // prx: extract second u32 (u24), increase it, insert it back + position = (v1 & 0xffffff); + new_queue.m_v1 = (v1 & 0xff000000) | ((position + 1) % depth); + new_queue.m_v2 = (1 << 24) | ((v2 & 0xffffff) + 1); + if (InterlockedCompareExchange(&queue->m_data(), new_queue.m_data(), old_data) == old_data) break; + } + + // prx: memcpy(position * m_size + m_addr, buffer_addr, m_size), sync + memcpy(Memory + (u64)queue->m_addr + position * size, Memory + buffer_addr, size); + + // prx: atomically insert 0 in 5th u8 + while (true) + { + const u64 old_data = queue->m_data(); + CellSyncQueue new_queue; + new_queue.m_data() = old_data; + + new_queue.m_v2 &= 0xffffff; // TODO: use InterlockedAnd() or something + if (InterlockedCompareExchange(&queue->m_data(), new_queue.m_data(), old_data) == old_data) break; + } return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/Modules/cellSync.h b/rpcs3/Emu/SysCalls/Modules/cellSync.h index 22afb9bebf..a1ef8fdf39 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSync.h +++ b/rpcs3/Emu/SysCalls/Modules/cellSync.h @@ -72,7 +72,8 @@ static_assert(sizeof(CellSyncRwm) == 16, "CellSyncBarrier: wrong size"); struct CellSyncQueue { - be_t m_value; + be_t m_v1; + be_t m_v2; be_t m_size; be_t m_depth; be_t m_addr;