Beruflich Dokumente
Kultur Dokumente
Like all compilers, the C data types mainly correspond with processor data types, which allows
fast and easy processing. The table on the right shows how various C compilers store the
different data types.
Since C doesn't have a byte type or word type, you'll find typedef functions, similar to the
following, at the beginning of many of the C programs we use:
These lines define the two types that are very important to system programming. In C, the
memory model that's used governs the use of NEAR and FAR pointers. The programs in this
book were developed using the SMALL memory model. So they work exclusively with NEAR
pointers. When FAR pointers are needed for system programming, the far modifier is used in the
variable declaration:
C Type Stored as
unsignedchar BYTE
char BYTE
int WORD
unsigned int WORD
near *void WORD
long DWORD
far *void DWORD
union REGS {
struct WORDREGS x;
struct BYTEREGS h;
};
struct WORDREGS {
unsigned int ax;
unsigned int bx;
unsigned int cx;
unsigned int dx;
unsigned int si;
unsigned int di;
unsigned int cflag;
};
struct BYTEREGS {
unsigned char al, ah;
unsigned char bl, bh;
unsigned char cl, ch;
unsigned char dl, dh;
};
The 16-bit processor registers AX to ES are represented by the unsigned int variables of the same name in
the WORDREGS structure. The 8-bit processor registers AL to DH are represented by the variables in the
BYTEREGS structure.
The variant record applies to the 8-bit registers, which are as important as the 16-bit registers for carrying
information during the interrupt call. Dividing the 8-bit and 16-bit registers into two variants results in an
overlapping of both register sets in memory, with two 8-bit variables overlapping "their" 16-bit variable. So,
AL and AH share the same memory space as AX, BL, and BH share the same memory space of BX. This
also applies to the CL/CH and DL/DH variables.
Notice the order in which 8-bit registers are specified. This order must mirror the format in which the 16-bit
register is placed in memory above them. Since, in memory, the low byte of a word precedes the high byte,
the L register must be declared before the corresponding H register.
If pregs is a variable of the REGS type, you can easily address the processor registers from the various
components of this variable:
Ø pregs.x.ax, Ø pregs.x.bx, Ø pregs.x.cx,
Ø pregs.h.ah, Ø pregs.h.dl, etc.
If you want to pass the value D3H (0xD3) to the DL register during an interrupt call, do the following:
pregs.h.dl = 0xD3;
Before calling an interrupt using Intr or MsDos, load the registers, which are used by the function you'll call,
with the information you want passed to the function. The interrupt ignores all other registers except those
on which it directly relies.
However, you'll encounter problems if you want to read other flags because some BIOS functions use the
zero flag for returning information. In these instances, you can't accomplish anything on Microsoft compilers
with the int...() functions.
However, the developers at Borland were clever enough to expand the WORDREGS structure by a FLAGS
variable, which reflects the contents of the entire flag register after the function call.
With Borland compilers, you can determine whether one of the flags is set in the flag register. This is done
after calling an int...() function through a binary combination of the flags variable with the value of the
particular flag. The table on the left shows the values of the various processor flags.
/********************** M E D I A I D C . C ************************/
#include <dos.h>
#include <stdio.h>
#ifndef MK_FP /* Macro MK_FP already defined */
#define MK_FP(seg,ofs) ((void far *) ((unsigned long) (seg)<<16|(ofs)))
#endif
void main( void )
{
union REGS pregs;
struct SREGS sregs;
unsigned char far *mp;
pregs.h.ah = 0x1B;
intdosx( &pregs, &pregs, &sregs );
mp = MK_FP( sregs.ds, pregs.x.bx );
printf( "Media ID = %d\n ", *mp );
}
If you examine the definition of the MK_FP() macro, you'll notice that it's quite simple despite the many
parentheses and keywords. Within the definition, the segment is cast into a long type, shifted to the left by
16 bits and the offset address is then set in the lower 16 bits of the resulting new long type. The result
corresponds exactly to the desired FAR pointer in its composition, so it only has to be accessed by a cast.
Port access in C
Both the Microsoft and Borland compilers offer various functions for accessing ports. However, they have
different names and are declared in different include files. Borland has its declarations in DOS.H, while
Microsoft has its declarations in CONIO.H. Port Access:C language
The following shows the different routines and declarations of the two compiler manufacturers:
This enables you to use the names of the Microsoft functions in your programs even if you're working with a
Borland compiler.
For example, the following statements read the contents of port 3C4H (0x3C4), which is part of the graphics
controller on an EGA/VGA card: