#pragma once namespace vm { using namespace ps3; } // Return Codes enum { CELL_SYNC_ERROR_AGAIN = 0x80410101, CELL_SYNC_ERROR_INVAL = 0x80410102, CELL_SYNC_ERROR_NOSYS = 0x80410103, CELL_SYNC_ERROR_NOMEM = 0x80410104, CELL_SYNC_ERROR_SRCH = 0x80410105, CELL_SYNC_ERROR_NOENT = 0x80410106, CELL_SYNC_ERROR_NOEXEC = 0x80410107, CELL_SYNC_ERROR_DEADLK = 0x80410108, CELL_SYNC_ERROR_PERM = 0x80410109, CELL_SYNC_ERROR_BUSY = 0x8041010A, CELL_SYNC_ERROR_ABORT = 0x8041010C, CELL_SYNC_ERROR_FAULT = 0x8041010D, CELL_SYNC_ERROR_CHILD = 0x8041010E, CELL_SYNC_ERROR_STAT = 0x8041010F, CELL_SYNC_ERROR_ALIGN = 0x80410110, CELL_SYNC_ERROR_NULL_POINTER = 0x80410111, CELL_SYNC_ERROR_NOT_SUPPORTED_THREAD = 0x80410112, CELL_SYNC_ERROR_SHOTAGE = 0x80410112, CELL_SYNC_ERROR_NO_NOTIFIER = 0x80410113, CELL_SYNC_ERROR_UNKNOWNKEY = 0x80410113, CELL_SYNC_ERROR_NO_SPU_CONTEXT_STORAGE = 0x80410114, }; struct alignas(4) sync_mutex_t // CellSyncMutex sync var { be_t rel; be_t acq; be_t acquire() { return acq++; } bool try_lock() { return acq++ == rel; } void unlock() { rel++; } }; using CellSyncMutex = atomic_t; CHECK_SIZE_ALIGN(CellSyncMutex, 4, 4); struct alignas(4) sync_barrier_t // CellSyncBarrier sync var { be_t value; be_t count; bool try_notify() { // extract m_value (repeat if < 0), increase, compare with second s16, set sign bit if equal, insert it back s16 v = value; if (v < 0) { return false; } if (++v == count) { v |= 0x8000; } value = v; return true; }; bool try_wait() { // extract m_value (repeat if >= 0), decrease it, set 0 if == 0x8000, insert it back s16 v = value; if (v >= 0) { return false; } if (--v == -0x8000) { v = 0; } value = v; return true; } }; using CellSyncBarrier = atomic_t; CHECK_SIZE_ALIGN(CellSyncBarrier, 4, 4); struct sync_rwm_t // CellSyncRwm sync var { be_t readers; be_t writers; bool try_read_begin() { if (writers) { return false; } readers++; return true; } bool try_read_end() { if (!readers) { return false; } readers--; return true; } bool try_write_begin() { if (writers) { return false; } writers = 1; return true; } }; struct alignas(16) CellSyncRwm { atomic_t ctrl; // sync var be_t size; vm::bptr buffer; }; CHECK_SIZE_ALIGN(CellSyncRwm, 16, 16); struct sync_queue_t // CellSyncQueue sync var { be_t m_v1; be_t m_v2; bool try_push_begin(u32 depth, u32& position) { const u32 v1 = m_v1; const u32 v2 = m_v2; // compare 5th byte with zero (break if not zero) // compare (second u32 (u24) + first byte) with depth (break if greater or equal) if ((v2 >> 24) || ((v2 & 0xffffff) + (v1 >> 24)) >= depth) { return false; } // extract first u32 (u24) (-> position), calculate (position + 1) % depth, insert it back // insert 1 in 5th u8 // extract second u32 (u24), increase it, insert it back position = (v1 & 0xffffff); m_v1 = (v1 & 0xff000000) | ((position + 1) % depth); m_v2 = (1 << 24) | ((v2 & 0xffffff) + 1); return true; } bool try_pop_begin(u32 depth, u32& position) { const u32 v1 = m_v1; const u32 v2 = m_v2; // extract first u8, repeat if not zero // extract second u32 (u24), subtract 5th u8, compare with zero, repeat if less or equal if ((v1 >> 24) || ((v2 & 0xffffff) <= (v2 >> 24))) { return false; } // insert 1 in first u8 // extract first u32 (u24), add depth, subtract second u32 (u24), calculate (% depth), save to position // extract second u32 (u24), decrease it, insert it back m_v1 = 0x1000000 | v1; position = ((v1 & 0xffffff) + depth - (v2 & 0xffffff)) % depth; m_v2 = (v2 & 0xff000000) | ((v2 & 0xffffff) - 1); return true; } bool try_peek_begin(u32 depth, u32& position) { const u32 v1 = m_v1; const u32 v2 = m_v2; if ((v1 >> 24) || ((v2 & 0xffffff) <= (v2 >> 24))) { return false; } m_v1 = 0x1000000 | v1; position = ((v1 & 0xffffff) + depth - (v2 & 0xffffff)) % depth; return true; } bool try_clear_begin_1() { if (m_v1 & 0xff000000) { return false; } m_v1 |= 0x1000000; return true; } bool try_clear_begin_2() { if (m_v2 & 0xff000000) { return false; } m_v2 |= 0x1000000; return true; } }; struct alignas(32) CellSyncQueue { atomic_t ctrl; be_t size; be_t depth; vm::bptr buffer; be_t reserved; u32 check_depth() { const auto data = ctrl.load(); if ((data.m_v1 & 0xffffff) > depth || (data.m_v2 & 0xffffff) > depth) { throw EXCEPTION("Invalid queue pointers"); } return depth; } }; CHECK_SIZE_ALIGN(CellSyncQueue, 32, 32); enum CellSyncQueueDirection : u32 // CellSyncLFQueueDirection { CELL_SYNC_QUEUE_SPU2SPU = 0, // SPU to SPU CELL_SYNC_QUEUE_SPU2PPU = 1, // SPU to PPU CELL_SYNC_QUEUE_PPU2SPU = 2, // PPU to SPU CELL_SYNC_QUEUE_ANY2ANY = 3, // SPU/PPU to SPU/PPU }; struct alignas(128) CellSyncLFQueue { struct pop1_t { be_t m_h1; be_t m_h2; be_t m_h3; be_t m_h4; }; struct pop2_t { be_t pack; }; struct pop3_t { be_t m_h1; be_t m_h2; }; struct push1_t { be_t m_h5; be_t m_h6; be_t m_h7; be_t m_h8; }; struct push2_t { be_t pack; }; struct push3_t { be_t m_h5; be_t m_h6; }; union // 0x0 { atomic_t pop1; atomic_t pop3; }; union // 0x8 { atomic_t push1; atomic_t push3; }; be_t m_size; // 0x10 be_t m_depth; // 0x14 vm::bcptr m_buffer; // 0x18 u8 m_bs[4]; // 0x20 be_t m_direction; // 0x24 CellSyncQueueDirection be_t m_v1; // 0x28 atomic_be_t init; // 0x2C atomic_t push2; // 0x30 be_t m_hs1[15]; // 0x32 atomic_t pop2; // 0x50 be_t m_hs2[15]; // 0x52 vm::bptr m_eaSignal; // 0x70 be_t m_v2; // 0x78 be_t m_eq_id; // 0x7C std::string dump() { std::string res = "CellSyncLFQueue dump:"; auto data = (be_t*)this; for (u32 i = 0; i < sizeof(CellSyncLFQueue) / sizeof(u64); i += 2) { res += "\n*** 0x"; res += fmt::to_hex(data[i + 0], 16); res += " 0x"; res += fmt::to_hex(data[i + 1], 16); } return res; } }; CHECK_SIZE_ALIGN(CellSyncLFQueue, 128, 128); // Prototypes s32 cellSyncLFQueueInitialize(vm::ptr queue, vm::cptr buffer, u32 size, u32 depth, u32 direction, vm::ptr eaSignal);