@CHAPTER TITLE = Working with PCX Files

@FIRSTPAR = The Picture file format used by ZSoft for their PC Paintbrush 
paint program has become one of the more popular formats around. I 
personally took an interest in it originally because I needed to get 
diagrams into Ventura Publisher; which not only accepts .pcx files, 
but will also expand and contract them sensibly.

The format turned out to be easy to work with.  The files can be loaded 
and written quickly, using a set of simple functions.  Screen update 
is fast, and the screen can easily be panned around a large picture. 
Drawing operations for multi-color formats aren't as fast, but that's 
due to the data structure that I've chosen. 

I'd like to start by describing PCX pictures in general terms.  The 
Header and Color palette are complicated enough to deserve some more 
detail.  I'll finish with a description of the modules that I've written 
to generate, write, read and display PCX pictures.  You'll probably 
want to make many improvements to them, but I'm confidant that they'll 
get a PCX related project off the ground for you. 

@MAJOR HEADING = The Basic Structure

A PCX file consists of a 128 byte header, followed by one or more 
compressed picture planes.  In memory, each plane is basically a bit 
map, with each bit usually corresponding to a pixel on the screen.  The 
number of bits per pixel, pixels per byte, bytes per line, lines per 
plane and planes per picture are all stored in the header.

On the EGA board, there are four planes (see figure 1.) Bit (X,Y) 
in each plane corresponds to pixel (X,Y) on the screen.  The binary 
value of the four (X,Y) bits in each plane is used to index into the 
color palette. On the CGA & HERC boards, only 1 plane is used.  For 
the 720x348 HERC and 640x200 CGA modes, each bit in the plane corresponds 
to a single pixel on the screen.  Using the 320x200 by 4 color CGA 
mode, there is again only one plane, but each pixel is made up of 
two adjacent bits.

It's possible to make a 1000x1000 by 2 color drawing which can be 
displayed on a CGA board 640x200 dots at a time, then show it on a 
Hercules board 720x348 dots at a time, but a picture isn't always 
so portable. A 320x200 CGA picture has to be translated before it 
can be shown in another mode.

As you can imagine, drawing the picture on the screen is easy <197> 
all that's needed is a block move of each line of the picture to the 
VRAM on the board. The trade off is that there has to be some interpretation 
of the header information, and the pictures don't look quite the same 
from one board to another due to changes in dot width and height.

@MAJOR HEADING = The Header

The structure for the header looks like this:

@LISTING = typedef struct {<R>
   unsigned char red, green, blue;<R>
} TRIPLET;<R>
<R>
typedef struct {<R>
   char maker,<R>
 	version,<R>
	code,<R>
	bpp;<R>
   int	x1, y1, x2, y2,<R>
   	hres, vres;<R>
   TRIPLET triple[16];<R>
   char vmode, <R>
   	nplanes;<R>
   int	bpl;<R>
   char __unused[128-68];<R>
} PCXHDR;

The fields<B> maker<D>, <B>version<D>, <B>code<D> and <B>vmode<D> 
all currently have fixed values <197> 10, 5, 1, & 0. They represent 
the code for the program that generated the picture, its version number 
and the type of compression that was applied to the picture planes; 
<B>vmode<D> is currently ignored; and <B>__unused<D> is reserved for 
future expansion.

<B>hres <D>and <B>vres<D> represent the number of dots, horizontal 
and vertical, on the video board to be used.  <B>nplanes <D>is the 
number of picture planes used,<B> bpp<D> is the number of bits per 
pixel and <B>bpl<D> is the number of bytes that are used to store 
a horizontal line of the picture. (<B>x1,y1<D>)-(<B>x2,y2<D>) refer 
to the upper left and lower right corners of the picture.  For a standard 
640x350x16 EGA picture, we would get the following: <B>hres<D>=640, 
<B>vres<D>=350, <B>x1<D>=0, <B>y1<D>=0, <B>x2<D>=639, <B>y2<D>=349, 
<B>nplanes<D>=4, <B>bpp<D>=1 and <B>bpl<D>=80. 

It's interesting to note that PC PaintBrush seems to take these numbers 
very seriously.  If a picture is made after PaintBrush is set up to 
use a HERC board, it won't even be recognized as a picture file if 
PaintBrush is then set up to use an EGA board. That's a bit picky 
considering that a HERC picture is black and white, and an EGA board 
supports those colors.

