                       BASIC PC GRAPHICS WITH C

                            Matthew Probert

                           Servile  Software



When programming graphics you should bear in mind that they are a 
machine dependant subject. Code to produce graphics on an IBM PC will 
not port to an Amiga, or VAX or any other type of computer. 

Introduction To PC Graphics

The IBM PC and compatible range of computers display information on a 
visual display unit (VDU). To enable the computer to send information 
to the VDU a component is included within the computer called a 
"display card". There are various display cards and VDUs which have 
been produced for the IBM PC range of computers; monochrome display 
adapter (MDA), colour graphics adapter (CGA), Hercules graphics card 
(HGC), Enhanced graphics adapter (EGA), video graphics array (VGA), 
super video graphics array (SVGA), memory controller gate array 
(MCGA), 8514/A and the Texas Instruments Graphics Architecture (TIGA). 
For simplicity, this info journal will concern itself only with the 
three more common types of display; 

                               CGA, EGA and VGA

Information about the VGA display is also relevant to the SVGA display 
which in simple terms can do the same and more. This info journal will 
not concern itself with monochrome displays since they are of limited 
use in graphics work. 


Display Modes

When an IBM PC computer is first switched on is set to display 80 
columns by 25 rows of writing. This measurement, 80 x 25 is called the 
"resolution". A display mode which is intended for displaying writing 
is called a "text" mode. Where as a display mode which is intended for 
displaying pictures is called a "graphics" mode. 

If you look closely at the display you will see that each displayed 
character is comprised of dots. In reality the entire display is 
comprised of dots, called "pixels", which may be set to different 
colours. In text display modes these pixels are not very relevant, 
however, in graphics display modes each pixel may be selected and set 
by a program. The size of the pixels varies with the display 
resolution. In 80 x 25 text mode the pixels are half the width they 
are in 40 x 25 text display mode. 

Depending upon the display card installed in the computer, there are a 
number of display modes which may be used; 

MODE    TYPE        RESOLUTION      COLOURS

 0      Text        40 x 25         4 (CGA), 16 (EGA, VGA) Shades of grey 
 1      Text        40 x 25         4 (CGA), 16 (EGA, VGA)
 2      Text        80 x 25         4 (CGA), 16 (EGA, VGA) Shades of grey 
 3      Text        80 x 25         4 (CGA), 16 (EGA, VGA)
 4      Graphics   320 x 200        4
 5      Graphics   320 x 200        4 (grey on CGA and EGA)
 6      Graphics   640 x 200        2
 7      Text        80 x 25         Mono (EGA, VGA)
13      Graphics   320 x 200        16 (EGA, VGA)
14      Graphics   640 x 200        16 (EGA, VGA)
15      Graphics   640 x 350        Mono (EGA, VGA)
16      Graphics   640 x 350        16 (EGA, VGA)
17      Graphics   640 x 480        2 (VGA)
18      Graphics   640 x 480        16 (VGA)
19      Graphics   320 x 200        256 (VGA)

The term resolution in graphics modes refers to the number of pixels 
across and down the VDU. The larger the number of pixels, the smaller 
each is and the sharper any displayed image appears. As you can see 
from the table, the VGA display can produce a higher resolution than 
the other display cards, resulting in sharper images being produced. 
 
The CGA display card can produce a maximum resolution of 320 x 200 
pixels, where as the VGA display card can produce a resolution of 640 
x 480 pixels. This is why writing on a VGA VDU looks so much sharper 
than the writing displayed on a CGA VDU. 


Accessing The Display

Inside the IBM PC computer is a silicon chip called the BIOS ROM, this 
chip contains functions which may be called by an external computer 
program to access the display card, which in turn passes the 
information on to the VDU. The BIOS display functions are all accessed 
by generating interrupt 10 calls, with the number of the appropriate 
function stored in the assembly language AH register. 

A programmer interested in creating graphics displays must first be 
able to switch the display card to an appropriate graphics mode. This 
is achieved by calling the BIOS display function 0, with th number of 
the desired display mode from the table stored in the assembly 
language AL register thus the following assembly language code segment 
will switch the display card to CGA graphics mode 4, assuming that is 
that the display card is capable of this mode; 

        mov     ah , 00
        mov     al , 04
        int     10h

