127 lines
5.9 KiB
C++
127 lines
5.9 KiB
C++
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
|
#include "prx/libSceAgc/Command/include/Memory.hpp"
|
|
#include "prx/libSceAgc/Command/include/RegisterDefaults.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdio>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
extern "C" std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t op, std::uint32_t state);
|
|
extern "C" std::uint32_t* APS5_VABI sceAgcDcbDrawIndexAuto(CommandBuffer* buf, std::uint32_t indexCount, std::uint64_t modifier);
|
|
extern "C" int APS5_VABI sceAgcWaitRegMemPatchReference(std::uint32_t* cmd, std::uint64_t reference);
|
|
|
|
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& error) {
|
|
check(error.what()[0] != '\0', "empty exception message");
|
|
return;
|
|
}
|
|
throw std::runtime_error("expected an exception");
|
|
}
|
|
|
|
struct Storage {
|
|
std::array<std::uint32_t, 64> words{};
|
|
CommandBuffer buffer{words.data(), words.data() + words.size(), words.data(), words.data() + words.size(), nullptr, nullptr, 0};
|
|
};
|
|
|
|
bool APS5_VABI grow(CommandBuffer* buffer, std::uint32_t count, void* userData) {
|
|
auto& storage = *static_cast<Storage*>(userData);
|
|
check(count == 5, "incorrect callback allocation including reserved words");
|
|
buffer->bottom = storage.words.data();
|
|
buffer->top = storage.words.data() + storage.words.size();
|
|
buffer->cursor_up = buffer->bottom;
|
|
buffer->cursor_down = buffer->top;
|
|
return true;
|
|
}
|
|
|
|
void testPackets() {
|
|
Storage storage;
|
|
sceAgcDcbResetQueue(&storage.buffer, 0, 3);
|
|
sceAgcDcbDrawIndexAuto(&storage.buffer, 17, 0);
|
|
const std::array<std::uint32_t, 5> expected{0xc0001200u, 3, 0xc0012d00u, 17, 2};
|
|
check(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");
|
|
const auto before = storage.words;
|
|
expectFailure([&] { sceAgcDcbResetQueue(&storage.buffer, 0, 16); });
|
|
check(storage.words == before, "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__);
|
|
check(empty.cursor_up == destination.words.data() + 5, "guest ABI allocation callback failed");
|
|
Storage exhausted;
|
|
exhausted.buffer.cursor_down = exhausted.words.data() + 2;
|
|
expectFailure([&] { Agc::Command::WriteNop(&exhausted.buffer, 3, __func__); });
|
|
check(exhausted.buffer.cursor_up == exhausted.words.data(), "failed allocation advanced cursor");
|
|
}
|
|
|
|
void testRegisters() {
|
|
Storage storage;
|
|
const std::array<ShaderRegister, 3> registers{{{0x10, 7}, {0x11, 8}, {0x20, 9}}};
|
|
Agc::Command::WriteRegisters(&storage.buffer, 0x76u, registers.data(), registers.size(), true, __func__);
|
|
const std::array<std::uint32_t, 7> expected{0xc0027600u, 0x10, 7, 8, 0xc0017600u, 0x20, 9};
|
|
check(std::equal(expected.begin(), expected.end(), storage.words.begin()), "register run packet mismatch");
|
|
auto* packet = Agc::Command::WriteIndirectRegisters(&storage.buffer, 0x63u, registers.data(), 0x3ffeu, __func__);
|
|
Agc::Command::PatchIndirectCount(packet, 0x63u, 1, __func__);
|
|
check(packet[4] == 0x3fffu, "indirect register count mismatch");
|
|
const auto before = storage.words;
|
|
expectFailure([&] { Agc::Command::PatchIndirectCount(packet, 0x63u, 1, __func__); });
|
|
expectFailure([&] { Agc::Command::PatchIndirectAddress(packet, 0x64u, registers.data(), __func__); });
|
|
check(storage.words == before, "invalid indirect patch modified memory");
|
|
}
|
|
|
|
void testMemory() {
|
|
Storage storage;
|
|
Agc::Command::WriteDma(&storage.buffer, false, 1, 0, 0, 0x2000, 2, 0, 0x12345678, 16, 0, 1, 1, __func__);
|
|
const std::array<std::uint32_t, 7> expected{0xc0055000u, 0xc0000001u, 0x12345678, 0, 0x2000, 0, 0x80000010u};
|
|
check(std::equal(expected.begin(), expected.end(), storage.words.begin()), "DMA packet mismatch");
|
|
std::uint64_t value = 0;
|
|
auto* packet = Agc::Command::WriteWait(&storage.buffer, 1, 3, 0, 0, &value, 0x1122334455667788ull, 0xffffffffffffffffull, 32, __func__);
|
|
check(packet[0] == 0xc0027901u && packet[4] == 0xc0079300u && packet[5] == 0x13u, "wait packet header mismatch");
|
|
check(packet[8] == 0x55667788u && packet[9] == 0x11223344u && packet[12] == 2u, "wait reference or poll interval mismatch");
|
|
sceAgcWaitRegMemPatchReference(packet, 7);
|
|
check(packet[8] == 7 && packet[9] == 0x11223344u, "reference patch changed the high word");
|
|
const auto before = storage.words;
|
|
expectFailure([&] { sceAgcWaitRegMemPatchReference(packet, 0x100000000ull); });
|
|
expectFailure([&] { Agc::Command::WriteWait(&storage.buffer, 0, 3, 0, 0, &value, 0x100000000ull, 0, 32, __func__); });
|
|
check(storage.words == before, "invalid memory operation modified packet memory");
|
|
}
|
|
|
|
void testDefaults() {
|
|
for (std::uint32_t version = 0; version < 14; ++version) {
|
|
for (const bool internal : {false, true}) {
|
|
auto* first = Agc::Command::GetRegisterDefaults(version, internal, __func__);
|
|
check(first != nullptr && first == Agc::Command::GetRegisterDefaults(version, internal, __func__), "unstable register defaults pointer");
|
|
}
|
|
}
|
|
expectFailure([] { Agc::Command::GetRegisterDefaults(14, false, __func__); });
|
|
expectFailure([] { Agc::Command::GetRegisterDefaults(0xffffffffu, true, __func__); });
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
try {
|
|
testPackets();
|
|
testRegisters();
|
|
testMemory();
|
|
testDefaults();
|
|
std::puts("AGC command tests passed");
|
|
return 0;
|
|
} catch (const std::exception& error) {
|
|
std::fprintf(stderr, "%s\n", error.what());
|
|
return 1;
|
|
}
|
|
}
|