With IEEE_MODE=1 the hardware quiets a signaling NaN that v_cubesc,
v_cubetc or v_cubema_f32 returns, keeping the sign the instruction gives
it; with IEEE=0 the bits pass through. Same rule as #1867, measured on
gfx1036 in both modes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A draw that rasterizes both faces with different front and back depth
bias was rejected, since Vulkan has one depth bias per draw and the
cull mode is pipeline state. Such a draw is now recorded twice in the
same pass: front faces with the back faces culled and the front bias,
then back faces with the front faces culled and the back bias. A face
whose offset is disabled gets no bias.
With f32 denormals flushed, as in the 0xc0 float mode titles use, the
hardware flushes all three v_mullit_f32 sources and its product like
v_mul_f32, so a positive denormal limit selects -FLT_MAX and a tiny
product gives zero. The recompiler kept denormals in every mode.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With IEEE_MODE=1 the hardware quiets each signaling NaN half that
v_pack_b32_f16 packs, after neg and abs; with IEEE=0 it copies the bits.
The recompiler always copied them. Same rule as #1867, measured on
gfx1036 in both modes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A title that opens a main and an overlay port flips both, and every flip
was presented as a whole frame, so the window alternated between the two
buses: PPSA01736 shows its intro on the main bus and an empty overlay,
and the intro blinked black on every other frame. Overlay flips now
complete and signal their events without being shown. Compositing the
overlay over the main output is not implemented.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Vulkan counts the framebuffer colour attachments against maxPerStageResources
for the fragment stage. The draw's attachment count now seeds the fragment
stage's resource total in ShaderResources, so a pixel shader whose buffers and
images fit the limit but whose pipeline exceeds it throws instead of creating
an invalid pipeline. The draw resource cache key carries the attachment count,
so a cached build is never reused by a draw with more attachments.
The f16 forms of v_fma_mix computed an f32 FMA and converted it to f16,
so a result that lands on an f16 tie after the f32 rounding was rounded
twice. RDNA2 rounds the exact product-sum once. They now use the exact
f64 path of v_interp_p2_f16 and flush f32 sources like v_fma_mix_f32.
They follow the f16 round and denormal fields on hardware, so another
f16 mode throws, as packed f16 does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DEPTH_CLEAR_ENABLE (DB_RENDER_CONTROL bit 0, Mesa sid.h) makes the DB
write DB_DEPTH_CLEAR wherever the draw covers. Such a draw is now
rasterized as submitted with an always-passing depth write, no depth
bounds, no depth bias and the viewport depth range pinned to the clear
value, so it touches exactly the pixels the title's geometry covers.
Clearing a read-only or absent depth plane throws, at decode and in the
draw precheck. Depth and stencil clears combine; copy, resummarize and
decompress draws still throw.
Uncharted: Legacy of Thieves Collection (PPSA05684) clears with draws
that set DB_RENDER_CONTROL to 0x3 and stopped with "depth/stencil
clear, copy, resummarize or decompress draws (DB_RENDER_CONTROL) is
unsupported (register 0x0 = 0x00000003)".
VOP3 0x140 is v_mad_legacy_f32 on gfx10.1. Measured on gfx1010 (RX 5700
XT) in wave32 and wave64, IEEE 0 and 1 and f32 denormal modes 0 to 3, it
rounds the product before the add, flushes f32 denormal sources, product
and result in every mode, and gives a +0 product for a zero factor.
The translation multiplies, applies the zero rule and flushTinyProduct,
adds, and keeps the nanResultF32 wrapper. f32DenormalFlushFor returns 3
for the instruction before reading FLOAT_MODE. A sum of two zeros takes
its sign on the bits, as lavapipe returned -0 for +0 + -0. The execution
test pins the measured results and compares NaN bits exactly. RDNA2
(gfx1031), where the encoding is v_fma_legacy_f32 and fused, is noted in
TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The padded, exact-fit and invalid-conversion swprintf checks come from #1512.
Co-Authored-By: Noface07 <yuvrajsoni7@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULF8vCbJFyzxVsW3SfiWC5
SDL queries the SCHED_OTHER range before it sets a thread priority. It gets
the same 256 to 767 range as SCHED_FIFO and SCHED_RR, which
pthread_setschedparam applies regardless of policy. The console's answer is
unverified and recorded in TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A CMASK that was neither all fast-cleared nor all expanded threw, so a
title that fast clears a target and then draws over part of it before the
eliminate could not run past it.
The CMASK is now decoded tile by tile: one nibble per 8x8 tile in
1024x512 metablocks of 4 KiB, addressed with the pipe-aligned
SW_64KB_Z_X CMASK equation for 16 pipes (addrlib
GFX10_CMASK_SW_PATTERN, Gfx10Lib::HwlComputeCmaskAddrFromCoord). Code 0
stores CB_COLOR_CLEAR_WORD into the tile's texels, 0xF keeps them; any
other code, a cleared tile of a DCC target and any other CMASK swizzle
mode throw. Edge tiles are included and the CMASK is then stored as
expanded.
For a resident target whose CMASK is host-imported the clear is a
compute pass recorded in stream order: it reads the nibbles from the
import, stores the clear into the cleared tiles through a UINT view of
the image and fills the CMASK expanded, so nothing waits for pending GPU
writes of the CMASK. Invalid codes it reads set a bit that the next
materialization throws on. Otherwise the CPU reads the CMASK and stores
the cleared tiles into guest memory, or fills the resident image when
every tile is cleared. A CMASK nothing has stored over since it was
expanded is skipped.
The decode requires CB_COLOR_ATTRIB3 FMASK_SW_MODE SW_64KB_Z_X with
CMASK_PIPE_ALIGNED, a 4 KiB aligned writable CMASK and texels of at most
64 bits, and CMASK_BASE_EXT is part of the draw key.
Tests: CMASK sizes and nibble addresses against addrlib; decode
rejections and draw key coverage; CPU eliminate of full, partial,
out-of-range-nibble and DCC decompress cases and invalid codes. The new
agc_driver_cmask_fast_clear GPU test draws over fast-cleared and partly
fast-cleared linear and SW_64KB_R_X targets, eliminates partial, edge
and checkerboard CMASKs through the compute pass and checks that an
invalid code it reads throws.
On Linux, sceKernelSync called sync(2), which blocks until all
dirty pages are written back. Unrelated host I/O can stall guest's
sceKernelSync, causing the guest_kernel_sync test to stall for minutes.
Share the Windows implementation instead.
Fsync each recorded path and its changed parent directories.
Use st_mtim and coarse realtime mark on Linux in place of FILETIME.
Measured on RDNA2 (gfx1036) with tools/hw-oracle in wave32 and wave64:
s_call_b64 writes PC + 4 to the link pair and branches to
PC + 4 + signext(SIMM16) * 4, and s_setpc_b64 of the link returns;
s_swappc_b64 with SDST 125 branches and leaves a marker pair untouched, so
its decode as s_setpc_b64 matches, and with an SGPR destination the link is
PC + 4.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GLfKH7Sqe51wX254k1XsV
A 3D image access bound to a 2D T# matched no runtime image mode and
threw "image descriptor is incompatible with the static runtime image
interface" (Sonic Origins, #2023). CouchRiv measured RDNA2 (gfx1036)
with the hardware oracle: image_sample_lz with dim 3D ignores r and
reads the (u,v) texel, and image_load reads the (x,y) texel when z is
0 and returns zero otherwise. Add a 2D mode for 3D accesses whose only
uses are those two forms, and make the load return zero for z != 0.
Every other access keeps throwing.
Fixes#2023
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A port kept only the last PCM buffer set before a push and played it again
at every later push without a new one. PPSA12544 sets one grain of a ring
buffer per call on its object port, often two or three between pushes and
then none for a few, so all but the last of each burst were dropped and the
stale grain repeated: a buzz on loud sounds and after a sound stopped.
Buffers set between pushes are now queued and played in order, at most 16
queued. A push without a new buffer repeats the last one only for a port
that has always been given the same buffer (rewritten in place, as the
timing tests do); a port whose buffers move on plays silence.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without VK_KHR_fragment_shader_barycentric the recompiler dropped
v_interp_p1_f32 and made v_interp_p2_f32 read the attribute Vulkan
interpolated, but never wrote the I/J VGPRs: a pixel shader that read
them for anything else got 0, and v_interp_mov_f32 of P10/P20 (or P0 of
an interpolated input) needed PerVertexKHR and failed pipeline
validation.
The fallback translation now loads the I/J VGPRs and marks v_interp_p1
and v_interp_p2 with witnesses. After the front-end simplification
(SSA, folding, DCE, read-lane and masked-select elimination),
HostInterpolationChecker requires every p2 to read its J, and its p1's
I, from a barycentric of the parameter's kind, every p1 result to reach
only the matching p2 (through a write masked by the p2's EXEC, whose
inactive lanes keep the old register), no parameter read as raw
vertices and no slot shared by different interpolations. It then
lowers the witnesses to the host-interpolated attribute as before,
reruns DCE and masked-select elimination, and accepts the program only
when no barycentric read remains, so reusing the I/J VGPRs after
interpolating keeps host interpolation and the previous SPIR-V. Any other
program is translated with the barycentric path and marked as emulated:
its barycentrics become vec3 inputs (NoPerspective for the linear
weights) and its per-vertex parameters flat vec4[3] inputs, placed in
the free locations by LayoutBarycentricEmulation. The driver then
inserts a generated geometry stage between the vertex and fragment
shaders that copies the position and parameters, writes the one-hot
weights of each vertex and the three vertices' parameter values, so the
rasterizer interpolates the same weights the extension provides.
The stage needs geometryShader and a vertex shader before it;
tessellation, mesh and rect-list pipelines, other primitive types and
interfaces beyond 32 locations or the device's geometry limits throw.
CompiledShaderArtifact carries the emulation (shader disk cache format 21).
Hardware snaps window corners to 1/256 pixel before interpolating
(measured by yahya308 on an RTX 4070 Laptop and a Radeon 780M in #1547);
lavapipe snaps only for coverage (lp_rast.h FIXED_ORDER 8), so the test
accepts either reference per pixel at the original 1e-4 margin.
Sources:
- v_interp_p1_f32/v_interp_p2_f32/v_interp_mov_f32 semantics (P0,
P10, P20, I/J): AMD RDNA2 ISA, "Parameter Interpolation".
- BaryCoordKHR/BaryCoordNoPerspKHR weights and PerVertexKHR vertex order:
SPV_KHR_fragment_shader_barycentric, VK_KHR_fragment_shader_barycentric.
- Geometry shader triangle input order and provoking vertex: Vulkan
specification, "Primitive Topologies" and "Geometry Shading".
- Interpolation decorations decided by the fragment input: Vulkan
specification, "Interpolation Decorations".
- lavapipe interpolation: Mesa src/gallium/drivers/llvmpipe/lp_rast.h
(FIXED_ORDER 8), lp_setup_tri.c (subpixel_snap for coverage only),
lp_state_setup.c (init_args, plane setup from float positions).
Every direct memory allocation had a memfd of its own, so each one the
GPU used became its own udmabuf and its own imported buffer object. An
imported buffer object cannot be always valid in the GPU VM: amdgpu
validates every one of them at every command submission. PPSA14632
(LEGO Horizon Adventures) imports about 7000 allocations, 5800 of them
64 KiB, and each vkQueueSubmit took about 2.3 ms (5 s per 10 s, held
under the GPU mutex). A standalone test shows the cost follows the
count of imported dma-buf buffer objects, not their size or the count
of ordinary allocations.
- libkernel: on Linux all direct memory lives in one sparse memfd of
DIRECT_MEMORY_SIZE, at file offset = physical address
(PhysicalBacking keeps the file and its offset; SharedBacking returns
that offset). A range allocated again is zeroed through a temporary
mapping, as the old per-allocation memfd was.
- Driver: an import of shared direct memory that lies inside one
64 MiB chunk of the file binds its own VkBuffer into that chunk's
memory, one udmabuf import per chunk (HostImport::chunk holds it, so
it lives until the last buffer bound to it retires). Ranges that
cross a chunk or do not meet the buffer alignment are imported alone
as before.
On PPSA14632 vkQueueSubmit drops from about 2400 us to 46 us on
average, the GPU idles 36 ms instead of 172 ms per present, and
presents go from about 40 to 80-97 per 10 s.
Released direct memory stays committed in the file (see
TechnicalDebt.md): a live chunk import pins its pages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macOS has no counterpart to userfaultfd write-protect or
MEM_WRITE_WATCH, so GuestWriteWatch had no backend there and the driver
re-read every guest buffer on each use.
The macOS backend write-protects watched pages. The first write to a
page faults, the SIGBUS/SIGSEGV handler records the page and restores
its protection, and Collect reports the recorded pages and protects
them again. A page is protected before its record is cleared, under the
lock the handler takes, so a write that lands in between is recorded.
The lock hands out turns in order: an unfair one starved writers under
Rosetta.
The protection is real, which adds three entry points:
- GuestWriteWatchBeginHostWrite/EndHostWrite lift it around a system
call that writes guest memory, which would otherwise fail with
EFAULT. GuestArena::HostWrite calls them, as it calls the Windows
equivalent.
- GuestWriteWatchProtectionChanged tells the watch the protection given
to watched pages by mprotect or a new mapping.
- GuestWriteWatchProtection reports that protection, since a watched
page is read-only between a collection and the next write.
Register keeps the recorded protection of a page it already watches:
read from the kernel it would be the watch's own read-only protection,
and the page would never be watched again.
The tests build without the prx libraries and run natively and through
Rosetta.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AI-assisted: yes. Full current-main project build passed. Seven focused Windows tests pass; four shader tests also pass in full and none optimization modes with actual Vulkan execution required. Updated Linux and title runs remain pending.
gmtime, localtime, mktime, strftime and asctime took and returned the
host std::tm. The guest's FreeBSD tm has tm_gmtoff and tm_zone after the
nine calendar fields, so on Windows, where std::tm is 36 bytes, a title
reading those fields read past the thread-local result, and gmtime_s left
them unwritten. Windows gmtime_s also accepts only 1969-12-31 12:00 to
3001-01-19 21:59 UTC, where the guest libc converts any time.
Convert through a GuestTm with the guest layout on both hosts. Compute
the UTC fields from the day count, so every time whose year fits an int
converts, and on Windows take the local offset and zone name from the
CRT. gmtime reports the zone as UTC like the guest libc. localtime and
mktime keep the CRT's range on Windows; recorded in TechnicalDebt.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A title that ships its own libc.prx unwinds C++ exceptions with that
libc's libunwind, which asks sceKernelGetModuleInfoForUnwind where each
frame's eh_frame is. On Linux the call always reported no tables, so
no C++ exception thrown in guest code could be caught. For the main
executable and .guest.prx images, report the PT_GNU_EH_FRAME tables
that sceKernelGetModuleInfoFromAddr finds, as Windows does from
.ehmeta. Other host images still report none.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
CB_COLOR_INFO format 6 (COLOR_10_11_11) with NUMBER_UNORM threw in
DecodeColorFormat: Vulkan has only the B10G11R11 float format. Such a
target now renders into an R32_UINT attachment in the layout of
format-30 images (unorm11 R and G in bits 0-21, unorm10 B in bits
22-31), so later image loads of the surface read the same words.
The target format is draw state, so the packing is a specialization of
the registered pixel shader: every float MRT export also computes the
packed word (EmitFormatStoreComponent) behind specialization constant
ExportPackingBase + target, and the specialized module turns a packed
target's output into a uint4 that ValidateShaders expects. The new
request field targetExportPacking is in the module key, the result memo,
the draw key and the serialized request (version 14). Blending, partial
RGB writes, UNORM16/SNORM16/UINT16 exports, DCC and ROUND_MODE throw.
Measured on a PS5 (firmware 13.60) through dlopen of
libSceVideoOut.sprx on a handle the payload opened.
sceVideoOutInitializeOutputOptions writes 0xFF to byte 2 of the 64-byte
options and returns INVALID_ADDRESS for a null pointer.
sceVideoOutIsOutputSupported checks the handle, then the reserved
arguments, then the mode, then the options. It never answers 0: a mode
the display lacks returns UNSUPPORTED_OUTPUT_MODE, and a value that is
not a mode returns 0x8029001E. Options must keep the initialized words,
except word 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fill takes either info struct, so sceKernelGetModuleInfo reports the
name and segments sceKernelGetModuleInfoFromAddr reports for the same
image, on Linux and Windows.
A sampler with FORCE_DEGAMMA (S# word 0 bit 20) threw at decode, so
every dispatch or draw sampling through one was skipped.
DecodeSamplerResource now records the bit, but accepts it only for a
caller that pairs the sampler with its images. The direct sampler
binding does, and RequireDegammaFormat checks each sampled image
against its paired samplers: only already sRGB formats (8, 8_8 and
8_8_8_8 SRGB, BC1, BC2, BC3 and BC7 SRGB) pass, decoded once, as
shadPS4 (PS4) models it. Other textures and unpaired decodes still
throw; gfx10 behaviour is not measured.
The AMD Windows driver returns 1 for a NaN operand when any float form
of ordered not-equal feeds a ballot on a 32-wide subgroup, which is how
e32 compares are read in wave32. FPOrdNotEqual32 is now emitted as an
unordered not-equal with both operands checked for NaN on their bits,
which gives the same result for every input.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep logical sample and skip counts across byte-only continuation buffers, assemble split superframes, and allow callbacks to refill, pause or reset the stream. Preserve decoded audio while waiting for input and reject truncated closed streams.
Validated with seven NGS2 tests, an ASan/UBSan streaming test build, and PPSA03747 repeatedly supplying continuation buffers during playback. Flag semantics follow the pinned KytyPS5 source documented in TechnicalDebt.md.
Written with AI assistance.
EVO-PLAYER (PPSA99039) v0.11.0 stops at sigaction once its earlier
imports resolve: it installs a crash handler for SIGSEGV, SIGBUS and
SIGABRT and a quit handler for SIGTERM, SIGINT and SIGHUP at start-up.
sigaction records the handler, flags and mask of signals 1 to 128 and
reports them back, with FreeBSD's EINVAL cases. signal shares the table
and records SA_RESTART, as FreeBSD's libc does. Plain handlers on the
signals signal already delivers run as signal handlers do; SA_SIGINFO
handlers and other signals are recorded only, and raise of such a
signal throws.
Assisted by Claude Code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwFnKjMxmwGDWiuYYf1v4D
A subnormal operand sent v_fma_f64 to a separate multiply and add, and a
subnormal result was rounded twice: once to 53 bits in the scaled domain
and again by the power-of-two rescale. Both differ from IEEE 754
fusedMultiplyAdd in the last bit.
Subnormal operands are now widened by 2^64 and enter the exact path with
their exponent lowered by 64. The exact path keeps the sign of the residual
between the 53-bit result and the exact sum; when the result lands on a
midpoint of the subnormal grid it is moved one ulp towards the exact sum
before the single final rounding. Normal results are rebuilt by setting
the exponent, so only a subnormal result rounds. v_div_fmas_f64 shares the
path.
Each Windows AIO request is an overlapped ReadFile/WriteFile at the
request's offset, which on a synchronous handle also moves the
descriptor's file pointer; on the console AIO transfers leave it alone.
The requests now go through the positioned path sceKernelPread and
sceKernelPwrite use (NativePositioned in Stdio.cpp, exposed as
NativePositioned_nid_no_patch), which restores the pointer afterwards.
Negative offsets still fail with EINVAL before the transfer.
GuestAio sets the offset to 2 before a write batch and checks it is
still 2 after. TechnicalDebt records that a concurrent read or write on
the same descriptor can see the moved pointer in between, as for the
pread family.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
Keeps both new TechnicalDebt entries: this branch's note that a raise
can still reenter host code it interrupted, and #2107's sigaltstack note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
read, write, _read and _write on a guest socket descriptor went to the
host, which has no such descriptor, so sceKernelRead and sceKernelWrite
threw. They now reach the socket as recv and send without flags, with
the socket layer's errno (EAGAIN 35, EPIPE 32, EBADF 9), a zero-length
read returns 0 as dofileread does, and the two sceKernel forms return
the kernel error for the same cases. The host's SIGPIPE on EPIPE, a
write above INT_MAX bytes, readv, writev and the positioned forms are
recorded in TechnicalDebt.
OpenSSL's QUIC notifier signals its socketpair with write and read.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The status layout is shadPS4's OrbisGameLiveStreamingStatus2 (72
bytes). Live streaming is not supported, so the call reports that no
stream is on air, with an invalid user id; a null status throws.
Uncharted: Legacy of Thieves Collection (PPSA05684) stopped at sceGameLiveStreamingGetCurrentStatus2.
msync follows FreeBSD 11's kern_msync and vm_map_sync over the guest's
mappings as sceKernelVirtualQuery reports them: the address is rounded
down to the page, a range that wraps and MS_ASYNC with MS_INVALIDATE are
EINVAL, an unmapped start or page is ENOMEM, and length 0 checks the
mapping that holds the address. The guest has no file mappings, so a
successful call has nothing to write back. MS_INVALIDATE on a mapped
range throws, since FreeBSD's EBUSY for mlocked pages needs lock state
that is not tracked.
EVO-PLAYER PPSA99039 v0.11.0 imports it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The IME panel cannot be open while sceImeOpen is unimplemented, so the
query returns SCE_IME_ERROR_NOT_OPENED and leaves the output untouched,
like sceImeSetCaret, sceImeSetText and sceImeSetTextGeometry. As with
sceImeSetCaret, the closed panel is reported before a null output.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
shadPS4 returns success for an object id that was never reserved and the invalid-object error only for a released one; this library returns the error for both.
Co-Authored-By: Claude <noreply@anthropic.com>
Both exports are imported by titles that link libSceAudio3d; without them the loader fails with an unresolved import before the title runs. Following the offline model, object attributes are validated but produce no audio and PortFlush drops the frames queued by sceAudio3dPortAdvance.
Pan follows the PS4 firmware 11.00 libSceNgs2.sprx: the speaker layout
from the init angles, then each param's angle and distance into a
volume matrix for the voice. Every param is checked before anything is
written, and angles too large to wrap throw. The pan functions are
exported from the existing Voice.cpp export block.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
C17 has no binary prefix: with base 0 or 2, strtol("0b101", &end, base)
reads the subject "0", returns 0 and leaves end at the 'b'. With glibc
2.38 or later, g++ (which defines _GNU_SOURCE) binds std::strtoll,
strtoull, strtoul, strtoimax and strtoumax to the C23 __isoc23_*
functions, which accept 0b/0B: the Linux exports returned 5 and
consumed the whole string. The Windows UCRT already follows C17, so the
same guest call gave different results on the two hosts.
strtol, strtoul, strtoll, strtoull, _Stoull, strtoimax, strtoumax and
_Stoul now return 0 with the end pointer after the '0' when, after
white space and an optional sign, the text starts with 0b or 0B and a
binary digit, in base 0 or 2. Everything else still goes to the host
function. The scanf family's %i has the same glibc behaviour; it is
recorded in TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A port without a matrix sent source channel n to destination channel n, so a
mono voice reached only the left channel of a stereo mix. The PS4 firmware
11.00 libSceNgs2.sprx mixes such ports, and ports whose matrix levels were
never set, through a fixed channel map instead: mono reaches both stereo
channels at -3 dB and the centre of 5.1 and 7.1, stereo folds to mono at
-3 dB per side, and wider sources fold down with 0.5 and -3 dB gains. The map
was measured for every source width into 1, 2, 6 and 8 channels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PPSA22520 routes its title music through port matrix 0 without setting the
matrix levels, so the music rendered silent. A matrix without levels now
mixes like a port without a matrix, source channel n to destination channel
n. The console's initial levels are unknown; the assumption is recorded in
TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD vsyslog logs the complaint with LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID passed as the priority (0x23), not LOG_ERR.
Co-Authored-By: Claude <noreply@anthropic.com>
ps5-opengl (PPSA99005) and ProsperoRadio (PPSA99001) import syslog
from libSceLibcInternal. Messages go to stderr with the priority, and
the format follows FreeBSD's vsyslog: %m expansion, masking of unknown
priority bits, LOG_USER as the default facility, errno preserved.
Assisted by Claude Code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A title's descriptors are taken as never being terminals: a file or
pipe gets ENOTTY with the termios untouched, a socket gets what its
protocol answers a termios ioctl on FreeBSD (EADDRNOTAVAIL for AF_INET,
EOPNOTSUPP otherwise), and a closed or invalid descriptor EBADF.
tcsetattr gives EINVAL for an action other than TCSANOW, TCSADRAIN or
TCSAFLUSH, with or without TCSASOFT, before anything else, and EFAULT
for a null termios before the descriptor is checked; with TCSASOFT a
null termios throws, where FreeBSD's libc faults copying it. What the
console gives a title as descriptors 0 to 2 is unknown, as recorded in
TechnicalDebt.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
LibwuIonIBw registers video out event id 16, which the host never triggers because its VRR state does not change; kP2L8t3j-aM returns 0 without effect.
Keeps this branch's CONTEXT_FULL | CONTEXT_SEGMENTS read, context-first
order and bounded retry in RaiseOn, and adds #1478's ymm save fields to
both deliveries. The test keeps both phase sets; the vectors phase runs
after the continuing, selfing and looping phases and counts their
deliveries. The TechnicalDebt entry about ymm upper halves is dropped,
since #1478 preserves them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Titles that link the release module libScePsml.prx failed to start
because no host library had that name; PPSA25344 links it. libScePsml
and libScePsml_debug are now built from the same source.
scePsmlMfsrGetDispatchMfsrPacket1000, which PPSA25344 imports, is
added without an implementation.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The guest process is assumed to run as root. Under FreeBSD's
sys_seteuid and sys_setegid, id 0 is then accepted without a privilege
check and changes no credential, so both return 0. Any other id would
change the effective credential, which is not modelled, and throws.
Part of #1578.
sceNpEAAccessInitialize and sceNpEAAccessTerminate resolve the NIDs
7CxI50-xlCk and pMxXhNozUX8, which shadPS4 names in np_partner.cpp. The
stubs still throw; only the names and no-argument signatures change.
Use INFINITY and NAN from <cmath> instead of a new <limits> include, and
list the TechnicalDebt entry beside the other NGS2 voice entries, so the
change merges cleanly with the pan, channel map and reverb changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The template allocated a fixed 4 KiB of LDS, so kernels with immediate
offsets past it could not run. --lds (Python lds) sets the group segment
size in bytes, up to 64 KiB, with 4096 as the default.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A reverb_size other than the default was accepted and ignored, and below
about 7.5 kHz an allpass length went negative and wrapped to a 16 GB buffer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parse legacy decimal, octal and hexadecimal forms before host lookup. Preserve FreeBSD shorthand, whitespace and 64-bit accumulation, clear the SDK resolver error on numeric success, and add regression coverage.
winpthreads initializes a PTHREAD_RWLOCK_INITIALIZER lock on its first
use and fails a concurrent first lock with EINVAL. std::shared_timed_mutex
ignores that result in release builds, so two guest threads could both
hold a fresh guest rwlock.
Lock and unlock the mutex once when the guest rwlock is created, before
another thread can reach it, and record the remaining host exposure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
libSceVdecsw is the software H.264 decoder interface PPSA05684 uses for
its movies. Decoder creation, compute queues, memory queries, decode
input and output, AVC picture info, reset and finalize are implemented
with the layouts of shadPS4's vdecsw.h, checked against the title's
movie decoder, and decode through libSceVideodec2. The H.264 stream the
Videodec2 test uses moves to a shared fixture, and a guest test decodes
it through Vdecsw.
Uncharted: Legacy of Thieves Collection (PPSA05684) plays its movies through libSceVdecsw and stopped at sceVdecswQueryComputeMemoryInfo.
llvmpipe builds the high dword of the shader clock with a left shift by
32 (emit_clock in Mesa's lp_bld_nir_soa.c), so it is always 0 and
s_memtime and s_memrealtime wrap every 2^32 ns. A pass of
agc_driver_shader_clock whose two reads straddle a wrap failed with
"memtime: lane N went back from 4294921902 to 3823"; dispatching right
before the boundary fails every lane of that pass.
On llvmpipe compare the low dwords modulo 2^32, which stays correct if
Mesa fixes the shift. Other devices keep the 64-bit comparison.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
PS5 titles pass a 0x230-byte SceAvPlayerInitDataEx: auto start is the
byte after debug_level at 0x74, nine 0x30-byte thread parameter blocks
fill 0x78-0x228 and the output video frame buffer count is at 0x228.
sceAvPlayerInitEx read both fields at their PS4 offsets: auto start at
0xa8 is the video decoder thread priority and the buffer count at 0xa4
lies in the audio decoder block. PPSA25063 (Poppy Playtime: Chapter 4)
sets every thread priority to 700, so its intro video started on its
own and stopped at once; it writes auto start 0 and starts playback
itself.
With this_size 0x230 or more both fields are read at the PS5 offsets;
smaller structures are read as before. The layout follows KytyPS5,
prosper, SharpProspero and sharpemu and is recorded in TechnicalDebt.
guest_avplayer builds a 0x230-byte structure with auto start 0 and
nonzero thread parameters, then with auto start 1, and checks when
playback starts and how many video buffers are allocated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceVideoOutAddPreVblankStartEvent threw "physical pre-vblank timing is
not implemented", so titles that pace on it stopped at startup. It now
registers the event like the vblank event does, and each emulated vblank
tick triggers the pre-vblank start events just before the vblank events,
with the pre-vblank count as data. The console's lead time is unknown.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v_fma_f16, v_fmac_f16, v_fmamk_f16, v_fmaak_f16, v_pk_fma_f16 and
v_pk_fmac_f16 rounded an f32 fused multiply-add to f16, rounding twice.
The product of two f16 values is exact in f32, so the sum is now rounded
to odd in f32 with an exact error term and then rounded to nearest even
in f16, which rounds the exact result once.
sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats and
sceNetGetMemoryPoolStats threw. PPSA16064 (Minecraft Dungeons II,
Unreal Engine) queries all three while its HTTP module starts, and
aborted there.
Each library now records the pool size given at sceHttp2Init,
sceSslInit or sceNetPoolCreate and reports it with a peak and current
use of 0: the offline Http2 and Ssl models and the host-socket Net
library allocate nothing from these pools. The layouts follow the PS4
SDK's *MemoryPoolStats structures. Net returns EBADF for an unknown
pool and EINVAL for a null output, like its other pool calls; Http2 and
Ssl throw for both, as their error codes are unknown. Terminating a
context or destroying a pool forgets its size.
sceHttp2SetResolveRetry, which the title calls next, accepts and
ignores its value like the other Http2 settings. The four Http2
WebSocket exports the title imports are added as unimplemented.
guest_http2, guest_ssl and guest_sce_net check the reported sizes;
guest_sce_net also checks the null-output and unknown-pool errors.
A draw with DB_RENDER_CONTROL.RESUMMARIZE_ENABLE recomputes the HTILE summary of the bound depth surface from its depth values. HTILE is not modeled, so such a draw with depth and stencil tests off and no color writes changes no modeled state. It now passes the maintenance check and draws nothing, instead of throwing. Resummarize draws with depth, stencil or color work, and copy and decompress draws, still throw.
The unified and service entitlement info list requests and polls threw.
They now return SCE_NP_ERROR_SIGNED_OUT, like the consume requests in
the same library. Their signatures are unknown, so they stay declared
without parameters.
PPSA15657 calls the unified request during start-up and continues past
the error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0134RWCyiyydJBbfKz8g9Bwe
Sackboy: A Big Adventure (PPSA01289) imports nine functions that no library
exports, so eager binding fails before it starts.
- libSceVoiceChat: sceVoiceChatRequestLeaveVoiceChatGroup and
sceVoiceChatRequestDeleteVoiceChatGroup, offline requests like the other
sceVoiceChatRequest* calls
- libSceNpSessionSignaling: sceNpSessionSignalingGetConnectionFromPeerAddress,
a throwing stub; the signature is unknown
- libSceCdlgG2P, a new library: the six sceG2PDialog* functions, throwing
stubs declared without parameters
TechnicalDebt records the assumed and unknown signatures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The param is void*, as RommPS declares it. The test also passes a null and an empty URI and a param that must stay untouched, and compares against 0x8002002D itself. The debt entries now say the error is returned for any arguments and that the param layout and the console's argument checks are unknown.
PPSA01289 (Sackboy: A Big Adventure) calls sceAudioOut2MasteringTerm when it
replaces its world, which threw. Like Init and SetParam it has no effect, as
there is no mastering stage. The title creates its next world's audio device
before it destroys the previous one, so Init runs twice before Term runs
twice; whether the console reports an error for either is unknown, so both
return 0 and the case is recorded in TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceSystemServiceLaunchWebBrowser fails with the kernel's EOPNOTSUPP: there is no web browser to open. RommPS PPSA76677 stops on main at this import and shows a QR code instead when the browser does not open.
A label in memory the device has no host import of was stored when its
batch was reaped, after later labels the GPU had already made visible.
A title that reused that memory saw its next command buffer overwritten
by the stale value (#1458). Wait for the recorded work and store it then.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With f32 denormals flushed, as in the 0xc0 float mode titles use, the
hardware flushes the denominator and numerator of v_div_fixup_f32 but
keeps a denormal quotient and result. The recompiler kept all three,
so a denormal divisor gave the quotient back instead of an infinity.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With IEEE=1, v_pk_min_f16 and v_pk_max_f16 return a quieted NaN for a
signaling NaN operand, and DX10_CLAMP=1 clamps it to +0. The clamped
form skipped the bit-level NaN selection and saturated the driver's
min/max, which returns the other operand, so it gave that operand.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
sceSystemServiceKillApp now ends the title as guest exit() does, so driver calls that wait for the exit park instead of throwing ProcessShutdown, and a second exit waits for the first. The test now fails when the kill returns, exits with a status other than 0 or bypasses the coordinator, and checks why a rejected call throws.
Report an app ID for the running title from sceSystemServiceGetAppIdOfRunningBigApp, and shut the guest down and exit when sceSystemServiceKillApp receives that ID with the arguments -1, 0 and 0. SwanStationPS5 (PPSA98510) 1.0.6 closes itself this way and stops on both imports on main.
The hardware findings give 0xF_F000_0000, which is 0xFF0000000, not 0xFF00000000. Check the ring across threads, at ps5-opengl's size of 0x1b000 and with a canary after the outputs, and say in the debt entry that nothing is written to the ring.
Record the tessellation factor ring that sceAgcDriverSetTFRing receives and report it from sceAgcDriverGetTFRing, with the console's default of 0xFF00000000 and 128 KiB before a title sets one. VITA5 (PPSA39410) and Homebrew UI Lab (PPSA99050) stop on this import once sceVideoOutGetResolutionStatus resolves.
EVO-PLAYER (PPSA99039) creates resident AVC, HEVC and VP9 decoders at
start-up, and stopped at "Videodec2: unsupported codec" on the HEVC one.
Codec types 974921 (HEVC) and 2382845 (VP9) now open FFmpeg's HEVC and
VP9 decoders. HEVC input goes through the same Annex B conversion as
H.264 and VP9 frames are passed through; output stays 8-bit NV12, and a
frame deeper than 8 bits throws. Picture info throws for HEVC and VP9,
whose layouts differ from the AVC one.
Assisted by Claude Code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwFnKjMxmwGDWiuYYf1v4D
Capturing a shader evaluates every V#, T# and S# the program uses,
including those behind a branch the wave does not take. When such a
descriptor was loaded through a pointer read from memory that is null
or unmapped, the capture threw "guest memory is not readable", while
the console faults only if a wave executes that s_load. The descriptor
now reads as zero words, and the flat SRT walk records the s_load's
dwords as it does for flat reads, so their guarded ReadConst raises
the BDA fault where the wave runs the s_load. A zero V# is an absent
buffer and a zero T# binds a null descriptor, as for any zero word
read from memory, so nothing new enters a key. A zeroed descriptor
whose read no flat slot records still throws, as do branch conditions,
uniform fills, table bases and user-data pointers.
Capturing a shader threw "null or misaligned address" when the flat
SRT walk read through a pointer loaded from memory that is null or
unmapped, even when the program skips that s_load. Such a read now
yields zero and a record with its pc and address, as does a slot whose
address needs it. The registered artifact checks one flag word per
guarded slot at each ReadConst; the capture appends the flags and the
records to the flattened SRT, and a set flag raises an Unmapped BDA
fault through the fault buffer and stops the invocation as a BDA read
does. Nothing per capture enters a key: poisoned and clean captures
share one variant and specialization. Descriptors, branch conditions,
uniform fills, table bases and user-data pointers that need such a
value still throw, and poisoned results stay out of the dispatch and
draw caches. The shader disk cache format becomes 21.
Measured on RDNA2 in #697: an invalid product or sum (inf * 0, inf - inf)
gives the negative default NaN, 0xffc00000 or 0xfe00, and beats a NaN
addend. A NaN delta, then a NaN coordinate, still wins, quieted; a NaN
addend is returned quieted only when the product is valid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on RDNA2 in #697: v_interp_p1ll_f16 and v_interp_p1lv_f16 are a
fused multiply-add rounded once to f32, and v_interp_p2_f16 rounds the exact
P20 * J + S2 once to f16. The separate multiply and add matched about 85% and
4% of the measured rows.
Both steps now use FPInterpolateF32 and FPInterpolateF16: the f16 delta times
the f32 coordinate is exact in f64, the sum is rounded to odd and then once to
nearest even at the target precision, so no driver can split or reround it.
Infinite and NaN operands take the unbounded f64 result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decode the three VOP3 16-bit interpolation instructions (attribute, channel
and high-half select in the src0 field, the I/J VGPR in src1, P0 or the first
step's result in src2) and translate them with a new
GetInterpolationParameterF16 IR value. It reads the selected 16-bit half of
each vertex's parameter through VK_KHR_fragment_shader_barycentric, so P10 and
P20 are vertex differences rounded to f16; both steps are a multiply and an
add, as in the f32 forms.
An input in FP16_INTERP_MODE whose low half is defaulted is still declared at
its slot when its high half is read, and the high half takes its default from
USE_DEFAULT_ATTR1 and DEFAULT_VAL_ATTR1. Without barycentrics, on an input
without FP16_INTERP_MODE or on a pass-through input the instructions throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The decoder threw on the GFX10.1 DS src2 opcodes 0x80-0x8b, 0x8d,
0x92, 0x93 and 0x95. Each reads DS[B], where B is the address plus
four times the 15-bit signed offset in offset1[6:0]:offset0, and
applies the matching operation to DS[A]: add, sub, rsub, inc, dec,
signed and unsigned min and max, and, or, xor, f32 min, max and add
through the 32-bit LDS atomics, and a plain write for
ds_write_src2_b32. The form that takes the offset from the address
bits (offset1[7]), the 64-bit forms and GDS still throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on a PS5 (firmware 13.60) through dlopen of libSceAudioOut.sprx:
a MAIN (type 0) port accepts 1, 2, 8 or 12 channels (bits 8-11) with
sample type 0 or 1 (bits 0-6). Bit 7 without 8 channels returns
0x80268001, any other unsupported format returns 0x8026800E, and both
write UINT64_MAX to the output handle. Bits 12-31 do not affect the
result. Other port types keep their current validation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AF_UNIX pairs as FreeBSD 11 makes them: SOCK_STREAM, SOCK_DGRAM and
SOCK_SEQPACKET with protocol 0, SOCK_NONBLOCK applied to both ends and
SOCK_CLOEXEC accepted, EAFNOSUPPORT for an unknown family, EOPNOTSUPP
for AF_INET and AF_INET6, EPROTONOSUPPORT for type 0 or a protocol,
EPROTOTYPE for another type, EFAULT for a null output, and nothing
written on failure. The ends are guest sockets, so send, recv,
shutdown, fcntl, poll and close work on them; getsockname and
getpeername report the unnamed AF_UNIX address, recvfrom and recvmsg
no address for a stream and the unnamed one for a datagram. On Linux
the pair is the host's socketpair; on Windows a loopback TCP connection
or two connected UDP sockets, and SOCK_SEQPACKET throws. The remaining
differences from the console are recorded in TechnicalDebt.
OpenSSL's QUIC notifier in 15 catalog titles creates such a pair.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Returns the name of the directory under which a title's sandbox exposes
system files, which homebrew SDKs splice into paths such as
/<name>/common/cert/CA_LIST.cer and /<name>/common/lib/libc.sprx. The
name is fixed here; recorded in TechnicalDebt together with the fact
that nothing is mounted under it.
Registers guest_sandbox_random_word.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
It returned the network error, which titles that poll their epoll treat
as fatal. No request is ever pending, since sending fails at once, so the
wait now times out with no events; an infinite wait throws.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With stride 0, OOB_SELECT 2 puts a fetch out of range only when NUM_RECORDS
is 0, and OOB_SELECT 3 only when the offset is not below NUM_RECORDS (GFX10
table in Mesa ac_descriptors.c). A one-record descriptor therefore returns
its whole element: Unreal's per-draw PrimitiveId stream is such a descriptor,
and refusing it skipped the draw. A wholly out-of-range fetch reads a zero
buffer.
PPSA13427 imports sceHttpSetAuthInfoCallback (jf4TB2nUO40), which no
library exported, so the relinked title failed to load. No request
reaches the network, so no server asks for authentication: the
callback is accepted and never called. The signature and callback
type follow shadPS4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
Decodes IMAGE_GATHER4H_PCK (MIMG opcode 98) and IMAGE_GATHER8H_PCK
(opcode 99), implements gather4h_pck through translation, the SPIR-V
backend and a GPU execution test, and documents gather8h_pck as a
desktop null-op.
Semantics were measured on gfx1031 hardware with hand-encoded MIMG
words (LLVM rejects the PCK gather mnemonics on every RDNA1/RDNA2
target). The measured rule for gather4h_pck: the four-texel horizontal
window is floor(u*width-0.5)+{-1,0,+1,+2} clamped to the width, the row
is floor(v*height) clamped, the raw bits of the window texels are
concatenated into a little-endian bitstream (texel k at bit offset
k*texelBits), and output register j - the j-th set DMASK bit in
ascending order - receives stream word j, zero past the end of the
stream. Exactly popcount(dmask) consecutive VGPRs are written from vdst
ascending; texels wider than 32 bits write zeros (hardware limit); rows
past the height read zeros; negative coordinates clamp to zero. The
window anchor matches the repo's existing PS5-validated HorizontalOffsets
rule, so this reuses the established convention.
The decoder accepts multi-bit DMASKs for these two opcodes only and
sizes their data span as popcount(dmask) (measured), unlike gather4h
which always writes four components. Translation carries the packed
flag through imageMemoryInfoFromInstruction; the SPIR-V emitter
reassembles the bitstream per lane with per-texel OpImageFetch and
byte assembly. D16 data throws until measured.
gather8h_pck never returns texel data on this hardware: across dmasks
0x1-0xFF including the upper nibble, RGBA8/RGBA16/RGBA32 formats, 1D/2D
and 3D descriptors, normalized and unnormalized sampling, sentinel-
prefilled destination registers confirm it only ever zero-fills
popcount(dmask) registers. It decodes but throws at translation.
The measured semantics and the gather8h_pck null-op are recorded in
docs/dev/TechnicalDebt.md.
Reject tuple and descriptor overlap, wrong-pair returns, relative SGPR reads, reversed regions and ordinary or conditional fall-through into a callee. Preserve the existing scalar write analysis and require exact return-pair matching.
Seven targeted regressions fail before these guards and pass afterward. Seven focused CMake tests and 15 SPIR-V validations pass; the execution matrix passes 3,200 comparisons and detects a one-bit expected-output mutation.
Hades (PPSA03355) stops at load on main with "undefined symbol:
9HzfhdtESio", the NID of pthread_getname_np, which it calls on
pthread_self() with a 32-byte buffer. Measured on a PS5 (firmware 13.60)
through dlopen of libkernel: the function takes (thread, buffer), copies
the name and its terminator, returns an empty name for an unnamed
thread, ESRCH for a null or joined thread and EFAULT for a null buffer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reuse the prerequisite call-region analysis for signed relative calls, including callees after s_endpgm. Lower the saved link as shader base plus PC+4 and reject unsupported targets and call shapes.
Add decoder, IR, CFG and Vulkan execution regressions. Seven focused CMake tests pass; five layouts cover 15 dispatches and 3,200 output comparisons on lavapipe. Record ISA sources and native RDNA2/PS5 limits in TechnicalDebt.
Decodes SOP1 0x21 s_swappc_b64 (link pair = pc + 4) and resolves its call/return pairing through the existing s_setpc_b64 path: static call regions in GraphBuilder and the vertex-entry fetch-shader call inlined from the parsed fetch-shader plan. s_call_b64 (SOPK 0x16) and s_rfe_b64 (SOP1 0x22) throw at decode. Unmodeled cases throw; see docs/dev/TechnicalDebt.md.
The PS4 firmware 11.00 libSceNgs2.sprx stores matrix levels clamped to
-4..4, keeps NaN, and rejects a level count of 0. Levels were stored
unclamped, so a level of 8 played twice as loud as on the console, and a
count of 0 cleared the matrix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three homebrew titles from #1578 import it and stop at start-up without
it: ps5-opengl (PPSA99005), EVO-PLAYER-PS5 (PPSA99039) and
ProsperoRadio (PPSA99001).
It reports the port's resolution as the full and pane size and the
refresh rate ID as sceVideoOutGetOutputStatus does. The 48-byte layout
follows the declarations in ps5-opengl and EVO-PLAYER-PS5, which agree;
the screen size is 0 and the flags are 0. An invalid handle or a null
status throws, as in the other status getters.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SPI_SHADER_COL_FORMAT 7 (UINT16_ABGR) was rejected by DecodeState,
though the recompiler already handles it: a pixel shader's MRT output
under mode 7 is a uvec4, and a compressed export's 16-bit halves are
taken with OpBitFieldUExtract. The format is now accepted when the
target's CB_COLOR_INFO number type is UINT; into any other target it
still throws, as does SINT16_ABGR (8), whose halves would need sign
extension.
The target records the export (ColorTarget::uintExport). Shader
validation then requires a uint4 output at its location instead of a
float4 one, and blending into it is refused (Vulkan has no blending of
integer attachments).
PPSA14632 (LEGO Horizon Adventures) writes a 2848x1600 R8_UINT target
this way; those draws were dropped.
agc_driver_pixel_uint16_export draws compressed MRT0 exports under mode
7 into a linear 16_16_16_16 UINT target, one row per pair of packed
words (0x0000ffff/0xffff0000, 0x00010002/0xffffffff and four distinct
halves), and reads every 16-bit component back under the identity,
STD_REV and ALT_REV export mappings. agc_driver_graphics covers the
decode and the blend refusal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When another live image overlaps the fill, a fill at a live image's dccAddress is a key fill
only while it stays within DccKeyCount (the pipe-aligned key count); with nothing overlapping
the length rule is unchanged. ClassifyFill, NoteKeysFill and ClearByKeysFill share the match.
agc_driver_fill_cover_keys pins Keys, Inside and Several for fills at and beside the keys.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A raw 5.1 LC frame decodes as 6 channels with a maximum of 6 and of 8,
which is only reached by moving past the mono/stereo configuration. The
configuration search is listed under unknown function info, together with
a mono raw stream being reported as 2 channels.
Co-authored-by: Dextroid17 <217507631+Dextroid17@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The retry that waits for a target to leave NtContinue, NtContinueEx or
libwinpthread-1.dll had no bound, so a target that stayed there made
sceKernelRaiseException spin forever. It now throws after 1 s. A new
guest_raise_exception phase keeps a thread inside NtContinue by
continuing a context that calls NtContinue again, and checks that the
raise throws and that the handler did not run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The console's DCC of a single-level 4-byte SW_64KB_R_X surface is
ceil(W/512) * ceil(H/512) * 4096 bytes, 49152 for 1920x1080, but the
driver stored and read one key per 256 surface bytes (34560).
PPSA26344 retiles its render DCC into the displayable copy it
registers (cs_dcc_retile_32bpp); a retile of the stale tail gives the
mixed keys PPSA12544 stops on.
DccKeyCount gives that extent. Color target and storage image key
stores cover it when the DCC is pipe-aligned (CB_COLOR_ATTRIB3 bit 30,
T# word 6 bit 19), whose CB keys reach its last meta block, and throw
where it is not writable; unaligned DCC keeps one key per 256 bytes,
as do texture and color target key reads. DisplayBufferKeys reads the
display DCC over the extent, presents only when every key agrees,
names the first differing key's pixels and checks a pending image
against those keys.
On GFX10.3 to GFX11.7, ACO lowers a subgroup-scope OpReadClockKHR to
s_getreg_b32 of SHADER_CYCLES, 20 bits zero-extended to 64, so s_memtime
wrapped every 2^20 cycles and went backwards. Detect those families from
the RADV device name and read the device clock there instead.
Fixes#985
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve ASCII case differences in guest paths and mount aliases while preferring exact names and rejecting ambiguous fallback matches. Preserve new filenames and host roots. Five filesystem tests pass; Skyrim now loads its INI and shader archives, initializes Vulkan and uploads intro textures instead of dereferencing null shader metadata. Written with AI assistance.
A draw whose PA_SC_CONSERVATIVE_RASTERIZATION_CNTL (context 0x313) was
not the default 0x6000 threw in DecodeState and the draw precheck.
0x6001 sets OVER_RAST_ENABLE and now decodes to Vulkan's overestimation
mode, chained to the pipeline's rasterization state and its key.
The device enables VK_EXT_conservative_rasterization only when its
primitiveOverestimationSize is at most 1/256 pixel and it rasterizes
degenerate triangles; elsewhere the pipeline throws. Overestimation is
accepted only when triangles reach the rasterizer: VGT_GS_OUT_PRIM_TYPE
behind a geometry shader, VGT_TF_PARAM's output topology behind
tessellation, VGT_PRIMITIVE_TYPE otherwise. Other 0x313 values and
centroid interpolation still throw.
Adds the agc_driver_conservative_rasterization GPU test and extends
agc_driver_graphics.
Skip CCP and Simplification for storage-only narrow types while keeping
the remaining passes and arithmetic-capable folding.
Add compiler and Vulkan readback regressions for default/full/none modes.
Tested: Linux/llvmpipe, full CTest with SPIRV-Tools on/off, ASan/UBSan
optimizer checks, and cold-cache Vulkan wave32/wave64 readback.
Fixesboykopovar/AnyPS5#1276
Invoke the title callback on planar mixed samples and silence. Preserve per-voice callback data, reset registration on setup, and release the render guard on callback failure. Include behavioral tests and source references. Written with AI assistance.
A V# whose base is not a multiple of 4 bytes from the start of its GPU
owner threw in GuestBufferMemory::Descriptor, because the shader added
the offset below the view only in whole dwords.
Descriptor now binds from the offset alignment below the view and
passes the whole remainder. Such a view's range covers the dwords
around it, and its descriptor region starts and ends on dword
boundaries, so a lone view copied to its own buffer binds the same way.
The buffer specialization records the V# base's low two bits. Raw
vector loads through such a V# join two dwords, glc dwordx2 loads
included, stores merge their bytes into both with compare-exchange
loops, and bounds checks include the second dword. s_buffer_load
ignores the low two bits, as SrtEvaluator does. Formatted accesses
whose element alignment (min(element bytes, 4)) the base breaks throw,
as they fault the wave on RDNA2, and so do atomics.
Preserve files across unmount, block new lookups until remount, and validate mount-dependent operations. Keep prior-run storage inaccessible after initialization.
Written with AI assistance.
Translate the FreeBSD pollfd layout and flags to WSAPoll on Windows and
poll on Linux. Closed socket descriptors report POLLNVAL, a timeout below
-1 fails with EINVAL and a null array with EFAULT. Non-socket
descriptors, unknown event bits, POLLPRI on Windows and an infinite wait
without sockets throw; these are recorded in TechnicalDebt.
DOOM (PPSA99666) imports poll for its upload server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GFX10 lowering of a wave64 reduction scans rows with DPP row_shr
1/2/4/8 and v_permlanex16_b32 under EXEC -1, then joins lanes 31 and 63
with a scalar op. The console fills lanes outside the entry EXEC with
the identity, but on the host they can be helpers that sit out subgroup
operations or invocations that were never launched: on 64 lanes (RADV)
the emulated chain reads 0 or stale values from them, and on 32 lanes
lane 63 throws since #997.
EmitReadLane now recognises the sequence on any host for UMin, UMax,
SMin, SMax, IAdd, AND and OR when the keys are the identity outside the
entry EXEC. On 64 lanes, lanes 31 and 63 become subgroup reductions of
the keys of invocations 0-31 and 32-63; on 32 lanes or fewer, lane 31
reduces all keys and lane 63 is the identity. It throws without
subgroup arithmetic or on a subgroup wider than the wave. VulkanDevice
reports GroupNonUniformArithmetic and ShaderValidation checks it.
#1398 and #1399 both rewrite the sceRtcParseDateTime entry that the s_sethalt entry followed. It now follows the libSceAudio3d entries, which no open pull request edits, so either can merge first without a conflict.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1372 adds its test after the vop1 decode tests and #1314 its debt entry after s_setkill, the places where this branch added its own. The wave control decode test now precedes the vop1 decode tests and the s_sethalt entry follows sceRtcParseDateTime, so either pull request can merge first without a conflict.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
s_sethalt with SIMM16[0] clear is a no-op; s_sethalt 1, s_sendmsghalt, a reached s_code_end, s_rfe_b64, the ds_gws_* instructions and ds_ordered_count throw with the reason at translation. Linear decoding stops at s_code_end like at s_endpgm, so code padded with s_code_end after a final backward branch decodes. Encodings from LLVM SOPInstructions.td and DSInstructions.td, checked against the gfx10 MC tests; semantics from the RDNA 2 ISA guide.
DecodeState and the draw precheck rejected DB_SHADER_CONTROL bit 16,
PRIMITIVE_ORDERED_PIXEL_SHADER (POPS), which runs the ordered section
of overlapping pixels' shaders in primitive order. The bit now reaches
the recompiler as a pixel stage field, and such a pixel shader declares
PixelInterlockOrderedEXT and runs its whole body between
OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT; an
invocation that a BDA read fault kills ends the interlock first. The
POPS collision SGPRs read 0, so a title skips its POPS wait and runs
its per-collision loop once. An image_load still reads a sampled
binding, which misses an earlier overlapping primitive's image_store in
the same draw. The device enables VK_EXT_fragment_shader_interlock when
it has fragmentShaderPixelInterlock; without it the recompile throws,
as does a nonzero POPS_OVERLAP_NUM_SAMPLES. The field is part of the
recompile cache key, the draw key and the serialized request (version 10).
SHA-1 and SHA-256 instructions with a [rip+disp32] operand, and
instructions with one that a short site absorbs, now move into the
stub. The stub records each displacement as a relocation relative to
the site, and the Linux, guest and Windows placements resolve it once
the stub address is known. Indirect branches through [rip+disp32]
still fail the relink.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use the shared atomic bounds check instead of wrapping at 64 KiB.
Expect zero returns and unchanged LDS for aligned out-of-range accesses,
and record the gfx1035 hardware measurements.
No request reaches the network, so sceHttpsSetSslVersion has no TLS
connection to configure, sceHttpSetRedirectCallback and
sceHttpSetCookieRecvCallback register callbacks that are never called,
and sceHttpsGetSslError reports error 0 with detail 0. A null errNum or
detail returns SCE_HTTP_ERROR_INVALID_VALUE like sceHttpGetLastErrno.
Signatures follow shadPS4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A condition wait on a recursive mutex locked more than once unlocked the
native mutex only once, so the thread that should signal blocked forever
on it. Unlock the remaining levels before the wait and lock them again
after it, as FreeBSD libthr does in _mutex_cv_unlock and _mutex_cv_lock.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
A thread that sceKernelRaiseException redirects resumes through
RtlRestoreContext with a CONTEXT that has no XSTATE, so the upper halves
of ymm0-ymm15 came back as the handler and the delivery path left them.
A handler or host routine that runs vzeroupper or any VEX instruction
(Zen 2 guest code, the CRT's memcpy) cleared the upper lanes of a thread
interrupted in the middle of AVX code. The entry stub now stores
ymm0-ymm15 next to the delivery when the CPU and the OS enable AVX, and
the exit loads them back right before RtlRestoreContext, which restores
only the low halves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
sceKernelGetCurrentCpu returned the raw host CPU index. Guest affinities
are bookkeeping only and sceKernelGetAvailableCpumask advertises CPUs 0
to 12, so on a host with more than 13 logical CPUs a title could get 13
or higher, and a thread pinned to one CPU could get another one.
The host index now selects a CPU of the calling thread's affinity
clipped to the available mask, so the result is always an advertised
CPU and a thread pinned to one CPU reports that CPU.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
Opening a volume needs administrator rights, so the volume flush skipped
every volume in a normal user session. The libc, libkernel, save data and
app content file functions now record each host file and directory they
change. On Windows sceKernelSync flushes the recorded paths that changed
since the previous call, plus their changed parent directories, through
handles a normal user can open. Linux still calls sync().
wcstod, wcstof, wcstold, wcstol, wcstoll, wcstoul, wcstoull, wcscoll and wcsxfrm still forwarded to the host's wchar_t functions, which use 32-bit units on Linux. The wcsto* functions now parse the ASCII prefix of the guest string with the narrow parsers, as the C locale accepts only ASCII white space and digits. wcscoll compares like wcscmp and wcsxfrm copies, as strcoll and strxfrm do in the C locale.
BnMAMrsfVWo is _ZSt19_Throw_bad_weak_ptrv. Replace the unknown stub
with a throw of a guest std::bad_weak_ptr, and add its type info,
vtable, destructors and what().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Treat equal BASE_ARRAY and DEPTH as a six-face cube. Measured with image_load, image_sample and image_get_resinfo on RX 6600 gfx1032 using base slices 0, 5 and 6. Partial cube ranges still throw because filtered sampling differs from load bounds.
Graphics tests and GPU snapshot/storage sampling tests pass for all six faces at each tested base.
Assisted-by: OpenAI Codex
sceKernelReadv, sceKernelWritev, sceKernelPreadv and sceKernelPwritev threw on Windows. They now gather or scatter through one host buffer and issue a single read or write, with the same argument checks and error codes as the Linux path. Positioned transfers restore the file offset afterwards, as pread and pwrite do on POSIX, and report ESPIPE for handles that are not disk files. guest_file_vector now runs on Windows as well.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
COPY_DATA rejected source select 9 (GPU clock count) and any cache
policy bits. PPSA16064 (Minecraft Dungeons II, Unreal Engine)
calibrates its GPU timer at start-up with a submission of five
COPY_DATA packets, control 0x06016209: GPU clock source, memory
destination, 64-bit count, source and destination cache policy 3. The
submission was rejected and the title aborted.
The clock source now writes the same counter as a RELEASE_MEM
timestamp (the host steady clock in 10 ns ticks), read when the packet
runs, through the direct execute path and through ResolveStore for
stores recorded on the GPU. A 32-bit count writes its low half. The
source and destination cache policy fields are accepted and ignored,
as for WRITE_DATA and DMA_DATA. The reference clock (source select 10)
and the other unimplemented sources still throw.
agc_driver_pm4 runs the title's packet twice and checks a nonzero,
non-decreasing counter and an 8-byte resolved store, and that source
select 10 is still rejected.
MinGW's thread_local goes through emutls and winpthreads' pthread_getspecific, which holds a per-thread spinlock. A raise that redirected a thread holding it ran the handler on that same thread, and a handler reading any thread_local, scePthreadSelf for one, spun on the lock forever. A raise that finds the target inside libwinpthread-1.dll now resumes it and suspends it again, as one inside NtContinue does.
guest_raise_exception's new phase raises at a thread that calls scePthreadSelf in a loop and checks that the handler sees its own thread. It hung in every run without the retry. TechnicalDebt records that a handler can still reenter other host code the thread was interrupted in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guest's wchar_t is 16 bits, but wcslen, wcscmp, wcsncmp, wcscpy, wcsncpy, wcschr, wcsrchr, wcsstr, wcspbrk, wcsspn and the wmem functions forwarded to the host's wchar_t functions, which use 32-bit units on Linux. Implement them on char16_t. The wide conversions that still use the host's wchar_t are listed in TechnicalDebt.
NVIDIA (595.91.07) refuses VK_EXT_external_memory_host imports of
read-only or executable mappings with VK_ERROR_OUT_OF_DEVICE_MEMORY and
then fails every later vkAllocateMemory on the device. Check the range
is mapped read-write before importing on Linux, as the Windows path
already does for read-only memory, and copy it otherwise.
Refs #1022
A CMASK holds 4 KiB per 1024x512-pixel metablock, not one nibble per 8x8
tile of the target: 1920x1080 spans 0x6000 bytes, as titles clear it,
not 16200. The swizzled nibbles of the lower-right tiles lie past the
packed size, so the fast-clear check missed them and the expanded store
left them cleared.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Detach the device cache buffer attached by sceFontAttachDeviceCacheBuffer. The signature is taken from the IDA type database of Orbital: it takes the library, a pointer to receive the buffer and a pointer to receive its size. When no cache buffer is attached, it reports NOT_ATTACHED_CACHE_BUFFER. If the library allocated the buffer itself and no buffer pointer is passed, the memory is freed. The owned bit is cleared and device_cache_buf is reset to null.
The driver copies a submission's packets when it is submitted, but
SET_SH_REG_INDIRECT, SET_UCONFIG_REG_INDIRECT and SET_CONTEXT_REG_INDIRECT
read their (offset, value) lists from guest memory only when the queue
worker reaches the packet. A title that reuses its per-frame memory once
it expects the GPU to be done with it can rewrite a list in between, and
the worker then throws on the data it finds there ("extended register
semantics are not implemented (offset dword 0x3a888889)").
The lists are now read with the packets, after validation, in Submit and
for a rewind tail, and the worker writes the registers from that copy.
Pm4::Execute keeps reading the list itself for direct callers.
PPSA21402 (Balatro) keeps three frame slots and does not wait for the GPU
before reusing one: its slot labels start at 0xffffffff and the GPU only
ever writes 0xffffffff to them, so the poll before a slot is reused never
blocks. When the worker falls more than two frames behind, the title
writes per-draw constants over a SET_CONTEXT_REG_INDIRECT list the worker
has not read yet.
The console's PFP reads the list when it reaches the packet; the
difference is recorded in TechnicalDebt.md.
guest_formatting_tests was built on Linux but registered with ctest on
Windows only, so the Linux printf and scanf exports had no coverage: on main
the test fails there at its first vsnprintf check and then crashes in
snprintf with eight integers.
It is registered on every platform now. The %ls checks stay Windows-only,
as glibc reads %ls as 32-bit wchar_t; that gap is recorded in TechnicalDebt.
A color target with both DCC and CB_COLOR_INFO.FAST_CLEAR keeps its fast
clear in the DCC keys and fills its CMASK with 0xF (expanded), as radv
does (radv_get_cmask_fast_clear_value) and as PPSA21567 does
(CB_COLOR_INFO 0x10042730, reported on #419). Such targets now decode;
their clear is materialized by the existing DCC path. A DCC target's
CMASK that is not all expanded throws.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Draws into a color target with CB_COLOR_INFO FAST_CLEAR (bit 13) were
rejected. The CMASK keeps one nibble per 8x8 tile: 0 means the tile is
fast-cleared and reads as CB_COLOR_CLEAR_WORD, 0xF means expanded (the
texels hold the color). The texture units and display scanout do not read
it, so a title runs a fast-clear eliminate pass before sampling or
flipping such a target.
DecodeColorBuffer now takes the CMASK address (CB_COLOR0_CMASK and
CB_COLOR0_CMASK_BASE_EXT) and the bytes one nibble per 8x8 tile spans.
Before a draw renders into the target, and in a fast-clear eliminate
pass, a CMASK whose nibbles are all 0 is materialized: the target is
filled with the clear texel (FillClear on its resident image, else
guest memory on the CPU) and the CMASK bytes are stored as expanded,
through the same GPU-ordered store as the DCC "uncompressed" keys
(0xFF either way). The result equals the draw followed by the eliminate
pass. An all-expanded CMASK changes nothing; a mixed one throws, as do
fast clears of DCC, mipmapped, 3D and array targets.
PPSA14632 (LEGO Horizon Adventures) fast-clears its two 3840x2160
display buffers through CMASK, so its final composite draw was dropped
on every frame and only black was presented.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Read the module layout from the PE image that holds the address. A
relinked image reports its .elf sections as segments and the
eh_frame_hdr recorded in .ehmeta; any other image reports its sections
with memory access and its .ehfram section. The eh_frame_hdr and
eh_frame helpers are shared with the Linux path, and the guest module
info test now also runs on Windows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A 16-bit PCM block whose num_samples exceed its data starts a stream, and waveform blocks with flag 0x2 append more data to it. The voice outputs silence while it has played all appended data and CONTINUE is set, and ends at num_samples or when CONTINUE is cleared.
A raised exception redirects the target thread to a stub that runs the handler and returns through RtlRestoreContext. On Windows 10 (19045) RtlRestoreContext restores in user mode only when the context flags are exactly CONTEXT_FULL | CONTEXT_SEGMENTS (CONTEXT_XSTATE aside); for the CONTEXT_FULL | CONTEXT_FLOATING_POINT context it was given, it calls NtContinue. When the next raise suspended the thread inside that system call, NtContinue then replaced the redirected context with the one it was restoring, so the handler never ran although sceKernelRaiseException had returned 0. guest_raise_exception lost such a delivery to its busy thread in about one run in five and hung until the ctest timeout.
The context is now read with CONTEXT_SEGMENTS, so the handler returns through the user-mode restore, and a raise that finds the thread inside NtContinue or NtContinueEx (an APC or an exception handler returning) resumes it and suspends it again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on a PS5 (firmware 13.60) through dlopen of
libSceVoiceQoS.sprx: a null block, any size other than exactly 0x40000,
and an app type other than 0x20000000 or 0x10000000 all return
0x804E0902 without touching the block, instead of throwing. A second
initialization returns 0x804E0905, as prosper reads from the module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add scePadOpenExt for special ports, which return DEVICE_NOT_CONNECTED because the host has no special controller, and the extended read and report exports, which throw.
The LOD warning lwe adds to the texel status is texture-streaming feedback,
which is not reported for the T# MIN_LOD_WARN either. Accept lwe on sample
and gather instructions as it already is on loads and stores; lwe on
image_get_lod still throws.
Once an image has .ehmeta, an eh_frame_hdr version other than 1, an
eh_frame_ptr encoding other than absptr or pcrel, a 64-bit record length,
an eh_frame_ptr outside the image or a missing terminator now throw
instead of returning the empty answer. The test queries a fixture DLL
whose .ehmeta it can break, since the unwinder reads the same section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a save progress dialog immediately reported FINISHED. Skyrim
waited for RUNNING before writing SAVEDATA.DAT and never resumed. Keep
progress-bar mode running through polling and progress updates until
Close, retaining the existing automatic responses for other dialog modes.
Add lifecycle regression coverage for repeated polling, completion,
reopening, result metadata and ordinary selection dialogs. The test
failed on the previous implementation and passes with the fix on Fedora
44 Release. Correcting the live dialog state recovered a blocked Skyrim
save without restarting its session.
Written with AI assistance.
Measured on a PS5 (firmware 13.60) through dlopen of libSceRtc.sprx.
The weekday is required in any case or length, month names may be
full, RFC 2822 and RFC 850 share one lenient grammar with 2-digit years
split at 50, and the asctime zone is ignored. Zones come from a name
table, a military letter, or +HHMM without a range check. Text the
console rejects returns a parse error with the tick untouched, and an
invalid calendar date returns 0 with only the offset applied to the
tick, as the console does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PS5 (firmware 13.60) every input that starts with a digit gets the
same result and tick from sceRtcParseDateTime as from
sceRtcParseRFC3339, except that leading spaces and tabs are skipped
first. A string without a zone, or with a space instead of T, is a
parse error rather than UTC.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A year before 1980 or after 2107 cannot be packed into the DOS time, so
GetDosTime threw. Following the disassembly-derived fpPS4
reimplementation of the PS4 library, it now returns INVALID_YEAR after
writing 0 for an earlier year and 0xff9fbf7d, the largest encodable
time, for a later one. The TechnicalDebt entry now records fpPS4 as the
source for the SetDosTime year decode as well.
A captured grain stored only the port slot and data pointer, so a port
created in a destroyed port's slot before the next push mixed the old
pointer with its own format. Ports now carry a generation that the
capture records. TechnicalDebt notes that the last grain pushed before a
title stops is never played.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wave64 graphics program on a 32-lane host runs one guest lane per
invocation, yet an EXEC written as a value keeps all 64 bits: the NGG
prologue's s_lshr_b64 exec, -1, 64 - count reads count 0 and sets -1.
A waterfall loop that copies EXEC with s_mov_b64 and retires one key
per pass then never empties its copy, since no ballot sets the lanes
the host lacks, and its v_readlane picks lane 32 and up.
readRawU32 now ANDs EXEC with a ballot of true when it is read as a
value (s_mov_b64, ALU sources, src_execz), and s_*_saveexec and
s_*_wrexec store that masked copy. EXEC's own words, threadBit and the
saveexec combination keep the raw words, so s_orn2_saveexec_b64 vcc,
exec still sets all 64 bits. On subgroups under 32 lanes the EXEC_LO
copies of agc_driver_scalar_sop1 now hold only the subgroup's lanes.
The Smurfs Dreams imports both and no library exports them, so eager
binding stops the title before it starts. sceAudioInAsyncOpen throws
like the other AudioIn opens that have no implementation;
sceHttp2SetMinSslVersion returns success like the other Http2 SSL
setters, as no request reaches the network.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host mixes without the console's mastering stage, so the mastering
parameters have nothing to configure, as with sceAudioOut2MasteringInit.
Accept them instead of aborting, keep throwing for a null parameter
block, and record SetParam next to MasteringInit in TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Return the id a graphics event was registered with in
sceAgcDriverAddEqEvent, which titles derive from their context
(PPSA12544: 0 or 1 for graphics, 0x20 + 8 * pipe + queue for compute) and
read back to dispatch the event. Other event filters, a null or
misaligned event and an id above 32 bits throw. The meaning is an
assumption and recorded in the technical debt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GuestAppContent now writes a param.json that declares downloadDataSize 0
and checks that sceAppContentDownloadDataGetAvailableSpaceKb returns OK
with 0 KB for /download0 (it threw before), and PARAMETER for a null
result. TechnicalDebt records that the console's behaviour for such a
title is unverified.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review: read a grain's buffers at the next push without spinning
under the context lock, keep the no-device model mixing at push time,
size the pad queue from the unplayed main grains again, use generic
names in comments and TechnicalDebt, and add audio_out2_timing (next-push
read, queue level beyond the cushion, priming after running dry).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The APC delivery was allocated after SuspendThread, so a target holding
the allocator lock deadlocked the raising thread; it is now allocated
before the suspend. The wait count a raise checks lived in the target's
thread_local storage and dangled once the thread exited; it is now a
member of PthreadPrivate, which TimedWait counts into through a
thread-local pointer bound when the thread starts, and a raise on a
thread that already finished returns ESRCH. TechnicalDebt gets one line
per unverified choice: Windows only, the FreeBSD ucontext layout, the
signal mask, the YMM upper halves, and delivery to threads outside
TimedWait.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PPSA03306 (Silt) loads the Unity plugin Media/Plugins/PSNCore.prx, which
imports scePlayerReviewDialogInitialize, Open, UpdateStatus, GetResult and
Terminate from libSceCdlgPlayerReview. No library provided it, so loading
the plugin failed with "Failed to load module ...\libs\libSceCdlgPlayerReview.prx".
The library follows libSceWebBrowserDialog: the dialog shows nothing, Open
finishes at once and GetResult reports that the user canceled. Terminate
also accepts a finished dialog. GetStatus and Close are added as well.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OpenAGC's include/agc_nids.h names three more of the raw-NID exports, and
each name hashes to its NID:
gQkqkLttcpw sceAgcAcb_gQkqkLttcpw -> sceAgcAcbAtomicGds_0900
dolOmWH+huQ sceAgcUnknownGetFusedShaderSize -> sceAgcGetFusedShaderSize_0080
V++UgBtQhn0 sceAgcGetDataPacketPayloadAddressUnk -> sceAgcGetDataPacketPayloadAddress_0090
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eight libSceAgc exports were published under a raw NID with a made-up
name. Each NID is the hash of a versioned SDK name (checked with
tools/nid_names.py compute_nid), so the functions now carry that name and
are exported from it directly:
23LRUSvYu1M sceAgcUnknownInitState -> sceAgcInit_0090
-KRzWekV120 sceAgcUnknown__MKRzWekV120 -> sceAgcDcbSetIndexSize_0600
Ikfdt-rIqCE sceAgcUnknown_Ikfdt_MrIqCE -> sceAgcJumpPatchSetTarget_0300
7Wa3aeJgeVU sceAgcUnknown_7Wa3aeJgeVU -> sceAgcBranchPatchSetThenTarget_0300
rP5xLdOf26k sceAgcUnknown_rP5xLdOf26k -> sceAgcBranchPatchSetElseTarget_0300
fd5Bp5tGTgo sceAgcUnknownFuseShaderHalves -> sceAgcFuseShaderHalves_0200
qj7QZpgr9Uw sceAgcDcbContextStateAnotherOp -> sceAgcDcbContextStateOp_0100
k0E7vkgqAuE sceAgcCreateInterpolantMappingVsPs -> sceAgcUpdateInterpolantMapping_0100
The exported NIDs are unchanged; the extra exports under the hashes of the
made-up names are gone. Behaviour is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PPSA01325 stops on sceFontGlyphDefineAttribute right after generating a character glyph: it sets attribute 0x11 to 0 on that glyph, deletes it without using it, and draws the character with sceFontRenderCharGlyphImageHorizontal. The meaning of the attributes is unknown, so the call checks the glyph and ignores the rest; TechnicalDebt lists it under silent stubs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
Astro's Playroom (PPSA01325) fails eager binding on three imports. The two voice chat group requests follow the offline model of the other sceVoiceChatRequest* calls and return SIGNED_OUT; sceRudpActivate, whose signature is unknown, is declared as a throwing stub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
PPSA16064 (Minecraft Dungeons II, Unreal Engine) failed to load because
these imports had no export:
- libkernel: sceCoredumpWriteUserString
- libSceSaveData: sceSaveDataConvert, sceSaveDataGetConvertProgress
- libSceVideoOut: sceVideoOutVrrPegToFixedRate,
sceVideoOutVrrUnpegFromFixedRate
None of them is called during the title's start-up, so they are added
as unimplemented exports that throw with their name if a title calls
them. Their signatures are unknown; TechnicalDebt records them. The
built libraries export the NIDs the title imports.
Every command writer checked bottom <= cursor_up <= cursor_down <= top.
PPSA16064 (Minecraft Dungeons II, Unreal Engine) builds small one-packet
buffers (8 dwords) with cursor_down left null, and the first
sceAgcDcbCopyData into one threw "invalid command buffer cursors".
Nothing in the library allocates downward, so cursor_down only bounds
the space above cursor_up. A null cursor_down now means that bound is
top. Non-null cursors are checked as before. TechnicalDebt records the
assumption.
agc_commands writes six dwords into such a buffer and checks that a
further allocation past top fails without moving the cursor.
NID fCWdlnmB1Ks was exported as an unknown function that threw. The
name scePadIsRemoteController hashes to it. PPSA16064 (Minecraft
Dungeons II) calls it once per controller after
scePadGetControllerInformation, with the pad handle and the address of a
byte it has just zeroed, and ignores the result.
It reports whether the controller is connected through Remote Play.
Controllers here are local devices, so the answer is always false. An
invalid handle returns INVALID_HANDLE and a null output INVALID_ARG, as
the other pad calls do. Without it the title aborted at the main menu.
scePadSetAngularVelocityBiasCorrectionState, which the title imports but
does not call during start-up, is added as an unimplemented export.
guest_pad checks the handle and null-output errors and the result.
sceHttp2SendRequestAsync no longer fails synchronously: it queues a
completion carrying the network error and triggers the user event named
by the kqueue option. sceHttp2WaitAsync returns queued completions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BREAKING CHANGE: CLI and Python callers must supply all seven float
controls. Add clamp, rounding and FP16 overflow settings so hardware
measurements cannot silently use an unintended mode.
Refs #981
Storage images of a SINT format threw "storage image descriptor uses
an unsupported format"; only write-only 32_SINT was accepted.
The recompiler now writes 16_SINT, 8_8_SINT, 32_SINT, 16_16_SINT,
8_8_8_8_SINT, 32_32_SINT, 16_16_16_16_SINT and 32_32_32_32_SINT
storage images with the Uint numeric class. 8- and 16-bit components
are saturated (EmitFormatStoreComponent) and 32-bit ones stored
unchanged, as measured on RDNA2 (gfx1035); image_store_pck stores
32-bit components unchanged too. The driver binds these surfaces
through a cached view of the same-size UINT format.
8_SINT, 32_32_32_SINT, the packed SINT formats (11_11_10, 10_11_11,
2_10_10_10, 10_10_10_2), D16 stores, packed stores of 8- and 16-bit
components and atomics other than 32-bit ones on 32_SINT and 64-bit
ones on 32_32_SINT still throw. New test: agc_driver_image_store_sint,
with DST_SEL X,Y,0,1 on the two-channel formats, as with X,Y,Z,W the
hardware writes channel 1 from the W data slot (TechnicalDebt).