CRYOGENIC

C# re-implementation of Cryo's Dune (CD, 1992)

Replaces x86 assembly routines in DNCDPRG.EXE with C# overrides running on the Spice86 emulator. The game is fully playable with sound and music.

Linux macOS Windows

What This Is

Cryogenic replaces x86 assembly routines in the DOS executable DNCDPRG.EXE with C# methods. Spice86 runs the original binary and redirects execution to the C# overrides at registered addresses. Assembly and C# code share the same emulated memory, so overrides can be introduced one function at a time without breaking the rest of the program.

The game is fully playable with sound and music through hybrid ASM/.NET execution. Ongoing work converts additional assembly routines into C#. Each converted routine is registered as an override and tested by running the game.

Required DNCDPRG.EXE SHA256 (Dune CD version 3.7):

5f30aeb84d67cf2e053a83c09c2890f010f2e25ee877ebec58ea15c5b30cfff9

The game files (DNCDPRG.EXE, DUNE.DAT) are copyrighted and must be obtained separately.

Screenshots

Orni

Orni

Chani dialogue

Chani dialogue

Spice shipment screen

Spice shipment screen

Harkonnen scene

Harkonnen scene

Dune map

Dune map

Calling a worm

Calling a worm

Code Structure

Memory Segments

The Overrides class declares five segment fields used when registering overrides at specific addresses:

Field Value Contents Overrides
cs1 0x1000 Main game code (DNCDPRG.EXE loaded here) ~80 functions across most files
cs2 0xD000 DNVGA — VGA graphics driver 32 functions in VgaDriverCode.cs
cs3 0xE000 DNPCS2 / DNSBP — PCM audio driver None (declared, unused)
cs4 0xE000 MIDI driver memory-dump hooks 2 inline hooks at 0x02DC, 0x03EE
cs5 0x0800 Interrupt handlers (replaces default 0xF000) None (address reference)

cs3 and cs4 share address 0xE000. In DriverLoadToolbox, PCM drivers load at DRIVER2_SEGMENT (0xE000) and music drivers load at DRIVER3_SEGMENT (0xF000). The MT-32 overrides in MT32DriverCode.cs use hardcoded 0xF000 (3 functions), not any cs field.

Driver Remapping

DriverLoadToolbox temporarily removes the memory allocator's segment limit so drivers load at fixed addresses. Hooks are injected at CS1:E57B (RemapDrivers) and CS1:E593 (ResetAllocator). Driver function tables are auto-detected at CS1:E589 (ReadDriverFunctionTable).

Driver Name Remapped to Purpose
DNVGA VGA graphics 0xD000 Display, blitting, palette, mouse cursor
DNPCS2 PC Speaker variant 2 0xE000 PCM sound effects
DNSBP Sound Blaster Pro 0xE000 PCM sound effects
DNPCS PC Speaker 0xF000 Music playback
DNMID MIDI 0xF000 Music playback (MT-32, AdLib)

Drivers DN386, DNADL, DNADP, DNADG, DNSDB are not remapped.

Override Files

Each file is a partial class of Overrides containing C# replacements for specific groups of assembly routines. Overrides are registered in DefineOverrides() using DefineFunction(segment, offset, method) for CALL targets and DoOnTopOfInstruction(segment, offset, method) for inline hooks.

VgaDriverCode.cs

23 functions replacing the DNVGA driver at segment 0xD000. Covers VGA mode setting, framebuffer blitting, rectangle copy, pixel write, palette loading, mouse cursor draw/restore, buffer fill, texture generation, map block copy, and VGA retrace synchronization.

MenuCode.cs

14 menu-type constants as hex offsets (MENU_TYPE_DIALOGUE = 0x1F7E, MENU_TYPE_GLOBE = 0x204A, MENU_TYPE_BOOK = 0x2032, etc.). Two overrides handle menu animation state at CS1:D316 and current menu-type selection at CS1:D41B.

DialoguesCode.cs

3 functions: incrementing a dialogue counter at DS:47A8 (CS1:A1E8), initializing dialogue state including video index and face-zoom timing (CS1:C85B), and converting the low nibble of AL to an ASCII hex digit (CS1:A8B1).

DisplayCode.cs

11 functions managing three framebuffers (front at DBD6, back at DC32, text at DBD8). Includes three font-selection functions (intro, menu, book), coordinate lookup, buffer clear, and register push/pop helpers.

MapCode.cs

5 functions and click-handler address constants for five map modes (flat map, globe, in-game, troop movement, ornithopter). Sets the active click handler, initializes map cursor type, and performs an 8-byte memory copy from DS:46E3.

VideoCode.cs & HnmCode.cs

4 functions for HNM video playback: resource flag lookup from a table at DS:33A3, playback index synchronization, completion check via the finished-flag at DBE7, and disk read into buffer through the DOS file manager.

TimeCode.cs & TimerCode.cs

TimeCode extracts the current hour from the lower 4 bits of the game elapsed-time word at DS:0002 and calculates the next sunlight-visible day. TimerCode writes a 16-bit counter value to PIT channel 0 (ports 0x43/0x40) with control byte 0x36.

ScriptedSceneCode.cs

2 functions that read 16-bit commands from the scene sequence data at DS:4854 and advance the sequence pointer.

DatastructuresCode.cs

2 functions: converts an index table to a pointer table by adding a base offset, and retrieves sprite-sheet resource pointers by index from the table at DS:DBB0.

