Merge pull request #158 from carman24r/main
feat(agc): implement register commands and count patching
This commit is contained in:
commit
ec0c39bf6c
|
|
@ -68,3 +68,21 @@ target_include_directories(agc_command_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
|||
target_link_libraries(agc_command_tests PRIVATE libSceAgc libc)
|
||||
configure_windows_unwind(agc_command_tests)
|
||||
configure_windows_unwind(libSceAgc)
|
||||
|
||||
add_executable(agc_register_command_tests EXCLUDE_FROM_ALL
|
||||
tests/RegisterCommands.cpp
|
||||
Command/src/Packet.cpp
|
||||
DcbState/src/Registers.cpp
|
||||
Patch/src/IndirectRegisters.cpp
|
||||
)
|
||||
target_include_directories(agc_register_command_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(agc_register_command_tests PRIVATE libc)
|
||||
configure_windows_unwind(agc_register_command_tests)
|
||||
if(BUILD_TESTING)
|
||||
set_target_properties(agc_register_command_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
|
||||
add_test(NAME agc_register_command_tests COMMAND agc_register_command_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(agc_register_command_tests PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ std::uint32_t* WriteRegisters(CommandBuffer* buffer, std::uint32_t opcode, const
|
|||
std::uint32_t* WriteIndirectRegisters(CommandBuffer* buffer, std::uint32_t opcode, const volatile ShaderRegister* registers, std::uint32_t count, const char* function);
|
||||
void PatchIndirectAddress(std::uint32_t* packet, std::uint32_t opcode, const volatile ShaderRegister* registers, const char* function);
|
||||
void PatchIndirectCount(std::uint32_t* packet, std::uint32_t opcode, std::uint32_t count, const char* function);
|
||||
void SetIndirectCount(std::uint32_t* packet, std::uint32_t opcode, std::uint32_t count, const char* function);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -159,4 +159,11 @@ void PatchIndirectCount(std::uint32_t* packet, std::uint32_t opcode, std::uint32
|
|||
packet[4] += count;
|
||||
}
|
||||
|
||||
void SetIndirectCount(std::uint32_t* packet, std::uint32_t opcode, std::uint32_t count, const char* function) {
|
||||
ValidatePacket(packet, opcode, 5, function);
|
||||
Require(packet[3] == 0x80000000u && packet[4] <= 0x3fffu, function, "invalid indirect register packet");
|
||||
CheckBits(count, 0x3fffu, function);
|
||||
packet[4] = count;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,8 @@ std::uint32_t* APS5_VABI sceAgcDcbSetCxRegistersIndirect(CommandBuffer* buf, con
|
|||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcDcbSetCxRegistersIndirectGetSize(std::uint32_t numRegs) {
|
||||
(void)numRegs;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
Agc::Command::CheckBits(numRegs, 0x3fffu, __func__);
|
||||
return 20;
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbSetShRegisterDirect(CommandBuffer* buf, ShaderRegister reg) {
|
||||
|
|
@ -56,9 +55,8 @@ std::uint32_t* APS5_VABI sceAgcDcbSetUcRegistersIndirect(CommandBuffer* buf, con
|
|||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcDcbSetUcRegistersIndirectGetSize(std::uint32_t numRegs) {
|
||||
(void)numRegs;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
Agc::Command::CheckBits(numRegs, 0x3fffu, __func__);
|
||||
return 20;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,9 @@ int APS5_VABI sceAgcSetCxRegIndirectPatchSetAddress(std::uint32_t* cmd, const vo
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcSetCxRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_t num_regs) {
|
||||
(void)cmd;
|
||||
(void)num_regs;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceAgcSetCxRegIndirectPatchSetNumRegisters(std::uint32_t* cmd, std::uint32_t numRegs) {
|
||||
Agc::Command::SetIndirectCount(cmd, 0x9fu, numRegs, __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcSetShRegIndirectPatchAddRegisters(std::uint32_t* cmd, std::uint32_t numRegs) {
|
||||
|
|
@ -36,11 +34,9 @@ int APS5_VABI sceAgcSetShRegIndirectPatchSetAddress(std::uint32_t* cmd, const vo
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcSetShRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_t num_regs) {
|
||||
(void)cmd;
|
||||
(void)num_regs;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceAgcSetShRegIndirectPatchSetNumRegisters(std::uint32_t* cmd, std::uint32_t numRegs) {
|
||||
Agc::Command::SetIndirectCount(cmd, 0x63u, numRegs, __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcSetUcRegIndirectPatchAddRegisters(std::uint32_t* cmd, std::uint32_t numRegs) {
|
||||
|
|
@ -53,11 +49,9 @@ int APS5_VABI sceAgcSetUcRegIndirectPatchSetAddress(std::uint32_t* cmd, const vo
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcSetUcRegIndirectPatchSetNumRegisters(uint32_t* cmd, uint32_t num_regs) {
|
||||
(void)cmd;
|
||||
(void)num_regs;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI sceAgcSetUcRegIndirectPatchSetNumRegisters(std::uint32_t* cmd, std::uint32_t numRegs) {
|
||||
Agc::Command::SetIndirectCount(cmd, 0x64u, numRegs, __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
#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, ®, 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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue