LOGO
A fully reverse-engineered real DOS app exemple, from the floppy version of DUNE.
High level view
In this animation there is a lot. Let's break it down:
- Call to the DOS Open File Interrupt (LOGO.HNM is the argument)
- The code checks 288 times overall (via the BIOS Keyboard interrupts) for any key received in the input buffer, and exit to DOS in that case.
- Constant changes to the VGA palette
- Loops to animate colors on the screen (and wait ~16.667 ms for the VGA retrace in between)
- Call to the DOS Read File Interrupt (several times)
- Copy part of the file into the main memory
- Decompression of video frames read in memory previously
- Show it to the screen (which means changes to the VGA palette, and deciding where to write which color on the screen)
- Call to the DOS Close File Interrupt
- Call to the "Quit with exit code" DOS Interrupt (here, in Spice86, Cpu.IsRunning is set to false, marking the end of the emulation loop)
- Call to to the DOS Print String Interrupt (for some reason)
- Call to the "Exit to DOS" Interrupt (here, the Spice86 emulation loop exits, since the DOS command prompt isn't emulated)
Purpose
This repository serves as an example in order to document and show case the usage of Spice86.
LOGO.EXE and LOGO.HNM only exist in the PC Floppy version of DUNE.
How to run
Get LOGO.EXE from the floppy version of Dune.
sha256 checksum:
896a55f02555f708b57c6fd7576c8404aa479c1ec6e90fbbb230130bc7a31921
Then:
dotnet run -e /path/to/LOGO.EXE -d false
Main files of interest
-
Program.cs : Entry point.
-
CodeGeneratorConfig.json: Configuration file used by the Ghidra script named "Spice86CodeGenerator.java". This script is available on the Spice86 repo.
Namespace
The namespace of your assembly the Generated Code must use.
CheckExternalEvents
CheckExternalEvents must be called often enough to let interrupts run. Interrupts such as DOS interrupts, keyboard interrupts, ...
You can either let the code generator insert it everywhere with
GenerateCheckExternalEventsBeforeInstruction
(probably overkill)
Or inject it at keypoints with (for example):
"CodeToInject": {
"CheckExternalEvents({nextSegment}, {nextOffset});": ["CS1:100B", "CS1:09C1", "CS1:09EC", "CS1:098C", "CS1:09D9"]
},
- GeneratedCode_OriginalAsm.cs: Code generated by Spice86CodeGenerator.java (Ghidra script)
- GeneratedCode_DecompiledAsm.cs: (optional step) Code written by the .NET Ahead of Time compilation and re-formed into C# from the IL by Jetbrains Dotpeek. This is optional, but the compiler removes a lot of GOTOs for us.
- GeneratedCode: Final high level translation of "C# ASM" to high level C#. This is done by hand.