
From jeffrey@carlyle.com Mon May  3 19:22:02 1999
Path: news.nic.utwente.nl!news.cs.utwente.nl!newsfeed.eris.dera.gov.uk!diablo.dera.gov.uk!nntp.primenet.com!newsfeed.enteract.com!cde.net!sipb-server-1.mit.edu!sipb-server-2.mit.edu!senator-bedfellow.mit.edu!faqserv
From: jeffrey@carlyle.com (Jeffrey Carlyle)
Newsgroups: comp.os.msdos.programmer,alt.msdos.programmer,comp.answers,alt.answers,news.answers
Subject: comp.os.msdos.programmer FAQ part 4/5
Supersedes: <msdos-programmer-faq/part4_919862829@rtfm.mit.edu>
Followup-To: comp.os.msdos.programmer
Date: 23 Apr 1999 13:34:32 GMT
Organization: Jeffrey Carlyle World Domination
Lines: 1064
Sender: jeffrey@carlyle.com (Jeffrey Carlyle)
Approved: news-answers-request@MIT.Edu
Expires: 27 May 1999 13:32:46 GMT
Message-ID: <msdos-programmer-faq/part4_924874366@rtfm.mit.edu>
References: <msdos-programmer-faq/part1_924874366@rtfm.mit.edu>
Reply-To: jeffrey@carlyle.com (Jeffrey Carlyle)
NNTP-Posting-Host: penguin-lust.mit.edu
Summary: Frequently asked questions by DOS programmers with tested answers.
X-Last-Updated: 1999/01/05
Originator: faqserv@penguin-lust.MIT.EDU
Xref: news.nic.utwente.nl comp.os.msdos.programmer:80559 alt.msdos.programmer:38294 comp.answers:32022 alt.answers:37090 news.answers:72410

Archive-name: msdos-programmer-faq/part4
Comp-os-msdos-programmer-archive-name: dos-faq-pt4.txt
Posting-frequency: 56 days
Last-modified: 05 Jan 99


------------------------------

Subject: comp.os.msdos.programmer FAQ
Date: Sun, 03 Jan 99 17:46:33 CST
 
 comp.os.msdos.programmer FAQ Version 1999.01

 This is the Frequently Asked Questions list for the newsgroup
 comp.os.msdos.programmer. This message is part 4 of 5 parts.
 
 Copyright  1999  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.
 

------------------------------

Subject: Table of Contents
Date: Sun, 03 Jan 99 17:46:33 CST
 
=============================
TABLE OF CONTENTS
=============================
 
Part 1: <first file>
  Section 1.  General FAQ and Newsgroup Information
    <Q:01.01> - Is MS-DOS dead?
    <Q:01.02> - What is this article for?
    <Q:01.03> - Who has contributed to this article?
    <Q:01.04> - How can I search this article for a particular topic?
    <Q:01.05> - Are the answers guaranteed to be correct and complete?
    <Q:01.06> - What is comp.os.msdos.programmer about?
    <Q:01.07> - Is comp.os.msdos.programmer just for C programmers?
    <Q:01.08> - What is comp.sys.ibm.pc.programmer?
    <Q:01.09> - Is  comp.os.msdos.programmer available as a  mailing list?
    <Q:01.10> - What's this "netiquette"?
    <Q:01.11> - How can I learn more about Usenet?
    <Q:01.12> - What other technical newsgroups should I know about?
    <Q:01.13> - Where are FAQ lists archived?
    <Q:01.14> - Where can I get the latest copy of this FAQ list?
  Section 2.  General Reference
    <Q:02.01> - Are there any good on-line references for PC hardware
                components?
    <Q:02.02> - Are there any good on-line references for PC interrupts?
    <Q:02.03> - What and where is "Ralf Brown's interrupt list"?
    <Q:02.04> - Where can I find lex, yacc, and language grammars?
    <Q:02.05> - What's the best book to learn programming?
    <Q:02.06> - Why won't my code work?
    <Q:02.07> - Are there any good sources of example code?
    <Q:02.08> - What and where is "SNIPPETS"?
    <Q:02.09> - Is the source code MS-DOS available?
    <Q:02.10> - What are my alternatives for MS-DOS compatible OSes?
    <Q:02.11> - What and where is "FreeDOS?"
    <Q:02.12> - What and where is "Caldera DR-DOS"?
