diff --git a/core/libs/prx/libSceAgc/Acb/src/Memory.cpp b/core/libs/prx/libSceAgc/Acb/src/Memory.cpp index 17df9c863..c2b181de8 100644 --- a/core/libs/prx/libSceAgc/Acb/src/Memory.cpp +++ b/core/libs/prx/libSceAgc/Acb/src/Memory.cpp @@ -31,8 +31,7 @@ std::uint32_t* APS5_VABI sceAgcAcbDmaData(CommandBuffer* buf, std::uint8_t dst, } std::uint32_t APS5_VABI sceAgcAcbDmaDataGetSize() { - NotImplemented_nid_no_patch(__func__); - return 0; + return 28; } std::uint32_t* APS5_VABI sceAgcAcbAtomicMem(CommandBuffer* buf, std::uint8_t atomicOp, std::uint8_t command, std::uint8_t cachePolicy, const volatile void* address, std::uint64_t srcData, std::uint64_t compareData, std::uint16_t loopInterval) { diff --git a/core/libs/prx/libSceAgc/CMakeLists.txt b/core/libs/prx/libSceAgc/CMakeLists.txt index ed4ed41a3..d58fe5d6a 100644 --- a/core/libs/prx/libSceAgc/CMakeLists.txt +++ b/core/libs/prx/libSceAgc/CMakeLists.txt @@ -113,3 +113,22 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$") endif() endif() + +add_test_executable(agc_dma_data_tests EXCLUDE_FROM_ALL + tests/DmaData.cpp + Command/src/Packet.cpp + Command/src/Memory.cpp + Acb/src/Memory.cpp + DcbFlow/src/Memory.cpp +) +target_include_directories(agc_dma_data_tests PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(agc_dma_data_tests PRIVATE libc) +configure_windows_unwind(agc_dma_data_tests) +if(BUILD_TESTING) + set_target_properties(agc_dma_data_tests PROPERTIES EXCLUDE_FROM_ALL FALSE) + add_test(NAME agc_dma_data_tests COMMAND agc_dma_data_tests) + if(WIN32) + set_tests_properties(agc_dma_data_tests PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceAgc/tests/DmaData.cpp b/core/libs/prx/libSceAgc/tests/DmaData.cpp new file mode 100644 index 000000000..77c80988f --- /dev/null +++ b/core/libs/prx/libSceAgc/tests/DmaData.cpp @@ -0,0 +1,94 @@ +#include "prx/libSceAgc/Command/include/Packet.hpp" + +#include +#include +#include +#include +#include +#include + +extern "C" { +std::uint32_t* APS5_VABI sceAgcAcbDmaData(CommandBuffer*, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t, std::uint64_t, std::uint32_t, std::uint8_t, std::uint8_t); +std::uint32_t APS5_VABI sceAgcAcbDmaDataGetSize(); +std::uint32_t* APS5_VABI sceAgcDcbDmaData(CommandBuffer*, std::uint8_t, std::uint8_t, std::uint8_t, std::uint64_t, std::uint8_t, std::uint8_t, std::uint64_t, std::uint32_t, std::uint8_t, std::uint8_t, std::uint8_t); +std::uint32_t APS5_VABI sceAgcDcbDmaDataGetSize(); +} + +namespace { + +constexpr std::uint32_t Sentinel = 0xabcdef01u; +constexpr std::uint64_t Destination = 0x0000778899aabbccull; +constexpr std::uint64_t Source = 0x0000112233445566ull; + +void check(bool condition, const char* message) { + if (!condition) throw std::runtime_error(message); +} + +template +void expectFailure(TAction action) { + try { + action(); + } catch (const std::runtime_error&) { + return; + } + throw std::runtime_error("expected invalid input to fail"); +} + +struct Storage { + std::array words{}; + CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0}; + + Storage() { + words.fill(Sentinel); + } + + void limitTo(std::uint32_t count) { + buffer.cursor_down = buffer.cursor_up + count; + } +}; + +std::uint32_t* writeCompute(CommandBuffer* buffer) { + return sceAgcAcbDmaData(buffer, 5, 2, Destination, 10, 1, Source, 0x1234u, 1, 1); +} + +std::uint32_t* writeDraw(CommandBuffer* buffer) { + return sceAgcDcbDmaData(buffer, 1, 5, 2, Destination, 10, 1, Source, 0x1234u, 1, 1, 1); +} + +template +void testCommand(TWriter writer, TSize size, std::uint32_t control) { + const std::array expected{0xc0055000u, control, 0x33445566u, 0x1122u, 0x99aabbccu, 0x7788u, 0xd8001234u}; + const auto count = static_cast(expected.size()); + check(size() == count * sizeof(std::uint32_t), "size query does not match the DMA packet"); + + Storage storage; + auto* packet = writer(&storage.buffer); + check(packet == storage.words.data() && std::equal(expected.begin(), expected.end(), packet), "incorrect DMA packet"); + check(storage.buffer.cursor_up == packet + size() / sizeof(std::uint32_t), "cursor advance does not match the size query"); + check(std::all_of(storage.words.begin() + count, storage.words.end(), [](std::uint32_t word) { return word == Sentinel; }), "DMA command overwrote following words"); + + Storage exact; + exact.limitTo(size() / sizeof(std::uint32_t)); + check(writer(&exact.buffer) == exact.words.data() && exact.buffer.cursor_up == exact.buffer.cursor_down, "DMA packet does not fill the queried size"); + + Storage shortBuffer; + shortBuffer.limitTo(size() / sizeof(std::uint32_t) - 1u); + const auto before = shortBuffer.words; + expectFailure([&] { writer(&shortBuffer.buffer); }); + check(shortBuffer.words == before && shortBuffer.buffer.cursor_up == shortBuffer.words.data(), "failed DMA write modified the buffer"); +} + +} + +int main() { + try { + testCommand(writeCompute, sceAgcAcbDmaDataGetSize, 0x44102000u); + testCommand(writeDraw, sceAgcDcbDmaDataGetSize, 0xc4102001u); + check(sceAgcAcbDmaDataGetSize() == sceAgcDcbDmaDataGetSize(), "compute and draw DMA sizes differ"); + std::puts("AGC DMA data tests passed"); + return 0; + } catch (const std::exception& error) { + std::fprintf(stderr, "%s\n", error.what()); + return 1; + } +}