Template project for developing DOS games/apps with DJGPP, including source-level remote debugging with GDB and DOSBox.
BSD-2-CLAUSE License
Want to relive the 90ies and create little demos and games for DOS using C/C++ with "modern" tools? Then this project template is for you.
It's a turn key solution to setup a develpoment environment to create DOS demos/apps. consisting of:
Install:
Git Bash
to run a shell script later.cmake
is in your PATH
.sudo apt install ninja-build
brew install ninja
PATH
.Open a shell (Git Bash on Windows), then:
git clone https://github.com/badlogic/dos-dev-template
cd dos-dev-template
./download-tools.sh
The download-tools.sh
script will download everything you need to start developing for DOS.
Note: On Linux, you need to install the following packages using your distribution's package manager:
libncurses5 libfl-dev libslirp-dev libfluidsynth-dev
Once complete, open the dos-dev-template/
folder in Visual Studio Code.
The first time you start Visual Studio Code, you may be promoted to install the clangd language service. Make sure you click "Install!"
You may also be prompted to select a kit. Select djgpp
.
.vscode/ <- configuration files for VS Code & extensions
assets/ <- files used by the program
doc/ <- documentation and example programs
src/ <- source code goes here
main.c <- a little mode 13h demo app
tools/ <- contains GDB, DJGPP, DOSBOX after download
dosbox-x.conf <- DOSBox-x config enabling debugging via serial port
toolchain-djgpp.cmake <- CMake toolchain definition file for DJGPP
CMakeLists.txt <- CMake build definition.
download-tools.sh <- Script to download GDB, DJGPP, DOSBOX,
and Visual Studio Code extensions
Open the Run and debug
view as shown in the screenshot above, then start one of the two launch configurations:
debug target
: builds and launches the currently selected CMake launch target in DOSBox-x, and attaches the debugger.run target
: builds and launches the currently selected CMake launch target in DOSBox-x without attaching a debugger.Make sure the Cmake Build Variant matches the launch configuration, e.g. set the
Debug
variant when launchingdebug target
, and theRelease
variant when launching therun target
. Otherwise, the debugger may not be able to attach (Release
variant when launchingdebug target
), or the program may hang, waiting for the debugger (Debug
variant when launchingrelease target
)
For your program to support debugging, you have to do 3 things:
.c
or .cpp
files, add the following code:
#define GDB_IMPLEMENTATION
#include "gdbstub.h"
main()
, call gdb_start()
.gdb_checkpoint()
to give the debugger a chance to interrupt your program.The debugger has a few limitations & gotchas:
Debug: Stop
command from the command palette.Pause
button or press F6
to pause the program, you will end up in gdb_checkpoint()
.printf
-ing your way through life!Easy, just open or create a new .h
, .c
, or .cpp
file in the src/
folder and type away. You will get full code completion, navigation and what young people call "lints". Newly created files will be added to the build automatically.
Your code is compiled with DJGPP, which means it will become a beautiful 32-bit protected mode application. Check out the resources below to get your DOS programming juices flowing:
While you can do a lot with just code, sometimes you need a good old file. Put the files for your program in the assets/
folder. The file will then be available to your program via the path assets/<your-file-name>
, which you can pass to e.g. fopen()
.
If you don't like graphical user interfaces, you can also do all of the above from the command line.
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=tools/toolchain-djgpp.cmake -S . -B build -G Ninja
This configures your build to produce a debug binary. For a release binary, specify -DCMAKE_BUILD_TYPE=Release
. You generally run this command only when your build type changes.
Once configured, you can build the program as follows:
cmake --build build
This puts the executable file and assets in the build/
directory.
./tools/dosbox-x/dosbox-x -fastlaunch -exit -conf tools/dosbox-x.conf build/main.exe
Launches the executable in DOSBox-x. Make sure to build with -DCMAKE_BUILD_TYPE=Release
before launching.
./tools/dosbox-x/dosbox-x -fastlaunch -exit -conf tools/dosbox-x.conf build/main.exe
Launches the executable in DOSBox-x. Make sure to build with -DCMAKE_BUILD_TYPE=Debug
before launching.
In a second shell:
./tools/gdb/gdb
(gdb) file build/main.exe
(gdb) target remote localhost:5123
GDB will connect to the program running in DOSBox-x. See the GDB cheat sheet on how to use the command line driver of GDB.
Here are a few questions you may have, if you want to dig deeper into the entire setup to modify it to your liking.
download-tools.sh
do?It will download the following tools for your operating system to the tools/
folder:
After downloading the tools, it will install these Visual Studio Code extensions necessary for C/C++ development:
I'm glad you ask! It's an unholy ball of duct tape consisting of:
COFF_GO32
executables as produced by DJGPP.And here's how it works:
gdb_start()
. This function initializes the serial port to use the highest baud rate possible, then triggers a breakpoint via int 3
. This in turn triggers a special signal handler implemented in the GDB stub which listens for incoming data from the serial port.gdb_checkpoint()
to trigger a software breakpoint via int 3
, which in turn will invoke the signal handler.In theory, all of this is very simple. In practice, it can fall apart in the most creative ways. The implementation was tested with the included GDB version, as well as the Native Debugger extension which sits on top the included GDB version.
I can make no guarantees it will work with other GDB versions or higher level debuggers as found in e.g. CLion, etc. It should. But it might not, as most debuggers don't stick to the protocol spec.
.vscode
?These are configuration files for the various extensions and VS Code itself to provide you with a passable out-of-the-box experience.
debug target
and run target
. They in turn depend on tasks defined in tasks.json