So, if you plan to use the pictures that you generate on some existing 
program, be careful to model your picture after one of the various 
video board modes.  To make a picture with a different size, only 
(<B>x2, y2<D>) and <B>bpl<D> would have to change; and the easiest 
way to add colors would be to add planes.  Although the software that 
I've written only supports a few of the standard configurations, it'll 
probably be easy to modify.

@MAJOR HEADING = The Color Palette

The palette information is stored in the array of 16 triples.  It's 
the only part of the PCX format that's really tricky <197> being totally 
board dependent. Perhaps Shannon at ZSoft can send Dave a letter explaining 
it better.  Here's the way I understand it:

The IBM EGA adapter has 16 single byte color registers, each of which 
holds a two bit intensity value of from 0 to 3 for each color gun 
(Red, Green, and Blue.) The intensity values are stored as pairs of 
bits, like this: "xxRGBrgb".  The first two bits, 7 & 6, are ignored.  The 
next three bits are the high bits of the intensity values for the 
Red, Green & Blue guns.  The last three bits are the low bits of the 
same intensity values. This allows each of the 16 registers to contain 
one of 64 possible color combinations.

The PCX header has a 16 element palette table, one element per color 
register.  Each element is a three byte structure (TRIPLET) which 
holds an intensity value for each color gun.  To set the color for 
any given palette register, the Red, Green & Blue intensity bytes 
are set with the desired intensity for that color multiplied by 85.  (I 
have no idea why we multiply by 85.) To set the EGA palette register, 
we take the intensity value, divide by 85, twiddle the bits, then 
take the easy route to the palette register by calling the BIOS Set 
Palette function.

The CGA board has a Color Select Register at I/O location 0x3d9.  The 
bit configuration for this register is "xxCAIRGB".  The first two 
bits are ignored.  The C bit selects one of two pre-defined palettes, 
called Color Sets by IBM.  Set 1 is Background, Green, Red & Brown.  Set 
2 is Background, Cyan, Magenta & White.  The Background color is one 
of 16 colors formed using the IRGB bits.  I is an intensity bit, R, 
G & B are enables for the Red, Green and Blue color guns, respectively.  If 
the A bit is set, it "Selects (an) Alternate, Intensified set of colors 
in Grapics Mode" (see ref. 1.) To me, this translates into 4 color 
sets and one of 16 background colors.

Storage of the CGA palette settings in the PCX header is simple: The 
first byte of the first element in the palette table contains a background 
color, multiplied by 16.  The first byte of the second element in 
the palette table apparently contains a three bit value, multiplied 
by 32.  (I haven't been able to figure out what to do with the third 
bit.) At any rate, there are 8 such settings in the PCX headers palette 
table, but it seems to me that only one of them can be in effect at 
a time.

There are probably many more palette formats for other boards, but 
I don't know of any documentation for them.  In the code that accompanies 
this article, the pcx_set_palette() function has a load_palette flag 
which must be set in order to get the routine to tinker with the palette 
registers.  I didn't bother much with the palette for a number of 
reasons; the first one being that the palette is automatically set 
each time a graphics mode is selected; another being that Ventura 
Publisher ignores it. 

@MAJOR HEADING = Compressing a Plane

When in a file, each plane in the picture is compressed.  The compression 
is very simple; designed simply to take advantage of the fact that 
a byte value will often repeat. 

The read byte algorithm works like this: Read a byte.  If the two 
high order bits are not set ((byte & 0xc0) != 0xc0), then return the 
byte. If the two high order bits are set ((byte & 0xc0) == 0xc0), 
then the remaining bits represent a repeat count (count = byte & 0x3f) 
and the next byte is the value that repeats.  As such, the repeat 
count can never be greater than 0x3f, or 63.  The write byte algorithm 
is obviously the opposite operation.

@MAJOR HEADING = The Software

Originally, what I wanted to do was copy schematics into Ventura.  I 
sat in front of the system for many sleepless nights decoding DXF 
files, only to find in the end that the GEM files Ventura creates 
from the DXF files don't properly scale text.  

To make a long story short, I wrote most of a DXF to PCX converter, 
then (balking at the prospect of having to write a font editor, create 
fonts, and write the code to scale them,) scrapped the idea <197> 
it was less time consuming to just chop the schematics into small 
blocks. In the mean time, I'd written a (simple) board independent 
graphics package, several test routines, and a slide show program.  As 
you've just guessed, text is not implemented.

