mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-01 15:39:47 +08:00
d7f6e3f578b0d0d290eb2c5b3bfacd388e52a51a
175 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fa2616d224 |
Linux and macOS support (#47)
* [macos/linux] Cross-platform host memory, TLS, and ABI layer for POSIX Introduces the foundation for running SharpEmu on macOS (osx-x64 under Rosetta 2) and Linux (linux-x64). The CPU backend executes guest x86-64 code natively, so these targets run the whole process as x86-64; this commit replaces the Windows-only host primitives with platform-dispatched equivalents so the guest boots and services HLE calls off Windows. Memory (HostMemory.cs, new): a Win32-semantics facade over mmap/mprotect/munmap with a shadow region table answering VirtualQuery. PhysicalVirtualMemory, DirectExecutionBackend, StubManager, and the two Kernel*CompatExports now go through it instead of kernel32 P/Invokes. Exact-address requests use MAP_FIXED_NOREPLACE (Linux) / guarded MAP_FIXED (macOS) so they match Win32 "map there or fail" semantics. TLS + host helpers (PosixHostStubs.cs, new): pthread-backed TLS and Win64-ABI-compatible stubs for the kernel32 helpers the backend embeds into emitted x86-64 code (TlsGetValue, QueryPerformanceCounter, SwitchToThread, Sleep). A Win64->SysV thunk wraps managed callbacks, since .NET on POSIX compiles them for the SysV ABI while the emitted call sites use Win64. Guest address layout: the 0x7FFx window is Windows-only (dyld shared cache / Rosetta runtime live there on POSIX), so stack/TLS/stub regions relocate to 0x6FFx off Windows. Vectored exception handling is gated off on POSIX for now (guest faults are not yet recovered) — the signal-based bridge is the next step. Also adds osx-x64 to the RID list and a Docker-based Linux smoke-test script. Status: on both macOS (Rosetta) and Linux (amd64), the guest now boots, runs native x86-64 code, and dispatches HLE imports. macOS stops at a Rosetta translation-cache issue; Linux runs ~252 imports through C++ static-init before hitting the missing fault handler (SIGSEGV). * [posix] Bridge the vectored exception handler to sigaction(SIGSEGV/SIGBUS/SIGILL) Guest faults on macOS/Linux previously terminated the process because the recovery logic in DirectExecutionBackend.Exceptions.cs was Windows-only. This adds a POSIX front-end that reuses the existing handler bodies: - DirectExecutionBackend.PosixSignals.cs installs SA_SIGINFO handlers via an [UnmanagedCallersOnly] entry, rebuilds the Win64 EXCEPTION_POINTERS / CONTEXT view from the platform mcontext (Darwin __ss thread state via the mcontext pointer at ucontext+48, Linux glibc gregs at ucontext+40 -- offsets verified against the headers on both platforms), runs the same chain as the VEH path (TryRecoverUnresolvedSentinel trap-sentinel recovery, TryHandleLazyCommittedPage demand paging, VectoredHandler diagnostics incl. FS/GS TLS-fault detection), and writes register changes back into the mcontext so sigreturn resumes the repaired guest. Unrecovered faults chain to the previously installed handler so the .NET runtime keeps mapping its own faults to managed exceptions. - The whole recovery path is warmed up with fabricated inputs before the handlers are installed. This is required under Rosetta 2: the signal trampoline cannot enter x86 code that has never been executed (and so never translated) -- a cold handler is silently never invoked and the faulting instruction retries forever (reproduced and verified in an isolated .NET test under Rosetta for Linux). It also keeps first-fault JIT work out of the signal frame. - Handlers run without SA_ONSTACK: the runtime's alternate stacks are too small for the diagnostic path, while guest (2MB) and host thread stacks match where Windows dispatches exceptions anyway. - The raw reads in the shared fault diagnostics (stack qwords, RBP walk, code bytes at RIP) now probe the region table on POSIX before touching memory, since a nested SIGSEGV inside the handler would kill the process before diagnostics finish. Windows keeps its try/catch reads. - Escape hatches: SHARPEMU_DISABLE_POSIX_SIGNALS=1 skips installation, SHARPEMU_DISABLE_RAW_HANDLER=1 disables sentinel recovery (parity with Windows), SHARPEMU_LOG_POSIX_SIGNALS=1 traces every delivery (first 16 and every 1024th are always traced). Verified with the test game: Linux (amd64 container) previously died with SIGSEGV right after import #252; it now recovers/diagnoses signals and the run proceeds to the real next blocker, an unpatched negative-offset guest TLS read (fault at TLS base - 0x1708), which gets the full NATIVE EXCEPTION dump before terminating. macOS is unchanged: the bridge installs and the game still stops at the known Rosetta translation-cache error at import 12, which is the next work item. * [posix] Fix guest memory layout faults: TLS prefix, exact mmap, map search base Three fixes that take the test game from dying during libc init to running its full main loop on macOS and Linux: - Static TLS blocks live below the TCB (FreeBSD amd64 variant II) and libc.prx reaches past -0x1700, but only a 4KB prefix was mapped below the TLS base. The prefix is now 64KB on POSIX (Windows keeps 4KB); the fault was a read at TLS base - 0x1708 during libc init. - HostMemory exact allocation on macOS used MAP_FIXED, which silently maps over untracked host memory. The direct-memory allocator's address scan walked into the .NET runtime's JIT heap and replaced live code, which under Rosetta 2 surfaced as "no code fragment associated with the given arm pc". Exact placement now passes the address as a hint and fails on relocation, like MAP_FIXED_NOREPLACE does on Linux. - sceKernelMapDirectMemory/MapFlexibleMemory searched for free space starting at 4GB, which is the Mach-O image base on macOS. The default search base is 0x20_0000_0000 on POSIX, and TryAllocateAtOrAbove now asks the kernel for a placement instead of page-stepping through host- owned address space (Rosetta ignores mmap hints for whole VA windows), over-allocating when the caller needs more than page alignment. Windows behavior is unchanged; all divergences are platform-guarded. * [macos] Video presenter on the main thread, MoltenVK support, window keyboard input Gets the test game from a headless loop to a playable window on macOS: - AppKit traps with SIGILL ("NSUpdateCycleInitialize() is called off the main thread") when GLFW runs on a worker thread. The CLI now moves emulation onto a worker thread on macOS and parks the real main thread in HostMainThread.Pump(); the presenter posts its whole window loop there instead of spawning a thread, and a shutdown handler asks the render loop to close the window so the pump unwinds on guest exit. - MoltenVK: enable VK_KHR_portability_enumeration (+ the portability instance flag) and VK_KHR_portability_subset when advertised, and gate robustBufferAccess2 on the device actually supporting it (Metal does not; the old code keyed it off robustImageAccess2 and vkCreateDevice failed with ErrorFeatureNotPresent). - Input: pad exports polled user32 GetAsyncKeyState, so POSIX hosts threw DllNotFoundException per scePadReadState call. The presenter now attaches the window's keyboard via Silk.NET.Input into HostWindowInput, and the pad exports map the existing VK-code layout onto it off Windows. Headless hosts (Linux containers) report a disconnected keyboard and fall back to neutral pad data silently. GLFW needs an x86-64 Vulkan loader under Rosetta: place a universal libMoltenVK.dylib next to SharpEmu named libvulkan.1.dylib (Homebrew's arm64-only copy cannot load into the x86-64 process) and export DYLD_LIBRARY_PATH to that directory. Verified: Dreaming Sarah boots to a MoltenVK-backed 2560x1440 window on macOS (Apple M4, Rosetta 2), renders the intro, title, and menus, and keyboard input drives it into gameplay. Linux (amd64 container) runs the same build headless through millions of imports with no faults. Windows paths unchanged; arm64 and x64 builds clean. * [posix] CoreAudio playback, self-contained MoltenVK loading, input/log polish - Audio: sceAudioOut ports now play through an AudioQueue backend on macOS (stereo PCM16 with the same 32KB backpressure pacing as the WinMM path). The WinMM port and the new CoreAudio port share an IHostAudioPort interface and sample converter; hosts without a backend (Linux containers) keep the silent fallback. - MoltenVK: GLFW resolves Vulkan with dlopen("libvulkan.1.dylib"), which cannot see the app-local universal MoltenVK build, so the presenter now feeds vkGetInstanceProcAddr straight into glfwInitVulkanLoader (GLFW 3.4) before creating the window. No DYLD_LIBRARY_PATH needed; the CLI also preloads the dylib for Silk.NET and prints setup hints when it is missing. scripts/fetch-macos-moltenvk.sh stages the official universal dylib next to a build. - The virtual-range allocator's failure trace now names the address and length instead of "AllocateAt invocation threw". Investigated and documented (not port defects): the savedata transaction failure is identical on Linux and macOS (HLE argument-register mapping for sceSaveDataCreateTransactionResource), and the in-game tile speckling has no platform-specific code in its path - the one macOS-only delta is that MoltenVK lacks robustBufferAccess2, so out-of-bounds shader reads return garbage instead of zeros. Verified on macOS: window, audio backend, and keyboard input all come up with zero environment configuration; the game runs to gameplay. Linux headless run unchanged (silent audio, no faults). Windows paths untouched; arm64 and x64 builds clean. * [cpu] Preserve guest registers and flags across patched TLS accesses The TLS patch handler replaces guest `mov reg, fs:[...]` instructions, which preserve every other register and the flags - but the handler loaded the TLS index into ecx and called TlsGetValue (Win64: clobbers rcx/rdx/r8-r11) with `sub/add rsp` trashing the arithmetic flags. Guest code that keeps live values or comparison results across a TLS access computed garbage deterministically. The handler now saves rcx, rdx, r8-r11, and the flags around the call, keeping the same inner stack alignment. This applies to the load patches and both store-helper stubs, on every platform. Also in this change, from the rendering-artifact investigation: - The present blit picks linear filtering for any fractional scale (nearest only for integer upscales): a 3840x2160 guest frame blitted into a 2560x1440 swapchain with nearest silently dropped every third row/column. - ClampViewport no longer trims the guest viewport rectangle to the render target; trimming changed the guest's scale/offset and skewed texel addressing. Vulkan permits viewports beyond the framebuffer (the scissor confines rendering), so only spec bounds are enforced. - Env-gated diagnostics grown during the investigation: guest texture dumps (SHARPEMU_TEXTURE_DUMP_DIR), aliased guest-image readback dumps (SHARPEMU_TRACE_GUEST_IMAGES=alias), small-render-target write movies (SHARPEMU_TRACE_GUEST_WRITES=small), unattended input injection (SHARPEMU_AUTO_CROSS=secs,...), viewport nudging (SHARPEMU_VIEWPORT_EPSILON), chunked-draw toggle (SHARPEMU_DISABLE_CHUNKED_DRAWS), and rect-list/draw vertex traces. Known remaining issue (root cause narrowed, not yet fixed): the game's terrain texture pages are corrupted in guest memory before any GPU work - the mound's solid-fill 32x32 tiles decode to fully transparent texels and the grass page has deterministic gaps, byte-identical across runs. Ruled out: memcpy/memmove/memset/realloc HLE semantics, sampler wrap modes, texel-boundary rounding, chunked draws, viewport handling. Next step is auditing the Chowdren asset decode path (custom compressed images) against the emulator's import surface. * [linux] ALSA playback backend for sceAudioOut sceAudioOut ports on Linux now play through libasound instead of the silent fallback. The PCM device opens in blocking mode with ~170ms of device buffer (the time-equivalent of the 32KB queue the WinMM and CoreAudio ports keep), so snd_pcm_writei provides the same backpressure pacing without a managed queue. Underruns and suspend/resume go through snd_pcm_recover with one retry per submit; anything else drops the buffer rather than stalling the guest. The "default" device routes through PulseAudio/PipeWire on desktops and straight to hardware on bare ALSA; SHARPEMU_ALSA_DEVICE overrides it (the null device makes the path testable in containers). A missing libasound or device fails port creation and lands in the existing silent fallback. Verified in an amd64 container: the test game opens the port (backend=alsa, 48kHz stereo float32) and streams sceAudioOutOutput through the null device for a full run; without a usable device the port logs a warning and falls back to silent. Playback on real Linux audio hardware has not been tested. * [fixes] Address review feedback: commit bounds, CoreAudio shutdown, dump errors - HostMemory: a MEM_COMMIT that runs past its reservation now fails like Win32 instead of committing a prefix and reporting success. All current callers already clamp their ranges to the region, so this only guards future callers. - CoreAudioPort: Dispose wakes a submitter waiting on backpressure and the wait treats ObjectDisposedException as a timed-out wait, so closing a port during playback can no longer throw. A failed AudioQueueStart tears the queue down and fails fast instead of leaving an undrainable queue that stalls every later submit on its timeout. - AgcExports: texture dumping catches all write failures (bad path, permissions), logging a warning instead of crashing when SHARPEMU_TEXTURE_DUMP_DIR points somewhere unusable. Verified with the Linux container run: game boots and streams audio with the stricter commit check, and a dump dir under /proc produces warnings instead of taking the process down. * [ci] Build linux-x64 and osx-x64 archives Adds a build-posix matrix job (ubuntu-latest / macos-latest) mirroring the Windows build: locked restore, Release build, self-contained CLI publish, and a tar.gz artifact per RID (tar keeps the executable bit). The macOS archive also stages the universal MoltenVK dylib via scripts/fetch-macos-moltenvk.sh so the artifact runs without any manual Vulkan setup. The release job still only ships the Windows archive. * [cli] Keep POSIX glfw natives outside the single-file bundle The KeepGlfwOutsideSingleFile target only matched filenames starting with 'glfw', which covers Windows (glfw3.dll) but not libglfw.3.dylib / libglfw.so.3. Those got embedded into the single-file bundle, and Silk.NET's library loader does not probe the bundle extraction directory, so a published build died with "Couldn't find a suitable window platform" (and the glfwInitVulkanLoader wiring, which loads the library from AppContext.BaseDirectory, could not run either). Keeping the POSIX names loose next to the executable fixes both, the same way the Windows build already handled it. Found by running the CI-built osx-x64 archive: video failed while local loose-file builds worked. With the fix the published single-file build opens the MoltenVK window, wires the loader, and reaches gameplay. * [ci] Publish linux-x64 and osx-x64 release archives The build-posix artifacts now ship as per-RID GitHub releases on main pushes and manual dispatches, tagged the same way as the win64 ones (<rid>-<ref>-<sha>). Archives stay tar.gz so the executable bit survives extraction. * [cli] Fail early on non-x86-64 host processes The CPU backend executes guest x86-64 code natively, so the process must be x86-64 (win-x64/linux-x64 on x64 hardware, osx-x64 under Rosetta 2 on Apple Silicon). An arm64 process previously failed deep inside emulation startup, indistinguishable from MoltenVK, signal handler, or guest memory problems. CLI mode now checks the process architecture up front and exits with a message naming the supported execution model (and the Rosetta install command on macOS). The GUI-only path stays usable on arm64. * [video] Log the selected Vulkan device name and API version The presenter never named the GPU it picked, so a 'no video' report could not be told apart from a real windowing failure without guessing. It now logs the device name, type, and API version right after selection. A software rasterizer (llvmpipe/lavapipe/SwiftShader) shows up here and typically lacks the device features the translated shaders need, which is the likely cause when a window opens and presents frames but nothing draws. * [video] Steer GLFW to XWayland on Wayland sessions GLFW's native Wayland backend does not reliably map the Vulkan window with some drivers (NVIDIA in particular): frames present but the window never becomes visible, so the game runs with audio and no picture. A report on an RTX 5080 showed exactly this — all device features present, frames presenting, but the log had 'libdecor-gtk.so failed to init' and a 1.4x-scaled window, both Wayland tells. On a Wayland session that also exposes an X server (DISPLAY set), the presenter now clears WAYLAND_DISPLAY for its own process before GLFW initializes, so GLFW selects its dependable X11/XWayland backend. SHARPEMU_ENABLE_WAYLAND=1 opts back into native Wayland. Headless (no DISPLAY) and non-Linux hosts are unaffected. * [video] Force GLFW X11 backend via the platform init hint, log the platform The previous attempt cleared WAYLAND_DISPLAY to steer GLFW off Wayland, but a reporter still hit the native-Wayland path (the Wayland-only libdecor error persisted), so that env trick doesn't switch GLFW. Use GLFW's supported mechanism instead: glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11) before GLFW initializes, called into the same libglfw GLFW itself loads (the pattern InitializeMacVulkanLoader already uses). Still gated on a Wayland session with an X server present (DISPLAY set) so we never force X11 where XWayland can't catch it, and still overridable with SHARPEMU_ENABLE_WAYLAND=1. Also logs 'GLFW windowing platform in use: <backend>' after init via glfwGetPlatform, so a 'no window' report shows X11 vs Wayland outright. Verified on macOS: the readback correctly reports Cocoa and the presenter is unaffected (the fix is a no-op off Linux). * [video] Run the GLFW window on the main thread on Linux too GLFW requires window creation and event processing on the process main thread on every platform: initialization, window creation, and glfwPollEvents are main-thread-only, and X11 in particular has a single event queue that must be serviced there. A window created and polled on another thread may never map — which is why the game ran (audio, imports, even Vulkan present) with no visible window on Linux. macOS already routed the window loop to the main-thread pump (AppKit needs it); Windows is fine because it has a per-thread event queue. Linux was the gap: it spawned a background thread for the presenter. Extend the existing HostMainThread pattern to Linux — emulation runs on a worker, the main thread pumps the window work the presenter posts. Refs GLFW intro guide (thread-safety): init, window creation, and event processing are restricted to the main thread. Verified: macOS still boots to its window unchanged; the Linux headless container runs to millions of imports with no deadlock or regression. On-screen confirmation on a real Linux desktop is still pending, but this is the documented root cause for a windowless-but-running Linux session. * [posix] Skip Win32 native guest workers * [vulkan] Synchronize offscreen targets before present * [vulkan] Transition fresh textures from undefined layout * [vulkan] Report swapchain pixels before source readback * [vulkan] Emit requested guest image diagnostics * [agc] Diagnose guest texture fallbacks * [linux] Keep guest GPU mappings in low address space * [video] Reduce diagnostic stalls and drain complete frames * [memory] Harden packed GPU address handling * [readme] Document Linux and macOS release support * [posix] Integrate the host platform abstraction * [posix] Restore guest thread address window * [video] Run the performance HUD on POSIX hosts The FPS/CPU/work HUD bailed out unless the host was Windows; only the per-thread hottest-thread scan actually needs Windows APIs. Keep that scan Windows-only (POSIX reports 'idle') and let the rest of the HUD run everywhere — the title is already set from the render thread, which owns the window on macOS and Linux. * [posix] Implement native guest worker threads Guest entry stubs must not run above CLR-managed frames on CLR-created threads (see the NativeWorker preamble); the PR previously fell back to the inline calli path on POSIX, which reproduced the documented 'attempted to call a UnmanagedCallersOnly method from managed code' fail-fast (observed after Dreaming Sarah's menu select) and left the runtime's suspension machinery walking guest frames. Provide the missing POSIX half of the worker loop: - PosixHostStubs grows Win64-convention WaitForSingleObject/SetEvent/ ExitThread stubs backed by dispatch semaphores (macOS) / unnamed POSIX semaphores (Linux) plus pthread_exit, with EINTR retry in the wait. - Worker events are creatable/signalable/waitable from managed code too, so NativeGuestExecutor.Run keeps its handshake (AutoResetEvent stays on Windows byte-for-byte). - PosixHostThreading implements CreateNativeThread/WaitForThreadExit/ CloseThreadHandle over pthreads (liveness probed with pthread_kill(0), then joined). - RunPrologue/RunEpilogue are routed through the existing Win64->SysV thunks, so the emitted loop stays identical across platforms. * [macos] Disable concurrent GC under Rosetta's write-watch hazard Background GC's write-watch revisit (SoftwareWriteWatch::GetDirty -> FlushProcessWriteBuffers) calls thread_get_register_pointer_values on every thread; under Rosetta 2 that Mach call stalls indefinitely on threads executing translated guest code. The background mark phase then never finishes and every allocating or Monitor-taking thread wedges behind it — observed as Dreaming Sarah freezing at the menu/loading screen with FPS 0 in 5 of 7 runs, dispatcher/watchdog parked in Monitor.Enter and all BGC threads waiting in t_join. Non-concurrent GC never takes that path; a 5-minute soak now holds 22-31 fps in-game with zero stalls. Windows and Linux keep concurrent GC. * [diag] Periodic guest-thread snapshots with gate-owner tracking SHARPEMU_PERIODIC_SNAPSHOT_SECONDS=N dumps the stall snapshot every N seconds even while imports are progressing, for soft stalls where the game stops advancing but threads keep spinning. The periodic dump never touches the guest-thread gate (it must keep reporting when the gate is what's wedged): it reads a lock-free owner record — every gate acquisition now goes through LockGate(site), which notes site/thread — and walks the thread table without the lock, tolerating torn reads. SHARPEMU_PERIODIC_SNAPSHOT_FILE redirects the dump to a side file for the case where the console itself is wedged (frozen log mirror was one of the observed failure modes). * [nuget] Add osx-x64 RID targets to lock files * [cpu] Back off the guest join poll TryJoinThread polled the host thread at a fixed 1ms; a game main thread joining a long-lived worker (Dreaming Sarah parks there for the whole session) burned ~5% of managed CPU in Join/Sleep syscalls. Ramp the poll interval to 10ms once the join is clearly long-lived — exit detection latency for long joins moves from ~1ms to at most 10ms, and short-lived joins still resolve on the first 1ms polls. * [nuget] Add linux-x64/win-x64 RID targets to lock files * [posix] Keep guest stacks clear of the import-stub descent The import-stub region descends from 0x7000_0000_0000 on the same 16MB grid as the guest thread windows; moving stacks to 0x6FFF_E000_0000 put them inside the stub region's 64-module descent range (floor 0x6FFF_C000_0000), silently consuming the top ~32 stack slots on hosts with many loaded modules. Drop the POSIX stack base to 0x6FFF_A000_0000: 512MB below the stub floor, still 2.5GB above the TLS window. Windows keeps 0x7FFF_E000_0000 (its bands are ~15TB apart). * [pad] Read window gamepads on POSIX hosts XInput and the DualSense hid reader are Windows-only, which left macOS/Linux with keyboard input only. The presenter's Silk/GLFW input context already enumerates gamepads on both platforms, so track their state in HostWindowInput (event-driven on the window thread, snapshot guarded like the key set) translated to ORBIS conventions: GLFW's Xbox layout maps A/B/X/Y to Cross/Circle/Square/Triangle, sticks bias from -1..1 to 0..255 with Y growing down, and triggers rescale from GLFW's -1..1 resting-at--1 range with digital L2/R2 bits past 25%. The merge into ReadHostInputState is gated to non-Windows so a physical pad is never sampled twice through both a native reader and GLFW. Hotplug is handled via ConnectionChanged; with no pad connected the path is inert. Untested against a physical controller (none attached to the dev host); axis conventions follow the GLFW gamepad-mapping contract. * [nuget] Refresh lock files after cross-RID restores * [posix] Adopt the host audio/input seams from main Main's #192 abstracted audio output and pad/keyboard input behind IHostAudioOutput/IHostInput; re-express the POSIX backends behind them: - CoreAudioPort/AlsaAudioPort move to Host/Posix as PosixCoreAudioStream/PosixAlsaAudioStream implementing IHostAudioStream. The seam converts to stereo PCM16 before Submit, so the ports' own conversion (and IHostAudioPort/AudioSampleConverter) is gone; queueing and backpressure are unchanged. - PosixHostAudio selects CoreAudio (macOS) / ALSA (Linux) as the platform's IHostAudioOutput. - PosixHostInput implements IHostInput over an IPosixWindowInputSource that HostWindowInput registers when the presenter attaches the window's GLFW input context: keyboard with virtual-key translation, the window gamepad snapshot (now in seam HostGamepadState/HostGamepadButtons terms), and keyboard-connected as the focus signal. Rumble/lightbar no-op (GLFW has no such API). - PadExports drops its direct HostWindowInput gamepad merge — pads now flow through IHostInput.GetGamepadStates like every platform. - PosixHostThreading.RequestTimerResolution is a documented no-op. All three RIDs build; SharpEmu.Libs.Tests pass (26/26). * [nuget] Regenerate GUI lock file for RID-less locked restore Local cross-RID builds stamped a win-x64 runtimes section into SharpEmu.GUI's lock file; the project declares no RuntimeIdentifiers, so CI's 'dotnet restore --locked-mode' failed with NU1004 on every platform. Regenerated via a plain solution restore (--force-evaluate), matching what the workflow validates. |
||
|
|
72645cb373 |
[Host] Abstract audio output and pad/keyboard input behind the host platform seam (#192)
* [Host] Abstract audio output behind IHostAudioOutput Add IHostAudioOutput (opens streams, names the backend for diagnostics) and IHostAudioStream (submit interleaved stereo 16-bit PCM, Dispose) to the host seam, with the winmm waveOut implementation moving whole into Host/Windows/WindowsWaveOutAudio — same device open, queueing, 32 KB backpressure wait, and buffer lifetime as WinMmAudioPort had. The DllImports become source-generated LibraryImports in the move, matching the other Windows backends. The guest-format conversion (mono/stereo/7.1, s16/float32 -> stereo PCM16) is platform policy, not device code, so it stays in Libs as AudioPcmConversion; AudioOutOutput converts into a pooled buffer and submits the result through the stream. Open failures still degrade to the silent paced port with the same warning, and the port log line now takes its backend name from the platform instead of a hardcoded string. * [Host] Abstract pad and keyboard input behind IHostInput Add IHostInput to the host seam: gamepad state snapshots, rumble / trigger-rumble / lightbar sinks, and the keyboard-fallback queries (window focus, key state). Gamepad state crosses the seam as the new unmanaged HostGamepadState with HostGamepadButtons flags — named after the PlayStation layout the guest API exposes but with the seam's own values, so SCE_PAD_BUTTON bits never leak into host backends and the per-frame poll can stackalloc its snapshot buffer. The DualSense raw-HID reader, the XInput reader, and the Win32 HID interop move whole into Host/Windows (report parsing, hot-plug loops, rumble/lightbar output reports, and log strings unchanged), translating to the neutral flags instead of ORBIS bits and converting their DllImports to source-generated LibraryImports. WindowsHostInput composes them plus the user32 keyboard queries; rumble still fans out to both readers, trigger rumble stays XInput-only, lightbar stays DualSense-only. PadExports keeps all policy: the keyboard mapping (now via named OrbisPadButton constants instead of raw hex), the controller-beats- keyboard-past-deadzone merge, and the new host->ORBIS button translation. The GUI's source-linked reader copies re-point to the moved files (it still cannot reference SharpEmu.HLE wholesale), which requires AllowUnsafeBlocks for the generated marshalling stubs; its navigation code switches to the neutral flags. * [Host] Move the timer-resolution request behind IHostThreading IHostThreading gains RequestTimerResolution (idempotent, best-effort ~1 ms timed-wait granularity; a no-op wherever the platform default is already fine). The winmm timeBeginPeriod call, its once-only latch, and both warning strings move from the Libs-level HostTimerResolution helper into WindowsHostThreading as a source-generated LibraryImport; the vblank pump requests it through the platform instead. HostSystemInfo in SharpEmu.Logging keeps its direct user32/kernel32 imports deliberately: Logging sits below HLE in the dependency chain so it cannot see the host seam, every path is already OS-gated with fallbacks, and it only runs once for the diagnostics banner. |
||
|
|
62e1775c5c |
[HLE] Remove steady-state allocations from the hot HLE paths (#190)
* [HLE] Stop allocating on the memcpy/memset and trace hot paths memcpy/memmove no longer allocate a bounce buffer sized to the whole copy (large copies previously landed on the LOH); they loop through a single pooled 256 KB rental, copying high-to-low when the destination overlaps above the source so memmove semantics survive the chunking. memset reuses a shared zero chunk for the dominant zero-fill case and rents/fills only min(length, 16K) bytes for non-zero values instead of allocating and filling a fresh 16 KB array per call; the map-time zero-fill loop shares the same zero chunk. SHARPEMU_LOG_SEMA / SHARPEMU_LOG_VIDEOOUT are now read once into cached bools and every TraceSemaphore/TraceVideoOut call site is guarded, so trace messages are no longer interpolated (and the env var no longer queried) on every semaphore op and every flip with tracing off. Trace output when the flags are set is unchanged. * [HLE] Remove per-frame allocations from the vblank/flip/equeue plumbing The 60 Hz vblank pump no longer allocates per edge: PumpVblanks reuses a pump-thread-only port list instead of a LINQ Where/ToArray, and SignalVblank/SubmitFlip snapshot their event registrations into pooled rentals instead of copying the List on every edge and every flip (the snapshot must still be taken, since triggers run outside _stateGate and a per-port reusable buffer would race the pump thread against a guest thread's first-edge signal). sceKernelWaitEqueue delivery rents the dequeue buffer from the pool instead of allocating an array per wait, and event-queue wake keys are formatted once per handle (cached in a ConcurrentDictionary, dropped on queue delete) instead of building the string on every enqueue. The semaphore wake key moves onto KernelSemaphoreState at creation, the same pattern the pthread mutex state already uses, removing the per-signal/per-wait formatting. SHARPEMU_LOG_EQUEUE is read once into a cached bool like the sema/videoout flags. * [HLE] Read guest C-strings without per-call buffer allocations CpuContext.TryReadNullTerminatedUtf8 allocated a byte[capacity] and issued one TryRead per byte for every string-argument import. It now reads through a stack buffer (pooled above 512 bytes) in 128-byte bulk chunks, falling back to per-byte reads only when a chunk touches an unreadable range so a terminator sitting just before unmapped memory still resolves exactly as before. The chunk bound also keeps the overread past the terminator smaller than the old loop's worst case is wide, so no fault can appear where the byte loop succeeded. TryReadAsciiZ (dlsym/symbol resolution) drops its List<byte> + ToArray round-trip for the same stack/pooled buffer, keeping the byte-by-byte TryReadByteCompat reads because their Marshal.ReadByte fallback must probe exactly up to the terminator. Only the final string is allocated on either path now. * [HLE] Replace blocking-wait closures with waiter continuation objects Every wait that actually parked a guest thread allocated two capturing lambdas (plus their display classes) for the scheduler's resume/wake callbacks. RequestCurrentThreadBlock and the backend's blocked-thread state now carry a single IGuestThreadBlockWaiter instead of the Func<int>/Func<bool> pair: TryWake keeps the run-under-the-scheduler- gate contract and Resume still produces the guest's RAX on the woken thread. The waiter stays attached through the wake transition (the old code nulled only the wake handler there) and is consumed at resume. The existing waiter objects absorb the captured state as fields, so a blocking wait now allocates exactly one object: SemaphoreWaiter, PthreadMutexWaiter, and EventFlagWaiter implement the interface directly, and the equeue, cond, and rwlock waits get small waiter classes replacing their closures. Handler bodies delegate to the same static methods with the same arguments as before; the untimed event flag wait's mutable captured result becomes a field on its waiter. * [HLE] Back pending event queues with a ring deque instead of LinkedList LinkedList<KernelQueuedEvent> allocated a node object on every non-coalesced enqueue — one per vblank/flip edge per registered queue, 60+ times a second in steady state. KernelEventDeque is a grow-only ring buffer over a KernelQueuedEvent[] with the three operations the queue actually uses (AddLast, RemoveFirst, find-and-update-in-place by ident/filter), so steady-state enqueue/dequeue allocates nothing and the coalescing update writes the struct back through an indexer instead of a node reference. All accesses stay under _eventQueueGate, matching the LinkedList usage it replaces. * [HLE] Cap memcpy chunk iterations at the requested size, not the rented length Address Copilot review: ArrayPool.Rent may return a larger array than requested, so sizing each iteration by chunk.Length let the copy granularity depend on pool bucketing internals instead of the intended 256 KB chunking. Behavior was already correct for any chunk size (each iteration re-reads the source, and the overlap ordering is size- independent), but the loop now mins against the requested chunkLength, matching what memset already does. * [HLE] Skip the flip/vblank snapshot rental when no events are registered Address Copilot review: SignalVblank and SubmitFlip rented (and returned) a pooled snapshot even with zero registrations — steady per-frame pool traffic for games that never register flip events and only poll flip status. Zero-count signals now skip the rental, the copy, and the trigger loop entirely, which also retires the Math.Max(count, 1) minimum-rent guard. |
||
|
|
9d88542efd |
Fix virtual memory allocation and access (#193)
* Fix virtual memory allocation and access * Update test dependency lock file |
||
|
|
373100a6b0 |
Add 21 missing SysAbi exports and GUI Environment tab for SHARPEMU_* toggles (#189)
Fills NID gaps hit by PS5 titles during boot, controller setup, and rendering, and surfaces the common runtime switches in the GUI. All exports are additive (no behavior change to existing exports) and free of NID and export-name collisions with upstream. New export libraries: - libSceBluetoothHid: Init/RegisterDevice/RegisterCallback success stubs so titles proceed past Bluetooth controller setup (opt-out via SHARPEMU_BTHID_UNAVAILABLE=1). - libSceNpCppWebApi: Common::initialize no-op success; UE5 online titles abort PS5-component startup on a negative SCE error. Additions to existing libraries: - libScePad: scePadOpenExt (shared PadOpenCore, accepts special ports 1/2 and the ScePadOpenExtParam pointer), scePadClose, scePadGetExtControllerInformation. - libSceVideoOut: sceVideoOutConfigureOutput, sceVideoOutInitializeOutputOptions. - libSceAgc: DCB builders sceAgcDcbSetIndexCount, sceAgcDcbJump, DcbSetPredication, SetPacketPredication (emit valid skippable packets; full draw processing TODO). - libSceAmpr: measure and write KernelEventQueueOnCompletion pair. - libKernel: scePthreadGet/Setschedparam, sceKernelChmod (validate and accept; POSIX permission bits have no host equivalent on Windows). - libSceNetCtl: sceNetCtlRegisterCallbackV6 (delegates to the v4 callback). - libSceMouse: sceMouseInit. - libSceUserService: sceUserServiceGetAgeLevel (adult, skips parental gates). GUI: new Options Environment tab exposing common SHARPEMU_* switches as toggles (BTHID_UNAVAILABLE, DISABLE_IMPORT_LOOP_GUARD, VK_VALIDATION, DUMP_SPIRV, LOG_DIRECT_MEMORY, LOG_NP). Persisted in gui-settings.json and applied to the emulator process environment at launch; localized with English fallback. |
||
|
|
f23161be9a |
Host platform abstraction layer for the execution engine (#181)
* [Host] Introduce host platform abstraction with IHostMemory Add SharpEmu.HLE/Host with IHostPlatform/IHostMemory interfaces, neutral page-protection/region enums, and a HostPlatform.Current factory that resolves the Windows backend (or throws PlatformNotSupportedException on other OSes, matching today's de-facto behavior). WindowsHostMemory wraps the exact VirtualAlloc/VirtualFree/VirtualProtect/VirtualQuery calls used across the engine today, with identical MEM_*/PAGE_* constants. Migrate StubManager as the first consumer: its private kernel32 P/Invokes and enums are replaced by IHostMemory calls that issue the same two native operations (RWX commit+reserve of the PLT arena, release on Dispose). No behavior change. This is the first step toward supporting non-Windows hosts; subsequent commits move the remaining direct P/Invokes in Core and Libs behind the same seam. * [Host] Route PhysicalVirtualMemory through IHostMemory Replace the class's private VirtualAlloc/VirtualFree/VirtualProtect/ VirtualQuery P/Invokes with IHostMemory calls. Every site maps 1:1 onto the exact native call it issued before: MEM_COMMIT|MEM_RESERVE -> Allocate, MEM_RESERVE -> Reserve, fault-path commits -> Commit, and MEM_RELEASE -> Free, with identical protection values produced by the Windows backend. IHostMemory gains ProtectRaw so the save/restore protection sequences in TryWriteExclusive and TryTemporarilyProtectForRead round-trip the raw OS protection word (including modifier bits the neutral enum cannot represent) exactly as before. Raw PAGE_* constants remain only for the internal region-classification helpers, which only ever see values this class itself assigned. The exact-address free-on-mismatch, lazy reserve-only threshold, prime loop, and all trace strings are unchanged. * [Host] Add IGuestAddressSpace and retire the reflection-based allocator lookup Introduce IGuestAddressSpace in SharpEmu.HLE (fixed-address AllocateAt / TryAllocateAtOrAbove and guest mprotect via TryProtect) with signatures copied from PhysicalVirtualMemory, which now implements it. TryProtect reproduces the read/write/execute decomposition that KernelMemoryCompatExports.ResolveHostProtection performs, yielding the same PAGE_* values through the Windows backend. KernelVirtualRangeAllocator previously located AllocateAt via cached MethodInfo reflection (because SharpEmu.Libs cannot see Core types) and walked wrapper memories through an untyped 'Inner' property. Both are now typed: ICpuMemoryWrapper exposes the decorated memory (implemented by TrackedCpuMemory, whose Inner property already existed) and the allocator type-tests for IGuestAddressSpace with the same bounded unwrap depth. Failure paths keep the exact [LOADER][TRACE] strings. * [Host] Move Kernel HLE memory exports off direct kernel32 P/Invokes KernelMemoryCompatExports loses its private VirtualQuery/VirtualProtect/ VirtualAlloc/VirtualFree declarations and MemoryBasicInformation struct: - Guest mprotect (sceKernelMprotect/sceKernelMtypeprotect) now routes through IGuestAddressSpace.TryProtect resolved from ctx.Memory. The orbis read/write/execute decomposition moves into a GuestPageProtection conversion whose mapping is value-identical to the removed ResolveHostProtection. - The guarded libc heap and host-page accessibility checks go through IHostMemory (same commit+reserve/protect/free sequence; guard-page and protection-mask checks compare HostRegionInfo.RawProtection against the same PAGE_* literals as before). - HostMemory is exposed as a property so merely loading the type never resolves the platform backend on non-Windows hosts. KernelRuntimeCompatExports' RDTSC stub allocates its 16-byte RWX page via IHostMemory.Allocate; the OperatingSystem.IsWindows() gate returning null is unchanged. * [Host] Abstract thread, TLS, and symbol primitives in the execution backend Add IHostThreading (native TLS slots, current-thread id, affinity, raw thread create/join, diagnostic register capture) and IHostSymbolResolver (enum-keyed host function addresses baked into emitted stubs), with Windows implementations wrapping the exact kernel32 calls the backend made directly before. DirectExecutionBackend takes an optional IHostPlatform (defaulting to HostPlatform.Current) and routes every TlsAlloc/TlsFree/TlsSet/GetValue, GetCurrentThreadId, SetThreadAffinityMask, GetModuleHandle/GetProcAddress and the suspend+GetThreadContext diagnostic snapshot through it. The snapshot moves wholesale into WindowsHostThreading (including the Win64 CONTEXT size/flags/offsets, which are Windows-specific by nature) and returns a neutral HostCapturedRegisters. NativeGuestExecutor resolves WaitForSingleObject/SetEvent/ExitThread via the symbol resolver — the same addresses end up in the emitted run loop, so stub bytes are unchanged — and creates/joins its raw worker thread through IHostThreading with the same stack-reservation semantics. The run-loop emitter itself does not move. Marshal.GetLastWin32Error() in the affinity-failure log still observes SetThreadAffinityMask's error because the wrapper makes no intervening SetLastError call. * [Host] Move fault handling and remaining backend memory ops behind the seam Add IHostFaultHandling (handler-thunk creation, first-chance handler install/remove, unhandled-filter set) with WindowsFaultHandling in a new Cpu/Native/Windows/ folder. The exception-handler trampoline emitter moves there whole — same pre-filtered NTSTATUS codes, same TEB gs:[8]/ gs:[0x10] stack-limit reads, same host-RSP TLS switch — parameterized only by (managed callback, TLS slot, TlsGetValue address), which is exactly what SetupExceptionHandler passed it before. Handler installation order, the AddVectoredExceptionHandler(first=1) flag, the SHARPEMU_DISABLE_RAW_HANDLER gate, and all install/teardown log strings are unchanged. Every remaining VirtualAlloc/VirtualProtect/VirtualFree/VirtualQuery/ FlushInstructionCache in the backend partials routes through IHostMemory with 1:1 call mapping (RWX emit -> RX downgrade -> flush for stub emission, reserve/commit for the PRT aperture and lazy-commit fault path, raw-protection round-trips via ProtectRaw). HostRegionInfo gains RawState/RawAllocationProtection so the lazy-commit trace lines and protection-mask checks keep printing and comparing the exact native values. Windows semantics leaked as bare literals become named constants with identical values: NTSTATUS codes (WindowsFaultCodes) and Win64 CONTEXT byte offsets (Win64ContextOffsets, with the existing CTX_* constants aliased to it and handler-local numeric offsets replaced by the names). * [Host] Resolve the host platform explicitly at the composition root SharpEmuRuntime.CreateDefault() now resolves HostPlatform.Current once and passes it explicitly to PhysicalVirtualMemory and (via a new optional CpuDispatcher parameter) to DirectExecutionBackend, replacing the implicit default-argument fallbacks. On unsupported OSes boot now fails at the root with PlatformNotSupportedException and a clear message instead of on the first native call. A future Linux/macOS backend plugs in by returning a different IHostPlatform here. * [Host] Convert the platform backends to source-generated P/Invokes Replace [DllImport] with [LibraryImport] in the four Windows backend files added by this branch (WindowsHostMemory, WindowsHostThreading, WindowsHostSymbolResolver, WindowsFaultHandling). Marshalling stubs are now generated at compile time instead of JIT-emitted at runtime, which fits the pre-JIT-everything boot model and keeps the backends NativeAOT/trimming ready. Interop stays zero-copy: all signatures are blittable, GetModuleHandleW now pins the managed string via Utf16 marshalling instead of copying, and GetProcAddress names marshal through a stack-allocated Utf8 buffer. Implicit contracts become explicit where LibraryImport requires it: TlsFree/TlsSetValue gain [MarshalAs(UnmanagedType.Bool)] (the 4-byte Win32 BOOL DllImport assumed silently), and GetModuleHandle targets the W entry point directly since LibraryImport never probes suffixes. The CONTEXT snapshot buffer stays a NativeMemory allocation rather than stackalloc: CONTEXT requires 16-byte alignment, now documented at the call site. Native call sequences are unchanged. * [Host] Address Copilot review: harden failure paths, honor injected platform - Free the handler thunk page when the RX protection downgrade fails (the leak predates this branch, but the failure path is boot-fatal so releasing the page is unobservable). - TraceThreadMode and the static diagnostics helpers now resolve host primitives through the backend bound to the current thread, falling back to HostPlatform.Current only when no run is active (identical on supported configs, honors injection everywhere a backend exists). - HostPlatform.Create additionally requires an x64 process so native Windows ARM64 fails with the promised PlatformNotSupportedException instead of emitting x86-64 stubs into an ARM64 process. |
||
|
|
081760be3f |
[AGC/Vulkan] Support multiple render targets (#149)
* [AGC] Support multiple typed pixel outputs Emit dense float, uint, and sint fragment outputs for sparse guest MRT slots. Preserve disabled components across partial exports, validate dense host locations, and retain the single-output compiler overload for compatibility. * [Vulkan] Execute translated draws with multiple color attachments Carry every active color target and its effective shader/register write mask through one Vulkan draw. Add per-attachment blending, independentBlend negotiation, device/format validation, multi-attachment synchronization, and safe image recreation after in-flight work completes. * [ShaderDump] Add MRT edge-case coverage Cover sparse mixed-type outputs, partial exports, merged partial exports, independent blend layouts, eight attachments, and invalid host locations. Run the synthetic shader suite in CI. --------- Co-authored-by: Dafenx <196083014+Dafenxz0@users.noreply.github.com> |
||
|
|
e604fb606d |
Fix pak size-collision that crashed Quake right after the intro demo (#187)
* [Tests] Add SharpEmu.Libs.Tests project Introduce an xunit project for the HLE libs with a minimal ICpuMemory fake, so library-level exports and helpers can be exercised without a live guest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [Ampr] Disambiguate pak size-collisions by read locality PakDirectoryTracker resolves a sequential AMPR read (offset -1) back to an absolute pak offset by matching the requested byte count against the PACK directory. When several files share that byte count it took the first unconsumed match in directory order, which mis-resolves out-of-order reads: progs/h_ogre.mdl and bots/navigation/death32c.nav are both 0x3A34 bytes, and death32c.nav sits earlier in the directory and is never read during Quake's intro demo, so requesting h_ogre.mdl returned the nav file's bytes. The engine then parsed "NAV2" as a brush model, failed the version check and aborted. Pick the unconsumed same-size entry nearest the running read cursor instead. id archives cluster related assets and the guest streams them with locality, so this lands on the intended file; contiguous same-size runs (the gfx/weapons/ww_*.lmp icons) still resolve in packed order. Verified against a Quake dump: the abort is gone, h_ogre.mdl reads correctly, and the intro demo reaches its main loop and renders instead of dying at the error dialog. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
df53ff59d9 |
[Json] Implement sce::Json::Value and String (construct / set / destroy) (#169)
* [Json] Implement sce::Json::Value and Json::String construct/set/destroy libSceJson previously only had the Initializer/MemAllocator setup path. The Value and String classes themselves were entirely absent, so a Prospero title that builds a JSON tree (Quake PPSA01880 does, to shape a web-API request) hit unresolved imports and faulted on the call. The imports it left unresolved right before its access violation are exactly these Value ctors/setters and String ctor/dtor. Model the Value/String payload host-side (JsonObjectHeap), keyed by the guest `this` pointer, following the handle-shadow pattern already used by Ngs2Exports. The guest object bytes are deliberately not written: these objects are usually stack-allocated with an unknown real layout, and writing a guessed layout risks smashing an adjacent stack canary (the same hazard the AudioOut2 context-param note in this tree records). Constructors and setters follow the Itanium ABI and return `this` in rax, which is correct whether the real setter returns void or Value&. Covered NIDs (complete-object C1/D1 variants, matching the observed imports): Value(default/bool/long/ulong/double/ValueType/char*/String), Value::~Value, Value::set(bool/long/ulong/double/ValueType/char*/String), Value::clear, String(char*/default/copy), String::~String. Only the payload the guest can reach through library methods is modelled; direct guest reads of the object bytes are out of scope and would need observed layout evidence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [Tests] Add SharpEmu.Libs.Tests covering the Json Value/String exports First test project for SharpEmu.Libs (xunit), the SharpEmu.Libs.Tests layout the maintainer already agreed to in issue #36. - A FakeCpuMemory (single contiguous region) drives the exports at the CpuContext level with no live guest. - Direct-call tests: ctor/setter round-trips for bool/int/uint/double (read from xmm0)/char*/String/ValueType, destructor cleanup, and the graceful-degradation paths (missing String shadow and a faulting char* pointer both fall back to the empty string instead of throwing). - Registration test: a real ModuleManager scans SharpEmu.Libs and the nine NIDs Quake left unresolved now resolve to the libSceJson exports and dispatch cleanly (returns `this` in rax). InternalsVisibleTo exposes JsonObjectHeap to the test assembly. The test project's packages.lock.json is committed for CI locked-mode restore; CI does not run tests yet, left as a maintainer decision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [Json] Add Initializer::setGlobalNullAccessCallback Quake calls it during kexPSNWebAPI::Initialize and treats the not-found error as fatal for the whole Np Web API bring-up. Store the guest hook (never invoked by this HLE: shadows degrade to defaults instead of dereferencing missing members) and return success. Verified against the dump: the "setGlobalNullAccessCallback failed (0x80020002)" line is gone and kexPSNWebAPI::Initialize now logs "Np Web API Initialized"; the next blockers are sceNpAuthCreateRequest and sceUserServiceInitialize ordering, outside libSceJson. Also pins both Json test classes to one xunit collection: they share JsonObjectHeap statics and parallel class execution raced ResetForTests against a running test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ae5ef0abe7 |
Add SaveData transaction and NP UDS layout HLE stubs (#168)
* Add SaveData transaction and NP UDS layout HLE stubs Wire Prepare, Commit, and Umount2 for implicit save transactions, unregister guest mounts on Umount2, and add NP UDS CreateEvent, DestroyEvent, and EventPropertyObjectSetString for layout-load imports. * Add NP UDS SetArray and PostEvent layout HLE stubs Add sceNpUniversalDataSystemEventPropertyObjectSetArray and sceNpUniversalDataSystemPostEvent for layout-load imports on PPSA02929. |
||
|
|
5e2c21edf1 |
Fix historic SysAbi exports bound to wrong symbol names (#167)
Move KMcEa+rHsIo from libKernel MapMemory mislabel to sceAvPlayerAddSource. Align WV1GwM32NgY ExportName with sceNpWebApi2PushEventCreateHandle. Behavior unchanged. |
||
|
|
de13735972 |
[CommonDialog] Fix dialog state machine and add MsgDialog progress-bar exports (#163)
Rework the sceMsgDialog and sceSaveDataDialog HLE state machines so the full Initialize -> Open -> poll -> GetResult -> Close/Terminate lifecycle honors the common-dialog contract, and add the three missing sceMsgDialogProgressBar* exports. - Fix an unreachable close path: sceSaveDataDialogClose already did a RUNNING -> FINISHED compare-exchange, but Open jumped straight to FINISHED, so RUNNING never existed and Close could only return NOT_RUNNING. Open now enters RUNNING and the first status poll advances it to FINISHED. Same model applied to sceMsgDialog. - Return the real SCE_COMMON_DIALOG_ERROR_* codes (0x80B8xxxx) from sceMsgDialog* instead of emulator-internal result codes, with the missing argument/state guards (ARG_NULL, NOT_INITIALIZED, BUSY, NOT_FINISHED, NOT_RUNNING). - GetResult reports buttonId = 1 (affirmative) instead of 0, the invalid sentinel a yes/no prompt could mis-branch on. - Add sceMsgDialogProgressBarSetValue, sceMsgDialogProgressBarInc and sceMsgDialogProgressBarSetMsg (NIDs wTpfglkmv34, Gc5k1qcK4fs, 6H-71OdrpXM), gated on the service being initialized. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
290f5fd3d7 |
Add SysAbi ExportName name2nid check script (#152)
* Add SysAbi ExportName name2nid check script * Make SysAbi ExportName check green on tip with catalog skips and one Np rename |
||
|
|
caf859cc52 |
Fix guest shutdown when VideoOut window is closed (#184)
Propagate Silk window close to runtime teardown so audio and CPU workers stop instead of continuing after the presentation window is dismissed. |
||
|
|
d8397b022e |
Performance Improvements and Optimization Tweaks (#156)
* Improve Gen5 rendering performance and compatibility * Pin .NET SDK for locked restore --------- Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com> |
||
|
|
1f09de8896 |
[AGC] Complete gfx10 v_cmpx_f32 decode and emit ordered/unordered float compares (#122)
* [AGC] Complete gfx10 v_cmpx_f32 decode and emit ordered/unordered float compares Add the missing v_cmpx_*_f32 VOPC decode entries (0x17-0x1C, 0x1F) and emission for the ordered/unordered predicates: nlg maps to OpFUnordEqual, while o/u are lowered from OpIsNan (unordered = isnan(a) || isnan(b), ordered = !unordered) because SPIR-V's OpOrdered/OpUnordered require the Kernel capability and are invalid in Vulkan shader modules. Opcode numbers cross-checked against LLVM's llvm-mc regression tests (llvm/test/MC/AMDGPU/gfx10_asm_vopc.s, gfx10_asm_vopcx.s); emitted lowering validated with spirv-val --target-env vulkan1.1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AGC] Write VCC only for non-X vector compares On gfx10 the VCmpx encodings have no sdst and define EXEC only, so the unconditional VCC store clobbered VCC on every VCmpx. Move the VCC store to the non-X path; EXEC keeps the existing old-EXEC & condition update. Addresses review feedback on #122. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: tensorcrush <tensorcrush@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
ddc452b4fc |
[Pad] Approximate trigger vibration on XInput (#140)
Co-authored-by: Dafenx <196083014+Dafenxz0@users.noreply.github.com> |
||
|
|
61a97baf85 |
[AGC] Emit Gen5 v_sad_u32 (#138)
Co-authored-by: Dafenx <196083014+Dafenxz0@users.noreply.github.com> |
||
|
|
e80f96ecf5 | Align SysAbi export names with Aerolib NID catalog (#137) | ||
|
|
d49c0f1f10 |
Emit Gen5 packed-integer and bit-count ops (#135)
Co-authored-by: Dafenx <196083014+Dafenxz0@users.noreply.github.com> |
||
|
|
26a570633c |
[HLE] Add strchr/strrchr/memchr/strcat/strncat/strstr libc exports (#132)
Implement six missing libc string/memory search and concatenation routines in the kernel compat layer. Titles frequently call these during startup string handling (path parsing, config lookups, format string assembly), and without them the loader currently falls through to unresolved-import handling. The implementations follow the existing byte-at-a-time compat helpers (TryReadCompat/TryWriteCompat) already used by strcpy/strncpy/memcmp, matching native semantics: strchr/strrchr scan through and including the terminator, memchr is bounded strictly by count, strcat/strncat overwrite the destination terminator and re-terminate, and strstr returns the haystack pointer for an empty needle. NIDs are the libSceLibcInternal/libc symbol hashes for each name. |
||
|
|
a84d2344fb |
Deadcell fix (#144)
* [agc] add resource registration * [libc] use C locale for printf |
||
|
|
787d3a1efb |
Fix Gen5 boot and restore stable AGC rendering (#139)
Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com> |
||
|
|
d43edc865a |
Agent/fix gen5 thread agc compat (#130)
* Fix Gen5 thread and AGC compatibility * Trim compatibility comments * Report selected Vulkan GPU * Clean up CPU title label * Improve emulator frame pacing and performance * Regenerate package locks with pinned SDK --------- Co-authored-by: Spooks4576 <Spooks4576@users.noreply.github.com> |
||
|
|
cf6964710a | [emulator] Improve emulator performance by optimizing memory access and reducing unnecessary overhead in kernel and CPU execution paths (#131) | ||
|
|
4f028d0483 |
Expand Aerolib from nids.csv and wire socket/net kernel NID handlers (#128)
* Expand Aerolib catalog from nids.csv and wire socket/net NID handlers Load authoritative NID pairs from scripts/nids.csv with ps5_names fallback. Replace mislabeled kernel zero stubs with socket/connect/bind/getsockname HLE and sceNet byte-order exports backed by the CSV symbol names. * Add inet_pton, htons, and bzero kernel compat with CSV NIDs Wire libc network helpers using authoritative NID names from nids.csv instead of synthetic Gst* exports used on the crt-loader branch. * Fix REUSE annotation for scripts/nids.csv * Drop bundled nids.csv; extend ps5_names and regenerate Aerolib Remove scripts/nids.csv from the repository and fold csv-only symbol names into scripts/ps5_names.txt so Aerolib keeps the full catalog via name2nid. |
||
|
|
4600a2ed1f | Display game icon (icon0.png) in window title bar (#124) | ||
|
|
c5c5ee1f36 | [logging] Fetch hardware info, add to video window title and log (#117) | ||
|
|
9cdc8550ec |
[pthread] Fix cond-var lock inversion and POSIX timedwait ABI (upstream #113) (#115)
Two follow-ups to upstream #102's condition-variable changes, as analyzed in upstream issue #113: - The pending-signal consume path reacquired the guest mutex while still holding the condition state lock, inverting lock order against cond-signal (mutex -> SyncRoot) and deadlocking both threads. Leave the condition lock before relocking, matching the normal wake path. This unfroze Dreaming Sarah (PPSA02929) at its title screen. - pthread_cond_timedwait's third argument is a pointer to an absolute CLOCK_REALTIME timespec, not a relative microsecond count; the guest address was being truncated into a duration, yielding arbitrary timeouts. Read the timespec and convert to a relative wait. scePthreadCondTimedwait keeps its separate relative-time ABI. |
||
|
|
d6fccedab8 |
[AGC] Support scalar high 32-bit multiplies (#109)
Signed-off-by: kostyaff <filipchukks@gmail.com> |
||
|
|
fed7a6d062 |
[AGC] Decode gfx10 SOPP hint instructions (#108)
* [AGC] Decode gfx10 SOPP hint instructions s_clause (0x21), s_waitcnt_depctr (0x23), s_round_mode (0x24) and s_denorm_mode (0x25) were missing from the SOPP decode table, so any shader containing one of these scheduling/mode hints failed to decode entirely with unknown-sopp. No emitter changes are needed: non-branch SOPP instructions are already emitted as no-ops. Opcodes verified against LLVM SOPInstructions.td (SOPP_Real_32_gfx10); decode and end-to-end SPIR-V compilation verified with a synthetic program containing all four hints. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AGC] Narrow SOPP additions to scheduling hints only Per review: s_round_mode (0x24) and s_denorm_mode (0x25) write the shader floating-point MODE state, and the emitter's blanket SOPP no-op would have silently ignored their simm16 payloads, trading a loud decode failure for a potential floating-point semantics mismatch. They are removed and keep failing decode explicitly until their semantics are modeled or conservatively validated. s_clause (0x21) and s_waitcnt_depctr (0x23) remain: they are pure scheduler/dependency hints with no value semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
50f78f3713 | [VideoOut] Drive vblank from a timer so UE render threads advance (#114) | ||
|
|
63b440efcd |
[HLE] Fix guest-thread sync and boot for Unreal Engine titles (#102)
* [HLE] Fix guest-thread sync and boot for Unreal Engine titles Silent Hill: The Short Message (and other UE titles) now boot the full engine thread graph instead of hanging early. Four related fixes: - pthread cond/mutex semantics: retain a signal raised with no waiter as pending, and key block/wake on the state's identity rather than a resolved address that could differ between lock and unlock. This ends the ~1.5M-call cond_wait busy-spin. - Warm HLE type initializers and force-JIT their methods on a host thread at Freeze(). A .cctor or first-time JIT running on a guest thread's hijacked stack fail-fasts the CLR as "Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code". - Guest thread scheduling: pump after a wake so a readied thread actually runs, add a dispatcher thread for when every guest thread is parked, and make the pump-depth guard an atomic CAS. - Route mutex/rwlock lock/unlock off the non-blocking leaf-import fast path so a contended lock can deschedule its guest thread. Ported from the unreal-boot-fixes branch. * [HLE] Keep mutex/rwlock unlock on the leaf-import fast path The previous change routed all mutex/rwlock lock and unlock NIDs off the leaf fast path so a contended lock could deschedule its guest thread. But unlock never blocks, and taking it off the fast path made it slow enough that Demon's Souls' job workers livelocked in a guest spinlock (millions of mutex_unlock calls, no import progress, main thread stuck in sceKernelWaitEventFlag). Only *lock* needs to leave the leaf path. Restore the four unlock NIDs (mutex + rwlock) so guest spinlocks stay cheap, while lock/rd/wrlock remain off it for the blocking case Silent Hill needs. * [HLE] Gate pthread_mutex_lock guest-thread blocking (fixes Demon's Souls) Re-enabling cooperative deschedule on a contended pthread_mutex_lock regressed Demon's Souls: its job workers run on libSceFiber, and blocking a guest thread mid-fiber left sceFiberSwitch returning ESRCH followed by a null fiber-context deref (0xC0000005). Bisect confirmed the pthread change as the cause; the game reaches the same point as before it once the block is skipped. Gate the block behind SHARPEMU_MUTEX_LOCK_BLOCKING (off by default) so contended locks fall through to the synchronous host-thread wait. The rest of the pthread fixes (cond_wait pending signals, identity wake keys) are unaffected. |
||
|
|
0565d01744 |
[AGC] Support VOP3 signed 32-bit multiplies (v_mul_lo_i32, v_mul_hi_i32) (#106)
Two gaps around the VOP3 signed multiplies caused whole-shader SPIR-V compilation failures: - v_mul_lo_i32 (0x16B) decoded correctly but had no emission case, so any shader containing it failed with "unsupported vector opcode VMulLoI32". Its low 32 result bits are identical to the unsigned multiply in two's complement, so it now shares the v_mul_lo_u32 IMul case. - v_mul_hi_i32 (0x16C) was missing from the VOP3 decode table entirely and decoded as an opaque Vop3Raw16C, which also fails at emission. It is now decoded and emitted by sign-extending both operands to 64 bits, multiplying, and taking the upper 32 bits of the product, mirroring the existing v_mul_hi_u32 pattern. Opcode numbers verified against LLVM's AMDGPU backend (VOP3Instructions.td): V_MUL_LO_U32 gfx10 = 0x169, V_MUL_HI_U32 = 0x16a, V_MUL_LO_I32 = 0x16b, V_MUL_HI_I32 = 0x16c. Behavior verified by decoding and fully compiling a synthetic program containing all four multiplies: previously the 0x16C word decoded as Vop3Raw16C and compilation failed at the v_mul_lo_i32 instruction; now all four decode by name and the program compiles to SPIR-V. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
56bf00e9a5 |
[RTC] Port core rtc helpers from PS5 3.20 libs (#105)
Co-authored-by: zocomputer <help@zocomputer.com> |
||
|
|
4b7df8623a |
[AGC] Fix v_fmac_f32 family decoding in Gen5 VOP2 table (#103)
VOP2 opcode 0x2B was mapped to v_ldexp_f32, which is its gfx6/gfx7 assignment. On gfx10-class hardware 0x2B is v_fmac_f32, so any shader using it silently computed ldexp(a, b) instead of dst += a * b. v_ldexp_f32 on gfx10 only exists as VOP3 0x362, which the VOP3 table already maps correctly. Also add the remaining members of the fmac family: - v_fmamk_f32 (0x2C) and v_fmaak_f32 (0x2D), including their mandatory literal dword in instruction sizing and operand construction, reusing the existing v_madmk/v_madak handling. - The VOP3-encoded form of v_fmac_f32 (0x12B), emitted when source modifiers are present. SPIR-V emission reuses the existing v_mac_f32 body (fma with the destination register as addend) and the v_mad/v_fma case group. Opcode assignments verified against LLVM's AMDGPU backend (VOP2Instructions.td): V_FMAC_F32 gfx10 = 0x02b, V_FMAMK_F32 = 0x02c, V_FMAAK_F32 = 0x02d; V_LDEXP_F32 is 0x02b only on gfx6/gfx7 and is VOP3-only 0x362 on gfx10. Decode verified by feeding hand-assembled gfx1013 words through Gen5ShaderTranslator: 0x560A0501 previously decoded as VLdexpF32 and a v_fmamk_f32 program failed with unknown-vop2 op=0x2C; both now decode correctly, and VOP3 0x362 still decodes as VLdexpF32. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
298ef01809 |
[VideoOut] Prefer NVIDIA/discrete GPUs over integrated (#97)
SelectPhysicalDevice took the first device exposing a graphics+present queue. On hybrid-graphics laptops that is the integrated GPU, so the discrete card went unused - and AMD's integrated driver access-violates inside vkCreateGraphicsPipelines while compiling some translated guest shaders, killing the process. The CLR surfaced that native AV as "Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code", which made it look like a CPU/threading fault. Score the candidates instead: NVIDIA parts win, other discrete GPUs come next, and an integrated GPU is only chosen when nothing else can present. SHARPEMU_VK_DEVICE=<substring> pins a specific adapter, and the selected device is logged. |
||
|
|
4bd42795c7 |
[logging] Migrate HLE diagnostics to SharpEmuLog (#80)
Signed-off-by: Digote <45742711+Digote@users.noreply.github.com> |
||
|
|
c03ca32a02 |
fix(vfs): harden getdirentries and APR filepath resolution (#77)
* fix(vfs): defer host cursor commit in getdirentries
Follow-up VFS hardening task applying the same guest-writes-first discipline established in the time subsystem to directory enumeration.
Deferred Commit in KernelGetdirentriesCore:
- Reordered guest output so the 512-byte dirent buffer is written first via TryWriteCompat, basep is updated second (when non-null) via TryWriteUInt64Compat, and directory.NextIndex is advanced only after both guest writes succeed.
- Removed the early basep write at method entry that could mutate guest memory before buffer validation and advance the host cursor before a successful dirent delivery, causing permanent entry loss on MEMORY_FAULT at bufferAddress.
- EOF handling: when currentIndex >= Entries.Length, write basep with the final offset and return 0 without mutating NextIndex, matching FreeBSD getdirentries(2) semantics and preventing infinite retry loops.
KernelGetdents path: basePointerAddress is passed as 0, so the transaction collapses to buffer write then host cursor advance with no basep side effect.
Out of scope: coalesced {id, size} writes in sceKernelAprResolveFilepathsToIdsAndFileSizes; NetCtl connected-state stubs.
Files: KernelMemoryCompatExports.cs
* fix(vfs): resolve-first bulk commit in APR filepath resolution
Refactored sceKernelAprResolveFilepathsToIdsAndFileSizes to stop writing ids and sizes into guest memory one element at a time.
- Removed the uint.MaxValue placeholder write at the start of each loop iteration.
- Path resolution and file size lookup now fill host-side buffers first; on EFAULT or NOT_FOUND the guest ids/sizes arrays are left untouched.
- ids and sizes are packed into contiguous byte buffers and written with one TryWriteCompat call per output array instead of separate TryWriteUInt32Compat / TryWriteUInt64Compat per index.
- AmprFileRegistry.Register is called only after guest writes succeed.
- AmprFileRegistry.ComputeFileId is internal so ids can be computed without registering paths during the resolve loop.
Files: KernelMemoryCompatExports.cs, AmprFileRegistry.cs
|
||
|
|
6e2878f2ff | Add commit hash to video window title (#93) | ||
|
|
8c1507777c |
[agc] Reset transparent Chowdren effect-layer fills (#83)
Treat the exact untextured transparent-black premultiplied fill used by Chowdren as an overwrite. This prevents Dreaming Sarah fog and vignette render targets from accumulating across frames; SHARPEMU_DISABLE_TRANSPARENT_FILL_CLEAR=1 restores prior behavior. |
||
|
|
5aadb7495a |
Libs: add libSceDiscMap HLE exports (ported from Kyty) (#79)
Port the libSceDiscMap stubs from Kyty (InoriRus/Kyty, MIT) into the SysAbiExport model. Disc-installed titles probe these NIDs on most file accesses to decide whether a read must be redirected to the disc drive; answering that every request is already resident on internal storage keeps I/O on the regular file system path instead of failing with unresolved-import errors. - sceDiscMapIsRequestOnHDD (lbQKqsERhtE): validates args, writes 1 to the result pointer, returns 0 - fJgP+wqifno / ioKMruft1ek: zero-fill the three output pointers, return 0 (names not present in ps5_names.txt; kept as descriptive Unknown exports like the existing sceKernelUnknown* convention) - DISC_MAP_ERROR_INVALID_ARGUMENT (0x81100001) on null pointers, matching the documented libSceDiscMap error range - optional tracing via SHARPEMU_LOG_DISCMAP=1 Co-authored-by: j92580498-max <252151737+j92580498-max@users.noreply.github.com> |
||
|
|
cdee77521e |
Fix UnmanagedCallersOnly boot crash & Core Engine Improvements (CPU, HLE, AGC) (#81)
* [agc] WAIT_REG_MEM suspend/resume, draw packet fixes, new HLE exports, debug cleanup
Rebased onto upstream
|
||
|
|
e1cf5b13ef |
[AGC] Quake rendering progress: WAIT_REG_MEM, draw fixes, VideoOut, and HLE improvements (#68)
* [agc] WAIT_REG_MEM suspend/resume, draw packet fixes, new HLE exports, debug cleanup
Rebased onto upstream
|
||
|
|
de4fc1e1a8 |
Add sceKernelNanosleep to libKernel (#72)
Implements the sceKernelNanosleep export (NID QvsZxomvUHs) for both Gen4 and Gen5 targets. Reads the requested timespec from guest memory, validates the pointer and tv_nsec range, sleeps for the requested duration, and zeroes the optional remaining-time struct on completion. Also fixes: reading rqtp as a guest pointer to a timespec (tv_sec/tv_nsec int64 pair) instead of raw register values, and keeps the optimized sceKernelUsleep short-sleep path untouched. Co-authored-by: par274 <par274@users.noreply.github.com> |
||
|
|
5e76554514 |
core: unify clock dispatch logic, add precise clocks, and enforce coalesced time writes (#71)
Comprehensive refactoring of the system time subsystem to unify clock dispatching, support precise clock extensions, and secure memory boundaries against partial state corruption.
Centralized Clock Dispatch Engine:
- Extracted shared elapsed-tick calculation and clock-routing math into a unified internal static bool ResolveClockTime() dispatch engine under KernelRuntimeCompatExports.cs.
- Moved all clock identifiers from KernelMemoryCompatExports to KernelRuntimeCompatExports as internal const int constants to eliminate cross-file duplication while preserving raw compiler switch-case layout optimizations.
- Added native alias mapping support for CLOCK_REALTIME_PRECISE (9) and CLOCK_MONOTONIC_PRECISE (11).
- Hardened the Orbis sceKernelClockGettime path by routing it through the new dispatcher, resolving a pre-existing logic flaw where any non-zero clock_id incorrectly fell back to monotonic time. Invalid IDs now properly fail with ORBIS_GEN2_ERROR_INVALID_ARGUMENT.
Coalesced Single-Transaction Memory Writes:
- Replaced consecutive isolated 8-byte scalar writes across POSIX clock_gettime, gettimeofday, and Orbis sceKernelClockGettime/sceKernelGettimeofday with safe single-transaction 16-byte stackalloc byte buffer writes via BinaryPrimitives and ctx.Memory.TryWrite. This entirely prevents partial memory state corruption on virtual page boundaries.
- Implemented a single 8-byte coalesced zero-fill transaction for the deprecated/legacy timezone buffer (timezoneAddress != 0), aligning it with standard FreeBSD stub behavior.
- Standardized POSIX failure path routines. Write faults cleanly issue TrySetErrno(ctx, Efault) while safely omitting explicit manual Rax writes, letting the import dispatcher natively sign-extend the return -1 value to 0xFFFFFFFFFFFFFFFF.
Zero-Alloc Host RDTSC Execution Stub:
- Patched CreateRdtscReader() to stream native architecture opcodes out of stack-allocated spans directly into host executable memory zones (VirtualAlloc) via unsafe { Buffer.MemoryCopy(...) }, completely removing the high-frequency .ToArray() runtime allocation overhead on the hot path.
Files: KernelRuntimeCompatExports.cs, KernelMemoryCompatExports.cs
|
||
|
|
3a24db567f |
core: implement coalesced writes for gettimeofday and set POSIX EFAULT (#70)
Follow-up task to enforce coalesced guest memory writes within the gettimeofday subsystem, removing remaining partial-write risks on virtual memory page boundaries. * sceKernelGettimeofday Hardening: Replaced consecutive isolated 8-byte scalar writes with a single 16-byte coalesced transaction buffer using stackalloc byte[16] and BinaryPrimitives. It preserves native Orbis semantics by returning ORBIS_GEN2_ERROR_MEMORY_FAULT on failure states without side-effect partial-writes. * POSIX gettimeofday Compliance: - Applied the identical single-transaction 16-byte write pattern for the timeval structure. - Implemented a single 8-byte coalesced zero-fill transaction for the deprecated/legacy timezone buffer (timezoneAddress != 0) using BinaryPrimitives.WriteInt32LittleEndian, aligning it with standard FreeBSD stub behavior. - Integrated proper TrySetErrno(ctx, Efault) tracking upon write failures. The method safely omits explicit manual Rax writes on error paths, allowing the import dispatcher to cleanly sign-extend the return -1 value to 0xFFFFFFFFFFFFFFFF. Out of scope: Subsystem clock and timeval validation is now fully complete; no further temporal partial-write vulnerabilities remain within the core runtime memory compat layers. Files: KernelRuntimeCompatExports.cs |
||
|
|
19added142 |
core: implement sceKernelGetCompiledSdkVersion based on target generation (#66)
Replaced the no-op stub for sceKernelGetCompiledSdkVersion with a proper runtime compliance implementation. Runtime Validation: Added explicit NULL pointer verification for the destination buffer address (versionAddress == 0). It returns ORBIS_GEN2_ERROR_INVALID_ARGUMENT and sign-extends the target Rax register to 0xFFFFFFFF80020003, strictly mirroring the PthreadJoin error-handling pattern of this subsystem. Target-Based SDK Fallback: Implemented deterministic fallback version routing based on ctx.TargetGeneration (0x05000000 for Gen4 and 0x09000000 for Gen5 standard Orbis layout). This ensures guest applications pass early firmware checks until native metadata extraction is implemented. Atomic Memory Write: Secured the state write sequence via the native ctx.TryWriteUInt32 layer, correctly catching virtual memory page faults, propagating ORBIS_GEN2_ERROR_MEMORY_FAULT to Rax, and safely bypassing partial-write state corruption. Out of scope (follow-up): Native parsing of the compiled SDK version flags directly out of the guest ELF note/metadata sections. |
||
|
|
edb4eb86a2 |
[kernel] Wake blocked waiters on semaphore signal, cancel, and delete (#67)
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. |
||
|
|
79a7437cd8 |
[GUI] Added Atrac9 audio decoder and improved GUI with audio preview and controller support (#64)
* [GUI] Added Atrac9 audio decoder and improved GUI with audio preview and controller support * fix: package.lock.json for SharpEmu.CLI to match the other projects * fix: packages.lock.json file to include new dependencies for GUI improvements * rollForward: "disable" |