[AGC] Quake rendering progress: WAIT_REG_MEM, draw fixes, VideoOut, and HLE improvements (#68)

* [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.
This commit is contained in:
Foued Attar
2026-07-11 23:22:48 +02:00
committed by GitHub
parent de4fc1e1a8
commit e1cf5b13ef
21 changed files with 2204 additions and 191 deletions
+40 -6
View File
@@ -13,7 +13,7 @@ public static class Ngs2Exports
private const int OrbisNgs2ErrorInvalidSystemHandle = unchecked((int)0x804A0230);
private const int OrbisNgs2ErrorInvalidRackHandle = unchecked((int)0x804A0261);
private const int OrbisNgs2ErrorInvalidVoiceHandle = unchecked((int)0x804A0300);
private const ulong HandleStorageSize = 0x20;
private const ulong HandleStorageSize = 0x1000;
private const int RenderBufferInfoSize = 0x18;
private const ulong MaximumRenderBufferSize = 16 * 1024 * 1024;
@@ -197,12 +197,19 @@ public static class Ngs2Exports
lock (StateGate)
{
return ctx.SetReturn(
Voices.ContainsKey(ctx[CpuRegister.Rdi])
? 0
Voices.ContainsKey(ctx[CpuRegister.Rdi])
? 0
: OrbisNgs2ErrorInvalidVoiceHandle);
}
}
[SysAbiExport(
Nid = "-4GCfYdNF1s",
ExportName = "sceNgs2VoiceControl",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2VoiceControlAlt(CpuContext ctx) => Ngs2VoiceControl(ctx);
[SysAbiExport(
Nid = "AbYvTOZ8Pts",
ExportName = "sceNgs2VoiceRunCommands",
@@ -210,6 +217,27 @@ public static class Ngs2Exports
LibraryName = "libSceNgs2")]
public static int Ngs2VoiceRunCommands(CpuContext ctx) => Ngs2VoiceControl(ctx);
[SysAbiExport(
Nid = "-TOuuAQ-buE",
ExportName = "sceNgs2VoiceRunCommands",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2VoiceRunCommandsAlt(CpuContext ctx) => Ngs2VoiceControl(ctx);
[SysAbiExport(
Nid = "x8qnXqh-tiM",
ExportName = "sceNgs2VoiceGetState",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2VoiceGetState(CpuContext ctx) => ctx.SetReturn(0);
[SysAbiExport(
Nid = "wPJGwI2RM2I",
ExportName = "sceNgs2VoiceGetStateFlags",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libSceNgs2")]
public static int Ngs2VoiceGetStateFlags(CpuContext ctx) => ctx.SetReturn(0);
[SysAbiExport(
Nid = "i0VnXM-C9fc",
ExportName = "sceNgs2SystemRender",
@@ -263,7 +291,6 @@ public static class Ngs2Exports
private static bool TryCreateHandle(CpuContext ctx, uint type, ulong ownerHandle, out ulong handle)
{
handle = 0;
if (!KernelMemoryCompatExports.TryAllocateHleData(ctx, HandleStorageSize, 16, out handle))
{
return false;
@@ -271,11 +298,18 @@ public static class Ngs2Exports
Span<byte> data = stackalloc byte[(int)HandleStorageSize];
data.Clear();
BinaryPrimitives.WriteUInt64LittleEndian(data[0..8], handle);
BinaryPrimitives.WriteUInt64LittleEndian(data[8..16], ownerHandle);
BinaryPrimitives.WriteUInt32LittleEndian(data[16..20], 1);
BinaryPrimitives.WriteUInt32LittleEndian(data[24..28], type);
return ctx.Memory.TryWrite(handle, data);
if (!ctx.Memory.TryWrite(handle, data))
{
return false;
}
// Offset 0 doubles as a vtable slot for guest code that treats this handle as a C++
// object; stamp it with the shared dummy vtable instead of a self-referential value.
KernelMemoryCompatExports.TryWriteDummyVtable(ctx, handle);
return true;
}
private static bool TryClearGuestBuffer(CpuContext ctx, ulong address, ulong length)