From 8e5a0bfb19fde66d261188002cd90edb402110c7 Mon Sep 17 00:00:00 2001 From: MarcelMediaDev Date: Thu, 23 Jul 2026 10:32:04 +0100 Subject: [PATCH] fix(agc): implement sceAgcDcbSetUcRegisterDirect (#558) Emit the 3-dword SET_UCONFIG_REG packet from the packed {offset,value} in RSI. Unresolved calls left GPU config registers unset during RenderThread/Main bring-up. Co-authored-by: Cursor --- src/SharpEmu.Libs/Agc/AgcExports.cs | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/SharpEmu.Libs/Agc/AgcExports.cs b/src/SharpEmu.Libs/Agc/AgcExports.cs index de9a59e3..56e821ed 100644 --- a/src/SharpEmu.Libs/Agc/AgcExports.cs +++ b/src/SharpEmu.Libs/Agc/AgcExports.cs @@ -1431,6 +1431,26 @@ public static partial class AgcExports public static int DcbSetUcRegistersIndirect(CpuContext ctx) => DcbSetRegistersIndirect(ctx, RUcRegsIndirect, "uc"); + [SysAbiExport( + Nid = "w4-d0n60hdo", + ExportName = "sceAgcDcbSetUcRegisterDirect", + Target = Generation.Gen5, + LibraryName = "libSceAgc")] + public static int DcbSetUcRegisterDirect(CpuContext ctx) => + DcbSetRegisterDirect(ctx, ItSetUconfigReg, "uc"); + + [SysAbiExport( + Nid = "aP1Ki9G3++4", + ExportName = "sceAgcDcbSetUcRegisterDirectGetSize", + Target = Generation.Gen5, + LibraryName = "libSceAgc")] + public static int DcbSetUcRegisterDirectGetSize(CpuContext ctx) + { + // SET_UCONFIG_REG header + offset + value. + ctx[CpuRegister.Rax] = 3u * sizeof(uint); + return (int)ctx[CpuRegister.Rax]; + } + [SysAbiExport( Nid = "GIIW2J37e70", ExportName = "sceAgcDcbSetIndexSize", @@ -10814,6 +10834,33 @@ public static partial class AgcExports return ReturnPointer(ctx, commandAddress); } + private static int DcbSetRegisterDirect(CpuContext ctx, uint op, string registerSpace) + { + var commandBufferAddress = ctx[CpuRegister.Rdi]; + // Uc/Cx/Sh register is passed by value as {u32 offset, u32 value} in RSI. + var packedRegister = ctx[CpuRegister.Rsi]; + var registerOffset = (uint)(packedRegister & 0xFFFF_FFFFUL); + var registerValue = (uint)(packedRegister >> 32); + if (commandBufferAddress == 0) + { + return ReturnPointer(ctx, 0); + } + + const uint packetDwords = 3; + if (!TryAllocateCommandDwords(ctx, commandBufferAddress, packetDwords, out var commandAddress) || + !TryWriteUInt32(ctx, commandAddress, Pm4(packetDwords, op, 0)) || + !TryWriteUInt32(ctx, commandAddress + 4, registerOffset & 0xFFFFu) || + !TryWriteUInt32(ctx, commandAddress + 8, registerValue)) + { + return ReturnPointer(ctx, 0); + } + + TraceAgc( + $"agc.dcb_set_{registerSpace}_direct buf=0x{commandBufferAddress:X16} " + + $"cmd=0x{commandAddress:X16} offset=0x{registerOffset:X4} value=0x{registerValue:X8}"); + return ReturnPointer(ctx, commandAddress); + } + private static bool TryAllocateCommandDwords(CpuContext ctx, ulong commandBufferAddress, uint sizeDwords, out ulong commandAddress) { commandAddress = 0;