AnyPS5/core/libs/prx/libSceAgc/tests/RegisterCommands.cpp

117 lines
5.6 KiB
C++

#include "prx/libSceAgc/Command/include/Packet.hpp"
#include <algorithm>
#include <array>
#include <cstdio>
#include <stdexcept>
extern "C" {
std::uint32_t* APS5_VABI sceAgcDcbSetCxRegisterDirect(CommandBuffer*, ShaderRegister);
std::uint32_t* APS5_VABI sceAgcDcbSetShRegisterDirect(CommandBuffer*, ShaderRegister);
std::uint32_t* APS5_VABI sceAgcDcbSetUcRegisterDirect(CommandBuffer*, ShaderRegister);
std::uint32_t APS5_VABI sceAgcDcbSetCxRegisterDirectGetSize();
std::uint32_t APS5_VABI sceAgcDcbSetShRegisterDirectGetSize();
std::uint32_t APS5_VABI sceAgcDcbSetUcRegisterDirectGetSize();
std::uint32_t* APS5_VABI sceAgcDcbSetCxRegistersIndirect(CommandBuffer*, const volatile ShaderRegister*, std::uint32_t);
std::uint32_t* APS5_VABI sceAgcDcbSetShRegistersIndirect(CommandBuffer*, const volatile ShaderRegister*, std::uint32_t);
std::uint32_t* APS5_VABI sceAgcDcbSetUcRegistersIndirect(CommandBuffer*, const volatile ShaderRegister*, std::uint32_t);
std::uint32_t APS5_VABI sceAgcDcbSetCxRegistersIndirectGetSize(std::uint32_t);
std::uint32_t APS5_VABI sceAgcDcbSetShRegistersIndirectGetSize(std::uint32_t);
std::uint32_t APS5_VABI sceAgcDcbSetUcRegistersIndirectGetSize(std::uint32_t);
int APS5_VABI sceAgcSetCxRegIndirectPatchSetNumRegisters(std::uint32_t*, std::uint32_t);
int APS5_VABI sceAgcSetShRegIndirectPatchSetNumRegisters(std::uint32_t*, std::uint32_t);
int APS5_VABI sceAgcSetUcRegIndirectPatchSetNumRegisters(std::uint32_t*, std::uint32_t);
}
namespace {
void check(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
template <typename TAction>
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<std::uint32_t, 16> words{};
CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0};
};
void testDirect() {
const std::array writers{sceAgcDcbSetCxRegisterDirect, sceAgcDcbSetShRegisterDirect, sceAgcDcbSetUcRegisterDirect};
const std::array sizes{sceAgcDcbSetCxRegisterDirectGetSize, sceAgcDcbSetShRegisterDirectGetSize, sceAgcDcbSetUcRegisterDirectGetSize};
const std::array headers{0xc0016900u, 0xc0017600u, 0xc0017900u};
for (std::size_t i = 0; i < writers.size(); ++i) {
Storage storage;
storage.words.fill(0xabcdef01u);
auto* packet = writers[i](&storage.buffer, {0xffffu, 0x12345678u});
const std::array expected{headers[i], 0xffffu, 0x12345678u};
check(packet == storage.words.data() && std::equal(expected.begin(), expected.end(), packet), "incorrect direct register packet");
check(storage.buffer.cursor_up == packet + 3 && sizes[i]() == 12, "direct size/cursor mismatch");
check(packet[3] == 0xabcdef01u, "direct command overwrote following word");
const auto before = storage.words;
expectFailure([&] { writers[i](&storage.buffer, {0x10000u, 1}); });
expectFailure([&] { writers[i](nullptr, {0, 1}); });
storage.buffer.cursor_down = storage.buffer.cursor_up + 2;
expectFailure([&] { writers[i](&storage.buffer, {0, 1}); });
check(storage.words == before && storage.buffer.cursor_up == packet + 3, "invalid direct write modified buffer");
}
}
void testIndirect() {
const std::array writers{sceAgcDcbSetCxRegistersIndirect, sceAgcDcbSetShRegistersIndirect, sceAgcDcbSetUcRegistersIndirect};
const std::array sizes{sceAgcDcbSetCxRegistersIndirectGetSize, sceAgcDcbSetShRegistersIndirectGetSize, sceAgcDcbSetUcRegistersIndirectGetSize};
const std::array setters{sceAgcSetCxRegIndirectPatchSetNumRegisters, sceAgcSetShRegIndirectPatchSetNumRegisters, sceAgcSetUcRegIndirectPatchSetNumRegisters};
const std::array headers{0xc0039f00u, 0xc0036300u, 0xc0036400u};
ShaderRegister reg{0x10u, 7};
for (std::size_t i = 0; i < writers.size(); ++i) {
Storage storage;
auto* packet = writers[i](&storage.buffer, &reg, 1);
const auto original = storage.words;
check(packet[0] == headers[i] && storage.buffer.cursor_up == packet + 5, "incorrect indirect packet");
for (const auto count : {0u, 0x3fffu, 2u}) {
check(sizes[i](count) == 20, "indirect size mismatch");
check(setters[i](packet, count) == 0 && packet[4] == count, "indirect count was not replaced");
auto expected = original;
expected[4] = count;
check(storage.words == expected, "count setter modified unrelated packet data");
}
const auto before = storage.words;
for (const auto count : {0x4000u, 0xffffffffu}) {
expectFailure([&] { sizes[i](count); });
expectFailure([&] { setters[i](packet, count); });
}
expectFailure([&] { setters[i](nullptr, 0); });
expectFailure([&] { setters[(i + 1) % setters.size()](packet, 0); });
check(storage.words == before, "invalid count setter modified packet");
for (const auto field : {0u, 3u, 4u}) {
storage.words = before;
packet[field] = field == 0 ? headers[i] ^ 0x10000u : 0xffffffffu;
const auto malformed = storage.words;
expectFailure([&] { setters[i](packet, 1); });
check(storage.words == malformed, "setter modified malformed packet");
}
}
}
}
int main() {
try {
testDirect();
testIndirect();
std::puts("AGC register command tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
return 1;
}
}