comp.os.msdos.programmer FAQ


Sender: jeffrey@carlyle.com (Jeffrey Carlyle)
Organization: JeffC.org
Approved: news-answers-request@MIT.edu
Newsgroups: comp.os.msdos.programmer,alt.msdos.programmer,comp.answers,alt.answers,news.answers
Followup-To: poster

Summary: Frequently asked questions from DOS programmers with tested answers.

Last-modified: 8 Feb 2002

comp.os.msdos.programmer FAQ Version 2002.02.08

This is the Frequently Asked Questions list for the newsgroup comp.os.msdos.programmer.

COPYRIGHT

Copyright 2002 by Jeffrey Carlyle. All rights reserved. This article is not in the public domain, but it may be redistributed so long as this notice, the acknowledgments, and the information on obtaining the latest copy of this list are retained and no fee is charged. The code fragments may be used freely; credit to the FAQ would be polite. This FAQ is not to be included in any static: archive (e.g. CD-ROM or book); however, a pointer to the FAQ may be included. See <Q:01.14> [Where can I get the latest copy of this FAQ list?] for a link to the latest version of the FAQ.)


TABLE OF CONTENTS


Section 1. General FAQ and Newsgroup Information

Date: 5 Feb 2002 22:03:03 -0500

The General FAQ and Newsgroup Information section contains information about how to use the FAQ and the newsgroup.


<Q:01.01> - Is MS-DOS Dead?

Date: 7 Feb 2002 14:31:56 -0500

No. Though Microsoft may not be actively developing MS-DOS there are still many computers that are not capable of running Microsoft Windows. The current versions of Microsoft Windows will also run most MS-DOS programs; therefore, MS-DOS is not dead, and will most- likely never die just as Commodore-64s and Amigas have not completely died.

Indeed, DOS has found a new life in embedded systems. Other parties continue to develop MS-DOS compatible operating systems for more information see <Q:02.10> [What are my alternatives for MS-DOS compatible OSes?]

Windows NT, 2000, and XP all have a "Command Prompt" which is similar to the orignal MS-DOS command prompt. The new Windows command prompt has some differences from the original MS-DOS command prompty; see <Q:01.12> [What other technical newsgroups should I know about?] for pointers on where to learn about these diffences.


<Q:01.02> - What is this article for?

Date: 5 Feb 2002 22:03:03 -0500

This is the FAQ (Frequently Asked Questions) list for the newsgroup comp.os.msdos.programmer.

FAQ lists are intended to reduce the noise level in their newsgroups that results from the repetition of the same questions, correct answers, wrong answers, corrections to the wrong answers, corrections to the corrections, debate, etc.

This list should serve as a repository of the canonical "best" answers to the questions in it. The names of folks who have helped to improve this FAQ list are listed in <Q:01.03> [Who has contributed to this article?]


<Q:01.03> - Who has contributed to this article?

Date: 7 Feb 2002 14:57:05 -0500

This list is maintained and edited by Jeffrey Carlyle. To contact him send email to <mailto:jeffrey@carlyle.com> or visit his website at <http://www.jeffc.org/> for more information.

Stan Brown, as the former list maintainer, has been the major contributor: Stan wrote most of this list.

Many articles posted in comp.os.msdos.programmer sparked ideas or provided information for the first version of this list. Though they are not responsible for any errors, thanks are due to the following persons for posted articles or private email that led to improvements in this FAQ list:

Jamshid Afshar, Mark Aitchison, Sanjay Aiyagari, George Almasi, Aaron Auseth, Robert Baker, Preston Bannister, Scott Barman, Denis Beauregard, Per Bergland, Mike Black, Chris Blum, Ron Bodkin, Mark Brader, Jon Brinkmann, Andrew James Bromage, Glynn Brooks, Paul Brooks, Ralf Brown, Stan Brown, Shaun Burnett, D'Arcy J.M. Cain, Jeffrey Carlyle, Raymond Chen, Kelly Cooper, Dale Curtis, Denny de Jonge, Eric DeVolder, Alan Drew, Paul Ducklin, Gary Dueck, Dave Dunfield, Roland Eriksson, Mark Evans, Markus Fischer, George Forsman, Roger Fulton, Vincent Giovannone, Robert Grunloh, B.Haible, Janos Haide, Klaus Hartnegg, Kris Heidenstrom, Tom Haapanen, Joel Hoffman, Ari Hovila, Chin Huang, Daniel P Hudson, Joe Huffman, Michael Holin, Mike Iarrobino, Byrial Jensen, Rune Jorgensen, Ajay Kamdar, Everett Kaser, Tim Kannel, JJ Keijser, Jeff Kellam, Igor Kerp, Jen Kilmer, Reinhard Kirchner, Dave Kirsch, Chad Knudsen, Samuel Ko, Jan Kotas, Janne Kukonlehto, Robert Luursema, Benjamin Lee, Stephen Lee, Jim Lynch, Greg Malknecht, Sidney Markowitz, Jim Marks, Dimitri Matzarakis, Fred McCall, Ken McKee, Doug Merrett, Tom Milner, Bill Moore, Duncan Murdoch, Steve Murphy, Daniel Neri, Mert Nickerson, David Nugent, John Oldenburg, David Pape, Keith Petersen, Kevin D. Quitt, Karl Riedling, Arthur Rubin, Gerald Ruderman, Timo Salmi, Tapio Sand, Charles Sandmann, John Schmid, Russell Schulz, Paul Schylter, Huseyin Sevay, Adam Seychell, Ajay Shah, Bob Smith, John Stockton, Bob Stout, Sean Sullivan, Steve Summit, Tom Swingle, Anders Thulin, Curt Tilmes, Rick Watkins, Ya-Gui Wei, Morten Welinder, Joe Wells, Scott Winder, Gregory Youngblood, Eli Zaretski, ceison@lis.net.au, khill@vax1.umkc.edu


<Q:01.04> - How can I search this article for a particular topic?

Date: 5 Feb 2002 22:03:03 -0500

To locate a certain word or phrase use your newsreader's, browser's, or editor's search function.


<Q:01.05> - Are the answers guaranteed to be correct and complete?

Date: 5 Feb 2002 22:03:03 -0500

There has been an attempt to check all facts, but THERE IS NO WARRANTY ON THE CODE OR ON THE TECHNIQUES DESCRIBED HEREIN. Please send corrections to <mailto:jeffrey@carlyle.org>. All the code has been tested; but the testing may not have been perfect, and machines and configurations vary. (Except where otherwise noted, C code was tested with MSC 5, BC++ 2.0, BC++ 4.x, MSVC 5, or MSVC 6.)

The mention of particular books or programs must not be construed to reflect unfavorably on any that are not mentioned.

If you encounter any errors in the FAQ please contact me via email.


<Q:01.06> - What is comp.os.msdos.programmer about?

Date: 5 Feb 2002 22:03:03 -0500

Comp.os.msdos.programmer (comp.sys.ibm.pc.programmer until September 1990) concerns programming for MS-DOS systems. The article "USENET Readership report for Jul 94" in news.lists shows 120,000 readers of this newsgroup worldwide. Traffic (exclusive of crossposts) was 1981 articles aggregating 3.1 Megabytes. It ranked as the 79th most popular newsgroup.

More programming topics in the newsgroup focus on C than on any one other language, but we are not just for C programmers (see <Q:01.07> [Is comp.os.msdos.programmer just for C programmers?]).

Since most MS-DOS systems run on hardware that is roughly compatible with the IBM PC, on Intel 8088, 80188, or 80x86 chips, we tend to get a lot of questions and answers about programming other parts of the hardware.


<Q:01.07> - Is comp.os.msdos.programmer just for C programmers?

Date: 7 Feb 2002 14:40:59 -0500

No, it is for all programmers who want to share information about programming in MS-DOS and DOS replacements like 4DOS. Programs and questions are also posted in Pascal, assembly, and other languages (including MS-DOS batch programming).

Why does the newsgroup seem to be so C-oriented sometimes? There are two reasons. First, comp.lang.c and comp.lang.pascal have evolved in different directions. Comp.lang.pascal has split into discussions about individual Pascal compilers. comp.lang.pascal.borland welcomes discussion specific to Turbo Pascal, and the other new groups likewise. Turbo Pascal programmers tend to find DOS questions welcomed in comp.lang.pascal.borland, so that comp.os.msdos.programmer gets less of the "DOS in Turbo Pascal" traffic. On the other hand, comp.lang.c has stayed closer to talking only about the C language, and vendor-specific or operating-system-specific questions are not welcome. This tends to push questions about disks, DOS file structure, video, the keyboard, TSRs, etc. to comp.os.msdos.programmer even when those programs are written in C.

This FAQ is definitely C-oriented, not because that's necessarily best but because I tried to stick to what I could verify personally. As a C programmer (with some assembler), I could most carefully verify solutions in C or assembler. I felt that short, clear programs could be published in just one language and programmers could translate them into their languages of choice. But the FAQ list also contains several long programs written only in C; this is a defect with no obvious remedy. Most answers that point to source code at archive sites include both C- and Pascal- language source when available.


<Q:01.08> - What is comp.sys.ibm.pc.programmer?

Date: 5 Feb 2002 22:03:03 -0500

The newsgroup comp.sys.ibm.pc.programmer is the old name of the modern newsgroup comp.os.msdos.programmer, and the old name has been obsolete since September 1990; however, some systems may not have removed the old group, or may have removed it but aliased it to the new name.This means that some people still think they're posting to comp.sys.ibm.pc.programmer even though they're actually posting to comp.os.msdos.programmer.

You can easily verify the non-existence of comp.sys.ibm.pc.programmer by reference to the "List of Active Newsgroups" posted to news.groups. It's available at:

<ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/active-newsgroups>

(For RTFM usage instructions see <Q:01.13> [Where are FAQ lists archived?])


<Q:01.09> - Is comp.os.msdos.programmer available as a mailing list?

Date: 5 Feb 2002 22:03:03 -0500

Sorry, the newsgroup is not available as a mailing list.


<Q:01.10> - What's this netiquette?

Date: 5 Feb 2002 22:03:03 -0500

Netiquette is good Usenet etiquette. It includes basic rules like the ones below. (See also <Q:01.11> [How can I learn more about Usenet?])


<Q:01.11> - How can I learn more about Usenet?

Date: 5 Feb 2002 22:03:03 -0500

There are two important newsgroups for learning about how Usenet and newsreader software works:

The following postings in news.announce.newusers might be considered the "mandatory course" for new users:

The articles mentioned above are downloadable via ftp from rtfm.mit.edu in the following files:

Welcome to news.newusers.questions (Weekly posting): <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/news-newusers-intro>

Introduction to news.announce.newusers: <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/news-announce/introduction/part1>

What is Usenet: <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/usenet/what-is/part1> <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/usenet/what-is/part2>

Answers to Frequently Asked Questions: <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/usenet/faq/part1>

Rules for posting to Usenet: <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/usenet/posting-rules/part1>

A Primer on How to Work with the Usenet Community: <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/usenet/primer/part1>

Hints on writing style for Usenet: <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/usenet/writing-style/part1>

Emily Postnews Answers Your Questions on Netiquette: <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/usenet/emily-postnews/part1>

For rtfm.mit.edu instructions, see <Q:01.13> [Where are FAQ lists archived?]


<Q:01.12> - What other technical newsgroups should I know about?

Date: 7 Feb 2002 14:35:45 -0500

It is impractical to attempt to list all relevant newsgroups here. The few that are listed are some of the older newsgroups. To find additional groups use your newsreader's newsgroup search facility.

Caution: Some of these newsgroups have specialized charters; you'll probably get flamed (and deserve it) if you post to an inappropriate group. Most groups have FAQ lists that will tell you what's appropriate. Don't post a request for the FAQ list; instead, retrieve it yourself: see <Q:01.13> [Where are FAQ lists archived?]


<Q:01.13> - Where are FAQ lists archived?

Date: 5 Feb 2002 22:03:03 -0500

Very possibly the FAQ list you want is already at your site. Check the newsgroup news.answers; if your site doesn't carry news.answers, check comp.answers, rec.answers, etc., according to the top-level name in the FAQ list's "home" newsgroup. Articles are posted to the *.answers groups in a way that should make them last until the next versions are posted. If they expire sooner at your site, you might want to lobby your sysadmin to treat the moderated *.answers groups as a special case and grant them longer expiration times than other groups.

To ftp most FAQ lists, connect to <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers>. The name of the file that you want is the Archive-name from the top of the article. For instance, if the Archive-name were software-eng/part1 you would retrieve <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/software-eng/part1>.

By email (only if you have no ftp access, please), the server is <mailto:mail-server@rtfm.mit.edu>. It accepts "send" commands that omit the leading "/pub/" from file names; for example:

send usenet-by-group/news.answers/software-eng/part1

For full instructions about the mail server, send it a message consisting of these two lines:

     help
     index

Not just FAQ lists, but every article listed in the "List of Periodic Informational Postings" (LoPIP) can be obtained by ftp or email from rtfm.mit.edu. If you have an old copy of an informational article, look for an "Archive-name" at the beginning. The article is stored under that name at <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers>. If the article has no Archive-name, check the first name on the Newsgroups line and change to that directory under <ftp://rtfm.mit.edu/pub/usenet-by-group>.

Stan Brown also maintains a FAQ on finding FAQs. It can be found at <http://www.mindspring.com/~brahms/faqget.htm>.


<Q:01.14> - Where can I get the latest copy of this FAQ list?

Date: 7 Feb 2002 01:05:15 -0500

The latest version of the FAQ posted to the newsgroup can always be found on the RTFM server: <ftp://rtfm.mit.edu/pub/usenet-by-group/news.answers/msdos-programmer-faq>

If you have no ftp access, see <Q:01.13> [Where are FAQ lists archived?] for instructions on retrieving it by email.

The most recent version of the FAQ can be obtained from: <http://www.stratoware.com/msdos/>

The Stratoware.Com site is the FAQ editor's official site and contains an HTML version of the FAQ. Various other HTML versions can be found, but they are not guaranteed to be up-to-date. You can find these HTML versions by searching for "comp.os.msdos.programmer FAQ" at any popular search engine.


Section 2. General Reference

Date: 5 Feb 2002 22:03:03 -0500

The General Reference section contains information about finding popular online MS-DOS reference materials.


<Q:02.01> - Are there any good on-line references for PC hardware components?

Date: 8 Feb 2002 19:36:40 -0500

Good reports of HELPPC21 have been posted. It is downloadable as: <ftp://garbo.uwasa.fi/pc/programming/helppc21.zip>

This hypertext system contains much information on ports and other hardware, as well as some overlap with Ralf Brown's interrupt list <Q:02.03> [What and where is Ralf Brown's interrupt list?]. It is shareware ($25).

Additional information (and more recent) information can be found in Ralf Brown's interrupt list; see <Q:02.03> [What and where is Ralf Brown's interrupt list?] for information on locating the list.


<Q:02.02> - Are there any good on-line references for PC interrupts?

Date: 5 Feb 2002 22:03:03 -0500

The definitive work is Ralf Brown's interrupt list. For more information see <Q:02.03> [What and where is Ralf Brown's interrupt list?].


<Q:02.03> - What and where is Ralf Brown's interrupt list?

Date: 8 Feb 2002 19:37:16 -0500

Ralf Brown's Interrupt List contains megabytes of information on documented and (officially) undocumented BIOS and DOS interrupts, DOS tables, and interrupts hooked by many software packages.

The distribution files contain not only the actual list, but also a collection of utilities and conversion programs for the list.

Ralf Brown's Interrupt List can be downloaded from his page at: <http://www.cs.cmu.edu/~ralf/files.html>

HTML versions of Ralf Brown's Interrupt List can be found at:

Updates are announced in comp.archives.msdos.announce and on Ralf Brown's web page at: <http://www.cs.cmu.edu/~ralf/>

Ralf's web page contains the somewhat unassuming line: "[h]e is well-known in cyberspace for maintaining the Interrupt List..." Ralf has done astounding work as the maintainer of the list; his work has been greatly appreciated by thousands of programmers.


<Q:02.04> - Where can I find lex, yacc, and language grammars?

Date: 5 Feb 2002 22:03:03 -0500

The FAQ list of the comp.compilers newsgroup answers this for BASIC, C, Pascal, and other languages. See <Q:01.13> [Where are FAQ lists archived?]


<Q:02.05> - What's the best book to learn programming?

Date: 5 Feb 2002 22:03:03 -0500

Sorry, this FAQ list cannot settle religious arguments.

Much of the heat over this topic arises because each person believes that the book that he or she learned from is the best book, but different people have very different experiences of the same book. The only person who can tell you which is the best book for learning a given topic is you.

Your best bet is to go to a fairly well stocked bookstore when you have a couple of hours to spare. Start at one end of the shelf and work your way methodically through every book that looks like it might cover what you want to learn. Look at the tables of contents; read a page or two from each book. Then make your decision. If money is a problem or if you're not sure of your choice, check out your top two or three from your library.


<Q:02.06> - Why won't my code work?

Date: 7 Feb 2002 14:44:40 -0500

