How SharpEmu Handles PS5 NID Dispatch: A Two-Stage Resolution System

SharpEmu resolves PlayStation Suite (PSS) function calls by loading a symbol catalog from aerolib.bin, registering HLE methods via attributes, and dispatching them through a concurrent dictionary that maps NIDs to delegates.

The par274/sharpemu project implements a clean two-stage mechanism to handle PS5 NID dispatch, bridging the gap between numeric function identifiers in emulated PlayStation binaries and their high-level emulated (HLE) implementations in .NET. This architecture separates symbol resolution from runtime dispatch, enabling efficient lookup of PSS functions during emulation.

Stage 1: Symbol Catalog Resolution via Aerolib

At initialization, SharpEmu loads the embedded binary resource aerolib.bin through the Aerolib.LoadFromEmbeddedBinary method. This file contains the canonical mapping of NIDs (numeric IDs) to export names for all PlayStation Suite functions.

The Aerolib class implements the ISymbolCatalog interface and exposes two critical lookup methods:

  • TryGetByNid(string nid, out SysAbiSymbol) – Retrieves the full symbol record using the numeric identifier.
  • TryGetByExportName(string name, out SysAbiSymbol) – Performs reverse lookup from function name to NID.

The catalog is globally accessible via Aerolib.Instance, allowing any HLE module to resolve symbol information without hardcoding NIDs. This abstraction ensures that symbol management remains centralized and maintainable as the emulator's compatibility layer expands.

Stage 2: Module Registration and Runtime Dispatch

When the emulator loads a .NET assembly containing HLE implementations, ModuleManager.RegisterFromAssembly scans all types and methods for the [SysAbiExport] attribute. This process builds the runtime dispatch infrastructure through three sequential steps:

  1. Resolution: ResolveExportInfo queries the supplied ISymbolCatalog (typically Aerolib.Instance) to fill missing NIDs for methods that specify only export names, or vice versa.
  2. Registration: The resolved NID and a compiled delegate accepting CpuContext are stored in the concurrent dictionary _dispatchTable.
  3. Invocation: When the CPU backend encounters a PSS call, it extracts the import NID and invokes ModuleManager.TryDispatch(nid, context, out result) to execute the corresponding HLE method.

If the NID exists in _dispatchTable, the associated SysAbiFunction delegate executes immediately. Otherwise, the emulator returns an "NID not found" error to the guest system.

Implementation Example

The following code demonstrates how HLE methods are defined, registered, and dispatched in the SharpEmu architecture:

// HLE method implementation with explicit NID attribution
[SysAbiExport(Nid = "scePssAudCreateSourcePlayer")]
public static int CreateSourcePlayer(CpuContext ctx)
{
    // Audio subsystem emulation logic...
    return (int)OrbisGen2Result.ORBIS_GEN2_SUCCESS;
}
// Registration during emulator startup
var runtime = new SharpEmuRuntime(...);
runtime.ModuleManager.RegisterFromAssembly(
    typeof(PssAudioExports).Assembly,  // Assembly containing HLE exports
    Generation.Gen5,                   // Target PS5 generation
    Aerolib.Instance);                 // Symbol catalog for NID resolution
// Runtime dispatch from the CPU backend
string nid = "scePssAudCreateSourcePlayer";
if (moduleManager.TryDispatch(nid, cpuContext, out var result))
{
    // HLE method executed; result contains return value
}
else
{
    // Handle missing NID error
}

Key Source Files

Understanding PS5 NID dispatch requires familiarity with these specific components in the par274/sharpemu repository:

File Role
src/SharpEmu.HLE/ModuleManager.cs Contains the central _dispatchTable and the TryDispatch logic that routes NIDs to HLE delegates.
src/SharpEmu.HLE/Aerolib/Aerolib.cs Implements ISymbolCatalog and loads the NID-to-export-name mappings from the embedded aerolib.bin resource.
src/SharpEmu.HLE/ISymbolCatalog.cs Defines the interface contract for symbol lookup used by the module registration system.
src/SharpEmu.Core/Runtime/SharpEmuRuntime.cs Orchestrates initialization, wiring the ModuleManager with the Aerolib catalog at startup.

Summary

  • SharpEmu uses a decoupled two-stage architecture for PS5 NID dispatch: symbol catalog loading and runtime dispatch table management.
  • The Aerolib class provides authoritative NID-to-name mappings via TryGetByNid and TryGetByExportName, sourcing data from aerolib.bin.
  • ModuleManager.RegisterFromAssembly automatically discovers HLE methods marked with [SysAbiExport] and populates the _dispatchTable with resolved NIDs.
  • ModuleManager.TryDispatch serves as the hot path for the CPU backend, executing the correct HLE delegate in constant time via concurrent dictionary lookup.
  • This design allows the emulator to handle PSS function calls without embedding NID constants directly in the HLE implementation code.

Frequently Asked Questions

What is the purpose of the aerolib.bin file in SharpEmu?

The aerolib.bin file is an embedded binary resource containing the official mapping between PlayStation Suite NIDs (numeric identifiers) and their corresponding export names. SharpEmu loads this via Aerolib.LoadFromEmbeddedBinary at startup to ensure accurate symbol resolution without hardcoding thousands of NID strings throughout the codebase.

How does SharpEmu handle methods that only specify an export name without a NID?

When ModuleManager.RegisterFromAssembly processes a method annotated with [SysAbiExport] that lacks a NID parameter, it invokes ResolveExportInfo to query the ISymbolCatalog (typically Aerolib.Instance). The catalog's TryGetByExportName method retrieves the corresponding NID, ensuring every registered function has a resolved numeric identifier before entering the dispatch table.

What happens when the emulator encounters an unknown NID during execution?

If the CPU backend calls ModuleManager.TryDispatch with a NID that does not exist in the _dispatchTable, the method returns false and the emulator reports an "NID not found" error to the guest system. This typically indicates that the HLE module for that specific PlayStation Suite function has not yet been implemented or registered.

Can the symbol catalog be replaced or extended for different PlayStation generations?

Yes. While Aerolib is the default implementation used for PS5 (Generation.Gen5), the ModuleManager accepts any ISymbolCatalog implementation. This allows the emulator to support multiple PlayStation generations by supplying different catalog instances during the RegisterFromAssembly call, making the architecture flexible for PS4 or future generation compatibility layers.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →