DOS Interrupts

14.3 DOS Interrupts

The MS-DOS operating system provides a great many subroutines for disk handling, video card manipulation, keyboard access, file operations, time & date functions and so on. These routines are generally at a higher level than the BIOS routines, but they are invoked in a similar manner.

One visual representation of the inter-relation of the computer, the BIOS, DOS and our

C program is to think of the whole system as an onion, viz.

Figure 1 Layering of PC system architecture

The nearer the outside of the onion, the more machine independent our code should be, the nearer the centre of the onion the more dependent our code is upon the computer we are using. Thus we would expect printf to work satisfactorily on most machines with a C compiler. We would hope that DOS interrupts will work in the same way on all computers with the same version of MS-DOS, whereas BIOS interrupts will only work correctly on computers with an IBM compatible BIOS system.

The following program shows the use of DOS interrupts to find files on a DOS disk. October 1996

61 OUCS

Programming in C l9.2/2 #include <stdio.h>

#include <dos.h> char *farstrcpy(char *dst, char far *src)

{ char

*str = dst;

while (*dst++ = *src++)

; return str; }

int main() { union REGS inregs, outregs; char

str[13];

char

*filespec = "c:*.c";

inregs.h.ah = 78; /* 78 = find first function */ inregs.x.dx = (unsigned)filespec;

/* file specification */

inregs.x.cx = 0;

/* file attributes */

int os(&inregs,&outregs); /* call DOS "find first" interrupt */

while (outregs.x.cflag == 0)

{ printf("Found: ");

farstrcpy(str,MK_FP(_psp,128+30)); /* find first writes the matching file name 30 bytes

into the "disk transfer area", which itself is 128 bytes into the "program segment prefix" or PSP */

printf("%s\n", str); inregs.h.ah = 79;

/* 79 = find next func tion */

intdos(&inregs,&outregs);

/* call DOS "find next" interrupt */

} return 0;

} Borland C compilers provide two library functions findfirst() and findnext()

to find files matching a given path name and attribute, but these functions are merely “front-ends” to the two DOS interrupts.

findfirst() initialises the search and fills in a structure giving details of the first matching file (if there is one). Repeated calls to findnext() will find all remaining matching files. Both functions return -1 if no match is found.

int findfirst(const char *pathname,

struct ffblk *ffblk, int attrib);

OUCS

62 October 1996 62 October 1996

The prototypes for findfirst() and findnext() are in dir.h . The structure called ffblk is defined in dir.h to be

struct ffblk /* struct used by findfirst() and findnext() */

char ff_reserved[21];

/* reserved by DOS */

char ff_attrib;

/* attribute found */

int

ff_ftime;

/* file time */

int

ff_fdate;

/* file date */

long ff_fsize;

/* fil e size */

char ff_name;

/* found file name */

The file attributes are set by MS-DOS (they are nothing to do with C). They are

read only

volume label

archive bit

Each individual attribute is set by setting a particular bit of an 8 bit byte. The rules for finding files with specific attributes are rather strange, again this is a

“feature” of MS-DOS and has nothing to do with the functions provided by Borland or other compiler manufacturers:

“Any combination of the hidden, system or directory attribute bits will match normal files as well as those with the attributes given. If a volume label attribute is used, then only the disk label will match. The archive and read- only bits do not apply to the search.”

To find files with combinations of attributes the separate attributes must be bitwise ORed together.

The following program will print the names of all C source files in the current directory.

/* findfirst and findnext example */ #include <stdio.h>

#include <dir.h>

October 1996

63 OUCS

Programming in C l9.2/2 int main(void)

{ struct ffblk

printf("Directory listing of *.*\n"); done = findfirst("*.*",&ffblk,0); while (!done)

{ printf(" %s\n", ffblk.ff_name); done = findnext(&ffblk); }

return 0; }

It is usually best to use the highest level interface available to a given operation, that is: ANSI C

Vendor specific C function DOS interrupt BIOS interrupt

The higher the level, the more protected you are from specific models of the IBM PC, specific versions of MS-DOS and specific compilers. Choose an ANSI C function if at all possible for the most portable solution.