First you need to try to determine whether the problem is in your use of the programming language or in your use of MS-DOS and your PC hardware. (Your manual should tell you which features are standard and which are vendor- or MS DOS- or PC-specific. You have read your manual carefully, haven't you?)

If the feature that seems to be working wrong is something related to your PC hardware or to the internals of MS-DOS, this group is the right place to ask. (Please check this FAQ list first, to make sure your question isn't already answered here.)

On the other hand, if your problem is with the programming language, the comp.lang hierarchy (including comp.lang.pascal.* and comp.lang.c) is probably a better resource. Please read the other group's FAQ list thoroughly before posting. (These exist in comp.lang.c, comp.lang.c++, comp.lang.modula3, comp.lang.lisp, comp.lang.perl; they may exist in other groups as well. comp.lang.pascal.borland has a Mini-FAQ.) It's almost never a good idea to crosspost between comp.os.msdos.programmer and a language group.

Before posting in either place, try to make your program as small as possible while still exhibiting the bad behavior. Sometimes this alone is enough to show you where the trouble is. Also edit your description of the problem to be as short as possible. This makes it look more like you tried to solve the problem on your own, and makes people more inclined to try to help you. See also <Q:01.10> [What's this netiquette?]


<Q:02.07> - Are there any good sources of example code?

Date: 5 Feb 2002 22:03:03 -0500

Bob Stout maintains a very large archive called SNIPPETS. For more information see <Q:02.08> [What and where is SNIPPETS?].


<Q:02.08> - What and where is SNIPPETS?

Date: 6 Feb 2002 00:00:45 -0500

Excerpt from the SNIPPETS FAQ follows:

     The  SNIPPETS archive, maintained by Bob Stout, contains  public
     domain/freeware portable C/C++ source code & instructional text.
     There are more than 500 files, including:
  
       Approx.  56,000  lines  of  code + approx.  10,000  lines  of
       tutorials.
       Approx. 30% PC-specific, 70% portable
       Approx. 6% C++-specific, 94% C/C++
  
     The  PC-specific functions are system-level utility  code  -  no
     multimedia or GUI code. Tested on all popular PC compilers  plus
     Unix  compilers  where  possible. An  eclectic  collection  with
     everything  from  macros  to complete cut-and-paste  C/C++  code
     solutions & utilities, along with FAQ and instructional files.

Official SNIPPETS sites: <ftp://ftp.snippets.org/> <http://www.snippets.org/>


<Q:02.09> - Is the source code MS-DOS available?

Date: 5 Feb 2002 22:03:03 -0500

No, the source code to MS-DOS is not currently available; however, the source code to an MS-DOS alternative known as FreeDOS is freely available; see <Q:02.11> [What and where is FreeDOS?] for more information.


<Q:02.10> - What are my alternatives for MS-DOS compatible OSes?

Date: 5 Feb 2002 22:03:03 -0500

The FreeDOS Project (see <Q:02.11> [What and where is FreeDOS?]) has created an open source MS-DOS compatible operating system known as FreeDOS. Additionly, IBM has released an updated version of their PC-DOS known as PC-DOS 2000.

Lineo currently owns the rights to DR-DOS, but they appear to no longer be developing or supporting it; however, one can still find DR-DOS and even CP/M on their FTP site: <ftp://ftp.lineo.com/pub/drdos/>


<Q:02.11> - What and where is FreeDOS?

Date: 7 Feb 2002 00:49:45 -0500

The FreeDOS Project creates and maintains FreeDOS an open source operating system covered by the GNU General Public License. FreeDOS is a functional operating system; however, they have not yet reached their stated of goal of being able to run Windows and DOOM. The FreeDOS Project has not accessed any Microsoft source code and is creating FreeDOS from scratch.

More information and the FreeDOS distribution itself can be found at: <http://www.freedos.org>


<Q:02.12> - Where can I find out about batch files?

Date: 7 Feb 2002 12:59:56 -0500

If the question is not answered elsewhere in this FAQ, it may be answered in Timo Salmi's "Frequently Asked Questions about MS-DOS batches." This list can be found at <ftp://garbo.uwasa.fi/pc/link/tsbat.zip>.


Section 3. Compile and link

Date: 5 Feb 2002 22:03:03 -0500

The Compile and Link section contains information issues involving compiling and linking of MS-DOS code.


<Q:03.01> - What the heck is DGROUP > 64K?

Date: 8 Feb 2002 19:38:12 -0500

This question explains the problem; the next question gives some remedies.

DGROUP is a link-time group of data segments, and the compiler typically generates code that expects DS to be pointing to DGROUP. (Exception: Borland's huge model has no DGROUP.)

Here's what goes into DGROUP:

In all of the above, which is to say all six models in Microsoft C and all but huge in Borland C, DGROUP is limited to 64K including string literal data (which are treated as static data). This limitation is due to the Intel CPU's segmented architecture.

For more information, see topics like "memory models" and "memory management" in the index of your compiler manual. Also for an extended general discussion of memory usage in Borland C programs, of which much applies to any C compiler in DOS see TI738.asc, downloadable as part of:

<ftp://garbo.uwasa.fi/pc/c-lang/bchelp10.zip>


<Q:03.02> - How do I fix 'automatic data segment exceeds 64K' or 'stack plus data exceed 64K'?

Date: 5 Feb 2002 22:03:03 -0500

These messages are a variation of "DGROUP > 64K". For causes, please see the preceding question.

If you get this error in tiny model, your program is simply too big and you must use a different memory model. If you get this link error in models small, compact, medium, large, or Microsoft's huge, there are some things you can do. (This error can't occur in Borland's huge model.)

If you have one or two big global arrays, simply declare them far. The compiler takes this to mean that any references to them will use 32-bit pointers, so they'll be in separate segments and no longer part of DGROUP.

Or you can use the /Gt[number] option with Microsoft or - Ff[=size] with Borland C++ 2.0 and up. This will automatically put variables above a certain size into their own segments outside of DGROUP.

Yet another option is to change global arrays to far pointers. Then at the beginning of your program, allocate them from the far heap (_fmalloc() in Microsoft, farmalloc() in Borland).

Finally, you can change to huge model (with Borland compilers, not Microsoft). Borland's H model still uses far pointers by default, but "sets aside the [64K] limit" and has no DGROUP group, according to the BC++ 2.0 Programmer's Guide. Microsoft's H model does use huge data pointers by default but retains DGROUP and its 64K limit, so switching to the huge model doesn't buy you anything if you have DGROUP problems.


<Q:03.03> - Will Borland C code and Microsoft C code link together?

Date: 5 Feb 2002 22:03:03 -0500

Typically someone who owns compiler A and is trying to write code to link with a third-party library that was compiled under compiler B asks this question.

The answer to the question is, Not in general. Here are some of the reasons:

Those problems will generate link-time errors. Others may not show up until run time:

That said, there are some circumstances where you can link hybrids. Your best chance of success comes if you compile in large model with the compiler switch that says to reload DS on entry to each function, avoid longs and floating point, use only 16-bit pointers, suppress stack checking, and specify all libraries used in the link.


<Q:03.04> - Why did my program bomb at run time with 'floating point formats not linked' or 'floating point not loaded'?

Date: 5 Feb 2002 22:03:03 -0500

These messages look similar but have very different causes.

"Floating point not loaded" is Microsoft C's run-time message when the code requires a numeric coprocessor but your computer doesn't have one installed. If the program is yours, relink it using the xLIBCE or xLIBCA library (where x is the memory model).

"Floating point formats not linked" is a Borland run-time error (Borland C or C++, Turbo C or C++). Borland's compilers try to be smart and not link in the floating- point (f-p) library unless you need it. Alas, they all get the decision wrong. One common case is where you don't call any f-p functions, but you have %f or other f-p formats in scanf() or printf() calls. The cure is to call an f-p function, or at least force one to be present in the link.

To do that, define this function somewhere in a source file but don't call it:

   static void forcefloat(float *p)
   {
     float f = *p;
     forcefloat(&f);
   }

It doesn't have to be in the module with the main program, as long as it's in a module that will be included in the link.

If you have Borland C++ 3.0, the README file documents a slightly less ugly work-around. Insert these statements in your program:

   extern unsigned _floatconvert;
   #pragma extref _floatconvert

<Q:03.05> - How can I change the stack size in Borland's C compilers?

Date: 5 Feb 2002 22:03:03 -0500

In Turbo C, Turbo C++, and Borland C++, you may not find "stack size" in the index but the global variable _stklen should be there. The manual will instruct you to put a statement like

   extern unsigned _stklen = 54321U;

in your code, outside of any function. You must assign the value right in the extern statement; it won't work to assign a value at run time. The linker may give you a duplicate symbol warning, which you can ignore.

If you are using the Borland PowerPack for DOS _stklen does not change the stack size. To change the stack size you must use STACKSIZE in your .DEF file. HEAPSIZE can be used to change the size of your program's heap.


<Q:03.06> - What's the format of an .OBJ file?

Date: 5 Feb 2002 22:03:03 -0500

Information about the base .OBJ format can be found in Intel's document number #121748-001, {8086 Relocatable Object Module Formats} (not verified).

Both Microsoft and Borland have extended the .OBJ format, as has IBM for OS/2; and according to the MS-DOS encyclopedia, Microsoft doesn't actually use all the listed formats.

Microsoft-specific .OBJ formats:

Borland-specific .OBJ formats:

A "tutorial on the .OBJ format" comes with the VAL experimental linker, downloadable as <ftp://garbo.uwasa.fi/pc/assembler/linker.zoo>.


<Q:03.07> - What's the format of an .EXE header?

Date: 5 Feb 2002 22:03:03 -0500

See PC Magazine 30 June 1992 (XI: 12) pages 349-350 for the old and new formats. For a more detailed layout, look under INT 21 AH=4B in Ralf Brown's interrupt list <Q:02.03> [What and where is Ralf Brown's interrupt list?] That list includes extensions for Borland's TLINK and Borland debugger info.

Among the books that detail formats of executable files are {DOS Programmer's Reference: 2d Edition} by Terry Dettman and Jim Kyle, ISBN 0-88022-458-4; and {Microsoft MS-DOS Programmer's Reference}, ISBN 1-55615-329-5.


<Q:03.08> - What's the difference between .COM and .EXE formats?

Date: 7 Feb 2002 14:47:51 -0500

To oversimplify: a .COM file is a direct image of how the program will look in main memory, and a .EXE file will undergo some further relocation when it is run (and so it begins with a relocation header). A .COM file is limited to 64K for all segments combined, but a .EXE file can have as many segments as your linker will handle and be as large as RAM can take.

The actual file extension doesn't matter. DOS knows that a file being loaded is in .EXE format if its first two bytes are MZ or ZM; otherwise it is assumed to be in .COM format. For instance, DR-DOS 6.0's COMMAND.COM is in .EXE format as is COMMAND.COM in recent versions of MS-DOS.

Reader Paul Schylter posted this description of .COM files vs. .EXE files to the newsgroup in message <a3rpp8$a9h$1@merope.saaf.se>:

"Actually they must be less than 0xFF00 bytes long, since the PSP, which isn't included in the COM file but is within those 64K, is 256 bytes long.

"Then CAN use many segments, but they don't have to. In particular, any .COM file can be converted to an .EXE file by adding an appropriate header to it.

"There are some other differences between a .COM file and a single segment .EXE file (both of which must be smaller than 64K):

"The entry point of the .COM file is _always_ 0x100, while the entry point of the .EXE file can be at any address.

"The stack size of the .COM file is the remainder of those 64K which isn't used by the code image, while the stack size if the single segment .EXE file can be set at any size as long as it fits within those 64K. Thus the stack can be smallere in the .EXE file.

"When a COM file is loaded, the entire TPA (= "free memory") of MS-DOS is allocated for that COM file -- including those parts of the TPA which are outside the 64k of the COM file. If you don't want this (e.g. because your COM file is a TSR and you want to load other programs later), you must explicitly free those parts of the TPA you want freed. In the header of an .EXE file you can specify how large part of the TPA that .EXE file should receive."


<Q:03.09> - How do I create a .COM file?

Date: 5 Feb 2002 22:03:03 -0500

There are two steps to creating a .COM file. First, your program must not have a stack. In C, you must compile your program with the TINY memory model. Second, use EXE2BIN or a similar program to convert an EXE file to a COM file. To find EXE2BIN see subject: <Q:03.10> [Where is EXE2BIN located?]


<Q:03.10> - Where is EXE2BIN located?

Date: 5 Feb 2002 22:03:03 -0500

EXE2BIN was formerly shipped with MS-DOS. If you are still using DOS 5.0 or earlier you can find EXE2BIN in your DOS directory. Users of DOS 6.x need to get the MS-DOS Supplemental Disks. These disks are available via FTP at ftp.microsoft.com.

<ftp://ftp.microsoft.com/peropsys/msdos/public/supplmnt>


<Q:03.11> - What does this message mean: 'A20 already enabled so test is meaning less?'

Date: 5 Feb 2002 22:03:03 -0500

The DPMIINST program included with older versions of Borland C++ and Turbo C++ compilers generates this message. Before running DPMIINST you must clean boot your computer.


Section 4. Keyboard

Date: 5 Feb 2002 22:03:03 -0500

The keyboards sections coontains information about how to access the keyboard.


<Q:04.01> - How can I read a character without echoing it to the screen, and without waiting for the user to press the Enter key?

Date: 5 Feb 2002 22:03:03 -0500

The C compilers from Microsoft and Borland offer getch() (or getche() to echo the character); Turbo Pascal has ReadKey.

In other programming languages, execute INT 21 AH=8; AL is returned with the character from standard input (possibly redirected). If you don't want to allow redirection, or you want to capture Ctrl-C and other special keys, use INT 16 AH=10; this will return the scan code in AH and ASCII code (if possible) in AL, but AL=E0 with AH nonzero indicates that one of the gray "extended" keys was pressed. (If your BIOS doesn't support the extended keyboard, use INT 16 AH=0 not 10.)


<Q:04.02> - How can I find out whether a character has been typed, without waiting for one?

Date: 5 Feb 2002 22:03:03 -0500

In Turbo Pascal, use KeyPressed. Both Microsoft C and Turbo C offer the kbhit() function. All of these tell you whether a key has been pressed. If no key has been pressed, they return that information to your program. If a keystroke is waiting, they tell your program that but leave the key in the input buffer.

You can use the BIOS call, INT 16 AH=01 or 11, to check whether an actual keystroke is waiting; or the DOS call, INT 21 AH=0B, to check for a keystroke from stdin (subject to redirection). See Ralf Brown's interrupt list <Q:02.03> [What and where is Ralf Brown's interrupt list?].


<Q:04.03> - How can I disable Ctrl-C/Ctrl-Break and/or Ctrl-Alt-Del?

Date: 5 Feb 2002 22:03:03 -0500

Several utilities are downloadable from: <ftp://ftp.simtel.net/pub/simtelnet/msdos/keyboard/>

In that directory, cadel.zip contains a TSR (with source code) to disable those keys. Also, keykill.arc contains two utilities: keykill.com lets you disable up to three keys of your choice, and deboot.com changes the boot key to leftShift-Alt-Del. C programmers who simply want to make sure that the user can't Ctrl-Break out of their program can use the ANSI-standard signal() function; the Borland compilers also offer ctrlbrk() for handling Ctrl-Break. However, if your program uses normal DOS input such as getch(), ^C will appear on the screen when the user presses Ctrl-C or Ctrl-Break. You can avoid the ^C echo for Ctrl-C by using _bios_keybrd() in MSC or bioskey() in BC++; however, Ctrl-Break will still terminate the program.

An alternative approach involves programming input at a lower level. You can use INT 21 AH=7, which allows redirection but doesn't echo the ^C (or any other character, for that matter); or use INT 16 AH=0 or 10; or hook INT 9 to discard Ctrl-C and Ctrl-Break before the regular BIOS keyboard handler sees them; etc., etc.

You should be aware that Ctrl-C and Ctrl-Break are processed quite differently internally. Ctrl-Break, like all keystrokes, is processed by the BIOS code at INT 9 as soon as the user presses the keys, even if earlier keys are still in the keyboard buffer: by default the handler at INT 1B is called. Ctrl-C is not special to the BIOS, nor is it special to DOS functions 6 and 7; it is special to DOS functions 1 and 8 when at the head of the keyboard buffer. You will need to make sure BREAK is OFF to prevent DOS polling the keyboard for Ctrl-C during non-keyboard operations.

Some good general references are {Advanced MS-DOS} by Ray Duncan, ISBN 1-55615-157-8; {8088 Assembler Language Programming: The IBM PC}, ISBN 0-672-22024-5, by Willen & Krantz; and {COMPUTE!'s Mapping the IBM PC}, ISBN 0-942386- 92-2.


<Q:04.04> - How can I disable the print screen function?

Date: 5 Feb 2002 22:03:03 -0500

There are really two print screen functions: 1) print current screen snapshot, triggered by PrintScreen or Shift- PrtSc or Shift-gray*, and 2) turn on continuous screen echo, started and stopped by Ctrl-P or Ctrl-PrtSc.