A C function for selecting video display modes can be written; 

#include <dos.h>

void setmode(unsigned char mode)
{
    /* Sets the video display mode */

    union REGS inregs outreg;

    inreg.h.ah = 0;
    inreg.h.al = mode;
    int86(0x10,&inreg,&outregs);
}

Any graphics are created by setting different pixels to different 
colours, this is termed "plotting", and is achieved by calling BIOS 
display function 12 with the pixel's horizontal coordinate in the 
assembly language CX register and it's vertical coordinate in the 
assembly language DX register and the required colour in the assembly 
language AL register thus; 

        mov     ah, 12
        mov     al, colour
        mov     bh, 0
        mov     cx, x_coord
        mov     dx, y_coord
        int     10h

The corresponding C function is;

#include <dos.h>

void plot(int x_coord, int y_coord, unsigned char colour)
{
    /* Sets the colour of a pixel */

    union REGS regs;

    regs.h.ah = 12;
    regs.h.al = colour;
    regs.h.bh = 0;
    regs.x.cx = x_coord;
    regs.x.dx = y_coord;
    int86(0x10,&regs,&regs);
}

The inverse function of plot is to read the existing colour setting of 
a pixel. This is done by calling BIOS ROM display function 13, again 
with the pixel's horizontal coordinate in the assembly language CX 
register and it's vertical coordinate in the assembly language DX 
register. This function then returns the pixel's colour in the 
assembly language AL register; 
 
#include <dos.h>

unsigned char get_pixel(int x_coord, int y_coord)
{
    /* Reads the colour of a pixel */

    union REGS inreg, outreg;

    inreg.h.ah = 13;
    inreg.h.bh = 0;
    inreg.x.cx = x_coord;
    inreg.x.dx = y_coord;
    int86(0x10,&inreg,&outreg);
    return(outreg.h.al);
}

Colour And The CGA

The CGA display card can display a maximum of 4 colours simultaneously 
at any time. However, the display card can generate a total of 8 
colours. There are two sets of colours, called "palettes". The first 
palette contains the colours; 

                     background, cyan, magenta and white.

the second palette contains the colours;

                      background, green, red and yellow.


Colour 0 is always the same as the background colour.

The pixels displayed on the VDU take their colours from the currently 
active palette, and are continuously being refreshed. So, if the 
active palette changes, so to do the colours of the displayed pixels 
on the VDU. 

Selection of the active CGA palette is achieved by calling the BIOS 
display function 11 with the number of the desired palette (either 0 
or 1) in the assembly language BH register; 

        mov     ah, 11
        mov     bh, palette
        int     10h
        
The C function for selecting the CGA palette is;

void palette(unsigned char palette)
{
    union REGS inreg, outreg;

    inreg.h.ah = 11;
    inreg.h.bh = palette;
    int86(0x10,&inreg,&outreg);
}

The background colour may be selected independantly from any of the 
eight available colours by calling the same BIOS display function with 
a value of 0 stored in the assembly language BH register and the 
desired background colour in the assembly language BL register; 

        mov     ah, 11
        mov     bh, 0
        mov     bl, colour
        int     10h
        
In C this function can be written;

void background(unsigned char colour)
{
    union REGS inreg, outreg;

    inreg.h.ah = 11;
    inreg.h.bh = 0;
    inreg.h.bl = colour;
    int86(0x10,&inreg,&outreg);
}

The background colours available are;

        0       Black
        1       Blue
        2       Green
        3       Cyan
        4       Red
        5       Magenta
        6       Yellow
        7       White


Colour And The EGA

The EGA display card can display a maximum of 16 colours 
simultaneously at any time. The colour of all pixels is continuously 
refreshed by the display card by reading the colour from the EGA 
palette. Unlike the CGA display card, the EGA display card allows you 
to redefine any or all of the colours in the palette. Unfortunately 
only the first 8 colours may be loaded into other palette colours. 

The colours are;

    0   Black
    1   Blue
    2   Green
    3   Cyan
    4   Red
    5   Magenta
    6   Brown
    7   Light grey
    8   Dark grey
    9   Light blue
   10   Light green
   11   Light cyan
   12   Light red
   13   Light magenta
   14   Yellow
   15   White

