                Virtual Memory Arrays in QB, version 1.11

     QBVMS is a set of QB routines to allow storage of data in virtual
memory (referred to here as the not necessarily accurate term "VMS") in much
the same fashion that it stores data using arrays in conventional memory.
It does NOT give you the ability to simply use the DIM statement to define a
standard QB array of "unlimited" size.  It simply provides an alternative
method of storing data in VMS and lets you access it in a manner that in
some sense simulates how you would store and retrieve data with a
conventional array.  The process is admittedly somewhat more cumbersome than
using conventional arrays, and slower.  "Virtual memory," by the way, simply
refers to the concept of using a hard disk to store the data.  (I recommend
you use a ramdrive if you can make one large enough to suit your needs; it's
faster and won't stress out your hard drive.)

     There are a few restrictions.  First, only one-dimensional arrays are
explicitly simulated.  The four standard numerical data types are supported:
INTEGER (2 bytes), LONG (4 bytes), SINGLE (or "real"--4 bytes), and DOUBLE
(8 bytes).  Further, VMS arrays are 1-based.  (The first element has index
1.)  The maximum number of VMS arrays you can have is set to be 10 less than
the FILES= parameter in your CONFIG.SYS.  This does not necessarily mean you
can have that many VMS arrays.  As explained below, any given VMS array in
actuallity is just a file on your hard/ramdisk.  DOS alloates a certain
number of file handles for its own use and you may be using files in your
own program.  I have no idea how many files DOS actually needs or how many
you'll be using.  That value of 10, above, is just a crude attempt to make
allowance for other processes needing to allocate file handles.  There are
two different points in QBVMS where you may get a termination with an error
message indicating that you need to increase your FILES= parameter.

     You use QBVMS by putting the code in QBVMS.INC at the top of your
program (or use an include statement) and the code in QBVMS.BAS at the
bottom of your program.  (You can use an include statement for that too if
(and only if) you're working outside of QB's IDE and using BC.EXE and LINK
straight from DOS.)  Alternatively, an object code version of QBVMS.BAS is
included as QBVMS.OBJ if you want to use that with LINK (or make a library)
instead of explicitly incorporating QBVMS.BAS in your code.

     Before running your program that uses QBVMS, you need to tell QBVMS
where to put it's "buffers", i.e., what disk/directory you want it to use
for its temporary files allowing your disk to be treated as memory.  You do
this by SETting DOS environment variable VMSDIR (your autoexec is a good
place for this).  I'll just use a couple of examples.  Let's say you have a
ramdisk characterized by the letter "D" and you want to use the root
directory of that drive.  You need to do

SET VMSDIR=D:\

If you want to use the "BUFFERS" directory on drive C, it would be

SET VMSDIR=C:\BUFFERS\

(If you don't supply the trailling backslash, QBVMS will supply it.)  If
you don't define VMSDIR, QBVMS will look for the directory specified by a
DOS TEMP variable and use that.  If it can't find that variable, it will
look for the DOS TMP variable and use that.  If it still can't find that,
it will use the root directory of drive C.  (If it can't find that, it will



abort with an error message.  I will again RECOMMEND using a ramdrive and
that you remember to define the VMSDIR variable so you don't end up
accidentally thrashing your hard disk more than you intended to.)

     Just as with normal QB arrays, VMS arrays must be dimensionalized
before you can put data in them.  Instead of DIM, however, you do this by
calling the subroutine DIMVMS.  The syntax of the call is

CALL DIMVMS (ARRAY, N, TYPE, EC)

where ARRAY is a character string representing the name of the array, N
is the number of elements in the array (a LONG integer), TYPE is a
character string giving the data type ("INTEGER", "SINGLE", "DOUBLE", or
"LONG") and EC is an error code (single precision/4-byte variable).  The
only restriction on N, other than the amount of available VMS is that, being
a LONG integer, it can't be larger than 2^31 - 1 (2147483647).  Since, even
for a 2-byte per element INTEGER array, this would give an array size of
4,096 MB, N really isn't a limiting factor here.  (QBVMS will at no time
allow you to access more than 2,048 MB of virtual memory.)  In the call to
DIMVMS, EC must be listed as a variable, not an explicit number.  EC is
returned from DIMVMS as 0 if DIMVMS was able to initialize the array
properly (or at least thinks it did).  It is returned as 1 if it detects an
inavailability of VMS and 2 if you've already maxed the number of VMS arrays
out at 50 when you tried to initialize a new one.  Subroutine DIMVMS cannot
be used to perform a REDIM operation; it will terminate execution if you
call it with the name of an array that already exists.  REDIMVMS, however,
can be used for that purpose.  The calling syntax is exactly the same as
with DIMVMS.  If you merely want to clear an already existing virtual memory
array and reallocate its memory for reuse by DIMVMS, you can do that with
subroutine CLRVMS:

CALL CLRVMS (ARRAY)

     There are a few RESTRICTIONS on what a VMS array name can be.  The way
QBVMS works is, when DIMVMS allocates space for a VMS array, all it really
does is create a file on your hard/ramdisk with that name and an extension
of "QBV".  Hence, your array names will be truncated if you specify them to
be longer than 8 characters.  Since DIMVMS supplies the ".", you must not
include such a character in your array names.  Also, you must not include
*any* character in your names that DOS does not allow to be in a file name.

     Now, this is where things get slightly cumbersome.  How you store
data in a VMS array and retrieve data from it depends on what type of array
it is (INTEGER, SINGLE, etc.).  There are four subroutines used to store
data in the arrays, one for each data type, and four functions used to get
data out of the arrays, one again for each data type.  Let's say you have an
array "A", for definitiveness, and you want to assign a number X to element
I of this array.  You do this by calling a PUT$$$ subroutine, where $$$ is
SNG for a SINGLE array, INT for an INTEGER array, LNG for a LONG array, and
DBL for a DOUBLE array.  The syntax of the call is

CALL PUT$$$ ("A", I, X)




Note that I must be a LONG integer and X must be of the same data type as
signified by $$$.  The routines will detect discrepancies between the
specified array and the type they're programmed to handle and terminate
program execution (with a STOP statement) if they find such discrepancies.
(Nonexistent arrays and I > N / < 1 will also cause such termination.)  How
would you get X back out of array A?  Use the appropriate GET$$$ function,
where $$$ has the same interpretation as before.  For example,

X = GET$$$ ("A", I)

where I is a LONG integer, would retrieve the Ith element of array A and
put it in the variable X.  (It is generally best if X's data type is
compatible with $$$, although the general numeric conversion procedures of
QB should apply.)  Clearly, VMS arrays are global.  There is no need to
worry about how to pass them from one QB subroutine to the next.  VMS
exists no matter what subroutine you're in.  (Of course, you can pass
variables representing array *names* to subroutines.)

     You can use the function UVMS to find the number of elements stored
