Stumpff's Thrown Together QB Graphics Tutorial, 7th edition

This is a recasting of what was originally a set of emails to a specific
individual to answer specific questions of his about QB and graphics.  I
have attempted to rewrite those messages (and add some stuff) and make a
more general tutorial.  However, being busy, I did not necessarily do as
complete a job as I might have if I had more time.

First, to do graphics in QB (or any other language/compiler/interpreter),
you need to put your screen/video system into a graphics mode.  If I don't
go into as much detail as you might like here, it's because you can get a
very good description of the video modes that QB can access by looking up
SCREEN, PUT, and GET in QB's (or Qbasic's) on-line help, and other
graphics topics are discussed there as well.  (Another reason is because I
didn't necessarily write this stuff because I know everything about QB and
graphics; I wrote it because I was asked to.)  I will, however, summarize
the video modes and their attributes here.  (If the bits per pixel and bit
planes per pixel parameters don't mean anything to you, don't worry.
They'll be explained eventually--at least to some degree.)


                         QB's GRAPHICS VIDEO MODES

                      HORIZONTAL
                          x
VIDEO MODE        VERTICAL RESOLUTION         BITS/PIXEL  BIT PLANES/PIXEL
     1 (CGA)           320 x 200                  2               1
     2 (CGA/mono)      640 x 200                  1               1
     3 (hercules)      720 x 348                  1               1
     4 (Olivetti/ATT)  640 x 400                  1               1
     7 (EGA)           320 x 200                  4               4
     8 (EGA)           640 x 200                  4               4
     9 (EGA)           640 x 350                  4               4 (> 64K
                                                             video memory)
     9 (EGA)           640 x 350                  2               2 (<= 64K
                                                             video memory)
    10 (EGA/monochrome
       monitor)        640 x 350                  2               2
    11 (VGA/MCGA)      640 x 480                  1               1
    12 (VGA/MCGA)      640 x 480                  4               4
    13 (VGA/MCGA)      320 x 200                  8               1


There is one video mode, number 0, that isn't listed in the above table.
This is a text mode; you can't do graphics in it.

Before performing any graphics function in QB, you need to go into a
graphics screen mode.  You use the SCREEN statement to do this:

SCREEN #

where # is one of the mode integers listed in the first column of the above
table.  You need to use a video mode that your computer/video system
supports.  However, unless you have a relatively old computer, you probably
have support for all of the listed modes except perhaps 3 and 4.  (There are
other uses of SCREEN.  If I get to it, I'll discuss some of them.
Otherwise, see your on-line help.)

Since the term "pixel" comes up quite often, I'm going to make a digression
and explain what that is just in case you really are a true beginner.  Any
picture (and even text, actually) that you have on the screen is an arrange-
ment of small dots that are lit up.  Those dots are called pixels.

The BITS/PIXEL parameter (call it BITS) relates to the number of different
pixel colors that can be on the screen at one time.  The actual number of
colors (call it MAXCOL) is 2^BITS (2 to the BITS power).  For example,
SCREEN 12 allows 16 colors to be displayed and SCREEN 13 allows 256.  Colors
are represented by integers, called attributes.  An attribute is in the
range 0 to MAXCOL - 1.  (A lot of quantities in Basic and other languages
are counted from zero, not one.  And for now, I'm going to act as if "color"
and "attribute" are synonyms.  They are NOT, but don't worry about it right
now.)

The BIT PLANES/PIXEL parameter (call it PLANES) relates to how the graphic
data is stored in video memory.  There's two reasons why you might want to
be concerned with such a detail.  One is if you want to bypass QB's
intrinsic graphics functions and access video memory yourself.  To do that,
you're going to need a lot more information than I'm planning on giving you
here.  (For one thing, I don't *have* all of the information you would
need.)  The other reason you might need to worry about such nitpicking
details is if you want to save a picture on the screen into an array.
Later, I'll get to how to use the parameters in the above table to calculate
how big that array has to be.

Another factor that may affect your choice of video mode is the resolution.
In the above table, the horizontal resolution is the number of pixels you
can put on a single row and the vertical resolution is the number of rows
of pixels that you can have on the screen.  And this gets us to coordinate
systems.

All of QB's graphics functions work in a rectangular (also called
"cartesian") (x,y) coordinate system.  If the resolution is W x H, x ranges
from 0 to W - 1 and y ranges from 0 to H - 1.  Further, x and y are
integers.  (A coordinate of (6.5,8.9) doesn't make any sense, for example.)
x increases to the right and y increases downwards.  (0,0), the coordinate
origin, is at the top lefthand corner of your screen.  (The WINDOW function
can be used to make y increase upwards or even to allow negative (x,y)
coordinates.  However, this function is another one of the things that keep
me from claiming to be a true graphics expert.  It's one of the things I'm
not going to discuss further here.  The VIEW function can be used to change
the location of the coordinate origin--and constrain the maximum number of
pixels you can put across or down in your picture.  I'll discuss VIEW if I
get to it.)

Okay, we finally get to how to put pixels on the screen.  QB's most
primitive or fundamental functions for doing that are PSET and PRESET.

PSET is used simply to plot a single pixel on the screen.  (But you can of
course use PSET over and over again.)  The syntax is simply

PSET (x,y), attribute

where attribute specifies the color of the pixel to be plotted.  PRESET
serves the same basic function as PSET and has the same syntax.  It simply
plots the pixel in "inverse video."

PSET and PRESET are most useful for generating pictures that can't be
described by simple mathematical shapes such as circles, lines, or boxes.
Although you can certainly use PSET to draw such shapes, QB has "higher
order" functions to do this:  CIRCLE and LINE.

CIRCLE draws circles (if you couldn't guess).  But it also draws ellipses
and arcs.  The syntax is (and this is all from the on-line help, to which
you are referred for anything I left out)

CIRCLE (x,y), radius[, [color][, [start][, [end][, aspect]]]]

The [] symbols mean that these parameters are optional.  You can leave them
off if you want to use default values.  (The default color is the
foreground color--set by the COLOR statements; I guess I'll have to get to
that later.  (x,y) gives the screen coordinates of the center of the circle.
start and end are angles in radians that specify the beginning and ending
angles that define the arc.  (It only draws a closed circle if the
difference between end and start is 2 * PI.  If you don't specify start and
end, the default values are 0 and 2 * PI--i.e., you want to draw a closed
circle/ellipse.)  aspect specifies the shape of the figure.  The default is
is the value required to draw a "perfect" cirular arc (as opposed to an
elliptical one).  This value is defined as 4 * (H / W) / 3.  (Why doesn't
aspect = 1 give a circular arc, you ask?  Because screen pixels are actually
rectangular, not square.)  The radius parameter specifies the radius (in
pixels) of a circular arc.  For nonunity aspects, radius is the x-radius if
aspect < 1 and it's the y-radius for aspect > 1.

You can also make a pie-shape with CIRCLE.  From the on-line help, I quote
(I'm making use of the "fair-use clause" in the copyright law ):

            If start or end is negative, then CIRCLE draws a
            radius to that point on the arc and treats the angle
            as if it were positive.

In using CIRCLE, if you want to leave out an optional parameter in the
middle but specify a later one, make sure you put a comma in the location
where you're leaving out a parameter (i.e., you may have two or more commas
in a row).  (If you're leaving off the last parameter, don't include what
would otherwise be the trailing comma.)

LINE draws lines and boxes (and it will also fill the box with the color
it's drawn in if you want).  The syntax is (again from the on-line help)

LINE (x1,y1) - (x2,y2)[, [color][, [B[F]][, style]]]

Again the [] symbols refer to optional parameters.  The default color is
again the current foreground color.  If you leave out the fourth parameter
(B or BF), you get a line.  Well, actually, it's a line segment.  (x1,y1)
give the coordinates of the beginning of the segment and (x2,y2) specifies
the end.  If the fourth parameter is B, you get a box. (x1,y1) then
specifies the upper lefthand corner and (x2,y2) specifies the lower right-
hand corner coordinates.  If you say BF, you still get a box (and the first
two parameters have the same meaning), but the box is filled with pixels (of
the specified/default color).

I'm discussing the style parameter in a separate paragraph because it's a
little tricky and might need special/separate attention.  This parameter
specifies how the line segment is to be drawn--solid, dashed, dotted, dash-
dot, whatever.  It is a two-byte integer.  The 16 bits in that integer
specify the pattern.  The default style is -1, which gives a solid line
segment.  Why?  Because -1 converted to binary is simply 16 ones,
1111111111111111.  A bit in the bit pattern corresponds to a pixel in the
line segment.  (If the line segment is more than 16 pixels long, just
imagine the bit pattern in style repeating itself.)  If the bit is unity,
the corresponding pixel is lit.  If the bit is zero, the pixel is not lit.
(By the way, a lit pixel simply means that the attribute is nonzero.)
Hence, in the -1 bit pattern, all pixels are to be lit--you get a solid
line.  If, for example, style = -13108 (or &HCCCC, if you know how to use
hex numbers), you get a dashed line segment--because that integer has the
binary representation 1100110011001100.

Obviously, the tricky part here is knowing how to convert binary numbers to
decimal or hexadecimal.  QB has the functionality to convert hex to
decimal--just put &H in front of the hex number.  But curiously, since an
advanced use of LINE requres reference to the binary number representation,
it does not have a binary number converting function.  It is, however, not
hard.  Here's how you convert a binary number to decimal.  Let's say you
have a binary number with N bits in it.  These bits are numbered/labelled
from 0 to N - 1.  Bit 0 is at the right and bit N - 1 is at the left.  Let
i refer to this bit position (i = 0 to N - 1).  Let the value of the
particular bit (0 or 1) be b.  The decimal representation of the binary
number is the sum of all quantities calculated as

b * 2^i.

For the style option of LINE, N = 16.  One snag is that the method of
converting binary to decimal just outlined gives an unsigned integer between
0 and 65,535 for such a two-byte value.  QB uses *signed* two-byte integers.
Such integers lie between -32,768 and 32,767, inclusive.  So, if the above
binary to decimal procedure gives you a value larger than 32,767, subtract
65,536 from it before assigning the result of that calculation to a QB
INTEGER variable.  One thing you can do that will work no matter what value
your SUM has (but don't make SUM an INTEGER variable) is

DIM STYLEPARAMETER AS INTEGER
STYLEPARAMETER = (SUM + 65536&) MOD 65536&

and use STYLEPARAMETER for the style option, where I just used the & suffix
above to be safe and force QB to make a LONG (four-byte) integer calculation
and keep it from making one of the numerical conversions that it likes to
make automatically that sometimes causes overflows.

A more general area filling function than LINE's BF is provided by the PAINT
function.  And I'm going to tell you right now that I have not used very
much of its capabilities.  I'm only going to talk about what I understand
and leave the rest for the on-line help, or others who know more.  Let's
say there's some area (not necessarily rectangular) on the screen that you
want to fill with pixels of some color.  You can identify that area because
it has attribute "bordercolor".  (Perhaps you've just drawn a circle and you
want to fill the circle.)  The PAINT function is what you need:

PAINT (x,y)[, [paint[, [bordercolor] [,background]]]]

(If I need to tell you what the [] mean, you haven't been paying attention.)
The parameter paint is the color you want to fill the region with.  (x,y)
specifies the coordinates of a point inside that region (but not on the
border).  (Actually, (x,y) can be outside the region instead of inside.
Then, however, you'll be painting the outside, not the inside.)  The
default value for paint is the current foreground color, as before.
The bordercolor parameter that tells PAINT to quit painting.  As PAINT
alters the colors on a row of pixels, it stops when it comes to a pixel
already on the screen with attribute bordercolor.

Okay, that's the part of PAINT that I've used and can say I more or less
understand.  Here's what you have to look up in the on-line help or ask
someone else if you're interested.  First, background is a character string
that tells PAINT how to paint over pixels that are already on the screen.
(The default use for PAINT is to paint over black (0 attribute) areas.)  The
default value for background is CHR$(0).  I don't ever specify a value for
background; I just leave the parameter out.  (I didn't even know it existed
until I went to look up PAINT in the on-line help for the sake of adding
some stuff to this in my attempt to recast it as a more general tutorial.)
The other thing you might be interested in looking up/researching for
yourself is the other use of the paint parameter.  It can also be specified
as a character string (instead of a numeric variable/constant).  This is
PAINT's tiling option.  It's sort of like LINE's style option; it allows
PAINT to fill a region with patterns that you specify, instead of just
filling it with a single color.  But I can't tell you any more than that.

There are other aspects of the above graphics functions that I also am not
going to go into here.  In particular, the STEP option is one such aspect.
This option is used to make the specified coordinates refer to offsets from
the last pixel position plotted instead of absolute screen positions.  I
again refer you to the on-line help.

Okay, how do you define a default color?  Use the COLOR statement:

COLOR #

where # is an integer between 0 and MAXCOL - 1.  (Generally, 0 isn't a real
good default color to use--it's usually the color of your usually black
screen.  Of course, if you've filled the screen with nonzero pixels, maybe
a 0 default attribute is precisely what you want.)

Now, for the "more advanced graphics routines."

Let's do GET first.  This function is used to store the data comprising
a picture on the screen in an array.  Actually, all it does is copy the
pixel data in a specifiable rectangular area into the array.  You do not
need to transfer the whole screen.  (For some screen modes, considering
that you're likely going to want to use BSAVE to save the picture to disk--
more on that later, copying the entire screen to an array isn't a very
useful thing to do.  (It can be *made* useful, but it's just a little
tedious.)

Let's say you've got some picture occupying a rectangular area on the
screen.  Let (xl,yl) be the coordinates of the upper lefthand corner of
your screen area and (xr,yr) be the coordinates of the lower righthand
corner.  The first thing you need to do is determine the size of the array
that you need to use to store the picture.  It's good practice to use LONG
or INTEGER arrays to store pictures.  (Because of the way your computer
stores numbers, bits can be randomly altered slightly in SINGLE or DOUBLE
arrays.)  In an INTEGER array, each array element takes up two bytes,
and in a LONG array, each element requires 4 bytes.  Let BPE be the number
of Bytes Per Element (2 or 4).  In pixels, your picture's width is W =
xr - xl + 1 and its height is H = yr - yl + 1 (where I'm temporarily
changing the definitions of W and H from thier initial reference to screen
dimensions).  Further specification of how many bytes it takes to store your
picture requires knowing the parameters associated with the graphics mode
you're in.  These parameters are given in the above table.

BYTES = 4 + PLANES * H * INT( (W * BITS / PLANES + 7) / 8 )

The "4" out in front is because, in addition to your graphics data, GET
puts 4 other bytes in the array--at its beginning.  The first two of these
bytes correspond to W * BITS / PLANES and the second two bytes give H.
(Also, "BITS / PLANES" is also called, e.g., in your on-line help, "bits
per pixel per plane.")  The "+ 7" is there because the number of bits in
a "scan line" (one bit plane's worth of pixels in a row on your screen)
may not be a multiple of 8 (there are 8 bits in a byte) and the truncated
byte at the end needs to be padded with zeros to fill out the last byte
in that situation.  (GET does this automatically.)  The "/ 8" simply
converts the number of bits in a scan line to the number of bytes.

Okay, now you know how many bytes your array needs to allocate.  This needs
to be converted to the number of elements to specify in a DIM statement.
This is given by

SIZE = INT( (BYTES + BPE - 1) / BPE )

and then you would do either (where I'm using "PICT" as the array name, but
you can use whatever you want)

DIM PICT(1 TO SIZE) AS LONG

or

DIM PICT(1 TO SIZE) AS INTEGER

depending on whether BPE is 4 or 2.  (Actually, it is of course the other
way around; it's LONG or INTEGER that determines BPE.  Note that using
LONG arrays means less array elements.  If you actually do things this
way and use a variable to define the size of your array, your array will
be dynamic.  If you use an explicit number, you may find that you need
to use the '$DYNAMIC metacommand in order to be able to handle the necessary
array size.  Also, you can use zero-based arrays, e.g.,

"DIM PICT(SIZE - 1) AS LONG",

if you want.  I'm just more used to one-based arrays.)  If the rectangular
screen region you want to save is so large that it turns out that SIZE is
larger than 32,767, you have a minor problem.  QB won't allow more than
32,767 elements in any one dimension of an array.  In that situation, you
have to use a two dimensional array:

DIM PICT(1 TO SIZENEW, 1 TO M) AS LONG

where M is 2 or 3, depending on what I'll discuss in a minute, when I'll
also get to SIZENEW.  First, I used "LONG" here because if you're having
array size problems, you probably want to do what minimizes the number of
elements that you need.  But you can use INTEGER if you want to.  If you
do use LONG, M should never need to be larger than 2.  Otherwise, you might
need 3.  Here's how to find out.  First, divide SIZE by 2.  (If SIZE isn't
an even integer, add one to it before performing the division.)  If that
gives you a value less than 32,768, then that's SIZENEW and M = 2.  (And
these are the results you should get if you're using a LONG array.)  If
you're still getting a number larger than 32,767 after dividing by 2, then
you need to try M = 3.  This time, before dividing SIZE by 3, if SIZE isn't
an even multiple of 3, add 2 or 3 (whatever's necessary) to make it so.
Then divide by 3.  That should give a value of SIZENEW less than 32,768.

After you have the array defined, your picture is copied to it via

GET (xl,yl) - (xr,yr), PICT

Okay, your picture data is in the array.  How do you save that picture data
to a file?  You use BSAVE.  BSAVE copies data from memory to a file.  To
do that, you need to tell it where in memory your data is.  Well, it's in
the memory area where your array is stored.  That's a relatively easy
piece of information to get (it's real easy to say things like that when you
know how to do it ), but I will make a brief digression in the hope that
it will help you understand what the functions that I'm eventually going to
tell you how to use actually do.  In "real mode" (don't worry about what
"real" and "protected" mode means--it's real unimportant to what you're
doing and I'm no expert on it), computer memory is accessed by referring to
segments and offsets.  The notation is segment:offset (e.g., 0:449, where
those are hexadecimal numbers, is the address of where your current video
mode is stored).  Quite often, segment and offset are specified in
hexadecimal notation.  You don't really need to worry about that here.
QB works with decimal numbers, and segments and offsets mean the same thing
whether you use decimal or hex.

Okay, back to BSAVE and where your array is.  You need to tell QB the
segment and offset of your array.  That's a two step process--but they can
be combined somewhat.  First, you have to get the segment and point QB to
it.  You get the segment using the VARSEG function and you point QB to that
segment with DEF SEG:

DEF SEG = VARSEG( PICT(1) )

You can also do something like

SM = VARSEG( PICT(1) )

and then do

DEF SEG = SM

Then, you can get the offset in the process of BSAVEing your picture.  The
offset is obtained with the VARPTR function.  If "PICT.GET" is the name of
the file that you want to save your graphics data to, BSAVE can be used as

BSAVE "PICT.GET", VARPTR( PICT(1) ), BYTES

where BYTES is the number of bytes to save (starting at the beginning of
your array because you referred to the first index in PICT when you used
VARSEG and VARPTR).  (BYTES would normally be the quantity you calculated
above, when you went to find out how big your array needed to be.)  You can
also use VARPTR and BSAVE separately:

OFFSET = VARPTR( PICT(1) )
BSAVE "PICT.GET", OFFSET, BYTES

If you assign the results of VARSEG and VARPTR separately to variables
instead of explicitly using them in the DEF SEG and BSAVE statements, it's
a good idea to do this just before you use BSAVE.  Otherwise, i.e., you
define SEGMENT and OFFSET just after DIMming your array and then sometime
later on use BSAVE, at least for dynamic arrays, it is possible that your
array has moved (changed memory locations).  Further, BYTES must be 65535
(64K - 1) or less.  (*This* is why saving really large pictures, such as
the entire screen, can be a problem for some video modes.  Many of QB's
video modes, e.g., the useful ones) require much more than 64K.  It is also
why you won't often have the above mentioned problem of needing more than
32,767 elements in an array--you couldn't save such an array with a single
call to BSAVE.  (You could, on the other hand, just want the picture data in
an array, so you can put it back on the screen later, and not necessarily
need to save it to a file.))

It's also not a good idea to use DEF SEG to redirect QB's memory pointer and
then call a subroutine before using BSAVE (or doing whatever else you might
have used DEF SEG for the purpose of doing).  That subroutine may make its
own use of DEF SEG, undoing your use of it.  (Mouse routines tend to do that
to you.  Like using VARSEG and VARPTR, it's a good idea to not use DEF SEG
to redirect the memory pointer until just before you actually *need* to do
so.)  You can reset the memory pointer back to whatever its default location
is by just using DEF SEG without any segment specification (or equal sign):

DEF SEG

Once your picture is in the array, you can put it back on the screen using
the PUT function--perhaps after you've cleared the screen.  You can even put
it at a different location than it originally was (unless it filled the
entire screen).  Another use of PUT, however, is to put a picture on the
screen *after* loading it into an array from a file.  So, before discussing
how to use PUT, the opposite function of BSAVE is discussed.

BLOAD copies graphics data from a file to memory.  And like BSAVE, that
memory is generally set aside by DIMming an array large enough to hold the
picture data.  And that's the first hurdle.  You don't know a priori how big
of an array to make because you don't know a priori what's in the graphics
file.  The simplest thing to do is assume that the picture is not going to
need 64K or more of memory, unless you want to use BLOAD repeatedly (because,
again like BSAVE, you can't BLOAD more then 65535 bytes).  Then you could
just make a 64K array to hold your picture and it should be more than large
enough:

DIM PICT (1 TO 16384) AS LONG

(Later on, I'm going to show you how to actually figure out how many bytes
you really need to set aside with your array.  For now, let's just proceed
with the assumption that, somehow, you have an array big enough to hold the
picture you're BLOADING.  And again, note that I've used a LONG array to
ensure that the graphics data is accurately stored.)  Okay, the array exists
and is ready to have a picture put in it.  First, you use VARSEG and DEF SEG
to point Basic to the first of the memory segments that contain your array:

DEF SEG = VARSEG( (PICT(1) )

and you can use whatever name instead of PICT that you want to of course.
(But use the same one with DEF SEG that you used with DIM. )  Then,
you need to get the offset of the array and BLOAD the picture to the array.
As with BSAVE, you can do this in one step, where PICT.GET will be taken to
be the name of your graphics file:

BLOAD "PICT.GET", VARPTR( (PICT(1) )

Note that you do not need to actually be in any graphics mode when you use
BLOAD.  All you've done so far is put the picture data in an array.  (The
same goes for BSAVE too, by the way.  Once GET is done, you can switch to
SCREEN 0, for whatever reason that you might want to, before BSAVEing the
data to a file.)  You can also define SEGMENT and OFFSET variables using
VARSEG and VARPTR and then use those variables with DEF SEG and BLOAD.
(But, as before, you may not want to use VARSEG and VARPTR in that manner
until just before you need to.)

Okay, before moving on to PUT, let's see how we can complicate things (but
possibly in a way that proves useful, especially if memory gets to be
scarce).  When BSAVE was used to save your picture, in addition to the data
in your array, it put 7 bytes of "header information" at the front of your
file.  The last two of those bytes give the number of graphic data bytes +
the four for the width and height data, i.e., the minimum number of bytes
that you must reserve in your array.  How do you get to those two bytes
in your file, you ask?  It's really easy.  First, since you want to get just
two bytes, you need to work with an INTEGER variable (because such variables
take up two bytes).  Define one via

DIM ASIZE AS INTEGER

and then do the following, substituting whatever the true file name is for
the one used here (and you can use a different file number if #1 is already
in use).

OPEN "PICT.GET" FOR BINARY AS #1
GET#1,6,ASIZE
CLOSE #1

Now, except for one minor snag, ASIZE is the size of the array (in bytes)
that you need to define with DIM.  That minor snag is that, since you
defined ASIZE to be an INTEGER, it can't be larger than 32,767 (or smaller
than -32,768).  Now, the number of bytes is, by definition, a positive
number.  If the two bytes at positions 6 and 7 in your data file correspond
to a number larger than 32,767, when QB went and assigned the value of those
two bytes taken together to the INTEGER variable ASIZE, it subtracted 65,536
from what would otherwise have been the number of data bytes in the file.
So, to ensure that you work with a positive number, define the number of
bytes to reserve in your array as

BYTES = (ASIZE + 65536&) MOD 65536&

where BYTES is a variable that uses more than 2 bytes, e.g., a LONG
variable.  (It can also be a SINGLE variable, i.e., the kind that QB uses by
default when DIM or a suffix character hasn't been used to change it, or it
could even be a DOUBLE variable (8 bytes).  However, since it's referring
to a quantity that is mathematically an integer, I'd DIM BYTES AS LONG
first.)  The "MOD" is just the remainder function.  "A MOD B" means to
divide A by B and just keep the remainder, e.g., 7 MOD 6 = 1, 6 MOD 6 = 0,
5 MOD 6 = 5, and 9 MOD 6 = 3.)  Then, the maximum element to allocate in a
one-based array is, as before,

SIZE = INT( (BYTES + BPE - 1) / BPE )

where BPE = 4 if you're using a LONG array and 2 if you're using an INTEGER
array.  (SIZE is what you'd use in place of the "16384," above.)

Okay, you have the picture in the array.  You use PUT to copy it from the
array to your screen.  Before using PUT, you need to do a few things and
make a few decisions or determinations.

As discussed at the outset, you need to use an appropriate SCREEN statement
to change to a graphics mode.  That graphics mode must correspond to the
graphics mode involved in the creation of the picture that you just BLOADed
(i.e., you have to know something about the graphics files that you're
working with).  (However, you can sometimes get interesting effects by using
the wrong graphics mode, but they're not usually desirable effects.)

You need to know where on the screen you want the picture to go.  This
is specified via the screen coordinates of the upper lefthand corner of your
picture.  You need to take into account the width and height (in pixels) of
your picture also in determining this so you don't overflow the edge of your
screen.  (I'll discuss how to determine the size of the picture stored in
the array you just BLOADed into eventually here.  For now, let's get on with
PUT.)

You need to know what "action verb" you want to use with PUT, i.e., you
need to know how you want the pixel attributes in your picture to interact
with the attributes of any pixels that are on the screen.  (Generally, for
animation purposes, this action verb is XOR.  XOR and the other action
verbs are in fact described in your on-line help.  However, if you look
under "PUT," you may not find as good a description mathematically as
you'll find if you just look up the XOR operator.  The results of XORing
pixels (or ANDing pixels, ORing them, whatever) are the same as using
these operators as mathematical operations with numbers.  It is somewhat
unfortunate, however, that QB's on-line help chose to use true (T) and false
(F) concepts instead of bits (0s and 1s).  In your on-line help's table, F
means 0 and T means 1.)  The default action verb is XOR.  Another useful
action verb is PSET (not to be confused with the stand-alone PSET function).
The latter action verb causes PUT to just overwrite whatever's on the screen
where it's putting the picture.

Okay, you know all that and you're in the right video mode.  Here's how you
use PUT.  Let (xl,yl) be the coordinates of where you want the upper
lefthand corner of your picture to go.  The syntax of PUT is

PUT (xl,yl), array name, action verb

e.g.,

PUT (10,15), PICT, XOR

You may want to keep in mind that, just because you put the picture on the
screen, that doesn't mean it's going to stay there.  Subsequent QB
statements (such as CLS) may/will cause the screen to change.  If you want
to force the picture to stay there and not have your program do anything to
change or erase it (program termination, for example, will also generally
clear a graphics screen), you need to pause your program after you PUT the
picture on the screen.  One way of doing that is

WHILE INKEY$ = "" : WEND

This will suspend your program until you press a key.

Now, then, about finding out how big the picture is in an array after you
use BLOAD to put the picture data in it.  This data is stored in the first
four bytes of the array.  If you're using an INTEGER array, it's really
simple.  First, DIM two variables AS INTEGER:

DIM WPAR AS INTEGER, H AS INTEGER

Using the same array name as before, do the following:

WPAR = PICT(1)
H = PICT(2)

If you used a LONG array (or any other array type that isn't INTEGER--if you
didn't heed my warnings), it's somewhat more complicated.  (But it's doable;
don't panic.)  Just do the following (you don't need to worry about DIMming
WPAR and H AS INTEGER in this case--but you can if you want):

DEF SEG = VARSEG( PICT(1) )
OFFSET = VARPTR( PICT(1) )
WPAR = PEEK(OS) + 256 * PEEK(OS + 1)
H = PEEK(OS + 2) + 256 * PEEK(OS + 3)
DEF SEG

(PEEK is just a function that reads data bytes from memory; see your on-line
help.)

However you got WPAR and H, this is what you do with them.  H is the height
(in pixels) of your picture.  WPAR may or may not be the width.  It depends
on the screen mode that the picture was originally created in.  The width is

W = WPAR * PLANES / BITS

(See the above table.)  Since PLANES quite often equals BITS (such as in
SCREEN 9 or 12), W is quite often WPAR.  However, for example, W certainly
does not equal WPAR in SCREEN 13.  (The technique of PEEKing these
parameters out of the array will of course also work with an INTEGER array;
there's just no need to be clumsy in that situation.)

As mentioned before, coordinates used with QB's graphics functions are
normally referenced to the top lefthand corner of the screen and all
coordinates on the screen can have a pixel plotted on them.  The VIEW
function can be used to alter this situation.  (And don't confuse this with
the VIEW PRINT statement.  That is for a text viewport.  Of more interest
here is a graphics viewport.)  Again from the on-line help, the syntax is

VIEW [[SCREEN] (x1,y1)-(x2,y2)[, [color][, border]]]

VIEW sets up what is called a graphics viewport.  It is a smaller
(rectangular) region of the screen with top lefthand corner at (x1,y1) and
lower righthand corner at (x2,y2).  If color is specified, the rectangular
region is filled with that color.  (Otherwise the area is left unfilled--the
perhaps usual case.)  If border is specified, a box is drawn around the
edges of the viewport if there is room for it.  (The box is drawn one
pixel's width *outside* of the rectangular area.)  If the SCREEN modifier/
option is omitted, the top lefthand corner becomes the coordinate origin
and all coordinates specified after VIEW is used are referenced to the top
lefthand corner of the viewport (and the maximum x coordinate that can be
used is x2 - x1 and the maximum y coordinate is y2 - y1).  In other words,
although screen coordinates are used in the VIEW statement itself, all
subsequent coordinates are viewport coordinates--(0,0) refers to the top
lefthand corner of the viewport.  (y still increases downwards.)

If you do put the word SCREEN between VIEW and the (x1,y1) parameter, things
are different.  All subsequent coordinates are still screen coordinates,
i.e., they are still referenced to the upper lefthand corner of the screen.
The function of VIEW in this case is to keep pixels from being plotted if
their coordinates are outside of the graphics viewport.  (I've never
used VIEW for this function; I just leave SCREEN out of the statement.)

There are various ways of erasing the viewport and resetting things so that
the entire screen is the graphics viewport (the default situation before
VIEW is used).  One method is to simply use VIEW all by itself, with no
arguments.  (That's why there are two "[[" before SCREEN in the syntax.)
Also, using the SCREEN statement to change video modes resets the viewport
to be the entire screen.  So does using RUN.

It should be noted that defining a graphics viewport affects QB's clear
screen statement, CLS.  The syntax for CLS is actually

CLS n

where n is 0, 1, or 2.  If a viewport is not defined, it doesn't matter what
you use for n; you might as well not even bother specifying it.  If a
viewport *is* defined, n = 0 causes the entire screen to be cleared and n =
1 clears only what's inside of the viewport.  (Even though graphics pixels
are only plotted *inside* of the viewport, you can PRINT text outside of
it.)  n = 2 is for a text viewport (VIEW PRINT) and there's no point in
going into that here.  (I've never used VIEW PRINT.)

Okay, I guess it's time to make the distinction between colors and
attributes more explicit.  When you, for example, say things like

COLOR 7

and then

PRINT "HELLO"

or you do something like

PSET (10,20), 7

you'll likely notice that things get printed or plotted to the screen with a
dull white color.  This is, technically, not because the color 7 means "dull
white."  The "7" here is an attribute, not the color.  You only get dull
white because that's the color currently assigned to the attribute 7.  That
can, however, be changed.  (This is important, incidentally, because when
you BSAVE a picture, it is the *attributes* that get saved to the file, not
the colors.)  It might also be pointed out that this is not something due to
QB.  This state of affairs reflects how your video hardware works.  QB has
no control over it, other than to use the power the video hardware gives it
to manipulate the colors assigned to attributes.

Essentially, the attribute is just an index into a table.  The table itself
contains the colors.  The attribute can just be made to point to different
locations in the table.  The data in that table depends on the video mode
that you're currently in, as does how you change the data in that table
(i.e., how you change what colors are assigned to what attributes).

QB's function for assigning colors to attributes is PALETTE.  (In computer
video terminology, the set of colors assigned to the MAXCOL attributes is
called the palette registers.  These registers are located in "special"
memory chips on your video board--or otherwise somewhere with your video
hardware if your video chipset is on your motherboard.)  I'm not going to go
into how to use PALETTE for all the different video modes.  I suspect that
SCREENs 9, 12, and 13 are of most interest.  (But I'll discuss 11 too.)

The general syntax for PALETTE is

PALETTE attribute, #

where # is a LONG variable or constant and otherwise depends on the video
mode.  The maximum value of attribute (MAXCOL - 1) also of course depends on
the video mode.  (The minimum value is always 0.)  If it isn't clear, I'll
point out that you must be in the video mode you're going to be using
*before* using PALETTE.

In SCREEN 9, although attribute is limited to a maximum of 15, there are 64
different colors from which you can choose to associate those 16 attributes
with, numbered from 0 to 63.  (And if I understand what I read in a bios
reference, if you're willing to bypass the functionality that QB explicitly
provides for manipulating the palette and manipulate the palette registers
yourself, using more direct access methods (not discussed here), there are
actually 4 different sets (or "banks") of those 64 colors that you can work
with.  However, QB itself only works with the first bank.)

I have looked as hard as I can in QB's on-line help and in my manual, and I
cannot find an actual listing of what those fixed 64 colors are.  So, even
though QB gives you the capability of changing the colors associated with
the 16 attributes in SCREEN 9, it doesn't give you much help in deciding
what choices you might like to make.  (Perhaps one reason for this omission
might be that, again, QB doesn't decide what these 64 colors are; your video
system does.  But since it's a standard list, there's really no good reason
for the omission.)  One method of making the choice is just to experiment
with PALETTE until you get a set of 16 attributes that you like.  I've gone
to the trouble of providing you with another option.  Those 64 colors are
tabulated below, from van Gilluwe's "The Undocumented PC" (first edition,
after converting his hexadecimal numbers to decimal), as a function of the #
parameter you use with PALETTE.  (This does not of course necessarily
completely eliminate the need for experimentation, perhaps just to see if
what's in the table gives you something that you like on the screen.  But it
may take a lot of the trial and error out of the experimentation.)


                   PALETTE # VALUES FOR SCREEN 9

PALETTE #       COLOR                     PALETTE #     COLOR
    0           black                        32        dark red
    1           blue                         33    deep blue-purple
    2           green                        34         green
    3           cyan                         35         cyan
    4           red                          36   bright red-orange
    5          magenta                       37       deep pink
    6        dull yellow                     38         orange
    7           white                        39         pink
    8         dark blue                      40      dark purple
    9        medium blue                     41      medium blue
   10        army green                      42      green-gray
   11         baby blue                      43      medium blue
   12         ruby red                       44      cherry red
   13         lavender                       45      deep magenta
   14        light gold                      46      light orange
   15      light lavender                    47     light lavender
   16        dark green                      48    dark army green
   17      medium-dark blue                  49      blue-purple
   18     fluorescent green                  50  bright green-yellow
   19        green-cyan                      51    light green-cyan
   20           brown                        52      orange-red
   21       medium purple                    53       hot pink
   22    bright yellow-green                 54     lemon yellow
   23        faded green                     55      warm white
   24         dark cyan                      56         gray
   25         deep blue                      57      bright blue
   26       bright green                     58      bright green
   27        bright cyan                     59      bright cyan
   28         faded red                      60      bright red
   29          purple                        61     bright magenta
   30    bright yellow-green                 62     bright yellow
   31         ice blue                       63     bright white


(Note that the default colors assigned to the attributes for SCREEN 9 are
not simply the first 16 values in the above table.  I'd tell you what they
are, but you *can* get those from the on-line help--under SCREEN.  It might
also be noted, however, that these standard default color assignments are in
fact the standard ones made by your video system.)

Although the maximum attribute allowed in SCREENs 11, 12, and 13 depend on
the specific video mode, they have at least one thing in common--the colors
are assigned to the available attributes in exactly the same way, from
exactly the same set of 64^3 = 262,144 colors.  However, these colors are
NOT numbered consecutively from 0 to 262,143.  In using PALETTE to assign a
color to an attribute, you MUST use the following formula for #:

# = R + 256& * G + 65536& * B,

where R, G, and B are the Red, Green, and Blue parameters and must each be
in the range 0 to 63.  (This constraint is also NOT from QB.  It is again an
artifact of how the palette registers are used by your video system.  Also,
you may be able to get away with not using the "&" LONG suffix; I just do it
to be safe.)

The only difference between the three video modes in this regard is that the
maximum value of attribute is 255 for SCREEN 13, 15 for SCREEN 12, and 1 for
SCREEN 11.  The default values for the RGB values assigned to the attributes
for SCREENs 12 and 13 are tabulated below, where only the first 16 apply for
SCREEN 12.


   DEFAULT RGB VALUES FOR SCREENs 12 AND 13

ATTRIBUTE     RED           GREEN         BLUE
 0             0             0             0
 1             0             0             42
 2             0             42            0
 3             0             42            42
 4             42            0             0
 5             42            0             42
 6             42            21            0
 7             42            42            42
 8             21            21            21
 9             21            21            63
 10            21            63            21
 11            21            63            63
 12            63            21            21
 13            63            21            63
 14            63            63            21
 15            63            63            63
 16            0             0             0
 17            5             5             5
 18            8             8             8
 19            11            11            11
 20            14            14            14
 21            17            17            17
 22            20            20            20
 23            24            24            24
 24            28            28            28
 25            32            32            32
 26            36            36            36
 27            40            40            40
 28            45            45            45
 29            50            50            50
 30            56            56            56
 31            63            63            63
 32            0             0             63
 33            16            0             63
 34            31            0             63
 35            47            0             63
 36            63            0             63
 37            63            0             47
 38            63            0             31
 39            63            0             16
 40            63            0             0
 41            63            16            0
 42            63            31            0
 43            63            47            0
 44            63            63            0
 45            47            63            0
 46            31            63            0
 47            16            63            0
 48            0             63            0
 49            0             63            16
 50            0             63            31
 51            0             63            47
 52            0             63            63
 53            0             47            63
 54            0             31            63
 55            0             16            63
 56            31            31            63
 57            39            31            63
 58            47            31            63
 59            55            31            63
 60            63            31            63
 61            63            31            55
 62            63            31            47
 63            63            31            39
 64            63            31            31
 65            63            39            31
 66            63            47            31
 67            63            55            31
 68            63            63            31
 69            55            63            31
 70            47            63            31
 71            39            63            31
 72            31            63            31
 73            31            63            39
 74            31            63            47
 75            31            63            55
 76            31            63            63
 77            31            55            63
 78            31            47            63
 79            31            39            63
 80            45            45            63
 81            49            45            63
 82            54            45            63
 83            58            45            63
 84            63            45            63
 85            63            45            58
 86            63            45            54
 87            63            45            49
 88            63            45            45
 89            63            49            45
 90            63            54            45
 91            63            58            45
 92            63            63            45
 93            58            63            45
 94            54            63            45
 95            49            63            45
 96            45            63            45
 97            45            63            49
 98            45            63            54
 99            45            63            58
 100           45            63            63
 101           45            58            63
 102           45            54            63
 103           45            49            63
 104           0             0             28
 105           7             0             28
 106           14            0             28
 107           21            0             28
 108           28            0             28
 109           28            0             21
 110           28            0             14
 111           28            0             7
 112           28            0             0
 113           28            7             0
 114           28            14            0
 115           28            21            0
 116           28            28            0
 117           21            28            0
 118           14            28            0
 119           7             28            0
 120           0             28            0
 121           0             28            7
 122           0             28            14
 123           0             28            21
 124           0             28            28
 125           0             21            28
 126           0             14            28
 127           0             7             28
 128           14            14            28
 129           17            14            28
 130           21            14            28
 131           24            14            28
 132           28            14            28
 133           28            14            24
 134           28            14            21
 135           28            14            17
 136           28            14            14
 137           28            17            14
 138           28            21            14
 139           28            24            14
 140           28            28            14
 141           24            28            14
 142           21            28            14
 143           17            28            14
 144           14            28            14
 145           14            28            17
 146           14            28            21
 147           14            28            24
 148           14            28            28
 149           14            24            28
 150           14            21            28
 151           14            17            28
 152           20            20            28
 153           22            20            28
 154           24            20            28
 155           26            20            28
 156           28            20            28
 157           28            20            26
 158           28            20            24
 159           28            20            22
 160           28            20            20
 161           28            22            20
 162           28            24            20
 163           28            26            20
 164           28            28            20
 165           26            28            20
 166           24            28            20
 167           22            28            20
 168           20            28            20
 169           20            28            22
 170           20            28            24
 171           20            28            26
 172           20            28            28
 173           20            26            28
 174           20            24            28
 175           20            22            28
 176           0             0             16
 177           4             0             16
 178           8             0             16
 179           12            0             16
 180           16            0             16
 181           16            0             12
 182           16            0             8
 183           16            0             4
 184           16            0             0
 185           16            4             0
 186           16            8             0
 187           16            12            0
 188           16            16            0
 189           12            16            0
 190           8             16            0
 191           4             16            0
 192           0             16            0
 193           0             16            4
 194           0             16            8
 195           0             16            12
 196           0             16            16
 197           0             12            16
 198           0             8             16
 199           0             4             16
 200           8             8             16
 201           10            8             16
 202           12            8             16
 203           14            8             16
 204           16            8             16
 205           16            8             14
 206           16            8             12
 207           16            8             10
 208           16            8             8
 209           16            10            8
 210           16            12            8
 211           16            14            8
 212           16            16            8
 213           14            16            8
 214           12            16            8
 215           10            16            8
 216           8             16            8
 217           8             16            10
 218           8             16            12
 219           8             16            14
 220           8             16            16
 221           8             14            16
 222           8             12            16
 223           8             10            16
 224           11            11            16
 225           12            11            16
 226           13            11            16
 227           15            11            16
 228           16            11            16
 229           16            11            15
 230           16            11            13
 231           16            11            12
 232           16            11            11
 233           16            12            11
 234           16            13            11
 235           16            15            11
 236           16            16            11
 237           15            16            11
 238           13            16            11
 239           12            16            11
 240           11            16            11
 241           11            16            12
 242           11            16            13
 243           11            16            15
 244           11            16            16
 245           11            15            16
 246           11            13            16
 247           11            12            16
 248           0             0             0
 249           0             0             0
 250           0             0             0
 251           0             0             0
 252           0             0             0
 253           0             0             0
 254           0             0             0
 255           0             0             0


I would like to say that this table also applies to SCREEN 11 if only the
first two attributes are referred to.  However, that isn't the case.
Attribute 1 does not print/plot as blue; it prints/plots as white.  This is
in spite of the fact that I got this table by reading the color palette--and
I get the same first two lines of data when I'm in SCREEN 11.  So, all I can
really say here is that I don't understand SCREEN 11; it does not appear to
use the palette registers the way my documentation leads me to believe it
should.  (There's not a lot of call for SCREEN 11.  If you're interested in
games, you probably aren't interested in a monochrome video mode.  I use it
for displaying black & white static pictures/images that I need to print out
on black & white printers--there's no need to worry about color if you need
to print your picture and you don't have a color printer.)

A difference between these video modes and SCREEN 9 is that the above table
does not represent the same set of colors assigned by the computer itself in
video modes 12 and 13.  When QB changes to either of these video modes, it
also changes the palette.

Another syntax of PALETTE is

PALETTE USING array(element)

where array is an INTEGER or LONG array that stores the palette # data--in
order of the attribute numbering (0 - MAXCOL - 1).  It must store sufficient
data for all of the attributes that are allowed in the current video mode.
The element parameter tells PALETTE which element to start getting data
from.  To use the on-line help's example, if you're in a 16-color mode and
element = 5, your array must have at least 20 elements in it.  (Your array
doesn't need to be named "array".)  Any array element = -1 causes that
particular attribute to have the color currently assigned to it to be
unaffected.  Otherwise, negative numbers are not allowed in the array.
Although PALETTE may generally allow the array to be INTEGER, you need to
use a LONG array if it is to store palette #s larger than 32,767 (and it
generally will for SCREENs 12 and 13).

To reset the palette registers (i.e., the colors assigned to the attributes)
to their default values, just use PALETTE without any arguments/parameters
after it.

It's interesting to note that the changes to the color assignments made by
PALETTE (or by any other method of changing the palette registers) not only
affects the colors of pixels yet to be put on the screen, but it also
changes the color of what's already on the screen.  So, it doesn't matter
whether you use PALETTE before or after putting your graphics on the screen.
(As your video board continually scans video memory to tell your monitor
what to put on the screen, it also dynamically reads the palette registers.)

There's another method of changing the color palette.  Some people like it
because it's slightly faster than using PALETTE.  However, my primary
motivation for discussing it here is as a precursor to discussing how to
read and save the data from the color palette registers.  Further, this
discussion primary applies to QB video modes 12 and 13.  (There may be ways
of using the method for lesser video modes, but I don't know any more about
that than you.  (Perhaps you already know *more* than I do about it.))  The
method involves writing the three RGB color values to port 3C9h.  (The "h"
means that it's a hexadecimal number.)  You do that by using QB's OUT
statement.  Before doing that, however, you need to tell your computer which
attribute is associated with the RGB values.  You do that by writing (again
using OUT) the attribute integer to port 3C8h.  Actually, you don't need to
write to port 3C8 before everytime you write to 3C9.  Whenever you write
three bytes to 3C9, the integer in 3C8 automatically gets incremented by
one.  Let's say you're in SCREEN 13 and you have 256 RGB values stored in
three arrays, which you may have dimensionalized via

DIM RED(255) AS INTEGER, GREEN(255) AS INTEGER, BLUE(255) AS INTEGER

(You don't really need to use INTEGER arrays, but it speeds things up
slightly and ensures that the integer data is stored accurately.)  You can
set the 256-color palette with the following code example:

OUT &H3C8, 0
FOR I = 0 TO 255
OUT &H3C9, RED(I)
OUT &H3C9, GREEN(I)
OUT &H3C9, BLUE(I)
NEXT I

Things are the same for SCREEN 12; just change "255" above to "15".  (You
could put an

OUT &H3C8, I

just after the FOR statement instead of using the

OUT &H3C8, 0

just before it, but that would only slow things down.)

Now, you may have noticed that when you use PALETTE, or the above method, to
change the color palette from QB's default, BSAVE a picture made with that
new palette, change the palette to something else (perhaps because you're
running a different program than the one that generated and saved the
picture), and then BLOAD that picture back in from the file, the colors
aren't the same as when you first generated it.  This is because BSAVE
doesn't save the palette data.  It only saves the attribute data in video
memory.  (The same is true for GET, if you first copied your picture data to
an array and then BSAVED the array--as opposed to BSAVEing directly from
video memory.)  Hence, if you're using PALETTE, in addition to GETting and
BSAVEing your picture data, you may also want to save your palette data.

Saving your palette data is a two-step process.  You first need to read that
data from the palette registers and then you need to save it to a file.
There are two ways of reading the palette registers (that I know of).  One
is to call an interrupt.  (You can also set the palette registers that way.
I just didn't see any point in going into that.)  A less clumsy way is again
to work with port 3C9h.  Instead of using OUT, however, you want to use the
opposite QB statement:  INP.  However, you still need to use OUT to tell the
computer which attribute you want the RGB data for.  Instead of port 3C8h,
you now use 3C7h.  There are also different methods used to save your
palette data to a file.  I'm going to show a simple method that uses an easy
to view text file and saves the data in conjunction with reading it from the
palette registers.  Here goes.

OPEN "PALETTE.DAT" FOR OUTPUT AS #1
OUT &H3C7, 0
FOR I = 0 TO 255
PRINT#1, INP(&H3C9), INP(&H3C9), INP(&H3C9)
NEXT I
CLOSE #1

(Again, the integer in port 3C7h is incremented by one automatically
everytime 3 bytes are read from port 3C9h.)  PALETTE.DAT now contains 256
rows of numbers, with 3 numbers on each line.  Each line corresponds to an
attribute; attribute 0 corresponds to the first line and attribute 255
corresponds to the last line.  The three numbers on each line are,
respectively, the RGB data for the the particular attribute.

Okay, you've got the palette data in a file.  How do you get it back into
QB and reset the palette, perhaps in some other program?  Here goes again.

DIM R AS INTEGER, G AS INTEGER, B AS INTEGER
OPEN "PALETTE.DAT" FOR INPUT AS #1
OUT &H3C8, 0
FOR I = 0 TO 255
INPUT#1, R, G, B
OUT &H3C9, R
OUT &H3C9, G
OUT &H3C9, B
NEXT I
CLOSE #1

Before I give this up, I'll discuss a few miscellaneous things.  Let's say
you've got a picture on the screen and you want to know the attribute of
some pixel on the screen at position (x,y), i.e., you want to do the
opposite of what PSET does.  Use the POINT function:

attribute = POINT(x,y)

The SCREEN statement has a more general syntax than the one shown initially.
I didn't discuss that then because you generally don't have to worry about
it.  But if you want to complicate things, here you go.  The general syntax
is

SCREEN [mode][, [colorswitch]][, [apage]][, [vpage]]

First, we're going to forget about colorswitch.  Just put two commas in a
row between mode and apage (if you're going to worry about this more general
syntax at all).  (This parameter is only applicable for SCREENs 0 and 1.
For those modes, if colorswitch is 0, color printing/plotting is enabled.
When it is nonzero, color printing/plotting is disabled.)  The most likely
reasons that you might want to use the more general syntax is because of the
apage and vpage parameters.

So far, it's been assumed that when you're drawing a picture or otherwise
putting pixels on the screen, you want to see them being put there.  What
you may not realize is that, in actual fact, at no time do you ever (in QB
or any other programming environment) write anything to the screen.  Your
screen display is not a static thing.  In other words, your computer didn't
just put stuff on your screen, sort of like you would write stuff on paper,
and then just let it there until you tell the computer to change it.  What
you see on the screen is a replica of what's currently in your video memory.
You see what you see because your video card causes your monitor to
constantly update the screen with what's in memory.  When you think you're
writing data to the screen, you're really just writing to video memory.

That all may seem like a theoretical excursion into how computers work that
really doesn't have any bearing on how you *use* the computer.  However, the
video memory that's being "mapped" to your screen isn't necessarily the only
video memory that you can write to.  Depending on the video mode and how
much video memory you have, you may have video memory that you can write to
that does not currently correspond to what you're seeing on the screen.
These different sets of video memory are called video pages.  Video pages
are numbered from 0 to MAXPAGE - 1, where MAXPAGE is the maximum number of
pages supported by the video mode.  Modes 11 - 13 only have one video page
(page 0).  However, if you have 256 KB of video memory, SCREEN 9 has two
video pages.

QB's manual calls the page you're seeing the visual page.  (I prefer
"visible page.")  This is the vpage parameter in the SCREEN statement.  The
page you're writing to is the active page--apage in the SCREEN statement.
Generally, such as when you don't specify vpage and apage in SCREEN, vpage
and apage are the same page (usually 0)--that's why you see what you're
writing as you write it.  However, if you're in SCREEN 9, for example, and
you got there via

SCREEN 9,, 1, 0

and then you proceed to use various QB graphics functions to put pixels on
the screen, you aren't actually going to see anything happening.  That's
because you told SCREEN to set the visible page to something different than
the page you're writing to.  When you're all done writing graphics, you can
do something like

SCREEN 9,,, 1

and you'll see your graphics data appear on the screen virtually
instantaneously.  (I left out apage.  If you want to simultaneously
change apage to something else (0, in this case) or just specify it
explicitly, you can put the apage value after the second comma.  I think,
since you aren't changing video modes, that you could also replace the "9"
with a comma if you want to.)  Where's whatever data that was on page 0,
you ask?  It's still there, you just aren't looking at it.

SCREEN 9,,, 0

brings it back and of course the previous use of SCREEN will bring page 1
back again.


    Source: geocities.com/gstumpff