| welcome

The History of Multitasking MS-DOS (Not quite finished yet, will be done ASAP!)

Today (April 25th, 2024), the source code to Microsoft MS-DOS 4.0 as well as part of the source code and documentation to a very early build of a very little-known and very limited release Microsoft operating system known as MT-DOS (Multitasking MS-DOS, also called MT-MSDOS, Multitasking DOS 4, and...MS-DOS 4.0. It's confusing, but you'll see why the distinction matters very soon) was released officially by Microsoft. I worked with Microsoft (mostly via Scott Hanselman) to have this released. I haven't perused the MS-DOS 4.0 code yet, but I have done quite a bit on the Multitasking MS-DOS stuff, so this post is on that. In a while, I'll write a follow-up blog post on the regular DOS 4 release.

MS-DOS was the primary and widely tolerated operating system for several decades of personal computing, from the release of the IBM PC in 1981 to the late 1990s, when Microsoft Windows and, to some extent, Linux, finally killed it. However, the original plan was for it to be effectively replaced with a "next-generation DOS" as early as 1985; this product actually, entirely in real mode, completely surpassed the functionality of both regular DOS and also the original versions of Windows, but was effectively cancelled as a standalone product before being turned into OS/2. What is this product? How did it work? And if it was so much better, why was it cancelled? I will answer all of this in this article on Multitasking MS-DOS, MT-DOS, or MS-DOS 4.0 (it went by many names), the failed next-generation DOS successor from the mid 1980s. This article will use insights gleaned from never before seen archived pre-release builds and original Microsoft source code, released with the official permission of the company, to explain the life and quick death of what was, at one point, the operating system Microsoft envisioned every IBM PC running - before even Windows 1.0, let alone Windows 95 or NT, and just how impressive its feature set was for the time it was written.

This is part 1 of a multi-part series. Part 2 will talk about the 1985 and 1986 builds (and what happened to Multitasking MS-DOS 4) and Part 3 will dive in more detail about the documents released with this build and how Microsoft designed the operating system. After that, there will be a source code analysis of regular MS-DOS 4.0!

The Beginning

Microsoft was founded in 1975 by Bill Gates and Paul Allen to develop a BASIC interpreter for the MITS Altair 8800, one of the first personal computers available. The initial version product was developed in thirty days, and was successfully demonstrated to the founder of MITS. I won't go into detail on what happened next, as the story of what happened immediately after has been told, mistold, and distorted a frankly insane number of times. Essentially, within a few years Microsoft was by far the most popular developer of BASIC interpreters, and had a large variety of other products - it was a moderately successful tools and languages company, although they also sold various "SoftCards" to allow computers to run programs intended for other architectures, as the PC industry was far from standardised during this time. While Microsoft's revenue hit several million dollars by 1980, this was nothing compared to what was to come - the company's real "big break" was in 1980, when through a heavily contested series of events that appears to involve nepotism and an unwillingness to sign IBM's incredibly strict and secretive contracts, Microsoft, which had rejected the contract the first time it was offered to them, managed to land the operating system contract for the IBM Personal Computer. It turned out that this was an extreme stroke of luck that, when combined with a few savvy business insights, eventually transformed Microsoft into one of the world's largest companies and Bill Gates into, for many years, the world's richest persion. His insistence on licensing MS-DOS - which Microsoft paid $75,000 for in total, $25,000 to license and then $50,000 to buy outright, to anyone who wanted it, combined with the IBM PC's open architecture leading to it being almost immediately cloned and becoming the de facto standard for PCs. This led to Microsoft basically having an infinite money printer from OEM revenues, although this would not be evident for a few years.

The Problem

