Files
AnyPS5/docs/dev/debt/libSceAmpr.md
T
Adrià 3443fd2ec8 Merge pull request #11727 from camezaro-intc/fix/ampr-command-buffer-expansion
fix(libSceAmpr): dynamically expand command buffer capacity on append
2026-10-29 20:35:27 +01:00

7.4 KiB

libSceAmpr technical debt

Silent stubs

Unknown function info

  • AMPR command buffer null arguments (libSceAmpr) - the result for a null command buffer is unknown for sceAmprCommandBufferConstructor, sceAmprAprCommandBufferConstructor, sceAmprCommandBufferReset and sceAmprCommandBufferSetBuffer; these return SCE_KERNEL_ERROR_EINVAL here, but the behavior has not been confirmed on a PS5.
  • AMPR WriteAddressFromCounterPairOnCompletion (libSceAmpr) - the pair of an even counter n is assumed to give counter n in the low 32 bits and n + 1 in the high 32 bits. Wait compare values, wait flush and counter index ranges follow the ampr_emu reimplementation; the flush is accepted and has no effect, as commands run in order
  • AMPR command buffer offsets (libSceAmpr) - recording with a current offset greater than the bound memory size throws before calculating remaining capacity or writing a command. This validates the emulator's own command-buffer encoding; the console's response to a corrupted opaque buffer object is unknown.
  • AMPR command sizes (libSceAmpr) - non-Nop commands use host encoding with an 8-byte header, exceeding console sizes. On overflow, the buffer expands up to a 32 MiB cap on the guest heap, relocating base and size; below this cap a full buffer avoids EBUSY, placing commands in host memory not allocated by the guest, and AMM submits resolve original pointers to expanded memory. Rebinding the same memory via SetBuffer frees prior expansions and resets that buffer's offset and commands to 0. Beyond the cap, appends return EBUSY. Nop(n) is n dwords and NopWithData(n) (n + 1) dwords with header 0x5452000F | payloadDwords << 8 | (type & 0xF) << 12, and ConstructNop writes its word before payload and zeroes padding (FW 13.42, #10936); null base reports before count or range. NopWithData and ConstructNop stop at the record without trailing zeros. The at-start flag of _04_00 writes is ignored; commands run in order.
  • APR map begin and end (libSceAmpr) - as in ampr_emu, MapBegin/MapDirectBegin map when they run and MapEnd only closes the region; while a region is open a second begin and every completion write return EPERM, the at-start _04_00 writes are accepted
  • AMPR markers: colors are not recorded and command sizes use the host encoding. Native signatures confirm that set takes a color pointer and push/measure take the color value.
  • __cxa_call_unexpected (libSceAmpr) - takes the in-flight exception's unwind header and never returns, as libcxxabi 18.1.8 does. No std::set_unexpected is exported and the exception header's unexpected handler is never set, so the handler is always the default one, which calls std::terminate (cxa_default_handlers.cpp); it therefore forwards to libc's __cxa_call_terminate, which begins the catch and runs the current terminate handler. That matches libcxxabi except for which terminate handler runs: there std::terminate after __cxa_begin_catch (cxa_exception.cpp L470-L477) runs the handler saved in the caught exception's header (cxa_handlers.cpp L72-L93), which __cxa_throw and __cxa_init_primary_exception set to std::get_terminate() when the exception is thrown (cxa_exception.cpp L209-L214, L279), while here the current global handler runs, as AnyPS5's own std::terminate does. The two differ only if std::set_terminate was called between the throw and this call. A null header terminates because this emulator's libc __cxa_begin_catch terminates on null (libcxxabi does not define that case). Dynamic exception specifications that would let a replaced unexpected handler throw a permitted type are not modelled

Functional

  • sceAmprAmmGetUsageStatsData (libSceAmpr) fills the caller-sized 24-byte statistics structure with 512 page-table pool entries, no allocated entries, a zero high watermark and ring idle flags 0x7, the constants of KytyPS5; they are not measured on a console, and the real call is a pass-through to sceKernelMapperGetUsageStatsData per drakmor/ampr_emu, which this port has no kernel-side counterpart for.
  • Dinkumware thread exports (libSceAmpr) follow the Dinkumware threads library shipped with Visual C++ 2013 (cthread.c) and its atomic.c, microsoft/STL cthread.cpp and C11 Annex K, without console verification. The spin lock (byte exchange loop, no yield) matches that disassembly. The shared_ptr spin lock is one process-wide flag that yields while it spins. wcstombs_s is wcsrtombs_s with a fresh conversion state, so *retval excludes the terminating null as in Annex K, not as in the Microsoft CRT, and _TRUNCATE has no meaning.
  • _Cnd_init_with_name (libSceAmpr) - takes a pointer to the handle slot like the libc _Cnd_* exports from #3079 and acts on the same conditions. The signature _Cnd_init_with_name(_Cnd_t*, const char* name) is inferred from fpPS4 ps4_mtx_internal.pas, where the PS4 _Mtx_init is _Mtx_init_with_name with the name SceLibcI_Mtx and the name only reaches the pthread object; here it is _Cnd_init and the name, which may be null, is ignored. This is not confirmed on a PS5 libc.prx.