An Advanced Guide To FreeDOS Internals
An Advanced Guide To FreeDOS Internals
by Jim Hall
We are Opensource.com
Do you have an open source story to tell? Submit a story idea at opensource.com/story
Email us at open@opensource.com
Table of Contents
Configure FreeDOS in plain text...........................................................................................................4
How FreeDOS boots.............................................................................................................................10
Automate tasks with BAT files on FreeDOS.......................................................................................13
Set and use environment variables in FreeDOS................................................................................19
How to archive files on FreeDOS........................................................................................................23
Copy files between Linux and FreeDOS...........................................................................................28
How to use the FreeDOS text editor.................................................................................................32
Edit text like Emacs in FreeDOS........................................................................................................40
Use this nostalgic text editor on FreeDOS.......................................................................................47
Why I like the FED text editor..............................................................................................................52
Listen to music on FreeDOS...............................................................................................................57
Program on FreeDOS with Bywater BASIC.......................................................................................61
Why I love programming on FreeDOS with GW-BASIC..................................................................67
How to program in C on FreeDOS......................................................................................................77
Get started programming with conio.................................................................................................83
Why FreeDOS has 16 colors................................................................................................................93
Print a holiday greeting with ASCII art on Linux.............................................................................102
Appendix: Get started with batch files in FreeDOS.......................................................................107
conio Cheat Sheet................................................................................................................................113
FreeDOS Cheat Sheet.........................................................................................................................115
Jim Hall
By Jim Hall
The main configuration file for FreeDOS is a file in the root directory called FDCONFIG.SYS.
This file contains a series of lines, each setting a value such as LASTDRIVE=Z or FILES=40.
For example, the default FDCONFIG.SYS in FreeDOS looks like this:
SET DOSDIR=C:\FDOS
!COUNTRY=001,858,C:\FDOS\BIN\COUNTRY.SYS
!LASTDRIVE=Z
!BUFFERS=20
!FILES=40
!MENUCOLOR=7,0
MENUDEFAULT=1,5
MENU 1 - Load FreeDOS with JEMMEX, no EMS (most UMBs), max RAM free
MENU 2 - Load FreeDOS with JEMM386 (Expanded Memory)
MENU 3 - Load FreeDOS low with some drivers (Safe Mode)
MENU 4 - Load FreeDOS without drivers (Emergency Mode)
12?DOS=HIGH
12?DOS=UMB
12?DOSDATA=UMB
1?DEVICE=C:\FDOS\BIN\JEMMEX.EXE NOEMS X=TEST I=TEST NOVME NOINVLPG
234?DEVICE=C:\FDOS\BIN\HIMEMX.EXE
2?DEVICE=C:\FDOS\BIN\JEMM386.EXE X=TEST I=TEST I=B000-B7FF NOVME NOINVLPG
34?SHELL=C:\FDOS\BIN\COMMAND.COM C:\FDOS\BIN /E:1024 /P=C:\FDAUTO.BAT
12?SHELLHIGH=C:\FDOS\BIN\COMMAND.COM C:\FDOS\BIN /E:1024 /P=C:\FDAUTO.BAT
But what do all those lines mean? Why do some have a question mark (?) or an exclamation
point (!), while other lines do not?
LASTDRIVE=Z
BUFFERS=20
FILES=40
DEVICE=C:\FDOS\BIN\HIMEMX.EXE
SHELL=C:\FDOS\BIN\COMMAND.COM C:\FDOS\BIN /E:1024 /P=C:\FDAUTO.BAT
1. LASTDRIVE=Z
2. BUFFERS=20
3. FILES=40
4. DEVICE=C:\FDOS\BIN\HIMEMX.EXE
5. SHELL=C:\FDOS\BIN\COMMAND.COM C:\FDOS\BIN /E:1024 /P=C:\
FDAUTO.BAT
The first instruction tells FreeDOS how many drive letters to reserve in memory. (DOS uses
letters to represent each drive attached to the system, and LASTDRIVE=Z says to reserve
drive letters from "A" to "Z."). LASTDRIVE affects the number of logical drives that your
system can recognize. You probably don't have any logical drives; the FreeDOS installer
doesn't set these up by default. In any case, it is safe to set LASTDRIVE=Z on any FreeDOS
system.
The BUFFERS line reserves memory for file buffers. A buffer helps to speed up certain
processes that require storage, such as copying files. If you set a larger value for BUFFERS,
FreeDOS will reserve more memory—and vice versa for smaller values. Most users will set this
to BUFFERS=20 or BUFFERS=40, depending on how often they need to read and write files
on the system.
The FILES setting determines how many files DOS allows you to open at one time. If you run
an application that needs to open many files at once, such as a Genealogy database, you may
need to set FILES to a larger value. For most users, FILES=40 is a reasonable value.
The last line tells the FreeDOS kernel where to find the command-line shell. By default, the
kernel will look for the shell as COMMAND.COM, but you can change it with
the SHELL instruction. In this example, SHELL=C:\FDOS\BIN\COMMAND.COM says the shell
is the COMMAND.COM program, located in the \FDOS\BIN directory on the C drive.
The other text at the end of the SHELL indicate the options to the COMMAND.COM shell. The
FreeDOS COMMAND.COM supports several startup options to modify its behavior, including:
Boot menu
FreeDOS supports a neat feature—multiple configurations on one system, using a "boot
menu" to select the configuration you want. The FDCONFIG.SYS file contains several lines
that define the menu:
!MENUCOLOR=7,0
MENUDEFAULT=1,5
MENU 1 - Load FreeDOS with JEMMEX, no EMS (most UMBs), max RAM free
MENU 2 - Load FreeDOS with JEMM386 (Expanded Memory)
MENU 3 - Load FreeDOS low with some drivers (Safe Mode)
MENU 4 - Load FreeDOS without drivers (Emergency Mode)
• 0 Black
• 1 Blue
• 2 Green
• 3 Cyan
• 4 Red
• 5 Magenta
• 6 Brown
• 7 White
So the MENUCOLOR=7,0 definition means to display the menu with white (7) text on a black
(0) background. If you instead wanted to use white text on a blue background, you could
define this as MENUCOLOR=7,1.
The exclamation point (!) at the start of the line means that this instruction will always be
executed, no matter what menu option you choose.
The MENUDEFAULT=1,5 line tells the kernel how long to wait for the user to select a boot
menu entry, or what default menu entry to use if the user did not select
one. MENUDEFAULT=1,5 indicates the system will wait for 5 seconds; if the user did not
attempt to select a menu item within that time, the kernel will assume boot menu "1" instead.
The MENU lines after that are labels for the different boot menu configurations. These are
presented in order, so menu item "1" is first, then "2," and so on.
In the lines that follow in FDCONFIG.SYS, you will see numbers before a question mark (?).
These indicate "for this boot menu entry, use this line." For example, this line with 234? will
only load the HimemX device driver if the user selects boot menu entries "2," "3," or "4."
234?DEVICE=C:\FDOS\BIN\HIMEMX.EXE
There are lots of ways to use FDCONFIG.SYS to configure your FreeDOS system. We've only
covered the basics here, the most common ways to define your FreeDOS kernel settings. For
more information, explore the FreeDOS Help system (type HELP at the command line) to
learn how to use all of the FreeDOS FDCONFIG.SYS options:
By Jim Hall
One thing I appreciate from growing up with DOS computers is that the boot process is
relatively easy to understand. There aren't a lot of moving parts in DOS. And today, I'd like to
share an overview of how your computer boots up and starts a simple operating system like
FreeDOS.
Initial bootstrapping
When you turn on the power to your computer, the system performs several self-checks, such
as verifying the memory and other components. This is called the Power On Self Test or
"POST." After the POST, the computer uses a hard-coded instruction that tells it where to
find its instructions to load the operating system. This is the "boot loader," and usually it will
try to locate a Master Boot Record or (MBR) on the hard drive. The MBR then loads the
primary operating system; in this case, that's FreeDOS.
This process of locating one piece of information so the computer can load the next part of
the operating system is called "bootstrapping," from the old expression of "picking yourself up
by your bootstraps." It is from this usage that we adopted the term "boot" to mean starting up
your computer.
The kernel
When the computer loads the FreeDOS kernel, one of the first things the kernel does is
identify any parameters the user has indicated to use. This is stored in a file
called FDCONFIG.SYS, stored in the same root directory as the kernel.
If FDCONFIG.SYS does not exist, then the FreeDOS kernel looks for an alternate file
called CONFIG.SYS.
If neither FDCONFIG.SYS nor CONFIG.SYS exist, then the kernel assumes several default
values, including where to find the shell. If you see the message "Bad or missing Command
Interpreter" when you boot your FreeDOS system, that means SHELL= or SHELLHIGH= is
pointing to a shell program that doesn't exist on your system.
You might debug this by looking at the SHELL= or SHELLHIGH= lines. Failing that, make sure
you have a program called COMMAND.COM in the root directory of your FreeDOS system. This
is the shell, which I'll talk about next.
The shell
The term "shell" on a DOS system usually means a command-line interpreter; an interactive
program that reads instructions from the user, then executes them. In this way, the FreeDOS
shell is similar to the Bash shell on Linux.
Unless you've asked the kernel to load a different shell using SHELL= or SHELLHIGH=, the
standard command-line shell on DOS is called COMMAND.COM. And as COMMAND.COM starts
You can change the shell, and the startup file for the shell, in the FDCONFIG.SYS file,
with SHELL= or SHELLHIGH=. The FreeDOS installer sets up the system to
read FDAUTO.BAT instead of AUTOEXEC.BAT. This is for the same reason that the kernel
reads an alternate configuration file; you can dual-boot FreeDOS on a hard drive with another
DOS. FreeDOS will use FDAUTO.BAT while MS-DOS will use AUTOEXEC.BAT..
Without a startup file like AUTOEXEC.BAT, the shell will simply prompt the user to enter the
date and time.
And that's it. Once FreeDOS has loaded the kernel, and the kernel has loaded the shell,
FreeDOS is ready for the user to type commands.
By Jim Hall
Even if you haven't used DOS before, you are probably aware of its command-line shell,
named simply COMMAND.COM. The COMMAND.COM shell has become synonymous with DOS,
and so it's no surprise that FreeDOS also implements a similar shell called "FreeCOM"—but
named COMMAND.COM just as on other DOS systems.
But the FreeCOM shell can do more than just provide a command-line prompt where you run
commands. If you need to automate tasks on FreeDOS, you can do that using batch files, also
called "BAT files" because these scripts use the .BAT extension.
Batch files are much simpler than scripts you might write on Linux. That's because when this
feature was originally added to DOS, long ago, it was meant as a way for DOS users to "batch
up" certain commands. There's not much flexibility for conditional branching, and batch files
do not support more advanced features such as arithmetic expansion, separate redirection for
standard output vs error messages, background processes, tests, loops, and other scripting
structures that are common in Linux scripts.
Here's a helpful guide to batch files under FreeDOS. Remember to reference environment
variables by wrapping the variable name with percent signs (%) such as %PATH%. However,
note that FOR loops use a slightly different construct for historical reasons.
ECHO Done
You don't need quotes in the ECHO statement. The FreeCOM ECHO statement will not treat
quotes in any special way and will print them just like regular text.
Normally, FreeDOS prints out every line in the batch file as it executes them. This is usually
not a problem in a very short batch file that only defines a few environment variables for the
user. But for longer batch files that do more work, this constant display of the batch lines can
become bothersome. To suppress this output, use the OFF keyword to the ECHO statement,
as:
ECHO OFF
To resume displaying the batch lines as FreeDOS runs them, use the ON keyword instead:
ECHO ON
Most batch files include an ECHO OFF statement on the first line, to suppress messages. But
the shell will still print ECHO OFF to the screen as it executes that statement. To hide that
message, batch files often use an at sign (@) in front. This special character at the start of any
line in a batch file suppresses printing that line, even if ECHO is turned on.
@ECHO OFF
Comments
When writing any long batch file, most programmers prefer to use comments to remind
themselves about what the batch file is meant to do. To enter a comment in a batch file, use
the REM (for remark) keyword. Anything after REM gets ignored by the FreeCOM shell.
@ECHO OFF
REM This is a comment
If you simply call the second batch file from a "running" batch file, FreeCOM switches entirely
to that second batch file and stops processing the first one. To instead run the second batch
file "inside" the first batch file, you need to tell the FreeCOM shell to call the second batch file
with the CALL keyword.
@ECHO OFF
CALL SETENV.BAT
Conditional evaluation
Batch files do support a simple conditional evaluation structure with the IF statement. This
has three basic forms:
To test if a program called MYPROG exited successfully, you actually want to examine if the
program returned a "zero" error level. Use the ERRORLEVEL keyword to test for a specific
value, such as:
@ECHO OFF
MYPROG
IF ERRORLEVEL 0 ECHO Success
Testing the error level with ERRORLEVEL is a clunky way to examine the exit status of a
program. A more useful way to examine different possible return codes for a DOS program is
with a special variable FreeDOS defines for you, called ERRORLEVEL. This stores the error
You can test if a variable is equal to a value using the == test with the IF statement. Like
some programming languages, you use == to directly compare two values. Usually, you will
reference an environment variable on one side and a value on the other, but you could also
compare the values of two variables to see if they are the same. For example, you could
rewrite the above ERRORLEVEL code with this batch file:
@ECHO OFF
MYPROG
IF %ERRORLEVEL%==0 ECHO Success
And another common use of the IF statement is to test if a file exists, and take action if so.
You can test for a file with the EXIST keyword. For example, to delete a temporary file
called TEMP.DAT, you might use this line in your batch file:
@ECHO OFF
IF EXIST TEMP.DAT DEL TEMP.DAT
With any of the IF statements, you can use the NOT keyword to negate a test. To print a
message if a file does not exist, you could write:
@ECHO OFF
IF NOT EXIST TEMP.DAT ECHO No file
Branched execution
One way to leverage the IF test is to jump to an entirely different part of the batch file,
depending on the outcome of a previous test. In the simplest case, you might want to skip to
the end of the batch file if a key command fails. Or you might want to execute other
statements if certain environment variables are not set up correctly.
You can skip around to different parts of a batch file using the GOTO instruction. This jumps to
a specific line, called a label, in the batch file. Note that this is a strict "go-to" jump; batch file
execution picks up at the new label.
Let's say a program needed an existing empty file to store temporary data. If the file did not
exist, you would need to create a file before running the program. You might add these lines
to a batch file, so your program always has a temporary file to work with:
Of course, this is a very simple example. For this one case, you might instead rewrite the batch
file to create the temporary file as part of the IF statement:
@ECHO OFF
IF NOT EXIST temp.dat TOUCH temp.dat
ECHO Running the program...
MYPROG
Iteration
What if you need to perform the same task over a set of files? You can iterate over a set of
files with the FOR loop. This is a one-line loop that runs a single command with a different file
each time.
The FOR loop uses a special syntax for the iteration variable, which is used differently than
other DOS environment variables. To loop through a set of text files so you can edit each one,
in turn, use this statement in your batch file:
@ECHO OFF
FOR %%F IN (*.TXT) DO EDIT %%F
Note that the iteration variable is specified with only one percent sign (%) if you run this loop
at the command line, without a batch file:
Command-line processing
FreeDOS provides a simple method to evaluate any command-line options the user might
have provided when running batch files. FreeDOS parses the command line, and stores the
first nine batch file options in the special variables %1, %2, .. and so on until %9. Notice that the
eleventh option (and beyond) are not directly accessible in this way. (The special
variable %0 stores the name of the batch file.)
Most batch files need to shift by one value. But if you need to shift by some other increment,
you can provide that parameter to the SHIFT statement, such as:
SHIFT 2
@ECHO OFF
ECHO %1 %2 %3 %4 %5 %6 %7 %8 %9
ECHO Shift by one ..
SHIFT 1
ECHO %1 %2 %3 %4 %5 %6 %7 %8 %9
Executing this batch file with ten arguments shows how the SHIFT statement reorders the
command line options, so the batch file can now access the tenth argument as %9:
C:\SRC>args 1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9
Shift by one ..
2 3 4 5 6 7 8 9 10
C:\SRC>
By Jim Hall
A useful feature in almost every command-line environment is the environment variable. Some
of these variables allow you to control the behavior or features of the command line, and
other variables simply allow you to store data that you might need to reference later.
Environment variables are also used in FreeDOS.
Variables on Linux
On Linux, you may already be familiar with several of these important environment variables.
In the Bash shell on Linux, the PATH variable identifies where the shell can find programs and
commands. For example, on my Linux system, I have this PATH value:
That means when I type a command name like cat, Bash will check each of the directories
listed in my PATH variable, in order:
1. /home/jhall/bin
2. /usr/lib64/ccache
3. /usr/local/bin
4. /usr/local/sbin
5. /usr/bin
6. /usr/sbin
And in my case, the cat command is located in the /usr/bin directory, so the full path to
that command is /usr/bin/cat.
bash$ var=Hello
bash$ echo $var
Hello
Variables on FreeDOS
On FreeDOS, environment variables serve a similar function. Some variables control the
behavior of the DOS system, and others are useful to store some temporary value.
To set an environment variable on FreeDOS, you need to use the SET keyword. FreeDOS
is case insensitive, so you can type that using either uppercase or lowercase letters. Then set
the variable as you might on Linux, using the variable name, an equals sign (=), and the value
you want to store.
It's important to use the percent signs both before and after the name because that's how
FreeDOS knows where the variable name begins and ends. This is very useful, as it allows you
to reference a variable's value while immediately appending (or prepending) other text to the
value. Let me demonstrate this by setting a new variable called reply with the value yes,
then referencing that value with the text "11" before and "22" after it:
Finally, you can see a list of all the environment variables currently defined in FreeDOS.
Without any arguments, the SET keyword will display all variables, so you can see everything
at a glance:
Environment variables are a useful staple in command-line environments, and the same
applies to FreeDOS. You can set your own variables to serve your own needs, but be careful
about changing some of the variables that FreeDOS uses. These can change the behavior of
your running FreeDOS system:
If you accidentally change any of the FreeDOS "internal" variables, you could prevent some
parts of FreeDOS from working properly. In that case, simply reboot your computer, and
FreeDOS will reset the variables from the system defaults.
By Jim Hall
On Linux, you may be familiar with the standard Unix archive command: tar. There's a version
of tar on FreeDOS too (and a bunch of other popular archive programs), but the de facto
standard archiver on DOS is Zip and Unzip. Both Zip and Unzip are installed in FreeDOS by
default.
The Zip file format was originally conceived in 1989 by Phil Katz of PKWARE, for the PKZIP
and PKUNZIP pair of DOS archive utilities. Katz released the specification for Zip files as an
open standard, so anyone could create Zip archives. As a result of the open specification, Zip
became a standard archive on DOS. The Info-ZIP project implements an open source set
of ZIP and UNZIP programs.
In my example, ZIP was able to compress my source files from about 33 kilobytes down to
about 22 kilobytes, saving me 11 kilobytes of valuable disk space. You might get different
compression ratios depending on what options you give to ZIP or what files (and how many)
you are trying to store in a Zip file. Generally, very long text files (such as source code) yield
good compression—very small text files (like DOS "batch" files of only a few lines) are usually
too short to compress well.
The output allows me to see the 14 entries in the Zip file: 13 files plus the SRC directory entry.
If I want to extract the entire Zip file, I could just use the UNZIP command and provide the Zip
file as a command-line option. That extracts the Zip file starting at my current working
directory. Unless I'm restoring a previous version of something, I usually don't want to
overwrite my current files. In that case, I will want to extract the Zip file to a new directory. You
can specify the destination path with the -d ("destination") command-line option:
Sometimes I want to extract a single file from a Zip file. In this example, let's say I wanted to
extract TEST.EXE, a DOS executable program. To extract a single file, you specify the full
path from the Zip file that you want to extract. By default, UNZIP will extract this file using the
path provided in the Zip file. To omit the path information, you can add the -j ("junk the
path") option.
You can also combine options. Let's extract the SRC\TEST.EXE program from the Zip file,
but omit the full path and save it in the TEMP directory:
Because Zip files are an open standard, we continue to see Zip files today. Every Linux
distribution supports Zip files using the Info-ZIP programs. Your Linux file manager may also
have Zip file support—on the GNOME file manager, you should be able to right-click on a
folder and select "Compress" from the drop-down menu. You'll have the option to create a
new archive file, including a Zip file.
Creating and managing Zip files is a key skill for any DOS user. You can learn more
about ZIP and UNZIP at the Info-ZIP website, or use the -h ("help") option on the command
line to print out a list of options.
By Jim Hall
I run Linux as my primary operating system, and I boot FreeDOS in a virtual machine. Most of
the time, I use QEMU as my PC emulator, but sometimes I'll run other experiments with
GNOME Boxes (which uses QEMU as a back-end virtual machine) or with VirtualBox.
I like to play classic DOS games, and sometimes I'll bring up a favorite DOS application. I
teach a Management Information Systems (MIS) class where I talk about the history of
computing, and I'll sometimes record a demonstration using FreeDOS and a legacy DOS
application, such as As-Easy-As (my favorite DOS spreadsheet—once released as "shareware"
but now available for free from TRIUS, Inc).
But using FreeDOS this way means I need to transfer files between my FreeDOS virtual
machine and my Linux desktop system. Let me show you how I do that.
Using guestmount is not as easy as double-clicking the file from the GNOME file manager,
but the command line isn't too difficult to use. The basic usage of guestmount is:
In this usage, image is the virtual disk image to use. On my system, I created my QEMU virtual
disk image with the qemu-img command. The guestmount program can read this disk image
format, as well as the QCOW2 image format used by GNOME Boxes, or the VDI image format
used in VirtualBox.
The device option indicates the partition on the virtual disk. Imagine using this virtual disk as a
real hard drive. You would access the first partition as /dev/sda1, the second partition
as /dev/sda2, and so on. That's the syntax for guestmount. By default, FreeDOS creates
one partition on an empty drive, so access that partition as /dev/sda1.
And mountpoint is the location to "mount" the DOS filesystem on your local Linux system. I'll
usually create a temporary directory to work with. You only need the mount point while you're
accessing the virtual disk.
Putting that all together, I use this set of commands to access my FreeDOS virtual disk image
from Linux:
$ mkdir /tmp/freedos
$ guestmount -a freedos.img -m /dev/sda1 /tmp/freedos
After that, I can access my FreeDOS files via the /tmp/freedos directory, using normal tools
on Linux. I might use ls /tmp/freedos at the command line, or open
the /tmp/freedos mount point using the desktop file manager.
$ ls -l /tmp/freedos
total 216
drwxr-xr-x. 5 root root 8192 May 10 15:53 APPS
-rwxr-xr-x. 1 root root 85048 Apr 30 07:54 COMMAND.COM
-rwxr-xr-x. 1 root root 103 May 13 15:48 CONFIG.SYS
drwxr-xr-x. 5 root root 8192 May 15 16:52 DEVEL
drwxr-xr-x. 2 root root 8192 May 15 13:36 EDLIN
-rwxr-xr-x. 1 root root 1821 May 10 15:57 FDAUTO.BAT
-rwxr-xr-x. 1 root root 740 May 13 15:47 FDCONFIG.SYS
drwxr-xr-x. 10 root root 8192 May 10 15:49 FDOS
-rwxr-xr-x. 1 root root 46685 Apr 30 07:54 KERNEL.SYS
For example, to copy several C source files from my Linux projects directory into C:\
SRC on the virtual disk image, so I can use the files under FreeDOS later, I can use the
Linux cp command:
$ cp /home/jhall/projects/*.c /tmp/freedos/SRC
$ ls /tmp/freedos
APPS CONFIG.SYS EDLIN FDCONFIG.SYS KERNEL.SYS SRC.ZIP
$ guestunmount /tmp/freedos
Note that this command is spelled slightly differently from the Linux umount system
command.
By Jim Hall
Editing files is a staple on any operating system. Whether you want to make a note about
something, write a letter to a friend, or update a system configuration file—you need an editor.
And FreeDOS provides a user-friendly text editor called (perhaps unimaginatively) "FreeDOS
Edit."
Editing files
The simplest invocation of FreeDOS Edit is just EDIT. This brings up an empty editor window.
The patterned background suggests an empty "desktop"—a reminder that you aren't editing
any files.
Like most DOS applications, you can access the menus in Edit by tapping the Alt key once on
your keyboard. This activates the menu. After hitting Alt, Edit will switch to "menu" access
and will highlight the "File" menu. If you want to access a different menu on the menu bar, use
the left and right arrow keys. Press the down arrow or hit the Enter key to go "into" the menu.
Do you notice that the first letter for each menu title is a different color? This highlight
indicates a shortcut. For example, the "F" in the "File" menu is highlighted in red. So you could
instead press Alt+F (Alt and F at the same time) and Edit will bring up the "File" menu.
You can use the "File" menu to either start a new (empty) file, or open an existing file. Let's
start a new file by using the arrow keys to move down to "New" and pressing the Enter key.
You can also start a new file by pressing Ctrl+N (Ctrl and N at the same time):
Editing files should be pretty straightforward after that. Most of the familiar keyboard
shortcuts exist in FreeDOS Edit: Ctrl+C to copy text, Ctrl+X to cut text, and Ctrl+V to
paste copied or cut text into a new location. If you need to find specific text in a long
document, press Ctrl+F. To save your work, use Ctrl+S to commit changes back to the
disk.
Programming in Edit
If you're a programmer, you may find the extended ASCII table a useful addition. DOS
systems supported an "extended" ASCII character set commonly referred to as "code page
437." The standard characters between 0 and 127 include the letters A through Z (uppercase
and lowercase), numbers, and special symbols like punctuation. However, the DOS extended
characters from code 128 to code 255 included foreign language characters and "line drawing"
elements. DOS programmers often made use of these extended ASCII characters, so
FreeDOS Edit makes it easy to view a table of all the ASCII codes and their associated
characters.
Along the left, the table shows the hexadecimal values "00" through "F0," and the top shows
the single values "0" through "F." These provide a quick reference to the hexadecimal code for
each character. For example, the item in the first row (00) and the first column (0) has the
hexadecimal value 00 + 0, or 0x00 (the "NULL" value). And the character in the fifth row
(40) and the second column (1) has the value 40 + 1, or 0x41 (the letter "A").
As you move the cursor through the table to highlight different characters, you'll see the
values at the bottom of the table change to show the character's code in decimal,
hexadecimal, and octal. For example, moving the cursor to highlight the "line intersection"
character at row C0 and column 5 shows this extended character code as 197 (dec), 0xc5
(hex), and 305 (oct). In a program, you might reference this extended character by typing the
hex value 0xc5, or the octal "escape code" \305.
Feel free to explore the menus in Edit to discover other neat features. For example, the
"Options" menu allows you to change the behavior and appearance of Edit. If you prefer to
use a more dense display, you can use the "Display" menu (under "Options") to set Edit to 25,
43, or 50 lines. You can also force Edit to display in monochrome (white on black) or reversed
mode (black on white).
By Jim Hall
On Linux, I often use the GNU Emacs editor to write the source code for new programs. I
learned GNU Emacs long ago when I was an undergraduate student, and I still have the "finger
memory" for all the keyboard shortcuts.
When I started work on FreeDOS in 1994, I wanted to include an Emacs-like text editor. You
can find many editors similar to Emacs, such as MicroEmacs, but these all take some
shortcuts to fit into the 16-bit address space on DOS. However, I was very pleased to find
Freemacs, by Russell "Russ" Nelson.
You can find Freemacs in FreeDOS, on the Bonus CD. You can use FDIMPLES to install the
package, which will install to \APPS\EMACS.
Initial setup
The first time you run Freemacs, the editor will need to "compile" all of the setup files into a
form that Freemacs can quickly process. This will take a few minutes to run, depending on
your system's speed and memory, but fortunately, you only need to do it once.
Freemacs actually processes the editor files in two passes. When Freemacs has successfully
completed the first pass, it prompts you to restart the editor so it can finish processing. So
don't be surprised that the process seems to start up again—it's just "part 2" of the
compilation process.
Using Freemacs
To edit a file with Freemacs, start the program with the text file as an argument on the
command line. For example, emacs readme.doc will open the Readme file for editing in
Freemacs. Typing emacs at the command line, without any options, will open an empty
"scratch" buffer in Freemacs.
Or, you can start Freemacs without any command-line options, and use the Emacs
shortcuts C-x C-f (or M-x find-file). Freemacs then prompts you for a new file to load
into the editor. The shortcut prefix C- means you should press the Ctrl key and some other
key, so C-x is Ctrl and the x key together. And M-x is shorthand for "press the 'Meta' key
(usually Esc) then hit x."
Freemacs automatically detects the file type and attempts to load the correct support. For
example, opening a C source file will also set Freemacs to "C-mode."
If you also use GNU Emacs (like me), then you are probably curious to get Freemacs to match
the C indentation that GNU Emacs uses (2 spaces.) Here is how to set Freemacs to use 2
spaces in C-mode:
A few limitations
Much of the rest of Freemacs operates like GNU Emacs. If you're already familiar with GNU
Emacs, you should feel right at home in Freemacs. However, Freemacs does have a few
limitations that you might need to know:
The extension language is not LISP. The biggest difference between GNU Emacs on Linux
and Freemacs on FreeDOS is that Freemacs uses a different extension language. Where GNU
Emacs implements a LISP-like interpreter, Freemacs implements a different extension
You shouldn't expect to evaluate LISP code in Freemacs. The MINT language is completely
different from LISP. For more information on MINT, see the reference manual. We provide the
full documentation via the FreeDOS files archive on Ibiblio, at /freedos/files/edit/emacs/docs.
In particular, the MINT language is defined in mint.txt and mint2.txt.
Freemacs cannot open files larger than 64 kilobytes. This is a common limitation in
many programs. 64kb is the maximum size of the data space for programs that do not
leverage extended memory.
There is no "Undo" feature. Be careful in editing. If you make a mistake, you will have to re-
edit your file to get it back to the old version. Also, save early and often. For very large
mistakes, your best path might be to abandon the version you're editing in Freemacs, and load
the last saved version.
The rest is up to you! You can find more information about Freemacs on Ibiblio,
at /freedos/files/edit/emacs/docs. For a quick-start guide to Freemacs, read quickie.txt. The
full manual is in tutorial.txt.
By Jim Hall
In the very early days of DOS, the standard editor was a no-frills line editor called Edlin. Tim
Paterson wrote the original Edlin for the first version of DOS, then called 86-DOS and later
branded PC-DOS and MS-DOS. Paterson has commented that he meant to replace Edlin
eventually, but it wasn't until ten years later that MS-DOS 5 (1991) replaced Edlin with Edit, a
full-screen editor.
You may know that FreeDOS is an open source DOS-compatible operating system that you
can use to play classic DOS games, run legacy business software, or develop embedded
systems. FreeDOS has very good compatibility with MS-DOS, and the "Base" package group
includes those utilities and programs that replicate the behavior of MS-DOS. One of those
classic programs is an open source implementation of the venerable Edlin editor; Edlin is
distributed under the GNU General Public License version 2.
Written by Gregory Pietsch, Edlin is a well-designed, portable editor. You can even compile
Edlin on Linux. As Gregory described Edlin in the free ebook 23 Years of FreeDOS, The top
tier parses the input and calls the middle tier, a library called edlib, which calls the string and
array-handling code to do the dirty work. But aside from its technical merits, I find Edlin is a
joy to use when I want to edit text the "old school" way.
FreeDOS includes Edlin 2.18. That's actually one revision out of date, but you can
download Edlin 2.19 from the FreeDOS files archive on Ibiblio. You'll find two files there—edlin-
2.19.zip contains the source code, and edlin-219exe.zip is just the DOS executable. Download
the edlin-219exe.zip file, and extract it to your FreeDOS system. I've unzipped my copy in C:\
EDLIN.
A walkthrough
Start editing a file by typing EDLIN and then the name of the file to edit. For example, to edit
a C programming source file called HELLO.C, you might type:
I've typed the FreeDOS commands in all lowercase here. FreeDOS is actually case insensitive,
so you can type commands and files in uppercase or lowercase.
Typing edlin or EDLIN or Edlin would each run the Edlin editor. Similarly, you can identify
the source file as hello.c or HELLO.C or Hello.C.
Once inside Edlin, you'll be greeted by a friendly * prompt. The interface is pretty minimal; no
shiny "menu" or mouse support here. Just type a command at the * prompt to start editing,
revise lines, search and replace, save your work, or exit the editor.
Since this is a new file, we'll need to add new lines. We'll do this with the insert command, by
typing i at the * prompt. The Edlin prompt changes to : where you'll enter your new text.
When you are done adding new text, type a period (.) on a line by itself.
*i
: #include <stdio.h>
:
: int
: main()
: {
: puts("Hello world");
: }
: .
*
*l
1: #include <stdio.h>
2:
3: int
4: main()
5: {
6: puts("Hello world");
7:*}
*
Did you notice the * on line 7, the last line in the file? That's a special mark indicating your
place in the file. If you inserted new text in the file, Edlin would add it at this location.
Let's update the C source file to return a code to the operating system. To do that, we'll need
to add a line above line 7. Since that's where Edlin has the mark, we can use i to insert next
text before this line. Don't forget to enter . on a line by itself to stop entering the new text.
By listing the file contents afterwards, you can see that we inserted the new text in the correct
place, before the closing "curly brace" in the program.
*i
: return 0;
: .
*l
1: #include <stdio.h>
2:
3: int
4: main()
5: {
6: puts("Hello world");
7: return 0;
8:*}
*
But what if you need to edit a single line in the file? At the * prompt,simply type the line
number that you want to edit. Edlin works one line at a time, so you'll need to re-enter the full
line. In this case, let's update the main() function definition to use a slightly different
programming syntax. That's on line 4, so type 4 at the prompt, and re-type the line in full.
When you've made all the changes you need to make, don't forget to save the updated file.
Enter w at the prompt to write the file back to disk, then use q to quit Edlin and return to DOS.
*w
hello.c: 8 lines written
*q
C:\EDLIN>
? Show help
num Edit a single line
a Append a line below the mark
[num]i Insert new lines before the mark
[num][,num]l List the file (starting 11 lines above the mark)
[num][,num]p Page (same as List, but starting at the mark)
[num],[num],num,[num]c Copy lines
[num],[num],numm Move lines
[num][,num][?]stext Search for text
[num][,num][?]rtext,text Replace text
[num][,num]d Delete lines
[num]tfilename Transfer (insert the contents of a new file at the mark)
[num]w[filename] Write the file to disk
q Quit Edlin
e[filename] End (write and quit)
Programmers will be interested to know they can enter special characters in Edlin, using these
special codes:
\a alert
\b backspace
\e escape
\f formfeed
\t horizontal tab
\v vertical tab
\" double quote
\' single quote
\. period
\\ backslash
\xXX hexadecimal number
\dNNN decimal number
\OOO octal number
\^C control character
By Jim Hall
When I’m not at work on my Linux desktop, you can usually find me writing code for a legacy
16-bit system. FreeDOS is an open source DOS-compatible operating system that you can
use to play classic DOS games, run legacy business software, or develop embedded systems.
Any program that works on MS-DOS should also run on FreeDOS.
I grew up with DOS. My family’s first personal computer was an Apple II clone, but we
eventually upgraded to an IBM PC running DOS. I was a DOS user for over ten years, from the
early 1980s until 1993, when I discovered Linux.
I was impressed by the freedom afforded by Linux and open source software. So when
Microsoft announced the end of DOS in 1994, with the forthcoming Windows 95, I decided to
write my own open source DOS. That’s how FreeDOS started.
All these years later, and I continue working on FreeDOS. It is an excellent hobby system,
where I can run my favorite DOS applications and games. And yes, I still write code for
FreeDOS.
My favorite editor for DOS programming is the FED editor. FED is a minimal text editor
without a lot of visual flair. This minimal approach helps me make the most of the standard
80x25 screen in DOS. When editing a file, FED displays a single status line at the bottom of
the screen, leaving you the remaining 24 lines to write your code. FED also supports color
syntax highlighting to display different parts of your code in various colors, making it easier to
spot typos before they become bugs.
When you need to do something in the menus, press the Alt key on the keyboard, and FED
displays a menu on the top line. FED supports keyboard shortcuts too, but be careful about
the defaults. For example, Ctrl-C will close a file, and Ctrl-V will change the view. If you don’t
like these default keys, you can change the key mapping in the Config menu.
If you don’t like the default black-on-white text display, you can change the colors under the
Config menu. I prefer white-on-blue for my main text, with keywords in bright white,
comments in bright blue, special characters in cyan, and numbers in green. FED makes it easy
to set the colors you want.
FED is also a folding text editor, which means that it can collapse or expand portions of my
code so that I can see more of my file. Tap Ctrl-F on a function name and FED will collapse
the entire function. Folding works on other code, as well. I also use folding to hide for and
while loops or other flow controls like if and switch blocks.
Shawn Hargreaves wrote and maintained FED from 1994 to 2004. Robert Riebisch has
maintained FED since then. FED is distributed under the GNU GPL and supports DOS, Linux,
and Windows.
By Jim Hall
Music is a great way to relax. On Linux, I listen to music using Rhythmbox. But did you know
you can listen to music on FreeDOS, as well? Let's take a look at two popular programs to
listen to music:
I use Mplayer to listen to music files on FreeDOS. For this example, I've copied one of my
favorite audiobooks, Doctor Who: Flashpoint by Big Finish Productions, and saved it as C:\
MUSIC\FLASHPNT.MP3 on my FreeDOS computer. To listen to Flashpoint on FreeDOS, I
launch Mplayer from the FreeDOS command line and specify the MP3 filename to play. The
basic usage of Mplayer is mplayer [options] filename so if the default settings work
well for you, then you can just launch Mplayer with the filename. In this case, I ran these
commands to change my working directory to \MUSIC and then run Mplayer with my MP3
audiobook file:
CD \MUSIC
MPLAYER FLASHPNT.MP3
FreeDOS is case insensitive, so it will accept uppercase or lowercase letters for DOS
commands and any files or directories. You could also type cd \music or Cd \Music to
move into the Music directory, and that would work the same.
Using Mplayer is a "no frills" way to listen to music files on FreeDOS. But at the same time, it's
not distracting, so I can leave FreeDOS to play the MP3 file on my DOS computer while I use
my other computer to do something else. However, FreeDOS runs tasks one at a time (in
other words, DOS is a "single-tasking" operating system) so I cannot run Mplayer in the
"background" on FreeDOS while I work on something else on the same FreeDOS computer.
Note that Mplayer is a big program that requires a lot of memory to run. While DOS itself
doesn't require much RAM to operate, I recommend at least 16 megabytes of memory to run
Mplayer.
In 1999, I recorded a short audio file of me saying, "Hello, this is Jim Hall, and I pronounce
'FreeDOS' as FreeDOS." This was meant as a joke, riffing off of a similar audio
file (english.au, included in the Linux source code tree in 1994) recorded by Linus Torvalds
You can listen to the FreeDOS audio clip using the Open Cubic Player. To run Open Cubic
Player, you normally would run CP from the \APPS\OPENCP directory. However, Open Cubic
Player is a 32-bit application that requires a 32-bit DOS extender. A common DOS extender is
DOS/4GW. While free to use, DOS/4GW is not an open source program, so we do not
distribute it as a FreeDOS package.
Instead, FreeDOS provides another open source 32-bit extender called DOS/32A. If you did
not install everything when you installed FreeDOS, you may need to install it using FDIMPLES.
I used these two commands to move into the \APPS\OPENCP directory, and to run Open
Cubic Player using the DOS/32A extender:
CD \APPS\OPENCP
DOS32A CP
Open Cubic Player doesn't sport a fancy user interface, but you can use the arrow keys to
navigate the File Selector to the directory that contains the media file you want to play.
The text appears smaller than in other DOS applications because Open Cubic Player
automatically changes the display to use 50 lines of text, instead of the usual 25 lines. Open
Cubic Player will reset the display back to 25 lines when you exit the program.
When you have selected your media file, Open Cubic Player will play it in a loop. (Press the
Esc key on your keyboard to quit.) As the file plays over the speakers, Open Cubic Player
displays a spectrum analyzer so you can see the audio for the left and right channels. The
FreeDOS audio clip is recorded in mono, so the left and right channels are the same.
DOS may be from an older era, but that doesn't mean you can't use FreeDOS to run modern
tasks or play current media. If you like to listen to digital music, try using Open Cubic Player or
Mplayer on FreeDOS.
By Jim Hall
In the early days of personal computing—from the late 1970s and through the 1980s—many
people got their start with BASIC programming. BASIC was a universal programming
language that came built into most personal computers, from Apple to IBM PCs.
When we started the FreeDOS Project in June 1994, it seemed natural that we should include
an open source BASIC environment. I was excited to discover one already existed in Bywater
BASIC.
The Bywater BASIC website reminds us that Bywater BASIC implements a large superset of
the ANSI Standard for Minimal BASIC (X3.60-1978) and a significant subset of the ANSI
Standard for Full BASIC (X3.113-1987). It's also distributed under the GNU General Public
License version 2, which means it's open source software. We only want to include open
source programs in FreeDOS, so Bywater BASIC was a great addition to FreeDOS in our early
days.
We've included Bywater BASIC since at least FreeDOS Alpha 5, in 1997. You can find Bywater
BASIC in FreeDOS in the "Development" package group on the Bonus CD. Load this:
FreeDOS installs the Bywater BASIC package in the \DEVEL\BWBASIC directory. Change to
this directory with CD \DEVEL\BWBASIC and type BWBASIC to run the Bywater BASIC
interpreter.
One feature I like in Bywater BASIC is the integrated "help" system. There's nothing more
frustrating than forgetting the syntax for a BASIC statement. For example, I always forget how
to create BASIC loops. Do I use FOR I IN 1 TO 10 or FOR I = 1 TO 10? Just
type help FOR at the Bywater BASIC prompt and the interpreter displays the usage and a
brief description.
Another neat feature in Bywater BASIC is how it reformats your BASIC instructions, so they
are easier to read. After typing my brief program, I can type list to see the full source listing.
Bywater BASIC automatically adds the CALL keyword to my RANDOMIZE statement on line 10
and indents the PRINT statement inside my loop. These small changes help me to see loops
and other features in my program, which can aid in debugging.
If everything looks okay, then type RUN to execute the program. Because I used
the RANDOMIZE statement at the start of my BASIC program, Bywater seeds the random
number generator with a random starting point. This ensures that my numbers are actually
random values and don't repeat when I re-run my program.
Install Bywater BASIC on your FreeDOS system and start experimenting with BASIC
programming. BASIC can be a great first programming language, especially if you are
interested in getting back to the "roots" of personal computing. You can find more
information about Bywater BASIC in the manual, installed in the \DEVEL\BWBASIC directory
as BWBASIC.DOC. You can also explore the online "help" system by typing HELP at the
Bywater BASIC prompt.
By Jim Hall
When I was growing up, it seemed every "personal computer" from the TRS-80 to the
Commodore to the Apple let you write your own programs in the Beginners' All-purpose
Symbolic Instruction Code (BASIC) programming language. Our family had a clone of the
Apple II called the Franklin ACE 1000, which—as a clone—also ran AppleSoft BASIC. I took to
AppleSoft BASIC right away and read books and magazines to teach myself about BASIC
programming.
Later, our family upgraded to an IBM PC running DOS. Just like every personal computer
before it, the IBM PC also ran its own version of DOS, called BASICA. Later versions of DOS
replaced BASIC with an updated interpreter called GW-BASIC.
BASIC was my entry into computer programming. As I grew up, I learned other programming
languages. I haven't written BASIC code in years, but I'll always have a fondness for BASIC
and GW-BASIC.
Since re-open-sourcing MS-DOS 1.25 & 2.0 on GitHub last year, we’ve received numerous
requests to also open-source Microsoft BASIC. Well, here we are! As clearly stated in the
repo's readme, these sources are the 8088 assembly language sources from 10th Feb 1983
and are being open-sourced for historical reference and educational purposes. This means we
will not be accepting PRs (Pull Requests) that modify the source in any way.
Unfortunately, the GW-BASIC code was entirely in Assembly, which wouldn't build with
modern tools. But open source developers got to work on that and adjusted the code to
assemble with updated DOS assemblers. One project is TK Chia's GitHub project to update
GW-BASIC to assemble with JWASM or other assemblers. You can find several source and
binary releases on TK Chia's project. The notes from the latest version (October 2020)
mention that this is a 'pre-release' binary of GW-BASIC as rebuilt in 2020 and that support for
serial port I/O is missing. Light pen input, joystick input, and printer (parallel port) output
need more testing. But if you don't need those extra features in GW-BASIC, you should be
able to use this latest release to get back into BASIC programming with an open-sourced
GW-BASIC.
FreeDOS doesn't include GW-BASIC, but installing it is pretty easy. Just download
the gwbas-20201025.zip archive file from TK Chia's October 2020 GW-BASIC release,
and extract it (unzip it) on your FreeDOS system. The binary archive uses a default path of \
DEVEL\GWBASIC.
Each line in a GW-BASIC program needs to start with a line number. GW-BASIC uses the line
numbers to make sure it executes your program statements in the correct order. With these
line numbers, you can later "insert" new program statements between two other statements
by giving it a line number that's somewhere in between the other line numbers. For this
reason, most BASIC programmers wrote line numbers that went up by tens so that line
numbers would go like 10, 20, 30, and so on.
New to GW-BASIC? You can learn about the programming language by reading an online
GW-BASIC reference. Microsoft didn't release a programming guide with the GW-BASIC
source code, but you can search for one. Here's one reference that seems to be a copy of the
original Microsoft GW-BASIC User's Guide.
Do you see those highlighted words at the bottom of the screen? Those are keyboard
shortcuts that you can access using the "F" keys (or function keys) on your keyboard. For
example, F1 will insert the word LIST into the GW-BASIC interpreter. The "left arrow"
indicates that the shortcut will hit Enter for you, so F2 will enter the RUN command and
immediately execute it. Let's run the program a few times to see what happens.
Interestingly, the list of random numbers is the same every time we run the BASIC program.
That's because the GW-BASIC random number generator resets every time you execute a
BASIC program.
To generate new random numbers every time, we need to "seed" the random number
generator with a value. One way to do this is by prompting the user to enter their own seed,
then use that value with the RANDOMIZE instruction. We can insert those two statements at
the top of the program using line numbers 1 and 2. GW-BASIC will automatically add those
statements before line 10.
With the random number generator using a new seed, we get a different list of random
numbers every time we run our program.
To help me practice a new programming language, I like to write a few test programs. One
sample program I often write is a simple "guess the number" game, where the computer picks
a number between one and 100 and asks me to guess it. The program loops until I guess
correctly.
Let's write a version of this "guess the number" game in GW-BASIC. To start, enter
the NEW instruction to tell GW-BASIC to forget the previous program and start a new one.
My "guess the number" program first prompts the user to enter a random number seed, then
generates a random number between 1 and 100. The RND(1) function actually generates a
The program then enters a simple loop where it prompts the user for a guess. If the guess is
too low or too high, the program lets the user know to adjust their guess. The loop continues
as long as the user's guess is not the same as the random number picked earlier.
We can run the program by tapping the F2 key. Using a random seed of 1234 generates a
completely new random number. It took me six guesses to figure out the secret number was
49.
And that's your first introduction to GW-BASIC programming! Thanks to Microsoft for
releasing this great piece of history as open source software, and thanks also to the many
open source developers who assembled GW-BASIC so we can run it.
One more thing before I go—It's not obvious how to exit GW-BASIC. The interpreter had a
special instruction for that—to quit, enter SYSTEM and GW-BASIC will exit back to DOS.
By Jim Hall
When I first started using DOS, I enjoyed writing games and other interesting programs using
BASIC, which DOS included. Much later, I learned the C programming language.
So it's probably not surprising that FreeDOS includes a C compiler—along with other
programming languages. The FreeDOS LiveCD includes two C compilers—Bruce's C compiler
(a simple C compiler) and the OpenWatcom C compiler. On the Bonus CD, you can also find
DJGPP (a 32-bit C compiler based on GNU GCC) and the IA-16 port of GCC (requires a '386
or better CPU to compile, but the generated programs can run on low-end systems).
1. You need to remain aware of how much memory you use. Linux allows programs
to use lots of memory, but FreeDOS is more limited. Thus, DOS programs used one of
four memory models (large, medium, compact, and small) depending on how much
memory they needed.
2. You can directly access the console. On Linux, you can create text-mode mode
programs that draw to the terminal screen using a library like ncurses. But DOS allows
programs to access the console and video hardware. This provides a great deal of
flexibility in writing more interesting programs.
I like to write my C programs in the IA-16 port of GCC, or OpenWatcom, depending on what
program I am working on. The OpenWatcom C compiler is easier to install since it's only a
single package. That's why we provide OpenWatcom on the FreeDOS LiveCD, so you can
install it automatically if you choose to do a "Full installation including applications and games"
opensource.com
DOS C programming
You can find documentation and library guides on the OpenWatcom project website to learn
all about the unique DOS C programming libraries provided by the OpenWatcom C compiler.
To briefly describe a few of the most useful functions:
From conio.h:
From graph.h:
DOS only supports sixteen text colors and eight background colors. You can use the values 0
(Black) to 15 (Bright White) to specify the text colors, and 0 (Black) to 7 (White) for the
background colors:
• 0—Black
• 1—Blue
• 2—Green
• 3—Cyan
• 4—Red
• 5—Magenta
• 6—Brown
• 7—White
• 8—Bright Black
• 9—Bright Blue
• 10—Bright Green
• 11—Bright Cyan
• 12—Bright Red
• 13—Bright Magenta
• 14—Yellow
• 15—Bright White
In this case, we'll iterate through each of the text colors, from 0 (Black) to 15 (Bright White).
As we print each line, we'll indent the next line by one space. When we're done, we'll wait for
the user to press any key, then we'll reset the screen and exit.
You can use any text editor to write your C source code. I like using a few different editors,
including FreeDOS Edit and Freemacs, but more recently I've been using the FED
editor because it provides syntax highlighting, making it easier to see keywords, strings, and
variables in my program source code.
Once your environment is ready, you can use the OpenWatcom compiler to compile this "Hello
world" program. I've saved my C source file as TEST.C, so I can type WCL TEST.C to compile
and link the program into a DOS executable, called TEST.EXE. In the output messages from
OpenWatcom, you can see that WCL actually calls the OpenWatcom C Compiler (WCC) to
compile, and the OpenWatcom Linker (WLINK) to perform the object linking stage:
If you don't see any error messages when compiling the C source file, you can now run your
DOS program. This "Hello world" example is TEST.EXE. Enter TEST on the DOS command
line to run the new program, and you should see this very pretty output:
By Jim Hall
One of the reasons so many DOS applications sported a text user interface (or TUI) is
because it was so easy to do. The standard way to control console input and output (conio)
was with the conio library for many C programmers. This is a de-facto standard library on
DOS, which gained popularity as implemented by Borland's proprietary C compiler as
conio.h. You can also find a similar conio implementation in TK Chia's IA-16 DOS port of
the GNU C Compiler in the libi86 library of non-standard routines. The library includes
implementations of conio.h functions that mimic Borland Turbo C++ to set video modes,
display colored text, move the cursor, and so on.
#include <conio.h>
#include <graph.h>
int
main()
{
_setvideomode(_TEXTC80);
…
When you're done with your program and ready to exit back to DOS, you should reset the
video mode back to whatever values it had before. For that, you can use _DEFAULTMODE as
the mode.
_setvideomode(_DEFAULTMODE);
return 0;
}
_settextcolor(14);
_setbkcolor(4);
Positioning text
In conio, screen coordinates are always row,col and start with 1,1 in the upper-left corner. For
a standard 80-column display with 25 lines, the bottom-right corner is 25,80.
Use the _settextposition function to move the cursor to a specific screen coordinate,
then use _outtext to print the text you want to display. If you've set the colors, your text will
use the colors you last defined, regardless of what's already on the screen.
For example, to print the text "FreeDOS" at line 12 and column 36 (which is more or less
centered on the screen) use these two functions:
_settextposition(12, 36);
_outtext("FreeDOS");
#include <conio.h>
#include <graph.h>
int
main()
{
_setvideomode(_TEXTC80);
_settextcolor(14);
_setbkcolor(4);
_settextposition(12, 36);
_outtext("FreeDOS");
getch();
_setvideomode(_DEFAULTMODE);
return 0;
}
Text windows
The trick to unleashing the power of conio is to leverage a feature of the PC video display
where a program can control the video hardware by region. These are called text windows and
are a really cool feature of conio.
A text window is just an area of the screen, defined as a rectangle starting at a particular
row,col and ending at a different row,col. These regions can take up the whole screen or be as
small as a single line. Once you define a window, you can clear it with a background color and
position text in it.
To define a text window starting at row 5 and column 10, and extending to row 15 and column
70, you use the _settextwindow function like this:
Now that you've defined the window, any text you draw in it uses 1,1 as the upper-left corner of
the text window. Placing text at 1,1 will actually position that text at row 5 and column 10, where
the window starts on the screen.
_setbkcolor(3);
_clearscreen(_GCLEARSCREEN);
_settextwindow(5, 10, 15, 70);
_setbkcolor(1);
_clearscreen(_GWINDOW);
This makes it really easy to fill in certain areas of the screen. In fact, defining a window and
filling it with color is such a common thing to do that I often create a function to do both at
once. Many of my conio programs include some variation of these two functions to clear the
screen or window:
#include <conio.h>
#include <graph.h>
void
clear_color(int fg, int bg)
{ _settextcolor(fg);
_setbkcolor(bg);
_clearscreen(_GCLEARSCREEN);
}
void
textwindow_color(int top, int left, int bottom, int right, int fg, int bg)
{
_settextwindow(top, left, bottom, right);
_settextcolor(fg);
_setbkcolor(bg);
_clearscreen(_GWINDOW);
}
A text window can be any size, even a single line. This is handy to define a title bar at the top
of the screen or a status line at the bottom of the screen. Again, I find this to be such a useful
addition to my programs that I'll frequently write functions to do it for me:
#include <conio.h>
#include <graph.h>
#include <string.h> /* for strlen */
This is the basics of many kinds of applications. Placing a text window towards the right of the
screen could be useful if you were writing a "monitor" program, such as part of a control
system, like this:
#include <conio.h>
#include <graph.h>
int
main()
{
_setvideomode(_TEXTC80);
clear_color(7, 1); /* white on blue */
_settextposition(2, 1);
Having already written our own window functions to do most of the repetitive work, this
program becomes very straightforward: clear the screen with a blue background, then print
"test" on the second line. There's a header line and a status line, but the interesting part is in
the middle where the program defines a text window near the right edge of the screen and
prints some sample text. The getch() function waits for the user to press a key on the
keyboard, useful when you need to wait until the user is ready:
Conio mon
#include <conio.h>
#include <graph.h>
int
main()
{
_setvideomode(_TEXTC80);
clear_color(7, 2); /* white on green */
_settextposition(2, 1);
_outtext("test");
print_header(14, 4, "SOLITAIRE"); /* br yellow on red */
textwindow_color(10, 10, 17, 22, 4, 7); /* red on white */
_settextposition(3, 2);
_outtext("hi mom");
print_status(7, 6, "press any key to quit..."); /* white on brown */
getch();
_setvideomode(_DEFAULTMODE);
return 0;
}
You could add other code to this sample program to print card values and suits, place cards on
top of other cards, and other functionality to create a complete game. But for this demo, we'll
just draw a single "card" displaying some text:
You can create other effects using text windows. For example, before drawing a message
window, you could first draw a black window that's offset by one row and one column. The text
window will appear to create a shadow over that area of the screen to the user. And we can do
it all by changing only a few values in our sample program:
#include <conio.h>
#include <graph.h>
int
main()
{
_setvideomode(_TEXTC80);
clear_color(7, 1); /* white on blue */
_settextposition(2, 1);
_outtext("test");
print_header(15, 3, "PROGRAMMING IN CONIO"); /* br white on cyan */
textwindow_color(11, 36, 16, 46, 7, 0); /* shadow */
textwindow_color(10, 35, 15, 45, 7, 4); /* white on red */
_settextposition(3, 2);
_outtext("hi mom");
print_status(0, 7, "press any key to quit..."); /* black on white */
getch();
You often see this "shadow" effect used in DOS programs as a way to add some visual flair:
The DOS conio functions can do much more than I've shown here, but with this introduction
to conio programming, you can create various practical and exciting applications. Direct
screen access means your programs can be more interactive than a simple command-line
utility that scrolls text from the bottom of the screen. Leverage the flexibility of conio
programming and make your next DOS program a great one.
By Jim Hall
If you've looked carefully at FreeDOS, you've probably noticed that text only comes in a
limited range of colors—sixteen text colors, and eight background colors. This is similar to how
Linux displays text color—you might be able to change what text colors are used in a Linux
terminal, but you're still stuck with just sixteen text colors and eight background colors.
IBM released the Personal Computer 5150 (the "IBM PC") in 1981. The PC used a simple
monitor screen that displayed text in green. Because this display only worked with one color, it
was dubbed monochrome (the "IBM 5151 monochrome display," with the IBM Monochrome
Display Adapter card, or "MDA").
That same year, IBM released an updated version of the PC that sported an amazing technical
achievement—color! The new IBM 5153 color display relied on a new IBM Color Graphics
Adapter, or "CGA." And it is because of this original CGA that all DOS text inherited their
colors.
But before we go there, we first need to understand something about color. When we talk
about colors on a computer screen, we're talking about mixing different values of the
three primary light colors—red, green, and blue. You can mix together different levels (or
"brightnesses") of red, green, and blue light to create almost any color. Mix just red and blue
light, and you get magenta. Mix blue and green, and you get cyan or aqua. Mix all colors
equally, and you get white. Without any light colors, you see black (an absence of color).
The IBM 5153 color display presented color to the user by lighting up tiny red, green, and blue
phosphor dots on a cathode ray tube (a "CRT"). These tiny dots were arranged very close
together and in a pattern where a triad of red, green, and blue dots would form a "pixel." By
controlling which phosphor dots were lit at one time, the IBM 5153 color display could show
different colored pixels.
By the way, even modern displays use this combination of red, green, and blue dots to
represent colors. The difference in modern computers is that instead of tiny phosphor dots,
each pixel uses a triad of red, green, and blue LED lights—usually arranged side by side. The
computer can turn each LED light on or off to mix the red, green, and blue colors in each
pixel.
Representing red, green, and blue with ones or zeroes means you can combine up to eight
colors, from 000 (red, green, and blue are all off) to 111 (red, green, and blue are all on). Note
000 Black
001 Blue
010 Green
011 Cyan
100 Red
101 Magenta
110 Yellow
111 White
But that's just the simplest case. A particularly clever IBM engineer realized you could double
the number of colors from eight to sixteen simply by adding another bit. So instead of a bit
pattern like RGB, we can use a bit pattern like iRGB. We'll call this extra "i" bit the "intensity"
bit because if we set the "intensity" bit to 1 (on), then we'll light up the red, green, and blue
phosphor dots at full brightness; if the "intensity" bit is 0 (off) we can use some mid-level
brightness.
And with that simple fix, now CGA could display sixteen colors! For the sake of simplicity, the
IBM engineers referred to the high-intensity colors as the "bright" versions of the regular color
names. So "red" pairs with "bright red," and "magenta" pairs with "bright magenta."
Oh no! But wait! This isn't actually sixteen colors. If you notice iRGB=0000 (black) and
iRGB=1000 (bright black), they are both the same black. There's no color to make "bright," so
they are just both regular black. This means we only have fifteen colors, not the sixteen we
were hoping for.
But IBM has clever engineers working for them, and they realized how to fix this to get sixteen
colors. Rather than implement a straight RGB to iRGB, IBM actually implemented
a modified iRGB scheme. With this change, IBM set four levels of brightness for each
Let me describe this to you another way, using web color code representation. If you are
familiar with the HTML colorspace, you probably know that you can represent colors using
#RGB, where RGB represents a combination of red, green, and blue values, each between the
hexadecimal values 0 through F. So using IBM's modified iRGB definition, iRGB=0001 is #00a
(blue) and iRGB=1001 is #55f (bright blue) because with high-intensity colors, all zeroes in
RGB=001 are lit at one-third brightness (around "5" on the 0 to F scale) and all ones in
RGB=001 are lit at two-third brightness (about "A" on the 0 to F scale).
And with those colors, we are finally done! We have a full spectrum of colors from iRGB=0000
(black) to iRGB=1111 (bright white) and every color in between. Like a rainbow of colors, this is
beautiful.
Except, no. Wait. Something's wrong here. We can't actually replicate all of the colors of the
rainbow yet. The handy mnemonic we learned in grade school was ROYGBIV, to help us
remember that a rainbow has colors from red, orange, yellow, green, blue, indigo, and violet.
Our modified iRGB color scheme includes red, yellow, green, and blue—and we can "fake" it
for indigo and "violet." But we're missing orange. Oh no!
To fix this, the smart IBM engineers made one final fix for RGB=110. The high-intensity color
(iRGB=1110) lit up the red and green phosphor dots at full brightness to make yellow. But at
the low-intensity color (iRGB=0110), they lit the red at two-thirds brightness and the green at
one-third brightness. This turned iRGB=0110 into an orange color—although it was later
dubbed "brown" because IBM had to mess up the standard names somewhere.
And that's how CGA—and by extension, DOS—got the sixteen colors! And in case you're
curious, that's also why there's a "bright black" color, even though it's just a shade of gray.
In brief, the CGA card expected each character's text color and background color to be
encoded in a single byte packet. That's eight bits. So where do the eight bits come from?
We just learned how iRGB (four bits) generates the sixteen colors. Text color uses iRGB, or
four bits. The background color is limited to the eight low-intensity colors (RGB, or three
bits). Together, that makes only seven bits. Where is the missing eighth bit?
The final bit was reserved for perhaps the DOS era's most important user interface element—
blinking text. While the blinking text might be annoying today, throughout the early 1980s,
blinking text was the friendly way to represent critical information such as error messages.
Adding this "blink" bit to the three background color bits (RGB) and the four text color bits
(iRGB) makes eight bits or a byte! Computers like to count in full bytes, making this a
convenient way to package color (and blink) information to the computer.
Thus, the full byte to represent color (and blink) was Bbbbffff, where ffff is the iRGB bit
pattern for the text color (from 0 to 15), bbb is the RGB bit pattern for the low-intensity
background color (from 0 to 7), and B is the "blink" bit.
The limit of sixteen text colors and eight background colors continues to this day. Certainly,
DOS is stuck with this color palette, but even Linux terminal emulators like GNOME Terminal
remain constrained to sixteen text colors and eight background colors. Sure, a Linux terminal
might let you change the specific colors used, but you're still limited to sixteen text colors and
eight background colors. And for that, you can thank DOS and the original IBM PC. You're
welcome!
By Jim Hall
Full-color ASCII art used to be quite popular on DOS, which could leverage the extended
ASCII character set and its collection of drawing elements. You can add a little visual interest
to your next FreeDOS program by adding ASCII art as a cool “welcome” screen or as a colorful
“exit” screen with more information about the program.
But this style of ASCII art isn’t limited just to FreeDOS applications. You can use the same
method in a Linux terminal-mode program. While Linux uses ncurses to control the screen
instead of DOS's conio, the related concepts apply well to Linux programs. This article looks
at how to generate colorful ASCII art from a C program.
Here’s part of a sample ASCII art file, saved as C source code. Note that the code snippet
defines a few values: IMAGEDATA_WIDTH and IMAGEDATA_DEPTH define the number of
columns and rows on the screen. In this case, it’s an 80x25 ASCII art “image.”
IMAGEDATA_LENGTH defines the number of entries in the IMAGEDATA array. Each character
in the ASCII art screen can be represented by two bytes of data: The character to display and
a color attribute containing both the foreground and background colors for the character. For
an 80x25 screen, where each character is paired with an attribute, the array contains 4000
entries (that’s 80 * 25 * 2 = 4000).
To display this ASCII art to the screen, you need to write a small program to read the array and
print each character with the right colors.
Character mode systems like ncurses on Linux or conio on DOS can display only sixteen
colors. That’s sixteen possible text colors and eight background colors. Counting sixteen
values (from 0 to 15) in binary requires only four bits:
• 1111 is 16 in binary
With color pairs, you can encode both the background and foreground colors in a single byte
of eight bits. That’s four bits for the text color (0 to 15 or 0 to F in hexadecimal) and three bits
for the background color (0 to 7 or 0 to E in hexadecimal). The leftover bit in the byte is not
used here, so we can ignore it.
To convert the color pair or attribute into color values that your program can use, you’ll need
to use a bit mask to specify only the bits used for the text color or background color. Using
the OpenWatcom C Compiler on FreeDOS, you can write this function to set the colors
appropriately from the color attribute:
void
textattr(int newattr)
{
The _settextcolor function sets just the text color, and the _setbkcolor function sets
the background color. Both are defined in graph.h. Note that because the color attribute
included both the background color and the foreground color in a single byte value, the
textattr function uses & (binary AND) to set a bit mask that isolates only the last four bits
in the attribute. That’s where the color pair stores the values 0 to 15 for the foreground color.
To get the background color, the function first performs a bit shift to “push” the bits to the
right. This puts the “upper” bits into the “lower” bit range, so any bits like 0xxx0000 become
00000xxx instead. We can use another bit mask with 7 (binary 0111) to pick out the
background color value.
Let’s leave room at the bottom of the screen for a separate message or prompt to the user.
That means instead of displaying all 25 lines of an 80-column ASCII screen, I only want to
show the first 24 lines.
Inside the for loop, we need to set the colors, then print the character. The OpenWatcom C
Compiler provides a function _outtext to display text with the current color values.
However, this requires passing a string and would be inefficient if we need to process each
character one at a time, in case each character on a line requires a different color.
Instead, OpenWatcom has a similar function called _outmem that allows you to indicate how
many characters to display. For one character at a time, we can provide a pointer to a
textattr(attr);
_outmem(ch, 1);
}
This updated for loop sets the character ch by assigning a pointer into the IMAGEDATA
array. Next, the loop sets the text attributes, and then displays the character with _outmem.
#include <stdio.h>
#include <conio.h>
#include <graph.h>
#include "imgdata.inc"
void
textattr(int newattr)
{
_settextcolor(newattr & 15); /* 0000xxxx */
_setbkcolor((newattr >> 4) & 7); /* 0xxx0000 */
}
int
main()
{
char *ch;
int attr;
int pos;
if (_setvideomode(_TEXTC80) == 0) {
fputs("Error setting video mode", stderr);
return 1;
}
/* draw the array */
_settextposition(1, 1); /* top left */
/* print one line less than the 80x25 that's in there:
80 x 24 x 2 = 3840 */
Compile the program using the OpenWatcom C Compiler on FreeDOS, and you’ll get a new
program that displays this holiday message:
Happy Halloween
(CC-BY-SA 4.0)
By Kevin O'Brien
On Linux, it's common to create shell scripts to automate repetitive tasks. Similarly, on
FreeDOS, the open source implementation of old DOS operating systems, you can create a
batch file containing several FreeDOS commands. Then you can run your batch file to execute
each command in order.
You create a batch file by using an ASCII text editor, such as the FreeDOS Edit application.
Once you create a batch file, you save it with a file name and the extension .bat. The file
name should be unique. If you use a FreeDOS command name as your own file name, the
FreeDOS command probably will execute instead of your batch file.
Virtually all internal and external FreeDOS commands can be used in a batch file. When you
create a batch file, you are essentially writing a program. FreeDOS batch files may not have
the power of a structured programming language, but they can be very handy for quick but
repetitive tasks.
FreeDOS ignores lines starting with REM. But anyone who looks at the source code (the text
you've written in your batch file) can read your comments and understand what it's doing. This
is also a way to temporarily disable a command without deleting it. Just open your batch file
for editing, place REM at the beginning of the line you want to disable, and save it. When you
Get set up
Before you start writing your own batch files, I suggest creating a temporary directory in
FreeDOS. This can be a safe space for you to play around with batch files without accidentally
deleting, moving, or renaming important system files or directories. On FreeDOS, you create a
directory with the MD command:
C:\>MD TEMP
C:\>CD TEMP
C:\TEMP>
The ECHO FreeDOS command controls what is shown on the screen when you run a batch file.
For instance, here is a simple one-line batch file:
If you create this file and run it, you will see the sentence displayed on the screen. The
quickest way to do this is from the command line: Use the COPY command to take the input
from your keyboard (CON) and place it into the file TEST1.BAT. Then press Ctrl+Z to stop
the copy process, and press Return or Enter on your keyboard to return to a prompt.
Try creating this file as TEST1.BAT in your temporary directory, and then run it:
This can be useful when you want to display a piece of text. For instance, you might see a
message on your screen telling you to wait while a program finishes its task, or in a networked
environment, you might see a login message.
What if you want to display a blank line? You might think that the ECHO command all by itself
would do the trick, but the ECHO command alone asks FreeDOS to respond whether ECHO is
on or off:
The way to get a blank line is to use a + sign immediately after ECHO:
C:\TEMP>ECHO+
C:\TEMP>
Create TEST3.BAT:
@MD BACKUPS
COPY %1 BACKUPS\%1
Variables are signified by the use of the percentage symbol followed by a number, so this
batch file creates a BACKUPS subdirectory in your current directory and then copies a variable
%1 into a BACKUPS folder. What is this variable? That's up to you to decide when you run the
batch file:
C:\TEMP>TEST3 TEMP1.BAT
TEST1.BAT => BACKUPS\TEST1.BAT
Your batch file has copied TEST1.BAT into a subdirectory called BACKUPS because you
identified that file as an argument when running your batch file. Your batch file substituted
TEST1.BAT for %1.
Variables are positional. The variable %1 is the first argument you provide to your command,
while %2 is the second, and so on. Suppose you create a batch file to list the contents of a
directory:
DIR %1
C:\TEMP>TEST4.BAT C:\HOME
If you try that, you get the listing of the first argument (C:\HOME) but not of the second (C:\
DOCS). This is because your batch file is only looking for one variable (%1), and besides, the
DIR command can take only one directory.
Also, you don't really need to specify a batch file's extension when you run it—unless you are
unlucky enough to pick a name for the batch file that matches one of the FreeDOS external
commands or something similar. When FreeDOS executes commands, it goes in the following
order:
1. Internal commands
2. External commands with the *.COM extension
3. External commands with the *.EXE extension
4. Batch files
Multiple arguments
OK, now rewrite the TEST4.BAT file to use a command that takes two arguments so that you
can see how this works. First, create a simple text file called FILE1.TXT using the EDIT
application. Put a sentence of some kind inside (e.g., "Hello world"), and save the file in your
TEMP working directory.
COPY %1 %2
DIR
Upon running your batch file, you see a directory listing of your TEMP directory. Among the
files listed, you have FILE1.TXT and FILE2.TXT, which were created by your batch file.
@ECHO OFF
ECHO Hello
CALL NBATCH2.BAT
ECHO world
The first line (@ECHO OFF) quietly tells the batch file to show only the output of the
commands (not the commands themselves) when you run it. You probably noticed in previous
examples that there was a lot of feedback about what the batch file was doing; in this case,
you're permitting your batch file to display only the results.
Create both of these files using EDIT, and save them in your TEMP subdirectory. Run
NBATCH1.BAT to see what happens:
C:\TEMP\>NBATCH1.BAT
Hello
from FreeDOS
world
Your second batch file was executed from within the first by the CALL command, which
provided the string "from FreeDOS" in the middle of your "Hello world" message.
The OpenWatcom conio.h and graph.h functions give you the flexibility to print text on different areas
of the screen, create windows, and apply text formatting.
Set text mode when your program starts, and reset it before your program exits.
Short _setvideomode(short mode);
_TEXTC80 80x25 color _TEXTBW80 80x25 black and white
_TEXTC40 40x25 color _TEXTBW40 40x25 black and white
_TEXTMONO 80x25 mono _DEFAULTMODE Mode before running your program
Text windows
Screen coordinates are row,col and start 1,1 in the upper-left corner:
void _FAR Define a text window from top,left to
_settextwindow(short top, short left, bottom,right
short bottom, short right);
void _FAR Clear screen or window
_clearscreen(short area);
_GCLEARSCREEN Clear the whole screen
_GWINDOW Clear the defined window
Printing text
Screen coordinates are row,col and start 1,1 in the upper-left corner:
struct rccoord _FAR Move the cursor to row,col
_settextposition(short row, short col);
short _FAR Set the text foreground color (0-15)
_settextcolor(short color);
long _FAR Set the text background color (0-7)
_setbkcolor(long color);
void _FAR Print text at cursor position in current color
_outtext(char _FAR *text);
0. Black 8. Bright Black 4. Red 12. Bright Red
1. Blue 9. Bright Blue 5. Magenta 13. Bright Magenta
2. Green 10. Bright Green 6. Brown 14. Yellow
3. Cyan 11. Bright Cyan 7. White 15. Bright White
Video
struct videoconfig * Probe the video capabilities
_getvideoconfig(struct videoconfig *cfg);
short humtextrows The number of text rows (such as 25)
short numtextcols The number of text columns (usually 80 or 40)
short numcolors The number of available colors
short mode The current video mode
short adapter The video adapter connected to the system
short monitor The display or monitor attached to the system
_settextposition(1, 1);
_outtext(text); }
Copy a directory and its contents XCOPY DIR NEWDIR cp -r dir newdir
Show all lines that contain "Hello" FIND "Hello" FILE.TXT grep "Hello" file.txt
Show the usage for a program DIR /? (for most programs) ls --help
Batch scripts
Reference normal batch script variables by enclosing the variable name with %,
such as %PATH%