[Back to TUTOR SWAG index] [Back to Main SWAG index] [Original]

                           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;

    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 with-
    standing 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 ggrotz@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 program-
    ming.  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 ggrotz@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!

[Back to TUTOR SWAG index] [Back to Main SWAG index] [Original]