FreeDOS on a Stick

how much eye candy can a post about putting dos on a usb stick require?There’s a lot of old hardware operating out there, and many a test/interface suite still exists in old-school DOS. Moving these antique applications into a modern computing environment with all the extensibility and reusability we’ve come to know and love requires being able to at least see the old application in operation, at most scope its inputs and outputs to see how it works.

One of my great mentors (not mentioning any names jpvb; you know who you are) often quipped that if copying an old architecture, of critical importance is replicating the original system’s bugs. Programmers often exploit architectural bugs to find more clever ways of accomplishing tasks; the more obscure and less documented the bug the better.

If seeing an app running in native DOS is required, how? 386 and 486 architectures are about as common as Dodo birds today, not to mention big and unwieldy. The open-source community has kept a version of DOS, FreeDOS, alive; it’s perfectly suited to this task. A FreeDOS environment can be built on a USB stick, from which a modern PC can boot, allowing access to an obsolete application.

I’m a command-line fan, as user interfaces change all the time, and it just feels more definitive to punch in a command and see the result confirmed. The down-side is, it requires more knowledge and is rarely intuitively obvious… maybe that’s an upside? It all depends on how robust you aspire to be. If you’re creating a bootable USB stick with DOS on it, it stands to reason you’re good with a command line interface.

I wanted this to be something easily reproduced, but the first few web-pages I read on FreeDOS led to several failed attempts to get it to work; it turned out to be complicated enough to warrant making notes for future reference:

To start, you’ll need to pick up four things:

  1. linux system rescue CD –
  2. syslinux –
  3. FreeDOS –
  4. USB stick, of course.

While a linux machine is not always available, any PC or laptop can boot the system rescue CD, assuming it has an optical drive. An easier misadventure can be had burning the system rescue ISO file onto a USB stick, same as we’re doing for FreeDOS here.

Beware the version of syslinux on the rescue CD. It’s not guaranteed to be the most recent version and may be out of sync with available documentation; be sure to get the latest one from the location listed above.

Download the linux system rescue CD ISO file, burn it onto a disc, and boot your machine from its optical drive. Usually several different options are presented, such as 64-bit kernel, 32-bit kernel, memory tester, etc. Choose whatever works best for you, but stay on the command line; when it says “type startx”, don’t.

It is assumed the stick is either blank, or you don’t care what’s on it, because it’s all going to be wiped. If you do care, back it up using your preferred method before continuing.

If the memory stick isn’t plugged in, plug it in now. Give it a few seconds to register and show up in the /dev directory, then type the following:

...$ parted <enter>

You should now be at the prompt:

(parted) _

If you get lost in parted or want a different setup, type this:

(parted) help <enter>

A list of commands and descriptions as to function should appear. Now’s the time to tinker if you’re curious, as there’s no downside and a big upside: knowledge.

Determine which device it is:

(parted) print all <enter>

You should have a list of devices the likes of /dev/sda1, /dev/sda2 and so on. The memory stick should show as something like “USB 2.0 Flash Drive [bla bla bla]” or similar and the size should match what you know it to be; you do know what size it is, right? So in my case I’m interested in /dev/sdc. To avoid modifying the wrong disc in the system, ensure the device is selected:

(parted) select /dev/sdc <enter>

There might already be stuff on it so /dev/sdc1 or /dev/sdc2 may have come up. These are partitions. Who cares, it’s /dev/sdc we want at this point.

FreeDOS expects a FAT16 partition, but FAT32 allows for bigger capacity with smaller sectors; why not use it? There is a downside, and that is that some FreeDOS commands may not work. For example CHKDSK does not support FAT32 partitions as they didn’t exist when DOS was king; don’t expect this to change. Anyway, as the saying goes… damn the torpedoes, full speed ahead:

(parted) mklabel msdos <enter>

This will create a new partition table on the disc, and you’ll be warned that the world is about to end, an event for which confirmation is required. It doesn’t hurt much, I swear; you will wipe the stick out completely though; so again, if you have pictures or anything important on it… you’ve been warned. The partition table holds information about each partition (seems sort of obvious), but we don’t have any yet, so the table’s empty. For the curious, all of this information resides in first 512 bytes of the disc, and the partition table lives at location 446. So, let’s put an entry in the table:

(parted) mkpart primary fat32 0 100% <enter>

You will likely get a warning about sector misalignment; ignore it; in fact, select ignore when asked. The alignment issue applies to pre-LBA discs; that’s not us, we don’t care.

Now you have a partition table inside the master boot record showing where it starts and ends, but there are several single-bit entries – flags – that can be set or cleared in the table. Since we want to boot from this partition, the boot flag must be set:

(parted) set 1 boot on <enter>
(parted) print <enter>

