DOS
From ReactOS
DOS was the original OS for the IBM PC. Many versions of it have been written over the years, all more or less compatible with each other. Examples include PC-DOS, DR-DOS and MS-DOS.
Contents |
How It Works (or Should)
Everything starts when CreateProcess is called upon to start a DOS executable. Somewhere inside CreateProcess, it is recognized that the executable is not a PE image, but instead a DOS one, so NTVDM.EXE (a regular PE executable) is used instead. (See "Inside Windows NT" book for more details)
If there is not already an NTVDM instance running (or if "separate virtual machine" is enabled), NTVDM is loaded. Otherwise, a message is sent to the current VDM. In either case, a new thread is started. If running on an x86 CPU, this thread should execute in VM86 mode - perhaps NtSetContextThread with the VM bit in Context.EFlags set? Before starting the thread, we need to map some things into the VDM's virtual address space, like the BIOS and CMOS data areas (some of this could be emulated - there seem to be .rom files containing this sort of thing in System32 on Windows systems). The LDT probably needs to be modified at this point as well. On x86 machines, there seems to be a one-to-one mapping from the VM's virtual address space to physical address space in the low megabyte. (An indication of this: "On a non-x86 machine, the VDM might not be based at physical address zero." This would seem to imply that the VDM is indeed at physical address zero on x86 machines. Reference.)
In order to implement the DOS "API" (int 21, etc.) we need a way to call Win32 and kernel procedures from the VM thread. This is accomplished in Microsoft's NTVDM by writing a stub 16-bit kernel (ntdos.sys, ntio.sys, ...) that hooks the same interrupts as a 16-bit DOS kernel would, but instead of actually handling these, it executes a special undefined opcode (C4 C4 followed by some more bytes indicating the desired operation) in order to trap back into kernel mode/protected mode (meaning execution will pass through KiTrap6, "INT 06: Invalid Opcode Code (#UD)"). The request is dispatched to the proper VDM and handled in protected mode, and then an iret takes execution back to the instruction after the invalid opcode sequence.
Devices are emulated by means of the Virtual Device Driver (VDD) interface, which again uses the special C4 C4 .. undefined opcode. VDDs are implemented as normal Win32/PE DLLs. The undefined opcode sequence has 3 forms: RegisterModule (C4 C4 58 00), UnRegisterModule (C4 C4 58 01), and DispatchCall (C4 C4 58 02). (References: OSR Online, DOS to 32 bit DLL under XP)
VDDs can also hook certain software interrupts, ranges of memory, and I/O ports in order to emulate devices.
There is quite a bit more to it, but this will have to do for now. :)
Current Status
There is already code in place that appears to implement the necessary VM86 mode management (ntoskrnl/ke/i386/v86m.c) and the beginnings of NtVdmControl (ntoskrnl/ke/i386/vdm.c). Everything else is pretty much unimplemented (NTVDM.EXE doesn't get loaded yet from CreateProcess, etc.)
Existing Code
VDMSound contains a wealth of useful code (including a VDD and associated 16-bit loader application).
FreeDOS is an open-source/free software implementation of an entire MS-DOS-compatible 16-bit OS. This could be useful for gaining insight into the inner workings of the DOS API, but the code would not be directly usable, except for the high-level utilities like FreeCOM (the COMMAND.COM replacement), EDIT, and so on.
DOSEMU performs a function similar to that of NTVDM on Linux.
Build Environment Considerations
It is notable that there is some amount of 16-bit code used in the DOS subsystem and ancillary programs. NASM (which is already in the standard build environment) can produce 16-bit .COM-style executables (and 16-bit .EXEs using a special set of macros, but this is rather limited). However, there is no C compiler capable of producing 16-bit executables in the current build environment, so if 16-bit C code is to be compiled (for example, a shell or other utilities), such a compiler will be required. OpenWatcom seems to be the most promising open-source/free software compiler capable of such a thing (and it can also produce both 16- and 32-bit OS/2 executables, which could be a consideration should the ROS/2 subsystem continue to be developed). Other compilers could certainly be considered if there are dissenting opinions.
Alternate Strategies
Some have suggested that it would be better to do full-machine emulation, like DOSBox. However, this would both reduce performance and disallow the close integration of DOS programs into the Win32 environment (using DOS apps from the command prompt, in pipes, etc.). If this is what is desired, the user might as well use DOSBox itself - but the DOS subsystem is aimed at a totally different audience.
Eventual Goals
It would be nice to use the VDD interface to totally emulate the VGA and VESA BIOSes to allow multiple graphical programs to run simultaneously in windows (rather than fullscreen). This is certainly not trivial, but it does not look impossible either.
Perhaps a large portion of the VDMSound code could be merged - the code base has been inactive for an extended period of time (3 years or more) and there doesn't seem to be much of a chance for it to become active again. Alternatively, VDMSound could be developed further while leaving it as an external component.
At the expense of complicating the code, it would be nice to take advantage of the Virtual Mode Extensions (VME) introduced with the Pentium (Explanation of why VME is useful).
CPU virtualization (as used at one time by NTVDM on non-x86 CPUs) could be useful if portability to other architectures becomes important (for example, x86-64, which does not allow entering vm86 mode from 64-bit mode).

