![]() |
|
Microsoft would like you to believe that MS-DOS is dead, but we should all be as dead as MS-DOS! Windows-95, which is preloaded on nearly every IBM-compatible PC that is sold these days, is little more than MS-DOS 7.0 gussied up with Windows for Workgroups, the WIN32S DLL's, some 32-bit VxDs, and a new shell. Earlier versions of MS-DOS, with or without Windows 3.1x, are still running on tens of millions of machines. And MS-DOS and its clones have become a popular platform for embedded or dedicated systems, due to the dirt-cheap Intel 16-bit CPUs, a ready supply of DOS programmers and inexpensive DOS-based programming tools, and the well-understood DOS file system which makes transporting programs and data in and out of the embedded system a trivial exercise. (This trend was foreshadowed in Robocop -- one shot from inside the visor shows the cybernetic crimefighter booting back up to a C> prompt after a total system crash.)
So MSDOS still has market-share among the motherboards, but does it have mind-share among programmers? It's hard to know exactly, since embedded systems programmers (most of whom are actually engineers by profession) rarely cross paths with the PC-oriented programmers that read magazines like BYTE and DDJ, or vice versa. There's one thing us old-timer MS-DOS book authors know for sure -- the market for mainstream MS-DOS programming books completely evaporated after the release of Windows-95, and it had been on terminal life support for at least a couple years prior. Yet here we have a brand-new book about MS-DOS programming, and moreover a book which devotes itself almost completely to the MS-DOS kernel and defines a whole new genre of such books. How extraordinary!
Pat Villani's FreeDOS Kernel differs sharply from the previous three generations of DOS technical books. The first generation, exemplified by the various (mostly ghostwritten) Peter Norton books such as Inside the IBM PC [1983], consisted mainly of low-brow rehashes of Microsoft and IBM documentation and relied heavily on celebrity marketing. The second generation, which I might say was pioneered by my own Advanced MS-DOS Programming [1986], went the next step beyond to flesh out the Microsoft documentation and show developers how to build useful programs on top of the documented MS-DOS APIs. The third generation, whose archetype was Andrew Schulman's Undocumented DOS [1990], dug into the muck of DOS and explained how its components fit together and why it behaved the way it did.
FreeDOS Kernel takes a new approach and stakes out its own territory. It presents an entirely algorithmic, functionally-oriented description of the DOS kernel and comes complete with a working model: DOS-C. DOS-C is implemented almost entirely in a high-level language (C) and is basically a stripped-down version of DOS/NT, a reentrant microkernel-based operating system that Villani wrote to support embedded systems projects. The DOS-C kernel makes no pretense at being a complete MS-DOS clone -- it exports the documented MS-DOS API and runs "well-behaved" MS-DOS applications. Structurally, any resemblances between DOS-C and MS-DOS are coincidental; for example, DOS-C uses a completely different boot strategy than MS-DOS. In a sense, DOS-C is (as the book's cover states) an MS-DOS "emulator," although the kernel and applications are running as native code and not under the supervision of any other program.
How are FreeDOS and DOS-C related? In spite of its title, the book FreeDOS Kernel is really not about FreeDOS per se. FreeDOS is an Internet-based collaborative project to create a completely free MS-DOS- compatible operating system, patterned loosely after the GNU project for UNIX and using the GNU "Copyleft." Pat Villani has donated the DOS-C kernel to the FreeDOS project and it forms the core of the FreeDOS distribution. The diskette that accompanies the book includes a bootable copy of DOS-C, including a simple command interpreter (COMMAND.COM) written by Villani. The source code on the diskette is intended to illustrate the discussions in the book and is limited to the files needed to rebuild DOS-C. The FreeDOS distribution, on the other hand, includes a more extensive command interpreter written by Tom Norman, a variety of utilities contributed by other authors, and a free C compiler written by Dave Dunfield. More information and source code for FreeDOS components can be found on the FreeDOS web site: http://sunsite.unc.edu/pub/micro/pc-stuff/freedos/freedos.html.
How will FreeDOS Kernel (the book, not the software) fare on the frantically confused, insanely overcrowded, Java-obsessed trade book scene? The sad truth is that it will probably sell only a few copies. The design and editing are kitchen-table-desktop-publishing quality, and R&D's marketing resources are limited. The embedded systems programmers who could really use this book are unlikely to ever hear about it, unless they stumble on this web page. It's interesting to speculate on what a radically different treatment this book might have received if it had fallen into the hands of Andrew Schulman when he was acquiring books for his Programming Series at Addison-Wesley.
-- Ray Duncan (duncan@cerf.net)
|
Chapter 1 |
Introduction |
|
Chapter 2 |
DOS Basics |
|
Chapter 3 |
Bird's-Eye View of DOS-C |
|
Chapter 4 |
DOS-C Kernel: File System Manager |
|
Chapter 5 |
DOS-C Kernel: Memory Manager and Task Manager |
|
Chapter 6 |
DOS-C Kernel: API |
|
Chapter 7 |
Command Line Interpreter |
|
Chapter 8 |
DOS-C Kernel: Putting It All Together |
|
Appendix A |
A Note About Portability |
|
Appendix B |
The FreeDOS Project: Official FAQ File |
|
Appendix C |
DOS-C Distribution Disk |
|
Index |
|
For memory allocation, DOS-C provides the function DosMemAlloc() (Listing 5.2) which can allocate memory using three different criteria: first fit, best fit, and last fit. It can also return the largest memory block available for program loading. The algorithm used is straightforward -- search each memory block until it encounters the last one or meets an exit criteria. If a block matches the criteria of the search, DosMemAlloc() sets a local variable TRUE and terminates the loop, unless it determines that it encountered an end block after the test, in which case it returns an error.
For each iteration, DosMemAlloc() checks for a corrupted memory block. The test it performs simply looks at the m_type member to determine whether the block is valid. If it is, [sic] neither an ASCII "M" nor an ASCII "Z", DosMemAlloc() terminates and returns an error. If it is a valid memory block DosMemAlloc() tests to see if it is free, and if so, proceeds to test it based on mode rules. The algorithm then diverges at this point when it tests a block. A switch statement splits the execution path based on the requested mode.
DosMemAlloc() tests for last fit by searching through each memory block. If the size of the block is greater than or equal to the requested size, DosMemAlloc() notes the address of the block. If DosMemAlloc() examined the block marked with the end of the memory arena, it terminates and returns an error; otherwise, it proceeds to common completion code.
DosMemAlloc() tests for first fit in a way similar to the last fit case. It also searches through each memory contorl block. If the size of the block is greater than or equal to the requested size, the address of the block is noted. However, unlike last fit, it exits the loop and proceeds to common completion code. If DosMemAlloc() reaches the end of the memory arena, it also terminates and returns an error.
The best fit test uses a local variable to main the size of the block closest to the requested size. In a manner similar to the last fit case, DosMemAlloc() compares the requested size to the block size. If the size of the block is greater than or equal to the requested size, DosMemAlloc() then proceeds to test the block size. If it is smaller than the lst block that was greater than the requested size, DosMemAlloc() notes the address of the block. In this way, it determines the smallest block that will satisfy the request. If DosMemAlloc() reaches the end of the memory arena, it terminates and returns an error; otherwise, it proceeds to common completion code.
The test for the largest memory block is the simplest case. DosMemAlloc() simply determines which memory block is the largest and notes the address of this block. In this way, DosMemAlloc() determines the smallest block that will satisfy the request. If it does not find a block, it terminates and returns an error; otherwise, it proceeds to common completion code.
DosMemAlloc completes the search by modifying the block that it found. If the block is larger than requested, DosMemAlloc() proceeds to carve out the size of the block from the block found. If, however, the block found is identical in size to that requested, DosMemAlloc() simply reassigns the block. With all arena maintenance out of the way, DosMemAlloc() exits by returning the segment of paragraph following the MCB [memory control block]. This segment forms the address of the first byte of allocated memory that the user may utilize.
DosMemAlloc (UWORD size, COUNT mode, seg FAR *para, UWORD FAR *asize)
{
REG mcb FAR *p;
mcb FAR *q;
COUNT i;
BOOL found;
/* Initialize */
p = (mcb FAR *)(MK_FP(first_mcb, 0));
/* Search through memory blocks */
for(q = (mcb FAR *)0, i = 0, found = FALSE; !found; )
{
/* check for corruption */
if(p -> m_type != MCB_NORMAL && p -> m_type != MCB_LAST)
return DE_MCBDESTRY;
/* Test if free based on mode rules */
switch(mode)
{
case LAST_FIT:
default:
/* Check for a last fit candidate */
if(p -> m_size >= size && p -> m_psp == FREE_PSP)
/* keep the last know fit */
q = p;
/* not free - bump the pointer */
if(p -> m_type != MCB_LAST)
p = MK_FP(far2para((VOID FAR *)p) + p -> m_size + 1, 0);
/* was there no room (q == 0)? */
else if(p -> m_type == MCB_LAST && q == (mcb FAR *)0)
return DE_NOMEM;
/* something was found - continue */
else
found = TRUE;
break;
case FIRST_FIT:
/* Check for a first fit candidate */
if(p -> m_size >= size && p -> m_psp == FREE_PSP)
{
q = p;
found = TRUE;
break;
}
/* not free - bump the pointer */
if(p -> m_type != MCB_LAST)
p = MK_FP(far2para((VOID FAR *)p) + p -> m_size + 1, 0);
/* nothing found till end - no room */
else
return DE_NOMEM;
break;
case BEST_FIT:
/* Check for a best fit candidate */
if(p -> m_size >= size && p -> m_psp == FREE_PSP)
{
if(i == 0 || p -> m_size < i)
{
i = p -> m_size;
q = p;
}
}
/* not free - bump the pointer */
if(p -> m_type != MCB_LAST)
p = MK_FP(far2para((VOID FAR *)p) + p -> m_size + 1, 0);
/* was there no room (q == 0)? */
else if(p -> m_type == MCB_LAST && q == (mcb FAR *)0)
return DE_NOMEM;
/* something was found - continue */
else
found = TRUE;
break;
case LARGEST:
/* Check for a first fit candidate */
if((p -> m_psp == FREE_PSP) && (i == 0 || p -> m_size > i))
{
size = *asize = i = p -> m_size;
q = p;
}
/* not free - bump the pointer */
if(p -> m_type != MCB_LAST)
p = MK_FP(far2para((VOID FAR *)p) + p -> m_size + 1, 0);
/* was there no room (q == 0)? */
else if(p -> m_type == MCB_LAST && q == (mcb FAR *)0)
return DE_NOMEM;
/* something was found - continue */
else
found = TRUE;
break;
}
}
p = q;
/* Larger fit case */
if(p -> m_size > size)
{
if(mode != LAST_FIT)
{
q = MK_FP(far2para((VOID FAR *)p) + size + 1, 0);
/* Always flow m_type up */
/* on alloc */
q -> m_type = p -> m_type;
p -> m_type = MCB_NORMAL;
p -> m_psp = cu_psp;
q -> m_psp = FREE_PSP;
q -> m_size = p -> m_size - size - 1;
p -> m_size = size;
for(i = 0; i < 8; i++)
p -> m_name[i] = q -> m_name[i] = '\0';
}
else
{
q = MK_FP(far2para((VOID FAR *)p) + (p -> m_size - size), 0);
/* Always flow m_type up */
/* on alloc */
q -> m_type = p -> m_type;
p -> m_type = MCB_NORMAL;
q -> m_psp = cu_psp;
p -> m_psp = FREE_PSP;
p -> m_size = p -> m_size - size - 1;
q -> m_size = size;
for(i = 0; i < 8; i++)
p -> m_name[i] = q -> m_name[i] = '\0';
}
/* Found - return good */
*para = far2para((VOID FAR *)(mode == LAST_FIT ? (VOID FAR *)q : (VOID FAR *)p));
return SUCCESS;
}
/* Exact fit case */
else if(p -> m_size == size)
{
p -> m_psp = cu_psp;
for(i = 0; i < 8; i++)
p -> m_name[i] = '\0';
/* Found - return good */
*para = far2para((VOID FAR *)(BYTE FAR *)p);
return SUCCESS;
}
else
return DE_MCBDESTRY;
}
-- The FreeDOS Kernel, pages 147-153
(This file is distributed on the diskette that accompanies the book)
Copyright (c) 1995 by Pasquale J. Villani
All Rights Reserved.
DOS-C started in 1988 as an experiment in writing device drivers in C for Microsoft's MS-DOS. Both block and character device drivers were written, along with special C data structures to match the MS-DOS request packet. It was then recognized that using the same techniques, an operating system could be written that would take advantage of the C language features and would require much less time to develop than the traditional assembly language techniques. Although UNIX had proven this earlier, it was not tried with a traditional pc operating system.
At this time, a minimal operating system using the device drivers written earlier along with a new 8086 interrupt API was developed. It was called XDOS and proved to be a functional operating system. This new operating system was used to develop booting techniques and a C library SDK was developed for it.
XDOS enhancements were started in 1989 and MS-DOS was chosen as the new API. A more advanced architecture was also developed. This included the use of an IPL (intermediate program loader) to set up the operating environment prior to loading the operating system itself and reentrant system calls facilitating real-time applications. This version, know as NSS-DOS, was completed and demonstrated in 1991. As a result of these demonstrations, NSS was approached to supply source license for this operating system by a major defense contractor. The only new requirement - it had to run on 68K processors.
This presented a new challenge. Due to the MS-DOS model used for the API, NSS-DOS relied heavily on a segmented architecture. To meet this challenge, a major redesign of NSS-DOS was undertaken. New proprietary techniques were developed that allowed the same source to be compiled on a variety of hosts and with a wide range of compilers. This new version, DOS/NT, was the result o of this new project. The kernel was redesigned as a micro kernel along with logical separation of the filesystem, memory and task managers. A new DOS API was designed along with a new DOS SDK to guarantee portability. Additionally, all processor unique code was separated from the core functions. The result is the highly portable operating system that DOS/NT represents.
After a number of successful commercial applications, DOS/NT became part of both dosemu and FreeDOS.
This version, DOS-C, is the subject of an upcoming book and is intended for binary redistribution, free of any royalty. See the accompanying license.txt file for details. If you have not received this file, contact me at the address below and I will send a copy to you.
The DOS-C kernel is also the FreeDOS kernel. FreeDOS is a project designed to provide an alternative to MS-DOS, PC-DOS and DR-DOS that is freely available. See the FreeDOS manifesto for details.
MS-DOS is a trademark of Microsoft Corporation.
UNIX is a trademark of USL, Inc.
| Readability |
|
| Originality |
|
| Organization |
|
| Accuracy |
|
| Consistency |
|
| Depth |
|
| Timeliness |
|
| Editing |
|
| Design |
|
| Overall Value |
|
Explanation of ERCB rating scale: No stars = unacceptable, 1 Star = marginal, 2 Stars = average, 3 Stars = above average, 4 Stars = exceptional.