You should have a list with one entry showing the size of your device, with flags lba and boot. If the flags are listed, they’re set. Also note that the start location is shown as 512. This is because the master boot record occupies locations 0-511. OK, our table is complete, almost. You may notice in the table there’s a column heading called “File system” and there’s nothing written under it. That’s because although we’ve created a table and put an entry in it, the file-system in the partition does not yet exist; we haven’t made it yet. For that, we have to leave parted:

(parted) quit <enter>

…you should be back at the shell prompt. If a warning popped up about updating /etc/fstab, ignore that too. So here’s where we get the fat32 type of the file system sorted:

...$ mkfs.fat -D 0x80 -M 0xf8 -n byzantium /dev/sdX1 <enter>

Where byzantium is the disc label (put your own in: medieval, darkages, pleistocene, jurassic, whatever) and the X in /dev/sdX1 should be changed to the partition for which you’re making the file system. You will get complaining about the label if it’s more than 11 characters long or contains lower case characters. I’m ignoring these; my preference is lower case and I’ll fix it later if I need to. The -D 0x80 has to do with the BIOS drive number/name; it means it’s a hard-disc, and should have a drive letter like C. If we said 0x00 instead, it would indicate it’s a floppy disc and should be named A, or B. The  -M 0xf8 has to do with the media-type; in this case it’s a descriptor that lives in the boot sector. The number could be from 0xf9 to 0xff if the partition was to be used for floppy emulation.

Just to confirm, pop back into parted and verify that it worked:

...$ parted <enter>
(parted) select /dev/sdX <enter>
(parted) print <enter>

The table shown should now have  “fat32” listed under “File system”. If it doesn’t, go back and see if you can spot where you went wrong. Moving along:

(parted) quit <enter>

Next, we need to put some files on the drive. I assume you have a working network connection and can download a file. Don’t mount the USB drive yet. Instead, you’ll need another location to hold syslinux. In my case it’s /dev/sdb1, which happens to be a windows partition, assumed to not be mounted, since we booted from the rescue cd:

...$ mount /dev/sdb1 /mnt/windows <enter>
...$ cd /mnt/windows <enter>
...$ wget <enter>

where X.YZ is the current version of syslinux.

You should see lots of action on the screen, an advancing bunch of ‘=’ signs; after a bit all will be quiet and the prompt will return. Now unpack it:

...$ tar -xzf syslinux-X.YZ.tar.gz . <enter>

Lots more verbiage should come up indicating decompression and unpacking of syslinux. The -xzf option is really three options combined; it just means extract everything from the compressed archive file. Don’t forget the ‘.’ at the end, as you need to specify a destination for everything pouring out of the archive. After that finishes, go to the directory containing syslinux:

...$ cd syslinux-X.YZ/bios <enter>

According to the man page for syslinux, it needs to be run from the drive on which it’s installed. Since our interest is very specific, we only need part of the syslinux package. To get it on the drive, mount it and copy the files:

...$ mount /dev/sdX1 /mnt/custom <enter>
...$ mkdir /mnt/custom/syslinux <enter>
...$ cp -rv com32 /mnt/custom/syslinux <enter>
...$ cp -rv core /mnt/custom/syslinux <enter>
...$ cp -rv mbr /mnt/custom/syslinux <enter>
...$ cp -rv linux /mnt/custom/syslinux <enter>

…and install it:

...$ cd /mnt/custom/syslinux/linux <enter>
...$ ./syslinux -i /dev/sdX1 <enter>

where X is your drive, in my case it’s /dev/sdc1. Now we have a bootloader, but no way of running it. According to the documentation for syslinux, the installation run above should have modified the mbr, but experience has shown, several times, on two sticks, with infuriating obstinance, that that doesn’t work. This may be a result of me running the install from a partition other than the one I want syslinux installed on. So, with notice that this step may be unnecessary, let’s modify the boot record ourselves. The thing to do is copy the mbr.bin file from the syslinux/mbr directory to the master boot record on the stick.

...$ cd ../mbr <enter>
...$ dd conv=notrunc bs=440 count=1 if=mbr.bin of=/dev/sdX <enter>

Note that we don’t spec a partition, but the whole device: not sdX1, but sdX. Note also that we changed to the directory containing master boot records. If you’ve got lost somewhere, the full path to the directory is /mnt/custom/syslinux/mbr. If the cd ../mbr command didn’t work, try again specifying the full path. So what was all that? Here’s a short breakdown:

  • dd means something like disc duplicate, but its origin is unclear, lost to time
  • conv=notrunc means do not truncate the file you’re about to copy
  • bs=440 means block-size is 440 bytes, which is the size of the boot-sector file
  • count=1 means only transfer one sector.
  • if=… specifies the source for copying, input-file?
  • of=… specifies the destination for copying, output-file?

So where are we now? We’ve formatted our stick, put a fat32 partition on it, and partially automagically, partially with brute force, installed the syslinux loader onto the stick. We still don’t have anything to load, and FreeDOS requires an intermediate program, launched by the bootloader, to load it. This is also part of syslinux, but running syslinux -i … doesn’t put that on the stick either. It’s all good though, we’ve already copied the files required, and just need to reference them. The chainloader needs to load the kernel for FreeDOS. Of course, we haven’t put FreeDOS on yet, so there’s that.