1) Screen snapshot to printer:

The BIOS uses INT 5 for this. Fortunately, you don't need to mess with that interrupt handler. The standard handler, in BIOS versions dated December 1982 or later, uses a byte at 0040:0100 (= 0000:0500) to determine whether a print screen is currently in progress. If it is, pressing PrintScreen again is ignored. So to disable the screen snapshot, all you have to do is write a 1 to that byte. When the user presses PrintScreen, the BIOS will think that a print screen is already in progress and will ignore the user's keypress. You can re-enable PrintScreen by zeroing the same byte.

Here's some simple code:

   void prtsc_allow(int allow) /* 0=disable, nonzero=enable */
   {
     unsigned char far* flag = (unsigned char far*)0x00400100UL;
     *flag = (unsigned char)!allow;
   }

2) Continuous echo of screen to printer:

If ANSI.SYS is loaded, you can easily disable the continuous echo of screen to printer (Ctrl-P or Ctrl- PrtSc). Just redefine the keys by "printing" strings like these to the screen (BASIC print, C printf(), Pascal Write statements, or ECHO command in batch files), where <27> stands for the Escape character, ASCII 27:

   <27>[0;114;"Ctrl-PrtSc disabled"p
   <27>[16;"^P"p

If you haven't installed ANSI.SYS, I can't offer an easy way to disable the echo-screen-to-printer function.

Actually, you might not need to disable Ctrl-P and Ctrl- PrtSc. If your only concern is not locking up your machine, when you see the "Abort, Retry, Ignore, Fail" prompt just press Ctrl-P again and then press I. As an alternative, install one of the many print spoolers that intercept printer-status queries and always return "Printer ready".


<Q:04.05> - How can my program turn NumLock (CapsLock, ScrollLock) on or off?

Date: 5 Feb 2002 22:03:03 -0500

First, if you just don't want NumLock turned on when you reboot, check your system's setups. (Press a special key like Del at boot time, or run the setup program supplied with your system.) Many systems may have an option in setup to turn NumLock off at boot time.

You need to twiddle bit 5, 6, or 4 of location 0040:0017. The code example below demonstrates changing NumLock status: lck() turns on a lock state, and unlck() turns it off.

   /* The  status  lights on some keyboards may not reflect  the
    * change.   If  yours  is one, call INT 16 AH=2,  "get  shift
    * status", and that may update them.  It will certainly do no
    * harm.)
    */
 
    #define NUM_LOCK  (1 >> 5)
    #define CAPS_LOCK (1 >> 6)
    #define SCRL_LOCK (1 >> 4)
  
    void lck(int shiftype)
    {
      char far* kbdstatus = (char far*)0x00400017UL;
      *kbdstatus |= (char)shiftype;
    }
    void unlck(int shiftype)
    {
      char far* kbdstatus = (char far*)0x00400017UL;
      *kbdstatus &= ~(char)shiftype;
    }

<Q:04.06> - How can I speed up the keyboard's auto-repeat?

Date: 8 Feb 2002 19:42:39 -0500

The keyboard speed has two components: delay (before a key that you hold down starts repeating) and typematic rate (the speed once the key starts repeating). Most BIOS versions since 1986 let software change the delay and typematic rate by calling INT 16 AH=3, "set typematic rate and delay"; see Ralf Brown's interrupt list <Q:02.03> [What and where is Ralf Brown's interrupt list?]. If you have DOS 4.0 or later, you can use the MODE CON command that you'll find in your DOS manual.

On 83-key keyboards (mostly XTs), the delay and typematic rate can't easily be changed. According to PC Magazine 15 Jan 1991 (x: 1) page 409, to adjust the typematic rate you need "a memory-resident program which simply '[watches]' the keyboard to see if you're holding down a key . and after a certain time [starts] stuffing extra copies of the held-down key into the buffer." No source code is given in that issue; but the QUICKEYS utility that PC Magazine published in 1986 does this sort of watching (not verified); source and object code are downloadable from <http://www.simtel.net/pub/pd/48667.html>


<Q:04.07> - What is the SysRq key for?

Date: 5 Feb 2002 22:03:03 -0500

There is no standard use for the key. The BIOS keyboard routines in INT 16 simply ignore it; therefore so do the DOS input routines in INT 21 as well as the keyboard routines in libraries supplied with high-level languages.

When you press or release a key, the keyboard triggers hardware line IRQ1, and the CPU calls INT 9. INT 9 reads the scan code from the keyboard and the shift states from the BIOS data area.

What happens next depends on whether your PC's BIOS supports an enhanced keyboard (101 or 102 keys). If so, INT 9 calls INT 15 AH=4F to translate the scan code. If the translated scan code is 54 hex (for the SysRq key) then INT 9 calls INT 15 AH=85 and doesn't put the keystroke into the keyboard buffer. The default handler of that function does nothing and simply returns. (If your PC has an older BIOS that doesn't support the extended keyboards, INT 15 AH=4F is not called. Early ATs have 84-key keyboards, so their BIOS calls INT 15 AH=85 but not 4F.)

Thus your program is free to use SysRq for its own purposes, but at the cost of some programming. You could hook INT 9, but it's probably easier to hook INT 15 AH=85, which is called when SysRq is pressed or released.


<Q:04.08> - How can my program tell what kind of keyboard is on the system?

Date: 5 Feb 2002 22:03:03 -0500

Ralf Brown's Interrupt List <Q:02.03> [What and where is Ralf Brown's interrupt list?] includes MEMORY.LST, a detailed breakdown by Robin Walker of the contents of the BIOS system block that starts at 0040:0000. Bit 4 of byte 0040:0096 is "1=enhanced keyboard installed". Here is a C code example to test the keyboard type:

   char far *kbd_stat_byte3 = (char far *)0x00400096UL;
   if (0x10 & *kbd_stat_byte3)
   {
     /* 101- or 102- keyboard is installed */
   }
   else
   {
     /* Not installed */
   }

PC Magazine 15 Jan 1991 (x: 1) suggests on page 412 that "for some clones [the above test] is not foolproof". If you use this method in your program you should provide the user some way to override this test, or at least some way to tell your program to assume a non-enhanced keyboard. The article suggests a different approach to determining the type of keyboard.


<Q:04.09> - How can I tell if input, output, or stderr has been redirected?

Date: 8 Feb 2002 19:49:17 -0500

Normally, input and output are associated with the console (i.e., with the keyboard and the screen, respectively). If either is not, you know that it has been redirected. Some source code to check this is available at the usual archive sites.

Timo Salmi has created a collection of Turbo Pascal units, one of which can be used to detect such redirection. These can be downloaded from <http://garbo.uwasa.fi/pc/ts.html>. The files you are looking for have names of the format tspaVV??.zip where the VV is the current version and ?? is 70, 60, 55, 50, or 40 for Turbo Pascal 7.0, 6.0, 5.5, 5.0, or 4.0 respectively.) Source code is not included. Also see the downloadable Frequently Asked Questions files by Timo Salmi: <ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip>

If you program in C, use isatty() if your implementation has it.

Good references for the principles are PC Magazine 16 Apr 1991 (x: 7) page 374; Ray Duncan's {Advanced MS-DOS}, ISBN 1-55615-157-8, or Ralf Brown's interrupt list (<Q:02.03> [What and where is Ralf Brown's interrupt list?]) for INT 21 AX=4400; and Terry Dettman and Jim Kyle's {DOS Programmer's Reference: 2d edition}, ISBN 0-88022-458-4, pages 602-603.


<Q:04.10> - How can I increase the size of the keyboard buffer?

Date: 8 Feb 2002 19:50:41 -0500

Microsoft has its own keyboard extender available on the MS-DOS supplemental disks for MS-DOS 6.22 which can be found at: <ftp://ftp.microsoft.com/peropsys/msdos/public/supplmnt/sup622.exe>

Stan Brown, the former list maintainer, tested only one of the many available device drivers that do this, namely BUF160, which extends the keyboard buffer to 160 characters. It performed flawlessly for two years with MS-DOS 5 and Windows 3.1. It's downloadable as: <http://www.simtel.net/pub/pd/47186.html> <ftp://garbo.uwasa.fi/pc/keyboard/buf160_6.zip>


<Q:04.11> - How can I stuff characters into the keyboard buffer?

Date: 5 Feb 2002 22:03:03 -0500

If your computer has an enhanced keyboard (see <Q:04.08> [How can my program tell what kind of keyboard is on the system?]), put the scan code in CH and the ASCII character in CL, then execute INT 16 AH=5. The return in AL is 0 for success or 1 for buffer full.


Section 5. Disks and files

Date: 5 Feb 2002 22:03:03 -0500

Information about accessing disks and files from MS-DOS.


<Q:05.01> - What drive was the PC booted from?

Date: 5 Feb 2002 22:03:03 -0500

Under DOS 4.0 or later, use INT 21 AX=3305. DL is returned with an integer indicating the boot drive (1=A:, etc.).


<Q:05.02> - How can I boot from drive B:?

Date: 8 Feb 2002 19:52:06 -0500

Downloadable shareware: <http://www.simtel.net/pub/pd/44102.html> <ftp://garbo.uwasa.fi/pc/bootutil/boot_b.zip>

The included documentation says it works by writing a new boot sector on a disk in your a: drive that redirects the boot to your B: drive. (A similar utility is bboot.zip in the same directory at Garbo only.)

If that doesn't work, you can always interchange your A: and B: drives by switching ribbon cables and changing the setup in your BIOS. From an article posted 27 Jan 1993 on another newsgroup:

 [begin quotation]
   Take  the  "ribbon" connector, as you call it, and  switch
   them.  To double-check, start at the end of the cable that
   connects to the motherboard or floppy controller.   Follow
   the  cable until you get to the first connector.   Connect
   this to the drive you want to be B:.
  
   After this, there should be a few lines on the cable  that
   get flipped left to right.  (On most cables, they just cut
   the lines and physically reverse them.  It should be quite
   obvious from looking at the cable.)  Anyway, the connector
   after  the pins get flipped right to left is the connector
   for your a: drive.
 [end quotation]

<Q:05.03> - Which real and virtual disk drives are valid?

Date: 5 Feb 2002 22:03:03 -0500

Use INT 21 AH=29 (parse filename). Point DS:SI at a null- terminated ASCII string that contains the drive letter and a colon, point ES:DI at a 37-byte dummy FCB buffer, and call INT 21 AX=2900. On return, AL is FF if the drive is invalid, something else if the drive is valid. RAM disks and SUBSTed drives are considered valid.

You can detect whether the drive is ASSIGNed by using INT 2F AX=0601. To check whether the drive is SUBSTed, use INT 21 AX=4409; or use INT 21 AH=52 to test for both JOIN and SUBST. See Ralf Brown's interrupt list: <Q:02.03> [What and where is Ralf Brown's interrupt list?].

Unfortunately, the b: drive is considered valid even on a single-diskette system. You can check that special case by interrogating the BIOS equipment byte at 0040:0010. Bits 7- 6 contain the one less than the number of diskette drives, so if those bits are zero you know that b: is an invalid drive even though function 29 says it's valid.

Following is some code originally posted by Doug Dougherty to test valid drives (treating SUBSTed and JOINed drives as valid), with my fix for the b: special case, tested in Borland C++ 4.5 (in the large model):

   #include <dos.h>
   #include <stdio.h>
   
   void drvlist(void)
   {
     char s[3] = "A:", fcb_buff[37];
     int valid;

     for( ;  *s<='Z';  (*s)++)
     {
       _SI = (unsigned) s;
       _DI = (unsigned) fcb_buff;
       _ES = _DS;
       _AX = 0x2900;

       geninterrupt(0x21);
       valid = _AL != 0xFF;

       if (*s == 'B'  &&  valid)
       {
         char far *equipbyte = (char far *)0x00400010UL;
         valid = (*equipbyte & (3 << 6)) != 0;
       }

       printf("Drive '%s' is %sa valid drive.\n", s, valid ? "" : "not ");
     }
   }

This code was translated to MSC 7.0 and tested it in small model:

   #include <dos.h>
   #include <stdio.h>

   void drvlist(void)
   {
     char s[3] = "A:", fcb_buff[37], *buff=fcb_buff;
     int valid;

     for (   ;  *s<='Z';  (*s)++)
     {
       __asm mov si,s      __asm mov di,buff
       __asm mov ax,ds     __asm mov es,ax
       __asm mov ax,0x2900 __asm int 21h
       __asm xor ah,ah     __asm mov valid,ax

       valid = (valid != 0xFF);

       if (*s == 'B'  &&  valid)
       {
         char far *equipbyte = (char far *)0x00400010UL;
         valid = (*equipbyte & (3 << 6)) != 0;
       }

       printf("Drive '%s' is %sa valid drive.\n", s, valid ? "" : "not");
     }
   }

<Q:05.04> - How can I make my single floppy drive both a: and b:?

Date: 5 Feb 2002 22:03:03 -0500

Under any DOS since DOS 2.0, you can put the following command into your AUTOEXEC.BAT file:

assign b=a

Then, when you type "dir b:" you'll no longer get the annoying prompt to insert diskette B (and the even more annoying prompt to insert A the next time you type "dir a:").

You may be wondering why anybody would want to do this. Suppose you use two different machines, maybe one at home and one at work. One of them has only a 3.5" diskette drive; the other machine has two drives, and b: is the 3.5" one. You're bound to type "dir b:" on the first one, and get the nuisance message:

Insert diskette for drive B: and press any key when ready.

But if you assign drive b: to point to a:, you avoid this problem.

Caution: there are a few commands, such as DISKCOPY, that will not work right on ASSIGNed or SUBSTed drives. See the DOS manual for the full list. Before typing one of those commands, be sure to turn off the mapping by typing "assign" without arguments.

The DOS 5.0 manual says that ASSIGN is obsolete, and recommends the equivalent form of SUBST: "subst b: a:\". Unfortunately, if this command is executed when a: doesn't hold a diskette, the command fails. ASSIGN doesn't have this problem, so under DOS 5.0 you should disregard that particular bit of advice in the manual.


<Q:05.05> - How can I disable access to a drive?

Date: 8 Feb 2002 19:53:23 -0500

Reader Eric DeVolder writes that he has made available a program to do this. I haven't tried it, but it's downloadable from <http://www.simtel.net/pub/pd/44403.html>

Reader Igor Karp reports that MS-DOS version 5.0 and greater provides two interrupts to do this.

   --------D-215F07-----------------------------
   INT 21 - DOS 5+ - ENABLE DRIVE
      AX = 5F07h
      DL = drive number (0=A:)
   Return: CF clear if successful
      CF set on error
      AX = error code (0Fh) (see #0885 at AH=59h)
   Notes:   simply  sets the "valid" bit in the  drive's  CDS
   this function is not supported by Novell DOS 7
   See Also: AH=52h,AX=5F08h"DOS"

   --------D-215F08-----------------------------
   INT 21 - DOS 5+ - DISABLE DRIVE
      AX = 5F08h
      DL = drive number (0=A:)
   Return: CF clear if successful
      CF set on error
      AX = error code (0Fh) (see #0885 at AH=59h)
   Notes:   simply clears the "valid" bit in the drive's  CDS
   this function is not supported by Novell DOS 7

<Q:05.06> - How can a batch file test existence of a directory?

Date: 8 Feb 2002 19:54:04 -0500

The standard way, which in fact is documented in the DOS manual, is:

if exist d:\path\nul goto found

Unfortunately, this is not entirely reliable. I found it failed in Pathworks (a/k/a PCSA, DEC's network that connects PCs and VAXes), or on a MARS box that uses an OEM version of MS-DOS 5.0. Readers have reported that it gave the wrong answer on Novell networks, on DR-DOS, and in a DOS window under OS/2. By "failed" I mean that it "found" a directory that didn't exist, or failed to find one that did exist, or both. (It has been reported that IBM fixed the OS/2 bug in version 2.11 of OS/2.) As a legacy from earlier versions of DOS it always succeeds if the path is DEV.

There appears to be no foolproof way to use pure batch commands to test for existence of a directory. The real solution is to write a program, which returns a value that your batch program can then test with an "if errorlevel". Reader Duncan Murdoch kindly posted the following Turbo Pascal version:

   program existdir;
   { Confirms  the  existence of a directory  given  on  the
     command  line.  Returns errorlevel 2 on error,  1  if  not
     found, 0 if found. }
  
   uses dos;
  
   var
     s : searchrec;
  
   begin
     if paramcount <> 1 then
       begin
         writeln('Syntax:  EXISTDIR directory');
         halt(2);
       end
     else
       begin
         findfirst(paramstr(1),Directory,S);
         while (Doserror = 0) and ((Directory and S.Attr) =  0) do
           findnext(S);
  
        if Doserror <> 0 then
          begin
            Writeln('Directory not found.');
            halt(1);
          end
        else
          begin
            Writeln('Directory found.');
            halt(0);
          end;
       end;
   end.

Timo Salmi also has a Turbo Pascal version in his Turbo Pascal FAQ, which is downloadable as <ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip>


<Q:05.07> - Why won't my C program open a file with a path?

Date: 5 Feb 2002 22:03:03 -0500

You've probably got something like the following code:

   char *filename = "c:\foo\bar\mumble.dat";
   FILE *fptr;
   /*.*/
   fptr = fopen(filename, "r");

The problem is that \f is a form feed, \b is a backspace, and \m is m. Whenever you want a backslash in a string constant in C, you must use two backslashes:

char *filename = "c:\\foo\\bar\\mumble.dat";

This is a feature of every C compiler, because Dennis Ritchie designed C this way. It's a problem only on MS-DOS systems, because only DOS (and Atari ST/TT running TOS) uses the backslash in directory paths. But even in DOS this backslash convention applies _only_ to string constants in your source code. For file and keyboard input at run time, \ is just a normal character, so users running your program would type in file specs the same way as in DOS commands, with single \ characters.

Another possibility is to code all paths in source programs with / rather than \ characters:

char *filename = "c:/foo/bar/mumble.dat";

Ralf Brown writes, "All versions of the DOS kernel accept either forward or backslashes as directory separators. I tend to use this form more frequently than backslashes since it is easier to type and read." This applies to DOS function calls (and therefore to calls to the file library of every programming language), but not to DOS commands.


<Q:05.08> - How can I redirect printer output to a file?

Date: 8 Feb 2002 19:55:31 -0500

Recommended: PRN2FILE from PC Magazine, downloadable as: <http://www.simtel.net/pub/pd/49066.html>

PRN2FILE contains ASM source code. PC Magazine has given copies away as part of its utilities disks, so you may already have a copy.

The directories mentioned above have lots of other utilities to redirect printer output.


<Q:05.09> - How can I redirect the output of a batch file?

Date: 7 Feb 2002 14:48:46 -0500

Assuming the batch file is called batch.bat, to send its output (stdout) to another file, just invoke COMMAND.COM as a secondary command processor:

command /c batch parameters_if_any >outfile

Timo Salmi's notes on this and other batch tricks are downloadable from: <ftp://garbo.uwasa.fi/pc/link/tsbat.zip>

A reader of comp.os.msdos.programmer has created a utility that can capture batch file output. It can be found at: <http://www.simtel.net/pub/dl/11141.shtml>


<Q:05.10> - How can I redirect stderr?

Date: 8 Feb 2002 19:58:21 -0500

Use freopen(..., stderr) and then execute the desired command via system(). There are downloadable versions of programs to do this.

This file includes TP4 source and an executable: <http://www.simtel.net/pub/pd/50430.html>

A C example is downloadable as: <http://www.simtel.net/pub/pd/41772.html>

I compiled it with MSC 7.0, and it works fine with one exception: Contrary to the included comments, redirected output starts writing at the beginning of the output file rather than appending. That is easily solved by adding "fseek(stderr, 0L, SEEK_END);" after the freopen() call for stderr.

A reader comp.os.msdos.programer has created a utilitiy that can capture console output. It can be found at: <http://www.simtel.net/pub/dl/11141.shtml>


<Q:05.11> - How can my program open more files than DOS's limit of 20?

Date: 5 Feb 2002 22:03:03 -0500

This is a summary of an article Ralf Brown posted on 8 August 1992, with some additions from a Microsoft tech note and information from Chin Huang.)

DOS imposes some limits. Once you overcome those, which is pretty easy, you may have to take additional measures to overcome the limitations built into your compiler's run- time library.

1) Limitations imposed by DOS:

There are separate limits on files and file handles. For example, DOS opens three files but five file handles: CON (stdin, stdout, and stderr), AUX (stdaux), and PRN (stdprn).

The limit in FILES= in CONFIG.SYS is a system-wide limit on files opened by all programs (including the three that DOS opens and any opened by TSRs); each process has a limit of 20 handles (including the five that DOS opens). Example: CONFIG.SYS has FILES=40. Then program #1 will be able to open 15 file handles. Assuming that the program actually does open 15 handles pointing to 15 different files, other programs could still open a total of 22 files (40-3-15 = 22), though no one program could open more than 15 file handles. If you're running DOS 3.3 or later, you can increase the per-process limit of 20 file handles by a call to INT 21 AH=67, Set Handle Count. Your program is still limited by the system-wide limit on open files, so you may also need to increase the FILES= value in your CONFIG.SYS file (and reboot). The run-time library that you're using may have a fixed-size table of file handles, so you may also need to get source code for the module that contains the table, increase the table size, and recompile it.

2) Limitations in Microsoft C run-time library:

In Microsoft C the run-time library limits you to 20 file handles. To change this, you must be aware of two limits:

File handles used with _open(), _read(), etc.: Edit _NFILE_ in CRT0DAT.ASM.

Stream files used with fopen(), fread(), etc.: Edit _NFILE_ in _FILE.C for DOS or FILE.ASM for Windows/QuickWin. This must not exceed the value of _NFILE_ in CRT0DAT.ASM. (QuickWin uses the constant _WFILE_ in CRT0DAT.ASM and WFILE.ASM for the maximum number of child text windows.)

After changing the limits, recompile using CSTARTUP.BAT. Microsoft recommends that you first read README.TXT in the same directory.

3) Limitations in Borland C++ run-time library:

(Reader Chin Huang provided this information on 12 Sep 1993.) To increase the open file limit for a program you compile with Borland C++ 3.1, edit the file _NFILE.H in the include directory and change the _NFILE_ value. Compile and link the modules FILES.C and FILES2.C from the lib directory into your program.


<Q:05.12> - How can I read, create, change, or delete the volume label?

Date: 5 Feb 2002 22:03:03 -0500

In DOS 5.0 (and possibly in 4.0 as well), there are actually two volume labels: the LABEL command reports only the first but changes both of them.

The rest of this answer assumes that by "volume label" you mean the traditional one, the one that DIR and VOL display. Though it's a directory entry in the root directory, you can't change it using the newer DOS file-access functions (INT 21 AH=3C, 41, 43); instead, use the old FCB-oriented directory functions. Specifically, you need to allocate a 64-byte buffer and a 41- byte extended FCB (file control block). Call INT 21 AH=1A to find out whether there is a volume label. If there is, AL returns 0 and you can change the label using DOS function 17 or delete it using DOS function 13. If there's no volume label, function 1A will return FF and you can create a label via function 16. Important points to notice are that ? wildcards are allowed but * are not; the volume label must be space filled not null terminated.

The following MSC 7.0 code worked for me in DOS 5.0; the functions it uses have been around since DOS 2.0. The function parameter is 0 for the current disk, 1 for a:, 2 for b:, etc. It doesn't matter what your current directory is; these functions always search the root directory for volume labels. (I didn't try to change the volume label of any networked drives.)

   // Requires DOS.H, STDIO.H, STRING.H

   void vollabel(unsigned char drivenum)
   {
     static  unsigned  char  extfcb[41],  dta[64],  status,  *newlabel;
     int chars_got = 0;

     #define DOS(buff,func) __asm { __asm mov dx,offset buff  \
       __asm mov ax,seg buff  __asm push ds  __asm mov ds,ax  \
       __asm  mov  ah,func   __asm int  21h   __asm  pop  ds  \
       __asm mov status,al }

     #define getlabel(buff,prompt) newlabel = buff;  \
       memset(newlabel,' ',11);  printf(prompt);   \
       scanf("%11[^\n]%n", newlabel, &chars_got);  \
       if (chars_got < 11) newlabel[chars_got] = ' ';

     // Set up the 64-byte transfer area used by function 1A.
     DOS(dta, 1Ah)
  
     //  Set  up  an extended FCB and search for the  volume label.
     memset(extfcb, 0, sizeof extfcb);
     extfcb[0] = 0xFF;           // denotes extended FCB
     extfcb[6] = 8;              // volume-label attribute  bit
     extfcb[7] = drivenum;       // 1=A,2=B,...; 0=current drive
  
     memset(&extfcb[8], '?', 11);// wildcard *.*
     DOS(extfcb,11h)
     if(status == 0)
     {                           // DTA has volume  label's FCB
       printf("volume label is %11.11s\n", &dta[8]);
       getlabel(&dta[0x18], "new label (\"delete\" to delete): ");
      
       if(chars_got==0)
         printf("label not changed\n");
       else if (strncmp(newlabel,"delete  ",11) == 0)
       {
         DOS(dta,13h)
         printf(status ? "label failed\n":"label deleted\n");
       }
       else
       {                  // user wants to change label
         DOS(dta,17h)
         printf(status ? "label failed\n" : "label changed\n");
       }
     }
     else
     {                      // no volume label was found
       printf("disk has no volume label.\n");
       getlabel(&extfcb[8], "new label (<Enter>  for  none): ");

       if (chars_got > 0)
       {
         DOS(extfcb,16h)
         printf(status ? "label failed\n" : "label created\n");
       }
     }
   } // end function vollabel