The product that they had acquired, MS-DOS (neƩ 86-DOS) was a mess, and obsolete even for 1982, when it was realised that it would be becoming the standard operating system for at least some personal computers. It had been thrown together in a little under a year, beginning in early 1980, by Tim Paterson to fill a pressing need: they were creating a new product that used the new Intel 8086 16-bit processor, but it simply did not have any operating systems available for it. So, he wrote an operating system designed to allow apps to be easily ported from CP/M (Control Program/Monitor, later Control Program for Microcomputers). The process of porting an app from CP/M to this new OS would simply involve converting 8080 or Z80 assembly to 8086 (using another tool Tim Paterson wrote) and hand-optimising it. This new OS, initially called QDOS (allegedly, as all known versions, even the recently discovered 0.11, call it 86-DOS) and then 86-DOS, was licensed by Microsoft; he was then asked to make numerous changes to the OS (one example is changing the A: prompt to A>). Then, Microsoft bought the OS later in 1981, with Tim Paterson joining the company to work on the OS full time - apparently to be immediately informed he was working for IBM. The OS was then rebranded again: it was renamed to PC-DOS if running on an IBM system, or MS-DOS (since Microsoft are the legal owners) if not running on an IBM system (these, and some other changes, were controlled by an ifdef - MSVER TRUE for Microsoft, IBMVER TRUE for IBM, although some OEMs compiled some parts of the system , as usually the OEM Adaptation Kit provided by Microsoft only contained certain parts of the source code, with different options to others), was first released in 1980, and then released with the IBM PC at version 1.0 (internally version ~1.14) in 1981. An update with support for double-sided (320KB at the time) disks followed in 1982, version 1.1 (MS-DOS 1.25).

While MS-DOS was making money hand over fist, the problem was that it sucked, even for the standards of the time. Due to the fact that it was rushed to meet a pressing need - QDOS quite literally stands for "Quick and Dirty Operating System" - the OS was missing a lot of basic functionality even by the limited standards of the time. Among the many things missing from MS-DOS 1.x are subdirectories, file handles (CP/M-style FCBs being used instead), device drivers, later versions of CP/M-86's odd sorta-multiuser "user areas" concept, environment variables, basic control flow in batch files, or even hard disk support. 1.0 does not even keep track of time. This is why MS-DOS 2.x is almost always required by any DOS app - just because 1.x doesn't do very much. The only editor provided was the infamously rudimentary EDLIN. In fact, 2.0, released in 1983 with the PC/XT, was a complete rewrite of the OS to add almost all of the aforementioned things (except the CP/M-86 styled "user areas", and also allowing 160 and 320KB floppies to become 180 and 360K with). The update was generally well received due to the addition of badly needed functionality, although some criticism was levied at its increased memory use. The kernel, IBMDOS.COM and driver blob, IBMBIO.COM, must always be resident in memory, the memory use running any DOS app increased by about 12KB directly upgrading from 1.1 to 2.0. At the time 2.0 was released, the initial batch of IBM PCs didn't even support the full 640KB, the architecture was not even meant to last that long, and most people didn't even have 640KB in 1982 - most people were in the 64-256KB range, so this mattered, although it mattered less as technology advanced over time.

However, users still demanded more functionality. They wanted to do more than one thing at a time on their new, then-powerful computers - and therefore needed an operating system to do it, as MS-DOS, a single tasking operating system, simply could not manage it. Additionally, Microsoft themselves had promised, since the release of MS-DOS 1.x, future extensions to MS-DOS - multi-user, networking, multitasking, you name it, they promised it. Microsoft did have another multitasking-capable operating system, XENIX, a licensed derivative of AT&T UNIX System III, it could not run any of the rapidly multiplying number MS-DOS apps and was not easily available for the IBM PC architecture. It also required far too much memory for the systems people were using. It was also far too slow ("asynchronous" being an apparently unknown word to its programmers), and never really sold before being abandoned and effectively sold off to SCO, although it did run all of Microsoft's compilers and was well-used internally at Microsoft until the late 1980s.