Part 2: <second file>
  Section 3.  Compile and Link
    <Q:03.01> - What the heck is "DGROUP > 64K"?
    <Q:03.02> - How  do I fix "automatic data segment exceeds 64K" or "stack
                plus data exceed 64K"?
    <Q:03.03> - Will Borland C code and Microsoft C code link together?
    <Q:03.04> - Why  did  my program bomb  at run time with "floating point
                formats not linked" or "floating point not loaded"?
    <Q:03.05> - How can I change the stack size in Borland's C compilers?
    <Q:03.06> - What's the format of an .OBJ file?
    <Q:03.07> - What's the format of an .EXE header?
    <Q:03.08> - What's  the difference between .COM and .EXE formats?
    <Q:03.09> - How do I create a .COM file?
    <Q:03.10> - Where is EXE2BIN located?
    <Q:03.11> - What does this message mean: "A20 already enabled so test
                is meaning less?"
  Section 4.  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?
    <Q:04.02> - How can I find out whether a character has been typed, without
                waiting for one?
    <Q:04.03> - How can I disable Ctrl-C/Ctrl-Break and/or Ctrl-Alt-Del?
    <Q:04.04> - How can I disable the print screen function?
    <Q:04.05> - How  can  my  program turn NumLock (CapsLock, ScrollLock)
                on or off?
    <Q:04.06> - How can I speed up the keyboard's auto-repeat?
    <Q:04.07> - What is the SysRq key for?
    <Q:04.08> - How can my program tell what kind of keyboard is on the
                system?
    <Q:04.09> - How can I tell if input, output, or stderr has been redirected?
    <Q:04.10> - How can I increase the size of the keyboard buffer?
    <Q:04.11> - How can I stuff characters into the keyboard buffer?
Part 3: <third file>
  Section 5.  Disks and files
    <Q:05.01> - What drive was the PC booted from?
    <Q:05.02> - How can I boot from drive B:?
    <Q:05.03> - Which real and virtual disk drives are valid?
    <Q:05.04> - How can I make my single floppy drive both A: and B:?
    <Q:05.05> - How can I disable access to a drive?
    <Q:05.06> - How can a batch file test existence of a directory?
    <Q:05.07> - Why won't my C program open a file with a path?
    <Q:05.08> - How can I redirect printer output to a file?
    <Q:05.09> - How can I redirect the output of a batch file?
    <Q:05.10> - How can I redirect stderr?
    <Q:05.11> - How can my program open more files than DOS's limit of 20?
    <Q:05.12> - How can I read, create, change, or delete the volume label?
    <Q:05.13> - How can I get the disk serial number?
    <Q:05.14> - What's the format of .OBJ, .EXE, .COM files?
    <Q:05.15> - How can I flush the software disk cache?
    <Q:05.16> - How can I see if a drive is a RAM drive?
  Section 6.  Serial ports (COM ports)
    <Q:06.01> - How do I set my machine up to use COM3 and COM4?
    <Q:06.02> - How do I find the I/O address of a COM port?
    <Q:06.03> - But aren't the COM ports always at I/O addresses 3F8, 2F8,
                3E8, and 2E8?
    <Q:06.04> - How do I configure a COM port and  use  it  to transmit data?
