Jwzumwalt already covered the basic, being not much younger I might add a few minor details still present to me.
While it didn't particularly work well for games (as far as I remember), it was popular for system operators of bulletin board systems.
It was perfect - well, let me rephrase this as rather useful - for well behaving (read no direct hardware access) programs to have thwo programs at hand. Like a BBS operating in the background while being able to do maintenance tasks at the same time.
How did DoubleDOS work? I know that I had used it on 8086 machines back in the day which I don't believe had many of the real context switching capabilities that modern CPUs use to support multitasking.
In general the process is the same on each and every CPU ever developed. To switch all CPU-states visible to a user task need to be exchanged. Some CPUs may offer special modes for special cases (like the second register set in the Z80) or instructions to store the whole state (or most of it) in a single operation. Still, it has to be done.
Also with a primary single taking BIOS/OS combination, several memory regions need to be swaped - just think of things like cursor position.
Same with screen memory. if there's only one screen adaprot, the content needs to be exchanged. Otherwise one would not be able to see the output of the bachground task when switched up.
Was it simply hooking up to an interrupt and storing session 1's CPU registers, context switching, restoring session 2's registers, running and then running until the next interrupt?
Exactly like that. The basic task switch was done via a timer interrupt. To make it work som additional precaution had to be taken. For example monitoring entry to certain BIOS and DOS interrupts. As well as handling BIOS for I/O related funcions. This is neccersarry since no (at least not many) DOS/BIOS functions are designed for reentry (i.e. two tasks running thru the same code). So DoubleDOS had to keep the tasks from doing so.
For example, if the timer interrupt (08h) fires while the active task issues a DOS function, it will be ignored. Prior to DOS 2.0 DoubleDOS did this by hooking Int 21h and seting a marker of their own, while with DOS 2.0 the InDOS Flag got introduced to sign when it's safe to (re)enter DOS (*1).
Was memory space adjusted accordingly
DoubleDOS was realy build around the Idea of spliting a machinen into two by partitioning memory. It wasn't realy a taking system with dynamic memory assignment. At startup (or if no programm was running in either task) available memory was to be spilt um in two regions and kept that way. Beside being the prime idea, it also avoides hassles wit programs manipulating the memory they assume to be entiteled to.
or did programs which didn't go simply through DOS and the BIOS fail to operate?
There are zillion ways to screw operations, and computers realy enable them :)) It depends on the way of switching and what kind of 'bypassing' DOS/BIOS had been used. The most important issue here is usually the screen content and keyboard. Programms accessing both directly did only run well in foreground. In backgound they may at best just screw up the front tasks screen and steal input.
In contrast 'well behaving' programs, using only DOS and BIOS could even continue to produce output in background. Same for Printers and serial lines (*2). In fact, I remember that somhow up to 6 'tasks' could run at the same time. Two real tasks, while the other beeing specialized like print spoolers.
In relity, many programms 'less than behaving' programs fail deu direct sreen access. At the same time the majority do wait for user interaction at some point and don't issue any scren update when switching. So puting them in background won't do damage beside wasting CPU time for an idling application.
Then again DoubleDOS did offer a ways to get around that to a certain degree by seting the background execution to halt. So when foreground and background switch, everything gets saved and the program stops operation (*3).
Addendum 1: Time slice handling
It might be interesting to add the ways of handling time attributin to each task.
- 2:1 Foreground/Background
- 1:2 Foreground/Background
- 1:1 (equal time)
- 2:1 Upper/Lower Programm
- 1:2 Upper/Lower Programm
- Full time for Foreground
Addendum 2: Multitasking (and TSR) hooks
With DOS 2 a lot of hooks where designed into DOS to enable multitasking - much the same way as the AT BIOS introduced similar measures. From there on it was rather straight foreward to build a tasking layer above DOS/BIOS. And Winows is maybe not the least example to do so.
On BIOS side this includes several functions to allow an OS independand protocoll to detect and handle critical sections and process task swap. With the SysReq-Key even an OS independant user interaction was designed.
DOS added not only InDOS via Int 21h Function 34h (?), but also INT 28 to give a switching point/activity entry during idle. Beside InDOS function 34h also introduced an error flag which could be used to make the active DOS function fail in a (somewhat) orderly fashion. This again could be used in conjuction with the Error Handler at Int 24h(*4). To speed up the process. Int 23H also added the
CTRL-BREAK handling as an entry point for taskswitchers/OSes due user request.
And then there was Int 2Fh, the MUX-Services. A collection of hooks mainly added by TSR utilities like
PRINT, but also various versions of DOS (and various DOSes). There where functions from
PRINT over network and CD handling to a countless number of services provided by Windows, VM-Ware and alike.
COMMAND.COM's comand line handling being not the least :))
*1 - There are some function, especial the Character I/O which are usually safe to be used while InDOS is set.
*2 - Ofc. only if both tasks are not trying to access the same device on a character base - that is single byte (or line) output via DOS/BIOS. As soon a utility like PRINT was used even sharing a printer on job base was possible.
*3 - Not realy desirable for a BBS, isn't it :))
*4 - Right, that's the one where the infamous Abort/Retry/Ignore/FAIL is displayed.