Another problem is that the original 8086/8088 CPUs are far from the most suitable processor architecture for multitasking - it doesn't have any memory management or protection hardware, therefore processes can simply overwrite each other. Intel had recently finished the Intel 80286, a deeply flawed but nonetheless plausible architecture for multitasking with built in memory protection features to enforce address space isolation. However, it was not going to be available in any computer for several years, and for many years beyond that the installed base of millions of 8086 machines would remain. This predicament - users wanting to do more, easier, and faster, with their computers - a more, easier, and faster that Microsoft had promised no less, and hardware fundamentally unfit to deliver those advances, led to Microsoft assembling some of its best programmers in January 1983 to develop a new version of MS-DOS. This product, initially called MS-DOS 3.0 (with MS-DOS 2.5 being developed at the same time from the same codebase as an interim release in order to add basic network redirector support for Microsoft and IBM's networking products), would be a truly multitasking operating system, with a pre-emptive priority scheduler (which Windows didn't manage until Windows NT 3.1 in 1993), multithreading, named pipes, sempahores, a session management user interface, and numerous other features that will be explained later, as well as making future compatibility with a 286 DOS as painless as possible. It was also to eventually subsume and become the core of another Microsoft product, one far more famous today, that was starting off at the same time - Windows.

The Solution?

Microsoft had actually planned this product since 1981 - initially called "XEDOS", as an "mid-range" OS between DOS and XENIX. The idea was that MS-DOS, as it existed, was eventually supposed to be enhanced into a single-user variant of XENIX with all of the creature comforts (except multiuser support, presumably for market segmentation reasons) of a real UNIX, such as multitasking and networking. Remnants of this philosophy can be seen in the available versions of Multitasking MS-DOS 4. It turns out writing a multitasking operating system, even on the no-protection 8086, is easy. However, writing one compatible with thousands of existing applications written for a single-tasking environment, many of which ignored the MS-DOS API, which while much improved from the days of DOS 1.x still had critical holes in functionality such as graphics, is a whole other matter. Many MS-DOS apps, due to these API flaws, do actions such as such as directly writing to display memory - which cannot be done on a multitasking OS, as the user picks which application is being displayed and the "screen" can change at any time. Since the new operating system is to be pre-emptively multitasked, any app can be pre-empted by the operating system in a context switch at any time; many DOS apps do not play very well with this. Apps would also directly write to and read from peripheral devices, which can be fatal in a multitasking environment if the OS is expecting data from those devices. Nevertheless, there were still some minor things in their favour. Since they had planned for it, some aspects of DOS 2's design (such as how child processes spawned) were actually intended for a future mutltitasking DOS.

MS-DOS 3.0 - probably due to these complications among others - was very quickly delayed so long that it was renamed to MS-DOS 4.0 in April 1984, with 2.5 renamed to 3.0 and released with the PC-AT (the network redirector was left undocumented, and finished in version 3.1 in early 1985.). The driver source code included with the released beta build calls it "3.0" in most places!

Despite this inauspicious start, on June 5th, 1984, several trusted OEMs and Microsoft partners received a package of disks in the mail, containing a "Multitasking MS-DOS Beta Release Version 1.00" (rather comedically misspelt on one of the documents as "Multi-Taking MS-DOS" - the first OS that drains your bank account?), compiled on May 29th, 1984. The product was kept under extreme secrecy - the disks were labelled as "extremely confidential", and each copy was individually serialised to track its distribution and prevent leaks, with only partners that had executed non-disclosure agreements with Microsoft and were especially trusted receiving the build:

Extremely confidential! Are you scared yet?

The May 29th, 1984 build of Multitasking MS-DOS 4.0, running the shell and nothing else.

Session Manager

This early build of the operating system, produced after approximately eighteen months of development, shows a good amount of functionality already implemented - multitasking is present and correct and the session manager (SM.EXE), which provides a user interface and a way to actually multitask applications is already working. The way that multitasking actually works in this build of MS-DOS 4.0 is quite strange, however. It's a very interesting, strange and early idea of how a multitasking operating system should work. Multitasking is implmented via a new system component, the Session Manager (SM.EXE). When SM.EXE runs, it reads a SM.INI file containing a list of keys and a list of applications that are then bound to those keys, as well as any arguments the user may wish to run them with. A maximum of six applications can be bound at any one time. When you run SM, you can enter into command mode by pressing Escape or press one of the hotkeys that you have assgined, which will then switch to the defined application:

The early Session Manager, as well as an example of the SM.INI syntax.

In what appears to be a bug, it will ONLY check the directory that you ran SM from, and appears to ignore the PATH environment variable. This makes it easy to accidentally launch SM with zero apps available to run; since defining apps inside of SM would write a duplicate SM.INI file in a different folder (which would make running SM from a different directory provide you with different apps), the only way to get out of this is to exit SM. If it is the only instance of SM running, this may effectively crash the operating system. This is because of the fact that SM (inconsistently!) kills the shell that you launched it from; if you then have no applications to run and then exit SM via the command mode (more information below), the OS then has no processes to run and simply crashes! This is due to the fact that in Multitasking MS-DOS 4, as in most operating systems, by default killing a process kills every process launched by that process (even if the process launching the process in question is a shell! by default). This is due to the concept of a "Command Subgroup ID" (CSID). A CSID is related to the operating system concept of a Process ID (which also exists here), but is not quite the same thing; a CSID identifies the "command chain" that launched it. Any child processes spawned by a process, have different PIDs, but the same CSID. In COMMAND.COM, this can be solved by using the detach command (which allocates a new CSID), but this does cause problems with some apps - the detach Therefore, killing SM also kills its children, and since SM and its children are sometimes the only processes running and there is not yet any way to relaunch SM outside of a shell, the OS has nothing to do and will then hang.

The Session Manager command prompt and its help.

Pressing Escape will place SM into "command mode". This is a limited command prompt that allows you to do some administrative functions, such as killing processes, running processes (despite you already being at a screen to do that), restarting apps, defining new program hotkeys and exiting SM (which kills all the apps you run with it due to the aforementioned "CSID" functionality). You can also type HELP, which will unsurprisingly provide help. It generally works quite well. Running an app and then switching back to SM.EXE will lead to its status changing from "New" to "Active" (confusingly, an app marked "New" has not actually been started):

Note the "Active" app

When the app exits, its status is changed to Dead. Note that there is another bug here - while COMMAND.COM had had an EXIT command implemented in order to allow exiting from various shells, it doesn't seem to actually work: the app just freezes when you run EXIT. However, using the SM command prompt to correctly send a signal to the process will actually kill it, and the "Dead" status can be seen:

Now it's dead, Jim!

SM has one other set of functions as well (this will come up again in the tech section of this post): it also traps and handles "hard errors", which in regular MS-DOS are called "critical errors" and are errors that occur at the OS level, such as attempting to access an invalid drive or a bad FAT filesystem. On regular MS-DOS, when a critical error occurs in an MS-DOS API, software interrupt 24h is triggered; MS-DOS installs its own handler by default while booting. The code that runs here by default prints and interprets the user's response to the famous "Abort, Retry, Fail?" error message (versions of MS-DOS prior to 3.0 instead printed Abort, Retry, Ignore?), but device drivers loaded via CONFIG.SYS can replace this handler on their own with their own code. This was usually done to preserve the display of an app so that MS-DOS cannot screw it up by printing "Abort, Retry, Fail?" if an error does occur - if you do not do this (as some early builds of Windows 1.0 don't do for disk errors), the program or driver may crash!

