1227 lines
57 KiB
Groff
1227 lines
57 KiB
Groff
Newsgroups: comp.os.msdos.programmer,comp.answers,news.answers
|
|
Subject: comp.os.msdos.programmer FAQ part 3 of 4
|
|
Expires: +27 days
|
|
Followup-To: comp.os.msdos.programmer
|
|
Distribution: world
|
|
Message-ID: <msdos-faq.9317.3@NCoast.ORG>
|
|
References: <msdos-faq.9317.2@NCoast.ORG>
|
|
Supersedes: <msdos-faq.9316.3@NCoast.ORG>
|
|
Approved: news-answers-request@MIT.Edu
|
|
Keywords:
|
|
|
|
Archive-name: msdos-programmer-faq/part3
|
|
Last-modified: 24 Sep 1993
|
|
|
|
|
|
(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 part 4 of this list for how to get an updated copy.
|
|
|
|
Copyright (C) 1993 Stan Brown, Oak Road Systems. All rights reserved.
|
|
|
|
|
|
section 4. Disks and files
|
|
===========================
|
|
|
|
Subject: 401. 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.).
|
|
|
|
Subject: 402. How can I boot from drive b:?
|
|
|
|
(rev: 9 Aug 1993) Downloadable shareware:
|
|
pd1:<msdos.dskutl>boot_b.zip from Simtel
|
|
/pc/bootutil/boot_b.zip from Garbo.
|
|
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:
|
|
|
|
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.
|
|
|
|
Subject: 403. Which real and virtual disk drives are valid?
|
|
|
|
(rev: 15 Aug 1993) 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.
|
|
|
|
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 function 52 to test for both JOIN and SUBST. See 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 (without regard to SUBST and JOIN), with SB's fix for
|
|
the b: special case, tested in Borland C++ 2.0 (in the 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 ");
|
|
}
|
|
}
|
|
|
|
SB translated this to MSC 7.0 and tested it in small model:
|
|
|
|
#include <dos.h>
|
|
#include <stdio.h>
|
|
void drvlist(void) {
|
|
char *s = "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 ");
|
|
}
|
|
}
|
|
|
|
Subject: 404. 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, so under DOS 5.0 you
|
|
should disregard that particular bit of advice in the manual.
|
|
|
|
Subject: 405. How can I disable access to a drive?
|
|
|
|
(new: 15 Aug 1993) Reader Eric DeVolder writes that he has made
|
|
available a program to do this. It's downloadable from Simtel as
|
|
pd1:<msdos.dskutl>rmdriv20.zip from Simtel
|
|
/pc/sysutil/rmdriv20.zip at Garbo.
|
|
(existence verified; files not tested by SB)
|
|
|
|
Subject: 406. How can a batch file test existence of a directory?
|
|
|
|
(new: 28 Aug 1993) 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. SB 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 failed on Novell networks or on DR-DOS.
|
|
|
|
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
|
|
/pc/ts/tsfaqp15.zip at Garbo
|
|
pd1:<msdos.info>tsfaqp15.zip at Simtel.
|
|
|
|
Subject: 407. 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) 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.
|
|
|
|
Subject: 408. How can I redirect printer output to a file?
|
|
|
|
(rev: 16 Aug 1993) Recommended: PRN2FILE from {PC Magazine},
|
|
downloadable as:
|
|
pd1:<msdos.printer>prn2file.zip at Simtel
|
|
/pc/printer/prn2file.zip at Garbo.
|
|
{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.
|
|
|
|
Subject: 409. How can I redirect the output of a batch file?
|
|
|
|
(new: 12 June 1993) 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:
|
|
pd1:<msdos.batutl>tsbat43.zip at Simtel
|
|
/pc/ts/tsbat43.zip at Garbo.
|
|
|
|
Subject: 410. How can I redirect stderr?
|
|
|
|
(new: 15 Aug 1993) Use freopen(..., stderr) and then execute the
|
|
desired command via system( ). There are downloadable versions of
|
|
programs to do this. Recommended by SB:
|
|
pd1:<msdos.sysutl>rdstderr.zip from Simtel.
|
|
Source code (in Turbo Pascal 4.0) and executable are included.
|
|
|
|
A C example is downloadable as
|
|
pd1:<msdos.c>redirect.c from Simtel.
|
|
SB 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.
|
|
|
|
Subject: 411. How can my program open more files than DOS's limit of 20?
|
|
|
|
(rev: 12 Sep 1993) This is a summary of an article Ralf Brown posted
|
|
on 8 August 1992, with some additions from a Microsoft tech note.)
|
|
|
|
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 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.
|
|
|
|
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.
|
|
|
|
Subject: 412. How can I read, create, change, or delete the volume
|
|
label?
|
|
|
|
In DOS 5.0 (and possibly in 4.0 as well), there are actually two
|
|
volume labels: one, the traditional one, is an entry in the root
|
|
directory of the disk; and the other is in the boot record along
|
|
with the serial number (see next Q). The DIR and VOL commands
|
|
report the traditional label; the LABEL command reports the
|
|
traditional one but changes both of them.
|
|
|
|
In DOS 4.0 and later, use INT 21 function 69 to access the boot
|
|
record's serial number and volume label together; see the next Q.
|
|
|
|
Assume 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 (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 SB 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, etc.; 0=current drive
|
|
memset(&extfcb[8], '?', 11); // wildcard *.*
|
|
DOS(extfcb,11h)
|
|
if (status == 0) { // DTA contains 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
|
|
|
|
Subject: 413. How can I get the disk serial number?
|
|
|
|
Use INT 21. AX=6900 gets the serial number; AX=6901 sets it. See
|
|
Ralf Brown's interrupt list, or page 496 of {PC Magazine} July 1992,
|
|
for details.
|
|
|
|
This function also gets and sets the volume label, but it's the
|
|
volume label in the boot record, not the volume label that a DIR
|
|
command displays. See the preceding Q.
|
|
|
|
Subject: 414. What's the format of .OBJ, .EXE., .COM files?
|
|
|
|
Please see section 2, "Compile and link".
|
|
|
|
Subject: 415. How can I flush the software disk cache?
|
|
|
|
Please see "How can a program reboot my PC?" in section 7, "Other
|
|
software questions and problems".
|
|
|
|
section 5. Serial ports (COM ports)
|
|
===================================
|
|
|
|
Subject: 501. 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
|
|
|
|
Subject: 502. How do I find the I/O address of a COM port?
|
|
|
|
(rev: 15 Aug 1993) 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 the first
|
|
seven are used by many chips).
|
|
|
|
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
|
|
|
|
pd1:<msdos.sysutl>pf271.zip at Simtel
|
|
/pub/msdos/utilities/sysutl/pf271.zip at nic.funet.fi
|
|
|
|
I (SB) haven't tried it myself, but a posted article reviewed it
|
|
very favorably and said it also lets you swap ports around.
|
|
|
|
Subject: 503. 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.
|
|
|
|
Subject: 504. How do I configure a COM port and use it to transmit data?
|
|
|
|
(rev: 17 Sep 1993) 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, pd1:<msdos.c>pcl4c34.zip (March 1993) is described as
|
|
"Asynchronous communications library for C"; Garbo has a whole
|
|
/pc/comm directory. Also, an extended example is in Borland's
|
|
TechFax TI445, downloadable as part of
|
|
pd1:<msdos.turbo-c>bchelp10.zip at Simtel
|
|
/pc/turbopas/bchelp10.zip at Garbo.
|
|
Though written by Borland, much of it is applicable to other forms
|
|
of C, and it should give you ideas for other programming languages.
|
|
|
|
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 1558281983), 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. You
|
|
can get a copy by ftp from pfsparc02.phil15.uni-sb.de. Look for
|
|
file names *Serial* in directory /pub/E-Technik/afd . (The archive
|
|
administrator warns that the ftp address may change, sometime in
|
|
the future, to etcip1.ee.uni-sb.de .) North American users should
|
|
access rtfm.mit.edu, directory /pub/usenet/comp.os.msdos.programmer,
|
|
file names T_S_P*_3.
|
|
|
|
section 6. Other hardware questions and problems
|
|
================================================
|
|
|
|
Subject: 601. Which 80x86 CPU is running my program?
|
|
|
|
(rev: 16 Aug 1993) 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 Intel's 486SX processor manual (order
|
|
number 240950-001). David Kirschbaum's improved version of this is
|
|
downloadable as
|
|
pd1:<msdos.sysutl>cpuid593.zip from Simtel
|
|
/pc/sysinfo/cpuid593.zip from Garbo.
|
|
|
|
According to an article posted by its author, WCPU knows the
|
|
differences between DX and SX varieties of 386 and 486 chips, and
|
|
can detect a math coprocessor and a Pentium. It's downloadable as
|
|
pd1:<msdos.sysinfo>wcpu050.zip at Simtel
|
|
/pc/sysinfo/wcpu050.zip at Garbo.
|
|
|
|
Subject: 602. 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.
|
|
|
|
Subject: 603. How can I redirect printer output to a file?
|
|
|
|
Please see section 4, "Disks and files", for the answer.
|
|
|
|
Subject: 604. 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 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.
|
|
|
|
This worked when tested with a VGA, but SB had no other adapter
|
|
types to test it with.
|
|
|
|
Subject: 605. How do I switch to 43- or 50-line mode?
|
|
|
|
pd1:<msdos.screen>vidmode.zip, downloadable from Simtel, contains
|
|
.COM utilities and .ASM source code.
|
|
|
|
Subject: 606. 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 the interrupt will probably work the same.
|
|
|
|
Also, many files are downloadable from pd1:<msdos.mouse> at Simtel.
|
|
|
|
Subject: 607. 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.
|
|
|
|
Subject: 608. How can I read or write my PC's CMOS memory?
|
|
|
|
(rev: 24 Sep 1993) There are a great many public-domain utilities
|
|
that do this. These are downloadable from Simtel:
|
|
|
|
pd1:<msdos.at>
|
|
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
|
|
/pc/ts/tsutle22.zip at Garbo
|
|
pd1:<msdos.sysutl>tsutle22.zip at Simtel.
|
|
|
|
Good reports of CMOS299.ZIP, available in the pc.dir directory of
|
|
cantva.canterbury.ac.nz [132.181.30.3], have been posted.
|
|
|
|
Of the above, SB's 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.
|
|
|
|
Subject: 609. How can I access memory beyond 640K?
|
|
|
|
(rev: 14 Sep 1993) This is a legitimate FAQ, in that it is frequently
|
|
asked. But there is no single agreed-upon answer. Please see the
|
|
separate article called "How to access memory above 640K" in
|
|
comp.os.msdos.programmer and in faqp*.zip at Simtel and Garbo.
|
|
|
|
The 29 June 1993 issue (xii:12) of {PC Magazine} carries an article,
|
|
"How DOS Programs Can Use Over 1MB of RAM" on pages 302-304.
|
|
|
|
Subject: 610. Where can I find a list of 80x86 opcodes?
|
|
|
|
(new: 2 May 1993) It's part of a rather long file, the 8 Dec 1992
|
|
edition of the Info-IBMPC Digest (V92 #185), downloadable as
|
|
pd2:<archives.ibmpc>9212.1-txt at Simtel. (Note: pd2, not
|
|
pd1.) Opcodes for the 8086 through 80386 are listed.
|
|
|
|
section 7. Other software questions and problems
|
|
================================================
|
|
|
|
Subject: 701. How can a program reboot my PC?
|
|
|
|
(rev: 11 Sep 1993) 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 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; but see notes below), 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 clock
|
|
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".)
|
|
|
|
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 in
|
|
FAQPAS2.TXT in
|
|
/pc/ts/tsfaqp15.zip at Garbo
|
|
pd1:<msdos.info>tsfaqp15.zip at Simtel.
|
|
|
|
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, ®, ®);
|
|
/* 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)( );
|
|
}
|
|
|
|
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.
|
|
|
|
Reader Roger Fulton reported (1 July 1993) that INT 15 AH=4F call
|
|
above 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 function 0D, which does flush SMARTDRV,
|
|
then wait five seconds in hopes that any other disk-caching
|
|
software would have time to flush its queue.
|
|
|
|
If you have a more bulletproof solution, please send it to the
|
|
editor.
|
|
|
|
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 is ready for new command}
|
|
in al,64h { read 8042 status byte}
|
|
test al,00000010b { Bit 1 of status indicates input buffer full }
|
|
jnz @@WaitOutReady
|
|
mov al,0FEh { Pulse "reset" = 8042 pin 0 }
|
|
out 64h,al
|
|
{ The PC will reboot now }
|
|
|
|
Subject: 702. How can I time events with finer resolution than the
|
|
system clock's 55 ms (about 18 ticks a second)?
|
|
|
|
(rev: 28 Aug 1993) The following files, among others, are
|
|
downloadable from Simtel:
|
|
|
|
pd1:<msdos.at>
|
|
atim.zip 4783 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)
|
|
ztimer11.zip 77625 920428 Microsecond timer for C, C++, ASM
|
|
(also at Garbo as /pc/c/ztimer11.zip)
|
|
|
|
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
|
|
(same as /pc/c/tctimer.zoo at Garbo; both are version 1.0)
|
|
|
|
For Turbo Pascal users, source and object code are downloadable in
|
|
pd1:<msdos.turbopas>bonus507.zip at Simtel
|
|
/pc/turbopas/bonus507.zip at Garbo.
|
|
Also see "Q: How is millisecond timing done?" in FAQPAS.TXT,
|
|
downloadable as
|
|
/pc/ts/tsfaqp15.zip at Garbo
|
|
pd1:<msdos.info>tsfaqp15.zip at Simtel.
|
|
|
|
Subject: 703. How can I find the error level of the previous program?
|
|
|
|
(rev: 16 Aug 1993) 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 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; the
|
|
code is downloadable as
|
|
pd1:<msdos.batutl>errlvl13.zip at Simtel
|
|
/pc/batchutil/errlvl12.zip (an older version) at Garbo.
|
|
(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.)
|
|
|
|
Subject: 704. How can a program set DOS environment variables?
|
|
|
|
(rev: 13 June 1993) 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.
|
|
SB looked at some of these, and liked
|
|
pd1:<msdos.envutil>rbsetnv1.zip at Simtel
|
|
/pc/envutil/rbsetnv1.zip at Garbo
|
|
the best. It includes some utilities to manipulate the environment,
|
|
with source code in C. A newer program is
|
|
pd1:<msdos.batutl>strings2.zip at Simtel
|
|
part of /pc/pcmag/vol11n22.zip at Garbo,
|
|
which is the code from {PC Magazine} 22 Dec 1992 (xi:22).
|
|
|
|
You can also use a call to INT 2E, Pass Command to Interpreter for
|
|
Execution; see Ralf Brown's interrupt list for details and cautions.
|
|
|
|
Subject: 705. How can I change the switch character to - from /?
|
|
|
|
Under DOS 5.0, you can't -- not completely, anyway. INT 21 function
|
|
3700, get switch character, always returns a '/' (hex 2F) -- and the
|
|
DOS commands don't even call that function, but 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 "How can I write a TSR?", below.) DOS commands
|
|
will still use the slash, but non-DOS programs that call DOS func-
|
|
tion 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: 706. Why does my interrupt function behave strangely?
|
|
|
|
(rev: 24 Sep 1993) 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; 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..) 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. A good discussion is downloadable as
|
|
pub/msdos/SIMTEL20-mirror/info/intshare.zip at ni.funet.fi
|
|
pd1:<msdos.info>intshare.zip at Simtel.
|
|
|
|
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.)
|
|
|
|
Subject: 707. How can I write a TSR (terminate-stay-resident utility)?
|
|
|
|
(rev: 20 June 1993) 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 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.
|
|
|
|
Next, the code. Some of it is companion code to published articles,
|
|
which are also listed below:
|
|
|
|
- The Alternate Multiplex Interrupt Specification, downloadable as
|
|
pd1:<msdos.info>altmpx35.zip at Simtel
|
|
/pc/programming/altmpx35.zip at Garbo
|
|
/afs/cs/user/ralf/pub/altmpx35.zip at cs.cmu.edu
|
|
|
|
- Ralf Brown's assembly-language implementation of the spec, with
|
|
utilities in C, downloadable as
|
|
pd1:<msdos.asmutl>amisl091.zip at Simtel
|
|
/pc/c/amisl091.zip at Garbo
|
|
/afs/cs/user/ralf/pub/amisl091.zip at cs.cmu.edu
|
|
|
|
- Douglas Boling's MASM template for a TSR is downloadable as
|
|
pd1:<msdos.asmutl>template.zip at Simtel.
|
|
|
|
- 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
|
|
pd1:<msdos.msjournal>msjv7-1.zip at Simtel
|
|
|
|
- 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
|
|
pd1:<msdos.c>tsrc.zip at Simtel
|
|
|
|
- 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
|
|
pd1:<msdos.msjournal>msjv3-5.arc at Simtel
|
|
|
|
Finally, there are commercial products, of which TesSeRact (for
|
|
C-language TSRs) is one of the best known.
|
|
|
|
Subject: 708. 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: 709. What can I use to manage versions of software?
|
|
|
|
(rev: 21 Aug 1993) A port of the Unix RCS utility is downloadable as
|
|
pd1:<msdos.gnuish>rcs55ax.zip (EXE and docs) from Simtel
|
|
pd1:<msdos.gnuish>rcs55as.zip (source) from Simtel
|
|
/pc/unix/alrcs5ex.zip (EXE and docs ?) from Garbo.
|
|
This is no longer limited to one-character extensions on filenames
|
|
(.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, SB 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: 710. What's this "null pointer assignment" after my C program
|
|
executes?
|
|
|
|
(rev: 17 Sep 1993) 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 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( ) statements 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, in the debugger, set a watch at location
|
|
0000 in your data segment, assuming you're in small or medium model.
|
|
(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);
|
|
|
|
and then 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
|
|
pd1:<msdos.turbo-c>bchelp10.zip at Simtel
|
|
/pc/turbopas/bchelp10.zip at Garbo.
|
|
|
|
Subject: 711. How can my program tell if it's running under Windows?
|
|
|
|
(rev: 18 Apr 1993) Set AX=4680 and execute INT 2F. If AX contains
|
|
0, you're in Windows real mode or standard mode (or under the DOS
|
|
5.0 shell). Otherwise, set AX=1600 and INT 2F. If AL does not
|
|
contain 0 or 80, you're in Windows 386 enhanced mode. See {PC
|
|
Magazine} 24 Nov 1992 (xi:20), pages 492-493.
|
|
|
|
When Windows 3.0 or 3.1 is running, the DOS environment will contain
|
|
a definition of the string windir, in lower case.
|
|
|
|
For more information, see {PC Magazine} 26 May 1992 (xi:10) pages
|
|
345-346. A program, WINMODE, is available as part of
|
|
pd1:<msdos.pcmag>vol11n10.zip at Simtel
|
|
/pc/pcmag/vol11n10.zip at Garbo.
|
|
|
|
Subject: 712. How do I copyright software that I write?
|
|
|
|
(rev: 9 Sep 1993) The following is adapted (and greatly condensed)
|
|
from chapter 4 of the Chicago Manual of Style (13th edition, ISBN
|
|
0-226-10390-0). Disclaimer: This is not written by a lawyer, and
|
|
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.
|
|
|
|
That said, in the U.S. (at least), when you write something, you own
|
|
the copyright. (The most significant exception to programmers is
|
|
"works made for hire", i.e., something 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.) 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) a date--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.
|
|
|
|
You can download a much more comprehensive treatment from the
|
|
Internet. Terry Carroll posts a six-part Copyright FAQ to
|
|
misc.legal, news.answers and other groups.
|
|
|
|
|
|
(continued in part 4)
|