From DaphneWiki

Jump to: navigation, search



This information applies mostly to the unreleased Daphne v2.0 code base. I am making notes as I develop it. This info is subject to change.

Program Entry

Program entry must be implemented on a platform-specific basis. It can be something like a main() function which loops and calls Daphne functions or whatever you need it to be. In short, these daphne-specific functions must be called regardless of the platform:

  • daphne_init (parses command line and returns an instance of the IGenericLoop interface)
  • IGenericLoop->Go() (call this over and over again until get_quitflag() returns true; this value returns the number of milliseconds before calling Go() again; this value must be respected in order for Daphne to run at the proper speed)
  • daphne_shutdown (when get_quitflag() has returned true)


Platform-specific code inherits from the IPlatform interface and lives in the src/platform folder. It is responsible for providing the following functions:

  • Platform input event handling (keyboard, mouse, joystick, any other abstract means of input)
  • Sound (providing a mechanism to stream PCM audio to audio device)
  • Video (can be as simple as providing platform initialization to OpenGL code to as complex as providing a complete video object)
  • Timing (millisecond precision)


All video functionality is inherits from the IVideoObject interface. The IPlatform interface returns an instance of IVideoObject. So you can extend pre-written IVideoObject classes or write your own from scratch. Very flexible.

TODO: The IVideoObject interface needs to be split up into smaller interfaces because it currently tries to do too many things.


The IPlatform interface has a SoundInit method, one argument of which is AudioStreamCallback. Your platform specific code must call this callback regularly (when audio buffer needs to be filled) to grab PCM audio generated by Daphne's sound code. Daphne's sound code currently is hard-coded to generate audio at 44.1khz, 16-bit stereo.


The platform-specific code is responsible for returning an implementation of the IGenericInput interface. As of right now, it appears it is safe for all platforms to just return an instance of GenericInput.