Changing a palette colour is achieved by calling the BIOS display 
function 16 with a value of 0 in the assembly language AL register, 
the colour value (0 to 7) in the assembly language BH register and the 
number of the palette colour (0 to 15) in the assembly language BL 
register thus; 

        mov     ah,16
        mov     al,0
        mov     bl,palette
        mov     bh,colour
        int     10h

In C this function may be written;

void ega_palette(unsigned char colour, unsigned char palette)
{
    union REGS inreg,outreg;

    inreg.h.ah = 16;
    inreg.h.al = 0;
    inreg.h.bl = palette;
    inreg.h.bh = colour;

    int86(0x10,&inreg,&outreg);
}


Colour And The VGA

The VGA display card can display a maximum of 256 colours on the VDU 
at any one time, these colours are defined by information held in 256 
special registers called "DAC" registers. As with the CGA and EGA 
displays, the colour of displayed pixels is continuously being 
refreshed, and as such any change to a DAC register is immediately 
visible. Each DAC register has three component data parts which record 
the amount of green, blue and red colours which make up the displayed 
colour. Each of these seperate data components can hold a value 
between 0 and 63 giving the VGA display card the ability to display 
262,144 colours! Although only a small subset of them can be displayed 
at any one time. 

Setting the value of a DAC register is achieved by calling the BIOS 
display function 16 with a value of 16 stored in the assembly language 
AL register, the green value stored in the assembly language CH 
register, the blue value stored in the assembly language CL register 
and the red value stored in the assembly language DH register and the 
number of the DAC register to be set stored in the assembly language 
BX register; 

        mov     ah,16
        mov     al,16
        mov     ch,green
        mov     cl,blue
        mov     dh,red
        mov     bx,dac
        int     10h

The C function to set a DAC register looks lik this;

void set_dac(int dac, unsigned char green, unsigned char blue,
             unsigned char red)
{
    union REGS regs;

    regs.h.ah = 16;
    regs.h.al = 16;
    regs.x.bx = dac;
    regs.h.ch = green;
    regs.h.cl = blue;
    regs.h.dh = red;
    int86(0x10,&regs,&regs);
}


Displaying Text

The BIOS ROM provides three functions for displaying a single 
character. The first function to consider is the one used extensively 
by DOS for displaying messages, this is function 14 called "write text 
in teletype mode". This function interprets some control characters; 
bell (ascii 7), backspace (ascii 8), carriage return (ascii 10) and 
line feed (ascii 13) but all other ascii codes are displayed, and the 
current cursor position updated accordingly, moving down a row when a 
character is displayed in the far right column. To call this function 
the assembly language register AL holds the ascii code of the 
character to be displayed and assembly language register BL holds the 
foreground colour for the character to be displayed in if a graphics 
mode is active; 

        mov     ah,14
        mov     al,character
        mov     bh,0
        mov     bl,foreground
        int     10h

A C function for accessing the write text in teletype mode may be 
written like this; 

#include <dos.h>

void teletype(unsigned char character, unsigned char foreground)
{
    union REGS inreg, outreg;

    inreg.h.ah = 14;
    inreg.h.al = character;
    inreg.h.bh = 0;
    inreg.h.bl = foreground;
    int86(0x10,&inrg,&outreg);
}

The second BIOS ROM display function for displaying a character allows 
the foreground and background colours of the displayed character to be 
defined. It also allows multiple copies of the character to be 
displayed one after another automatically displaying subsequent 
characters at the next display position, although the current cursor 
position is not changed by this function. 

This function is called "write character and attribute", and is BIOS 
ROM display function number 9. It is called with the ascii code of the 
character to be displayed in the assembly language AL register, the 
display page in assembly language register BH, the foreground colour 
in the first four bits of the assembly language register BL and the 
background colour in the last four bits of the assembly language 
register BL, the number of times the character is to be displayed is 
stored in the assembly language CX register thus; 

        mov     ah,9
        mov     al,character
        mov     bh,0
        mov     bl,foreground + 16 * background
        mov     cx,number
        int     10h

And in C;

