feat(agc): packet sizes, register writes, GS/HS fusion, jump patches
- packet sizes for the existing builders and the tessellation ring config - direct register writes and end-of-pipe action data, type and cache patches - GS/HS shader halves fused by merging their registers, after Kyty - jump target patches; reserved jumps are allowed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c206f7049a
commit
fd4700b909
|
|
@ -17,15 +17,11 @@ std::uint32_t* APS5_VABI sceAgcCbSetShRegisterRangeDirect(CommandBuffer* buf, st
|
|||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcCbSetShRegisterRangeDirectGetSize(uint32_t num_values) {
|
||||
(void)num_values;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return (num_values + 2u) * 4u; // header + offset + values
|
||||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcCbSetUcRegisterRangeDirectGetSize(std::uint32_t numValues) {
|
||||
(void)numValues;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return (numValues + 2u) * 4u; // header + offset + values
|
||||
}
|
||||
|
||||
std::uint32_t* APS5_VABI sceAgcCbSetShRegistersDirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs) {
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ std::uint32_t* APS5_VABI sceAgcCbNop_nid_postfix(CommandBuffer* buf, std::uint32
|
|||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcCbNopGetSize(uint32_t size_in_dwords) {
|
||||
(void)size_in_dwords;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return size_in_dwords * 4u; // a NOP packet is exactly size_in_dwords dwords including its header
|
||||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcCbQueueEndOfPipeActionGetSize() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ std::uint32_t* WriteJump(CommandBuffer* buffer, std::uint8_t chain, std::uint8_t
|
|||
CheckBits(chain, 1, function);
|
||||
CheckBits(cachePolicy, 3, function);
|
||||
CheckBits(sizeInDwords, 0xfffffu, function);
|
||||
CheckAddress(guestAddress, 4, function);
|
||||
// Titles reserve a jump with a null target and write the address into the packet later.
|
||||
Require((guestAddress & 3u) == 0, function, "misaligned jump target");
|
||||
// INDIRECT_BUFFER: address, then size in bits 19:0, chain (no return) in bit 20, cache policy in 29:28.
|
||||
return Emit(buffer, 0x3fu, {static_cast<std::uint32_t>(guestAddress) & ~3u, static_cast<std::uint32_t>(guestAddress >> 32u), 0x0f200000u | (static_cast<std::uint32_t>(cachePolicy) << 28u) | (static_cast<std::uint32_t>(chain) << 20u) | sizeInDwords}, function);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,7 @@ std::uint32_t* APS5_VABI sceAgcDcbDrawIndexOffset(CommandBuffer* buf, std::uint3
|
|||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbDrawIndexOffsetGetSize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return 20; // DRAW_INDEX_OFFSET_2: header + 4 dwords
|
||||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbDrawIndexIndirect(CommandBuffer* buf, uint32_t data_offset_in_bytes, uint64_t modifier) {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ std::uint32_t* APS5_VABI sceAgcDcbSetIndexBuffer(CommandBuffer* buf, std::uint64
|
|||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcDcbSetIndexBufferGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return 12; // INDEX_BASE: header + 2 dwords
|
||||
}
|
||||
|
||||
std::uint32_t* APS5_VABI sceAgcDcbSetIndexCount(CommandBuffer* buf, std::uint32_t indexCount) {
|
||||
|
|
@ -33,8 +32,7 @@ std::uint32_t* APS5_VABI sceAgcDcbSetIndexSize(CommandBuffer* buf, std::uint8_t
|
|||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcDcbSetIndexSizeGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return 12; // SET_UCONFIG_REG_INDEX: header + 2 dwords
|
||||
}
|
||||
|
||||
std::uint32_t* APS5_VABI sceAgcDcbSetIndexIndirectArgs(CommandBuffer* buf, std::uint32_t dataOffsetInBytes) {
|
||||
|
|
|
|||
|
|
@ -42,9 +42,7 @@ std::uint32_t* APS5_VABI sceAgcDcbEventWrite(CommandBuffer* buf, std::uint8_t ev
|
|||
}
|
||||
|
||||
std::uint64_t APS5_VABI sceAgcDcbEventWriteGetSize(std::uint8_t eventType) {
|
||||
(void)eventType;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return (eventType & 0xfeu) == 0x38u ? 16 : 8; // EVENT_WRITE, plus the address for the 0x38/0x39 events
|
||||
}
|
||||
|
||||
std::uint32_t* APS5_VABI sceAgcDcbStallCommandBufferParser(CommandBuffer* buf) {
|
||||
|
|
|
|||
|
|
@ -9,15 +9,11 @@
|
|||
extern "C" {
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbSetCxRegisterDirect(CommandBuffer* buf, ShaderRegister reg) {
|
||||
(void)buf;
|
||||
(void)reg;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
return Agc::Command::WriteRegisterRange(buf, 0x69u, reg.offset, ®.value, 1u, __func__);
|
||||
}
|
||||
|
||||
uint32_t APS5_VABI sceAgcDcbSetCxRegisterDirectGetSize(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return 12;
|
||||
}
|
||||
|
||||
std::uint32_t* APS5_VABI sceAgcDcbSetCxRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs) {
|
||||
|
|
@ -31,15 +27,11 @@ std::uint32_t APS5_VABI sceAgcDcbSetCxRegistersIndirectGetSize(std::uint32_t num
|
|||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbSetShRegisterDirect(CommandBuffer* buf, ShaderRegister reg) {
|
||||
(void)buf;
|
||||
(void)reg;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
return Agc::Command::WriteRegisterRange(buf, 0x76u, reg.offset, ®.value, 1u, __func__);
|
||||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcDcbSetShRegisterDirectGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return 12;
|
||||
}
|
||||
|
||||
std::uint32_t* APS5_VABI sceAgcDcbSetShRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs) {
|
||||
|
|
@ -52,15 +44,11 @@ std::uint32_t APS5_VABI sceAgcDcbSetShRegistersIndirectGetSize(std::uint32_t num
|
|||
}
|
||||
|
||||
uint32_t* APS5_VABI sceAgcDcbSetUcRegisterDirect(CommandBuffer* buf, ShaderRegister reg) {
|
||||
(void)buf;
|
||||
(void)reg;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
return Agc::Command::WriteRegisterRange(buf, 0x79u, reg.offset, ®.value, 1u, __func__);
|
||||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcDcbSetUcRegisterDirectGetSize() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return 12;
|
||||
}
|
||||
|
||||
std::uint32_t* APS5_VABI sceAgcDcbSetUcRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, std::uint32_t numRegs) {
|
||||
|
|
|
|||
|
|
@ -1,33 +1,116 @@
|
|||
#include "prx/libSceAgc/Misc/include/ShaderFusion.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libSceAgc/Shader/include/ShaderConstants.hpp"
|
||||
|
||||
struct SizeAlign {
|
||||
uint64_t m_size;
|
||||
size_t m_align;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace ShaderRegs;
|
||||
|
||||
constexpr int GRAPHICS5_ERROR_INVALID_SHADER_HALVES = static_cast<int>(0x8a6c0008u);
|
||||
constexpr std::uint32_t SPI_SHADER_PGM_CHKSUM_GS = 0x080u;
|
||||
constexpr std::uint32_t SPI_SHADER_PGM_RSRC1_GS = 0x08Au;
|
||||
constexpr std::uint32_t SPI_SHADER_PGM_RSRC2_GS = 0x08Bu;
|
||||
constexpr std::uint32_t SPI_SHADER_PGM_CHKSUM_HS = 0x100u;
|
||||
constexpr std::uint32_t SPI_SHADER_PGM_RSRC1_HS = 0x10Au;
|
||||
constexpr std::uint32_t SPI_SHADER_PGM_RSRC2_HS = 0x10Bu;
|
||||
|
||||
bool ValidHalves(const Shader* front, const Shader* back) {
|
||||
const auto frontType = static_cast<ShaderBinaryType>(front->type);
|
||||
const auto backType = static_cast<ShaderBinaryType>(back->type);
|
||||
return (frontType == ShaderBinaryType::GsFront && backType == ShaderBinaryType::GsBack) ||
|
||||
(frontType == ShaderBinaryType::HsFront && backType == ShaderBinaryType::HsBack);
|
||||
}
|
||||
|
||||
ShaderRegister& FindRegister(ShaderRegister* regs, std::uint32_t count, std::uint32_t offset, std::uint32_t occurrence = 0) {
|
||||
for (std::uint32_t i = 0; regs != nullptr && i < count; ++i) {
|
||||
if (regs[i].offset == offset && occurrence-- == 0) return regs[i];
|
||||
}
|
||||
throw std::runtime_error("sceAgcUnknownFuseShaderHalves: shader half lacks register " + std::to_string(offset));
|
||||
}
|
||||
|
||||
void MergeMax(ShaderRegister& dst, const ShaderRegister& src, std::uint32_t shift, std::uint32_t mask) {
|
||||
const auto field = std::max((dst.value >> shift) & mask, (src.value >> shift) & mask);
|
||||
dst.value = (dst.value & ~(mask << shift)) | (field << shift);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
APS5_EXPORT("fd5Bp5tGTgo", sceAgcUnknownFuseShaderHalves);
|
||||
int APS5_VABI sceAgcUnknownFuseShaderHalves(Shader* fused_result, const Shader* front, const Shader* back, void* scratch_mem) {
|
||||
(void)fused_result;
|
||||
(void)front;
|
||||
(void)back;
|
||||
(void)scratch_mem;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
if (fused_result == nullptr || front == nullptr || back == nullptr) APS5_INVALID_ARG_EX;
|
||||
if (!ValidHalves(front, back)) return GRAPHICS5_ERROR_INVALID_SHADER_HALVES;
|
||||
const bool isGs = static_cast<ShaderBinaryType>(front->type) == ShaderBinaryType::GsFront;
|
||||
const std::uint32_t stageBit = isGs ? (1u << 22u) : (1u << 21u);
|
||||
if (((front->specials->vgt_shader_stages_en.value ^ back->specials->vgt_shader_stages_en.value) & stageBit) != 0) {
|
||||
return GRAPHICS5_ERROR_INVALID_SHADER_HALVES;
|
||||
}
|
||||
*fused_result = *back;
|
||||
fused_result->type = static_cast<std::uint8_t>(isGs ? ShaderBinaryType::Gs : ShaderBinaryType::Hs);
|
||||
if (scratch_mem != nullptr) {
|
||||
auto* regs = static_cast<ShaderRegister*>(scratch_mem);
|
||||
std::memcpy(regs, back->sh_registers, std::size_t{back->num_sh_registers} * sizeof(ShaderRegister));
|
||||
fused_result->sh_registers = regs;
|
||||
}
|
||||
ShaderRegister* fused = fused_result->sh_registers;
|
||||
const std::uint32_t fusedCount = fused_result->num_sh_registers;
|
||||
const std::uint32_t frontCount = front->num_sh_registers;
|
||||
const std::uint32_t checksum = isGs ? SPI_SHADER_PGM_CHKSUM_GS : SPI_SHADER_PGM_CHKSUM_HS;
|
||||
for (std::uint32_t occurrence = 0; occurrence < 2; ++occurrence) {
|
||||
FindRegister(fused, fusedCount, checksum, occurrence).value = FindRegister(front->sh_registers, frontCount, checksum, occurrence).value;
|
||||
}
|
||||
const auto& frontRsrc1 = FindRegister(front->sh_registers, frontCount, isGs ? SPI_SHADER_PGM_RSRC1_GS : SPI_SHADER_PGM_RSRC1_HS);
|
||||
const auto& frontRsrc2 = FindRegister(front->sh_registers, frontCount, isGs ? SPI_SHADER_PGM_RSRC2_GS : SPI_SHADER_PGM_RSRC2_HS);
|
||||
auto& fusedRsrc1 = FindRegister(fused, fusedCount, isGs ? SPI_SHADER_PGM_RSRC1_GS : SPI_SHADER_PGM_RSRC1_HS);
|
||||
auto& fusedRsrc2 = FindRegister(fused, fusedCount, isGs ? SPI_SHADER_PGM_RSRC2_GS : SPI_SHADER_PGM_RSRC2_HS);
|
||||
// Both halves run in one wave, so VGPRs are the larger of the two and shared VGPRs are reallocated (after Kyty).
|
||||
const std::uint32_t frontVgprs = ((frontRsrc1.value & 0x3fu) + 1u) * 4u;
|
||||
const std::uint32_t backVgprs = ((fusedRsrc1.value & 0x3fu) + 1u) * 4u;
|
||||
const std::uint32_t frontTotal = frontVgprs + (frontRsrc2.value >> 28u) * 8u;
|
||||
const std::uint32_t backTotal = backVgprs + (fusedRsrc2.value >> 28u) * 8u;
|
||||
const std::uint32_t maxTotal = std::max(frontTotal, backTotal);
|
||||
const std::uint32_t shared = std::max(frontVgprs, backVgprs) >= maxTotal ? 0u : (maxTotal - std::min(frontTotal, backTotal) + 7u) / 64u;
|
||||
fusedRsrc2.value = (fusedRsrc2.value & 0x0fffffffu) | ((shared & 0xfu) << 28u);
|
||||
MergeMax(fusedRsrc1, frontRsrc1, 0, 0x3fu);
|
||||
if (isGs) {
|
||||
MergeMax(fusedRsrc1, frontRsrc1, 29, 0x3u);
|
||||
MergeMax(fusedRsrc2, frontRsrc2, 16, 0x3u);
|
||||
fusedRsrc2.value = (fusedRsrc2.value & 0xfffbffffu) | (frontRsrc2.value & 0x00040000u);
|
||||
} else {
|
||||
MergeMax(fusedRsrc1, frontRsrc1, 28, 0x3u);
|
||||
}
|
||||
fusedRsrc2.value = (fusedRsrc2.value & 0xf7ffffc1u) | (frontRsrc2.value & 0x0800003eu);
|
||||
// The fused program starts at the front half; its address goes in the ES/LS program registers.
|
||||
const std::uint32_t lo = isGs ? SPI_SHADER_PGM_LO_ES : SPI_SHADER_PGM_LO_LS;
|
||||
auto& loRegister = FindRegister(fused, fusedCount, lo);
|
||||
auto& hiRegister = FindRegister(fused, fusedCount, lo + 1u);
|
||||
const auto address = reinterpret_cast<std::uint64_t>(front->code);
|
||||
loRegister.value = static_cast<std::uint32_t>(address >> 8u);
|
||||
hiRegister.value = (hiRegister.value & 0xffffff00u) | static_cast<std::uint32_t>((address >> 40u) & 0xffu);
|
||||
fused_result->user_data = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("dolOmWH+huQ", sceAgcUnknownGetFusedShaderSize);
|
||||
int APS5_VABI sceAgcUnknownGetFusedShaderSize(SizeAlign* dst, const Shader* front, const Shader* back) {
|
||||
(void)dst;
|
||||
(void)front;
|
||||
(void)back;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
if (dst == nullptr || front == nullptr || back == nullptr) APS5_INVALID_ARG_EX;
|
||||
if (!ValidHalves(front, back)) return GRAPHICS5_ERROR_INVALID_SHADER_HALVES;
|
||||
dst->m_size = std::uint64_t{back->num_sh_registers} * sizeof(ShaderRegister);
|
||||
dst->m_align = 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,14 +7,35 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
// INDIRECT_BUFFER as sceAgcDcbJump writes it: [1..2] target, [3] size in 19:0, chain in 20, cache policy in 29:28.
|
||||
void PatchJump(std::uint32_t* cmd, const volatile std::uint32_t* target, std::uint32_t sizeInDwords, const char* function) {
|
||||
Agc::Command::ValidatePacket(cmd, 0x3fu, 4, function);
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(target);
|
||||
Agc::Command::CheckAddress(address, 4, function);
|
||||
Agc::Command::CheckBits(sizeInDwords, 0xfffffu, function);
|
||||
cmd[1] = static_cast<std::uint32_t>(address);
|
||||
cmd[2] = static_cast<std::uint32_t>(address >> 32u);
|
||||
cmd[3] = (cmd[3] & ~0xfffffu) | sizeInDwords;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceAgcJumpPatchSetTarget(uint32_t* cmd, const volatile uint32_t* target, uint32_t size_in_dwords) {
|
||||
(void)cmd;
|
||||
(void)target;
|
||||
(void)size_in_dwords;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
PatchJump(cmd, target, size_in_dwords, __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Named after Kyty's reading: a jump patch that also sets the cache policy.
|
||||
APS5_EXPORT("Ikfdt-rIqCE", sceAgcUnknown_Ikfdt_MrIqCE);
|
||||
int APS5_VABI sceAgcUnknown_Ikfdt_MrIqCE(uint32_t* cmd, uint64_t cache_policy, const volatile uint32_t* target, uint32_t size_in_dwords) {
|
||||
Agc::Command::CheckBits(cache_policy, 3u, __func__);
|
||||
PatchJump(cmd, target, size_in_dwords, __func__);
|
||||
cmd[3] = (cmd[3] & ~(3u << 28u)) | (static_cast<std::uint32_t>(cache_policy) << 28u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,25 +31,37 @@ int APS5_VABI sceAgcQueueEndOfPipeActionPatchAddress(std::uint32_t* cmd, const v
|
|||
}
|
||||
|
||||
int APS5_VABI sceAgcQueueEndOfPipeActionPatchData(uint32_t* cmd, uint32_t context_id, uint32_t data_sel, uint64_t data) {
|
||||
(void)cmd;
|
||||
(void)context_id;
|
||||
(void)data_sel;
|
||||
(void)data;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
// RELEASE_MEM as built by sceAgcCbReleaseMem: [2] dst | interrupt << 24 | data select << 29, [5..6] data, [7] interrupt context.
|
||||
Agc::Command::ValidatePacket(cmd, 0x49u, 8, __func__);
|
||||
Agc::Command::Require(data_sel <= 3, __func__, "invalid release data selector");
|
||||
Agc::Command::CheckBits(context_id, 0x7ffffffu, __func__);
|
||||
if (data_sel == 1) {
|
||||
Agc::Command::CheckBits(data, 0xffffffffu, __func__);
|
||||
}
|
||||
Agc::Command::Require(((cmd[2] >> 24u) & 7u) != 4u || data_sel == 0, __func__, "interrupt-only release cannot write data");
|
||||
cmd[2] = (cmd[2] & 0x1fffffffu) | (data_sel << 29u);
|
||||
cmd[5] = static_cast<std::uint32_t>(data);
|
||||
cmd[6] = static_cast<std::uint32_t>(data >> 32u);
|
||||
cmd[7] = context_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcQueueEndOfPipeActionPatchGcrCntl(std::uint32_t* cmd, std::uint16_t gcrControl) {
|
||||
(void)cmd;
|
||||
(void)gcrControl;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
Agc::Command::ValidatePacket(cmd, 0x49u, 8, __func__);
|
||||
Agc::Command::CheckBits(gcrControl, 0xfffu, __func__);
|
||||
std::uint32_t control = gcrControl;
|
||||
if ((control & 0x300u) == 0x100u) {
|
||||
control |= 0x200u;
|
||||
}
|
||||
cmd[1] = (cmd[1] & ~(0xfffu << 12u)) | (control << 12u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcQueueEndOfPipeActionPatchType(std::uint32_t* cmd, std::uint8_t action) {
|
||||
(void)cmd;
|
||||
(void)action;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
Agc::Command::ValidatePacket(cmd, 0x49u, 8, __func__);
|
||||
Agc::Command::CheckBits(action, 0x3fu, __func__);
|
||||
const std::uint32_t eventIndex = action >= 0x2fu ? 6u : 5u;
|
||||
cmd[1] = (cmd[1] & ~0xf3fu) | action | (eventIndex << 8u);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,25 +2,33 @@
|
|||
|
||||
#include "prx/libSceAgc/Command/include/Memory.hpp"
|
||||
#include "prx/libSceAgc/Command/include/Packet.hpp"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
namespace {
|
||||
|
||||
int APS5_VABI sceAgcRewindPatchSetRewindState(uint32_t* cmd, uint8_t state) {
|
||||
(void)cmd;
|
||||
(void)state;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcAsyncRewindPatchSetRewindState(std::uint32_t* cmd, std::uint8_t state) {
|
||||
(void)cmd;
|
||||
(void)state;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
// The valid bit (31) releases a queue stalled on the REWIND; the release store publishes the commands
|
||||
// the title wrote behind it before setting the bit.
|
||||
int SetRewindState(std::uint32_t* cmd, std::uint8_t state, const char* function) {
|
||||
Agc::Command::ValidatePacket(cmd, 0x59u, 2, function);
|
||||
Agc::Command::CheckBits(state, 1, function);
|
||||
std::atomic_ref<std::uint32_t>(cmd[1]).store(static_cast<std::uint32_t>(state) << 31u, std::memory_order_release);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceAgcRewindPatchSetRewindState(uint32_t* cmd, uint8_t state) {
|
||||
return SetRewindState(cmd, state, __func__);
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcAsyncRewindPatchSetRewindState(std::uint32_t* cmd, std::uint8_t state) {
|
||||
return SetRewindState(cmd, state, __func__);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,12 +121,6 @@ int APS5_VABI sceAgcUnknown_ICkECTBxrMw() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("Ikfdt-rIqCE", sceAgcUnknown_Ikfdt_MrIqCE);
|
||||
int APS5_VABI sceAgcUnknown_Ikfdt_MrIqCE() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("RTpj-tIlvZc", sceAgcUnknown_RTpj_MtIlvZc);
|
||||
int APS5_VABI sceAgcUnknown_RTpj_MtIlvZc() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
|
|
|
|||
Loading…
Reference in New Issue