DOS-C Architecture


The DOS-C architecture is a layered one similar to what you'll find in other operating systems. The only difference between DOS-C and other monolithic operating systems is that there is a decoupling between the upper and lower layers of the operating system. To guarantee that DOS-C is MS-DOS compatible, there are two interface layers where full compliance is necessary. The first interface layer is the DOS API. This is the set of entry points that you expect from a DOS compatible operating system. These include the traditional INT 20h, 21h, 22h, 23h, 24h, 27h, 28h and 2Fh. The other interface layer is the device driver interface. This layer is also well documented and supported by many hardware and software vendors. This layer allows DOS-C to be compatible with MS-DOS to assure proper support of loadable device drivers.

The kernel is written in C wherever possible. However, certain functions are written in assembly language for either efficiency or necessity. These functions typically perform direct machine control, such as stack manipulation and flipping interrupt bits. You will find this code in a number of assembly files along with C to assembly interface functions.

DOS-C API

A DOS API entry point handler intercepts any system call to DOS-C and translates it into a C call. We do this as close to the entry point as possible in order to use C as early as possible. The C call then does some context switching and performs a call to the appropriate internal service function. It is up to the service routine to dispatch the system call through the kernel to the appropriate handler.

The dispatcher directs a system call into the kernel. A large number of calls are directed into the dosfns, or DOS Functions, layer. This layer works with the lower layers to add the DOS appearance to the system calls. It does this by combining calls into the various managers in the next lower layers with code to add features to the manager calls that are unique to a DOS interface. For example, this layer contains functions that encompass all rules for handling file I/O that deals with PSP, handles or character and block I/O functionality.

DOS-C File System

The DOS-C design allows for a file manager that is independent from the design of the rest of the operating system. The fs module is the DOS-C file manager. Whenever an application requests an operation on a file resident on a block device, fs performs the operation. It does this by working with an internal table, called the fnode (or file node) table.

The fnode table is the foundation for the design of the FAT file system manager. The fnode data structure controls all internal file operations and virtualizes the file. When fs performs an operation such as create or open, it allocates an entry from the fnode table and populates the fnode fields. The call returns a number that is the index into the fnode table. From here on out, any function that performs an operation on the file receives this number. The fnode table entry that corresponds to this index controls all file parameters.

If you are familiar with DOS internals from the undocumented internals books and articles, you will recognize the need to map internal fnode to the SFT (System File Table) structure used by DOS. We handle this through the dosfns layer function. The module dosfns maps the fnode number to an SFT handle, which is part its "DOS personality" responsibility. At the fs level, it does its work in an "OS neutral" mode in order to localize unique features to the personality module.

DOS-C Memory Management

Memory management in DOS-C closely emulates MS-DOS. Since DOS-C is a real mode operating system, the DOS-C memory manager is much simpler than what you'll find in other protected mode 80X86 operating systems. In a manner similar to the MS-DOS, DOS-C manages memory through the use of a memory block linked list that utilizes the same arena header as the one MS-DOS uses.

The arena header in DOS-C is known as the MCB (Memory Control Block). As with MS-DOS, this data structure is in front of each allocated and free memory block. DOS-C uses the same 'M' and 'Z' signatures to identify the block type. Also, the segment value of the PSP of the program it belongs to or a 0008h if it belongs to DOS-C, identifies the owner of the block in the same way. DOS-C uses all other entries in an identical manner. Many applications make use of this information and DOS-C provides this same information for sake of compatibility. Unlike fs where MS-DOS personality splits between it and dosfns, memmgr contains all DOS personality.

DOS-C Task Management

Because DOS-C design is designed to emulate MS-DOS, it should be of little surprise that the operating system is not multitasking. As a result, the task manager, task, manages a single user task. Its primary function is to act as a task loader, unlike other operating systems where task loading is an incidental to managing the task. Also, there are only two relatively simple executable file types, .COM and .EXE.

Both executable file types start from a single load entry. It is up to DOS-C to identify which file type it is. It does this by examining the first two bytes of the file. If it is an 'MZ', then it is an .EXE file and task invokes the .EXE loader, otherwise task assumes it to be a .COM binary image and task invokes the .COM loader.

Both loaders initially allocate memory from memmgr to place the environment strings in. The differences begin when the actual file loading occurs. The .COM loader merely allocates memory and begins loading the file into memory for a maximum of 64K. The .EXE loader, on the other hand, computes the size required and then allocates memory. It then proceeds to load the image. When task completes loading the image, it does a seek to the relocation offset and does a segment fixup (necessary for the segmented architecture of the 80X86 family). From this point, both loaders proceed with creating the PSP, cloning the file table, initializing the task's registers and executing the task if so requested.

DOS-C Device Driver Interface Layer and Device Drivers

At the bottom of the DOS-C architecture, but just above the device drivers, is the device driver interface layer. There are two handlers in this layer, chario and blockio. Although each handler uses different methods, both handlers are the primary interface between the device drivers and the remainder of the kernel. Both handlers perform all the necessary buffer management for both types of I/O, including line buffer management.

Both I/O handlers are loosely based on the UNIX I/O model. As with UNIX, there are two types of I/O handlers. The first type is the character I/O type. This type of I/O appears as a stream of bytes to the kernel. The kernel either sequentially reads a byte from or writes a byte to a device driver. There are also functions to read a buffer into memory and handle the familiar DOS line editing functions.

The block I/O interface provides functions to read and write a block of data to a block device -- usually a disk. Each block corresponds to a sector and is represented in memory as a data structure in a block cache. When the kernel reads data from a disk, blockio reads the sector into a buffer and places it into a Least Recently Used (LRU) chain. When blockio needs a new buffer, it writes the tail of the list to disk, if needed, and returns that buffer. When blockio completes the data transfer, the buffer goes to the head of the LRU chain, indicating that it is the newest buffer. Management functions handle these operations for dirty buffer write back, as well as buffer fill. There are also buffer management functions to perform operations such as LRU management and flush.

As with the API, the device driver interface is well documented. Since the interface is designed for an assembly language system, a special assembly language function interfaces all device drivers. This function, execrh, accepts a request packet from the I/O handlers that contains a function number requested by the device driver. It handles the correct sequence of calls to the strategy and interrupt entry points for the device driver and returns the packet to the I/O handler.

The DOS-C device drivers are the bottom layer of the kernel. Like the remainder of the kernel, C is used in these device drivers also. The device drivers perform the necessary device interface between the kernel and the device itself. DOS-C has the same device drivers as MS-DOS and they perform the same functions as their MS-DOS counterparts.


(c) Copyright 1997 by Pasquale J. Villani
All Rights Reserved.