<Q:05.13> - How can I get the disk serial number?

Date: 5 Feb 2002 22:03:03 -0500

If the disk was formatted by DOS 4.0 or later, use INT 21: AX=6900 gets the serial number; AX=6901 sets it. (The disk serial number doesn't exist if the disk was formatted with an earlier version of DOS, or with some third-party formatters.) See Ralf Brown's interrupt list (<Q:02.03> [What and where is Ralf Brown's interrupt list?]), or PC Magazine July 1992 (xi:13) page 496, for details.

INT 21 AH=69 also gets and sets the volume label in the boot record, which is not necessarily the same as "the" volume label displayed by the DIR, VOL, and LABEL commands. For that volume label, see <Q:05.12> [How can I read, create, change, or delete the volume label?]


<Q:05.14> - What's the format of .OBJ, .EXE., .COM files?

Date: 5 Feb 2002 22:03:03 -0500

Please see <Q:03.06> [What's the format of an .OBJ file?]; <Q:03.07> [What's the format of an .EXE header?]; and <Q:03.08> [What's the difference between .COM and .EXE formats?]


<Q:05.15> - How can I flush the software disk cache?

Date: 5 Feb 2002 22:03:03 -0500

Please see <Q:08.01> [How can a program reboot my PC?] (Trust me.)


<Q:05.16> - How can I see if a drive is a RAM drive?

Date: 5 Feb 2002 22:03:03 -0500

Use INT 21 AX=4409h. See Ralph Brown's interrupt list (<Q:02.03> [What and where is Ralf Brown's interrupt list?]) for more information.


<Q:05.17> - How can I determine a hard drive's manufacturer?

Date: 5 Feb 2002 22:03:03 -0500

Information about the hard drive's manufacturer is retrieved by using the ATA and ATAPI specifications. Please see "<Q:05.18> [Where can I find information about the ATA/ATAPI specification?]


<Q:05.18> - Where can I find information about the ATA/ATAPI specification?

Date: 5 Feb 2002 22:03:03 -0500

The AT Attachment (ATA) standard is maintained by T13, a Technical Committee for the National Committee on Information Technology Standards which as accredited by ANSI. Their web site can be found at <http://www.t13.org>. At that web site the ATA and ATAPI specifications are availible in PDF form.


<Q:05.19> - How can I copy files to or from filenames containing date information?

Date: 7 Feb 2002 14:26:15 -0500

You can use the NOWMINUS program. This program creates environment variables containing date and time information. See <Q:08.15> [How can I place date and time information into environment variables?] for more information.

Here is an example of using NOWMINUS to rename the files thisweek.* to {lastweeksdate}.*:

        NOWMINUS d7 z7 f1 j0 vLASTWEEK
        ren thisweek.* %LASTWEEK%.*
        set LASTWEEK=

Section 6. Serial ports (COM ports)

Date: 5 Feb 2002 22:03:03 -0500

This section provides information about how to access the serial ports. In the future I will be working on adding information about using MS-DOS for networking and internet access.


<Q:06.01> - How do I set my machine up to use COM3 and COM4?

Date: 5 Feb 2002 22:03:03 -0500

Unless your machine is fairly old, it's probably already set up. After installing the board that contains the extra COM port(s), check the I/O addresses in word 0040:0004 or 0040:0006. (In DEBUG, type "D 40:4 L4" and remember that every word is displayed low byte first, so if you see "03 56" the word is 5603.) If those addresses are nonzero, your PC is ready to use the ports and you don't need the rest of this answer.

If the I/O address words in the 0040 segment are zero after you've installed the I/O board, you need some code to store these values into the BIOS data segment:

   0040:0004  word  I/O address of COM3
   0040:0006  word  I/O address of COM4
   0040:0011   byte  (bits  3-1):  number  of  serial   ports installed

The documentation with your I/O board should tell you the port addresses. When you know the proper port addresses, you can add code to your program to store them and the number of serial ports into the BIOS data area before you open communications. Or you can use DEBUG to create a little program to include in your AUTOEXEC.BAT file, using this script:

   n  SET_ADDR.COM     <--- or a different name  ending in .COM
   a 100
   mov  AX,0040
   mov  DS,AX
   mov  wo [0004],aaaa <--- replace aaaa with COM3 address or 0
   mov  wo [0006],ffff <--- replace ffff with COM4 address or 0
   and  by [0011],f1
   or    by [0011],8   <--- use number of serial ports times 2
   mov  AH,0
   int  21
                         <--- this line must be blank
   rCX
   1f
   rBX
   0
   w
   q

<Q:06.02> - How do I find the I/O address of a COM port?

Date: 8 Feb 2002 20:00:09 -0500

Look in the four words beginning at 0040:0000 for COM1 through COM4. (The DEBUG command "D 40:0 L8" will do this. Remember that words are stored and displayed low byte first, so a word value of 03F8 will be displayed as F8 03.) If the value is zero, that COM port is not installed (or you've got an old BIOS; see <Q:06.01> [How do I set my machine up to use COM3 and COM4?]). If the value is nonzero, it is the I/O address of the transmit/receive register for the COM port.

Each COM port occupies eight consecutive I/O addresses (though many chips use only the first seven).

Here's some C code to find the I/O address:

   unsigned ptSel(unsigned comport)
   {
     unsigned io_addr;
  
     if (comport >= 1  &&  comport <= 4)
     {
       unsigned far *com_addr = (unsigned far *)0x00400000UL;
       io_addr = com_addr[comport-1];
     }
     else
       io_addr = 0;
  
     return io_addr;
   }

You might also want to explore Port Finder, downloadable as: <http://www.simtel.net/pub/pd/47138.html>

I haven't tried it myself, but a posted article reviewed it very favorably and said it also lets you swap ports around.


<Q:06.03> - But aren't the COM ports always at I/O addresses 3F8, 2F8, 3E8, and 2E8?

Date: 5 Feb 2002 22:03:03 -0500

The first two are usually right (though not always); the last two are different on many machines.


<Q:06.04> - How do I configure a COM port and use it to transmit data?

Date: 8 Feb 2002 20:03:52 -0500

Do you want actual code, or do you want books that explain what's going on?

1) Source code

First, check your compiler's run-time library. Many compilers offer functions similar to Microsoft C's _bios_serialcom() or Borland's bioscom(), which may meet your needs.

Second, check for downloadable resources at SimTel and Garbo. At SimTel, <http://www.simtel.net/pub/pd/41750.html> (March 1993) is described as "Asynchronous communications library for C"; Garbo has a whole <ftp://garbo.uwasa.fi/pc/comm> directory. Also, an extended example is in Borland's TechFax TI445, downloadable as part of: <http://www.simtel.net/pub/pd/50843.html> <ftp://garbo.uwasa.fi/pc/c-lang/bchelp10.zip>

Though written by Borland, much of it is applicable to other forms of C, and it should give you ideas for other programming languages.

Third, SNIPPETS (see <Q:02.08> [What and where is SNIPPETS?]) contains a sample interrupt-driven serial communications library.

2) Reference books

Highly recommended: Joe Campbell's {C Programmer's Guide to Serial Communications}, ISBN 0-672-22584-0. He gives complete details on how serial ports work, along with complete programs for doing polled or interrupt-driver I/O. The book is quite thick, and none of it looks like filler.

