             ---------------------------------
             ZipGraph: The Low-Level Routines!
             ---------------------------------

                        version 1.20
                      1 November, 1989

                              
                        written by:
                        -----------
                     Scott Robert Ladd
                     705 West Virginia
                     Gunnison CO 81230

                     BBS (303)641-5125
                     FidoNet 1:104/708

                  Legal Disclaimer (Yuck)
                  =======================

The software presented in this package is public domain. You
may do anything you please with this software. As such,
there are no warranties or guarantees whatsoever as to the
quality, suitability, or general functionality of this
software. If you use this software, you accept full
responsibility for what it does or doesn't do.

Personally, I hate these disclaimers; however, in the
litigious world we live in, you can't even give something
away for free without covering yourself. Frankly, I suspect
most people are generally appreciative that I've made this
stuff available to them.

This software was developed using Zortech's C 2.01 on a
20Mhz 80386-based PC running MS-DOS 3.30. It's also been
tested with Mcirosft C 5.10 and QuickC 2.01, Borland Turbo
C 2.0, and Lattice C 6.01. I suspect other compilers will
work, too, but I haven't had time to try them.

One last request: if you do use this module, I'd like to hear
from you. I'm interested in any changes you might make. If
you find any bugs (!), drop me a line, and I'll see what I
can do. Again, since this is free, I can't make any
guarantees about how quickly any bug will get fixed. But I
will do my best!

                        Introduction
                        ============

This document should be part of an archived package, which
contains a complete C-language module for the low-level portion
of the ZipGraph graphics library for PCs using MS-DOS. Included
in the archive should be:

        ZG_LWLVL.H   -- the required header file
        ZG_LWLVL.C   -- C language cource code
        ZGTEST.C     -- a program to test the above
        ZG_LWLVL.DOC -- This document!


If you do redistribute this code, please do so with all of
the files together in an archive. While I use LHARC to
distribute this package, you can use any archiver you want.
Just keep these three files together, since that's how they
belong.

                   General Documentation
                   =====================

The following is an excerpt reprint from my Januray-February 1990
Cing Clearly column in Micro Cornucopia Magazine, where this module
first appeared.



The files in this archive are a part of a library called ZipGraph,
which provides several levels of functionality. These are
the low-level routines, which handle basic tasks such as the
determination of the installed adapter type and the plotting
of pixels. Future segments will cover drawing primitives,
clipping, region filling, and other basic tasks. In the
highest level of this library, I will present a series of
C++ classes, built on the lower-level C code, which will
handle advanced routines to handle ray tracing, 3D
modelling, and animation.

An obvious question might be: why write a graphics module?
Not only are there dozens of commercial graphics libraries
for C, but almost every compiler vendor now includes their
own graphics routines. What does ZipGraph offer that others
don't?

I had several goal in mind when building ZipGraph. To begin
with, I wanted it to be fast. The current version is more
than twice as fast as any other graphics library I have
tried. I was surprised to find that my C-language version of
the low-level graphics routines was nearly as fast as the
one I had built in assembler language. The advantages of
having easily maintainable C source far outweighed the few
percentage points loss in speed when compared to my
assembler language implementation.

My second goal was to make ZipGraph portable. As time
passes, more and more of my article involve programs which
do graphics. Alas, C compiler vendors do not have any
interest in making their proprietary graphics libraries
compatible. If I write a program using the Borland Graphic
Interface (BGI) included with Turbo C, that program won't
compile with any other C compiler. Rather than shut people
out, I decided to build a library which compiled with all of
the popular compilers. The version of ZipGraph presented
here compiles with Borland Turbo C 2.0, Lattice C 6.01,
Microsoft C 5.10 and QuickC 2.01, and Zortech C/C++ 2.01. In
addition, the resulting object modules can be linked to
Microsoft Fortran 5.0 and Stony Brook Modula-2 2.01.

My final reason for writing ZipGraph is that I find
commercial libraries limited. For example, most do not
include printer routines, and some do not support certain
graphics adapters. On top of that they lack fundamental
capabilities, such as a function to generate non-orthagonal
ellipses. No graphics library I know of completely supports
ray tracing, animation, object rotation, and 3D plotting.
Additionally, commercial libraries are written using C and
assembler language only, without utilizing the object-
oriented features of C++. As you'll see in future columns,
C++ provides the ability to do some fantastic things.