in an array.  (It's similar to QB's UBOUND function.)  UVMS inputs the
character string representing the name of an array and returns a LONG
integer giving the array's size and, via the parameter list, the DOS file
handle associated with the file storing your array data:

N = UVMS (ARRAY, handle)

The variable handle is of INTEGER type.  Why would you need handle?  You
might not.  It's useful if you want to use QBVMS' file I/O routines
directly, bypassing the normal VMS access routines, for purposes of speed.
The VMS arrays are just files on your hard/ramdisk.  The GET/PUT$$$
simply call various file access routines.  If you want to see how to use
those routines, look in the source code and read the commentary for the
routines FREAD, FWRITE, and FPOINT.  (There are others, but you probably
don't need to be concerned about them--but feel free to read anything you
want. :)  )

     Subroutine VBSAVE can be used to achieve roughly the same functionality
as QB's BSAVE command.  Rather than work with address segments and offsets,
however, VBSAVE just works with the name of an INTEGER virtual memory array,
starting from the beginning of that array.  The format of the call is

CALL VBSAVE (ARRAY, BYTES, FILE)

where ARRAY is a string (literal or variable) giving the name of the INTEGER
VMS array, BYTES is a LONG integer giving the number of bytes to save (it
does NOT, of course, have QB's 65,535 constraint), and FILE is a string
giving the name of the file to save the data to.  The format of this file is
not quite the same as a conventional BSAVE file--the primary reason being
that BSAVE stores the number of bytes saved in the file as a 2-byte integer,
which is totally incompatible with the purpose of VBSAVE.  (QB's BLOAD
command is NOT compatible with an VBSAVE file.)  So here's the format of an
VBSAVE file, where all byte references are 1-based:



Byte 1 - character FDh, just like a BSAVE file;
Bytes 2, 3, and 4 - they spell out "VMS";
Byte 5 - the high byte of a 3-byte integer giving the number of bytes saved;
Bytes 6 and 7 - the low and middle bytes of the 3-byte integer giving the
                number of bytes saved (just like bytes 6 and 7 in a normal
                BSAVE file);
Bytes 8 and on up - the data that got saved.

     Since BLOAD can't be used to load this data back into an array,
conventional or otherwise, you need something else.  Well, you're in
luck!  I included (like you couldn't guess <g>) subroutine VBLOAD for that
purpose.  The call is given by

CALL VBLOAD (ARRAY, FILE)

where both inputs are strings and have the same interpretation as before.
Although both VBSAVE and VBLOAD are SIGNIFICANTLY SLOWER than BSAVE/BLOAD
(you may want to have your program beep at you when they're finished),
VBLOAD has what might be considered one advantage over BLOAD (other than the
fact that it doesn't have the 64K - 1 byte limit):  you don't have to use
DIMVMS to initialize ARRAY before calling VBLOAD--the subroutine does that
itself via REDIMVMS.  (By the way, now that I've said a couple of times that
BLOAD can't work with this file, I'll point out that it CAN if two
conditions are met:  1) byte 5 in the file (the high byte of the 3-byte
integer) is zero--i.e., you saved less than 64K bytes, and 2) you aren't
using BLOAD in a manner that requires the default segment and offset that
BSAVE normally puts at bytes 2 - 5.)  VBLOAD initializes ARRAY as an INTEGER
array.

     There is one VERY IMPORTANT bureaucratic consideration.  When you're
all done using the QBVMS routines, before you quit your program, you SHOULD
call the routine VMSCLOSE.  The syntax is

CALL VMSCLOSE

This closes the files QBVMS uses as virtual memory (releasing their file
handles) and then deletes them from your disk.

     QBVMS.INC contains various COMMON SHARED ... and DIM SHARED ...
statements that store various global variables.  Don't even think about
changing the global variables unless you really understand how they're
being used and are intentionally trying to modify the functionality of
QBVMS.  (There also two variables (ERRORCODE and OX) that are not SHARED
but still DIMmed.  You can use these variables in your code if you want;
just don't try to use them in a DIM statement--except in subroutines, where
that should be okay.)  There are a few other routines that you don't really
need to be concerned with, other than to know not to try to create your own
routines with the same names.  These are the subroutine VMSINIT and the
function EXIST.  VMSINIT is called by the code in QBVMS.INC and is what
reads the VMSDIR environment variable.  EXIST is used to determine if a file
exists.  (See its commentary if you're interested in using it in your own
programming.)



     Now for the legal stuff.  Although I don't really think QBVMS can do
any direct physical harm to your equipment or other aspects of your system,
I must insist that, whatever risk using QBVMS has, said risk must be YOURS.
(You might want to try to avoid filling up your hard disk.  :)  And I one
more time recommend using a ramdrive.  It's not only faster, it doesn't
contribute to wear and tear on your hard drive.)  Although, I'm not
necessarily relinquishing copyright or desire for credit of authorship, I'm
not too concerned about what you do with the software--except for selling
it.



Glenn Stumpff

6423 Heritage Park Blvd.
Dayton, Ohio  45424

gstumpff@yahoo.com