If Campbell's book is overkill for you, you'll find a good short description of serial I/O in {DOS 5: A Developer's Guide}, ISBN 1-55851-177-6, by Al Williams.

Finally, a reader has recommended {Serial Communications Programming in C/C++} by Mark Goodwin (ISBN 1-55828-198-3), with source code in the book and on disk. Topics include the basics, various methods of serial communications on the PC (with consideration of high-speed modems), ANSI screen interface, file transfer protocols (Xmodem and Ymodem), etc. There is code in C, and that code is extended into a C++ class for those who use C++. There are also subroutines in Assembly.

3) Downloadable information files

A "Serial Port FAQ" is occasionally posted to this newsgroup, and is downloadable as multiple files:

<ftp://ftp.phil.uni-sb.de/pub/people/chris/>

This directory contains a series of files beginning with Serial_Port.


Section 7. Other hardware questions and problems

Date: 5 Feb 2002 22:03:03 -0500

The subject says it all. Bv)


<Q:07.01> - Which 80x86 CPU is running my program?

Date: 5 Feb 2002 22:03:03 -0500

SNIPPETS (see <Q:02.08> [What and where is SNIPPETS?]) contains C-callable x86 assembly language code for determining the type of CPU in CPUCHECK.ASM.


<Q:07.02> - How can a C program send control codes to my printer?