Everything was compiled using Manx Aztec 'C 86, V. 4.10a.  With the 
exception of the code that handles flicker on the CGA board, it should 
compile without complaint on most systems.  Note, however, that it'll 
only work in the large data model; not only because of the huge amount 
of RAM that it uses, but because of the way I accessed the VRAM.

There isn't enough space to list all the code, so I'll document the 
software here, and you can download the files from one of the following 
sources: The file "$$$PCX.ARC" will be on the MicroC BBS long before 
you see this; and I'll put it in one of the IBM forums on Compu-Serve, 
in thanks to the many people who put together the EGA.ARC file (see 
ref. 2.) Finally, there'll be a copy in the LISTINGS/IBM.ARC section 
of BIX. 

@MAJOR HEADING = The Functions

Basically, the functions can be divided into three groups.  The lowest 
level is the board level driver modules.  There's one for the HERC, 
EGA and CGA boards.  To make it easy to use, the PCX module is set 
up as just another video board.  Each module has the same collection 
of functions, each with the same name, although the function name 
is prefixed with the board name to keep them distinct.

The next level is what I call the VGR modules, for Video GRaphics.  It's 
a simple interface between the driver modules and the next higher 
level. This level is based on a bunch of macros to allow access to 
the driver level while still being able to switch drivers at run-time.  Using 
these macros, object draw functions can be written which are independent 
of the low level drivers.  So far, I've put in Bressenham's line drawing 
algorithm and an inefficient but functional irregular polygon fill. 
There's a circle drawing algorithm in the May '83 issue of Doctor 
Dobbs Journal which is likely to go in next.

The highest level is the Picture level.  The functions at this level 
operate on the following picture structure:

@LISTING = typedef struct { <R>
   PCXHDR hdr;<R>
   char **rows[4];<R>
} PCXPIC;

(I feel I should warn you that the following few paragraphs are exceptionally 
boring.  If you're not interested in how I chose to handle the pointer 
and bit twiddling details, skip to the next heading...)

The PCXHDR is obvious enough, but the rows array deserves some explanation. 
Figure 2 shows how I visualize it when drawing a 640x350x16 EGA-type 
picture.  A plane is a pointer to an array of rows, each of which 
is a pointer to an array of characters.  That array of characters 
contains the bits which constitute a horizontal line of pixels.

This approach is relatively inefficient in that it forces several 
32 bit pointer operations, but it's easy to work with.  As you can 
see, there's currently a hardwired limit of 4 planes, and the arrays 
are dynamically allocated. 

For all but the 320x200x4 CGA modes, we can read the bit at position 
(<B>x,y<D>) on a given plane using the following expression:

@LISTING = !!(pcx_cpic<197>>>rows[plane][y][x>>>>3] & (0x80 >>>> (x & 7)));

The global variable <B>pcx_cpic<D> is defined in the PCX module as 
a pointer to a PCXPIC picture (cpic == current picture) It's used 
to tell the pixel routines what picture to work on.  For the HERC 
board, or the 640x200x2 CGA mode, plane is always zero; the 640x350x16 
EGA mode requires that the operation be repeated once for each plane.  Since 
<B>rows[plane] <D>is a pointer to an array of rows, <B>rows[plane][y] 
<D>is a pointer to the Yth row.  All that's needed now is the byte 
to look at in the row, and the bit in that byte.  The low order three 
bits of <B>x<D> (<B>x<D> & 7) select the bit, and the remaining bits 
of <B>x<D> (<B>x<D> >>>> 3) become the byte offset for the row. The 
above expression would return 1 if the bit at (<B>x,y<D>) is set, 
or zero if it's not.

The 320x200x4 CGA mode is slightly more complicated since two adjacent 
bits are used to select a color.  The expression to return the color 
of the pixel at location (<B>x,y<D>) in that mode would be:

@LISTING = i = (x & 0x03) <<<< 1;<R>
color = (pcx<197>>>rows[0][y][x>>>>2] & (0xc0 >>>> i)) >>>> (6<197>i);