MT32DriverCode.cs

3 functions at segment 0xF000 for Roland MT-32 output: a primary entry point, a MIDI byte-write to port 0x330, and an unused stub.

UnknownCode.cs

20 functions with observed behavior but unclear purpose. Includes bit-test operations on DBC8, multi-register shifts, 8-byte memory copies, state-flag setters, a 10-byte structure builder, a 0x5C-byte fill at DS:47F8, and three no-ops.

StaticDefinitions.cs

Registers 137+ symbolic function names (e.g. play_intro, open_dune_dat, allocator_init) at their addresses for Spice86's trace output. These are not overridden; they provide readable names in execution logs.

Supporting Code

Program.cs

Entry point. Injects ProvidedAsmHandlersSegment=0x800 into args and calls Spice86.Program.RunWithOverrides<DuneCdOverrideSupplier> with the expected SHA256 checksum.

DuneCdOverrideSupplier.cs

Implements IOverrideSupplier. Its GenerateFunctionInformations method instantiates Overrides.Overrides, which populates the address-to-function map.

DriverLoadToolbox.cs

Remaps VGA/PCM/MIDI drivers to fixed segment addresses (0xD000/0xE000/0xF000) by temporarily disabling the allocator's segment limit at CS1:E57B and restoring it at CS1:E593. Also reads driver function tables at CS1:E589 and auto-registers entry points for Spice86 tracing. Drivers DN386, DNADL, DNADP, DNADG, DNSDB are left at their original allocations.

Globals/ & Generated/

Typed accessors for game state stored at specific memory offsets. Files in Generated/ are produced by Spice86 dump tooling and should not be edited by hand. Manual additions go in Globals/Extra* files.

Spice86

What It Does

Spice86 is a .NET 10 PC emulator for 16-bit real-mode x86 programs. It runs the original DOS binary and allows C# methods to replace specific assembly routines at registered segment:offset addresses.

The emulated CPU, memory, and I/O devices (VGA, AdLib, Sound Blaster, keyboard, mouse) are accessible from C# code. Overrides read and write the same memory that the assembly code uses, so both can coexist in the same execution.

Capabilities Used by Cryogenic

Function Replacement

DefineFunction(segment, offset, method) registers a C# method at a segment:offset address. When the emulated CPU reaches that address via a CALL, the C# method runs instead.

Inline Hooks

DoOnTopOfInstruction(segment, offset, method) injects C# code at an arbitrary instruction address without replacing the surrounding function.

Runtime Analysis

Memory dumps, execution traces, and runtime data can be collected while the program runs. MemoryDataExporter captures snapshots at specific addresses.

GDB Debugging

Built-in GDB remote protocol support. Breakpoints, memory inspection, and single-stepping work through standard GDB clients.

Hardware Emulation

VGA/EGA/CGA graphics, PC Speaker, AdLib (OPL2), Sound Blaster, keyboard, and mouse are emulated. Audio drivers are loaded at remapped segments.

Cross-Platform

Built on .NET 10. Runs on Windows, macOS, and Linux.

Execution Flow

1

Program.cs

Configures args (injects ProvidedAsmHandlersSegment=0x800) and calls Spice86.Program.RunWithOverrides<DuneCdOverrideSupplier> with the expected SHA256.

2

DuneCdOverrideSupplier

Spice86 calls GenerateFunctionInformations, which instantiates Overrides.Overrides and populates the address-to-function dictionary.

3

DefineOverrides()

Registers all C# methods at their segment:offset addresses. Sets up driver remapping hooks at CS1:E57B and CS1:E593, and memory dump hooks.

4

Runtime

The emulated CPU runs DNCDPRG.EXE. When it reaches a registered address, the C# method executes instead of the assembly. The method ends with NearRet() or FarRet() to return control to the emulated program.

Build and Run

Prerequisites

  • .NET 10 SDK
  • DNCDPRG.EXE and DUNE.DAT from Dune CD version 3.7 (copyrighted; obtain separately)

Build

git clone https://github.com/OpenRakis/Cryogenic
cd Cryogenic/src
dotnet build

Run (no audio)

cd Cryogenic/src
dotnet run --Exe /path/to/DNCDPRG.EXE --UseCodeOverride true -p 4096

--UseCodeOverride true is required. Without it, no C# overrides execute.

Run with audio

AdLib music and Sound Blaster PCM:

cd Cryogenic/src/Cryogenic
dotnet publish
bin/Release/net10.0/publish/Cryogenic --Exe /path/to/DNCDPRG.EXE --UseCodeOverride true -p 4096 --Cycles 8000 -a "ADP330 SBP2227"

Contributing

See CONTRIBUTING.md for setup instructions, coding conventions, and the override implementation workflow.

Contribution Areas

Reverse Engineering

Analyze assembly routines, implement equivalent C# methods, and register them in DefineOverrides().

Documentation

Add XML doc comments to override methods. Document data structures, memory offsets, and function behavior.

Testing

Run the game and compare behavior against the original DOS executable. Report differences or regressions.

Code Quality

Refactor existing overrides, improve naming, add error handling, remove duplication.

Workflow

1

Fork and clone

git clone https://github.com/YOUR-USERNAME/Cryogenic.git
2

Install .NET 10 SDK and obtain the Dune CD files

3

Create a branch, make changes, test by running the game

4

Open a pull request

Links