The platform-specific code must implement some event handler to call the IGenericInput methods input_enable, input_disable to indicate that input has come in. The platform-specific code may optionally also support mouse motion by capturing mouse events and then calling IGenericInput->OnMouseMotion. The platform-specific code may optionally also support keyboard events (to handle Thayer's Quest and SINGE games) by calling IGenericInput->key_enable and IGenericInput->key_disable.

Input Events

The IGenericInput interface provides the option for any code in Daphne to register to receive input events. This means that, for example, a game class could receive events when the user takes a screenshot even though game classes normally have no knowledge of this. The reason for this design is so that the IGenericInput interface doesn't know what it is sending events to (to reduce coupling). The events that are available for notification are: game input (joystick, button), keyboard input, mouse movement, screenshot/pausing of Daphne, when enabling/disabling the debug console.


Daphne has an IGenericLoop interface which is essentially the outer loop that runs over and over again until the program exits. The loop generates an event every 1 millisecond called a "Think" event which basically means that anything that receives this event can check every 1 ms to see if it has any work to do. Any part of the Daphne code can register to receive think events by calling IGenericLoop->RegisterThinkObserver. All game and ldp classes automatically register to receive these events (so if you write your own you will get these events without having to do any work).


All game and laserdisc drivers will receive highly accurate vblank start and stop events. The vblank events are tied to the first emulated CPU's current elapsed cycle count and will have an accuracy of +/- 1 emulated CPU cycles. If the vblank event would come in the middle of a CPU instruction, it will be received after the CPU instruction has finished.

Vblank event calculation is self-correcting meaning that on average, the space between vsync pulses will constantly be approaching (1001 * 1000000) / (1000 * 60) microseconds (~16.6 milliseconds).

If the game driver has no emulated CPU's, a dummy CPU will be added to handle vblank. (TODO, this is not currently implemented)

Game Drivers

All game drivers must inherit from the 'game' base class.

CPUs should be set up in the game's init() method (see star rider driver for preferred method). Many of the game drivers currently set up CPUs and sound chips inside the game's constructor but this method is deprecated and discouraged. ROM paths may still be set up inside the constructor.

Most methods have defaults in the 'game' base class and do not need to be explicitly defined, but some of the methods you probably will need to define if you want the game driver to do anything useful:

  • init
  • cpu_mem_read
  • cpu_mem_write
  • input_enable
  • input_disable
  • OnVblankChanged

if using video overlay

  • palette_calculate
  • video_repaint

Video Overlay

If using video overlay, you must set the following variables:

  • m_game_uses_video_overlay = true;
  • m_video_overlay_width = [your game's video overlay width]
  • m_video_overlay_height = [your game's video overlay height]
  • m_palette_color_count = [your game's colors per palette, not to exceed 256]

video_blit() is currently called automatically at the end of every vblank. It used to be mandatory for the game driver to call it manually and most of them still do, which results in a harmless NO-OP aside from the redundant function call.

Coding Standards

The current Daphne code was started in 1999 and has evolved as the developers have evolved. Unfortunately, a lot of legacy code exists, where the coding practices and style is no longer ideal. Here are some suggestions for how to improve the code base:


All concrete classes should derive from interfaces. Concrete classes should only be created when the program first runs or by abstract factories. (ie the Dragon's Lair driver should not create a concrete instance of the ldv1000 class, for example)

No globals

Global variables (and methods) still exist in the code base. These must be eradicated. This includes the cpu and sound code, as well as the g_ldp and g_game pointers. Global methods should be replaced with static class methods at the very least. The daphne globals should be put inside of a proper daphne class so that stuff like shutting down gets automatically handled.

Use dependency injection model (aka Inversion of Control)

Class constructors (via instantiator methods, see Instantiation section) should take in interfaces, similar to how Unity Application Block works in C# or Spring works in Java. This can be faked by the beginning of the program entry (ie "main" or shortly thereafter) without using a specific dependency injection for C++ (if one even exists!).

All dynamic objects wrapped in shared_ptr or shared_array

We never want to call delete/free. boost's (or c++ tr1) shared_ptr should be used with all dynamic objects so that they do not need to be explicitly freed.

Throwing exceptions is preferred

A function should not return true/false on success or failure but should return on success or throw an exception on failure. Thrown exceptions must always derive from std::exception. The 'exception' to this rule is if the code needs to be optimized for performance reasons (ie cpu emulation) then 'assert' may be used instead to check for errors.

No inheritance

Game drivers should no longer inherit from a generic game class, and ldp drivers should no longer inherit from a generic ldp class. Common game and ldp functions should be put inside common classes that are abstracted away by interfaces, and game/ldp-specific implementations should also be hidden behind interfaces.

No linked lists

The linked lists used by the old cpu and sound code should be destroyed and replaced with STL's list.

No function pointers

C++ interfaces should always be used instead of function pointers. The only exception is if the code is written in C (ie the LDP interpreters, which are shared by Dexter) in which case function pointers may still be used.

Init/Shutdown functions

Shutdown methods should never need to be explicitly called (and should be made private if it makes sense). Shutdown functions should be automatically called by destructors. Calling init methods twice in a row should always be supported (ie if an init method is called twice, it should silently do any shutdown needed behind the scenes).

Avoid coupling

Game common code should not be coupled to LD-V1000 events (as it is today). Game common code also should probably not be coupled to cpu emulation (as it is today), because it is valid for a game driver to not have any cpu emulation (ie seektest).

"Out" parameters should use pointers, not be passed in by reference

void ChangeThis(int *pDstParm);

instead of

void ChangeThis(int &dstParm);


All constructor methods should be private. A public static method is used to instantiate which will return a shared_ptr. GetInstance() will return a 'null' shared pointer on error (ie result.get() is NULL), CreateInstance() will throw a std::exception on instantiation error. CreateInstance() is preferred unless performance is a concern. All destructors must be private. The 'MpoDeleter' pattern using "void DeleteInstance() { delete this; }" should be part of all concrete classes.

Naming convention

For C++, "Camel Case" is preferred for class names and function names. (For example: "MyClass::MyFunction".) Acronyms should be treated like words. (For example, GetPlayerId, not GetPlayerID.) Local variables or class variables should begin with a lowercase letter and be camel case thereafter, and should have some indication of what the variable type is. (For example, list<string> might be called lstResult). Interfaces should begin with a capital 'I' (for example IMyStuff).

For C, all letters should be lower-case separated by '_' characters. For example, "my_result".

Personal tools