home *** CD-ROM | disk | FTP | other *** search
- Xref: sparky comp.os.msdos.programmer:8811 news.answers:2627
- Newsgroups: comp.os.msdos.programmer,news.answers
- Path: sparky!uunet!zaphod.mps.ohio-state.edu!magnus.acs.ohio-state.edu!usenet.ins.cwru.edu!ncoast!brown
- From: brown@NCoast.ORG (Stan Brown)
- Subject: comp.os.msdos.programmer FAQ part 3 of 4
- Expires: Sat, 10 Oct 1992 00:25:46 GMT
- Organization: Oak Road Systems, Cleveland Ohio USA
- Date: Wed, 26 Aug 1992 00:25:46 GMT
- Approved: news-answers-request@MIT.Edu
- Message-ID: <msdos-faq.920825.3@NCoast.ORG>
- Followup-To: comp.os.msdos.programmer
- References: <msdos-faq.920825.1@NCoast.ORG>
- Supersedes: <1992Jul25.161111.7026@NCoast.ORG>
- Lines: 690
-
- Archive-name: msdos-programmer-faq/part3
- Last-modified: 22 August 1992
-
-
- (continued from part 2) (no warranty on the code or information)
-
- If the posting date is more than six weeks in the past, see instructions
- in the last part of this list for how to get an updated copy.
-
- Copyright (C) 1992 Stan Brown, Oak Road Systems
-
-
- section 4. Disks and files
- ===========================
-
- Q401. What drive was the PC booted from?
-
- Under DOS 4.0 or later, load 3305 hex into AX; do an INT 21. DL is
- returned with an integer indicating the boot drive (1=A:, etc.).
-
- Q402. Which real and virtual disk drives are valid?
-
- Use INT 21 function 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, set AX to 2900h, and do
- an INT 21. 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.
-
- 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, with my
- fix for the b: special case, tested in Borland C++ 2.0 small model:
-
- #include <dos.h>
- void drvlist(void) {
- char *s = "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 ");
- }
- }
-
- Q403. How can I make my single floppy drive both a: and b:?
-
- Under any DOS since DOS 2.0, you can put the command
-
- assign b=a
-
- into your AUTOEXEC.BAT file. 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.
-
- Q404. Why won't my C program open a file with a path?
-
- You've probably got something like the following code:
-
- char *filename = "c:\foo\bar\mumble.dat";
- . . . 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, I'm told) 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 of your program would type in file specs at run time the same
- way as in DOS commands, with single backslashes.
-
- Another possibility is to code all paths in source programs with /
- rather than \ characters:
-
- char *filename = "c:/foo/bar/mumble.dat";
-
- Ralf Brown writes that "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.
-
- Q405. How can I redirect printer output to a file?
-
- My personal favorite utility for this purpose is PRN2FILE from {PC
- Magazine}, available from Simtel as PD1:<MSDOS.PRINTER>PRN2FILE.ARC,
- 14451 bytes including .ASM source, or from garbo as prn2file.zip in
- /pc/printer. ({PC Magazine} has given copies away as part of its
- utilities disks, so you may already have a copy.)
-
- The other listings that I found in Simtel's index as of mid-January
- 1992 are, from newest to oldest:
-
- PD1:<MSDOS.PRINTER>
- PRIND25.ZIP 17008 900420 Redirect printer to file/screen/printer
- RPRN.ARC 2631 900120 Redirect printer output to disk file
- PRINDIR.ARC 6836 891210 Redirects printer output to disk file
- VPRNT301.ARC 15259 891030 Printer redirection to disk file, v3.01
- PSTASH20.ARC 2830 890216 Redirects lpt output to file
- PRNDSK.ARC 27172 871224 Redirect Printer Output To Disk
- LPTX600.ARC 31596 870319 Redirect printer output to a file
-
- At garbo, in the /pc/printer directory, you can find
-
- lamneth.zip PrintScreen redirect to a disk file v09-Mar-88
- lptx.zip Redirect printer output v6.0
- lptx700.zip Redirect printer output v7.0 (not update, just
- different)
- prindir8.zip Printer and port Redirection from Allen Creations
-
- Q406. What's the format of an .EXE header?
-
- See pages 349-350 of {PC Magazine}'s June 30, 1992 issue (xi:12) for
- the old and new formats. For a more detailed layout, look under INT
- 21 function 4B in Ralf Brown's interrupt list.
-
- Q407. How can my program open more files than DOS's limit of 20?
-
- (This is a summary of an article Ralf Brown posted on 8 August 1992.)
-
- 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 function 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.
-
-
- section 5. Serial ports (COM ports)
- ===================================
-
- Q501. How do I set my machine up to use COM3 and COM4?
-
- 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
-
- Q502. How do I find the I/O address of a COM port?
-
- 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 the preceding Q). 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 only seven are used by many chips).
-
- Here's some C code (tested under MSC 5.0 and BC++ 2.0) 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;
- }
-
- Q503. But aren't the COM ports always at I/O addresses 3F8, 2F8, 3E8,
- and 2E8?
-
- The first two are usually right (though not always); the last two
- are different on many machines.
-
- Q504. How do I configure a COM port and use it to transmit data?
-
- After hearing several recommendations, I looked at Joe Campbell's {C
- Programmer's Guide to Serial Communications}, ISBN 0-672-22584-0,
- and agree that it is excellent. 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.
-
-
- section 6. Other hardware questions and problems
- ================================================
-
- Q601. Which 80x86 CPU is running my program?
-
- According to an article posted by Michael Davidson, Intel's approved
- code for distinguishing among 8086, 80286, 80386, and 80486 and for
- detecting the presence of an 80287 or 80387 is published in the
- Intel's 486SX processor manual (order number 240950-001). You can
- download David Kirschbaum's improved version of this from Simtel as
- PD1:<MSDOS.SYSUTL>CPUID593.ZIP.
-
- According to an article posted by its author, WCPU041.ZIP knows the
- differences between DX and SX varieties of 386 and 486 chips, and
- can also detect a math coprocessor. It's in PD1:<MSDOS.SYSUTL> at
- Simtel.
-
- Q602. 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, PRN or LPT1 must not be followed by a colon in DOS 5.0.
-
- 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 function 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.
-
- Q603. How can I redirect printer output to a file?
-
- Please see section 4, "Disks and files", for the answer.
-
- Q604. Which video adapter is installed?
-
- This technique should work if your BIOS is not too old. It uses
- three functions from INT 10, the BIOS video interrupt.
-
- Set AH=12h, AL=0, BL=32h; INT 10h. If AL is 12h, you have a VGA.
- If not, set AH=12h, BL=10h; INT 10h. If BL is 0,1,2,3, you have an
- EGA with 64,128,192,256K memory. If not, set AH=0Fh; INT 10h. If
- AL is 7, you have an MDA (original monochrome adapter) or Hercules;
- if not, you have a CGA.
-
- I've tested this for my VGA and got the right answer; but I can't
- test it for the other equipment types. Please let me know by email
- at brown@ncoast.org if your results vary.
-
- Q605. How do I switch to 43- or 50-line mode?
-
- Download PD1:<MSDOS.SCREEN>VIDMODE.ZIP, 4412 bytes, from Simtel or
- one of the mirror sites. It contains .COM utilities and .ASM
- source code.
-
- Q606. How can I find the Microsoft mouse position and button status?
-
- Use INT 33 function 3, described in Ralf Brown's interrupt list.
-
- The Windows manual says that the Logitech mouse is compatible with
- the Microsoft one, so I assume the interrupt will work the same.
-
- Also, see the directory PD1:<MSDOS.MOUSE> at Simtel.
-
- Q607. 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; in Turbo Pascal use
- the pseudo-arrays Mem, MemW, and MemL. As an alternative, you can
- construct a far pointer: use Ptr in Turbo Pascal, MK_FP in the
- Turbo C family, and FP_OFF and FP_SEG in Microsoft C.
-
- Caution: Turbo C and Turbo C++ also have FP_OFF and FP_SEG macros,
- but they can't be used to construct a pointer. In Borland C++ those
- macros work the same as in Microsoft C, but MK_FP is easier to use.
-
- 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.
-
- Q608. How can I read or write my PC's CMOS memory?
-
- There are a great many public-domain utilities that do this. These
- were available for download from Simtel as of 31 March 1992:
-
- PD1:<MSDOS.AT>
- CMOS13.ZIP 7258 920330 Backup and restore damaged CMOS memory
- 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.ARC 20497 900131 Save AT and 386 CMOS data to file and restore
- SETUP21.ARC 24888 880613 Setup program which modifies CMOS RAM
- VIEWCMOS.ARC 15374 900225 Display contents of AT CMOS RAM, w/C source
-
- At garbo, /pc/ts/tsutle17.zip contains a CMOS program to check and
- display CMOS memory, but not to write to it.
-
- I have heard good reports of CMOS299.ZIP, available in the pc.dir
- directory of cantva.canterbury.ac.nz [132.181.30.3].
-
- Of the above, my only experience is with CMOSRAM, which seems to
- work 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.
-
-
- section 7. Other software questions and problems
- ================================================
-
- Q701. How can a program reboot my PC?
-
- 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
- (tested under MSC 5.0 and BC++ 2.0):
-
- 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 even trash 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 function 4F), and (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 function 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 cluck
- ticks and then try again. (None of this matters on older machines
- whose BIOS can't support 101- or 102-key keyboards; see "What is the
- SysRq key for?" in section 3, "Keyboard".)
-
- Here's C code that tries to signal the disk cache (if any) to
- flush (compiles under MSC 5.0 and BC++ 2.0):
-
- #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.x.ax = 0x4F53; /* 0x53 = Del's scan code */
- reg.x.cflag = 1; /* sentinel for ignoring key */
- int86(0x15, ®, ®);
- /* 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, ®, ®);/* system time into CX:DX */
- ticks = reg.x.dx;
- for (time_to_waste = 3; time_to_waste > 0; ) {
- reg.h.ah = 0;
- int86(0x1A, ®, ®);
- if (ticks != reg.x.dx)
- ticks = reg.x.dx , --time_to_waste;
- }
- }
- /* Issue a DOS disk reset request: */
- reg.h.ah = 0x0D;
- int86(0x21, ®, ®);
- /* Set boot type and boot: */
- *boottype = (want_warm ? 0x1234 : 0);
- (*boot)( );
- }
-
- Q702. How can I time events with finer resolution than the system
- clock's 55 ms (about 18 ticks a second)?
-
- The following files can be downloaded from Simtel:
-
- PD1:<MSDOS.AT>
- ATIM.ARC 5946 881126 Precision program timing for AT
-
- PD1:<MSDOS.C>
- MILLISEC.ZIP 37734 911205 MSC/asm src for millisecond res timing
- MSCHRT3.ZIP 53708 910605 High-res timer toolbox for MSC 5.1
- MSEC_12.ZIP 8484 920320 High-def millisec timer v1.2 (C,ASM)
-
- PD1:<MSDOS.TURBO-C>
- TCHRT3.ZIP 53436 910606 High-res timer toolbox for Turbo C 2.0
- TCTIMER.ARC 20087 891030 High-res timing of events for Turbo C
- ZTIMER11.ZIP 74477 920428 Time C/C++/Pascal/ASM-microsec accuracy
-
- PD1:<MSDOS.TURBOPAS>
- BONUS507.ARC 150435 900205 [Turbo Pascal source: high-res timing]
-
- Pascal users can download source code in /pc/turbopas/bonus507.zip
- at garbo.
-
- Q703. How can I find the error level of the previous program?
-
- 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-typoe
- 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 function 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; you
- can download it from Simtel as PD1:<MSDOS.BATUTL>ERRLVL12.ZIP. (The
- code uses undocumented features in DOS 3.3 through 5.0. Theall says
- in the .DOC file that the values returned under 4DOS or other
- replacements won't be right.)
-
- Q704. How can a program set DOS environment variables?
-
- Program functions that read or write "the environment" typically
- access only the program's copy of the environment. 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 programming varies for different versions of DOS.
-
- A fairly well-written article in {PC Magazine} volume 8 number 20
- (1989 Nov 28), 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.
-
- In searching Simtel for source code, I found many possibilities. I
- liked PD1:<MSDOS.SYSUTL>RBSETNV1.ZIP of the ones I looked at (not
- all of them). It includes some utilities to manipulate the environ-
- ment, with source code in C.
-
- Q705. How can I change the switch character to - from /?
-
- Under DOS 5.0, you can't.
-
- Earlier DOS versions let you change the "switch character" that
- introduces command-line options by using SWITCHAR= in CONFIG.SYS or
- by calling a DOS function. Then you would type "DIR -P" rather than
- "DIR /P". Not only did this make DOS look more like Unix (oooo!),
- but you could type / rather than \ in directory names.
-
- Under DOS 5.0, all this has changed. The DOS 5.0 commands hard-code
- / as the switch character. I've seen several reports that programs
- to change the switch character stopped working under DOS 5.0.
-
- Programs like SLASH.ZIP claim to change the switch character from /
- to something else. They do, but the change affects only the
- third-party programs that query the switch character via INT 21
- function 3700. DOS internal commands and most MS-DOS external
- commands will still use /. (DOS replacements like 4DOS may honor
- the switch character for internal commands.)
-
- Q706. Why does my interrupt function behave strangely?
-
- 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 the next Q for general help before you have 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.
- 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 and functions 34, 5D06, 5D0B of INT 21. 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..) and standard I/O
- functions (scanf, printf) call DOS functions and also depend on
- setups that they can't get at from inside an interrupt. 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. Since
- that interrupt expects to be called 18.2 times a second, 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. For a good writeup, download INTSHARE (from ni.funet.fi
- in pub/msdos/simtel20/info or from Simtel in PD1:<MSDOS.INFO>).
-
- Did you forget to restore all registers at the end of your routine?
-
- 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.)
-
- Q707. How can I write a TSR (terminate-stay-resident) utility?
-
- Several books can help you with this.
-
- - 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 5 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.
-
- - For C programmers, there's a chapter in Herbert Schildt's {The Art
- of C: Elegant Programming Solutions}. I haven't seen the book,
- but a posted article recommended it.
-
- You might want to download PD1:<MSDOS.ASMUTL>TEMPLATE.ZIP, 13161
- bytes, from Simtel. It's Douglas Boling's MASM template for a TSR.
- In the same directory, AMISL083.ZIP, 45230 bytes, contains Ralf
- Brown's assembly-language implementation of the Alternate Multiplex
- Interrupt Specification, with utilities in C.
-
- Finally, there are commercial products, of which TesSeRact (for
- C-language TSRs) is one of the best known.
-
- Q708. 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.
-
- Q709. What can I use to manage versions of software?
-
- In PD1:<MSDOS.PGMUTL> at Simtel you'll find RCS55.ZIP, 388K dated 18
- Dec 1991. I haven't used it myself, but I understand this is a port
- of the Unix RCS utility, but is limited to one-character extensions
- on filenames (no .CPP).
-
- (continued in part 4)
- --
-
- Stan Brown, Oak Road Systems brown@Ncoast.ORG
-