diff --git a/core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp b/core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp index bb68d80d8..2a6a89c88 100644 --- a/core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp +++ b/core/libs/prx/libSceAgc/DcbFlow/src/Control.cpp @@ -6,9 +6,45 @@ #include "prx/libSceAgcDriver/Execution/include/VideoOutput.hpp" #include #include +#include +#include +#include +#include +#include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +namespace { + +std::vector resetQueueGroup(std::uint32_t bit, std::uint32_t state) { + const std::uint32_t stateThree = state == 3u ? 0x00840f80u : 0x00000000u; + switch (bit) { + case 0x001u: + return {0xc0039f00u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u}; + case 0x002u: + return {0xc0036300u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u}; + case 0x004u: + return {0xc0036400u, 0x00000000u, stateThree, 0x80000000u, 0x00000000u}; + case 0x008u: + return {0xc0002f00u, 0x00000001u}; + case 0x010u: + return {0xc0017a00u, 0x20000243u, 0x00000480u, 0xc0012600u, 0x00000000u, 0x00000000u, 0xc0001300u, 0xffffffffu}; + case 0x020u: + return {0xc0021102u, 0x00000001u, 0x00000000u, 0x00000000u, 0xc0021100u, 0x00000001u, 0x00000000u, 0x00000000u}; + case 0x080u: + return {0xc0004600u, 0x00000407u}; + case 0x100u: + return {0xc0004600u, 0x00000410u}; + case 0x800u: + return {0xc0036400u, 0x00000000u, stateThree, 0x80000000u, 0x00000000u}; + default: + throw std::runtime_error(std::string("sceAgcDcbResetQueue: unsupported op bit ") + std::to_string(bit)); + } +} + +} + extern "C" { @@ -31,7 +67,34 @@ std::uint32_t APS5_VABI sceAgcDcbJumpGetSize() { std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t op, std::uint32_t state) { Agc::Command::CheckBits(op, 0xfffu, __func__); Agc::Command::CheckBits(state, 0xfu, __func__); - return Agc::Command::Emit(buf, 0x12u, {state}, __func__); + + constexpr std::array unsupported{0x040u, 0x200u, 0x400u}; + for (const auto bit : unsupported) { + if ((op & bit) != 0u) { + char message[64]; + std::snprintf(message, sizeof(message), "unsupported op bit 0x%x", bit); + Agc::Command::Require(false, __func__, message); + } + } + + const std::uint32_t envelopeFlag = state == 2u ? 0x00000008u : 0x00000000u; + std::vector words{ + 0xffff1000u, + 0xc0027904u, 0x00000342u, 0xce200000u | (op & 0xffffu), envelopeFlag}; + + constexpr std::array order{0x001u, 0x002u, 0x004u, 0x008u, 0x010u, 0x020u, 0x080u, 0x100u, 0x800u}; + for (const auto bit : order) { + if ((op & bit) != 0u) { + const auto group = resetQueueGroup(bit, state); + words.insert(words.end(), group.begin(), group.end()); + } + } + + words.insert(words.end(), {0xc0017904u, 0x00000342u, 0xcea00000u}); + + auto* packet = Agc::Command::Allocate(buf, static_cast(words.size()), __func__); + std::copy(words.begin(), words.end(), packet); + return packet; } std::uint32_t* APS5_VABI sceAgcDcbClearState(CommandBuffer* buf, std::uint32_t command) { diff --git a/core/libs/prx/libSceAgc/tests/Commands.cpp b/core/libs/prx/libSceAgc/tests/Commands.cpp index 58b9f1026..bd953a62d 100644 --- a/core/libs/prx/libSceAgc/tests/Commands.cpp +++ b/core/libs/prx/libSceAgc/tests/Commands.cpp @@ -81,14 +81,46 @@ bool APS5_VABI growReserved(CommandBuffer* buffer, std::uint32_t count, void* us void testPackets() { Storage storage; - sceAgcDcbResetQueue(&storage.buffer, 0, 3); + auto* reset = sceAgcDcbResetQueue(&storage.buffer, 0, 3); sceAgcDcbDrawIndexAuto(&storage.buffer, 17, 0); - const std::array expected{0xc0001200u, 3, 0xc0012d00u, 17, 2}; - check(std::equal(expected.begin(), expected.end(), storage.words.begin()), "reset or draw packet mismatch"); + const std::array expected{0xffff1000u, 0xc0027904u, 0x00000342u, 0xce200000u, 0x00000000u, 0xc0017904u, 0x00000342u, 0xcea00000u, 0xc0012d00u, 17, 2}; + check(reset == storage.words.data() && std::equal(expected.begin(), expected.end(), storage.words.begin()), "reset or draw packet mismatch"); check(storage.buffer.cursor_up == storage.words.data() + expected.size(), "incorrect packet cursor advance"); + + Storage base; + const auto* baseReset = sceAgcDcbResetQueue(&base.buffer, 0, 0); + const std::array baseEnvelope{0xffff1000u, 0xc0027904u, 0x00000342u, 0xce200000u, 0x00000000u, 0xc0017904u, 0x00000342u, 0xcea00000u}; + check(baseReset == base.words.data() && std::equal(baseEnvelope.begin(), baseEnvelope.end(), baseReset), "op=0 state=0 envelope mismatch"); + check(base.buffer.cursor_up == base.words.data() + baseEnvelope.size(), "op=0 state=0 cursor advance mismatch"); + + Storage single; + sceAgcDcbResetQueue(&single.buffer, 0x001u, 0); + const std::array singleExpected{0xffff1000u, 0xc0027904u, 0x00000342u, 0xce200001u, 0x00000000u, 0xc0039f00u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0xc0017904u, 0x00000342u, 0xcea00000u}; + check(std::equal(singleExpected.begin(), singleExpected.end(), single.words.begin()), "op=0x001 group mismatch"); + check(single.buffer.cursor_up == single.words.data() + singleExpected.size(), "op=0x001 cursor advance mismatch"); + + Storage flagged; + sceAgcDcbResetQueue(&flagged.buffer, 0, 2); + check(flagged.words[4] == 0x00000008u, "state=2 did not set the envelope flag dword"); + + Storage stated; + sceAgcDcbResetQueue(&stated.buffer, 0x004u, 3); + const std::array statedExpected{0xffff1000u, 0xc0027904u, 0x00000342u, 0xce200004u, 0x00000000u, 0xc0036400u, 0x00000000u, 0x00840f80u, 0x80000000u, 0x00000000u, 0xc0017904u, 0x00000342u, 0xcea00000u}; + check(std::equal(statedExpected.begin(), statedExpected.end(), stated.words.begin()), "state=3 did not set the 0x004 group payload dword"); + + Storage upper; + sceAgcDcbResetQueue(&upper.buffer, 0x800u, 0); + const std::array upperExpected{0xffff1000u, 0xc0027904u, 0x00000342u, 0xce200800u, 0x00000000u, 0xc0036400u, 0x00000000u, 0x00000000u, 0x80000000u, 0x00000000u, 0xc0017904u, 0x00000342u, 0xcea00000u}; + check(std::equal(upperExpected.begin(), upperExpected.end(), upper.words.begin()), "op=0x800 group mismatch"); + check(upper.buffer.cursor_up == upper.words.data() + upperExpected.size(), "op=0x800 cursor advance mismatch"); + const auto before = storage.words; expectFailure([&] { sceAgcDcbResetQueue(&storage.buffer, 0, 16); }); - check(storage.words == before, "invalid reset modified packet memory"); + expectFailure([&] { sceAgcDcbResetQueue(&storage.buffer, 0x040u, 0); }); + expectFailure([&] { sceAgcDcbResetQueue(&storage.buffer, 0x200u, 0); }); + expectFailure([&] { sceAgcDcbResetQueue(&storage.buffer, 0x400u, 0); }); + expectFailure([&] { sceAgcDcbResetQueue(&storage.buffer, 0x1000u, 0); }); + check(storage.words == before && storage.buffer.cursor_up == storage.words.data() + expected.size(), "invalid reset modified packet memory"); Storage destination; CommandBuffer empty{nullptr, nullptr, nullptr, nullptr, grow, &destination, 0}; Agc::Command::Emit(&empty, 0x15u, {1, 1, 1, 0x41u}, __func__);