mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-20 01:46:22 +08:00
e1cf5b13ef
* [agc] WAIT_REG_MEM suspend/resume, draw packet fixes, new HLE exports, debug cleanup
Rebased onto upstream 79a7437 (par274/sharpemu, rewritten history).
- GpuWaitRegistry: DCBs suspended on unsatisfied WAIT_REG_MEM are re-polled
against guest memory on every submit; fixed 64-bit and standard packet parse
offsets, apply the mask, treat PM4 compare function 0 as "always".
- TryReadSubmittedDrawCount: accept the 5-dword ItDrawIndex2 form emitted by
DcbDrawIndex (count at +4); menu draws were silently discarded before.
- sceAgcDriverSubmitMultiDcbs: reversed ABI (rdi=address array, rsi=dword
sizes, rdx=count).
- VideoOut: vblank events, sceVideoOutGetFlipStatus, buffers registered via
sceVideoOutRegisterBuffers are valid flip targets.
- New HLE: libc stdio (fopen/fread/fseek/ftell/fclose/fgets), Dinkumware
_Getpctype ctype table, NpTrophy2 stubs, AMPR PAK sequential-read tracker,
MsgDialog lifecycle, NGS2 alt NIDs + dummy vtable for handle objects,
guarded memset intrinsic, abort()/strcasecmp null-arg recovery.
- Removed investigation-only code (INT3 breakpoints, qfont/mcpp dumps,
error-candidate printf traces, unconditional debug logs).
First rendered frame: Quake (PPSA01880) presents a 1920x1080 guest frame.
* Implemented a guarded native intrinsic (rep movsb) in DirectExecutionBackend to bypass HLE dispatch overhead, while preserving memory safety checks.
527 lines
18 KiB
C#
527 lines
18 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.HLE;
|
|
using System.Threading;
|
|
|
|
namespace SharpEmu.Libs.Kernel;
|
|
|
|
public static class KernelExports
|
|
{
|
|
private static readonly object _cxaGate = new();
|
|
private static readonly List<CxaDestructorEntry> _cxaDestructors = new();
|
|
private static readonly object _coredumpGate = new();
|
|
private static ulong _coredumpHandler;
|
|
private static ulong _coredumpHandlerContext;
|
|
private const uint Gen4CompiledSdkVersion = 0x05000000;
|
|
private const uint Gen5CompiledSdkVersion = 0x09000000;
|
|
|
|
private readonly record struct CxaDestructorEntry(
|
|
ulong Function,
|
|
ulong Argument,
|
|
ulong ModuleHandle);
|
|
|
|
[SysAbiExport(
|
|
Nid = "WB66evu8bsU",
|
|
ExportName = "sceKernelGetCompiledSdkVersion",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelGetCompiledSdkVersion(CpuContext ctx)
|
|
{
|
|
var versionAddress = ctx[CpuRegister.Rdi];
|
|
if (versionAddress == 0)
|
|
{
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
var sdkVersion = ctx.TargetGeneration == Generation.Gen5
|
|
? Gen5CompiledSdkVersion
|
|
: Gen4CompiledSdkVersion;
|
|
|
|
if (!ctx.TryWriteUInt32(versionAddress, sdkVersion))
|
|
{
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
}
|
|
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "8zLSfEfW5AU",
|
|
ExportName = "sceCoredumpRegisterCoredumpHandler",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceCoredump")]
|
|
public static int CoredumpRegisterHandler(CpuContext ctx)
|
|
{
|
|
lock (_coredumpGate)
|
|
{
|
|
_coredumpHandler = ctx[CpuRegister.Rdi];
|
|
_coredumpHandlerContext = ctx[CpuRegister.Rsi];
|
|
}
|
|
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "uMei1W9uyNo",
|
|
ExportName = "exit",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int Exit(CpuContext ctx) => RequestProcessExit(ctx, "exit");
|
|
|
|
[SysAbiExport(
|
|
Nid = "L1SBTkC+Cvw",
|
|
ExportName = "abort",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int Abort(CpuContext ctx)
|
|
{
|
|
// Route through the same graceful guest-entry-exit path as exit(): letting the call
|
|
// fall through to the host's native abort() does not unwind the guest thread cleanly.
|
|
Console.Error.WriteLine("[LOADER][INFO] abort() called by guest - terminating");
|
|
GuestThreadExecution.RequestCurrentEntryExit("abort", -1);
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)(-1L));
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "XKRegsFpEpk",
|
|
ExportName = "catchReturnFromMain",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int CatchReturnFromMain(CpuContext ctx)
|
|
{
|
|
var status = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
Console.Error.WriteLine($"[LOADER][INFO] catchReturnFromMain(status={status})");
|
|
GuestThreadExecution.RequestCurrentEntryExit("catchReturnFromMain", status);
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)status);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "bzQExy189ZI",
|
|
ExportName = "_init_env",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int InitEnv(CpuContext ctx)
|
|
{
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "8G2LB+A3rzg",
|
|
ExportName = "atexit",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int Atexit(CpuContext ctx)
|
|
{
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "tsvEmnenz48",
|
|
ExportName = "__cxa_atexit",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int CxaAtexit(CpuContext ctx)
|
|
{
|
|
var destructorFunction = ctx[CpuRegister.Rdi];
|
|
var destructorArgument = ctx[CpuRegister.Rsi];
|
|
var moduleHandle = ctx[CpuRegister.Rdx];
|
|
if (destructorFunction == 0)
|
|
{
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
lock (_cxaGate)
|
|
{
|
|
_cxaDestructors.Add(new CxaDestructorEntry(
|
|
destructorFunction,
|
|
destructorArgument,
|
|
moduleHandle));
|
|
}
|
|
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "H2e8t5ScQGc",
|
|
ExportName = "__cxa_finalize",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int CxaFinalize(CpuContext ctx)
|
|
{
|
|
var moduleHandle = ctx[CpuRegister.Rdi];
|
|
|
|
lock (_cxaGate)
|
|
{
|
|
if (moduleHandle == 0)
|
|
{
|
|
_cxaDestructors.Clear();
|
|
}
|
|
else
|
|
{
|
|
for (var i = _cxaDestructors.Count - 1; i >= 0; i--)
|
|
{
|
|
if (_cxaDestructors[i].ModuleHandle == moduleHandle)
|
|
{
|
|
_cxaDestructors.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "kbw4UHHSYy0",
|
|
ExportName = "__pthread_cxa_finalize",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int PthreadCxaFinalize(CpuContext ctx)
|
|
{
|
|
_ = ctx;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "6Z83sYWFlA8",
|
|
ExportName = "_exit",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int UnderscoreExit(CpuContext ctx) => RequestProcessExit(ctx, "_exit");
|
|
|
|
[SysAbiExport(
|
|
Nid = "Ac86z8q7T8A",
|
|
ExportName = "sceKernelExitSblock",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelExitSblock(CpuContext ctx)
|
|
{
|
|
_ = ctx;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "6UgtwV+0zb4",
|
|
ExportName = "scePthreadCreate",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int PthreadCreate(CpuContext ctx)
|
|
{
|
|
var threadIdAddress = ctx[CpuRegister.Rdi];
|
|
var attrAddress = ctx[CpuRegister.Rsi];
|
|
var entryAddress = ctx[CpuRegister.Rdx];
|
|
var argument = ctx[CpuRegister.Rcx];
|
|
var nameAddress = ctx[CpuRegister.R8];
|
|
var name = nameAddress == 0 ? string.Empty : ReadCString(ctx, nameAddress, 256);
|
|
var threadHandle = KernelPthreadState.CreateThreadHandle(name);
|
|
KernelPthreadExtendedCompatExports.GetThreadStartScheduling(
|
|
ctx,
|
|
attrAddress,
|
|
out var priority,
|
|
out var affinityMask);
|
|
KernelPthreadExtendedCompatExports.RegisterThreadStart(
|
|
threadHandle,
|
|
name,
|
|
priority,
|
|
affinityMask);
|
|
if (threadIdAddress != 0 && !ctx.TryWriteUInt64(threadIdAddress, threadHandle))
|
|
{
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
}
|
|
|
|
if (ShouldTracePthread())
|
|
{
|
|
Console.Error.WriteLine(
|
|
$"[LOADER][TRACE] pthread_create: out=0x{threadIdAddress:X16} attr=0x{attrAddress:X16} " +
|
|
$"entry=0x{entryAddress:X16} arg=0x{argument:X16} name_ptr=0x{nameAddress:X16} " +
|
|
$"name='{name}' priority={priority} affinity=0x{affinityMask:X} -> thread=0x{threadHandle:X16}");
|
|
}
|
|
|
|
var scheduler = GuestThreadExecution.Scheduler;
|
|
if (scheduler is not null && entryAddress != 0)
|
|
{
|
|
var request = new GuestThreadStartRequest(
|
|
threadHandle,
|
|
entryAddress,
|
|
argument,
|
|
attrAddress,
|
|
name,
|
|
priority,
|
|
affinityMask);
|
|
if (!scheduler.TryStartThread(ctx, request, out var error))
|
|
{
|
|
Console.Error.WriteLine(
|
|
$"[LOADER][ERROR] pthread_create: failed to schedule guest thread '{name}' entry=0x{entryAddress:X16}: {error}");
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN;
|
|
}
|
|
}
|
|
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "OxhIB8LB-PQ",
|
|
ExportName = "pthread_create",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int PosixPthreadCreate(CpuContext ctx) => PthreadCreate(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "Jmi+9w9u0E4",
|
|
ExportName = "pthread_create_name_np",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int PosixPthreadCreateNameNp(CpuContext ctx) => PthreadCreate(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "3kg7rT0NQIs",
|
|
ExportName = "scePthreadExit",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int PthreadExit(CpuContext ctx)
|
|
{
|
|
var value = ctx[CpuRegister.Rdi];
|
|
GuestThreadExecution.RequestCurrentEntryExit("scePthreadExit", value);
|
|
ctx[CpuRegister.Rax] = value;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "FJrT5LuUBAU",
|
|
ExportName = "pthread_exit",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libScePosix")]
|
|
public static int PosixPthreadExit(CpuContext ctx)
|
|
{
|
|
var value = ctx[CpuRegister.Rdi];
|
|
GuestThreadExecution.RequestCurrentEntryExit("pthread_exit", value);
|
|
ctx[CpuRegister.Rax] = value;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "onNY9Byn-W8",
|
|
ExportName = "scePthreadJoin",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int PthreadJoin(CpuContext ctx)
|
|
{
|
|
var threadId = ctx[CpuRegister.Rdi];
|
|
var returnValueAddress = ctx[CpuRegister.Rsi];
|
|
|
|
if (ShouldTracePthread())
|
|
{
|
|
Console.Error.WriteLine(
|
|
$"[LOADER][TRACE] pthread_join: thread=0x{threadId:X16} retval_out=0x{returnValueAddress:X16}");
|
|
}
|
|
|
|
var returnValue = 0UL;
|
|
if (GuestThreadExecution.Scheduler is { } scheduler &&
|
|
!scheduler.TryJoinThread(ctx, threadId, out returnValue, out var error))
|
|
{
|
|
Console.Error.WriteLine(
|
|
$"[LOADER][ERROR] pthread_join: thread=0x{threadId:X16}: {error}");
|
|
var result = string.Equals(
|
|
error,
|
|
"thread cannot join itself",
|
|
StringComparison.Ordinal)
|
|
? OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT
|
|
: OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)(int)result);
|
|
return (int)result;
|
|
}
|
|
|
|
if (returnValueAddress != 0 &&
|
|
!ctx.TryWriteUInt64(returnValueAddress, returnValue))
|
|
{
|
|
ctx[CpuRegister.Rax] =
|
|
unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
|
}
|
|
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "h9CcP3J0oVM",
|
|
ExportName = "pthread_join",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int PosixPthreadJoin(CpuContext ctx) => PthreadJoin(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "wuCroIGjt2g",
|
|
ExportName = "open",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int Open(CpuContext ctx) => KernelMemoryCompatExports.KernelOpenUnderscore(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "1G3lF1Gg1k8",
|
|
ExportName = "sceKernelOpen",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelOpen(CpuContext ctx) => KernelMemoryCompatExports.KernelOpenUnderscore(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "mqQMh1zPPT8",
|
|
ExportName = "fstat",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int Fstat(CpuContext ctx) => KernelMemoryCompatExports.KernelFstat(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "hcuQgD53UxM",
|
|
ExportName = "printf",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int Printf(CpuContext ctx)
|
|
{
|
|
ulong fmtPtr = ctx[CpuRegister.Rdi];
|
|
string fmt = ReadCString(ctx, fmtPtr, 4096);
|
|
string outStr = KernelMemoryCompatExports.FormatStringFromVarArgs(ctx, fmt, firstGpArgIndex: 1);
|
|
if (outStr.EndsWith('\n') || outStr.EndsWith('\r'))
|
|
{
|
|
Console.Write($"[DEBUG][PRINF] {outStr}");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"[DEBUG][PRINF] {outStr}");
|
|
}
|
|
|
|
ctx[CpuRegister.Rax] = (ulong)System.Text.Encoding.UTF8.GetByteCount(outStr);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "EMutwaQ34Jo",
|
|
ExportName = "perror",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libc")]
|
|
public static int Perror(CpuContext ctx)
|
|
{
|
|
ulong sPtr = ctx[CpuRegister.Rdi];
|
|
|
|
string msg;
|
|
if (sPtr == 0)
|
|
{
|
|
msg = "perror(NULL)";
|
|
}
|
|
else
|
|
{
|
|
msg = ReadCString(ctx, sPtr, 2048);
|
|
msg = $"perror(\"{msg}\")";
|
|
}
|
|
|
|
Console.WriteLine(msg);
|
|
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
private static string ReadCString(CpuContext ctx, ulong address, int maxLen)
|
|
{
|
|
Span<byte> buf = stackalloc byte[maxLen];
|
|
if (!ctx.Memory.TryRead(address, buf))
|
|
return $"<unreadable 0x{address:X16}>";
|
|
|
|
int len = 0;
|
|
while (len < buf.Length && buf[len] != 0) len++;
|
|
|
|
try { return System.Text.Encoding.UTF8.GetString(buf.Slice(0, len)); }
|
|
catch { return System.Text.Encoding.ASCII.GetString(buf.Slice(0, len)); }
|
|
}
|
|
|
|
private static bool ShouldTracePthread()
|
|
{
|
|
return string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_PTHREADS"), "1", StringComparison.Ordinal);
|
|
}
|
|
|
|
private static int RequestProcessExit(CpuContext ctx, string syscallName)
|
|
{
|
|
var status = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
Console.Error.WriteLine($"[LOADER][INFO] {syscallName}(status={status})");
|
|
GuestThreadExecution.RequestCurrentEntryExit(syscallName, status);
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)status);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
// Unidentified kernel NIDs observed at runtime; stubbed to return 0 until they are
|
|
// resolved against scripts/ps5_names.txt.
|
|
[SysAbiExport(
|
|
Nid = "9T2pDF2Ryqg",
|
|
ExportName = "sceKernelUnknown9T2p",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelUnknown9T2p(CpuContext ctx) => SetZeroReturn(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "TU-d9PfIHPM",
|
|
ExportName = "sceKernelUnknownTU",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelUnknownTU(CpuContext ctx) => SetZeroReturn(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "iWQWrwiSt8A",
|
|
ExportName = "sceKernelUnknowniWQW",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelUnknowniWQW(CpuContext ctx) => SetZeroReturn(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "KuOmgKoqCdY",
|
|
ExportName = "sceKernelUnknownKuOmg",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelUnknownKuOmg(CpuContext ctx) => SetZeroReturn(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "RenI1lL1WFk",
|
|
ExportName = "sceKernelUnknownRenI",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelUnknownRenI(CpuContext ctx) => SetZeroReturn(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "pQGpHYopAIY",
|
|
ExportName = "sceKernelUnknownpQGp",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelUnknownpQGp(CpuContext ctx) => SetZeroReturn(ctx);
|
|
|
|
[SysAbiExport(
|
|
Nid = "Rbvt+5Y2iEw",
|
|
ExportName = "sceKernelUnknownRbvt",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libKernel")]
|
|
public static int KernelUnknownRbvt(CpuContext ctx) => SetZeroReturn(ctx);
|
|
|
|
// NOTE: "SHtAad20YYM"/"DIxvoy7Ngvk" are sce::Json::Value::getType/getInteger, and
|
|
// "3GPpjQdAMTw"/"9rAeANT2tyE"/"tsvEmnenz48" are __cxa_guard_acquire/__cxa_guard_release/
|
|
// __cxa_atexit (verified by hashing against scripts/ps5_names.txt). Do not register them
|
|
// here as kernel mutex functions: the cxa guards are implemented in CxxAbiExports.cs and
|
|
// shadowing them breaks every C++ static-init guard in the game.
|
|
|
|
private static int SetZeroReturn(CpuContext ctx)
|
|
{
|
|
ctx[CpuRegister.Rax] = 0;
|
|
return 0;
|
|
}
|
|
}
|