Part 4: <this file>
  Section 7.  Other hardware questions and problems
    <Q:07.01> - Which 80x86 CPU is running my program?
    <Q:07.02> - How can a C program send control codes  to  my printer?
    <Q:07.03> - How can I redirect printer output to a file?
    <Q:07.04> - Which video adapter is installed?
    <Q:07.05> - How do I switch to 43- or 50-line mode?
    <Q:07.06> - How can I find the Microsoft mouse position and button status?
    <Q:07.07> - How can I access a specific address in the PC's memory?
    <Q:07.08> - How can I read or write my PC's CMOS memory?
    <Q:07.09> - How can I access memory beyond 640K?
    <Q:07.10> - How can I use the protected mode?
    <Q:07.11> - How can I tell if my program is running on a
                PS/2-style machine.
    <Q:07.12> - Is there a 80x87 math unit installed?
  Section 8.  Other software questions and problems
    <Q:08.01> - How can a program reboot my PC?
    <Q:08.02> - How can I time events with finer resolution than the system
                clock's 55 ms (about 18 ticks a second)?
    <Q:08.03> - How can I find the error level of the previous program?
    <Q:08.04> - How can a program set DOS environment variables?
    <Q:08.05> - How can I change the switch character to - from /?
    <Q:08.06> - How can I write a TSR (terminate-stay-resident utility)?
    <Q:08.07> - Why does my interrupt function behave strangely?
    <Q:08.08> - How can I write a device driver?
    <Q:08.09> - What can I use to manage versions of software?
    <Q:08.10> - What's this "null pointer assignment" after my C
                program executes?
    <Q:08.11> - How can a batch file tell whether it's being run in a DOS box
                under Windows?
    <Q:08.12> - How can my program tell if it's running under Windows?
    <Q:08.13> - How can a program tell whether ANSI.SYS is installed?
    <Q:08.14> - How do I copyright software that I write?
Part 5: <fifth file>
  Section 9.  Downloading
    <Q:09.01> - What are SimTel, Garbo, and Wustl?
    <Q:09.02> - Can I get archives on CD-ROM?
    <Q:09.03> - Where do I find program <mumble>?
  Section 10. Vendors and products
    <Q:10.01> - How can I contact Borland?
    <Q:10.02> - How can I contact Microsoft?
    <Q:10.03> - What's the current version of UNZIP?
    <Q:10.04> - What is the phone number for a vendor's BBS?
    <Q:10.05> - What is the current version of DJGPP?
    <Q:10.06> - What and where is DJGPP?
    <Q:10.07> - Are there any good shareware/freeware compilers?
    <Q:10.08> - Is the source code to MS-DOS available?
    <Q:10.09> - What is a vendor's web site address?


------------------------------

Subject: Section 7. Other hardware questions and problems
Date: Mon, 04 Jan 99 10:52:41 CST

 The subject says it all. 8-)


------------------------------

Subject: <Q:07.01> - Which 80x86 CPU is running my program?
Date: Sun, 03 Aug 97 20:32:26 CST
 
 SNIPPETS  (see <Q:02.08>) contains C-callable x86  assembly
 language   code  for  determining  the  type  of   CPU   in
 CPUCHECK.ASM.
 

------------------------------

Subject: <Q:07.02> - How can a C program send control codes to my printer?
 
 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.
 

------------------------------

Subject: <Q:07.03> - How can I redirect printer output to a file?
 
 Please see <Q:05.08> "How can I redirect printer output  to
 a file?"
 

------------------------------

Subject: <Q:07.04> - Which video adapter is installed?
 
 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.
 

------------------------------

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

 The  following file contains .COM utilities and .ASM source code:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/vidmode.zip>


------------------------------

Subject: <Q:07.06> - How can I find the Microsoft mouse position and button status?
Date: Sun, 03 Aug 97 20:35:38 CST

 Use  INT 33 AX=3, described in Ralf Brown's interrupt  list <Q:02.03>.
 
 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:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/mouse>


------------------------------

Subject: <Q:07.07> - How can I access a specific address in the PC's memory?
 
 First check the library that came with your compiler.  Many
 vendors  have some variant of peek and poke functions.  For
 example:
  - In Turbo Pascal, use the pseudo-arrays Mem, MemW, and
    MemL.  Be sure you use the correct array for the size of
    data you want to access: byte, word, or double word.
  
  - In  Turbo  C/Borland C, and in  recent  versions  of
    Microsoft C, use MK_FP; in older versions of Microsoft C,
    use FP_OFF and FP_SEG. (Caution:  Turbo C and Turbo C++ also
    have FP_OFF and FP_SEG macros, but they can't be used to
    construct a pointer.)  Be sure to pick the right data type:
    probably "unsigned char far *" if you're planning to access
    bytes and "unsigned short far *" for words.  (The "far"
    isn't needed if your memory model uses 32-bit data pointers,
    but including it does no harm.)
 
 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.
 

