home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload
/
ShartewareOverload.cdr
/
database
/
baswiz14.zip
/
BASWIZ.DOC
< prev
next >
Wrap
Text File
|
1990-10-10
|
91KB
|
2,003 lines
The BASIC Wizard's Library page 1
=------------------------=
Version 1.4
BASWIZ Copyright (c) 1990 Thomas G. Hanlin III
This is BASWIZ, a library of assembly language and BASIC routines for use
with QuickBASIC version 4.5. Support for other recent versions of the BASIC
compiler is provided with registration. The BASWIZ collection is copyrighted
and may be distributed only under the following conditions:
1) No fee of over $10.00 may be charged for distribution. This
restriction applies only to physical copies and is not meant to
prevent distribution by telecommunication services.
2) All BASWIZ files must be distributed together in original, unaltered
form. This includes BASWIZ.DOC, BASWIZ.LIB, BASWIZ.NEW, BASWIZ.QLB,
BASWIZ.REF, BIBLIO.TXT, CATALOG.TXT, CREATE.BAT, DEMO.BAS, DEMO.DAT,
FILES.LST, LIBRARY.TXT, QUESTION.TXT, REGISTER.TXT and TERM.BAS.
You use this library at your own risk. It has been tested by me on my own
computer, but I will not assume any responsibility for any problems which
BASWIZ may cause you. If you do encounter a problem, please let me know
about it, and I will do my best to verify and repair the error.
It is expected that if you find BASWIZ useful, you will register your copy.
You may not use BASWIZ routines in programs intended for sale unless you have
registered. Registration entitles you to receive the latest version of
BASWIZ, complete with full source code in assembly language and BASIC. The
assembly code is designed for the OPTASM assembler by SLR Systems and will
require modifications if you wish to use it with MASM or TASM. You will then
be able to compile the BASIC code with whatever version of the compiler you
have, allowing you to use BASWIZ with QuickBASIC versions 4.0 - 4.5 and
BASCOM versions 6.0 - 7.1. Note that Microsoft's "far strings" can't be used
with BASWIZ at this time, so BASWIZ can't be used with QBX.
Warning: Use of BASWIZ for more than 30 days without registering has been
determined to cause cancer in laboratory animals! If you use this product,
please do register.
For an example of how to set up your program to access the BASWIZ library,
how to LINK the routines, and so forth, take a look at the CREATE.BAT and
DEMO.BAS files. The LIBRARY.TXT file explains how to use libraries.
So who's the BASIC Wizard? Why, with this library, you will be! Read this
tome well, for invoking these routines without proper preparation may bring
unexpected results. Cape and hat (optional) not included. No assembly
required.
NOTE!!! The Hercules Graphics routines will be expanded and moved into my
GRAFWIZ library as of the next version! Since GRAFWIZ focuses on graphics,
it is a more appropriate place for the Hercules routines than BASWIZ. This
will also help keep the size of BASWIZ manageable in the near future.
Table of Contents page 2
Expression Evaluator .................................................. 3
Far Strings ........................................................... 4
File Handling ......................................................... 6
Hercules Graphics ..................................................... 14
Memory Management and Pointers ........................................ 16
Telecommunications .................................................... 19
Virtual Windowing System .............................................. 25
Just for Kicks ........................................................ 36
Miscellaneous Notes ................................................... 37
Error Codes ........................................................... 38
Troubleshooting ....................................................... 40
History & Philosophy .................................................. 42
Using BASWIZ with PDQ ................................................. 43
Expression Evaluator page 3
The expression evaluator allows you to find the result of an expression
contained in a string. Normal algebraic precedence is used, e.g. 4+3*5
evaluates to 19. The usual numeric operators (*, /, +, -, ^) are supported
(multiply, divide, add, subtract, and raise to a power). Use of negative
numbers is just fine, of course. Parentheses for overriding the default
order of operations are also supported.
To evaluate an expression, you pass it to the evaluator as a string. You
will get back either an error code or a single-precision result. Try this
example to see how the expression evaluator works:
REM $INCLUDE: 'BASWIZ.BI'
DEFINT A-Z
DO
INPUT "Expression? "; Expr$
IF LEN(Expr$) THEN
Evaluate Expr$, Result!, ErrCode
IF ErrCode THEN
PRINT "Invalid expression. Error code = "; ErrCode
ELSE
PRINT "Result: "; Result!
END IF
END IF
LOOP WHILE LEN(Expr$)
END
An expression evaluator adds convenience to any program that needs to accept
numbers. Why make someone reach for a calculator when number crunching is
what a computer does best?
Far Strings page 4
One of the best things about BASIC is its support for variable-length
strings. Few other languages support such dynamically-allocated strings and
they're a terrifically efficient way of using memory. At least, they would
be, except for one minor limitation... in every version of QuickBASIC and
BASCOM (except for the new, very expensive BASCOM 7.0 "Professional
Development System"), string space is limited to a mere 50K-60K bytes. As if
this weren't trouble enough, this space is also shared with a number of other
things. Running out of string space is a common and painful problem.
Anyway, it used to be. The BASWIZ library comes with an assortment of
routines and functions which allow you to keep variable-length strings
outside of BASIC's tiny string area. Currently, you may have up to 65,535
far strings of up to 255 characters each, subject to available memory.
Either normal system memory or expanded memory may be used. Extended memory
can also be used if you have a driver that converts extended memory to
expanded memory.
Using far strings works almost the same way as using normal strings. Rather
than referring to a far string with a string variable name, however, you
refer to it with an integer variable called a "handle". To create a new far
string, you use a handle of zero. A new handle will be returned to you which
will identify that string for future reference.
Before you use any far strings, you must initialize the far string handler.
When you are done using far strings, you must terminate the far string
handler. Normally, each of these actions will take place only once in your
program: you initialize at the beginning and terminate at the end.
On the next page is an example program that reads a file into an array of far
strings, then displays it. I'll leave out such niceties as error trapping to
keep the example easy to follow.
Far Strings page 5
REM $INCLUDE: 'BASWIZ.BI'
DEFINT A-Z
REDIM Text(1 TO 5000) ' array for far string handles
FSInit 0 ' initialize far string handler
TextLines = 0
OPEN "ANYFILE.TXT" FOR INPUT AS #1
DO UNTIL EOF(1)
LINE INPUT#1, TextRow$
Handle = 0 ' use zero to create new far string
FSSet Handle, TextRow$ ' set the far string
TextLines = TextLines + 1
Text(TextLines) = Handle ' save the far string handle
LOOP
CLOSE
FOR Row = 1 TO TextLines
PRINT FSGet$(Text(Row)) ' display a far string
NEXT
FSDone ' terminate far string handler
END
If you wanted to change an existing far string, you would specify its
existing handle for FSSet. The handle of zero is used only to create new far
strings, rather in the manner of using a new variable for the first time.
Note the zero after the FSInit call. That specifies that main system memory
is to be used. If you would prefer to use expanded memory, use a one
instead. If EMS memory is not available, BASWIZ will ignore the one and use
main memory instead.
File Handling page 6
The file handling capabilities of BASIC were improved considerably as of
QuickBASIC 4.0. A binary mode was added and it became possible to use
structured (TYPE) variables instead of the awkward FIELD-based random access
handling. Even today, however, BASIC file handling is inefficient for many
tasks. It requires error trapping to avoid problems like open floppy drive
doors and cannot transfer information in large quantities at a time.
The BASWIZ routines provide additional flexibility and power. They allow you
to access files at as low or high a level as you wish. Here are some of the
features of BASWIZ file handling:
- File sharing is automatically used if the DOS version is high enough,
providing convenient network compatibility.
- Critical errors, like other errors, are detected at any point you find
convenient via a single function call.
- Optional input buffers speed up reading from files.
- Up to 32K of data may be read or written at one time.
- Files can be flushed to disk to avoid loss due to power outages, etc.
Files are not considered to be strongly moded by BASWIZ, although there are a
few limitations on how you can deal with text files as opposed to other kinds
of files. Reads and writes normally take place sequentially, like the INPUT
and OUTPUT modes allowed by BASIC. However, you can also do random access by
moving the file pointer to anywhere in the file, just as with the RANDOM and
BINARY modes allowed by BASIC. These routines place no arbitrary limitations
on the programmer.
As with BASIC, files are referred to by a number after they are opened for
access. Unlike BASIC, the number is returned to you when the file is
successfully opened, rather than being specified by you when you open the
file. This means that you never have to worry about a file number already
being in use. We'll refer to the file number as a "file handle" from now on.
File Handling page 7
Before doing anything else, you must initialize the file handling routines.
This is typically done only once, at the beginning of your program. The
FInit routine needs to know the number of files you want to deal with. This
can be up to 15 files, or possibly up to 50 if you are using DOS 3.3 or
higher. Support for over 15 files has not been tested, since I don't have
DOS 3.3 or higher, so you use that feature at your own risk! A future
version of BASWIZ will support over 15 open files for DOS 3.0 and above.
FInit Files, ErrCode
A file is opened for access like so:
FOpen File$, FMode$, BufferLen, Handle, ErrCode
You pass the File$, FMode$, and BufferLen. The Handle and ErrCode are
returned to you. The "BufferLen" is the length of the buffer desired for
input. This must be zero if you want to write to the file. The filename is
passed in File$, naturally enough. There is a choice of various modes for
FMode$ and these can be combined to some extent:
A Append to file used to add more information to an existing file
C Create file creates a new file
R Read access allows reading (input) from a file
T Text mode file allows text-mode input from a file
W Write access allows writing (output) to a file
For the most part, the combinations are self-explanatory. For instance, it
would be reasonable to open a file for read and write, for create and write,
for append and write, or for read and text. Text files always require a
buffer. If you request text access without specifying a buffer, a buffer of
512 bytes will be provided for you. If you request create access without
additional parameters, the file will be opened for write by default.
You may not use a buffer if you want to write to a file. This includes text
files, which always use a buffer, as well as binary files. This is an
artificial limitation which will change in a future version of BASWIZ. It
exists now to reduce the internal complexity of the routines which write to
the file, so that they do not have to account for any buffering as well as
the current file pointer. However, writing may be done to a text-type file
if the file was not opened in text mode. We'll see how that works presently.
When you are done using a particular file, you can close it, just as in
ordinary BASIC:
FClose Handle
Before your program ends, you should terminate the file handler. This will
close any open files as well as concluding use of the file routines:
FDone
File Handling page 8
That covers the basic set-up routines: initialize, open, close, and
terminate. Of more interest are the routines which actually deal with the
file itself. These provide assorted read/write services, the ability to get
or set the file read/write pointer, size, time, and date, and the ability to
get or set the error code for a specific file, among other things. Let's
take a look at the error handler first.
The FInit and FOpen routines return an error code directly, since you need to
know immediately if these have failed. The other file routines do not return
a direct error code, however. In order to discover whether an error has
occurred, you use the FGetError% function. This will return an error of zero
if there was no error, or a specific error code (listed at the end of this
manual) if some problem occurred. The error code will remain the same until
you reset it using FError. The FError service also allows you to test your
error handler by forcing specific error codes even when everything is fine.
PRINT "Error code: "; FGetError(Handle)
FError Handle, 0 ' clear the error code
It is recommended that you check for errors after any file routine is used if
there is a chance that your program will be executed on a floppy disk. These
are particularly prone to user errors (like leaving the drive door open) or
running out of space. If your program will only run on a hard drive, you may
not need to check as frequently. It's your choice. Note that the error code
is not cleared automatically-- use FError to reset the error code to zero if
you determine that it wasn't a "fatal" error.
Down to the nitty-gritty... we've seen how to open and close a file, how to
check operations for errors, and so forth. So how do we actually manipulate
the file? There are assorted alternatives, depending on how you want to deal
with the file: text reads, text writes, byte-oriented reads and writes, and
block reads and writes, not to mention handling the time, date, size, and
read/write pointer. We'll start off with the routines which read from a
file.
If you opened the file for text access, you must want to read the file a line
at a time. Each line is assumed to be less than 256 characters and delimited
by a carriage return and linefeed (<CR><LF>, or ^M^L, in normal notation).
In that case, you should use the FReadLn$ function:
St$ = FReadLn$(Handle)
A simple program to display a text file directly on the screen might look
something like this in BASIC:
OPEN COMMAND$ FOR INPUT AS #1
WHILE NOT EOF(1)
LINE INPUT#1, St$
PRINT St$
WEND
CLOSE #1
File Handling page 9
The same program using BASWIZ would look something like this:
REM $INCLUDE: 'BASWIZ.BI'
DEFINT A-Z
FInit 15, ErrCode
FOpen COMMAND$, "RT", 0, Handle, ErrCode
WHILE NOT FEOF(Handle)
PRINT FReadLn$(Handle)
WEND
FDone
In either case, we're accepting a command-line parameter which specifies the
name of the file. In the BASWIZ example, note the use of the FEOF% function,
which tells whether we've gone past the end of the file. This works like the
EOF function in BASIC.
There are two ways of reading from binary files. You can get the results as
a string of a specified (maximum) length:
St$ = FRead$(Handle, Bytes)
In plain BASIC, the same thing might be expressed this way:
St$ = INPUT$(Bytes, FileNumber)
The other way of reading from a binary file has no equivalent in BASIC. It
allows you to read in up to 32K bytes at a time, directly into an array or
TYPEd variable. You can read the information into anything that doesn't
contain normal strings (the fixed-length string type can be used, though):
Segm = VARSEG(Array(0))
Offs = VARPTR(Array(0))
FBlockRead Handle, Segm, Offs, Bytes
That would read the specified number of bytes into the array "Array",
starting at array element zero. You can use any data type, whether single
variable or array, as long as it is not a variable length string. In other
words, Vbl$ and Vbl$(0) would not work. If you want to use a string with the
block read, it must be a fixed-length string. For example:
DIM Vbl AS STRING * 1024
Segm = VARSEG(Vbl)
Offs = VARPTR(Vbl)
FBlockRead Handle, Segm, Offs, Bytes
It's a good idea to calculate the Segm and Offs values each time. These tell
FBlockRead where to store the information it reads. QuickBASIC may move the
variable around in memory, so VARSEG and VARPTR should be used just before
FBlockRead, to insure that they return current and correct information.
File Handling page 10
The file output commands are similar. File output can only be done if there
is no input buffer. This means that you can't use file output if the file
was opened in text mode, either, since text mode always requires an input
buffer. That's a limitation that will be removed in a future version of
BASWIZ. It is possible to do text output on a file that was opened in binary
mode, however. The limitation just means that you can't open a file for both
reading and writing if you use a buffer (or text mode).
To output (write) a string to a file, use this:
FWrite Handle, St$
This is like the plain BASIC statement:
PRINT #FileNumber, St$;
If you would like the string to be terminated by a carriage return and
linefeed, use this instead:
FWriteLn Handle, St$
This is like the plain BASIC statement:
PRINT #FileNumber, St$
In BASIC, the difference between the two writes is controlled by whether you
put a semicolon at the end. With BASWIZ, different routines are used
instead. FWrite is like PRINT with a semicolon and FWriteLn is like PRINT
without a semicolon.
As well as simple string output, you can also output TYPEd variables and
even entire arrays. This type of output has no corresponding BASIC
instruction, although it's somewhat similar to the file PUT statement. Up to
32K can be output at a time:
Segm = VARSEG(Array(0))
Offs = VARPTR(Array(0))
FBlockWrite Handle, Segm, Offs, Bytes
If you haven't already read the section on FBlockRead, go back a page and
review it. The same comments apply for FBlockRead: it can handle
fixed-length strings but not old-style strings, and VARSEG/VARPTR should
immediately preceed the block I/O, among other things.
File Handling page 11
Normally, reads and writes take place sequentially. If you want to move to a
specific spot in the file, though, that's easy. You can do it in text mode
or binary mode, whether or not you have a buffer, giving you additional
flexibility over the usual BASIC file handling. Set the location for the
next read or write like so:
FLocate Handle, Position&
The Position& specified will be where the next read or write takes place. It
starts at one and (since it's specified as a LONG integer) can go up to
however many bytes are in the file. If you want a record position rather
than a byte position, you can do that too. Just convert the record number to
a byte number, like so:
Position& = (RecordNumber& - 1&) * RecordLength& + 1&
If you do not want to maintain RecordNumber and RecordLength as LONG
integers, convert them to such by using the CLNG() function on them before
doing the calculation. Otherwise you may get an overflow error in the
calculation, since QuickBASIC will assume that the result will be an integer.
You can get the current position of the file read/write pointer too:
Position& = FGetLocate&(Handle)
Let's see... we've examined initialization and termination, opening and
closing, reading and writing, and manipulating the file read/write pointer.
What else could there be? Well, how about checking the size of a file and
getting or setting the file time and date? Why, sure! The "get" routines
are pretty well self-explanatory:
FileSize& = FGetSize&(Handle)
FileTime$ = FGetTime$(Handle)
FileDate$ = FGetDate$(Handle)
Setting the time and date is equally easy. This should be done just before
you close the file with FClose or FDone. You may use any date and time
delimiters you choose. If a field is left blank, the appropriate value from
the current time or date will be used. Years may be specified in four-digit
or two-digit format. Two-digit years will be assumed to be in the 20th
century ("90" == "1990"). Careful there! Your program should allow
four-digit dates to be used or disaster will strike when the year 2000
rolls around. The 21st century is closer than you think!
FTime Handle, FileTime$
FDate Handle, FileDate$
File Handling page 12
There's just one more file routine. It allows you to "flush" a file to disk.
This insures that the file has been properly updated to the current point, so
nothing will be lost if there is a power outage or similar problem. If you
do not use the "flush" routine, data may be lost if the program terminates
unexpectedly (without going through FClose or FDone). Note that use of
FFlush requires that a free file handle be available, under most DOS versions.
FFlush Handle
That's it for the BASWIZ file handler. As a quick review, let's run through
the available routines, then try a couple of example programs. Remember that
the BASWIZ.REF file contains a brief reference for all of these routines too!
You might also wish to examine the DEMO.BAS program, which also makes use of
the file routines.
FInit initialize the file handler
FDone terminate the file handler and close any open files
FOpen open a file for access (like OPEN)
FClose close a file (like CLOSE)
FRead$ read a string from a binary file (like INPUT$)
FReadLn$ read a string from a text file (like LINE INPUT)
FBlockRead read an item (TYPE, STRING*##, or array) from a binary file
FWrite write a string to a binary file
FWriteLn write a string with a <CR><LF> to a binary file
FBlockWrite write an item (TYPE, STRING*##, or array) to a binary file
FLocate set the read/write pointer to a specified position
FTime set the time stamp
FDate set the date stamp
FError set the error code
FGetLocate& get the read/write pointer
FGetTime$ get the time stamp
FGetDate$ get the date stamp
FGetError get the error code
FFlush flush to disk (makes sure file is updated and current)
FGetSize& get size
FEOF determine whether the end of the file has been reached
File Handling page 13
So much for theory. Let's try something practical. A common problem is
copying one file to another. We'll limit this to text files, so we can do it
in both plain BASIC and with BASWIZ. Although BASWIZ can handle any type of
file readily, BASIC has problems in efficiently handling variable-length
binary files. So, we'll do this first in BASIC, then BASWIZ, for text files.
In BASIC, a text-file copying program might look like this:
INPUT "File to copy"; FromFile$
INPUT "Copy file to"; ToFile$
OPEN FromFile$ FOR INPUT AS #1
OPEN ToFile$ FOR OUTPUT AS #2
WHILE NOT EOF(1)
LINE INPUT#1, St$
PRINT#2, St$
WEND
CLOSE
With BASWIZ, the same program would look more like this:
REM $INCLUDE: 'BASWIZ.BI'
DEFINT A-Z
INPUT "File to copy"; FromFile$
INPUT "Copy file to"; ToFile$
FInit 15, ErrCode
FOpen FromFile$, "RT", 1024, FromHandle, ErrCode
FOpen ToFile$, "CW", 0, ToHandle, ErrCode
FileTime$ = FGetTime$(FromHandle)
FileDate$ = FGetDate$(FromHandle)
WHILE NOT FEOF(FromHandle)
WriteLn ToHandle, ReadLn$(FromHandle)
WEND
FTime ToHandle, FileTime$
FDate ToHandle, FileDate$
FDone
You might have noticed that the BASWIZ version of the program is a bit longer
than the plain BASIC version. It has a number of advantages, however. It's
faster, produces smaller code under ordinary circumstances, and also
preserves the date and time of the original file in the copied file. Unlike
BASIC, the BASWIZ routines do not automatically add a ^Z to the end of text
files, so the BASWIZ example will not alter the original file.
Hercules Graphics page 14
Even in this age of super VGA displays, there are still many people using
monochrome systems. After all, they're extremely inexpensive and are
adequate for many purposes. As an alternative graphics standard, the
Hercules monochrome adapter is quite popular.
Curiously enough, QuickBASIC provides only indirect support for the Hercules
through a TSR. The BASWIZ library allows you to dispense with this TSR and
to take advantage of the full resolution of the Hercules. Rather than
attempt to emulate 25x80 text mode and get the bottom row truncated, as
QuickBASIC does, BASWIZ provides a handy 43x90 text mode which makes the best
use of the Herc's capabilities.
The first thing you need to do in order to use these Hercules routines is to
put the display into graphics mode. That's done like so:
HGMode 1
When you are done, you do the same thing, but with a zero instead of a one.
That puts the display back into text mode, which is important. The computer
does not know how to handle Hercules graphics mode by itself (I'm afraid IBM
snubbed this poor adapter!) and will display only garbage otherwise. Any
time you want to display something, use the routines provided here. The
normal PRINT statement will do you no good in Hercules graphics mode.
Since we can't rely on the usual BASIC display statements, we need to have
replacements for them. As a matter of fact, the following routines are not
merely replacements but extensions to BASIC's display capabilities. They are
for use in Hercules graphics mode only.
To start off with, let's check out the replacements for LOCATE, COLOR, CLS
and PRINT:
HGCls
HGColor Fore, Back
HGLocate Row, Column
HGWrite St$
HGWriteLn St$
Pretty straightforward, isn't it? The legal colors are 0 (black) and 1
(white, or actually green or amber depending on your display). Valid rows
are 1-43 and valid columns are 1-90. Note that the range of rows is the same
as an EGA or VGA allows!
Don't forget to put the display back into text mode before your program
exits! You'll get really funny looking screens otherwise.
HGMode 0
Hercules Display Adapter page 15
The HGWrite and HGWriteLn routines deserve a bit of additional explanation.
HGWrite is like PRINT with a semicolon-- the cursor is not moved down a line
after the string is printed. HGWriteLn is like PRINT without a semicolon.
Both of these routines use standard ASCII control codes rather than the
idiosyncratic interpretation offered by QuickBASIC. This means that CHR$(13)
homes the cursor to the start of the line, CHR$(10) moves the cursor directly
down one line without homing it, and CHR$(8) does a backspace. Other
supported control codes include bell (CHR$(7)), tab (CHR$(9)), and formfeed
(CHR$(12)). The latter is interpreted as meaning "clear screen", which is
the usual way of handling it on display-based systems.
If you need to print a number rather than a string, you can do that too.
Just convert the number to a string using the STR$ function provided by
BASIC.
Suppose you want to get the current colors or cursor position? That's
handled by these routines:
HGGetColor Fore, Back
HGGetLocate Fore, Back
Last but not least, there are two graphics routines. Graphics mode wouldn't
be much fun without graphics, now would it...
HGPlot Column, Row, Colour
HGLine Column1, Row1, Column2, Row2, Colour
As you might have noticed, the coordinates are in (Column, Row) order as is
appropriate for graphics mode, rather than the (Row, Column) used by text
mode. Why does the convention change like this? Well, probably not even
Microsoft can answer that any more! It's just the way it works.
Graphics mode coordinates may range 0-719 (columns) and 0-347 (rows). The
color range is the same as for text, i.e., 0-1.
Memory Management and Pointers page 16
On the whole, BASIC is easily a match for any other language, as far as
general-purpose programming goes. There is one major lack, however-- a set
of valuable features that is supported by most other languages, but was
inexplicably left out of BASIC. Perhaps Microsoft felt it was too advanced
and dangerous for a so-called "beginner's" language. In truth, using
pointers and memory management takes a little understanding of what you're
doing-- the compiler can't protect you from all of your mistakes. However,
they can be extraordinarily useful for many things, so I have added these
capabilities to BASWIZ.
A "pointer" is essentially just the address of an item. It is useful in two
respects: it allows you to pass just the pointer, rather than the whole item
(be it a TYPEd variable, normal variable, entire array, or whatever) to a
subprogram. This is faster and more memory-efficient than the alternatives.
Secondly, a pointer combined with memory management allows you to allocate
and deallocate memory "on the fly", in just the amount you need. You don't
have to worry about DIMensioning an array too large or too small, or even
with how large each element of the array should be, for example. You can
determine that when your program -runs-, rather than at compile time, and set
up your data structures accordingly. You can also create a large variety of
data structures, such as trees and linked lists, which would be difficult and
cumbersome to emulate using BASIC alone.
The BASWIZ memory/pointer routines allow you to allocate and deallocate
memory, fill, copy or move a block of memory, get or put a single character
according to a pointer, and convert back and forth between a segment/offset
address and a pointer.
Pointers are kept in LONG integers, using an absolute memory addressing
scheme. This means that you can manipulate pointers just like any ordinary
LONG integer, e.g. to move to the next memory address, just add one. Since
you can convert from a segment/offset address to a pointer and you can copy
information from one pointer to another, you can move information back and
forth between allocated memory and a TYPEd variable, numeric variable, or
array. You can even do things like set a pointer to screen memory and
transfer the screen into a variable or vice versa! Or implement your own
"far string" routines, heirarchical evaluations, or any number of other
things. Pointers are incredibly powerful!
Note that there are different ways of representing the same segment/offset
address, but only one absolute pointer representation. If you need to
compare two addresses, using pointers is terrific. However, it's good to
keep in mind that an segment/offset address may -appear- to change if you
convert it to a pointer and then back to a segment/offset address. When you
convert from a pointer to a segment/offset, the segment will be maximized and
the offset minimized.
Although the byte count for these routines is handled through a LONG integer,
the routines handle a maximum of 65,520 bytes at a time. In other words, a
pointer can only access a bit less than 64K at a time. If I get enough
requests to extend this range, I will do so. Meantime, that's the limit!
Memory Management and Pointers page 17
There are two routines which take care of memory management. These allow you
to allocate or deallocate memory. Note that if you allocate too much memory,
QuickBASIC won't have any memory to work with! Use the BASIC function
"SETMEM" to see how much memory is available before going hog-wild.
You can allocate memory like so:
MAllocate Bytes&, Ptr&, ErrCode%
If there isn't enough memory available, an error code will be returned.
Otherwise, Ptr& will point to the allocated memory. Memory is allocated in
chunks of 16 bytes, so there may be some memory wasted if you choose a number
of bytes that isn't evenly divisible by 16.
When you are finished with that memory, you can free it up by deallocation:
MDeallocate Ptr&, ErrCode%
An error code will be returned if Ptr& doesn't point to previously allocated
memory.
In the best of all possible worlds, there would be a third routine which
would allow you to reallocate or resize a block of memory. However, due to
certain peculiarities of QuickBASIC, I was unable to implement that. You can
simulate such a thing by allocating a new area of memory of the desired size,
moving an appropriate amount of information from the old block to the new,
and finally deallocating the old block.
Once you've allocated memory, you can move any sort of information in or out
of it except normal strings-- fixed-length strings, TYPEd values, arrays, or
numeric values. To do that, you use BASIC's VARSEG and VARPTR functions on
the variable. Convert the resulting segment/offset address to a pointer:
TSeg% = VARSEG(Variable)
TOfs% = VARPTR(Variable)
VariablePtr& = MJoinPtr&(TSeg%, TOfs%)
Moving the information from one pointer to another works like so:
MMove FromPtr&, ToPtr&, Bytes&
For STRING or TYPEd values, you can get the number of bytes via the LEN
function. For numeric values, the following applies:
Type Bytes per value
======= ===============
INTEGER 2
LONG 4
SINGLE 4
DOUBLE 8
Memory Management and Pointers page 18
The "memory move" (MMove) routine is good for more than just transferring
information between a variable and allocated memory, of course. Pointers can
refer to any part of memory. For instance, CGA display memory starts at
segment &HB800, offset 0, and goes on for 4000 bytes in text mode. That
gives a pointer of &HB8000. You can transfer from the screen to a variable
or vice versa. For that matter, you can scroll the screen up, down, left, or
right by using the appropriate pointers. Add two to the pointer to move it
to the next character or 160 to move it to the next row. As I said, pointers
have all kinds of applications! You don't need to worry about overlapping
memory-- if the two pointers, combined with the bytes to move, overlap at
some point, why, the MMove routine takes care of that for you. It avoids
pointer conflicts. MMove is a very efficient memory copying routine.
Suppose you've got a pointer and would like to convert it back to the
segment/offset address that BASIC understands. That's no problem:
MSplitPtr Ptr&, TSeg%, TOfs%
You might also want to fill an area of memory with a specified byte value,
perhaps making freshly-allocated memory zeroes, for example:
MFill Ptr&, Value%, Bytes&
Finally, there may be many occasions when you might want to transfer a single
character. Rather than going through putting the character into a STRING*1,
getting the VARSEG/VARPTR, and using MJoinPtr&, there is a simpler method:
MPutChr Ptr&, Ch$
Ch$ = MGetChr$(Ptr&)
Hopefully, this will give you some ideas to start with. I'll expand on the
uses of pointers and give further examples in future versions of BASWIZ.
There are many, many possible uses for such capabilities. Pointers and
memory management used to be the only real way in which BASIC could be
considered inferior to other popular languages-- that is no more!
NOTE:
QuickBASIC may move its arrays around in memory! Don't expect the address
of an array to remain constant while your program is running. Be sure to
get the VARSEG/VARPTR for arrays any time you're not sure they're in the
same location. Among the things which can cause arrays to move are use of
DIM, REDIM, or ERASE, and possibly calls to SUBs or FUNCTIONs. I'm not
sure if anything else may cause the arrays to move, so be cautious!
Telecommunications page 19
BASIC is unusual among languages in that it comes complete with built-in
telecommunications support. Unfortunately, that support is somewhat crude.
Amongst other problems, it turns off the DTR when the program SHELLs or ends,
making it difficult to write doors for BBSes or good terminal programs. It
also requires use of the /E switch for error trapping, since it generates
errors when line noise is encountered, and doesn't provide much control. It
doesn't even support COM3 and COM4, which have been available for years.
BASWIZ rectifies these troubles. It allows comprehensive control over
communications, includes COM3 and COM4, and doesn't require error trapping.
It won't fiddle with the DTR unless you tell it to do so. The one limitation
is that you may use only a single comm port at a time.
Before you can use communications, you must initialize the communications
handler. If you didn't have BASWIZ, you would probably use something like:
OPEN "COM1:2400,N,8,1,RS,CS,DS" AS #1
With BASWIZ, you do not have to set the speed, parity, and so forth.
Communications will proceed with whatever the current settings are, unless
you choose to specify your own settings. When you initialize the comm
handler, you specify only the port number (1-4) and the size of the input and
output buffers (1-32,767 bytes):
TCInit Port, InSize, OutSize, ErrCode
The size you choose for the buffers should be guided by how your program will
use communications. Generally, a small output buffer of 128 bytes will be
quite adequate. You may wish to expand it up to 1,500 bytes or so if you
expect to write file transfer protocols. For the input buffer, you will want
perhaps 512 bytes for normal use. For file transfer protocols, perhaps 1,500
bytes would be better. If a high baud rate is used, or for some other reason
you might not be emptying the buffer frequently, you may wish to expand the
input buffer size to 4,000 bytes or more.
When you are done with the telecomm routines, you must terminate them. In
BASIC, this would be done with something like:
CLOSE #1
With the BASWIZ routines, though, you would use this instead:
TCDone
The BASWIZ "TCDone" does not drop the DTR, unlike BASIC's "CLOSE". This
means that the modem will not automatically be told to hang up. With BASWIZ,
you have complete control over the DTR with the TCDTR routine. Use a value
of zero to drop the DTR or nonzero to raise the DTR:
TCDTR DTRstate
Telecommunications page 20
You may set the speed of the comm port to any baud rate from 1-65,535. If
you will be dealing with comm programs that were not written using BASWIZ,
you may wish to restrict that to the more common rates: 300, 1200, 2400,
4800, 9600, 19200, and 38400.
TCSpeed Baud&
The parity, word length, and stop bits can also be specified. You may use
1-2 stop bits, 6-8 bit words, and parity settings of None, Even, Odd, Mark,
or Space. Nearly all BBSes use settings of None, 8 bit words, and 1 stop
bit, although you will sometimes see Even, 7 bit words, and 1 stop bit. The
other capabilities are provided for dealing with mainframes and other systems
which may require unusual communications parameters.
When specifying parity, only the first character in the string is used, and
uppercase/lowercase distinctions are ignored. Thus, using either "none" or
"N" would specify that no parity is to be used.
TCParms Parity$, WordLength, StopBits
If your program needs to be aware of when a carrier is present, it can check
the carrier detect signal from the modem with the TCCarrier function. This
function returns zero if no carrier is present:
IF TCCarrier THEN
PRINT "Carrier detected"
ELSE
PRINT "No carrier"
END IF
Suppose, though, that you need to know immediately when someone has dropped
the carrier? It wouldn't be too convenient to have to spot TCCarrier
functions all over your program! In that case, use the "ON TIMER" facility
provided by BASIC for keeping an eye on things. It will enable you to check
the carrier at specified intervals and act accordingly. Here's a brief
framework for writing such code:
ON TIMER(30) GOSUB CarrierCheck
TIMER ON
' ...your program goes here...
CarrierCheck:
IF TCCarrier THEN ' if the carrier is present...
RETURN ' ...simply resume where we left off
ELSE ' otherwise...
RETURN Restart ' ...return to the "Restart" label
END IF
Telecommunications page 21
To get a character from the comm port, use the TCInkey$ function:
ch$ = TCInkey$
To send a string to the comm port, use TCWrite:
TCWrite St$
If you are dealing strictly with text, you may want to have a carriage return
and a linefeed added to the end of the string. No problem:
TCWriteLn St$
Note that the length of the output buffer affects how the TCWrite and
TCWriteLn routines work. They don't actually send string directly to the
comm port. Instead, they put the string into the output buffer, and it gets
sent to the comm port whenever the comm port is ready. If there is not
enough room in the output buffer for the whole string, the TCWrite/TCWriteLn
routines are forced to wait until enough space has been cleared for the
string. This can delay your program. You can often avoid this delay simply
by making the output buffer larger.
If you'd like to know how many bytes are waiting in the input buffer or
output buffer, there are functions which will tell you:
PRINT "Bytes in input buffer:"; TCInStat
PRINT "Bytes in output buffer:"; TCOutStat
Finally, if you would like to clear the buffers for some reason, you can do
that too. The following routines clear the buffers, discarding anything
which was waiting in them:
TCFlushIn
TCFlushOut
Don't forget to use TCDone to terminate the comm handler before ending your
program! If you do, the computer will lock up. Worse, it may not lock up
immediately, so forgetting TCDone can be very unpleasant.
Telecommunications page 22
Finally, there is a routine which allows you to handle ANSI codes in a
window. Besides the IBM semi-ANSI display code subset, mock-ANSI music is
allowed. This routine is designed as a subroutine that you can access via
GOSUB, since there are a number of variables that the routine needs to
maintain that would be a nuisance to pass as parameters, and QuickBASIC
unfortunately can't handle SUBs in $INCLUDE files (so SHARED won't work).
To use it, either include ANSI.BAS directly in your code, or use:
REM $INCLUDE: 'ANSI.BAS'
Set St$ to the string to process, set Win% to the handle of the window to
which to display, and set Music% to zero if you don't want sounds or -1 if
you do want sounds. Then:
GOSUB ANSIprint
Note that the virtual screen tied to the window must be at least an 80 column
by 25 row screen, since ANSI expects that size. You are also advised to have
an ON ERROR trap if you use ANSIprint with Music% = -1, just in case a "bad"
music sequence slips through and makes BASIC unhappy. Check for ERR = 5
(Illegal Function Call). I'll add a music handler later to avoid this.
To get some idea of how these routines all tie together in practice, see the
TERM.BAS example program. It provides a simple "dumb terminal" program to
demonstrate the BASWIZ comm handler. Several command-line switches are
allowed:
/43 use 43-line mode (EGA and VGA only)
/COM2 use COM2
/COM3 use COM3
/COM4 use COM4
/300 use 300 baud
/1200 use 1200 baud
/QUIET ignore "ANSI" music
By default, the TERM.BAS program will use COM1 at 2400 baud with no parity, 8
bit words and 1 stop bit. You can exit the program by pressing Alt-X.
Telecommunications page 23
The Xmodem file transfer protocol is currently supported for sending files
only. It automatically handles any of the usual variants on the Xmodem
protocol: 128-byte or 1024-byte blocks, plus checksum or CRC error detection.
In other words, it is compatible with Xmodem (checksum), Xmodem CRC, and
Xmodem-1K (single-file Ymodem-like variant).
There are only two routines which must be used to transfer a file. The first
is called once to initialize the transfer. The second is called repeatedly
until the transfer is finished or aborted. Complete status information is
returned by both routines. You can ignore most of this information or
display it any way you please.
The initialization routine looks like this:
StartXmodemSend Handle, Protocol$, Baud$, MaxRec, Record, EstTime$, ErrCode
Only the first three parameters are passed to the routine. These are the
Handle of the file that you wish to send (use FOpen to get the handle) and
the Protocol$ that you wish to use ("Xmodem" or "Xmodem-1K"), and the current
Baud$. On return, you will get an ErrCode if the other computer did not
respond, or MaxRec (the number of blocks to be sent), Record (the current
block number), and EstTime$ (an estimate of the time required to complete the
transfer. The Protocol$ will have "CHK" or "CRC" added to it to indicate
whether checksum or CRC error detection is being used, depending on which the
receiver requested.
The secondary routine looks like this:
XmodemSend Handle, Protocol$, MaxRec, Record, ErrCount, ErrCode
The ErrCode may be zero (no error), greater than zero (error reading file),
or less than zero (file transfer error, completion or abort). See the
appendix on Error Codes for specific details. The TERM.BAS example program
shows how these routines work together in practice.
The file accessed by the Xmodem routine will remain open. Remember to close
it when the transfer is done (for whatever reason), using the FClose routine.
Telecommunications page 24
A few notes on the ins and outs of telecommunications...
The DTR signal is frequently used to control the modem. When the DTR is
"raised" or "high", the modem knows that we're ready to do something. When
the DTR is "dropped" or "low", the modem knows that we're not going to do
anything. In most cases, this tells it to hang up or disconnect the phone
line. Some modems may be set to ignore the DTR, in which case it will not
disconnect when the DTR is dropped. Usually this can be fixed by changing a
switch on the modem. On some modems, a short software command may suffice.
The DTR is generally the best way to disconnect. The Hayes "ATH" command is
supposed to hang up, but it doesn't work very well.
The BASWIZ comm handler makes sure the DTR is raised when TCInit is used. It
does not automatically drop the DTR when TCDone is used, so you can keep the
line connected in case another program wants to use it. If this is not
suitable, just use TCDTR to drop the DTR. Your program must always use
TCDone before it exits, but it need only drop the DTR if you want it that
way.
If you want to execute another program via SHELL, it is ok to leave
communications running as long as control will return to your program when
the SHELLed program is done. In that case, the input buffer will continue to
receive characters from the comm port unless the SHELLed program provides
its own comm support. The output buffer will likewise continue to transmit
characters unless overruled.
An assortment of file transfer protocols will be provided in future versions
of BASWIZ. Among the ones supported will be Xmodem, Xmodem-1K, Ymodem
(batch), and Modem7 (batch). I do not expect to support Kermit or Zmodem,
since they are unduly complicated. You can handle any file transfer protocol
you like by SHELLing to an external protocol program, or of course you can
write your own support code.
The Virtual Windowing System page 25
The virtual windowing system offers pop-up and collapsing windows, yes... but
that is just a small fraction of what it provides. When you create a window,
the part that you see on the screen may be only a view port on a much larger
window, called a virtual screen. You can make virtual screens of up to 255
rows long or 255 columns wide. The only limitation is that any single
virtual screen must take up less than 65,520 bytes. Each virtual screen is
treated much like the normal screen display, with simple replacements for the
standard PRINT, LOCATE, and COLOR commands. Many other commands are provided
for additional flexibility. The window on the virtual screen may be moved,
resized, or requested to display a different portion of the virtual screen.
If you like, you may choose to display a frame and/or title around a window.
When you open a new window, any windows under it are still there and can
still be updated-- nothing is ever destroyed unless you want it that way!
With the virtual windowing system, you get a tremendous amount of control for
a very little bit of work.
The current version of the virtual windowing system only allows text mode
screens to be used. All standard text modes are supported, however. This
includes 25x40 CGA screens, the standard 25x80 screen, and longer screens
such as the 43x80 EGA screen. The virtual windowing system is designed for
computers that offer hardware-level compatibility with the IBM PC, which
includes almost all MS-DOS/PC-DOS computers in use today.
Note that certain old and inexpensive CGAs may flicker noticeably when the
virtual windowing system updates the display. If the performance penalty is
acceptable, this will be remedied in a future version.
Terminology:
-----------
DISPLAY
The actual screen.
SHADOW SCREEN
This is a screen kept in memory which reflects any changes you make to
windows. Rather than making changes directly on the actual screen, the
virtual windowing system works with a "shadow screen" for increased speed
and flexibility. You specify when to update the display from the shadow
screen. This makes any changes appear very smoothly.
VIRTUAL SCREEN
This is a screen kept in memory which can be treated much like the actual
screen. You may choose to make a virtual screen any reasonable size.
Every virtual screen will have a corresponding window.
WINDOW
This is the part of a virtual screen which is actually displayed. You
might think of a window as a "view port" on a virtual screen. A window
may be smaller than its virtual screen or the same size. It may have a
frame or a title and can be moved or resized.
The Virtual Windowing System page 26
Frankly, the virtual windowing system is one of those things that's more
difficult to explain than to use. It's very easy to use, as a matter of
fact, but the basic concepts will need a little explanation. Rather than
launching into a tedious and long-winded description of the system, I'm
going to take a more tutorial approach, giving examples and explaining as I
go along. Take a look at the DEMO.BAS program for additional insights.
Let's begin with the simplest possible scenario, where only a background
window is created. This looks just like a normal screen.
REM $INCLUDE: 'BASWIZ.BI'
DEFINT A-Z
Rows = 25: Columns = 80 ' define display size
WInit Rows, Columns, ErrCode ' initialize window system
IF ErrCode THEN ' stop if we couldn't...
PRINT "Insufficient memory"
END
END IF
Handle = 0 ' use background handle
WWriteLn Handle, "This is going to be displayed on the background window."
WWriteLn Handle, "Right now, that's the full screen."
WUpdate ' update the display
WDone ' terminate window system
What we just did was to display two lines on the screen-- nothing at all
fancy, but it gives you the general idea of how things work. Let's take a
closer look at what the program does:
- We INCLUDE the BASWIZ.BI definition file to let QuickBASIC know that
we'll be using the BASWIZ routines.
- We define the size of the display using the integer variables Rows and
Columns (you can use any variable names you want). If you have an EGA
display and had previously used WIDTH ,43 to go into 43x80 mode, you'd
use "Rows = 43" here, for example.
- We initialize the windowing system with WInit, telling it how large the
display is. It returns an error code if it is unable to initialize.
- We define the Handle of the window that we want to use. The "background
window" is always available as handle zero, so we choose "Handle = 0".
- We print two strings to the background window with WWriteLn, which is
like a PRINT without a semicolon on the end (it moves to the next line).
- At this point, only the shadow screen has been updated. We're ready to
display the information, so we use WUpdate to update the actual screen.
- We're all done with the program, so we finish up with WDone.
The Virtual Windowing System page 27
See, there's nothing to it! We initialize the screen, print to it or
whatever else we need to do, tell the windowing system to update the display,
and when the program is done, we close up shop.
The background screen is always available. It might help to think of it as a
virtual screen that's the size of the display. The window on this virtual
screen is exactly the same size, so the entire virtual screen is displayed.
As with other virtual screens, you can print to it without disturbing
anything else. That means you can treat the background screen the same way
regardless of whether it has other windows on top of it-- the other windows
just "cover" the background information, which will still be there.
This leads us to the topic of creating windows. Both a virtual screen and a
window are created simultaneously-- remember, a window is just a view port on
a virtual screen. The window can be the same size as the virtual screen, in
which case the entire virtual screen is visible (as with the background
window) or it can be smaller than the virtual screen, in which case just a
portion of the virtual screen will be visible.
A window is created like so:
' This is a partial program and can be inserted in the original example
' after the second WWriteLn statement...
VRows = 43: VColumns = 80 ' define virtual screen size
WOpen VRows, VColumns, 1, 1, Rows, Columns, Handle, ErrCode ' create window
IF ErrCode THEN ' error if we couldn't...
PRINT "Insufficient memory available"
WDone ' (or use an error handler)
END
END IF
What we have done here is to create a virtual screen of 43 rows by 80
columns. The window will be the size of the display, so if you are in the
normal 25x80 mode, only the first 25 rows of the virtual screen will be
visible. If you have an EGA or VGA in 43x80 mode, though, the entire virtual
screen will be visible! So, this window lets you treat a screen the same way
regardless of the display size.
The Handle returned is used any time you want to print to this new window or
otherwise deal with it. If you are using many windows, you might want to
keep an array of handles, to make it easier to keep track of which is which.
By default, a virtual screen is created with the following attributes:
- The cursor is at (1,1), the upper left corner of the virtual screen.
- The cursor size is 0 (invisible).
- The text color is 7,0 (white foreground on a black background).
- There is no title or frame.
- The window starts at (1,1) in the virtual screen, which displays the
area starting at the upper left corner of the virtual screen.
The Virtual Windowing System page 28
When you create a new window, it becomes the "top" window, and will be
displayed on top of any other windows that are in the same part of the
screen. Remember, you can print to a window or otherwise deal with it, even
if it's only partially visible or entirely covered by other windows.
Don't forget WUpdate! None of your changes are actually displayed until
WUpdate is used. You can make as many changes as you like before calling
WUpdate, which will display the results smoothly and at lightning speed.
We've created a window which is exactly the size of the display, but which
might well be smaller than its virtual screen. Let's assume that the normal
25x80 display is being used, in which case our virtual screen (43x80) is
larger than the window. We can still print to the virtual screen normally,
but if we print below line 25, the results won't be displayed. What a
predicament! How do we fix this?
The window is allowed to start at any given location in the virtual screen,
so if we want to see a different portion of the virtual screen, all we have
to do is tell the window to start somewhere else. When the window is
created, it starts at the beginning of the virtual screen, coordinate (1,1).
The WView routine allows us to change this.
In our example, we're displaying a 43x80 virtual screen in a 25x80 window.
To begin with, then, rows 1-25 of the virtual screen are visible. To make
rows 2-26 of the virtual screen visible, we simply do this:
WView Handle, 2, 1
That tells the window to start at row 2, column 1 in the virtual screen.
Sounds easy enough, doesn't it? Well, if not, don't despair. Play with it
in the QuickBASIC environment a little until you get the hang of it.
You've noticed that the window doesn't need to be the same size as the
virtual screen. Suppose we don't want it the same size as the display,
either... suppose we want it in a nice box, sitting out of the way in a
corner of the display? Well, we could have created it that way to begin with
when we used WOpen. Since we've already created it, though, let's take a
look at the routines to change the size of a window and to move it elsewhere.
The window can be made as small as 1x1 or as large as its virtual screen, and
it can be moved anywhere on the display you want it.
Let's make the window a convenient 10 rows by 20 columns:
WSize Handle, 10, 20
And move it into the lower right corner of the display:
WPlace Handle, 12, 55
The Virtual Windowing System page 29
Don't forget to call WUpdate or the changes won't be visible! Note also that
we didn't really lose any text. The virtual screen, which holds all the
text, is still there. We've just changed the size and position of the
window, which is the part of the virtual screen that we see, so less of the
text (if there is any!) is visible. If we made the window larger again, the
text in the window would expand accordingly.
If you were paying close attention, you noticed that we didn't place the
resized window flush against the corner of the display. We left a little bit
of room so we can add a frame and a title. Let's proceed to do just that.
Window frames are displayed around the outside of a window and will not be
displayed unless there is room to do so. We have four different types of
standard frames available:
0 (no frame)
1 single lines
2 double lines
3 single horizontal lines, double vertical lines
4 single vertical lines, double horizontal lines
We must also choose the colors for the frame. It usually looks best if the
background color is the same background color as used by the virtual screen.
Let's go ahead and create a double-line frame in bright white on black:
FType = 2: Fore = 15: Back = 0
WFrame Handle, FType, Fore, Back
If you'd rather not use the default frame types, there's ample room to get
creative! Frames 5-9 can be defined any way you please. They are null by
default. To create a new frame type, you must specify the eight characters
needed to make the frame: upper left corner, upper middle columns, upper
right corner, left middle rows, right middle rows, lower left corner, lower
middle columns, and lower right corner.
+----------------------------------------+
| Want a plain text frame like this? |
| Use the definition string "+-+||+-+" |
+----------------------------------------+
The above window frame would be defined something like this:
Frame = 5
FrameInfo$ = "+-+||+-+"
WUserFrame Frame, FrameInfo$
Of course, you can choose any values you like. As always, the names of the
variables can be anything, as long as you name them consistently within your
program. You can even use constants if you prefer:
WUserFrame 5, "+-+||+_+"
The Virtual Windowing System page 30
We can have a title regardless of whether a frame is present or not. Like
the frame, the title is displayed only if there is enough room for it. If
the window is too small to accommodate the full title, only the part of the
title that fits will be displayed. The maximum length of a title is 70
characters. Titles have their own colors.
Title$ = "Wonderful Window!"
Fore = 0: Back = 7
WTitle Handle, Title$, Fore, Back
To get rid of a title, just use a null title string (Title$ = "").
There are only a few more ways of dealing with windows themselves. After
that, I'll explain the different things you can do with text in windows and
how to get information about a specific window or virtual screen.
If you have a lot of windows, one window may be on top of another, obscuring
part or all of the window(s) below. In order to make sure a window is
visible, all you need to do is to put it on top, right? Hey, is this easy or
what?!
WTop Handle
Note that the background window will always be the background window. You
can't put handle zero, the background window, on top. What? You say you
need to do that?! Well, that's one of the ways you can use the WCopy
routine. WCopy copies one virtual screen to another one of the same size:
WCopy FromHandle, ToHandle
You can copy the background window (or any other window) to another window.
The new window can be put on top, resized, moved, or otherwise spindled and
mutilated. The demo program uses this trick.
We've been through how to open windows, print to them, resize them and move
them around, among other things. We've seen how to put a frame and a title
on a window and pop it onto the display. If you're a fan of flashy displays,
though, you'd probably like to be able to make a window "explode" onto the
screen or "collapse" off. It's the little details like that which make a
program visually exciting and professional-looking. I wouldn't disappoint
you by leaving something fun like that out!
Since we're using a virtual windowing system rather than just a plain ol'
ordinary window handler, there's an extra benefit. When a window explodes or
collapses, it does so complete with its frame, title, and even its text.
This adds rather nicely to the effect.
The Virtual Windowing System page 31
To "explode" a window, we just set up all its parameters the way we normally
would-- open the window, add a title or frame if we like, print any text that
we want displayed, and set the screen position. Then we use WExplode to zoom
the window from a tiny box up to its full size:
WExplode Handle
The "collapse" routine works similarly. It should be used only when you are
through with a window, because it closes the window when it's done. The
window is collapsed from its full size down to a tiny box, then eliminated
entirely:
WCollapse Handle
Note that WExplode and WCollapse automatically use WUpdate to update the
display. You do not need to use WUpdate yourself and you should make sure
that the screen is the way you want it displayed before you call either
routine.
The WCollapse and WExplode routines were written in BASIC, so they'll be easy
to customize just the way you want them if you register BASWIZ and get the
source code. They're not particularly difficult routines, however, so you
might want to design a set of your own similar routines just for the
exercise. All it takes is moving and resizing the windows.
The great thing about being a programmer is that you can do it your way.
Hold the pickles, hold the lettuce! Might be fun to add some sound effects
to those exploding windows, hmmm? I'll do that in a later version, but don't
feel obliged to wait for me!
That's it for the windows. We've been through all the "tricky stuff". There
are a number of useful things you can do with a virtual screen, though,
besides printing to it with WWriteLn. Let's take a look at what we can do.
WWriteLn is fine if you want to use a "PRINT St$" sort of operation. Suppose
you don't want to move to a new line afterward, though? In BASIC, you'd use
something like "PRINT St$;" (with a semicolon). With the virtual windowing
system, you use WWrite, which is called just like WWriteLn:
WWrite Handle, St$
There are also routines that work like CLS, COLOR and LOCATE:
WClear Handle
WColor Handle, Fore, Back
WLocate Handle, Row, Column
The WClear routine is not quite like CLS in that it does not affect the
cursor position. If you want the cursor "homed", use WLocate.
The Virtual Windowing System page 32
Note that the coordinates for WLocate are based on the virtual screen, not
the window. If you move the cursor to a location outside the view port
provided by the window, it will disappear. Speaking of disappearing cursors,
you might have noticed that our WLocate doesn't mimic LOCATE exactly: it
doesn't provide for controlling the cursor size. Don't panic! There's
another routine available for that:
WCursor Handle, CSize
The CSize value may range from zero (in which case the cursor will be
invisible) to the maximum size allowed by your display adapter. This will
always be at least eight.
Now, since each virtual screen is treated much like the full display, you may
be wondering what happens if the cursor is "on" in more than one window.
Does that mean multiple cursors are displayed? Well, no. That would get a
little confusing! Only the cursor for the top window is displayed. If you
put a different window on top, the cursor for that window will be activated
and the cursor for the old top window will disappear. The virtual windowing
system remembers the cursor information for each window, but it only actually
displays the cursor for the window that's on top.
In addition to the usual screen handling, the windowing system provides four
new capabilities which you may find very handy. These are routines to insert
and delete both characters and rows. This is done at the current cursor
position within a selected virtual screen:
WDelChr Handle
WDelLine Handle
WInsChr Handle
WInsLine Handle
These routines can also be used for scrolling. Remember, the display isn't
updated until you use WUpdate, and then it's updated all at once. You can
use any of the routines multiple times and the display will still be updated
perfectly smoothly-- all the real work goes on behind the scene!
When you are done with a virtual screen and no longer need it, you can
dispose of it like so:
WClose Handle
All of the information that can be "set" can also be retrieved. That's
useful in general, of course, but it's also a great feature for writing
portable subprograms. You can create subprograms that will work with any
virtual screen, since it can retrieve any information it needs to know about
the virtual screen or its window. That's power!
The Virtual Windowing System page 33
Here is a list of the available window data retrieval routines:
WGetColor Handle, Fore, Back
' gets the current foreground and background colors
WGetCursor Handle, CSize
' gets the cursor size
WGetFrame Handle, Frame, Fore, Back
' gets the frame type and frame colors
WGetLocate Handle, Row, Column
' gets the cursor position
WGetPlace Handle, Row, Column
' gets the starting position of a window on the display
WGetSize Handle, Rows, Columns
' gets the size of a window
Title$ = SPACE$(70)
WGetTitle Handle, Title$, TLen, Fore, Back
Title$ = LEFT$(Title$, TLen)
' gets the title string (null if there's no title) and title colors
WGetTop Handle
' gets the handle of the top window
FrameInfo$ = SPACE$(8)
WGetUFrame$ Frame, FrameInfo$
' gets the specification for a given user-defined frame type
WGetView Handle, Row, Column
' gets the starting position of a window within a virtual screen
WGetVSize Handle, Rows, Columns
' gets the size of a virtual screen
The Virtual Windowing System page 34
As well as displaying information in a window, you will frequently want to
allow for getting input from the user. Of course, INKEY$ will still work
fine, but that's not an effective way of handling more than single
characters. The virtual windowing system includes a flexible string input
routine which is a lot more powerful:
WInput Handle, Valid$, ExitCode$, ExtExitCode$, MaxLength, St$, ExitKey$
The Valid$ variable allows you to specify a list of characters which may be
entered. If you use a null string (""), any character will be accepted.
ExitCode$ specifies the normal keys that can be used to exit input. You'll
probably want to use a carriage return, CHR$(13), for this most of the time.
You can also specify exit on extended key codes like arrow keys and function
keys via ExtExitCode$.
MaxLength is the maximum length of the string you want. Use zero to get the
longest possible string. The length may go up to the width of the virtual
screen, minus one character. The window will be scrolled sideways as needed
to accomodate the full length of the string.
The St$ variable is used to return the entered string, but you can also use
it to pass a default string to the routine.
ExitKey$ returns the key that was used to exit input.
A fairly strong set of editing capabilities is available through WInput.
The editing keys can be overridden by ExitCode$ or ExtExitCode$, but by
default they include support for both the cursor keypad and WordStar:
Control-S LeftArrow move left once
Control-D RightArrow move right once
Control-V Ins switch between insert and overstrike modes
Control-G Del delete current character
Control-H Backspace destructive backspace
Home move to the start of input
End move to the end of input
The Virtual Windowing System page 35
There are two more routines which allow the virtual windowing system to work
on a wide variety of displays: WFixColor and WSnow.
Chances are, as a software developer you have a color display. However,
there are many people out there who have monochrome displays, whether due to
preference, a low budget, or use of notebook-style computers with LCD or
plasma screens. WFixColor allows you to develop your programs in color while
still supporting monochrome systems. It tells the VWS whether to keep the
colors as specified or to translate them to their monochrome equivalents:
WFixColor Convert%
Set Convert% to zero if you want true color (default), or to any other value
if you want the colors to be translated to monochrome. In the latter case,
the translation will be done based on the relative brightness of the
foreground and background colors. The result is guaranteed to be readable on
a monochrome system if it's readable on a color system. You should check the
results on your system to make sure that such things as highlight bars are
still appear highlighted, however.
In the case of some of the older or less carefully designed CGA cards, the
high-speed displays of the virtual windowing system can cause the display to
flicker annoyingly. You can get rid of the flicker at the expense of slowing
the display:
WSnow Remove%
Set Remove% to zero if there is no problem with "snow" or flickering
(default), or to any other value if you need "snow removal". Using snow
removal will slow down the display substantially, which may be a problem if
you update (WUpdate) it frequently.
Note that you can't detect either of these cases automatically with perfect
reliability. Not all CGA cards have flicker problems. Monochrome displays
may be attached to CGA displays and the computer won't know the difference.
A VGA with a paper-white monitor may well think it has color, and will mostly
act like it, but some "color" combinations can be very difficult to read.
While you can self-configure the program to some extent using the GetDisplay
routine (see Just for Kicks), you should also provide command-line switches
so that the user can override your settings. Microsoft generally uses "/B"
to denote a monochrome ("black and white") display, so you may want to follow
that as a standard. I would suggest "/F" to turn on flicker suppression.
And that, folks, is all there is to it. See DEMO.BAS and TERM.BAS for
working examples. Also see "Telecommunications" for information about a
routine which handles ANSI display and music codes; it displays to the window
of your choice.
Just for Kicks page 36
There are two more routines that may be of some use to you. One of 'em lets
you find out about the active display adapter. The other tells how much
expanded (EMS) memory is available.
To see how much expanded memory is available, use the GetEMS function. It'll
return zero if there is no expanded memory installed:
PRINT "Kbytes of expanded memory:"; GetEMS
The GetDisplay routine tells what kind of display adapter is active and
whether it's hooked up to a color monitor. The only time it can't detect the
monitor type is on CGA setups (it assumes "color"). It's a good idea to
allow a "/B" switch for your program so the user can specify if a monochrome
monitor is attached to a CGA.
GetDisplay Adapter, Mono
IF Mono THEN
PRINT "Monochrome monitor"
ELSE
PRINT "Color monitor"
END IF
SELECT CASE Adapter
CASE 1: PRINT "MDA"
CASE 2: PRINT "Hercules"
CASE 3: PRINT "CGA"
CASE 4: PRINT "EGA"
CASE 5: PRINT "MCGA"
CASE 6: PRINT "VGA"
END SELECT
Miscellaneous Notes page 37
Limitations:
The virtual windowing system allows up to 16 windows to be open at a time,
including the background window, which is opened automatically. This is
subject to available memory, of course.
The far string handler allows up to 65,535 strings of up to 255 characters
each, subject to available memory. When the handler needs additional
memory for string storage, it allocates more in blocks of 16 Kbytes. If
that much memory is not available, an "out of memory" error will be
generated (BASIC error number 7). You can check the size of the available
memory pool using the SETMEM function provided by QuickBASIC.
The communications handler only allows one comm port to be used at a time.
This will change in a future version of BASWIZ.
The file handler does not allow you to combine Write mode with Text mode
or input buffering. This will change in a future version of BASWIZ.
Errors:
All routines are designed to be as bomb-proof as possible. If you pass an
invalid value to a routine which does not return an error code, it will
simply ignore the value.
It's always possible that a problem has escaped notice. If you run into
something that you believe to be a bug or incompatibility, please tell me
about it, whether you've registered BASWIZ or not.
Feedback:
Do you like what you see? Tell me what you like, what you don't like, and
what you'd be interested in seeing in future versions! Chances are good
that I'll use your suggestions. If you don't speak up, though, I won't
know what you're looking for. You can reach me through U.S. Mail or
through several of the international BASIC conferences on BBSes.
Error Codes page 38
The expression evaluator returns the following error codes:
0 No error, everything went fine
2 A number was expected but not found
4 Unbalanced parentheses
8 The expression string had a length of zero
9 The expression included an attempt to divide by zero
The far string handler does not return error codes. If an invalid string
handle is specified for FSSet, it will be ignored; if for FSGet, a null
string will be returned. If you run out of memory for far strings, an "out
of memory" error will be generated (BASIC error #7). You can prevent this by
checking available memory beforehand with the SETMEM function provided by
QuickBASIC. Far string space is allocated as needed in blocks of just over
16 Kbytes, or 16,400 bytes to be exact.
The telecommunications handler returns the following error codes for TCInit:
0 No error, everything A-Ok
1 The comm handler is already installed (you tried to install it twice)
2 Invalid comm port specified
3 Not enough memory available for input and output buffers
The telecommunications handler returns these error codes for XmodemSend:
-12 FATAL : Abort due to excessive errors
-11 FATAL : Abort (by keyboard <ESC> or receiver CANcel request)
-10 DONE : No error, transfer completed ok
-5 WARNING : Checksum or CRC error
-1 WARNING : Time-out error (the receiver didn't respond)
0 WORKING : No error, everything A-Ok
>0 ERROR : Problem reading from the file (see file error codes)
Error Codes page 39
The file services return the following error codes:
(The asterisk "*" is used to identify so-called "critical errors")
0 No error
1 Invalid function number (usually means "invalid parameter(s)")
2 File not found
3 Path not found
4 Too many open files
5 Access denied (probably "write to read-only file")
6 Invalid file handle
7 Memory control blocks destroyed
8 Insufficient memory (usually RAM, sometimes disk)
9 Incorrect memory pointer specified
15 Invalid drive specified
* 19 Tried to write on a write-protected disk
* 21 Drive not ready
* 23 Disk data error
* 25 Disk seek error
* 26 Unknown media type
* 27 Sector not found
* 28 Printer out of paper
* 29 Write fault
* 30 Read fault
* 31 General failure
32 Sharing violation
33 Lock violation
* 34 Invalid disk change
36 Sharing buffer overflow
A "critical error" is one that would normally give you the dreaded prompt:
A>bort, R>etry, I>gnore, F>ail?
Such errors generally require some action on the part of the user. For
instance, they may need to close a floppy drive door or replace the paper in
a printer. If a critical error occurs on a hard drive, it may indicate a
problem in the drive hardware or software setup. In that case, the problem
may possibly be cleared up by "CHKDSK /F", which should be executed directly
from the DOS command line (do not execute this by SHELL).
Troubleshooting page 40
Problem:
QB says "subprogram not defined".
Solution:
The definition file was not included. Your program must contain the line
REM $INCLUDE: 'BASWIZ.BI'
before any executable code in your program. You should also start
QuickBASIC with
QB /L BASWIZ
so it knows to use the BASWIZ library.
Problem:
LINK says "unresolved external reference".
Solution:
Did you specify BASWIZ as the library when you used LINK? You should!
The BASWIZ.LIB file must be in the current directory or along a path
specified by the LIB environment variable (like PATH, but for LIB files).
Problem:
The virtual windowing system doesn't display anything.
Solution:
Perhaps you left out the WUpdate routine? If so, the shadow screen is not
reflected to the actual screen and nothing will appear. The screen also
needs to be in text mode (either no SCREEN statement or SCREEN 0).
Finally, only the default "page zero" is supported on color monitors.
Problem:
The virtual windowing system causes the display to flicker.
Solution:
This is a problem with some old CGA setups. You can cure the problem
permanently and speed up your display into the bargain simply by buying a
new CGA card (about $35 by mail order). I'll add an optional anti-flicker
switch to a later version of BASWIZ if the performance penalty is not too
severe.
Problem:
QuickBASIC doesn't get along with the Hercules display routines.
Solution:
Are you using an adapter which includes Hercules mode along with EGA or
VGA mode? QuickBASIC doesn't like that, since it thinks you'll be using
EGA or VGA mode. Use the stand-alone compiler (BC.EXE) instead of the
environment (QB.EXE) and you should be fine. You might also consider
getting a separate Herc adapter and monochrome monitor. It's possible to
combine a Hercules monochrome adapter with a CGA, EGA or VGA.
Troubleshooting page 41
Problem:
QB says "out of memory" (or "range out of bounds" on a DIM or REDIM).
Solution:
If you're using the memory management/pointer routines, you've probably
allocated too much memory! You need to leave some for QuickBASIC. Use
the SETMEM function provided by BASIC to determine how much memory is
available before allocating memory. The amount needed by QuickBASIC will
depend on your program. The primary memory-eaters are arrays and
recursive subprograms or functions.
Many of the BASWIZ routines need to allocate memory, including the virtual
window manager, telecommunications handler, and memory management system.
Besides checking with SETMEM to make sure there's memory to spare, don't
forget to check the error codes returned by these routines to make sure
they're working properly!
History and Philosophy page 42
"History," you say. "Philosophy. What the heck does that have to do with a
BASIC library? Yuck! Go away and leave me alone!"
Ok. This section is not strictly necessary for using BASWIZ. If you're not
interested, you can certainly avoid reading this without ill effects. "Suit
yourself," he said with a bow and a flourish.
Still here? Thank you! I'll keep it short.
Back in 'bout 1984 or so, I created ADVBAS, one of the very first assembly
language libraries for BASIC. That was for IBM BASCOM 1.0, well before
QuickBASIC came out. I created the library for my own use and ended up making
a moderately successful shareware project out of it.
ADVBAS was designed in bits and pieces that came along whenever I felt like
adding to the library or needed a new capability. The routines were designed
at a low level, with most of the actual work needed to accomplish anything
useful left to BASIC. All this resulted in a decent amount of flexibility
but also a good deal of chaos as new routines provided capabilities that
overlapped with old routines. Although I tried to keep the calling sequence
reasonably standardized, it didn't always work out that way. Then too, the
library was designed well before the neat capabilities of QuickBASIC 4.0 came
into being and couldn't take good advantage of them.
Second came ProBas, a commercial version of ADVBAS. ProBas is a vastly more
powerful superset of the ADVBAS library. Unfortunately, it suffers from the
same flaws. No old routines can be discarded or even modified if at all
possible, to provide compatibility from one version to the next. The lack of
thought inherent in the original design caused a mad proliferation of
routines, many overlapping, most still low-level and hard to use. At version
4.0, there are over 500 (or is it 600?!) different routines-- something for
everyone, to be sure, but tending towards complete pandemonium.
The BASWIZ project is a third-generation library. It is designed to overcome
the liabilities I've encountered with ADVBAS and every other library I've
seen for BASIC. Rather than being put together haphazardly, one routine at a
time, I have designed BASWIZ as a coordinated collection. The virtual
windowing system is an excellent example of this. Rather than having
separate print routines, window routines, screen saving routines, virtual
screen routines and all the rest, it is all combined into one single
package. The routines are designed at a high level, providing a maximum of
functionality with a minimum of programming effort. The gritty details are
kept hidden inside the library where you need never deal with them. Consider
the apparent simplicity of the far string handler! Many more capabilities
will be added in future versions, but... very carefully.
This library represents the culmination of many years of experience in the
fields of BASIC and assembly language programming. I have spared no effort.
It's the best I can offer and I hope you'll forgive me for taking some pride
in my work! If you find this library powerful and easy to use, I'll count my
efforts a great success.
As you might have guessed, I'm not exactly in it "just for the money."
Nonetheless, money is always nice! If you like BASWIZ, please do register.
That will enable me to upgrade my equipment and design more advanced BASWIZ
routines. Currently I have a 386SX with an EGA display. I would like to be
able to support the VGA, scanners, sound boards and other hardware as well.
Using BASWIZ with PDQ page 43
Crescent's PDQ library does not currently support the SETMEM function, which
is required by many of the BASWIZ routines. Fortunately, the use of PDQ
precludes the need for SETMEM, so all we need to do is to include a "dummy"
SETMEM routine to satisfy LINK:
LINK program+PDQSTUB/NOE,,NUL,PDQ+BASWIZ;
If you use LINK differently, that's fine. The only thing necessary is to
make sure "+PDQSTUB" is listed just after the name of your program as the
first LINK argument. Use of /EX and other LINK parameters is no problem.
Use of other libraries, if any, is also supported.
Crescent thoughtfully provided me with a free copy of PDQ in order that I
might resolve any incompatibilities between it and BASWIZ. If you are not
familiar with PDQ, it is a replacement library for BASIC's own runtime
libraries. While not providing every capability of plain QuickBASIC, it
allows you to create substantially smaller EXE files for those programs that
qualify. Support is currently lacking for floating point (single/double
precision) numbers, music, and graphics, among other things. Communications
support is available as an add-on. Check with Crescent for more recent
details.