#include <dos.h>

void char_attrib(unsigned char character, unsigned char foreground,
                 unsigned char background, int number)
{
    union REGS inreg,outreg;

    inreg.h.ah = 9;
    inreg.h.al = character;
    inreg.h.bh = 0;
    inreg.h.bl = (background << 4) + foreground;
    inreg.x.cx = number;
    int86(0x10,&inreg,&outreg);
}

The last BIOS ROM display function for displaying a character retains 
the foreground and background colours of the display position. 

This function is called "write character", and is BIOS ROM display 
function number 10. It is identical to BIOS ROM display function 9 
except that the colours of the displayed character are those which are 
prevalent at the display position, except in graphics modes when the 
foreground colour of the character is determined by the value in the 
assembly language BL register. Its use is as follows; 

        mov     ah,10
        mov     al,character
        mov     bh,0
        mov     bl,foreground   ; For graphics modes ONLY
        mov     cx,number
        int     10h

And in C;

#include <dos.h>

void char_attrib(unsigned char character, unsigned char foreground,
                 int number)
{
    union REGS inreg,outreg;

    inreg.h.ah = 10;
    inreg.h.al = character;
    inreg.h.bh = 0;
    inreg.h.bl = foreground;    /* For graphics modes ONLY */
    inreg.x.cx = number;
    int86(0x10,&inreg,&outreg);
}

Positioning of the text cursor is provided for by the ROM BIOS display 
function number 2. It is called with the row number in the assembly 
language register DH and the column number in the assembly language 
register DL; 

        mov     ah,2
        mov     bh,0
        mov     dh,row
        mov     dl,column
        int     10h

The corresponding function in C looks like this;

#include <dos.h>

void at(unsigned char row, unsigned char column)
{
    union REGS regs;

    regs.h.ah = 2;
    regs.h.bh = 0;             
    regs.h.dh = row;
    regs.h.dl = column;
    int86(0x10,&regs,&regs);
}

From these basic functions a more useful replacement for the C 
language's "printf()" function can be written which allows data to be 
displayed at the current cursor position, previously set by a call to 
"at()", with prescribed attributes; 

#include <dos.h>
#include <stdarg.h>

void at(unsigned char row, unsigned char column)
{
    union REGS regs;

    regs.h.ah = 2;
    regs.h.bh = 0;             
    regs.h.dh = row;
    regs.h.dl = column;
    int86(0x10,&regs,&regs);
}

void xprintf(unsigned char foreground, unsigned char background,
             char *format,...)
{
    union REGS inreg,outreg;
    va_list arg_ptr;
    static char output[1000];
    unsigned char col;
    unsigned char row;
    unsigned char n;
    unsigned char p;
    unsigned char text;
    unsigned char attr;

    /* Convert foreground and background colours into a single attribute */
    attr = (background << 4) + foreground;

    /* Copy data into a single string */
    va_start(arg_ptr, format);
    vsprintf(output, format, arg_ptr);

    /* Determine number of display columns */
    inreg.h.ah = 15;
    int86(0x10,&inreg,&outreg);
    n = outreg.h.ah;

    /* Determine current cursor position */
    inreg.h.ah = 3;
    inreg.h.bh = 0;
    int86(0x10,&inreg,&outreg);
    row = outreg.h.dh;
    col = outreg.h.dl;

    /* Now display data */
    p = 0;
    while (output[p])
    {
        /* Display this character */
        inreg.h.bh = 0;
        inreg.h.bl = attr;
        inreg.x.cx = 01;
        inreg.h.ah = 9;
        inreg.h.al = output[p++];
        int86(0x10,&inreg,&outreg);

        /* Update cursor position */
        /* moving down a row if required */
        col++;
        if (col < (n - 1))
            at(row, col);
        else
        {
            col = 0;
            at(++row, col);
        }
    }
}

This function, "xprintf()" illustrates two more functions of the BIOS 
ROM. The first is the call to function 15 which returns the number of 
text display columns for the currently active display mode. 

The other function illustrated, but not yet discussed, is BIOS ROM 
function 3 which returns information about the cursor. The cursor's 
row is returned in the assembly language register DH, and it's column 
in the assembly language register DL. 

