How SharpEmu Manages Generation Targeting for OrbisGen2: A Deep Dive into PlayStation 4 and 5 API Compatibility
SharpEmu uses a bitwise flag enum and [ExportedFunction] attributes to selectively expose PlayStation 4 (Gen4) or PlayStation 5 (Gen5) HLE APIs at runtime, filtering system calls through generation targeting while maintaining a unified OrbisGen2 error code surface.
The SharpEmu emulator implements a sophisticated dual-generation architecture that cleanly separates PlayStation 4-style APIs from their PlayStation 5 successors. By leveraging a flag-based Generation enum and targeted export attributes, the codebase hosted at par274/sharpemu enables the same emulator binary to host both Gen4 and Gen5 implementations while keeping the underlying system call implementations isolated.
The Generation Flag Architecture
At the core of SharpEmu's generation targeting system lies a compact [Flags] enum that defines available hardware generations. Located in /src/SharpEmu.HLE/Generation.cs, this enumeration provides the bitwise foundation for all generation-based filtering:
[Flags]
public enum Generation {
None = 0,
Gen4 = 1,
Gen5 = 2
}
Each bit represents a distinct console generation, allowing simultaneous targeting through bitwise OR operations. When the emulator initializes, it sets a single generation flag in SharpEmuRuntimeOptions (typically via command-line flags like --gen5), establishing the execution context for the current session.
Kernel-Style Result Codes
Regardless of which generation is active, all OrbisGen2 APIs return standardized error codes defined in /src/SharpEmu.HLE/OrbisGen2Result.cs. This enum ensures consistent kernel-style semantics across both Gen4 and Gen5 execution paths:
public enum OrbisGen2Result : int {
ORBIS_GEN2_OK = 0,
ORBIS_GEN2_ERROR_PERMISSION_DENIED = unchecked((int)0x80020001),
/* ... */
ORBIS_GEN2_ERROR_MEMORY_FAULT = unchecked((int)0x80020101)
}
Every exported function casts these results to int before returning, preserving the PlayStation kernel's error code conventions regardless of the underlying generation.
Export Function Targeting
SharpEmu annotates each High-Level Emulation (HLE) function with an [ExportedFunction] attribute that specifies which generations the implementation supports. This metadata-driven approach appears throughout the library exports, such as in /src/SharpEmu.Libs/VideoOut/VideoOutExports.cs:
[ExportedFunction(Target = Generation.Gen4 | Generation.Gen5)]
public static int sceVideoOutGetResolution(IntPtr ctx, int videoOutHandle,
out int width, out int height)
{
// Implementation supports both generations
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
Functions can restrict themselves to specific generations by omitting unwanted flags. A Gen5-exclusive feature would declare:
[ExportedFunction(Target = Generation.Gen5)]
public static int scePlayGoInitialize(IntPtr ctx)
{
// PlayStation 5 specific functionality
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
Runtime Selection and Dispatch
The generation filtering mechanism activates during system call dispatch. When a guest thread invokes a system call, the runtime evaluates the bitwise intersection between the export's Target flag and the current SharpEmuRuntime.Options.Generation setting.
The dispatch logic in /src/SharpEmu.Core/Runtime/SharpEmuRuntime.cs performs this check:
// Runtime selection established at startup
runtime.Options.Generation = Generation.Gen5; // User-specified via CLI
// During system call dispatch:
if ((export.Target & runtime.Options.Generation) != 0) {
// Expose the function to the guest
ExecuteExport(export);
} else {
// Hide or reject the call
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED;
}
This design ensures that only functions explicitly marked for the current generation become callable. If a Gen4-only title attempts to invoke a Gen5-exclusive API while running in Gen4 mode, the dispatcher intercepts the call before execution.
Practical Implementation Examples
Developers extending SharpEmu's HLE layer follow a three-step pattern to implement generation targeting:
1. Declare multi-generation support for shared APIs:
[ExportedFunction(Target = Generation.Gen4 | Generation.Gen5)]
public static int sceAudioOutOpen(IntPtr ctx, int userId, int type,
int index, uint len, uint freq, uint param)
{
// Shared audio implementation
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
2. Restrict new APIs to specific generations:
[ExportedFunction(Target = Generation.Gen5)]
public static int sceSystemServiceHideSplashScreen(IntPtr ctx)
{
// PS5-specific system service
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
3. Configure runtime options at initialization:
var options = new SharpEmuRuntimeOptions
{
Generation = Generation.Gen5 // Forces visibility of Gen5-only exports
};
var runtime = new SharpEmuRuntime(options);
Summary
- Flag-based architecture: The
[Flags]enum inGeneration.csenables bitwise combination of Gen4 and Gen5 targets. - Metadata-driven filtering: The
Targetproperty on[ExportedFunction]attributes declares generation compatibility without conditional compilation. - Runtime dispatch: The dispatcher checks
(export.Target & runtime.Options.Generation) != 0to determine API visibility. - Unified error surface:
OrbisGen2Resultprovides consistent kernel-style error codes across both generations. - Clean separation: Generation targeting allows shared infrastructure while isolating incompatible PlayStation 4 and PlayStation 5 behaviors.
Frequently Asked Questions
What is OrbisGen2 and how does it relate to generation targeting?
OrbisGen2 refers to SharpEmu's implementation layer for Sony's second-generation Orbis operating system APIs, encompassing both PlayStation 4 (Gen4) and PlayStation 5 (Gen5) system call interfaces. The generation targeting mechanism allows the emulator to expose the appropriate subset of these APIs based on which console generation the user is emulating, ensuring that a PlayStation 4 game cannot accidentally invoke PlayStation 5-specific kernel functions.
How does SharpEmu filter system calls based on the selected generation?
The emulator maintains a Generation flag in SharpEmuRuntimeOptions that is set at startup (often via command-line arguments like --gen5). When dispatching system calls, SharpEmu performs a bitwise AND operation between this runtime flag and the Target flag specified on each [ExportedFunction] attribute. If the result is non-zero, the function is exposed to the guest; otherwise, it remains hidden or returns a "not implemented" error.
Can a single HLE function support multiple generations?
Yes. Developers specify multiple generations using bitwise OR syntax in the attribute declaration, such as [ExportedFunction(Target = Generation.Gen4 | Generation.Gen5)]. This approach is common for system calls that remained functionally identical between the PlayStation 4 and PlayStation 5, allowing code reuse while maintaining the ability to split implementations if generational differences emerge.
What error handling does SharpEmu use for OrbisGen2 API calls?
All OrbisGen2 functions return values from the OrbisGen2Result enum defined in /src/SharpEmu.HLE/OrbisGen2Result.cs. These kernel-style error codes (prefixed with ORBIS_GEN2_) mirror PlayStation 5 kernel conventions, with ORBIS_GEN2_OK (0) indicating success and values like ORBIS_GEN2_ERROR_MEMORY_FAULT (0x80020101) indicating specific failure conditions. This ensures consistent error semantics regardless of which generation is currently active.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →