Copy Link
Add to Bookmark
Report
QBNews Volume 1 Number 2
Volume 1, Number 2 February 2, 1990
**************************************************
* *
* QBNews *
* *
* International QuickBASIC Electronic *
* Newsleter *
* *
* Dedicated to promoting QuickBASIC around *
* the world *
* *
**************************************************
The QBNews is an electronic newsletter published by Clearware
Computing. It can be freely distributed providing NO CHARGE is charged
for distribution. The QBNews is copyrighted in full by Clearware
Computing. The authors hold the copyright to their individual
articles. All program code appearing in QBNews is released into the
public domain. You may do what you wish with the code except
copyright it. QBNews must be distributed whole and unmodified.
You can write The QBNews at:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
Copyright (c) 1989 by Clearware Computing.
The QBNews Page i
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
T A B L E O F C O N T E N T S
1. From the Editors Desk
With a future so bright, I got to wear shades ... ............ 1
2. Mail Bag
Mail From Our Readers ........................................ 2
3. Algorithms
Fast String Searching in QuickBASC by Ethan Winer ............ 4
Reversing INSTR by Larry Stone ............................... 6
4. Who ya gonna call? CALL INTERRUPT
Using the PSP by Hector Plasmic .............................. 9
5. Product Announcements
Microsoft Professional BASIC 7.0 ............................. 13
Index Manager CDP Consultants .............................. 15
MicroHelp Library Manager .................................... 16
6. The Tool Shed
Update on MicroHelp's QB Optimizer Package by Larry Stone ... 19
7. Ask MR. WIZZARD
Mr. Wizard tells what "Bytes Free" means ..................... 20
8. Graphically Speaking
Getting and Putting Graphics by Frederick Volking ............ 22
9. And I Heard it Through the Grapevine
Exerpts from the QUIK_BAS echo ............................... 30
10. Some Assembly Required
Retuning Errorlevels to QB by Harold Thomson ................. 34
11. Swap Shop
Extended Key Codes ........................................... 39
Menus ........................................................ 43
12. Input Past End
Contacting the QBNews ........................................ 47
The QBNews Page ii
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
F r o m t h e E d i t o r s D e s k
----------------------------------------------------------------------
The Future of the QBNews by David Cleary
With this issue, I can say I am actually proud. The support you
have shown has been overwhelming. I just feel that this newsletter
will take off because the better it gets, the more support it will
receive. The more support it receives, the better it will get. It's a
viscous circle.
I have received some very nice letters from people. Two of them
are in our Mailbag section. I would like to here suggestions on what
you would like to see in this newsletter. With every issue, we will
continue to evolve to suit the needs of the QuickBASIC programmer. I
do feel that this issue does not address the needs of the beginner. I
would like to see a section devoted to beginners so if anybody out
there has some ideas, please contact me.
I am also thinking of making the QBNews available on disk. If you
are interested in receiving it on disk, for a nominal charge, let me
know. If enough would like it this way I may institute it for the next
issue. Also, if you write me, I would be interested in knowing where
you got your issue.
One final note. I would like this newsletter distributed
internationally, specifically England and Australia. If you run a BBS
in either of these countries, I would be interested in hearing from
you. I would be willing to upload it to your board. If anybody outside
of the USA or Canada is reading this, I want to here from you. I'd
like to know that this is being distributed as widely as possible.
Until April, goodbye.
David Cleary
The QBNews Page 1
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
M a i l B a g
----------------------------------------------------------------------
Dear Mr. Cleary,
Your news letter is a great idea, please continue it. The
programs were, also, very informative and useful. In the section
describing the program, SCRNSUBS.BAS, you make this statement.
"The only problem when using internal QB routines is that I
haven't found a way to use them in the environment."
I have found a way by making a Quick Library with the routine.
The routine will only work if you include the DECLARE statement in the
module.
Examples: DECLARE SUB MemCopy ALIAS "B$ASSN" (BYVAL...
DEFINT A-Z
SUB ScrSave (Buffer(), Page) STATIC
.
.
END SUB
Compile: BC SCRSAVE/O;
LINK /Q SCRSAVE, SCRSAVE.QLB,, BQLB45.LIB;
Environment: A DECLARE statement is not needed, just make your
calls.
Call ScrSave(Buffer(), Page)
Gaylon Hill
Louisville, TN
[EDITOR'S NOTE]
It seems that the simplest way is the one that works. I was busy
trying a bunch of convoluted ways to do it with no luck at all. Thanks
for sharing this with us. David Cleary
Dear Mr. Cleary,
I just read your QBNews Vol 1 No 1 which I picked off the Probas
BBS. Great idea. I am like most other BASIC programmers, no training,
ever. However, I am interested in the things that interrupts can do.
The catch is, I really don't understand what an interrupt is ect. I
can duplicate Hector's code but that's about it. How about getting
Hector to write a little about interrupts and what they are and what
the terminology means.
The QBNews Page 2
Volume 1, Number 2 February 2, 1990
John F. Farrell
Mesquite, TX
[HECTOR'S RESPONSE]
I'm not gonna go all techie on you here, so relax. But what is
an interrupt anyway? About what the name implies. When an interrupt
occurs, the central processing unit (CPU) "interrupts" what it's doing
and transfers control to an interrupt handler. The handler is
responsible for determining the cause of the interrupt and taking
appropriate action, then returning control to the interrupted process.
The CPU knows where the interrupt handlers are located by looking
up their addresses in special sections of memory known as the
interrupt vector table. In this way an interrupt can be replaced or
chained.
Software interrupts, the ones we're using in this column, can be
triggered by any program by using the INT instruction (QuickBASIC does
that for you when you use CALL INTERRUPT). Interrupt 21h is the MS-
DOS function dispatch interrupt, which provides easy access to many
operating system functions not directly available through QuickBASIC.
Other interrupts can also be useful, notable 10h (for IBM-compatibles)
for video, 13h for disk control, 16h for the keyboard, and so forth.
There are several readily available books covering the various
MS-DOS and BIOS interrupts, and an excellent file (in archive format)
called INTER489 from Ralf Brown (1:129/46 at last report) if you'd
like to learn more about them.
Hector Plasmic
The QBNews Page 3
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
A l g o r i t h m s
----------------------------------------------------------------------
FAST STRING SEARCHING IN QUICKBASIC by Ethan Winer
A wise programmer once said, "You can never write a sort routine
that's fast enough or small enough." No doubt, this same philosophy
holds true for string searching routines. Therefore, I was quite
interested to see an article in the July/August Programmers Journal
which presented an assembly language version of the Boyer-Moore
searching algorithm. Compared to some of the other implementations I
had seen, this one appeared to be very tight and well coded. I've
written a few searching routines myself to use with QuickBASIC string
arrays, and was very eager to see how much faster this "new" method
would be.
After carefully modifying the printed routine to work with
QuickBASIC 4.5, I devised a short test program to time it. My
benchmark searched a 10,000 character string in a loop, and compared
two algorithms -- the Boyer-Moore routine published in Programmers
Journal, and QuickBASIC's built-in INSTR function. However, based on
the results I obtained, it appears that the Boyer-Moore method is
little more than a mathematical curiosity. Indeed, it was only half
as fast as QuickBASIC!
HOW QUICKBASIC DOES IT
So how does QuickBASIC search strings so fast? A quick
examination under Microsoft CodeView reveals a fundamentally simple,
yet elegant algorithm. I have adapted QuickBASIC's method (with
permission from Microsoft), and named it SearchString. This is shown
in Listing 1. Please understand that this routine is not intended for
use with QuickBASIC. Rather, it is to provide a foundation for
achieving BASIC's high performance in other high-level languages. If
you do intend to try SearchString with QuickBASIC, bear in mind that
it has been designed as a function, and therefore must be declared.
Assembler functions were added to QuickBASIC beginning with version
4.00, and they are one of the most important features that Microsoft
has added to the language.
Three passed parameters are required to specify the starting
character, the string being searched, and the sub-string to locate.
Even though this version uses the same basic algorithm as QuickBASIC,
it is slightly faster because it does not check for an illegal Start
parameter. Also, the start variable is passed "by value", rather than
by address which is the method used by most high-level languages.
This eliminates an extra step of locating the variable within the
assembler routine, thus saving a few bytes and clock cycles.
Rather than relying on a purely "brute force" approach that
compares every character in both strings, QuickBASIC instead isolates
the first character. In this case, it is loaded into AL. Then the
8088's speedy Scasb instruction is used to find the first occurrence
where a possible match might begin. The beauty of this approach is
The QBNews Page 4
Volume 1, Number 2 February 2, 1990
twofold -- Scasb is the fastest way to find a single matching
character, and subsequent searches then require comparing one less
character in each string.
SearchString begins by obtaining the starting character offset in
the string being searched. Even though most situations would require
searching the entire string, being able to resume a search in the
middle of the string is often valuable. Next, SI and DI are loaded
with the address of the first character in each string, and CX is
assigned the number of characters to search. Unlike C or Pascal,
BASIC maintains a string descriptor which tells how long a string is,
and where in near memory its data is located. The final
initialization steps are to adjust DI to point to the correct portion
of the string being searched, and to load AL with the first character
of the string to find.
The main loop begins at the Scan label, and ends at the label
Found. When Scasb finds a character that matches the one in AL, the
remaining characters in both strings are compared. If a match is
found, SearchString returns to the caller with AX holding the offset
into the source string. Otherwise, Scasb is used again to find the
next occurrence of the first character. This is repeated until either
the search string is located, or the source string is exhausted.
ADAPTING THE ROUTINE TO OTHER LANGUAGES
SearchString may be easily modified to work with C, Pascal,
assembly language, or whatever by simply changing how the various
registers are initially loaded. For example, the length of a string in
Pascal is stored in the byte that precedes its first character.
Conversely, C uses a zero-byte to mark the end of its strings, which
means that extra steps (and time) will be needed to determine their
length before beginning the search. SearchString could also be
modified to accept additional parameters for the string lengths when
called from C to save that added overhead. Indeed, BASIC's use of
string descriptors is one of the primary reasons it is so fast at
string operations.
I believe it is important to point out how fast and well-designed
BASIC really is. Many programmers unfairly criticize BASIC as a
language, solely because of the interpreter that comes with DOS.
Besides being extremely easy to use, current versions of QuickBASIC
offer a wealth of structured programming features, while offering more
commands and features than any other high-level language. And as we
have seen, QuickBASIC is also aptly named.
Ethan Winer is President of Cresent Software and the author of
QuickPak and PDQ.
[EDITORS NOTE]
The source code to String Search is in the archive SSEARCH.ZIP.
The QBNews Page 5
Volume 1, Number 2 February 2, 1990
The Speedy INSTR Function - Programming a Backwards INSTR Routine
by Larry Stone
One of the easiest traps to fall into is using the FOR...NEXT
loop to search a string for a sub-string. I call it a trap because it
seems the obvious choice of functions to use, when, often-time, it is
neither the fastest, nor the best of available choices.
The choice of the FOR...NEXT loop seems the obvious because when
we use it, we do so on a variable who's length is known. So why not
just step through it, character by character, and look for the sub-
string?
The drawback in using the FOR...NEXT loop is, primarily, speed.
A typical use (as demonstrated by the correspondence in the Quik_BAS
echo) is finding the starting point of a sub-string, i.e., looking for
a file name embedded within a complete path and filename statement.
Now, let's look what happens when you use a FOR...NEXT loop:
fullName$ = "C:\UTILITY\DISK\PCTOOLS\PCTOOLS.EXE"
length% = LEN(fullName$)
FOR A% = 1 TO length%
IF MID$(fullName$, A%, 1) = "\" THEN B% = A%
NEXT
In the above example, we are looking for a sub-string composed of
only one backslash character. The string to search is 35 characters
long. This means we must loop 35 times in order to find four
occurrences of the sub-string. Pretty inefficient, isn't it? All we
wanted was the last occurrence of the backslash character and we had
to loop 35 times only to discover that the last occurrence was at
position 24 (In the above example, B% will equal 24).
The INSTR function handles the above solution in a much more
elegant manner. The example below is a backwards INSTR routine.
Although it does not, in actuality, step negatively through a string,
it quickly reports the last occurrence of any sub-string.
fullName$ = "C:\UTILITY\DISK\PCTOOLS\PCTOOLS.EXE"
searchString$ = fullName$
subString$ = "\"
P% = BackInstr%(0, searchString$, subString$)
PRINT "The last position of the backslash is"; P%
FUNCTION BackInstr% (start%, searchString$, subString$)
IF start% = 0 THEN start% = 1
N% = INSTR(start%, searchString$, subString$)
A% = N%
DO WHILE N%
N% = N% + 1
N% = INSTR(N%, searchString$, subString$)
IF N% THEN A% = N%
LOOP
BackInstr% = A%
END FUNCTION
The QBNews Page 6
Volume 1, Number 2 February 2, 1990
The elegance of this function is that your DO...LOOP executes
exactly four times - once for each occurrence of the backslash! Speed
of execution is achieved because there is only four iterations of the
loop instead of the 35 in the FOR...NEXT loop, and speed of execution
is further improved because of the way the BASIC's INSTR function
works.
The INSTR function does not do a "brute-force" evaluation,
comparing every character in both strings. Instead, it isolates the
first character and then looks for the first occurrence of a possible
match. Looking for a single character is much faster than checking
every character. If a match is found, and if subString$ had more
characters in it, then the next character is searched. If the next
character is not matched, INSTR proceeds from the next position in the
string where the first match was found, requiring that much less
string to search. The actual asm instruction used is scasb. This
technique is twice as fast as the Boyer-Moore algorithm, considered by
many as a bench-mark. For a more exact description of the process,
read an article called, "QuickBASIC's Fast String Searching
Algorithm", in the Programmer's Journal, 7.6, authored by Ethan Winer
of Crescent Software.(See Above)
Would you like your programs, when they search for a sub-string,
to search for exact matches as well as, embedded matches? Simply swap
the locations of the search string and the sub-string. Here's how
INSTR can work for you:
searchString$ = "These"
subString$ = "The"
start% = 1
'---- Check one string against the other.
N% = INSTR(start%, searchString$, subString$)
IF N% THEN
'---- If a match was found, swap locations and search again.
N% = INSTR(subString$, searchString$)
IF N% THEN
'---- Now, if a match was found then it's an exact match.
PRINT "Sub-String is Exact Match of Search String"
ELSE
'---- Otherwise, it's an embedded match.
PRINT "Sub-String is Embedded in Search String"
END IF
ELSE
'---- The sub-string was not located in the Search String.
The QBNews Page 7
Volume 1, Number 2 February 2, 1990
PRINT "Sub-String is Not Found in Search String"
END IF
The example above will report that N% is equal to one because
"The" is a subset of "These". To have this code determine whether
"The" and "These" are exact matches would require the BackInstr
function. Use it to find the end of the word that was matched (use a
space, period, and hyphen as sub-strings). When you know where the
end of the word is, simply state that: word$ = MID$(searchString$, N%,
P%). Then, use the INSTR function to compare INSTR(subString$,
word$).
If you wish to move through the searchString$ for the next
occurrence of a match, place all but the first three lines of code
within a DO WHILE start%...LOOP. If N% is some positive value, then,
at the bottom of the loop, LET start% = N% + 1. Otherwise, if N% is
zero, then LET start% = N% to end the loop. When you are searching
for more than one occurrence of a match, you should have an escape key
assigned to get you out of the loop. A$ = INKEY$: IF A$ = CHR$(27)
THEN EXIT DO works quite well.
One word of caution. The INSTR function returns zero whenever a
compiled .EXE uses the literals CHR$(1) or CHR$(2) in a string. This
should not, however, cause any problems in QB's environment.
The QBNews Page 8
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
W h o y a g o n n a c a l l ? C A L L I N T E R R U P T
----------------------------------------------------------------------
Using the PSP by Hector Plasmic
COMMAND$ lets you see what was entered on the command line (the
command tail) by the user when your program was started. For
instance, if the user typed:
C:\> PROGRAM -a -b
PRINT COMMAND$ would display "-A -B". Notice that the command
tail was uppercased by COMMAND$, though. What if you need to receive
case sensitive information from the command line?
Well, it just happens that DOS can provide you with the answer.
DOS keeps an area called the PSP (program segment prefix) which
contains a lot of potentially useful information in the following
format:
0000h Interrupt 20h
0002h Segment, end of allocation block
0004h Reserved
0005h Long call to MS-DOS function handler
000Ah Previous contents of termination handler interrupt vector
(Int 22h)
000Eh Previous contents of CTRL-C interrupt vector (Int 23h)
0012h Previous contents of critical-error interrupt vector (Int 24h)
0016h Reserved
002Ch Segment address of environment block
002Eh Reserved
005Ch Default file control block #1
006Ch Default file control block #2 (overlaid if FCB #1 opened)
0080h Command tail and default DTA (buffer)
So, to get the command tail in its pre-uppercased format, all we
have to do is PEEK into the PSP. But how do we find out where the PSP
is? Interrupt 21h function 62h will get it for you, returning the
PSP's segment in register .bx. If DEF SEG is set to the returned .bx
value, PEEK will be looking at the PSP.
What follows is a simple program to get the command tail from the
PSP, list the environment, and return the name of the program that's
currently running.
DEFINT A-Z
TYPE RegType
AX AS INTEGER
BX AS INTEGER
CX AS INTEGER
DX AS INTEGER
The QBNews Page 9
Volume 1, Number 2 February 2, 1990
BP AS INTEGER
SI AS INTEGER
DI AS INTEGER
Flags AS INTEGER
END TYPE
TYPE RegType2
AX AS INTEGER
BX AS INTEGER
CX AS INTEGER
DX AS INTEGER
BP AS INTEGER
SI AS INTEGER
DI AS INTEGER
Flags AS INTEGER
DS AS INTEGER
ES AS INTEGER
END TYPE
'You must link in QB.LIB (QB.QLB) for Interrupt
DECLARE SUB Interrupt (Intnum%, InReg AS RegType, OutReg AS RegType)
DECLARE SUB InterruptX (Intnum%, InReg2 AS RegType2, OutReg2 AS
RegType2)
DIM SHARED InReg AS RegType
DIM SHARED OutReg AS RegType
InReg.AX = 25088 'Find PSP
Interrupt &H21, InReg, OutReg
DEF SEG = OutReg.BX 'Segment of PSP
EnvLo = PEEK(&H2C) 'The pointers from the PSP to our copy of the
EnvHi = PEEK(&H2D) 'environment
ComlineLength = PEEK(&H80) 'Length of command tail in PSP
CLS
PRINT " Command tail:": PRINT
'Print command tail
FOR X = &H81 TO &H81 + ComlineLength - 1
PRINT CHR$(PEEK(X));
NEXT
PRINT : PRINT
DEF SEG = EnvLo + (EnvHi * 256) 'Segment of environment
X = 0
'Print environment
The QBNews Page 10
Volume 1, Number 2 February 2, 1990
PRINT " Environment:": PRINT
DO UNTIL PEEK(X) = 0
DO UNTIL PEEK(X) = 0
PRINT CHR$(PEEK(X));
X = X + 1
LOOP
PRINT
X = X + 1
LOOP
PRINT
X = X + 3
PRINT " Program drive:\path\name:": PRINT
'Print program filespec
DO UNTIL PEEK(X) = 0
PRINT CHR$(PEEK(X));
X = X + 1
LOOP
PRINT
END
Knowing where the PSP is gives you yet another advantage. When a
QuickBASIC program RUNs another program outside the environment, the
RUN program inherits the old PSP, _including_ the old command tail.
If we change that command tail before RUNning the new program, we can
pass arguments to it as if they were typed on the command line!
Here's the Sub to do it:
'
SUB RunIt (Tail$, Prog$) 'Changes command tail and RUN program
InReg.AX = 25088 'Find PSP
Interrupt &H21, InReg, OutReg
DEF SEG = OutReg.BX 'Point PEEK and POKE at it
Text$ = Text$ + " " + CHR$(13) 'Prep the new command tail
POKE &H80, LEN(Text$) - 1 'Poke the length
FOR X = &H81 TO &H81 + LEN(Text$) 'Poke the string
POKE X, ASC(MID$(Text$, X - &H80, 1) + " ")
NEXT
RUN Prog$ 'Run the program (.EXE or .COM)
END SUB
The QBNews Page 11
Volume 1, Number 2 February 2, 1990
To test this Sub, use it to RUN a program that just PRINTs COMMAND$,
and see if it picks up your new command tail.
The QBNews Page 12
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
P r o d u c t A n n o u n c e m e n t s
----------------------------------------------------------------------
Microsoft Professional BASIC 7.0
REDMOND, Wash. -- November 27, 1989 --
Microsoft Corporation today announced the MicrosoftR BASIC
Professional Development System, the first BASIC product designed to
offer serious BASIC programmers the tools they need to develop
powerful professional applications. The product will be available
through Microsoft retail outlets in December.
"This product represents a quantum leap for BASIC," said Charles
Stevens, general manager of Microsoft's Data Access Business Unit.
"It addresses the needs our BASIC customers said were most important.
While Microsoft QuickBASIC is still the right choice for beginning
and occasional programmers, the BASIC Professional Development System
is truly tailored for serious BASIC programming professionals."
The Microsoft BASIC Professional Development System is the result of
extensive research that showed BASIC programmers have a strong need
for high-end tools. It includes the Microsoft Professional ISAM
(Indexed Sequential Access Method) for data management tasks; new
language features; increased memory capacity to support large
application development; and an integrated development environment
based on Microsoft QuickBASIC. Executables generated with the new
BASIC product rival those of traditional "high-performance" languages
in both size and speed.
New Features Expedite Business Programming
Many applications written in BASIC are for business uses, such as
accounting and data management tasks. To reduce the effort needed to
develop these applications, Microsoft has added a fully integrated
ISAM to the new BASIC product. The single-user integrated ISAM
provides file handling capabilities; but unlike in other ISAMs, such
as NovellR BTrieveR, the powerful and elegant command syntax is
integrated directly into the BASIC language.
The Microsoft BASIC Professional Development System also includes a
new fixed-decimal-point data type for representing currencies; and
three add-on libraries, derived from Microsoft Excel, for numeric
formatting, date/time and financial functions.
Microsoft Answers User Requests with New Language Features Microsoft
added several features requested by BASIC programmers. Static arrays
in records allow creation of more sophisticated data structures.
Errors can now be trapped and handled locally in a subprogram. DOS
file control statements make it easy to write programs to manipulate
the DOS file system.
Increased Memory Capacity Removes Limitations
The Microsoft BASIC Professional Development System has removed
The QBNews Page 13
Volume 1, Number 2 February 2, 1990
barriers limiting the size of BASIC programs. To accomplish this,
Microsoft added EMS (Expanded Memory Specification) support and
increased total string space in the development environment.
Compiled executables can also take advantage of more total string
space, as well as overlays, to create programs as large as 16 MB.
Smaller, Faster Executables Rival Other Languages
BASIC programmers told Microsoft that their top priorities were a
reduction in .EXE size and an increase in speed. With special
enhancements in code generation, run- time performance and 80286 code
generation, BASIC now matches the features and performance offered by
other professional languages. Developers have increased control over
the size and content of the BASIC run-time, thus reducing executable
program size.
Microsoft QuickBASIC Extended Environment, Other Tools
BoostProductivity
The new product includes a high-performance development environment,
sample-code toolboxes, and tools for mixed- language and MSR OS/2
system programming. Professional programmers will appreciate the
increased capacities and added features of the integrated development
environment, which is extended from Microsoft QuickBASIC. The
sample-code toolboxes -- including user-interface, presentation
graphics and matrix math routines -- illustrate how common
programming problems can be solved with generalized and reusable
BASIC code. The Microsoft CodeViewR debugger and Microsoft Editor
are included for mixed-language and OS/2 systems programming.
System Requirements, Pricing and Availability
The Microsoft BASIC Professional Development System requires a
personal computer running MS-DOSR or PC-DOSTM operating system 3.0 or
OS/2 operating system 1.1 or higher; one double-sided disk drive and
a hard disk; and a minimum of 640K of memory (EMS 4.0 is
recommended). A Microsoft Mouse is optional. Compiled programs run
on MS-DOS 2.1 or higher or OS/2 operating system 1.1 or higher.
The Microsoft BASIC Professional Development System will be
available in December 1989 at Microsoft retail outlets. It has a
suggested retail price of $495. The upgrade price for registered
users of Microsoft Basic 6.0 is $150.
#########
Microsoft BASIC Professional Development System Microsoft, MS, MS-
DOS, CodeView and the Microsoft logo are registered trademarks of
Microsoft Corporation.
Btrieve is a registered trademark of Software, Inc., a Novell
Company.
Novell is a registered trademark of Novell, Inc.
PC-DOS is a trademark of International Business Machines Corporation.
The QBNews Page 14
Volume 1, Number 2 February 2, 1990
QUICKBASIC B+ TREE FILE INDEXING
For years there has been a crying need for an easy to use, reasonably
priced file indexing system for QuickBASIC. This problem has now
been solved with Index Manager(tm). Programmers can now create B+
tree file indexes within their QuickBASIC programs. Files can be
accessed randomly by full key, browsed by partial key, or sorted
forward or backward. All functions of Index Manager are performed by
just one external subroutine, making Index Manager very easy to use.
Only indexes are managed. The programmer still retains full control
over all data files.
Index Manager creates its indexes using a prefix B+ tree - one of the
most powerful index structures available today. The program is
written in assembler language for maximum speed and minimum size. It
utilizes a large cache buffer to keep the most important index
records in memory and thereby reduce disk access to a minimum. The
result is a very fast, exceptionally powerful indexing system.
Two-thirds of the Beta Testers for Index Managers gave it an overall
rating of 10 out of 10. ProWindows author David Stasinski called
Index Manager "the absolute best B-tree system I have ever seen."
A demo version of Index Manager is available for downloading on
Compuserve and GEnie. On Compuserve it is located on the Microsoft
System Forum (GO MSSYS) on data library 1 or 2, and called
INDEXM.ARC. On GEnie, it is located in Microsoft (M 505) RoundTable
data library 10 as file 828.
The Index Manager package includes a high quality perfect bound 64
page Users Guide, 8 sample programs, and a Quick Reference card.
Index Manager can be purchased for $59 from
CDP Consultants
1700 Circo Del Cielo Drive
El Cajon, CA 92020
or by phone at 619-440-6482.
The QBNews Page 15
Volume 1, Number 2 February 2, 1990
QB/Pro Volume 8
The MicroHelp Library Manager
A Simple Concept
We've revolutionized the way that programmers think about libraries.
Instead of worrying about what object modules go in what libraries,
our Library Manager (LM.EXE) lets you organize your routines by
project.
Whenever you want to build a fresh library, whether it's a Quick
Library, a LINK Library, or an Extended Runtime Library, simply tell
LM the Project(s) that you want the library for and then tell it to
build the library!
View Library Contents
LM lets you view the contents of both Quick and LINK libraries. In
fact, the program lets you modify LINK libraries without having to
worry about command line switches. Simply mark the action you want to
take on each module in the library.
Easy-To-Use Search Capabilities
You can configure LM so that it automatically searches your disk for
"unresolved externals". That means you don't have to worry about where
a routine is located - all you need is the routine name! LM can search
object modules and LINK libraries in order to resolve all external
references.
As a matter of fact, LM even lets you look inside object modules, so
you can tell what routines are included. If you see one you want to
use, simply tell LM to add it to your standard list of routines.
When you're viewing a LINK library or set of object modules, you can
tell LM to search for specified public symbols or externals.
The Nitty Gritty
LM is designed to work with two main types of information:
Standard Routines that you use on a regular basis. The product
comes with a text file containing the names of all the routines in all
of MicroHelp's products. You can add your own routines, as well as
routines from other add-on products, and delete the routines you
don't normally use.
Standard Projects tell LM how you want to group your Standard
Routines. You can load up to 250 different projects at one time, each
having a unique code and a description. After designating your
projects, you can go through your standard routines and tell LM which
routines are to be included with each product. We even give you a
"wildcard project" that tells LM to include a routine in all
libraries.
You can add to your list of Standard Routines automatically whenever
you View a LINK library or View Object Modules. When you want to build
The QBNews Page 16
Volume 1, Number 2 February 2, 1990
a library, you simply select one or more Projects and then tell LM to
go to work. If you prefer, you can tell LM to use the routines that
you specify instead of or in addition to the Project(s) you selected.
This allows you to build custom libraries without having to specify a
new Project.
For Your Convenience
LM is very flexible with its configuration. Not only does it search
for its configuration file using the same logic described above, it
also lets you configure the default path for:
Quick Libraries
LINK Libraries
Standard Routine and Standard Project files
Temporary work files
Object modules
You can also configure the following to suit your preferences:
Quick Library Support Library
Default LINK library name
Default Quick library name
Whether or not to resolve external references when
building a library.
Whether or not to search libraries in order to resolve the
externals.
Whether or not object module presence should be verified
before invoking LINK or LIB.
Free Utility Program Included
Included with The MicroHelp Library Manager is a FREE Utility program
called PREQLB. When you absolutely positively have to have a new
library for a program right now, just tell PREQLB the name of your
source file. PREQLB will quickly scan your program, including
supporting BASIC modules, and will help you create a new Quick Library
or LINK Library immediately.
PREQLB also tells you if you have DECLAREd a SUBprogram, but it is not
used in your program, or if you have a SUB or FUNCTION in your program
that is not invoked by the program.
System Requirements
The minimum system requirements for using The MicroHelp Library
Manager are:
An IBM PC, XT, AT, PS/2 or close compatible.
PC/MS DOS version 3.00 or later.
LIB.EXE (any version) and LIB.EXE 3.61 or later.
Any Microsoft or IBM BASIC compiler. Quick Libraries
require Microsoft QuickBASIC 4.0 or later or Microsoft
BASIC Compiler 6.0 or later, as well as the LINK program
that comes with the compiler.
The QBNews Page 17
Volume 1, Number 2 February 2, 1990
A hard disk (fixed disk).
1 Diskette drive (5.25"). 3.5" disks are available for a
nominal charge.
An 80 column monitor.
The Last Word
The MicroHelp Library Manager comes with a three-ring bound manual and
free full-time technical support. The suggested retail price is $59.00.
To order, or for more information, contact:
MicroHelp, Inc.
4636 Huntridge Drive
Roswell, GA 30075-2012
(800) 922-3383
(404) 552-0565
The QBNews Page 18
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
T h e T o o l S h e d
----------------------------------------------------------------------
Update on QB Optimizer by Larry Stone
When "The QBNews" last went to press, I was still awaiting QB
4.0b from Microsoft. Without this upgrade, it was not possible to
give a more extensive review of MicroHelp's QB Optimizer.
Well, the update from Microsoft finally arrived and I immediately
re-compiled my test program, FULLCUR.BAS, using all of the Xmodules
supplied by MicroHelp. The stand-alone exe compiled to 3343 bytes, as
I anticipated.
Because I now have the opportunity to thoroughly test Mark
Novisoff's utility, I will post my findings in future editions of this
electronic newsletter.
[EDITORS NOTE]
Because of my procrastination, there are no new reviews in this
issue. In the next issue, (April), look for reviews on Index Manager,
P-Screen Professional, and Stay-Res Plus.
David Cleary
The QBNews Page 19
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
A s k M R . W I Z Z A R D
----------------------------------------------------------------------
Ask Mr. Wizard Information from the Microsoft Knowledge Base
The Microsoft Knowledge Base is a service available to
subscribers on Compuserve. It is a database of frequently asked
questions that Microsoft Tech Support uses to find answers to
questions asked to them. The QBNews has been given permission to
reprint some of these answers. You can access the Knowledge Base on
Compuserve by typing GO MSOFT at any ! prompt.
Question: Gee Mr. Wizard, What do the "Bytes Available" and "Bytes
Free" mean when I compile?
Well Tudor . . .
Title: Explanation of Compiler "Bytes Available" and "Bytes Free"
Document Number: Q27347 Publ Date: 16-JAN-1989
Product Name: Microsoft QuickBASIC Compiler
Product Version: 4.00 4.00 4.50
Operating System: MS-DOS
Summary:
At the end of a successful compilation, the BC.EXE compiler displays
the following message:
nnnnn Bytes Available
nnnnn Bytes Free
0 Warning Error(s)
0 Severe Error(s)
This message gives the amount of workspace available before (Bytes
Available) and after (Bytes Free) a program is compiled.
If the Bytes Free is approaching 1024 or less, then you are
approaching the limits of code generation for this module, and you
should break your program into smaller, separately compiled
subprograms or FUNCTION procedures that can be linked together (with
LINK.EXE).
This information applies to QuickBASIC Compiler Versions 1.00, 1.01,
1.02, 2.00, 2.01, 3.00, 4.00, 4.00b, and 4.50 for MS-DOS, the
Microsoft BASIC Compiler Versions 5.35 and 5.36 for MS-DOS, and
Versions 6.00 and 6.00b for MS OS/2 and MS-DOS.
More Information:
The 64K memory segmentation architecture of the 8086 chip has
influenced design limitations of the BASIC Compiler so that it can
only generate 64K or less for a program's code segment. The 64K
temporary work space available for the compiler itself can also limit
The QBNews Page 20
Volume 1, Number 2 February 2, 1990
code generation.
Bytes Available is the initial amount of compiler workspace available
for storing the symbol table and the line number table, and for
working storage for code generation and optimization.
Bytes Free is the size of unused compiler workspace after the
compiler has finished.
Note: If you want to see the number of bytes that were generated for
a module's code segment, refer to the .MAP file that can be
optionally output from the LINK.EXE linker. The program must have
been successfully compiled and linked to get a valid link .MAP file.
COPYRIGHT Microsoft Corporation, 1989.
The QBNews Page 21
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
G r a p h i c a l l y S p e a k i n g
----------------------------------------------------------------------
Getting and Putting Graphics by Frederick Volking
Due to the Graphic nature of this article, parental discretion is
advised.
I am involved in writing several graphics oriented games. Each of
these games require many different images (called sprites). I was
evaluating the best method to store these sprites for the games I'm
writing. I noticed the graphics GET & PUT commands could store
relatively large images in surprisingly small arrays.
I decided that if I could understand the method used to store the
images in the arrays, then I could write a few routines which would
make my games easier to program. Specifically routines which would
manipulate the images ONCE THEY WERE ALREADY STORED IN THE ARRAY!
Routines like; (1) "flipping" an image upside down or left to right.
(2) rotating an image around a fixed point (3) changing all
occurrences of color-X to a new color-Y [easy way to make a "good guy"
into a "bad guy"] ... and several other routines.
I thought that if I could write small fast routines to do the
above actions, then I would need far fewer sprites to accomplish
the same visual actions, since I could simply "change" the
sprite, as needed, on-the-fly, while the program was running.
The below article is the result of many hours of research into
the method used for storage of images into numeric arrays.
Enjoy ...
================================================> Frederick
All versions of Microsoft Basic have several commands which
provide graphic functions. Two of the most powerful are the graphics
GET and graphics PUT. These two commands allow the programmer to move
a copy of a graphic image into a numerical array and then, later,
redisplay the image whenever needed anyplace on the screen.
Almost every graphics game uses the technique of GET'ing and
PUT'ing graphics images to create animation. During a game, when our
eye sees a picture of an object moving across the screen, we are
actually seeing many separate images. Each image was initially stored
with a GET command. Then, throughout the game, the image is repeatedly
redisplayed with subsequent PUT commands. The key concept, is the
speed at which this redisplay happens. The computer is capable of
PUT'ing, so rapidly, that the human eye cannot see the individual
different frames.
The GET and PUT commands use numerical arrays to store images.
The method used for storage is the main concept to be examined by this
article.
In Basic, only rectangularly shaped screen areas can be stored
The QBNews Page 22
Volume 1, Number 2 February 2, 1990
and displayed. The height and width dimensions of such areas are
determined on a pixel basis. The height and width of a graphic image
is described as X-pixels-tall and Y-pixels-wide.
GET requires a numeric array which has been pre-dimensioned to
the proper size to receive the entire graphic image. Depending upon
several criteria, such as screen mode (CGA, EGA or VGA) or how many
bytes of graphics memory are available, the exact size and dimension
of these arrays tend to vary significantly. It is this variation in
array size, which provides the first clue to unraveling the complexity
of image storage.
In Screen Two a 16x16 pixel image requires an array of 18
integers. However, 16x16 pixels in Screen One requires 34 integers and
in Screen Nine 66 integers are needed. It is immediately obvious the
screen mode being used has a great impact on the array size needed.
In Screen Two (CGA Monochrome), the color of each pixel may only
be either white(ON) or black(OFF). In Screen One (CGA color) there are
only FOUR possible color choices and in Screen Nine, each pixel must
be one of SIXTEEN possible color choices.
The quantity of colors available, under any specified screen
mode, is the main factor affecting the size of the array required to
hold an image.
Included with this article is a SUB which will calculate the
array size needed, in integers, for most screen modes. GETSIZE.BAS
Numeric arrays dimensioned for image storage include two integers
in the first and second positions which are not part of the graphics
image itself. The first and second integers are the height and width
of the image contained therein. These values are automatically set by
the GET command.
Examination of array sizes (minus the two extra integers) for a
variety of screen modes, for storage of a 16x16 pixel image, reveals a
geometric pattern rise, in step with the quantity of colors available.
SCREEN 2 .... 16 integers needed ... 2 colors available
SCREEN 1 .... 32 integers needed ... 4 colors available
SCREEN 9 .... 64 integers needed ... 16 colors available
If we examine the storage needs of a 16x16 pixel image we find
there are a total of 256 pixels to be stored. (16 pixels tall) times
(16 pixels wide) equals (256 pixels) to store.
The question that must be answered is, "How do you store 256
pixels in arrays which are only 16, 32 or 64 integers in size?
CGA - MONOCHROME SCREEN
Let's begin by concentrating on Screen Two where the entire 256
pixels are stored in only 16 integers and each pixel is either Black
or White ... ON or OFF ... 0(zero) or 1(one) ...
The QBNews Page 23
Volume 1, Number 2 February 2, 1990
- One array of 16 INTEGERS
- Each integer is composed of TWO BYTES
- Each byte is made up of EIGHT BITS
- Each bit is either a 0(zero) or 1(one) ...
A little mathematics shows us we have (16 integers) times (2
bytes) times (8 bits) which equals (256 bits).
Since we have 256 PIXELS which are either 0(zero) or 1(one) ... and
Since we have 256 BITS which are either 0(zero) or 1(one) ...
We can then THEORIZE that each pixel is "mapped" to the "bit-level".
Included with this article is a program which will validate bit level
mapping in Screen Two: SCR2BITS.BAS
To prove our THEORY ... let's assume we have an array of 18
integers (two for height and width and 16 for pixel storage). Each
integer is composed of two bytes and each byte is composed of 8 bits.
Set every integer in our array to 0(zero) and examine each bit, of
each byte, of each integer:
Integer #1 0000 0000 0000 0000 = 0 (tall)
Integer #2 0000 0000 0000 0000 = 0 (wide)
Byte #1 Byte #2
Integer #3 0000 0000 0000 0000
Integer #4 0000 0000 0000 0000
Integer #5 0000 0000 0000 0000
Integer #6 0000 0000 0000 0000
Integer #7 0000 0000 0000 0000
Integer #8 0000 0000 0000 0000
Integer #9 0000 0000 0000 0000
Integer #10 0000 0000 0000 0000
Integer #11 0000 0000 0000 0000
Integer #12 0000 0000 0000 0000
Integer #13 0000 0000 0000 0000
Integer #14 0000 0000 0000 0000
Integer #15 0000 0000 0000 0000
Integer #16 0000 0000 0000 0000
Integer #17 0000 0000 0000 0000
Integer #18 0000 0000 0000 0000
Let's further ...
1. turn on Screen 2 > SCREEN 2
2. draw a box in the corner > LINE (0,0)-(15,15),1,B
3. draw a diagonal line in the box > LINE (0,0)-(15,15),1
4. GET the image into our array > GET (0,0)-(15,15),Array
Now let's examine our array again ...
Integer #1 0001 0000 0000 0000 = 16 (tall)
Integer #2 0001 0000 0000 0000 = 16 (wide)
Byte #1 Byte #2
Integer #3 1111 1111 1111 1111
Integer #4 1100 0000 0000 0001
Integer #5 1010 0000 0000 0001
The QBNews Page 24
Volume 1, Number 2 February 2, 1990
Integer #6 1001 0000 0000 0001
Integer #7 1000 1000 0000 0001
Integer #8 1000 0100 0000 0001
Integer #9 1000 0010 0000 0001
Integer #10 1000 0001 0000 0001
Integer #11 1000 0000 1000 0001
Integer #12 1000 0000 0100 0001
Integer #13 1000 0000 0010 0001
Integer #14 1000 0000 0001 0001
Integer #15 1000 0000 0000 1001
Integer #16 1000 0000 0000 0101
Integer #17 1000 0000 0000 0011
Integer #18 1111 1111 1111 1111
We have proven our theory. Examination of the "bit-pattern"
proves under Screen Two, each pixel is mapped at the bit level. Given
the visual pattern above, it's rather obvious how Screen Two (CGA
Mono) images are stored in the array.
CGA - COLOR SCREEN
In Screen Two (CGA Mono) it is only necessary to store a single
ON/OFF switch (one bit). Thus, mapping to each integer is rather
straight forward. However, in Screen One (CGA color), it is necessary
to store a value which can be one of FOUR possible colors (0,1,2,3).
Using binary mathematics, we can determine it will only require
TWO BITS to store a value which has a range of 0 thru 3 ....
Bit#1 Bit#2 Value
OFF OFF . . . . . 0 0 = 0
OFF on . . . . . 0 1 = 1
on OFF . . . . . 1 0 = 2
on on . . . . . 1 1 = 3
We must now determine how those two bits, for each pixel, are mapped
on the integer array.
Included with this article is a program which will validate bit level
mapping in Screen One: SCR1BITS.BAS
Let's examine an example ... Since we are now dealing with Screen
One, we will need an array with 34 integers. Let's set the entire
array to 0(zero) and examine the "bit-pattern" of the array.
Since we need 2 bits to display each pixel, a single line of 16
pixels will now span 2 integers (rather than 1 integer as in Screen
Two). Thus we'll change the way our integers are displayed such that
we have two integers side by side
Integer #1 0001 0000 0000 0000 = 16 (tall)
Integer #2 0001 0000 0000 0000 = 16 (wide)
Byte #1 Byte #2 Byte #1 Byte #2
Integer # 3 0000 0000 0000 0000 - 0000 0000 0000 0000 # 4 Integer
Integer # 5 0000 0000 0000 0000 - 0000 0000 0000 0000 # 6 Integer
The QBNews Page 25
Volume 1, Number 2 February 2,
1990
Integer # 7 0000 0000 0000 0000 - 0000 0000 0000 0000 # 8 Integer
Integer # 9 0000 0000 0000 0000 - 0000 0000 0000 0000 #10 Integer
Integer #11 0000 0000 0000 0000 - 0000 0000 0000 0000 #12 Integer
Integer #13 0000 0000 0000 0000 - 0000 0000 0000 0000 #14 Integer
Integer #15 0000 0000 0000 0000 - 0000 0000 0000 0000 #16 Integer
Integer #17 0000 0000 0000 0000 - 0000 0000 0000 0000 #18 Integer
Integer #19 0000 0000 0000 0000 - 0000 0000 0000 0000 #20 Integer
Integer #21 0000 0000 0000 0000 - 0000 0000 0000 0000 #22 Integer
Integer #23 0000 0000 0000 0000 - 0000 0000 0000 0000 #24 Integer
Integer #25 0000 0000 0000 0000 - 0000 0000 0000 0000 #26 Integer
Integer #27 0000 0000 0000 0000 - 0000 0000 0000 0000 #28 Integer
Integer #29 0000 0000 0000 0000 - 0000 0000 0000 0000 #30 Integer
Integer #31 0000 0000 0000 0000 - 0000 0000 0000 0000 #32 Integer
Integer #33 0000 0000 0000 0000 - 0000 0000 0000 0000 #34 Integer
Let's ...
1. turn on Screen 1 > SCREEN 1
2. define the color to use > Clr = 1
3. draw a box in the corner > LINE (0,0)-(15,15),Clr,B
4. draw a diagonal line in the box > LINE (0,0)-(15,15),Clr
5. GET the image into our array > GET (0,0)-(15,15),Array
Now let's examine our array again ...
Integer #1 0001 0000 0000 0000 = 16 (tall)
Integer #2 0001 0000 0000 0000 = 16 (wide)
Byte #1 Byte #2 Byte #1 Byte #2
Integer # 3 0101 0101 0101 0101 - 0101 0101 0101 0101 # 4 Integer
Integer # 5 0101 0000 0000 0000 - 0000 0000 0000 0001 # 6 Integer
Integer # 7 0100 0100 0000 0000 - 0000 0000 0000 0001 # 8 Integer
Integer # 9 0100 0001 0000 0000 - 0000 0000 0000 0001 #10 Integer
Integer #11 0100 0000 0100 0000 - 0000 0000 0000 0001 #12 Integer
Integer #13 0100 0000 0001 0000 - 0000 0000 0000 0001 #14 Integer
Integer #15 0100 0000 0000 0100 - 0000 0000 0000 0001 #16 Integer
Integer #17 0100 0000 0000 0001 - 0000 0000 0000 0001 #18 Integer
Integer #19 0100 0000 0000 0000 - 0100 0000 0000 0001 #20 Integer
Integer #21 0100 0000 0000 0000 - 0001 0000 0000 0001 #22 Integer
Integer #23 0100 0000 0000 0000 - 0000 0100 0000 0001 #24 Integer
Integer #25 0100 0000 0000 0000 - 0000 0001 0000 0001 #26 Integer
Integer #27 0100 0000 0000 0000 - 0000 0000 0100 0001 #28 Integer
Integer #29 0100 0000 0000 0000 - 0000 0000 0001 0001 #30 Integer
Integer #31 0100 0000 0000 0000 - 0000 0000 0000 0101 #32 Integer
Integer #33 0101 0101 0101 0101 - 0101 0101 0101 0101 #34 Integer
Since we drew our box using COLOR 1 ... and ... since the two-bit
bit-pattern for COLOR 1 is ... 01 ....... It appears each pixel is
mapped onto two consecutive bits of each integer.
By changing the box to color 2 and 3 and repeating the exercise,
it's possible to verify the bit mapping suggested by the above bit
pattern.
The two-bit bit-pattern for COLOR 2 is - 10 - thus ........
Byte #1 Byte #2 Byte #1 Byte #2
The QBNews Page 26
Volume 1, Number 2 February 2, 1990
Integer # 3 1010 1010 1010 1010 - 1010 1010 1010 1010 # 4 Integer
Integer # 5 1010 0000 0000 0000 - 0000 0000 0000 0100 # 6 Integer
Integer # 7 1000 1000 0000 0000 - 0000 0000 0000 0100 # 8 Integer
Integer # 9 1000 0010 0000 0000 - 0000 0000 0000 0100 #10 Integer
Integer #11 1000 0000 1000 0000 - 0000 0000 0000 0100 #12 Integer
Integer #13 1000 0000 0010 0000 - 0000 0000 0000 0100 #14 Integer
Integer #15 1000 0000 0000 1000 - 0000 0000 0000 0100 #16 Integer
Integer #17 1000 0000 0000 0010 - 0000 0000 0000 0100 #18 Integer
Integer #19 1000 0000 0000 0000 - 1000 0000 0000 0100 #20 Integer
Integer #21 1000 0000 0000 0000 - 0010 0000 0000 0100 #22 Integer
Integer #23 1000 0000 0000 0000 - 0000 1000 0000 0100 #24 Integer
Integer #25 1000 0000 0000 0000 - 0000 0010 0000 0100 #26 Integer
Integer #27 1000 0000 0000 0000 - 0000 0000 1000 0100 #28 Integer
Integer #29 1000 0000 0000 0000 - 0000 0000 0010 0100 #30 Integer
Integer #31 1000 0000 0000 0000 - 0000 0000 0000 1010 #32 Integer
Integer #33 1010 1010 1010 1010 - 1010 1010 1010 1010 #34 Integer
The two-bit bit-pattern for COLOR 3 is - 11 - thus ........
Byte #1 Byte #2 Byte #1 Byte #2
Integer # 3 1111 1111 1111 1111 - 1111 1111 1111 1111 # 4 Integer
Integer # 5 1111 0000 0000 0000 - 0000 0000 0000 0011 # 6 Integer
Integer # 7 1100 1100 0000 0000 - 0000 0000 0000 0011 # 8 Integer
Integer # 9 1100 0011 0000 0000 - 0000 0000 0000 0011 #10 Integer
Integer #11 1100 0000 1100 0000 - 0000 0000 0000 0011 #12 Integer
Integer #13 1100 0000 0011 0000 - 0000 0000 0000 0011 #14 Integer
Integer #15 1100 0000 0000 1100 - 0000 0000 0000 0011 #16 Integer
Integer #17 1100 0000 0000 0011 - 0000 0000 0000 0011 #18 Integer
Integer #19 1100 0000 0000 0000 - 1100 0000 0000 0011 #20 Integer
Integer #21 1100 0000 0000 0000 - 0011 0000 0000 0011 #22 Integer
Integer #23 1100 0000 0000 0000 - 0000 1100 0000 0011 #24 Integer
Integer #25 1100 0000 0000 0000 - 0000 0011 0000 0011 #26 Integer
Integer #27 1100 0000 0000 0000 - 0000 0000 1100 0011 #28 Integer
Integer #29 1100 0000 0000 0000 - 0000 0000 0011 0011 #30 Integer
Integer #31 1100 0000 0000 0000 - 0000 0000 0000 1111 #32 Integer
Integer #33 1111 1111 1111 1111 - 1111 1111 1111 1111 #34 Integer
EGA - COLOR SCREEN
We will use the same methods we have already employed, to help us
examine how pixels are stored in Screen Nine.
Included with this article is a program which will validate bit level
mapping in Screen Nine: SCR9BITS.BAS
First let's examine how many bits will be required to store a
pixel color in the range of 0 thru 15 (thus 16 colors). Using bit
mathematics we can determine we'll need 4 bits.
Bits-> (1) (2) (3) (4)
1 + 2 + 4 + 8 = 15 + 1 for Zero
Bit
Value Switches Pattern
0 ....... OFF OFF OFF OFF .... 0000
The QBNews Page 27
Volume 1, Number 2 February 2, 1990
1 ....... OFF OFF OFF on .... 0001
2 ....... OFF OFF on OFF .... 0010
3 ....... OFF OFF on on .... 0011
4 ....... OFF on OFF OFF .... 0100
5 ....... OFF on OFF on .... 0101
6 ....... OFF on on OFF .... 0110
7 ....... OFF on on on .... 0111
8 ....... on OFF OFF OFF .... 1000
9 ....... on OFF OFF on .... 1001
10 ....... on OFF on OFF .... 1010
11 ....... on OFF on on .... 1011
12 ....... on on OFF OFF .... 1100
13 ....... on on OFF on .... 1101
14 ....... on on on OFF .... 1110
15 ....... on on on on .... 1111
EGA Screen Nine image storage is much different from Screens One
& Two. Rather than having one pixel mapped onto 1 or 2 CONSECUTIVE
bits, Screen Nine takes a substantially different approach. Let's look
at what appears to happen from a pseudo-code standpoint...
100 the quantity of pixels WIDE is determined.
200 that quantity is rounded up to the nearest whole INTEGER
quantity
300 the rounded integer quantity is multiplied by the number of
bits required (four)
400 the pixel color value, of the first pixel is determined
500 the bit-pattern of that value is separated into its four
smallest basic pieces, the individual bits.
600 the first BIT of the 1ST pixel's color value is mapped onto
the 1ST BIT position of the FIRST BYTE.
Up until this point, things are happening like they would in Screen
One or Two. But now things get strange ....
700 rather than mapping the SECOND pixel color BIT onto the BYTE's
SECOND BIT (as would happen in CGA), the procedure begins
again at line 400. However, instead of working with the 1ST
pixel and the byte's first bit position, the FIRST BIT of the
SECOND PIXEL is used.
Thus, if you have an image, say 13 pixels wide, then the first 13
bits of the 2 bytes of the first integer all reflect the FIRST BITS OF
13 DIFFERENT PIXELS! and the next 13 bits of the next two bytes of the
next integer are the SECOND BIT of 13 different pixels, and so on, and
so on ....
Pixels 1 2 3 4 5 6 7 8 9 10 11 12 13
Their Color 15 1 5 9 1 1 5 9 1 1 5 9 1
The QBNews Page 28
Volume 1, Number 2 February 2, 1990
Their Bit 1 0 0 1 0 0 0 1 0 0 0 1 0 A
Patterns 1 0 1 0 0 0 1 0 0 0 1 0 0 B
1 0 0 0 0 0 0 0 0 0 0 0 0 C
1 1 1 1 1 1 1 1 1 1 1 1 1 D
Byte #1 Byte #2
Integer #1 1001 0001 0001 0000 A
Integer #1 1010 0010 0010 0000 B
Integer #1 1000 0000 0000 0000 C
Integer #1 1111 1111 1111 1000 D
Notice that each integer has been filled to the integer border
with zero's. When storing images, with a quantity of pixels which are
not evenly divisible by 8, then substantial wasted space occurs. Run
the program provided with this article, SCR9BITS and you'll be able to
see this bit-spread across-bytes in action.
Without going into great detail ... this trait of bit-mapping is
found in discussions of "planes" in a variety of books. Planes refer
to a technique taken by the lowest software level to maximize speed
when addressing hardware requirements of the EGA card.
VGA COLOR & MCGA SCREENS
Sorry ... but I only have EGA and CGA computers at my disposal. I
have not had the opportunity to examine how bits are stored in VGA
modes. Possibly someone else will continue my research ... and next
issue ... we can learn how VGA images are stored ...?
Included is a function which will determine the condition of a
specified BIT inside an integer, and return TRUE or FALSE if the BIT
is ON or OFF: BITON.BAS
The QBNews Page 29
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
A n d I H e a r d i t T h r o u g h t h e G r a p e v i n e
----------------------------------------------------------------------
Heard it through the grapevine ... Exerpts from the QUIK_BAS echo
From: Bob Ross
To: Gregg Pace
Subject: ENHANCED KEYBOARD
In a message of <02 Nov 89 11:29:13>, Gregg Pace (1:371/8) writes:
>Does anyone know the trick to accessing the cursor keys on an
enhanced keyboard? I can access the ones on the number pad, but the
separate cursor pad does nothing. Any info will be GREATLY
appreciated!!
Some AT type computers boot up with the Num Lock key on which
disables the cursor keys on the numeric keypad. If this is what it
happening on your system, try toggling the NumLock key off. There
are a few programs floating around that you could put in your
Autoexec.bat file to turn NumLock off on boot-up as well. Hope this
is helpful...
Bob.
From: Dana Bell
To: Gregg Pace
Subject: Re: ENHANCED KEYBOARD
If you can tackle the cursor keys on the pad, then you know about
scan codes. On the enhanced keyboard, the page and cursor keys scan
codes are extended, that is, preceded by E0, on both AT and XT mode.
There's some other characteristics (combos), but it might be best to
consult a tech manual, or possibly your DOS manual. I've not done it,
but I assume you add the extended code just as you would CTRL or ALT,
I.e. KEY 15,CHR$(&HE0)+CHR$(&H48) to 'trap' the new up-arrow key.
Dana
From: Keli W'Hazel
To: Dana Bell
Subject: Re: Enhanced Keyboard
I was waiting for others to respond to the question on cursor
keys, but it seems no one has done so yet (properly?).
To the original poster:
"Extended" key-strokes return two characters. A chr$(0)
followed by aletter. For the method to read these keys from the user,
try:
i$ = ""
The QBNews Page 30
Volume 1, Number 2 February 2, 1990
while i$ = ""
i$ = inkey$
wend
If an "extended" key is hit, I$ will hold two characters.
Left key: Chr$(0) + "K"
right key: Chr$(0) + "M"
Up key: Chr$(0) + "H"
Down Key: Chr$(0) + "P"
PgUp Key: Chr$(0) + "I"
PgDn Key: Chr$(0) + "Q"
There are many more. Such as: F1,F2,F3,F4,F5,F6,F7,F8,F9,F10
first have chr$(0) followed by:
; < = > ? @ A B C D
respectivly.
Home and end, respectivly: G O
Tab is Chr$(9) ONLY, with no preceeding chr$(0).
Shift/TAB is Chr$(0) + chr$(15)
Ins and Del keys, respectivly: R S
Note that the characters following the chr$(0) are case
sensitive.
From: Doug Wilson @ 965/9
To: Gregg Pace
Subject: ENHANCED KEYBOARD
Gregg:
(At least I THINK you are the one who posted the question
about accessing the keys on a separate cursor pad...)
From your message it sounds like you know in general how to
access the keys, you just need the codes for the keys in
question. The short program below will display the key codes
returned by INKEY$ for any key you press - including Ctrl- and
Alt- combinations. Try it on the cursor pad and see what you
get. Once you have the codes, if you need help doing what
you want with them, post another message.
Cheers, Doug
CLS
PRINT "Press a key to see its INKEY$ code(s) - Esc will exit"
More [Y,n]?
PRINT
The QBNews Page 31
Volume 1, Number 2 February 2, 1990
DO UNTIL a$ = CHR$(27)
a$ = ""
WHILE a$ = "": a$ = INKEY$: WEND
LOCATE CSRLIN, 1
FOR i = 1 TO LEN(a$)
PRINT ASC(MID$(a$, i, 1)), ;
NEXT
PRINT STRING$(4, 32);
LOOP
END
From: Malcolm Toon @ 914/602
To: Gregg Pace
Subject: Enhanced Keyboard
Try this:
DO: A$=INKEY$:LOOP UNTIL A$<>""
b% = ASC(RIGHT$(a$, 1))
'B%' is now the ASCII char code for the INKEY$...
Here are the codes:
72 - up
77 - right
80 - down
75 - left
There is only one setback... If you push the Uppercase key on the
keyboard that is one of the above numbers, it uses the up, down,
left, or right!
So this will not be good for a word processor, but here is some code
to move around a lighted bar to select things in a series...
This was first written by Dwain Goforth a friend of mine.
x% = 4: y% = 1: z% = 18
start:
LOCATE x%, y%: file$ = ""
FOR yy% = 1 TO 18: file$ = file$ + CHR$(SCREEN(x%, y% + yy% - 1)):
NEXT yy%
COLOR clr0%, clr7%: PRINT file$: COLOR clr7%, clr0%
getkey2:
DO: a$ = INKEY$: LOOP WHILE a$ = ""
IF a$ = CHR$(13) THEN GOTO Bye
IF LEN(a$) <> 2 THEN GOTO getkey2
LOCATE x%, y%: PRINT file$
b% = ASC(RIGHT$(a$, 1))
IF b% = 72 THEN x% = x% - 1 ELSE IF b% = 80 THEN x% = x% + 1
IF b% = 77 THEN y% = y% + 18 ELSE IF b% = 75 THEN y% = y% - 18
IF b% = 79 THEN x% = 23: y% = 55
IF b% = 71 THEN x% = 3: y% = 1
IF y% > 55 THEN y% = 1 ELSE IF y% < 1 THEN y% = 55
IF x% < 4 THEN x% = 23 ELSE IF x% > 23 THEN x% = 4
GOTO start
Bye: ' Continue your program here....
[EDITORS NOTE]
The QBNews Page 32
Volume 1, Number 2 February 2, 1990
The purpose of this conference is to discuss MicroSoft QuickBASIC
and related applications and utilities. SysOps looking for a Group
Mail or EchoMail link into QUIK_BAS should contact the Alliance node
known as 520/323, the FidoNet node known as 107/323, or the Good
Egg Net Node known as 9230/323, or simply 1-201-247-8252 for
information on local feeds.
The QBNews Page 33
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
S o m e A s s e m b l y R e q u i r e d
----------------------------------------------------------------------
Using the DOS EXEC function from QB
by Harold Thomson
** Caution - the following program will not work in the ENVIRONMENT **
I have been monitoring the messages in the QB message bases for
the last few months and one of the things that I noticed is that a lot
of people wish the the BASIC SHELL command would return an ERRORLEVEL
if the child program returns one. I don't think that this is asking
too much and I cannot figure out why MS didn't impliment it. Seeing
this gave me one more routine to add to my growing library of routines
for QB4 and above that I have written entirly in MS assembler. Right
now the library is at version 4.5 and contains over 130 routine, but
enough for the sales pitch (BTW, it's free).
Before we get into the program, a little about the DOS EXEC
function 4BH along with the related function, 4DH Get return code
which is used by a parent process, after the successful execution of
an EXEC call to obtain the return code and termination type of a child
process.
When I first saw the EXEC function, I realized that this was
something that I could make use of. But as I started reading the
documentation, I really started to get confused. It talked about
different blocks that were needed, the parameter block, the
environment block, the command tail as well as the ASCIIZ program
pathname. I was almost tempted to say that it was time to start
another project. Instead I just picked up my worn copy of ADVANCED
MSDOS PROGRAMMING by Ray Duncan and worked my way thru his explanation
and examples and was able to make some sense of it. The results of it
follows.
As I said above, there are some blocks that are necessary in
order to get the EXEC function to work. These are commented in the
followingexample but here is a brief description:
PARAMETER BLOCK Contains the addresses of four data objects
! 1. The environment block
2. The command tail
3. File Control Block 1
4. File Control Block 2
ENVIRONMENT BLOCK The documentation stated that if the
ENVIRONMENT block pointer is zero, then the
child acquires a copy of the parents
environment which is the method that I chose.
COMMAND TAIL MSDOS copies the command tail into the childs
PSP at offset 0080H. This is the normal
location for any switches or other pieces of
information that are to be passed to the
The QBNews Page 34
Volume 1, Number 2 February 2, 1990
child program.
DEFAULT FCS's The default FCBs are seldom used in MSDOS
versions 2 & 3 because they don't support the
hierarchical file structure but some programs
may use them. I point then to the default FCB
locations in the parent program PSP to
conserve on space.
The only other thing to do is point at the program name, which
MUST contain the correct extention, either COM or EXE. I take care of
making it an ASCIIZ string which just means that it is a string which
is ended by a HEX 0.
There are a few error codes that the routine will return that may
need some special attention. First, if a -1 is returned, then QEXEC
has determined that a null string has been passed to the routine and
it is not able to continue. The following 5 error codes are returned
by DOS and the true DOS error code can be found by adding +256 to the
RC.
1. -255 ( 1) Invalid function
2. -254 ( 2) File not found
3. -248 ( 8) Not enough memory
4. -246 (10) Bad environment
5. -245 (11) Bad format
Any error code returned that is greater that zero is a valid
error code and the appropriate action should be taken.
Following the assembler listing is a short QB program that shows
the necessary DECLARE FUNCTION and some examples of using the QEXEC
function.
**** WRITTEN FOR MICROSOFT ASSEMBLER VERSION 5.10 ****
Page 60, 130
EXTRN Pascal B$ShellSetup:Far
EXTRN Pascal B$ShellRecover:Far
.MODEL MEDIUM,BASIC
.CODE
Qexec Proc Far Uses DS ES SI DI, Arg1:Ptr
Jmp Start
HoldRC DW 0
Pars DW 0 ;Environment segment
CmdLine DW 0 ;Command line for child - length
CmdLineS DW 0 ; Command line text
DW 5CH ;Default FCB 1 offset
Fcb1 DW 0 ;FCB 1 segment address
DW 6CH ;Default FCB 2 offset
Fcb2 DW 0 ;FCB 2 segment address
PgmTxt DB 65 Dup(?) ;Work area to build pgm string
The QBNews Page 35
Volume 1, Number 2 February 2, 1990
CmdBuf DB ? ;This will hold the length
DB ' ' ;Must allow one space
CmdTxt DB 80 Dup(?) ;The command line text
Start: Mov CS:HoldRC,-1 ;Set RC to -1
Mov BX,Arg1 ;Get Program name length
Mov CX,[BX] ; in CX
Jcxz GetOut ;Error - Get out
Mov SI,[BX+2] ;Address of Program string
Mov DI,Offset PgmTxt ;Program text buffer
Push ES ;Save ES register
Mov AX,CS ;Make the ES register
Mov ES,AX ;the same as the CS register
Pline: Lodsb ;Get a character
Cmp AL,' ' ;Is it a space?
Je Pline1 ;Yes - go to next routine
Stosb ;Move to buffer
Loop Pline ;Do it again
Pline1: Xor AX,AX ;Zero out AX register
Stosb ;Make PgmTxt an ASCIIZ string
Jcxz Pline2 ;No more - continue
Dec CX ;Adjust for space
Pline2: Mov CS:CmdBuf,CL ;Save command tail length
Mov DI,Offset CmdTxt ;Command tail buffer
Jcxz Tline ;No more - continue
Rep Movsb ;Move command tail to buffer
Tline: Inc CS:CmdBuf ;Add one to tail length
Mov AL,13 ;CR value in AL
Stosb ;Move to buffer
Pop ES ;Restore ES register
Mov DI,Offset CmdBuf ;New command tail
Mov CS:CmdLine,DI ;Move address to parameter block
Mov DI,CS ;Get CS register address
Mov CS:CmdLineS,DI ;Move address to parameter block
Mov AH,62H ;Get current PSP segment
Int 21H ;DOS interrupt
Mov CS:Fcb1,BX ;Move PSP address to param block
Mov CS:Fcb2,BX ;Move PSP address to param block
Call B$ShellSetup ;QB routine to compress memory
Mov DX,Offset PgmTxt ;DX points to program name
;string
Mov BX,Offset Pars ;BX points to child environment
Push DS ;Save DS register
Mov AX,CS ;Put CS register address
Mov ES,AX ; into the ES register
Mov DS,AX ; into the DS register
Mov AH,4BH ;Function 4B - EXEC function
Mov AL,00H ;Sub Function 0 - load & exec pgm
Int 21H ;DOS interrupt
Pop DS ;Restore DS register
Jnc Cont ;No Error - Continue
The QBNews Page 36
Volume 1, Number 2 February 2, 1990
Mov AH,-1 ;Set AH = FFh
Jmp Cont1 ;Error - Get out
Cont: Mov AH,4DH ;Function 4D - Get child errorlevel
Int 21H ;DOS interrupt
Xor AH,AH ;Zero out AH register
Cont1: Mov CS:HoldRC,AX ;Set RC to -1
Call B$ShellRecover ;QB routine to uncompress memory
GetOut: Mov AX,CS:HoldRC ;Set the return code
Ret ;Go back to caller
Qexec Endp
End
A little explanation should be given to the following routine as
it contains some routines from my QB4BAS.LIB. The first routine,
QREPLACE changes all occurances of one character to another character.
This is used to change all occurances of ";" to a space that I can
then parse to the PATH string using QWORD and QWORDS in a FOR loop,
concatinating it to the file name and then using QEXIST to see if the
file exists. If it does, I break out of the FOR loop and then invoke
QEXEC. The resulting return code will be printed to the screen. If
the file is not found, then the routine just exits.
DEFINT A-Z
DECLARE SUB QREPLACE (StringName AS STRING, _
OldStr AS STRING, _
NewStr AS STRING)
DECLARE FUNCTION QWORDS% (StringName AS STRING)
DECLARE FUNCTION QWORD$ (StringName AS STRING, _
BYVAL Index AS INTEGER)
DECLARE FUNCTION QEXIST% (FileName AS STRING)
DECLARE FUNCTION QEXEC% (PgmStr AS STRING)
Tmp$ = "SDIR.COM"
Work$ = ENVIRON$("PATH")
QREPLACE Work$, ";", " "
FOR X = 1 TO QWORDS(Work$)
Work2$ = QWORD(Work$, X) + "\" + Tmp$
IF QEXIST(Work2$ + CHR$(0)) = 0 THEN
EXIT FOR
ELSE
Work2$ = ""
END IF
NEXT X
IF Work2$ <> "" THEN
PRINT QEXEC(Work2$)
END IF
END
This second example shows the necessary format to execute a BATCH
The QBNews Page 37
Volume 1, Number 2 February 2, 1990
program. This requires that another copy of COMMAND.COM be first
loaded using the /C option and then the BAT file name is passed in the
command tail.
DEFINT A-Z
DECLARE FUNCTION QEXEC% (PgmStr AS STRING)
Work1$ = ENVIRON$("COMSPEC")
Work2$ = " /C THISTEST.BAT"
Work$ = Work1$ + Work2$
RC = QEXEC(Work$)
END
[EDITOR'S NOTE]
QEXEC.ASM is in QEXEC.ZIP in assembled form for thoughs without
assemblers. Also included is a smmal program called ASK. This was
written in QuickBASIC and PDQ and it just asks for a number (0-9) and
returns it as an errorlevel.
The QBNews Page 38
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
S w a p S h o p
----------------------------------------------------------------------
Extended Key Codes Made Easy
Detecting various key codes in QuickBasic couldn't be easier, given
the SELECT CASE construct. In my QB programs I usually INCLUDE a
file called, FKEYDEFS.BAS, which consists of the following lines:
=====================================================================
' Special Key Assignments
UpKey$ = CHR$(0) + CHR$(72)
DownKey$ = CHR$(0) + CHR$(80)
LeftKey$ = CHR$(0) + CHR$(75)
RightKey$ = CHR$(0) + CHR$(77)
PageUp$ = CHR$(0) + CHR$(73)
PageDown$ = CHR$(0) + CHR$(81)
HomeKey$ = CHR$(0) + CHR$(71)
EndKey$ = CHR$(0) + CHR$(79)
InsKey$ = CHR$(0) + CHR$(82)
DelKey$ = CHR$(0) + CHR$(83)
EnterKey$ = CHR$(13)
TabKey$ = CHR$(9)
sTabKey$ = CHR$(0) + CHR$(15)
cHomekey$ = CHR$(0) + CHR$(119)
cEndKey$ = CHR$(0) + CHR$(117)
cPrtSc$ = CHR$(0) + CHR$(114)
cLeftKey$ = CHR$(0) + CHR$(115)
cRightKey$ = CHR$(0) + CHR$(116)
cPageDown$ = CHR$(0) + CHR$(118)
cPageUp$ = CHR$(0) + CHR$(132)
' Function Keys
F1Key$ = CHR$(0) + CHR$(59)
F2Key$ = CHR$(0) + CHR$(60)
F3Key$ = CHR$(0) + CHR$(61)
F4Key$ = CHR$(0) + CHR$(62)
F5Key$ = CHR$(0) + CHR$(63)
F6Key$ = CHR$(0) + CHR$(64)
F7Key$ = CHR$(0) + CHR$(65)
F8Key$ = CHR$(0) + CHR$(66)
F9Key$ = CHR$(0) + CHR$(67)
F10Key$ = CHR$(0) + CHR$(68)
F11Key$ = CHR$(0) + CHR$(133)
F12Key$ = CHR$(0) + CHR$(134)
' Shifted Function Keys
sF1Key$ = CHR$(0) + CHR$(84)
sF2Key$ = CHR$(0) + CHR$(85)
The QBNews Page 39
Volume 1, Number 2 February 2, 1990
sF3Key$ = CHR$(0) + CHR$(86)
sF4Key$ = CHR$(0) + CHR$(87)
sF5Key$ = CHR$(0) + CHR$(88)
sF6Key$ = CHR$(0) + CHR$(89)
sF7Key$ = CHR$(0) + CHR$(90)
sF8Key$ = CHR$(0) + CHR$(91)
sF9Key$ = CHR$(0) + CHR$(92)
sF10Key$ = CHR$(0) + CHR$(93)
sF11Key$ = CHR$(0) + CHR$(135)
sF12Key$ = CHR$(0) + CHR$(136)
' Control Function Keys
cF1Key$ = CHR$(0) + CHR$(94)
cF2Key$ = CHR$(0) + CHR$(95)
cF3Key$ = CHR$(0) + CHR$(96)
cF4Key$ = CHR$(0) + CHR$(97)
cF5Key$ = CHR$(0) + CHR$(98)
cF6Key$ = CHR$(0) + CHR$(99)
cF7Key$ = CHR$(0) + CHR$(100)
cF8Key$ = CHR$(0) + CHR$(101)
cF9Key$ = CHR$(0) + CHR$(102)
cF10Key$ = CHR$(0) + CHR$(103)
cF11Key$ = CHR$(0) + CHR$(137)
cF12Key$ = CHR$(0) + CHR$(138)
' Alt Function Keys
aF1Key$ = CHR$(0) + CHR$(104)
aF2Key$ = CHR$(0) + CHR$(105)
aF3Key$ = CHR$(0) + CHR$(106)
aF4Key$ = CHR$(0) + CHR$(107)
aF5Key$ = CHR$(0) + CHR$(108)
aF6Key$ = CHR$(0) + CHR$(109)
aF7Key$ = CHR$(0) + CHR$(110)
aF8Key$ = CHR$(0) + CHR$(111)
aF9Key$ = CHR$(0) + CHR$(112)
aF10Key$ = CHR$(0) + CHR$(113)
aF11Key$ = CHR$(0) + CHR$(139)
aF12Key$ = CHR$(0) + CHR$(140)
' Alt other keys
alt1$ = CHR$(0) + CHR$(120)
alt2$ = CHR$(0) + CHR$(121)
alt3$ = CHR$(0) + CHR$(122)
alt4$ = CHR$(0) + CHR$(123)
alt5$ = CHR$(0) + CHR$(124)
alt6$ = CHR$(0) + CHR$(125)
alt7$ = CHR$(0) + CHR$(126)
alt8$ = CHR$(0) + CHR$(127)
alt9$ = CHR$(0) + CHR$(128)
alt0$ = CHR$(0) + CHR$(129)
altHyphen$ = CHR$(0) + CHR$(130)
altEqual$ = CHR$(0) + CHR$(131)
The QBNews Page 40
Volume 1, Number 2 February 2, 1990
altQ$ = CHR$(0) + CHR$(16)
altW$ = CHR$(0) + CHR$(17)
altE$ = CHR$(0) + CHR$(18)
altR$ = CHR$(0) + CHR$(19)
altT$ = CHR$(0) + CHR$(20)
altY$ = CHR$(0) + CHR$(21)
altU$ = CHR$(0) + CHR$(22)
altI$ = CHR$(0) + CHR$(23)
altO$ = CHR$(0) + CHR$(24)
altP$ = CHR$(0) + CHR$(25)
altA$ = CHR$(0) + CHR$(30)
altS$ = CHR$(0) + CHR$(31)
altD$ = CHR$(0) + CHR$(32)
altF$ = CHR$(0) + CHR$(33)
altG$ = CHR$(0) + CHR$(34)
altH$ = CHR$(0) + CHR$(35)
altJ$ = CHR$(0) + CHR$(36)
altK$ = CHR$(0) + CHR$(37)
altL$ = CHR$(0) + CHR$(38)
altZ$ = CHR$(0) + CHR$(44)
altX$ = CHR$(0) + CHR$(45)
altC$ = CHR$(0) + CHR$(46)
altV$ = CHR$(0) + CHR$(47)
altB$ = CHR$(0) + CHR$(48)
altN$ = CHR$(0) + CHR$(49)
altM$ = CHR$(0) + CHR$(50)
All of the variables that start with 'a' (such as 'aF10Key$')
represent the code for the ALT-Key stroke. Variables with 's' (such
as 'sF12Key$' are for the shifted-Key stroke, and 'c' (such as
'cF1Key$') for the Control-Key stroke.
Now, in your program where you want to detect and act on specific
keys, use the following:
DO: x$ = INKEY$: LOOP WHILE x$=""
SELECT CASE x$
CASE F10Key$
' Do something when F10 is pressed
CASE sF10Key$
' Do something when Shift-F10 is pressed
CASE aF10Key$
' Do something when Alt-F10 is pressed
CASE altX$
' Do something when Alt-X is pressed
CASE UpKey$
' Do something when the Up arrow key is pressed
CASE PageDown$
' Do something when the Page Down key is pressed
CASE "P"
' Do something when the "P" key is pressed
The QBNews Page 41
Volume 1, Number 2 February 2, 1990
CASE ELSE
' Do something if none of the previous keys were pressed
END SELECT
This arrangement makes it particularly easy to act on any key stroke,
and has the added benefit that your program is also MUCH more
readable.
William R. Hliwa, Jr.
Clinical Assistant Professor
Department of Medical Technology
State University of New York at Buffalo
The QBNews Page 42
Volume 1, Number 2 February 2, 1990
DECLARE SUB OpenMenu ()
DECLARE SUB MenuSelection ()
DECLARE SUB TextMessages ()
DECLARE SUB RotateSign ()
DECLARE SUB TimeAMPM ()
COMMON SHARED AMPM$, A$, B, C
'=====
'This little example is to show how you can have a 'menu' screen with
'various selections, and also have rather detailed information also
'presented to the user regarding each of his potential selections. If
'you use some of the shareware fast screen prints, you can have some
'very elaborate instructions on the screen, since the speed of
'printing of a lot of details does not become a factor and the
'screenful of detailed information snaps into place. The sample below
'is partially padded; for the values between #1 and #7. It does not
'attempt to restrict a <RETURN> hit with no numeric selection nor some
'other combinations. It's purpose is to give you the basics of an on
'line additional information' from a menu.
'This shows how simple the menu programming can be, yet obviously with
'just a little work, many of QuickBasic's built-in material can be
'used to clean up this listing. The purpose here, was not to show how
'well it can be programed, but to show that a programer who knows very
'little about BASIC language can still do some pretty sophisticated
'programming.
'SELECT CASE is used in the A.M. / P.M. selection of the TIME so you
'would need QB 4.5 or redo it with some IF Statements, and then you
'could use this in nearly any BASIC.
'YUP, there are a few line numbers. Doubt, the world will come to an
'end.
CLS
CALL OpenMenu
CALL MenuSelection 'selections #1 - #7
460 DO
A$ = INKEY$
IF LEN(A$) = 0 THEN 'nothing, so on to the screen
CALL RotateSign 'time / date / scrolling stuff
GOTO 470 'we want to continue
END IF
470 B = VAL(A$) 'the original A$
IF LEN(A$) = 0 THEN GOTO 460 'loop
IF ASC(RIGHT$(A$, 1)) = 13 THEN GOTO 480 'a CR was hit,
' call the
'routine, or run the module that
'is numbered between 1 and 7
A = B
IF B < 1 THEN GOTO 460 'heave input values less than 1
IF B > 7 THEN GOTO 460 'discard values over 7
The QBNews Page 43
Volume 1, Number 2 February 2, 1990
CALL TextMessages
LOOP
480 COLOR 14 + 16, 1: LOCATE 22, 16
'ON C GOTO 101 , 102 , 103 , OneO4 , 105 , 106 , AllDone
' 101 RUN "ITEM1.EXE"
' 102 RUN "ITEM2.EXE"
' 103 ...
' OneO4 ...
' 105 ...
' 106 ...
' AllDone
PRINT "You Pushed the RETURN and would also get Item #"; C
COLOR 15, 4: LOCATE 23, 19
PRINT "Please push CTL-BREAK to end this program"
LOCATE 24, 10: COLOR 0, 0
PRINT SPACE$(70);
DO
LOOP
SUB MenuSelection
Y = 25
LOCATE 14, Y: COLOR 15, 1
PRINT "[ 1 ] -- Item One"
LOCATE 15, Y
PRINT "[ 2 ] -- Item Two"
LOCATE 16, Y
PRINT "[ 3 ] -- Circles"
LOCATE 17, Y
PRINT "[ 4 ] -- No Circles"
LOCATE 18, Y
PRINT "[ 5 ] -- Item #5"
LOCATE 19, Y
PRINT "[ 6 ] -- Item #6"
LOCATE 20, Y
PRINT "[ 7 ] -- Item #7"
END SUB
SUB OpenMenu
LOCATE 6, 1: COLOR 7, 1
PRINT " This little program shows how you may place on the screen a_
short "
PRINT " instruction or greater information based upon the key_
selected by "
PRINT " the user. This information stays on the screen until_
another possible"
PRINT " key is selected, or the <RETURN> key is tapped, which takes_
you to "
PRINT " the item that is CALLED by the progam associated with the_
input key. "
The QBNews Page 44
Volume 1, Number 2 February 2, 1990
COLOR 12 + 16, 1
PRINT " Tap any key; but preferably #1 - #7_
"
END SUB
SUB RotateSign
S1$ = "Select a numeric item and then tap <RETURN>"
Z1 = LEN(S1$)
S$ = " Be Sure You Have Made Adequate Backup Disks!! "
Z = LEN(S$)
DO UNTIL I = LEN(S$)
A$ = INKEY$ 'this is WITHIN the first INKEY$
IF A$ > STR$(0) THEN EXIT SUB
IF LEN(A$) = 0 THEN GOTO 33
IF ASC(RIGHT$(A$, 1)) = 104 THEN EXIT SUB
IF ASC(RIGHT$(A$, 1)) = 13 THEN EXIT SUB 'a CR was hit
33 StartTime = TIMER
IF I = LEN(S$) - 1 THEN I = 0
LOCATE 12, 16: COLOR 15, 4
DO UNTIL EndTime - StartTime >= .1
EndTime = TIMER
LOOP
PRINT LEFT$(S$, LEN(S$) - I) 'scrolling upper message
LOCATE 12, 16: COLOR 15, 4
PRINT RIGHT$(S$, Z - (Z - I))
Y = Y + 1
IF Y = 15 THEN Y = 8
COLOR 15, 0
LOCATE 24, 18
PRINT RIGHT$(S1$, LEN(S$) - 2 - I);'scrolling bottom message
I = I + 1
CALL TimeAMPM
LOCATE 22, 25: COLOR 15, 0: PRINT AMPM$
LOCATE 22, 45: PRINT DATE$
LOOP
END SUB
SUB TextMessages
LOCATE 3, 10
PRINT SPACE$(70)
LOCATE 4, 10
PRINT SPACE$(70)
LOCATE 3, 10
C = B
ON B GOTO 1, 2, 3, 4, 5, 6, 7
1 COLOR 14, 2
PRINT " You are considering selecting item #1 "
EXIT SUB
2 COLOR 12, 1
PRINT " This could be a reminder for item #2 "
LOCATE 4, 10
PRINT " This also shows you are NOT restricted to line quantity"
The QBNews Page 45
Volume 1, Number 2 February 2, 1990
LOCATE 4, 36: COLOR 15 + 16, 1: PRINT "NOT"
EXIT SUB
3 COLOR 15, 9
PRINT " If you select this item, you will run in circles "
EXIT SUB
4 COLOR 14, 6
PRINT " Obviously this is not for going in circles "
EXIT SUB
5 COLOR 11, 10
PRINT " Maybe you have an area that should be called by item #5"
EXIT SUB
6 COLOR 13, 2
PRINT "The sixth item on the menu might have this informative_
message "
LOCATE 4, 10
PRINT "This could be an entire window, snapped on with a_
SHAREWARE program"
EXIT SUB
7 COLOR 11, 6
PRINT " Maybe item #7 could be an instruction to end the program"
EXIT SUB
END SUB
SUB TimeAMPM 'a 12 hour clock with either AM or PM at
the end
AMPM$ = LEFT$(TIME$, 2)
SELECT CASE VAL(AMPM$)
CASE IS >= 12
AMPM = VAL(AMPM$) - 12
AMPM$ = STR$(AMPM) + RIGHT$(TIME$, 6) + " PM"
CASE IS < 12
AMPM$ = TIME$ + " AM"
CASE ELSE
EXIT SUB
END SELECT
END SUB
by Don Avila
Compuserve 71525,2041
[EDITOR'S NOTE]
I may have screwed up the spacing with my word processor. Sorry.
David Cleary
The QBNews Page 46
Volume 1, Number 2 February 2, 1990
----------------------------------------------------------------------
I n p u t P a s t E n d
----------------------------------------------------------------------
WE NEED AUTHORS!
If you are interested in writing for the QBNews, you can contact
me at the address below. I can also be reached on Compuserve as
76510,1725 or on Prodigy as HSRW18A. If you are submitting articles, I
ask that they be ASCII text with no more than 70 characters per line.
As far as reviews go, I am pretty well set so what I really want is
code.
You can write me at:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
David Cleary
The QBNews Page 47