Date: 5 Feb 2002 22:03:03 -0500

If you just fprintf(stdprn, ...), C will translate some of your control codes. The way around this is to reopen the printer in binary mode: prn = fopen("PRN", "wb");

You must use a different file handle because stdprn isn't an lvalue. By the way, in DOS 5.0 a colon must not follow PRN or LPT1.

There's one special case, Ctrl-Z (ASCII 26), the DOS end-of- file character. If you try to send an ASCII 26 to your printer, DOS simply ignores it. To get around this, you need to reset the printer from "cooked" to "raw" mode. Microsoft C users must use INT 21 AH=44, "get/set device information". Turbo C and Borland C++ users can use ioctl to accomplish the same thing: ioctl(fileno(prn), 1, ioctl(fileno(prn),0) & 0xFF | 0x20, 0);

An alternative approach is simply to write the printer output into a disk file, then copy the file to the printer with the /B switch.

A third approach is to bypass DOS functions entirely and use the BIOS printer functions at INT 17. If you also fprintf(stdprn,...) in the same program, you'll need to use fflush() to synchronize fprintf()'s buffered output with the BIOS's unbuffered.

By the way, if you've opened the printer in binary mode from a C program, remember that outgoing \n won't be translated to carriage return/line feed. Depending on your printer, you may need to send explicit \n\r sequences.


<Q:07.03> - How can I redirect printer output?

Date: 5 Feb 2002 22:03:03 -0500

Please see <Q:05.08> [How can I redirect printer output to a file?]


<Q:07.04> - Which video adapter is installed?

Date: 5 Feb 2002 22:03:03 -0500

The technique below should work if your BIOS is not too old. It uses three functions from INT 10, the BIOS video interrupt. (If you're using a Borland language, you may not have to do this the hard way. Look for a function called DetectGraph or something similar.)

Set AX=1200, BL=32 and call INT 10. If AL returns 12, you have a VGA. If not, set AH=12, BL=10 and call INT 10 again. If BL returns 0,1,2,3, you have an EGA with 64,128,192,256K memory. If not, set AH=0F and call INT 10 a third time. If AL is 7, you have an MDA (original monochrome adapter) or Hercules; if not, you have a CGA.

This worked when tested with a VGA, but I had no other adapter types to test it with.


<Q:07.05> - How do I switch to 43- or 50-line mode?

Date: 8 Feb 2002 20:05:49 -0500

The following file contains .COM utilities and .ASM source code: <http://www.simtel.net/pub/pd/49657.html>


<Q:07.06> - How can I find the Microsoft mouse position and button status?

Date: 8 Feb 2002 20:07:14 -0500

Use INT 33 AX=3, described in Ralf Brown's interrupt list (<Q:02.03> [What and where is Ralf Brown's interrupt list?]).

The Windows manual says that the Logitech mouse is compatible with the Microsoft one, so the interrupt will probably work the same.

Also, many files are downloadable from: <http://www.simtel.net/pub/msdos/mouse/>


<Q:07.07> - How can I access a specific address in the PC's memory?

Date: 7 Feb 2002 14:50:10 -0500

First check the library that came with your compiler. Many vendors have some variant of peek and poke functions. For example:

By the way, it's not useful to talk about "portable" ways to do this. Any operation that is tied to a specific memory address is not likely to work on another kind of machine.


<Q:07.08> - How can I read or write my PC's CMOS memory?

Date: 8 Feb 2002 20:11:37 -0500

There are a great many public-domain utilities that do this. The following files can be downloaded from <http://www.simtel.net/pub/msdos/sysutl/>:

   cmos14.zip    5965  920817  Saves/restores CMOS to/from file
   cmoser11.zip 28323  910721  386/286 enhanced  CMOS setup program
   cmosram.zip  76096  920214  Save AT/386/486 CMOS data to file and restore
   rom2.zip     15692  900131  Save AT and 386 CMOS data to file and restore
   setup21.zip  18172  880613  Setup program which modifies CMOS RAM
   viewcmos.zip 11068  900225  Display contents of AT CMOS RAM, w/C source

A program to check and display CMOS memory (but not write to it) is downloadable as part of: <http://www.simtel.net/pub/pd/50522.html> <ftp://garbo.uwasa.fi/pc/ts/tsutle23.zip>

Good reports of CMOS299.ZIP have been posted, but it no longer appears to be online.

Stan Brown, the former list maintainer, reports that he personally tested CMOSRAM and that it worked fine. It contains an excellent (and witty) .DOC file that explains the hardware involved and gives specific recommendations for preventing disaster or recovering from it. It's $5 shareware.

Robert Jourdain's {Programmer's Problem Solver for the IBM PC, XT, and AT} has code for accessing the CMOS RAM, according to an article posted in this newsgroup.


<Q:07.09> - How can I access memory beyond 640K?

Date: 8 Feb 2002 20:50:34 -0500

Part of this involves switching into protected mode.

An article entitled "How DOS Programs Can Use Over 1MB of RAM" appeared in PC Magazine 29 June 1993 (xii: 12) pages 302-304.

I also suggest John English's XMS classes for C++: <ftp://ftp.brighton.ac.uk/pub/je/xms200je.zip>.

See also <Q:07.10> [How can I use the protected mode?]


<Q:07.10> - How can I use the protected mode?

Date: 8 Feb 2002 20:15:02 -0500

DJ Delorie has produced DJGPP, a protected mode programming environment which supports a port of the GNU C/C++/Ada. For more informat see <Q:10.04> [What and where is DJGPP?]

Users of Borland C++ Version 4.xx could once purchase the Borland PowerPack for DOS Version 1.00; however, it appears that its sell has been discontinued. This package includes Borland C++ 4.02 Service Update, 16-bit DPMI libraries and extenders, 32-bit DPMI libraries and extenders, TurboVision 2.0 (16-bit DOS, 16-bit DPMI, 32-bit DPMI), SuperVGA BGI Drivers (16-bit DOS, 16-bit DPMI, 32- bit DPMI).

A reader of comp.os.msdos.programmer has the following updateu about the PowerPack and additional information about DPMI programming:

 [begin quote]
   About protected-mode programming (Question 7.10), Borland no longer
   supports the PowerPack (from what I've read in this newsgroup), so
   I doubt that it can still be purchased. However, the FAQ could 
   mention that the extender is integrated into Borland C++ version 5.x
   (and probably the Builder product as well).
   
   Borland doesn't fully support that either, since the documentation
   isn't very clear about compiling DPMI programs and a stub for the
   executable has to be extracted from one of Borland's executables. The
   necessary steps were posted a long time ago (by someone else), and I
   can provide the information if necessary. Even if the FAQ didn't
   describe those steps, it could at least indicate that version 5.x can
   compile DPMI programs (as well as clarifying the PowerPack info).
 [end quote]

There are more extenders out there. One notable DOS extender is Adam Seychell's DOS32 Version 3.5 beta. It can be found at <http://www.programmersheaven.com/zone5/cat19/1363.htm> Also check out Adam's page at <http://www.geocities.com/SiliconValley/2151/pmode.html>


<Q:07.11> - How can I tell if my program is running on a PS/2-style machine.

Date: 5 Feb 2002 22:03:03 -0500

Use INT 15 AX=C0, described in Ralf Brown's interrupt list (<Q:02.03> [What and where is Ralf Brown's interrupt list?]).


<Q:07.12> - Is there a 80x87 math unit installed?

Date: 5 Feb 2002 22:03:03 -0500

SNIPPETS (see <Q:02.08> [What and where is SNIPPETS?]) contains C-callable assembly code to determine presence of coprocessor in NDPCHECK.ASM.


<Q:07.13> - How can I power off the computer from a batch file?

Date: 6 Feb 2002 20:36:38 -0500

A utility known as ATXOFF.COM is available at <http://www.informatik.fh-muenchen.de/~ifw98223/dostools.htm>. It uses APM 1.2 to power off the system but doesn't attempt to flush the disk cache.


Section 8. Other software questions and problems

Date: 5 Feb 2002 22:03:03 -0500

This section is noteworthy for having the longest code snippets.


<Q:08.01> - How can a program reboot my PC?

Date: 8 Feb 2002 20:16:09 -0500

