Beginning Pascal Tutorial Part 21

                       Turbo Pascal for DOS Tutorial
                           by Glenn Grotzinger
                         Part 21: Use of the BGI.
                 copyright (c) 1995-6 by Glenn Grotzinger

Here is a solution to the problem presented last time.

program part20; uses dos;

  const
    oneminute = 273;  { # of timer ticks that happens in 15 seconds }

  var
    saveint09, saveint1c: procedure;
    timecounter, keycounter: integer;
    achar: char;
    timeup: boolean;

  {$F+}
  procedure counttime; interrupt;
    begin
      inc(timecounter);
      if timecounter = oneminute then
        timeup := true;
      inline($9C);
      saveint1c;
    end;

  procedure countkeys; interrupt;
    begin
      inc(keycounter);
      inline($9C);
      saveint09;
    end;

  {$F-}

  begin
    timeup := false;
    timecounter := 0;
    keycounter := 0;
    getintvec($09, @saveint09);
    getintvec($1C, @saveint1C);
    setintvec($09, @countkeys);
    setintvec($1C, @counttime);

    while timeup = false do
      read(achar);

    setintvec($09, @saveint09);
    setintvec($1C, @saveint1C);
    writeln('There were ', keycounter div 2, ' keys pressed.');
  end.

BGI Intro
=========
This part is basically about using the Borland Graphics Interface.  It
is not generally recommended to use it -- use assembler instead to
make it a lot quicker, and smaller.... but due to demand, and the
ability to use the BGI for basic graphics, we will talk about use of
the BGI.  For heavy use of graphics, assembler is indeed better....

BGI loader
==========
Here, I will describe how to make the BGI files functionally more
useable than in their current state....Compile with looking for the
external BGI files is fine, but will run into an annoyance quick,
especially with a distributed utility...it is easier to have the EXE
available with the BGI files binded to it than to make a user keep
track of the BGI files....

There is a utility available in TP called BINOBJ.  The BGI files are
video control files, while CHR files are font definition files.  They
both are usable in graphics as binary files...and that is what BINOBJ
does...it converts binary files to OBJect files, which may be handled
like described before.  The proper usage of this utility is...

BINOBJ <binary file name> <object file name> <internal procedure name>

Here is a sample command-line.  To convert VESA16.BGI to an OBJ, do
this:

BINOBJ VESA16.BGI VESA16.OBJ Vesa16driver

Locate the BGI files that should be in your TP/BGI directory.
You will need to copy them off to a separate directory.

After you convert all BGI files like this, I recommend you write a
unit to link the BGI files in -- optionally, if you need fonts, you
may write a unit to link those in likewise.  Be sure to define your
proper external procedures like it needs to be done.  Make use of
meaningful names, as you will have to remember them to make use of the
graphics set. For example, I call my unit bgivideo; for each
procedure, I put the first 8 chars of the name of the file, and then
driver for the BGI files.  These function names will be easy to
remember when we need to use them.

Going into Graphics Mode
========================
Now, hopefully you have your BGI video unit ready now, and sitting in
whatever directory you use to place your pascal code.  Now we will
discuss about how to go into graphics mode using the BGI.

Generally, going to graphics mode initially would require setting up
the BGI unit you created, and then performing an autodetect on the
graphics, followed by an init into graphics mode.

Let us look at some sample code...

program video_example_1;{1} uses graph, bgivideo;

var
  graphicsdriver, graphicsmode: integer;

procedure errormessage(driver: string);
  begin
    writeln('There was an error: ', grapherrormsg(graphresult),
driver);
    halt(1);
  end;

begin
{2}if (registerbgidriver(@attdriver) < 0) then
     errormessage('ATT');
   if (registerbgidriver(@cgadriver) < 0) then
     errormessage('CGA');
   if (registerbgidriver(@egavgadriver) < 0) then
     errormessage('EGA/VGA');
   if (registerbgidriver(@hercdriver) < 0) then
     errormessage('Herc');
   if (registerbgidriver(@pc3270driver) < 0) then
     errormessage('PC 3270');

{3}detectgraph(graphicsdriver, graphicsmode);
   graphicsdriver := Detect;
{4}initgraph(graphicsdriver, graphicsmode, '');
{5}if GraphResult <> grOk then
    begin
      writeln('Video error.');
      halt(1);
    end;

{6}repeat
    putpixel(random(getmaxx), random(getmaxy), random(getmaxcolor));
   until keypressed;
   readln;

{7}closegraph;
end.

This is basically a random pixel place system using the video mode in
the recommended auto-detect.  Let's go through a few of the features
of the code, which were marked by {}'s.

{1} As we see, the graph, and bgivideo units are used here.  The graph
unit is the basic function interface for the BGI system.  We are
familiar with bgivideo from earlier.

{2} RegisterBGIDriver() must be called for any and all realistic
possibilities. I recommend that only the ones listed really need to be
checked.  If the function is less than zero, then there is a problem.
The errormessage function holds a function called grapherrormsg()
which will output a direct error as to why things aren't working
right.

{3} detectgraph detects the graphics card.  it takes integers
represented by graphicsdriver, and graphicsmode....graphicsdriver will
hold the recommended video type, and graphicsmode will hold the
recommended video mode (it can be changed).  This is why we registered
all the drivers...it will use whichever one it needs, and ultimately,
the program will work with all video modes.

{4} initgraph() takes us into graphics mode.  It is called basically
as indicated in the program.  the third param '', is for when we load
the BGI files externally.  To do that, do not include BGIVIDEO and
provide a path to the BGI files....to get a full auto-detect
capability, just make all the BGI files available....it is easier in
the long run to have the BGI files combined in the exec.

{5} For almost any graphics function, a variable called graphresult is
changed.  There are graphics mode constants, which will be covered
later, which are in there representing different things.  grok is the
one which indicates that things are OK.

{6} This is the real meat of the procedure.  It keeps placing pixels
of random position and color on the screen until a key is pressed.
getmaxx is the maximum screen position on the x axis.  getmaxy is the
maximum screen position on the y axis.  Graphics screens have the same
kind of dimensional setup as the crt graphics screens do.  The
putpixel procedure takes a x, y coordinate, then a color...getmaxcolor
is a constant that holds the maximum # of colors present in the
current video mode.

{7} Closegraph is pretty much self-explanatory.  It shuts down
graphics mode and takes us back to text.

BGI Font Usage
==============
The CHR files you saw, are font files, which have an ability to be
used in graphics programs....

These files can be converted to OBJs, and I recommend that you do so
for purposes of using the fonts.....

Here is a small example of loading and using fonts -- I'm not
repeating the video load code, so I will omit that....

program video_example; uses graph, bgivideo;

var
  graphicsdriver, graphicsmode: integer;
  i: integer;

{1} {$L GOTH.OBJ}
procedure Gothfont; external;

procedure errormessage(driver: string);
  begin
    writeln('There was an error: ', grapherrormsg(graphresult),
driver);
    halt(1);
  end;

begin
  { This is the video load code }

{2}  if registerbgifont(@gothfont) < 0 then
      errormessage('Script font');

    i := 100;
{3}  settextstyle(DefaultFont, HorizDir, 1);
{4}  setcolor(blue);
{5}  outtextxy(20, i, 'This is the DEFAULT font.');
{6}  inc(i, textheight('T')+2);
     readln;
{7}  cleardevice;
     settextstyle(Gothicfont, horizdir, 2);
     setcolor(green);
     outtextxy(20, i, 'This is the GOTHIC font.');
     readln;
     closegraph;
end.

This basically goes into graphic mode, and writes those two statements
to the screen.  We will go through the areas marked by {}'s..

{1} This is exactly like I described before.  This is how we load the
CHR file to not make it separate.  We do it exactly like the BGI
files, and set them up as external OBJ files.

{2} registerbgifont works exactly like registerbgidriver does...it
registers the font into the program -- load fonts judiciously -- only
if you need them.

{3} settextstyle() changes the font, direction, and size....things can
be written out either horizontally, or vertically.  Fonts letters are
8X8 pixels in size...so it is also possible to zoom the fonts...the
third is the factor in which it may be done...a 2 in the third
parameter makes the letters 16X16 pixels, and so on and so forth.

{4} setcolor(blue) sets the foreground graphics draw color to blue.

{5} outtextxy() puts out a text statement at coordinate x, y for
graphics mode.

{6} textheight is a function which guages the height in pixels of a
text placed in the statement.

Font file names
===============
DefaultFont; TriplexFont; SmallFont; SanSerifFont; GothicFont;
represent   fonts.
HorizDir; VertDir; represent the directions of text.

A VERY QUICK overview of commands available from BGI
====================================================
Due to the volume of commands available, all of them can not be
sufficiently covered in the space of this document.  Most if not all
of them are straight-forward to use.  Look at page 185 of the Turbo
Pascal language guide for a list of all of the BGI commands.

Conclusion
==========
IMO, Borland made the BGI very hard to use.  I have stumbled across
many things that looked like bugs in their system (I couldn't use
their included script font).  Beyond that, it works OK for light-duty
graphics.  Anything beyond that truly needs assembler.

There is enough knowledge here to set up and make use of BGI (not
withstanding the basic commands in that list -- for example,
rectangle.... draws a rectangle....).

As another side note, you may have noticed that executables you create
using methods described here are large.  Get a program such as PKLITE,
or LZEXE, and compress it.  They compress down about 45-55%, in my
experience.  Graphics programs using BGI seem to characteristically
compile to be large.

Practice Programming Problem #21
================================
Make a program which will successively place rectangles on the screen
in random colors.  Since it is hard to illustrate what I'm wanting,
given this medium, I will place the final executable, named part21.EXE
in the file at Garbo.   Cut and paste the document after you save
this, please. Here are the basic stats behind the program:

1) Squares are used.  use the rectangle() function.  it takes for
arguments, the coordinates of the upper left hand corner, and the
lower right hand corner.
2) Each square drawn successively is one pixel larger than the
previous....
3) Continually draw the squares until the user presses a key.
4) The colors are randomly determined.

Look at the program to get an idea of what I am looking for.  Also
please send comments back to ggr...@2sprint.net if it happens to not
work on your system for no readily apparent reason.  I need to get an
idea of how well these sample video routines work.

Next Time
=========
Things will be indeterminate.  I will be covering object-oriented
programming.  As I have not set down and figured out how many parts
object-oriented programming will take to cover, I do not know.

E-mail ggr...@2sprint.net and suggest anything that has to do with TP,
which I may have not covered.  If it sounds good, I will cover it!

Glenn Grotzinger
mailto:ggr...@2sprint.net
MOD and S3M user extraordinaire.
Writer of TP tutorial.  All released parts findable at:
ftp://garbo.uwasa.fi/pc/turbopas/tptutr0h.zip