Today, in partnership with IBM and in the spirit of open innovation, we’re releasing the source code to MS-DOS 4.00 under the MIT license. There’s a somewhat complex and fascinating history behind the 4.0 versions of DOS, as Microsoft partnered with IBM for portions of the code but also created a branch of DOS called Multitasking DOS that did not see a wide release.
↫ Scott Hanselman
Not only did they release the source code to MS-DOS 4.00, they also released disk images of a very early version of Multitasking DOS, which did not see a wide release, as the article states. I’ve only vaguely heard of MT-DOS over the decades, so I had to do some minor reading and research to untangle what, exactly, MT-DOS really is. Much of this information is probably table stakes for the many older readers we have, but bear with me.
MT-DOS, which has the official name MS-DOS 4.0 (often further specified by adding “multitasking” in brackets after the version number) was a version of MS-DOS developed by Microsoft based on MS-DOS 2.0, whose headlining feature was pre-emptive multitasking, which allowed specifically written applications to continue to run in a special background mode. Interestingly enough, it had to perform this multitasking with the same 640k memory limitation as other versions of DOS. Very few OEMs ended up licensing it, and most notably IBM wasn’t interested, so after one or two more OEM-specific versions, it was quickly abandoned by Microsoft.
MS-DOS 4.0 (multitasking) is entirely unrelated to the “real” versions 4 of MS-DOS that followed later. The actual version 4 was called MS-DOS 4.00, and it’s the source code to this specific version that’s being released as open source today. MS-DOS 4.00 was quickly followed by 4.01 and 4.01a, but apparently OEMs would confusingly still label 4.01 disks as “MS-DOS 4.0”. The whole MS-DOS 4 saga is quite convoluted and messy, and I’m probably oversimplifying a great deal.
Regardless, this code joins the open source releases of MS-DOS 1.25 and 2.0 that Microsoft released years ago.
How is MT-DOS different from the TSR functionality found in some DOS versions (not sure which, as my only exposure to DOS is DOS 6.22 and DOS 7 that’s found in Windows 9x) that allowed utilities such as DOSKEY to operate?
Having written TSRs…
They were an abomination.
Basically every utility had to write their own “kernel” and “multitasking”. You had to hook in to one or more interrupts to tigger, and have to manually save state of the current running program (sometimes unsuccessfully).
It became even worse when you had multiple TSRs conflicting with each other.
(Some more context. For example, you had to hook into the keyboard interrupt. Then watch for your activation key combination. If that was hit, you’d have to manually save the current program, save the registers, make sure it was not in a DOS call or similar, save the screen / VGA state, and hope to restore it correctly.
If you wanted to run in the “background” you need to hook into the timer interrupt, which triggered roughly 17 times per second. However of course that could be changed by other programs.
I could go on, but it should be obvious how broken that design was… yet it still worked somehow).
DOS TSRs were limited to 64KB in size, and would work by hooking into hardware or software interrupts to run code when an interrupt was generated. The TSR code would have to explicitly return control to DOS.
MT-DOS would preemptively multitask DOS programs that were designed for it, switching between the foreground task (Which had access to the screen and input devices) and background tasks, without waiting for a specific interrupt to occur. Since it was preemptive multitasking, programs wouldn’t have to explicitly return control to DOS or to the foreground program.
MT-DOS didn’t have protected memory, so broken programs could crash other programs by accident.
In order for a program to be a TSR, it has to specifically be written as a TSR, for example, when it’s loaded it has to register a “trigger” that will wake it up and then execute a special system call which releases the CPU while requesting its memory to not be freed. Also, when waken up, a TSR does whatever it has to do and then executes the same special system call mentioned above to relinquish control again.
Normal applications do none of that. Once loaded, they run assuming they have full control of the CPU and only release the CPU when they finish (without asking for their memory to not be freed obv). They never relinquish control before they finish.
The whole point of OS-level multitasking is to take programs that assume they have full control of the CPU and interrupt them (while fooling them into thinking they have not been interrupted).
BTW it’s worth mentioning that theoretically, every application can be a TSR, aka voluntarily releasing control to the next app in the process table after it has finished doing its task and is waiting for user input (also, the code to achieve this can be injected by the compiler, it doesn’t have to be written manually). This is known as “cooperative multitasking” and it sucks because there is no guarantee the application will in-fact release control (it may get stuck in an infinite loop or whatever). Windows 1.0 used “cooperative multitasking”, and so did early versions of MacOS.
One “correction”
TSR and regular programs had a difference. It is literally in the name.
When terminating regular programs you call INT 20h, or INT 21h, service = 0
When terminating and staying resident you call INT 21h, service = 49 with size of resident portion (up to 65535 x 16 bytes). It also did not close open file handles.
That’s what I meant by “special system call which releases the CPU while requesting its memory to not be freed”.
It’s not the regular system call for exiting.
I see, I missed that comment above.
Windows up to and including Windows 3.x were all cooperatively multitasked, with a couple of caveats.
First, Windows/386 (i.e. Windows 2.1, but on a 386) ran a slightly modified version of Windows 2.1 under a fully 32-bit preemptively multitasked virtual machine monitor, along side DOS virtual machines. The Windows VM and the DOS VMs were preemptively multitasked, but since all Windows programs ran inside of the single Windows VM, they were cooperatively multitasked within the VM. This is also true for Windows 3.x when running in Enhanced mode on a 386.
Second, many Windows libraries would release control to the next process, so even if an application was misbehaving and not sharing if they called any Windows library functions the library would surrender control to the next process.
MacOS was cooperatively multitasked all the way up until OSX, and I imagine it was done much the same way (Though, the earliest versions of MacOS, before it was even branded MacOS, could only run a single program at a time, apart from the tiny applets such as calculator or whatever else the first versions came with)
Yeah, things got complicated after Windows 1.0 and before Windows 95, since what type of multitasking you’d get depended on which version of Windows you had and what CPU you had. For example, Windows 2.0, 2.1, and 3.0 can run on an 8086, and since the 8086 has no support for memory protection whatsoever, Windows run as a cooperatively-multitasked OS in real mode. Curiously though, Windows 3.1 doesn’t support real mode like 3.0 does and refuses to run on the 8086. Another curiosity is that Windows 3.0 and 3.1 support pre-emptive multitasking in 16-bit protected mode (as found in the 286) but Windows 2.0 and 2.1 don’t despite being more period-correct to the 286, When 2.0 and 2.1 encounter a 286, they use real mode and cooperative multitasking. Anyway, Windows 95 eventually came and used 32-bit protected mode and pre-emptive multitasking exclusively. I intentionally left all that from my initial comment to avoid an overlong post.
MacOS (pre-OS X) is a weird case, it had a pre-emptively multitasked nanokernel to run its own services but all MacOS applications are run in a single process called the “blue task” and they cooperatively-multitask with each other:
https://web.archive.org/web/20071013225430/http://lists.apple.com/archives/Mt-smp/2001/May/msg00007.html
So it’s neither fully pre-emptive nor fully cooperative.
BTW the problem with cooperative multitasking is that the application may be stuck in an infinite loop while doing business logic calculations before it reaches any library code that will relinquish control.
BTW minor correction: The above applies to classic MacOS 8.6 and above as mentioned in the link. Versions of MacOS before 8.6 were cooperative-multitasked only.
I did not know this about pre-OSX MacOS. I always thought it was fully cooperative.