sceKernelWaitSema parks a guest thread on the scheduler when the count is not
yet available, but sceKernelSignalSema only incremented the count and returned:
there was no WakeBlockedThreads call anywhere in the file, so a thread blocked
in WaitSema was never woken and the game hung there. sceKernelCancelSema and
sceKernelDeleteSema left parked waiters stranded the same way.
Give each semaphore a per-handle wake key and each waiter a small record with
the count it needs and a result slot. Signal, cancel, and delete wake the
waiters through the scheduler after releasing the semaphore lock, matching the
lock order the event flag and event queue paths already use. The wake handler
runs under the scheduler gate and consumes the count under the semaphore lock,
so a waiter needing more than is available stays parked while a smaller waiter
can still proceed; the resume handler hands the recorded result back as the
guest's return value.
Cancel bumps an epoch and delete sets a flag so woken waiters return what the
kernel returns in those cases: ECANCELED (0x80020055) for a canceled wait and
the EACCES-class 0x8002000D for a deleted semaphore. Delete succeeds even with
waiters present. Only the woken waiter's own handler adjusts the waiting-thread
count, so a waiter that parks during a cancel is not double-counted, and the
create path now wakes a waiter that raced onto the handle if the handle
write-back fails instead of stranding it.
This does not change the immediate paths: an available count is still consumed
inline, and a wait with a timeout pointer still returns immediately (honoring
the timeout through the scheduler is a separate change).
Verified with a block/wake harness that drives real guest threads through the
real import trampolines: signal-after-block, signal racing the park,
multi-waiter signal, need-count gating with a smaller waiter slipping past, and
cancel and delete with parked waiters including the reported waiter count, plus
event flag and event queue regression checks. Builds clean on Windows and
Linux.
* [agc] Add shader type 4 (GS) and register defaults v13 support
Astro Bot (#11) crashes on boot due to two missing GPU features:
1. Shader type 4 (Geometry Shader) — SPI_SHADER_PGM_LO/HI register
offsets 0x8A/0x8B were missing. Added constants and switch cases
for shader type 4 in GetExpectedSpiShaderPgmLo/Hi. Also added
type 4 to IsEsGeometryShaderType (2 or 4 or 6).
2. Register defaults version 13 — was not recognized as supported.
Added RegisterDefaultsVersion13 constant and included it in
IsSupportedRegisterDefaultsVersion.
* [kernel] Add POSIX pthread_cond_timedwait export
SILENT HILL (#4) and Poppy Playtime (#3) crash on boot due to
missing POSIX pthread_cond_timedwait (NID 27bAgiJmOh0).
The Sony wrapper scePthreadCondTimedwait (NID BmMjYxmew1w) was
already implemented, but the raw POSIX symbol was not exported.
Added [SysAbiExport] for pthread_cond_timedwait delegating to
existing PthreadCondWaitCore with timed: true.
* [memory] Fix FlushInstructionCache null process handle
PhysicalVirtualMemory.cs called FlushInstructionCache with null as the
process handle in two places (SetProtection and TryWriteExclusive).
On Windows, a null handle does not reliably resolve to the current
process — the correct call is GetCurrentProcess() (pseudo-handle -1).
Also corrected the P/Invoke signature:
- Changed return type from void to bool with [return: MarshalAs(Bool)]
- Added SetLastError = true
- Added GetCurrentProcess() P/Invoke import
This matches the pattern already used in DirectExecutionBackend.cs
which correctly passes GetCurrentProcess() to all FlushInstructionCache
calls.
* [hle] Distinguish NOT_FOUND from NOT_IMPLEMENTED and log duplicate NIDs
Three diagnostic improvements to the HLE dispatch path:
1. ModuleManager.RegisterFromAssembly — duplicate NID registration was
silently skipped (dispatchTable first-wins, exportTable last-wins,
causing metadata divergence). Now logs a warning with the NID and
export name so conflicts are visible.
2. ModuleManager.TryDispatch — generation mismatch returned
ORBIS_GEN2_ERROR_NOT_FOUND, conflating 'function does not exist'
with 'function exists but not for this generation'. Now returns
ORBIS_GEN2_ERROR_NOT_IMPLEMENTED for generation mismatch, matching
the existing convention in CpuDispatcher. Also adds debug logging
for both NOT_FOUND and NOT_IMPLEMENTED paths.
3. DirectExecutionBackend.Imports.cs — the import dispatch else-branch
(the actual hot path that bypasses ModuleManager.TyDispatch via
cached export) had the same conflation. Split into:
- else if (export exists but generation mismatch) → NOT_IMPLEMENTED
- else (no export at all) → NOT_FOUND
This makes runtime diagnostics correctly distinguish missing exports
from generation-unsupported exports.
* [cpu] Check VirtualProtect return values in all stub creation paths
9 VirtualProtect calls in DirectExecutionBackend.cs had unchecked
return values. If VirtualProtect silently fails, memory protection
remains incorrect — stubs allocated with PAGE_EXECUTE_READWRITE (0x40)
never get downgraded to PAGE_EXECUTE_READ (0x20), or guest thread
entry stubs never get upgraded to writable. This causes access
violations on next execution or silent data corruption.
Fixed all 9 sites with proper error handling:
- 6 stub creation methods (return 0 on failure + log error)
- 2 guest thread entry methods (set reason + return Exception)
- 1 guest entry method (set LastError + return MEMORY_FAULT)
Stub creation sites fixed:
- CreateImportDispatchStub (line ~1683)
- EnsureTlsHandler (void, log + return)
- CreateUnresolvedReturnStub (return 0)
- CreateGuestReturnStub (return 0)
- CreateExceptionHandlerTrampoline (return 0)
- CreateTlsStoreHelperStub (return 0)
Guest thread entry sites fixed:
- StartGuestThreadNativeCall (return Exception)
- StartGuestContinuationNativeCall (return Exception)
- RunGuestEntryPoint (return MEMORY_FAULT)
* [kernel] Remove unused duplicate _nextFileDescriptor field
KernelExports.cs declared _nextFileDescriptor but never used it.
The actual field used for file descriptor allocation lives in
KernelMemoryCompatExports.cs (lines 1314, 1337). This was a dead
duplicate causing CS0414 warning.
Build is now 0 errors, 0 warnings.
---------
Co-authored-by: Hermes Atlas <hermesatlas@example.com>