#include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include using Entry = void (APS5_VABI*)(std::uint64_t, std::uint64_t); extern "C" { std::int32_t APS5_VABI _sceFiberInitializeImpl_nid_postfix(FiberObject*, const char*, Entry, std::uint64_t, void*, std::uint64_t, const void*, std::uint32_t); std::int32_t APS5_VABI sceFiberFinalize(FiberObject*); std::int32_t APS5_VABI sceFiberRun_nid_postfix(FiberObject*, std::uint64_t, std::uint64_t*); std::int32_t APS5_VABI sceFiberSwitch(FiberObject*, std::uint64_t, std::uint64_t*); std::int32_t APS5_VABI sceFiberReturnToThread(std::uint64_t, std::uint64_t*); std::int32_t APS5_VABI sceFiberGetSelf(FiberObject**); } static void Require(bool value, const char* what) { if (!value) { std::fprintf(stderr, "Fiber check failed: %s\n", what); std::abort(); } } namespace { constexpr std::int32_t FiberErrorPermission = static_cast(0x80590005); constexpr std::int32_t FiberErrorState = static_cast(0x80590006); alignas(16) std::array g_firstContext; alignas(16) std::array g_secondContext; struct alignas(8) FiberStorage { unsigned char bytes[0x100]; }; FiberStorage g_firstStorage; FiberStorage g_secondStorage; auto* const g_first = reinterpret_cast(&g_firstStorage); auto* const g_second = reinterpret_cast(&g_secondStorage); void APS5_VABI FirstEntry(std::uint64_t argOnInitialize, std::uint64_t argOnRun) { Require(argOnInitialize == 11 && argOnRun == 100, "first fiber arguments"); FiberObject* self = nullptr; Require(sceFiberGetSelf(&self) == 0 && self == g_first, "first fiber self"); volatile double carried = 1.5; std::uint64_t received = 0; Require(sceFiberSwitch(g_second, 200, &received) == 0, "switch to second fiber"); Require(received == 300 && carried == 1.5, "switch back to first fiber"); for (;;) { Require(sceFiberReturnToThread(received + 1, &received) == 0, "first fiber return"); } } void APS5_VABI SecondEntry(std::uint64_t argOnInitialize, std::uint64_t argOnRun) { Require(argOnInitialize == 22 && argOnRun == 200, "second fiber arguments"); Require(sceFiberSwitch(g_second, 0, nullptr) == FiberErrorState, "switch to self"); _mm_setcsr(_mm_getcsr() | 0x8000u); std::uint64_t received = 0; Require(sceFiberReturnToThread(250, &received) == 0 && received == 260, "second fiber resumed on another thread"); Require((_mm_getcsr() & 0x8000u) != 0, "second fiber keeps its MXCSR"); Require(sceFiberSwitch(g_first, 300, nullptr) == 0, "switch to first fiber"); Require(false, "second fiber resumed after its last switch"); } } int main() { FiberObject* self = nullptr; Require(sceFiberGetSelf(&self) == FiberErrorPermission, "no fiber on the thread"); Require(_sceFiberInitializeImpl_nid_postfix(g_first, "first", FirstEntry, 11, g_firstContext.data(), g_firstContext.size(), nullptr, 0) == 0, "initialize first"); Require(_sceFiberInitializeImpl_nid_postfix(g_second, "second", SecondEntry, 22, g_secondContext.data(), g_secondContext.size(), nullptr, 0) == 0, "initialize second"); const auto threadCsr = _mm_getcsr(); std::uint64_t returned = 0; Require(sceFiberRun_nid_postfix(g_first, 100, &returned) == 0 && returned == 250, "run first until second returns"); Require(_mm_getcsr() == threadCsr, "thread MXCSR restored after the fibers"); std::thread([&] { Require(sceFiberRun_nid_postfix(g_second, 260, &returned) == 0 && returned == 301, "resume second on another thread"); }).join(); Require(sceFiberFinalize(g_first) == 0, "finalize first"); Require(sceFiberFinalize(g_second) == 0, "finalize second"); }