Since each byte represents 4 pixels, <B>x<D>>>>>2 is used as the byte 
offset for the row, and <B>x<D> & 0x03 is the number of the pixel 
in the byte. Multiplying that number by 2 gives <B>i<D>, the number 
of bits from bit 7 that we skip over to get to the first of the two 
bits that select the color of the pixel. 0xc0 >>>> <B>i<D> makes a mask 
for those bits. The big difference between this expression and the 
previous one is that we need a two bit result, so we can't use the 
!! operator to clean up after the bit-wise AND. Shifting to the right 
6<197><B>i<D> bits does the job.

The only difference between the two expressions above and the ones 
actually used in the EGA and CGA modules is that there aren't any 
planes.  The EGA board uses a plane register to enable each plane 
and all four planes use the same memory locations.  The HERC board 
has two pages, which can be thought of as unrelated planes.  I visualized 
each horizontal row of pixels as being an array of columns, so the 
arrays are called columns instead of rows.  The array of pointers 
to each row is dynamically allocated.  The pointers are initialized 
with sequential row addresses for the EGA board, and scattered addresses 
for the HERC and CGA boards. 

Although the pointer operations and bit twiddling never get more complicated 
than that, some of the expressions get a bit dense, and aren't much 
fun to read.  Fortunately, all the low level operations are already 
implemented; you can just throw them into your library and use them. 
With that said, let's get down to function names and parameter lists...

@MAJOR HEADING = Using the Functions

Note that the declarations here don't match the ones in the modules. 
Many functions that don't return anything are declared as returning 
int. Most parameters to most of the functions are not range-checked.

@PROTO = void allocf(char *p);<R>
void map_not(int *map,int len);

These two come out of my library.  allocf() checks the pointer p to 
see if it's NULL.  If so, it doesn't do anything.  If not, it calls 
free(). If free() returns an error code, an error message is produced, 
and the program quits.  map_not() inverts len ints in the bit-map 
pointed at by map. 

@PROTO = void cga_movmem(char far *s,char far *d,int n);<R>
void cga_peekb(char far *p);<R>
void cga_pokeb(char far *p,char b);

These functions are the same as movmem(), peekb() and pokeb(); except 
that they wait for a horizontal retrace to start on the CGA board 
before accessing it, to avoid flicker. The cga_movmem() function is 
a straight block move, so you'll have to check for overlapping blocks 
if you plan to move a source to a destination which are both in the 
CGA VRAM.

@PROTO = void ega_select_plane(int plane);<R>
int pcx_select_plane(int plane);

The EGA VRAM holds 4 planes, each of them mapped into the same memory 
space.  ega_select_plane() is used to enable one or more planes.  If 
it's passed a negative number, the absolute value of that number is 
used as an enable mask, ie: if <B>plane<D> == <197>0x0f, all 4 planes 
are simultaneously enabled.  This is handy when clearing the screen 
since a single setmem() call can clear all four planes! If <B>plane<D> 
is not negative, then it must be from 0 to 3, and only that plane 
is enabled. pcx_select_plane() selects a plane for use by the VGR_ROW() 
macro when drawing on a PCX picture. 

@PROTO = void ega_set_palette(char reg,char red,char green,char blue);

Sets one of the 16 EGA palette registers.  <B>reg<D> can have a value 
of from 0 to 15.  <B>red, green<D> and <B>blue<D> are the desired 
intensities for each color gun, for that <B>reg<D>ister.  They must 
each have a value of from 0 to 3.

@PROTO = int herc_set_page(int page);

The HERC driver will default to page 0, but either of the two pages 
can be selected using this function.

@PROTO = int *pcx_init(void);<R>
cga_init(void);<R>
ega_init(void);<R>
herc_init(void);<R>
<R>
int	VGR_HRES,<R>
	VGR_VRES,<R>
	VGR_NCOLORS,<R>
	VGR_NBPL; /* #bytes per line */

The _init() routines initialize all static local variables in a module, 
allocate any required space (if it hasn't been allocated by a previous 
_init() call,) and set up the VGR module to use the module that was 
INITed.  The VGR module, among other things, holds the four ints shown 
above.  The HERC and EGA modules always set these values, as they're 
constants.  The PCX module will set them only if pcx_cpic is non-null, 
therefore pointing to a valid PCXPIC picture.  The CGA module sets 
them after a VGR_MODE() call.  The _init() functions each return OK 
or ERROR, with ERROR usually meaning that an out of memory condition 
occurred. 

@PROTO = int VGR_MODE(int m);