The ZG_LWLVL module, presented here, is the basis of all the
other modules in the ZipGraph system. It uses some of the
more powerful features of C, and provides the basic
functions for detecting the type of graphics adapter
installed in your machine, and supports the plotting and
reading pixels on all common IBM adapters. As mentioned
earlier, this module is almost 850 lines long, making it the
longest piece of code ever published in this column.
Subsequently, I'll have to forego a long detailed discussion
of ZG_LWLVL. Instead, I'll be focusing on the techniques
used to make it work.

IBM PCs and their compatibles were originally designed to be
modular; in spite of IBM's recent move toward building video
hardware into the computer's motherboard, most vendors have
maintained the ability to install whatever kind of video
adapter you want. Currently, there are six standard video
adapters in common use in PCs: the Monochrome Display
Adapter (MDA), the Color Graphics Adapter (CGA), the
Hercules Graphics Card (HGC), the Enhanced Graphics Adapter
(EGA), the Multi-Colored Graphics Array (MCGA), and the
Video Graphics Array (VGA).

One problem PC software has always faced is that any one of
these video adapters can be installed in a PC. While most of
the adapters are supported by the PC's BIOS, the Hercules
card is not. I'm sure most of you have had the experience of
finding a pieces of graphics software which will not run on
your computer.

Some graphics libraries allow you to "detect" which kind of
video adapter is installed. Borland's Graphic Interface
(BGI) has this capability. However, the BGI uses external
drivers, which are loaded at run time. While you can convert
the BGI drivers to object modules, you then have to go
through a clumsy procedure to link them in and make the
program aware of their resident status. Other "auto-sensing
packages" fail to support certain adapters, or are just
simply clumsy.

I've always been a believer that computers should do work
for you. ZipGraph not only detects the presence of all of
the above adapters, it also automatically sets up its
routines so that you can write one program with which works
with all of those adapters. The type of adapter you're
working with is as transparent as possible.

Listing 1 shows ZG_LWLVL.H, the header file which must be
included in any source program which uses the LG_LWLVL
module. Listing 2 is ZG_LWLVL.C, the implementation of the
module. Finally, Listing 3 shows one of my test programs,
ZGTEST.C, which will give you an example of how to use the
ZG_LWLVL module.

We'll start with a quick synopsis of how to use the ZG_LWLVL
functions. First, you need to call ZG_Init to initialize
the module. ZG_Init detects the adapter installed in the
PC, and saves its initial status. Information on the adapter
will be returned in the public ZG_VideoInfo structure.
Then you need to set a graphics mode using the
ZG_SetMode function. It sets the video mode you request,
assigns the proper pixel plotting and reading function to
the function pointers ZG_PlotPixel and ZG_ReadPixel,
and returns information on the new graphics mode in
ZG_VideoInfo. You can then plot pixels by calling the
function pointed to by ZG_PlotPixel, a read pixels via
the function pointer ZG_ReadPixel. When your program is
done, it should call ZG_Done to restored the video
adapter to its pre-program state. Examining ZGTEST.C will
show you the details of how this all fits together. No let's
examine the above process in detail.

When ZG_Init is called, it attempts to identify the
video adapter installed in your computer. There isn't an
built-in way to determine the adapter type, but we can use
the process of elimination. ZG_Init() begins be calling
an MCGA and VGA BIOS function which returns the adapter
type. If an MCGA or VGA BIOS is installed, this call will
tell us which one of those it is. If the adapter installed
is not a VGA, the call to this BIOS function will fail. Once
we've eliminated the VGA, we call an EGA BIOS function.
Again, if the EGA BIOS is not present, the function will not
return expected values, and we know that an EGA is not
present. If no EGA is found, we ask the BIOS for the
hardware information word. We check the appropriate bits to
see if a color or monochrome adapter is installed. A color
adapter will be a CGA at this point (since we have
eliminated the other color adapters). If a monochrome
adapter is installed, our final task is to differentiate
between an MDA and a HGC. This is done by monitoring the
vertical synch bit of the monochrome card's status register;
if the bit changes, we have a Hercules card.

