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.