Once the VGR module has been set up, using the appropriate *_init() 
function, the "mode" must be set.  Since the _init() call set up the 
array of pointers to functions in the VGR module, it's possible to 
use the #defined macros in vgr.h.  VGR_MODE() is one of those macros. 
<B>m<D> must be one of the following:

@LISTING = MODE_TEXT0 <197> text, 80x25<R>
MODE_APA0 <197> APA, 640x350x16<R>
MODE_APA1 <197> APA, 720x348x2<R>
MODE_APA2 <197> APA, 640x200x2<R>
MODE_APA3 <197> APA, 320x200x4

When drawing on a PCX picture, the mode is only used to tell the PCX 
module if it's producing a one or two bit per pixel picture; so available 
PCX modes are effectively MODE_APA0 or MODE_APA3.  Available EGA modes 
are MODE_TEXT0 or MODE_APA0; for the CGA it's MODE_TEXT0, MODE_APA2 
or MODE_APA3; and for HERC it's MODE_TEXT0 or MODE_APA1. ERROR or 
OK is returned, with ERROR indicating an invalid mode number. 

@PROTO = void VGR_CLEAR()<R>
void VGR_SET(int x,int y,int c)<R>
void VGR_CLR(int x,int y)<R>
void VGR_XOR(int x,int y,int c)<R>
int VGR_GET(int x,int y)<R>
void VGR_ROW(int r,char far *p,int n)<R>
void VGR_MOVE(char far *s,char far *d,int n)<R>
void VGR_PEEKB(char far *p)<R>
void VGR_POKEB(char far *p,int b)

These VGR_ macros allow access to the board independent functions. 
_CLEAR clears the screen. _SET sets the color of the pixel at (<B>x,y<D>) 
to <B>c<D>. Note that if <B>c<D> has a value of zero, it has the effect 
of "clearing" the pixel. It's a good idea to not confuse the two operations 
since the HERC module ignores the value of <B>c<D>. _CLR sets the 
color of the pixel to zero. _XOR does a bit-wise exclusive or of the 
color of the pixel with <B>c<D>. Again, the HERC module ignores <B>c<D>. 
_GET returns the color of the pixel. _ROW moves <B>n<D> bytes from 
location <B>p<D> to the VRAM row <B>r<D>. _MOVE, _PEEKB & _POKEB either 
call movmem(), peekb() & pokeb(), or cga_movmem(), cga_peekb() & cga_pokeb(); 
depending on the driver module in use.

@PROTO = void vgr_fill(int x,int y,int color);<R>
void vgr_line(int x1,int y1,int x2,int y2,int color);<R>
void vgr_rectangle(int x1,int y1,int x2,int y2,int color);<R>
void vgr_point(int x2,int y2,int color);

vgr_line(), _rectangle() and _fill() do what they sound like. vgr_point() 
does one of two things.  If <B>color<D> is -1, the values (<B>x2,y2<D>) 
are copied to the local static values (<B>x1,y1<D>).  If <B>color<D> 
is not -1, vgr_point() calls vgr_line(), then copies (<B>x2,y2<D>) 
to (<B>x1,y1<D>).  This makes it possible to draw lines between a 
series of points. 

@PROTO = int vgr_get_board(void);<R>
int vgr_mode(char mode);

vgr_get_board() attempts to figure out what kind of video graphics 
board is in use.  The return value is one of TYPE_UNKNOWN, TYPE_EGA, 
TYPE_CGA, TYPE_MDA or TYPE_HERC.  These values are defined in <B>vgr.h<D>. 
vgr_mode() calls the BIOS set graphic mode function; INT 0x10, AH==0. 
See ref. 2, page A-46 for more info on that BIOS call.

@PROTO = PCXPIC *pcx_init_pic(int hres,int vres,int nplanes);<R>
void pcx_free_pic(PCXPIC *pic);<R>
void pcx_invert_pic(PCXPIC *pic);

pcx_init_pic() dynamically allocates space for a PCXPIC structure, 
and returns a pointer to it. <B>hres<D> & <B>vres<D> are the number 
of dots horizontally and vertically; <B>nplanes<D> is the number of 
planes in the picture. All arrays are dynamically allocated, and a 
pointer to the result is returned. If an out of memory condition occurs, 
any allocated arrays are de-allocated, and NULL is returned. pcx_free_pic() 
is used to free up all the space allocated for a picture. pcx_invert_pic() 
inverts all the bits in each row of the picture.