The two critical directories are:


If the syslinux -i command already put ldlinux.sys in the root directory of the stick, you can delete the core directory too. I’ll leave it to you to remove it. You can do it in DOS with the DEL command if you want, or use the rm command here to poke around removing things you don’t want.

Next, FreeDOS. Download and extract the FreeDOS OS to somewhere convenient. This can be on the stick itself if it’s big enough; otherwise, put it somewhere else. If you haven’t already done it, go to and download the base package of the latest release. It’s available as an ISO for burning onto a disc, but I’m too lazy for that, so I’ll just extract the files I need out of the iso:

...$ cd /mnt/custom <enter>
...$ mkdir fdiso <enter>
...$ cd fdiso <enter>
...$ wget .<enter>
...$ 7z x fd11src.iso <enter>

Now we need to get several pieces out of the file:

...$ find . -iname \*.zip -exec {} \; <enter>

This should result in gobs of files being extracted, and possibly some questions towards the end about whether you want to overwrite stuff. Just type ‘A’ in response to each question, you’re not going to lose anything important as an end-user. Now we should have a bin directory that has all of the actual executable files. Weirdly there will also be a BIN directory. Copy those to the stick:

...$ mkdir /mnt/custom/fdos <enter>
...$ cp -rv ./bin /mnt/custom/fdos <enter>
...$ cp -rv ./BIN/* /mnt/custom/fdos/bin <enter>
...$ cp -rv ./util/* /mnt/custom/fdos/bin <enter>
...$ cp -rv ./UTIL/* /mnt/custom/fdos/bin <enter>

If you want documentation, and there is a very nice document viewer built into FreeDOS, grab help and documentation files:

...$ cp -rv ./doc /mnt/custom/fdos <enter>
...$ cp -rv ./DOC/* /mnt/custom/fdos/doc <enter>
...$ cp -rv ./help /mnt/custom/fdos/help <enter>
...$ cp -rv ./HELP/* /mnt/custom/fdos/help <enter>

Regardless of whether english is your preferred language, the help system will lock up your machine so badly even <ctrl>+<alt>+<delete> will not recover; unless (!) you include these extra files:

...$ cp -rv ./nls /mnt/custom/fdos/nls <enter>
...$ cp -rv ./NLS/* /mnt/custom/fdos/nls <enter>

…don’t ask how I know.

We need autoexec.bat and config.sys in the root directory on the stick too. There are two files called autoexec.txt and config.txt that are exactly what’s required, so copy and rename each of them in one step:

...$ cp ./autoexec.txt /mnt/custom/autoexec.bat <enter>
...$ cp ./config.txt /mnt/custom/config.sys <enter>

Note that I’ve renamed the files as I copied them.

Lastly, just for the uberkeen it’s nice to have a memory tester somewhere:

...$ mkdir memtest <enter>
...$ cd memtest <enter>
...$ wget <enter>
...$ 7z x memtest86+-A.BC.bin.gz <enter>

Now we need to put it on the drive:

...$ mkdir /mnt/custom/img <enter>
...$ cp memtest86+-A.BC.bin /mnt/custom/img <enter>

Almost there. Getting back to syslinux and setting up the stick, we now have all the files on it, and only a couple of things left to do. First, well OK last, we need a configuration file so syslinux can figure out what to do at start-up. The file must be called syslinux.cfg, must reside in the stick’s root directory currently mounted at /mnt/custom, and should look something like this:

# syslinux configuration file

default freedos

# prompt values: 0 - don't show boot: prompt, 1 - show boot: prompt.

prompt 1

# timeout values seem to be 10ths of a second:
timeout 100

# need a path to the chainloader and other various libraries used by syslinux. Note that if this is absent, full paths to each file must be specified under label. Also note path entries are separated by a `:'

path /syslinux/com32/chain:/syslinux/com32/lib:/syslinux/com32/libutil

say welcome to freedos
say type freedos to start dos
say type memtest to start the memtest

label freedos
    menu label freedos - boot FreeDOS v1.1
    com32 /syslinux/com32/chain/chain.c32
    append freedos=/fdos/bin/kernel.sys

label memtest
    menu label memtest - boot Memtest86+ A.BC
    linux /img/memtest+-A.BC.bin

Obviously the menu is oversimplified, but it comes up at boot. Make it as elaborate as you want. The ‘#’ symbol denotes a comment, and anything after the keyword ‘say’ will be displayed on the screen. Blank lines and more than one space are all ignored.

If you don’t want to copy/paste the above, see repository at github:

Once the above is finished, you’re essentially done. Test by rebooting your machine, or unmounting the memory stick and plugging it into another machine.


Leave a comment

Your email address will not be published. Required fields are marked *