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.
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.
Orni
Chani dialogue
Spice shipment screen
Harkonnen scene
Dune map
Calling a worm
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.
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.
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.
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.
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.
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).
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.
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.
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 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.
2 functions that read 16-bit commands from the scene sequence data at DS:4854 and advance the sequence pointer.
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.
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.
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.
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.
Entry point. Injects ProvidedAsmHandlersSegment=0x800
into args and calls
Spice86.Program.RunWithOverrides<DuneCdOverrideSupplier>
with the expected SHA256 checksum.
Implements IOverrideSupplier. Its
GenerateFunctionInformations method instantiates
Overrides.Overrides, which populates the address-to-function
map.
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.
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 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.
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.
DoOnTopOfInstruction(segment, offset, method) injects C#
code
at an arbitrary instruction address without replacing the surrounding function.
Memory dumps, execution traces, and runtime data can be collected while the program runs.
MemoryDataExporter captures snapshots at specific addresses.
Built-in GDB remote protocol support. Breakpoints, memory inspection, and single-stepping work through standard GDB clients.
VGA/EGA/CGA graphics, PC Speaker, AdLib (OPL2), Sound Blaster, keyboard, and mouse are emulated. Audio drivers are loaded at remapped segments.
Built on .NET 10. Runs on Windows, macOS, and Linux.
Configures args (injects ProvidedAsmHandlersSegment=0x800)
and calls Spice86.Program.RunWithOverrides<DuneCdOverrideSupplier>
with the expected SHA256.
Spice86 calls GenerateFunctionInformations,
which instantiates Overrides.Overrides and
populates the address-to-function dictionary.
Registers all C# methods at their segment:offset addresses. Sets up driver remapping hooks at CS1:E57B and CS1:E593, and memory dump hooks.
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.
git clone https://github.com/OpenRakis/Cryogenic
cd Cryogenic/src
dotnet build
cd Cryogenic/src
dotnet run --Exe /path/to/DNCDPRG.EXE --UseCodeOverride true -p 4096
--UseCodeOverride true is required. Without it, no C# overrides
execute.
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"
See CONTRIBUTING.md for setup instructions, coding conventions, and the override implementation workflow.
Analyze assembly routines, implement equivalent C# methods, and register them
in DefineOverrides().
Add XML doc comments to override methods. Document data structures, memory offsets, and function behavior.
Run the game and compare behavior against the original DOS executable. Report differences or regressions.
Refactor existing overrides, improve naming, add error handling, remove duplication.
git clone https://github.com/YOUR-USERNAME/Cryogenic.git