@PROTO = int pcx_read_pic(PCXPIC *pic,FILE *fp);<R>
int pcx_getc(int *c,int *n,FILE *fp,int maxn);<R>
int pcx_write_pic(PCXPIC *pic,FILE *fp);<R>
int pcx_xputc(int c,FILE *fp);<R>
int pcx_putc(int c,int n,FILE *fp);

These functions operate on a file which has been opened by one of 
the STDIO buffered file open functions; <B>fp<D> is the file pointer 
returned by the open call. <B>pic<D> is a pointer to a PCXPIC picture 
structure. All these functions return OK or ERROR on failure.

pcx_read_pic() loads a picture from the file.  The pic structure can 
either be statically allocated, or dynamically allocated using the 
pcx_init_pic(0,0,0) call.  pcx_read_pic() automatically allocates 
whatever arrays it needs to get the picture in.  pcx_write_pic() writes 
the picture to the file.

pcx_getc() reads a character or pair of characters from the file.  The 
character is placed in the integer pointed at by <B>c<D>, and a repeat 
count of one or more is placed in the integer pointed at by <B>n<D>. 
<B>maxn<D> is the maximum repeat count that the calling function wants 
to receive. 

pcx_write_pic() calls pcx_xputc(), which counts the number of times 
it receives a given byte value, then calls pcx_putc() with that number. 
If pcx_xputc() is called with <B>c<D> == -1, the buffered byte value 
and count are passed immediately (flushed) to pcx_putc(). pcx_putc() 
writes the byte <B>c<D> to the file, using a repeat count of <B>n<D>. 
<B>n<D> may have a value of up to 32767; pcx_putc() will recursively 
call itself with <B>n<D><<=63 until the correct total has been written 
to the file.

@PROTO = void pcx_showpic(PCXPIC *pic,int hoffs,int voffs,int load_palette_flg);

This function uses VGR_ROW to copy rows from the <B>pic<D> picture 
to the video board driver module in use. <B>hoffs<D> is an offset 
from the start of each row in <B>pic<D>. <B>voffs<D> is an offset 
which is applied to the row number. These two offsets allow the screen 
to be panned around a large picture. The <B>load_palette_flg<D>, when 
set, allows pcx_showpic() to set the hardware palette registers from 
the PCXPIC palette table. Consider the palette functions to be unreliable.

@MAJOR HEADING = The Test Routine

As I'm writing this, PCX.EXE is bug free and running.  It's essentially 
a test routine which excercises the various modules. If executed without 
command line parameters, it tries to figure out what kind of video 
board is available. If a CGA/HERC/EGA board is found, it creates a 
simple test picture (see figure 3,) displays it, then returns to DOS. 


If it's invoked with a board name, CGA (640x200x2), EGA, HERC or CGA2 
(320x200x4); it creates a picture for that board, and tries to display 
it on that adapter. 

If it's invoked with a board name followed by a file name, the picture 
is saved, using the specified file name, after it's been displayed.

PCXSHOW.EXE, the slide show program, is in need of some upgrading 
<197> it was written before the latest improvements to the PCX/VGR 
modules. It'll be ready before you see this, and will be included 
with the other files.

@MAJOR HEADING = Bibliography

@BIBN = 1

@BIB = The IBM Personal Computer XT Technical Reference Manual, April 
1983 revision. Part Number 1502237.

@BIBN = 2

@BIB = The file EGA.ARC, which was found somewhere on Compu-Serve, 
probably in one of the IBM forums.

@BIBN = 3

@BIB = ZSoft Technical Reference Manual.  ZSoft Corporation, 1950 
Spectrum Circle, Suite A-495, Marietta, GA 30067.  Tel.  (404) 428-0008. 
Many thanks to Shannon.

@BIBN = 4

@BIB = A Hercules Primer, by Larry Fogg, MicroCornucopia, Jan-Feb 
1988, page 26.

@BIBN = 5

@BIB = Tidbits, by Gary Entsminger, MicroCornucopia, Jan-Feb '88, 
page 84.

@BIBN = 6

@BIB = Language Connections, by Gary Entsminger, Turbo Technix, Jan-Feb 
'88, page 136. 

@BIBN = 7

@BIB = MicroEMACS by Dan Laurence & others. Public domain text editor, 
with 'C sources. Available on BIX in LISTINGS/IBM.ARC.