------------------------------

Subject: <Q:07.08> - How can I read or write my PC's CMOS memory?
Date: Sun, 03 Aug 97 20:35:57 CST
 
 There  are  a  great many public-domain utilities  that  do
 this.    The   following  files  can  be  downloaded   from
 <ftp://ftp.simtel.net/pub/simtelnet/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:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/sysutl/tsutle22.zip>
   <ftp://garbo.uwasa.fi/pc/ts/tsutle22.zip>
 
 Good reports of CMOS299.ZIP have been posted:
   <ftp://cantva.canterbury.ac.nz/pub/pc/cmos299.zip>
 
 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.
 

------------------------------

Subject: <Q:07.09> - How can I access memory beyond 640K?
Date: Sun, 03 Aug 97 14:24:56 CST

 PC  Magazine 29 June 1993 (xii: 12) pages 302-304 carry  an
 article, "How DOS Programs Can Use Over 1MB of RAM".
 
 See also <Q:07.10> - How can I use the protected mode?
 

------------------------------

Subject: <Q:07.10> - How can I use the protected mode?
Date: Sun, 03 Aug 97 20:43:48 CST

 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.06> - What and where is DJGPP?"
 
 If you are using Borland C++ Version 4.xx, you can purchase
 the  Borland  PowerPack for DOS Version 1.00. 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).
 
 Reader Tim Kannel has the following update 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. It can be found at:
   <ftp://ftp.cdrom.com/pub/demos/code/pmode/dos32b35.zip>
 

------------------------------

Subject: <Q:07.11> - How can I tell if my program is running on a
      PS/2-style machine.
 
 Use  INT 15 AX=C0, described in Ralf Brown's interrupt list <Q:02.03>.
 

------------------------------

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

 SNIPPETS (see <Q: 2.08>) contains C-callable assembly  code
 to determine presence of coprocessor in NDPCHECK.ASM.
 

------------------------------

Subject: Section 8. Other software questions and problems
Date: Mon, 04 Jan 99 10:53:41 CST

 This section is noteworthy for having the longest code snippets.


------------------------------

Subject: <Q:08.01> - How can a program reboot my PC?
Date: Tue, 7 Mar 95 12:00:00 CDT
 
 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 "<Q04.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: (Where ?? is the current version.)
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/info/tsfaqp??.zip>
   <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:
 2.03>).
 
 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


------------------------------

Subject: <Q:08.02> - How can I time events with finer resolution than the
      system clock's 55 ms (about 18 ticks a second)?
Date: Sun, 03 Aug 97 20:47:02 CST

 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:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/info/pctim003.zip>
 
 The  following  files, among others, are downloadable from SimTel
 in the <ftp://ftp.simtel.net/pub/simtelnet/msdos/sysutl> directory:
   atim.zip       4783  881126  Precision program timing for AT
 
 In the <ftp://ftp.simtel.net/pub/simtelnet/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://ftp.simtel.net/pub/simtelnet/msdos/turbopas/bonus507.zip>
   <ftp://garbo.uwasa.fi/pc/turbopas/bonus507.zip>
 
 Also   see   "Q:  How  is  millisecond  timing  done?"   in
 FAQPAS.TXT, downloadable as part of: (Where ?? is the current version.)
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/info/tsfaqp??.zip>
   <ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip>


------------------------------

Subject: <Q:08.03> - How can I find the error level of the previous program?
Date: Sun, 03 Aug 97 20:47:59 CST
 
 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:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/batchutil/errlvl13.zip>
 
 (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.)
 

------------------------------

