sharpemu/src/SharpEmu.Libs/Agc/AgcExports.PacketPatching.cs

691 lines
26 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using SharpEmu.Libs.Kernel;
namespace SharpEmu.Libs.Agc;
public static partial class AgcExports
{
// This partial patches previously constructed AGC command packets.
// Uncatalogued name; the NID and import library identify this AGC export.
// Observed ABI: packet, cache policy, next-buffer address, length in dwords.
// The caller patches the previous DcbJump after allocating its continuation.
#pragma warning disable SHEM006
[SysAbiExport(Nid = "Ikfdt-rIqCE", ExportName = "sceAgcUnknownIkfdt",
Target = Generation.Gen5, LibraryName = "libSceAgc")]
public static int UnknownIkfdt(CpuContext ctx)
{
var packet = ctx[CpuRegister.Rdi];
var cachePolicy = (uint)ctx[CpuRegister.Rsi];
var target = ctx[CpuRegister.Rdx];
var dwords = (uint)ctx[CpuRegister.Rcx];
if (cachePolicy > 3 || dwords > 0xFFFFFu ||
!TryGetPacketIdentity(ctx, packet, out var opcode, out _) ||
opcode != ItIndirectBuffer ||
!TryReadUInt32(ctx, packet, out var header) ||
((header >> 16) & 0x3FFFu) != 2 ||
!TryReadUInt32(ctx, packet + 12, out var control))
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
// Preserve call/chain mode and every other control bit.
control = (control & ~0x300F_FFFFu) | (cachePolicy << 28) | dwords;
return TryWriteUInt32(ctx, packet + 4, (uint)target) &&
TryWriteUInt32(ctx, packet + 8, (uint)(target >> 32) & 0xFFFFu) &&
TryWriteUInt32(ctx, packet + 12, control)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
#pragma warning restore SHEM006
[SysAbiExport(
Nid = "vcmNN+AAXnY",
ExportName = "sceAgcSetCxRegIndirectPatchSetAddress",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int SetCxRegIndirectPatchSetAddress(CpuContext ctx) =>
SetIndirectPatchAddress(ctx, "cx");
[SysAbiExport(
Nid = "whb1RL7K4Ss",
ExportName = "sceAgcSetCxRegIndirectPatchSetNumRegisters",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int SetCxRegIndirectPatchSetNumRegisters(CpuContext ctx) =>
SetIndirectPatchRegisterCount(ctx, "cx");
[SysAbiExport(
Nid = "Qrj4c+61z4A",
ExportName = "sceAgcSetShRegIndirectPatchSetAddress",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int SetShRegIndirectPatchSetAddress(CpuContext ctx) =>
SetIndirectPatchAddress(ctx, "sh");
[SysAbiExport(
Nid = "6lNcCp+fxi4",
ExportName = "sceAgcSetUcRegIndirectPatchSetAddress",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int SetUcRegIndirectPatchSetAddress(CpuContext ctx) =>
SetIndirectPatchAddress(ctx, "uc");
[SysAbiExport(
Nid = "d-6uF9sZDIU",
ExportName = "sceAgcSetCxRegIndirectPatchAddRegisters",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int SetCxRegIndirectPatchAddRegisters(CpuContext ctx) =>
AddIndirectPatchRegisters(ctx, "cx");
[SysAbiExport(
Nid = "z2duB-hHQSM",
ExportName = "sceAgcSetShRegIndirectPatchAddRegisters",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int SetShRegIndirectPatchAddRegisters(CpuContext ctx) =>
AddIndirectPatchRegisters(ctx, "sh");
[SysAbiExport(
Nid = "vRoArM9zaIk",
ExportName = "sceAgcSetUcRegIndirectPatchAddRegisters",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int SetUcRegIndirectPatchAddRegisters(CpuContext ctx) =>
AddIndirectPatchRegisters(ctx, "uc");
[SysAbiExport(
Nid = "IxYiarKlXxM",
ExportName = "sceAgcDmaDataPatchSetDstAddressOrOffset",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DmaDataPatchSetDstAddressOrOffset(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
var destinationAddress = ctx[CpuRegister.Rsi];
if (!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register) ||
op != ItNop ||
register != RDmaData ||
!TryReadUInt32(ctx, commandAddress, out var header))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var packetLength = ((header >> 16) & 0x3FFFu) + 2;
var destinationOffset = packetLength == 7 ? 4UL : 16UL;
return ctx.TryWriteUInt64(commandAddress + destinationOffset, destinationAddress)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
// SRC counterpart of sceAgcDmaDataPatchSetDstAddressOrOffset. Without this,
// the source stays 0 and the DMA copy — often a label write — never runs.
[SysAbiExport(
Nid = "cdDRpqcFGbU",
ExportName = "sceAgcDmaDataPatchSetSrcAddressOrOffsetOrImmediate",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int DmaDataPatchSetSrcAddressOrOffsetOrImmediate(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
var sourceValue = ctx[CpuRegister.Rsi];
if (!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register) ||
op != ItNop ||
register != RDmaData ||
!TryReadUInt32(ctx, commandAddress, out var header))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var packetLength = ((header >> 16) & 0x3FFFu) + 2;
var sourceOffset = packetLength == 7 ? 12UL : 24UL;
return ctx.TryWriteUInt64(commandAddress + sourceOffset, sourceValue)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "eAy8eGNsCuU",
ExportName = "sceAgcWriteDataPatchSetCachePolicy",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int WriteDataPatchSetCachePolicy(CpuContext ctx) =>
PatchWriteDataControlByte(ctx, byteIndex: 1);
[SysAbiExport(
Nid = "tmy-+rBpspY",
ExportName = "sceAgcWriteDataPatchSetDst",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int WriteDataPatchSetDst(CpuContext ctx) =>
PatchWriteDataControlByte(ctx, byteIndex: 0);
[SysAbiExport(
Nid = "fPSCdQxgpSw",
ExportName = "sceAgcWriteDataPatchSetAddressOrOffset",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int WriteDataPatchSetAddressOrOffset(CpuContext ctx)
{
// SDK revisions disagree on whether the packet or destination is the
// first argument. Astro passes (destination, packet), while older
// captures use (packet, destination), so identify the packet by its
// header instead of hard-coding one ordering.
var first = ctx[CpuRegister.Rdi];
var second = ctx[CpuRegister.Rsi];
ulong commandAddress;
ulong destinationAddress;
if (TryGetPacketIdentity(ctx, first, out var firstOp, out var firstRegister) &&
firstOp == ItNop && firstRegister == RWriteData)
{
commandAddress = first;
destinationAddress = second;
}
else if (TryGetPacketIdentity(ctx, second, out var secondOp, out var secondRegister) &&
secondOp == ItNop && secondRegister == RWriteData)
{
commandAddress = second;
destinationAddress = first;
}
else
{
// Astro's SDK 9 ABI passes (address-or-offset, pointer-to-field)
// rather than the whole packet. The field is already the packet's
// 64-bit address payload, so patch it directly.
if (second == 0 || !ctx.TryWriteUInt64(second, first))
{
return SetReturn(ctx, second == 0
? OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT
: OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
TraceAgc(
$"agc.patch_write_data_field field=0x{second:X16} value=0x{first:X16}");
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
}
TraceAgc(
$"agc.patch_write_data_addr cmd=0x{commandAddress:X16} dst=0x{destinationAddress:X16}");
return ctx.TryWriteUInt64(commandAddress + 8, destinationAddress)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "3KDcnM3lrcU",
ExportName = "sceAgcWaitRegMemPatchAddress",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int WaitRegMemPatchAddress(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
var address = ctx[CpuRegister.Rsi];
if (!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var fieldOffset = op == ItWaitRegMem
? 8UL
: op == ItNop && register is RWaitMem32 or RWaitMem64
? 4UL
: 0;
if (fieldOffset == 0)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var wrote = op == ItNop && register is RWaitMem32 or RWaitMem64
? TryWriteUInt32(
ctx,
commandAddress + fieldOffset,
(uint)address & (register == RWaitMem32 ? ~0x3u : ~0x7u)) &&
TryWriteUInt32(ctx, commandAddress + fieldOffset + 4, (uint)(address >> 32) & 0x3FFFFu)
: ctx.TryWriteUInt64(commandAddress + fieldOffset, address);
return wrote
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "n485EBnIWmk",
ExportName = "sceAgcWaitRegMemPatchCompareFunction",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int WaitRegMemPatchCompareFunction(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
var compareFunction = (uint)ctx[CpuRegister.Rsi];
if (compareFunction > 7 ||
!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var fieldOffset = op == ItWaitRegMem
? 4UL
: op == ItNop && register == RWaitMem32
? 20UL
: op == ItNop && register == RWaitMem64
? 28UL
: 0;
return fieldOffset != 0 &&
TryPatchUInt32Bits(ctx, commandAddress + fieldOffset, 0x7u, compareFunction)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, fieldOffset == 0
? OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT
: OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "7nOoijNPvEU",
ExportName = "sceAgcWaitRegMemPatchReference",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int WaitRegMemPatchReference(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
var reference = ctx[CpuRegister.Rsi];
if (!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var wrote = op == ItWaitRegMem
? TryWriteUInt32(ctx, commandAddress + 16, (uint)reference)
: op == ItNop && register == RWaitMem32
? TryWriteUInt32(ctx, commandAddress + 16, (uint)reference)
: op == ItNop && register == RWaitMem64 &&
ctx.TryWriteUInt64(commandAddress + 20, reference);
return wrote
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, op == ItWaitRegMem ||
(op == ItNop && register is RWaitMem32 or RWaitMem64)
? OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT
: OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
[SysAbiExport(
Nid = "hXAnLgDHCoI",
ExportName = "sceAgcWaitRegMemPatchMask",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int WaitRegMemPatchMask(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
var mask = ctx[CpuRegister.Rsi];
if (!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var wrote = op == ItWaitRegMem
? TryWriteUInt32(ctx, commandAddress + 20, (uint)mask)
: op == ItNop && register == RWaitMem32
? TryWriteUInt32(ctx, commandAddress + 12, (uint)mask)
: op == ItNop && register == RWaitMem64 &&
ctx.TryWriteUInt64(commandAddress + 12, mask);
return wrote
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, op == ItWaitRegMem ||
(op == ItNop && register is RWaitMem32 or RWaitMem64)
? OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT
: OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
// PatchAddress/PatchData touch UInt64 fields at +12/+20 of an RReleaseMem
// packet, so the packet is at least 7 dwords; use the 8-dword RELEASE_MEM
// family size already used elsewhere in this file.
[SysAbiExport(
Nid = "hL7C0IRpWZI",
ExportName = "sceAgcCbQueueEndOfPipeActionGetSize",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int CbQueueEndOfPipeActionGetSize(CpuContext ctx)
{
ctx[CpuRegister.Rax] = 8u * sizeof(uint);
return (int)ctx[CpuRegister.Rax];
}
[SysAbiExport(
Nid = "0fWWK5uG9rQ",
ExportName = "sceAgcQueueEndOfPipeActionPatchAddress",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int QueueEndOfPipeActionPatchAddress(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
var address = ctx[CpuRegister.Rsi];
if (!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register) ||
op != ItNop ||
register != RReleaseMem)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
return ctx.TryWriteUInt64(commandAddress + 12, address)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "J8YCgfKAMQs",
ExportName = "sceAgcQueueEndOfPipeActionPatchGcrCntl",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int QueueEndOfPipeActionPatchGcrCntl(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
if (!IsAgcReleaseMemPacket(ctx, commandAddress))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
return TryPatchUInt32Bits(
ctx,
commandAddress + 8,
0x0000_FFFFu,
(uint)ctx[CpuRegister.Rsi] & 0xFFFFu)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "MlEw1feXcjg",
ExportName = "sceAgcQueueEndOfPipeActionPatchData",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int QueueEndOfPipeActionPatchData(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
if (!IsAgcReleaseMemPacket(ctx, commandAddress))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
return ctx.TryWriteUInt64(commandAddress + 20, ctx[CpuRegister.Rsi])
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "T9fjQIINoeE",
ExportName = "sceAgcQueueEndOfPipeActionPatchType",
Target = Generation.Gen5,
LibraryName = "libSceAgc")]
public static int QueueEndOfPipeActionPatchType(CpuContext ctx)
{
var commandAddress = ctx[CpuRegister.Rdi];
var dataSelection = (uint)ctx[CpuRegister.Rsi];
TraceAgc(
$"agc.eop_patch_type cmd=0x{commandAddress:X16} value=0x{dataSelection:X8}");
if (dataSelection > 3 || !IsAgcReleaseMemPacket(ctx, commandAddress))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
return TryPatchUInt32Bits(
ctx,
commandAddress + 8,
0x00FF_0000u,
dataSelection << 16)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
private static bool IsAgcReleaseMemPacket(CpuContext ctx, ulong commandAddress) =>
TryGetPacketIdentity(ctx, commandAddress, out var op, out var register) &&
op == ItNop &&
register == RReleaseMem;
private static bool TryPatchUInt32Bits(
CpuContext ctx,
ulong address,
uint mask,
uint value)
{
return TryReadUInt32(ctx, address, out var current) &&
TryWriteUInt32(ctx, address, PatchUInt32Bits(current, mask, value));
}
private static uint PatchUInt32Bits(uint current, uint mask, uint value) =>
(current & ~mask) | (value & mask);
private static int SetIndirectPatchAddress(CpuContext ctx, string registerSpace)
{
var commandAddress = ctx[CpuRegister.Rdi];
var registersAddress = ctx[CpuRegister.Rsi];
if (commandAddress == 0 || registersAddress == 0)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
if (!TryReadUInt32(ctx, commandAddress, out _))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (!TryGetIndirectPatchLayout(
ctx,
commandAddress,
registerSpace,
out var addressOffset,
out _,
out _) ||
!TryReadUInt32(ctx, commandAddress + addressOffset, out var currentLow) ||
!TryWriteUInt32(
ctx,
commandAddress + addressOffset,
(currentLow & 0x3u) | (uint)(registersAddress & 0xFFFF_FFFCUL)) ||
!TryWriteUInt32(ctx, commandAddress + addressOffset + 4, (uint)(registersAddress >> 32)))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
TraceAgc($"agc.patch_{registerSpace}_addr cmd=0x{commandAddress:X16} regs=0x{registersAddress:X16}");
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int SetIndirectPatchRegisterCount(CpuContext ctx, string registerSpace)
{
var commandAddress = ctx[CpuRegister.Rdi];
var registerCount = (uint)ctx[CpuRegister.Rsi];
if (commandAddress == 0)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
if (!TryReadUInt32(ctx, commandAddress, out _))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (!TryGetIndirectPatchLayout(
ctx,
commandAddress,
registerSpace,
out _,
out var countOffset,
out var countMask) ||
!TryReadUInt32(ctx, commandAddress + countOffset, out var currentCount) ||
!TryWriteUInt32(
ctx,
commandAddress + countOffset,
(currentCount & ~countMask) | (registerCount & countMask)))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
TraceAgc($"agc.patch_{registerSpace}_count cmd=0x{commandAddress:X16} count={registerCount}");
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static int PatchWriteDataControlByte(CpuContext ctx, int byteIndex)
{
if (!TryResolveWriteDataPatchArguments(
ctx,
ctx[CpuRegister.Rdi],
ctx[CpuRegister.Rsi],
out var commandAddress,
out var value) ||
!TryReadUInt32(ctx, commandAddress + 4, out var control))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var shift = byteIndex * 8;
var patchedControl = (control & ~(0xFFu << shift)) | (((uint)value & 0xFFu) << shift);
return TryWriteUInt32(ctx, commandAddress + 4, patchedControl)
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
private static bool TryResolveWriteDataPatchArguments(
CpuContext ctx,
ulong first,
ulong second,
out ulong commandAddress,
out ulong value)
{
if (IsWriteDataPacket(ctx, first))
{
commandAddress = first;
value = second;
return true;
}
if (IsWriteDataPacket(ctx, second))
{
commandAddress = second;
value = first;
return true;
}
commandAddress = 0;
value = 0;
return false;
}
private static bool IsWriteDataPacket(CpuContext ctx, ulong commandAddress)
{
if (!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register))
{
return false;
}
return op == ItWriteData || (op == ItNop && register == RWriteData);
}
private static int AddIndirectPatchRegisters(CpuContext ctx, string registerSpace)
{
var commandAddress = ctx[CpuRegister.Rdi];
var registerCount = (uint)ctx[CpuRegister.Rsi];
if (commandAddress == 0)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
if (!TryReadUInt32(ctx, commandAddress, out _))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
if (!TryGetIndirectPatchLayout(
ctx,
commandAddress,
registerSpace,
out _,
out var countOffset,
out var countMask) ||
!TryReadUInt32(ctx, commandAddress + countOffset, out var currentCount))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
var newCount = ((currentCount & countMask) + registerCount) & countMask;
if (!TryWriteUInt32(
ctx,
commandAddress + countOffset,
(currentCount & ~countMask) | newCount))
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
TraceAgc($"agc.patch_{registerSpace}_add cmd=0x{commandAddress:X16} add={registerCount} total={newCount}");
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
private static bool TryGetIndirectPatchLayout(
CpuContext ctx,
ulong commandAddress,
string registerSpace,
out ulong addressOffset,
out ulong countOffset,
out uint countMask)
{
addressOffset = 0;
countOffset = 0;
countMask = 0;
if (!TryGetPacketIdentity(ctx, commandAddress, out var op, out var register) ||
!TryReadUInt32(ctx, commandAddress, out var header))
{
return false;
}
var expectedOp = registerSpace switch
{
"cx" => ItSetContextRegIndirect,
"sh" => ItSetShRegIndirect,
"uc" => ItSetUconfigRegIndirect,
_ => 0u,
};
if (op == expectedOp && Pm4Length(header) == 5)
{
addressOffset = 4;
countOffset = 16;
countMask = 0x3FFFu;
return true;
}
var expectedLegacyRegister = registerSpace switch
{
"cx" => RCxRegsIndirect,
"sh" => RShRegsIndirect,
"uc" => RUcRegsIndirect,
_ => uint.MaxValue,
};
if (op == ItNop && register == expectedLegacyRegister && Pm4Length(header) == 4)
{
addressOffset = 8;
countOffset = 4;
countMask = uint.MaxValue;
return true;
}
return false;
}
private static bool TryGetPacketIdentity(
CpuContext ctx,
ulong commandAddress,
out uint op,
out uint register)
{
op = 0;
register = 0;
if (commandAddress == 0 || !TryReadUInt32(ctx, commandAddress, out var header))
{
return false;
}
op = (header >> 8) & 0xFFu;
register = (header >> 2) & 0x3Fu;
return true;
}
}