You can generate a "cold" boot or a "warm" boot. A cold boot is the same as turning the power off and on; a warm boot is the same as Ctrl-Alt-Del and skips the power-on self 'test.

For a warm boot, store the hex value 1234 in the word at 0040:0072. For a cold boot, store 0 in that word. Then, if you want to live dangerously, jump to address FFFF:0000. Here's C code to do it:

   /* WARNING:  data loss possible */
   void bootme(int want_warm)  /* arg 0 = cold boot, 1 = warm */
   {
     void (far* boot)(void) = (void (far*)(void))0xFFFF0000UL;
     
     unsigned far* type = (unsigned far*)0x00400072UL;
     *type = (want_warm ? 0x1234 : 0);
     (*boot)( );
   }

What's wrong with that method? It will boot right away, without closing files, flushing disk caches, etc. If you boot without flushing a write-behind disk cache (if one is running), you could lose data or trash the file allocation table in your hard drive.

There are two methods of signaling the cache to flush its buffers:

(1) Simulate a keyboard Ctrl-Alt-Del in the keystroke translation function of the BIOS (INT 15 AH=4F; but see notes below)

(2) Issue a disk reset (DOS function 0D). Most disk-cache programs hook one or both of those interrupts, so if you use both methods you'll probably be safe.

When user code simulates a Ctrl-Alt-Del, one or more of the programs that have hooked INT 15 AH=4F can ask that the key be ignored by clearing the carry flag. For example, HyperDisk does this when it has started but not finished a cache flush. So if the carry flag comes back cleared, the boot code has to wait a couple of clock ticks and then try again. (None of this matters on older machines whose BIOS can't support 101- or 102-key keyboards; see the discussion of INT 21 AH=4F in <Q:04.07> [What is the SysRq key for?])

C code that tries to signal the disk cache (if any) to flush is given below. Turbo Pascal code by Timo Salmi that does more or less the same job may be found at question 49 (as of this writing) in the Turbo Pascal FAQ in comp.lang.pascal, and is downloadable as file FAQPAS2.TXT, which is part of: <ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip>

Here's C code that reboots after trying to signal the disk cache:

   #include <dos.h>
   
   void bootme(int want_warm)  /* arg 0 = cold boot, 1 = warm  */
   {
     union REGS reg;

     void (far* boot)(void) = (void (far*)(void))0xFFFF0000UL;
     unsigned far* boottype = (unsigned far*)0x00400072UL;
     char     far* shiftstate = (char far*)0x00400017UL;
     unsigned      ticks;
     int           time_to_waste;

     /* Simulate reception of Ctrl-Alt-Del: */
     for (;;)
     {
       *shiftstate |= 0x0C;    /* turn on Ctrl & Alt */
       reg.h.ah = 0x4F;        /* see notes below */
       reg.h.al = 0x53;        /* 0x53 = Del's scan code */
       reg.x.cflag = 1;        /* sentinel for ignoring key  */
       int86(0x15, &reg, &reg);

       /* If carry flag is still set, we've finished. */
       if(reg.x.cflag)
         break;

       /* Else waste some time before trying again: */
       reg.h.ah = 0;
       int86(0x1A, &reg, &reg); /* system time into CX:DX */

       ticks = reg.x.dx;
       for (time_to_waste = 3;  time_to_waste > 0;  )
       {
         reg.h.ah = 0;
         int86(0x1A, &reg, &reg);
         if (ticks != reg.x.dx)
           ticks = reg.x.dx , --time_to_waste;
       }
     }

     /* Issue a DOS disk reset request: */
     reg.h.ah = 0x0D;
     int86(0x21, &reg, &reg);

     /* Set boot type and boot: */
     *boottype = (want_warm ? 0x1234 : 0);
     (*boot)( );
   }

Reader Timo Salmi reported (26 July 1993) that the INT 15 AH=4F call may not work on older PCs (below AT, XT2, XT286), according to Ralf Brown's interrupt list (<Q:02.03> [What and where is Ralf Brown's interrupt list?]).

Reader Roger Fulton reported (1 July 1993) that INT 15 AH=4F hangs even a modern PC "ONLY when ANSI.SYS [is] loaded high using EMM386.EXE. (Other things loaded high with EMM386.EXE were OK; ANSI.SYS loaded high with QEMM386.SYS was OK; ANSI.SYS loaded low with EMM386.EXE installed was OK.)" His solution was to use only the disk reset, INT 21 AH=0D, which does flush SMARTDRV, then wait five seconds in hopes that any other disk-caching software would have time to flush its queue.

Reader Per Bergland reported (10 Sep 1993) that the jump to FFFF:0000 will not work in Windows or other protected-mode programs. (For example, when the above reboot code ran in a DOS session under Windows, a box with "waiting for system shutdown" appeared. The PC hung and had to be reset by cycling power.) His solution, which does a cold boot not a warm boot, is to pulse pin 0 of the 8042 keyboard controller, which is connected to the CPU's "reset" line.

He has tested the following code on various Compaqs, and expects it will work for any AT-class machine; he cautions that you must first flush the disk cache as indicated above.

   cli
   @@WaitOutReady:   ;  Busy-wait until  8042 ready for new command
   in al,64h         ; read 8042 status byte
   test al,00000010b ; this bit indicates input buffer full
   jnz @@WaitOutReady
   mov al,0FEh       ; Pulse "reset" = 8042 pin 0
   out 64h,al        ; The PC will reboot now

<Q:08.02> - How can I time events with finer resolution than the system clock's 55 ms (about 18 ticks a second)?

Date: 8 Feb 2002 20:19:05 -0500

The PC Timing FAQ / Application Note, maintained by Kris Heidenstrom (kheidenstrom@actrix.gen.nz), contains information relating to timing with PC hardware and software. It can be found on SimTel: <http://www.simtel.net/pub/pd/46935.html>

The following files, among others, are downloadable from SimTel in the <http://www.simtel.net/pub/msdos/sysutl/> directory:

   atim.zip       4783  881126  Precision program timing for AT

In the <http://www.simtel.net/pub/msdos/c/> directory:

   millisec.zip   37734  911205  MSC/asm src for millisecond timing
   mschrt3.zip    53708  910605  High-res timer toolbox for MSC 5.1
   msec_12.zip    8484   920320  High-def timer v1.2 (C,ASM)
   ztimer11.zip  77625  920428  Microsecond timer for C, C++, ASM

For Turbo Pascal users, source and object code are downloadable as: <ftp://garbo.uwasa.fi/pc/turbopas/bonus507.zip>

Also see "Q: How is millisecond timing done?" in FAQPAS.TXT, downloadable as part of: <ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip>


<Q:08.03> - How can I find the error level of the previous program?

Date: 8 Feb 2002 20:21:49 -0500

First, which previous program are you talking about? If your current program ran another one, when the child program ends its error level is available to the program that spawned it. Most high-level languages provide a way to do this; for instance, in Turbo Pascal it's Lo(DosExitCode) and the high byte gives the way in which the child terminated. In Microsoft C, the exit code of a synchronous child process is the return value of the spawn- type function that creates the process.

If your language doesn't have a function to return the error code of a child process, you can use INT 21 AH=4D (get return code). By the way, this will tell you the child's exit code and the manner of its ending (normal, Ctrl-C, critical error, or TSR).

It's much trickier if the current program wants to get the error level of the program that ran and finished before this one started. G.A. Theall has published source and compiled code to do this; the code is downloadable as: <http://www.simtel.net/pub/pd/40610.html>

(The code uses undocumented features in DOS 3.3 through 5.0. In the .DOC file Theall says that the values returned under 4DOS or other replacements won't be right.)


<Q:08.04> - How can a program set DOS environment variables?

Date: 8 Feb 2002 20:22:43 -0500

Program functions that read or write "the environment" typically access only the program's copy of it. What this Q really wants to do is to modify the active environment, the one that is affected by SET commands in batch files or at the DOS prompt. You need to do some programming to find the active environment, and that depends on the version of DOS.

A fairly well-written article in PC Magazine 28 Nov 1989 (viii:20), pages 309-314, explains how to find the active environment, and includes Pascal source code. The article hints at how to change the environment, and suggests creating paths longer than 128 characters as one application.

Now as for downloadable source code, there are many possibilities.

Stan Brown, the former list maintainer recommends the following: <ftp://garbo.uwasa.fi/pc/envutil/rbsetnv1.zip>

It includes some utilities to manipulate the environment, with source code in C. A newer program from PC Magazine 22 Dec 1992 (XI: 22) is: <ftp://garbo.uwasa.fi/pc/pcmagvol/vol11n22.zip>

You can also use a call to INT 2E, Pass Command to Interpreter for Execution; see Ralf Brown's interrupt list (<Q:02.03> [What and where is Ralf Brown's interrupt list?]) for details and cautions.

Reader Dr. John Stockton has written a unit for Turbo Pascal known as jrs_envu.pas to facilitate writing to the environment. It is for DOS (not DPMI) mode programs running under DOS to Win98, but not WinNT. It can be downloaded from here: <http://www.merlyn.demon.co.uk/programs/jrs_envu.pas>. For more information, see <http://www.merlyn.demon.co.uk/batprogs.htm>.


<Q:08.05> - How can I change the switch character to - from /?

Date: 5 Feb 2002 22:03:03 -0500

Under DOS 5.0 and above you can not. INT 21 AX=3700, get switch character, always returns a '/' (hex 2F). But the DOS commands don't even call that function: they simply hard code '/' as the switch character.

Some history: DOS used to let you change the switch character by using SWITCHAR= in CONFIG.SYS or by calling DOS function 3701. DOS commands and other programs called DOS function 3700 to find out the switch character. If you changed the switch character to '-' (the usual choice), you could then type "dir c:/c700 -p" rather than "dir c:\c700 /p". Under DOS 4.0, the DOS commands ignored the switch character but functions 3700 and 3701 still worked and could be used by other programs. Under DOS 5.0, even those functions no longer work, though all DOS functions still accept '/' or '\' in file specs.

You can reactivate the functions to get and set switchar by using programs like SLASH.ZIP or the sample TSR called SWITCHAR in amisl091.zip (see <Q:08.06> [How can I write a TSR (terminate-stay-resident utility)?]). DOS commands will still use the slash, but non-DOS programs that call DOS function 3700 will use your desired switch character. (DOS replacements like 4DOS may honor the switch character for internal commands.)

Some readers may wonder why this is even an issue. Making '-' the switch character frees up the front slash to separate names in the path part of a file spec. This is easier for the ten-fingered to type, and it's one less difference to remember for commuters between DOS and Unix. The switch character is the only issue, since all the INT 21 functions accept '/' or '\' to separate directory names.


<Q:08.06> - How can I write a TSR (terminate-stay-resident utility)?

Date: 8 Feb 2002 20:31:08 -0500

There are books, and there's code to download.

First, the books:

Next, the code. Some of it is companion code to published articles, which are also listed below:

Finally, there are (were?) commercial products, of which TesSeRact (for C-language TSRs) is one of the best known.


<Q:08.07> - Why does my interr