rpcsx/ps3fw/cellSync.cpp

1708 lines
37 KiB
C++
Raw Permalink Normal View History

2020-12-05 13:08:24 +01:00
#include "stdafx.h"
2016-03-21 20:43:03 +01:00
#include "Emu/Cell/PPUModule.h"
#include "cellos/sys_event.h"
#include "cellos/sys_process.h"
2014-07-20 19:14:04 +02:00
#include "cellSync.h"
LOG_CHANNEL(cellSync);
template <>
void fmt_class_string<CellSyncError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(CELL_SYNC_ERROR_AGAIN);
STR_CASE(CELL_SYNC_ERROR_INVAL);
STR_CASE(CELL_SYNC_ERROR_NOSYS);
STR_CASE(CELL_SYNC_ERROR_NOMEM);
STR_CASE(CELL_SYNC_ERROR_SRCH);
STR_CASE(CELL_SYNC_ERROR_NOENT);
STR_CASE(CELL_SYNC_ERROR_NOEXEC);
STR_CASE(CELL_SYNC_ERROR_DEADLK);
STR_CASE(CELL_SYNC_ERROR_PERM);
STR_CASE(CELL_SYNC_ERROR_BUSY);
STR_CASE(CELL_SYNC_ERROR_ABORT);
STR_CASE(CELL_SYNC_ERROR_FAULT);
STR_CASE(CELL_SYNC_ERROR_CHILD);
STR_CASE(CELL_SYNC_ERROR_STAT);
STR_CASE(CELL_SYNC_ERROR_ALIGN);
STR_CASE(CELL_SYNC_ERROR_NULL_POINTER);
STR_CASE(CELL_SYNC_ERROR_NOT_SUPPORTED_THREAD);
STR_CASE(CELL_SYNC_ERROR_NO_NOTIFIER);
STR_CASE(CELL_SYNC_ERROR_NO_SPU_CONTEXT_STORAGE);
}
return unknown;
});
}
error_code cellSyncMutexInitialize(vm::ptr<CellSyncMutex> mutex)
{
cellSync.trace("cellSyncMutexInitialize(mutex=*0x%x)", mutex);
if (!mutex) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-03-10 20:16:31 +01:00
if (!mutex.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
2016-08-17 18:50:20 +02:00
mutex->ctrl.exchange({0, 0});
2015-03-07 22:20:38 +01:00
return CELL_OK;
}
error_code cellSyncMutexLock(ppu_thread& ppu, vm::ptr<CellSyncMutex> mutex)
{
cellSync.trace("cellSyncMutexLock(mutex=*0x%x)", mutex);
if (!mutex) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-03-10 20:16:31 +01:00
if (!mutex.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
2016-08-17 18:50:20 +02:00
// Increase acq value and remember its old value
const auto order = mutex->ctrl.atomic_op(&CellSyncMutex::Counter::lock_begin);
2016-08-17 18:50:20 +02:00
// Wait until rel value is equal to old acq value
while (mutex->ctrl.load().rel != order)
2016-08-17 18:50:20 +02:00
{
if (ppu.test_stopped())
{
return 0;
}
}
2014-07-20 19:14:04 +02:00
atomic_fence_acq_rel();
return CELL_OK;
}
error_code cellSyncMutexTryLock(vm::ptr<CellSyncMutex> mutex)
{
cellSync.trace("cellSyncMutexTryLock(mutex=*0x%x)", mutex);
if (!mutex) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-03-10 20:16:31 +01:00
if (!mutex.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
if (!mutex->ctrl.atomic_op(&CellSyncMutex::Counter::try_lock))
{
2016-08-15 17:30:33 +02:00
return not_an_error(CELL_SYNC_ERROR_BUSY);
}
return CELL_OK;
}
error_code cellSyncMutexUnlock(vm::ptr<CellSyncMutex> mutex)
{
cellSync.trace("cellSyncMutexUnlock(mutex=*0x%x)", mutex);
if (!mutex) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!mutex.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
mutex->ctrl.atomic_op(&CellSyncMutex::Counter::unlock);
return CELL_OK;
}
error_code cellSyncBarrierInitialize(vm::ptr<CellSyncBarrier> barrier, u16 total_count)
{
cellSync.trace("cellSyncBarrierInitialize(barrier=*0x%x, total_count=%d)", barrier, total_count);
if (!barrier) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!barrier.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
if (!total_count || total_count > 32767) [[unlikely]]
{
return CELL_SYNC_ERROR_INVAL;
}
// clear current value, write total_count and sync
2016-08-17 18:50:20 +02:00
barrier->ctrl.exchange({0, total_count});
2015-03-07 22:20:38 +01:00
return CELL_OK;
}
error_code cellSyncBarrierNotify(ppu_thread& ppu, vm::ptr<CellSyncBarrier> barrier)
{
cellSync.trace("cellSyncBarrierNotify(barrier=*0x%x)", barrier);
if (!barrier) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!barrier.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
while (!barrier->ctrl.atomic_op(&CellSyncBarrier::try_notify))
{
if (ppu.test_stopped())
{
return 0;
}
}
2015-03-07 22:20:38 +01:00
return CELL_OK;
}
error_code cellSyncBarrierTryNotify(vm::ptr<CellSyncBarrier> barrier)
{
cellSync.trace("cellSyncBarrierTryNotify(barrier=*0x%x)", barrier);
if (!barrier) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!barrier.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
atomic_fence_acq_rel();
2015-03-07 22:20:38 +01:00
if (!barrier->ctrl.atomic_op(&CellSyncBarrier::try_notify))
2014-08-07 09:59:56 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(CELL_SYNC_ERROR_BUSY);
2014-08-07 09:59:56 +02:00
}
return CELL_OK;
}
error_code cellSyncBarrierWait(ppu_thread& ppu, vm::ptr<CellSyncBarrier> barrier)
{
cellSync.trace("cellSyncBarrierWait(barrier=*0x%x)", barrier);
if (!barrier) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!barrier.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
atomic_fence_acq_rel();
while (!barrier->ctrl.atomic_op(&CellSyncBarrier::try_wait))
{
if (ppu.test_stopped())
{
return 0;
}
}
2015-03-07 22:20:38 +01:00
return CELL_OK;
}
error_code cellSyncBarrierTryWait(vm::ptr<CellSyncBarrier> barrier)
{
cellSync.trace("cellSyncBarrierTryWait(barrier=*0x%x)", barrier);
if (!barrier) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!barrier.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
atomic_fence_acq_rel();
if (!barrier->ctrl.atomic_op(&CellSyncBarrier::try_wait))
2014-10-11 00:37:20 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(CELL_SYNC_ERROR_BUSY);
2014-10-11 00:37:20 +02:00
}
return CELL_OK;
}
error_code cellSyncRwmInitialize(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer, u32 buffer_size)
{
cellSync.trace("cellSyncRwmInitialize(rwm=*0x%x, buffer=*0x%x, buffer_size=0x%x)", rwm, buffer, buffer_size);
if (!rwm || !buffer) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!rwm.aligned() || !buffer.aligned(128)) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
if (buffer_size % 128 || buffer_size > 0x4000) [[unlikely]]
{
return CELL_SYNC_ERROR_INVAL;
}
// clear readers and writers, write buffer_size, buffer addr and sync
rwm->ctrl.store({0, 0});
rwm->size = buffer_size;
rwm->buffer = buffer;
2014-10-16 21:34:17 +02:00
atomic_fence_acq_rel();
2015-03-07 22:20:38 +01:00
return CELL_OK;
}
error_code cellSyncRwmRead(ppu_thread& ppu, vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
{
cellSync.trace("cellSyncRwmRead(rwm=*0x%x, buffer=*0x%x)", rwm, buffer);
if (!rwm || !buffer) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!rwm.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
// wait until `writers` is zero, increase `readers`
while (!rwm->ctrl.atomic_op(&CellSyncRwm::try_read_begin))
{
if (ppu.test_stopped())
{
return 0;
}
}
// copy data to buffer
std::memcpy(buffer.get_ptr(), rwm->buffer.get_ptr(), rwm->size);
// decrease `readers`, return error if already zero
if (!rwm->ctrl.atomic_op(&CellSyncRwm::try_read_end))
2014-10-16 21:34:17 +02:00
{
return CELL_SYNC_ERROR_ABORT;
2014-10-16 21:34:17 +02:00
}
return CELL_OK;
}
error_code cellSyncRwmTryRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
{
cellSync.trace("cellSyncRwmTryRead(rwm=*0x%x, buffer=*0x%x)", rwm, buffer);
if (!rwm || !buffer) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!rwm.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
// increase `readers` if `writers` is zero
if (!rwm->ctrl.atomic_op(&CellSyncRwm::try_read_begin))
2014-08-07 23:34:56 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(CELL_SYNC_ERROR_BUSY);
2014-08-07 23:34:56 +02:00
}
2014-09-24 20:44:26 +02:00
// copy data to buffer
std::memcpy(buffer.get_ptr(), rwm->buffer.get_ptr(), rwm->size);
2014-08-07 23:34:56 +02:00
// decrease `readers`, return error if already zero
if (!rwm->ctrl.atomic_op(&CellSyncRwm::try_read_end))
2014-10-16 21:34:17 +02:00
{
return CELL_SYNC_ERROR_ABORT;
2014-10-16 21:34:17 +02:00
}
return CELL_OK;
}
2014-08-07 23:34:56 +02:00
error_code cellSyncRwmWrite(ppu_thread& ppu, vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
{
cellSync.trace("cellSyncRwmWrite(rwm=*0x%x, buffer=*0x%x)", rwm, buffer);
if (!rwm || !buffer) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!rwm.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
// wait until `writers` is zero, set to 1
while (!rwm->ctrl.atomic_op(&CellSyncRwm::try_write_begin))
{
if (ppu.test_stopped())
{
return 0;
}
}
2014-08-07 23:34:56 +02:00
// wait until `readers` is zero
while (rwm->ctrl.load().readers != 0)
{
if (ppu.test_stopped())
{
return 0;
}
}
2014-08-07 23:34:56 +02:00
// copy data from buffer
std::memcpy(rwm->buffer.get_ptr(), buffer.get_ptr(), rwm->size);
2014-08-07 23:34:56 +02:00
// sync and clear `readers` and `writers`
rwm->ctrl.exchange({0, 0});
2015-03-07 22:20:38 +01:00
return CELL_OK;
}
error_code cellSyncRwmTryWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
{
cellSync.trace("cellSyncRwmTryWrite(rwm=*0x%x, buffer=*0x%x)", rwm, buffer);
if (!rwm || !buffer) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!rwm.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
// set `writers` to 1 if `readers` and `writers` are zero
if (!rwm->ctrl.compare_and_swap_test({0, 0}, {0, 1}))
{
2016-08-15 17:30:33 +02:00
return not_an_error(CELL_SYNC_ERROR_BUSY);
}
2014-08-07 23:34:56 +02:00
// copy data from buffer
std::memcpy(rwm->buffer.get_ptr(), buffer.get_ptr(), rwm->size);
2014-08-07 23:34:56 +02:00
// sync and clear `readers` and `writers`
rwm->ctrl.exchange({0, 0});
2015-03-07 22:20:38 +01:00
return CELL_OK;
}
error_code cellSyncQueueInitialize(vm::ptr<CellSyncQueue> queue, vm::ptr<u8> buffer, u32 size, u32 depth)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueueInitialize(queue=*0x%x, buffer=*0x%x, size=0x%x, depth=0x%x)", queue, buffer, size, depth);
if (!queue) [[unlikely]]
2014-07-22 21:02:45 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (size && !buffer) [[unlikely]]
2014-07-22 21:02:45 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned() || !buffer.aligned(16)) [[unlikely]]
2014-07-22 21:02:45 +02:00
{
return CELL_SYNC_ERROR_ALIGN;
}
if (!depth || size % 16) [[unlikely]]
2014-07-22 21:02:45 +02:00
{
return CELL_SYNC_ERROR_INVAL;
}
// clear sync var, write size, depth, buffer addr and sync
2019-12-13 01:21:43 +01:00
queue->ctrl.store({});
queue->size = size;
queue->depth = depth;
queue->buffer = buffer;
2014-09-21 15:02:05 +02:00
atomic_fence_acq_rel();
2015-03-07 22:20:38 +01:00
2014-09-21 15:02:05 +02:00
return CELL_OK;
}
error_code cellSyncQueuePush(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::cptr<void> buffer)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueuePush(queue=*0x%x, buffer=*0x%x)", queue, buffer);
2014-07-22 21:02:45 +02:00
if (!queue || !buffer) [[unlikely]]
2014-07-23 23:51:57 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned()) [[unlikely]]
2014-07-23 23:51:57 +02:00
{
return CELL_SYNC_ERROR_ALIGN;
}
const u32 depth = queue->check_depth();
2014-07-23 23:51:57 +02:00
u32 position;
while (!queue->ctrl.atomic_op([&](CellSyncQueue::ctrl_t& ctrl)
{
return CellSyncQueue::try_push_begin(ctrl, depth, &position);
}))
{
if (ppu.test_stopped())
{
return 0;
}
}
// copy data from the buffer at the position
std::memcpy(&queue->buffer[position * queue->size], buffer.get_ptr(), queue->size);
2014-07-23 23:51:57 +02:00
queue->ctrl.atomic_op(&CellSyncQueue::push_end);
2015-03-07 22:20:38 +01:00
2014-07-22 21:02:45 +02:00
return CELL_OK;
}
error_code cellSyncQueueTryPush(vm::ptr<CellSyncQueue> queue, vm::cptr<void> buffer)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueueTryPush(queue=*0x%x, buffer=*0x%x)", queue, buffer);
2014-07-22 21:02:45 +02:00
if (!queue || !buffer) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
const u32 depth = queue->check_depth();
u32 position;
while (!queue->ctrl.atomic_op([&](CellSyncQueue::ctrl_t& ctrl)
{
return CellSyncQueue::try_push_begin(ctrl, depth, &position);
}))
2014-09-21 15:02:05 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(CELL_SYNC_ERROR_BUSY);
}
// copy data from the buffer at the position
std::memcpy(&queue->buffer[position * queue->size], buffer.get_ptr(), queue->size);
2015-03-07 22:20:38 +01:00
queue->ctrl.atomic_op(&CellSyncQueue::push_end);
2015-03-07 22:20:38 +01:00
2014-09-21 15:02:05 +02:00
return CELL_OK;
}
error_code cellSyncQueuePop(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueuePop(queue=*0x%x, buffer=*0x%x)", queue, buffer);
2014-07-22 21:02:45 +02:00
if (!queue || !buffer) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
const u32 depth = queue->check_depth();
u32 position;
while (!queue->ctrl.atomic_op([&](CellSyncQueue::ctrl_t& ctrl)
{
return CellSyncQueue::try_pop_begin(ctrl, depth, &position);
}))
{
if (ppu.test_stopped())
{
return 0;
}
}
// copy data at the position to the buffer
std::memcpy(buffer.get_ptr(), &queue->buffer[position % depth * queue->size], queue->size);
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
2015-03-07 22:20:38 +01:00
2014-07-22 21:02:45 +02:00
return CELL_OK;
}
error_code cellSyncQueueTryPop(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueueTryPop(queue=*0x%x, buffer=*0x%x)", queue, buffer);
2014-07-22 21:02:45 +02:00
if (!queue || !buffer) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
const u32 depth = queue->check_depth();
u32 position;
while (!queue->ctrl.atomic_op([&](CellSyncQueue::ctrl_t& ctrl)
{
return CellSyncQueue::try_pop_begin(ctrl, depth, &position);
}))
2014-09-21 15:02:05 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(CELL_SYNC_ERROR_BUSY);
}
// copy data at the position to the buffer
std::memcpy(buffer.get_ptr(), &queue->buffer[position % depth * queue->size], queue->size);
2015-03-07 22:20:38 +01:00
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
2015-03-07 22:20:38 +01:00
2014-09-21 15:02:05 +02:00
return CELL_OK;
}
error_code cellSyncQueuePeek(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueuePeek(queue=*0x%x, buffer=*0x%x)", queue, buffer);
2014-07-22 21:02:45 +02:00
if (!queue || !buffer) [[unlikely]]
2014-08-08 15:54:46 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned()) [[unlikely]]
2014-08-08 15:54:46 +02:00
{
return CELL_SYNC_ERROR_ALIGN;
}
const u32 depth = queue->check_depth();
2014-08-08 15:54:46 +02:00
u32 position;
while (!queue->ctrl.atomic_op([&](CellSyncQueue::ctrl_t& ctrl)
{
return CellSyncQueue::try_peek_begin(ctrl, depth, &position);
}))
{
if (ppu.test_stopped())
{
return 0;
}
}
// copy data at the position to the buffer
std::memcpy(buffer.get_ptr(), &queue->buffer[position % depth * queue->size], queue->size);
2015-03-07 22:20:38 +01:00
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
2015-03-07 22:20:38 +01:00
2014-07-22 21:02:45 +02:00
return CELL_OK;
}
error_code cellSyncQueueTryPeek(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueueTryPeek(queue=*0x%x, buffer=*0x%x)", queue, buffer);
2014-08-08 15:54:46 +02:00
if (!queue || !buffer) [[unlikely]]
2014-08-08 15:54:46 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned()) [[unlikely]]
2014-08-08 15:54:46 +02:00
{
return CELL_SYNC_ERROR_ALIGN;
}
const u32 depth = queue->check_depth();
2014-08-08 15:54:46 +02:00
u32 position;
while (!queue->ctrl.atomic_op([&](CellSyncQueue::ctrl_t& ctrl)
{
return CellSyncQueue::try_peek_begin(ctrl, depth, &position);
}))
2014-09-21 15:02:05 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(CELL_SYNC_ERROR_BUSY);
2014-08-08 15:54:46 +02:00
}
// copy data at the position to the buffer
std::memcpy(buffer.get_ptr(), &queue->buffer[position % depth * queue->size], queue->size);
2015-03-07 22:20:38 +01:00
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
2015-03-07 22:20:38 +01:00
2014-07-22 21:02:45 +02:00
return CELL_OK;
}
error_code cellSyncQueueSize(vm::ptr<CellSyncQueue> queue)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueueSize(queue=*0x%x)", queue);
2014-07-22 21:02:45 +02:00
if (!queue) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
2021-01-12 11:01:06 +01:00
queue->check_depth();
2016-08-15 17:30:33 +02:00
return not_an_error(queue->ctrl.load().count & 0xffffff);
2014-07-22 21:02:45 +02:00
}
error_code cellSyncQueueClear(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue)
2014-07-22 21:02:45 +02:00
{
cellSync.trace("cellSyncQueueClear(queue=*0x%x)", queue);
if (!queue) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
2021-01-12 11:01:06 +01:00
queue->check_depth();
2014-10-16 21:34:17 +02:00
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_1))
{
if (ppu.test_stopped())
{
return 0;
}
}
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_2))
{
if (ppu.test_stopped())
{
return 0;
}
}
2019-12-13 01:21:43 +01:00
queue->ctrl.store({});
2014-07-22 21:02:45 +02:00
return CELL_OK;
}
2014-08-11 20:35:34 +02:00
// LFQueue functions
void syncLFQueueInitialize(vm::ptr<CellSyncLFQueue> queue, vm::cptr<void> buffer, u32 size, u32 depth, u32 direction, vm::ptr<void> eaSignal)
2014-08-18 23:16:48 +02:00
{
queue->m_size = size;
queue->m_depth = depth;
2014-09-05 22:26:36 +02:00
queue->m_buffer = buffer;
2014-08-18 23:16:48 +02:00
queue->m_direction = direction;
memset(queue->m_hs1, 0, sizeof(queue->m_hs1));
memset(queue->m_hs2, 0, sizeof(queue->m_hs2));
2014-09-05 22:26:36 +02:00
queue->m_eaSignal = eaSignal;
2014-08-18 23:16:48 +02:00
if (direction == CELL_SYNC_QUEUE_ANY2ANY)
{
queue->pop1.store({});
queue->push1.store({});
queue->m_buffer.set(queue->m_buffer.addr() | 1);
2014-08-18 23:16:48 +02:00
queue->m_bs[0] = -1;
queue->m_bs[1] = -1;
// m_bs[2]
// m_bs[3]
2014-08-18 23:16:48 +02:00
queue->m_v1 = -1;
queue->push2.store({0xffff});
queue->pop2.store({0xffff});
2014-08-18 23:16:48 +02:00
}
else
{
queue->pop1.store({0, 0, queue->pop1.load().m_h3, 0});
queue->push1.store({0, 0, queue->push1.load().m_h7, 0});
2014-08-18 23:16:48 +02:00
queue->m_bs[0] = -1; // written as u32
queue->m_bs[1] = -1;
queue->m_bs[2] = -1;
queue->m_bs[3] = -1;
queue->m_v1 = 0;
queue->push2.store({});
queue->pop2.store({});
2014-08-18 23:16:48 +02:00
}
2014-09-23 16:27:18 +02:00
queue->m_v2 = 0;
queue->m_eq_id = 0;
2014-08-18 23:16:48 +02:00
}
error_code cellSyncLFQueueInitialize(vm::ptr<CellSyncLFQueue> queue, vm::cptr<void> buffer, u32 size, u32 depth, u32 direction, vm::ptr<void> eaSignal)
2014-08-18 23:16:48 +02:00
{
cellSync.warning("cellSyncLFQueueInitialize(queue=*0x%x, buffer=*0x%x, size=0x%x, depth=0x%x, direction=%d, eaSignal=*0x%x)", queue, buffer, size, depth, direction, eaSignal);
2015-06-26 16:45:13 +02:00
if (!queue) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
2014-08-18 23:16:48 +02:00
if (size)
{
if (!buffer) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (size > 0x4000 || size % 16) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_INVAL;
}
}
2015-06-26 16:45:13 +02:00
if (!depth || depth > 0x7fff || direction > 3) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_INVAL;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned() || !buffer.aligned(16)) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_ALIGN;
}
2015-06-26 16:45:13 +02:00
// get sdk version of current process
2014-08-18 23:16:48 +02:00
s32 sdk_ver;
2015-06-26 16:45:13 +02:00
if (s32 ret = process_get_sdk_version(process_getpid(), sdk_ver))
2014-08-18 23:16:48 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(ret);
2014-08-18 23:16:48 +02:00
}
2015-06-26 16:45:13 +02:00
2014-08-18 23:16:48 +02:00
if (sdk_ver == -1)
{
sdk_ver = 0x460000;
}
2015-06-26 16:45:13 +02:00
// reserve `init`
2014-08-18 23:16:48 +02:00
u32 old_value;
2015-06-26 16:45:13 +02:00
2014-08-18 23:16:48 +02:00
while (true)
{
const auto old = queue->init.load();
2014-09-23 01:07:40 +02:00
auto init = old;
2014-08-18 23:16:48 +02:00
2015-09-15 18:23:17 +02:00
if (old)
2014-08-18 23:16:48 +02:00
{
if (sdk_ver > 0x17ffff && old != 2) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_STAT;
}
2015-06-26 16:45:13 +02:00
2015-01-14 00:08:00 +01:00
old_value = old;
2014-08-18 23:16:48 +02:00
}
else
{
if (sdk_ver > 0x17ffff)
{
for (const auto& data : vm::_ref<u64[16]>(queue.addr()))
2014-08-18 23:16:48 +02:00
{
if (data) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_STAT;
}
}
}
2015-06-26 16:45:13 +02:00
2014-09-23 01:07:40 +02:00
init = 1;
old_value = 1;
2014-08-18 23:16:48 +02:00
}
if (queue->init.compare_and_swap_test(old, init))
break;
2014-08-18 23:16:48 +02:00
}
2014-09-23 01:07:40 +02:00
if (old_value == 2)
2014-08-18 23:16:48 +02:00
{
if (queue->m_size != size || queue->m_depth != depth || queue->m_buffer != buffer) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_INVAL;
}
2015-06-26 16:45:13 +02:00
2014-08-18 23:16:48 +02:00
if (sdk_ver > 0x17ffff)
{
if (queue->m_eaSignal != eaSignal || queue->m_direction != direction) [[unlikely]]
2014-08-18 23:16:48 +02:00
{
return CELL_SYNC_ERROR_INVAL;
}
}
2015-06-26 16:45:13 +02:00
atomic_fence_acq_rel();
2014-08-18 23:16:48 +02:00
}
else
{
2015-06-26 16:45:13 +02:00
syncLFQueueInitialize(queue, buffer, size, depth, direction, eaSignal);
2014-08-18 23:16:48 +02:00
queue->init.exchange(0);
2014-08-18 23:16:48 +02:00
}
return CELL_OK;
}
error_code _cellSyncLFQueueGetPushPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
2014-08-18 23:16:48 +02:00
{
cellSync.warning("_cellSyncLFQueueGetPushPointer(queue=*0x%x, pointer=*0x%x, isBlocking=%d, useEventQueue=%d)", queue, pointer, isBlocking, useEventQueue);
2014-08-18 23:16:48 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_PPU2SPU) [[unlikely]]
2014-08-12 17:46:22 +02:00
{
return CELL_SYNC_ERROR_PERM;
}
2015-05-08 11:45:21 +02:00
const s32 depth = queue->m_depth;
2014-08-17 17:08:26 +02:00
u32 var1 = 0;
2015-05-08 11:45:21 +02:00
2014-08-12 17:46:22 +02:00
while (true)
{
while (true)
{
const auto old = queue->push1.load();
2014-09-23 01:07:40 +02:00
auto push = old;
2014-08-12 17:46:22 +02:00
2014-08-17 17:08:26 +02:00
if (var1)
2014-08-12 17:46:22 +02:00
{
2014-09-23 01:07:40 +02:00
push.m_h7 = 0;
2014-08-12 17:46:22 +02:00
}
2019-12-02 22:31:34 +01:00
if (isBlocking && useEventQueue && std::bit_cast<s32>(queue->m_bs) == -1)
2014-08-12 17:46:22 +02:00
{
return CELL_SYNC_ERROR_STAT;
}
2019-12-02 22:31:34 +01:00
s32 var2 = static_cast<s16>(push.m_h8);
2014-08-12 17:46:22 +02:00
s32 res;
2019-12-02 22:31:34 +01:00
if (useEventQueue && (+push.m_h5 != var2 || push.m_h7))
2014-08-12 17:46:22 +02:00
{
res = CELL_SYNC_ERROR_BUSY;
}
else
{
2019-12-02 22:31:34 +01:00
var2 -= queue->pop1.load().m_h1;
2014-08-12 17:46:22 +02:00
if (var2 < 0)
{
var2 += depth * 2;
}
if (var2 < depth)
{
2019-12-02 22:31:34 +01:00
const s32 _pointer = static_cast<s16>(push.m_h8);
2015-06-26 16:45:13 +02:00
*pointer = _pointer;
if (_pointer + 1 >= depth * 2)
2014-08-12 17:46:22 +02:00
{
2014-09-23 01:07:40 +02:00
push.m_h8 = 0;
2014-08-12 17:46:22 +02:00
}
else
{
2014-09-23 01:07:40 +02:00
push.m_h8++;
2014-08-12 17:46:22 +02:00
}
res = CELL_OK;
}
2014-08-17 23:35:10 +02:00
else if (!isBlocking)
2014-08-12 17:46:22 +02:00
{
2015-03-07 22:20:38 +01:00
return CELL_SYNC_ERROR_AGAIN;
2014-08-17 23:35:10 +02:00
}
else if (!useEventQueue)
{
continue;
}
else
{
res = CELL_OK;
2014-09-23 01:07:40 +02:00
push.m_h7 = 3;
2014-08-17 23:35:10 +02:00
if (isBlocking != 3)
2014-08-12 17:46:22 +02:00
{
2014-08-17 23:35:10 +02:00
break;
2014-08-12 17:46:22 +02:00
}
}
}
2014-09-23 01:07:40 +02:00
if (queue->push1.compare_and_swap_test(old, push))
2014-08-12 17:46:22 +02:00
{
2015-09-15 18:23:17 +02:00
if (!push.m_h7 || res)
2014-08-12 17:46:22 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(res);
2014-08-12 17:46:22 +02:00
}
break;
}
}
ensure(sys_event_queue_receive(ppu, queue->m_eq_id, vm::null, 0) == CELL_OK);
2014-08-17 17:08:26 +02:00
var1 = 1;
2014-08-12 17:46:22 +02:00
}
}
2014-08-09 18:23:53 +02:00
2021-03-05 20:05:37 +01:00
error_code _cellSyncLFQueueGetPushPointer2(ppu_thread& /*ppu*/, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
{
2014-08-11 20:35:34 +02:00
// arguments copied from _cellSyncLFQueueGetPushPointer
cellSync.todo("_cellSyncLFQueueGetPushPointer2(queue=*0x%x, pointer=*0x%x, isBlocking=%d, useEventQueue=%d)", queue, pointer, isBlocking, useEventQueue);
2014-08-11 20:35:34 +02:00
return CELL_OK;
}
error_code _cellSyncLFQueueCompletePushPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal)
{
cellSync.warning("_cellSyncLFQueueCompletePushPointer(queue=*0x%x, pointer=%d, fpSendSignal=*0x%x)", queue, pointer, fpSendSignal);
2015-06-26 16:45:13 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_PPU2SPU) [[unlikely]]
2014-08-13 23:39:54 +02:00
{
return CELL_SYNC_ERROR_PERM;
}
2015-05-08 11:45:21 +02:00
const s32 depth = queue->m_depth;
2014-08-13 23:39:54 +02:00
while (true)
{
const auto old = queue->push2.load();
2014-09-23 16:27:18 +02:00
auto push2 = old;
2014-08-13 23:39:54 +02:00
// Loads must be in this order
const auto old2 = queue->push3.load();
2014-09-23 16:27:18 +02:00
auto push3 = old2;
2014-08-13 23:39:54 +02:00
2019-12-02 22:31:34 +01:00
s32 var1 = pointer - push3.m_h5;
2014-08-13 23:39:54 +02:00
if (var1 < 0)
{
var1 += depth * 2;
}
2019-12-02 22:31:34 +01:00
s32 var2 = static_cast<s16>(queue->pop1.load().m_h4) - queue->pop1.load().m_h1;
2014-08-13 23:39:54 +02:00
if (var2 < 0)
{
var2 += depth * 2;
}
s32 var9_ = 15 - var1;
2014-09-27 20:49:33 +02:00
// calculate (u16)(1 slw (15 - var1))
2014-08-13 23:39:54 +02:00
if (var9_ & 0x30)
{
var9_ = 0;
}
else
{
var9_ = 1 << var9_;
}
s32 var9 = std::countl_zero<u32>(static_cast<u16>(~(var9_ | push3.m_h6))) - 16; // count leading zeros in u16
2015-06-26 16:45:13 +02:00
2019-12-02 22:31:34 +01:00
s32 var5 = push3.m_h6 | var9_;
2014-08-13 23:39:54 +02:00
if (var9 & 0x30)
{
var5 = 0;
}
else
{
var5 <<= var9;
}
2019-12-02 22:31:34 +01:00
s32 var3 = push3.m_h5 + var9;
2014-08-13 23:39:54 +02:00
if (var3 >= depth * 2)
{
var3 -= depth * 2;
}
2014-09-23 16:27:18 +02:00
u16 pack = push2.pack; // three packed 5-bit fields
2014-08-13 23:39:54 +02:00
s32 var4 = ((pack >> 10) & 0x1f) - ((pack >> 5) & 0x1f);
if (var4 < 0)
{
var4 += 0x1e;
}
u32 var6;
if (var2 + var4 <= 15 && ((pack >> 10) & 0x1f) != (pack & 0x1f))
{
s32 var8 = (pack & 0x1f) - ((pack >> 10) & 0x1f);
if (var8 < 0)
{
var8 += 0x1e;
}
2019-12-02 22:31:34 +01:00
if (var9 > 1 && static_cast<u32>(var8) > 1)
2014-08-13 23:39:54 +02:00
{
ensure((16 - var2 <= 1));
2014-08-13 23:39:54 +02:00
}
s32 var11 = (pack >> 10) & 0x1f;
if (var11 >= 15)
{
var11 -= 15;
}
u16 var12 = (pack >> 10) & 0x1f;
if (var12 == 0x1d)
{
var12 = 0;
}
else
{
var12 = (var12 + 1) << 10;
}
2014-09-23 16:27:18 +02:00
push2.pack = (pack & 0x83ff) | var12;
2019-12-02 22:31:34 +01:00
var6 = queue->m_hs1[var11];
2014-08-13 23:39:54 +02:00
}
else
{
var6 = -1;
}
2019-12-02 22:31:34 +01:00
push3.m_h5 = static_cast<u16>(var3);
push3.m_h6 = static_cast<u16>(var5);
2014-08-13 23:39:54 +02:00
2014-09-23 16:27:18 +02:00
if (queue->push2.compare_and_swap_test(old, push2))
2014-08-13 23:39:54 +02:00
{
ensure((var2 + var4 < 16));
if (var6 != umax)
2014-08-13 23:39:54 +02:00
{
ensure((queue->push3.compare_and_swap_test(old2, push3)));
ensure((fpSendSignal));
return not_an_error(fpSendSignal(ppu, vm::cast(queue->m_eaSignal.addr()), var6));
2014-08-13 23:39:54 +02:00
}
else
{
pack = queue->push2.load().pack;
2014-08-17 23:35:10 +02:00
if ((pack & 0x1f) == ((pack >> 10) & 0x1f))
2014-08-13 23:39:54 +02:00
{
2014-09-23 16:27:18 +02:00
if (queue->push3.compare_and_swap_test(old2, push3))
2014-08-13 23:39:54 +02:00
{
return CELL_OK;
2014-08-13 23:39:54 +02:00
}
}
}
}
}
}
2021-03-05 20:05:37 +01:00
error_code _cellSyncLFQueueCompletePushPointer2(ppu_thread&, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal)
2014-08-11 20:35:34 +02:00
{
// arguments copied from _cellSyncLFQueueCompletePushPointer
cellSync.todo("_cellSyncLFQueueCompletePushPointer2(queue=*0x%x, pointer=%d, fpSendSignal=*0x%x)", queue, pointer, fpSendSignal);
2014-08-11 20:35:34 +02:00
return CELL_OK;
2014-08-11 20:35:34 +02:00
}
error_code _cellSyncLFQueuePushBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::cptr<void> buffer, u32 isBlocking)
2014-08-11 20:35:34 +02:00
{
// cellSyncLFQueuePush has 1 in isBlocking param, cellSyncLFQueueTryPush has 0
cellSync.warning("_cellSyncLFQueuePushBody(queue=*0x%x, buffer=*0x%x, isBlocking=%d)", queue, buffer, isBlocking);
2014-08-11 20:35:34 +02:00
if (!queue || !buffer) [[unlikely]]
2014-08-11 20:35:34 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned() || !buffer.aligned(16)) [[unlikely]]
2014-08-11 20:35:34 +02:00
{
return CELL_SYNC_ERROR_ALIGN;
}
vm::var<s32> position;
2015-02-18 23:54:31 +01:00
2014-08-11 20:35:34 +02:00
while (true)
{
s32 res;
2014-10-24 15:24:09 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
2014-08-11 20:35:34 +02:00
{
res = _cellSyncLFQueueGetPushPointer(ppu, queue, position, isBlocking, 0);
2014-08-11 20:35:34 +02:00
}
else
{
res = _cellSyncLFQueueGetPushPointer2(ppu, queue, position, isBlocking, 0);
2014-08-11 20:35:34 +02:00
}
if (!isBlocking || res + 0u != CELL_SYNC_ERROR_AGAIN)
2014-08-11 20:35:34 +02:00
{
if (res)
return not_an_error(res);
2015-06-26 16:45:13 +02:00
2014-08-11 20:35:34 +02:00
break;
}
if (ppu.test_stopped())
{
return 0;
}
2014-08-11 20:35:34 +02:00
}
2015-05-08 11:45:21 +02:00
const s32 depth = queue->m_depth;
const s32 size = queue->m_size;
2015-08-13 15:28:42 +02:00
const s32 pos = *position;
const u32 addr = vm::cast<u64>((queue->m_buffer.addr() & ~1ull) + size * (pos >= depth ? pos - depth : pos));
std::memcpy(vm::base(addr), buffer.get_ptr(), size);
2015-02-18 23:54:31 +01:00
2014-10-24 15:24:09 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
2014-08-11 20:35:34 +02:00
{
return _cellSyncLFQueueCompletePushPointer(ppu, queue, pos, vm::null);
2014-08-11 20:35:34 +02:00
}
else
{
return _cellSyncLFQueueCompletePushPointer2(ppu, queue, pos, vm::null);
2014-08-11 20:35:34 +02:00
}
}
error_code _cellSyncLFQueueGetPopPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 arg4, u32 useEventQueue)
{
cellSync.warning("_cellSyncLFQueueGetPopPointer(queue=*0x%x, pointer=*0x%x, isBlocking=%d, arg4=%d, useEventQueue=%d)", queue, pointer, isBlocking, arg4, useEventQueue);
2015-06-26 16:45:13 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_SPU2PPU) [[unlikely]]
2014-08-17 17:08:26 +02:00
{
return CELL_SYNC_ERROR_PERM;
}
2015-05-08 11:45:21 +02:00
const s32 depth = queue->m_depth;
2014-08-17 17:08:26 +02:00
u32 var1 = 0;
2015-05-08 11:45:21 +02:00
2014-08-17 17:08:26 +02:00
while (true)
{
while (true)
{
const auto old = queue->pop1.load();
2014-09-23 01:07:40 +02:00
auto pop = old;
2014-08-17 17:08:26 +02:00
if (var1)
{
2014-09-23 01:07:40 +02:00
pop.m_h3 = 0;
2014-08-17 17:08:26 +02:00
}
2019-12-02 22:31:34 +01:00
if (isBlocking && useEventQueue && std::bit_cast<s32>(queue->m_bs) == -1)
2014-08-17 17:08:26 +02:00
{
return CELL_SYNC_ERROR_STAT;
}
2019-12-02 22:31:34 +01:00
s32 var2 = static_cast<s16>(pop.m_h4);
2014-08-17 17:08:26 +02:00
s32 res;
2019-12-02 22:31:34 +01:00
if (useEventQueue && (static_cast<s32>(pop.m_h1) != var2 || pop.m_h3))
2014-08-17 17:08:26 +02:00
{
res = CELL_SYNC_ERROR_BUSY;
}
else
{
2019-12-02 22:31:34 +01:00
var2 = queue->push1.load().m_h5 - var2;
2014-08-17 17:08:26 +02:00
if (var2 < 0)
{
var2 += depth * 2;
}
if (var2 > 0)
{
2019-12-02 22:31:34 +01:00
const s32 _pointer = static_cast<s16>(pop.m_h4);
2015-06-26 16:45:13 +02:00
*pointer = _pointer;
if (_pointer + 1 >= depth * 2)
2014-08-17 17:08:26 +02:00
{
2014-09-23 01:07:40 +02:00
pop.m_h4 = 0;
2014-08-17 17:08:26 +02:00
}
else
{
2014-09-23 01:07:40 +02:00
pop.m_h4++;
2014-08-17 17:08:26 +02:00
}
res = CELL_OK;
}
2014-08-17 23:35:10 +02:00
else if (!isBlocking)
2014-08-17 17:08:26 +02:00
{
2015-03-07 22:20:38 +01:00
return CELL_SYNC_ERROR_AGAIN;
2014-08-17 23:35:10 +02:00
}
else if (!useEventQueue)
{
continue;
}
else
{
res = CELL_OK;
2014-09-23 01:07:40 +02:00
pop.m_h3 = 3;
2014-08-17 23:35:10 +02:00
if (isBlocking != 3)
2014-08-17 17:08:26 +02:00
{
2014-08-17 23:35:10 +02:00
break;
2014-08-17 17:08:26 +02:00
}
}
}
2014-09-23 01:07:40 +02:00
if (queue->pop1.compare_and_swap_test(old, pop))
2014-08-17 17:08:26 +02:00
{
2015-09-15 18:23:17 +02:00
if (!pop.m_h3 || res)
2014-08-17 17:08:26 +02:00
{
2016-08-15 17:30:33 +02:00
return not_an_error(res);
2014-08-17 17:08:26 +02:00
}
break;
}
}
ensure((sys_event_queue_receive(ppu, queue->m_eq_id, vm::null, 0) == CELL_OK));
2014-08-17 17:08:26 +02:00
var1 = 1;
}
}
2021-03-05 20:05:37 +01:00
error_code _cellSyncLFQueueGetPopPointer2(ppu_thread&, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
2014-08-11 20:35:34 +02:00
{
2014-08-17 17:08:26 +02:00
// arguments copied from _cellSyncLFQueueGetPopPointer
cellSync.todo("_cellSyncLFQueueGetPopPointer2(queue=*0x%x, pointer=*0x%x, isBlocking=%d, useEventQueue=%d)", queue, pointer, isBlocking, useEventQueue);
2014-08-11 20:35:34 +02:00
return CELL_OK;
2014-08-11 20:35:34 +02:00
}
error_code _cellSyncLFQueueCompletePopPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal, u32 noQueueFull)
{
2015-06-26 16:45:13 +02:00
// arguments copied from _cellSyncLFQueueCompletePushPointer + unknown argument (noQueueFull taken from LFQueue2CompletePopPointer)
cellSync.warning("_cellSyncLFQueueCompletePopPointer(queue=*0x%x, pointer=%d, fpSendSignal=*0x%x, noQueueFull=%d)", queue, pointer, fpSendSignal, noQueueFull);
2015-06-26 16:45:13 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_SPU2PPU) [[unlikely]]
2014-08-17 17:08:26 +02:00
{
return CELL_SYNC_ERROR_PERM;
}
2015-05-08 11:45:21 +02:00
const s32 depth = queue->m_depth;
2014-08-17 17:08:26 +02:00
while (true)
{
const auto old = queue->pop2.load();
2014-09-23 16:27:18 +02:00
auto pop2 = old;
2014-08-17 17:08:26 +02:00
// Loads must be in this order
const auto old2 = queue->pop3.load();
2014-09-23 16:27:18 +02:00
auto pop3 = old2;
2014-08-17 17:08:26 +02:00
2019-12-02 22:31:34 +01:00
s32 var1 = pointer - pop3.m_h1;
2014-08-17 17:08:26 +02:00
if (var1 < 0)
{
var1 += depth * 2;
}
2019-12-02 22:31:34 +01:00
s32 var2 = static_cast<s16>(queue->push1.load().m_h8) - queue->push1.load().m_h5;
2014-08-17 17:08:26 +02:00
if (var2 < 0)
{
var2 += depth * 2;
}
s32 var9_ = 15 - var1;
2014-09-27 20:49:33 +02:00
// calculate (u16)(1 slw (15 - var1))
2014-08-17 17:08:26 +02:00
if (var9_ & 0x30)
{
var9_ = 0;
}
else
{
var9_ = 1 << var9_;
}
s32 var9 = std::countl_zero<u32>(static_cast<u16>(~(var9_ | pop3.m_h2))) - 16; // count leading zeros in u16
2014-08-17 17:08:26 +02:00
2019-12-02 22:31:34 +01:00
s32 var5 = pop3.m_h2 | var9_;
2014-08-17 17:08:26 +02:00
if (var9 & 0x30)
{
var5 = 0;
}
else
{
var5 <<= var9;
}
2019-12-02 22:31:34 +01:00
s32 var3 = pop3.m_h1 + var9;
2014-08-17 17:08:26 +02:00
if (var3 >= depth * 2)
{
var3 -= depth * 2;
}
2014-09-23 16:27:18 +02:00
u16 pack = pop2.pack; // three packed 5-bit fields
2014-08-17 17:08:26 +02:00
s32 var4 = ((pack >> 10) & 0x1f) - ((pack >> 5) & 0x1f);
if (var4 < 0)
{
var4 += 0x1e;
}
u32 var6;
2014-08-17 23:35:10 +02:00
if (noQueueFull || var2 + var4 > 15 || ((pack >> 10) & 0x1f) == (pack & 0x1f))
{
var6 = -1;
}
else
2014-08-17 17:08:26 +02:00
{
s32 var8 = (pack & 0x1f) - ((pack >> 10) & 0x1f);
if (var8 < 0)
{
var8 += 0x1e;
}
2019-12-02 22:31:34 +01:00
if (var9 > 1 && static_cast<u32>(var8) > 1)
2014-08-17 17:08:26 +02:00
{
ensure((16 - var2 <= 1));
2014-08-17 17:08:26 +02:00
}
s32 var11 = (pack >> 10) & 0x1f;
if (var11 >= 15)
{
var11 -= 15;
}
u16 var12 = (pack >> 10) & 0x1f;
if (var12 == 0x1d)
{
var12 = 0;
}
else
{
var12 = (var12 + 1) << 10;
}
2014-09-23 16:27:18 +02:00
pop2.pack = (pack & 0x83ff) | var12;
2019-12-02 22:31:34 +01:00
var6 = queue->m_hs2[var11];
2014-08-17 17:08:26 +02:00
}
2019-12-02 22:31:34 +01:00
pop3.m_h1 = static_cast<u16>(var3);
pop3.m_h2 = static_cast<u16>(var5);
2014-08-17 17:08:26 +02:00
2014-09-23 16:27:18 +02:00
if (queue->pop2.compare_and_swap_test(old, pop2))
2014-08-17 17:08:26 +02:00
{
if (var6 != umax)
2014-08-17 17:08:26 +02:00
{
ensure((queue->pop3.compare_and_swap_test(old2, pop3)));
ensure((fpSendSignal));
return not_an_error(fpSendSignal(ppu, vm::cast(queue->m_eaSignal.addr()), var6));
2014-08-17 17:08:26 +02:00
}
else
{
pack = queue->pop2.load().pack;
2014-08-17 23:35:10 +02:00
if ((pack & 0x1f) == ((pack >> 10) & 0x1f))
2014-08-17 17:08:26 +02:00
{
2014-09-23 16:27:18 +02:00
if (queue->pop3.compare_and_swap_test(old2, pop3))
2014-08-17 17:08:26 +02:00
{
return CELL_OK;
}
}
}
}
}
}
2021-03-05 20:05:37 +01:00
error_code _cellSyncLFQueueCompletePopPointer2(ppu_thread&, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal, u32 noQueueFull)
2014-08-11 20:35:34 +02:00
{
// arguments copied from _cellSyncLFQueueCompletePopPointer
cellSync.todo("_cellSyncLFQueueCompletePopPointer2(queue=*0x%x, pointer=%d, fpSendSignal=*0x%x, noQueueFull=%d)", queue, pointer, fpSendSignal, noQueueFull);
2014-08-11 20:35:34 +02:00
return CELL_OK;
2014-08-11 20:35:34 +02:00
}
error_code _cellSyncLFQueuePopBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<void> buffer, u32 isBlocking)
2014-08-11 20:35:34 +02:00
{
// cellSyncLFQueuePop has 1 in isBlocking param, cellSyncLFQueueTryPop has 0
cellSync.warning("_cellSyncLFQueuePopBody(queue=*0x%x, buffer=*0x%x, isBlocking=%d)", queue, buffer, isBlocking);
2014-08-11 20:35:34 +02:00
if (!queue || !buffer) [[unlikely]]
2014-08-11 20:35:34 +02:00
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned() || !buffer.aligned(16)) [[unlikely]]
2014-08-11 20:35:34 +02:00
{
return CELL_SYNC_ERROR_ALIGN;
}
vm::var<s32> position;
2015-02-18 23:54:31 +01:00
2014-08-11 20:35:34 +02:00
while (true)
{
s32 res;
2015-06-26 16:45:13 +02:00
2014-10-24 15:24:09 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
2014-08-11 20:35:34 +02:00
{
res = _cellSyncLFQueueGetPopPointer(ppu, queue, position, isBlocking, 0, 0);
2014-08-11 20:35:34 +02:00
}
else
{
res = _cellSyncLFQueueGetPopPointer2(ppu, queue, position, isBlocking, 0);
2014-08-11 20:35:34 +02:00
}
if (!isBlocking || res + 0u != CELL_SYNC_ERROR_AGAIN)
2014-08-11 20:35:34 +02:00
{
if (res)
return not_an_error(res);
2015-06-26 16:45:13 +02:00
2014-08-11 20:35:34 +02:00
break;
}
if (ppu.test_stopped())
{
return 0;
}
2014-08-11 20:35:34 +02:00
}
2015-05-08 11:45:21 +02:00
const s32 depth = queue->m_depth;
const s32 size = queue->m_size;
2015-08-13 15:28:42 +02:00
const s32 pos = *position;
const u32 addr = vm::cast<u64>((queue->m_buffer.addr() & ~1) + size * (pos >= depth ? pos - depth : pos));
std::memcpy(buffer.get_ptr(), vm::base(addr), size);
2015-02-18 23:54:31 +01:00
2014-10-24 15:24:09 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
2014-08-11 20:35:34 +02:00
{
return _cellSyncLFQueueCompletePopPointer(ppu, queue, pos, vm::null, 0);
2014-08-11 20:35:34 +02:00
}
else
{
return _cellSyncLFQueueCompletePopPointer2(ppu, queue, pos, vm::null, 0);
2014-08-11 20:35:34 +02:00
}
}
error_code cellSyncLFQueueClear(vm::ptr<CellSyncLFQueue> queue)
2014-08-11 20:35:34 +02:00
{
cellSync.warning("cellSyncLFQueueClear(queue=*0x%x)", queue);
if (!queue) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
while (true)
{
const auto old = queue->pop1.load();
2014-09-23 01:07:40 +02:00
auto pop = old;
// Loads must be in this order
const auto push = queue->push1.load();
s32 var1, var2;
2014-10-24 15:24:09 +02:00
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
{
2019-12-02 22:31:34 +01:00
var1 = var2 = queue->pop2.load().pack;
}
else
{
2019-12-02 22:31:34 +01:00
var1 = push.m_h7;
var2 = pop.m_h3;
}
2019-12-02 22:31:34 +01:00
if (static_cast<s16>(pop.m_h4) != +pop.m_h1 ||
static_cast<s16>(push.m_h8) != +push.m_h5 ||
((var2 >> 10) & 0x1f) != (var2 & 0x1f) ||
((var1 >> 10) & 0x1f) != (var1 & 0x1f))
{
return CELL_SYNC_ERROR_BUSY;
}
2014-09-23 01:07:40 +02:00
pop.m_h1 = push.m_h5;
pop.m_h2 = push.m_h6;
pop.m_h3 = push.m_h7;
pop.m_h4 = push.m_h8;
if (queue->pop1.compare_and_swap_test(old, pop))
break;
}
return CELL_OK;
}
error_code cellSyncLFQueueSize(vm::ptr<CellSyncLFQueue> queue, vm::ptr<u32> size)
{
cellSync.warning("cellSyncLFQueueSize(queue=*0x%x, size=*0x%x)", queue, size);
if (!queue || !size) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
while (true)
{
const auto old = queue->pop3.load();
// Loads must be in this order
2019-12-02 22:31:34 +01:00
u32 var1 = queue->pop1.load().m_h1;
u32 var2 = queue->push1.load().m_h5;
2014-09-23 01:07:40 +02:00
if (queue->pop3.compare_and_swap_test(old, old))
{
if (var1 <= var2)
{
2014-09-01 02:51:48 +02:00
*size = var2 - var1;
}
else
{
2019-12-02 22:31:34 +01:00
*size = var2 - var1 + queue->m_depth * 2;
}
2015-06-26 16:45:13 +02:00
return CELL_OK;
}
}
}
error_code cellSyncLFQueueDepth(vm::ptr<CellSyncLFQueue> queue, vm::ptr<u32> depth)
{
cellSync.trace("cellSyncLFQueueDepth(queue=*0x%x, depth=*0x%x)", queue, depth);
if (!queue || !depth) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
2014-09-01 02:51:48 +02:00
*depth = queue->m_depth;
2015-06-26 16:45:13 +02:00
return CELL_OK;
}
error_code _cellSyncLFQueueGetSignalAddress(vm::cptr<CellSyncLFQueue> queue, vm::pptr<void> ppSignal)
{
cellSync.trace("_cellSyncLFQueueGetSignalAddress(queue=*0x%x, ppSignal=**0x%x)", queue, ppSignal);
if (!queue || !ppSignal) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
2015-05-08 11:45:21 +02:00
*ppSignal = queue->m_eaSignal;
return CELL_OK;
}
error_code cellSyncLFQueueGetDirection(vm::cptr<CellSyncLFQueue> queue, vm::ptr<u32> direction)
{
cellSync.trace("cellSyncLFQueueGetDirection(queue=*0x%x, direction=*0x%x)", queue, direction);
if (!queue || !direction) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
2014-09-01 02:51:48 +02:00
*direction = queue->m_direction;
2015-06-26 16:45:13 +02:00
return CELL_OK;
}
error_code cellSyncLFQueueGetEntrySize(vm::cptr<CellSyncLFQueue> queue, vm::ptr<u32> entry_size)
{
cellSync.trace("cellSyncLFQueueGetEntrySize(queue=*0x%x, entry_size=*0x%x)", queue, entry_size);
if (!queue || !entry_size) [[unlikely]]
{
return CELL_SYNC_ERROR_NULL_POINTER;
}
2015-06-26 16:45:13 +02:00
if (!queue.aligned()) [[unlikely]]
{
return CELL_SYNC_ERROR_ALIGN;
}
2014-09-01 02:51:48 +02:00
*entry_size = queue->m_size;
2015-06-26 16:45:13 +02:00
return CELL_OK;
2014-08-18 23:16:48 +02:00
}
error_code _cellSyncLFQueueAttachLv2EventQueue(vm::ptr<u32> spus, u32 num, vm::ptr<CellSyncLFQueue> queue)
{
cellSync.todo("_cellSyncLFQueueAttachLv2EventQueue(spus=*0x%x, num=%d, queue=*0x%x)", spus, num, queue);
2014-08-18 23:16:48 +02:00
return CELL_OK;
}
error_code _cellSyncLFQueueDetachLv2EventQueue(vm::ptr<u32> spus, u32 num, vm::ptr<CellSyncLFQueue> queue)
{
cellSync.todo("_cellSyncLFQueueDetachLv2EventQueue(spus=*0x%x, num=%d, queue=*0x%x)", spus, num, queue);
2014-08-18 23:16:48 +02:00
return CELL_OK;
}
2016-03-21 20:43:03 +01:00
DECLARE(ppu_module_manager::cellSync)("cellSync", []()
{
REG_FUNC(cellSync, cellSyncMutexInitialize);
REG_FUNC(cellSync, cellSyncMutexLock);
REG_FUNC(cellSync, cellSyncMutexTryLock);
REG_FUNC(cellSync, cellSyncMutexUnlock);
REG_FUNC(cellSync, cellSyncBarrierInitialize);
REG_FUNC(cellSync, cellSyncBarrierNotify);
REG_FUNC(cellSync, cellSyncBarrierTryNotify);
REG_FUNC(cellSync, cellSyncBarrierWait);
REG_FUNC(cellSync, cellSyncBarrierTryWait);
REG_FUNC(cellSync, cellSyncRwmInitialize);
REG_FUNC(cellSync, cellSyncRwmRead);
REG_FUNC(cellSync, cellSyncRwmTryRead);
REG_FUNC(cellSync, cellSyncRwmWrite);
REG_FUNC(cellSync, cellSyncRwmTryWrite);
REG_FUNC(cellSync, cellSyncQueueInitialize);
REG_FUNC(cellSync, cellSyncQueuePush);
REG_FUNC(cellSync, cellSyncQueueTryPush);
REG_FUNC(cellSync, cellSyncQueuePop);
REG_FUNC(cellSync, cellSyncQueueTryPop);
REG_FUNC(cellSync, cellSyncQueuePeek);
REG_FUNC(cellSync, cellSyncQueueTryPeek);
REG_FUNC(cellSync, cellSyncQueueSize);
REG_FUNC(cellSync, cellSyncQueueClear);
REG_FUNC(cellSync, cellSyncLFQueueGetEntrySize);
REG_FUNC(cellSync, cellSyncLFQueueSize);
REG_FUNC(cellSync, cellSyncLFQueueClear);
REG_FUNC(cellSync, _cellSyncLFQueueCompletePushPointer2);
REG_FUNC(cellSync, _cellSyncLFQueueGetPopPointer2);
REG_FUNC(cellSync, _cellSyncLFQueueCompletePushPointer);
REG_FUNC(cellSync, _cellSyncLFQueueAttachLv2EventQueue);
REG_FUNC(cellSync, _cellSyncLFQueueGetPushPointer2);
REG_FUNC(cellSync, _cellSyncLFQueueGetPopPointer);
REG_FUNC(cellSync, _cellSyncLFQueueCompletePopPointer2);
REG_FUNC(cellSync, _cellSyncLFQueueDetachLv2EventQueue);
REG_FUNC(cellSync, cellSyncLFQueueInitialize);
REG_FUNC(cellSync, _cellSyncLFQueueGetSignalAddress);
REG_FUNC(cellSync, _cellSyncLFQueuePushBody);
REG_FUNC(cellSync, cellSyncLFQueueGetDirection);
REG_FUNC(cellSync, cellSyncLFQueueDepth);
REG_FUNC(cellSync, _cellSyncLFQueuePopBody);
REG_FUNC(cellSync, _cellSyncLFQueueGetPushPointer);
REG_FUNC(cellSync, _cellSyncLFQueueCompletePopPointer);
});