Information on the adapter's type and its installed monitor
are placed into the ZG_VideoInfo structure. Constants
for these values are defined in the ZG_LWLVL.H files can be
used to make you code a bit clearer. At this point, all
ZipGraph has done is determine what kind of video adapter
you have. I've tested the detection routine on several
computers with a variety of adapters installed. So far, it
has worked flawlessly with VGA, EGA, MDA, and Hercules
adapters. I do not have a CGA or MCGA adapter available to
me, but I was able to test those routines on my Paradise 16-
bit VGA card, which emulates the CGA and MCGA. I'd
appreciate hearing from you if you have problems.

In order to display graphics, you now need to set a graphics
mode. You do this by calling the ZG_SetMode function,
passing it as its first parameter one of the ZG_MOD
constants defined in ZG_LWLVL.H. ZG_SetMode will return
1 if the requested mode is not valid for the adapter
detected. Information on the mode will again be returned in
the public global variable ZG_VideoInfo. That
information includes the x and y dimensions of the new
graphics mode, and the number of colors available. If all
goes well, ZG_SetMode will return 0 to indicate that
success.

ZG_SetMode also does some automagical work for you. Two
special graphics modes are ZG_MOD_BESTRES and
ZG_MOD_MOSTCOLOR. Respectively, they represent -- for
the detected adapter -- the best possible resolution, and
the resolution providing the greatest selection of colors.
The global static array VideoTable contains the actual
modes for both of these modes, for each adapter. In
addition, VideoTable also contains a bit mask which
indicates the valid modes for a given adapter. The requested
graphics mode is compared against the ModeList value for
a given adapter type, to be sure it is valid. If it isn't,
ZG_SetMode returns an error.

Once it has determined the validity of the requested
graphics mode, ZG_SetMode uses the information stored in
the global static array ModeData to do the actual mode
set-up. ModeData contains the equivalent BIOS mode for
the mode requested, the addresses of the appropriate pixel
plotting and reading functions, and the dimensions and color
counts for each mode. When the mode is set, ZG_SetMode
assigns the appropriate values in the ModeData table to
the function pointers ZG_PlotPixel and ZG_ReadPixel.
We are ensured that for each graphics mode, the correct
pixel plotting and reading routines are set -- all
automatically. Once that is done, ZG_SetMode assigns
values from the ModeData table to the width, height, and
color count values of the ZG_VideoInfo structure.

It should be noted here that the Hercules card is handled in
a special manner by ZG_LWLVL.C. Since the HGC is not
supported by the PC BIOS in any way, it's graphics and text
modes must be set via special functions. All the ZipGraph
functions do is make exceptions to their normal set-up by
calling the special Hercules functions rather than the BIOS
(as used for the other graphics cards).

And we're ready to plot pixels! Different video modes
require different pixel plotting and reading routines, which
is why I use pointers to the functions for these tasks.
ZG_PlotPixel and ZG_ReadPixel can be used exactly as
if they were regular functions. However, they do different
things based on the specific type of graphics adapter
installed. Their value is set based on the graphics mode you
requested via ZG_SetMode. In a way, you can look upon
this as a form of polymorphism (an object-oriented
programming concept) for C.

This document has already run far longer than it should, so
I won't go into the specifics of how the individual pixel
plotting and reading functions work. If you're dying to
know, pick up a copy of Richard Wilton's Programmer's
Guide to PC & PS/2 Video Systems (ISBN 1-55615-103-9), by
far the best reference ever published about the nuts and
bolts of PC graphics programming.

I doubt ZG_LWLVL will remain static. The most obvious
enhancement is to include support for the "Super VGA modes,
such as 800 x 600 graphics. Additionally, it would be nice
to add the capability to use some of the non-documented VGA
modes, such as 320 by 400 pixels with 256 colors. But all in
good time. I suspect this issue's source code is more than
enough for most of you to "chew" on for a while.

                    VERSION HISTORY!
                    ----------------

Version 1.00 was built and released quickly because of a magazine
article deadline. Later, as I looked at it, I saw that the performance
could be increased dramatically. So, that's what I did, generating
version 1.10. 1.20 corrected some minor bugs in the handling of
modes which are only rarely used, such as 320 by 200 by 16 color
and EGA monochrome mode.