Subject: <Q:08.04> - How can a program set DOS environment variables?
Date: Sun, 03 Aug 97 20:52:18 CST

 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://ftp.simtel.net/pub/simtelnet/msdos/envutil/rbsetnv1.zip>
   <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://ftp.simtel.net/pub/simtelnet/msdos/batchutl/string25.zip>
   <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> for details and cautions.
 

------------------------------

Subject: <Q:08.05> - How can I change the switch character to - from /?
 
 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: 8.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.


------------------------------

Subject: <Q:08.06> - How can I write a TSR (terminate-stay-resident utility)?
Date: Sun, 03 Aug 97 21:03:10 CST

 There are books, and there's code to download.
 
 First, the books:
   - Ray  Duncan's {Advanced MS-DOS}, ISBN 1-55615-157-8, gives a brief
     checklist intended  for   experienced programmers.  The ISBN is for
     the second edition, through DOS  4;  but  check to see whether the
     DOS 6  version  is available yet.
 
   - {DOS 5: A Developer's Guide} by Al Williams, ISBN  1-55851-177-6,
     goes into a little more detail, 90 pages worth!
 
   - Pascal  programmers might look at {The  Ultimate  DOS
     Programmer's Manual} by John Mueller and Wallace Wang, ISBN
     0-8306-3534-3, for an extended example in mixed Pascal and
     assembler.

  -  For a pure assembler treatment, check Steven Holzner's
     {Advanced Assembly Language}, ISBN 0-13-663014-6.  He has a
     book  with the same title out from Brady Press, but  it's
     about half as long as this one.
   
 
 Next, the code. Some of it is companion code to published articles, which
 are also listed below:
   - John English has created a wonderful C++ class for creating TSRs which
     includes several examples. This one is my personal favorite. It is
     downloadable as:
       <ftp://ftp.simtel.net/pub/simtelnet/msdos/cpluspls/tsr100je.zip>
  
   - The  Alternate  Multiplex  Interrupt  Specification,downloadable as:
       <ftp://ftp.simtel.net/pub/simtelnet/msdos/info/altmpx35.zip>
       <ftp://garbo.uwasa.fi/pc/programming/altmpx35.zip>

   - Ralf Brown's assembly-language implementation of the spec, with utilities
     in C, is downloadable as:
       <ftp://ftp.simtel.net/pub/simtelnet/msdos/asmutil/amisl092.zip>
       <ftp://garbo.uwasa.fi/pc/c-lang/amisl092.zip>
  
   - Douglas  Boling's  MASM  template  for  a  TSR   is downloadable as:
       <ftp://ftp.simtel.net/pub/simtelnet/msdos/asmutl/template.zip>
  
   - A  posted article mentions Boling's "Strategies  and
     Techniques for Writing State-of-the-Art TSRs that Exploit MS-
     DOS 5", Microsoft Systems Journal, Jan-Feb 1992, Volume 7,
     Number 1, pages 41-59, with examples downloadable in:
       <ftp://ftp.simtel.net/pub/simtelnet/msdos/msjournl/msjv7-1.zip>
  
   -  Code  for  Al Stevens's "Writing Terminate-and-Stay-Resident Programs",
      Computer Language, February 1988, pages 37-48 and March 1988, pages 67-76
      is downloadable as:
        <ftp://ftp.simtel.net/pub/simtelnet/msdos/c/tsrc.zip>
  
   - Software examples to accompany Kaare Christian's "Using
     Microsoft C Version 5.1 to Write Terminate-and-Stay-Resident
     Programs", Microsoft Systems Journal, September 1988, Volume
     3, Number 5, pages 47-57 are downloadable as:
       <ftp://ftp.simtel.net/pub/simtelnet/msdos/msjournl/msjv3-5.zip>
 
 Finally, there are commercial products, of which TesSeRact (for C-language TSRs)
 is one of the best known.
 

------------------------------

Subject: <Q:08.07> - Why does my interrupt function behave strangely?
Date: Sun, 03 Aug 97 21:05:15 CST
 
 Interrupt service routines can be tricky, because you  have
 to  do some things differently from "normal" programs.   If
 you  make  a  mistake,  debugging is  a  pain  because  the
 symptoms  may not point at what's wrong.  Your machine  may
 lock  up or behave erratically, or just about anything else
 can  happen.  Here are some things to look for.   (See  <Q:8.06>
 "How  can  I  write  a  TSR (terminate-stay-resident utility)?"
 for  general  techniques  that  may  prevent  a problem.)
 
 First, did you fail to set up the registers at the start of
 your routine?  When your routine begins executing, you  can
 count  on  having CS point to your code segment  and  SS:SP
 point  to some valid stack (of unknown length), and  that's
 it.   In particular, an interrupt service routine must  set
 DS  to  DGROUP  before  accessing  any  data  in  its  data
 segments.  (If you're writing in a high-level language, the
 compiler  may  generate  this code for  you  automatically;
 check  your compiler manual.  For instance, in Borland  and
 Microsoft C, give your function the "interrupt" attribute.)
 
 Did  you remember to turn off stack checking when compiling
 your  interrupt  server and any functions  it  calls?   The
 stack  during the interrupt is not where the stack-checking
 code   expects  it  to  be.  (Caution:   Some   third-party
 libraries  have stack checking compiled in,  so  you  can't
 call them from your interrupt service routine.)
 
 Next, are you calling any DOS functions (INT 21, 25, or 26)
 in  your routine?  DOS is not re-entrant.  This means  that
 if  your interrupt happens to be triggered while the CPU is
 executing a DOS function, calling another DOS function will
 wreak havoc.  (Some DOS functions are fully re-entrant,  as
 noted in Ralf Brown's interrupt list <Q: 2.03>.  Also, your
 program can test, in a way too complicated to present here,
 when  it's  safe to call non-re-entrant DOS functions.  See
 INT  28,  INT  21 AH=34, and INT 21 AX=5D06  or  5D0B;  and
 consult  {Undocumented  DOS}  by  Andrew  Schulman.    Your
 program  must read both the "InDOS flag" and the  "critical
 error flag".)
 
 Is  a  function  in your language library causing  trouble?
 Does  it  depend  on some initializations done  at  program
 startup  that  is  no longer available when  the  interrupt
 executes?  Does it call DOS (see preceding paragraph)?  For
 example,  in  both  Borland and  Microsoft  C  the  memory-
 allocation  functions (malloc(), etc..) call DOS  functions
 and  also  depend  on setups that they can't  get  at  from
 inside an interrupt; so do the standard I/O functions  like
 scanf()  and  printf().  Many other library functions  have
 the same problem, so you can't use them inside an interrupt
 function without special precautions.
 
 Is  your  routine simply taking too long?  This  can  be  a
 problem if you're hooking on to the timer interrupt, INT 1C
 or  INT 8.  That interrupt expects to be called about every
 55  ms,  which  is  18.2  times a second.   Therefore  your
 routine,  plus  any others hooked to the  same  interrupts,
 must  execute  in  less than 55 ms.  If  they  use  even  a
 substantial  fraction of that time, you'll see  significant
 slowdowns of your foreground program.  A good discussion is
 downloadable as:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/info/intshare.zip>
 
 Did  you forget to restore all registers at the end of your
 routine?
 
 Reader,   Morten   Welinder,  notes  that  programmers   of
 interrupt procedures in Borland/Turbo Pascal 7.0 should  be
 aware  that the high words or the 32-bit registers are  not
 saved automatically and that the run-time library may trash
 them  if,  e.g., you use longint operations.  The easy  way
 around this is to do "Test8086 := 0;" before installing the
 interrupt handler.
 
 Did  you  chain improperly to the original interrupt?   You
 need  to restore the stack to the way it was upon entry  to
 your routine, then do a far jump (not call) to the original
 interrupt service routine.
 
 (The   process   is  a  little  different   in   high-level
 languages.)
 

------------------------------

Subject: <Q:08.08> - How can I write a device driver?
 
 Many books answer this in detail.  Among them are {Advanced
 MS-DOS}  and  {DOS 5: A Developer's Guide},  cited  in  the
 preceding  Q.   Michael Tischer's {PC System  Programming},
 ISBN  1-55755-036-0, has an extensive  treatment,  as  does
 Dettman   and   Kyle's  {DOS  Programmer's  Reference:   2d
 Edition},  ISBN  0-88022-458-4.   For  a  really   in-depth
 treatment,  look for a specialized book like  Robert  Lai's
 {Writing MS-DOS Device Drivers}, ISBN 0-201-13185-4.
 

------------------------------

Subject: <Q:08.09> - What can I use to manage versions of software?
Date: Sun, 03 Aug 97 21:07:50 CST

 
 A port of the Unix RCS utility is downloadable as:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/gnuish/rcs55ax.zip> (executable)
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/gnuish/rcs55as.zip> (source)
 
 This  version  of RCS is no longer limited to one-character
 extensions on filenames (for example, .CPP and .BAS are now
 OK).
 
 An  RCS56 is available at a number of archive sites, but it
 appears to be unauthorized.  In response to a query,  Keith
 Petersen, SimTel administrator, said that RCS56 was removed
 from  SimTel  at the author's request because  it  did  not
 contain  source code and thus was in violation of  the  GNU
 copyleft.
 
 As  for commercial software, I posted a question asking for
 readers'  experiences  in  July  1993  and  seven   readers
 responded.   PVCS  from Intersolv (formerly  Polymake)  got
 five  positive  reviews, though several  readers  commented
 that it's expensive; RCS from MKS got one positive and  one
 negative review; Burton TLIB got one negative review;  DRTS
 from ILSI got one positive review.
 

------------------------------

Subject: <Q:08.10> - What's this "null pointer assignment" after my C
      program executes?
 
 Somewhere  in your program, you assigned a value through  a
 pointer  without  first assigning a value to  the  pointer.
 (This might have been something like a strcpy() or memcpy()
 with  a  pointer as its first argument, not necessarily  an
 actual assignment statement.)
 
 Your program may look like it ran correctly, but if you get
 this  message  you  can  be  certain  that  there's  a  bug
 somewhere.
 
 Microsoft and Borland C, as part of their exit code  (after
 a  call  to  exit() or a return from your  main  function),
 check  whether  the  location 0000  in  your  data  segment
 contains a different value from what you started with.   If
 so,  they  infer  that you must have used an  uninitialized
 pointer.  This implies that the message will appear at  the
 end  of  execution of your program regardless of where  the
 error actually occurred.
 
 To  track  down  the problem, you can put exit()  calls  at
 various  spots  in the program and narrow  down  where  the
 uninitialized pointer is being used by seeing  which  added
 exit()  makes the null-pointer message disappear.   Or,  if
 your  program  was  compiled with small or  medium  models,
 which use 16-bit data pointers, tell the debugger to set  a
 watch  at  location  0000 in your data segment.   (If  data
 pointers are 32 bits, as in the compact and large models, a
 null  pointer  will  overwrite  the  interrupt  vectors  at
 0000:0000 and probably lock up your machine.)
 
 Under MSC/C++ 7.0, you can declare the undocumented library
 function:
   extern _cdecl _nullcheck(void);
 
 Sprinkle  calls  to _nullcheck() through  your  program  at
 regular intervals.
 
 Borland's TechFax document TI726 discusses the null pointer
 assignment from a Borland point of view.  It's one of  many
 documents downloadable as part of:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/turbo_c/bchelp10.zip>
   <ftp://garbo.uwasa.fi/pc/turbopas/bchelp10.zip>
   

------------------------------

Subject: <Q:08.11> - How can a batch file tell whether it's
      being run in a DOS box under Windows?
 
 When  Windows  3.0 or 3.1 is running, the  DOS  environment
 will  contain a definition of the string windir,  in  lower
 case.  That's not really useful, however, because the batch
 statement
 
  if "%windir%" == "" ...
 
 will test for an environment variable WINDIR in upper case.
 
 Your only real option is to write a program as suggested by
 the  following question, and have it return a  value  which
 your batch file can test via "if errorlevel".
 

------------------------------

Subject: <Q:08.12> - How can my program tell if it's running under Windows?
Date: Sun, 03 Aug 97 21:12:33 CST

 Execute INT 2F AX=4680.  If AX returns 0, you're in Windows
 real  mode  or  standard  mode (or under  the  DOS  shell).
 Otherwise,  call  INT 2F AX=1600.  If AL returns  something
 other  than  0 or 80, you're in Windows 386 enhanced  mode.
 See PC Magazine 24 Nov 1992 (xi:20) pages 492-493.
 
 For  more information, see PC Magazine 26 May 1992  (xi:10)
 pages 345-346.  A program, WINMODE, is available as part of:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/pcmag/vol11n10.zip>
   <ftp://garbo.uwasa.fi/pc/pcmagvol/vol11n10.zip>
 
 PC  Magazine  29  March 1994 (xiii: 6) pages  312  and  320
 published a new program, WINVER.  This would be in:
   <ftp://ftp.simtel.net/pub/simtelnet/msdos/pcmag/vol13n06.zip>
   <ftp://garbo.uwasa.fi/pc/pcmagvol/vol13n06.zip>


------------------------------

Subject: <Q: 8.13> - How can a program tell whether ANSI.SYS is installed?

 
 In DOS 4.0 and above, call INT 2F AX=1A00.  If the value FF
 is  returned  in  AL,  ANSI.SYS  is  installed.   For  more
 information, see Ralf Brown's interrupt list (<Q:02.03>).
 

------------------------------

Subject: <Q: 8.14> - How do I copyright software that I write?
 
 You  can  download  a very comprehensive  answer  from  the
 Internet. Terry Carroll posts a six-part Copyright  FAQ  to
 misc.legal, news.answers and other groups.  A short  answer
 follows, not based on that article.
 
 Disclaimer:  I  am  not a lawyer, and  this  is  not  legal
 advice.   Also, there are very likely to be differences  in
 copyright law among nations.  No matter where you live,  if
 significant  money may be involved, get legal advice.   The
 following is adapted (and greatly condensed) from chapter 4
 of  the  Chicago Manual of Style (13th edition, ISBN 0-226-
 10390-0).
 
 In  the U.S. (at least), when you write something, you  own
 the  copyright.   (The  exception  that  matters  most   to
 programmers is "works made for hire", i.e., code you  write
 because  your employer or client pays you to.  A  contract,
 agreed in advance, can vest the copyright in the programmer
 even  if  an  employee;  otherwise the  employer  owns  the
 copyright.)  You don't have to register the work  with  the
 Copyright  Office unless (until) the copyright is infringed
 and  you  intend to bring suit; however, it  is  easier  to
 recover  damages  in  court if you did  register  the  work
 within three months of publication.
 
 From paragraph 4.16 of the Chicago Manual:
  
  "...  the [copyright] notice consists of three parts:  (1)
  the  symbol  [C-in-a-circle] (preferred  because  it  also
  suits   the   requirements  of  the  Universal   Copyright
  Convention),  the  word 'Copyright', or  the  abbreviation
  'Copr.',  (2) the year of first publication, and  (3)  the
  name of the copyright owner.  Most publishers also add the
  phrase  'All  rights  reserved' because  it  affords  some
  protection in Central and South American countries...."
 
 Surprise:  "(C)"  is legally not the same  as  the  C-in-a-
 circle,  so  those of us who are ASCII-bound must  use  the
 word or the abbreviation.


------------------------------

Subject: End of Part 4

 This is the end of part 4 of The comp.os.msdos.programmer FAQ Version 1999.01.
 
 This text is copyright 1999 by Jeffrey Carlyle. All rights reserved. Please
 see the top of this article for additional copyright information.

// Jeffrey Coleman Carlyle <jeffrey@carlyle.com> <www.jeffrey.carlyle.com>
// Computer Engineering major at the University of Evansville in Indiana.
// Bowling Green, Kentucky, USA; Creator of Windows Restart 98 and
// StratoSetup; comp.os.msdos.programmer FAQ maintainer; Ruler or Earth!


