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 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!