However, on Multitasking DOS 4, apps do not receive int 24h at all. The OS installs all critical error handler (which have been renamed to "hard errors"), and apps are never able to set their own critical error handlers. In this early build, no hard error handler is installed during boot at all. If a hard error occurs, instead of the error being handled, an "Internal Error" occurs (an internal kernel error that leads to the OS crashing, similar to a BSOD - there are also "User Errors" that are caused by user apps failing to interface correctly with the kernel) and the OS crashes:

The OS crashing when a hard error occurs due to the fact that no hard error handler was ever actually installed.

SM.EXE installs its own Hard Error handler that displays full-screen error information, as well as the famous "Abort, Retry, Fail?" text (although here it's instead phrased as "Abort, Retry, Ignore, or Help"), the process ID that caused the error and lets the user pick what to do. One neat addition, which no other version of DOS has within the actual error message itself as far as I know, is an explanation of what option actually does, which can be reached by pressing "h" ("?" in later builds) at the error prompt:

Left: The Hard Error handler catching an error. Right: The help command. Note that ignoring the error (of a missing disk!) is merely "not recommended". Ah, the 80s...!

There is actually another Hard Error handler inside of the operating system, HE_DAEM.EXE (Hard Error Daemon - a Unixism, one of many in this OS), but it seems to be bugged in this build. In later builds, it's the only hard error handler and works as intended. The documentation provided with the release states that it is not compatible with SM, but it seems to be also incompatible with itself. Since it's the only one present in later builds and the hard error handler integrated with SM has been removed, it's possible that either Microsoft intended to SM to be optional as it is in this build, or simply that they were rearchitecting the code and SM on its own does not work yet.

HE_DAEM knows what its own name is. But not much else...

As with any beta operating system, there are many things in the code that are not enabled by default or broken, things that point towards more functionality planned for the operating system that isn't yet implemented in this build; this will be explored in the next section.

The Utilities

As with any operating system, this build of Multitasking MS-DOS 4 comes with numerous utilities. Most of these appear to be debug / test utilities, and they even appear to be compiled with a very early build of Microsoft C 3.0 (or as it's called in the October 1984 documents, Microsoft "CMERGE" C), the first C compiler actually written by Microsoft; earlier versions were rebranded variants of Lattice's IBM PC C compiler (which was developed independently until the early 2000s, interestingly enough). This is evidenced by many of the apps having the following strings in their data segments (shared with ATTRIB.EXE in regular MS-DOS 3.1):

Zbikowski C startup (C) Microsoft 1983
***** Stack overflow *****

Marc Zbikowski, incidentally, is the person responsible for creating the original DOS .EXE relocatable binary format, and due to the DOS stub provided by all Windows linkers to this day, his initials are in every single MS-DOS, OS/2, and Windows binary (as long as it was not a non-relocatable .COM file, which is just a binary image that cannot use segments) made since 1981! (Some early apps used 'ZM' due to flaws in early compilers, and all real DOSes, including MT-DOS, check for both variants of the string)

To start with, this build of Multitasking DOS 4 is missing many utilities that, by this time, were (either if made by IBM, Microsoft or by the OEM themselves) provided with MS-DOS. Not even the most basic DOS utilities are provided - there is no FDISK.EXE, for example (included since DOS 2.0 and 86-DOS respectively), and not even FORMAT.COM or DISKCOPY.COM to read or write from disks, although for some reason CHKDSK.EXE is provided (and crashes the OS). You can do these tasks by using the DOS 3.3 versions of these utilities, but you might have to patch their version number checks as they usually have strict checks preventing them running on any DOS version that is not the one they are intended for. Additionally, COMMAND.COM does not have very many internal commands (even including commands only meant for use in batch files) at all, and so by default what the OS can do, as compared to a "standard" single-tasking DOS install, is not particularly impressive:


As you can see, it's not much - there isn't even really IO, and while there is supposed to be file redirection (using the > / >> keywords to override or to append to a file, I haven't got it to work consistently - it's even hard-crashed the OS when I've tried it, but it could just be a skill issue on my part. The only interesting command that I can see is EXIT (which exits...) which isn't required in a single-tasking OS. Even the DETACH command (implemented as an internal shell command in later builds) is an external utility (DETACH.COM here). However, this lack of feature set is slightly deceiving.

The COMMAND.COM (the DOS shell, which can now be multitasked) present here appears to be based on the MS-DOS 2.1 (probably 2.11) COMMAND.COM. This tracks with the documentation provided with this beta release, which states that they had not yet rebased the code of Multitasking MS-DOS 4.0 on MS-DOS 3.0, and it was still based on the MS-DOS 2.x codebase. This makes sense, as MS-DOS 3.0 was released in August 1984, and therefore most likely unfinished, its codebase was a moving target and could not be merged until it was finished. Later builds of the OS are based on MS-DOS 3.2 (most likely a beta build, as the November 1985 build pre-dates MS-DOS 3.2's release date in early 1986), so they were merging code with the single-tasking DOS right through the development process. An interesting quirk about this shell is that the cd.. command, recognised by most real DOS and DOS clone shells, as well as the modern Windows Command Prompt, but not Unix-like shells, doesn't work (it's recognised but it doesn't change directory) - you have to use cd .. instead. Another Unixism! This is very much unlike the later available builds (from 1985 to 1986) of Multitasking MS-DOS 4.0, when COMMAND.COM were completely rewritten in C. Using BinDiff and reverse engineering shows that this application became the OS/2 CMD.EXE, which was then ported to Windows NT...and then became the Windows Command Prompt you know and love(?) today; this topic will be discussed in further detail in Part 2 of this series.

The utilities that are not included with a standard DOS are additionally very interesting, and shine a light on the unique and clever ways they managed to make multitasking work under the extreme limitations of an early IBM PC (many of these techniques were shared by Windows 1.0!). An interesting part of these utilities is that they all use - often exclusively - Unix-style command line switches (-, as opposed to the MS-DOS, Windows, and even some parts of later MT-DOS builds, which use /). This is another Unixism, possibly tying into the "XE-DOS" concept discussed earlier.

Left: The utilities (except the previously discussed SM) provided with this build.

Hidden Stuff: Debug Logging

The kernel can be patched to enable debug logging (internally called "BUGBITS"). There are two bytes, both bitflags, that control debug logging information; the first byte is a general "area" of the operating system (internally called the "group bits"), and the second byte (internally called the "level bits") is a more specific debug level, locally applied to that group. The first byte supersedes the second byee and is checked first; if any bugbits are set at all, and a serial connection (9600 baud, 8 data bits, 1 stop bit, no parity bit) is opened to the system running the build, debug information will be dumped to the console. If you enable every bugbit by patching the kernel, a very large amount of debugging information - including extensive information about the scheduler, every single INT 21h call made into the kernel, file accesses, device driver calls, and much more are all dumped out to the console. This SEVERELY slows down the operating system, and worse, the code is not portable between different PC-compatible architectures (due to directly writing to serial ports using the x86 IN and OUT instructions). While this is fine when dprintf is linked into the DOS BIOS (IBMBIO.COM), it's also linked into the kernel, therefore making the point of IBMBIO.COM (or IO.SYS in MSVER-compiled implementations), that is to allow OEMs to make DOS work on their PC by abstracting the hardware from the kernel - completely pointless!. Formatting is also a problem - some debug prints also don't have newlines, so have fun parsing it when it spits it out. The source code to the dprintf routine, and the debug macro used to call it, which provides the string as well as the BUGBITS group and level bitflags required to actually print it, are included with the IBMBIO.COM source code included with the build release. This code is actually gated behind a "DEBUGFLG" ifdef, meaning that this build was compiled as a debug build! While all known later builds of Multitasking DOS 4.0 are release builds (they have "BUGBITS") strings but do not include any debug prints or the dprintf code), the functionality is supplemented with a kernel debugging device driver (DEBUGDD.SYS, also called System Debug 070185), based on an enhanced version of the classic DEBUG.COM with breakpoint support added. It works well enough for what it does - further exploration of this utility will be in Part 2.

An example of some of the (very messy!) debug output produced by this build.

A final interesting note about this functionality is that the dprintf function uses C printf-like syntax, but with one peculiar difference: the format specifier character is $ instead of the standard %! Additionally, according to the function documentation provided with the source code, little error checking is done (due to being a debug only function), and only the $x (hex string), $d (decimal), $c (ascii character) and $b (hexadecimal byte, which is different to the usual printf %b, which indicates a boolean value) format specifiers are supported. It's not really of any significance, I just thought it was interesting...

An example of some of the strings spewed out by dprintf.

todo: get locations!

The Tech

Next Part

Watch out for the next part of this series as we dive into the later builds of Multitasking MS-DOS 4, and discuss what it ultimately became. Then, in Part 3, we will be detailing the surprisingly advanced (and simultaneously ancient) design of the OS, and discuss if it could have actually succeeded in the marketplace if it had come out for a wide release.

Special Thanks

Special thanks to Scott Hanselman, Ray Ozzie, Jeff Wilcox and others for getting immense amounts of historically important information legally approved for release by Microsoft.
Thanks to Larry Osterman (who worked on Multitasking DOS 4 and 4.1) for answering many of my (probably annoying) questions.