~ehmry/retro_frontend

Libretro frontend for the Genode OS framework
Consistent licensing
Add .gitignore
Add new compiliation units from libretro-common

clone

read-only
https://git.sr.ht/~ehmry/retro_frontend
read/write
git@git.sr.ht:~ehmry/retro_frontend

You can also use your local clone with git send-email.

#Libretro frontend for Genode

The retro_frontend component is a Genode native runtime for Libretro. It is the Genode analogue to the Retroarch reference frontend. A Libretro application, typically an game engine or emulator, implements the Libretro API and is linked as a shared library, referred to as a "core". A Libretro frontend loads these cores and registers callbacks for features implemented in the frontend to the core. The frontend is responsible for managing facilities such as framebuffers, audio, input, and driving the core once per-video frame.

This frontend is a from-scratch implementation with a few algorithms borrowed from the libretro-common repository. It is Genode specific and unportable. The current implementation is approximately 2.5 KSLOC, may it serve as a reference for creating other Libretro frontends or Genode components.

#Building

To build the frontend this repository must be placed in a super-repository with a Tup build system and a Genode SDK must be available. Please refer to the authors super-repo as an example. The typical workflow is to define a Genode runtime package that bundles a core and frontend together, build a "mini-depot" using the tup command, and copy this a running Sculpt instance.

#Shortcomings

Most features present in Retroarch are not found in this frontend. Some are appropriate to internalize and others are best implemented as additional components.

To list a few:

  • Analog input axes
  • Vulkan rendering contexts
  • Shaders and pixel upscalers
  • Time acceleration and dilation
  • State rewinding
  • Remote input over IP
  • User interface for frontend management

Patches are welcome!

#Required environment

The 'retro_frontend' requires at minimum 'Timer', 'Nitpicker'. Multiple additional 'Input' sessions are requested when the frontend is configured with
multiple controllers. 'Audio_out' sessions are only requested if a core indicates that it has audio output, in which case the 'Audio_out' service is not optional. Should a core request a hardware rendering context the frontend will dynamically load the Genode EGL library and the Genode environment must route the request for EGL driver library appropriately. Hardware rendering is otherwise an experimental frontend feature and is undocumented.

#Configuration

The most important configuration choice is the core. Core selection is not managed by the frontend but by the parent environment. In the common case every Libretro core linked for Genode will be named libretro.so and the core is selected during packaging. The frontend has no interface or logic for selecting a core at runtime.

For many cores it is sufficient that the frontend will search the root of its file-system for an appropriate image using file extension matching. To invoke this behavior the frontend must have an empty <game/> node within its
configuration. To load a specific image file or data directory the a path attribute may be added to the game node. A second meta attribute is supported,
but its semantics are core-specific and it is seldom used. When no 'game' node is present a core must explicitly indicated to the frontend that no game data is required.

To Load a data file:

<config ...>
  <game path="cartridge.bin"/>
  ...
</config>

#Input

The frontend supports multiple controllers and maps input codes from Genode to Libretro. Controllers are specified by the default-controller and controller
nodes within the configuration. The default-controller configuration is
applied to the Nitpicker Input session and is always connected.

For each controller it is recommended to specify a device type. The frontend and core will assume by default that the controller is a joypad and will likely only poll for joypad input. Defining a device type at the frontend does not restrict or mask controller input from other types, but determines which inputs the core will poll and how inputs are interpreted. There are several basic device types and cores may define special device types by sub-classing from a basic type. A
ore will publish its device types and descriptions through the frontend via the 'controllers' report described later in this document. Cores may additionally publish textual descriptions of each button on each port via an 'input' report.

Base supported device types: 1 | Joypad 2 | Mouse (axes not supported) 3 | Keyboard

Mapping Nitpicker input to a Libretro keyboard on port 0, a joypad with remapping to port 1, and a joypad derived device on port 2:

<config ...>
  <default-controller port="0" device="3"/>
  <controller port="1" device="1">
    <map from="BTN_TR" to="R2"/>
    <map from="BTN_TR2" to="R"/>
    <map from="BTN_TL" to="L2"/>
    <map from="BTN_TL2" to="L"/>
  </controller>
  <controller port="2" device="257"/>
</config>

Port 1 in the example above port show four key remappings. In this example the four trigger or bumper buttons on a gamepad are vertically reversed. The from attribute on a map node indicates a Genode input code and the to attribute
indicates a Libretro input code. Genode input codes may be determined externally
using the test-input component and Libretro input codes may be found in
input report described later. Mappings may only be made within the base
device. Mapping keyboards to joypads is supported but remapping keyboard keys is not. The input_merger component may be used to remap the Libretro keyboard device.

#Variables

Cores usually request their own configuration options from the frontend and these are in turn resolved from the XML configuration provided to the frontend.
These options are published by the core via the frontend variables report described later. A variable node within the configuration binds a configuration value to a configuration key.

<config ... >
  <variable key="region" value="PAL"/>
  ...
</config>

#C runtime

The frontend initializes a POSIX environment for cores by preemptively linking to the Genode C runtime. In this regard the frontend should be configured with a VFS and connections for standard I/O.

<config>
  <vfs>
    <tar name="..."/>
    <dir name="dev"> <log label="core"/> </dir>
  </vfs>
  <libc stdout="/dev/log" stderr="/dev/log"/>
</config>

#Reports

The frontend published information from the core in the form of Report sessions. The reports are variables controllers, and inputs.

#Variables

The variables report contains pairs of keys and possible values. These inform what variables should be included in the configuration passed to the frontend. Valid options for a given variable are separated by the '|' character.

<variables>
  <variable key="nospritelimit" value="No Sprite Limit; disabled|enabled"/>
  <variable key="overscan_v" value="Crop Overscan (Vertical); enabled|disabled"/>
  <variable key="region" value="Region Override; Auto|NTSC|PAL|Dendy"/>
</variables>

#Controllers

The controllers report conveys which device types a core recognizes or expects and informs how inputs to the frontend should be configured.

<controllers>
  <controller port="0">
    <type desc="Gamepad" id="1"/>
    <type desc="Keyboard/Mouse" id="3"/>
    <type desc="Zapper" id="258"/>
  </controller>
</controllers>

#Inputs

The input report describes the function of different inputs over different controller ports. The value of an 'id' attribute on a descriptor node may be used to map from a Genode input to a Libretro input as described previously.

<inputs>
  <descriptor port="0" device="JOYPAD" index="0" id="LEFT" description="D-Pad Left"/>
  <descriptor port="0" device="JOYPAD" index="0" id="UP" description="D-Pad Up"/>
  <.../>
  <descriptor port="0" device="JOYPAD" index="0" id="SELECT" description="Toggle console"/>
  <descriptor port="0" device="JOYPAD" index="0" id="START" description="Menu"/>
</inputs>

#Runtime

#Pausing

The frontend recognizes the Pause key found on standard keyboards and acts accordingly. When the frontend pauses the core is halted and save RAM is dumped
to file for games that are loaded from ROM. When a game warns "do not turn of the power" it is recommended to tap pause twice after the warning to dump the RAM. When unpausing the frontend will load back save RAM and synchronize the framebuffer and audio-out buffer.

#Resetting

The frontend will restart when it receives CTRL-ALT-DELETE (if none of these keys are swallowed by the desktop environment). These will not replace the core but it will cause a game reload. For cores that load cartridge data into memory, this allows the user to replace cartridge files while the frontend is running and a three-finger-salute will cause the frontend to reset with a new cartridge.

Do not follow this link