Dec 052017
 
TP 5.0+ programs that allow you to examine Borland's BGI graphic libraries (.BGI and .CHR). Includes full source code.
File BGICHR.ZIP from The Programmer’s Corner in
Category Pascal Source Code
TP 5.0+ programs that allow you to examine Borland’s BGI graphic libraries (.BGI and .CHR). Includes full source code.
File Name File Size Zip Size Zip Type
BGI-TEST.PAS 9507 2973 deflated
CHR-TEST.PAS 2864 998 deflated
INTERNAL.DOC 9203 3850 deflated

Download File BGICHR.ZIP Here

Contents of the INTERNAL.DOC file


BGI-TEST and CHR-TEST were written by Sam Denton, St. Louis, MO,
CompuServ ID 76314,1512.

The programs and this documentation are Copyright 1988 by Sam Denton,
all rights reserved. Private use is permitted, otherwise call me.
(My rates are reasonable, and/or you can hire me if you like my code.)

----------------------------------------------------------------------------

NOTE/WARNING:

If, like me, you make the .BGI and .CHR files ReadOnly, you should insert
"System.FileMode := 0;" before calling InitGraph. I spent an entire night
tracing the code before I realized what I had done to myself.

----------------------------------------------------------------------------

BGI-TEST.PAS

To use with DOS DEBUG, try "DEBUG C:\TP4\TURBO.EXE C:\MYPGMS\BGI-TEST.PAS".
At the DEBUG prompt, enter the "G" command. BGI-TEST will stop of its own
volition at the start of the .BGI driver if you remove the "$" from the
"{$define trace}" at the start of the program. Use the "RIP" command to
manually advance the program counter past the "INT 3" instruction.

All .BGI files start with a 2 byte signature = 'pk' (checked for by code in
the Graph unit), followed by two backspace characters. For comparison, .CHR
files start with 'PK', and are otherwise similar. BTW, I presume that 'pk'
is Philipe Kahn, and the backspaces are so you can TYPE the files and be
able to read the comments without seeing the 'pk'. Then comes free-form
ASCII comments ending in Ctrl-Z, then:
dw offset within file to the start of code
dw driver id, a value from 0 to 5,
0=CGA, 1=EGA/VGA, 2=reserved(?), 3=Hercules, 4=AT&T, 5=PC3270
dw size of just the code, NOT the entire file
db a non-zero value
db zero
db either 0 or 1
db dup(0) until start of code

GRAPH normally does a FAR CALL to the start-of-code address (normalized so
that the offset is 0) with a value in SI determining the type of call.
The value is always even, so the driver may use it to index a branch-table.

All numbers from here on are given in HEXADECIMAL, and all offsets are
taken from CGA.BGI and are relative to start-of-code.

org 0111
dw ? ; cp.x
dw ? : cp.y

BitBlt code is at 0EAC, it is invoked at 0F60, it is really weird stuff.

In the following table, for each legal value of SI is shown the offset into
the driver's code of the routine. Also, if there seems to be a direct
correspondence between the routine and a GRAPH entry point, the name of the
entry point and how the arguments are passed is shown. Some GRAPH calls
turn into a pair of calls, first a MoveTo to set the X and Y parameters,
then a call to pass the rest of the parameters. In this case, the first
two parameters are shown as CP.X and CP.Y. During InitGraph, the driver
gets a CALL FAR instruction stuffed into offset 0010. The CALL FAR points
back into GRAPH and will execute many complicated "primitives" by doing
recursive calls back into the driver. This means that if you write your
own graphic device driver, you don't have to write 'hard' routines.

si addr name (if any), args :- results
-- ---- ------------------------------
00 01D7 AL=1 :- CX=nbr of modes?
AL=2,CL=mode :- [ES:BX]=string(name of mode)
AL=?,CL=mode :- [ES:BX]=descriptor table, other stuff happens
02 0231 :- set graphics hardware to mode
04 0248 ClrScr?
06 0015 ?No Operation
08 0297 MoveTo(AX,BX)
0A 029F LineTo(AX,BX)
0C 02B3 Line(AX,BX,CX,DX)
0E 0010* Do Polygon [ES:BX]=point list, CX=nbr pts, ?=draw or fill
10 0010* Bar3D(cp.x,cp.y,AX,BX,CX,DX)
12 07FE* Bar(AX,BX,CX,DX)
14 0010* Ellipse(cp.x,cp.y,AX,BX,CX,DX)
(this entry point is also used by Arc and Circle)
16 0010* PieSlice(cp.x,cp.y,AX,BX,CX)
18 0015 ?No Operation
1A 00BD SetBkColor(BX)
1C 00DE ?SetColor(BX)
1E 0286 SetColor, AL=AH=color
20 00E3*?SetFillStyle/Pattern, AL=pattern nbr, or if FF then [ES:BX]=pattern
22 03D2 SetLineStyle(AX,BX,CX)
24 0398* SetTextStyle AL=direction, BX=8*scale, CX=8*scale
When AH has bit 80 on, DX is also significant
26 035C* OutText @ CP, [ES:BX]=string, CX=length
28 00FD* TextSize [ES:BX]=string, CX=length :- BX=width, CX=height in pixels
2A 0015 ?No Operation
2C 009F FloodFill(AX,BX,CX)
2E 0FDC GetPixel(AX,BX) :- DX
30 0FF5 PutPixel(AX,BX,DL)
32 006C really strange, see below
34 0084 GetImage CX=x1,DX=y1,[ES:BX]=image(dx,dy,dup(?))
36 0099 PutImage AL=BitBlt,CX=x,DX=y,[ES:BX]=image(dx,dy,data...)
38 0050 SetViewport(AX,BX,CX,DX)

SI=32 returns [ES:BX] pointing to a table of offsets into the code that GRAPH
uses as additional entry points. The code GRAPH uses is kludgy, but the net
effect is to simulate CALL [ES:[ES:BX+n]]. There are only 6 entries and
most of them point to RETF instructions, but [ES:BX+8] will return the number
of bits per pixel in the current graphics mode. However, in EGAVGA.BGI only,
[ES:BX+0A] does some sort of segment arithmetic involving display memory. In
any event, SI=32 is used by GetImageSize and SetActivePage

When InitGraph is called, GRAPH calls the device driver seventeen times with
SI= 00, 02, 24, 38, 08, 1E, 1E, 1E, 1E, 20, 24, 1E, 1E, 20, 22, 24, and 08,
and only Frank Borland knows why. SetGraphMode issues the same sequence of
calls. Also, when LineTo or LineRel is called, GRAPH calls SI=08 (MOVE! to a
new place), SI=0A (draw a line to the OLD! point), SI=08 (move back to new
point), even though a single call to SI=0A (draw a line to the new point)
would have the same net effect. OutText and OutTextXY both call SI=08 TWICE
in succession with IDENTICAL arguments before writing out the string. I
guess they want to be absolutely certain the text lands in the right place.
(As nearly as I can tell, both GRAPH and the .BGI drivers maintain their own
copies of the current position, but the copy in the driver is used to pass
positioning data when Bar3D, Arc, PieSlice, etc. are called. Only the
current position kept by GRAPH matches the documentation.)

(P.S. If, like me, you make the .BGI files ReadOnly, you should insert
"System.FileMode := 0;" before calling InitGraph. I spent an entire night
tracing the code before I realized what I had done to myself.)

----------------------------------------------------------------------------

CHR-TEST.PAS

All .CHR files start with a 2 byte signature = 'PK' (checked for by code in
the Graph unit), followed by two backspaces (which are not checked anywhere).
For comparison, .BGI files start with 'pk', and are otherwise similar. Then
comes free-form ASCII comments ending in Ctrl-Z, then:
dw offset within file to the start of descriptors
db driver id, four characters
GOTH=gothic, LITT=small, SANS=sans serif, TRIP=triplex
dw size of just the code, NOT the entire file
db one
db zero
db one
db dup(0) until start of descriptors

Within the descriptors, first is a ten byte area laid out as:
db unknown, apparently alway $2B
dw number of characters defined
db unknown, apparently alway zero
db the first character drawable with this character set
(usually blank ($20))
dw offset to table of plot commands
db unknown, apparently alway zero
db Y value of top of character cell (shortint)
db unknown, apparently alway zero
db Y value of bottom of character cell (shortint, usually negative)
db 5 bytes, unknown, apparently alway zero

Next is an array of words pointing to the plot commands:
dw 0,4,... ; offset to blank, exclaimation point, ...

Thirdly is an array of bytes containing the widths of each character:
db 6,3,... ; width of blank, exclaimation point, ...

Finally come the plot commands. Each consists of 16 bits broken into several
fields. The high order bit ($80) from both bytes are used to decide what to
do. The cases are (expressed in binary):
1xxxxxxx 0yyyyyyy -- move to (x,y)
1xxxxxxx 1yyyyyyy -- line to (x,y)
0xxxxxxx 0yyyyyyy -- done, (x,y) is ignored
0xxxxxxx 1yyyyyyy -- no operation, (x,y) is ignored
The remaining seven bits of each byte are two's complement numbers. Each
character is drawn within a box with the leftmost portion and the base of
the image lying on the y and x axii, respectively. Lower-case descenders
and some punctuation will drop below the x axis.

The first data area is of fixed size at the start of the 'segment', so
addressing it is no problem, and the second data is also easy to address,
although it is of variable size. The third and fourth areas are more
problematical. To calculate the offset of the third area, use the formula
2*(number of characters defined) + 16, and then index to the needed byte.
To find the plot commands in the fourth area for a given character, c, use
2*(ord(c) - (first char defined)) + 16 to get the offset of a word whose
value is then added to the offset of the table of plot commands. See the
program for examples of Pascal code. It's actually somewhat easier to do in
assembler (or C), however.


 December 5, 2017  Add comments

Leave a Reply