Home of the original IBM PC emulator for browsers.
The following document is from the Microsoft Programmer’s Library 1.3 CD-ROM.
Microsoft QuickBASIC Programmer's Toolbox
════════════════════════════════════════════════════════════════════════════
Microsoft(R) QuickBASIC Programmer's Toolbox
By John Clark Craig
════════════════════════════════════════════════════════════════════════════
PUBLISHED BY
Microsoft Press
A Division of Microsoft Corporation
16011 NE 36th Way, Box 97017, Redmond, Washington 98073-9717
Copyright (C) 1988 by John Clark Craig
All rights reserved. No part of the contents of this book may be
reproduced or transmitted in any form or by any means without the written
permission of the publisher.
Library of Congress Cataloging in Publication Data
Craig, John Clark.
The Microsoft QuickBASIC programmer's toolbox.
1. BASIC (Computer program language) 2. Microsoft QuickBASIC
(Computer program) I. Title.
QA76.73.B3C7 1988 005.13'3 88-5115
ISBN 1-55615-127-6
Printed and bound in the United States of America.
1 2 3 4 5 6 7 8 9 MLML 3 2 1 0 9 8
Distributed to the book trade in the
United States by Harper & Row.
Distributed to the book trade in
Canada by General Publishing Company, Ltd.
Distributed to the book trade outside the
United States and Canada by Penguin Books Ltd.
Penguin Books Ltd., Harmondsworth, Middlesex, England
Penguin Books Australia Ltd., Ringwood, Victoria, Australia
Penguin Books N.Z. Ltd., 182─190 Wairau Road, Auckland 10, New Zealand
British Cataloging in Publication Data available
──────────────────────────────────────────────────────────────────────────
Project Editor: Suzanne Viescas Manuscript Editor: Michele Tomiak
Technical Editor: Jon Harshaw
──────────────────────────────────────────────────────────────────────────
Dedication
This book is dedicated with love to
the three most important people in my life:
Jeanie, Jennifer, and Adam.
────────────────────────────────────────────────────────────────────────────
Contents
PART I: GETTING STARTED
QUICKBASIC AND TOOLBOXES
Advantages of Structured Programming
The Toolboxes in This Book
MINICAL.BAS──A COMPLETE PROGRAM
Modular Source-Code Editing
Building a Quick Library
Creating the Source Code for MINICAL
Compiling and Running as an Executable (.EXE) Program
PART II: QUICKBASIC TOOLBOXES AND PROGRAMS
USING QUICKBASIC TOOLBOXES
Special Requirements
QuickBASIC vs Executable Files
ATTRIB
BIN2HEX
BIOSCALL
BITS
CALENDAR
CARTESIA
CIPHER
COLORS
COMPLEX
DOLLARS
DOSCALLS
EDIT
ERROR
FIGETPUT
FILEINFO
FRACTION
GAMES
HEX2BIN
JUSTIFY
KEYS
LOOK
MONTH
MOUSGCRS
MOUSSUBS
MOUSTCRS
OBJECT
PARSE
PROBSTAT
QBFMT
QBTREE
QCAL
QCALMATH
RANDOMS
STDOUT
STRINGS
TRIANGLE
WINDOWS
WORDCOUN
PART III: MIXED-LANGUAGE TOOLBOXES
USING MIXED-LANGUAGE TOOLBOXES
Near and Far Addressing
Passing Variables
Creating Mixed-Language Toolboxes
CDEMO1.BAS AND CTOOLS1.C
CDEMO2.BAS AND CTOOLS2.C
PART IV: APPENDIXES
APPENDIX A Requirements for Running Toolboxes/Programs
APPENDIX B Functions-to-Modules Cross Reference
APPENDIX C Subprograms-to-Modules Cross Reference
APPENDIX D Hexadecimal Format (.OBJ) Files
APPENDIX E Line-Drawing Characters
────────────────────────────────────────────────────────────────────────────
PART 1 GETTING STARTED
────────────────────────────────────────────────────────────────────────────
Chapter One QuickBASIC and Toolboxes
Thanks to Microsoft QuickBASIC 4.0, BASIC has finally grown into a
flexible, full-featured, and powerful programming language. By thumbing
through this book and glancing at the program listings, you'll see that
BASIC isn't what it used to be. Microsoft QuickBASIC is easier to read,
has a faster learning curve, and gives you the power to quickly create
sophisticated programs that would have been difficult, if not impossible,
with traditional BASIC.
A key difference between traditional BASIC and QuickBASIC is that
QuickBASIC allows structured programming, an important feature that makes
large programs easier to create and maintain.
Advantages of Structured Programming
With early versions of BASIC, a program was written and executed as a
single block of program lines. Inexperienced programmers writing large
programs could unknowingly create "spaghetti code" (programs that make
frequent and improper use of GOTO statements) making them generally
difficult to follow and maintain.
A key feature of QuickBASIC is its ability to let you create structured
programs──large programs constructed of small, individual program modules.
Instead of having to create and work with a large (and often overwhelming)
single block of code, the QuickBASIC programmer need only construct the
program modules, which are in turn constructed from procedures called
subprograms and functions. Each of these procedures performs a specific,
well-defined task. By concentrating on the functionality of a single
procedure, the programmer is freed from having to worry about other parts
of the program and can devote full concentration to the task at hand. It's
been proven that programmers can develop complex programs more quickly and
accurately using this modular approach.
An additional advantage to structured programming is that these modules
and procedures can be organized and saved in such a way that they can be
reused with other programs──avoiding duplication of effort from one
programming project to another. By grouping modules with complementary
functionality, a programmer can easily create "toolboxes" of useful
routines that can, over time, make large programming projects progress
quickly because major portions of the program are already written.
After construction, a module can also be organized into a Quick Library,
which is a file saved on disk in a special format. You can then load Quick
Libraries with the QuickBASIC program, effectively adding the routines in
the Quick Library to the ones built into QuickBASIC.
The Toolboxes in This Book
If you are using (or even thinking of using) QuickBASIC, this book will be
a valuable reference. If you're only starting out and learning QuickBASIC
as a first language, you'll find the book immediately useful for learning
by example. If you're a seasoned, professional programmer using QuickBASIC
as a software development system, you'll find the routines in this book to
be valuable extensions to the QuickBASIC language.
Part 1 provides step-by-step instructions for constructing a complete,
working program called MINICAL. Beginning programmers in particular will
find this tutorial helpful. Part II contains all the QuickBASIC toolboxes
and begins with a brief section that explains how to load and run them.
Part III describes the use of mixed-language toolboxes and contains
several examples. Finally, five appendixes contain information on the
requirements for running the toolboxes, cross references to functions and
subprograms, and additional important information.
If you're an experienced programmer, you may want to skip ahead to Part
II and start using some of the toolboxes. If you're new to QuickBASIC,
turn now to the next section. You'll create two modules containing
functions and subprograms and use them to build a Quick Library. Once
you've learned the steps needed to create your own programs and your own
Quick Library, you can use the modules in Parts II and III of this book,
as well as modules of your own, to create even more Quick Libraries.
You'll soon have powerful toolboxes that you can use to build programs
quickly.
────────────────────────────────────────────────────────────────────────────
Chapter Two MINICAL.BAS──A Complete Program
In this section we will start from scratch and build a complete, working
program. We'll construct the program, build a Quick Library to extend the
QuickBASIC language, and build a stand-alone, executable program.
Before we begin, let's take a quick look at the capabilities of the
QuickBASIC programming environment and at some of the major concepts
involved. The sample program in this section is made up of two separate
modules, each consisting of several subprograms and functions. Let's look
at how QuickBASIC handles each of these.
Modular Source-Code Editing
One of the new features of Microsoft QuickBASIC 4.0 is the way that you
review and edit programs from within the QuickBASIC environment. If you've
programmed in QuickBASIC, perhaps you've noticed that a program comprising
several subprograms and functions can't be shown and edited all in one
piece on the screen. If you haven't yet programmed in QuickBASIC, you need
only know at this point that you select one subprogram or function from a
list of currently loaded subprograms and functions as the one you want to
view and edit. All the other routines are hidden from view. This might
seem strange at first, but after working on a few programs, you'll begin
to appreciate the power that this modular editing provides.
A second major advance in QuickBASIC 4.0 is its ability to load into
memory more than one source-code file at a time. This opens the door to
creating collections of subprograms and functions, stored in separate
source-code files by subject, that several different programs can load and
use independently.
The important concepts about these new features can be summarized in this
way: A program can be made up of one or more source-code files (modules),
and each source-code file can be made up of one or more subprograms or
functions. You can load several of these source files into the QuickBASIC
environment simultaneously, and all modules can work together to make a
complete program. Although you can display and edit only one portion of a
source file at a time, it's easy to jump from one portion to another while
editing a program.
Building a Quick Library
Wouldn't it be nice to create new QuickBASIC statements and functions that
you could add to the language in such a way that they'd be available for
your use every time you fired up QuickBASIC? That's what Quick Libraries
can do for you!
For example, suppose you use hyperbolic functions in almost every program
you write. You could create these functions in a source-code file to be
loaded into memory along with each main program you write, or you could
create a Quick Library so that these functions load at the same time you
load QuickBASIC. To build a Quick Library, you would simply load the
hyperbolic function source-code file and select the appropriate menu
choices from the Run menu.
Creating the Source Code for MINICAL
Let's walk through the creation of a complete programming project, step by
step, to get your feet wet. Fire up your computer and follow along to get
the maximum benefit. The MINICAL program performs five functions:
addition, subtraction, multiplication, division, and square root. The
program is simple in scope, yet it has most of the major components of
much larger software projects.
Before we start coding, let's look at how the program should run once we
get it built. The MINICAL program uses Reverse Polish Notation (RPN) for
input. Using RPN simplifies the programming considerably because it
eliminates the coding necessary to rearrange those math commands
enshrouded in parentheses.
Using RPN, you enter numbers first, followed by the operators. For
example, to add 3 and 4, you would enter:
3 4 +
(We'll show how these numbers are actually entered later in the section.)
To add 1 and 2 and then multiply the result by 3, you would enter:
1 2 + 3 *
MINICAL uses double-precision numbers, so you can enter any type of
integer or floating-point numeric values. Results are displayed using as
many as 16 digits. For example, to divide 1.2 by -3.45, you would enter:
1.2 -3.45 /
and the display would read:
Result... -.3478260869565217
NOTE: Because a computer keyboard does not have x and ÷ keys, an asterisk
(*) is used for multiplication and a forward slash (/) is used for
division.
By the way, MINICAL uses a structure called a stack to hold the numbers
and the operators while performing the calculations. (Technically, the
structure used in MINICAL only mimics a traditional stack, but for
discussion purposes it can be thought of as a stack.) A stack is a
sequential series of memory locations set aside to hold a number of
separate items──in this case, the numbers and operators provided by the
user. RPN is used for specifying numbers and operators primarily because
of the existence of the stack──the RPN syntax is ideal for stack-based
calculations. The alternative method, parsing, involves "reading" the
equation entered by the user, rearranging and selecting the separate
elements, and acting on them. This latter method involves much more
coding.
The stack in MINICAL can hold as many as 20 values plus the associated
operators. You can enter numbers as explained above, or you can enter all
numbers and then the operators. For example, to add the numbers 1 through
5, you can enter either of these two command lines:
1 2 + 3 + 4 + 5 +
1 2 3 4 5 + + + +
The MINIMATH Module
This project consists of two parts: the MINIMATH module and the MINICAL
module. Let's begin with MINIMATH.
If you haven't done so yet, at the system prompt type QB and press Enter
to start QuickBASIC. Type in the title block on the following page.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MINIMATH **
' ** Type: Toolbox **
' ** Module: MINIMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Collection of math subprograms for the MINICAL
' program.
──────────────────────────────────────────────────────────────────────────
At this point, it's convenient to tell QuickBASIC this module's name. Pull
down the File menu and choose Save As. Type the filename MINIMATH, and
press the Enter key or move your mouse pointer to the OK box and click the
left mouse button. The file is then saved to disk, and you're ready to
continue entering more of the program. Note that if you omit the .BAS
extension, as you did here, QuickBASIC automatically adds it for you.
This first module will be made up of the five subprograms that perform the
math functions. First, let's create the Add subprogram. Pull down the Edit
menu and choose New SUB. When you're asked for the name of the subprogram,
respond with Add. QuickBASIC then creates the first and last lines of your
new subprogram:
SUB Add
END SUB
Note that QuickBASIC also adjusts the editing window so that only the Add
subprogram is displayed, allowing you to concentrate on this subprogram
only. (You'll greatly appreciate this feature later on, when your
programming projects become larger.) The next step is to add comment
information before the first line of the subprogram and to insert the
"guts" of the subprogram between the two lines displayed by QuickBASIC.
Start by adding comments. Move the cursor to the first character of the
first line of the subprogram and press Enter. When you do, a dialog box
appears with the message Blank lines not allowed before SUB/FUNCTION line.
Is remark OK? Since a remark (comment) is what you want, choose OK.
QuickBASIC then inserts a blank line preceded by a ' character for the
comment. After you type the first comment line (taken from the lines on
the following page) and press Enter, the dialog box appears again. Click
OK and then type the next comment line.
Use the lines below to build the Add subprogram. Note the locations of the
SUB Add and END SUB lines, and note that you need to append items to the
SUB Add line. Also note that some lines are indented: Although this
indention is not required, it is good programming style to indent
subordinate lines so that the program is easier to read and relationships
between lines are visually apparent. When you're done, your Add subprogram
should look exactly like this:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Add **
' ** Type: Subprogram **
' ** Module: MINIMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Performs addition for the MINICAL program.
'
SUB Add (stack#(), ptr%) STATIC
ptr% = ptr% - 1
IF ptr% THEN
stack#(ptr%) = stack#(ptr%) + stack#(ptr% + 1)
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
You can save your work at any point. (It's a good idea to back up your
work often to prevent losing your work in case of a power failure or other
disaster.) Do so now by pulling down the File menu and choosing Save All.
You're well on your way now! You will create and edit the other four math
subprograms (shown on pages 11-12) in the same way.
Here's a tip that can speed up the process. Notice that the initial
comment lines for each of the five subprograms are nearly identical. So,
instead of retyping each one, you'll copy the lines you typed in for the
Add subprogram and paste them into the other subprograms. Let's do that
now.
First, set up the new subprograms. For the Subtract subprogram, choose
New SUB from the Edit menu, enter the name Subtract, and press Enter.
After the two lines of the Subtract subprogram are displayed, repeat the
New SUB process for the Multiply, Divide, and SquareRoot subprograms.
Be sure you type the subprogram names as shown here──each starts with a
capital letter, and no space is allowed in the subprogram name SquareRoot.
Next, make a copy of the comment lines in the Add subprogram. Pull down
the View menu and choose SUBS. QuickBASIC displays a list of all
subprogram names you entered. Here's where the power of QuickBASIC is
really apparent: Using the SUBS command, you can jump to any subprogram
for editing by simply selecting the name of the desired subprogram. You do
this by double-clicking on the desired subprogram name with your mouse or
by using the cursor movement keys followed by the Enter key. For now,
choose Add.
To copy the comment lines, move the cursor to the first character of the
first comment line, and then (using the keyboard) hold down either Shift
key and press the Down arrow key until all comment lines are highlighted;
or (using the mouse) move the mouse pointer to the location of the cursor,
hold down the left mouse button, and drag down until all comment lines are
highlighted. Finally, choose Copy from the Edit menu to copy the
highlighted lines to the Clipboard.
Next, copy those lines into the four other subprograms. Choose SUBS from
the View menu to again display a list of subprograms. Select Subtract,
move the cursor to the first character of the first line, pull down the
Edit menu, and choose Paste. The lines copied from the Add subprogram
should appear. (If they don't or if you get a dialog box telling you that
blank lines can't appear before a subprogram, go back to the Add
subprogram and repeat the Edit-Copy process. Remember to copy only comment
lines──those preceded by a ' character.)
Select the remaining subprogram names in the same way, and repeat the
Edit-Paste process. You don't have to repeat the Copy operation, because
an item copied to the Clipboard stays there until something else is copied
to the Clipboard or until you quit QuickBASIC. When you're done, go back
to each subprogram. Edit the comment lines and enter the program lines as
you did for the Add subprogram. Be sure to choose Save All from the File
menu after completing each subprogram. Also, be sure to go back and review
your work──the ability to view each subprogram separately using the SUBS
option on the View menu makes this important task easier and your program
clearer to read. Your results should match the four subprograms on the
following two pages.
NOTE: Don't forget to edit the comments! Remember──the comments you pasted
in were the comments for the Add subprogram. You must change the name in
each comment block and the information on the line below the block to
reflect the subprogram the comments identify.
The following is the Subtract subprogram:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Subtract **
' ** Type: Subprogram **
' ** Module: MINIMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Performs subtraction for the MINICAL program.
'
SUB Subtract (stack#(), ptr%) STATIC
ptr% = ptr% - 1
IF ptr% THEN
stack#(ptr%) = stack#(ptr%) - stack#(ptr% + 1)
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
The following is the Multiply subprogram:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Multiply **
' ** Type: Subprogram **
' ** Module: MINIMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Performs multiplication for the MINICAL program.
'
SUB Multiply (stack#(), ptr%) STATIC
ptr% = ptr% - 1
IF ptr% THEN
stack#(ptr%) = stack#(ptr%) * stack#(ptr% + 1)
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
The following is the Divide subprogram:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Divide **
' ** Type: Subprogram **
' ** Module: MINIMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Performs division for the MINICAL program.
'
SUB Divide (stack#(), ptr%) STATIC
ptr% = ptr% - 1
IF ptr% THEN
stack#(ptr%) = stack#(ptr%) / stack#(ptr% + 1)
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
The following is the SquareRoot subprogram:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SquareRoot **
' ** Type: Subprogram **
' ** Module: MINIMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Determines square root for the MINICAL program.
'
SUB SquareRoot (stack#(), ptr%) STATIC
stack#(ptr%) = SQR(stack#(ptr%))
END SUB
──────────────────────────────────────────────────────────────────────────
This completes the first part of the MINICAL program. The subprograms you
created and saved as MINIMATH form the heart of MINICAL because they
perform the actual calculations. The next part involves creating MINICAL
itself. MINICAL performs the overhead work──taking the numbers the user
wants to calculate, passing them to the appropriate subprograms in
MINIMATH, and displaying the result.
The MINICAL Module
From here, you can proceed in one of two ways. Because both MINIMATH and
MINICAL are small, you can build the MINICAL program entirely in memory by
creating a second module named MINICAL. (You do this by choosing Create
File from the File menu, accepting the default choice of Module, and then
choosing OK.) Or you can turn MINIMATH into a Quick Library and load it
with the QuickBASIC system so that it becomes an extension to the
language. We'll use the second method to see how easy it is to use this
advanced QuickBASIC feature.
To create a Quick Library, pull down the Run menu and choose Make Library.
You'll be asked to name the library. MINIMATH will be fine, because
QuickBASIC automatically appends a default extension of .QLB for the Quick
Library and .LIB for the normal library it also builds. After you type
MINIMATH, choose Make Library. (You can ignore the other options in the
dialog box for now.) When completed, QuickBASIC will have created two new
files in the current directory: MINIMATH.QLB and MINIMATH.LIB.
Now quit QuickBASIC so that you can restart it and load the new Quick
Library with it. To do this, pull down the File menu and choose Exit. Then
start QuickBASIC from the system prompt by entering:
QB /L MINIMATH
You'll see no obvious sign that anything is different, but a very exciting
event has actually taken place! Your QuickBASIC has been extended. It's
now more than it used to be. The subprograms in MINIMATH are part of the
QuickBASIC language, ready to be used like many of the other QuickBASIC
keywords. In fact, because you can optionally use the CALL keyword when
calling subprograms, these new subprograms will appear much like new
keywords in QuickBASIC.
Proceed with the rest of the program now so that you can try out the new,
extended QuickBASIC. Be sure QuickBASIC is loaded, as described earlier,
with the MINIMATH Quick Library as part of the system. Then type in the
following program, MINICAL.BAS:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MINICAL **
' ** Type: Program **
' ** Module: MINICAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
' Functions
DECLARE FUNCTION NextParameter$ (cmd$)
' Subprograms
DECLARE SUB Process (cmd$, stack#(), ptr%)
DECLARE SUB DisplayStack (stack#(), ptr%)
DECLARE SUB Add (stack#(), ptr%)
DECLARE SUB Subtract (stack#(), ptr%)
DECLARE SUB Multiply (stack#(), ptr%)
DECLARE SUB Divide (stack#(), ptr%)
DECLARE SUB SquareRoot (stack#(), ptr%)
' Get the command line
cmd$ = COMMAND$
' Create a pseudo stack
DIM stack#(1 TO 20)
ptr% = 0
' Process each part of the command line
DO UNTIL cmd$ = ""
parm$ = NextParameter$(cmd$)
Process parm$, stack#(), ptr%
IF ptr% < 1 THEN
PRINT "Not enough stack values"
SYSTEM
END IF
LOOP
' Display results
DisplayStack stack#(), ptr%
' All done
END
──────────────────────────────────────────────────────────────────────────
This is the main part of the MINICAL program, where all the action begins.
Note the first two lines in the DO-LOOP structure, the ones that read
parm$ = NextParameter$(cmd$) and Process parm$, stack#(), ptr%. The first
line calls the user-defined function named NextParameter$, and the second
line calls the user-defined subprogram named Process. (No, you haven't
defined them yet. That's next on the list of tasks to do.) Notice that the
keyword CALL was not used to call the Process subprogram. You can use CALL
if desired, but there's no need to anymore. Because of the way QuickBASIC
deals with subprograms, the Process subprogram that you'll create shortly
will be more like part of the QuickBASIC system, rather than part of the
program, because you can't list it or modify it while this portion of the
program is on the screen. You also don't have to think about it or
recompile it! Your creative energies are free to tackle the next higher
level of the program's complexity.
Once you've entered the main program's lines, it's again time to save this
module to disk. Select Save As from the File menu and enter the filename
MINICAL.
You still have a few pieces of coding to do before you can try the
program. To create the one function of this program, select New Function
from the Edit menu. Type in the function name NextParameter$, and press
the Enter key. Creating and editing functions are really no different from
creating and editing subprograms. In fact, the only major difference
between a function and a subprogram is that a function returns a value to
be used in a calculation or assigned to a QuickBASIC variable. Subprograms
return values only through passed variables. Follow the same steps you
used to create the subprograms in MINIMATH to create the function
NextParameter$:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: NextParameter$ **
' ** Type: Function **
' ** Module: MINICAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Extracts parameters from the front of the
' command line. Parameters are groups of any
' characters separated by spaces.
'
FUNCTION NextParameter$ (cmd$) STATIC
parm$ = ""
DO WHILE LEFT$(cmd$, 1) <> " " AND cmd$ <> ""
parm$ = parm$ + LEFT$(cmd$, 1)
cmd$ = MID$(cmd$, 2)
LOOP
DO WHILE LEFT$(cmd$, 1) = " " AND cmd$ <> ""
cmd$ = MID$(cmd$, 2)
LOOP
NextParameter$ = parm$
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Now create and edit the following two subprograms as part of the MINICAL
module.
Create the subprogram DisplayStack:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DisplayStack **
' ** Type: Subprogram **
' ** Module: MINICAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Displays anything left on the stack when MINICAL
' finishes processing the command line.
'
SUB DisplayStack (stack#(), ptr%) STATIC
PRINT
IF ptr% > 1 THEN
PRINT "Stack... ",
ELSE
PRINT "Result... ",
END IF
FOR i% = 1 TO ptr%
PRINT stack#(i%),
NEXT i%
PRINT
END SUB
──────────────────────────────────────────────────────────────────────────
Next create the subprogram Process:
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Process **
' ** Type: Subprogram **
' ** Module: MINICAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Processes each command parameter for the MINICAL
' program.
'
SUB Process (parm$, stack#(), ptr%) STATIC
SELECT CASE parm$
CASE "+"
Add stack#(), ptr%
CASE "-"
Subtract stack#(), ptr%
CASE "*"
Multiply stack#(), ptr%
CASE "/"
Divide stack#(), ptr%
CASE "SQR"
SquareRoot stack#(), ptr%
CASE ELSE
ptr% = ptr% + 1
stack#(ptr%) = VAL(parm$)
END SELECT
END SUB
──────────────────────────────────────────────────────────────────────────
Be sure you save your efforts on disk by selecting Save All from the File
menu.
You've done it! One last detail remains, however. This program reads the
command line and assumes that numbers and operators were typed in
following the name of the program at the system prompt. Fortunately,
QuickBASIC provides a mechanism to let you type in a command line, even
though you're currently going to be running the program in memory from the
QuickBASIC system. From the Run menu select Modify COMMAND$. You'll be
asked to enter a new command line. Enter this for the first try:
3 4 +
Everything should be in place now, so try running the program. Select
Start from the Run menu. If all is well, you'll see the following:
Result... 7
If all is not well, you'll probably find yourself staring at an error
message from QuickBASIC, describing an error that's probably the indirect
result of a typographical error. If so, double-check your typing, and
rebuild the library if the problem was in the MINIMATH module.
Once you get the program working, take a little time to try different
command line parameters. See what happens if several numbers are placed on
the stack but not enough operators are given to reduce the stack to a
final result. For example, try entering:
3 4 5 6 7 + *
Also, find out what happens if not enough numbers are on the stack. For
example, enter:
3 4 + *
Compiling and Running as an Executable (.EXE) Program
Finally, to see how you can create programs that you can run from MS-DOS,
select Make EXE File from the Run menu. You can create two types of .EXE
files. The first type results in a smaller MINICAL.EXE file, but it
requires access to the QuickBASIC file named BRUN40.EXE at runtime. The
second type results in a larger MINICAL.EXE file that stands completely on
its own. When you select Make EXE File from the Run menu, you are prompted
to select the type of .EXE file you want to create. Try it both ways. Take
a look at the resulting file sizes, and note that the BRUN40.EXE file must
be accessible in the current directory or in a place defined by the MS-DOS
PATH setting. (Your QuickBASIC manual discusses this subject in more
detail.)
Either way, running the MINICAL.EXE program from the system prompt uses
the command line in the way that COMMAND$ expects. For example, to
subtract 5 from 17, enter the following at the system prompt:
MINICAL 17 5 -
While building MINICAL, you've learned how easy it is to create toolboxes
of your own or to edit existing toolboxes. Turn now to Part II of this
book. Be sure to read the first section, which explains how to use the
QuickBASIC toolboxes. Then choose a toolbox that interests you, and have
fun.
────────────────────────────────────────────────────────────────────────────
PART 2 QUICKBASIC TOOLBOXES AND PROGRAMS
────────────────────────────────────────────────────────────────────────────
Chapter Three Using QuickBASIC Toolboxes
The toolboxes in Part II cover a wide range of topics and are presented
alphabetically by subject. Each is designed to be loaded and called by
user-written application software. You can run the demo module that begins
each toolbox to illustrate the routines within the toolbox, you can ignore
the demo module and use the routines as they are written, or you can
restructure the routines so that they meet your application requirements.
The toolboxes and utility programs do not require any knowledge of
previously presented toolboxes, so you can run them in any order. Try them
all at least once, and review the code as you run them. You'll find unique
techniques and programming concepts in most of the listings. You'll also
find that these toolbox routines, along with your own creations, will be
useful in your future programming projects.
Many of the utility programs use command line input or the COMMAND$
variable from within QuickBASIC to pass values or parameters to the
program. Others (those using toolboxes from different programs) might
require an associated .MAK file. A few programs require color and graphics
capability, and others require a mouse. Check the comments at the
beginning of each listing or Appendix A to determine environmental and
running requirements for each toolbox and utility program.
Special Requirements
The following toolboxes require that the MIXED.QLB Quick Library be loaded
into memory with QuickBASIC: BIOSCALL, CDEMO1, CDEMO2, COLORS,
DOSCALLS, FILEINFO, MOUSGCRS, MOUSSUBS, MOUSTCRS, QBTREE, STDOUT,
and WINDOWS. MIXED.QLB consists of a handful of subprograms and functions
written in assembly language and in Microsoft QuickC. (The
assembly-language and C source listings for MIXED.QLB are in Part III.)
Although MIXED.QLB isn't required by all toolboxes in this book, it's a
good idea to load it each time you start QuickBASIC to use the toolboxes
in this book. Toolboxes that do not require its presence will not be
affected if MIXED.QLB is loaded.
Below are instructions for creating MIXED.QLB, which was written in
Microsoft QuickC and assembly language to demonstrate how other languages
can be used with QuickBASIC. It is beyond the scope of this book to
explain in detail mixed-language programming concepts, so simply follow
the steps presented here to create MIXED.QLB. In Part III of the book, you
will have the opportunity to try some examples of mixed-language programs.
Creating MIXED.QLB
If you own Microsoft QuickC, the first step is to compile an object-code
file for CTOOLS1.C and CTOOLS2.C.
You can load the C source-code files from the companion disk if you have
purchased it, or you can type them in yourself. You will find CTOOLS1.C
on page 445, and CTOOLS2.C on page 462. Once you have these files, enter
the following commands at the system prompt to compile the CTOOLS1.C and
CTOOLS2.C source-code files to create object-code files:
QCL /Ox /AM /Gs /c CTOOLS1.C
QCL /Ox /AM /Gs /c CTOOLS2.C
NOTE: If you don't have QuickC, you can still create MIXED.QLB. Compile
the assembly-language source-code files as explained below. You will then
be able to run all toolboxes in this book except CDEMO1 and CDEMO2.
If you have version 5.0 or later of the Microsoft Macro Assembler, load
the assembly-language source-code files from the companion disk or type
them in. MOUSE.ASM is on page 437 and CASEMAP.ASM is on page 436. The
third assembly-language file, INTRPT.ASM, is part of QuickBASIC itself and
can be found on the disk that comes with the program. Then, enter the
following commands at the system prompt to compile the source-code files
into object-code files:
MASM MOUSE;
MASM CASEMAP;
MASM INTRPT;
If you have an earlier version of the Microsoft Macro Assembler, follow
the guidelines in your QuickBASIC documentation to replace the .MODEL
directives with appropriate statements.
If you don't have the Microsoft Macro Assembler, you can use HEX2BIN (on
pages 210 and 211) to convert the MOUSE.HEX, CASEMAP.HEX, and INTRPT.HEX
files into object-code files. The hexadecimal character files are listed
in Appendix D.
Once you've created the object-code files, you can build the MIXED library
files to use with QuickBASIC. (Note that two files will be created:
MIXED.QLB and MIXED.LIB. MIXED.QLB will be loaded with the QuickBASIC
program because it is needed by some toolboxes; MIXED.LIB will be used for
creating stand-alone programs that can be executed directly from MS-DOS.)
The following commands accomplish this task:
LINK /Q INTRPT+MOUSE+CASEMAP+CTOOLS1+CTOOLS2,MIXED.QLB,,BQLB40.LIB;
DEL MIXED.LIB
LIB MIXED.LIB+INTRPT+MOUSE+CASEMAP+CTOOLS1+CTOOLS2;
NOTE: If you don't have QuickC, remember that you cannot run CDEMO1 and
CDEMO2; therefore, you must delete +CTOOLS1 and +CTOOLS2 from the above
commands.
If you have a problem, the cause might be that the necessary files can't
be located. Try moving all the files and programs into the current
directory, including the programs LINK.EXE and LIB.EXE; the QuickBASIC
library file, BQLB40.LIB; and, if you have Quick C, MLIBCE.LIB.
Finally, after the MIXED.QLB and MIXED.LIB files are successfully created,
enter the following lines to create a file named Q.BAT:
COPY CON Q.BAT
QB /L MIXED.QLB
^Z
NOTE: The ^Z is obtained by pressing F6 or Ctrl-Z.
Using this batch file automates the loading process so that MIXED.QLB
loads along with QuickBASIC. To use it, type Q and press the Enter key at
the system prompt.
Using a .MAK File
Those toolboxes that consist of more than one module require a .MAK file.
When you save a program consisting of more than one module, QuickBASIC
automatically creates a .MAK file so that it knows where to locate each
module the next time it loads the program. If the .MAK file is not
available, or if you must create a new .MAK file, you must load each
module from within QuickBASIC by selecting Load File from the File menu,
selecting the module to load, and then repeating the process for each
additional module. After loading all the modules, choose Save All from the
File menu and QuickBASIC creates the new .MAK file. Appendix A lists all
toolboxes and required .MAK files.
QuickBASIC vs Executable Files
You can run the demo modules and utility programs from within the
QuickBASIC environment by selecting the applicable source-code file, or
you can compile the code and create files to execute directly from MS-DOS.
Source-code files are those with the .BAS file extension. Executable files
are created from .BAS files from within the QuickBASIC environment and are
saved with a .EXE file extension. All toolbox and utility programs on the
companion diskettes are .BAS files. The steps necessary for loading and
running the programs and toolboxes are simple.
Running Programs from the QuickBASIC Environment
Check your QuickBASIC manual, "Learning and Using Microsoft QuickBASIC,"
for starting QuickBASIC on your system. When the program starts, you are
ready to load and run a demo module or utility program. Using the
keyboard, the steps are:
1. Press the Alt key and then F to display the File menu.
2. Press O to choose the Open Program command.
3. Select the demo module or program from the list of .BAS files shown.
If the file is not shown, you must set the path to the drive and
directory where the file resides. First, type the correct path in the
File Name box and press Enter to display the .BAS files in that path.
Then type the name of the file you want to load, or use the Tab key to
move to the list box and use the arrow keys to select the filename you
want and press Enter. Filenames are displayed in all lowercase,
directory names in all uppercase. You can also select directory names,
including the parent(..) directory, in the list box until the .BAS
files you want are displayed.
4. If command line parameters are required for execution, press Alt and
then R to pull down the Run menu, and then press C to choose the
Modify COMMAND$ option. Type the value(s) or input parameter(s)
separated by spaces in the dialog box provided, and press the Enter
key. You are now ready to execute the program and can do so by
pressing Alt-R again and then S (or in this case Enter because the
option is already highlighted) to choose the Start option.
NOTE: Each listing contains a USAGE line within the comments to let you
know when user input is expected and in what format. Parameters might be
other filenames, numeric values, alphanumeric characters, math functions,
drive designators, paths to directories and subdirectories, or symbols.
You can also find parameters in Appendix A.
5. If no command line parameters are required, simply press Alt-R and
then S to start the selected module. (You can also press Shift-F5 to
start.)
These steps are basically the same for systems using a mouse device.
Instead of pressing the Alt key, however, you move the mouse pointer to
the desired menu names, menu options, and dialog box fields and press the
left mouse button to make dialog box selections and execute menu commands.
If you receive no response or the program doesn't seem to work correctly,
look up the module in Appendix A and check that .MAK files, libraries
(including MIXED.QLB), color graphics requirements, and so on are resident
and that the paths to them are properly set.
Running Programs as Executable Files
Some of the utility programs, especially those expecting command line
variable input, are more conveniently run as stand-alone executable files
(.EXE). If you plan to develop commercial or public domain software, the
.EXE format is usually preferable.
Before you can run the toolboxes and utility programs directly from
MS-DOS, you must first compile a .BAS file or files using a special option
to create a .EXE file. Two options are available for compiling programs.
The first option is to compile the source code into a stand-alone .EXE
file that runs by itself when executed from MS-DOS. The file, however,
will be quite large, even if it is a simple application or module. The
second option is to create a .EXE file that requires another file,
BRUN40.EXE, to be in the same drive and directory at runtime (when you
execute the program). The resulting compiled program file will be much
smaller, but BRUN40.EXE must always accompany the executable file.
To create a stand-alone executable file from within QuickBASIC, follow the
steps below. Refer to your QuickBASIC manual for instructions on how to
create other .EXE files from within the QuickBASIC environment or from
MS-DOS using BC.EXE.
1. Load the file using steps 1 through 3 above in "Running Programs from
the QuickBASIC Environment."
2. Press Alt-R to display the Run Menu.
3. Press X (Make EXE File).
4. Press Alt-A to select the Stand-Alone .EXE File option. (This method
produces a file with the same filename as the .BAS file but appends
the .EXE file extension. To change the filename, type over the
displayed name before pressing Alt-A.)
5. Press E to choose the Make EXE and Exit command.
QuickBASIC then creates the executable file, the QuickBASIC program is
terminated, and you return to the system prompt. To verify that the file
exists, type DIR and press Enter, and look for that program's filename
with a .EXE extension.
To run the program as a stand-alone .EXE file, type the filename (without
the .EXE extension) from the system prompt. Type any command line values
or input parameters after the filename, with spaces between the filename
and parameters. The program or toolbox module begins executing as soon as
you press the Enter key.
────────────────────────────────────────────────────────────────────────────
ATTRIB
The ATTRIB program generates a table showing combinations of text-mode
character attributes, including all combinations of foreground and
background colors. Only the blink attribute isn't demonstrated, but it is
described at the head of the table. Use this program as a utility program.
Compile it as a stand-alone executable program, and run it to decide what
colors to use in your own programs.
The sole purpose of the ATTRIB main program is to call the single
subprogram, also named Attrib. Actually, the program has only enough
supporting module-level code to demonstrate the subprogram.
The Attrib subprogram may be called by other programs, either by loading
the entire ATTRIB module into memory or by copying only the Attrib
subprogram into another module. Refer to the MOUSTCRS program for an
example.
Program Module: ATTRIB
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ATTRIB **
' ** Type: Program **
' ** Module: ATTRIB.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Displays all combinations of text mode character
' attributes on the screen for review.
'
' USAGE: No command line parameters
' REQUIREMENTS: CGA
' .MAK FILE: (none)
' FUNCTIONS: (none)
' PARAMETERS: (none)
' VARIABLES: (none)
DECLARE SUB Attrib ()
' Call the subprogram
Attrib
' All done
END
──────────────────────────────────────────────────────────────────────────
Subprogram: Attrib
Creates the color attribute table on the screen for the ATTRIB module.
Sixteen foreground and eight background color attributes are available in
the default SCREEN 0 text mode, not counting the blink attribute for the
foreground color. This subprogram displays all 128 combinations in a way
that makes it easy to see which numbers result in which colors.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Attrib **
' ** Type: Subprogram **
' ** Module: ATTRIB.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Displays table of color attributes for text mode.
'
' EXAMPLE OF USE: Attrib
' PARAMETERS: (none)
' VARIABLES: bgd% Background number for COLOR statement
' fgd% Foreground number for COLOR statement
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Attrib ()
'
SUB Attrib STATIC
SCREEN 0
CLS
PRINT "Attributes for the COLOR statement in text mode (SCREEN 0)."
PRINT "Add 16 to the foreground to cause the character to blink."
FOR bgd% = 0 TO 7
COLOR bgd% XOR 7, bgd%
PRINT
PRINT "Background%"; STR$(bgd%),
PRINT "Foreground% ..."; SPACE$(41)
FOR fgd% = 0 TO 15
COLOR fgd%, bgd%
PRINT STR$(fgd%); " ";
NEXT fgd%
NEXT bgd%
COLOR 7, 0
PRINT
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
BIN2HEX
The BIN2HEX program is a utility that creates hexadecimal format files
showing the contents of a given binary file. In this book, its most useful
purpose is in displaying the contents of .OBJ files created by the
Microsoft Macro Assembler. This enables you to create the necessary files
for using the assembly-language routines in this book, even if you don't
have the Macro Assembler.
The program reads bytes of the input file using binary mode, converts each
to a two-character hexadecimal string, and then formats the output by
blocking the hexadecimal numbers into two groups of eight bytes per line.
The output file can be listed or printed and can easily be transferred
over a modem using conventional ASCII protocol.
The HEX2BIN program performs the opposite function of this program,
converting a hexadecimal listing back to a binary file.
You will find several assembly-language subprograms in Part III of this
book, and Microsoft provides two assembly listings with the QuickBASIC
language. The suggested method of creating the .OBJ files from these
listings is to use the Microsoft Macro Assembler, version 5.0. However, if
you don't have the Macro Assembler, type in the hexadecimal files using
the QuickBASIC Document editing capability, and then run the HEX2BIN
program to convert each to the required .OBJ file.
To use BIN2HEX, type the input filename and output filename on the command
line when you run the program. For example, to convert MOUSE.OBJ to the
hexadecimal listing MOUSE.HEX, enter these two command line parameters:
MOUSE.OBJ MOUSE.HEX
Be sure to use the full filename, including the extension. Separate
filenames with spaces, as shown, or with commas if preferred.
The BIN2HEX program was used to create the hexadecimal listings in
Appendix D.
Also review the HEX2BIN program for more information on using these
routines.
Program Module: BIN2HEX
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BIN2HEX **
' ** Type: Program **
' ** Module: BIN2HEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Reads in any file and writes out a hexadecimal format file
' suitable for rebuilding the original file using the HEX2BIN
' program.
'
' USAGE: BIN2HEX inFileName.ext outFileName.ext
' .MAK FILE: BIN2HEX.BAS
' PARSE.BAS
' PARAMETERS: inFileName Name of file to be duplicated in hexadeci
' format
' outFileName Name of hexadecimal format file to be cre
' VARIABLES: cmd$ Working copy of the command line
' inFile$ Name of input file
' outFile$ Name of output file
' byte$ Buffer for binary file access
' i& Index to each byte of input file
' h$ Pair of hexadecimal characters representi
' each byte
DECLARE SUB ParseWord (a$, sep$, word$)
' Initialization
CLS
PRINT "BIN2HEX "; COMMAND$
PRINT
' Get the input and output filenames from the command line
cmd$ = COMMAND$
ParseWord cmd$, " ,", inFile$
ParseWord cmd$, " ,", outFile$
' Verify that both filenames were given
IF outFile$ = "" THEN
PRINT
PRINT "Usage: BIN2HEX inFileName outFileName"
SYSTEM
END IF
' Open the input file
OPEN inFile$ FOR BINARY AS #1 LEN = 1
IF LOF(1) = 0 THEN
CLOSE #1
KILL inFile$
PRINT
PRINT "File not found - "; inFile$
SYSTEM
END IF
' Open the output file
OPEN outFile$ FOR OUTPUT AS #2
' Process each byte of the file
byte$ = SPACE$(1)
FOR i& = 1 TO LOF(1)
GET #1, , byte$
h$ = RIGHT$("0" + HEX$(ASC(byte$)), 2)
PRINT #2, h$; SPACE$(1);
IF i& = LOF(1) THEN
PRINT #2, ""
ELSEIF i& MOD 16 = 0 THEN
PRINT #2, ""
ELSEIF i& MOD 8 = 0 THEN
PRINT #2, "- ";
END IF
NEXT i&
' Clean up and quit
CLOSE
END
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
BIOSCALL
The BIOSCALL toolbox provides a collection of utility BIOS system calls.
Several useful routines and data tables are in your computer's BIOS ROM
(Basic Input/Output Services, Read Only Memory), ready to be tapped into
by your QuickBASIC programs. This toolbox of routines provides a sampler
of the most useful interrupt calls that return the information provided by
BIOS. With QuickBASIC, it's easy to access the BIOS ROM.
The module-level code provides demonstrations of the available subprograms
when BIOSCALL is the designated main program. Later, load the BIOSCALL
module along with any program you're developing when you need any
information provided by the subprograms.
Be aware that whenever you use the BIOSCALL toolbox, the mixed-language
subprograms Interrupt and InterruptX must be accessible. Refer to "Using
QuickBASIC Toolboxes" on page 21 for instructions on creating and loading
the Quick Library MIXED.QLB with the QuickBASIC system.
The Scroll subprogram, demonstrated first, prints a block of fifteen
lines of uppercase characters on the screen. The first line is filled with
As, the second with Bs, and so on. Each line is also printed in a
different color scheme to make it easier to see exactly which characters
are scrolled. The Scroll subprogram scrolls the rectangular area (from
row 2, column 3 to row 6, column 16) up 3 lines. The attribute byte is set
to green foreground on blue background, the same as the attribute byte at
row 2, column 3. Study the displayed result, and notice that the lines
moved up three rows and that the three blank lines show the blue
background.
The Equipment subprogram determines the computer equipment settings as
maintained by BIOS. A short table is displayed, listing the availability
or count of the printers, the game adapter, the serial I/O ports, the
floppy disk drives, and the math coprocessor. Also displayed is the
initial video state at boot-up time.
VideoState, the next subprogram demonstrated, determines the current
video state. The current video mode, number of text columns, and current
active video page are displayed. Finally, GetShiftStates displays a table
of the shift keys and shift states. This table is continuously updated
until you press the Enter key, allowing you to try out the various shift
keys. For example, press the left and right shift keys, singly or
together, and notice how the state of each is monitored independently.
In the demo module, two subprograms, PrintScreen and ReBoot, are
commented out, as the actions they take are a bit extreme. To demo these
subprograms, remove the apostrophes in front of these statements, which
you'll find near the end of the module-level code. Don't forget that once
you reboot, everything currently in memory is erased, and you'll be
starting fresh.
For more information on the available BIOS calls, refer to the technical
reference manual for your computer.
Name Type Description
──────────────────────────────────────────────────────────────────────────
BIOSCALL.BAS Demo module
Equipment Sub Equipment/hardware information
GetShiftStates Sub Shift key states
PrintScreen Sub Screen dump
ReBoot Sub System reboot
Scroll Sub Moves text in designated area of screen
VideoState Sub Mode, col, and page display of current
state
──────────────────────────────────────────────────────────────────────────
Demo Module: BIOSCALL
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BIOSCALL **
' ** Type: Toolbox **
' ** Module: BIOSCALL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates several interrupt calls to the ROM BIOS.
'
' USAGE: No command line parameters
' REQUIREMENTS: MIXED.QLB/.LIB
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: i% Loop index for creating lines to scroll
' equip Structure of type EquipmentType
' mode% Video mode returned by VideoState
' columns% Video columns returned by VideoState
' page% Video page returned by VideoState
' shift Structure of type ShiftType
' Constants
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Declare the Type structures
TYPE RegType
ax AS INTEGER
bx AS INTEGER
cx AS INTEGER
dx AS INTEGER
Bp AS INTEGER
si AS INTEGER
di AS INTEGER
flags AS INTEGER
END TYPE
TYPE RegTypeX
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
TYPE EquipmentType
printers AS INTEGER
gameAdapter AS INTEGER
serial AS INTEGER
floppies AS INTEGER
initialVideo AS INTEGER
coprocessor AS INTEGER
END TYPE
TYPE ShiftType
right AS INTEGER
left AS INTEGER
ctrl AS INTEGER
alt AS INTEGER
scrollLockState AS INTEGER
numLockState AS INTEGER
capsLockState AS INTEGER
insertState AS INTEGER
END TYPE
DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
DECLARE SUB PrintScreen ()
DECLARE SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%)
DECLARE SUB Equipment (equip AS EquipmentType)
DECLARE SUB VideoState (mode%, columns%, page%)
DECLARE SUB GetShiftStates (shift AS ShiftType)
DECLARE SUB ReBoot ()
' Demonstrate the Scroll subprogram
CLS
FOR i% = 1 TO 15
COLOR i%, i% - 1
PRINT STRING$(25, i% + 64)
NEXT i%
COLOR 7, 0
PRINT
PRINT "Press <Enter> to scroll part of the screen"
DO
LOOP UNTIL INKEY$ = CHR$(13)
Scroll 2, 3, 6, 16, 3, SCREEN(2, 3, 1)
' Wait for user before continuing
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
CLS
' Determine the equipment information
DIM equip AS EquipmentType
Equipment equip
PRINT "Printers:", equip.printers
PRINT "Game adapter:", equip.gameAdapter
PRINT "Serial IO:", equip.serial
PRINT "Floppies:", equip.floppies
PRINT "Video:", equip.initialVideo
PRINT "Coprocessor:", equip.coprocessor
' Determine the current video state
PRINT
VideoState mode%, columns%, page%
PRINT "Video mode:", mode%
PRINT "Text columns:", columns%
PRINT "Video page:", page%
' Wait for user before continuing
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
' Demonstrate the shift key states
CLS
PRINT "(Press shift keys, then <Enter> to continue...)"
DIM shift AS ShiftType
DO
LOCATE 4, 1
PRINT "Shift states:"
GetShiftStates shift
PRINT
PRINT "Left shift:", shift.left
PRINT "Right shift:", shift.right
PRINT "Ctrl:", shift.ctrl
PRINT "Alt:", shift.alt
PRINT "Scroll Lock:", shift.scrollLockState
PRINT "Num Lock:", shift.numLockState
PRINT "Caps Lock:", shift.capsLockState
PRINT "Insert:", shift.insertState
LOOP UNTIL INKEY$ = CHR$(13)
' Uncomment the following line to cause a screen dump to printer....
' PrintScreen
' Uncomment the following line only if you want to reboot....
' ReBoot
END
──────────────────────────────────────────────────────────────────────────
Subprogram: Equipment
Returns information about the available computer hardware by calling the
BIOS service at interrupt 11H, which returns bit patterns indicating the
equipment configuration. The definition of the data structure named
EquipmentType lists the items that this call can determine.
This subprogram allows your program to decide how to handle input and
output chores. As one example, the user can be prompted to Remove the
first disk and insert the second or to Insert the second disk in drive B,
depending on whether the computer has one or two floppy disk drives
available.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Equipment **
' ** Type: Subprogram **
' ** Module: BIOSCALL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns equipment configuration information from BIOS.
'
' EXAMPLE OF USE: Equipment equip
' PARAMETERS: equip Structure of type EquipmentType
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' Bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' TYPE EquipmentType
' printers AS INTEGER
' gameAdapter AS INTEGER
' serial AS INTEGER
' floppies AS INTEGER
' initialVideo AS INTEGER
' coprocessor AS INTEGER
' END TYPE
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType
' DECLARE SUB Equipment (equip AS EquipmentType)
'
SUB Equipment (equip AS EquipmentType) STATIC
DIM reg AS RegType
Interrupt &H11, reg, reg
equip.printers = (reg.ax AND &HC000&) \ 16384
equip.gameAdapter = (reg.ax AND &H1000) \ 4096
equip.serial = (reg.ax AND &HE00) \ 512
equip.floppies = (reg.ax AND &HC0) \ 64 + 1
equip.initialVideo = (reg.ax AND &H30) \ 16
equip.coprocessor = (reg.ax AND 2) \ 2
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: GetShiftStates
Returns the state of each shift key at the moment the subprogram is called
and the current shift key states.
The left Shift, right Shift, Ctrl, and Alt keys return a 1 in the
appropriate structure variables if they are pressed at the moment this
subprogram is called. If not pressed, a 0 is returned instead.
This subprogram can also monitor the four shift states. If active, the
Scroll Lock, Num Lock, Caps Lock, and Insert states return a value of 1 in
the appropriate variable. If your keyboard has lights indicating the
current states of these shift keys, this subprogram returns a 1 whenever a
light is on and a 0 when the light is off.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetShiftStates **
' ** Type: Subprogram **
' ** Module: BIOSCALL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns state of the various shift keys.
'
' EXAMPLE OF USE: GetShiftStates shift
' PARAMETERS: shift Structure of type ShiftType
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' Bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' TYPE ShiftType
' right AS INTEGER
' left AS INTEGER
' ctrl AS INTEGER
' alt AS INTEGER
' scrollLockState AS INTEGER
' numLockState AS INTEGER
' capsLockState AS INTEGER
' insertState AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE SUB GetShiftStates (shift AS ShiftType)
'
SUB GetShiftStates (shift AS ShiftType) STATIC
DIM reg AS RegType
reg.ax = &H200
Interrupt &H16, reg, reg
shift.right = reg.ax AND 1
shift.left = (reg.ax AND 2) \ 2
shift.ctrl = (reg.ax AND 4) \ 4
shift.alt = (reg.ax AND 8) \ 8
shift.scrollLockState = (reg.ax AND 16) \ 16
shift.numLockState = (reg.ax AND 32) \ 32
shift.capsLockState = (reg.ax AND 64) \ 64
shift.insertState = (reg.ax AND 128) \ 128
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: PrintScreen
Performs exactly the same screen-to-printer dump that occurs when the
Shift-Print Screen keys are pressed.
Whenever you press the Shift-Print Screen keys, the operating system
performs an interrupt 5 to activate the BIOS-level code for performing the
screen dump. With the PrintScreen subprogram, you can program such a
screen dump at any point in the operation of a running program without
requiring user intervention.
Because the screen dump BIOS routine is interrupt driven, any changes to
the screen dump code are automatically taken into account. For example, if
your computer loads and patches in an improved version of the screen dump
at boot-up time, this subprogram activates the new routine with no
problem. That's one of the nice features of the interrupt mechanism
provided by the 8086 family of computers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: PrintScreen **
' ** Type: Subprogram **
' ** Module: BIOSCALL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Activates interrupt 5 to cause a dump of the
' screen's contents to the printer.
'
' EXAMPLE OF USE: PrintScreen
' PARAMETERS: (none)
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' Bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE SUB PrintScreen ()
'
SUB PrintScreen STATIC
DIM reg AS RegType
Interrupt 5, reg, reg
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ReBoot
Causes the system to reboot. Depending on the computer and its
configuration, this reboot won't always work perfectly. Be sure to test
the subprogram carefully for your specific circumstances if you plan to
use it on a routine basis.
Perhaps the best and safest use for this subprogram is as an escape route
for unauthorized access to software, because rebooting can frustrate
attempts to overcome copy protection schemes. For example, try rebooting
after a user fails a password check for the third time or if an
unauthorized copy of a program is detected.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ReBoot **
' ** Type: Subprogram **
' ** Module: BIOSCALL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Causes the computer to reboot.
'
' EXAMPLE OF USE: ReBoot
' PARAMETERS: (none)
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' Bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE SUB ReBoot ()
'
SUB ReBoot STATIC
DIM reg AS RegType
Interrupt &H19, reg, reg
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Scroll
Provides a quick scroll of text lines in a rectangular area of the
display. The BIOS video interrupt 10H is set up to scroll. You place the
correct parameters in the processor registers, and the BIOS code does the
rest.
Six parameters are passed to this subprogram. The first four define the
upper left and lower right corners of the area to be scrolled. These
coordinates refer to text-mode character locations, with the upper left
corner of the screen defined as row 1, column 1. The lower right corner of
the screen is defined as row 25, column 80 for 80-column text mode, or row
25, column 40 for 40-column text mode.
The last two parameters provide the line count and the color attribute. If
the line count is a positive number, the lines scroll up by the indicated
number of rows, leaving blank lines at the bottom of the scrolled area. If
the line count is negative, the lines scroll down. The blank lines are
filled with space characters, and the color attribute is set by the
attribute byte passed in the sixth parameter.
Usually this subprogram is used to scroll text one line at a time, such as
when displaying the contents of a long file using the MS-DOS TYPE command.
A handy feature is the subprogram's ability to completely clear any or all
of the screen, setting the background color at the same time. To do this,
pass a line count of 0. The BIOS routine will fill the entire rectangular
area with spaces, much faster than if you were to PRINT the same number of
space strings.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Scroll **
' ** Type: Subprogram **
' ** Module: BIOSCALL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Scrolls the screen in the rectangular area defined
' by the row and col parameters. Positive line count
' moves the lines up, leaving blank lines at bottom;
' negative line count moves the lines down.
'
' EXAMPLE OF USE: Scroll row1%, col1%, row2%, col2%, lines%, attr%
' PARAMETERS: row1% Upper left character row defining rectangular
' scroll area
' col1 Upper left character column defining rectangu
' scroll area
' row2% Lower right character row defining rectangula
' scroll area
' col2% Lower right character column defining
' rectangular scroll area
' lines% Number of character lines to scroll
' attr% Color attribute byte to be used in new text
' lines scrolled onto the screen
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' Bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%
'
SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%) STATIC
DIM reg AS RegType
IF lines% > 0 THEN
reg.ax = &H600 + lines% MOD 256
ELSE
reg.ax = &H700 + ABS(lines%) MOD 256
END IF
reg.bx = (attribute% * 256&) AND &HFF00
reg.cx = (row1% - 1) * 256 + col1% - 1
reg.dx = (row2% - 1) * 256 + col2% - 1
Interrupt &H10, reg, reg
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: VideoState
Returns the current mode, the number of columns, and the page of the
display.
This subprogram returns information about the current video mode. The
mode% parameter returned by the ROM BIOS is different from the number used
in the SCREEN statement to set a video mode. The two parameters do
correlate, however, and the following table provides a useful comparison:
SCREEN Mode WIDTH Mode% (from VideoState)
──────────────────────────────────────────────────────────────────────────
0 40 1
0 80 3
1 40 4
2 80 6
7 40 13
8 80 14
9 80 16
10 80 15
11 80 17
12 80 18
13 40 19
──────────────────────────────────────────────────────────────────────────
The column% parameter is always 40 or 80, depending on the current SCREEN
and WIDTH settings.
The page% parameter is the currently active page number as set by the
SCREEN statement. The default is page 0, and the maximum active page
number is a function of the current screen mode. See the SCREEN statement
in your QuickBASIC documentation for more information about active and
virtual pages as set by the SCREEN statement.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: VideoState **
' ** Type: Subprogram **
' ** Module: BIOSCALL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Determines the current video mode parameters.
'
' EXAMPLE OF USE: VideoState mode%, columns%, page%
' PARAMETERS: mode% Current video mode
' columns% Current number of text columns
' page% Current active display page
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' Bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE SUB VideoState (mode%, columns%, page%)
'
SUB VideoState (mode%, columns%, page%) STATIC
DIM reg AS RegType
reg.ax = &HF00
Interrupt &H10, reg, reg
mode% = reg.ax AND &HFF
columns% = (CLNG(reg.ax) AND &HFF00) \ 256
page% = (CLNG(reg.bx) AND &HFF00) \ 256
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
BITS
The BITS toolbox provides four bit manipulation routines. The
Bin2BinStr$ and BinStr2Bin% functions convert integer numbers to and
from binary string representations. This action is similar to that of the
QuickBASIC HEX$, OCT$, and VAL functions, except that the conversions deal
with base 2 representations.
The BitGet and BitPut subprograms let you store and retrieve single bits
from any location in any string. Up to 32767 bits can be accessed in a
single string, which results in a string of 4096 bytes. These subprograms
would be useful for data acquisition and process control applications
involving a large number of contact closures. The famous sieve of
Eratosthenes for finding prime numbers is used to demonstrate these two
subprograms. Prime numbers from 1 through 1000 are found and printed by
keeping track of a string of bits, each representing an integer from 1
through 1000.
You can change the value of max% in this demonstration to find prime
numbers up to about 10937. Larger values of max% will cause overflow, but
by reprogramming the variables involved, you can probably find even bigger
primes.
Name Type Description
──────────────────────────────────────────────────────────────────────────
BITS.BAS Demo module
Bin2BinStr$ Func Integer to 16-character binary string
BinStr2Bin% Func 16-character binary string to integer
BitGet Sub Value from any bit position in a string
BitPut Sub Sets or clears bit at location in a string
──────────────────────────────────────────────────────────────────────────
Demo Module: BITS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BITS **
' ** Type: Toolbox **
' ** Module: BITS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates the bit manipulation functions
' and subprograms.
'
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: max% Upper limit for the prime number generator
' b$ Bit string for finding prime numbers
' n% Loop index for sieve of Eratosthenes
' bit% Bit retrieved from b$
' i% Bit loop index
' q$ The double quote character
DECLARE FUNCTION BinStr2Bin% (b$)
DECLARE FUNCTION Bin2BinStr$ (b%)
' Subprograms
DECLARE SUB BitGet (a$, bitIndex%, bit%)
DECLARE SUB BitPut (b$, bitIndex%, bit%)
' Prime numbers less than max%, using bit fields in B$
CLS
max% = 1000
PRINT "Primes up to"; max%; "using BitGet and BitPut for sieve..."
PRINT
PRINT 1; 2;
b$ = STRING$(max% \ 8 + 1, 0)
FOR n% = 3 TO max% STEP 2
BitGet b$, n%, bit%
IF bit% = 0 THEN
PRINT n%;
FOR i% = 3 * n% TO max% STEP n% + n%
BitPut b$, i%, 1
NEXT i%
END IF
NEXT n%
PRINT
' Demonstration of the Bin2BinStr$ function
PRINT
PRINT "Bin2BinStr$(12345) = "; Bin2BinStr$(12345)
' Demonstration of the BinStr2Bin% function
PRINT
q$ = CHR$(34)
PRINT "BinStr2Bin%("; q$; "1001011"; q$; ") = ";
PRINT BinStr2Bin%("1001011")
' That's all
END
──────────────────────────────────────────────────────────────────────────
Function: Bin2BinStr$
Returns a 16-character binary representation of an integer value. This
function is similar to QuickBASIC's HEX$ and OCT$ functions, except that
the conversion base is 2 instead of 16 or 8, and that 16 characters are
always returned. For example, Bin2BinStr$(7) returns 0000000000000111, and
Bin2BinStr$(-1) returns 1111111111111111.
You can easily remove leading zeros in the 16-character string by using
the LtrimSet$ function, as shown in the STRINGS module:
bin$ = LtrimSet$(bin$, "0")
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Bin2BinStr$ **
' ** Type: Function **
' ** Module: BITS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a string of sixteen "0" and "1" characters
' that represent the binary value of b%.
'
' EXAMPLE OF USE: PRINT Bin2BinStr$(b%)
' PARAMETERS: b% Integer number
' VARIABLES: t$ Working string space for forming
binary string
' b% Integer number
' mask% Bit isolation mask
' i% Looping index
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Bin2BinStr$ (b%)
'
FUNCTION Bin2BinStr$ (b%) STATIC
t$ = STRING$(16, "0")
IF b% THEN
IF b% < 0 THEN
MID$(t$, 1, 1) = "1"
END IF
mask% = &H4000
FOR i% = 2 TO 16
IF b% AND mask% THEN
MID$(t$, i%, 1) = "1"
END IF
mask% = mask% \ 2
NEXT i%
END IF
Bin2BinStr$ = t$
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: BinStr2Bin%
Returns the integer represented by a string of up to 16 0s and 1s. For
example, BinStr2Bin%("111") returns 7; BinStr2Bin%("000101") returns 5.
If the string has more than 16 characters, only the rightmost 16 are used.
Any character other than 1 is treated as 0.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BinStr2Bin% **
' ** Type: Function **
' ** Module: BITS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the integer represented by a string of up
' to 16 "0" and "1" characters.
'
' EXAMPLE OF USE: PRINT BinStr2Bin%(b$)
' PARAMETERS: b$ Binary representation string
' VARIABLES: bin% Working variable for finding value
' t$ Working copy of b$
' mask% Bit mask for forming value
' i% Looping index
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION BinStr2Bin% (b$)
'
FUNCTION BinStr2Bin% (b$) STATIC
bin% = 0
t$ = RIGHT$(STRING$(16, "0") + b$, 16)
IF LEFT$(t$, 1) = "1" THEN
bin% = &H8000
END IF
mask% = &H4000
FOR i% = 2 TO 16
IF MID$(t$, i%, 1) = "1" THEN
bin% = bin% OR mask%
END IF
mask% = mask% \ 2
NEXT i%
BinStr2Bin% = bin%
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: BitGet
Returns a bit value extracted from any bit position in a string. The bits
are numbered consecutively, starting with bit 1 in the most significant
bit position of the first byte of the string. Bit 8 is the least
significant bit of this same byte, bit 9 is the most significant bit of
the second byte, and so on. This subprogram can access up to 32767 bits,
in which case the string must be 4096 bytes in length. For example:
a$ = "A B C" 0 1 0 0 0 0 0 1 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1
BitGet (a$, 17, bit%) ... bit% = 0
BitGet (a$, 18, bit%) ... bit% = 1
The BitPut subprogram lets you set the bits in a string as desired.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BitGet **
' ** Type: Subprogram **
' ** Module: BITS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Extracts the bit at bitIndex% into a$ and returns
' either 0 or 1 in bit%. The value of bitIndex%
' can range from 1 to 8 * LEN(a$).
'
' EXAMPLE OF USE: BitGet a$, bitIndex%, bit%
' PARAMETERS: a$ String where bit is stored
' bitIndex% Bit position in string
' bit% Extracted bit value, 0 or 1
' VARIABLES: byte% Byte location in string of the bit
' mask% Bit isolation mask for given bit
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB BitGet (a$, bitIndex%, bit%)
'
SUB BitGet (a$, bitIndex%, bit%) STATIC
byte% = (bitIndex% - 1) \ 8 + 1
SELECT CASE bitIndex% MOD 8
CASE 1
mask% = 128
CASE 2
mask% = 64
CASE 3
mask% = 32
CASE 4
mask% = 16
CASE 5
mask% = 8
CASE 6
mask% = 4
CASE 7
mask% = 2
CASE 0
mask% = 1
END SELECT
IF ASC(MID$(a$, byte%, 1)) AND mask% THEN
bit% = 1
ELSE
bit% = 0
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: BitPut
Sets or clears a single bit at any bit location in a string. The string
can be up to 4096 bytes in length, allowing access of up to 32767 bits.
Bits are numbered from left to right; the most significant bit of the
first byte is bit 1, the least significant bit of the first byte is bit 8,
the most significant bit of the second byte is bit 9, and so on. You can
use the BitGet subprogram to get the bit values from the string as
necessary. To initialize a string to all zeros or ones, use the STRING$
function. For example, STRING$(4096, 0) returns a string of 32767 cleared
bits, and STRING$(4096, 255) returns a string of 32767 set bits.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BitPut **
' ** Type: Subprogram **
' ** Module: BITS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' If bit% is non-zero, then the bit at bitIndex% into
' a$ is set to 1; otherwise, it's set to 0. The value
' of bitIndex% can range from 1 to 8 * LEN(a$).
'
' EXAMPLE OF USE: BitPut a$, bitIndex%, bit%
' PARAMETERS: a$ String containing the bits
' bitIndex% Index to the bit of concern
' bit% Value of bit (1 to set, 0 to clear)
' VARIABLES: bytePtr% Pointer to the byte position in the string
' mask% Bit isolation mask
' byteNow% Current numeric value of string byte
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB BitPut (b$, bitIndex%, bit%)
'
SUB BitPut (a$, bitIndex%, bit%) STATIC
bytePtr% = bitIndex% \ 8 + 1
SELECT CASE bitIndex% MOD 8
CASE 1
mask% = 128
CASE 2
mask% = 64
CASE 3
mask% = 32
CASE 4
mask% = 16
CASE 5
mask% = 8
CASE 6
mask% = 4
CASE 7
mask% = 2
CASE 0
mask% = 1
bytePtr% = bytePtr% - 1
END SELECT
byteNow% = ASC(MID$(a$, bytePtr%, 1))
IF byteNow% AND mask% THEN
IF bit% = 0 THEN
MID$(a$, bytePtr%, 1) = CHR$(byteNow% XOR mask%)
END IF
ELSE
IF bit% THEN
MID$(a$, bytePtr%, 1) = CHR$(byteNow% XOR mask%)
END IF
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
CALENDAR
The CALENDAR toolbox is a collection of easy-to-use functions and
subprograms for date and time conversions and calculations. See the
MONTH program for an example of how this module can be loaded as a
toolbox for use by another main program.
Wherever possible, dates and times are passed in a string format identical
to that used by the QuickBASIC DATE$ and TIME$ functions. This makes the
required parameters easier to remember and makes it possible to define
many of the routines as functions that would otherwise have to be defined
as subprograms. For example, the Julian2Date$ function returns a date in
the string format mentioned. Alternative approaches would require defining
three functions (one for returning the year, one for the month, and one
for the day) or defining a subprogram that returned the three numbers in
the parameter list. Returning dates in this string format also eliminates
output numeric formatting because the string is ready to be printed as is.
The Julian day number is an astronomical convention that allows dates to
be cataloged by a single, large integer. A useful feature of the Julian
day number is that a simple subtraction can calculate the number of days
between any two dates. Leap years and the strange pattern of days in the
various months make this calculation difficult when dealing with the usual
month, day, and year numbers. The Date2Julian& and Julian2Date$
conversion functions take care of all the details for you, making calendar
calculations a breeze. Other functions return the day of the week, day of
the year, day of the century, name of each month, and other related
details──just about everything you ever wanted to know, but were afraid to
ask, about dates and time.
The calculations are usually accurate for dates from 1583 to the
indefinite future, although some functions generate errors for dates
between 1583 and 1599 if the calculations involve earlier dates. For
example, consider how the DayOfTheCentury& function would attempt to
calculate the day of the century for July 4, 1599. First, the function
calculates the Julian day number for 07-04-1599 and then it attempts to
subtract from that the Julian day number for the last day of the previous
century. Because 12-31-1499 is earlier than 1583, the function will not
work correctly.
╓┌─┌─────────────────────────────┌─────────────┌─────────────────────────────╖
Name Type Description
──────────────────────────────────────────────────────────────────────────
CALENDAR.BAS Demo module
CheckDate% Func Validates date with return of
TRUE/FALSE
Date2Day% Func Day of month number from date
string
Date2Julian& Func Julian day number for a given
Name Type Description
──────────────────────────────────────────────────────────────────────────
Date2Julian& Func Julian day number for a given
date
Date2Month% Func Month number from date string
Date2Year% Func Year number from date string
DayOfTheCentury& Func Day of the given century
DayOfTheWeek$ Func Name of day of the week for
given date
DayOfTheYear% Func Day of the year (1 through
366) for given date
DaysBetweenDates& Func Number of days between two
dates
HMS2Time$ Func Time string for given hour,
minute, and second
Julian2Date$ Func Date string from given Julian
day number
MDY2Date$ Func Date string from given month,
day, and year
MonthName$ Func Name of month for a given date
OneMonthCalendar Sub One-month calendar for given
Name Type Description
──────────────────────────────────────────────────────────────────────────
OneMonthCalendar Sub One-month calendar for given
date
Second2Date$ Func Seconds from last of 1979 to
date given
Second2Time$ Func Time of day from seconds since
last of 1979
Time2Hour% Func Hour number from time string
Time2Minute% Func Minute number from time string
Time2Second% Func Seconds number from time
string
TimeDate2Second& Func Seconds from last of 1979 from
date/time
──────────────────────────────────────────────────────────────────────────
Demo Module: CALENDAR
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CALENDAR **
' ** Type: Toolbox **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: month% Month for demonstration
' day% Day for demonstration
' year% Year for demonstration
' dat$ Date for demonstration
' j& Julian day number
' tim$ System time right now
' hour% Hour right now
' minute% Minute right now
' second% Second right now
' sec& Seconds since last second of 1979
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Functions
DECLARE FUNCTION CheckDate% (dat$)
DECLARE FUNCTION Date2Day% (dat$)
DECLARE FUNCTION Date2Julian& (dat$)
DECLARE FUNCTION Date2Month% (dat$)
DECLARE FUNCTION Date2Year% (dat$)
DECLARE FUNCTION DayOfTheCentury& (dat$)
DECLARE FUNCTION DayOfTheWeek$ (dat$)
DECLARE FUNCTION DayOfTheYear% (dat$)
DECLARE FUNCTION DaysBetweenDates& (dat1$, dat2$)
DECLARE FUNCTION HMS2Time$ (hour%, minute%, second%)
DECLARE FUNCTION Julian2Date$ (julian&)
DECLARE FUNCTION MDY2Date$ (month%, day%, year%)
DECLARE FUNCTION MonthName$ (dat$)
DECLARE FUNCTION Second2Date$ (second&)
DECLARE FUNCTION Second2Time$ (second&)
DECLARE FUNCTION Time2Hour% (tim$)
DECLARE FUNCTION Time2Minute% (tim$)
DECLARE FUNCTION Time2Second% (tim$)
DECLARE FUNCTION TimeDate2Second& (tim$, dat$)
' Subprograms
DECLARE SUB OneMonthCalendar (dat$, row%, col%)
' Let's choose the fourth of July for the demonstration
CLS
PRINT "All about the fourth of July for this year..."
month% = 7
day% = 4
year% = Date2Year%(DATE$)
' Demonstrate the conversion to dat$
PRINT
dat$ = MDY2Date$(month%, day%, year%)
PRINT "QuickBASIC string format for this date is "; dat$
' Check the validity of this date
IF CheckDate%(dat$) = FALSE THEN
PRINT "The date you entered is faulty... " + dat$
SYSTEM
END IF
' Day of the week and name of the month
PRINT "The day of the week is "; DayOfTheWeek$(dat$); "."
' Astronomical Julian day number
j& = Date2Julian&(dat$)
PRINT "The Julian day number is"; j&
' Conversion of Julian number to date
PRINT "Date for the given Julian number is "; Julian2Date$(j&); "."
' Convert the date string to numbers
PRINT "The month, day, and year numbers are ";
PRINT Date2Month%(dat$); ","; Date2Day%(dat$); ","; Date2Year%(dat$)
' The month name
PRINT "The month name is "; MonthName$(dat$)
' Day of the year
PRINT "The day of the year is"; DayOfTheYear%(dat$)
' Day of the century
PRINT "The day of the century is"; DayOfTheCentury&(dat$)
' Days from right now
IF Date2Julian&(dat$) < Date2Julian&(DATE$) THEN
PRINT "That was"; DaysBetweenDates&(dat$, DATE$); "days ago."
ELSEIF Date2Julian&(dat$) > Date2Julian&(DATE$) THEN
PRINT "That is"; DaysBetweenDates&(dat$, DATE$); "days from now."
ELSE
PRINT "The date you entered is today's date."
END IF
' Print a one-month calendar
OneMonthCalendar dat$, 14, 25
' Wait for user
LOCATE 23, 1
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
CLS
' Demonstrate extracting hour, minute, and second from tim$
dat$ = DATE$
tim$ = TIME$
hour% = Time2Hour%(tim$)
minute% = Time2Minute%(tim$)
second% = Time2Second%(tim$)
PRINT "The date today... "; dat$
PRINT "The time now ... "; tim$
PRINT "The hour, minute, and second numbers are ";
PRINT hour%; ","; minute%; ","; second%
' Now put it all back together again
PRINT "Time string created from hour, minute, and second is ";
PRINT HMS2Time$(hour%, minute%, second%)
' Seconds since end of 1979
dat$ = DATE$
PRINT "The number of seconds since the last second of 1979 is";
sec& = TimeDate2Second&(tim$, dat$)
PRINT sec&
PRINT "From this number we can extract the date and time..."
PRINT Second2Date$(sec&); " and "; Second2Time$(sec&); "."
──────────────────────────────────────────────────────────────────────────
Function: CheckDate%
Returns TRUE if date is valid or FALSE if date is faulty.
Was February 29, 1726, a real date? The CheckDate% function quickly finds
the answer to this question. If the date checks out as valid, a value of
TRUE (non-zero) is returned. If the date is faulty, a value of FALSE (0)
is returned.
This function is useful in any program that prompts the user to enter a
date. A quick check can be made of the entered date, and the user can be
asked to repeat the input if the entered date is faulty.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CheckDate% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns TRUE if the given date represents a real
' date or FALSE if the date is in error.
'
' EXAMPLE OF USE: test% = CheckDate%(dat$)
' PARAMETERS: dat$ Date to be checked
' VARIABLES: julian& Julian day number for the date
' test$ Date string for given Julian day number
' MODULE LEVEL
' DECLARATIONS: CONST FALSE = 0
' CONST TRUE = NOT FALSE
'
' DECLARE FUNCTION CheckDate% (dat$)
' DECLARE FUNCTION Date2Julian& (dat$)
' DECLARE FUNCTION Julian2Date$ (julian&)
'
FUNCTION CheckDate% (dat$) STATIC
julian& = Date2Julian&(dat$)
test$ = Julian2Date$(julian&)
IF dat$ = test$ THEN
CheckDate% = TRUE
ELSE
CheckDate% = FALSE
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Date2Day%
Extracts the day number from a date string that is in the standard format
MM-DD-YYYY.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Date2Day% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the day number given a date in the
' QuickBASIC string format MM-DD-YYYY.
'
' EXAMPLE OF USE: day% = Date2Day%(dat$)
' PARAMETERS: dat$ Date of concern
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Date2Day% (dat$)
'
FUNCTION Date2Day% (dat$) STATIC
Date2Day% = VAL(MID$(dat$, 4, 2))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Date2Julian&
Returns the Julian day number for a given date. This function and the
related function Julian2Date$ are at the heart of many of the other
functions in this toolbox. This function calculates the astronomical
Julian day number for any date from January 1, 1583, into the indefinite
future, accounting for leap years and century adjustments.
The main advantage of converting dates to long integer numbers is in being
able to easily calculate the number of days between dates and the day of
the week for any date. Further, if you need to store a large number of
dates in a disk file, storing them as four-byte, long integers is more
efficient than storing them in the longer string format or as separate
integers representing the month, day, and year.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Date2Julian& **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the astronomical Julian day number given a
' date in the QuickBASIC string format MM-DD-YYYY.
'
' EXAMPLE OF USE: j& = Date2Julian&(dat$)
' PARAMETERS: dat$ Date of concern
' VARIABLES: month% Month number for given date
' day% Day number for given date
' year% Year number for given date
' ta& First term of the Julian day number calcula
' tb& Second term of the Julian day number calcul
' tc& Third term of the Julian day number calcula
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Date2Day% (dat$)
' DECLARE FUNCTION Date2Julian& (dat$)
' DECLARE FUNCTION Date2Month% (dat$)
' DECLARE FUNCTION Date2Year% (dat$)
'
FUNCTION Date2Julian& (dat$) STATIC
month% = Date2Month%(dat$)
day% = Date2Day%(dat$)
year% = Date2Year%(dat$)
IF year% < 1583 THEN
PRINT "Date2Julian: Year is less than 1583"
SYSTEM
END IF
IF month% > 2 THEN
month% = month% - 3
ELSE
month% = month% + 9
year% = year% - 1
END IF
ta& = 146097 * (year% \ 100) \ 4
tb& = 1461& * (year% MOD 100) \ 4
tc& = (153 * month% + 2) \ 5 + day% + 1721119
Date2Julian& = ta& + tb& + tc&
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Date2Month%
Extracts the month number from a date string that is in the standard
format MM-DD-YYYY.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Date2Month% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the month number given a date in the
' QuickBASIC string format MM-DD-YYYY.
'
' EXAMPLE OF USE: month% = Date2Month%(dat$)
' PARAMETERS: dat$ Date of concern
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Date2Month% (dat$)
'
FUNCTION Date2Month% (dat$) STATIC
Date2Month% = VAL(MID$(dat$, 1, 2))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Date2Year%
Extracts the year number from a date string that is in the standard format
MM-DD-YYYY.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Date2Year% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the year number given a date in the
' QuickBASIC string format MM-DD-YYYY.
'
' EXAMPLE OF USE: year% = Date2Year%(dat$)
' PARAMETERS: dat$ Date of concern
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Date2Year% (dat$)
'
FUNCTION Date2Year% (dat$) STATIC
Date2Year% = VAL(MID$(dat$, 7))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: DayOfTheCentury&
Returns the day of the given century. Each century has more than 32767
days, requiring this function to be declared as returning a long integer
result.
Dates before 01-01-1600 generate an error. See page 55 for an
explanation.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DayOfTheCentury% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the number of the day of the century.
'
' EXAMPLE OF USE: cDay& = DayOfTheCentury&(dat$)
' PARAMETERS: dat$ Date of concern
' VARIABLES: year% Year for given date
' dat1$ Date for last day of previous century
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION DayOfTheCentury& (dat$)
'
FUNCTION DayOfTheCentury& (dat$)
year% = Date2Year%(dat$)
dat1$ = MDY2Date$(12, 31, year% - (year% MOD 100) - 1)
DayOfTheCentury& = DaysBetweenDates&(dat1$, dat$)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: DayOfTheWeek$
Finds the name of the day of the week for any date. In displaying calendar
calculation results, it's often desirable to be able to print the name of
the day of the week. This function lets you conveniently do so.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DayOfTheWeek$ **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a string stating the day of the week.
' Input is a date expressed in the QuickBASIC string
' format MM-DD-YYYY.
'
' EXAMPLE OF USE: PRINT "The day of the week is "; DayOfTheWeek$(dat$)
' PARAMETERS: dat$ Date of concern
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION DayOfTheWeek$ (dat$)
'
FUNCTION DayOfTheWeek$ (dat$) STATIC
SELECT CASE Date2Julian&(dat$) MOD 7
CASE 0
DayOfTheWeek$ = "Monday"
CASE 1
DayOfTheWeek$ = "Tuesday"
CASE 2
DayOfTheWeek$ = "Wednesday"
CASE 3
DayOfTheWeek$ = "Thursday"
CASE 4
DayOfTheWeek$ = "Friday"
CASE 5
DayOfTheWeek$ = "Saturday"
CASE 6
DayOfTheWeek$ = "Sunday"
END SELECT
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: DayOfTheYear%
Returns a number in the range 1 through 366, indicating the day of the
year for the given date, by subtracting the Julian day number for the last
day of the previous year from that of the given date. This calculation
generates an error if the date is before January 1, 1584.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DayOfTheYear% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the number of the day of the year (1-366).
'
' EXAMPLE OF USE: PRINT "The day of the year is"; DayOfTheYear%(dat$)
' PARAMETERS: dat$ Date of concern
' VARIABLES: dat1$ Date of last day of previous year
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION DayOfTheYear% (dat$)
'
FUNCTION DayOfTheYear% (dat$) STATIC
dat1$ = MDY2Date$(12, 31, Date2Year%(dat$) - 1)
DayOfTheYear% = DaysBetweenDates&(dat1$, dat$)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: DaysBetweenDates&
Returns the number of days between two dates by subtracting the Julian day
numbers of the dates. The absolute value of the difference is returned, so
the first date can be earlier or later than the second. The number of days
returned will always be a positive value.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DaysBetweenDates& **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the number of days between any two dates.
'
' EXAMPLE OF USE: days& = DaysBetweenDates&(dat1$, dat2$)
' PARAMETERS: dat1$ First date
' dat2$ Second date
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION DaysBetweenDates& (dat1$, dat2$)
'
FUNCTION DaysBetweenDates& (dat1$, dat2$) STATIC
DaysBetweenDates& = ABS(Date2Julian&(dat1$) - Date2Julian&(dat2$))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: HMS2Time$
Given hour, minute, and second numbers, returns a time string, in the same
format as the string returned by QuickBASIC's TIME$ function. For example,
HMS2Time$(23, 59, 59) returns 23:59:59.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: HMS2Time$ **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the time in the QuickBASIC string format
' HH:MM:SS given hour%, minute%, and second%.
'
' EXAMPLE OF USE: PRINT HMS2Time$(hour%, minute%, second%)
' PARAMETERS: hour% Hour number
' minute% Minutes number
' second% Seconds number
' VARIABLES: t$ Workspace for building the time string
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION HMS2Time$ (hour%, minute%, second%)
'
FUNCTION HMS2Time$ (hour%, minute%, second%) STATIC
t$ = RIGHT$("0" + MID$(STR$(hour%), 2), 2) + ":"
t$ = t$ + RIGHT$("0" + MID$(STR$(minute%), 2), 2) + ":"
HMS2Time$ = t$ + RIGHT$("0" + MID$(STR$(second%), 2), 2)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Julian2Date$
Converts a Julian day number to a date. The smallest long integer number
that can be passed to this function without generating an error is
2299239, the Julian number for the date 01-01-1583.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Julian2Date$ **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a date in the QuickBASIC string format
' MM-DD-YYYY as calculated from a Julian day number.
'
' EXAMPLE OF USE:
' PRINT "Date for the given Julian number is ";Julian2Date$(j&)
' PARAMETERS: j& Julian day number
' VARIABLES: x& Temporary calculation variable
' y& Temporary calculation variable
' d& Day number in long integer form
' m& Month number before adjustment
' month% Month number
' year% Year number
' day% Day number
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Julian2Date$ (julian&)
'
FUNCTION Julian2Date$ (julian&) STATIC
x& = 4 * julian& - 6884477
y& = (x& \ 146097) * 100
d& = (x& MOD 146097) \ 4
x& = 4 * d& + 3
y& = (x& \ 1461) + y&
d& = (x& MOD 1461) \ 4 + 1
x& = 5 * d& - 3
m& = x& \ 153 + 1
d& = (x& MOD 153) \ 5 + 1
IF m& < 11 THEN
month% = m& + 2
ELSE
month% = m& - 10
END IF
day% = d&
year% = y& + m& \ 11
dat$ = MDY2Date$(month%, day%, year%)
Julian2Date$ = dat$
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: MDY2Date$
Creates a date string from the numeric values of month, day, and year for
a given date. The string format is the same as that returned by the
QuickBASIC DATE$ function, MM-DD-YYYY.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MDY2Date$ **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Converts month%, day%, and year% to a date string
' in the QuickBASIC string format MM-DD-YYYY.
'
' EXAMPLE OF USE: dat$ = MDY2Date$(month%, day%, year%)
' PARAMETERS: month% Month for the date
' day% Day of the month
' year% Year number
' VARIABLES: y$ Temporary year string
' m$ Temporary month string
' d$ Temporary day string
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION MDY2Date$ (month%, day%, year%)
'
FUNCTION MDY2Date$ (month%, day%, year%) STATIC
y$ = RIGHT$("000" + MID$(STR$(year%), 2), 4)
m$ = RIGHT$("0" + MID$(STR$(month%), 2), 2)
d$ = RIGHT$("0" + MID$(STR$(day%), 2), 2)
MDY2Date$ = m$ + "-" + d$ + "-" + y$
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: MonthName$
Returns the name of the month for a given date. If the passed date string
has the wrong number of characters, the returned name defaults to
MM-DD-YYYY to remind you of the required format for date strings. If the
string is the right length but the first two characters don't represent a
valid month number, ?MonthName? is returned.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MonthName$ **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a string stating the month as indicated
' in dat$ (QuickBASIC string format MM-DD-YYYY).
'
' EXAMPLE OF USE: PRINT MonthName$(dat$)
' PARAMETERS: dat$ Date of concern
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION MonthName$ (dat$)
'
FUNCTION MonthName$ (dat$) STATIC
IF LEN(dat$) <> 10 THEN
dat$ = "MM-DD-YYYY"
END IF
SELECT CASE LEFT$(dat$, 2)
CASE "01"
MonthName$ = "January"
CASE "02"
MonthName$ = "February"
CASE "03"
MonthName$ = "March"
CASE "04"
MonthName$ = "April"
CASE "05"
MonthName$ = "May"
CASE "06"
MonthName$ = "June"
CASE "07"
MonthName$ = "July"
CASE "08"
MonthName$ = "August"
CASE "09"
MonthName$ = "September"
CASE "10"
MonthName$ = "October"
CASE "11"
MonthName$ = "November"
CASE "12"
MonthName$ = "December"
CASE ELSE
MonthName$ = "?MonthName?"
END SELECT
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: OneMonthCalendar
Uses several functions from the CALENDAR toolbox to print a small,
one-month calendar at any location on the screen. The stand-alone program
named MONTH provides a good demonstration of this subprogram at work.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: OneMonthCalendar **
' ** Type: Subprogram **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Prints a small, one-month calendar at the row%
' and col% indicated.
'
' EXAMPLE OF USE: OneMonthCalendar dat$, row%, col%
' PARAMETERS: dat$ Date of concern
' row% Screen row for upper left corner of calenda
' col% Screen column for upper left corner of cale
' VARIABLES: mname$ Name of given month
' month% Month number
' day% Day number
' year% Year number
' dat1$ Date for first of the given month
' j& Julian day number for each day of the month
' heading$ Title line for calendar
' wa% Day of the week for each day of the month
' rowloc% Row for printing each day number
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB OneMonthCalendar (dat$, row%, col%)
'
SUB OneMonthCalendar (dat$, row%, col%) STATIC
mname$ = MonthName$(dat$)
LOCATE row%, col% + 12 - LEN(mname$) \ 2
PRINT mname$; ","; Date2Year%(dat$)
month% = Date2Month%(dat$)
day% = 1
year% = Date2Year%(dat$)
dat1$ = MDY2Date$(month%, day%, year%)
j& = Date2Julian&(dat1$)
heading$ = " Sun Mon Tue Wed Thu Fri Sat"
wa% = INSTR(heading$, LEFT$(DayOfTheWeek$(dat1$), 3)) \ 4
LOCATE row% + 1, col%
PRINT heading$
rowloc% = row% + 2
LOCATE rowloc%, col% + 4 * wa%
DO
PRINT USING "####"; day%;
IF wa% = 6 THEN
rowloc% = rowloc% + 1
LOCATE rowloc%, col%
END IF
wa% = (wa% + 1) MOD 7
j& = j& + 1
day% = Date2Day%(Julian2Date$(j&))
LOOP UNTIL day% = 1
PRINT
END SUB
──────────────────────────────────────────────────────────────────────────
Function: Second2Date$
Returns a date string given the number of seconds since the last second of
1979. The number of seconds is limited to the range of positive long
integers (1 to 2147483647). Given the largest possible long integer, the
function returns the date 01-19-2048.
Related functions are Second2Time$ and TimeDate2Second&. The
Second2Time$ function finds the time string for a given second, and the
TimeDate2Second$ function finds the seconds since 1979 for a given date
and time.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Second2Date$ **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the date in the QuickBASIC string format
' MM-DD-YYYY given a number of seconds since the
' last second of 1979. Use Second2Time$ to find
' the time of day at the indicated second.
'
' EXAMPLE OF USE: dat$ = Second2Date$(second&)
' PARAMETERS: second& Number of seconds since the last second of
' VARIABLES: days& Julian day number of the date
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Second2Date$ (second&)
'
FUNCTION Second2Date$ (second&) STATIC
days& = second& \ 86400 + 2444240
Second2Date$ = Julian2Date$(days&)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Second2Time$
Returns a time string given the number of seconds since the last second of
1979.
Related functions are Second2Date$ and TimeDate2Second&. The
Second2Date$ function finds the date string for a given second, and the
TimeDate2Second$ function finds the seconds since 1979 for a given date
and time.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Second2Time$ **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the time in the QuickBASIC string format
' HH:MM:SS given the number of seconds since the
' last second of 1979. Use Second2Date$ to find
' the date at the indicated second.
'
' EXAMPLE OF USE: tim$ = Second2Time$(second&)
' PARAMETERS: second& Number of seconds since the last second of
' VARIABLES: time& Number of seconds in current day
' second% Current second of the minute
' minute% Current minute of the hour
' hour% Current hour of the day
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Second2Time$ (second&)
'
FUNCTION Second2Time$ (second&) STATIC
IF second& > 0 THEN
time& = second& MOD 86400
second% = time& MOD 60
time& = time& \ 60
minute% = time& MOD 60
hour% = time& \ 60
Second2Time$ = HMS2Time$(hour%, minute%, second%)
ELSE
Second2Time$ = "HH:MM:SS"
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Time2Hour%
Extracts the numeric value of the hour from a time string if in the
standard TIME$ format HH:MM:SS.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Time2Hour% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the hour number as indicated in a time
' string in the format HH:MM:SS.
'
' EXAMPLE OF USE: hour% = Time2Hour%(tim$)
' PARAMETERS: tim$ Time of concern
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Time2Hour% (tim$)
'
FUNCTION Time2Hour% (tim$) STATIC
Time2Hour% = VAL(LEFT$(tim$, 2))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Time2Minute%
Extracts the numeric value of the hour from a time string that is in the
standard TIME$ format HH:MM:SS.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Time2Minute% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the minute number as indicated in a time
' string in the format HH:MM:SS.
'
' EXAMPLE OF USE: minute% = Time2Minute%(tim$)
' PARAMETERS: tim$ Time of concern
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Time2Minute% (tim$)
'
FUNCTION Time2Minute% (tim$) STATIC
Time2Minute% = VAL(MID$(tim$, 4, 2))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Time2Second%
Extracts the numeric value of the seconds from a time string that is in
the standard TIME$ format HH:MM:SS.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Time2Second% **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the second number as indicated in a time
' string in the format HH:MM:SS.
'
' EXAMPLE OF USE: second% = Time2Second%(tim$)
' PARAMETERS: tim$ Time of concern
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Time2Second% (tim$)
'
FUNCTION Time2Second% (tim$) STATIC
Time2Second% = VAL(MID$(tim$, 7))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: TimeDate2Second&
Returns the number of seconds since the last second of 1979 given any date
and time between the first second of 1980 and a moment in the year 2048.
The largest positive long integer that can be stored in four bytes using
two's complement notation is 2147483647. From the arbitrary point in time
at the start of 1980, counting in seconds reaches this largest possible
positive integer at 03:14:07 on 01-19-2048.
One advantage of converting date and time to a number of seconds is that
this long integer is more compact; this is an advantage, for example, when
a large number of dates and times must be recorded in a disk file. Event
logging, data acquisition, and business transaction time stamping are
examples of the use of this type of subprogram.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: TimeDate2Second& **
' ** Type: Function **
' ** Module: CALENDAR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the number of seconds since the last
' second of 1979. If the date is not in the years
' 1980 to 2047, an error message is output.
'
' EXAMPLE OF USE: sec& = TimeDate2Second&(tim$, dat$)
' PARAMETERS: tim$ Time of concern
' dat$ Date of concern
' VARIABLES: days& Days since 12-31-1979
' hour% Hour of the day
' minute% Minute of the hour
' second% Second of the minute
' secs& Working number of total seconds
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION TimeDate2Second& (tim$, dat$)
'
FUNCTION TimeDate2Second& (tim$, dat$) STATIC
days& = Date2Julian&(dat$) - 2444240
hour% = VAL(LEFT$(tim$, 2))
minute% = VAL(MID$(tim$, 4, 2))
second% = VAL(RIGHT$(tim$, 2))
secs& = CLNG(hour%) * 3600 + minute% * 60 + second%
IF days& >= 0 AND days& < 24857 THEN
TimeDate2Second& = days& * 86400 + secs&
ELSE
PRINT "TimeDate2Second: Not in range 1980 to 2047"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
CARTESIA
The CARTESIA toolbox contains two subprograms and two functions that
convert between Cartesian and polar coordinates. The program first prompts
you to enter x and y values defining a point on the Cartesian plane and
then prints the equivalent coordinate in polar notation.
┌────────────────────────────────────────────────────────────────────────┐
│ This figure can be found on p.78 of the printed version of the book. │
└────────────────────────────────────────────────────────────────────────┘
All of the variables in this module are defined as single-precision,
floating-point values. If you need greater precision, globally change all
exclamation point characters to pound sign characters. You'll also want to
change the CONST PI statement in the Angle! function to provide a
double-precision value for PI.
Name Type Description
──────────────────────────────────────────────────────────────────────────
CARTESIA.BAS Demo module
Angle! Func Angle between X axis and line to x, y
point
Magnitude! Func Distance from origin to x, y point
Pol2Rec Sub Polar to Cartesian conversion
Rec2Pol Sub Cartesian to polar conversion
──────────────────────────────────────────────────────────────────────────
Demo Module: CARTESIA
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CARTESIA **
' ** Type: Toolbox **
' ** Module: CARTESIA.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates a set of functions and subprograms
' dealing with Cartesian coordinates.
'
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: x! X value of Cartesian coordinate
' y! Y value of Cartesian coordinate
' r! Polar notation distance from origin
' theta! Polar notation angle from X axis
DECLARE FUNCTION Angle! (x!, y!)
DECLARE FUNCTION Magnitude! (x!, y!)
DECLARE SUB Pol2Rec (r!, theta!, x!, y!)
DECLARE SUB Rec2Pol (x!, y!, r!, theta!)
CLS
INPUT "Enter X ", x!
INPUT "Enter Y ", y!
PRINT
PRINT "Magnitude!(x!, y!)", Magnitude!(x!, y!)
PRINT "Angle!(x!, y!)", Angle!(x!, y!)
PRINT
Rec2Pol x!, y!, r!, theta!
PRINT "Rec2Pol", , r!; theta!
Pol2Rec r!, theta!, x!, y!
PRINT "Pol2Rec", , x!; y!
──────────────────────────────────────────────────────────────────────────
Function: Angle!
Returns the angle from the origin to a given Cartesian coordinate,
measured from the positive X axis. The angle, expressed in radians, is
returned in the range -PI < Angle! <= +PI.
This function has a good example of an IF-ELSEIF-ELSE-ENDIF structured
statement. Notice that even though this function contains quite a few
statements, the routine quickly skips over the unnecessary instructions.
These tests let the function cover all the special case situations, such
as when the coordinate falls on one or both axes.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Angle! **
' ** Type: Function **
' ** Module: CARTESIA.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the angle (in radians) between the X axis
' and the line from the origin to the point x!,y!
'
' EXAMPLE OF USE: a! = Angle!(x!, y!)
' PARAMETERS: x! X part of the Cartesian coordinate
' y! Y part of the Cartesian coordinate
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Angle! (x!, y!)
'
FUNCTION Angle! (x!, y!) STATIC
CONST PI = 3.141593
CONST HALFPI = PI / 2
IF x! = 0! THEN
IF y! > 0! THEN
Angle! = HALFPI
ELSEIF y! < 0! THEN
Angle! = -HALFPI
ELSE
Angle! = 0!
END IF
ELSEIF y! = 0! THEN
IF x! < 0! THEN
Angle! = PI
ELSE
Angle! = 0!
END IF
ELSE
IF x! < 0! THEN
IF y! > 0! THEN
Angle! = ATN(y! / x!) + PI
ELSE
Angle! = ATN(y! / x!) - PI
END IF
ELSE
Angle! = ATN(y! / x!)
END IF
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Magnitude!
Returns the distance from the origin to a given Cartesian coordinate.
This function, together with the Angle! function, provides the
calculations that perform rectangular to polar coordinate conversions.
They are both called by the Rec2Pol subprogram.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Magnitude! **
' ** Type: Function **
' ** Module: CARTESIA.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the distance from the origin to the
' point x!,y!
'
' EXAMPLE OF USE: r! = Magnitude!(x!, y!)
' PARAMETERS: x! X part of the Cartesian coordinate
' y! Y part of the Cartesian coordinate
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Magnitude! (x!, y!)
'
FUNCTION Magnitude! (x!, y!) STATIC
Magnitude! = SQR(x! * x! + y! * y!)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: Pol2Rec
Converts a polar notation point (magnitude, angle) to its equivalent (x,
y) Cartesian coordinates. The conversion assumes that theta! is expressed
in radians and uses the built-in QuickBASIC functions for finding the sine
and cosine of this angle.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Pol2rec **
' ** Type: Subprogram **
' ** Module: CARTESIA.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Converts polar coordinates to Cartesian notation.
'
' EXAMPLE OF USE: Pol2Rec r!, theta!, x!, y!
' PARAMETERS: r! Distance of point from the origin
' theta! Angle of point from the X axis
' x! X coordinate of the point
' y! Y coordinate of the point
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Pol2Rec (r!, theta!, x!, y!)
'
SUB Pol2Rec (r!, theta!, x!, y!) STATIC
x! = r! * COS(theta!)
y! = r! * SIN(theta!)
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Rec2Pol
Converts a point expressed as a Cartesian coordinate pair (x, y) to the
equivalent polar notation (magnitude, angle). The Angle! and Magnitude!
functions within this toolbox perform the calculations for this
conversion.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Rec2pol **
' ** Type: Subprogram **
' ** Module: CARTESIA.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Converts Cartesian coordinates to polar notation.
'
' EXAMPLE OF USE: Rec2Pol x!, y!, r!, theta!
' PARAMETERS: x! X coordinate of the point
' y! Y coordinate of the point
' r! Distance of point from the origin
' theta! Angle of point from the X axis
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Angle! (x!, y!)
' DECLARE FUNCTION Magnitude! (x!, y!)
' DECLARE SUB Rec2Pol (x!, y!, r!, theta!)
'
SUB Rec2Pol (x!, y!, r!, theta!) STATIC
r! = Magnitude!(x!, y!)
theta! = Angle!(x!, y!)
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
CIPHER
The CIPHER program securely ciphers and deciphers any file. You probably
have some files or data that you'd prefer to keep secret, such as personal
financial information or proprietary business matters. Several packages on
the market let you keep your files secure from prying eyes, but they do it
at some expense. This program does it quickly and simply.
For each byte in the file to be ciphered, the program generates a
pseudorandom byte in the range 0 through 255. The pseudorandom byte and
the file byte are combined using the QuickBASIC XOR function, and the byte
in the file is replaced with this result. To decipher the file, use the
CIPHER program to process the file a second time, using exactly the same
key. XOR then returns the file to its original state.
The bytes in the ciphered file will appear to be as random as the sequence
of pseudorandom bytes generated for this process. The RandInteger%
function generates the bytes, which makes the number of possible
pseudorandom sequences astronomical. Without knowing the key string used
to initialize this sequence, a person could probably not break the cipher.
This points out an important fact about the security of this technique:
The ciphered file is only as secure as the key you select. Let's see how
you can choose a secure key.
First, the "don'ts": Don't use simple, obvious keys such as your name,
initials, names of family members or pets, addresses, phone numbers, and
the like. Don't use the same key repeatedly. Don't record the keys in an
easy-to-find place, such as in a batch file containing CIPHER commands.
And don't forget to keep track of your key in some safe way. You won't be
able to get your file back if you forget the key!
There are several ways to generate your own keys in a safe, secure manner.
Be creative! For example, you might choose your own private "magic number"
that you can easily remember and use it to define a key. If your number is
17, you could use the first 17 characters of line 17, page 17, in your
favorite novel. Another technique is to create a common phrase that's easy
to remember yet contains deliberately misspelled words or an odd
combination of upper- and lowercase characters──3 blined MiSe, for
example. Don't get too carried away with your creativity, though, and end
up with something you can't remember. Changing even one character in the
key will generate an entirely different sequence of pseudorandom bytes.
The CIPHER program has a unique feature built into it to help generate new
words that you can use as keys. Instead of typing the filename and key
string on the command line that invokes the CIPHER program, type CIPHER
/NEWKEY and press the Enter key. The program will generate nine
pseudorandom words, created by randomly selecting characters from sets of
consonants and vowels in a way that makes most of them readable.
When you use the /NEWKEY command line option, a unique set of new words is
generated for every possible clock tick in the life of your computer. In
the module-level code of CIPHER.BAS is the statement RandShuffle DATE$ +
TIME$ + STR$(TIMER), which initializes the random number generator when
you give the /NEWKEY option. The key string for the initialization is
formed by combining the date, time, and timer information into a string of
about 27 characters.
Eighteen times each second your computer updates its internal clock. The
TIMER function returns a different value each time this happens, and the
date and time are unique for every possible second of each day. As a
result, the key string that initializes the random number generator will
always be unique, and it's safe to say you'll never see the same group of
nine words repeated.
If you have a large number of files that you'd like to cipher, you can
automate the process. Create a batch file containing CIPHER command lines,
complete with filenames and keys. Keep this batch file ciphered at all
times, except when you want to use it to cipher or decipher the group of
files listed in the commands. This way, the only key you must remember is
the one for unlocking the batch command file.
To try out the CIPHER program, follow these steps. Create a small,
readable file using the Document mode of the QuickBASIC editor, and save
it as TEST.TXT. Verify the file contents by typing TYPE TEST.TXT. Now, to
cipher the file, run CIPHER with the command line TEST.TXT ABC. When
CIPHER is finished, the file will be unreadable, and entering TYPE
TEST.TXT will result in strange characters on your screen. To decipher the
file, once again run CIPHER with the same command line: TEST.TXT ABC. Be
sure to enter the key string ("ABC" in this case) exactly the same as you
did when the file was ciphered. Finally, type out the file to verify that
it was correctly deciphered.
Name Type Description
──────────────────────────────────────────────────────────────────────────
CIPHER.BAS Program module
NewWord$ Func Creates pseudorandom new word
ProcesX Sub Enciphers string by XORing bytes
──────────────────────────────────────────────────────────────────────────
Program Module: CIPHER
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CIPHER **
' ** Type: Program **
' ** Module: CIPHER.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: CIPHER filename.ext key or CIPHER /NEWKEY
' .MAK FILE: CIPHER.BAS
' RANDOMS.BAS
' PARAMETERS: filename Name of file to be ciphered or deciphere
' key String of one or more words used as the
' cipher key
' VARIABLES: cmd$ Working copy of COMMAND$
' i% Loop index
' firstSpace% Location in command line of first charac
' fileName$ Name of file to be processed
' key$ String to be used as cipher key
' fileLength& Length of file to be processed
' a$ Workspace for groups of bytes from the f
' count% Number of groups of bytes to be processe
' j& Location in file of each group of bytes
' Constants
CONST BYTES = 1000&
' Functions
DECLARE FUNCTION NewWord$ ()
DECLARE FUNCTION Rand& ()
DECLARE FUNCTION RandInteger% (a%, b%)
' Subprograms
DECLARE SUB RandShuffle (key$)
DECLARE SUB ProcesX (a$)
' Initialization
CLS
PRINT "CIPHER "; COMMAND$
PRINT
' Grab the command line parameters
cmd$ = COMMAND$
' If no command line parameters, then tell user what's needed
IF cmd$ = "" THEN
PRINT
PRINT "Usage: CIPHER /NEWKEY"
PRINT "(or) CIPHER filename key-string"
PRINT
SYSTEM
END IF
' If /NEWKEY option, generate a few new words, and then quit
IF INSTR(cmd$, "/NEWKEY") THEN
' Clear the screen and describe the output
CLS
PRINT "Randomly created words that can be used as cipher keys..."
PRINT
RandShuffle DATE$ + TIME$ + STR$(TIMER)
FOR i% = 1 TO 9
PRINT NewWord$; " ";
NEXT i%
PRINT
SYSTEM
END IF
' Get the filename from the command line
cmd$ = cmd$ + " "
firstSpace% = INSTR(cmd$, " ")
fileName$ = LEFT$(cmd$, firstSpace% - 1)
' Grab the rest of the command line as the cipher key
key$ = LTRIM$(MID$(cmd$, firstSpace% + 1))
' Prepare the pseudorandom numbers using the key for shuffling
RandShuffle key$
' Open up the file
OPEN fileName$ FOR BINARY AS #1
fileLength& = LOF(1)
' Process the file in manageable pieces
a$ = SPACE$(BYTES)
count% = fileLength& \ BYTES
' Loop through the file
FOR i% = 0 TO count%
j& = i% * BYTES + 1
IF i% = count% THEN
a$ = SPACE$(fileLength& - BYTES * count%)
END IF
GET #1, j&, a$
ProcesX a$
PUT #1, j&, a$
NEXT i%
' All done
SYSTEM
──────────────────────────────────────────────────────────────────────────
Function: NewWord$
Creates a pseudorandom, new "word" by randomly selecting appropriate
consonants and vowels to form one to three syllables. These words can be
useful as passwords, cipher keys, or new product names.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: NewWord$ **
' ** Type: Function **
' ** Module: CIPHER.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a pseudorandom word of a possibly
' speakable form.
'
' EXAMPLE OF USE: PRINT NewWord$
' PARAMETERS: (none)
' VARIABLES: vowel$ String constant listing the set of vowels
' consonant$ String constant listing the set of consonant
' syllables% Random number of syllables for the new word
' i% Loop index for creating each syllable
' t$ Temporary work string for forming the new wo
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION NewWord$ ()
'
FUNCTION NewWord$ STATIC
CONST vowel$ = "aeiou"
CONST consonant$ = "bcdfghjklmnpqrstvwxyz"
syllables% = Rand& MOD 3 + 1
FOR i% = 1 TO syllables%
t$ = t$ + MID$(consonant$, RandInteger%(1, 21), 1)
IF i% = 1 THEN
t$ = UCASE$(t$)
END IF
t$ = t$ + MID$(vowel$, RandInteger%(1, 5), 1)
NEXT i%
IF Rand& MOD 2 THEN
t$ = t$ + MID$(consonant$, RandInteger%(1, 21), 1)
END IF
NewWord$ = t$
t$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: ProcesX
Enciphers a string by XORing the bytes with a sequence of pseudorandom
bytes. The bytes are generated as pseudorandom integers in the range 0
through 255 by the RandInteger% function.
If you initialize the random number generator with the same sequence and
process the ciphered string a second time with this subprogram, the
original string will result. The CIPHER program allows deciphering in this
way by simply requiring that the ciphered file be "ciphered" a second time
with the same key.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ProcesX **
' ** Type: Subprogram **
' ** Module: CIPHER.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Enciphers a string by XORing with pseudorandom bytes.
'
' EXAMPLE OF USE: ProcesX a$
' PARAMETERS: a$ String to be ciphered
' VARIABLES: i% Index into the string
' byte% Numeric value of each string character
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB ProcesX (a$)
'
SUB ProcesX (a$) STATIC
FOR i% = 1 TO LEN(a$)
byte% = ASC(MID$(a$, i%, 1)) XOR RandInteger%(0, 255)
MID$(a$, i%, 1) = CHR$(byte%)
NEXT i%
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
COLORS
The COLORS program provides a handy utility for interactively selecting
colors from the 262,144 available in the VGA and MCGA graphics modes. To
run this program, you must have a mouse and VGA or MCGA graphics
capability.
The program is easy to use. Simply click on any of the three color bars to
set the intensity of that color. The ellipse on the left side of the
screen shows the color shade you selected, and the long integer value at
the top of the screen shows the numeric value to use with the PALETTE
statement for setting this same color in other programs. When you're ready
to quit, click on the X at the lower left corner of the screen.
You can run this program from the QuickBASIC environment, but to make it
an easily accessible utility, it's probably better to compile it and
create a stand-alone .EXE program module.
Name Type Description
──────────────────────────────────────────────────────────────────────────
COLORS.BAS Program module
Shade& Func Color value from given red, green, and
blue
──────────────────────────────────────────────────────────────────────────
Program Module: COLORS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: COLORS **
' ** Type: Program **
' ** Module: COLORS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Provides interactive selection of a color shade.
'
' USAGE: No command line parameters
' REQUIREMENTS: VGA or MCGA
' MIXED.QLB/.LIB
' Mouse
' .MAK FILE: COLORS.BAS
' BITS.BAS
' MOUSSUBS.BAS
' PARAMETERS: (none)
' VARIABLES: red! Intensity of red, from 0 to 1
' green! Intensity of green, from 0 to 1
' blue! Intensity of blue, from 0 to 1
' mask$ Mouse graphics cursor definition strin
' xHot% Mouse cursor hot spot X location
' yHot% Mouse cursor hot spot Y location
' cursor$ Mouse cursor binary definition string
' fill% Color bar height calculation
' x% Color bar horizontal left edge
' x2% Color bar horizontal right edge
' y% Color bar vertical top edge
' y2% Color bar vertical bottom edge
' leftButton% State of left mouse button
' rightButton% State of right mouse button
' xMouse% Horizontal mouse location
' yMouse% Vertical mouse location
' clickFlag% Toggle for left mouse button state
' xM% Modified mouse horizontal location
' quitFlag% Signal to end program
' Logical constants
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Constants
CONST REDPAL = 1
CONST BLUEPAL = 2
CONST GREENPAL = 3
CONST TESTPAL = 4
CONST WHITEPAL = 5
CONST BARPAL = 6
CONST DX = 15
CONST DY = 150
CONST RX = 180
CONST RY = 30
CONST GX = RX + DX + DX
CONST GY = RY
CONST BX = GX + DX + DX
CONST BY = RY
' Functions
DECLARE FUNCTION Shade& (red!, green!, blue!)
' Subprograms
DECLARE SUB MouseHide ()
DECLARE SUB MouseMaskTranslate (mask$, xHot%, yHot%, cursor$)
DECLARE SUB MouseSetGcursor (cursor$)
DECLARE SUB MouseShow ()
DECLARE SUB Cursleft (mask$, xHot%, yHot%)
DECLARE SUB MouseNow (leftButton%, rightButton%, xMouse%, yMouse%)
' Set 256 color mode
SCREEN 13
' Set first three colors as pure red, green, blue
PALETTE REDPAL, Shade&(1!, 0!, 0!)
PALETTE GREENPAL, Shade&(0!, 1!, 0!)
PALETTE BLUEPAL, Shade&(0!, 0!, 1!)
' Set a pure white color choice
PALETTE WHITEPAL, Shade&(1!, 1!, 1!)
' Set bar background color
PALETTE BARPAL, Shade&(0!, 0!, 0!)
' Set background to light gray
PALETTE 0, Shade&(.4, .4, .4)
' Start each intensity at midscale
red! = .5
green! = .5
blue! = .5
' Set starting shade
PALETTE TESTPAL, Shade&(red!, green!, blue!)
' Create ellipse of circle to show current shade selected
CIRCLE (70, 100), 80, TESTPAL, , , 1.4
PAINT (70, 100), TESTPAL
' Create the three color bars
LINE (RX, RY)-(RX + DX, RY + DY), WHITEPAL, B
LINE (GX, GY)-(GX + DX, GY + DY), WHITEPAL, B
LINE (BX, BY)-(BX + DX, BY + DY), WHITEPAL, B
' Mark place to quit by clicking
LOCATE 25, 1
PRINT "(X) "; CHR$(27); " Quit";
' Make the left arrow mouse cursor
Cursleft mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, cursor$
MouseSetGcursor cursor$
' Main loop
DO
' Put title and current shade number at top
LOCATE 1, 1
PRINT "COLOR CHOOSER"; TAB(22);
PRINT USING "##########"; Shade&(red!, green!, blue!)
' Fill in the red color bar
fill% = red! * (DY - 3) + 1
x% = RX + 1
x2% = RX + DX
y% = RY + 1
y2% = RY + DY
LINE (x%, y%)-(x2% - 1, y2% - fill% - 1), BARPAL, BF
LINE (x%, y2% - fill%)-(x2% - 1, y2% - 1), REDPAL, BF
' Fill in the green color bar
fill% = green! * (DY - 3) + 1
x% = GX + 1
x2% = GX + DX
y% = GY + 1
y2% = GY + DY
LINE (x%, y%)-(x2% - 1, y2% - fill% - 1), BARPAL, BF
LINE (x%, y2% - fill%)-(x2% - 1, y2% - 1), GREENPAL, BF
' Fill in the blue color bar
fill% = blue! * (DY - 3) + 1
x% = BX + 1
x2% = BX + DX
y% = BY + 1
y2% = BY + DY
LINE (x%, y%)-(x2% - 1, y2% - fill% - 1), BARPAL, BF
LINE (x%, y2% - fill%)-(x2% - 1, y2% - 1), BLUEPAL, BF
' Change the shade of the ellipse
PALETTE TESTPAL, Shade&(red!, green!, blue!)
' Refresh mouse cursor
MouseShow
' Wait for fresh mouse left button click
DO
MouseNow leftButton%, rightButton%, xMouse%, yMouse%
IF leftButton% = FALSE THEN
clickFlag% = FALSE
END IF
IF clickFlag% THEN
leftButton% = 0
END IF
LOOP UNTIL leftButton%
' Hide mouse and set parameters
MouseHide
clickFlag% = TRUE
xM% = xMouse% \ 2
' Is mouse in the "Quit" area?
IF xMouse% < 45 AND yMouse% > 190 THEN
quitFlag% = TRUE
END IF
' Is mouse at the right height to be in a bar?
IF yMouse% > RY - 2 AND yMouse% < RY + DY + 2 THEN
' Is mouse in the red bar?
IF xM% > RX AND xM% < RX + DX THEN
red! = 1! - (yMouse% - RY) / DY
IF red! < 0 THEN
red! = 0
ELSEIF red! > 1 THEN
red! = 1
END IF
END IF
' Is mouse in the green bar?
IF xM% > GX AND xM% < GX + DX THEN
green! = 1! - (yMouse% - RY) / DY
IF green! < 0 THEN
green! = 0
ELSEIF green! > 1 THEN
green! = 1
END IF
END IF
' Is mouse in the blue bar?
IF xM% > BX AND xM% < BX + DX THEN
blue! = 1! - (yMouse% - RY) / DY
IF blue! < 0 THEN
blue! = 0
ELSEIF blue! > 1 THEN
blue! = 1
END IF
END IF
END IF
LOOP UNTIL quitFlag%
SCREEN 0
WIDTH 80
CLS
END
──────────────────────────────────────────────────────────────────────────
Function: Shade&
Returns the long integer number for a given shade of color.
This is the only function the COLORS utility provides, but it's useful
when programming the VGA and MCGA SCREEN modes 11, 12, and 13. You can use
the long integer value returned in a PALETTE statement for setting a color
attribute to one of 262,144 color choices.
Three single-precision numbers are passed to this routine, representing
the desired intensities of the red, green, and blue colors. These numbers
must be in the range 0.0 through 1.0. Shade&(1!, 0!, 0!), for example,
returns the long integer value for bright red; Shade&(.5, .5, .5) returns
a number for medium gray.
The best way to see the results of setting the three colors to various
intensity levels is by running the COLORS program.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Shade& **
' ** Type: Function **
' ** Module: COLORS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the long integer color number given red,
' green, and blue intensity numbers in the range
' 0 through 1.
'
' EXAMPLE OF USE: PALETTE 1, Shade&(red!, green!, blue!)
' PARAMETERS: red! Intensity of red, from 0 to 1
' green! Intensity of green, from 0 to 1
' blue! Intensity of blue, from 0 to 1
' VARIABLES: r& Red amount
' g& Green amount
' b& Blue amount
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Shade& (red!, green!, blue!)
'
FUNCTION Shade& (red!, green!, blue!) STATIC
r& = red! * 63!
g& = green! * 63!
b& = blue! * 63!
Shade& = r& + g& * 256& + b& * 65536
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
COMPLEX
The COMPLEX toolbox provides a collection of subprograms for working with
complex numbers. The QuickBASIC TYPE definition statement is ideal for
declaring variables to be of type Complex, as shown. The variables a, b,
and c each comprise a pair of single-precision numbers representing the
real and imaginary parts of a complex number. These variables are passed
to and from the subprograms as easily as if they were simple numeric
values.
Complex numbers are expressed as the sum of a real and an imaginary
number. Usually you show a complex number by writing the real number,
followed immediately by a plus or minus sign, the imaginary number, and a
small letter "i" or "j," which represents the square root of -1 and
indicates the imaginary numeric component of the complex number.
In this program, complex numbers are entered and displayed in a similar
format. You use parentheses to surround each complex number. This sample
run of COMPLEX shows typical input and output complex-number formats:
Enter first complex number ? (4-5i)
(4-5i)
ComplexExp (15.48743+52.35549i)
ComplexLog 1.856786-.8960554i)
ComplexReciprocal 9.756097E-02+.1219512i)
ComplexSqr 2.280693-1.096158i)
Enter second complex number ? (3+4i)
(3+4i)
ComplexAdd (7-1i)
ComplexSub (1-9i)
ComplexMul (32+1i)
ComplexDiv (-.32-1.24i)
ComplexPower (251.4394-9454.315i)
ComplexRoot (.9952651-.4262131i)
Press any key to continue
Name Type Description
──────────────────────────────────────────────────────────────────────────
COMPLEX.BAS Demo module
Complex2String Sub String representation of a complex number
ComplexAdd Sub Adds two complex numbers
ComplexDiv Sub Divides two complex numbers
ComplexExp Sub Exponential function of a complex number
ComplexLog Sub Natural log of a complex number
ComplexMul Sub Multiplies two complex numbers
ComplexPower Sub Complex number raised to a complex number
ComplexReciprocal Sub Reciprocal of a complex number
ComplexRoot Sub Complex root of a complex number
ComplexSqr Sub Square root of a complex number
ComplexSub Sub Subtracts two complex numbers
String2Complex Sub Converts string to complex variable
──────────────────────────────────────────────────────────────────────────
Demo Module: COMPLEX
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: COMPLEX **
' ** Type: Toolbox **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates a set of complex number functions and
' subprograms.
'
' USAGE: No command line parameters
' .MAK FILE: COMPLEX.BAS
' CARTESIA.BAS
' PARAMETERS: (none)
' VARIABLES: a Variable of type Complex
' b Variable of type Complex
' c Variable of type Complex
' x$ String representation of a complex number
' y$ String representation of a complex number
' z$ String representation of a complex number
TYPE Complex
r AS SINGLE
i AS SINGLE
END TYPE
' Subprograms
DECLARE SUB ComplexSub (a AS Complex, b AS Complex, c AS Complex)
DECLARE SUB ComplexSqr (a AS Complex, c AS Complex)
DECLARE SUB ComplexRoot (a AS Complex, b AS Complex, c AS Complex)
DECLARE SUB ComplexReciprocal (a AS Complex, c AS Complex)
DECLARE SUB ComplexAdd (a AS Complex, b AS Complex, c AS Complex)
DECLARE SUB ComplexLog (a AS Complex, c AS Complex)
DECLARE SUB ComplexPower (a AS Complex, b AS Complex, c AS Complex)
DECLARE SUB Complex2String (a AS Complex, x$)
DECLARE SUB String2Complex (x$, a AS Complex)
DECLARE SUB ComplexDiv (a AS Complex, b AS Complex, c AS Complex)
DECLARE SUB ComplexExp (a AS Complex, c AS Complex)
DECLARE SUB ComplexMul (a AS Complex, b AS Complex, c AS Complex)
DECLARE SUB Rec2pol (x!, y!, r!, theta!)
DIM a AS Complex, b AS Complex, c AS Complex
CLS
INPUT "Enter first complex number "; x$
String2Complex x$, a
Complex2String a, x$
PRINT x$
PRINT
ComplexExp a, c
Complex2String c, z$
PRINT "ComplexExp", , z$
ComplexLog a, c
Complex2String c, z$
PRINT "ComplexLog", , z$
ComplexReciprocal a, c
Complex2String c, z$
PRINT "ComplexReciprocal", z$
ComplexSqr a, c
Complex2String c, z$
PRINT "ComplexSqr", , z$
PRINT
INPUT "Enter second complex number "; y$
String2Complex y$, b
Complex2String b, y$
PRINT y$
PRINT
ComplexAdd a, b, c
Complex2String c, z$
PRINT "ComplexAdd", , z$
ComplexSub a, b, c
Complex2String c, z$
PRINT "ComplexSub", , z$
ComplexMul a, b, c
Complex2String c, z$
PRINT "ComplexMul", , z$
ComplexDiv a, b, c
Complex2String c, z$
PRINT "ComplexDiv", , z$
ComplexPower a, b, c
Complex2String c, z$
PRINT "ComplexPower", , z$
ComplexRoot a, b, c
Complex2String c, z$
PRINT "ComplexRoot", , z$
──────────────────────────────────────────────────────────────────────────
Subprogram: Complex2String
Creates a string representation of a complex number suitable for printing
or displaying the results of complex number calculations. The string
consists of two numbers enclosed in parentheses and separated by either a
plus or minus sign, with the second number followed by a lowercase "i" to
indicate the imaginary component. The length of this string result will
vary, depending on the numeric values of the real and imaginary parts.
All results displayed by the demonstrations are formatted using this
subprogram.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Complex2String **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Makes a string representation of a complex number.
'
' EXAMPLE OF USE: Complex2String a, x$
' PARAMETERS: a Complex number variable (type Complex)
' x$ String representation of the complex number
' VARIABLES: r$ Working string, real part
' i$ Working string, imaginary part
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB Complex2String (a AS Complex, x$)
'
SUB Complex2String (a AS Complex, x$) STATIC
' Form the left part of the string
IF a.r < 0 THEN
r$ = "(" + STR$(a.r)
ELSE
r$ = "(" + MID$(STR$(a.r), 2)
END IF
' Form the right part of the string
IF a.i < 0 THEN
i$ = STR$(a.i)
ELSE
i$ = "+" + MID$(STR$(a.i), 2)
END IF
' The whole is more complex than the sum of the parts
x$ = r$ + i$ + "i)"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexAdd
Calculates the sum of two complex numbers. Complex number a is added to
complex number b, and the result is placed in the variable c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexAdd **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Adds two complex numbers.
'
' EXAMPLE OF USE: ComplexAdd a, b, c
' PARAMETERS: a First complex number for the addition
' b Second complex number for the addition
' c Result of the complex number addition
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexAdd (a AS Complex, b AS Complex, c AS Comple
'
SUB ComplexAdd (a AS Complex, b AS Complex, c AS Complex) STATIC
c.r = a.r + b.r
c.i = a.i + b.i
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexDiv
Calculates the result of dividing one complex number by another. The
result of a/b is placed in the variable c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexDiv **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Divides two complex numbers.
'
' EXAMPLE OF USE: ComplexDiv a, b, c
' PARAMETERS: a First complex number for the division
' b Second complex number for the division
' c Result of the complex number division a/b
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexDiv (a AS Complex, b AS Complex, c AS Complex
'
SUB ComplexDiv (a AS Complex, b AS Complex, c AS Complex) STATIC
t! = b.r * b.r + b.i * b.i
c.r = (a.r * b.r + a.i * b.i) / t!
c.i = (a.i * b.r - a.r * b.i) / t!
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexExp
Calculates the exponential function of a complex number a. The result is
placed in the variable c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexExp **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calculates the exponential function of a complex number.
'
' EXAMPLE OF USE: ComplexExp a, c
' PARAMETERS: a Complex number argument
' c Complex result of the calculations
' VARIABLES: t! Temporary working value
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexExp (a AS Complex, c AS Complex)
'
SUB ComplexExp (a AS Complex, c AS Complex) STATIC
t! = EXP(a.r)
c.r = t! * COS(a.i)
c.i = t! * SIN(a.i)
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexLog
Calculates the complex logarithm of a complex number a. The result is
placed in the variable c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexLog **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calculates the log of a complex number.
'
' EXAMPLE OF USE: ComplexLog a, c
' PARAMETERS: a Complex number argument
' c Complex result of the calculations
' VARIABLES: r! Magnitude of complex number a
' theta! Angle of complex number a
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexLog (a AS Complex, c AS Complex)
' DECLARE SUB Rec2pol (x!, y!, r!, theta!)
'
SUB ComplexLog (a AS Complex, c AS Complex) STATIC
CALL Rec2pol(a.r, a.i, r!, theta!)
IF r! <> 0! THEN
c.r = LOG(r!)
c.i = theta!
ELSE
ERROR 5
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexMul
Calculates the product of two complex numbers. Complex variables a and b
are multiplied, and the result is placed in the variable c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexMul **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Multiplies two complex numbers.
'
' EXAMPLE OF USE: ComplexMul a, b, c
' PARAMETERS: a First complex number for the multiplication
' b Second complex number for the multiplicatio
' c Result of the complex number multiplication
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexMul (a AS Complex, b AS Complex, c AS Comple
'
SUB ComplexMul (a AS Complex, b AS Complex, c AS Complex) STATIC
c.r = a.r * b.r - a.i * b.i
c.i = a.r * b.i + a.i * b.r
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexPower
Calculates the result of raising one complex number to the power of
another. The result of raising a to the power of b is then placed in the
variable c.
Notice that this subprogram calls several others. If you extract this
routine for use in another program module, be sure to extract the other
subprograms as well.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexPower **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calculates a complex number raised to a complex number.
'
' EXAMPLE OF USE: ComplexPower a, b, c
' PARAMETERS: a Complex number to be raised to a power
' b Complex number to raise a to
' c Result of a raised to the power of b
' VARIABLES: t1 Structure of type Complex
' t2 Structure of type Complex
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexPower (a AS Complex, b AS Complex, c AS Complex
' DECLARE SUB ComplexExp (a AS Complex, c AS Complex)
' DECLARE SUB ComplexLog (a AS Complex, c AS Complex)
' DECLARE SUB ComplexMul (a AS Complex, b AS Complex, c AS Complex)
'
SUB ComplexPower (a AS Complex, b AS Complex, c AS Complex) STATIC
DIM t1 AS Complex, t2 AS Complex
IF a.r <> 0! OR a.i <> 0! THEN
CALL ComplexLog(a, t1)
CALL ComplexMul(t1, b, t2)
CALL ComplexExp(t2, c)
ELSE
ERROR 5
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexReciprocal
Calculates the reciprocal of a complex number by dividing the complex
number (1+0i) by the complex number a. The result is placed in the
variable c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexReciprocal **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calculates the reciprocal of a complex number.
'
' EXAMPLE OF USE: ComplexReciprocal a, c
' PARAMETERS: a Complex number to be processed
' c Result of calculating 1/a
' VARIABLES: t Structure of type Complex
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexReciprocal (a AS Complex, c AS Complex)
' DECLARE SUB ComplexDiv (a AS Complex, b AS Complex, c AS Comple
'
SUB ComplexReciprocal (a AS Complex, c AS Complex) STATIC
DIM t AS Complex
t.r = 1!
t.i = 0
ComplexDiv t, a, c
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexRoot
Calculates the complex root of a complex number. The ComplexReciprocal
and ComplexPower subprograms are called by this subprogram. These
routines allow the root to be found by raising a to the power of 1/b.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexRoot **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calculates the complex root of a complex number.
'
' EXAMPLE OF USE: ComplexRoot a, b, c
' PARAMETERS: a First complex number
' b Complex number root
' c Result of finding the bth root of a
' VARIABLES: t Structure of type Complex
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexRoot (a AS Complex, b AS Complex, c AS Complex
' DECLARE SUB ComplexReciprocal (a AS Complex, c AS Complex)
' DECLARE SUB ComplexPower (a AS Complex, b AS Complex, c AS Comple
'
SUB ComplexRoot (a AS Complex, b AS Complex, c AS Complex) STATIC
DIM t AS Complex
IF b.r <> 0! OR b.i <> 0! THEN
CALL ComplexReciprocal(b, t)
CALL ComplexPower(a, t, c)
ELSE
ERROR 5
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexSqr
Calculates the complex square root of a complex number. The square root of
a is placed in the variable c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexSqr **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calculates the square root of a complex number.
'
' EXAMPLE OF USE: ComplexSqr a, c
' PARAMETERS: a Complex number argument
' c Result of finding the square root of a
' VARIABLES: r! Magnitude of complex number a
' theta! Angle of complex number a
' rs! Square root of r!
' h! One half of theta!
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexSqr (a AS Complex, c AS Complex)
'
SUB ComplexSqr (a AS Complex, c AS Complex) STATIC
CALL Rec2pol(a.r, a.i, r!, theta!)
rs! = SQR(r!)
h! = theta! / 2!
c.r = rs! * COS(h!)
c.i = rs! * SIN(h!)
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ComplexSub
Calculates the difference between two complex numbers. The result of
subtracting b from a is returned in the variable c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ComplexSub **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Subtracts two complex numbers.
'
' EXAMPLE OF USE: ComplexSub a, b, c
' PARAMETERS: a First complex number
' b Second Complex number
' c Result of subtracting b from a
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB ComplexSub (a AS Complex, b AS Complex, c AS Comple
'
SUB ComplexSub (a AS Complex, b AS Complex, c AS Complex) STATIC
c.r = a.r - b.r
c.i = a.i - b.i
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: String2Complex
Converts a string representation of a complex number to a complex number
variable of type Complex. This routine is useful for converting user input
of a complex number to a complex variable.
In general, the string should be in the same format as that produced by
the Complex2String function. However, there is some flexibility to allow
for variations in the way a user might type in complex numbers. For
example, the "i" character indicates the imaginary part of a complex
number, but a "j" will also be recognized. Also, parentheses around the
numbers are optional.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: String2Complex **
' ** Type: Subprogram **
' ** Module: COMPLEX.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Converts a string representation of a complex
' number to a type Complex variable.
'
' EXAMPLE OF USE: String2Complex x$, a
' PARAMETERS: x$ String representation of a complex number
' a Complex number structure of type Complex
' VARIABLES: j% Index to first numerical character
' i% Pointer to the "i" or "j" character
' k% Pointer to start of imaginary part
' MODULE LEVEL
' DECLARATIONS: TYPE Complex
' r AS SINGLE
' i AS SINGLE
' END TYPE
'
' DECLARE SUB Complex2String (a AS Complex, x$)
'
SUB String2Complex (x$, a AS Complex) STATIC
' Real part starts just after left parenthesis
j% = INSTR(x$, "(") + 1
' Step forward to find start of number
DO UNTIL INSTR("+-0123456789", MID$(x$, j%, 1)) OR j% > LEN(x$)
j% = j% + 1
LOOP
' Imaginary part ends at the "i" or "j"
i% = INSTR(LCASE$(x$), "i")
IF INSTR(LCASE$(x$), "j") > i% THEN
i% = INSTR(LCASE$(x$), "j")
END IF
' Step back to find start of imaginary part
FOR k% = i% TO 1 STEP -1
IF INSTR("+-", MID$(x$, k%, 1)) THEN
EXIT FOR
END IF
NEXT k%
' Error if pointers don't make sense
IF j% = 0 OR j% > LEN(x$) THEN
PRINT "Error: String2Complex - unrecognizable string format"
SYSTEM
END IF
' Grab the real part
a.r = VAL(MID$(x$, j%))
' Grab the imaginary part
IF k% > j% THEN
a.i = VAL(MID$(x$, k%))
ELSEIF k% = j% THEN
a.r = 0
a.i = VAL(MID$(x$, j%))
ELSE
a.i = 0
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
DOLLARS
The DOLLARS toolbox contains three functions for working with monetary
amounts.
QuickBASIC provides a way for you to put commas between groups of three
digits in numbers as they're printed or displayed. However, the Comma$
and DollarString$ functions have the added advantage of allowing you to
manipulate the string results further before outputting the results.
The Round# function is presented here as a means of rounding dollar
amounts to the nearest cent, but you can also use it for scientific and
engineering calculations when you want to round numbers to a given number
of decimal places.
Name Type Description
──────────────────────────────────────────────────────────────────────────
DOLLARS.BAS Demo module
Comma$ Func Double-precision with commas inserted
DollarString$ Func Dollar representation rounded with commas
Round# Func Rounding at specified decimal place
──────────────────────────────────────────────────────────────────────────
Demo Module: DOLLARS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DOLLARS **
' ** Type: Toolbox **
' ** Module: DOLLARS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: n# Number for demonstration of the functions
DECLARE FUNCTION Comma$ (n#)
DECLARE FUNCTION DollarString$ (amount#, length%)
DECLARE FUNCTION Round# (n#, place%)
CLS
n# = 1234567.76543#
PRINT "Number n#:", , n#
PRINT "Comma$(n#)", , Comma$(n#)
PRINT "Comma$(Round#(n#, -2))", Comma$(Round#(n#, -2))
PRINT
PRINT "DollarString$(n#, 20)", ":"; DollarString$(n#, 20); ":"
PRINT , , " 12345678901234567890"
PRINT
PRINT "Round#(n#, -3)", Round#(n#, -3)
PRINT "Round#(n#, -2)", Round#(n#, -2)
PRINT "Round#(n#, -1)", Round#(n#, -1)
PRINT "Round#(n#, 0)", , Round#(n#, 0)
PRINT "Round#(n#, 1)", , Round#(n#, 1)
PRINT "Round#(n#, 2)", , Round#(n#, 2)
──────────────────────────────────────────────────────────────────────────
Function: Comma$
Returns a string representation of a given double-precision number, with
commas separating groups of three digits to the left of the decimal point.
The returned string is the same as that returned by the QuickBASIC STR$
function, except for the addition of the commas.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Comma$ **
' ** Type: Function **
' ** Module: DOLLARS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Creates a string representing a double-precision
' number, with commas inserted every three digits.
'
' EXAMPLE OF USE: n$ = Comma$(n#)
' PARAMETERS: n# Number to be formatted
' VARIABLES: tn$ Temporary string of the number
' dp% Position of the decimal point
' i% Index into tn$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Comma$ (n#)
'
FUNCTION Comma$ (n#) STATIC
tn$ = STR$(n#)
dp% = INSTR(tn$, ".")
IF dp% = 0 THEN
dp% = LEN(tn$) + 1
END IF
IF dp% > 4 THEN
FOR i% = dp% - 3 TO 3 STEP -3
tn$ = LEFT$(tn$, i% - 1) + "," + MID$(tn$, i%)
NEXT i%
END IF
Comma$ = LTRIM$(tn$)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: DollarString$
Returns a string representation of a dollar amount, as passed in a
double-precision variable. The Round# function rounds the number to the
nearest penny, and the Comma$ function separates each group of three
digits to the left of the decimal point with commas. The string is then
padded on the left with spaces until the desired string length is
achieved, and a dollar sign is placed to the left of the spaces. Thus, you
can conveniently display the dollar amounts in columns.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DollarString$ **
' ** Type: Function **
' ** Module: DOLLARS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a string representation of a dollar amount,
' rounded to the nearest cent, with commas separating
' groups of three digits, and with a preceding dollar sign.
'
' EXAMPLE OF USE: d$ = DollarString$(dollars#)
' PARAMETERS: dollars# Amount of money
' VARIABLES: tmp$ Temporary working string
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Comma$ (n#)
' DECLARE FUNCTION DollarString$ (amount#, length%)
' DECLARE FUNCTION Round# (n#, place%)
'
FUNCTION DollarString$ (amount#, length%) STATIC
tmp$ = SPACE$(length%) + "$" + Comma$(Round#(amount#, -2))
DollarString$ = RIGHT$(tmp$, length%)
tmp$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Round#
Rounds numbers to any decimal position, as specified by the passed power
of ten rounding value. For example, to round pi to the nearest integer
value, you would use Round#(3.1416#, 0); to round 2/3 of ten dollars to
the nearest cent, Round#(6.6666667#, -2); and finally, to round the
distance to the moon to the nearest thousand miles, Round#(238857#, 3).
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Round# **
' ** Type: Function **
' ** Module: DOLLARS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Rounds a number at the power of 10 decimal place.
'
' EXAMPLE OF USE: x# = Round#(n#, place%)
' EXAMPLES: Round#(12.3456#, -2) = 12.35#
' Round#(12.3456#, -1) = 12.3#
' Round#(12.3456#, 0) = 12#
' Round#(12.3456#, 1) = 10#
' PARAMETERS: n# Number to be rounded
' place% Power of 10 for rounding the number
' VARIABLES: pTen# 10 raised to the indicated power of 10
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Round# (n#, place%)
'
FUNCTION Round# (n#, powerOfTen%) STATIC
pTen# = 10# ^ powerOfTen%
Round# = INT(n# / pTen# + .5#) * pTen#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
DOSCALLS
These routines use the Interrupt and InterruptX subprograms, provided as
part of the QuickBASIC package, to access the operating system through
software interrupts. The information returned by these functions and
subprograms is extensive and useful.
The DOSCALLS demo module proceeds in this way:
The BufferedKeyInput$ function prompts you to enter up to nine
characters. The appearance of the prompt and the input action from the
keyboard seem very similar to the QuickBASIC INPUT statement, but there
are some fundamental differences.
Next, the DOSVersion! function returns the MS-DOS version number.
The SetDrive subprogram temporarily switches the current drive, and the
GetDrive$ function displays the results.
The GetMediaDescriptor subprogram returns several useful pieces of
information about the current disk drive.
The Verify state is normally set using the MS-DOS VERIFY command, but with
the GetVerifyState% function and the SetVerifyState subprogram, your
programs can now control this setting directly.
The GetDiskFreeSpace subprogram returns five useful details about the
structure of the data and free space on any disk drive in your system.
The GetCountry subprogram returns a data structure filled with details
that enable you to modify your program outputs for use in other countries.
Also returned is the address of the MS-DOS character translation
subroutine called CaseMap, which translates certain characters for some
foreign languages.
The TranslateCountry$ function uses the address returned by the
GetCountry subprogram to translate a string of characters for the
currently set country.
The GetDirectory$ function and SetDirectory subprogram let you determine
or set the current directory-path string.
The WriteToDevice subprogram is useful for outputting strings directly to
the indicated device, using the MS-DOS output handler rather than
QuickBASIC's. One advantage of this approach is in being able to use the
ANSI.SYS escape-code sequences to control cursor movement, screen mode,
and color attributes.
Finally, the GetFileAttributes and SetFileAttributes subprograms let you
determine or change the file attribute bits as desired. With these
routines, it's easy to hide or unhide files and to set or clear the
archive bit for use by the MS-DOS XCOPY command.
Name Type Description
──────────────────────────────────────────────────────────────────────────
DOSCALLS.BAS Demo module
BufferedKeyInput$ Func ASCII string of specified length
DOSVersion! Func Version number of MS-DOS returned
GetCountry Sub Current country setting
GetDirectory$ Func Path to disk directory specified
GetDiskFreeSpace Sub Disk space format and usage for input
drive
GetDrive$ Func Current drive string
GetFileAttributes Sub Attribute bits for given file
GetMediaDescriptor Sub Drive information for system
GetVerifyState% Func Verify setting (state)
SetDirectory Sub Sets current directory
SetDrive Sub Sets current disk drive
SetFileAttributes Sub Sets the attribute bits for a given file
SetVerifyState Sub Sets or clears verify state (writing to
file)
TranslateCountry$ Func Translates string──current country
setting
WriteToDevice Sub Outputs a string to a device
──────────────────────────────────────────────────────────────────────────
Demo Module: DOSCALLS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DOSCALLS **
' ** Type: Toolbox **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates several interrupt calls to MS-DOS.
'
' USAGE: No command line parameters
' REQUIREMENTS: MS-DOS 3.0 or later
' MIXED.QLB/.LIB
'.MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: buffer$ String for buffered input demonstration
' x$ Buffered input string
' drive$ Current disk drive name
' desc Structure of type MediaDescriptorType
' state% Current status of the Verify state
' oppositeState% Opposite state for Verify
' disk Structure of type DiskFreeSpaceType
' country Structure of type CountryType
' i% Loop index for creating translation characte
' a$ Characters to be translated
' path$ Current directory
' result% Result code from call to SetDirectory
' t$ Temporary copy of TIME$
' attr Structure of type FileAttributesType
' fileName$ Name of file for determining file attributes
TYPE RegType
ax AS INTEGER
bx AS INTEGER
cx AS INTEGER
dx AS INTEGER
bp AS INTEGER
si AS INTEGER
di AS INTEGER
flags AS INTEGER
END TYPE
TYPE RegTypeX
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
TYPE MediaDescriptorType
sectorsPerAllocationUnit AS INTEGER
bytesPerSector AS INTEGER
FATIdentificationByte AS INTEGER
END TYPE
TYPE DiskFreeSpaceType
sectorsPerCluster AS INTEGER
bytesPerSector AS INTEGER
clustersPerDrive AS LONG
availableClusters AS LONG
availableBytes AS LONG
END TYPE
TYPE CountryType
dateTimeFormat AS STRING * 11
currencySymbol AS STRING * 4
thousandsSeparator AS STRING * 1
decimalSeparator AS STRING * 1
dateSeparator AS STRING * 1
timeSeparator AS STRING * 1
currencyThenSymbol AS INTEGER
currencySymbolSpace AS INTEGER
currencyPlaces AS INTEGER
hours24 AS INTEGER
caseMapSegment AS INTEGER
caseMapOffset AS INTEGER
dataListSeparator AS STRING * 1
END TYPE
TYPE FileAttributesType
readOnly AS INTEGER
hidden AS INTEGER
systemFile AS INTEGER
archive AS INTEGER
result AS INTEGER
END TYPE
' Subprograms
DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
DECLARE SUB SetDrive (drive$)
DECLARE SUB GetMediaDescriptor (drive$, desc AS MediaDescriptorType)
DECLARE SUB SetVerifyState (state%)
DECLARE SUB GetDiskFreeSpace (drive$, disk AS DiskFreeSpaceType)
DECLARE SUB GetCountry (country AS CountryType)
DECLARE SUB CaseMap (character%, BYVAL Segment%, BYVAL Offset%)
DECLARE SUB SetDirectory (path$, result%)
DECLARE SUB WriteToDevice (handle%, a$, result%)
DECLARE SUB GetFileAttributes (fileName$, attr AS FileAttributesType)
DECLARE SUB SetFileAttributes (fileName$, attr AS FileAttributesType)
' Functions
DECLARE FUNCTION DOSVersion! ()
DECLARE FUNCTION BufferedKeyInput$ (n%)
DECLARE FUNCTION GetDrive$ ()
DECLARE FUNCTION GetVerifyState% ()
DECLARE FUNCTION TranslateCountry$ (a$, country AS CountryType)
DECLARE FUNCTION GetDirectory$ (drive$)
' Try the Buffered Keyboard Input call
CLS
PRINT "BufferedKeyInput$:"
PRINT "Enter a string of up to nine characters... ";
x$ = BufferedKeyInput$(9)
PRINT
PRINT "Here's the nine-character string result... ";
PRINT CHR$(34); x$; CHR$(34)
' Get the MS-DOS version number
PRINT
PRINT "DosVersion!:"
PRINT "DOS Version number is "; DOSVersion!
' Demonstrate the GetDrive and SetDrive routines
PRINT
PRINT "GetDrive$ and SetDrive:"
drive$ = GetDrive$
PRINT "The current drive is "; drive$
PRINT "Setting the current drive to A:"
SetDrive "A:"
PRINT "Now the current drive is "; GetDrive$
PRINT "Setting the current drive back to "; drive$
SetDrive drive$
PRINT "Now the current drive is "; GetDrive$
' Call the MS-DOS "Media Descriptor" function for the current drive
PRINT
PRINT "GetMediaDescriptor"
DIM desc AS MediaDescriptorType
GetMediaDescriptor drive$, desc
PRINT "Drive "; drive$
PRINT "Sectors per allocation unit "; desc.sectorsPerAllocationUnit
PRINT "Bytes per sector "; desc.bytesPerSector
PRINT "FAT identification byte &H"; HEX$(desc.FATIdentificationByt
' Wait for user
PRINT
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
CLS
' Demonstrate the GetVerifyState and SetVerifyState routines
PRINT
PRINT "GetVerifyState% and SetVerifyState:"
state% = GetVerifyState%
PRINT "Current verify state is"; state%
oppositeState% = 1 AND NOT state%
SetVerifyState oppositeState%
PRINT "Now the verify state is"; GetVerifyState%
SetVerifyState state%
PRINT "Now the verify state is"; GetVerifyState%
' Determine free space on the current drive
PRINT
PRINT "GetDiskFreeSpace:"
DIM disk AS DiskFreeSpaceType
GetDiskFreeSpace drive$, disk
PRINT "Sectors per cluster "; disk.sectorsPerCluster
PRINT "Bytes per sector "; disk.bytesPerSector
PRINT "Total clusters on drive "; disk.clustersPerDrive
PRINT "Available clusters "; disk.availableClusters
PRINT "Available bytes "; disk.availableBytes
' Wait for user
PRINT
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
CLS
' Get country-dependent information
PRINT
PRINT "GetCountry:"
DIM country AS CountryType
GetCountry country
PRINT "Date and time format "; country.dateTimeFormat
PRINT "Currency symbol "; country.currencySymbol
PRINT "Thousands separator "; country.thousandsSeparator
PRINT "Decimal separator "; country.decimalSeparator
PRINT "Date separator "; country.dateSeparator
PRINT "Time separator "; country.timeSeparator
PRINT "Currency before symbol "; country.currencyThenSymbol
PRINT "Currency symbol space "; country.currencySymbolSpace
PRINT "Currency decimal places"; country.currencyPlaces
PRINT "24-hour time "; country.hours24
PRINT "Case map segment "; country.caseMapSegment
PRINT "Case map offset "; country.caseMapOffset
PRINT "Data list separator "; country.dataListSeparator
' Let's translate lowercase characters for the current country
PRINT
PRINT "TranslateCountry$:"
FOR i% = 128 TO 175
a$ = a$ + CHR$(i%)
NEXT i%
PRINT "Character codes 128 to 175, before and after translation... "
PRINT a$
PRINT TranslateCountry$(a$, country)
' Wait for user
PRINT
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
CLS
' Demonstrate the SetDirectory and GetDirectory routines
PRINT
PRINT "GetDirectory$ and SetDirectory:"
path$ = GetDirectory$(drive$)
PRINT "Current directory is "; path$
SetDirectory GetDrive$ + "\", result%
PRINT "Now the directory is "; GetDirectory$(drive$)
SetDirectory path$, result%
PRINT "Now the directory is "; GetDirectory$(drive$)
' Write to a file or device
PRINT
PRINT "WriteToDevice:"
PRINT "Writing a 'bell' character to the CRT"
WriteToDevice 1, CHR$(7), result%
t$ = TIME$
DO
LOOP UNTIL t$ <> TIME$
PRINT "Writing a 'bell' character to the printer"
WriteToDevice 4, CHR$(7), result%
' Wait for user
PRINT
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
CLS
' Demonstrate the GetFileAttributes and SetFileAttributes routines
PRINT
PRINT "GetFileAttributes and SetFileAttributes:"
DIM attr AS FileAttributesType
fileName$ = "C:\IBMDOS.COM"
GetFileAttributes fileName$, attr
PRINT "File attributes for "; fileName$
PRINT "Result of call "; attr.result
PRINT "Read only "; attr.readOnly
PRINT "Hidden "; attr.hidden
PRINT "System "; attr.systemFile
PRINT "Archive "; attr.archive
PRINT
attr.hidden = 0
SetFileAttributes fileName$, attr
GetFileAttributes fileName$, attr
PRINT "File attributes for "; fileName$
PRINT "Result of call "; attr.result
PRINT "Read only "; attr.readOnly
PRINT "Hidden "; attr.hidden
PRINT "System "; attr.systemFile
PRINT "Archive "; attr.archive
PRINT
attr.hidden = 1
SetFileAttributes fileName$, attr
GetFileAttributes fileName$, attr
PRINT "File attributes for "; fileName$
PRINT "Result of call "; attr.result
PRINT "Read only "; attr.readOnly
PRINT "Hidden "; attr.hidden
PRINT "System "; attr.systemFile
PRINT "Archive "; attr.archive
PRINT
──────────────────────────────────────────────────────────────────────────
Function: BufferedKeyInput$
Calls the MS-DOS Buffered Keyboard Input routine, which is similar in
concept to QuickBASIC's LINE INPUT statement but contains some useful
differences.
When you call the BufferedKeyInput$ function, you pass an integer that
tells the MS-DOS routine the maximum number of characters to be input. If
extra characters are typed, the computer beeps, and the keystrokes are
ignored. The Backspace and Left arrow keys allow editing of the input, and
the screen is constantly updated to display the input buffer at the
current cursor location.
The returned string is always n% characters in length, even if the user
entered fewer than n% characters. If necessary, the string is padded on
the right with spaces to bring the length up to n%.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BufferedKeyInput$ **
' ** Type: Function **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calls the "Buffered Keyboard Input" MS-DOS function
' and returns the entered string of characters.
'
' EXAMPLE OF USE: x$ = BufferedKeyInput$(n%)
' PARAMETERS: buffer$ Buffer for keyboard input
' VARIABLES: regX Structure of type RegTypeX
' bufSize% Length of buffer$
' b$ Working copy of buffer$
' count% Count of characters entered
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' 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
'
' DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE FUNCTION BufferedKeyInput$ (n%)
'
FUNCTION BufferedKeyInput$ (n%) STATIC
DIM regX AS RegTypeX
b$ = CHR$(n% + 1) + SPACE$(n% + 1)
regX.ax = &HA00
regX.ds = VARSEG(b$)
regX.dx = SADD(b$)
InterruptX &H21, regX, regX
count% = ASC(MID$(b$, 2, 1))
BufferedKeyInput$ = MID$(b$, 3, count%) + SPACE$(n% - count%)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: DOSVersion!
Returns the version number of MS-DOS. Sometimes it's necessary to know the
current version of MS-DOS before proceeding with certain MS-DOS functions.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DOSVersion! **
' ** Type: Function **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the version number of MS-DOS.
'
' EXAMPLE OF USE: PRINT "MS-DOS Version number is "; DOSVersion!
' PARAMETERS: (none)
' VARIABLES: reg Structure of type RegType
' major% Integer part of the MS-DOS version number
' minor% Fractional part of the MS-DOS version numbe
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE FUNCTION DOSVersion! ()
'
FUNCTION DOSVersion! STATIC
DIM reg AS RegType
reg.ax = &H3000
Interrupt &H21, reg, reg
major% = reg.ax MOD 256
minor% = reg.ax \ 256
DOSVersion! = major% + minor% / 100!
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: GetCountry
Returns information from MS-DOS about the current country settings. This
information can be invaluable for programs slated to be marketed in more
than one country. A program's data output can be modified to conform to
the standards of each country. For example, the date 3-4-88 can refer to
March 4th or April 3rd, depending on which part of the world you are in.
The date and time format string indicates the order of the six numeric
values that make up a given date and time.
Several variables are returned to indicate the desirable way to format
monetary values. These determine whether the currency symbol is before or
after the monetary value, whether a space separates the two, and the
number of decimal places to use.
The currency symbol is a four-character string such as "Lira". For
dollars, the string contains three spaces followed by a $.
The thousands separator is a one-character string, usually a decimal point
or a comma.
The decimal separator is also a one-character string, usually a decimal
point or a comma.
The date separator is a one-character string such as "-" or "/".
The time separator is a one-character string such as ":".
The hours designation indicates whether a 24-hour format or an A.M. and
P.M. 12-hour format is more commonly used. The CaseMap address is the
segment and offset address of the MS-DOS character translation function.
Refer to the TranslateCountry$ function to see how this address is used.
(The CaseMap subprogram is discussed in Part III of this book.)
The data list separator is a one-character string such as ",".
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetCountry **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns country-dependent information as defined
' by MS-DOS.
'
' EXAMPLE OF USE: GetCountry country
' PARAMETERS: country Structure of type CountryType
' VARIABLES: regX Structure of type RegTypeX
' c$ Buffer for data returned from interrupt
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' 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
'
' TYPE CountryType
' DateTimeFormat AS STRING * 11
' CurrencySymbol AS STRING * 4
' ThousandsSeparator AS STRING * 1
' DecimalSeparator AS STRING * 1
' DateSeparator AS STRING * 1
' TimeSeparator AS STRING * 1
' CurrencyThenSymbol AS INTEGER
' CurrencySymbolSpace AS INTEGER
' CurrencyPlaces AS INTEGER
' Hours24 AS INTEGER
' caseMapSegment AS INTEGER
' caseMapOffset AS INTEGER
' DataListSeparator AS STRING * 1
' END TYPE
'
' DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE SUB GetCountry (country AS CountryType)
'
SUB GetCountry (country AS CountryType)
DIM regX AS RegTypeX
regX.ax = &H3800
c$ = SPACE$(32)
regX.ds = VARSEG(c$)
regX.dx = SADD(c$)
InterruptX &H21, regX, regX
SELECT CASE CVI(LEFT$(c$, 2))
CASE 0
country.dateTimeFormat = "h:m:s m/d/y"
CASE 1
country.dateTimeFormat = "h:m:s d/m/y"
CASE 2
country.dateTimeFormat = "y/m/d h:m:s"
CASE ELSE
country.dateTimeFormat = "h:m:s m/d/y"
END SELECT
country.currencySymbol = MID$(c$, 3, 4)
country.thousandsSeparator = MID$(c$, 8, 1)
country.decimalSeparator = MID$(c$, 10, 1)
country.dateSeparator = MID$(c$, 12, 1)
country.timeSeparator = MID$(c$, 14, 1)
country.currencyThenSymbol = ASC(MID$(c$, 16)) AND 1
country.currencySymbolSpace = (ASC(MID$(c$, 16)) AND 2) \ 2
country.currencyPlaces = ASC(MID$(c$, 17))
country.hours24 = ASC(MID$(c$, 18))
country.caseMapSegment = CVI(MID$(c$, 21, 2))
country.caseMapOffset = CVI(MID$(c$, 19, 2))
country.dataListSeparator = MID$(c$, 23, 1)
END SUB
──────────────────────────────────────────────────────────────────────────
Function: GetDirectory$
Returns the complete path for any drive on your system. The called MS-DOS
function doesn't return the drive designation or the first slash,
representing the root directory, but the GetDirectory$ function adds these
parts to the returned string for you.
For the current directory of the current, default drive, pass a null
string. For a specific drive, pass a string containing the letter of the
drive in the first character position. For example, GetDirectory$("A:")
might return A:\QB4\SOURCE.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetDirectory$ **
' ** Type: Function **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the name of the current directory for any drive.
'
' EXAMPLE OF USE: path$ = GetDirectory$(drive$)
' PARAMETERS: drive$ Drive of concern, or null string for defaul
' drive
' VARIABLES: regX Structure of type RegTypeX
' d$ Working copy of drive$
' p$ Buffer space for returned path
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' 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
'
' DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE FUNCTION GetDirectory$ (drive$)
'
FUNCTION GetDirectory$ (drive$) STATIC
DIM regX AS RegTypeX
IF drive$ = "" THEN
d$ = GetDrive$
ELSE
d$ = UCASE$(drive$)
END IF
drive% = ASC(d$) - 64
regX.dx = drive%
regX.ax = &H4700
p$ = SPACE$(64)
regX.ds = VARSEG(p$)
regX.si = SADD(p$)
InterruptX &H21, regX, regX
p$ = LEFT$(p$, INSTR(p$, CHR$(0)) - 1)
GetDirectory$ = LEFT$(d$, 1) + ":\" + p$
IF regX.flags AND 1 THEN
GetDirectory$ = ""
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: GetDiskFreeSpace
Returns information about the current usage and format of a given disk
drive's contents. The data structure of type DiskFreeSpaceType lists the
various information returned by this subprogram. Some information, such as
the sectors per cluster, bytes per sector, and total clusters information,
is constant in nature, so the subprogram always returns the same value for
a given drive. The available clusters and available bytes are the variable
information that this subprogram returns.
Probably the most important information this subprogram returns is the
total bytes available. Call this subprogram before creating a large file
on a disk to prevent the program from being interrupted by a "disk full"
message. This lets you prompt the user to insert a different disk or take
other action before any data is lost.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetDiskFreeSpace **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Get information about a drive's organization, including
' total number of bytes available.
'
' EXAMPLE OF USE: GetDiskFreeSpace drive$, disk
' PARAMETERS: drive$ Disk drive designation
' disk Structure of type DiskFreeSpaceType
' VARIABLES: reg Structure of type RegType
' drive% Numeric drive designation
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' TYPE DiskFreeSpaceType
' sectorsPerCluster AS INTEGER
' bytesPerSector AS INTEGER
' clustersPerDrive AS LONG
' availableClusters AS LONG
' availableBytes AS LONG
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE SUB GetDiskFreeSpace (drive$, disk AS DiskFreeSpaceType)
'
SUB GetDiskFreeSpace (drive$, disk AS DiskFreeSpaceType)
DIM reg AS RegType
IF drive$ <> "" THEN
drive% = ASC(UCASE$(drive$)) - 64
ELSE
drive% = 0
END IF
IF drive% >= 0 THEN
reg.dx = drive%
ELSE
reg.dx = 0
END IF
reg.ax = &H3600
Interrupt &H21, reg, reg
disk.sectorsPerCluster = reg.ax
disk.bytesPerSector = reg.cx
IF reg.dx >= 0 THEN
disk.clustersPerDrive = reg.dx
ELSE
disk.clustersPerDrive = reg.dx + 65536
END IF
IF reg.bx >= 0 THEN
disk.availableClusters = reg.bx
ELSE
disk.availableClusters = reg.bx + 65536
END IF
disk.availableBytes = disk.availableClusters * reg.ax * reg.cx
END SUB
──────────────────────────────────────────────────────────────────────────
Function: GetDrive$
Returns a two-character string designation for the current disk drive. The
first character is always an uppercase letter, and the second character is
always a colon.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetDrive$ **
' ** Type: Function **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the current disk drive name, such as "A:".
'
' EXAMPLE OF USE: drive$ = GetDrive$
' PARAMETERS: (none)
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegType
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE FUNCTION GetDrive$ ()
'
FUNCTION GetDrive$ STATIC
DIM reg AS RegType
reg.ax = &H1900
Interrupt &H21, reg, reg
GetDrive$ = CHR$((reg.ax AND &HFF) + 65) + ":"
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: GetFileAttributes
Returns current attribute bits for a given file. Each file has several
attribute bits that serve useful purposes in MS-DOS. For example, whenever
a change is made to a file, the "archive" bit is set. The MS-DOS XCOPY
utility can check the setting of this bit and copy only those files that
have been modified since the last XCOPY command was given for the same set
of files. XCOPY clears this bit when a file is copied.
The "read only" attribute bit protects a file by preventing you from
changing or deleting its contents. You can read the file, list it, or
access it in any normal way, but the operating system will generate an
error if you try to edit or delete it.
The "hidden" attribute bit makes a file invisible to the user. A good
example of this bit's action is shown by the module-level code that
demonstrates this subprogram. The hidden file IBMDOS.COM has its "hidden"
bit cleared and then reset. If you leave this bit cleared, the IBMDOS.COM
file will show up in your root directory whenever you give the DIR
command.
DOSCALLS
The "system" attribute bit marks files such as IBMBIO.COM and IBMDOS.COM
as special system files. These two files are in the root directory of all
your bootable disks and are necessary for MS-DOS to be able to
successfully boot from a given disk.
The variable attr.result returns a 0 if the attempt to read the file
attribute bits was successful.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetFileAttributes **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the file attribute settings for a file.
'
' EXAMPLE OF USE: GetFileAttributes fileName$, attr
' PARAMETERS: fileName$ Name of file
' attr Structure of type FileAttributesType
' VARIABLES: regX Structure of type RegTypeX
' f$ Null terminated copy of fileName$
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' 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
'
' TYPE FileAttributesType
' readOnly AS INTEGER
' hidden AS INTEGER
' systemFile AS INTEGER
' archive AS INTEGER
' result AS INTEGER
' END TYPE
'
' DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE SUB GetFileAttributes (fileName$, attr AS FileAttributesType)
'
SUB GetFileAttributes (fileName$, attr AS FileAttributesType) STATIC
DIM regX AS RegTypeX
regX.ax = &H4300
f$ = fileName$ + CHR$(0)
regX.ds = VARSEG(f$)
regX.dx = SADD(f$)
InterruptX &H21, regX, regX
IF regX.flags AND 1 THEN
attr.result = regX.ax
ELSE
attr.result = 0
END IF
attr.readOnly = regX.cx AND 1
attr.hidden = (regX.cx \ 2) AND 1
attr.systemFile = (regX.cx \ 4) AND 1
attr.archive = (regX.cx \ 32) AND 1
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: GetMediaDescriptor
Returns media information about any disk drive currently defined by
MS-DOS. For any given drive, you can determine the number of sectors per
allocation unit, the number of bytes per sector, and the FAT
identification byte MS-DOS uses to determine how to treat the drive. This
information is returned by the MS-DOS function 21H.
The GetDiskFreeSpace subprogram returns related information.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetMediaDescriptor **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calls the MS-DOS "Get Media Descriptor" function for
' the indicated drive. Results are returned in a
' structure of type MediaDescriptorType.
'
' EXAMPLE OF USE: GetMediaDescriptor drive$, desc
' PARAMETERS: drive$ Drive designation, such as "A:"
' desc Structure of type MediaDescriptorType
' VARIABLES: regX Structure of type RegTypeX
' drive% Numeric drive designation
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' 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
'
' TYPE MediaDescriptorType
' sectorsPerAllocationUnit AS INTEGER
' bytesPerSector AS INTEGER
' FATIdentificationByte AS INTEGER
' END TYPE
'
' DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE SUB GetMediaDescriptor (drive$, desc AS MediaDescriptorType)
'
SUB GetMediaDescriptor (drive$, desc AS MediaDescriptorType) STATIC
DIM regX AS RegTypeX
IF drive$ <> "" THEN
drive% = ASC(UCASE$(drive$)) - 64
ELSE
drive% = 0
END IF
IF drive% >= 0 THEN
regX.dx = drive%
ELSE
regX.dx = 0
END IF
regX.ax = &H1C00
InterruptX &H21, regX, regX
desc.sectorsPerAllocationUnit = regX.ax AND &HFF
desc.bytesPerSector = regX.cx
DEF SEG = regX.ds
desc.FATIdentificationByte = PEEK(regX.bx)
DEF SEG
END SUB
──────────────────────────────────────────────────────────────────────────
Function: GetVerifyState%
Returns the current setting of the MS-DOS Verify flag. If Verify is on,
this function returns a 1. If Verify is off, a 0 is returned.
See the SetVerifyState subprogram to see how to set the Verify on or off
as desired.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetVerifyState% **
' ** Type: Function **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the current state of the MS-DOS "Verify After
' Write" flag.
'
' EXAMPLE OF USE: state% = GetVerifyState%
' PARAMETERS: (none)
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE FUNCTION GetVerifyState% ()
'
FUNCTION GetVerifyState% STATIC
DIM reg AS RegType
reg.ax = &H5400
Interrupt &H21, reg, reg
GetVerifyState% = reg.ax AND &HFF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: SetDirectory
Sets the current directory for the default drive in the same way as the
MS-DOS CHDIR command.
For example, to cause a program to change to the directory C:\TXT, use
this program statement:
SetDirectory "C:\TXT", result%
The returned value of result% indicates whether the attempt to change the
directory was successful. If result% is 0, the directory change was
successful.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SetDirectory **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets the current directory.
'
' EXAMPLE OF USE: SetDirectory path$, result%
' PARAMETERS: path$ The path to the directory
' result% Returned error code, zero if successful
' VARIABLES: regX Structure of type RegTypeX
' p$ Null terminated copy of path$
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' 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
'
' DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE SUB SetDirectory (path$, result%)
'
SUB SetDirectory (path$, result%) STATIC
DIM regX AS RegTypeX
regX.ax = &H3B00
p$ = path$ + CHR$(0)
regX.ds = VARSEG(p$)
regX.dx = SADD(p$)
InterruptX &H21, regX, regX
IF regX.flags AND 1 THEN
result% = regX.ax
ELSE
result% = 0
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: SetDrive
Lets a QuickBASIC program change the current disk drive. Another way of
doing the same thing would be to use the SHELL statement:
SHELL "CD " + d$
However, this subprogram is much more efficient and much faster.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SetDrive **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calls MS-DOS to set the current drive.
'
' EXAMPLE OF USE: SetDrive d$
' PARAMETERS: d$ Drive designation, such as "A:"
' VARIABLES: reg Structure of type RegType
' drive% Numeric value of drive
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE SUB SetDrive (drive$)
'
SUB SetDrive (drive$) STATIC
DIM reg AS RegType
IF drive$ <> "" THEN
drive% = ASC(UCASE$(drive$)) - 65
ELSE
drive% = 0
END IF
IF drive% >= 0 THEN
reg.dx = drive%
ELSE
reg.dx = 0
END IF
reg.ax = &HE00
Interrupt &H21, reg, reg
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: SetFileAttributes
Sets the file attribute bits for a file as desired. For example, to make a
file invisible to the user, set the "hidden" attribute bit. To protect a
file from accidentally being modified or deleted, set the "read only"
attribute bit.
The GetFileAttributes subprogram describes these file attribute bits in
more detail.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SetFileAttributes **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets attribute bits for a file.
'
' EXAMPLE OF USE: SetFileAttributes fileName$, attr
' PARAMETERS: fileName$ Name of file
' attr Structure of type FileAttributesType
' VARIABLES: regX Structure of type RegTypeX
' f$ Null terminated copy of fileName$
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' 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
'
' TYPE FileAttributesType
' readOnly AS INTEGER
' hidden AS INTEGER
' systemFile AS INTEGER
' archive AS INTEGER
' result AS INTEGER
' END TYPE
'
' DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE SUB SetFileAttributes (fileName$, attr AS FileAttributesType)
'
SUB SetFileAttributes (fileName$, attr AS FileAttributesType)
DIM regX AS RegTypeX
regX.ax = &H4301
IF attr.readOnly THEN
regX.cx = 1
ELSE
regX.cx = 0
END IF
IF attr.hidden THEN
regX.cx = regX.cx + 2
END IF
IF attr.systemFile THEN
regX.cx = regX.cx + 4
END IF
IF attr.archive THEN
regX.cx = regX.cx + 32
END IF
f$ = fileName$ + CHR$(0)
regX.ds = VARSEG(f$)
regX.dx = SADD(f$)
InterruptX &H21, regX, regX
IF regX.flags AND 1 THEN
attr.result = regX.ax
ELSE
attr.result = 0
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: SetVerifyState
Sets or clears the write verify flag MS-DOS uses during disk file writing,
duplicating the actions of the MS-DOS commands VERIFY ON and VERIFY OFF.
If a parameter (state%) of 0 is passed to the routine, the subprogram sets
the Verify state to off. If non-zero, it sets it to on.
To determine the current setting of the Verify flag, use the
GetVerifyState% function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SetVerifyState **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets or clears the "Verify After Write" MS-DOS flag.
'
' EXAMPLE OF USE: SetVerifyState state%
' PARAMETERS: state% If 0, resets Verify; If non-zero,
' then sets Verify on
' VARIABLES: reg Structure of type RegType
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' ax AS INTEGER
' bx AS INTEGER
' cx AS INTEGER
' dx AS INTEGER
' bp AS INTEGER
' si AS INTEGER
' di AS INTEGER
' flags AS INTEGER
' END TYPE
'
' DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegTyp
' DECLARE SUB SetVerifyState (state%)
'
SUB SetVerifyState (state%) STATIC
DIM reg AS RegType
IF state% THEN
reg.ax = &H2E01
ELSE
reg.ax = &H2E00
END IF
Interrupt &H21, reg, reg
END SUB
──────────────────────────────────────────────────────────────────────────
Function: TranslateCountry$
Returns the translated version of the string passed to it, according to
the current MS-DOS country setting. Only characters with byte values in
the range 128 through 255 are candidates for translation.
Before calling this function, you must call the GetCountry subprogram to
fill in the structure of type GetCountryType with the address of the
translation routine in the operating system. This housekeeping is all
taken care of automatically if you only remember to call GetCountry
before calling TranslateCountry$.
The TranslateCountry$ function calls an assembly-language subprogram named
CaseMap to translate each character of the passed string. CaseMap
demonstrates the powerful DECLARE statement of QuickBASIC. ( CaseMap is
discussed in Part III of this book.) Notice that the segment% and offset%
variables representing the address of the MS-DOS translation routine are
passed by value rather than by address, the default.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: TranslateCountry$ **
' ** Type: Function **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a string of characters translated according to
' the current country setting of MS-DOS.
'
' EXAMPLE OF USE: b$ = TranslateCountry$(a$, country)
' PARAMETERS: a$ String to be translated
' country Structure of type CountryType
' VARIABLES: i% Index to each character of a$
' c% Byte value of each character in a$
' MODULE LEVEL
' DECLARATIONS: TYPE CountryType
' DateTimeFormat AS STRING * 11
' CurrencySymbol AS STRING * 4
' ThousandsSeparator AS STRING * 1
' DecimalSeparator AS STRING * 1
' DateSeparator AS STRING * 1
' TimeSeparator AS STRING * 1
' CurrencyThenSymbol AS INTEGER
' CurrencySymbolSpace AS INTEGER
' CurrencyPlaces AS INTEGER
' Hours24 AS INTEGER
' caseMapSegment AS INTEGER
' caseMapOffset AS INTEGER
' DataListSeparator AS STRING * 1
' END TYPE
'
' DECLARE SUB CaseMap (character%, BYVAL Segment%, BYVAL Offset
' DECLARE FUNCTION TranslateCountry$ (a$, country AS CountryTyp
'
FUNCTION TranslateCountry$ (a$, country AS CountryType) STATIC
FOR i% = 1 TO LEN(a$)
c% = ASC(MID$(a$, i%))
CaseMap c%, country.caseMapSegment, country.caseMapOffset
MID$(a$, i%, 1) = CHR$(c%)
NEXT i%
TranslateCountry$ = a$
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: WriteToDevice
Outputs a string of bytes or characters to any device or file. QuickBASIC
provides comprehensive input and output capabilities and should be used
whenever possible. This routine is for those rare instances when accessing
the MS-DOS output routines is of benefit. For example, the STDOUT toolbox
is a good example of the use of the MS-DOS level code for output.
QuickBASIC PRINT statements bypass the extended screen and keyboard
control device named ANSI.SYS. Using this WriteToDevice subprogram (or the
routines in the STDOUT toolbox) lets you use the ANSI.SYS driver's
capabilities.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: WriteToDevice **
' ** Type: Subprogram **
' ** Module: DOSCALLS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Writes bytes to a file or device.
'
' EXAMPLE OF USE: WriteToDevice handle%, a$, result%
' PARAMETERS: handle% File or device handle
' a$ String to be output
' result% Error code returned from MS-DOS
' VARIABLES: regX Structure of type RegTypeX
' MODULE LEVEL
' DECLARATIONS: TYPE RegTypeX
' 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
'
' DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE SUB WriteToDevice (handle%, a$, result%)
'
SUB WriteToDevice (handle%, a$, result%) STATIC
DIM regX AS RegTypeX
regX.ax = &H4000
regX.cx = LEN(a$)
regX.bx = handle%
regX.ds = VARSEG(a$)
regX.dx = SADD(a$)
InterruptX &H21, regX, regX
IF regX.flags AND 1 THEN
result% = regX.ax
ELSEIF regX.ax <> LEN(a$) THEN
result% = -1
ELSE
result% = 0
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
EDIT
The EDIT toolbox is a collection of subprograms for line and screen input
of strings. The EditLine subprogram allows full input editing on a single
line, and the EditBox subprogram allows user input and editing inside a
rectangular area of the screen. The DrawBox, FormatTwo, and
InsertCharacter subprograms enhance the capabilities of the EditBox
routine and provide capabilities that can be useful in themselves.
Name Type Description
──────────────────────────────────────────────────────────────────────────
EDIT.BAS Demo module
DrawBox Sub Creates a double-lined box on the display
EditBox Sub Allows editing in a boxed area of the
screen
EditLine Sub Allows editing of string at cursor
position
FormatTwo Sub Splits string into two strings
InsertCharacter Sub Inserts a character
──────────────────────────────────────────────────────────────────────────
Demo Module: EDIT
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: EDIT **
' ** Type: Toolbox **
' ** Module: EDIT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' .MAK FILE: EDIT.BAS
' KEYS.BAS
' PARAMETERS: (none)
' VARIABLES: a$ String to be edited by the user
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Key code numbers
CONST BACKSPACE = 8
CONST CTRLLEFTARROW = 29440
CONST CTRLRIGHTARROW = 29696
CONST CTRLY = 25
CONST CTRLQ = 17
CONST DELETE = 21248
CONST DOWNARROW = 20480
CONST ENDKEY = 20224
CONST ENTER = 13
CONST ESCAPE = 27
CONST HOME = 18176
CONST INSERTKEY = 20992
CONST LEFTARROW = 19200
CONST RIGHTARROW = 19712
CONST TABKEY = 9
CONST UPARROW = 18432
' Functions
DECLARE FUNCTION KeyCode% ()
' Subprograms
DECLARE SUB EditLine (a$, exitCode%)
DECLARE SUB DrawBox (row1%, col1%, row2%, col2%)
DECLARE SUB EditBox (a$, row1%, col1%, row2%, col2%)
DECLARE SUB FormatTwo (a$, b$, col%)
DECLARE SUB InsertCharacter (x$(), kee$, cp%, rp%, wide%, high%)
' Demonstrate the EditLine subprogram
a$ = " Edit this line, and then press Up arrow, Down arrow, or Enter "
CLS
COLOR 14, 1
EditLine a$, exitCode%
COLOR 7, 0
PRINT
PRINT
PRINT "Result of edit ..."
COLOR 14, 0
PRINT a$
COLOR 7, 0
PRINT
SELECT CASE exitCode%
CASE 0
PRINT "Enter";
CASE -1
PRINT "Down arrow";
CASE 1
PRINT "Up arrow";
CASE ELSE
END SELECT
PRINT " key pressed."
' Demonstrate the EditBox subprogram
a$ = "Now, edit text inside this box. Press "
a$ = a$ + "Esc to end the editing..."
COLOR 12, 1
DrawBox 8, 17, 19, 57
COLOR 11, 1
EditBox a$, 8, 17, 19, 57
LOCATE 21, 1
COLOR 7, 0
PRINT "Result..."
COLOR 14, 0
PRINT a$
COLOR 7, 0
──────────────────────────────────────────────────────────────────────────
Subprogram: DrawBox
Draws a rectangular, double-lined box on the screen. No attempt is made to
save the screen contents under the box area, and no control of the
character colors is provided. The DrawBox subprogram simply provides a
fast, flexible way to get a rectangular area of the screen cleared and
outlined using the current foreground and background color settings. Use
the COLOR statement before calling this subprogram if you want to change
the foreground and background colors.
The WINDOWS.BAS module provides a more comprehensive method of creating
and removing windows for information and menuing tasks.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DrawBox **
' ** Type: Subprogram **
' ** Module: EDIT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Draws a double-lined box.
'
' EXAMPLE OF USE: DrawBox row1%, col1%, row2%, col2%
' PARAMETERS: row1% Screen text row at upper left corner of the b
' col1% Screen text column at upper left corner of th
' row2% Screen text row at lower right corner of the
' col2% Screen text column at lower right corner of t
' box
' VARIABLES: wide% Inside width of box
' row3% Loop row number for creating sides of box
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB DrawBox (row1%, col1%, row2%, col2%)
'
SUB DrawBox (row1%, col1%, row2%, col2%) STATIC
' Determine inside width of box
wide% = col2% - col1% - 1
' Across the top
LOCATE row1%, col1%, 0
PRINT CHR$(201);
PRINT STRING$(wide%, 205);
PRINT CHR$(187);
' Down the sides
FOR row3% = row1% + 1 TO row2% - 1
LOCATE row3%, col1%, 0
PRINT CHR$(186);
PRINT SPACE$(wide%);
PRINT CHR$(186);
NEXT row3%
' Across the bottom
LOCATE row2%, col1%, 0
PRINT CHR$(200);
PRINT STRING$(wide%, 205);
PRINT CHR$(188);
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: EditBox
Lets a user input and edit string characters in a rectangular area of the
screen. This routine doesn't draw a box around the area on the display,
but you can easily create one by calling the DrawBox subprogram before
calling EditBox.
This subprogram is a simple text editor. Features include automatic
wordwrap and reformatting, line insert and delete, and support of many of
the same editing keys used in the QuickBASIC editing environment. The keys
acted upon are Left arrow, Right arrow, Up arrow, Down arrow, Home, End,
Insert, Backspace, Delete, Ctrl-Y, Ctrl-Q-Y, Ctrl-Right arrow, Ctrl-Left
arrow, Enter, and Escape.
You can force a reformat of the entire rectangular area by moving the
cursor to the upper left corner of the rectangular area and then pressing
the Backspace key. The cursor won't move anywhere, but all text in the
area will be reformatted.
To escape from the editing mode, press the Escape key. The string result
of the edit is returned in a$ to the calling program. Note that linefeeds
and all double spaces are removed from a$ and that a$ is trimmed of spaces
from each end before being returned.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: EditBox **
' ** Type: Subprogram **
' ** Module: EDIT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Allows the user to edit text inside a rectangular area.
'
' EXAMPLE OF USE: EditBox a$, row1%, col1%, row2%, col2%
' PARAMETERS: a$ String to be edited
' row1% Screen text row at upper left corner of the are
' col1% Screen text column at upper left corner of the
' row2% Screen text row at lower right corner of the ar
' col2% Screen text column at lower right corner of the
' VARIABLES: r1% Upper inside row of rectangular area
' r2% Lower inside row of rectangular area
' c1% Left inside column of rectangular area
' c2% Right inside column of rectangular area
' wide% Width of area
' high% Height of area
' rp% Index to current working row
' cp% Index to current working column
' insert% Flag for insert/replace mode
' quit% Flag for quitting the subprogram
' across% Saved current cursor column
' down% Saved current cursor row
' x$() Workspace string array
' i% Looping index
' b$ Works with a$ to format a$ into x$()
' keyNumber% Integer code for any key press
' c$ Temporary string workspace
' ds% Index to double-space groupings
' sp% Index to character where split of string is to oc
' ctrlQflag% Indicates Ctrl-Q has been pressed
' kee$ Character entered from keyboard
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION KeyCode% ()
' DECLARE SUB EditBox ($, row1%, col1%, row2%, col2%)
' DECLARE SUB FormatTwo (a$, b$, col%)
' DECLARE SUB InsertCharacter (x$(), kee$, cp%, rp%,
' wide%, high%)
'
SUB EditBox (a$, row1%, col1%, row2%, col2%) STATIC
' Set up some working variables
r1% = row1% + 1
r2% = row2% - 1
c1% = col1% + 2
c2% = col2% - 2
wide% = c2% - c1% + 1
high% = r2% - r1% + 1
rp% = 1
cp% = 1
insert% = TRUE
quit% = FALSE
' Record the current cursor location
across% = POS(0)
down% = CSRLIN
' Dimension a workspace array
REDIM x$(1 TO high%)
' Format a$ into array space
FOR i% = 1 TO high%
FormatTwo a$, b$, wide%
x$(i%) = a$
a$ = b$
NEXT i%
' Display the strings
FOR i% = 1 TO high%
LOCATE r1% + i% - 1, c1%, 0
PRINT x$(i%);
NEXT i%
' Process each keystroke
DO
' Update the current line
LOCATE r1% + rp% - 1, c1%, 0
PRINT x$(rp%);
' Place the cursor
IF insert% THEN
LOCATE r1% + rp% - 1, c1% + cp% - 1, 1, 6, 7
ELSE
LOCATE r1% + rp% - 1, c1% + cp% - 1, 1, 1, 7
END IF
' Grab next keystroke
keyNumber% = KeyCode%
' Process the key
SELECT CASE keyNumber%
CASE INSERTKEY
IF insert% THEN
insert% = FALSE
ELSE
insert% = TRUE
END IF
CASE BACKSPACE
' Rub out character to the left
IF cp% > 1 THEN
x$(rp%) = x$(rp%) + " "
b$ = LEFT$(x$(rp%), cp% - 2)
c$ = MID$(x$(rp%), cp%)
x$(rp%) = b$ + c$
cp% = cp% - 1
' Upper left corner, so reformat the whole box
ELSEIF rp% = 1 THEN
' Pull all the strings together
a$ = ""
FOR i% = 1 TO high%
a$ = a$ + LTRIM$(RTRIM$(x$(i%))) + " "
NEXT i%
' Remove double spaces
ds% = INSTR(a$, " ")
DO WHILE ds%
a$ = LEFT$(a$, ds% - 1) + MID$(a$, ds% + 1)
ds% = INSTR(a$, " ")
LOOP
' Format into the array and display lines
FOR i% = 1 TO high%
FormatTwo a$, b$, wide%
x$(i%) = a$
a$ = b$
LOCATE r1% + i% - 1, c1%, 0
PRINT x$(i%);
NEXT i%
' Concatenate to the preceding line
ELSE
' Use the InsertCharacter sub to insert a space
rp% = rp% - 1
cp% = wide% + 1
InsertCharacter x$(), " ", rp%, cp%, wide%, high%
' Remove the extra spaces introduced
IF cp% > 2 THEN
b$ = LEFT$(x$(rp%), cp% - 3)
c$ = MID$(x$(rp%), cp%)
ELSE
b$ = ""
c$ = MID$(x$(rp%), cp% + 1)
END IF
' Pull the line pieces together
x$(rp%) = LEFT$(b$ + c$ + SPACE$(3), wide%)
' Adjust the cursor position
cp% = cp% - 1
' Display the lines
FOR i% = 1 TO high%
LOCATE r1% + i% - 1, c1%, 0
PRINT x$(i%);
NEXT i%
END IF
CASE DELETE
x$(rp%) = x$(rp%) + " "
b$ = LEFT$(x$(rp%), cp% - 1)
c$ = MID$(x$(rp%), cp% + 1)
x$(rp%) = b$ + c$
CASE UPARROW
IF rp% > 1 THEN
rp% = rp% - 1
END IF
CASE DOWNARROW
IF rp% < high% THEN
rp% = rp% + 1
END IF
CASE LEFTARROW
IF cp% > 1 THEN
cp% = cp% - 1
END IF
CASE RIGHTARROW
IF cp% < wide% THEN
cp% = cp% + 1
END IF
CASE ENTER
IF rp% < high% AND x$(high%) = SPACE$(wide%) THEN
' Shuffle lines down
FOR i% = high% TO rp% + 1 STEP -1
x$(i%) = x$(i% - 1)
NEXT i%
' Split current line at cursor
sp% = wide% - cp% + 1
IF sp% THEN
MID$(x$(rp%), cp%, sp%) = SPACE$(sp%)
END IF
' Move to next line
rp% = rp% + 1
x$(rp%) = MID$(x$(rp%), cp%) + SPACE$(cp% - 1)
cp% = 1
' Display the modified lines
FOR i% = rp% - 1 TO high%
LOCATE r1% + i% - 1, c1%, 0
PRINT x$(i%);
NEXT i%
ELSE
' Nowhere to push things down
BEEP
END IF
CASE HOME
cp% = 1
CASE ENDKEY
cp% = wide% + 1
' Move back to just after last character
IF x$(rp%) <> SPACE$(wide%) THEN
DO UNTIL MID$(x$(rp%), cp% - 1, 1) <> " "
cp% = cp% - 1
LOOP
ELSE
cp% = 1
END IF
CASE CTRLRIGHTARROW
' Find next space
DO UNTIL MID$(x$(rp%), cp%, 1) = " " OR cp% = wide%
cp% = cp% + 1
LOOP
' Find first non-space character
DO UNTIL MID$(x$(rp%), cp%, 1) <> " " OR cp% = wide%
cp% = cp% + 1
LOOP
CASE CTRLLEFTARROW
' Find first space to the left
DO UNTIL MID$(x$(rp%), cp%, 1) = " " OR cp% = 1
cp% = cp% - 1
LOOP
' Find first non-space character to the left
DO UNTIL MID$(x$(rp%), cp%, 1) <> " " OR cp% = 1
cp% = cp% - 1
LOOP
' Find next space to the left
DO UNTIL MID$(x$(rp%), cp%, 1) = " " OR cp% = 1
cp% = cp% - 1
LOOP
' Adjust cursor position to first non-space character
IF cp% > 1 THEN
cp% = cp% + 1
END IF
CASE CTRLY
IF rp% < high% THEN
' Shuffle lines up, spacing out the last
FOR i% = rp% TO high%
IF i% < high% THEN
x$(i%) = x$(i% + 1)
ELSE
x$(i%) = SPACE$(wide%)
END IF
LOCATE r1% + i% - 1, c1%, 0
PRINT x$(i%);
NEXT i%
END IF
' Move cursor to far left
cp% = 1
CASE CTRLQ
ctrlQflag% = TRUE
CASE ESCAPE
quit% = TRUE
CASE IS > 255
SOUND 999, 1
CASE IS < 32
SOUND 999, 1
CASE ELSE
kee$ = CHR$(keyNumber%)
' Insert mode
IF insert% THEN
InsertCharacter x$(), kee$, rp%, cp%, wide%, high%
FOR i% = 1 TO high%
LOCATE r1% + i% - 1, c1%, 0
PRINT x$(i%);
NEXT i%
' Must be overstrike mode
ELSE
MID$(x$(rp%), cp%, 1) = kee$
IF cp% < wide% + 1 THEN
cp% = cp% + 1
ELSE
IF rp% < high% THEN
LOCATE r1% + rp% - 1, c1%, 0
PRINT x$(rp%);
rp% = rp% + 1
cp% = 1
END IF
END IF
END IF
' Correct for bottom right corner problem
IF rp% > high% THEN
cp% = wide%
rp% = high%
END IF
' Check for Ctrl-Q-Y combination (del to end of line)
IF kee$ = "y" AND ctrlQflag% THEN
cp% = cp% - 1
IF cp% = 0 THEN
cp% = wide%
rp% = rp% - 1
END IF
sp% = wide% - cp% + 1
MID$(x$(rp%), cp%, sp%) = SPACE$(sp%)
END IF
' Clear out the possible Ctrl-Q signal
ctrlQflag% = FALSE
END SELECT
LOOP UNTIL quit%
' Concatenate the array strings to form the result
a$ = ""
FOR i% = 1 TO high%
a$ = a$ + " " + LTRIM$(RTRIM$(x$(i%)))
NEXT i%
' Remove double spaces
ds% = INSTR(a$, " ")
DO WHILE ds%
a$ = LEFT$(a$, ds% - 1) + MID$(a$, ds% + 1)
ds% = INSTR(a$, " ")
LOOP
' Trim both ends of spaces
a$ = LTRIM$(RTRIM$(a$))
' Restore original cursor position
LOCATE down%, across%, 1
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: EditLine
Allows the user to edit a single line of text. The string is displayed at
the current cursor location using the current foreground and background
colors. Many of the same editing keys from the QuickBASIC editing
environment are supported in the expected manner. For example, pressing
Ctrl-Right arrow moves the cursor to the start of the next word, and
pressing Ctrl-Q-Y deletes to the end of the line. Insert and overstrike
modes are both supported, and you can delete characters by pressing the
Delete or Backspace key.
To exit the editing, press the Enter, Up arrow, or Down arrow key. The
exitCode% value is set to 0, 1, or -1 respectively, allowing your calling
program to determine which key terminated the editing.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: EditLine **
' ** Type: Subprogram **
' ** Module: EDIT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Allows the user to edit a string at the current cursor position
' on the screen. Keys acted upon are Ctrl-Y, Ctrl-Q-Y, Right arrow,
' Left arrow, Ctrl-Left arrow, Ctrl-Right arrow, Home, End,
' Insert, Escape, Backspace, and Delete.
' Pressing Enter, Up arrow, or Down arrow terminates
' the subprogram and returns exitCode% of 0, +1, or -1.
'
' EXAMPLE OF USE: EditLine a$, exitCode%
' PARAMETERS: a$ String to be edited
' exitCode% Returned code indicating the terminating
' key press
' VARIABLES: row% Saved current cursor row
' col% Saved current cursor column
' length% Length of a$
' ptr% Location of cursor during the editing
' insert% Insert mode toggle
' quit% Flag for quitting the editing
' original$ Saved copy of starting a$
' keyNumber% Integer code for any key press
' ctrlQflag% Indicates Ctrl-Q key press
' kee$ Character of key just pressed
' sp% Length of space string
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION KeyCode% ()
' DECLARE SUB EditLine (a$, exitCode%)
'
SUB EditLine (a$, exitCode%) STATIC
' Set up some variables
row% = CSRLIN
col% = POS(0)
length% = LEN(a$)
ptr% = 0
insert% = TRUE
quit% = FALSE
original$ = a$
' Main processing loop
DO
' Display the line
LOCATE row%, col%, 0
PRINT a$;
' Show appropriate cursor type
IF insert% THEN
LOCATE row%, col% + ptr%, 1, 6, 7
ELSE
LOCATE row%, col% + ptr%, 1, 1, 7
END IF
' Get next keystroke
keyNumber% = KeyCode%
' Process the key
SELECT CASE keyNumber%
CASE INSERTKEY
IF insert% THEN
insert% = FALSE
ELSE
insert% = TRUE
END IF
CASE BACKSPACE
IF ptr% THEN
a$ = a$ + " "
a$ = LEFT$(a$, ptr% - 1) + MID$(a$, ptr% + 1)
ptr% = ptr% - 1
END IF
CASE DELETE
a$ = a$ + " "
a$ = LEFT$(a$, ptr%) + MID$(a$, ptr% + 2)
CASE UPARROW
exitCode% = 1
quit% = TRUE
CASE DOWNARROW
exitCode% = -1
quit% = TRUE
CASE LEFTARROW
IF ptr% THEN
ptr% = ptr% - 1
END IF
CASE RIGHTARROW
IF ptr% < length% - 1 THEN
ptr% = ptr% + 1
END IF
CASE ENTER
exitCode% = 0
quit% = TRUE
CASE HOME
ptr% = 0
CASE ENDKEY
ptr% = length% - 1
CASE CTRLRIGHTARROW
DO UNTIL MID$(a$, ptr% + 1, 1) = " " OR ptr% = length% - 1
ptr% = ptr% + 1
LOOP
DO UNTIL MID$(a$, ptr% + 1, 1) <> " " OR ptr% = length% - 1
ptr% = ptr% + 1
LOOP
CASE CTRLLEFTARROW
DO UNTIL MID$(a$, ptr% + 1, 1) = " " OR ptr% = 0
ptr% = ptr% - 1
LOOP
DO UNTIL MID$(a$, ptr% + 1, 1) <> " " OR ptr% = 0
ptr% = ptr% - 1
LOOP
DO UNTIL MID$(a$, ptr% + 1, 1) = " " OR ptr% = 0
ptr% = ptr% - 1
LOOP
IF ptr% THEN
ptr% = ptr% + 1
END IF
CASE CTRLY
a$ = SPACE$(length%)
ptr% = 0
CASE CTRLQ
ctrlQflag% = TRUE
CASE ESCAPE
a$ = original$
ptr% = 0
insert% = TRUE
CASE IS > 255
SOUND 999, 1
CASE IS < 32
SOUND 999, 1
CASE ELSE
' Convert key code to character string
kee$ = CHR$(keyNumber%)
' Insert or overstrike
IF insert% THEN
a$ = LEFT$(a$, ptr%) + kee$ + MID$(a$, ptr% + 1)
a$ = LEFT$(a$, length%)
ELSE
IF ptr% < length% THEN
MID$(a$, ptr% + 1, 1) = kee$
END IF
END IF
' Are we up against the wall?
IF ptr% < length% THEN
ptr% = ptr% + 1
ELSE
SOUND 999, 1
END IF
' Special check for Ctrl-Q-Y (del to end of line)
IF kee$ = "y" AND ctrlQflag% THEN
IF ptr% <= length% THEN
sp% = length% - ptr% + 1
MID$(a$, ptr%, sp%) = SPACE$(sp%)
ptr% = ptr% - 1
END IF
END IF
' Clear out the Ctrl-Q signal
ctrlQflag% = FALSE
END SELECT
LOOP UNTIL quit%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: FormatTwo
Formats text lines to a given maximum length. The value of col% is used to
find a point in a$ where a$ can be split into two strings between words.
The length of the returned a$ will be less than or equal to col%, and the
rest of the original a$ will be returned in b$.
Notice that repeated calls to this subprogram can format an entire
paragraph of text. An example of this is shown in the subprogram EditBox.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FormatTwo **
' ** Type: Subprogram **
' ** Module: EDIT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Splits a string into two strings between words,
' and with spaces padded to the first string to bring it to
' length, col%.
'
' EXAMPLE OF USE: FormatTwo a$, b$, col%
' PARAMETERS: a$ String to be split
' b$ Returns the tail of the string
' col% Maximum length of a$ after being split
' VARIABLES: ptr% Pointer to split point in a$
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB FormatTwo (a$, b$, col%)
'
SUB FormatTwo (a$, b$, col%) STATIC
' Be sure string is long enough
a$ = a$ + SPACE$(col%)
' Look for rightmost space
ptr% = col% + 1
DO WHILE MID$(a$, ptr%, 1) <> " " AND ptr% > 1
ptr% = ptr% - 1
LOOP
' Do the split
IF ptr% = 1 THEN
b$ = MID$(a$, col% + 1)
a$ = LEFT$(a$, col%)
ELSE
b$ = MID$(a$, ptr% + 1)
a$ = LEFT$(a$, ptr% - 1)
END IF
' Pad the first string with spaces to length col%
a$ = LEFT$(a$ + SPACE$(col%), col%)
' Trim extra spaces from end of second string
b$ = RTRIM$(b$)
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: InsertCharacter
Inserts a character into the array of text being maintained by the
EditBox subprogram. While in Insert mode, the EditBox subprogram calls
InsertCharacter. The character insertion is simple enough, but this
subprogram also handles the chore of performing automatic wordwrap and
formatting.
This task of character insertion could have been performed in the
EditBox subprogram, but breaking the code out into a separate subprogram
makes it much easier to isolate this task from the others. One great
advantage of QuickBASIC is the ability to break complex programming tasks
into smaller, more manageable tasks.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: InsertCharacter **
' ** Type: Subprogram **
' ** Module: EDIT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Handles the task of inserting a character
' for the EditBox subprogram.
'
' EXAMPLE OF USE: InsertCharacter x$(), kee$, rp%, cp%, wide%, high%
' PARAMETERS: x$() Array in EditBox where character is to be
' inserted
' kee$ Character to be inserted
' rp% Row location of insert
' cp% Column location of insert
' wide% Width of rectangular area being edited
' high% Height of rectangular area being edited
' VARIABLES: dum$ Marker character
' b$ String from array at insertion point
' c$ Right part of string at insertion point
' i% Looping index
' ds% Position in string of double spaces
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB InsertCharacter (x$(), kee$, cp%, rp%,
' wide%, high%)
'
SUB InsertCharacter (x$(), kee$, rp%, cp%, wide%, high%) STATIC
' First, insert a dummy character as a marker
dum$ = CHR$(255)
b$ = LEFT$(x$(rp%), cp% - 1)
c$ = MID$(x$(rp%), cp%)
b$ = b$ + dum$ + c$
' If end of string is a space, then drop it
IF RIGHT$(b$, 1) = " " THEN
x$(rp%) = LEFT$(b$, wide%)
' Otherwise, need to adjust the lines
ELSE
' If not in the last line, then tack them all together
IF rp% < high% THEN
FOR i% = rp% + 1 TO high%
b$ = b$ + " " + x$(i%)
NEXT i%
END IF
' Trim both ends
b$ = LTRIM$(RTRIM$(b$))
' Remove all double spaces
ds% = INSTR(b$, " ")
DO WHILE ds%
b$ = LEFT$(b$, ds% - 1) + MID$(b$, ds% + 1)
ds% = INSTR(b$, " ")
LOOP
' Reformat the lines
FOR i% = rp% TO high%
FormatTwo b$, c$, wide%
x$(i%) = b$
b$ = c$
NEXT i%
END IF
' Find out where that dummy character is now
FOR rp% = 1 TO high%
cp% = INSTR(x$(rp%), dum$)
IF cp% THEN
EXIT FOR
END IF
NEXT rp%
' Replace the dummy character with the keystroke character
IF cp% THEN
MID$(x$(rp%), cp%, 1) = kee$
END IF
' Increment the cursor location
IF cp% < wide% + 1 THEN
cp% = cp% + 1
ELSE
IF rp% < high% THEN
cp% = 1
rp% = rp% + 1
END IF
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
ERROR
The ERROR toolbox contains a single subprogram that displays an error
message in a box. If you have a color monitor, you can make the display
quite eye-catching. In this example, the message is yellow on a red
background.
Name Type Description
──────────────────────────────────────────────────────────────────────────
ERROR.BAS Demo module
ErrorMessage Sub Error message display
──────────────────────────────────────────────────────────────────────────
Demo Module: ERROR
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ERROR **
' ** Type: Toolbox **
' ** Module: ERROR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: (none)
' Subprogram
DECLARE SUB ErrorMessage (message$)
' Demonstrate the subprogram
ErrorMessage "This is a sample message for ErrorMessage"
──────────────────────────────────────────────────────────────────────────
Subprogram: ErrorMessage
Provides a convenient, noticeable way to display error messages.
QuickBASIC has a built-in mechanism for terminating a program when a
serious error occurs. For example, if you try to divide by 0, the program
immediately halts and displays the message Division by zero.
In other situations, you might want to terminate a program because of a
serious error that QuickBASIC would otherwise let pass. One approach in
such a situation would be to use QuickBASIC's ERROR n% statement. This
works fine, but unless one of the built-in error messages happens to fit
the given situation, you're stuck with the default message Unprintable
error, which sounds ghastly.
A second approach to terminating a program in a controlled way would be to
print your own descriptive error message and then follow with the SYSTEM
statement. In many cases this technique is sufficient, but it's preferable
to present a more polished, eye-catching display.
This subprogram lets you systematically display your own error messages in
a unique error-message window, just before terminating and returning to
MS-DOS. The display──in this example, a red background and bright yellow
message──immediately lets you know that a serious error has been detected.
The table of color-defining constants in this subprogram can be useful in
any program where you use the COLOR statement. A statement such as COLOR
YELLOW, RED is much more descriptive than the equivalent COLOR 23, 4. It
also makes programming easier because you don't have to remember or look
up the numbers for the various colors.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ErrorMessage **
' ** Type: Subprogram **
' ** Module: ERROR.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Displays an error message and then exits to the system.
'
' EXAMPLE OF USE: ErrorMessage "This is a sample message for ErrorMessage
' PARAMETERS: message$ String to be displayed in the error bo
' VARIABLES: lm% Length of message$ during processing
' col% Screen character column for left edge
' of error box
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB ErrorMessage (message$)
'
SUB ErrorMessage (message$) STATIC
' Define color constants
CONST BLACK = 0
CONST BLUE = 1
CONST GREEN = 2
CONST CYAN = 3
CONST RED = 4
CONST MAGENTA = 5
CONST BROWN = 6
CONST WHITE = 7
CONST BRIGHT = 8
CONST BLINK = 16
CONST YELLOW = BROWN + BRIGHT
' Trim off spaces on each end of message
message$ = LTRIM$(RTRIM$(message$))
' Make message length an odd number
IF LEN(message$) MOD 2 = 0 THEN
message$ = message$ + " "
END IF
' Minimum length of message is 9 characters
DO WHILE LEN(message$) < 9
message$ = " " + message$ + " "
LOOP
' Maximum length of message is 75
message$ = LEFT$(message$, 75)
' Initialization of display
SCREEN 0
WIDTH 80
CLS
' Calculate screen locations
lm% = LEN(message$)
col% = 38 - lm% \ 2
' Create the error box
COLOR RED + BRIGHT, RED
LOCATE 9, col%
PRINT CHR$(201); STRING$(lm% + 2, 205); CHR$(187)
LOCATE 10, col%
PRINT CHR$(186); SPACE$(lm% + 2); CHR$(186)
LOCATE 11, col%
PRINT CHR$(186); SPACE$(lm% + 2); CHR$(186)
LOCATE 12, col%
PRINT CHR$(200); STRING$(lm% + 2, 205); CHR$(188)
' The title
COLOR CYAN + BRIGHT, RED
LOCATE 10, 36
PRINT "* ERROR *";
' The message$
COLOR YELLOW, RED
LOCATE 11, col% + 2
PRINT message$;
' System will prompt for "any key"
COLOR WHITE, BLACK
LOCATE 22, 1
SYSTEM
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
FIGETPUT
The FIGETPUT toolbox demonstrates the FileGet$ function and FilePut
subprogram, routines that allow efficient binary-mode access to files up
to 32767 bytes in length. Because each of these routines uses binary-file
mode, an entire file can be read by one GET statement or written by one
PUT statement. Any type of file containing no more than 32767 bytes can be
read into or written from a QuickBASIC string variable by these routines.
When reading an ASCII file, the FileGet$ function returns all lines of
the file in one string. A carriage return/line feed pair of characters
marks the separation of each line in the file.
These routines can be useful for file-processing utility programs, such as
byte-for-byte file comparisons, text searches, and file ciphering.
To demonstrate the routines, the module-level code reads a copy of itself
into a single string, converts all characters to uppercase, counts the
occurrences of each letter of the alphabet, and saves the resulting string
in a file named FIGETPUT.TST. For a meaningful character count save
FIGETPUT.BAS in ASCII format.
Name Type Description
──────────────────────────────────────────────────────────────────────────
FIGETPUT.BAS Demo module
FileGet$ Func Returns a string with contents of file
FilePut Sub Writes contents of string into binary
file
──────────────────────────────────────────────────────────────────────────
Demo Module: FIGETPUT
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FIGETPUT **
' ** Type: Toolbox **
' ** Module: FIGETPUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Reads itself (FIGETPUT.BAS) into a string,
' converts characters to uppercase, counts occurrences of
' the characters "A" through "Z," and saves the
' result in a file named FIGETPUT.TST.
'
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: filename
' VARIABLES: count%() Tally array for the 26 alpha characters
' fileName$ Name of file to be processed
' a$ Contents of the file
' i% Looping index
' c% ASCII value of each file byte
' Functions
DECLARE FUNCTION FileGet$ (fileName$)
' Subprograms
DECLARE SUB FilePut (fileName$, a$)
' Dimension array of counts for each ASCII code "A" to "Z"
DIM count%(65 TO 90)
' Read in the file (must be no greater than 32767 bytes long)
a$ = FileGet$("FIGETPUT.BAS")
' Convert to uppercase
a$ = UCASE$(a$)
' Count the letters
FOR i% = 1 TO LEN(a$)
c% = ASC(MID$(a$, i%, 1))
IF c% >= 65 AND c% <= 90 THEN
count%(c%) = count%(c%) + 1
END IF
NEXT i%
' Output the results
CLS
PRINT "Alphabetic character count for FIGETPUT.BAS"
PRINT
FOR i% = 65 TO 90
PRINT CHR$(i%); " -"; count%(i%),
NEXT i%
' Write out the new file
FilePut "FIGETPUT.TST", a$
' All done
END
──────────────────────────────────────────────────────────────────────────
Function: FileGet$
Uses the binary file mode to read the contents of any MS-DOS file into a
string variable. The file length must be fewer than 32768 bytes to fit in
one string. If you try to read a larger file, an error message is
displayed, and the program halts.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FileGet$ **
' ** Type: Function **
' ** Module: FIGETPUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a string containing the contents of a file.
' Maximum file length is 32767 bytes.
'
' EXAMPLE OF USE: a$ = FileGet$(fileName$)
' PARAMETERS: fileName$ Name of file to be accessed
' VARIABLES: fileNumber Next available free file number
' length& Length of file
' a$ String for binary read of file
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION FileGet$ (fileName$)
'
FUNCTION FileGet$ (fileName$) STATIC
fileNumber = FREEFILE
OPEN fileName$ FOR BINARY AS #fileNumber
length& = LOF(fileNumber)
IF length& <= 32767 THEN
a$ = SPACE$(length&)
GET #fileNumber, , a$
FileGet$ = a$
a$ = ""
ELSE
PRINT "FileGet$()... file too large"
SYSTEM
END IF
CLOSE #fileNumber
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: FilePut
Writes the contents of any string variable to a file using binary file
mode. The biggest file that you can create in this way is 32767 bytes
because that's the longest string you can build.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FilePut **
' ** Type: Subprogram **
' ** Module: FIGETPUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Writes contents of a$ into a binary file named fileName$.
'
' EXAMPLE OF USE: FilePut fileName$, a$
' PARAMETERS: fileName$ Name of file to be written
' a$ Bytes to be placed in the file
' VARIABLES: fileNumber Next available free file number
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB FilePut (fileName$, a$)
'
SUB FilePut (fileName$, a$) STATIC
' Find available file number
fileNumber = FREEFILE
' Truncate any previous contents
OPEN fileName$ FOR OUTPUT AS #fileNumber
CLOSE #fileNumber
' Write string to file
OPEN fileName$ FOR BINARY AS #fileNumber
PUT #fileNumber, , a$
' All done
CLOSE #fileNumber
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
FILEINFO
The FILEINFO toolbox contains subprograms that obtain directory
information about files. Basically, this program mimics the MS-DOS DIR
command or the QuickBASIC FILES command.
As set up, this program finds normal file entries. You can change the
FILEATTRIBUTE constant to access other types of files. Refer to the CONST
statements that define the various file attribute bits.
The starting path$ for the search is set to the current directory, but you
can change the path$ assignment to search any desired drive or directory.
Name Type Description
──────────────────────────────────────────────────────────────────────────
FILEINFO.BAS Demo module
FindFirstFile Sub Finds first file that matches parameter
FindNextFile Sub Locates next file that matches parameter
GetFileData Sub Extracts file directory information
──────────────────────────────────────────────────────────────────────────
Demo Module: FILEINFO
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FILEINFO **
' ** Type: Toolbox **
' ** Module: FILEINFO.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Collection of subprograms and functions for accessing
' directory information about files.
'
' USAGE: No command line parameters
' REQUIREMENTS: MIXED.QLB/.LIB
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: path$ Path to files for gathering directory
' information; wildcard characters accepted
' dta$ Disk transfer area buffer string
' result% Code returned as result of directory search
' file Structure of type FileDataType
' n% File count
' File search attribute bits
CONST ISNORMAL = 0
CONST ISREADONLY = 1
CONST ISHIDDEN = 2
CONST ISSYSTEM = 4
CONST ISVOLUMELABEL = 8
CONST ISSUBDIRECTORY = 16
CONST ISARCHIVED = 32
' Here we'll search for normal files and subdirectories
CONST FILEATTRIBUTE = ISNORMAL + ISSUBDIRECTORY
TYPE RegTypeX
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
TYPE FileDataType
finame AS STRING * 12
year AS INTEGER
month AS INTEGER
day AS INTEGER
hour AS INTEGER
minute AS INTEGER
second AS INTEGER
attribute AS INTEGER
size AS LONG
END TYPE
' Subprograms
DECLARE SUB INTERRUPTX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
DECLARE SUB FindFirstFile (path$, dta$, result%)
DECLARE SUB FindNextFile (dta$, result%)
DECLARE SUB GetFileData (dta$, file AS FileDataType)
' Data structures
DIM file AS FileDataType
' For demonstration purposes, list current directory
CLS
path$ = "*.*"
' Always start by finding the first match
FindFirstFile path$, dta$, result%
' Check that the path$ got us off to a good start
IF result% THEN
PRINT "Error: FindFirstFile - found no match for path$"
SYSTEM
END IF
' List all the files in this directory
DO
IF n% MOD 19 = 0 THEN
CLS
PRINT TAB(4); "File"; TAB(18); "Date"; TAB(29); "Time";
PRINT TAB(39); "Size"; TAB(48); "Attributes"
PRINT
END IF
GetFileData dta$, file
PRINT file.finame;
PRINT USING " ##/##/####"; file.month, file.day, file.year;
PRINT USING " ##:##:##"; file.hour, file.minute, file.second;
PRINT USING " ########"; file.size;
PRINT USING " &"; RIGHT$("0" + HEX$(file.attribute), 2)
PRINT " &H";
PRINT USING "&"; RIGHT$("0" + HEX$(file.attribute), 2)
n% = n% + 1
FindNextFile dta$, result%
IF n% MOD 19 = 0 THEN
PRINT
PRINT "Press any key to continue"
DO
LOOP WHILE INKEY$ = ""
END IF
LOOP UNTIL result%
PRINT
PRINT n%; "files found"
──────────────────────────────────────────────────────────────────────────
Subprogram: FindFirstFile
Finds the first directory entry that matches a given path$. This
subprogram is called once before FindNextFile is called numerous times to
find the rest of the entries.
The result of this file search is returned in the dta$ variable. Call the
GetFileData subprogram to extract the information from the string.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FindFirstFile **
' ** Type: Subprogram **
' ** Module: FILEINFO.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Finds first file that matches the path$.
'
' EXAMPLE OF USE: FindFirstFile path$, dta$, result%
' PARAMETERS: path$ Complete path, including wildcard character
' desired, for the directory search
' dta$ Disk transfer area buffer space
' result% Returned result code for the search
' VARIABLES: reg Structure of type RegTypeX
' thePath$ Null terminated version of path$
' sgmt% Current DTA address segment
' ofst% Current DTA address offset
' MODULE LEVEL
' DECLARATIONS: File search attribute bits
' CONST ISNORMAL = 0
' CONST ISREADONLY = 1
' CONST ISHIDDEN = 2
' CONST ISSYSTEM = 4
' CONST ISVOLUMELABEL = 8
' CONST ISSUBDIRECTORY = 16
' CONST ISARCHIVED = 32
'
' CONST FILEATTRIBUTE = ISNORMAL + ISSUBDIRECTORY
'
' TYPE RegTypeX
' 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
'
' DECLARE SUB INTERRUPTX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE SUB FindFirstFile (path$, dta$, result%)
'
SUB FindFirstFile (path$, dta$, result%) STATIC
' Initialization
DIM reg AS RegTypeX
' The path must be a null terminated string
thePath$ = path$ + CHR$(0)
' Get current DTA address
reg.ax = &H2F00
INTERRUPTX &H21, reg, reg
sgmt% = reg.es
ofst% = reg.bx
' Set dta address
dta$ = SPACE$(43)
reg.ax = &H1A00
reg.ds = VARSEG(dta$)
reg.dx = SADD(dta$)
INTERRUPTX &H21, reg, reg
' Find first file match
reg.ax = &H4E00
reg.cx = FILEATTRIBUTE
reg.ds = VARSEG(thePath$)
reg.dx = SADD(thePath$)
INTERRUPTX &H21, reg, reg
' The carry flag tells if a file was found or not
result% = reg.flags AND 1
' Reset the original DTA
reg.ax = &H1A00
reg.ds = sgmt%
reg.dx = ofst%
INTERRUPTX &H21, reg, reg
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: FindNextFile
Continues the search for file directory entries after the FindFirstFile
subprogram was called once. This subprogram is usually called repeatedly
until all files that match the original path$ are found. The value of
result% is 0 until the last file is found.
The dta$ variable carries the important information about the search from
call to call of this subprogram. Be careful not to alter its contents
between calls to this routine. To extract details about each file's
directory entry, pass dta$ to the subprogram GetFileData.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FindNextFile **
' ** Type: Subprogram **
' ** Module: FILEINFO.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Locates next file. FindFirstFile must be called
' before this subprogram is called.
'
' EXAMPLE OF USE: FindNextFile dta$, result%
' PARAMETERS: dta$ Previously filled-in Disk Transfer Area
' buffer string
' result% Result code for the search
' VARIABLES: reg Structure of type RegTypeX
' thePath$ Null terminated version of path$
' sgmt% Current DTA address segment
' ofst% Current DTA address offset
' MODULE LEVEL
' DECLARATIONS: CONST ISNORMAL = 0
' CONST ISREADONLY = 1
' CONST ISHIDDEN = 2
' CONST ISSYSTEM = 4
' CONST ISVOLUMELABEL = 8
' CONST ISSUBDIRECTORY = 16
' CONST ISARCHIVED = 32
'
' CONST FILEATTRIBUTE = ISNORMAL + ISSUBDIRECTORY
'
' TYPE RegTypeX
' 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
'
' DECLARE SUB INTERRUPTX (intnum%, inreg AS RegTypeX, outreg AS RegType
' DECLARE SUB FindNextFile (dta$, result%)
'
SUB FindNextFile (dta$, result%) STATIC
' Initialization
DIM reg AS RegTypeX
' Be sure dta$ was built (FindFirstFile should have been called)
IF LEN(dta$) <> 43 THEN
result% = 2
EXIT SUB
END IF
' Get current DTA address
reg.ax = &H2F00
INTERRUPTX &H21, reg, reg
sgmt% = reg.es
ofst% = reg.bx
' Set dta address
reg.ax = &H1A00
reg.ds = VARSEG(dta$)
reg.dx = SADD(dta$)
INTERRUPTX &H21, reg, reg
' Find next file match
reg.ax = &H4F00
reg.cx = FILEATTRIBUTE
reg.ds = VARSEG(thePath$)
reg.dx = SADD(thePath$)
INTERRUPTX &H21, reg, reg
' The carry flag tells whether a file was found or not
result% = reg.flags AND 1
' Reset the original DTA
reg.ax = &H1A00
reg.ds = sgmt%
reg.dx = ofst%
INTERRUPTX &H21, reg, reg
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: GetFileData
Extracts the information about a file's directory entry from the variable
dta$ passed back from calls to FindFirstFile and FindNextFile. The
information is returned in the data structure of type FileDataType and
includes the date and time of the last file update, the filename, the file
size, and the file attribute byte.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GetFileData **
' ** Type: Subprogram **
' ** Module: FILEINFO.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Extracts the file directory information from a Disk
' Transfer Area (dta$) that has been filled in by a
' call to either FindFirstFile or FindNextFile.
'
' EXAMPLE OF USE: GetFileData dta$, file
' PARAMETERS: dta$ Disk Transfer Area buffer string passed bac
' either FindFirstFile or FindNextFile
' VARIABLES: tim& Time stamp of the file
' dat& Date stamp of the file
' f$ Filename during extraction
' MODULE LEVEL
' DECLARATIONS: TYPE FileDataType
' finame AS STRING * 12
' year AS INTEGER
' month AS INTEGER
' day AS INTEGER
' hour AS INTEGER
' minute AS INTEGER
' second AS INTEGER
' attribute AS INTEGER
' size AS LONG
' END TYPE
'
' DECLARE SUB GetFileData (dta$, file AS FileDataType)
'
SUB GetFileData (dta$, file AS FileDataType) STATIC
file.attribute = ASC(MID$(dta$, 22, 1))
tim& = CVI(MID$(dta$, 23, 2))
IF tim& < 0 THEN
tim& = tim& + 65536
END IF
file.second = tim& AND &H1F
file.minute = (tim& \ 32) AND &H3F
file.hour = (tim& \ 2048) AND &H1F
dat& = CVI(MID$(dta$, 25, 2))
file.day = dat& AND &H1F
file.month = (dat& \ 32) AND &HF
file.year = ((dat& \ 512) AND &H1F) + 1980
file.size = CVL(MID$(dta$, 27, 4))
f$ = MID$(dta$, 31) + CHR$(0)
file.finame = LEFT$(f$, INSTR(f$, CHR$(0)) - 1)
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
FRACTION
The FRACTION toolbox is a set of subprograms and functions for working
with fractions. The fractions are handled as data structures, defined by
the TYPE statement in the module-level code. This effectively allows
fractions, comprising a pair of long integer numerator and denominator
numbers, to be referenced as a new type of variable.
The demo module displays examples of the LeastComMul& and
GreatestComDiv& functions and then prompts you to enter fraction problems
involving the addition, subtraction, multiplication, or division of two
fractions. Enter the problems using the format displayed on screen. The
results, reduced to lowest terms, will be displayed.
Name Type Description
──────────────────────────────────────────────────────────────────────────
FRACTION.BAS Demo module
Fraction2String$ Func Converts type Fraction variable to a
string
FractionAdd Sub Adds two fractions and reduces
FractionDiv Sub Divides two fractions and reduces
FractionMul Sub Multiplies two fractions and reduces
FractionReduce Sub Reduces fraction to lowest terms
FractionSub Sub Subtracts two fractions and reduces
GreatestComDiv& Func Returns greatest common divisor
LeastComMul& Func Returns least common multiple
SplitFractions Sub Parses fraction problem string
String2Fraction Sub Converts a string to Fraction variable
──────────────────────────────────────────────────────────────────────────
Demo Module: FRACTION
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FRACTION **
' ** Type: Toolbox **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates a collection of functions and subprograms
' for working with fractions.
'
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: a Structure of type Fraction
' b Structure of type Fraction
' c Structure of type Fraction
' f$ Input string for fraction problems
' fa$ First fraction in string format
' fb$ Second fraction in string format
' operator$ Function indicator
' fc$ Resultant fraction in string output form
' Data structure definitions
TYPE Fraction
Num AS LONG
Den AS LONG
END TYPE
' Subprograms
DECLARE SUB FractionReduce (a AS Fraction)
DECLARE SUB String2Fraction (f$, a AS Fraction)
DECLARE SUB FractionAdd (a AS Fraction, b AS Fraction, c AS Fraction)
DECLARE SUB FractionDiv (a AS Fraction, b AS Fraction, c AS Fraction)
DECLARE SUB FractionMul (a AS Fraction, b AS Fraction, c AS Fraction)
DECLARE SUB FractionSub (a AS Fraction, b AS Fraction, c AS Fraction)
DECLARE SUB SplitFractions (f$, fa$, operator$, fb$)
' Functions
DECLARE FUNCTION Fraction2String$ (a AS Fraction)
DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
DECLARE FUNCTION LeastComMul& (n1&, n2&)
' Data structures
DIM a AS Fraction
DIM b AS Fraction
DIM c AS Fraction
' Demonstrate the LeastComMul& function
CLS
PRINT "LeastComMul&(21&, 49&) =", LeastComMul&(21&, 49&)
PRINT
' Demonstrate the GreatestComDiv& function
PRINT "GreatestComDiv&(21&, 49&) =", GreatestComDiv&(21&, 49&)
PRINT
' Demonstrate the fraction routines
DO
PRINT
PRINT "Enter a fraction problem, or simply press Enter"
PRINT "Example: 2/3 + 4/5"
PRINT
LINE INPUT f$
IF INSTR(f$, "/") = 0 THEN
EXIT DO
END IF
SplitFractions f$, fa$, operator$, fb$
String2Fraction fa$, a
String2Fraction fb$, b
SELECT CASE operator$
CASE "+"
FractionAdd a, b, c
CASE "-"
FractionSub a, b, c
CASE "*"
FractionMul a, b, c
CASE "/"
FractionDiv a, b, c
CASE ELSE
BEEP
END SELECT
fc$ = Fraction2String$(c)
PRINT "Result (reduced to lowest terms) is "; fc$
LOOP
──────────────────────────────────────────────────────────────────────────
Function: Fraction2String$
Returns a string representation of a fraction. The numerator and
denominator values are converted to strings by the QuickBASIC STR$
function, and a slash (/) is concatenated between them to form the
resultant string.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Fraction2String$ **
' ** Type: Function **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Converts a type Fraction variable to a string.
'
' EXAMPLE OF USE: fa$ = Fraction2String$(a)
' PARAMETERS: a Structure of type Fraction
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Fraction
' Num AS LONG
' Den AS LONG
' END TYPE
'
' DECLARE FUNCTION Fraction2String$ (a AS Fraction)
'
FUNCTION Fraction2String$ (a AS Fraction) STATIC
Fraction2String$ = STR$(a.Num) + "/" + STR$(a.Den)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: FractionAdd
Adds fraction a to fraction b, reduces the result to lowest terms, and
returns the result in fraction c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FractionAdd **
' ** Type: Subprogram **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Adds two fractions and reduces the result to lowest terms.
'
' EXAMPLE OF USE: FractionAdd a, b, c
' PARAMETERS: a First fraction to add
' b Second fraction to add
' c Resulting fraction
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Fraction
' Num AS LONG
' Den AS LONG
' END TYPE
'
' DECLARE SUB FractionReduce (a AS Fraction)
' DECLARE SUB FractionAdd (a AS Fraction, b AS Fraction, c AS Fractio
' DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
'
SUB FractionAdd (a AS Fraction, b AS Fraction, c AS Fraction)
c.Num = a.Num * b.Den + a.Den * b.Num
c.Den = a.Den * b.Den
FractionReduce c
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: FractionDiv
Divides fraction b into fraction a, reduces the result to lowest terms,
and returns the result in fraction c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FractionDiv **
' ** Type: Subprogram **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Divides two fractions and reduces the result to
' lowest terms.
'
' EXAMPLE OF USE: FractionDiv a, b, c
' PARAMETERS: a First fraction
' b Fraction to divide into first
' c Resulting fraction
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Fraction
' Num AS LONG
' Den AS LONG
' END TYPE
' DECLARE SUB FractionReduce (a AS Fraction)
' DECLARE SUB FractionDiv (a AS Fraction, b AS Fraction, c AS Fractio
' DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
'
SUB FractionDiv (a AS Fraction, b AS Fraction, c AS Fraction)
c.Num = a.Num * b.Den
c.Den = a.Den * b.Num
FractionReduce c
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: FractionMul
Multiplies fraction a times fraction b, reduces the result to lowest
terms, and returns the result in fraction c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FractionMul **
' ** Type: Subprogram **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Multiplies two fractions and reduces the result to
' lowest terms.
'
' EXAMPLE OF USE: FractionMul a, b, c
' PARAMETERS: a First fraction to multiply
' b Second fraction to multiply
' c Resulting fraction
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Fraction
' Num AS LONG
' Den AS LONG
' END TYPE
'
' DECLARE SUB FractionReduce (a AS Fraction)
' DECLARE SUB FractionMul (a AS Fraction, b AS Fraction, c AS Fractio
' DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
'
SUB FractionMul (a AS Fraction, b AS Fraction, c AS Fraction)
c.Num = a.Num * b.Num
c.Den = a.Den * b.Den
FractionReduce c
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: FractionReduce
Reduces a fraction to its lowest terms by dividing the numerator and
denominator by their greatest common divisor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FractionReduce **
' ** Type: Subprogram **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Reduces a fraction to its lowest terms.
'
' EXAMPLE OF USE: FractionReduce a
' PARAMETERS: a Fraction to reduce
' VARIABLES: d& Greatest common divisor of the numerator an
' denominator
' MODULE LEVEL
' DECLARATIONS: TYPE Fraction
' Num AS LONG
' Den AS LONG
' END TYPE
'
' DECLARE SUB FractionReduce (a AS Fraction)
' DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
'
SUB FractionReduce (a AS Fraction)
d& = GreatestComDiv&(a.Num, a.Den)
a.Num = a.Num / d&
a.Den = a.Den / d&
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: FractionSub
Subtracts fraction b from fraction a, reduces the result to lowest terms,
and returns the result in fraction c.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FractionSub **
' ** Type: Subprogram **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Subtracts two fractions and reduces the result to
' lowest terms.
'
' EXAMPLE OF USE: FractionSub a, b, c
' PARAMETERS: a First fraction
' b Fraction to subtract from the first
' c Resulting fraction
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: TYPE Fraction
' Num AS LONG
' Den AS LONG
' END TYPE
'
' DECLARE SUB FractionReduce (a AS Fraction)
' DECLARE SUB FractionSub (a AS Fraction, b AS Fraction, c AS Fractio
' DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
'
SUB FractionSub (a AS Fraction, b AS Fraction, c AS Fraction)
c.Num = a.Num * b.Den - a.Den * b.Num
c.Den = a.Den * b.Den
FractionReduce c
END SUB
──────────────────────────────────────────────────────────────────────────
Function: GreatestComDiv&
Returns the greatest common divisor of two long integers.
The greatest common divisor of the numerator and denominator of a fraction
is efficient for reducing the fraction to its lowest terms, as
demonstrated by the FractionReduce subprogram.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GreatestComDiv& **
' ** Type: Function **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the greatest common divisor of two long integers.
'
' EXAMPLE OF USE: gcd& = GreatestComDiv& (n1&, n2&)
' PARAMETERS: n1& First long integer
' n2& Second long integer
' VARIABLES: ta& Working copy of n1&
' tb& Working copy of n2&
' tc& Working variable
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION GreatestComDiv& (n1&, n2&)
'
FUNCTION GreatestComDiv& (n1&, n2&)
ta& = n1&
tb& = n2&
DO
tc& = ta& MOD tb&
ta& = tb&
tb& = tc&
LOOP WHILE tc&
GreatestComDiv& = ta&
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: LeastComMul&
Returns the least common multiple of two long integers.
Although this function is not used by any other routine in the
FRACTION.BAS module, it is included because of its close ties to the
GreatestComDiv& function. In fact, by using the GreatestComDiv& function
in the calculations, the LeastComMul& function is shortened to one program
line, as shown.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: LeastComMul& **
' ** Type: Function **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the least common multiple of two long integers.
'
' EXAMPLE OF USE: lcm& = LeastComMul& (n1&, n2&)
' PARAMETERS: n1& First long integer
' n2& Second long integer
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION LeastComMul& (n1&, n2&)
'
FUNCTION LeastComMul& (n1&, n2&)
LeastComMul& = ABS(n1& * n2& / GreatestComDiv&(n1&, n2&))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: SplitFractions
Splits the input fraction problem string into two fraction strings and one
operator string.
This subprogram has a special purpose in the FRACTION.BAS program. After
you enter a fraction problem, this subprogram splits your input into three
strings: a string representation of the first fraction, a one-character
symbol representing the desired mathematical operation, and a string
representation of the second fraction.
The results of this subprogram are passed to the String2Fraction
subprogram before the indicated calculations are performed.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SplitFractions **
' ** Type: Subprogram **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Splits an input fraction problem string into
' three strings representing each of the two
' fractions and a one-character string of the
' operation given.
'
' EXAMPLE OF USE: SplitFractions f$, fa$, operator$, fb$
' PARAMETERS: f$ Input string from the FRACTIONS demonstratio
' program
' fa$ First fraction, extracted from f$
' operator$ Mathematical operation symbol, from f$
' fb$ Second fraction, extracted from f$
' VARIABLES: i% Looping index
' ndx% Index to mathematical operation symbol
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB SplitFractions (f$, fa$, operator$, fb$)
'
SUB SplitFractions (f$, fa$, operator$, fb$)
fa$ = ""
fb$ = ""
operator$ = ""
FOR i% = 1 TO 4
ndx% = INSTR(f$, MID$("+-*/", i%, 1))
IF ndx% THEN
IF i% = 4 THEN
ndx% = INSTR(ndx% + 1, f$, "/")
END IF
fa$ = LEFT$(f$, ndx% - 1)
fb$ = MID$(f$, ndx% + 1)
operator$ = MID$(f$, ndx%, 1)
EXIT FOR
END IF
NEXT i%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: String2Fraction
Converts a string representation of a fraction to a data structure of type
Fraction. This routine is useful for converting user input of fractional
values to type Fraction variables.
This subprogram extracts numerator and denominator values from a string
representation of a fraction and fills in a data structure of type
Fraction with the results.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: String2Fraction **
' ** Type: Subprogram **
' ** Module: FRACTION.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Converts a string to a type Fraction variable.
'
' EXAMPLE OF USE: String2Fraction f$, a
' PARAMETERS: f$ String representation of a fraction
' a Structure of type Fraction
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB String2Fraction (f$, a AS Fraction)
'
SUB String2Fraction (f$, a AS Fraction)
a.Num = VAL(f$)
a.Den = VAL(MID$(f$, INSTR(f$, "/") + 1))
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
GAMES
The GAMES toolbox is a collection of subprograms and functions that
provide some common tasks for programming games with QuickBASIC.
QuickBASIC is an ideal language for developing many graphics- and
text-oriented games, partly because of the interactive nature of the
development process, and partly because of the excellent set of graphics
functions and subprograms provided by the language.
Name Type Description
──────────────────────────────────────────────────────────────────────────
GAMES.BAS Demo module
Card$ Func Returns name of card given a number from
1 through 52
Collision% Func Returns TRUE or FALSE collision condition
Dice% Func Returns total showing for throwing N dice
FillArray Sub Fills an integer array with a sequence of
numbers defined by the bounds
Shuffle$ Func Randomizes character bytes in a string
ShuffleArray Sub Randomizes integers in an array
──────────────────────────────────────────────────────────────────────────
Demo Module: GAMES
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GAMES **
' ** Type: Toolbox **
' ** Module: GAMES.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' REQUIREMENTS: CGA
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: a$ String containing the 26 letters of the
' alphabet
' x% Lower bound for array a%()
' y% Upper bound for array a%()
' a%() Array of numbers to be shuffled
' i% Looping index
' size% Dimension of bouncing ball array
' object%() Array for GET and PUT of bouncing ball
' backGround%() Array for GET and PUT of background
' dx% X velocity of bouncing ball
' dy% Y velocity of bouncing ball
' px% X coordinate of bouncing ball
' py% Y coordinate of bouncing ball
' testNumber% One of four bounce direction tests
' test% Result of the Collision% test
' Constants
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Functions
DECLARE FUNCTION Shuffle$ (a$)
DECLARE FUNCTION Dice% (numberOfDice%)
DECLARE FUNCTION Card$ (cardNumber%)
DECLARE FUNCTION Collision% (object%(), backGround%())
' Subprograms
DECLARE SUB FillArray (a%())
DECLARE SUB ShuffleArray (a%())
' Demonstration of the Shuffle$ function
CLS
RANDOMIZE TIMER
a$ = "abcdefghijklmnopqrstuvwxyz"
PRINT "a$ = "; a$
PRINT "Shuffle$(a$) = "; Shuffle$(a$)
PRINT
' Demonstration of the FillArray subprogram
x% = -7
y% = 12
DIM a%(x% TO y%)
PRINT "FillArray a%() where DIM a%( -7 TO 12) ..."
FillArray a%()
FOR i% = x% TO y%
PRINT a%(i%);
NEXT i%
PRINT
' Demonstration of the ShuffleArray subprogram
PRINT
PRINT "ShuffleArray a%() ..."
ShuffleArray a%()
FOR i% = x% TO y%
PRINT a%(i%);
NEXT i%
PRINT
' Demonstration of the Dice% function
PRINT
PRINT "Dice%(2)..."
FOR i% = 1 TO 20
PRINT Dice%(2);
NEXT i%
PRINT
' Deal a hand of seven cards
PRINT
PRINT "Seven random cards, without replacement..."
REDIM a%(1 TO 54)
FillArray a%()
ShuffleArray a%()
FOR i% = 1 TO 7
PRINT Card$(a%(i%))
NEXT i%
PRINT
' Wait for user to press a key
PRINT
PRINT "Press any key to continue"
DO
LOOP WHILE INKEY$ = ""
' Demonstration of the Collision% function
size% = 6
DIM object%(size%), backGround%(size%)
' Set medium resolution graphics mode
SCREEN 1
' Create the bouncing ball
CIRCLE (2, 2), 2, 3
PAINT (2, 2), 3
GET (0, 0)-(4, 4), object%
' Make solid border around screen
LINE (14, 18)-(305, 187), 1, B
PAINT (0, 0), 1
PRINT " Collision% function... Press any key to quit "
' Make three obstacles
CIRCLE (115, 78), 33, 2, , , .6
PAINT (115, 78), 2
CIRCLE (205, 78), 33, 2, , , .6
PAINT (205, 78), 2
LINE (90, 145)-(230, 155), 2, BF
' Initialize position and velocity of the object
dx% = 1
dy% = 1
px% = 160
py% = 44
PUT (px%, py%), object%
' Move the object around the screen, avoiding collisions,
' until any key is pressed
DO
testNumber% = 0
DO
PUT (px%, py%), object%
px% = px% + dx%
py% = py% + dy%
GET (px%, py%)-(px% + 4, py% + 4), backGround%
PUT (px%, py%), object%
test% = Collision%(object%(), backGround%())
IF test% THEN
testNumber% = testNumber% + 1
PUT (px%, py%), object%
px% = px% - dx%
py% = py% - dy%
SELECT CASE testNumber%
CASE 1
dx% = -dx%
CASE 2
dx% = -dx%
dy% = -dy%
CASE 3
dy% = -dy%
CASE ELSE
END SELECT
PUT (px%, py%), object%
END IF
LOOP UNTIL test% = 0
LOOP UNTIL INKEY$ <> ""
' Clean up a little
SCREEN 0
WIDTH 80
CLS
SYSTEM
──────────────────────────────────────────────────────────────────────────
Function: Card$
Returns the name of a card from a standard, 52-card deck, given a number
from 1 through 52.
The passed number is first checked to determine the suit. Numbers from 1
through 13 indicate a Spade, 14 through 26 a Club, and so on. The face
name or the number of the card is then determined using the MOD function.
If the number is less than 1 or greater than 52, this function returns
Joker, making it convenient to deal a 54-card deck if desired.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Card$ **
' ** Type: Function **
' ** Module: GAMES.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the name of a playing card given a number
' from 1 to 52. Any other number returns "Joker."
'
' EXAMPLE OF USE: PRINT Card$(n%)
' PARAMETERS: n% Number from 1 to 52 representing a card (an
' other number returns a Joker)
' VARIABLES: suit$ Name of one of the four card suits
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Card$ (cardNumber%)
'
FUNCTION Card$ (cardNumber%)
SELECT CASE (cardNumber% - 1) \ 13 ' Which suit?
CASE 0
suit$ = " of Spades"
CASE 1
suit$ = " of Clubs"
CASE 2
suit$ = " of Hearts"
CASE 3
suit$ = " of Diamonds"
CASE ELSE
Card$ = "Joker"
EXIT FUNCTION
END SELECT
SELECT CASE (cardNumber% - 1) MOD 13 ' Which card?
CASE 0
Card$ = "Ace" + suit$
CASE 1 TO 9
Card$ = MID$(STR$(cardNumber% MOD 13), 2) + suit$
CASE 10
Card$ = "Jack" + suit$
CASE 11
Card$ = "Queen" + suit$
CASE 12
Card$ = "King" + suit$
END SELECT
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Collision%
Returns -1 or 0 (TRUE or FALSE), indicating whether a collision or near
collision has occurred between two graphics objects stored in integer
arrays.
The graphics images are copied into the arrays by using the QuickBASIC GET
statement. If you're not familiar with using the GET and PUT statements to
create graphics animation, refer to your QuickBASIC documentation. These
two statements provide a powerful method for quickly moving or duplicating
graphics objects on your screen.
To use the Collision function, you must pass two integer arrays of the
same dimension. Normally, the background is copied into one array (using
the GET statement) just before the object stored in the second is PUT on
the screen at that same location. These two arrays are passed to the
Collision% function, and the returned result determines whether the object
overlaps (or very nearly overlaps) any pixel already on the screen.
The check for near collision of pixels proceeds as follows. The first
three integers in each array are skipped, as these integers contain
object-dimensioning information and don't represent any pixel. The
remaining integers from each array are compared to the corresponding
integers from the other. Pixels having color attribute 0 represent the
background and are stored in the integer array as one or more 0 bits. If
an integer is 0, then all the pixels it represents are of the background
color. If an integer is non-zero, then one or more of the pixels stored in
it have a non-zero color attribute. To make the collision check fast and
efficient, this function simply checks for non-zero bits in any
corresponding integers from the two arrays. The pixels might not actually
be overlapping, but they'll be very close neighbors. If a near collision
is detected, the remaining integers are not checked, and the function
returns a value of TRUE. If all the integers are checked and no collisions
are detected, the function returns FALSE.
The demonstration module shows one way to use the collision function to
allow objects to bounce off each other. The bouncing ball moves around
quickly in the "square face," bouncing off the mouth, eyes, and screen
edges. Try experimenting by changing the size of the mouth or eyes or by
drawing additional objects on the screen. The ball should bounce off any
non-zero pixel objects.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Collision% **
' ** Type: Function **
' ** Module: GAMES.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns TRUE if any non-zero pixels occur in the
' same byte of video memory, as saved in the object%()
' and backGround%() arrays. The arrays must be the
' same size.
'
' EXAMPLE OF USE: test% = Collision%(object%(), backGround%())
' PARAMETERS: object%() First array, filled in with the GET
' statement
' backGround%() Second array, filled in with the GET
' statement
' VARIABLES: lo% Lower bound of first array
' up% Upper bound of first array
' lb% Lower bound of second array
' ub% Upper bound of second array
' i% Index to integers in each array
' MODULE LEVEL
' DECLARATIONS: CONST FALSE = 0
' CONST TRUE = NOT FALSE
' DECLARE FUNCTION Collision% (object%(), backGround%())
'
FUNCTION Collision% (object%(), backGround%()) STATIC
lo% = LBOUND(object%)
uo% = UBOUND(object%)
lb% = LBOUND(backGround%)
ub% = UBOUND(backGround%)
IF lo% <> lb% OR uo% <> ub% THEN
PRINT "Error: Collision - The object and background"
PRINT "graphics arrays have different dimensions."
SYSTEM
END IF
FOR i% = lo% + 2 TO uo%
IF object%(i%) THEN
IF backGround%(i%) THEN
Collision% = TRUE
EXIT FUNCTION
END IF
END IF
NEXT i%
Collision% = FALSE
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Dice%
Returns a total for all dots that are showing when n% pseudorandom dice
are thrown.
The QuickBASIC RND function creates the pseudorandom sequence of
unpredictable numbers to simulate the dice. Unless you want the same
scores to show up every time a program is run, you should randomize the
QuickBASIC random number generator by using the RANDOMIZE statement.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Dice% **
' ** Type: Function **
' ** Module: GAMES.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the total of the dots showing when any
' desired number of dice are rolled.
'
' EXAMPLE OF USE: total% = Dice%(n%)
' PARAMETERS: n% Number of dice
' VARIABLES: toss% Loop index for throwing the n% dice
' total% Total of the dots showing
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Dice% (numberOfDice%)
'
FUNCTION Dice% (numberOfDice%)
IF numberOfDice% < 1 THEN
PRINT "Error: Dice%() - Can't throw fewer than one die"
SYSTEM
END IF
FOR toss% = 1 TO numberOfDice%
total% = total% + INT(RND * 6) + 1
NEXT toss%
Dice% = total%
END FUNCTION
──────────────────────────────────────────────────────────────────────────
GAMES
Subprogram: FillArray
Fills an integer array with a sequence of numbers defined by the bounds.
For example, consider these two statements:
DIM year%(1900 TO 1999)
FillArray year%
The array will be filled with year numbers from 1900 through 1999.
As a second example, consider an array dimensioned from 1 through 52.
After filling this array with the numbers 1 through 52, the array contents
can be shuffled efficiently with the ShuffleArray subprogram. The result
is a freshly shuffled deck of 52 cards. Pulling these random "cards"
sequentially from the array prevents duplication of a card.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FillArray **
' ** Type: Subprogram **
' ** Module: GAMES.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Initializes an integer array by putting i% into
' each i%th element.
'
' EXAMPLE OF USE: FillArray a%()
' PARAMETERS: a%() Array to be filled with a sequence of numbe
' VARIABLES: i% Looping index
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB FillArray (a%())
'
SUB FillArray (a%()) STATIC
FOR i% = LBOUND(a%) TO UBOUND(a%)
a%(i%) = i%
NEXT i%
END SUB
──────────────────────────────────────────────────────────────────────────
Function: Shuffle$
Shuffles the contents of a string by randomly swapping bytes throughout
the string.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Shuffle$ **
' ** Type: Function **
' ** Module: GAMES.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Randomizes the order of the character bytes in a$.
'
' EXAMPLE OF USE: b$ = Shuffle$(a$)
' PARAMETERS: a$ String to be shuffled
' VARIABLES: x$ Working string space
' lenx% Number of bytes in the string
' i% Pointer to each byte
' j% Pointer to randomly selected byte
' t$ Temporary byte-swapping string
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Shuffle$ (a$)
'
FUNCTION Shuffle$ (a$) STATIC
x$ = a$
lenx% = LEN(x$)
FOR i% = 1 TO lenx%
j% = INT(RND * lenx% + 1)
t$ = MID$(x$, i%, 1)
MID$(x$, i%, 1) = MID$(x$, j%, 1)
MID$(x$, j%, 1) = t$
NEXT i%
Shuffle$ = x$
x$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: ShuffleArray
Shuffles the contents of an integer array. The array dimensions are
automatically determined, and each integer entry is swapped with a
randomly selected second entry.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ShuffleArray **
' ** Type: Subprogram **
' ** Module: GAMES.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Randomizes the order of the integers in a%()
' by swapping contents in a pseudorandom order.
'
' EXAMPLE OF USE: ShuffleArray a%()
' PARAMETERS: a%() Array to be shuffled
' VARIABLES: lb% Lower bound of the array
' ub% Upper bound of the array
' range% Number of array entries
' i% Looping index
'
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB ShuffleArray (a%())
'
SUB ShuffleArray (a%()) STATIC
lb% = LBOUND(a%)
ub% = UBOUND(a%)
range% = ub% - lb% + 1
FOR i% = lb% TO ub%
SWAP a%(i%), a%(INT(RND * range% + lb%))
NEXT i%
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
HEX2BIN
The HEX2BIN program reads in a file containing hexadecimal notation and
creates a file containing the bytes that are indicated. Characters that
are not in the set of hexadecimal characters are ignored, and each byte is
assumed to be indicated by a pair of hexadecimal characters.
This program converts the hexadecimal format files created by the
BIN2HEX program into the object code files they represent. For example,
you can create the MOUSE.OBJ file from the MOUSE.HEX file if you don't
have the Microsoft Macro Assembler. (If you do have the Macro Assembler,
you should create MOUSE.OBJ directly from the MOUSE.ASM listing.)
The command line for performing this conversion (assuming you've compiled
HEX2BIN to an executable program to be run from the MS-DOS prompt) is:
HEX2BIN MOUSE.HEX MOUSE.OBJ
Refer to the BIN2HEX program for information about creating this and the
other .HEX files.
Program Module: HEX2BIN
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: HEX2BIN **
' ** Type: Program **
' ** Module: HEX2BIN.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Reads in a hexadecimal format file and writes out a binary
' file created from the hexadecimal byte numbers.
'
' USAGE: HEX2BIN inFileName.ext outFileName.ext
' .MAK FILE: HEX2BIN.BAS
' PARSE.BAS
' STRINGS.BAS
' PARAMETERS: inFileName.ext Name of hexadecimal format file to b
' outFileName.ext Name of file to be created
' VARIABLES: cmd$ Working copy of the command line
' inFile$ Name of input file
' outFile$ Name of output file
' h$ Pair of hexadecimal characters representing
' each byte
' i% Index into list of hexadecimal character pa
' byte$ Buffer for binary file access
DECLARE SUB ParseWord (a$, sep$, word$)
DECLARE FUNCTION FilterIn$ (a$, set$)
' Get the input and output filenames from the command line
cmd$ = COMMAND$
ParseWord cmd$, " ,", inFile$
ParseWord cmd$, " ,", outFile$
' Verify both filenames were given
IF outFile$ = "" THEN
PRINT
PRINT "Usage: HEX2BIN inFileName.ext outFileName.ext"
SYSTEM
END IF
' Open the input file
OPEN inFile$ FOR INPUT AS #1
' Truncate the output file if it already exists
OPEN outFile$ FOR OUTPUT AS #2
CLOSE #2
' Now open it for binary output
OPEN outFile$ FOR BINARY AS #2 LEN = 1
' Process each line of the hexadecimal file
DO
LINE INPUT #1, h$
h$ = FilterIn$(UCASE$(h$), "0123456789ABCDEF")
FOR i% = 1 TO LEN(h$) STEP 2
byte$ = CHR$(VAL("&H" + MID$(h$, i%, 2)))
PUT #2, , byte$
NEXT i%
LOOP WHILE NOT EOF(1)
' Clean up and quit
CLOSE
END
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
JUSTIFY
The JUSTIFY toolbox contains the subprogram, Justify, which pads a string
with spaces between words in a pseudorandom manner until the string is a
desired number of characters. This sounds simple, but the process is
surprisingly complicated. For example, the inserted spaces must fall
randomly between words, but it's desirable to keep the density of spaces
as even as possible. You wouldn't want five spaces between the first two
words and two spaces between the next two words.
The demo module prints a paragraph justified to three different widths. As
shown in the demo, the FormatTwo subprogram works hand in hand with the
Justify subprogram to format a long string into several smaller strings.
By padding the resulting shorter strings with a fixed number of spaces on
the left, you're able to format paragraphs of text between arbitrary
margins. Refer to .MAK FILE in the comment lines of the listing to see the
other modules you must load for this program to run correctly.
Name Type Description
──────────────────────────────────────────────────────────────────────────
JUSTIFY.BAS Demo module
Justify Sub Adjusts strings to specified widths
──────────────────────────────────────────────────────────────────────────
Demo Module: JUSTIFY
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: JUSTIFY **
' ** Type: Toolbox **
' ** Module: JUSTIFY.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates the Justify subprogram.
'
' USAGE: No command line parameters
' .MAK FILE: JUSTIFY.BAS
' EDIT.BAS
' PARSE.BAS
' KEYS.BAS
' PARAMETERS: (none)
' VARIABLES: a$ String to be justified
' col% Number of columns for each example of Justify
' x$ Working copy of a$
' y$ Working string space
DECLARE SUB Justify (a$, n%)
DECLARE SUB ParseLine (x$, sep$, a$())
DECLARE SUB FormatTwo (a$, b$, col%)
CLS
a$ = ""
a$ = a$ + "This paragraph is used to demonstrate the Justify "
a$ = a$ + "subprogram. First, the entire paragraph is "
a$ = a$ + "placed in a single string variable. This string "
a$ = a$ + "is then split between words into shorter strings, "
a$ = a$ + "and these shorter strings are then justified in "
a$ = a$ + "order to align both the left and right edges of "
a$ = a$ + "the text."
FOR col% = 50 TO 70 STEP 10
x$ = a$
DO
FormatTwo x$, y$, col%
IF y$ <> "" THEN
Justify x$, col%
END IF
PRINT x$
x$ = y$
LOOP WHILE y$ <> ""
PRINT
NEXT col%
END
──────────────────────────────────────────────────────────────────────────
Subprogram: Justify
Inserts spaces between words until the given string is the desired length.
Spaces are not added before the first word or after the last word,
resulting in a string that is left- and right-justified to the length
indicated.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Justify **
' ** Type: Subprogram **
' ** Module: JUSTIFY.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Spaces words with extra spaces until line
' is n% characters long.
'
' EXAMPLE OF USE: Justify a$, n%
' PARAMETERS: a$ String to be justified
' n% Desired string length
' VARIABLES: ary$() Array to store individual words from the st
' cnt% Count of non-space characters
' i% Looping index
' j% Count of words
' each% Minimum space count to insert between words
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Justify (a$, n%)
' DECLARE SUB ParseLine (x$, sep$, a$())
' DECLARE SUB FormatTwo (a$, b$, col%)
'
SUB Justify (a$, n%) STATIC
' If string is shorter than n%, don't bother
IF LEN(a$) < n% THEN
EXIT SUB
END IF
' Array for list of words from original string
REDIM ary$(1 TO n%)
' Split line up into individual words
ParseLine a$, " ", ary$()
' Count the words and total of non-space characters
cnt% = 0
FOR i% = n% TO 1 STEP -1
cnt% = cnt% + LEN(ary$(i%))
IF ary$(i%) = "" THEN
j% = i% - 1
END IF
NEXT i%
' If only one or zero words, there's not much we can do
IF j% < 2 THEN
a$ = LEFT$(ary$(1) + SPACE$(n%), n%)
EXIT SUB
END IF
' We want an extra space at the ends of sentences, questions, etc.
FOR i% = 1 TO j% - 1
IF INSTR(".!?", RIGHT$(ary$(i%), 1)) THEN
ary$(i%) = ary$(i%) + " "
cnt% = cnt% + 1
END IF
NEXT i%
' How many spaces minimum to add to each word?
each% = (n% - cnt%) \ (j% - 1)
' Tack on the minimum spaces to each word
FOR i% = 1 TO j% - 1
ary$(i%) = ary$(i%) + SPACE$(each%)
cnt% = cnt% + each%
NEXT i%
' Which is quicker, adding remaining spaces, or
' adding spaces to all and removing a few of them?
IF (n% - cnt%) < j% \ 2 THEN
' We'll add a few spaces at random
DO UNTIL cnt% = n%
DO
i% = INT(RND * (j% - 1) + 2)
LOOP UNTIL LEFT$(ary$(i%), 1) <> " "
ary$(i%) = " " + ary$(i%)
cnt% = cnt% + 1
LOOP
ELSE
' We'll add a space to each, and then remove some at random
FOR i% = 2 TO j%
ary$(i%) = " " + ary$(i%)
cnt% = cnt% + 1
NEXT i%
' Now we'll take a few away at random
DO UNTIL cnt% = n%
DO
i% = INT(RND * (j% - 1) + 2)
LOOP UNTIL LEFT$(ary$(i%), 1) = " "
ary$(i%) = MID$(ary$(i%), 2)
cnt% = cnt% - 1
LOOP
END IF
' Glue it all back together
a$ = ary$(1)
FOR i% = 2 TO j%
a$ = a$ + ary$(i%)
NEXT i%
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
KEYS
The KEYS toolbox performs two enhanced keyboard input functions. It prints
the unique integer number returned by the KeyCode% or InKeyCode%
function for any key pressed. Run the program and press a few keys to see
the numbers. To try the InKeyCode% function for one second at a time,
press the Escape key followed immediately by other keys.
The QuickBASIC INKEY$ function returns a string of zero, one, or two
characters, depending on whether a key was pressed and whether the key has
an extended key code. The two functions presented here always return a
unique integer for any key pressed, even if the key normally returns an
extended key code. For example, pressing the letter "a" returns 97, F1
returns 15104, Alt-F1 returns 26624, and the Home key returns 18176. Run
the program to determine other returned values.
The EDIT.BAS module uses these functions and presents a table of CONST
statements that define several common editing keys. Note that most
standard alphanumeric keys return the expected ASCII code number.
Name Type Description
──────────────────────────────────────────────────────────────────────────
KEYS.BAS Demo module
InKeyCode% Func Returns unique integer for any key pressed
KeyCode% Func Waits and returns integer value for key
──────────────────────────────────────────────────────────────────────────
Demo Module: KEYS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: KEYS **
' ** Type: Toolbox **
' ** Module: KEYS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates keyboard access functions.
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: kee% Unique integer returned by KeyCode% and
' InKeyCode%
DECLARE FUNCTION KeyCode% ()
DECLARE FUNCTION InKeyCode% ()
CLS
PRINT "Press any key to see the unique number returned by KeyCode%."
PRINT "Press Esc to see InKeyCode% results for 1 second."
PRINT "Press Esc twice in a row to quit."
PRINT
DO
kee% = KeyCode%
PRINT kee%
IF kee% = 27 THEN
t0 = TIMER
DO
kee% = InKeyCode%
PRINT kee%;
IF kee% THEN
PRINT
END IF
IF kee% = 27 THEN
quitFlag% = -1
t0 = t0 - 1
END IF
LOOP UNTIL TIMER - t0 > 1
PRINT
END IF
LOOP UNTIL quitFlag%
END
──────────────────────────────────────────────────────────────────────────
Function: InKeyCode%
Immediately returns a unique integer for any key pressed or 0 if no key
was pressed.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: InKeyCode% **
' ** Type: Function **
' ** Module: KEYS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a unique integer for any key pressed or
' a zero if no key was pressed.
'
' EXAMPLE OF USE: k% = InKeyCode%
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION KeyCode% ()
'
FUNCTION InKeyCode% STATIC
InKeyCode% = CVI(INKEY$ + STRING$(2, 0))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: KeyCode%
Waits until a key is pressed, and then returns the unique key-code integer
for each key on the keyboard.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: KeyCode% **
' ** Type: Function **
' ** Module: KEYS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a unique integer for any key pressed.
'
' EXAMPLE OF USE: k% = KeyCode%
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION KeyCode% ()
'
FUNCTION KeyCode% STATIC
DO
k$ = INKEY$
LOOP UNTIL k$ <> ""
KeyCode% = CVI(k$ + CHR$(0))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
LOOK
The LOOK program is a utility for viewing text-file contents. The program
displays ASCII text-file contents and provides limited keyboard control to
allow scrolling or paging through files.
This program presents the FileRead subprogram for reading ASCII files
into an array of strings and also demonstrates the VIEW PRINT statement
for limiting printing and scrolling of text to only those display lines
desired.
Name Type Description
──────────────────────────────────────────────────────────────────────────
LOOK.BAS Program module
FileRead Sub Reads lines of ASCII files into an array
──────────────────────────────────────────────────────────────────────────
Program Module: LOOK
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: LOOK **
' ** Type: Program **
' ** Module: LOOK.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: LOOK filename.ext
' .MAK FILE: LOOK.BAS
' KEYS.BAS
' PARAMETERS: filename.ext Name of file to view
' VARIABLES: a$() Array of lines from the file
' fileName$ Name of file, from the command line
' lineCount% Count of lines read from the file
' linePtr% First file line currently on the display
' i% Loop index for printing 24 lines
' quitFlag% Indicates Escape key press
' updateFlag% Indicates if update of screen is necessa
' Constants
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Key code numbers
CONST UPARROW = 18432
CONST DOWNARROW = 20480
CONST PGUP = 18688
CONST PGDN = 20736
CONST HOME = 18176
CONST ENDKEY = 20224
CONST ESCAPE = 27
' Functions
DECLARE FUNCTION KeyCode% ()
' Subprograms
DECLARE SUB FileRead (fileName$, lineCount%, a$())
' Dimension string array
' NOTE:
' Must be dimensioned big enough to read in all lines from the file
DIM a$(1 TO 2000)
' Get the command line parameters
fileName$ = COMMAND$
' Read in the file
ON ERROR GOTO FileError
FileRead fileName$, lineCount%, a$()
ON ERROR GOTO 0
' Prepare the screen
SCREEN 0, 0, 0, 0
CLS
' Set line pointer
linePtr% = 1
' Main loop
DO
' Print information bar at top
VIEW PRINT 1 TO 1
COLOR 0, 3
LOCATE 1, 1
PRINT " Line:"; LEFT$(STR$(linePtr%) + SPACE$(7), 8);
PRINT "File: "; LEFT$(fileName$ + SPACE$(19), 19);
PRINT "Quit: ESC"; SPACE$(3);
PRINT "Move: "; CHR$(24); " "; CHR$(25); " PGUP PGDN HOME END ";
' Update the 24 lines of text
VIEW PRINT 2 TO 25
COLOR 7, 1
FOR i% = 0 TO 23
LOCATE i% + 2, 1
PRINT LEFT$(a$(i% + linePtr%) + SPACE$(80), 80);
NEXT i%
' Wait for a meaningful key to be pressed
SELECT CASE KeyCode%
CASE UPARROW
IF linePtr% > 1 THEN
linePtr% = linePtr% - 1
END IF
CASE DOWNARROW
IF linePtr% < lineCount% THEN
linePtr% = linePtr% + 1
END IF
CASE PGUP
IF linePtr% > 1 THEN
linePtr% = linePtr% - 24
IF linePtr% < 1 THEN
linePtr% = 1
END IF
END IF
CASE PGDN
IF linePtr% < lineCount% - 24 THEN
linePtr% = linePtr% + 24
IF linePtr% > lineCount% THEN
linePtr% = lineCount%
END IF
END IF
CASE HOME
IF linePtr% > 1 THEN
linePtr% = 1
END IF
CASE ENDKEY
IF linePtr% < lineCount% - 24 THEN
linePtr% = lineCount% - 24
END IF
CASE ESCAPE
quitFlag% = TRUE
CASE ELSE
updateFlag% = FALSE
END SELECT
LOOP UNTIL quitFlag%
' Set color back to normal
COLOR 7, 0
END
FileError:
PRINT
PRINT "Usage: LOOK filename.ext"
SYSTEM
RESUME NEXT
──────────────────────────────────────────────────────────────────────────
Subprogram: FileRead
Reads all lines of a text (ASCII) file into a string array and returns the
array of lines from the file and the count of the read lines.
The string array must be large enough to hold all the lines from the file.
The file to read must be readable using the LINE INPUT statement.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FileRead **
' ** Type: Subprogram **
' ** Module: LOOK.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Reads lines of an ASCII file into a$(). The
' lineCount% is set to the number of lines read
' in. If a$() wasn't dimensioned large enough,
' then lineCount% will be set to -1.
'
' EXAMPLE OF USE: FileRead fileName$, lineCount%, a$()
' PARAMETERS: fileName$ Name of file to be read into the array
' lineCount% Returned count of lines read from the fi
' a$() String array of file contents
' VARIABLES: FileNumber% Next available free file number
' i% Index for string array
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB FileRead (fileName$, lineCount%, a$())
'
SUB FileRead (fileName$, lineCount%, a$()) STATIC
FileNumber% = FREEFILE
OPEN fileName$ FOR INPUT AS FileNumber%
FOR i% = LBOUND(a$) TO UBOUND(a$)
LINE INPUT #FileNumber%, a$(i%)
lineCount% = i%
IF EOF(FileNumber%) THEN
EXIT FOR
END IF
NEXT i%
IF NOT EOF(FileNumber%) THEN
lineCount% = -1
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
MONTH
The MONTH program demonstrates how to use the CALENDAR.BAS toolbox to
perform calendar-related calculations.
When MONTH is run, a display of three one-month calendars is created. The
current system date determines the second month displayed; the previous
and next month are also shown.
Included with the display are instructions on how to increment or
decrement the years or months. Press the lowercase y key to display the
same three months of the previous year. Press a shifted (uppercase) Y to
increment the year. In the same way, press M to increment or m to
decrement the range of months displayed.
Program Module: MONTH
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MONTH **
' ** Type: Program **
' ** Module: MONTH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Creates and displays a three-month calendar.
' USAGE: No command line parameters
' .MAK FILE: MONTH.BAS
' CALENDAR.BAS
' PARAMETERS: (none)
' VARIABLES: year% Year of concern
' month% Month of concern
' quitFlag% Indicates that program is to terminate
' day% Day near middle of the month
' d2$ Date for second calendar month
' j2& Julian day number for second calendar month
' d1$ Date for first calendar month
' j1& Julian day number for first calendar month
' d3$ Date for third calendar month
' j3& Julian day number for third calendar month
' k$ Key press character
' Constants
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Functions
DECLARE FUNCTION Date2Julian& (dat$)
DECLARE FUNCTION MDY2Date$ (month%, day%, year%)
DECLARE FUNCTION Date2Year% (dat$)
DECLARE FUNCTION Date2Month% (dat$)
DECLARE FUNCTION Julian2Date$ (julian&)
' Subprograms
DECLARE SUB OneMonthCalendar (dat$, row%, col%)
' Get today's month and year
year% = Date2Year%(DATE$)
month% = Date2Month%(DATE$)
' Make calendars until the Esc key is pressed
DO UNTIL quitFlag%
' Get Julian day number for about the middle of the month
day% = 15
d2$ = MDY2Date$(month%, day%, year%)
j2& = Date2Julian&(d2$)
' Get last month's date
j1& = j2& - 30
d1$ = Julian2Date$(j1&)
' Get next month's date
j3& = j2& + 30
d3$ = Julian2Date$(j3&)
' Display the heading
CLS
LOCATE 1, 57
PRINT "THREE-MONTH CALENDAR"
LOCATE 2, 57
PRINT "QuickBASIC 4.0"
' Create the three calendar sheets
OneMonthCalendar d1$, 1, 1
OneMonthCalendar d2$, 8, 25
OneMonthCalendar d3$, 15, 49
' Display the instructions
LOCATE 17, 1
PRINT "Press <Y> to increment the year"
LOCATE 18, 1
PRINT "Press <y> to decrement the year"
LOCATE 19, 1
PRINT "Press <M> to increment the months"
LOCATE 20, 1
PRINT "Press <m> to decrement the months"
LOCATE 22, 1
PRINT "Press the Esc key to quit"
' Wait for a keystroke
DO
k$ = INKEY$
LOOP UNTIL k$ <> ""
' Check for appropriate keystroke
SELECT CASE k$
CASE "y"
year% = year% - 1
CASE "Y"
year% = year% + 1
CASE "m"
month% = month% - 3
CASE "M"
month% = month% + 3
CASE CHR$(27)
quitFlag% = TRUE
CASE ELSE
END SELECT
' Adjust month for proper range
IF month% < 1 THEN
month% = month% + 12
year% = year% - 1
ELSEIF month% > 12 THEN
month% = month% - 12
year% = year% + 1
END IF
LOOP
' All done
END
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
MOUSGCRS
The MOUSGCRS program is a utility for designing graphics-mode mouse
cursors.
This program lets you create new graphics-mode cursors for programs that
use the Microsoft Mouse. The program's output is a QuickBASIC subprogram
file that other programs can load and use. To run MOUSGCRS, your computer
must have CGA graphics capability and a mouse.
This module can also be used as a toolbox for choosing any of the
predefined cursors. For an example of a program using this module as a
toolbox, see the OBJECT.BAS utility program.
The MOUSE.ASM subprogram must be assembled and loaded with the QuickBASIC
environment for this program to run correctly. See the MOUSE.ASM
subprogram in Part III of this book for more information on loading this
routine.
Two masks are displayed while this program is running. The memory-resident
mouse driver uses the screen mask to define areas of the cursor where the
background pixels are to be left alone (0s) or blanked out (1s) before the
cursor mask is displayed. Often, the screen-mask pixels define an area of
the same shape but slightly larger than the cursor mask, creating an
outline around the cursor when it's located on a pure white background.
To edit a cursor, click with either the left or right mouse button on any
of the small squares that make up the two masks. The left button sets
pixel locations on, and the right button sets them off. To change the hot
spot to a new location, press both mouse buttons simultaneously.
When you're ready to try out your cursor creation, click on the "Try new
cursor" box. A solid white area at the right side of the screen lets you
view your new cursor against a white background.
Click on the "Try standard cursors" box to select one of the predefined
cursors. Each time you click on this box, the cursor changes to the next
available predefined cursor type, allowing you to preview them all.
When you click on the "Create cursor subroutine" box, the currently
defined cursor masks are written to a QuickBASIC subprogram source file
named GCURSOR.BAS. This file can be loaded by or merged with any program
in which you want to use the new cursor. To create more than one cursor,
be sure to rename the GCURSOR.BAS file after creating each cursor
subprogram.
Program Module: MOUSGCRS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MOUSGCRS **
' ** Type: Program **
' ** Module: MOUSGCRS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Program for the interactive design of graphics-
' mode mouse cursor subroutines.
'
' USAGE: No command line parameters
' REQUIREMENTS: CGA
' MIXED.QLB/.LIB
' Mouse
' .MAK FILE: MOUSGCRS.BAS
' BITS.BAS
' MOUSSUBS.BAS
' PARAMETERS: (none)
' VARIABLES: curs$() Array of binary cursor string data
' defaultMask$ Pattern mask for the default cursor
' xdef% Default hot spot X value
' ydef% Default hot spot Y value
' mask$ Pattern mask for a cursor
' xHot% Hot spot X value
' yHot% Hot spot Y value
' maskChr% Index into the pattern mask
' maskPtr% Index to the background or foreground mas
' pattern
' y% Cursor bit pointer, vertical
' x% Cursor bit pointer, horizontal
' xbox% X location on screen for cursor bit box
' ybox% Y location on screen for cursor bit box
' xh% Screen X location for hot spot
' yh% Screen Y location for hot spot
' click$ DRAW string for creating the click boxes
' quitFlag% Indication that user wants to quit
' t$ Copy of TIME$
' toggle% Once per second toggle for hot spot visib
' pxl% Pixel value at the hot spot
' leftButton% Current state of the left mouse button
' rightButton% Current state of the right mouse button
' resetBox% Indicates cursor is in the "Try standard
' cursors" box
' tryBox% Indicates cursor is in the "Try new curso
' box
' subBox% Indicates cursor is in the "Create cursor
' subroutine" box
' quitBox% Indicates cursor is in the "Quit" box
' xold% X location of just-modified pixel box
' yold% Y location of just-modified pixel box
' ix% X bit pointer for pixel change
' iy% Y bit pointer for pixel change
' q$ Double-quote character
' Define constants
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Subprograms
DECLARE SUB Cursdflt (mask$, xHot%, yHot%)
DECLARE SUB Curschek (mask$, xHot%, yHot%)
DECLARE SUB Curshand (mask$, xHot%, yHot%)
DECLARE SUB Curshour (mask$, xHot%, yHot%)
DECLARE SUB Cursjet (mask$, xHot%, yHot%)
DECLARE SUB Cursleft (mask$, xHot%, yHot%)
DECLARE SUB Cursplus (mask$, xHot%, yHot%)
DECLARE SUB Cursup (mask$, xHot%, yHot%)
DECLARE SUB Cursx (mask$, xHot%, yHot%)
DECLARE SUB MouseShow ()
DECLARE SUB MouseNow (lbutton%, rbutton%, xMouse%, yMouse%)
DECLARE SUB MouseHide ()
DECLARE SUB MouseMaskTranslate (mask$, xHot%, yHot%, cursor$)
DECLARE SUB MouseSetGcursor (cursor$)
' Arrays
DIM curs$(0 TO 8)
' Initialization
SCREEN 2
CLS
' Create set of cursors
Cursdflt defaultMask$, xdef%, ydef%
MouseMaskTranslate defaultMask$, xdef%, ydef%, curs$(0)
Curschek mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, curs$(1)
Curshand mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, curs$(2)
Curshour mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, curs$(3)
Cursjet mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, curs$(4)
Cursleft mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, curs$(5)
Cursplus mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, curs$(6)
Cursup mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, curs$(7)
Cursx mask$, xHot%, yHot%
MouseMaskTranslate mask$, xHot%, yHot%, curs$(8)
' Set the default cursor
MouseSetGcursor curs$(0)
' Make the default cursor the starting point for editing
mask$ = defaultMask$
xHot% = xdef%
yHot% = ydef%
' Place titles above pixel boxes
LOCATE 2, 22, 0
PRINT "Screen mask";
LOCATE 2, 50, 0
PRINT "Cursor mask";
' Outline the pixel boxes, filling the "ones" using the Mask$
maskChr% = 0
FOR maskPtr% = 0 TO 1
FOR y% = 1 TO 16
FOR x% = 1 TO 16
xbox% = x% * 12 + maskPtr% * 222 + 107
ybox% = y% * 9 + 10
maskChr% = maskChr% + 1
LINE (xbox%, ybox%)-(xbox% + 12, ybox% + 9), 1, B
IF MID$(mask$, maskChr%, 1) = "1" THEN
LINE (xbox% + 3, ybox% + 2)-(xbox% + 9, ybox% + 7), 1,
END IF
IF maskPtr% = 0 THEN
IF x% = xHot% + 1 AND y% = yHot% + 1 THEN
xh% = xbox%
yh% = ybox%
END IF
END IF
NEXT x%
NEXT y%
NEXT maskPtr%
' Instruction text at bottom of display
LOCATE 23, 1
PRINT TAB(16); "Left button Right button Both buttons"
PRINT TAB(16); "to set pixel to clear pixel for hot spot";
' Print menu items
LOCATE 3, 2, 0
PRINT "Try";
LOCATE 4, 2, 0
PRINT "standard";
LOCATE 5, 2, 0
PRINT "cursors";
LOCATE 9, 2, 0
PRINT "Try new";
LOCATE 10, 2, 0
PRINT "cursor";
LOCATE 14, 2, 0
PRINT "Create"
LOCATE 15, 2, 0
PRINT "cursor";
LOCATE 16, 2, 0
PRINT "subroutine";
LOCATE 16, 74, 0
PRINT "Quit";
' Make click box draw string
click$ = "R20D10L20U10BF5BR1F3E6"
' Draw the click boxes
DRAW "BM20,45" + click$
DRAW "BM20,85" + click$
DRAW "BM20,132" + click$
DRAW "BM592,132" + click$
' Make a white cursor testing area
LOCATE 5, 71
PRINT "Cursor";
LOCATE 6, 71
PRINT "viewing";
LOCATE 7, 71
PRINT "area";
LINE (560, 60)-(610, 100), 1, BF
' Turn on the mouse
MouseShow
' Main processing loop control
DO
GOSUB MainLoop
LOOP UNTIL quitFlag%
' Exit the loop and end program because Quitflag% has been set
CLS
SYSTEM
' Main processing loop
MainLoop:
' Toggle the hot spot once per second
IF t$ <> TIME$ THEN
t$ = TIME$
IF toggle% = 1 THEN
toggle% = 0
ELSE
toggle% = 1
END IF
pxl% = POINT(xh% + 3, yh% + 2) XOR toggle%
LINE (xh% + 5, yh% + 3)-(xh% + 7, yh% + 6), pxl%, BF
pxl% = POINT(xh% + 3 + 222, yh% + 2) XOR toggle%
LINE (xh% + 5 + 222, yh% + 3)-(xh% + 7 + 222, yh% + 6), pxl%, BF
END IF
' What is the mouse location and button state right now?
MouseNow leftButton%, rightButton%, x%, y%
' Are both buttons being pressed right now?
IF leftButton% AND rightButton% THEN
GOSUB WhichBox
IF xbox% THEN
GOSUB SetHotSpot
END IF
END IF
' Are we traversing the "Try standard cursors" click box?
IF x% > 20 AND x% < 40 AND y% > 45 AND y% < 55 THEN
IF resetBox% = 0 THEN
MouseHide
resetBox% = 1
LINE (17, 43)-(43, 57), 1, B
MouseShow
END IF
ELSE
IF resetBox% = 1 THEN
MouseHide
resetBox% = 0
LINE (17, 43)-(43, 57), 0, B
MouseShow
END IF
END IF
' Are we traversing the "Try new cursor" click box?
IF x% > 20 AND x% < 40 AND y% > 85 AND y% < 95 THEN
IF tryBox% = 0 THEN
MouseHide
tryBox% = 1
LINE (17, 83)-(43, 97), 1, B
MouseShow
END IF
ELSE
IF tryBox% = 1 THEN
MouseHide
tryBox% = 0
LINE (17, 83)-(43, 97), 0, B
MouseShow
END IF
END IF
' Are we traversing the "Create cursor subroutine" click box?
IF x% > 20 AND x% < 40 AND y% > 132 AND y% < 142 THEN
IF subBox% = 0 THEN
MouseHide
subBox% = 1
LINE (17, 130)-(43, 144), 1, B
MouseShow
END IF
ELSE
IF subBox% = 1 THEN
MouseHide
subBox% = 0
LINE (17, 130)-(43, 144), 0, B
MouseShow
END IF
END IF
' Are we traversing the "Quit" click box?
IF x% > 592 AND x% < 612 AND y% > 132 AND y% < 142 THEN
IF quitBox% = 0 THEN
MouseHide
quitBox% = 1
LINE (589, 130)-(615, 144), 1, B
MouseShow
END IF
ELSE
IF quitBox% = 1 THEN
MouseHide
quitBox% = 0
LINE (589, 130)-(615, 144), 0, B
MouseShow
END IF
END IF
' If just one button or the other is pressed, then check further
IF leftButton% XOR rightButton% THEN
GOSUB ButtonWasPressed
ELSE
xold% = 0
yold% = 0
END IF
' End of main loop
RETURN
' Is the mouse currently pointing at a pixel box?
WhichBox:
IF x% > 320 THEN
maskPtr% = 1
x% = x% - 222
ELSE
maskPtr% = 0
END IF
ix% = (x% - 107) \ 12
iy% = (y% - 10) \ 9
xbox% = 0
ybox% = 0
IF ix% >= 1 AND ix% <= 16 THEN
IF iy% >= 1 AND iy% <= 16 THEN
xbox% = ix% * 12 + maskPtr% * 222 + 107
ybox% = iy% * 9 + 10
END IF
END IF
RETURN
' Move the hot spot to the current pixel box
SetHotSpot:
IF (xbox% <> xh% AND xbox% - 222 <> xh%) OR ybox% <> yh% THEN
MouseHide
pxl% = POINT(xh% + 3, yh% + 2)
LINE (xh% + 5, yh% + 3)-(xh% + 7, yh% + 6), pxl%, BF
pxl% = POINT(xh% + 3 + 222, yh% + 2)
LINE (xh% + 5 + 222, yh% + 3)-(xh% + 7 + 222, yh% + 6), pxl%, BF
MouseShow
IF xbox% > 320 THEN
xh% = xbox% - 222
ELSE
xh% = xbox%
END IF
yh% = ybox%
END IF
RETURN
' Process the button press depending on mouse location
ButtonWasPressed:
IF quitBox% THEN
GOSUB DoQuitBox
ELSEIF resetBox% THEN
GOSUB DoResetCursor
ELSEIF tryBox% THEN
GOSUB DoSetNewCursor
ELSEIF subBox% THEN
GOSUB DoSetNewCursor
GOSUB DoCreateSub
ELSE
GOSUB DoPixelControl
END IF
RETURN
' Button was pressed while mouse was in the "Quit" box
DoQuitBox:
MouseHide
quitFlag% = TRUE
RETURN
' Button was pressed while mouse was in the "Try new cursor" box
DoSetNewCursor:
MouseHide
maskChr% = 0
FOR maskPtr% = 0 TO 1
FOR y% = 1 TO 16
FOR x% = 1 TO 16
xbox% = x% * 12 + maskPtr% * 222 + 107
ybox% = y% * 9 + 10
maskChr% = maskChr% + 1
IF POINT(xbox% + 3, ybox% + 2) THEN
MID$(mask$, maskChr%, 1) = "1"
ELSE
MID$(mask$, maskChr%, 1) = "0"
END IF
IF xbox% = xh% AND ybox% = yh% THEN
xHot% = x% - 1
yHot% = y% - 1
END IF
NEXT x%
NEXT y%
NEXT maskPtr%
MouseMaskTranslate mask$, xHot%, yHot%, cursor$
MouseSetGcursor cursor$
MouseShow
RETURN
' Button was pressed while mouse was in the "Try standard cursors" box
DoResetCursor:
MouseHide
cursorIndex% = (cursorIndex% + 1) MOD 9
MouseSetGcursor curs$(cursorIndex%)
MouseShow
DO
MouseNow leftButton%, rightButton%, xMouse%, yMouse%
LOOP UNTIL leftButton% = 0 AND rightButton% = 0
RETURN
' Button was pressed while mouse was in the "Create cursor subroutine" bo
DoCreateSub:
q$ = CHR$(34)
OPEN "GCURSOR.BAS" FOR OUTPUT AS #1
PRINT #1, " ' ************************************************"
PRINT #1, " ' ** Name: Gcursor **"
PRINT #1, " ' ** Type: Subprogram **"
PRINT #1, " ' ** Module: GCURSOR.BAS **"
PRINT #1, " ' ** Language: Microsoft QuickBASIC 4.00 **"
PRINT #1, " ' ************************************************"
PRINT #1, " '"
PRINT #1, " SUB Gcursor (mask$, xHot%, yHot%) STATIC"
PRINT #1, ""
PRINT #1, " mask$ = "; q$; q$
FOR i% = 0 TO 31
PRINT #1, " mask$ = mask$ + ";
PRINT #1, q$; MID$(mask$, 16 * i% + 1, 16); q$
IF i% = 15 THEN
PRINT #1, ""
END IF
NEXT i%
PRINT #1, ""
PRINT #1, " xHot% ="; STR$(xHot%)
PRINT #1, " yHot% ="; STR$(yHot%)
PRINT #1, ""
PRINT #1, " END SUB"
RETURN
' Set or clear pixel box if mouse is on one
DoPixelControl:
GOSUB WhichBox
IF xbox% THEN
IF xold% <> xbox% OR yold% <> ybox% THEN
xold% = xbox%
yold% = ybox%
MouseHide
IF leftButton% THEN
LINE (xbox% + 3, ybox% + 2)-(xbox% + 9, ybox% + 7), 1, BF
ELSE
LINE (xbox% + 3, ybox% + 2)-(xbox% + 9, ybox% + 7), 0, BF
END IF
MouseShow
END IF
END IF
RETURN
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
MOUSSUBS
The MOUSSUBS toolbox presents a collection of subprograms for accessing
and using your mouse. Your computer must have CGA graphics capability and
a mouse for this program to be useful. If you have a mouse but are limited
to monochrome text modes, see the MOUSTCRS.BAS module.
The assembly-language subroutine named MOUSE.ASM must be assembled and
linked with these routines or included in the user library loaded with
QuickBASIC. See the MOUSE.ASM subprogram description in Part III of this
book for more information on doing this.
To use these subprograms in your own programs, load this module (along
with the MOUSE.ASM routine), and be sure to declare the subprograms used
by your main program module. For examples of programs that use this module
as a toolbox, see the OBJECT.BAS, MOUSGCRS.BAS, MOUSTCRS.BAS, and
WINDOWS.BAS program modules.
Each subprogram that creates cursors defines a graphics-mode mouse cursor
by filling in the pattern mask string and hot spot location variables.
After this subprogram is called, call MouseMaskTranslate to translate the
variables to a binary format string, which should then be passed to the
MouseSetGcursor subprogram to quickly set the indicated cursor.
You might find it helpful to follow the program listing as the interactive
demonstration progresses.
╓┌─┌─────────────────────────────┌──────┌────────────────────────────────────╖
Name Type Description
──────────────────────────────────────────────────────────────────────────
MOUSSUBS.BAS Demo module
Curschek Sub Check mark mouse cursor
Cursdflt Sub Arrow mouse cursor pointing up and
left
Curshand Sub Pointing hand mouse cursor
Curshour Sub Hourglass mouse cursor
Cursjet Sub Jet-shaped mouse cursor
Cursleft Sub Left arrow mouse cursor
Cursplus Sub Plus sign mouse cursor
Cursup Sub Up arrow mouse cursor
Cursx Sub X-mark mouse cursor
MouseHide Sub Turns off mouse visibility
MouseInches Sub Sets mouse-to-cursor motion ratio
MouseInstall Sub Checks mouse availability; resets
mouse parameters
Name Type Description
──────────────────────────────────────────────────────────────────────────
mouse parameters
MouseLightPen Sub Mouse emulation of a lightpen
MouseMaskTranslate Sub Translates pattern/hot spot to binary
MouseMickey Sub Returns motion increments since last
call
MouseNow Sub Current state/location of the mouse
MousePressLeft Sub Location of mouse──left button press
MousePressRight Sub Location of mouse──right button press
MousePut Sub Moves cursor to the given position
MouseRange Sub Limits mouse cursor motion to
rectangle
MouseReleaseLeft Sub Location of mouse──left button
release
MouseReleaseRight Sub Location of mouse──right button
release
MouseSetGcursor Sub Sets graphics-mode mouse cursor
MouseShow Sub Activates and displays mouse cursor
MouseSoftCursor Sub Sets text-mode attributes (mouse
cursor)
Name Type Description
──────────────────────────────────────────────────────────────────────────
cursor)
MouseWarp Sub Sets mouse double-speed threshold
──────────────────────────────────────────────────────────────────────────
Demo Module: MOUSSUBS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MOUSSUBS **
' ** Type: Toolbox **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Collection of subprograms for using the Microsoft Mouse.
'
' Note: The assembly-language subroutine named MOUSE.ASM
' must be assembled and linked with these routines
' or included in the user library loaded with
' QuickBASIC.
' USAGE: No command line parameters
' REQUIREMENTS: CGA
' MIXED.QLB/.LIB
' Mouse
' .MAK FILE: MOUSSUBS.BAS
' BITS.BAS
' PARAMETERS: (none)
' VARIABLES: i% Looping index
' mask$ Pattern mask for each graphics mouse cursor
' xHot% X hot spot location
' yHot% Y hot spot location
' curs$ Binary bit pattern for defining mouse cursor
' j% Test for left mouse button press and release
' leftButton% State of left mouse button
' rightButton% State of right mouse button
' xMouse% X location of mouse
' yMouse% Y location of mouse
' mflag% Indicates mouse is available
' horizontal% Horizontal mouse mickies
' vertical% Vertical mouse mickies
' xpLeft% X location of last left button press
' ypLeft% Y location of last left button press
' xrLeft% X location of last left button release
' yrLeft% Y location of last left button release
' xpRight% X location of last right button press
' ypRight% Y location of last right button press
' xrRight% X location of last right button release
' yrRight% Y location of last right button release
' t0 Timer value
'
' Functions
DECLARE FUNCTION BinStr2Bin% (b$)
' Subprograms
DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
DECLARE SUB MouseRange (x1%, y1%, x2%, y2%)
DECLARE SUB MousePut (xMouse%, yMouse%)
DECLARE SUB MouseHide ()
DECLARE SUB MouseInches (horizontal%, vertical%)
DECLARE SUB MouseInstall (mflag%)
DECLARE SUB MouseMickey (horizontal%, vertical%)
DECLARE SUB MousePressLeft (leftCount%, xMouse%, yMouse%)
DECLARE SUB MousePressRight (rightCount%, xMouse%, yMouse%)
DECLARE SUB MouseReleaseLeft (leftCount%, xMouse%, yMouse%)
DECLARE SUB MouseReleaseRight (rightCount%, xMouse%, yMouse%)
DECLARE SUB MouseWarp (threshold%)
DECLARE SUB Cursdflt (mask$, xHot%, yHot%)
DECLARE SUB Curschek (mask$, xHot%, yHot%)
DECLARE SUB Curshand (mask$, xHot%, yHot%)
DECLARE SUB Curshour (mask$, xHot%, yHot%)
DECLARE SUB Cursjet (mask$, xHot%, yHot%)
DECLARE SUB Cursleft (mask$, xHot%, yHot%)
DECLARE SUB Cursplus (mask$, xHot%, yHot%)
DECLARE SUB Cursup (mask$, xHot%, yHot%)
DECLARE SUB Cursx (mask$, xHot%, yHot%)
DECLARE SUB MouseMaskTranslate (mask$, xHot%, yHot%, cursor$)
DECLARE SUB MouseNow (leftButton%, rightButton%, xMouse%, yMouse%)
DECLARE SUB MouseSetGcursor (cursor$)
DECLARE SUB MouseShow ()
' Check for mouse
SCREEN 2
CLS
MouseInstall mflag%
PRINT "MouseInstall ... "; mflag%
' Demonstrate the available graphics-mode cursors
PRINT
PRINT "Click left mouse button to see the graphics-mode cursors..."
MouseShow
FOR i% = 1 TO 9
SELECT CASE i%
CASE 1
Curschek mask$, xHot%, yHot%
CASE 2
Curshand mask$, xHot%, yHot%
CASE 3
Curshour mask$, xHot%, yHot%
CASE 4
Cursjet mask$, xHot%, yHot%
CASE 5
Cursleft mask$, xHot%, yHot%
CASE 6
Cursplus mask$, xHot%, yHot%
CASE 7
Cursup mask$, xHot%, yHot%
CASE 8
Cursx mask$, xHot%, yHot%
CASE ELSE
Cursdflt mask$, xHot%, yHot%
END SELECT
MouseMaskTranslate mask$, xHot%, yHot%, curs$
FOR j% = -1 TO 0
DO
MouseNow leftButton%, rightButton%, xMouse%, yMouse%
LOOP UNTIL leftButton% = j%
NEXT j%
MouseSetGcursor curs$
NEXT i%
' Mouse hide and show
PRINT "MouseHide ... (Press any key to continue)"
MouseHide
DO
LOOP UNTIL INKEY$ <> ""
PRINT "MouseShow ... (Press any key to continue)"
MouseShow
DO
LOOP UNTIL INKEY$ <> ""
' Mouse inches per screen
MouseHide
PRINT
PRINT "Setting MouseWarp to 9999 to prevent doubling of speed."
MouseWarp 9999
PRINT
PRINT "Setting MouseInches to 8 by 11. (8 inches of mouse motion"
PRINT "across desk to move across screen, and 11 inches vertical"
PRINT "mouse motion from top to bottom of screen) ..."
PRINT
PRINT "Press any key to continue"
MouseInches 8, 11
MouseShow
DO
LOOP UNTIL INKEY$ <> ""
' Resetting the mouse
MouseHide
PRINT
PRINT "Resetting the mouse"
MouseInstall mflag%
' Show realtime mouse data
CLS
PRINT "Instantaneous mouse information (Press any key to continue)"
MouseShow
DO
MouseMickey horizontal%, vertical%
MouseNow leftButton%, rightButton%, xMouse%, yMouse%
MousePressLeft leftCount%, xpLeft%, ypLeft%
MouseReleaseLeft leftCount%, xrLeft%, yrLeft%
MousePressRight rightCount%, xpRight%, ypRight%
MouseReleaseRight rightCount%, xrRight%, yrRight%
LOCATE 3, 1
PRINT "Mickies ";
PRINT USING "#######, #######"; horizontal%, vertical%
PRINT "Position ";
PRINT USING "#######, #######"; xMouse%, yMouse%
PRINT
PRINT "Buttons ";
PRINT USING "#######, #######"; leftButton%, rightButton%
PRINT
PRINT "Left Press ";
PRINT USING "#######, #######"; xpLeft%, ypLeft%
PRINT "Left Release ";
PRINT USING "#######, #######"; xrLeft%, yrLeft%
PRINT
PRINT "Right Press ";
PRINT USING "#######, #######"; xpRight%, ypRight%
PRINT "Right Release ";
PRINT USING "#######, #######"; xrRight%, yrRight%
LOOP UNTIL INKEY$ <> ""
' Mouse placement
CLS
MouseHide
PRINT "MousePut..."
MouseShow
FOR i% = 1 TO 20
xMouse% = RND * 639
yMouse% = RND * 199
MousePut xMouse%, yMouse%
t0 = TIMER
DO
LOOP UNTIL TIMER - t0 > .2
NEXT i%
' Range limiting
CLS
MouseHide
PRINT "Range limited to a rectangular area ..."
PRINT "Press any key to continue"
MouseShow
MouseRange 200, 50, 400, 100
DO
LOOP UNTIL INKEY$ <> ""
' All done
SCREEN 0
CLS
──────────────────────────────────────────────────────────────────────────
Subprogram: Curschek
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining a check mark cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Curschek **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a graphics-mode mouse cursor (check mark).
'
' EXAMPLE OF USE: Curschek mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Curschek (mask$, xHot%, yHot%)
'
SUB Curschek (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "1111111111110000"
mask$ = mask$ + "1111111111100000"
mask$ = mask$ + "1111111111000000"
mask$ = mask$ + "1111111110000001"
mask$ = mask$ + "1111111100000011"
mask$ = mask$ + "0000011000000111"
mask$ = mask$ + "0000000000001111"
mask$ = mask$ + "0000000000011111"
mask$ = mask$ + "1100000000111111"
mask$ = mask$ + "1111000001111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000110"
mask$ = mask$ + "0000000000001100"
mask$ = mask$ + "0000000000011000"
mask$ = mask$ + "0000000000110000"
mask$ = mask$ + "0000000001100000"
mask$ = mask$ + "0111000011000000"
mask$ = mask$ + "0001110110000000"
mask$ = mask$ + "0000011100000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
xHot% = 6
yHot% = 7
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Cursdflt
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining the default cursor (an arrow pointing up and left).
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Cursdflt **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a default graphics-mode mouse cursor (arrow pointing up and lef
'
' EXAMPLE OF USE: Cursdflt mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATION: DECLARE SUB Cursdflt (mask$, xHot%, yHot%)
'
SUB Cursdflt (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1001111111111111"
mask$ = mask$ + "1000111111111111"
mask$ = mask$ + "1000011111111111"
mask$ = mask$ + "1000001111111111"
mask$ = mask$ + "1000000111111111"
mask$ = mask$ + "1000000011111111"
mask$ = mask$ + "1000000001111111"
mask$ = mask$ + "1000000000111111"
mask$ = mask$ + "1000000000011111"
mask$ = mask$ + "1000000000001111"
mask$ = mask$ + "1000000000000111"
mask$ = mask$ + "1000100001111111"
mask$ = mask$ + "1001100001111111"
mask$ = mask$ + "1111110000111111"
mask$ = mask$ + "1111110000111111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0010000000000000"
mask$ = mask$ + "0011000000000000"
mask$ = mask$ + "0011100000000000"
mask$ = mask$ + "0011110000000000"
mask$ = mask$ + "0011111000000000"
mask$ = mask$ + "0011111100000000"
mask$ = mask$ + "0011111110000000"
mask$ = mask$ + "0011111111000000"
mask$ = mask$ + "0011111111100000"
mask$ = mask$ + "0011111000000000"
mask$ = mask$ + "0010001100000000"
──────────────────────────────────────────────────────────────────────────
mask$ = mask$ + "0000001100000000"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0000000110000000"
xHot% = 1
yHot% = 1
END SUB
Subprogram: Curshand
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining a pointing hand cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Curshand **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a graphics-mode mouse cursor (pointing hand).
'
' EXAMPLE OF USE: Curshand mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Curshand (mask$, xHot%, yHot%)
'
SUB Curshand (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "1110000111111111"
mask$ = mask$ + "1110000111111111"
mask$ = mask$ + "1110000111111111"
mask$ = mask$ + "1110000111111111"
mask$ = mask$ + "1110000111111111"
mask$ = mask$ + "1110000000000000"
mask$ = mask$ + "1110000000000000"
mask$ = mask$ + "1110000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0001111000000000"
mask$ = mask$ + "0001001000000000"
mask$ = mask$ + "0001001000000000"
mask$ = mask$ + "0001001000000000"
mask$ = mask$ + "0001001000000000"
mask$ = mask$ + "0001001111111111"
mask$ = mask$ + "0001001001001001"
mask$ = mask$ + "0001001001001001"
mask$ = mask$ + "1111001001001001"
mask$ = mask$ + "1001000000000001"
mask$ = mask$ + "1001000000000001"
mask$ = mask$ + "1001000000000001"
mask$ = mask$ + "1000000000000001"
mask$ = mask$ + "1000000000000001"
mask$ = mask$ + "1000000000000001"
mask$ = mask$ + "1111111111111111"
xHot% = 5
yHot% = 0
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Curshour
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining an hourglass cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Curshour **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a graphics-mode mouse cursor (hourglass).
'
' EXAMPLE OF USE: Curshour mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Curshour (mask$, xHot%, yHot%)
'
SUB Curshour (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "1000000000000001"
mask$ = mask$ + "1100000000000011"
mask$ = mask$ + "1110000000000111"
mask$ = mask$ + "1111000000001111"
mask$ = mask$ + "1110000000000111"
mask$ = mask$ + "1100000000000011"
mask$ = mask$ + "1000000000000001"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0111111111111110"
mask$ = mask$ + "0110000000000110"
mask$ = mask$ + "0011000000001100"
mask$ = mask$ + "0001100000011000"
mask$ = mask$ + "0000110000110000"
mask$ = mask$ + "0000011001100000"
mask$ = mask$ + "0000001111000000"
mask$ = mask$ + "0000011001100000"
mask$ = mask$ + "0000110000110000"
mask$ = mask$ + "0001100110011000"
mask$ = mask$ + "0011001111001100"
mask$ = mask$ + "0110011111100110"
mask$ = mask$ + "0111111111111110"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
xHot% = 7
yHot% = 7
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Cursjet
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining a jet aircraft cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Cursjet **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a graphics-mode mouse cursor (jet aircraft).
'
' EXAMPLE OF USE: Cursjet mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Cursjet (mask$, xHot%, yHot%)
'
SUB Cursjet (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111011111111"
mask$ = mask$ + "1111110001111111"
mask$ = mask$ + "1111100000111111"
mask$ = mask$ + "1111100000111111"
mask$ = mask$ + "1111100000111111"
mask$ = mask$ + "1111000000011111"
mask$ = mask$ + "1110000000001111"
mask$ = mask$ + "1100000000000111"
mask$ = mask$ + "1000000000000011"
mask$ = mask$ + "1000000000000011"
mask$ = mask$ + "1111100000111111"
mask$ = mask$ + "1111100000111111"
mask$ = mask$ + "1111000000011111"
mask$ = mask$ + "1110000000001111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000100000000"
mask$ = mask$ + "0000001110000000"
mask$ = mask$ + "0000001110000000"
mask$ = mask$ + "0000001110000000"
mask$ = mask$ + "0000011111000000"
mask$ = mask$ + "0000111111100000"
mask$ = mask$ + "0001111111110000"
mask$ = mask$ + "0011111111111000"
mask$ = mask$ + "0110001110001100"
mask$ = mask$ + "0000001110000000"
mask$ = mask$ + "0000001110000000"
mask$ = mask$ + "0000011111000000"
mask$ = mask$ + "0000110001100000"
mask$ = mask$ + "0000000000000000"
xHot% = 7
yHot% = 1
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Cursleft
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining a left arrow cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Cursleft **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a graphics-mode mouse cursor (left arrow).
'
' EXAMPLE OF USE: Cursleft mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Cursleft (mask$, xHot%, yHot%)
'
SUB Cursleft (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "1111111000011111"
mask$ = mask$ + "1111000000011111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "1111000000011111"
mask$ = mask$ + "1111111000011111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000011000000"
mask$ = mask$ + "0000011111000000"
mask$ = mask$ + "0111111111111110"
mask$ = mask$ + "0000011111000000"
mask$ = mask$ + "0000000011000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
xHot% = 0
yHot% = 3
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Cursplus
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining a plus sign cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Cursplus **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a graphics-mode mouse cursor (plus sign).
'
' EXAMPLE OF USE: Cursplus mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Cursplus (mask$, xHot%, yHot%)
'
SUB Cursplus (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "1111110000111111"
mask$ = mask$ + "1111110000111111"
mask$ = mask$ + "1111110000111111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "1111110000111111"
mask$ = mask$ + "1111110000111111"
mask$ = mask$ + "1111110000111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0111111111111110"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
xHot% = 7
yHot% = 4
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Cursup
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining an up arrow cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Cursup **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a graphics-mode mouse cursor (up arrow).
'
' EXAMPLE OF USE: Cursup mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Cursup (mask$, xHot%, yHot%)
'
SUB Cursup (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "1111100111111111"
mask$ = mask$ + "1111000011111111"
mask$ = mask$ + "1110000001111111"
mask$ = mask$ + "1110000001111111"
mask$ = mask$ + "1100000000111111"
mask$ = mask$ + "1100000000111111"
mask$ = mask$ + "1000000000011111"
mask$ = mask$ + "1000000000011111"
mask$ = mask$ + "0000000000001111"
mask$ = mask$ + "0000000000001111"
mask$ = mask$ + "1111000011111111"
mask$ = mask$ + "1111000011111111"
mask$ = mask$ + "1111000011111111"
mask$ = mask$ + "1111000011111111"
mask$ = mask$ + "1111000011111111"
mask$ = mask$ + "1111000011111111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000011000000000"
mask$ = mask$ + "0000111100000000"
mask$ = mask$ + "0000111100000000"
mask$ = mask$ + "0001111110000000"
mask$ = mask$ + "0001111110000000"
mask$ = mask$ + "0011111111000000"
mask$ = mask$ + "0011111111000000"
mask$ = mask$ + "0111111111100000"
mask$ = mask$ + "0000011000000000"
mask$ = mask$ + "0000011000000000"
mask$ = mask$ + "0000011000000000"
mask$ = mask$ + "0000011000000000"
mask$ = mask$ + "0000011000000000"
mask$ = mask$ + "0000011000000000"
mask$ = mask$ + "0000000000000000"
xHot% = 5
yHot% = 0
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Cursx
Creates a graphics mouse cursor; fills in the pattern mask and hot spot
values for defining an X-mark cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Cursx **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Defines a graphics-mode mouse cursor (X mark).
'
' EXAMPLE OF USE: Cursx mask$, xHot%, yHot%
' PARAMETERS: mask$ Pattern mask for creating cursor
' xHot% X location for cursor hot spot
' yHot% Y location for cursor hot spot
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Cursx (mask$, xHot%, yHot%)
'
SUB Cursx (mask$, xHot%, yHot%) STATIC
mask$ = ""
mask$ = mask$ + "0000011111100000"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "1100000000000011"
mask$ = mask$ + "1111000000001111"
mask$ = mask$ + "1100000000000011"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000110000000"
mask$ = mask$ + "0000001111000000"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "1111111111111111"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0111000000001110"
mask$ = mask$ + "0001110000111000"
mask$ = mask$ + "0000011001100000"
mask$ = mask$ + "0000001111000000"
mask$ = mask$ + "0000011001100000"
mask$ = mask$ + "0001110000111000"
mask$ = mask$ + "0111000000001110"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
mask$ = mask$ + "0000000000000000"
xHot% = 7
yHot% = 4
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseHide
Deactivates the mouse cursor, making it invisible and inaccessible. Use
the MouseShow subprogram to reactivate the cursor.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseHide **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Hides the mouse cursor.
'
' EXAMPLE OF USE: MouseHide
'
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseHide ()
'
SUB MouseHide STATIC
Mouse 2, 0, 0, 0
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseInches
Sets the ratio of mouse motion to cursor motion. The horizontal% and
vertical% parameters indicate the number of inches of desktop motion that
your mouse must move to move the mouse cursor from one edge of the screen
to the other. Note that the vertical and horizontal values are independent
of each other.
Before calling this subprogram, set the double-speed threshold to a large
value by calling the MouseWarp subprogram. This prevents fast mouse
motion from doubling the cursor velocity, keeping the motion ratios
constant.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseInches **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets mouse motion ratio in inches per screen.
'
' EXAMPLE OF USE: MouseInches horizontal%, vertical%
' PARAMETERS: horizontal% Inches of horizontal mouse motion per
' screen width
' vertical% Inches of vertical% mouse motion per
' screen height
' VARIABLES: h% Calculated value to pass to mouse driver
' v% Calculated value to pass to mouse driver
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseInches (horizontal%, vertical%)
'
SUB MouseInches (horizontal%, vertical%) STATIC
IF horizontal% > 100 THEN
horizontal% = 100
END IF
IF vertical% > 100 THEN
vertical% = 100
END IF
h% = horizontal% * 5 \ 2
v% = vertical% * 8
Mouse 15, 0, h%, v%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseInstall
Checks the memory-resident mouse driver to determine whether a mouse is
available. The value of mflag% is returned as 0 if no mouse is available
and as -1 if one is.
This subprogram also initializes the mouse driver to the default state.
The original cursor is set, and the mouse velocity, threshold, and other
parameters are all set to their original states.
Normally, this subprogram is called immediately when a program is run.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseInstall **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Determines whether mouse is available and resets all mouse parameters.
'
' EXAMPLE OF USE: MouseInstall mflag%
' PARAMETERS: mflag% Returned indication of mouse availability
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseInstall (mflag%)
'
SUB MouseInstall (mflag%) STATIC
mflag% = 0
Mouse mflag%, 0, 0, 0
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseLightPen
Activates or deactivates lightpen emulation by the mouse.
The QuickBASIC PEN function provides ten unique functions for accessing
information on the lightpen, depending on the parameter you pass to it.
This complete set of lightpen functions can be emulated using the mouse
rather than the lightpen. To activate lightpen emulation, call
MouseLightPen with a non-zero parameter. To deactivate lightpen emulation,
use a zero parameter.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseLightPen **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
' Activates and deactivates lightpen emulation mode.
'
' EXAMPLE OF USE: MouseLightPen switch%
' PARAMETERS: switch% non-zero to activate lightpen emulation,
' zero to deactivate
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseLightPen (switch%)
'
SUB MouseLightPen (switch%) STATIC
IF switch% THEN
Mouse 13, 0, 0, 0
ELSE
Mouse 14, 0, 0, 0
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseMaskTranslate
Translates the pattern mask and hot spot values for a given graphics-mode
mouse cursor to a binary format string suitable for passing to the
memory-resident mouse driver for setting the cursor.
This translation process is relatively time-consuming and should normally
only be performed once, when a program first starts up. To save multiple
cursors for quick switching between cursor types, save only the cursor$
result from this subprogram. The call to MouseSetCursor, using this binary
format cursor$, is very fast.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseMaskTranslate **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Translates mouse graphics cursor Mask$ to Cursor$.
'
' EXAMPLE OF USE: MouseMaskTranslate mask$, xHot%, yHot%, cursor$
' PARAMETERS: mask$ Pattern mask that defines a mouse
' graphics-mode cursor
' xHot% X location of the hot spot
' yHot% Y location of the hot spot
' cursor$ The returned binary buffer string
' for the cursor
' VARIABLES: i% Looping index
' b% Integer formed from string bit representati
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB MouseMaskTranslate (mask$, xHot%, yHot%,
' cursor$)
'
SUB MouseMaskTranslate (mask$, xHot%, yHot%, cursor$) STATIC
cursor$ = CHR$(xHot%) + CHR$(yHot%) + STRING$(64, 0)
IF LEN(mask$) = 512 THEN
FOR i% = 1 TO 32
b% = BinStr2Bin%(MID$(mask$, i% * 16 - 15, 16))
MID$(cursor$, i% + i% + 1, 2) = MKI$(b%)
NEXT i%
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseMickey
Returns the mouse "mickies," or relative motion counts, since the last
call to this routine. If the mouse has not been moved since the last call,
zeros are returned.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseMickey **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Reads mouse mickey counts.
'
' EXAMPLE OF USE: MouseMickey horizontal%, vertical%
' PARAMETERS: horizontal% Horizontal motion mickey counts
' vertical% Vertical motion mickey counts
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseMickey (horizontal, vertical%)
'
SUB MouseMickey (horizontal%, vertical%) STATIC
Mouse 11, 0, horizontal%, vertical%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseNow
Returns the state of the mouse buttons and the mouse location. This
subprogram is one of the most useful routines presented. Four parameters
are passed back: the states of the two mouse buttons and the horizontal
and vertical location of the mouse.
The horizontal position is scaled according to the current video mode. In
most cases, the X position at the right edge of the screen is 639, no
matter what the screen pixel range is. Check the returned values for the
mode you want to use.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseNow **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the state of the mouse.
'
' EXAMPLE OF USE: MouseNow leftButton%, rightButton%, xMouse%, yMouse%
' PARAMETERS: leftButton% Indicates left mouse button state
' rightButton% Indicates right mouse button state
' xMouse% X location of mouse
' yMouse% Y location of mouse
' VARIABLES: m2% Mouse driver parameter containing button
' press information
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseNow (leftButton%, rightButton%,
' xMouse%, yMouse%)
'
SUB MouseNow (leftButton%, rightButton%, xMouse%, yMouse%) STATIC
Mouse 3, m2%, xMouse%, yMouse%
leftButton% = ((m2% AND 1) <> 0)
rightButton% = ((m2% AND 2) <> 0)
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MousePressLeft
Returns the position of the mouse at the time the left button was last
pressed. Also returned is the number of left button presses since the last
call to this subprogram.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MousePressLeft **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the mouse state at last press of left button.
'
' EXAMPLE OF USE: MousePressLeft leftCount%, xMouse%, yMouse%
' PARAMETERS: leftCount% Number of times the left button has been
' pressed since the last call to this
' subprogram
' xMouse% X location of the mouse at the last pres
' of the left button
' yMouse% Y location of the mouse at the last pres
' of the left button
' VARIABLES: m1% Parameter for call to mouse driver
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MousePressLeft (leftCount%, xMouse%, yMo
'
SUB MousePressLeft (leftCount%, xMouse%, yMouse%) STATIC
m1% = 5
leftCount% = 0
Mouse m1%, leftCount%, xMouse%, yMouse%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MousePressRight
Returns the position of the mouse at the time the right button was last
pressed. Also returned is the number of right button presses since the
last call to this subprogram.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MousePressRight **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the mouse state at last press of right button.
'
SUB MousePressRight (rightCount%, xMouse%, yMouse%) STATIC
m1% = 5
rightCount% = 1
Mouse m1%, rightCount%, xMouse%, yMouse%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MousePut
Allows you to move the mouse to any desired location.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MousePut **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets the mouse position.
'
' EXAMPLE OF USE: MousePut xMouse%, yMouse%
' PARAMETERS: xMouse% Horizontal location to place cursor
' yMouse% Vertical location to place cursor
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MousePut (xMouse%, yMouse%)
'
SUB MousePut (xMouse%, yMouse%) STATIC
Mouse 4, 0, xMouse%, yMouse%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseRange
Sets a rectangular area of the screen to which the mouse cursor will be
limited. The mouse cursor will stay in the bounds defined, no matter which
way the mouse is moved.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseRange **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets mouse range of motion.
'
' EXAMPLE OF USE: MouseRange x1%, y1%, x2%, y2%
' PARAMETERS: x1% Upper left corner X coordinate
' y1% Upper left corner Y coordinate
' x2% Lower right corner X coordinate
' y2% Lower right corner Y coordinate
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseRange (x1%, y1%, x2%, y2%)
'
SUB MouseRange (x1%, y1%, x2%, y2%) STATIC
Mouse 7, 0, x1%, x2%
Mouse 8, 0, y1%, y2%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseReleaseLeft
Returns the position of the mouse at the time the left button was last
released. Also returned is the number of left button releases since the
last call to this subprogram.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseReleaseLeft **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the mouse state at last release of left button.
'
' EXAMPLE OF USE: MouseReleaseLeft leftCount%, xMouse%, yMouse%
' PARAMETERS: leftCount% Number of times the left button has been
' released since the last call to this
' subprogram
' xMouse% X location of the mouse at the last
' release of the left button
' yMouse% Y location of the mouse at the last
' release of the left button
' VARIABLES: m1% Parameter for call to mouse driver
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseReleaseLeft (leftCount%, xMouse%,
' yMouse%)
'
SUB MouseReleaseLeft (leftCount%, xMouse%, yMouse%) STATIC
m1% = 6
leftCount% = 0
Mouse m1%, leftCount%, xMouse%, yMouse%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseReleaseRight
Returns the position of the mouse at the time the right button was last
released. Also returned is the number of right button releases since the
last call to this subprogram.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseReleaseRight **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the mouse state at last release of right button.
'
' EXAMPLE OF USE: MouseReleaseRight rightCount%, xMouse%, yMouse%
' PARAMETERS: rightCount% Number of times the right button has bee
' released since the last call to this
' subprogram
' xMouse% X location of the mouse at the last
' release of the right button
' yMouse% Y location of the mouse at the last
' release of the right button
' VARIABLES: m1% Parameter for call to mouse driver
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseReleaseRight (rightCount%, xMouse%,
' yMouse%)
'
SUB MouseReleaseRight (rightCount%, xMouse%, yMouse%) STATIC
m1% = 6
rightCount% = 1
Mouse m1%, rightCount%, xMouse%, yMouse%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseSetGcursor
Sets the mouse cursor using the binary-format cursor string created by an
earlier call to the subprogram MouseMaskTranslate.
To quickly switch among a selection of mouse cursors, keep the
binary-format cursor strings available, and call this subprogram to change
cursors.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseSetGcursor **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets mouse graphics cursor using cursor$.
'
' EXAMPLE OF USE: MouseSetGcursor cursor$
' PARAMETERS: cursor$ Binary format cursor string
' VARIABLES: xHot% X hot spot location
' yHot% Y hot spot location
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseSetGcursor (cursor$)
'
SUB MouseSetGcursor (cursor$) STATIC
xHot% = ASC(LEFT$(cursor$, 1))
yHot% = ASC(MID$(cursor$, 2, 1))
Mouse 9, xHot%, yHot%, SADD(cursor$) + 2
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseShow
Activates the mouse cursor, making it visible and movable by the mouse. To
turn the cursor off, use the MouseHide subprogram.
When you are updating the screen, such as when printing text in a graphics
mode, it's a good idea to hide the mouse just before printing and then
show it after the printing is done. This helps prevent glitches or blank
spots from appearing due to overlapping and unsynchronized pixel mapping
between your program and the mouse driver.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseShow **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Shows the mouse cursor.
'
' EXAMPLE OF USE: MouseShow
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseShow ()
'
SUB MouseShow STATIC
Mouse 1, 0, 0, 0
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseSoftCursor
Sets the software mouse cursor for text mode. This cursor changes the
attributes of screen characters (foreground/background color, intensity,
or underscoring) when the display adapter is in text mode. The easiest way
to get a feel for how these masks work is by running the MOUSTCRS.BAS
program.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseSoftCursor **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets text-mode software cursor.
'
' EXAMPLE OF USE: MouseSoftCursor screenMask%, cursorMask%
' PARAMETERS: screenMask% Integer bit pattern for the screen mask
' cursorMask% Integer bit pattern for the cursor mask
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB MouseSoftCursor (screenMaks%, cursorMask%)
' DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
'
SUB MouseSoftCursor (screenMask%, cursorMask%) STATIC
Mouse 10, 0, screenMask%, cursorMask%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: MouseWarp
Sets the double-speed threshold for the mouse in units of mickies per
second. The default setting is 64 mickies per second.
Whenever the mouse is moved at a rate greater than the threshold value,
the cursor motion is doubled. This helps zip the cursor across the screen
during quick moves but allows slower, more accurate motion at slower
speeds.
To use the MouseInches subprogram to approximate the action of an
absolute-motion pointing device, set the threshold to a large, unreachable
value. For example, MouseWarp 9999 effectively turns off the threshold
checking.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MouseWarp **
' ** Type: Subprogram **
' ** Module: MOUSSUBS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
' Sets double-speed threshold.
'
' EXAMPLE OF USE: MouseWarp threshold%
' PARAMETERS: threshold% Mickies per second rate of threshold
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
' DECLARE SUB MouseWarp (threshold%)
'
SUB MouseWarp (threshold%) STATIC
Mouse 19, 0, 0, threshold%
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
MOUSTCRS
The MOUSTCRS program lets you experiment with the screen and cursor masks
that define the action of the software mouse cursor in text modes.
Run the program and move the mouse cursor to any of the mask bits
displayed near the bottom of the screen. Click with the left mouse button
on any bit to toggle that bit, and move the cursor around the screen to
see how the cursor's appearance is affected.
To set any screen and cursor mask combination in your own programs, record
the hexadecimal numbers for the two masks, and pass these two numbers to
the MouseSoftCursor subprogram in the MOUSSUBS.BAS toolbox.
Program Module: MOUSTCRS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: MOUSTCRS **
' ** Type: Program **
' ** Module: MOUSTCRS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' REQUIREMENTS: MIXED.QLB/.LIB
' Mouse
' .MAK FILE: MOUSTCRS.BAS
' MOUSSUBS.BAS
' BITS.BAS
' ATTRIB.BAS
' PARAMETERS: (none)
' VARIABLES: screenMask% Integer bit mask for screen mask
' cursorMask% Integer bit mask for cursor mask
' leftCount% Count of left mouse button presses
' xm% Mouse X position at last left button pre
' ym% Mouse Y position at last left button pre
' row% Code for which screen bit row was select
' bit% Bit pattern determined by screen column
' click on
' screenMask$ String of 0s and 1s for bit pattern disp
' cursorMask$ String of 0s and 1s for bit pattern disp
' i% Looping index
' Shex$ Hexadecimal representation of the screen
' Chex$ Hexadecimal representation of the cursor
' Define constants
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Functions
DECLARE FUNCTION Bin2BinStr$ (b%)
' Subprograms
DECLARE SUB Attrib ()
DECLARE SUB MouseHide ()
DECLARE SUB MouseInstall (mouseFlag%)
DECLARE SUB MousePressLeft (leftCount%, xMouse%, yMouse%)
DECLARE SUB MouseShow ()
DECLARE SUB MouseSoftCursor (screenMask%, cursorMask%)
' Is the mouse out there?
MouseInstall mouseFlag%
IF mouseFlag% = 0 THEN
PRINT "Mouse does not appear to be installed. Check"
PRINT "your mouse documentation for proper installation."
PRINT
SYSTEM
END IF
' Put all attributes on the screen
Attrib
' Set masks to initial state
screenMask% = &H77FF
cursorMask% = &H7700
' Create the outlined boxes
COLOR 14, 0
PRINT " +---+-------+---+--------+----------+--------+
PRINT " | b | bckgd | i | foregd | char | = |
PRINT " +-------------+---+-------+---+--------+----------+--------+
PRINT " | screen mask | 0 | 111 | 0 | 111 | 11111111 | &H77FF |
PRINT " | cursor mask | 0 | 111 | 0 | 111 | 00000000 | &H7700 |
PRINT " +-------------+---+-------+---+--------+----------+--------+
' Print the instructions
COLOR 11, 0
PRINT "Click the mouse on any of the mask bits shown. Then, try the"
PRINT "new cursor by moving across the attribute fields above.";
' Special indication for quitting
COLOR 15, 0
LOCATE 17, 1, 0
PRINT "Click here";
LOCATE 18, 1, 0
PRINT "to Quit - ";
COLOR 10, 0
PRINT "X";
' Put mask bits into boxes on screen
GOSUB PrintScreenMask
GOSUB PrintCursorMask
' Activate the mouse
MouseShow
' Do the main processing loop until the quit flag is set
DO
GOSUB MainLoop
LOOP UNTIL quitFlag%
' All done
MouseHide
CLS
SYSTEM
' Main processing loop
MainLoop:
' Where was mouse when left button was last pressed?
MousePressLeft leftCount%, xm%, ym%
' Was it on one of the two important rows of the screen?
SELECT CASE ym%
CASE 152
row% = 1
CASE 160
row% = 2
CASE ELSE
row% = 0
END SELECT
' Was it on an important column?
SELECT CASE xm%
CASE 80
IF ym% = 136 THEN
quitFlag% = TRUE
END IF
CASE 160
bit% = &H8000
CASE 200
bit% = &H4000
CASE 208
bit% = &H2000
CASE 216
bit% = &H1000
CASE 256
bit% = &H800
CASE 296
bit% = &H400
CASE 304
bit% = &H200
CASE 312
bit% = &H100
CASE 360
bit% = &H80
CASE 368
bit% = &H40
CASE 376
bit% = &H20
CASE 384
bit% = &H10
CASE 392
bit% = &H8
CASE 400
bit% = &H4
CASE 408
bit% = &H2
CASE 416
bit% = &H1
CASE ELSE
bit% = 0
END SELECT
' Modify the masks and update the cursor
IF leftCount% THEN
SELECT CASE row%
CASE 1
screenMask% = screenMask% XOR bit%
CASE 2
cursorMask% = cursorMask% XOR bit%
CASE ELSE
END SELECT
MouseSoftCursor screenMask%, cursorMask%
GOSUB PrintScreenMask
GOSUB PrintCursorMask
END IF
' End of main processing loop
RETURN
' Put screen mask bits on the screen
PrintScreenMask:
COLOR 12, 0
screenMask$ = ""
screenMask$ = Bin2BinStr$(screenMask%)
MouseHide
FOR i% = 0 TO 15
SELECT CASE i%
CASE 0 TO 7
LOCATE 20, 53 - i%, 0
PRINT MID$(screenMask$, 16 - i%, 1);
CASE 8 TO 10
LOCATE 20, 48 - i%, 0
PRINT MID$(screenMask$, 16 - i%, 1);
CASE 11
LOCATE 20, 44 - i%, 0
PRINT MID$(screenMask$, 16 - i%, 1);
CASE 12 TO 14
LOCATE 20, 40 - i%, 0
PRINT MID$(screenMask$, 16 - i%, 1);
CASE 15
LOCATE 20, 36 - i%, 0
PRINT MID$(screenMask$, 16 - i%, 1);
CASE ELSE
END SELECT
NEXT i%
shex$ = "&H" + RIGHT$("000" + HEX$(screenMask%), 4)
LOCATE 20, 57, 0
COLOR 10, 0
PRINT shex$;
MouseShow
RETURN
' Put cursor mask bits on the screen
PrintCursorMask:
COLOR 12, 0
cursorMask$ = ""
cursorMask$ = Bin2BinStr$(cursorMask%)
MouseHide
FOR i% = 0 TO 15
SELECT CASE i%
CASE 0 TO 7
LOCATE 21, 53 - i%, 0
PRINT MID$(cursorMask$, 16 - i%, 1);
CASE 8 TO 10
LOCATE 21, 48 - i%, 0
PRINT MID$(cursorMask$, 16 - i%, 1);
CASE 11
LOCATE 21, 44 - i%, 0
PRINT MID$(cursorMask$, 16 - i%, 1);
CASE 12 TO 14
LOCATE 21, 40 - i%, 0
PRINT MID$(cursorMask$, 16 - i%, 1);
CASE 15
LOCATE 21, 36 - i%, 0
PRINT MID$(cursorMask$, 16 - i%, 1);
CASE ELSE
END SELECT
NEXT i%
chex$ = "&H" + RIGHT$("000" + HEX$(cursorMask%), 4)
LOCATE 21, 57, 0
COLOR 10, 0
PRINT chex$;
MouseShow
RETURN
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
OBJECT
The OBJECT program lets you interactively create subprograms that produce
graphics objects for your programs.
When a QuickBASIC program uses the GET and PUT statements for graphics
animation purposes, you'll often notice the objects being created on the
screen as the program first starts up. The normal procedure is to create
the graphics objects using the LINE and DRAW statements and then to save
the objects in integer arrays using the GET statement. The creation of the
objects the first time is relatively slow, compared with the very fast PUT
statement.
The OBJECT program lets you create these objects interactively and
"off-line" until you're satisfied with their appearance. Then, this
program automatically writes a subprogram source file that, when loaded or
merged with your main program, quickly creates the integer arrays by
simply reading the appropriate integers into the arrays.
The best way to get a feel for this program is to give it a try. Run it,
and follow the directions. You can edit a DRAW string, try it to see what
the new object looks like, and then re-edit the string until you like the
results. When you select the "Save" option, the program automatically
determines the smallest integer array that will hold the object you've
created and writes a source code subprogram file that creates and fills an
integer array with the bit pattern for your object. Later on, you can load
this source code file and re-edit the object to make other changes.
To use the new object source code in your own program, merge the file into
the program where you want to use the object. The program should run
through the statements once, but you can use PUT statements repeatedly to
animate or duplicate the image.
Name Type Description
──────────────────────────────────────────────────────────────────────────
OBJECT.BAS Program module
SaveObject Sub Creates graphics "PUT" file source code
──────────────────────────────────────────────────────────────────────────
Program Module: OBJECT
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: OBJECT **
' ** Type: Program **
' ** Module: OBJECT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Allows interactive graphics object creation.
' Dumps code for another program to be able to create
' the graphics object "PUT array" directly.
'
' USAGE: No command line parameters
' REQUIREMENTS: CGA
' .MAK FILE: OBJECT.BAS
' KEYS.BAS
' EDIT.BAS
' PARAMETERS: (none)
' VARIABLES: quitFlag% Indicates user is ready to quit
' modeFlag% Indicates a valid graphics mode was sele
' mode% Graphics mode
' xMax% Maximum screen X coordinate
' yMax% Maximum screen Y coordinate
' fileName$ Name of object creation subprogram file
' exitCode% Return code from EditLine subprogram
' t$ Temporary work string while reading file
' contents
' a$ The DRAW string
' editFlag% Indicates an edit of the string is desir
' drawErrorFlag% Indicates an error occurred during the D
' keyNumber% Integer key code returned by KeyCode%
' function
' okayFlag% Shared flag for determining array dimens
' Logical constants
CONST FALSE = 0
CONST TRUE = NOT FALSE
' Key code constants
CONST SKEYLC = 115
CONST SKEYUC = SKEYLC - 32
CONST QKEYLC = 113
CONST QKEYUC = QKEYLC - 32
CONST ESC = 27
' Color constants
CONST BLACK = 0
CONST BLUE = 1
CONST GREEN = 2
CONST CYAN = 3
CONST RED = 4
CONST MAGENTA = 5
CONST BROWN = 6
CONST WHITE = 7
CONST BRIGHT = 8
CONST BLINK = 16
CONST YELLOW = BROWN + BRIGHT
' Functions
DECLARE FUNCTION KeyCode% ()
' Subprograms
DECLARE SUB DrawBox (row1%, col1%, row2%, col2%)
DECLARE SUB EditBox (a$, row1%, col1%, row2%, col2%)
DECLARE SUB EditLine (a$, exitCode%)
DECLARE SUB SaveObject (mode%, xMax%, yMax%, fileName$, a$)
' Initialization
SCREEN 0
CLS
quitFlag% = FALSE
' Title
PRINT "OBJECT - Interactive graphics object editor"
PRINT
PRINT
' Display screen mode table
PRINT "Adapter SCREEN modes allowed"
PRINT "---------- --------------------"
PRINT "Monochrome (none)"
PRINT "Hercules 3"
PRINT "CGA 1,2"
PRINT "EGA 1,2,7,8,9"
PRINT "MCGA 1,2,11,13"
PRINT "VGA 1,2,7,8,9,11,12,13"
PRINT
' Ask user for the graphics screen mode
DO
PRINT "Enter a SCREEN mode number, ";
INPUT "based on your graphics adapter "; mode%
modeFlag% = TRUE
SELECT CASE mode%
CASE 1, 7, 13
xMax% = 319
yMax% = 199
CASE 2, 8
xMax% = 639
yMax% = 199
CASE 9, 10
xMax% = 639
yMax% = 349
CASE 11, 12
xMax% = 639
yMax% = 479
CASE 3
xMax% = 719
yMax% = 347
CASE ELSE
modeFlag% = FALSE
END SELECT
LOOP UNTIL modeFlag% = TRUE
' Ask user for the filename
fileName$ = "IMAGEARY.BAS" + SPACE$(20)
SCREEN 0
WIDTH 80
CLS
COLOR WHITE, BLACK
PRINT "Name of the file where source code will be written:"
PRINT
PRINT "Edit the default filename IMAGEARY.BAS ";
PRINT "if desired, and then press Enter..."
PRINT
PRINT SPACE$(12);
COLOR YELLOW, BLUE
EditLine fileName$, exitCode%
COLOR WHITE, BLACK
' Try to read in previous contents of the file
ON ERROR GOTO FileError
OPEN fileName$ FOR INPUT AS #1
ON ERROR GOTO 0
DO UNTIL EOF(1)
LINE INPUT #1, t$
IF INSTR(t$, "(DRAW$)") THEN
t$ = MID$(t$, INSTR(t$, CHR$(34)) + 1)
t$ = LEFT$(t$, INSTR(t$, CHR$(34)) - 1)
a$ = a$ + t$
END IF
LOOP
CLOSE #1
' Main loop
DO
' Prepare for DRAW string editing by the user
SCREEN 0
WIDTH 80
CLS
editFlag% = FALSE
' Display useful information
PRINT "OBJECT - Screen mode"; mode%
PRINT
PRINT "Edit the DRAW string workspace; then press"
PRINT "the Esc key to try out your creation..."
PRINT
PRINT , " Cn Color"
PRINT , " H U E Mx,y Move absolute"
PRINT , " \ | / M+|/-x,y Move relative"
PRINT , " L - - R An Angle (1=90,2=180...)"
PRINT , " / | \ TAn Turn angle (-360 to 360)"
PRINT , " G D F Sn Scale factor"
PRINT , " Pc,b Paint (color, border)"
PRINT "(These commands are described in detail in the ";
PRINT "Microsoft QuickBASIC Language Reference)"
' Input DRAW string via EditBox subprogram
COLOR GREEN + BRIGHT, BLUE
DrawBox 15, 1, 24, 80
COLOR YELLOW, BLUE
EditBox a$, 15, 1, 24, 80
' Try out the DRAW string
SCREEN mode%
drawErrorFlag% = FALSE
ON ERROR GOTO DrawError
DRAW a$
ON ERROR GOTO 0
' Give user idea of what to do
LOCATE 1, 1
PRINT "<S>ave, <Esc> to edit, or <Q>uit"
' Get next valid keystroke
DO UNTIL editFlag% OR drawErrorFlag% OR quitFlag%
' Grab key code
keyNumber% = KeyCode%
' Process the keystroke
SELECT CASE keyNumber%
CASE ESC
editFlag% = TRUE
CASE QKEYLC, QKEYUC
quitFlag% = TRUE
CASE SKEYLC, SKEYUC
SaveObject mode%, xMax%, yMax%, fileName$, a$
CASE ELSE
END SELECT
LOOP
LOOP UNTIL quitFlag%
' All done
CLS
SCREEN 0
WIDTH 80
END
FileError:
' Create the new file
OPEN fileName$ FOR OUTPUT AS #1
CLOSE #1
OPEN fileName$ FOR INPUT AS #1
RESUME NEXT
DrawError:
drawErrorFlag% = TRUE
SCREEN 0
CLS
PRINT "Your DRAW string caused an error"
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
RESUME NEXT
ArrayError:
okayFlag% = FALSE
RESUME NEXT
──────────────────────────────────────────────────────────────────────────
Subprogram: SaveObject
Creates a source code subprogram module file for the OBJECT program.
This subprogram performs the tricky task of finding the boundaries of the
graphics object, dimensioning an integer array of exactly the right size,
getting the object from the screen and into the array, and writing a
source code subprogram file that will recreate the array when merged into
a different program.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SaveObject **
' ** Type: Subprogram **
' ** Module: OBJECT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Creates source code file for creating graphics mode
' objects for efficient "PUT" graphics.
'
' EXAMPLE OF USE: SaveObject mode%, xMax%, yMax%, fileName$, a$
' PARAMETERS: mode% Graphics mode
' xMax% Maximum X screen coordinate for given
' graphics mode
' yMax% Maximum Y screen coordinate for given
' graphics mode
' fileName$ Name of source code file to edit and/or
' create
' a$ The DRAW string that creates the object
' initially
' VARIABLES: okayFlag% Shared flag used to determine array size
' size% Array sizing
' edge% Array for efficiently finding edges of obj
' stepSize% Scanning step for search for object edges
' yTop% Y coordinate at top edge of object
' yBot% Y coordinate at bottom edge of object
' y1% Starting edge search Y coordinate
' y2% Ending edge search Y coordinate
' i% Looping index
' xLeft% X coordinate at left edge of object
' xRight% X coordinate at right edge of object
' x1% Starting edge search X coordinate
' x2% Ending edge search X coordinate
' object%() Array to hold GET object from screen
' objName$ Name of object, derived from filename
' ndx% Index to any special characters in objName
' ary$ Name of array, derived from filename
' d$ Works string for building lines for file
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION SaveObject (mode%, xMax%, yMax%,
' fileName$, a$)
'
SUB SaveObject (mode%, xMax%, yMax%, fileName$, a$) STATIC
' Shared error trap variable
SHARED okayFlag%
' Select the right array size for the mode
SELECT CASE mode%
CASE 1, 2
size% = 93
CASE 7, 8
size% = 367
CASE 9
size% = 667
CASE 10
size% = 334
CASE 11
size% = 233
CASE 12
size% = 927
CASE 13
size% = 161
CASE ELSE
END SELECT
' Build the array space
DIM edge%(size%)
' Scan to find top and bottom edges of the object
stepSize% = 32
yTop% = yMax%
yBot% = 0
y1% = 17
y2% = yMax%
DO
FOR y% = y1% TO y2% STEP stepSize%
IF y% < yTop% OR y% > yBot% THEN
GET (0, y%)-(xMax%, y%), edge%
LINE (0, y%)-(xMax%, y%)
FOR i% = 2 TO size%
IF edge%(i%) THEN
IF y% < yTop% THEN
yTop% = y%
END IF
IF y% > yBot% THEN
yBot% = y%
END IF
i% = size%
END IF
NEXT i%
PUT (0, y%), edge%, PSET
END IF
NEXT y%
IF yTop% <= yBot% THEN
y1% = yTop% - stepSize% * 2
y2% = yBot% + stepSize% * 2
IF y1% < 17 THEN
y1% = 17
END IF
IF y2% > yMax% THEN
y2% = yMax%
END IF
END IF
stepSize% = stepSize% \ 2
LOOP UNTIL stepSize% = 0
' Scan to find left and right edges of the object
stepSize% = 32
xLeft% = xMax%
xRight% = 0
x1% = 0
x2% = xMax%
DO
FOR x% = x1% TO x2% STEP stepSize%
IF x% < xLeft% OR x% > xRight% THEN
GET (x%, yTop%)-(x%, yBot%), edge%
LINE (x%, yTop%)-(x%, yBot%)
FOR i% = 2 TO size%
IF edge%(i%) THEN
IF x% < xLeft% THEN
xLeft% = x%
END IF
IF x% > xRight% THEN
xRight% = x%
END IF
i% = size%
END IF
NEXT i%
PUT (x%, yTop%), edge%, PSET
END IF
NEXT x%
IF xLeft% <= xRight% THEN
x1% = xLeft% - stepSize% * 2
x2% = xRight% + stepSize% * 2
IF x1% < 0 THEN
x1% = 0
END IF
IF x2% > xMax% THEN
x2% = xMax%
END IF
END IF
stepSize% = stepSize% \ 2
LOOP UNTIL stepSize% = 0
' Draw border around the object
LINE (xLeft% - 1, yTop% - 1)-(xRight% + 1, yBot% + 1), , B
' Build the right size integer array
stepSize% = 256
size% = 3
DO
DO
IF size% < 3 THEN
size% = 3
END IF
REDIM object%(size%)
okayFlag% = TRUE
ON ERROR GOTO ArrayError
GET (xLeft%, yTop%)-(xRight%, yBot%), object%
ON ERROR GOTO 0
IF okayFlag% = FALSE THEN
size% = size% + stepSize%
ELSE
IF stepSize% > 1 THEN
size% = size% - stepSize%
END IF
END IF
LOOP UNTIL okayFlag%
stepSize% = stepSize% \ 2
LOOP UNTIL stepSize% = 0
' Make the name of the object
objName$ = LTRIM$(RTRIM$(fileName$)) + "."
ndx% = INSTR(objName$, "\")
DO WHILE ndx%
objName$ = MID$(objName$, ndx% + 1)
ndx% = INSTR(objName$, "\")
LOOP
ndx% = INSTR(objName$, ":")
DO WHILE ndx%
objName$ = MID$(objName$, ndx% + 1)
ndx% = INSTR(objName$, ":")
LOOP
ndx% = INSTR(objName$, ".")
objName$ = LCASE$(LEFT$(objName$, ndx% - 1))
IF objName$ = "" THEN
objName$ = "xxxxxx"
END IF
' Make array name
ary$ = objName$ + "%("
' Open the file for the new source lines
OPEN fileName$ FOR OUTPUT AS #1
' Print the lines
PRINT #1, " "
PRINT #1, " ' " + objName$
FOR i% = 1 TO LEN(a$) STEP 50
PRINT #1, " ' (DRAW$) "; CHR$(34);
PRINT #1, MID$(a$, i%, 50); CHR$(34)
NEXT i%
PRINT #1, " DIM " + ary$; "0 TO";
PRINT #1, STR$(size%) + ")"
PRINT #1, " FOR i% = 0 TO"; size%
PRINT #1, " READ h$"
PRINT #1, " " + ary$ + "i%) = VAL(";
PRINT #1, CHR$(34) + "&H" + CHR$(34);
PRINT #1, " + h$)"
PRINT #1, " NEXT i%"
FOR i% = 0 TO size%
IF d$ = "" THEN
d$ = " DATA "
ELSE
d$ = d$ + ","
END IF
d$ = d$ + HEX$(object%(i%))
IF LEN(d$) > 60 OR i% = size% THEN
PRINT #1, d$
d$ = ""
END IF
NEXT i%
PRINT #1, " "
' Close the file
CLOSE
' Erase the border around the object
LINE (xLeft% - 1, yTop% - 1)-(xRight% + 1, yBot% + 1), 0, B
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
PARSE
The PARSE toolbox demonstrates the ParseLine and ParseWord subprograms.
A sample string of text (x$) is parsed by each of these subprograms, and
the results are displayed for review.
The purpose of these subprograms is to split a string into substrings,
where each substring is delineated by any of a given set of characters
that you define. For example, a string can be parsed into individual words
by splitting the string wherever spaces or commas occur.
A common use for these subprograms would be the processing of a list of
commands passed to a QuickBASIC program from the MS-DOS command line,
available in the special variable COMMAND$. The HEX2BIN, BIN2HEX, and
QBFMT programs use the PARSE toolbox in this way.
Name Type Description
──────────────────────────────────────────────────────────────────────────
PARSE.BAS Demo module
ParseLine Sub Breaks a string into individual words
ParseWord Sub Parses and removes first word from string
──────────────────────────────────────────────────────────────────────────
Demo Module: PARSE
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: PARSE **
' ** Type: Toolbox **
' ** Module: PARSE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: a$() Array of words parsed from x$
' x$ String to be parsed
' sep$ Characters defining word separation
' word$ Each word from the string
' n% Index to each word in array
DECLARE SUB ParseLine (x$, sep$, a$())
DECLARE SUB ParseWord (a$, sep$, word$)
' Initialization
CLS
DIM a$(1 TO 99)
' Demonstrate ParseWord
x$ = "This is a test line. A,B,C, etc."
sep$ = " ,"
PRINT "x$:", x$
PRINT "sep$:", CHR$(34); sep$; CHR$(34)
ParseWord x$, sep$, word$
PRINT "ParseWord x$, sep$, word$"
PRINT "x$:", x$
PRINT "word$:", word$
' Demonstrate ParseLine
PRINT
x$ = "This is a test line. A,B,C, etc."
sep$ = " ,"
PRINT "x$:", x$
PRINT "sep$:", CHR$(34); sep$; CHR$(34)
ParseLine x$, sep$, a$()
PRINT "ParseLine x$, sep$, a$()"
PRINT "a$()..."
DO
n% = n% + 1
PRINT n%, a$(n%)
LOOP UNTIL a$(n% + 1) = ""
' All done
END
──────────────────────────────────────────────────────────────────────────
Subprogram: ParseLine
Parses a string into individual words, returning the list of words in a
string array. You can list any characters in sep$ to define the division
between words, but the most commonly used characters are space, comma, and
tab. The string array will contain a null string after the last word
parsed from the string.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ParseLine **
' ** Type: Subprogram **
' ** Module: PARSE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Breaks a string into an array of words, as defined
' by any characters listed in sep$.
'
' EXAMPLE OF USE: ParseLine x$, sep$, a$()
' PARAMETERS: x$ String to be parsed
' sep$ List of characters defined as word separators
' a$() Returned array of words
' VARIABLES: t$ Temporary work string
' i% Index to array entries
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB ParseLine (x$, sep$, a$())
'
SUB ParseLine (x$, sep$, a$()) STATIC
t$ = x$
FOR i% = LBOUND(a$) TO UBOUND(a$)
ParseWord t$, sep$, a$(i%)
IF a$(i%) = "" THEN
EXIT FOR
END IF
NEXT i%
t$ = ""
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ParseWord
Extracts the first word from the front of a string, returning the word and
the original string minus the leading word. You can call this routine
repeatedly to parse out each word, one at a time. You set the characters
that separate words in sep$. For example, to parse words separated by
either spaces or commas, set as follows:
sep$ = " ,"
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ParseWord **
' ** Type: Subprogram **
' ** Module: PARSE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Breaks off the first word in a$, as delimited by
' any characters listed in sep$.
'
' EXAMPLE OF USE: ParseWord a$, sep$, word$
' PARAMETERS: a$ String to be parsed
' sep$ List of characters defined as word separato
' word$ Returned first word parsed from a$
' VARIABLES: lena% Length of a$
' i% Looping index
' j% Looping index
' k% Looping index
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB ParseWord (a$, sep$, word$)
'
SUB ParseWord (a$, sep$, word$) STATIC
word$ = ""
lena% = LEN(a$)
IF a$ = "" THEN
EXIT SUB
END IF
FOR i% = 1 TO lena%
IF INSTR(sep$, MID$(a$, i%, 1)) = 0 THEN
EXIT FOR
END IF
NEXT i%
FOR j% = i% TO lena%
IF INSTR(sep$, MID$(a$, j%, 1)) THEN
EXIT FOR
END IF
NEXT j%
FOR k% = j% TO lena%
IF INSTR(sep$, MID$(a$, k%, 1)) = 0 THEN
EXIT FOR
END IF
NEXT k%
IF i% > lena% THEN
a$ = ""
EXIT SUB
END IF
IF j% > lena% THEN
word$ = MID$(a$, i%)
a$ = ""
EXIT SUB
END IF
word$ = MID$(a$, i%, j% - i%)
IF k% > lena% THEN
a$ = ""
ELSE
a$ = MID$(a$, k%)
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
PROBSTAT
The PROBSTAT toolbox is a collection of functions for probability and
statistics calculations.
Name Type Description
──────────────────────────────────────────────────────────────────────────
PROBSTAT.BAS Demo module
ArithmeticMean# Func Arithmetic mean of an array of
numbers
Combinations# Func Combinations of n items, r at a time
Factorial# Func Factorial of a number
GeometricMean# Func Geometric mean of an array of numbers
HarmonicMean# Func Harmonic mean of an array of numbers
Permutations# Func Permutations of n items, r at a time
QuadraticMean# Func Quadratic mean of an array of numbers
──────────────────────────────────────────────────────────────────────────
Demo Module: PROBSTAT
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: PROBSTAT **
' ** Type: Toolbox **
' ** Module: PROBSTAT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates several probability- and statistics-
' related mathematical functions.
'
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: a#() Array of numbers to be processed
' i% Index into array
' n& Number of items for combinations and permuta
' r& Quantity for combinations and permutations
'
DECLARE FUNCTION Combinations# (n&, r&)
DECLARE FUNCTION Factorial# (n&)
DECLARE FUNCTION Permutations# (n&, r&)
DECLARE FUNCTION GeometricMean# (a#())
DECLARE FUNCTION HarmonicMean# (a#())
DECLARE FUNCTION ArithmeticMean# (a#())
DECLARE FUNCTION QuadraticMean# (a#())
' Demonstrations
CLS
PRINT "PROBSTAT"
PRINT
PRINT "Array of numbers..."
DIM a#(-3 TO 6)
FOR i% = -3 TO 6
READ a#(i%)
PRINT a#(i%),
NEXT i%
PRINT
DATA 1.2,3.4,5.6,7.8,9.1,2.3,4.5,6.7,8.9,1.2
PRINT
PRINT "Arithmetic mean = "; ArithmeticMean#(a#())
PRINT "Geometric mean = "; GeometricMean#(a#())
PRINT "Harmonic mean = "; HarmonicMean#(a#())
PRINT "Quadratic mean = "; QuadraticMean#(a#())
PRINT
n& = 17
r& = 5
PRINT "Combinations of"; n&; "objects taken";
PRINT r&; "at a time = "; Combinations#(n&, r&)
PRINT "Permutations of"; n&; "objects taken";
PRINT r&; "at a time = "; Permutations#(n&, r&)
PRINT
PRINT "Factorial of 17 = "; Factorial#(17&)
' All done
END
──────────────────────────────────────────────────────────────────────────
Function: ArithmeticMean#
Returns the arithmetic mean of an array of double-precision numbers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ArithmeticMean# **
' ** Type: Function **
' ** Module: PROBSTAT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the arithmetic mean of an array of numbers.
'
' EXAMPLE OF USE: ArithmeticMean# a#()
' PARAMETERS: a#() Array of double-precision numbers to be
' processed
' VARIABLES: n% Count of array entries
' sum# Sum of the array entries
' i% Looping index
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION ArithmeticMean# (a#())
'
FUNCTION ArithmeticMean# (a#()) STATIC
n% = 0
sum# = 0
FOR i% = LBOUND(a#) TO UBOUND(a#)
n% = n% + 1
sum# = sum# + a#(i%)
NEXT i%
ArithmeticMean# = sum# / n%
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Combinations#
Calculates the number of combinations of n& items taken r& at a time. This
function returns a double-precision result to allow for larger answers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Combinations# **
' ** Type: Function **
' ** Module: PROBSTAT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the number of combinations of n& items
' taken r& at a time.
'
' EXAMPLE OF USE: c# = Combinations#(n&, r&)
' PARAMETERS: n& Number of items
' r& Taken r& at a time
' VARIABLES: result# Working result variable
' j& Working copy of r&
' k& Difference between n& and r&
' h& Values from r& through n&
' i& Values from 1 through j&
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Combinations# (n&, r&)
'
FUNCTION Combinations# (n&, r&) STATIC
result# = 1
j& = r&
k& = n& - r&
h& = n&
IF j& > k& THEN
SWAP j&, k&
END IF
FOR i& = 1 TO j&
result# = (result# * h&) / i&
h& = h& - 1
NEXT i&
Combinations# = result#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Factorial#
Returns the factorial of a long integer. The returned value is
double-precision, allowing for larger arguments. This is a recursive
function. If the argument n& is greater than 1, n& is multiplied by the
result of finding the factorial of n& - 1. The result is that this
function will call itself n& times.
Notice that the STATIC keyword is missing from the end of the FUNCTION
statement because recursive functions must not be defined as STATIC.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Factorial# **
' ** Type: Function **
' ** Module: PROBSTAT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the factorial of n& (recursive).
'
' EXAMPLE OF USE: f# = Factorial#(n&)
' PARAMETERS: n& Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Factorial# (n&)
'
FUNCTION Factorial# (n&)
IF n& > 1 THEN
Factorial# = n& * Factorial#(n& - 1)
ELSE
Factorial# = 1
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: GeometricMean#
Returns the geometric mean of an array of double-precision numbers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: GeometricMean# **
' ** Type: Function **
' ** Module: PROBSTAT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the geometric mean of an array of numbers.
'
' EXAMPLE OF USE: gm# = GeometricMean#(a#())
' PARAMETERS: a#() Array of numbers to be processed
' VARIABLES: n% Count of numbers
' product# Product of all the numbers
' i% Index to array entries
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION GeometricMean# (a#())
'
FUNCTION GeometricMean# (a#()) STATIC
n% = 0
product# = 1
FOR i% = LBOUND(a#) TO UBOUND(a#)
n% = n% + 1
product# = product# * a#(i%)
NEXT i%
GeometricMean# = product# ^ (1 / n%)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: HarmonicMean#
Returns the harmonic mean of an array of double-precision numbers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: HarmonicMean# **
' ** Type: Function **
' ** Module: PROBSTAT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the harmonic mean of an array of numbers.
'
' EXAMPLE OF USE: hm# = HarmonicMean#(a#())
' PARAMETERS: a#() Array of numbers to be processed
' VARIABLES: n% Number of array entries
' sum# Sum of the reciprocal of each number
' i% Index to each array entry
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION HarmonicMean# (a#())
'
FUNCTION HarmonicMean# (a#()) STATIC
n% = 0
sum# = 0
FOR i% = LBOUND(a#) TO UBOUND(a#)
n% = n% + 1
sum# = sum# + 1# / a#(i%)
NEXT i%
HarmonicMean# = n% / sum#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Permutations#
Returns the number of permutations of n& items taken r& at a time.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Permutations# **
' ** Type: Function **
' ** Module: PROBSTAT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the permutations of n& items taken r& at a time.
'
' EXAMPLE OF USE: perm# = Permutations#(n&, r&)
' PARAMETERS: n& Number of items
' r& Taken r& at a time
' VARIABLES: p# Working variable for permutations
' i& Loop index
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Permutations# (n&, r&)
'
FUNCTION Permutations# (n&, r&) STATIC
p# = 1
FOR i& = n& - r& + 1 TO n&
p# = p# * i&
NEXT i&
Permutations# = p#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: QuadraticMean#
Returns the quadratic mean of an array of double-precision numbers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: QuadraticMean# **
' ** Type: Function **
' ** Module: PROBSTAT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the quadratic mean of an array of numbers.
'
' EXAMPLE OF USE: qm# = QuadraticMean#(a#())
' PARAMETERS: a#() Array of numbers to be processed
' VARIABLES: n% Count of array entries
' sum# Sum of the square of each number
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION QuadraticMean# (a#())
'
FUNCTION QuadraticMean# (a#()) STATIC
n% = 0
sum# = 0
FOR i% = LBOUND(a#) TO UBOUND(a#)
n% = n% + 1
sum# = sum# + a#(i%) ^ 2
NEXT i%
QuadraticMean# = SQR(sum# / n%)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
QBFMT
The QBFMT program reformats QuickBASIC modules by indenting the lines
according to the structure of the statements. For example, all lines found
between matching DO and LOOP statements are indented four character
columns more than the DO and LOOP statements. Of course, nested structures
are indented even farther.
One advantage of processing a file with this program is that improperly
matched statements are detected. Improper matching can happen if, for
example, you forget to type an END IF statement to match an IF. A special
comment line is placed in the file at the point where each error is
detected.
This utility program was an immense help throughout the creation of this
book. Each module was formatted with this program, resulting in a
consistent structure, style, and general appearance to the listings.
Notice that QuickBASIC programs to be processed by the QBFMT program must
be saved in text format and have the extension .BAS.
Name Type Description
──────────────────────────────────────────────────────────────────────────
QBFMT.BAS Program module
Indent Sub Performs line indention
SetCode Sub Determines indention code by keyword
SplitUp Sub Splits line into major components
──────────────────────────────────────────────────────────────────────────
Program Module: QBFMT
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: QBFMT **
' ** Type: Program **
' ** Module: QBFMT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Reformats a QuickBASIC program by indenting
' lines according to the structure of the statements. The
' default amount is 4 spaces if no indention parameter
' is given on the command line.
'
' USAGE: QBFMT filename [indention]
' Command$ = filename [indention]
' .MAK FILE: QBFMT.BAS
' PARSE.BAS
' STRINGS.BAS
' PARAMETERS: filename(.BAS) Name of QuickBASIC module to be formatted
' the module must be saved in "Text" format
' VARIABLES: md$ Working copy of COMMAND$ contents
' fileName$ Name of QuickBASIC module to be formatted
' dpoint% Position of the decimal point character
' in cmd$
' ndent$ Part of cmd$ dealing with optional
' indention amount
' indention% Number of character columns per
' indention level
' progline$ Each line of the file being processed
' indentLevel% Keeps track of current indention amount
' nest$ Message placed in file if faulty structur
' detected
DECLARE FUNCTION LtrimSet$ (a$, set$)
DECLARE FUNCTION RtrimSet$ (a$, set$)
DECLARE SUB Indent (a$, indention%, indentLevel%)
DECLARE SUB ParseWord (a$, sep$, word$)
DECLARE SUB SetCode (a$, keyWord$, code%)
DECLARE SUB SplitUp (a$, comment$, keyWord$)
' Decipher the user command line
cmd$ = COMMAND$
IF cmd$ = "" THEN
PRINT
PRINT "Usage: QBFMT filename(.BAS) [indention]"
SYSTEM
ELSE
ParseWord cmd$, " ,", fileName$
dpoint% = INSTR(fileName$, ".")
IF dpoint% THEN
fileName$ = LEFT$(fileName$, dpoint% - 1)
END IF
ParseWord cmd$, " ,", ndent$
indention% = VAL(ndent$)
IF indention% < 1 THEN
indention% = 4
END IF
END IF
' Try to open the indicated files
PRINT
ON ERROR GOTO ErrorTrapOne
OPEN fileName$ + ".BAS" FOR INPUT AS #1
OPEN fileName$ + ".@$@" FOR OUTPUT AS #2
ON ERROR GOTO 0
' Process each line of the file
DO
LINE INPUT #1, progLine$
Indent progLine$, indention%, indentLevel%
PRINT progLine$
PRINT #2, progLine$
IF indentLevel% < 0 OR (EOF(1) AND indentLevel% <> 0) THEN
SOUND 555, 5
SOUND 333, 9
nest$ = "'<<<<<<<<<<<<<<<<<<<<< Nesting error detected!"
PRINT nest$
PRINT #2, nest$
indentLevel% = 0
END IF
LOOP UNTIL EOF(1)
' Close all files
CLOSE
' Delete any old .BAK file
ON ERROR GOTO ErrorTrapTwo
KILL fileName$ + ".BAK"
ON ERROR GOTO 0
' Rename the files
NAME fileName$ + ".BAS" AS fileName$ + ".BAK"
NAME fileName$ + ".@$@" AS fileName$ + ".BAS"
' We're done
END
'----------- Error trapping routines
ErrorTrapOne:
PRINT "Error while opening files"
SYSTEM
ErrorTrapTwo:
RESUME NEXT
──────────────────────────────────────────────────────────────────────────
Subprogram: Indent
Performs the task of indenting each line of a program for the QBFMT
program. The indention amount is determined by the first word of each
line, and spaces are added to the front end of each line accordingly.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Indent **
' ** Type: Subprogram **
' ** Module: QBFMT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Determines the indention for each line.
'
' EXAMPLE OF USE: Indent a$, indention%, indentLevel%
' PARAMETERS: a$ Program line to be indented
' indention% Spaces to add for each indention level
' indentLevel% Level of indention
' VARIABLES: comment$ Part of program line that represents a
' REMARK
' keyWord$ First word of the program line
' code% Indention control code determined by
' keyWord$
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Indent (a$, indention%, indentLevel%)
'
SUB Indent (a$, indention%, indentLevel%) STATIC
' Break line into manageable parts
SplitUp a$, comment$, keyWord$
IF keyWord$ <> "" THEN
' Set indention code according to type of keyword
SetCode a$, keyWord$, code%
' Build a string of spaces for the indicated indention
SELECT CASE code%
CASE -2
a$ = SPACE$(indention% * indentLevel%) + a$
CASE -1
a$ = SPACE$(indention% * indentLevel%) + a$
indentLevel% = indentLevel% - 1
CASE 0
a$ = SPACE$(indention% * (indentLevel% + 1)) + a$
CASE 1
indentLevel% = indentLevel% + 1
a$ = SPACE$(indention% * indentLevel%) + a$
CASE ELSE
END SELECT
ELSE
a$ = SPACE$(indention% * indentLevel% + 2)
END IF
' Round out the position of trailing comments
IF comment$ <> "" THEN
IF a$ <> SPACE$(LEN(a$)) AND a$ <> "" THEN
a$ = a$ + SPACE$(16 - (LEN(a$) MOD 16))
END IF
END IF
' Tack the comment back onto the end of the line
a$ = a$ + comment$
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: SetCode
Determines the indention code for the QBFMT program based on the first
word of each program line. For example, if the first word of a program
line is FOR, a code number is returned that signals the QBFMT program to
indent the following lines one more level. When NEXT is encountered, the
indention level decreases by one.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SetCode **
' ** Type: Subprogram **
' ** Module: QBFMT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Determines a code number for the type of indention
' implied by the various types of keywords that begin
' each line of QuickBASIC programs.
'
' EXAMPLE OF USE: SetCode a$, keyWord$, code%
' PARAMETERS: a$ Program line to indent
' keyWord$ First word of the program line
' code% Returned code indicating the action to be
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB SetCode (a$, keyWord$, code%)
'
SUB SetCode (a$, keyWord$, code%) STATIC
SELECT CASE keyWord$
CASE "DEF"
IF INSTR(a$, "=") THEN
code% = 0
ELSE
IF INSTR(a$, " SEG") = 0 THEN
code% = 1
END IF
END IF
CASE "ELSE"
code% = -2
CASE "ELSEIF"
code% = -2
CASE "CASE"
code% = -2
CASE "END"
IF a$ <> "END" THEN
code% = -1
ELSE
code% = 0
END IF
CASE "FOR"
code% = 1
CASE "DO"
code% = 1
CASE "SELECT"
code% = 1
CASE "IF"
IF RIGHT$(a$, 4) = "THEN" THEN
code% = 1
ELSE
code% = 0
END IF
CASE "NEXT"
code% = -1
CASE "LOOP"
code% = -1
CASE "SUB"
code% = 1
CASE "FUNCTION"
code% = 1
CASE "TYPE"
code% = 1
CASE "WHILE"
code% = 1
CASE "WEND"
code% = -1
CASE ELSE
code% = 0
END SELECT
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: SplitUp
Breaks each program line into its major components for the QBFMT program.
Leading spaces and tabs are removed, and the first word and any REMARK
part are returned. Later, after the line is indented the proper amount,
the parts of the line are patched back together and output to the program
listing file.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SplitUp **
' ** Type: Subprogram **
' ** Module: QBFMT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Splits the line into statement, comment, and keyword.
'
' EXAMPLE OF USE: SplitUp a$, comment$, keyWord$
' PARAMETERS: a$ Program line to be split up
' comment$ Part of line following "REM" or "'"
' keyWord$ First word of program line
' VARIABLES: set$ Characters to be trimmed, space and tab
' strFlag% Indication of a quoted string
' k% Index to start of REMARK
' i% Looping index
' m% Pointer to REMARK
' sptr% Pointer to first space following the
' first word in a$
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB SplitUp (a$, comment$, keyWord$)
'
SUB SplitUp (a$, comment$, keyWord$) STATIC
set$ = " " + CHR$(9)
strFlag% = 0
k% = 0
FOR i% = LEN(a$) TO 1 STEP -1
IF MID$(a$, i%, 1) = CHR$(34) THEN
IF strFlag% = 0 THEN
strFlag% = 1
ELSE
strFlag% = 0
END IF
END IF
IF MID$(a$, i%, 1) = "'" OR MID$(a$, i%, 3) = "REM" THEN
IF strFlag% = 0 THEN
k% = i%
END IF
END IF
NEXT i%
IF k% > 0 THEN
m% = 0
FOR j% = k% - 1 TO 1 STEP -1
IF INSTR(set$, MID$(a$, j%, 1)) = 0 THEN
IF m% = 0 THEN m% = j%
END IF
NEXT j%
IF m% THEN
comment$ = MID$(a$, m% + 1)
a$ = LEFT$(a$, m%)
ELSE
comment$ = a$
a$ = ""
END IF
ELSE
comment$ = ""
END IF
a$ = LtrimSet$(a$, set$)
a$ = RtrimSet$(a$, set$)
comment$ = LtrimSet$(comment$, set$)
comment$ = RtrimSet$(comment$, set$)
sptr% = INSTR(a$, " ")
IF sptr% THEN
keyWord$ = UCASE$(LEFT$(a$, sptr% - 1))
ELSE
keyWord$ = UCASE$(a$)
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
QBTREE
The QBTREE program performs a recursive directory search and then displays
all file entries indented for each level of subdirectory encountered. If a
command line parameter is given, the search starts at the indicated path.
If no command line parameter is given, the search begins with the current
directory.
Name Type Description
──────────────────────────────────────────────────────────────────────────
QBTREE.BAS Program module
FileTreeSearch Sub Recursive directory search routine
──────────────────────────────────────────────────────────────────────────
Program Module: QBTREE
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: QBTREE **
' ** Type: Program **
' ** Module: QBTREE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' This program creates a list of directories and
' subdirectories, and all files in them. If no
' command line path is given, the search
' begins with the current directory.
'
' USAGE: QBTREE [path]
' REQUIREMENTS: MIXED.QLB/.LIB
' .MAK FILE: QBTREE.BAS
' FILEINFO.BAS
' PARAMETERS: path Path for starting directory search
' VARIABLES: path$ Path string, from the command line, or set
' to "*.*"
' indent% Indention amount for printing
TYPE RegTypeX
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
TYPE FileDataType
finame AS STRING * 12
year AS INTEGER
month AS INTEGER
day AS INTEGER
hour AS INTEGER
minute AS INTEGER
second AS INTEGER
attribute AS INTEGER
size AS LONG
END TYPE
' Subprograms
DECLARE SUB Interruptx (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
DECLARE SUB FindFirstFile (path$, dta$, result%)
DECLARE SUB FindNextFile (dta$, result%)
DECLARE SUB GetFileData (dta$, file AS FileDataType)
DECLARE SUB FileTreeSearch (path$, indent%)
' Create structure for deciphering the DTA file search results
DIM file AS FileDataType
' Get the command line path for starting the file search
path$ = COMMAND$
' If no path was given, then use "*.*" to search the current directory
IF path$ = "" THEN
path$ = "*.*"
END IF
' If only a drive was given, then add "*.*"
IF LEN(path$) = 2 AND RIGHT$(path$, 1) = ":" THEN
path$ = path$ + "*.*"
END IF
' Adjust the given path if necessary
IF INSTR(path$, "*") = 0 AND INSTR(path$, "?") = 0 THEN
FindFirstFile path$, dta$, result%
IF result% = 0 OR RIGHT$(path$, 1) = "\" THEN
IF RIGHT$(path$, 1) <> "\" THEN
path$ = path$ + "\"
END IF
path$ = path$ + "*.*"
END IF
END IF
' Start with a clean slate
CLS
' Call the recursive search subprogram
FileTreeSearch path$, indent%
' That's all there is to it
END
──────────────────────────────────────────────────────────────────────────
Subprogram: FileTreeSearch
Performs a recursive search for filenames in directories. Whenever a
subdirectory is encountered, the subprogram builds a modified search path
string (by adding the subdirectory name to the end of the current search
path) and calls itself again. In this way, all files in all subdirectories
are located, starting with the initial search path given.
The filenames are printed with an indention amount that is a function of
the level of recursion. This means that each subdirectory entry is
indented four spaces more than its parent directory.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FileTreeSearch **
' ** Type: Subprogram **
' ** Module: QBTREE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Directory searching and listing subprogram for
' the QBTREE program. (recursive)
'
' EXAMPLE OF USE: FileTreeSearch path$, indent%
' PARAMETERS: path$ Path for search of files
' indent% Level of indention, function of recursion
' level
' VARIABLES: file Structure of type FileDataType
' path$ Path for search of files
' dta$ Disk Transfer Area buffer string
' result% Returned result code from FindFirstFile or
' FindNextFile
' newPath$ Path with added subdirectory for recursive
' search
' MODULE LEVEL
' DECLARATIONS: TYPE FileDataType
' finame AS STRING * 12
' year AS INTEGER
' month AS INTEGER
' day AS INTEGER
' hour AS INTEGER
' minute AS INTEGER
' second AS INTEGER
' attribute AS INTEGER
' size AS LONG
' END TYPE
'
' DECLARE SUB FindFirstFile (path$, dta$, result%)
' DECLARE SUB FindNextFile (dta$, result%)
' DECLARE SUB GetFileData (dta$, file AS FileDataType)
' DECLARE SUB FileTreeSearch (path$, indent%)
'
SUB FileTreeSearch (path$, indent%)
' Create structure for deciphering the DTA file search results
DIM file AS FileDataType
' Find the first file given the current search path
FindFirstFile path$, dta$, result%
' Search through the directory for all files
DO UNTIL result%
' Unpack the Disk Transfer Area for file information
GetFileData dta$, file
' Skip the "." and ".." files
IF LEFT$(file.finame, 1) <> "." THEN
' Print the filename, indented to show tree structure
PRINT SPACE$(indent% * 4); file.finame;
' Print any other desired file information here
PRINT TAB(50); file.size;
PRINT TAB(58); file.attribute
' If we found a directory, then recursively search through it
IF file.attribute AND &H10 THEN
' Modify path$ to add this new directory to the search pa
newPath$ = path$
IF INSTR(newPath$, "\") = 0 THEN
newPath$ = "\" + newPath$
END IF
DO WHILE RIGHT$(newPath$, 1) <> "\"
newPath$ = LEFT$(newPath$, LEN(newPath$) - 1)
LOOP
newPath$ = newPath$ + file.finame + "\*.*"
' Example of recursion here
FileTreeSearch newPath$, indent% + 1
END IF
END IF
' Try to find the next file in this directory
FindNextFile dta$, result%
LOOP
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
QCAL
The QCAL program provides scientific calculator functions from the MS-DOS
command line. This program is a modified and expanded version of
MINICAL.BAS, presented earlier in this book. The original version's goal
was to demonstrate the methods used to create a small, modular program.
The functionality of the program wasn't the important issue. Here, the
program has been enhanced, and several new functions and capabilities make
this program more useful as a utility. Run the program by typing QCAL
HELP, QCAL ?, or QCAL, and a list of the available functions will be
displayed. In addition to the original five functions, several new
trigonometric, hyperbolic, and logarithmic functions have been added.
You might want to review the original MINICAL program, which is on pages
5 through 18. You'll find an explanation of how the numeric values are
placed on the stack and how the functions operate on those values.
Because of the modular, structured organization of QuickBASIC programs,
you can easily modify this program to include other functions. To add a
new function, modify the Process and QcalHelp subprograms where
applicable, and follow the same pattern of stack and variable
manipulations exhibited by the other routines when writing your own.
Name Type Description
──────────────────────────────────────────────────────────────────────────
QCAL.BAS Program module
DisplayStack Sub Displays final results of the program
NextParameter$ Func Extracts number or command from
COMMAND$
Process Sub Controls action for command line
parameters
QcalHelp Sub Provides a "Help" display for program
──────────────────────────────────────────────────────────────────────────
Program Module: QCAL
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: QCAL **
' ** Type: Program **
' ** Module: QCAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: QCAL [number] [function] [...]
' .MAK FILE: QCAL.BAS
' QCALMATH.BAS
' PARAMETERS: [number] Numbers to be placed on the stack
' [function] Operations to be performed on the stack
' contents
' VARIABLES: cmd$ Working copy of COMMAND$
' stack#() Array representing the numeric stack
' ptr% Index into the stack
' parm$ Each number of command extracted from cm
' Constants
CONST PI = 3.141592653589793#
' Functions
DECLARE FUNCTION AbsoluteX# (x#)
DECLARE FUNCTION Add# (y#, x#)
DECLARE FUNCTION ArcCosine# (x#)
DECLARE FUNCTION ArcHypCosine# (x#)
DECLARE FUNCTION ArcHypSine# (x#)
DECLARE FUNCTION ArcHypTangent# (x#)
DECLARE FUNCTION ArcSine# (x#)
DECLARE FUNCTION ArcTangent# (x#)
DECLARE FUNCTION Ceil# (x#)
DECLARE FUNCTION ChangeSign# (x#)
DECLARE FUNCTION Cosine# (x#)
DECLARE FUNCTION Divide# (y#, x#)
DECLARE FUNCTION Exponential# (x#)
DECLARE FUNCTION FractionalPart# (x#)
DECLARE FUNCTION HypCosine# (x#)
DECLARE FUNCTION HypSine# (x#)
DECLARE FUNCTION HypTangent# (x#)
DECLARE FUNCTION IntegerPart# (x#)
DECLARE FUNCTION LogBase10# (x#)
DECLARE FUNCTION LogBaseN# (y#, x#)
DECLARE FUNCTION LogE# (x#)
DECLARE FUNCTION Modulus# (y#, x#)
DECLARE FUNCTION Multiply# (y#, x#)
DECLARE FUNCTION NextParameter$ (cmd$)
DECLARE FUNCTION OneOverX# (x#)
DECLARE FUNCTION Sign# (x#)
DECLARE FUNCTION Sine# (x#)
DECLARE FUNCTION SquareRoot# (x#)
DECLARE FUNCTION Subtract# (y#, x#)
DECLARE FUNCTION Tangent# (x#)
DECLARE FUNCTION Xsquared# (x#)
DECLARE FUNCTION YRaisedToX# (y#, x#)
' Subprograms
DECLARE SUB QcalHelp ()
DECLARE SUB Process (parm$, stack#(), ptr%)
DECLARE SUB DisplayStack (stack#(), ptr%)
DECLARE SUB SwapXY (stack#(), ptr%)
' Get the command line
cmd$ = COMMAND$
' First check if user is asking for help
IF cmd$ = "" OR cmd$ = "HELP" OR cmd$ = "?" THEN
QcalHelp
SYSTEM
END IF
' Create a pseudo stack
DIM stack#(1 TO 20)
ptr% = 0
' Process each part of the command line
DO UNTIL cmd$ = ""
parm$ = NextParameter$(cmd$)
Process parm$, stack#(), ptr%
IF ptr% < 1 THEN
PRINT "Not enough stack values"
SYSTEM
END IF
LOOP
' Display results
DisplayStack stack#(), ptr%
' All done
END
──────────────────────────────────────────────────────────────────────────
Subprogram: DisplayStack
Displays the final results of the QCAL program. When the QCAL program is
finished, one or more numeric values are left on the stack, representing
the final values of the calculations. If the stack has a single value
remaining on it, this number is displayed with the label Result.... If,
however, two or more values are left on the stack after QCAL has acted on
all functions, the values are displayed with the label Stack ...,
indicating to the user that more than a single result was left on the
stack.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: DisplayStack **
' ** Type: Subprogram **
' ** Module: QCAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Displays the value(s) left on the stack when QCAL
' is finished processing the command line.
'
' EXAMPLE OF USE: DisplayStack stack#(), ptr%
' PARAMETERS: stack#() Array of numbers representing the stack
' ptr% Index into the stack
' VARIABLES: i% Looping index
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB DisplayStack (stack#(), ptr%)
'
SUB DisplayStack (stack#(), ptr%) STATIC
PRINT
IF ptr% > 1 THEN
PRINT "Stack ... ",
ELSE
PRINT "Result... ",
END IF
FOR i% = 1 TO ptr%
PRINT stack#(i%),
NEXT i%
PRINT
END SUB
──────────────────────────────────────────────────────────────────────────
Function: NextParameter$
Returns the first group of nonspace characters found at the left of the
passed string. The passed string is then trimmed of these characters,
along with any extra spaces.
The PARSE.BAS module contains alternative routines that perform the same
function in a slightly different way. Take a look at the ParseWord and
ParseLine routines found there. The NextParameter$ subprogram
demonstrates how the code from a module can be copied and modified for a
specific purpose, with any extra code removed. This results in a smaller
program but has the disadvantage that any future changes to the
PARSE.BAS module will probably not show up here in the QCAL.BAS module.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: NextParameter$ **
' ** Type: Function **
' ** Module: QCAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Extracts parameters from the front of the
' command line. Parameters are groups of any
' characters separated by spaces.
'
' EXAMPLE OF USE: parm$ = NextParameter$(cmd$)
' PARAMETERS: cmd$ The working copy of COMMAND$
' VARIABLES: parm$ Each number or command from cmd$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION NextParameter$ (cmd$)
'
FUNCTION NextParameter$ (cmd$) STATIC
parm$ = ""
DO WHILE LEFT$(cmd$, 1) <> " " AND cmd$ <> ""
parm$ = parm$ + LEFT$(cmd$, 1)
cmd$ = MID$(cmd$, 2)
LOOP
DO WHILE LEFT$(cmd$, 1) = " " AND cmd$ <> ""
cmd$ = MID$(cmd$, 2)
LOOP
NextParameter$ = parm$
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: Process
Acts upon each command line parameter. If the parameter is a valid
function, the function is called, and the stack is adjusted appropriately.
If the parameter isn't a recognizable function, it is assumed to be a
numeric quantity. The VAL function converts the parameter to its numeric
equivalent, and the result is pushed on the stack, ready for the next
operation.
This subprogram demonstrates a fairly long CASE statement. The same logic
could be developed using IF THEN, ELSE IF, ELSE, and END IF statements,
but the CASE statement is ideal for making selections from a large number
of choices in this way.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Process **
' ** Type: Subprogram **
' ** Module: QCAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Processes each command parameter for the QCAL
' program.
'
' EXAMPLE OF USE: Process parm$, stack#(), ptr%
' PARAMETERS: parm$ The command line parameter to be processed
' stack#() Array of numbers representing the stack
' ptr% Index pointing to last stack entry
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Process (parm$, stack#(), ptr%)
'
SUB Process (parm$, stack#(), ptr%) STATIC
SELECT CASE parm$
CASE "+"
ptr% = ptr% - 1
IF ptr% > 0 THEN
stack#(ptr%) = Add#(stack#(ptr%), stack#(ptr% + 1))
END IF
CASE "-"
ptr% = ptr% - 1
IF ptr% > 0 THEN
stack#(ptr%) = Subtract#(stack#(ptr%), stack#(ptr% + 1))
END IF
CASE "*"
ptr% = ptr% - 1
IF ptr% > 0 THEN
stack#(ptr%) = Multiply#(stack#(ptr%), stack#(ptr% + 1))
END IF
CASE "/"
ptr% = ptr% - 1
IF ptr% > 0 THEN
stack#(ptr%) = Divide#(stack#(ptr%), stack#(ptr% + 1))
END IF
CASE "CHS"
IF ptr% > 0 THEN
stack#(ptr%) = ChangeSign#(stack#(ptr%))
END IF
CASE "ABS"
IF ptr% > 0 THEN
stack#(ptr%) = AbsoluteX#(stack#(ptr%))
END IF
CASE "SGN"
IF ptr% > 0 THEN
stack#(ptr%) = Sign#(stack#(ptr%))
END IF
CASE "INT"
IF ptr% > 0 THEN
stack#(ptr%) = IntegerPart#(stack#(ptr%))
END IF
CASE "MOD"
ptr% = ptr% - 1
IF ptr% > 0 THEN
stack#(ptr%) = Modulus#(stack#(ptr%), stack#(ptr% + 1))
END IF
CASE "FRC"
IF ptr% > 0 THEN
stack#(ptr%) = FractionalPart#(stack#(ptr%))
END IF
CASE "1/X"
IF ptr% > 0 THEN
stack#(ptr%) = OneOverX#(stack#(ptr%))
END IF
CASE "SQR"
IF ptr% > 0 THEN
stack#(ptr%) = SquareRoot#(stack#(ptr%))
END IF
CASE "X2"
IF ptr% > 0 THEN
stack#(ptr%) = Xsquared#(stack#(ptr%))
END IF
CASE "SIN"
IF ptr% > 0 THEN
stack#(ptr%) = Sine#(stack#(ptr%))
END IF
CASE "COS"
IF ptr% > 0 THEN
stack#(ptr%) = Cosine#(stack#(ptr%))
END IF
CASE "TAN"
IF ptr% > 0 THEN
stack#(ptr%) = Tangent#(stack#(ptr%))
END IF
CASE "ASN"
IF ptr% > 0 THEN
stack#(ptr%) = ArcSine#(stack#(ptr%))
END IF
CASE "ACS"
IF ptr% > 0 THEN
stack#(ptr%) = ArcCosine#(stack#(ptr%))
END IF
CASE "ATN"
IF ptr% > 0 THEN
stack#(ptr%) = ArcTangent#(stack#(ptr%))
END IF
CASE "HSN"
IF ptr% > 0 THEN
stack#(ptr%) = HypSine#(stack#(ptr%))
END IF
CASE "HCS"
IF ptr% > 0 THEN
stack#(ptr%) = HypCosine#(stack#(ptr%))
END IF
CASE "HTN"
IF ptr% > 0 THEN
stack#(ptr%) = HypTangent#(stack#(ptr%))
END IF
CASE "AHS"
IF ptr% > 0 THEN
stack#(ptr%) = ArcHypSine#(stack#(ptr%))
END IF
CASE "AHC"
IF ptr% > 0 THEN
stack#(ptr%) = ArcHypCosine#(stack#(ptr%))
END IF
CASE "AHT"
IF ptr% > 0 THEN
stack#(ptr%) = ArcHypTangent#(stack#(ptr%))
END IF
CASE "LOG"
IF ptr% > 0 THEN
stack#(ptr%) = LogE#(stack#(ptr%))
END IF
CASE "LOG10"
IF ptr% > 0 THEN
stack#(ptr%) = LogBase10#(stack#(ptr%))
END IF
CASE "LOGN"
ptr% = ptr% - 1
IF ptr% > 0 THEN
stack#(ptr%) = LogBaseN#(stack#(ptr%), stack#(ptr% + 1))
END IF
CASE "EXP"
IF ptr% > 0 THEN
stack#(ptr%) = Exponential#(stack#(ptr%))
END IF
CASE "CEIL"
IF ptr% > 0 THEN
stack#(ptr%) = Ceil#(stack#(ptr%))
END IF
CASE "Y^X"
ptr% = ptr% - 1
IF ptr% > 0 THEN
stack#(ptr%) = YRaisedToX#(stack#(ptr%), stack#(ptr% + 1))
END IF
CASE "PI"
ptr% = ptr% + 1
stack#(ptr%) = PI
CASE "SWAP"
SwapXY stack#(), ptr%
CASE "DUP"
IF ptr% > 0 THEN
stack#(ptr% + 1) = stack#(ptr%)
ptr% = ptr% + 1
END IF
CASE ELSE
ptr% = ptr% + 1
stack#(ptr%) = VAL(parm$)
END SELECT
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: QcalHelp
Provides a Help display for the QCAL program.
One feature that sets good software apart from mediocre software is the
ability to provide on-line help for the user. Nothing is more frustrating
than a program that terminates suddenly, without any explanation of the
problem or suggestion for solving it.
The QcalHelp subprogram demonstrates one approach to helping the user with
a program. Entering any of the following command lines will cause the QCAL
program to call QcalHelp:
QCAL HELP
QCAL ?
QCAL
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: QcalHelp **
' ** Type: Subprogram **
' ** Module: QCAL.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Displays a help screen when QCAL is run with no
' parameters or with a parameter of ? or HELP.
'
' EXAMPLE OF USE: QcalHelp
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB QcalHelp ()
'
SUB QcalHelp STATIC
PRINT
PRINT "Usage: QCAL [number] [function] [...] <Enter>"
PRINT
PRINT "Numbers are placed on an RPN stack, and functions operate"
PRINT "on the stacked quantities. When the program is finished,"
PRINT "whatever is left on the stack is displayed."
PRINT
PRINT "List of available functions..."
PRINT
PRINT "Two numbers: + - * /"
PRINT "One number: CHS ABS SGN INT MOD FRC CHS 1/X SQR X2 CEIL
PRINT "Trigonometric: SIN COS TAN ASN ACS ATN"
PRINT "Hyperbolic: HSN HCS HTN AHS AHC AHT"
PRINT "Logarithmic: LOG LOG10 LOGN EXP Y^X"
PRINT "Constants: PI"
PRINT "Stack: SWAP DUP"
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
QCALMATH
QCALMATH is a toolbox of scientific functions for the QCAL program.
Several functions included in QCALMATH are similar to functions provided
by QuickBASIC. You could shorten QCALMATH by deleting these functions here
and coding the QuickBASIC routines directly in the Process subprogram,
located in the QCAL.BAS module. However, there's something to be said for
keeping the functions as shown here: QCALMATH checks for additional error
conditions and generates clear messages if errors exist. For example,
although the SquareRoot# function duplicates a QuickBASIC function,
SquareRoot# checks for values less than 0 before trying to find the
square root and prints a clear message if such an attempt is made.
It's easy to add your own functions to the QCAL program. Simply create
the function in the same format and style as shown in this module. You'll
also need to modify the Process subprogram in the QCAL module to let the
program call the new function. For the final touch, be sure to add the new
function to the list displayed by the QcalHelp subprogram.
QCALMATH is the only toolbox in this book that doesn't have any
module-level code to demonstrate the subprograms and functions. The QCAL
program loads this toolbox and provides the demonstration code.
╓┌─┌─────────────────────────────┌──────┌────────────────────────────────────╖
Name Type Description
Name Type Description
──────────────────────────────────────────────────────────────────────────
QCALMATH.BAS Toolbox
AbsoluteX# Func Absolute value of a number
Add# Func Sum of two numbers
ArcCosine# Func Arc cosine function of a number
ArcHypCosine# Func Inverse hyperbolic cosine of a number
ArcHypSine# Func Inverse hyperbolic sine of a number
ArcHypTangent# Func Inverse hyperbolic tangent of a
number
ArcSine# Func Inverse sine of a number
ArcTangent# Func Inverse tangent of a number
Ceil# Func Smallest whole number greater than a
number
ChangeSign# Func Reverses sign of a number
Cosine# Func Cosine of a number
Divide# Func Result of dividing two numbers
Dup Sub Duplicates top entry on the stack
Exponential# Func Exponential function of a number
FractionalPart# Func Fractional part of a number
HypCosine# Func Hyperbolic cosine of a number
Name Type Description
──────────────────────────────────────────────────────────────────────────
HypCosine# Func Hyperbolic cosine of a number
HypSine# Func Hyperbolic sine of a number
HypTangent# Func Hyperbolic tangent of a number
IntegerPart# Func Integer part of a number
LogBase10# Func Log base 10 of a number
LogBaseN# Func Log base N of a number
LogE# Func Natural logarithm of a number
Modulus# Func Remainder of the division of two
numbers
Multiply# Func Product of two numbers
OneOverX# Func Result of dividing 1 by a number
Sign# Func Sign of a number
Sine# Func Sine of a number
SquareRoot# Func Square root of a number
Subtract# Func Difference between two numbers
SwapXY Sub Swaps top two entries on the stack
Tangent# Func Tangent of a number
Xsquared# Func Square of a number
YRaisedToX# Func Number raised to the power of a
Name Type Description
──────────────────────────────────────────────────────────────────────────
YRaisedToX# Func Number raised to the power of a
second
──────────────────────────────────────────────────────────────────────────
Toolbox: QCALMATH
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: QCALMATH **
' ** Type: Toolbox **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Collection of math functions and subprograms for
' the QCAL program.
'
' USAGE: (loaded by the QCAL program)
'.MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: (none)
' Constants
CONST PI = 3.141592653589793#
CONST L10 = 2.302585092994046#
' Functions
DECLARE FUNCTION AbsoluteX# (x#)
DECLARE FUNCTION Add# (y#, x#)
DECLARE FUNCTION ArcCosine# (x#)
DECLARE FUNCTION ArcHypCosine# (x#)
DECLARE FUNCTION ArcHypSine# (x#)
DECLARE FUNCTION ArcHypTangent# (x#)
DECLARE FUNCTION ArcSine# (x#)
DECLARE FUNCTION ArcTangent# (x#)
DECLARE FUNCTION Ceil# (x#)
DECLARE FUNCTION ChangeSign# (x#)
DECLARE FUNCTION Cosine# (x#)
DECLARE FUNCTION Divide# (y#, x#)
DECLARE FUNCTION Exponential# (x#)
DECLARE FUNCTION FractionalPart# (x#)
DECLARE FUNCTION HypCosine# (x#)
DECLARE FUNCTION HypSine# (x#)
DECLARE FUNCTION HypTangent# (x#)
DECLARE FUNCTION IntegerPart# (x#)
DECLARE FUNCTION LogBase10# (x#)
DECLARE FUNCTION LogBaseN# (y#, x#)
DECLARE FUNCTION LogE# (x#)
DECLARE FUNCTION Modulus# (y#, x#)
DECLARE FUNCTION Multiply# (y#, x#)
DECLARE FUNCTION OneOverX# (x#)
DECLARE FUNCTION Sign# (x#)
DECLARE FUNCTION Sine# (x#)
DECLARE FUNCTION SquareRoot# (x#)
DECLARE FUNCTION Subtract# (y#, x#)
DECLARE FUNCTION Tangent# (x#)
DECLARE FUNCTION Xsquared# (x#)
DECLARE FUNCTION YRaisedToX# (y#, x#)
──────────────────────────────────────────────────────────────────────────
Function: AbsoluteX#
Returns the absolute value of the passed value. The absolute value of a
number is that number's positive value.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: AbsoluteX# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = AbsoluteX#(x#)
' PARAMETERS: x# Double-precision value to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION AbsoluteX# (x#)
'
FUNCTION AbsoluteX# (x#) STATIC
AbsoluteX# = ABS(x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Add#
Returns the sum of two double-precision numbers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Add# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: z# = Add#(y#, x#)
' PARAMETERS: y# First number
' x# Second number
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Add# (y#, x#)
'
FUNCTION Add# (y#, x#) STATIC
Add# = y# + x#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: ArcCosine#
Returns the arc cosine of a number; the returned angle is expressed in
radians. If the number passed is less than 1, an error message is
displayed, and the program terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ArcCosine# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = ArcCosine#(x#)
' PARAMETERS: x# Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION ArcCosine# (x#)
'
FUNCTION ArcCosine# (x#) STATIC
x2# = x# * x#
IF x2# < 1# THEN
ArcCosine# = PI / 2# - ATN(x# / SQR(1# - x# * x#))
ELSE
PRINT "Error: ACS(x#) where x# < 1"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: ArcHypCosine#
Returns the inverse hyperbolic cosine of a number. If the number passed is
less than or equal to 1, an error message is displayed, and the program
terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ArcHypCosine# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = ArcHypCosine#(x#)
' PARAMETERS: x# Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION ArcHypCosine# (x#)
'
FUNCTION ArcHypCosine# (x#) STATIC
IF ABS(x#) > 1# THEN
ArcHypCosine# = LOG(x# + SQR(x# * x# - 1#))
ELSE
PRINT "Error: AHS(x#) where -1 <= x# <= +1"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: ArcHypSine#
Returns the inverse hyperbolic sine of a number.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ArcHypSine# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = ArcHypSine#(x#)
' PARAMETERS: x# Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION AryHypSine# (x#)
'
FUNCTION ArcHypSine# (x#) STATIC
ArcHypSine# = LOG(x# + SQR(1# + x# * x#))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: ArcHypTangent#
Returns the inverse hyperbolic tangent of a number. If the number passed
is less than -1 or greater than 1, an error message is displayed, and the
program terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ArcHypTangent# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = ArcHypTangent#(x#)
' PARAMETERS: x# Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION ArcHypTangent# (x#)
'
FUNCTION ArcHypTangent# (x#) STATIC
IF ABS(x#) < 1 THEN
ArcHypTangent# = LOG((1# + x#) / (1# - x#)) / 2#
ELSE
PRINT "Error: AHT(x#) where x# <= -1 or x# >= +1"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: ArcSine#
Returns the inverse sine of a number. If the number passed is greater than
or equal to 1, the function displays an error message, and the program
terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ArcSine# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = ArcSine#(x#)
' PARAMETERS: x# Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION ArcSine# (x#)
'
FUNCTION ArcSine# (x#) STATIC
x2# = x# * x#
IF x2# < 1# THEN
ArcSine# = ATN(x# / SQR(1# - x# * x#))
ELSE
PRINT "Error: ASN(x#) where x# >= 1"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: ArcTangent#
Returns the inverse tangent of a number.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ArcTangent# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = ArcTangent#(x#)
' PARAMETERS: x# Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION ArcTangent# (x#)
'
FUNCTION ArcTangent# (x#) STATIC
ArcTangent# = ATN(x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Ceil#
Returns the smallest whole number that is greater than a number. For
example, Ceil#(3.14) returns 4, Ceil#(-3.14) returns -3, and Ceil#(17)
returns 17.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Ceil# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Ceil#(x#)
' PARAMETERS: x# Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Ceil# (x#)
'
FUNCTION Ceil# (x#) STATIC
Ceil# = -INT(-x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: ChangeSign#
Returns a number with its sign changed. This function could easily be
deleted from QCAL by changing the Process subprogram so that it directly
performs negation in the CASE statement in which the CHS command is acted
upon. I decided to provide a consistent interface between the functions in
the QCALMATH module and the Process subprogram, however, making it
easier to add, delete, or modify functions as desired.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ChangeSign# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = ChangeSign#(x#)
' PARAMETERS: x# Number to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION ChangeSign# (x#)
'
FUNCTION ChangeSign# (x#) STATIC
ChangeSign# = -x#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Cosine#
Returns the cosine of an angle.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Cosine# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Cosine#(x#)
' PARAMETERS: x# Angle to be evaluated
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Cosine# (x#)
'
FUNCTION Cosine# (x#) STATIC
Cosine# = COS(x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Divide#
Returns the result of dividing two numbers. If a division by 0 is
attempted, the function displays an error message, and the program
terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Divide# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Divide#(y#, x#)
' PARAMETERS: y# Number to be processed
' x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Divide# (y#, x#)
'
FUNCTION Divide# (y#, x#) STATIC
IF x# <> 0 THEN
Divide# = y# / x#
ELSE
PRINT "Error: Division by zero"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: Dup
Duplicates the top entry on the stack for the QCAL program.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Dup **
' ** Type: Subprogram **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: Dup stack#(), ptr%
' PARAMETERS: stack#() Numeric stack
' ptr% Index to last entry on stack
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Dup (Stack#(), ptr%)
'
SUB Dup (stack#(), ptr%) STATIC
IF ptr% THEN
ptr% = ptr% + 1
stack#(ptr%) = stack#(ptr% - 1)
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Function: Exponential#
Returns the exponential function of a number.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Exponential# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Exponential#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Exponential# (x#)
'
FUNCTION Exponential# (x#) STATIC
Exponential# = EXP(x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: FractionalPart#
Returns the fractional part of a number. For example, the fractional part
of 3.14 is .14, of -3.14 is -.14, and of 17 is 0.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FractionalPart# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = FractionalPart#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION FractionalPart# (x#)
'
FUNCTION FractionalPart# (x#) STATIC
IF x# >= 0 THEN
FractionalPart# = x# - INT(x#)
ELSE
FractionalPart# = x# - INT(x#) - 1#
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: HypCosine#
Returns the hyperbolic cosine of a number.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: HypCosine# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = HypCosine#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION HypCosine# (x#)
'
FUNCTION HypCosine# (x#) STATIC
HypCosine# = (EXP(x#) + EXP(-x#)) / 2#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: HypSine#
Returns the hyperbolic sine of a number.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: HypSine# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = HypSine#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION HypSine# (x#)
'
FUNCTION HypSine# (x#) STATIC
HypSine# = (EXP(x#) - EXP(-x#)) / 2#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: HypTangent#
Returns the hyperbolic tangent of a number.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: HypTangent# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = HypTangent#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION HypTangent# (x#)
'
FUNCTION HypTangent# (x#) STATIC
HypTangent# = (EXP(x#) - EXP(-x#)) / (EXP(x#) + EXP(-x#))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: IntegerPart#
Returns the integer part of a number. For example, the integer part of
3.14 is 3, of -3.14 is -4, and of 17 is 17.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: IntegerPart# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = IntegerPart#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION IntegerPart# (x#)
'
FUNCTION IntegerPart# (x#) STATIC
IntegerPart# = INT(x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: LogBase10#
Returns the logarithm, base 10, of a number. If the number is not greater
than 0, the function displays an error message, and the program
terminates.
Look in the listing at the constant L10, defined in the module-level code
of QCALMATH. This constant is the double-precision natural logarithm of
10. The constant can be replaced with LOG(10), its mathematic equivalent,
but using a constant makes the program faster and the compiled program
shorter.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: LogBase10# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Log10#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION LogBase10# (x#)
'
FUNCTION LogBase10# (x#) STATIC
IF x# > 0 THEN
LogBase10# = LOG(x#) / L10
ELSE
PRINT "Error: LOG10(x#) where x# <= 0"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: LogBaseN#
Returns the logarithm, base N, of a number. This function checks for
several possible error conditions. The number to be processed must be
greater than 0, and the base for finding the logarithm must be greater
than 0 and must not be exactly 1. If one of these checks fails, a message
is displayed, and the program terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: LogBaseN# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = LogBaseN#(y#, x#)
' PARAMETERS: y# Number to be processed
' x# The base for finding the logarithm
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION LogBaseN# (y#, x#)
'
FUNCTION LogBaseN# (y#, x#) STATIC
IF x# <= 0 THEN
PRINT "Error: LOGN(y#, x#) where x# <= 0"
SYSTEM
ELSEIF x# = 1# THEN
PRINT "Error: LOGN(y#, x#) where x# = 1"
SYSTEM
ELSEIF y# <= 0 THEN
PRINT "Error: LOGN(y#, x#) where y# is <= 0"
SYSTEM
ELSE
LogBaseN# = LOG(y#) / LOG(x#)
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: LogE#
Returns the natural logarithm of a number. The QuickBASIC function LOG()
is used to calculate the logarithm, but this function first checks that
the number is greater than 0. If the number is equal to or less than 0, an
error message is displayed, and the program terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: LogE# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = LogE#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION LogE# (x#)
'
FUNCTION LogE# (x#) STATIC
IF x# > 0 THEN
LogE# = LOG(x#)
ELSE
PRINT "Error: LOGE(x#) where x# <= 0"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Modulus#
Returns the remainder of the division of two numbers. If a division by 0
is attempted, the function displays an error message, and the program
terminates. The function is valid for non-integer quantities.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Modulus# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Modulus#(y#, x#)
' PARAMETERS: y# Number to be divided
' x# Number for dividing by
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Modulus# (y#, x#)
'
FUNCTION Modulus# (y#, x#) STATIC
IF x# <> 0 THEN
Modulus# = y# - INT(y# / x#) * x#
ELSE
PRINT "Error: MOD(y#, x#) where x# = 0"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Multiply#
Returns the product of two numbers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Multiply# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Multiply#(y#, x#)
' PARAMETERS: y# First number to be processed
' x# Second number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Multiply# (y#, x#)
'
FUNCTION Multiply# (y#, x#) STATIC
Multiply# = y# * x#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: OneOverX#
Returns the result of dividing 1 by a number. If a division by 0 is
attempted, the function displays an error message, and the program
terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: OneOverX# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = OneOverX#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION OneOverX# (x#)
'
FUNCTION OneOverX# (x#) STATIC
IF x# <> 0 THEN
OneOverX# = 1# / x#
ELSE
PRINT "Error: 1/x where x = 0"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Sign#
Returns -1 for all negative numbers, 1 for positive numbers, and 0 for
zero.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Sign# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Sign#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Sign# (x#)
'
FUNCTION Sign# (x#) STATIC
Sign# = SGN(x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Sine#
Returns the sine of an angle; assumes the angle is expressed in radians.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Sine# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Sine#(x#)
' PARAMETERS: x# Angle, expressed in radians
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Sine# (x#)
'
FUNCTION Sine# (x#) STATIC
Sine# = SIN(x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: SquareRoot#
Returns the square root of a number. Before the QuickBASIC SQR function is
used to actually find the square root, the number is checked to be sure it
isn't negative. If it is, an error message is displayed, and the program
terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SquareRoot# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = SquareRoot#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION SquareRoot# (x#)
'
FUNCTION SquareRoot# (x#) STATIC
IF x# >= 0 THEN
SquareRoot# = SQR(x#)
ELSE
PRINT "Error: SQR(x#) where x# < 0"
SYSTEM
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Subtract#
Returns the difference of two numbers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Subtract# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Subtract#(y#, x#)
' PARAMETERS: y# Number to be processed
' x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Subtract# (y#, x#)
'
FUNCTION Subtract# (y#, x#) STATIC
Subtract# = y# - x#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: SwapXY
Swaps the top two entries on the stack.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: SwapXY **
' ** Type: Subprogram **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: SwapXY stack#(), ptr%
' PARAMETERS: stack#() Numeric stack
' ptr% Pointer to top of stack
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB SwapXY (stack#(), ptr%
'
SUB SwapXY (stack#(), ptr%) STATIC
IF ptr% > 1 THEN
SWAP stack#(ptr%), stack#(ptr% - 1)
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
Function: Tangent#
Returns the tangent of an angle; assumes the angle is in radians.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Tangent# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Tangent#(x#)
' PARAMETERS: x# Angle, expressed in radians
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Tangent# (x#)
'
FUNCTION Tangent# (x#) STATIC
Tangent# = TAN(x#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Xsquared#
Returns the square of a number.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Xsquared# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: y# = Xsquared#(x#)
' PARAMETERS: x# Number to be processed
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Xsquared# (x#)
'
FUNCTION Xsquared# (x#) STATIC
Xsquared# = x# * x#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: YRaisedToX#
Returns a number raised to the power of a second number.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: YRaisedToX# **
' ** Type: Function **
' ** Module: QCALMATH.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' EXAMPLE OF USE: z# = YRaisedToX#(y#, x#)
' PARAMETERS: y# Number to be raised to a power
' x# Power to raise the other number to
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION YRaisedToX# (y#, x#)
'
FUNCTION YRaisedToX# (y#, x#) STATIC
YRaisedToX# = y# ^ x#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
RANDOMS
The RANDOMS toolbox provides a collection of random number generators.
At the heart of these routines are two techniques, which are described in
The Art of Computer Programming, Vol. 2, Seminumerical Algorithms, by
Donald Knuth and which are combined to form the method in the Rand&
function. The Rand& function returns pseudorandom integers in the range 0
through 999999999. No multiplication or division is used, the algorithm is
easily translated to any language that supports 32-bit integers, and all
digits in the returned numbers are equally random. A table-shuffling
technique further increases the randomness of the sequence.
Several other functions use the random long integers returned by the
Rand& function to create other random number distributions. For example,
the RandReal!(x!, y!) function returns random real numbers in the range x!
through y!. One common example of this function, RandReal!(0!, 1!),
returns a pseudorandom, single-precision, floating-point value in the
range 0 through 1.
The RandShuffle subprogram and the RandInteger% function are used by
CIPHER to generate a repeatable but secure sequence of random byte values
in the range 0 through 255. See the CIPHER program for more information
on using this file-ciphering technique.
Name Type Description
──────────────────────────────────────────────────────────────────────────
RANDOMS.BAS Demo module
Rand& Func Long integers
RandExponential! Func Real value with exponential
distribution from mean
RandFrac! Func Single-precision positive value < 1.0
RandInteger% Func Integers within desired range
RandNormal! Func Single-precision value from mean
and standard deviation
RandReal! Func Single-precision value in desired
range
RandShuffle Sub Initializes random number generator
──────────────────────────────────────────────────────────────────────────
Demo Module: RANDOMS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: RANDOMS **
' ** Type: Toolbox **
' ** Module: RANDOMS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: i% Loop index for generating pseudorandom numbers
DECLARE FUNCTION Rand& ()
DECLARE FUNCTION RandExponential! (mean!)
DECLARE FUNCTION RandFrac! ()
DECLARE FUNCTION RandInteger% (a%, b%)
DECLARE FUNCTION RandNormal! (mean!, stddev!)
DECLARE FUNCTION RandReal! (x!, y!)
DECLARE SUB RandShuffle (key$)
' Array of long integers for generating all randoms
DIM SHARED r&(1 TO 100)
' Clear the screen
CLS
' Shuffle the random number generator, creating a
' unique sequence for every possible second
RandShuffle DATE$ + TIME$
PRINT "Rand&"
FOR i% = 1 TO 5
PRINT Rand&,
NEXT i%
PRINT
PRINT "RandInteger%(0, 9)"
FOR i% = 1 TO 5
PRINT RandInteger%(0, 9),
NEXT i%
PRINT
PRINT "RandReal!(-10!, 10!)"
FOR i% = 1 TO 5
PRINT RandReal!(-10!, 10!),
NEXT i%
PRINT
PRINT "RandExponential!(100!)"
FOR i% = 1 TO 5
PRINT RandExponential!(100!),
NEXT i%
PRINT
PRINT "RandNormal!(100!, 10!)"
FOR i% = 1 TO 5
PRINT RandNormal!(100!, 10!),
NEXT i%
PRINT
PRINT "RandFrac!"
FOR i% = 1 TO 5
PRINT RandFrac!,
NEXT i%
PRINT
──────────────────────────────────────────────────────────────────────────
Function: Rand&
Returns a pseudorandom long integer in the range 0 through 999999999,
inclusive. Using the Rand& function provides you several advantages: It is
fast because a minimal number of mathematical manipulations are performed;
the sequence length is long, much greater than 2^55; and all digits in the
returned random integer are equally random.
The array of long integers, r&(1 TO 100), is shared by this function and
the RandShuffle subprogram. This array contains a table of 55 random
integers, a table of 42 values for shuffling the order of the random
numbers upon output, two index pointers into the first 55 values, and the
last generated random integer.
You must call the RandShuffle subprogram once before you use the Rand&
function. This initializes the tables and presets the two index numbers
used to access table entries. If you don't call RandShuffle
first, the Rand& function stops, you receive a Subscript out of range
error message, and the program halts.
Here's how Rand& works. The index numbers stored in r&(98) and r&(99) are
always in the range 1 through 55 and are used to access two numbers stored
in the first 55 entries of r&(). The first of these values is subtracted
from the second, and if the result is less than zero, 1000000000 is added
to bring the result somewhere into the range 0 through 999999999. This
result replaces the number at the first location accessed. Finally, the
two index numbers are decremented by 1, adjusted if necessary so that they
remain in the range 1 through 55, and stored back in r&(98) and r&(99) for
the next call to this routine.
This table subtraction algorithm results in a good-quality random long
integer, but an additional technique is used within Rand& to generate a
significantly more random sequence of numbers. The generated number is
used to point to one of the 42 entries in the locations r&(56) through
r&(97). The previously generated number stored at that location is
extracted, saved in r&(100), and replaced with the number just generated.
Finally, the value saved in r&(100) is returned as the result. This
randomly shuffles the order of the output values and effectively
obliterates any subtle patterns that the sequence might have.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Rand& **
' ** Type: Function **
' ** Module: RANDOMS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a pseudorandom long integer in the range
' 0 through 999999999.
'
' EXAMPLE OF USE: n& = Rand&
' PARAMETERS: (none)
' VARIABLES: i% First index into random number table
' j% Second index into random number table
' t& Working variable
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Rand& ()
' DIM SHARED r&(1 TO 100)
'
FUNCTION Rand& STATIC
' Get the pointers into the table
i% = r&(98)
j% = r&(99)
' Subtract the two table values
t& = r&(i%) - r&(j%)
' Adjust result if less than zero
IF t& < 0 THEN
t& = t& + 1000000000
END IF
' Replace table entry with new random number
r&(i%) = t&
' Decrement first index, keeping in range 1 through 55
IF i% > 1 THEN
r&(98) = i% - 1
ELSE
r&(98) = 55
END IF
' Decrement second index, keeping in range 1 through 55
IF j% > 1 THEN
r&(99) = j% - 1
ELSE
r&(99) = 55
END IF
' Use last random number to index into shuffle table
i% = r&(100) MOD 42 + 56
' Grab random from table as current random number
r&(100) = r&(i%)
' Put new calculated random into table
r&(i%) = t&
' Return the random number grabbed from the table
Rand& = r&(100)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: RandExponential!
Returns a pseudorandom real value with an exponential distribution, which
is defined by the passed value of the mean.
Be sure to call the RandShuffle subprogram before using this function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: RandExponential! **
' ** Type: Function **
' ** Module: RANDOMS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns an exponentially distributed pseudorandom,
' single-precision number given the mean of the
' distribution.
'
' EXAMPLE OF USE: x! = RandExponential!(mean!)
' PARAMETERS: mean! The mean of the exponential distribution
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION RandExponential! (mean!)
'
FUNCTION RandExponential! (mean!) STATIC
RandExponential! = -mean! * LOG(RandFrac!)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: RandFrac!
Returns a pseudorandom real value in the range 0 through 1. This function
is similar to the QuickBASIC function RND, but has a much longer sequence
and a more random distribution.
Be sure to call the RandShuffle subprogram before using this function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: RandFrac! **
' ** Type: Function **
' ** Module: RANDOMS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a pseudorandom, single-precision number
' in the range 0 through 1.
'
' EXAMPLE OF USE: x! = RandFrac!
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION RandFrac! ()
'
FUNCTION RandFrac! STATIC
RandFrac! = Rand& / 1E+09
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: RandInteger%
Returns a pseudorandom integer in the range a% through b%, inclusive. For
example, RandInteger%(0, 9) returns a random digit from 0 through 9.
The passed value of a% must be less than b%; if it is not, this function
generates incorrect random numbers. These parameters must be in the legal
range of 16-bit signed integers, not less than -32768 nor greater than
32767.
Be sure to call the RandShuffle subprogram before using this function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: RandInteger% **
' ** Type: Function **
' ** Module: RANDOMS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a pseudorandom integer in the range
' a% to b% inclusive.
'
' EXAMPLE OF USE: n% = RandInteger%(a%, b%)
' PARAMETERS: a% Minimum value for returned integer
' b% Maximum value for returned integer
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION RandInteger% (a%, b%)
'
FUNCTION RandInteger% (a%, b%) STATIC
RandInteger% = a% + (Rand& MOD (b% - a% + 1))
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: RandNormal!
Returns pseudorandom real values with a normal distribution, which is
defined by the passed mean and standard deviation.
Be sure to call the RandShuffle subprogram before using this function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: RandNormal! **
' ** Type: Function **
' ** Module: RANDOMS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a normally distributed single-precision,
' pseudorandom number given the mean and standard deviation.
'
' EXAMPLE OF USE: x! = RandNormal!(mean!, stddev!)
' PARAMETERS: mean! Mean of the distribution of returned
' values
' stddev! Standard deviation of the distribution
' VARIABLES: u1! Pseudorandom positive real value
' less than 1
' u2! Pseudorandom positive real value
' less than 1
' x! Working value
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION RandNormal! (mean!, stddev!)
'
FUNCTION RandNormal! (mean!, stddev!) STATIC
u1! = RandFrac!
u2! = RandFrac!
x! = SQR(-2! * LOG(u1!)) * COS(6.283185 * u2)
RandNormal! = mean! + stddev! * x!
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: RandReal!
Returns a pseudorandom real value in the range x! through y! For example,
RandReal!(-10!, 10!) returns a floating-point, single-precision value in
the range -10 through +10.
Be sure to call the RandShuffle subprogram before using this function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: RandReal! **
' ** Type: Function **
' ** Module: RANDOMS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a pseudorandom, single-precision real
' number in the range x! to y!.
' EXAMPLE OF USE: z! = RandReal!(x!, y!)
' PARAMETERS: x! Minimum for returned value
' y! Maximum for returned value
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION RandReal! (x!, y!)
'
FUNCTION RandReal! (x!, y!) STATIC
RandReal! = x! + (y! - x!) * (Rand& / 1E+09)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: RandShuffle
Initializes the sequence of random numbers that the Rand& function
returns. The r&() array contains all the values necessary for the Rand&
function. This subprogram initializes all values in r&() based on the
characters passed in key$. Refer to the Rand& function for a description
of the contents of the shared array r&().
The passed string key$ is first modified to a length of 97 characters.
Notice that an arbitrary string (in this subprogram, Abra Ca Da Bra) is
concatenated to the front end of key$. Any string can be used, but at
least one character must have an odd byte number. This guarantees that at
least one initial table entry will be odd, a necessity of this
random-number-generation algorithm.
Each character of the new key string (k$) is used to generate a
pseudorandom long integer to be entered in the first 97 entries of r&().
To "warm up" the sequence, 997 iterations of the Rand& algorithm, slightly
modified, are performed on the table.
Finally, starting values for the index values necessary for the Rand&
function are stored in r&(98) and r&(99), and an initial value for the
last generated number is stored in r&(100).
All the other random number generators in this toolbox call the Rand&
function, which generates an error and quits if RandShuffle isn't run
first to initialize r&(). Therefore, you must be sure to call RandShuffle
once during a program run before calling any of these functions.
To generate the same sequence every time the program is run, pass the same
key$ each time. To generate a unique sequence each time, pass a unique
string. For example, to generate a unique sequence for every clock tick of
your computer's existence, you could enter RandShuffle(DATE$ + TIME$ +
STR$(TIMER)).
The key$ can be any reasonable length, but only the first 83 characters
are used to seed the generator. Because there are 256 possible characters
for each of the 83, there are 256^83 possible unique sequences. It's safe
to say you'll never run out!
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: RandShuffle **
' ** Type: Subprogram **
' ** Module: RANDOMS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Creates original table of pseudorandom long integers
' for use by the function Rand&. The contents of key$
' are used to seed the table.
'
' EXAMPLE OF USE: RandShuffle(key$)
' PARAMETERS: key$ String used to seed the generator
' r&(1 TO 100) (shared) Array of long integers for
' generating pseudorandom numbers
' VARIABLES: k$ Modified key string
' i% Index into k$, index into table
' j% Index into table
' k% Loop count for warming up generator
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB RandShuffle (key$)
'
SUB RandShuffle (key$) STATIC
' Form 97-character string, with key$ as part of it
k$ = LEFT$("Abra Ca Da Bra" + key$ + SPACE$(83), 97)
' Use each character to seed table
FOR i% = 1 TO 97
r&(i%) = ASC(MID$(k$, i%, 1)) * 8171717 + i% * 997&
NEXT i%
' Preserve string space
k$ = ""
' Initialize pointers into table
i% = 97
j% = 12
' Randomize the table to get it warmed up
FOR k% = 1 TO 997
' Subtract entries pointed to by i% and j%
r&(i%) = r&(i%) - r&(j%)
' Adjust result if less than zero
IF r&(i%) < 0 THEN
r&(i%) = r&(i%) + 1000000000
END IF
' Decrement first index, keeping in range of 1 through 97
IF i% > 1 THEN
i% = i% - 1
ELSE
i% = 97
END IF
' Decrement second index, keeping in range of 1 through 97
IF j% > 1 THEN
j% = j% - 1
ELSE
j% = 97
END IF
NEXT k%
' Initialize pointers for use by Rand& function
r&(98) = 55
r&(99) = 24
' Initialize pointer for shuffle table lookup by Rand& function
r&(100) = 77
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
STDOUT
The STDOUT toolbox is a collection of subprograms for outputting
characters through the MS-DOS standard output channel rather than through
the QuickBASIC PRINT statement.
QuickBASIC bypasses the ANSI.SYS driver. However, some nice features are
built into this driver, and this toolbox lets you access them from
QuickBASIC. For example, the AssignKey subprogram lets you redefine keys
on the keyboard to any character or string of characters you want.
Be sure you load the ANSI.SYS driver before trying this program. Several
of the escape code sequences create meaningless output if the ANSI.SYS
driver is not resident. In most cases, a statement similar to the
following in your CONFIG.SYS file will load the ANSI.SYS driver at boot-up
time:
DEVICE = \DOS\ANSI.SYS
When you run the STDOUT demo module, pay close attention to the prompts
that appear. In one case you are prompted to press the "a" and "b" keys,
immediately before the program exits to MS-DOS via the SHELL statement. Be
sure you press "a" and then "b" to prevent the program from getting lost.
Name Type Description
──────────────────────────────────────────────────────────────────────────
STDOUT.BAS Demo module
AssignKey Sub Reassigns a string to a key
Attribute Sub Sets screen color (ANSI driver
definition)
ClearLine Sub Clears current line from cursor to end of
line
ClearScreen Sub Clears screen
CrLf Sub Sends carriage return and line feed
CursorDown Sub Moves cursor down specified number of
lines
CursorHome Sub Moves cursor to upper left corner of
screen
CursorLeft Sub Moves cursor left specified number of
spaces
CursorPosition Sub Moves cursor to specified row and column
CursorRight Sub Moves cursor right specified number of
spaces
CursorUp Sub Moves cursor up specified number of lines
StdOut Sub Sends a string to standard output channel
──────────────────────────────────────────────────────────────────────────
Demo Module: STDOUT
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: STDOUT **
' ** Type: Toolbox **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' REQUIREMENTS: MIXED.QLB/.LIB
' ANSI.SYS
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: t0 Timer variable
' bell$ ASCII character 7 (bell)
' Attribute definitions
CONST NORMAL = 0
CONST BRIGHT = 1
CONST UNDERSCORE = 4
CONST BLINK = 5
CONST REVERSE = 7
CONST INVISIBLE = 8
CONST BLACKFOREGROUND = 30
CONST REDFOREGROUND = 31
CONST GREENFOREGROUND = 32
CONST YELLOWFOREGROUND = 33
CONST BLUEFOREGROUND = 34
CONST MAGENTAFOREGROUND = 35
CONST CYANFOREGROUND = 36
CONST WHITEFOREGROUND = 37
CONST BLACKBACKGROUND = 40
CONST REDBACKGROUND = 41
CONST GREENBACKGROUND = 42
CONST YELLOWBACKGROUND = 43
CONST BLUEBACKGROUND = 44
CONST MAGENTABACKGROUND = 45
CONST CYANBACKGROUND = 46
CONST WHITEBACKGROUND = 47
TYPE RegTypeX
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
' Subprograms
DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
DECLARE SUB ClearLine ()
DECLARE SUB ClearScreen ()
DECLARE SUB StdOut (a$)
DECLARE SUB CrLf ()
DECLARE SUB CursorPosition (row%, col%)
DECLARE SUB CursorDown (n%)
DECLARE SUB CursorLeft (n%)
DECLARE SUB CursorRight (n%)
DECLARE SUB CursorUp (n%)
DECLARE SUB AssignKey (keyCode%, assign$)
DECLARE SUB Attribute (attr%)
' Demonstrate the ClearLine and ClearScreen routines
CLS
PRINT "This will be erased quickly, in two steps..."
t0 = TIMER
DO
LOOP UNTIL TIMER - t0 > 2
LOCATE 1, 27
ClearLine
t0 = TIMER
DO
LOOP UNTIL TIMER - t0 > 2
LOCATE 15, 1
ClearScreen
' Demonstrate the StdOut routine
bell$ = CHR$(7)
StdOut "Sending a 'Bell' to StdOut" + bell$
CrLf
' Set cursor position
CursorPosition 3, 20
StdOut "* CursorPosition 3, 20"
CrLf
' Move the cursor around the screen
StdOut "Cursor movements..."
CrLf
CursorDown 1
StdOut "Down 1"
CursorRight 12
StdOut "Right 12"
CursorDown 2
StdOut "Down 2"
CursorLeft 99
StdOut "Left 99"
CrLf
' Character attributes
CrLf
Attribute YELLOWFOREGROUND
Attribute BRIGHT
Attribute BLUEBACKGROUND
StdOut "Bright yellow on blue"
CrLf
Attribute NORMAL
StdOut "Back to normal attributes"
CrLf
'
' Key reassignment
AssignKey 97, "REM The 'a' and 'b' keys have been redefined" + CHR$(13)
AssignKey 98, "EXIT" + CHR$(13)
CursorDown 1
Attribute BRIGHT
Attribute YELLOWFOREGROUND
StdOut "NOTE:"
CrLf
StdOut "Press the 'a' key and then the 'b' key ... "
CrLf
StdOut "The program will then continue ........ "
Attribute NORMAL
CrLf
SHELL
AssignKey 97, ""
AssignKey 98, ""
──────────────────────────────────────────────────────────────────────────
Subprogram: AssignKey
Assigns a string to any key on the keyboard. The first parameter is the
key code number returned by the ASC(INKEY$) statement for a given key
press. The second parameter is a string of characters assigned to the
indicated key. The string can be a maximum of 63 characters in length. If
the string is null, the original key definition is returned to the key.
One complication arises if the key normally returns an extended key code.
Recall that such keys return CHR$(0), followed by a second character that
identifies the key. The AssignKey subprogram recognizes negative key
numbers as extended key codes. Pass the negative of the second byte of an
extended key code to indicate the key.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: AssignKey **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Assigns a string to any key using ANSI.SYS driver.
'
' EXAMPLE OF USE: AssignKey keyCode%, assign$
' PARAMETERS: keyCode% ASCII number for key to be reassigned
' assign$ String to assign to key
' VARIABLES: k$ Command string for ANSI.SYS driver
' i% Index to each character of assign$
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB AssignKey (keyCode%, assign$)
'
SUB AssignKey (keyCode%, assign$) STATIC
IF keyCode% <= 0 THEN
k$ = "[0;"
ELSE
k$ = "["
END IF
k$ = k$ + MID$(STR$(keyCode%), 2)
IF assign$ <> "" THEN
FOR i% = 1 TO LEN(assign$)
k$ = k$ + ";" + MID$(STR$(ASC(MID$(assign$, i%))), 2)
NEXT i%
END IF
StdOut CHR$(27) + k$ + "p"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: Attribute
Sets screen color attributes as defined by the ANSI.SYS driver.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Attribute **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sets the foreground, background, and other color
' attributes.
'
' EXAMPLE OF USE: Attribute attr%
' PARAMETERS: attr% Number for attribute to be set
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB StdOut (a$)
' DECLARE SUB Attribute (attr%)
'
SUB Attribute (attr%) STATIC
StdOut CHR$(27) + "[" + MID$(STR$(attr%), 2) + "m"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ClearLine
Sends to standard output the ANSI.SYS escape-code sequence that erases the
current line from the cursor to the end of the line. The current cursor
position is maintained.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ClearLine **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Clears the display line from the current cursor
' position to the end of the line.
'
' EXAMPLE OF USE: ClearLine
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB ClearLine ()
' DECLARE SUB StdOut (a$)
'
SUB ClearLine STATIC
StdOut CHR$(27) + "[K"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: ClearScreen
Sends to standard output the ANSI.SYS escape-code sequence that clears the
screen; positions the cursor at the top left of the screen.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ClearScreen **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Clears the screen and moves the cursor to the
' home position.
'
' EXAMPLE OF USE: ClearScreen
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB ClearScreen ()
' DECLARE SUB StdOut (a$)
'
SUB ClearScreen STATIC
StdOut CHR$(27) + "[2J"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: CrLf
Sends carriage return and line feed to a standard output.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CrLf **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Sends line feed and carriage return characters
' to standard output.
'
' EXAMPLE OF USE: CrLf
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB StdOut (a$)
' DECLARE SUB CrLf ()
'
SUB CrLf STATIC
StdOut CHR$(13) + CHR$(10)
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: CursorDown
Sends to standard output the ANSI.SYS escape-code sequence that moves the
cursor down the screen n% lines. The cursor stays in the same column and
stops at the bottom line of the screen.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CursorDown **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Moves the cursor n% lines down the screen.
'
' EXAMPLE OF USE: CursorDown n%
' PARAMETERS: n% Number of lines to move the cursor down
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB StdOut (a$)
' DECLARE SUB CursorDown (n%)
'
SUB CursorDown (n%) STATIC
StdOut CHR$(27) + "[" + MID$(STR$(n%), 2) + "B"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: CursorHome
Sends to standard output the ANSI.SYS escape-code sequence that moves the
cursor to the home position; does not erase the display.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CursorHome **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Moves the cursor to the top left of the
' screen.
'
' EXAMPLE OF USE: CursorHome
' PARAMETERS: (none)
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB CursorHome
'
SUB CursorHome STATIC
StdOut CHR$(27) + "[H"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: CursorLeft
Sends to standard output the ANSI.SYS escape-code sequence that moves the
cursor to the left n% columns. The cursor stays in the same row and stops
at the left column of the screen.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CursorLeft **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Moves the cursor n% columns left on the screen.
'
' EXAMPLE OF USE: CursorLeft n%
' PARAMETERS: n% Number of columns to move the cursor left
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB CursorLeft (n%)
'
SUB CursorLeft (n%) STATIC
StdOut CHR$(27) + "[" + MID$(STR$(n%), 2) + "D"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: CursorPosition
Sends to standard output the ANSI.SYS escape-code sequence that moves the
cursor to a given row and column.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CursorPosition **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Moves the cursor to the indicated row and column.
'
' EXAMPLE OF USE: CursorPosition row%, col%
' PARAMETERS: row% Row to move the cursor to
' col% Column to move the cursor to
' VARIABLES: row$ String representation of row%
' col$ String representation of col%
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB CursorPosition (row%, col%)
'
SUB CursorPosition (row%, col%) STATIC
row$ = MID$(STR$(row%), 2)
col$ = MID$(STR$(col%), 2)
StdOut CHR$(27) + "[" + row$ + ";" + col$ + "H"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: CursorRight
Sends to standard output the ANSI.SYS escape-code sequence that moves the
cursor to the right n% columns. The cursor stays in the same row and stops
at the right column of the screen.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CursorRight **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Moves the cursor n% columns right on the screen.
'
' EXAMPLE OF USE: CursorRight n%
' PARAMETERS: n% Number of columns to move the cursor right
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB CursorRight (n%)
'
SUB CursorRight (n%) STATIC
StdOut CHR$(27) + "[" + MID$(STR$(n%), 2) + "C"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: CursorUp
Sends to standard output the ANSI.SYS escape-code sequence that moves the
cursor up the screen n% lines. The cursor stays in the same column and
stops at the top line of the screen.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CursorUp **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Moves the cursor n% lines up the screen.
'
' EXAMPLE OF USE: CursorUp n%
' PARAMETERS: n% Number of lines to move the cursor up
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB CursorUp (n%)
'
SUB CursorUp (n%) STATIC
StdOut CHR$(27) + "[" + MID$(STR$(n%), 2) + "A"
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: StdOut
Sends a string of bytes to the standard output device. The string is
output through the MS-DOS function for string output, bypassing the
QuickBASIC PRINT statement.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: StdOut **
' ** Type: Subprogram **
' ** Module: STDOUT.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Writes string to the MS-DOS standard output.
'
' EXAMPLE OF USE: StdOut a$
' PARAMETERS: a$ String to be output
' VARIABLES: regX Structure of type RegTypeX
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX,
' outreg AS RegTypeX)
' DECLARE SUB StdOut (a$)
'
SUB StdOut (a$) STATIC
DIM regX AS RegTypeX
regX.ax = &H4000
regX.cx = LEN(a$)
regX.bx = 1
regX.ds = VARSEG(a$)
regX.dx = SADD(a$)
InterruptX &H21, regX, regX
IF regX.flags AND 1 THEN
PRINT "Error while calling StdOut:"; regX.ax
SYSTEM
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
STRINGS
The STRINGS toolbox provides several common (and not so common)
string-manipulation functions and subprograms.
╓┌─┌────────────────────────┌───────┌────────────────────────────────────────╖
Name Type Description
──────────────────────────────────────────────────────────────────────────
STRINGS.BAS Demo module
Ascii2Ebcdic$ Func Converts string from ASCII to EBCDIC
BestMatch$ Func Returns best match to input string
BuildAEStrings Sub Builds ASCII and EBCDIC character
translation tables
Center$ Func Centers string by padding with spaces
Detab$ Func Replaces tab characters with spaces
Ebcdic2Ascii$ Func Converts a string from EBCDIC to ASCII
Entab$ Func Replaces spaces with tab characters
FilterIn$ Func Retains only specified characters in
string
FilterOut$ Func Deletes specified characters from string
Lpad$ Func Returns left-justified input string
LtrimSet$ Func Deletes specified characters from left
Ord% Func Returns byte number for ANSI mnemonic
Repeat$ Func Combines multiple copies into one string
Replace$ Func Replaces specified characters in string
Reverse$ Func Reverses order of characters in a string
Name Type Description
──────────────────────────────────────────────────────────────────────────
Reverse$ Func Reverses order of characters in a string
ReverseCase$ Func Reverses case for each character in a
string
Rpad$ Func Returns right-justified input string
RtrimSet$ Func Deletes specified characters from right
Translate$ Func Exchanges characters in string from table
──────────────────────────────────────────────────────────────────────────
Demo Module: STRINGS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: STRINGS **
' ** Type: Toolbox **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
' USAGE: No command line parameters
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: a$ Working string for demonstrations
' b$ Working string for demonstrations
' c$ Working string for demonstrations
' x$ Working string for demonstrations
' y$ Working string for demonstrations
' set$ Set of characters that define word separation
DECLARE FUNCTION Ascii2Ebcdic$ (a$)
DECLARE FUNCTION BestMatch$ (a$, x$, y$)
DECLARE FUNCTION Center$ (a$, n%)
DECLARE FUNCTION Detab$ (a$, tabs%)
DECLARE FUNCTION Ebcdic2Ascii$ (e$)
DECLARE FUNCTION Entab$ (a$, tabs%)
DECLARE FUNCTION FilterIn$ (a$, set$)
DECLARE FUNCTION FilterOut$ (a$, set$)
DECLARE FUNCTION Lpad$ (a$, n%)
DECLARE FUNCTION LtrimSet$ (a$, set$)
DECLARE FUNCTION Ord% (a$)
DECLARE FUNCTION Repeat$ (a$, n%)
DECLARE FUNCTION Replace$ (a$, find$, substitute$)
DECLARE FUNCTION Reverse$ (a$)
DECLARE FUNCTION ReverseCase$ (a$)
DECLARE FUNCTION Rpad$ (a$, n%)
DECLARE FUNCTION RtrimSet$ (a$, set$)
DECLARE FUNCTION Translate$ (a$, f$, t$)
' Subprograms
DECLARE SUB BuildAEStrings ()
' Quick demonstrations
CLS
a$ = "This is a test"
PRINT "a$", , a$
PRINT "ReverseCase$(a$)", ReverseCase$(a$)
PRINT "Reverse$(a$)", , Reverse$(a$)
PRINT "Repeat$(a$, 3)", Repeat$(a$, 3)
PRINT
set$ = "T this"
PRINT "set$", , set$
PRINT "LtrimSet$(a$, set$)", LtrimSet$(a$, set$)
PRINT "RtrimSet$(a$, set$)", RtrimSet$(a$, set$)
PRINT "FilterOut$(a$, set$)", FilterOut$(a$, set$)
PRINT "FilterIn$(a$, set$)", FilterIn$(a$, set$)
PRINT
a$ = "elephant"
x$ = "alpha"
y$ = "omega"
PRINT "a$", , a$
PRINT "x$", , x$
PRINT "y$", , y$
PRINT "BestMatch$(a$, x$, y$)", BestMatch$(a$, x$, y$)
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
CLS
a$ = "BEL"
PRINT "a$", , a$
PRINT "Ord%(a$)", , Ord%(a$)
PRINT
a$ = "This is a test"
find$ = "s"
substitute$ = "<s>"
PRINT "a$", , , a$
PRINT "find$", , , find$
PRINT "substitute$", , , substitute$
PRINT "Replace$(a$, find$, substitute$)", Replace$(a$, find$, substitut
PRINT
PRINT "a$", , a$
PRINT "Lpad$(a$, 40)", , ":"; Lpad$(a$, 40); ":"
PRINT "Rpad$(a$, 40)", , ":"; Rpad$(a$, 40); ":"
PRINT "Center$(a$, 40)", ":"; Center$(a$, 40); ":"
PRINT
a$ = "a$ character" + STRING$(2, 9) + "count" + CHR$(9) + "is"
PRINT a$; LEN(a$)
PRINT "a$ = Detab$(a$, 8)"
a$ = Detab$(a$, 8)
PRINT a$; LEN(a$)
PRINT "a$ = Entab$(a$, 8)"
a$ = Entab$(a$, 8)
PRINT a$; LEN(a$)
PRINT
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
CLS
a$ = "You know this test string has vowels."
x$ = "aeiou"
y$ = "eioua"
PRINT "a$", , a$
PRINT "x$", , x$
PRINT "y$", , y$
PRINT "Translate$(a$, x$, y$)", Translate$(a$, x$, y$)
PRINT
a$ = "This is a test."
b$ = Ascii2Ebcdic$(a$)
c$ = Ebcdic2Ascii$(b$)
PRINT "a$", , a$
PRINT "b$ = Ascii2Ebcdic$(a$)", b$
PRINT "c$ = Ebcdic2Ascii$(b$)", c$
PRINT
END
──────────────────────────────────────────────────────────────────────────
Function: Ascii2Ebcdic$
Converts a string of ASCII characters to EBCDIC equivalents.
Almost all computers use the ASCII character set to define which byte
represents which character. This standard makes it possible for computers,
printers, plotters, and other equipment to communicate effectively.
However, IBM's larger computers have long used the EBCDIC character set,
an alternative way for computers and peripherals to communicate. If files
are to be transferred to or from an IBM mainframe, it's necessary to
translate character bytes between the two methods. This function, along
with its counterpart Ebcdic2Ascii$, translates strings of characters
between the ASCII character set and the EBCDIC character set.
These functions and the BuildAEStrings subprogram share a pair of string
variables, ascii$ and ebcdic$. The SHARED statement lets these two strings
be accessed by each of these three routines while remaining invisible and
unalterable to all other parts of a program.
The BuildAEStrings subprogram is called only once, to build both the
ascii$ and ebcdic$ translation strings the first time that the
Ascii2Ebcdic$ or Ebcdic2Ascii$ function is called. All subsequent calls
to these functions use these strings immediately, as the contents of the
strings are preserved between calls.
Refer to the BuildAEStrings subprogram for more information about how
these two strings are built. Refer to the Translate$ function for more
information about the character-by-character translation.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Ascii2Ebcdic$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a$ with each character translated from ASCII to EBCDIC.
'
' EXAMPLE OF USE: e$ = Ascii2Ebcdic$(a$)
' PARAMETERS: a$ String of ASCII characters to be
' converted
' VARIABLES: ebcdic$ Table of translation characters
' ascii$ Table of translation characters
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Ascii2Ebcdic$ (a$)
'
FUNCTION Ascii2Ebcdic$ (a$) STATIC
SHARED ebcdic$, ascii$
IF ebcdic$ = "" THEN
BuildAEStrings
END IF
Ascii2Ebcdic$ = Translate$(a$, ascii$, ebcdic$)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: BestMatch$
Compares two strings with a third and returns the string that most closely
matches the third.
Everybody's talking "artificial intelligence" these days. Programs capable
of making decisions based on "fuzzy" facts are already being used for
voice analysis, pattern matching, and other similar tasks. This function
provides a way to make an educated guess as to the best pattern match when
comparing two strings.
The method of comparison used here scans substrings of the target string
and checks for occurrences of these substrings in each of the other two
strings. A score is kept for the number of substring matches found for
each string. The score is weighted heavier for longer substring matches.
For example, finding an occurrence of the substring "ABC" is worth 6
points, while finding separate occurrences of "E," "F," and "G" is worth a
total of only 3 points.
When all substrings of the target string have been checked, the points are
compared for each test string. The highest score wins, and that string is
returned as the result.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BestMatch$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns either x$ or y$, whichever is a best match to a$.
'
' EXAMPLE OF USE: b$ = BestMatch$(a$, x$, y$)
' PARAMETERS: a$ The string to be matched
' x$ The first string to compare with a$
' y$ The second string to compare with a$
' VARIABLES: ua$ Uppercase working copy of a$
' ux$ Uppercase working copy of x$
' uy$ Uppercase working copy of y$
' lena% Length of a$
' i% Length of substrings of ua$
' j% Index into ua$
' t$ Substrings of ua$
' xscore% Accumulated score for substring matches
' found in ux$
' yscore% Accumulated score for substring matches
' found in uy$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION BestMatch$ (a$, x$, y$)
'
FUNCTION BestMatch$ (a$, x$, y$) STATIC
ua$ = UCASE$(a$)
ux$ = UCASE$(x$)
uy$ = UCASE$(y$)
lena% = LEN(ua$)
FOR i% = 1 TO lena%
FOR j% = 1 TO lena% - i% + 1
t$ = MID$(ua$, j%, i%)
IF INSTR(ux$, t$) THEN
xscore% = xscore% + i% + i%
END IF
IF INSTR(uy$, t$) THEN
yscore% = yscore% + i% + i%
END IF
NEXT j%
NEXT i%
IF xscore% > yscore% THEN
BestMatch$ = x$
ELSE
BestMatch$ = y$
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: BuildAEStrings
Initializes the ASCII-EBCDIC translation table strings. This subprogram is
called once per program run, by either the Ascii2Ebcdic$ or
Ebcdic2Ascii$ function, when one is called first. Each function checks to
see whether the shared strings, ascii$ and ebcdic$, are filled in or
whether they are still null (empty) strings. If they are null, this
subprogram is called to fill them in before they are used as character
translation tables.
The method used to fill in the strings can easily create strings
containing any binary bytes. First, ebcdic$ is created as a string of
hexadecimal characters, each pair of which represents a single byte. At
this point, ebcdic$ is twice the desired length. The processing loop near
the end of the function converts each pair of hexadecimal characters to
the byte it represents and replaces the hexadecimal characters with these
bytes. After all hexadecimal character pairs are converted, the STRINGS
first half of ebcdic$ contains the desired string of bytes. The second
half of ebcdic$ is deleted.
The string variable ascii$ is filled in with binary byte values 0 through
127. This string is built to be passed to the Translate$ function, which
requires a string table for both lookup as well as replacement.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: BuildAEStrings **
' ** Type: Subprogram **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Called by the Ascii2Ebcdic$ and Ebcdic2Ascii$
' functions to build the translation strings.
' This subprogram is called only once.
'
' EXAMPLE OF USE: Called automatically by either the Ascii2Ebcdic$ or
' Ebcdic2Ascii$ function
' PARAMETERS: ascii$ Shared by Ascii2Ebcdic$, Ebcdic2Ascii$, and
' BuildAEStrings
' ebcdic$ Shared by Ascii2Ebcdic$, Ebcdic2Ascii$, and
' BuildAEStrings
' VARIABLES: i% Index into strings
' byte% Binary value of character byte
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB BuildAEStrings ()
'
SUB BuildAEStrings STATIC
SHARED ebcdic$, ascii$
ascii$ = SPACE$(128)
ebcdic$ = ebcdic$ + "00010203372D2E2F1605250B0C0D0E0F"
ebcdic$ = ebcdic$ + "101112133C3D322618193F271C1D1E1F"
ebcdic$ = ebcdic$ + "404F7F7B5B6C507D4D5D5C4E6B604B61"
ebcdic$ = ebcdic$ + "F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F"
ebcdic$ = ebcdic$ + "7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6"
ebcdic$ = ebcdic$ + "D7D8D9E2E3E4E5E6E7E8E94AE05A5F6D"
ebcdic$ = ebcdic$ + "79818283848586878889919293949596"
ebcdic$ = ebcdic$ + "979899A2A3A4A5A6A7A8A9C06AD0A107"
FOR i% = 0 TO 127
MID$(ascii$, i% + 1, 1) = CHR$(i%)
byte% = VAL("&H" + MID$(ebcdic$, i% + i% + 1, 2))
MID$(ebcdic$, i% + 1, 1) = CHR$(byte%)
NEXT i%
ebcdic$ = LEFT$(ebcdic$, 128)
END SUB
──────────────────────────────────────────────────────────────────────────
Function: Center$
Returns a string of length n% by padding a$ with spaces on both ends.
The original string is centered in the new string. If n% is less than the
length of a$ (after any spaces are stripped from the ends), the string is
returned with no spaces tacked on and with a length greater than n%.
One obvious use for this function is centering titles and labels on a
printed or displayed page.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Center$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Pads a$ with spaces on both ends until text is
' centered and the string length is n%.
'
' EXAMPLE OF USE: b$ = Center$(a$, n%)
' PARAMETERS: a$ String of characters to be padded with spac
' n% Desired length of resulting string
' VARIABLES: pad% Number of spaces to pad at ends of string
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Center$ (a$, n%)
'
FUNCTION Center$ (a$, n%) STATIC
a$ = LTRIM$(RTRIM$(a$))
pad% = n% - LEN(a$)
IF pad% > 0 THEN
Center$ = SPACE$(pad% \ 2) + a$ + SPACE$(pad% - pad% \ 2)
ELSE
Center$ = a$
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Detab$
Replaces tab characters with the appropriate number of spaces.
Tab characters are useful for forcing text alignment into predictable
columns and for conserving space in text files. If you then need to
exchange the tab characters for the equivalent number of spaces, this
function lets you do so.
Your computer display and (probably) your printer use a tab spacing
constant of 8. For this reason, the most common value passed to this
function for tabs% is 8. Spaces are inserted in a$ in place of tab
characters to align the following characters into columns that are
multiples of 8. Displaying or printing the string before and after it's
processed by this function should result in exactly the same output.
Also see Entab$, which performs exactly the opposite function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name Detab$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Replaces all tab characters with spaces, using
' tabs% to determine proper alignment.
'
' EXAMPLE OF USE: b$ = Detab$(a$, tabs%)
' PARAMETERS: a$ String with possible tab characters
' tabs% Tab spacing
' VARIABLES: t$ Working copy of a$
' tb$ Tab character
' tp% Pointer to position in t$ of a tab charac
' sp$ Spaces to replace a given tab character
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Detab$ (a$, tabs%)
'
FUNCTION Detab$ (a$, tabs%) STATIC
t$ = a$
tb$ = CHR$(9)
DO
tp% = INSTR(t$, tb$)
IF tp% THEN
Sp$ = SPACE$(tabs% - ((tp% - 1) MOD tabs%))
t$ = LEFT$(t$, tp% - 1) + Sp$ + MID$(t$, tp% + 1)
END IF
LOOP UNTIL tp% = 0
Detab$ = t$
t$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Ebcdic2Ascii$
Converts a string of EBCDIC characters to ASCII equivalents. This function
performs the exact opposite of the Ascii2Ebcdic$ function.
Refer to the Ascii2Ebcdic$ function for more information.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Ebcdic2Ascii$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a$ with each character translated from
' EBCDIC to ASCII.
'
' EXAMPLE OF USE: b$ = Ascii2Ebcdic$(a$)
' PARAMETERS: a$ String of EBCDIC characters to be converte
' VARIABLES: ebcdic$ Table of translation characters
' ascii$ Table of translation characters
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Ebcdic2Ascii$ (e$)
'
FUNCTION Ebcdic2Ascii$ (e$) STATIC
SHARED ebcdic$, ascii$
IF ebcdic$ = "" THEN
BuildAEStrings
END IF
Ebcdic2Ascii$ = Translate$(e$, ebcdic$, ascii$)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Entab$
Replaces spaces with tab characters wherever possible, providing a way to
compress the size of a text file.
For the opposite function, replacing tabs with appropriate numbers of
spaces, see Detab$.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Entab$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Replaces groups of spaces, where possible, with
' tab characters, keeping the alignment indicated
' by the value of tabs%.
'
' EXAMPLE OF USE: b$ = Entab$(a$, tabs%)
' PARAMETERS: a$ String with possible tab characters
' tabs% Tab spacing
' VARIABLES: t$ Working copy of a$
' tb$ Tab character
' i% Index into t$
' k% Count of spaces being replaced
' j% Index into t$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Entab$ (a$, tabs%)
'
FUNCTION Entab$ (a$, tabs%) STATIC
t$ = a$
tb$ = CHR$(9)
FOR i% = (LEN(t$) \ tabs%) * tabs% + 1 TO tabs% STEP -tabs%
IF MID$(t$, i% - 1, 1) = " " THEN
k% = 0
FOR j% = 1 TO tabs%
IF MID$(t$, i% - j%, 1) <> " " THEN
k% = i% - j%
EXIT FOR
END IF
NEXT j%
IF k% = 0 THEN
k% = i% - tabs% - 1
END IF
t$ = LEFT$(t$, k%) + tb$ + MID$(t$, i%)
END IF
NEXT i%
Entab$ = t$
t$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: FilterIn$
Filters a string, character by character, and removes any characters that
are not in the designated set. FilterIn$("EXAMPLE", "AEIOU"), for example,
returns the string EAE, because all characters, except uppercase vowels,
are removed from EXAMPLE.
To filter a string by removing characters listed in set$ (as opposed to
removing all characters not in set$), see the FilterOut$ function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FilterIn$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a$ with all occurrences of any characters
' that are not in set$ deleted.
'
' EXAMPLE OF USE: b$ = FilterIn$(a$, set$)
' PARAMETERS: a$ String to be processed
' set$ Set of characters to be retained
' VARIABLES: i% Index into a$
' j% Count of characters retained
' lena% Length of a$
' t$ Working string space
' c$ Each character of a$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION FilterIn$ (a$, set$)
'
FUNCTION FilterIn$ (a$, set$) STATIC
i% = 1
j% = 0
lena% = LEN(a$)
t$ = a$
DO UNTIL i% > lena%
c$ = MID$(a$, i%, 1)
IF INSTR(set$, c$) THEN
j% = j% + 1
MID$(t$, j%, 1) = c$
END IF
i% = i% + 1
LOOP
FilterIn$ = LEFT$(t$, j%)
t$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: FilterOut$
Filters a string, character by character, and removes any characters that
are listed in the designated set. FilterOut$("EXAMPLE", "AEIOU"),
STRINGSfor example, returns the string XMPL, because all uppercase vowels
are removed from EXAMPLE.
To filter a string by removing characters not listed in set$ (as opposed
to removing all characters found in set$), see the FilterIn$ function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: FilterOut$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a$ with all occurrences of any characters
' from set$ deleted.
'
' EXAMPLE OF USE: b$ = FilterOut$(a$, set$)
' PARAMETERS: a$ String to be processed
' set$ Set of characters to be retained
' VARIABLES: i% Index into a$
' j% Count of characters retained
' lena% Length of a$
' t$ Working string space
' c$ Each character of a$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION FilterOut$ (a$, set$)
'
FUNCTION FilterOut$ (a$, set$) STATIC
i% = 1
j% = 0
lena% = LEN(a$)
t$ = a$
DO UNTIL i% > lena%
c$ = MID$(a$, i%, 1)
IF INSTR(set$, c$) = 0 THEN
j% = j% + 1
MID$(t$, j%, 1) = c$
END IF
i% = i% + 1
LOOP
FilterOut$ = LEFT$(t$, j%)
t$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Lpad$
Returns a left-justified string of n% characters by shifting a$ to the
left and adding space characters on the right.
This function actually does an amazing amount of work for only one program
line. First, the string passed as parameter a$ has all spaces removed from
its left, the final goal being to left justify the string.
The desired string length is n%. To guarantee that you have at least n%
characters to work with, n% space characters are added to the right of the
string. Most likely, the string is now longer than desired. So, the LEFT$
function returns the first n% characters from the string, finishing the
desired processing of a$ and assigning the result to Lpad$, the name of
the function.
See Rpad$ for a similar function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Lpad$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a string of length n%, with a$ left justified
' and padded on the right with spaces.
'
' EXAMPLE OF USE: b$ = Lpad$(a$, n%)
' PARAMETERS: a$ String to be left justified and padded
' n% Length of string result
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Lpad$ (a$, n%)
'
FUNCTION Lpad$ (a$, n%) STATIC
Lpad$ = LEFT$(LTRIM$(a$) + SPACE$(n%), n%)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: LtrimSet$
Trims characters in set$ from the left of a$ until a character is found
that is not in set$.
STRINGS
The QuickBASIC LTRIM$() function removes space characters from the end of
a string. This function goes a step further and lets you remove any of
several characters from the left of a string. For example,
LtrimSet$("EXAMPLE", "AXE") returns MPLE.
One use for this function is to remove tabs and spaces from the left of a
string.
See RtrimSet$ for a similar function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: LtrimSet$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Trims occurrences of any characters in set$
' from the left of a$.
'
' EXAMPLE OF USE: b$ = LtrimSet$(a$, set$)
' PARAMETERS: a$ String to be trimmed
' set$ Set of characters to be trimmed
' VARIABLES: i% Index into a$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION LtrimSet$ (a$, set$)
'
FUNCTION LtrimSet$ (a$, set$) STATIC
IF a$ <> "" THEN
FOR i% = 1 TO LEN(a$)
IF INSTR(set$, MID$(a$, i%, 1)) = 0 THEN
LtrimSet$ = MID$(a$, i%)
EXIT FUNCTION
END IF
NEXT i%
END IF
LtrimSet$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Ord%
Returns the byte number defined by ANSI standard mnemonics.
This function interprets ANSI standard mnemonics for control characters
and returns the numeric value of the byte the mnemonics represent (the
ordinal of the mnemonic). Ord%("BEL"), for example, returns 7, the byte
number for the bell character. (Recall that PRINT CHR$(7) causes your
computer to beep.)
Other common control-character mnemonics include CR (carriage return), LF
(line feed), FF (form feed), and NUL (the zero byte value). Many others
are available, however, including mnemonics for the lowercase alphabetic
characters.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Ord% **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Similar to ASC() function; returns
' numeric byte values for the ANSI standard
' mnemonics for control characters.
'
' EXAMPLE OF USE: byte% = Ord%(a$)
' PARAMETERS: a$ ANSI standard character mnemonic string
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Ord% (a$)
'
FUNCTION Ord% (a$) STATIC
SELECT CASE UCASE$(a$)
CASE "NUL" 'Null
Ord% = 0
CASE "SOH" 'Start of heading
Ord% = 1
CASE "STX" 'Start of text
Ord% = 2
CASE "ETX" 'End of text
Ord% = 3
CASE "EOT" 'End of transmission
Ord% = 4
CASE "ENQ" 'Enquiry
Ord% = 5
CASE "ACK" 'Acknowledge
Ord% = 6
CASE "BEL" 'Bell
Ord% = 7
CASE "BS" 'Backspace
Ord% = 8
CASE "HT" 'Horizontal tab
Ord% = 9
CASE "LF" 'Line feed
Ord% = 10
CASE "VT" 'Vertical tab
Ord% = 11
CASE "FF" 'Form feed
Ord% = 12
CASE "CR" 'Carriage return
Ord% = 13
CASE "SO" 'Shift out
Ord% = 14
CASE "SI" 'Shift in
Ord% = 15
CASE "DLE" 'Data link escape
Ord% = 16
CASE "DC1" 'Device control 1
Ord% = 17
CASE "DC2" 'Device control 2
Ord% = 18
CASE "DC3" 'Device control 3
Ord% = 19
CASE "DC4" 'Device control 4
Ord% = 20
CASE "NAK" 'Negative acknowledge
Ord% = 21
CASE "SYN" 'Synchronous idle
Ord% = 22
CASE "ETB" 'End of transmission block
Ord% = 23
CASE "CAN" 'Cancel
Ord% = 24
CASE "EM" 'End of medium
Ord% = 25
CASE "SUB" 'Substitute
Ord% = 26
CASE "ESC" 'Escape
Ord% = 27
CASE "FS" 'File separator
Ord% = 28
CASE "GS" 'Group separator
Ord% = 29
CASE "RS" 'Record separator
Ord% = 30
CASE "US" 'Unit separator
Ord% = 31
CASE "SP" 'Space
Ord% = 32
CASE "UND" 'Underline
Ord% = 95
CASE "GRA" 'Grave accent
Ord% = 96
CASE "LCA" 'Lowercase a
Ord% = 97
CASE "LCB" 'Lowercase b
Ord% = 98
CASE "LCC" 'Lowercase c
Ord% = 99
CASE "LCD" 'Lowercase d
Ord% = 100
CASE "LCE" 'Lowercase e
Ord% = 101
CASE "LCF" 'Lowercase f
Ord% = 102
CASE "LCG" 'Lowercase g
Ord% = 103
CASE "LCH" 'Lowercase h
Ord% = 104
CASE "LCI" 'Lowercase i
Ord% = 105
CASE "LCJ" 'Lowercase j
Ord% = 106
CASE "LCK" 'Lowercase k
Ord% = 107
CASE "LCL" 'Lowercase l
Ord% = 108
CASE "LCM" 'Lowercase m
Ord% = 109
CASE "LCN" 'Lowercase n
Ord% = 110
CASE "LCO" 'Lowercase o
Ord% = 111
CASE "LCP" 'Lowercase p
Ord% = 112
CASE "LCQ" 'Lowercase q
Ord% = 113
CASE "LCR" 'Lowercase r
Ord% = 114
CASE "LCS" 'Lowercase s
Ord% = 115
CASE "LCT" 'Lowercase t
Ord% = 116
CASE "LCU" 'Lowercase u
Ord% = 117
CASE "LCV" 'Lowercase v
Ord% = 118
CASE "LCW" 'Lowercase w
Ord% = 119
CASE "LCX" 'Lowercase x
Ord% = 120
CASE "LCY" 'Lowercase y
Ord% = 121
CASE "LCZ" 'Lowercase z
Ord% = 122
CASE "LBR" 'Left brace
Ord% = 123
CASE "VLN" 'Vertical line
Ord% = 124
CASE "RBR" 'Right brace
Ord% = 125
CASE "TIL" 'Tilde
Ord% = 126
CASE "DEL" 'Delete
Ord% = 127
CASE ELSE 'Not ANSI Standard ORD mnemonic
Ord% = -1
END SELECT
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Repeat$
Returns the string result of concatenating n% copies of a$. If the length
of the result is less than 0 or greater than 32767, an error message is
displayed, and the program terminates.
To create a string of 80 spaces, you can use the QuickBASIC function
SPACE$(80). To create a string of 80 equal signs, you can use STRING$(80,
"="). But how can you create an 80-character string made up of 40
repetitions of "+-"? The Repeat$ function lets you do so. Repeat$("+-",
40) would do the trick.
At first glance, this function looks like more code than is needed.
Consider the short function on the following page that returns the same
result.
FUNCTION SlowRepeat$ (a$, n%) STATIC
x$ = ""
FOR i% = 1 to n%
x$ = x$ + a$
NEXT i%
SlowRepeat$ = x$
END FUNCTION
In tests of operating speed, this shorter function often ran about 10
times slower than did the Repeat$ function! The difference is in how the
string space is handled.
SlowRepeat$ performs much string-manipulation overhead for each n%
repetition of a$. In particular, the statement x$ = x$ + a$ creates
working copies of x$ and a$ in the string workspace for each iteration. As
x$ becomes larger, this shuffling of strings begins to bog down the
function, even though QuickBASIC performs these functions efficiently.
The Repeat$ function avoids much of this string-manipulation overhead by
assigning string results to the MID$ of a large string (t$) that was
created only once. First, t$ is created as a string of spaces long enough
to hold all n% copies of a$. Each copy of a$ is then assigned to the
appropriate substring location in t$ by use of the MID$ statement.
This technique can often be used to speed up other string manipulations.
The difference in speed is often insignificant, except in cases where a
large number of string operations are performed.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Repeat$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a string formed by concatenating n%
' copies of a$ together.
'
' EXAMPLE OF USE: b$ = Repeat$(a$, n%)
' PARAMETERS: a$ String to be repeated
' n% Number of copies of a$ to concatenate
' VARIABLES: lena% Length of a$
' lent& Length of result
' t$ Work space for building result
' ndx% Index into t$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Repeat$ (a$, n%)
'
FUNCTION Repeat$ (a$, n%) STATIC
lena% = LEN(a$)
lent& = n% * lena%
IF lent& < 0 OR lent& > 32767 THEN
PRINT "ERROR: Repeat$ - Negative repetition, or result too long
SYSTEM
ELSEIF lent& = 0 THEN
Repeat$ = ""
ELSE
t$ = SPACE$(lent&)
ndx% = 1
DO
MID$(t$, ndx%, lena%) = a$
ndx% = ndx% + lena%
LOOP UNTIL ndx% > lent&
Repeat$ = t$
t$ = ""
END IF
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Replace$
Replaces all occurrences of find$ in a$ with substitute$.
One common function provided by text editors and word processors is the
ability to globally replace occurrences of character strings with other
character strings. This function performs such a global replacement in a
single string. By using this function repeatedly, you can globally edit
entire files of strings.
For example, Replace$ ("This is a test", "i", "ii") returns the string
Thiis iis a test.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Replace$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Replaces all occurrences of find$ in a$ with substitute$.
'
' EXAMPLE OF USE: b$ = Replace$(a$, find$, substitute$)
' PARAMETERS: a$ String to make substring replacements in
' find$ Substring to be searched for
' substitutes$ String for replacing the found
' substrings
' VARIABLES: t$ Working copy of a$
' lenf% Length of find$
' lens% Length of substitute$
' i% Index into a$, pointing at substrings
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Replace$ (a$, find$, substitute$)
'
FUNCTION Replace$ (a$, find$, substitute$) STATIC
t$ = a$
lenf% = LEN(find$)
lens% = LEN(substitute$)
i% = 1
DO
i% = INSTR(i%, t$, find$)
IF i% = 0 THEN
EXIT DO
END IF
t$ = LEFT$(t$, i% - 1) + substitute$ + MID$(t$, i% + lenf%)
i% = i% + lens%
LOOP
Replace$ = t$
t$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Reverse$
Quickly reverses the order of all characters in a string. For example,
Reverse$("QuickBASIC") returns CISABkciuQ.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Reverse$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Reverses the order of all characters in a$.
'
' EXAMPLE OF USE: b$ = Reverse$(a$)
' PARAMETERS: a$ String to be processed
' VARIABLES: n% Length of the string
' r$ Working string space
' i% Index into the string
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Reverse$ (a$)
'
FUNCTION Reverse$ (a$) STATIC
n% = LEN(a$)
r$ = a$
FOR i% = 1 TO n%
MID$(r$, i%, 1) = MID$(a$, n% - i% + 1, 1)
NEXT i%
Reverse$ = r$
r$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: ReverseCase$
Changes the case of all alphabetical characters in a passed string.
Nonalphabetic characters are left undisturbed.
Some text editors can change the case of characters from the current
cursor location to the end of the line. This function was designed with
that concept in mind. ReverseCase$("Testing 1,2,3"), for example, returns
tESTING 1,2,3.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: ReverseCase$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Changes all lowercase characters to uppercase
' and all uppercase characters to lowercase.
'
' EXAMPLE OF USE: b$ = ReverseCase$(a$)
' PARAMETERS: a$ String to be processed
' VARIABLES: r$ Working copy of a$
' i% Index into r$
' t$ Character from middle of a$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION ReverseCase$ (a$)
'
FUNCTION ReverseCase$ (a$) STATIC
r$ = a$
FOR i% = 1 TO LEN(a$)
t$ = MID$(a$, i%, 1)
IF LCASE$(t$) <> t$ THEN
MID$(r$, i%, 1) = LCASE$(t$)
ELSE
MID$(r$, i%, 1) = UCASE$(t$)
END IF
NEXT i%
ReverseCase$ = r$
r$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Rpad$
Returns a right-justified string of n% characters by shifting a$ to the
right as far as possible and adding space characters on the left.
See Lpad$ for a similar function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Rpad$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns string of length n%, with a$ right justified
' and padded on the left with spaces.
'
' EXAMPLE OF USE: b$ = Rpad$(a$, n%)
' PARAMETERS: a$ String to be right justified and padded
' n% Length of string result
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Rpad$ (a$, n%)
'
FUNCTION Rpad$ (a$, n%) STATIC
Rpad$ = RIGHT$(SPACE$(n%) + RTRIM$(a$), n%)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: RtrimSet$
Trims characters in set$ from the right of a$ until a character is found
that is not in set$.
The QuickBASIC RTRIM$() function removes space characters from the right
of a string. This function goes a step further and lets you remove any of
several characters from the right of a string. For example,
RtrimSet$("EXAMPLE", "LEAVE") returns EXAMP.
One use for this function is to remove tabs and spaces from the right of a
string.
See LtrimSet$ for a similar function.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: RtrimSet$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Trims occurrences of any characters in set$
' from the right of a$.
'
' EXAMPLE OF USE: b$ = LtrimSet$(a$, set$)
' PARAMETERS: a$ String to be trimmed
' set$ Set of characters to be trimmed
' VARIABLES: i% Index into a$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION RtrimSet$ (a$, set$)
'
FUNCTION RtrimSet$ (a$, set$) STATIC
IF a$ <> "" THEN
FOR i% = LEN(a$) TO 1 STEP -1
IF INSTR(set$, MID$(a$, i%, 1)) = 0 THEN
RtrimSet$ = LEFT$(a$, i%)
EXIT FUNCTION
END IF
NEXT i%
END IF
RtrimSet$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Translate$
Performs a table-lookup translation of the characters in a$. Each
character in a$ is searched for in f$. If found, the character is replaced
by the character located in the same position in t$. Take a look at a
simple example to help clarify the explanation.
Translate$("EXAMPLE", "ABCDE", "vwxyz") returns zXvMPLz. The first
character of "EXAMPLE" is found in the fifth character position of
"ABCDE," so it is replaced with the fifth character of "vwxyz." (The "E"
is replaced with a "z.") Then each remaining character in "EXAMPLE" is
searched for and replaced in the same way.
The Ascii2Ebcdic$ and Ebcdic2Ascii$ functions call this function to
translate characters from one standard set to the other.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Translate$ **
' ** Type: Function **
' ** Module: STRINGS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns a$ with each character translated from
' f$ to t$. If a character from a$ is found in f$,
' it is replaced with the character located
' in the same position in t$.
'
' EXAMPLE OF USE: b$ = Translate$ (a$, f$, t$)
' PARAMETERS: a$ String to be translated
' f$ Table of lookup characters
' t$ Table of replacement characters
' VARIABLES: ta$ Working copy of a$
' lena% Length of a$
' lenf% Length of f$
' lent% Length of t$
' i% Index into ta$
' ptr% Pointer into f$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Translate$ (a$, f$, t$)
'
FUNCTION Translate$ (a$, f$, t$) STATIC
ta$ = a$
lena% = LEN(ta$)
lenf% = LEN(f$)
lent% = LEN(t$)
IF lena% > 0 AND lenf% > 0 AND lent% > 0 THEN
FOR i% = 1 TO lena%
ptr% = INSTR(f$, MID$(ta$, i%, 1))
IF ptr% THEN
MID$(ta$, i%, 1) = MID$(t$, ptr%, 1)
END IF
NEXT i%
END IF
Translate$ = ta$
ta$ = ""
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
TRIANGLE
The TRIANGLE toolbox is a collection of analytical geometry functions and
subprograms for calculating parts of triangles.
The demonstration module-level code of this toolbox provides a useful
triangle calculator utility.
Run the program, and enter the known sides and/or angles of a triangle
when prompted. If it's possible to calculate the remaining sides and
angles, the program does so and then displays the results.
Name Type Description
──────────────────────────────────────────────────────────────────────────
TRIANGLE.BAS Demo module
Deg2Rad# Func Converts degree angular units to
radians
Rad2Deg# Func Converts radian angular units to
degrees
Triangle Sub Calculates sides and angles of
triangle
TriangleArea# Func Calculates area of triangle from 3
sides
──────────────────────────────────────────────────────────────────────────
Demo Module: TRIANGLE
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: TRIANGLE **
' ** Type: Toolbox **
' ** Module: TRIANGLE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' REQUIREMENTS: CGA
' .MAK FILE: TRIANGLE.BAS
' QCALMATH.BAS
' PARAMETERS: (none)
' VARIABLES: sA$ User input of side a
' sB$ User input of side b
' sC$ User input of side c
' aA$ User input of angle A
' aB$ User input of angle B
' aC$ User input of angle C
' sA# Side A
' sB# Side B
' sC# Side C
' aA# Angle A
' aB# Angle B
' aC# Angle C
' Functions
DECLARE FUNCTION Deg2Rad# (deg#)
DECLARE FUNCTION Rad2Deg# (rad#)
DECLARE FUNCTION ArcCosine# (x#)
DECLARE FUNCTION ArcSine# (x#)
DECLARE FUNCTION TriangleArea# (sA#, sB#, sC#)
' Subprograms
DECLARE SUB Triangle (sA#, sB#, sC#, aA#, aB#, aC#)
' Initialization
SCREEN 2
CLS
PRINT "TRIANGLE"
' Draw a representative triangle
WINDOW (0, 0)-(1, 1)
LINE (.3, .7)-(.8, .7)
LINE -(.4, 1)
LINE -(.3, .7)
' Label the triangle sides
LOCATE 4, 26
PRINT "a"
LOCATE 3, 48
PRINT "b"
LOCATE 9, 42
PRINT "c"
' Label the triangle angles
LOCATE 7, 55
PRINT "A"
LOCATE 7, 28
PRINT "B"
LOCATE 2, 33
PRINT "C"
' Ask user for the known data
LOCATE 12, 1
PRINT "Enter known sides and angles (deg),"
PRINT "and press Enter for unknowns..."
LOCATE 16, 1
LINE INPUT "Side a "; sA$
LINE INPUT "Side b "; sB$
LINE INPUT "Side c "; sC$
PRINT
LINE INPUT "Angle A "; aA$
LINE INPUT "Angle B "; aB$
LINE INPUT "Angle C "; aC$
PRINT
' Convert to numeric values
sA# = VAL(sA$)
sB# = VAL(sB$)
sC# = VAL(sC$)
aA# = Deg2Rad#(VAL(aA$))
aB# = Deg2Rad#(VAL(aB$))
aC# = Deg2Rad#(VAL(aC$))
' Solve for the unknowns
Triangle sA#, sB#, sC#, aA#, aB#, aC#
' Output the results
LOCATE 16, 1
PRINT "Side a "; sA#
PRINT "Side b "; sB#
PRINT "Side c "; sC#
PRINT
PRINT "Angle A "; Rad2Deg#(aA#); "Deg"
PRINT "Angle B "; Rad2Deg#(aB#); "Deg"
PRINT "Angle C "; Rad2Deg#(aC#); "Deg"
LOCATE 20, 40
PRINT "Area = "; TriangleArea#(sA#, sB#, sC#)
' All done
LOCATE 24, 1
PRINT "Press any key to continue";
DO
LOOP WHILE INKEY = ""
SCREEN 0
END
──────────────────────────────────────────────────────────────────────────
Function: Deg2Rad#
Converts degrees to radians.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Deg2Rad# **
' ** Type: Function **
' ** Module: TRIANGLE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Converts degree angular units to radians.
'
' EXAMPLE OF USE: r# = Deg2Rad#(deg#)
' PARAMETERS: deg# Degrees
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Deg2Rad# (deg#)
'
FUNCTION Deg2Rad# (deg#) STATIC
Deg2Rad# = deg# / 57.29577951308232#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Function: Rad2Deg#
Converts radians to degrees.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Rad2Deg# **
' ** Type: Function **
' ** Module: TRIANGLE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Converts radian angular units to degrees.
'
' EXAMPLE OF USE: d# = Rad2Deg#(rad#)
' PARAMETERS: rad# Radians
' VARIABLES: (none)
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION Rad2Deg# (rad#)
'
FUNCTION Rad2Deg# (rad#) STATIC
Rad2Deg# = rad# * 57.29577951308232#
END FUNCTION
──────────────────────────────────────────────────────────────────────────
Subprogram: Triangle
Calculates sides and angles of a triangle if enough sides and/or angles
are given to be able to deduce the rest. Any combination of sides and
angles can be given, although illegal combinations will produce
unpredictable results.
Double-precision numbers are used throughout this subprogram to maintain
high accuracy. Change all the pound signs to exclamation points if you
prefer to work with single-precision numbers.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Triangle **
' ** Type: Subprogram **
' ** Module: TRIANGLE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Calculates all sides and angles of a triangle,
' assuming enough sides and angles are given.
'
' EXAMPLE OF USE: Triangle sA#, sB#, sC#, aA#, aB#, aC#
' PARAMETERS: sA# Side A
' sB# Side B
' sC# Side C
' aA# Angle A
' aB# Angle B
' aC# Angle C
' VARIABLES: i% Looping index
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB Triangle (sA#, sB#, sC#, aA#, aB#, aC#)
'
SUB Triangle (sA#, sB#, sC#, aA#, aB#, aC#) STATIC
FOR i% = 1 TO 18
IF aA# = 0# THEN
IF sA# <> 0# AND sB# <> 0# AND sC# <> 0# THEN
t# = sB# * sB# + sC# * sC# - sA# * sA#
aA# = ArcCosine#(t# / 2# / sB# / sC#)
END IF
END IF
IF aB# = 0# THEN
IF sA# <> 0# AND sB# <> 0# AND aA# <> 0# THEN
aB# = ArcSine#(sB# * SIN(aA#) / sA#)
END IF
END IF
IF aC# = 0# THEN
IF aA# <> 0# AND aB# <> 0# THEN
aC# = 3.141592653589793# - aA# - aB#
END IF
END IF
IF sB# = 0# THEN
IF sA# <> 0# AND aB# <> 0# AND aA# <> 0# THEN
sB# = sA# * SIN(aB#) / SIN(aA#)
END IF
END IF
IF sC# = 0# THEN
IF sA# <> 0# AND sB# <> 0# AND aC# <> 0# THEN
t# = sA# * sA# + sB# * sB#
sC# = SQR(t# - 2# * sA# * sB# * COS(aC#))
END IF
END IF
IF i% MOD 2 THEN
SWAP sB#, sC#
SWAP aB#, aC#
ELSE
SWAP sA#, sB#
SWAP aA#, aB#
END IF
NEXT i%
END SUB
──────────────────────────────────────────────────────────────────────────
Function: TriangleArea#
Calculates the area of a triangle given the three sides of the triangle.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: TriangleArea# **
' ** Type: Function **
' ** Module: TRIANGLE.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the area of a triangle given the three sides.
'
' EXAMPLE OF USE: TriangleArea# sA#, sB#, sC#
' PARAMETERS: sA# Side A
' sB# Side B
' sC# Side C
' VARIABLES: s# Sum of the three sides of the triangle
' divided by two
' t1# Temporary variable
' t2# Temporary variable
' t3# Temporary variable
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION TriangleArea# (sA#, sB#, sC#)
'
FUNCTION TriangleArea# (sA#, sB#, sC#) STATIC
s# = (sA# + sB# + sC#) / 2#
t1# = s# - sA#
t2# = s# - sB#
t3# = s# - sC#
TriangleArea# = SQR(s# * t1# * t2# * t3#)
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
WINDOWS
The WINDOWS demo module demonstrates the windows subprograms. One lets
you create several types of windows for displaying information and menu
selections. A second subprogram removes the most recently created window.
The WindowsType data structure completely defines the action and
appearance of the windows that you can create. Although the list of
variables in this structure is fairly long, you have the advantage of
complete control over the windows.
Set the action code to 0, 1, or 2. An action code of 0 indicates a return
to the calling program immediately after the window is created, leaving
the window on the screen. You could use this type of window for simply
displaying information.
An action code of 1 creates the window and then waits for the user to
press any key before continuing. The code for the key pressed is returned
to the calling program, making it possible to use a type 1 window to ask
yes-or-no, multiple choice, or other questions.
An action code of 2 creates the most sophisticated type of window. In this
case, a menu window is created, providing several methods for the user to
select from among the available menu choices. One line of the menu window
is highlighted to indicate the currently selected choice. You can use the
up and down arrow keys or the mouse to move this highlight to the desired
line. Clicking with the mouse or pressing the Enter key selects the
currently highlighted line. You can also press the key for the first
unique character of a line, which immediately selects that line. The
Windows subprogram then returns the line number for a type 2 action code
menu.
The edgeLine variable in the WindowsType data structure should also be set
to 0, 1, or 2. This parameter tells the Windows subprogram to draw a
border around the window with 0-, 1-, or 2-line graphics characters.
You can select and control the foreground and background colors for each
part of a window individually. The color definition constants used in the
demonstration program can be very useful for setting these parameters.
The row and column variables define the placement of the upper left corner
of the window. The Windows subprogram automatically sizes the window for
both the number of lines and the length of the longest line. Be sure to
place the window where it won't hit the edges of the screen.
The title string appears in the center of the top border of the window,
and the prompt string appears in the center of the bottom border of the
display. If these strings are null, nothing is displayed in the window
borders.
The PCOPY statement copies screen pages for saving and restoring the
background information under the windows. This technique results in very
quick window appearance and disappearance without using complicated
assembly-language routines. In 40-column SCREEN mode 0, you can display up
to seven windows at once, one for each available screen page. Depending on
the graphics adapter you have, other video modes let you display two to
four windows simultaneously.
Name Type Description
──────────────────────────────────────────────────────────────────────────
WINDOWS.BAS Demo module
Windows Sub Creates a pop-up window
WindowsPop Sub Removes last displayed window
──────────────────────────────────────────────────────────────────────────
Demo Module: WINDOWS
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: WINDOWS **
' ** Type: Toolbox **
' ** Module: WINDOWS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
' USAGE: No command line parameters
' REQUIREMENTS: MIXED.QLB/.LIB
' Mouse (optional)
' .MAK FILE: WINDOWS.BAS
' BITS.BAS
' BIOSCALL.BAS
' MOUSSUBS.BAS
' KEYS.BAS
' PARAMETERS: (none)
' VARIABLES: w1 Structure of type WindowsType
' w2 Structure of type WindowsType
' w3 Structure of type WindowsType
' w1Text$() Strings to display in first window
' w2Text$() Strings to display in second window
' w3Text$() Strings to display in third window
' w1Title$ Title string for first window
' w1Prompt$ Prompt string for first window
' w2Title$ Title string for second window
' w2Prompt$ Prompt string for second window
' w3Title$ Title string for third window
' arrow$ String showing up and down arrows
' entSymbol$ String showing the Enter key symbol
' w3Prompt$ Prompt string for third window
' i% Looping index
' t0 Timer value
' Define color constants
CONST BLACK = 0
CONST BLUE = 1
CONST GREEN = 2
CONST CYAN = 3
CONST RED = 4
CONST MAGENTA = 5
CONST BROWN = 6
CONST WHITE = 7
CONST BRIGHT = 8
CONST BLINK = 16
CONST YELLOW = BROWN + BRIGHT
TYPE WindowsType
action AS INTEGER
edgeLine AS INTEGER
row AS INTEGER
col AS INTEGER
fgdEdge AS INTEGER
bgdEdge AS INTEGER
fgdBody AS INTEGER
bgdBody AS INTEGER
fgdHighlight AS INTEGER
bgdHighlight AS INTEGER
fgdTitle AS INTEGER
bgdTitle AS INTEGER
fgdPrompt AS INTEGER
bgdPrompt AS INTEGER
returnCode AS INTEGER
END TYPE
' Functions
DECLARE FUNCTION InKeyCode% ()
' Subprograms
DECLARE SUB Windows (w AS WindowsType, wText$(), wTitle$, wPrompt$)
DECLARE SUB WindowsPop ()
DECLARE SUB VideoState (mode%, columns%, page%)
DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
DECLARE SUB MouseMickey (horizontal%, vertical%)
DECLARE SUB MouseNow (leftButton%, rightButton%, xMouse%, yMouse%)
' Data structures
DIM w1 AS WindowsType
DIM w2 AS WindowsType
DIM w3 AS WindowsType
' Arrays
DIM w1Text$(1 TO 5)
DIM w2Text$(1 TO 3)
DIM w3Text$(1 TO 9)
' Define first window
w1.action = 0
w1.edgeLine = 1
w1.row = 2
w1.col = 3
w1.fgdEdge = YELLOW
w1.bgdEdge = BLUE
w1.fgdBody = BRIGHT + WHITE
w1.bgdBody = BLUE
w1.fgdHighlight = 0
w1.bgdHighlight = 0
w1.fgdTitle = YELLOW
w1.bgdTitle = BLUE
w1.fgdPrompt = YELLOW
w1.bgdPrompt = BLUE
w1Title$ = " First Window "
w1Text$(1) = "This window demonstrates how information"
w1Text$(2) = "can be displayed without requesting any"
w1Text$(3) = "response from the user. The action code"
w1Text$(4) = "is 0, causing an immediate return to the"
w1Text$(5) = "program after the window is displayed."
w1Prompt$ = ""
' Define second window
w2.action = 1
w2.edgeLine = 2
w2.row = 10
w2.col = 12
w2.fgdEdge = CYAN + BRIGHT
w2.bgdEdge = BLACK
w2.fgdBody = YELLOW
w2.bgdBody = BLACK
w2.fgdHighlight = 0
w2.bgdHighlight = 0
w2.fgdTitle = CYAN + BRIGHT
w2.bgdTitle = BLUE
w2.fgdPrompt = CYAN + BRIGHT
w2.bgdPrompt = BLUE
w2Title$ = " Second window, action code is 1 "
w2Text$(1) = "This window waits for the user to press"
w2Text$(2) = "any key before continuing. The key code"
w2Text$(3) = "is passed back to the calling program."
w2Prompt$ = " Press any key to continue. "
' Define third window
w3.action = 2
w3.edgeLine = 2
w3.row = 7
w3.col = 15
w3.fgdEdge = YELLOW
w3.bgdEdge = WHITE
w3.fgdBody = BLACK
w3.bgdBody = WHITE
w3.fgdHighlight = WHITE + BRIGHT
w3.bgdHighlight = BLACK
w3.fgdTitle = YELLOW
w3.bgdTitle = WHITE
w3.fgdPrompt = YELLOW
w3.bgdPrompt = WHITE
w3Title$ = " Third window, action is 2 (menu selection) "
arrows$ = CHR$(24) + " " + CHR$(25) + " "
entSymbol$ = CHR$(17) + CHR$(196) + CHR$(217)
w3Prompt$ = " <Character> " + arrows$ + entSymbol$ + " or use mouse "
w3Text$(1) = "1. This is the first line in the window."
w3Text$(2) = "2. This is the second."
w3Text$(3) = "3. This is the third line."
w3Text$(4) = "4. The fourth."
w3Text$(5) = "5. The fifth."
w3Text$(6) = "A. You can press <A> or <a> to select this line."
w3Text$(7) = "B. You can press <1> to <5> for one of the first 5 lines.
w3Text$(8) = "C. Try moving the cursor up or down and pressing Enter."
w3Text$(9) = "D. Also, try the mouse. Click with left button."
' Initialize the display
SCREEN 0, , 0, 0
WIDTH 80
CLS
FOR i% = 1 TO 20
PRINT STRING$(80, 178)
NEXT i%
LOCATE 6, 24
PRINT " * Windows toolbox demonstration * "
' Wait for any key to be pressed
LOCATE 22, 1
PRINT "Press any key to continue"
DO
LOOP UNTIL INKEY$ <> ""
' Clear the "press any key" prompt
LOCATE 22, 1
PRINT SPACE$(25)
' Create the three windows
Windows w1, w1Text$(), w1Title$, w1Prompt$
Windows w2, w2Text$(), w2Title$, w2Prompt$
Windows w3, w3Text$(), w3Title$, w3Prompt$
' Display the result codes, and erase each window
FOR i% = 1 TO 4
LOCATE 21, 1
COLOR WHITE, BLACK
PRINT "The three return codes...";
PRINT w1.returnCode; w2.returnCode; w3.returnCode
COLOR YELLOW
PRINT "Every five seconds another window will disappear..."
COLOR WHITE, BLACK
t0 = TIMER
DO
LOOP UNTIL TIMER - t0 > 5
WindowsPop
NEXT i%
' All done
CLS
END
──────────────────────────────────────────────────────────────────────────
Subprogram: Windows
Creates a pop-up window for displaying string data or menu selections. The
data structure of type WindowsType defines the action, colors, borders,
and other attributes of the windows. If you provide invalid parameters,
you receive an appropriate error message, and the program terminates.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Windows **
' ** Type: Subprogram **
' ** Module: WINDOWS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Displays a rectangular window for information display
' or menu selection.
'
' EXAMPLE OF USE: Windows w1, wText$(), wTitle$, wPrompt$
' PARAMETERS: w1 Structure of type WindowsType
' wTest$() Array of strings to be displayed
' wTitle$ Title string
' wPrompt$ Prompt string
' VARIABLES: mode% Current video mode
' columns% Current number of character columns
' page% Current video page
' cursorRow% Saved cursor row position
' cursorCol% Saved cursor column position
' newpage% Next video page
' lbText% Lower boundary of array of text lines
' ubText% Upper boundary of array of text lines
' i% Looping index
' maxlen% Length of longest string to display
' length% Length of each array string
' row2% Row number at bottom right corner of win
' col2% Column number at bottom right corner of
' window
' ul% Upper left corner border character code
' ur% Upper right corner border character code
' ll% Lower left corner border character code
' lr% Lower right corner border character code
' vl% Vertical border character code
' hl% Horizontal border character code
' r% Index to each line of text
' ptr% Highlighted line pointer
' lastPtr% Last highlighted line
' horizontal% Horizontal mouse mickies
' vertical% Vertical mouse mickies
' mickies Accumulated vertical mickies
' choice$ Set of unique characters for each menu l
' tmp$ Work string
' kee% Key code returned by InKeyCode% function
' leftButton% Mouse left button state
' rightButton% Mouse right button state
' xMouse% Mouse X position
' yMouse% Mouse Y position
' MODULE LEVEL
' DECLARATIONS: SUB Windows (w AS WindowsType, wText$(), wTitle$,
' wPrompt$) STATIC
'
SUB Windows (w AS WindowsType, wText$(), wTitle$, wPrompt$) STATIC
' Key code numbers
CONST DOWNARROW = 20480
CONST ENTER = 13
CONST ESCAPE = 27
CONST UPARROW = 18432
' Determine current video page
VideoState mode%, columns%, page%
' Record current cursor location
cursorRow% = CSRLIN
cursorCol% = POS(0)
' Window will be on the next page, if available
newpage% = page% + 1
IF newpage% > 7 THEN
SCREEN , , 0, 0
PRINT "Error: Windows - not enough video pages"
SYSTEM
END IF
' Copy current page to new page
PCOPY page%, newpage%
' Show the current page while building window on new page
SCREEN , , newpage%, page%
' Determine array bounds
lbText% = LBOUND(wText$)
ubText% = UBOUND(wText$)
' Check the text array bounds, lower always 1, upper > 0
IF lbText% <> 1 OR ubText% < 1 THEN
SCREEN , , 0, 0
PRINT "Error: Windows - text array dimensioned incorrectly"
SYSTEM
END IF
' Determine longest string in the text array
maxLen% = 0
FOR i% = lbText% TO ubText%
length% = LEN(wText$(i%))
IF length% > maxLen% THEN
maxLen% = length%
END IF
NEXT i%
' Determine the bottom right corner of window
row2% = w.row + ubText% + 1
col2% = w.col + maxLen% + 3
' Check that window is on screen
IF w.row < 1 OR w.col < 1 OR row2% > 25 OR col2% > columns% THEN
SCREEN , , 0, 0
PRINT "Error: Windows - part of window is off screen"
SYSTEM
END IF
' Set the edge characters
SELECT CASE w.edgeLine
CASE 0
ul% = 32
ur% = 32
ll% = 32
lr% = 32
vl% = 32
hl% = 32
CASE 1
ul% = 218
ur% = 191
ll% = 192
lr% = 217
vl% = 179
hl% = 196
CASE 2
ul% = 201
ur% = 187
ll% = 200
lr% = 188
vl% = 186
hl% = 205
CASE ELSE
SCREEN , , 0, 0
PRINT "Error: Windows - Edge line type incorrect"
SYSTEM
END SELECT
' Draw top edge of the box
LOCATE w.row, w.col, 0
COLOR w.fgdEdge, w.bgdEdge
PRINT CHR$(ul%); STRING$(maxLen% + 2, hl%); CHR$(ur%);
' Draw the body of the window
FOR r% = w.row + 1 TO row2% - 1
LOCATE r%, w.col, 0
COLOR w.fgdEdge, w.bgdEdge
PRINT CHR$(vl%);
COLOR w.fgdBody, w.bgdBody
tmp$ = LEFT$(wText$(r% - w.row) + SPACE$(maxLen%), maxLen%)
PRINT " "; tmp$; " ";
COLOR w.fgdEdge, w.bgdEdge
PRINT CHR$(vl%);
NEXT r%
' Draw bottom edge of the box
LOCATE row2%, w.col, 0
COLOR w.fgdEdge, w.bgdEdge
PRINT CHR$(ll%); STRING$(maxLen% + 2, hl%); CHR$(lr%);
' Center and print top title if present
IF wTitle$ <> "" THEN
LOCATE w.row, (w.col + col2% - LEN(wTitle$) + 1) \ 2, 0
COLOR w.fgdTitle, w.bgdTitle
PRINT wTitle$;
END IF
' Center and print prompt if present
IF wPrompt$ <> "" THEN
LOCATE row2%, (w.col + col2% - LEN(wPrompt$) + 1) \ 2, 0
COLOR w.fgdPrompt, w.bgdPrompt
PRINT wPrompt$;
END IF
' Now make the new page visible and active
SCREEN , , newpage%, newpage%
' Take next action based on action code
SELECT CASE w.action
CASE 1
' Get a key code number and return it
DO
w.returnCode = InKeyCode%
LOOP UNTIL w.returnCode
CASE 2
' Set choice pointer to last selection if known
IF w.returnCode > 0 AND w.returnCode < ubText% THEN
ptr% = w.returnCode
ELSE
ptr% = 1
END IF
' Start with last pointer different, to update highlighting
IF ptr% > 1 THEN
lastPtr% = 1
ELSE
lastPtr% = 2
END IF
' Clear any mouse mickey counts
MouseMickey horizontal%, vertical%
mickies% = 0
' Create unique key selection string
choice$ = ""
FOR i% = 1 TO ubText%
tmp$ = UCASE$(LTRIM$(wText$(i%)))
DO
IF tmp$ <> "" THEN
t$ = LEFT$(tmp$, 1)
tmp$ = MID$(tmp$, 2)
IF INSTR(choice$, t$) = 0 THEN
choice$ = choice$ + t$
END IF
ELSE
SCREEN 0, , 0
PRINT "Error: Windows - No unique character"
SYSTEM
END IF
LOOP UNTIL LEN(choice$) = i%
NEXT i%
' Main loop, monitor mouse and keyboard
DO
' Add the mouse mickies
MouseMickey horizontal%, vertical%
mickies% = mickies% + vertical%
' Check for enough mickies
IF mickies% < -17 THEN
mickies% = 0
IF ptr% > 1 THEN
ptr% = ptr% - 1
END IF
ELSEIF mickies% > 17 THEN
mickies% = 0
IF ptr% < ubText% THEN
ptr% = ptr% + 1
END IF
END IF
' Check keyboard
kee% = InKeyCode%
IF kee% >= ASC("a") AND kee% <= ASC("z") THEN
kee% = ASC(UCASE$(CHR$(kee%)))
END IF
SELECT CASE kee%
CASE UPARROW
IF ptr% > 1 THEN
ptr% = ptr% - 1
END IF
CASE DOWNARROW
IF ptr% < ubText% THEN
ptr% = ptr% + 1
END IF
CASE ENTER
w.returnCode = ptr%
CASE ESCAPE
w.returnCode = -1
CASE ELSE
w.returnCode = INSTR(choice$, CHR$(kee%))
IF w.returnCode THEN
ptr% = w.returnCode
END IF
END SELECT
' Check the left mouse button
MouseNow leftButton%, rightButton%, xMouse%, yMouse%
IF leftButton% THEN
w.returnCode = ptr%
END IF
' Update the highlight if line has changed
IF ptr% <> lastPtr% THEN
LOCATE lastPtr% + w.row, w.col + 2, 0
COLOR w.fgdBody, w.bgdBody
tmp$ = LEFT$(wText$(lastPtr%) + SPACE$(maxLen%), maxLen
PRINT tmp$;
LOCATE ptr% + w.row, w.col + 2, 0
COLOR w.fgdHighlight, w.bgdHighlight
tmp$ = LEFT$(wText$(ptr%) + SPACE$(maxLen%), maxLen%)
PRINT tmp$;
lastPtr% = ptr%
END IF
LOOP WHILE w.returnCode = 0
CASE ELSE
w.returnCode = 0
END SELECT
' Reset the cursor position
LOCATE cursorRow%, cursorCol%
END SUB
──────────────────────────────────────────────────────────────────────────
Subprogram: WindowsPop
Removes the most recently created window from the screen. The SCREEN
statement is used to change the apage and vpage parameters simultaneously,
resulting in nearly instant removal of the window.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: WindowsPop **
' ** Type: Subprogram **
' ** Module: WINDOWS.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Removes last displayed window.
'
' EXAMPLE OF USE: WindowsPop
' PARAMETERS: (none)
' VARIABLES: mode% Current video mode
' columns% Current number of display columns
' page% Current display page
' MODULE LEVEL
' DECLARATIONS: DECLARE SUB WindowsPop ()
'
SUB WindowsPop STATIC
VideoState mode%, columns%, page%
IF page% THEN
SCREEN 0, , page% - 1, page% - 1
END IF
END SUB
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
WORDCOUN
The WORDCOUN toolbox counts words in a file and contains a function that
counts words in a string. Enter a filename on the command line when you
run this program.
Name Type Description
──────────────────────────────────────────────────────────────────────────
WORDCOUN.BAS Demo module
WordCount% Func Returns number of words in a string
──────────────────────────────────────────────────────────────────────────
Demo Module: WORDCOUN
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: WORDCOUN **
' ** Type: Toolbox **
' ** Module: WORDCOUN.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: WORDCOUN filename
' .MAK FILE: (none)
' PARAMETERS: filename Name of file to be processed
' VARIABLES: fileName$ Name of file from the command line
' sep$ List of characters defined as word separ
' a$ Each line from the file
' totalCount& Total count of words
DECLARE FUNCTION WordCount% (a$, sep$)
' Assume a filename has been given on the command line
fileName$ = COMMAND$
' Open the file
OPEN fileName$ FOR INPUT AS #1
' Define the word-separating characters as space, tab, and comma
sep$ = " " + CHR$(9) + ","
' Read in and process each line
DO
LINE INPUT #1, a$
totalCount& = totalCount& + WordCount%(a$, sep$)
LOOP UNTIL EOF(1)
' Print the results
PRINT "There are"; totalCount&; "words in "; fileName$
' That's all
END
──────────────────────────────────────────────────────────────────────────
Function: WordCount%
Returns the number of words in a string. Words are defined as groups of
characters separated by one or more of the characters in sep$. The
WORDCOUN toolbox passes sep$ with a space, a tab, and a comma in it, but
you can place any characters in sep$ that you want to use to define the
separation of words.
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: Wordcount% **
' ** Type: Function **
' ** Module: WORDCOUN.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Returns the number of words in a string.
'
' EXAMPLE OF USE: WordCount% a$, sep$
' PARAMETERS: a$ String containing words to be counted
' sep$ List of word separation characters
' VARIABLES: count% Count of words
' flag% Indicates if scanning is currently inside o
' word
' la% length of a$
' i% Index to each character of a$
' MODULE LEVEL
' DECLARATIONS: DECLARE FUNCTION WordCount% (a$, sep$)
'
FUNCTION WordCount% (a$, sep$) STATIC
count% = 0
flag% = 0
la% = LEN(a$)
IF la% > 0 AND sep$ <> "" THEN
FOR i% = 1 TO la%
IF INSTR(sep$, MID$(a$, i%, 1)) THEN
IF flag% THEN
flag% = 0
count% = count% + 1
END IF
ELSE
flag% = 1
END IF
NEXT i%
END IF
WordCount% = count% + flag%
END FUNCTION
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
PART 3 MIXED-LANGUAGE TOOLBOXES
────────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
Chapter Four Using Mixed-Language Toolboxes
Although Microsoft QuickBASIC is a sophisticated and powerful
software-development tool, other languages, such as C, FORTRAN, Pascal,
and assembly language, have unique strengths. The ability to mix
subprograms and functions written in any of these languages lets you use
the best features of each. FORTRAN, for example, has extensive mathematics
and engineering libraries, and C is a powerful development language. For
the fastest running programs, assembly language can't be beat. Once you
understand a few concepts, you'll be able to easily combine routines from
these languages.
Near and Far Addressing
MS-DOS runs on the 8088, 8086, 80286, and 80386 family of microprocessors
found at the heart of IBM Personal Computers and compatibles. These chips
use special hardware registers that allow quicker access and shorter
instruction when referring to data or procedures located in the same block
of 65536 (64 KB) bytes of memory.
From a software point of view, microprocessor instructions can address
memory locations by referring to either locations in a currently defined,
single block of 64 KB memory addresses or to any possible locations in
memory space. These references are called "near" and "far," respectively.
Near references require one word, and far references require two words.
Normally, high-level languages such as QuickBASIC take care of all these
details for you, but when you link subprograms from other languages with
QuickBASIC, you need to be sure that references to variables and calls to
subprograms and functions all use the same type of near or far addressing.
You can adjust Microsoft QuickC and Microsoft Macro Assembler 5.0 to
create programs using a variety of memory models that indicate whether
memory locations are referred to by near or far addressing. To be
compatible with QuickBASIC's method, you should use Medium Model settings
for both compilers. In fact, this is the default setting for QuickC,
making it easy for you to write QuickC routines to be called from
QuickBASIC.
Passing Variables
Most programming languages, including QuickBASIC and QuickC, let you pass
variables to called subprograms, functions, and subroutines by listing
them in parentheses as part of the calling statement. This list matches
one for one the parameters defined and used in the called routine.
You can pass these parameters to and from a subprogram or function by
reference or by value. Some languages pass the address of the referenced
variable, and changes made to the variable by the routine modify the
contents of memory that this address points to. Other languages pass
copies of the values of the variables. Changes to the passed variables
don't affect the originals because the changes are made only to the
copies.
The important concept is that both the calling routine and the called
routine must agree as to how they pass parameters back and forth. For
example, QuickBASIC usually passes parameters by reference, and QuickC by
value. Fortunately, both languages let you control whether parameters are
passed by value or by reference. See the CDECL modifier of the QuickBASIC
DECLARE statement for an example of how you can control parameter passing.
Parameter passing can also vary from language to language in the order in
which the parameters are pushed onto the stack as well as in the method
used to remove these parameters from the stack when the called routine is
finished. For example, consider a QuickBASIC program that passes
parameters (A, B, C) to a QuickC subprogram. The QuickC routine processes
the parameters as (C, B, A). The CDECL modifier in the QuickBASIC DECLARE
statement can tell QuickBASIC to reverse the normal order of parameter
pushing to be compatible with QuickC.
In most languages, when a subprogram or function is finished, the
parameters are removed from the stack before the routine returns to the
calling program. QuickC routines expect the calling program to clean up
the stack after the return. (This allows passing a variable number of
parameters in C.) The CDECL modifier instructs QuickBASIC to clean up the
stack after calling a subprogram or function.
By using standard parameter-passing techniques and by following the
examples in this book and in your Microsoft QuickBASIC and Microsoft Macro
Assembler manuals, you'll find mixed-language programming easy and
convenient.
The routines in Part III of this book demonstrate passing integers, arrays
of integers, and string variables. The Microsoft Macro Assembler was used
to develop the mouse interface subprogram, and Microsoft QuickC was used
to create some bit manipulation and byte-movement routines.
Creating Mixed-Language Toolboxes
In the section "Using QuickBASIC Toolboxes," you will find the steps to
follow for using the QuickC and Macro Assembler routines presented in Part
III of this book. Please see "Creating MIXED.QLB," beginning on page 22.
In addition to compiling from the system prompt, you can also compile each
QuickC toolbox, CTOOLS1.C and CTOOLS2.C, from within the QuickC
environment. Run QuickC by typing QC, and notice that the environment is
very similar in appearance and feel to that of QuickBASIC. Pull down the
Files menu and choose Open. Select CTOOLS1.C to load the first toolbox
into QuickC. Then pull down the Run menu and choose Compile. A dialog box
opens, providing several compiling options. In the Output Options section,
select Obj, rather than the default Memory, and then select Compile File,
rather than the default Build Program option. You can experiment with the
options listed under Miscellaneous, but selecting Optimizations and
deselecting the Stack Checking and Debug options generally results in a
smaller .OBJ file. QuickC then compiles the CTOOLS1.C source code
currently in memory and writes the resulting CTOOLS1.OBJ file to disk.
Repeat the process for the CTOOLS2.C file.
Once you have created the object-code files and the MIXED.QLB Quick
Library and have loaded MIXED.QLB with QuickBASIC, you can then call any
or all of the QuickC and Macro Assembler functions and subprograms from
within programs running in the QuickBASIC environment. Be sure to declare
the subprogram or function in the module-level code, and then call the
routines freely, as though they were part of the standard set of
QuickBASIC functions and commands.
Once a program is running as desired in the QuickBASIC environment you may
want to compile it into a stand-alone .EXE format file. Simply compile the
program from within QuickBASIC and the appropriate .LIB file will be
searched. The MIXED.LIB file will automatically pull in the necessary code
during the LINK process.
NOTE: You must have QuickC installed to compile CDEMO1 and CDEMO2 into
executable files.
Assembly Source-Code Files
The CASEMAP.ASM and MOUSE.ASM source-code files are listed here for your
convenience in building both the MIXED.QLB and MIXED.LIB libraries.
The CASEMAP.ASM subprogram is called by TranslateCountry$, a function
found in the DOSCALLS module, to translate each character, one at a time.
This routine demonstrates how you can use QuickBASIC's DECLARE statement
to pass parameters by value rather than by reference. In this case, the
segment- and offset-address parameters for the MS-DOS translation routine
are passed by value, resulting in a very efficient branch to the MS-DOS
character-translation routine from CaseMap.
──────────────────────────────────────────────────────────────────────────
; **********************************************
; ** CASEMAP.ASM MASM 5.0 **
; ** **
; ** Assembly subprogram for translating **
; ** some characters according to the **
; ** currently loaded MS-DOS country- **
; ** dependent information. **
; ** **
; ** Use: CALL CASEMAP (CHAR%, SEG%, OFS%) **
; ** Note: CHAR% is passed by reference **
; ** SEG% and OFS% are passed by value **
; **********************************************
;
; EXAMPLE OF USE: CALL CaseMap (char%, seg%, ofs%)
; PARAMETERS: char% Character byte to be translated
; seg% Segment of address of MS-DOS translate routi
; ofs% Offset of address of MS-DOS translate routin
; VARIABLES: (none)
; MODULE LEVEL
; DECLARATIONS: DECLARE SUB GetCountry (country AS CountryType)
; DECLARE SUB CaseMap (character%, BYVAL Segment%,
; BYVAL Offset%)
; DECLARE FUNCTION TranslateCountry$ (a$, country AS Coun
.MODEL MEDIUM
.CODE
public casemap
casemap proc
; Standard entry
push bp
mov bp,sp
; Get CHAR% into AX register
mov bx,[bp+10]
mov ax,[bx]
; Call the translate function in MS-DOS
call dword ptr [bp+6]
; Return translated character to CHAR%
mov bx,[bp+10]
mov [bx],ax
; Standard exit, assumes three variables passed
pop bp
ret 6
; End of the procedure
casemap endp
end
──────────────────────────────────────────────────────────────────────────
The MOUSE.ASM subprogram provides a fast and efficient method of
interfacing QuickBASIC with the memory-resident mouse-driver software.
(See your mouse documentation for information on loading this driver into
memory.)
──────────────────────────────────────────────────────────────────────────
; **********************************************
; ** MOUSE.ASM Macro Assembler **
; ** **
; ** Assembly subprogram for accessing the **
; ** Microsoft Mouse from QuickBASIC 4.00 **
; ** **
; ** Use: CALL MOUSE (M1%, M2%, M3%, M4%) **
; **********************************************
;
; EXAMPLE OF USE: CALL Mouse (m1%, m2%, m3%, m4%)
; PARAMETERS: m1% Passed in AX to the mouse driver
; m2% Passed in BX to the mouse driver
; m3% Passed in CX to the mouse driver
; m4% Passed in DX to the mouse driver
; VARIABLES: (none)
; MODULE LEVEL
; DECLARATIONS: DECLARE SUB Mouse (m1%, m2%, m3%, m4%)
.MODEL MEDIUM
.CODE
public mouse
mouse proc
; Standard entry
push bp
mov bp,sp
; Get M1% and store it on the stack
mov bx,[bp+12]
mov ax,[bx]
push ax
; Get M2% and store it on the stack
mov bx,[bp+10]
mov ax,[bx]
push ax
; Get M3% into CX register
mov bx,[bp+8]
mov cx,[bx]
; Get M4% into DX register
mov bx,[bp+6]
mov dx,[bx]
; Move M2% from stack into BX register
pop bx
; Move M1% from stack into AX register
pop ax
; Set ES to same as DS (for mouse function 9)
push ds
pop es
; Do the mouse interrupt
int 33h
; Save BX (M2%) on stack to free register
push bx
; Return M1% from AX
mov bx,[bp+12]
mov [bx],ax
; Return M2% from stack (was BX)
pop ax
mov bx,[bp+10]
mov [bx],ax
; Return M3% from CX
mov bx,[bp+8]
mov [bx],cx
; Return M4% from DX
mov bx,[bp+6]
mov [bx],dx
; Standard exit, assumes four variables passed
pop bp
ret 8
; End of this procedure
mouse endp
end
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
CDEMO1.BAS AND CTOOLS1.C
The CDEMO1.BAS program is a QuickBASIC program that demonstrates the
proper declaration and calling of the QuickC routines presented in the
CTOOLS1.C toolbox.
The IsIt[Type]% functions can efficiently determine the classification of
any character, given its ASCII numeric value. For example, given c% =
ASC("A"), the IsItAlnum%, IsItAlpha%, IsItAscii%, IsItGraph%,
IsItPrint%, IsItUpper%, and IsItXDigit% functions all return a true
(non-zero) value, and all other functions return zero.
The MovBytes and MovWords subprograms allow movement of bytes or words
from any location in memory to any other, using the QuickC movedata
function. You can use these subprograms to copy the contents of variables
into variables of a different type. The eight bytes of a double-precision
number, for example, can be easily extracted from an eight-character
string after the data has been moved from the number into the string.
Large arrays of data can efficiently be moved into arrays of a different
type, and video memory can be stored in a string, as demonstrated by the
module-level code of CDEMO1.
Name Type Description
──────────────────────────────────────────────────────────────────────────
CDEMO1.BAS QuickBASIC program module
CTOOLS1.C C-language toolbox containing
functions/subprograms
IsItAlnum% Func Alphanumeric character determination
IsItAlpha% Func Alphabetic character determination
IsItAscii% Func Standard ASCII character determination
IsItCntrl% Func Control character determination
IsItDigit% Func Decimal digit (0─9) determination
IsItGraph% Func Graphics character determination
IsItLower% Func Lowercase character determination
IsItPrint% Func Printable character determination
IsItPunct% Func Punctuation character determination
IsItSpace% Func Space character determination
IsItUpper% Func Uppercase character determination
IsItXDigit% Func Hexadecimal character determination
MovBytes Sub Moves bytes from one location to another
MovWords Sub Moves blocks of words in memory
──────────────────────────────────────────────────────────────────────────
Program Module: CDEMO1
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CDEMO1 **
' ** Type: Program **
' ** Module: CDEMO1.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' Demonstrates the QuickC routines presented in
' the file CTOOLS1.C.
'
' USAGE: No command line parameters
' REQUIREMENTS: CGA
' MIXED.QLB/.LIB
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: a%(0 TO 1999) Storage space for first text screen
' b%(0 TO 1999) Storage space for second text screen
' i% Looping index
' sseg% Word and byte move source segment
' part of address
' soff% Word and byte move source offset
' part of address
' dseg% Word and byte move destination segment
' part of address
' doff% Word and byte move destination offset
' part of address
' nwords% Number of words to move
' nbytes% Number of bytes to move
' t$ Copy of TIME$
' quitflag% Signal to end first demonstration
' Functions
DECLARE FUNCTION IsItAlnum% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItAlpha% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItAscii% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItCntrl% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItDigit% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItGraph% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItLower% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItPrint% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItPunct% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItSpace% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItUpper% CDECL (BYVAL c AS INTEGER)
DECLARE FUNCTION IsItXDigit% CDECL (BYVAL c AS INTEGER)
' Subprograms
DECLARE SUB MovBytes CDECL (sseg%, soff%, dseg%, doff%, nbytes%)
DECLARE SUB MovWords CDECL (sseg%, soff%, dseg%, doff%, nwords%)
' Make two buffers for the first page of video memory
DIM a%(0 TO 1999), b%(0 TO 1999)
' Prevent scrolling when printing in row 25, column 80
VIEW PRINT 1 TO 25
' Create the first page of text
CLS
COLOR 14, 4
FOR i% = 1 TO 25
PRINT STRING$(80, 179);
NEXT i%
COLOR 15, 1
LOCATE 11, 25
PRINT STRING$(30, 32);
LOCATE 12, 25
PRINT " - Calling MovWords - "
LOCATE 13, 25
PRINT STRING$(30, 32);
' Move the screen memory into the first array
sseg% = &HB800
soff% = 0
dseg% = VARSEG(a%(0))
doff% = VARPTR(a%(0))
nwords% = 2000
MovWords sseg%, soff%, dseg%, doff%, nwords%
' Create the second page of text
CLS
COLOR 14, 4
FOR i% = 1 TO 25
PRINT STRING$(80, 196);
NEXT i%
COLOR 15, 1
LOCATE 11, 25
PRINT STRING$(30, 32);
LOCATE 12, 25
PRINT " - Calling MovBytes - "
LOCATE 13, 25
PRINT STRING$(30, 32);
' Move the screen memory into the second array
sseg% = &HB800
soff% = 0
dseg% = VARSEG(b%(0))
doff% = VARPTR(b%(0))
nwords% = 2000
MovWords sseg%, soff%, dseg%, doff%, nwords%
' Set destination to the video screen memory
dseg% = &HB800
doff% = 0
' Do the following until a key is pressed
DO
' Move 2000 words from first array to screen memory
sseg% = VARSEG(a%(0))
soff% = VARPTR(a%(0))
nwords% = 2000
MovWords sseg%, soff%, dseg%, doff%, nwords%
' Wait one second
t$ = TIME$
DO
IF INKEY$ <> "" THEN
t$ = ""
quitFlag% = 1
END IF
LOOP UNTIL TIME$ <> t$
' Move 4000 bytes from second array to screen memory
sseg% = VARSEG(b%(0))
soff% = VARPTR(b%(0))
nbytes% = 4000
MovBytes sseg%, soff%, dseg%, doff%, nbytes%
' Wait one second
t$ = TIME$
DO
IF INKEY$ <> "" THEN
t$ = ""
quitFlag% = 1
END IF
LOOP UNTIL TIME$ <> t$
LOOP UNTIL quitFlag%
' Create a table of all 256 characters and their type designations
FOR i% = 0 TO 255
' After each screenful, display a heading
IF i% MOD 19 = 0 THEN
' If not the first heading, prompt user before continuing
IF i% THEN
PRINT
PRINT "Press any key to continue"
DO WHILE INKEY$ = ""
LOOP
END IF
' Print the heading
CLS
PRINT "Char Alnum Alpha Ascii Cntrl Digit Graph ";
PRINT "Lower Print Punct Space Upper XDigit"
PRINT
END IF
' Some characters we don't want to display
SELECT CASE i%
CASE 7, 8, 9, 10, 11, 12, 13, 29, 30, 31
PRINT USING "### "; i%;
CASE ELSE
PRINT USING "### \ \"; i%, CHR$(i%);
END SELECT
' Display "1" if test is true, "0" otherwise
PRINT USING " # "; 1 + (0 = IsItAlnum%(i%));
PRINT USING " # "; 1 + (0 = IsItAlpha%(i%));
PRINT USING " # "; 1 + (0 = IsItAscii%(i%));
PRINT USING " # "; 1 + (0 = IsItCntrl%(i%));
PRINT USING " # "; 1 + (0 = IsItDigit%(i%));
PRINT USING " # "; 1 + (0 = IsItGraph%(i%));
PRINT USING " # "; 1 + (0 = IsItLower%(i%));
PRINT USING " # "; 1 + (0 = IsItPrint%(i%));
PRINT USING " # "; 1 + (0 = IsItPunct%(i%));
PRINT USING " # "; 1 + (0 = IsItSpace%(i%));
PRINT USING " # "; 1 + (0 = IsItUpper%(i%));
PRINT USING " # "; 1 + (0 = IsItXDigit%(i%))
NEXT i%
END
──────────────────────────────────────────────────────────────────────────
Toolbox: CTOOLS1.C
The CTOOLS1.C toolbox provides access to the efficient QuickC functions
for classifying characters and to QuickC's fast memory move functions for
copying blocks of bytes or words from any location in memory to any other.
You can determine character types using QuickBASIC code, but the QuickC
routines are optimized for speed. Also, adhering to the definitions
provided by QuickC guarantees that character classifications will be the
same for both languages.
You must add both of the following #include statements at the top of the
CTOOLS1.C source-code file, before the function definitions are given.
These two statements pull in the contents of header files necessary for
correct compilation by QuickC:
#include <ctype.h>
#include <memory.h>
Function: IsItAlnum%
Determines whether a character is alphanumeric. This function returns a
non-zero value if the integer value represents an alphanumeric ASCII
character or a zero if the value does not. Alphanumeric characters are in
the ranges A through Z, a through z, and 0 through 9.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItAlnum% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItAlnum%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitalnum (c)
int c;
{
return (isalnum(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItAlpha%
Determines whether a character is alphabetic. This function returns a
non-zero value if the integer value represents an alphabetic ASCII
character or a zero if the value does not. The alphabetic characters are
in the ranges A through Z and a through z.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItAlpha% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItAlpha%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitalpha (c)
int c;
{
return (isalpha(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItAscii%
Determines whether a character is standard ASCII. This function returns a
non-zero value if the integer value represents an ASCII character or a
zero if the value does not. The ASCII character values are in the range 0
through 127.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItAscii% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItAscii%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitascii (c)
int c;
{
return (isascii(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItCntrl%
Determines whether a character is a control character. This function
returns a non-zero value if the integer value represents a control
character or a zero if the value does not. The control characters are in
the range 0 through 31, and 127.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItCntrl% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItCntrl%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitcntrl (c)
int c;
{
return (iscntrl(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItDigit%
Determines whether a character is a numeric digit. This function returns a
non-zero value if the integer value represents a decimal digit or a zero
if the value does not. The digit characters are in the range 0 through 9.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItDigit% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
──────────────────────────────────────────────────────────────────────────
* EXAMPLE OF USE: result% = IsItDigit%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitdigit (c)
int c;
{
return (isdigit(c));
}
Function: IsItGraph%
Determines whether a character is graphic. This function returns a
non-zero value if the integer value represents a printable character, not
including the space character. These character values are in the range 33
through 126.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItGraph% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItGraph%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitgraph (c)
int c;
{
return (isgraph(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItLower%
Determines whether a character is lowercase. This function returns a
non-zero value if the integer value represents a lowercase character or a
zero if the value does not. The lowercase characters are in the range a
through z.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItLower% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItLower%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitlower (c)
int c;
{
return (islower(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItPrint%
Determines whether a character is printable. This function returns a
non-zero value if the integer value represents a printable character or a
zero if the value does not. The printable characters are in the range 32
through 126.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItPrint% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItPrint%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitprint (c)
int c;
{
return (isprint(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItPunct%
Determines whether a character is punctuation. This function returns a
non-zero value if the integer value represents a punctuation character or
a zero if the value does not. The punctuation characters are in the ranges
33 through 47, 59 through 64, 91 through 96, or 123 through 126.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItPunct% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItPunct%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitpunct (c)
int c;
{
return (ispunct(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItSpace%
Determines whether a character is white space. This function returns a
non-zero value if the integer value represents a white-space character or
a zero if the value does not. The white-space character values are in the
range 9 through 13, and 32.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItSpace% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItSpace%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitspace (c)
int c;
{
return (isspace(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItUpper%
Determines whether a character is uppercase. This function returns a
non-zero value if the integer value represents an uppercase character or a
zero if the value does not. The uppercase characters are in the range A
through Z.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItUpper% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItUpper%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitupper (c)
int c;
{
return (isupper(c));
}
──────────────────────────────────────────────────────────────────────────
Function: IsItXDigit%
Determines whether a character is a hexadecimal digit. This function
returns a non-zero value if the integer value represents a hexadecimal
character or a zero if the value does not. The hexadecimal characters are
in the ranges 0 through 9, a through f, or A through F.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: IsItXDigit% **
** Type: Function **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* EXAMPLE OF USE: result% = IsItXDigit%(c%)
* PARAMETERS: c% ASCII character code
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <ctype.h> */
int isitxdigit (c)
int c;
{
return (isxdigit(c));
}
──────────────────────────────────────────────────────────────────────────
Subprogram: MovBytes
Calls the QuickC movedata function to quickly copy a block of bytes from
any address in memory to any other.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: MovBytes **
** Type: Subprogram **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Moves bytes from a source segment and offset
* location in memory to a destination segment and
* offset location.
*
* EXAMPLE OF USE: MovBytes sseg%, soff%, dseg%, doff%, nbytes%
* PARAMETERS: sseg% Source segment address of bytes to be moved
* soff% Source offset address of bytes to be moved
* dseg% Destination segment address of bytes to be m
* doff% Destination offset address of bytes to be mo
* nbytes% Number of bytes to be moved
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <memory.h> */
void movbytes (srcseg, srcoff, destseg, destoff, nbytes)
unsigned int *srcseg, *srcoff, *destseg, *destoff, *nbytes;
{
movedata(*srcseg, *srcoff, *destseg, *destoff, *nbytes);
}
──────────────────────────────────────────────────────────────────────────
Subprogram: MovWords
Moves a block of words from any memory location to any other. This
subprogram calls the QuickC movedata function to quickly copy a block of
words from any address in memory to any other.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: MovWords **
** Type: Subprogram **
** Module: CTOOLS1.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Moves words from a source segment and offset
* location in memory to a destination segment and
* offset location.
*
* EXAMPLE OF USE: MovWords sseg%, soff%, dseg%, doff%, nbytes%
* PARAMETERS: sseg% Source segment address of words to be moved
* soff% Source offset address of words to be moved
* dseg% Destination segment address of words to be mo
* doff% Destination offset address of words to be mov
* nwords% Number of words to be moved
* VARIABLES: (none)
* MODULE LEVEL
* DECLARATIONS: #include <memory.h> */
void movwords (srcseg, srcoff, destseg, destoff, nwords)
unsigned int *srcseg, *srcoff, *destseg, *destoff, *nwords;
{
unsigned int nbytes;
nbytes = *nwords + *nwords;
movedata(*srcseg, *srcoff, *destseg, *destoff, nbytes);
}
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
CDEMO2.BAS AND CTOOLS2.C
The CDEMO2.BAS program is a QuickBASIC program that demonstrates the
proper declaration and calling of the QuickC routines presented in the
CTOOLS2.C toolbox.
The MenuString% function creates a horizontal bar menu, similar to the
menu line at the top of the display in the QuickBASIC environment. The
function call returns the number of the word selected. You can place the
menu bar anywhere on the screen, and it can contain any number of one-word
choices. The first letter of each word must be uppercase, and no more than
two words can have the same first letter.
The BitShiftLeft% and BitShiftRight% functions let you shift all the
bits in a string of bytes one position to the left or right. Each function
returns the bit shifted off the end of the string and shifts in a zero at
the other end.
The NumberOfBits& function returns the number of bits in all the bytes of
a string. You could do this by using the bit-shifting functions and adding
up the returned values, but the NumberOfBits& function is much faster.
The string contents are unchanged by the function.
The PackWord and UnPackWord subprograms let you pack and unpack two byte
values (integers in the range 0 through 255) into an integer variable.
This can be accomplished using the QuickBASIC math functions (although it
gets complicated when dealing with negative numbers), but QuickC has
features ideal for performing these types of data manipulations. By
declaring a union of a two-byte structure with an integer, the bytes can
simply be moved into place instead of calculated.
The TextGet and TextPut subprograms let you quickly save and restore
rectangular areas of the text-mode screen. These routines are similar in
concept to the QuickBASIC GET and PUT statements that save and restore
rectangular areas of graphics-mode screens. Unlike the GET and PUT
statements, though, the rectangular area restored can be of a different
shape than the area that was saved. The total number of bytes must be
identical, but the width and height of the area can differ. The program
module first prints a line of text that is saved into a string using
TextGet and is then restored in a vertical (one column wide) rectangular
area.
Name Type Description
──────────────────────────────────────────────────────────────────────────
CDEMO2.BAS QuickBASIC program module
CTOOLS2.C C-language toolbox containing
functions/subprograms
BitShiftLeft% Func Shifts all bits in a string left one bit
BitShiftRight% Func Shifts all bits in a string right one bit
MenuString% Func Bar menu and user response function
NumberOfBits& Func Determines number of 1 bits in a string
PackWord Sub Packs two bytes into an integer value
TextGet Sub Saves characters and attributes from area
of screen
TextPut Sub Restores text from TextGet to screen
UnPackWord Sub Unpacks values from high and low bytes
──────────────────────────────────────────────────────────────────────────
Program Module: CDEMO2
──────────────────────────────────────────────────────────────────────────
' ************************************************
' ** Name: CDEMO2 **
' ** Type: Program **
' ** Module: CDEMO2.BAS **
' ** Language: Microsoft QuickBASIC 4.00 **
' ************************************************
'
' USAGE: No command line parameters
' REQUIREMENTS: CGA
' MIXED.QLB/.LIB
' .MAK FILE: (none)
' PARAMETERS: (none)
' VARIABLES: m$ Menu string
' word% Integer to be packed with two bytes
' hi% Most significant byte unpacked from an
' integer
' lo% Least significant byte unpacked from an
' integer
' a$ Workspace for TextGet and TextPut
' b$ Workspace for TextGet and TextPut
' n% Timing constant for TextPut demonstratio
' row% Row location to put small "window" using
' TextPut
' col% Column location to put small "window" us
' TextPut
' t0 Timer variable
' x$ String variable for bit shifting
' i% Looping index
' Functions
DECLARE FUNCTION MenuString% CDECL (row%, col%, a$)
DECLARE FUNCTION BitShiftleft% CDECL (a$)
DECLARE FUNCTION BitShiftRight% CDECL (a$)
DECLARE FUNCTION NumberOfBits& CDECL (a$)
' Subprograms
DECLARE SUB PackWord CDECL (word%, hi%, lo%)
DECLARE SUB UnPackWord CDECL (word%, hi%, lo%)
DECLARE SUB TextGet CDECL (r1%, c1%, r2%, c2%, a$)
DECLARE SUB TextPut CDECL (r1%, c1%, r2%, c2%, a$)
' Build menu string
m$ = "Packword Unpackword Textget Textput "
m$ = m$ + "Bitshiftleft Bitshiftright Numberofbits Quit"
' Let user repeatedly select the demonstrations
DO
COLOR 15, 1
CLS
PRINT
PRINT
PRINT "MenuString function..."
PRINT
PRINT "Select one of the CTOOLS2 demonstrations by ";
PRINT "pressing the Left arrow,"
PRINT "Right arrow, first letter of the choice, or Enter keys."
' Use MenuString to choose demonstrations
SELECT CASE MenuString%(1, 1, m$)
' PackWord demonstration
CASE 1
CLS
PRINT "PackWord word%, 255, 255 ... word% = ";
PackWord word%, 255, 255
PRINT word%
PRINT "PackWord word%, 0, 1 ... word% = ";
PackWord word%, 0, 1
PRINT word%
PRINT "PackWord word%, 1, 0 ... word% = ";
PackWord word%, 1, 0
PRINT word%
PRINT
PRINT "Press any key to continue..."
DO
LOOP UNTIL INKEY$ <> ""
' UnPackWord demonstration
CASE 2
CLS
PRINT "UnPackWord -1, hi%, lo% ... hi%, lo% =";
UnPackWord -1, hi%, lo%
PRINT hi%; lo%
PRINT "UnPackWord 1, hi%, lo% ... hi%, lo% =";
UnPackWord 1, hi%, lo%
PRINT hi%; lo%
PRINT "UnPackWord 256, hi%, lo% ... hi%, lo% =";
UnPackWord 256, hi%, lo%
PRINT hi%; lo%
PRINT
PRINT "Press any key to continue..."
DO
LOOP UNTIL INKEY$ <> ""
' TextGet and TextPut demonstration
CASE 3, 4
' TextGet a line of text
CLS
PRINT "A Vertical Message"
a$ = SPACE$(36)
TextGet 1, 1, 1, 18, a$
' TextPut it back, but stretch it vertically
TextPut 6, 1, 23, 1, a$
' Now just a normal line of text at top
LOCATE 1, 1
PRINT "TextGet and TextPut - Press any key to stop"
' Create first of two colorful text patterns
COLOR 14, 4
LOCATE 13, 13, 0
PRINT CHR$(201); CHR$(205); CHR$(209); CHR$(205); CHR$(187)
LOCATE 14, 13, 0
PRINT CHR$(199); CHR$(196); CHR$(197); CHR$(196); CHR$(182)
LOCATE 15, 13, 0
PRINT CHR$(200); CHR$(205); CHR$(207); CHR$(205); CHR$(188)
a$ = SPACE$(30)
TextGet 13, 13, 15, 17, a$
' Create second of two colorful text patterns
COLOR 10, 1
LOCATE 13, 13, 0
PRINT CHR$(218); CHR$(196); CHR$(210); CHR$(196); CHR$(191)
LOCATE 14, 13, 0
PRINT CHR$(198); CHR$(205); CHR$(206); CHR$(205); CHR$(181)
LOCATE 15, 13, 0
PRINT CHR$(192); CHR$(196); CHR$(208); CHR$(196); CHR$(217)
b$ = SPACE$(30)
TextGet 13, 13, 15, 17, b$
' Randomly pop up little "windows"
n% = 0
DO
row% = INT(RND * 21 + 3)
col% = INT(RND * 73 + 4)
TextPut row%, col%, row% + 2, col% + 4, a$
row% = INT(RND * 21 + 3)
col% = INT(RND * 73 + 4)
TextPut row%, col%, row% + 2, col% + 4, b$
IF n% < 10 THEN
n% = n% + 1
t0 = TIMER
DO
LOOP UNTIL TIMER > t0 + (10 - n%) / 10
END IF
LOOP UNTIL INKEY$ <> ""
' BitShiftLeft demonstration
CASE 5
CLS
x$ = "This string will be shifted left 8 bits"
PRINT x$
FOR i% = 1 TO 8
PRINT "bit ="; BitShiftleft%(x$)
NEXT i%
PRINT x$
PRINT
PRINT "Press any key to continue..."
DO
LOOP UNTIL INKEY$ <> ""
' BitShiftRight demonstration
CASE 6
CLS
x$ = "This string will be shifted right 8 bits"
PRINT x$
FOR i% = 1 TO 8
PRINT "bit ="; BitShiftRight%(x$)
NEXT i%
PRINT x$
PRINT
PRINT "Press any key to continue..."
DO
LOOP UNTIL INKEY$ <> ""
' BitShiftRight demonstration
CASE 7
CLS
x$ = "The number of bits in this string is ..."
PRINT x$
PRINT NumberOfBits&(x$)
PRINT
PRINT "Press any key to continue..."
DO
LOOP UNTIL INKEY$ <> ""
' Must be time to quit
CASE ELSE
COLOR 7, 0
CLS
END
END SELECT
LOOP
──────────────────────────────────────────────────────────────────────────
Toolbox: CTOOLS2.C
The CTOOLS2.C toolbox provides a collection of functions and subprograms
that perform tasks that QuickC is well suited for.
You must enter the following block of lines into the first lines of the
CTOOLS2.C source-code file, immediately before the functions and
subprograms. Note that the definition for VIDEO_START should be changed to
0xb0000000 for monochrome operation.
#include <ctype.h>
#include <conio.h>
#define VIDEO_START 0xb8000000
#define BLACK_ON_CYAN 48
#define RED_ON_CYAN 52
#define BRIGHT_WHITE_ON_RED 79
#define ENTER 13
#define RIGHT_ARROW 77
#define LEFT_ARROW 75
/* Definition of the QuickBASIC string descriptor structure */
struct bas_str
{
int sd_len;
char *sd_addr;
};
Function: BitShiftLeft%
Shifts all bits in a QuickBASIC string variable to the left one bit
position. The number of bits in a string is eight times the length of the
string. The function returns the leftmost bit of the first character of
the string.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: BitShiftLeft% **
** Type: Function **
** Module: CTOOLS2.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Shifts all bits in a QuickBASIC string one bit
* to the left. The leftmost bit is returned, and
* the rightmost bit is set to zero.
*
* EXAMPLE OF USE: bit% = BitShiftLeft%(bit$)
* PARAMETERS: bit$ String containing a bit pattern
* VARIABLES: len Length of the string (number of bytes)
* str Pointer to string contents
* i Looping index to each byte of the string
* carry Bit carried over from byte to byte
* the_byte Working copy of each byte of the string
*
* Definition of the QuickBASIC string descriptor structure
* struct bas_str
* {
* int sd_len;
* char *sd_addr;
* }; */
int bitshiftleft (basic_string)
struct bas_str *basic_string;
{
int len = basic_string->sd_len;
unsigned char *str = basic_string->sd_addr;
int i, carry;
unsigned int the_byte;
for (i=len-1, carry=0; i>=0; i--)
{
the_byte = *(str + i);
*(str + i) = (the_byte << 1) + carry;
carry = the_byte >> 7;
}
return (carry);
}
──────────────────────────────────────────────────────────────────────────
Function: BitShiftRight%
Shifts all bits in a QuickBASIC string variable to the right one bit
position. The number of bits in a string is eight times the length of the
string. The function returns the rightmost bit of the last character of
the string.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: BitShiftRight% **
** Type: Function **
** Module: CTOOLS2.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Shifts all bits in a QuickBASIC string one bit to
* the right. The rightmost bit is returned, and the
* leftmost bit is set to zero.
*
* EXAMPLE OF USE: bit% = BitShiftRight%(bit$)
* PARAMETERS: bit$ String containing a bit pattern
* VARIABLES: len Length of the string (number of bytes)
* str Pointer to string contents
* i Looping index to each byte of the string
* carry Bit carried over from byte to byte
* the_byte Working copy of each byte of the string
*
* Definition of the QuickBASIC string descriptor structure
* struct bas_str
* {
* int sd_len;
* char *sd_addr;
* }; */
int bitshiftright (basic_string)
struct bas_str *basic_string;
{
int len = basic_string->sd_len;
unsigned char *str = basic_string->sd_addr;
int i, carry;
unsigned int the_byte;
for (i=0, carry=0; i<len; i++)
{
the_byte = *(str + i);
*(str + i) = (the_byte >> 1) + carry;
carry = (the_byte & 1) << 7;
}
if (carry)
return(1);
else
return(0);
}
──────────────────────────────────────────────────────────────────────────
Function: MenuString%
Creates a horizontal menu bar, highlights the one-word choices, and
returns the number of the choice selected when the Enter key is pressed.
The menu is highlighted using the same color scheme as the main pull-down
menu bars of both the QuickBASIC and QuickC environments.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: MenuString% **
** Type: Function **
** Module: CTOOLS2.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Displays a horizontal bar menu and waits for a
* response from the user. Returns the number of
* the word selected from the string.
* EXAMPLE OF USE: choice% = MenuString%(row%, col%, menu$)
* PARAMETERS: row% Row location to display the menu string
* col% Column location to display the menu string
* menu$ String containing list of words representing
* choices
* VARIABLES: len Length of the menu string
* str Pointer to string contents
* vidptr Pointer to video memory
* attribute Index into string
* character Character from keyboard press
* both Combination of a character and its attribute
* i Looping index
* j Looping index
* k Looping index
* c Looping index
* choice Menu selection number
* wordnum Sequential count of each word in the menu str
* refresh Signals to redraw the menu string
* #include <ctype.h>
* #include <conio.h>
* #define VIDEO_START 0xb8000000
* #define BLACK_ON_CYAN 48
* #define RED_ON_CYAN 52
* #define BRIGHT_WHITE_ON_RED 79
* #define ENTER 13
* #define RIGHT_ARROW 77
* #define LEFT_ARROW 75
*
* Definition of the QuickBASIC string descriptor structure
* struct bas_str
* {
* int sd_len;
* char *sd_addr;
* }; */
int menustring (row, col, basic_string)
int *row, *col;
struct bas_str *basic_string;
{
int len;
char * str;
int far * vidptr;
int attribute, character, both;
int i, j, k, c;
int choice, wordnum;
int refresh;
void packword();
/* Initialize variables */
len = basic_string->sd_len;
str = basic_string->sd_addr;
vidptr = (int far *) VIDEO_START + (*row - 1) * 80 + (*col - 1);
choice = 1;
refresh = 1;
/* Loop until return() statement */
while (1)
{
/* Display the string only if refresh is non-zero */
if (refresh)
{
refresh = 0;
/* Loop through each character of the string */
for (wordnum = 0, i=0; i<len; i++)
{
/* Set the character and default attribute */
character = str[i];
attribute = BLACK_ON_CYAN;
/* Uppercase? */
if (isupper(character))
{
wordnum++;
attribute = RED_ON_CYAN;
}
/* In the middle of the current selection? */
if (wordnum == choice && character != ' ')
attribute = BRIGHT_WHITE_ON_RED;
/* Move data to video */
packword(&both, &attribute, &character);
vidptr[i] = both;
}
}
/* Check for any key presses */
if (kbhit())
{
/* Get the key code and process it */
switch (c = getch())
{
/* Return the choice when Enter is pressed */
case ENTER:
return (choice);
/* Highlight next choice if Right arrow is pressed */
case RIGHT_ARROW:
if (choice < wordnum)
{
choice++;
refresh = 1;
}
break;
/* Highlight previous choice if Left arrow is pressed */
case LEFT_ARROW:
if (choice > 1)
{
choice--;
refresh = 1;
}
break;
/* Check for match on first character of each word */
default:
c = _toupper(c);
for (k=0, j=0; j<len; j++)
{
/* Each choice starts at an uppercase char */
if (isupper(str[j]))
k++;
/* Match if same char and not current choice */
if (str[j] == c && k != choice)
{
choice = k;
refresh = 1;
break;
}
}
break;
}
}
}
}
──────────────────────────────────────────────────────────────────────────
Function: NumberOfBits&
Returns the number of bits in a string without altering its contents.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: NumberOfBits& **
** Type: Function **
** Module: CTOOLS2.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Counts the 1 bits in a QuickBASIC string.
*
* EXAMPLE OF USE: count& = NumberOfBits&(a$)
* PARAMETERS: a$ String containing bits to be counted
* VARIABLES: len Length of the string
* str Pointer to string contents
* i Looping index to each byte
* the_byte Working copy of each byte of the string
* count Count of the bits
*
* Definition of the QuickBASIC string descriptor structure
* struct bas_str
* {
* int sd_len;
* char *sd_addr;
* }; */
long numberofbits (basic_string)
struct bas_str *basic_string;
{
int len = basic_string->sd_len;
unsigned char *str = basic_string->sd_addr;
int i,the_byte;
long count = 0;
for (i=0; i<len; i++)
{
the_byte = *(str+i);
while (the_byte)
{
count += (the_byte & 1);
the_byte >>= 1;
}
}
return (count);
}
──────────────────────────────────────────────────────────────────────────
Subprogram: PackWord
Packs two bytes into an integer value. For example, the high and low (most
significant and least significant) bytes of the integer value 258 are 1
and 2. QuickBASIC can pack two values into an integer by multiplying the
first value by 256 and adding the second. This works well for small byte
values but becomes awkward when the high byte is 128 or greater. In such
cases, the resulting integer is a negative number.
PackWord uses the QuickC union and structure data definition features to
pack the byte values using simple data moves in memory.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: PackWord **
** Type: Subprogram **
** Module: CTOOLS2.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Packs two byte values into the high and low
* bytes of an integer (word).
*
* EXAMPLE OF USE: PackWord hiloword%, hibyte%, lobyte%
* PARAMETERS: hiloword% Integer word to pack the two bytes into
* hibyte% Integer value of the most significant byte
* lobyte% Integer value of the least significant byte
* VARIABLES: both A union of a two-byte structure and an intege
* variable */
void packword (hiloword, hibyte, lobyte)
int *hiloword, *hibyte, *lobyte;
{
union
{
struct
{
unsigned char lo;
unsigned char hi;
} bytes;
int hilo;
} both;
both.bytes.hi = *hibyte;
both.bytes.lo = *lobyte;
*hiloword = both.hilo;
}
──────────────────────────────────────────────────────────────────────────
Subprogram: TextGet
Copies a rectangular area of the text screen into a string variable. This
is similar in concept to the graphics-oriented GET statement, except that
this subprogram copies text-mode screen data. To redisplay the text
anywhere on the screen, use the TextPut subprogram.
The string variable must be exactly the right length for the amount of
data to be copied, or the call will be ignored. There are two bytes of
screen memory for each character displayed (the character and its color
attribute), so the string must contain width * height * 2 bytes. For
example, to save the area from row 3, column 4, to row 5, column 9, the
string length must be 3 * 6 * 2, or 36 characters. The SPACE$ statement is
ideal for preparing strings for this call. For example, a$ = SPACE$(36)
makes the previous string the correct length.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: TextGet **
** Type: Subprogram **
** Module: CTOOLS2.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Saves characters and attributes from a rectangular
* area of the screen.
*
* EXAMPLE OF USE: TextGet r1%, c1%, r2%, c2%, a$
* PARAMETERS: r1% Pointer to row at upper left corner
* c1% Pointer to column at upper left corner
* r2% Pointer to row at lower right corner
* c2% Pointer to column at lower right corner
* a$ String descriptor, where screen contents
* will be stored
* VARIABLES: len Length of string
* str Pointer to string contents
* video Pointer to video memory
* i Index into string
* row Looping index
* col Looping index
* #define VIDEO_START 0xb8000000
*
* Definition of the QuickBASIC string descriptor structure
*
* struct bas_str
* {
* int sd_len;
* char *sd_addr;
* }; */
void textget (r1,c1,r2,c2,basic_string)
int *r1,*c1,*r2,*c2;
struct bas_str *basic_string;
{
int len;
int * str;
int far * video;
int i,row,col;
len = basic_string->sd_len;
str = (int *) basic_string->sd_addr;
video = (int far *) VIDEO_START;
if (len == (*r2 - *r1 + 1) * (*c2 - *c1 + 1) * 2)
for (row = *r1 - 1, i = 0; row < *r2; row++)
for (col = *c1 - 1; col < *c2; col++)
str[i++] = video[row * 80 + col];
}
──────────────────────────────────────────────────────────────────────────
Subprogram: TextPut
Restores a rectangular area of a text screen from a string variable
previously used to copy screen contents via the TextGet subprogram. This
is similar to the graphics-oriented PUT statement, except that this
subprogram copies text-mode screen data.
The string variable must be exactly the right length for the amount of
data to be copied onto the screen, or the call will be ignored. There are
two bytes of screen memory for each character displayed (the character and
its color attribute), so the string must contain width * height * 2 bytes.
See the TextGet subprogram for more details on string length
requirements.
The shape of the restored area can differ from the original area as long
as the total number of bytes is the same. For example, an area three
characters wide by four characters high can be copied using TextGet and
then placed back on the screen in an area two wide by six high. As long as
the width times the height remains constant, TextPut will move the data
onto the screen.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: TextPut **
** Type: Subprogram **
** Module: CTOOLS2.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Restores characters and attributes to a rectangular
* area of the screen.
*
* EXAMPLE OF USE: TextPut r1%, c1%, r2%, c2%, a$
* PARAMETERS: r1% Pointer to row at upper left corner
* c1% Pointer to column at upper left corner
* r2% Pointer to row at lower right corner
* c2% Pointer to column at lower right corner
* a$ String descriptor where screen contents are s
* VARIABLES: len Length of string
* str Pointer to string contents
* video Pointer to video memory
* i Index into string
* row Looping index
* col Looping index
* #define VIDEO_START 0xb8000000
*
* Definition of the QuickBASIC string descriptor structure
* struct bas_str
* {
* int sd_len;
* char *sd_addr;
* }; */
void textput (r1,c1,r2,c2,basic_string)
int *r1,*c1,*r2,*c2;
struct bas_str *basic_string;
{
int len;
int * str;
int far * video;
int i,row,col;
len = basic_string->sd_len;
str = (int *) basic_string->sd_addr;
video = (int far *) VIDEO_START;
if (len == (*r2 - *r1 + 1) * (*c2 - *c1 + 1) * 2)
for (row = *r1 - 1, i = 0; row < *r2; row++)
for (col = *c1 - 1; col < *c2; col++)
video[row * 80 + col] = str[i++];
}
──────────────────────────────────────────────────────────────────────────
Subprogram: UnPackWord
Extracts two bytes from a QuickBASIC integer value. For example, the high
and low (most significant and least significant) bytes of the integer
value 258 are 1 and 2, and the two bytes of -1 are 255 and 255.
CDEMO2.BAS AND CTOOLS2.C
This subprogram uses the QuickC union and structure data definition
features to unpack the byte values using simple data moves in memory.
──────────────────────────────────────────────────────────────────────────
/***********************************************
** Name: UnPackWord **
** Type: Subprogram **
** Module: CTOOLS2.C **
** Language: Microsoft QuickC/QuickBASIC **
************************************************
*
* Unpacks two byte values from the high and low
* bytes of an integer (word).
*
* EXAMPLE OF USE: UnPackWord hiloword%, hibyte%, lobyte%
* PARAMETERS: hiloword% Integer word containing the two bytes
* hibyte% Integer value of the most significant byte
* lobyte% Integer value of the least significant byte
* VARIABLES: both A union of a two-byte structure and an intege
* variable */
void unpackword (hiloword, hibyte, lobyte)
int *hiloword, *hibyte, *lobyte;
{
union
{
struct
{
unsigned char lo;
unsigned char hi;
} bytes;
int hilo;
} both;
both.hilo = *hiloword;
*hibyte = both.bytes.hi;
*lobyte = both.bytes.lo;
}
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
PART 4 APPENDIXES
────────────────────────────────────────────────────────────────────────────
Appendix A Requirements for Running Toolboxes/Programs
In the following table, the Usage line assumes execution from the system
prompt after you compile the .BAS program. From QuickBASIC, modify
COMMAND$ and enter any parameters before selecting Run and Start.
CGA─Color Graphics Adapter/Monitor
EGA─Enhanced Graphics Adapter/Monitor
VGA─Video Graphics Adapter/Monitor
Sub─Subprogram
Func─Function
╓┌─┌────────────────────┌────────────────────────────────────────────────────╖
Name Description
──────────────────────────────────────────────────────────────────────────
ATTRIB.BAS Screen/Text Attribute Display
Utility Program: 1 Sub
Usage: ATTRIB
Requirements: CGA
BIN2HEX.BAS Binary-to-Hex Conversion
Utility Program
Usage: BIN2HEX inFileName.ext outFileName.ext
.MAK File: BIN2HEX.BAS
PARSE.BAS
BIOSCALL.BAS ROM BIOS Interrupt Calls
Toolbox: 6 Sub with Demo Module
Usage: BIOSCALL
Name Description
──────────────────────────────────────────────────────────────────────────
Usage: BIOSCALL
Requirements: MIXED.QLB/.LIB
BITS.BAS Bit Manipulation
Toolbox: 2 Func/2 Sub with Demo Module
Usage: BITS
CALENDAR.BAS Calendar and Time Routines
Toolbox: 19 Func/1 Sub with Demo Module
Usage: CALENDAR
CARTESIA.BAS Cartesian Coordinate Routines
Toolbox: 2 Func/2 Sub with Demo Module
Usage: CARTESIA
CDEMO1.BAS Demo 1 of C-Language Routines
Program
Usage: CDEMO1
Requirements: CGA
MIXED.QLB/.LIB
Microsoft QuickC
CDEMO2.BAS Demo 2 of C-Language Routines
Program
Name Description
──────────────────────────────────────────────────────────────────────────
Program
Usage: CDEMO2
Requirements: CGA
MIXED.QLB/.LIB
Microsoft QuickC
CIPHER.BAS Cipher File Security
Utility Program: 1 Func/1 Sub
Usage: CIPHER filename.ext key or CIPHER /NEWKEY
.MAK File: CIPHER.BAS
RANDOMS.BAS
COLORS.BAS VGA Color Selection
Utility Program: 1 Func
Usage: COLORS
Requirements: VGA or MCGA
MIXED.QLB/.LIB
Mouse
.MAK File: COLORS.BAS
BITS.BAS
MOUSSUBS.BAS
Name Description
──────────────────────────────────────────────────────────────────────────
MOUSSUBS.BAS
COMPLEX.BAS Complex Numbers
Toolbox: 12 Sub with Demo Module
Usage: COMPLEX
.MAK File: COMPLEX.BAS
CARTESIA.BAS
CTOOLS1.C C Functions──Characters
C-Language Toolbox: 12 Func/2 Sub
Usage: Place in MIXED.QLB and MIXED.LIB
after compiling into object files and then run
CDEMO1.BAS from QuickBASIC
CTOOLS2.C C Functions──Text
C-Language Toolbox: 4 Func/4 Sub
Usage: Place in MIXED.QLB and MIXED.LIB
after compiling into object files and then run
CDEMO2.BAS from QuickBASIC
DOLLARS.BAS Dollar Formatting
Toolbox: 3 Func with Demo Module
Usage: DOLLARS
Name Description
──────────────────────────────────────────────────────────────────────────
Usage: DOLLARS
DOSCALLS.BAS MS-DOS System Calls
Toolbox: 6 Func/9 Sub with Demo Module
Usage: DOSCALLS
Requirements: MIXED.QLB/.LIB
MS-DOS 3.3 or later
EDIT.BAS Editing
Toolbox: 5 Sub with Demo Module
Usage: EDIT
.MAK File: EDIT.BAS
KEYS.BAS
ERROR.BAS Error Message
Toolbox: 1 Sub with Demo Module
Usage: ERROR
FIGETPUT.BAS FILEGET and FILEPUT Routines
Toolbox: 1 Func/1 Sub with Demo Module
Usage: FIGETPUT
FILEINFO.BAS Directory/File Listing Information
Toolbox: 3 Sub with Demo Module
Name Description
──────────────────────────────────────────────────────────────────────────
Toolbox: 3 Sub with Demo Module
Usage: FILEINFO
Requirements: MIXED.QLB/.LIB
FRACTION.BAS Fractions
Toolbox: 3 Func/7 Sub with Demo Module
Usage: FRACTION
GAMES.BAS Games
Toolbox: 4 Func/2 Sub with Demo Module
Usage: GAMES
Requirements: CGA
HEX2BIN.BAS Hex-to-Binary Conversion
Utility Program
Usage: HEX2BIN inFileName.ext outFileName.ext
.MAK File: HEX2BIN.BAS
PARSE.BAS
STRINGS.BAS
JUSTIFY.BAS Paragraph Justification
Toolbox: 1 Sub with Demo Module
Usage: JUSTIFY
Name Description
──────────────────────────────────────────────────────────────────────────
Usage: JUSTIFY
.MAK File: JUSTIFY.BAS
EDIT.BAS
KEYS.BAS
PARSE.BAS
KEYS.BAS Enhanced Keyboard Input Functions
Toolbox: 2 Func with Demo Module
Usage: KEYS
LOOK.BAS Text File Display Utility
Utility Program
Usage: LOOK filename.ext
.MAK File: LOOK.BAS
KEYS.BAS
MONTH.BAS Three-Month Calendar
Utility Program
Usage: MONTH
.MAK File: MONTH.BAS
CALENDAR.BAS
MOUSGCRS.BAS Custom Graphics Mouse Cursors
Name Description
──────────────────────────────────────────────────────────────────────────
MOUSGCRS.BAS Custom Graphics Mouse Cursors
Utility Program
Usage: MOUSGCRS
Requirements: CGA
MIXED.QLB/.LIB
Mouse
.MAK File: MOUSGCRS.BAS
BITS.BAS
MOUSSUBS.BAS
MOUSSUBS.BAS Mouse Subroutines
Toolbox: 26 Subs with Demo Module
Usage: MOUSSUBS
Requirements: CGA
MIXED.QLB/.LIB
Mouse
.MAK File: MOUSSUBS.BAS
BITS.BAS
MOUSTCRS.BAS Text-Mode Mouse Cursor
Utility Program
Name Description
──────────────────────────────────────────────────────────────────────────
Utility Program
Usage: MOUSTCRS
Requirements: MIXED.QLB/.LIB
Mouse
.MAK File: MOUSTCRS.BAS
MOUSSUBS.BAS
BITS.BAS
ATTRIB.BAS
OBJECT.BAS Interactive Graphics Creation
Toolbox and Utility Program: 1 Sub
Usage: OBJECT
Requirements: CGA
.MAK File: OBJECT.BAS
KEYS.BAS
EDIT.BAS
PARSE.BAS Command Line Parsing
Toolbox: 2 Subs with Demo Module
Usage: PARSE
PROBSTAT.BAS Probability and Statistical Routines
Name Description
──────────────────────────────────────────────────────────────────────────
PROBSTAT.BAS Probability and Statistical Routines
Toolbox: 7 Func with Demo Module
Usage: PROBSTAT
QBFMT.BAS Formatting Utility
Utility Program: 3 Subs
Usage: QBFMT filename [indention]
.MAK File: QBFMT.BAS
PARSE.BAS
STRINGS.BAS
QBTREE.BAS Directory/Subdirectory/Files Listing
Utility Program
Usage: QBTREE [path]
Requirements: MIXED.QLB/.LIB
.MAK File: QBTREE.BAS
FILEINFO.BAS
QCAL.BAS Command Line Scientific Calculator
Utility Program: 1 Func/3 Subs
Usage: QCAL [number] [function] [...]
.MAK File: QCAL.BAS
Name Description
──────────────────────────────────────────────────────────────────────────
.MAK File: QCAL.BAS
QCALMATH.BAS
QCALMATH.BAS Math Functions
Toolbox: 31 Func/2 Sub
Usage: loaded by the QCAL program
RANDOMS.BAS Pseudorandom Numbers
Toolbox: 6 Func/1 Sub with Demo Module
Usage: RANDOMS
STDOUT.BAS MS-DOS Standard (ANSI) Output
Toolbox: 12 Sub with Demo Module
Usage: STDOUT
Requirements: MIXED.QLB/.LIB
ANSI.SYS
STRINGS.BAS String Manipulation
Toolbox: 18 Func/1 Sub with Demo Module
Usage: STRINGS
TRIANGLE.BAS Triangles
Toolbox: 3 Func/1 Sub with Demo Module
Usage: TRIANGLE
Name Description
──────────────────────────────────────────────────────────────────────────
Usage: TRIANGLE
Requirements: CGA
.MAK File: TRIANGLE.BAS
QCALMATH.BAS
WINDOWS.BAS Windows
Toolbox: 2 Sub with Demo Module
Usage: WINDOWS
Requirements: MIXED.QLB/.LIB
Mouse (optional)
.MAK File: WINDOWS.BAS
BIOSCALL.BAS
BITS.BAS
KEYS.BAS
MOUSSUBS.BAS
WORDCOUN.BAS Word Counting
Toolbox: 1 Func with Demo Module
Usage: WORDCOUN filename
──────────────────────────────────────────────────────────────────────────
Name Description
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
Appendix B Functions-to-Modules Cross Reference
╓┌─┌───────────────────┌─────────────┌───────────────────────────────────────╖
Function Module Description
──────────────────────────────────────────────────────────────────────────
AbsoluteX# QCALMATH Absolute value of a number
Add# QCALMATH Sum of two numbers
Angle! CARTESIA Angle between X axis and line to x, y
point
ArcCosine# QCALMATH Arc cosine function of a number
ArcHypCosine# QCALMATH Inverse hyperbolic cosine of a number
ArcHypSine# QCALMATH Inverse hyperbolic sine of a number
ArcHypTangent# QCALMATH Inverse hyperbolic tangent of a number
Function Module Description
──────────────────────────────────────────────────────────────────────────
ArcHypTangent# QCALMATH Inverse hyperbolic tangent of a number
ArcSine# QCALMATH Inverse sine of a number
ArcTangent# QCALMATH Inverse tangent of a number
ArithmeticMean# PROBSTAT Arithmetic mean of an array of numbers
Ascii2Ebcdic$ STRINGS Converts string from ASCII to EBCDIC
BestMatch$ STRINGS Returns best match to input string
Bin2BinStr$ BITS Integer to 16-character binary string
BinStr2Bin% BITS 16-character binary string to integer
BitShiftLeft% CTOOLS2 Shifts all bits in a string left one bit
BitShiftRight% CTOOLS2 Shifts all bits in a string right one
1 through 52
BufferedKeyInput$ DOSCALLS numberstring of specified length
Card$ GAMES Returns name of card given a number from
Ceil# QCALMATH Smallest whole number greater than a
Center$ STRINGS Centers string by padding with spaces
ChangeSign# QCALMATH Reverses sign of a number
CheckDate% CALENDAR Validates date with return of TRUE/FALSE
Collision% GAMES Returns TRUE or FALSE collision
condition
Function Module Description
──────────────────────────────────────────────────────────────────────────
condition
Combinations# PROBSTAT Combinations of n items, r at a time
Comma$ DOLLARS Double-precision with commas inserted
Cosine# QCALMATH Cosine of a number
Date2Day% CALENDAR Day of month number from date string
Date2Julian& CALENDAR Julian day number for a given date
Date2Month% CALENDAR Month number from date string
Date2Year% CALENDAR Year number from date string
DayOfTheCentury& CALENDAR Day of the given century
DayOfTheWeek$ CALENDAR Name of day of the week for given date
DayOfTheYear% CALENDAR Day of the year (1 through 366) for
given date
DaysBetweenDates& CALENDAR Number of days between two dates
Deg2Rad# TRIANGLE Converts degree angular units to radians
Detab$ STRINGS Replaces tab characters with spaces
Dice% GAMES Returns total showing for throwing n
dice
Divide# QCALMATH Result of dividing two numbers
DollarString$ DOLLARS Dollar representation rounded with
Function Module Description
──────────────────────────────────────────────────────────────────────────
DollarString$ DOLLARS Dollar representation rounded with
commas
DOSVersion! DOSCALLS Version number of MS-DOS returned
Ebcdic2Ascii$ STRINGS Converts a string from EBCDIC to ASCII
Entab$ STRINGS Replaces spaces with tab characters
Exponential# QCALMATH Exponential function of a number
Factorial# PROBSTAT Factorial of a number
FileGet$ FIGETPUT Returns a string with contents of file
FilterIn$ STRINGS Retains only specified characters in
string
FilterOut$ STRINGS Deletes specified characters from string
Fraction2String$ FRACTION Converts type Fraction variable to a
string
FractionalPart# QCALMATH Fractional part of a number
GeometricMean# PROBSTAT Geometric mean of an array of numbers
GetDirectory$ DOSCALLS Path to disk directory specified
GetDrive$ DOSCALLS Current drive string
GetVerifyState% DOSCALLS Verify setting (state)
GreatestComDiv& FRACTION seconds greatest common divisor
Function Module Description
──────────────────────────────────────────────────────────────────────────
GreatestComDiv& FRACTION seconds greatest common divisor
HarmonicMean# PROBSTAT Harmonic mean of an array of numbers
HMS2Time$ CALENDAR Time string for given hour, minute, and
HypCosine# QCALMATH Hyperbolic cosine of a number
HypSine# QCALMATH Hyperbolic sine of a number
HypTangent# QCALMATH Hyperbolic tangent of a number
InKeyCode% KEYS Returns unique integer for any key
pressed
IntegerPart# QCALMATH Integer part of a number
IsItAlnum% CTOOLS1 Alphanumeric character determination
IsItAlpha% CTOOLS1 Alphabetic character determination
IsItAscii% CTOOLS1 Standard ASCII character determination
IsItCntrl% CTOOLS1 Control character determination
IsItDigit% CTOOLS1 Decimal digit (0─9) determination
IsItGraph% CTOOLS1 Graphics character determination
IsItLower% CTOOLS1 Lowercase character determination
IsItPrint% CTOOLS1 Printable character determination
IsItPunct% CTOOLS1 Punctuation character determination
IsItSpace% CTOOLS1 Space character determination
Function Module Description
──────────────────────────────────────────────────────────────────────────
IsItSpace% CTOOLS1 Space character determination
IsItUpper% CTOOLS1 Uppercase character determination
IsItXDigit% CTOOLS1 Hexadecimal character determination
Julian2Date$ CALENDAR Date string from given Julian day number
KeyCode% KEYS Waits and returns integer value for key
LeastComMul& FRACTION Returns least common multiple
LogBase10# QCALMATH Log base 10 of a number
LogBaseN# QCALMATH Log base N of a number
LogE# QCALMATH Natural logarithm of a number
Lpad$ STRINGS Returns left-justified input string
LtrimSet$ STRINGS Deletes specified characters from left
Magnitude! CARTESIA Distance from origin to x, y point
MDY2Date$ CALENDAR Date string from given month, day, and
year
MenuString% CTOOLS2 Bar menu and user response function
Modulus# QCALMATH Remainder of the division of two numbers
MonthName$ CALENDAR Name of month for a given date
Multiply# QCALMATH COMMAND$of two numbers
NewWord$ CIPHER Creates pseudorandom new word
Function Module Description
──────────────────────────────────────────────────────────────────────────
NewWord$ CIPHER Creates pseudorandom new word
NextParameter$ QCAL Extracts number or command from
NumberOfBits& CTOOLS2 Determines number of 1 bits in a string
OneOverX# QCALMATH Result of dividing 1 by a number
Ord% STRINGS Returns byte number for ANSI mnemonic
Permutations# PROBSTAT Permutations of n items, r at a time
QuadraticMean# PROBSTAT Quadratic mean of an array of numbers
Rad2Deg# TRIANGLE from meanradian angular units to degrees
Rand& RANDOMS Long integers
RandExponential! RANDOMS Real value with exponential distribution
RandFrac! RANDOMS deviationecision positive value < 1.0
RandInteger% RANDOMS Integers within desired range
RandNormal! RANDOMS Single-precision from mean and standard
RandReal! RANDOMS Single-precision value in desired range
Repeat$ STRINGS Combines multiple copies into one string
Replace$ STRINGS Replaces specified characters in string
Reverse$ STRINGS Reverses order of characters in a string
ReverseCase$ STRINGS Reverses case for each charater in a
string
Function Module Description
──────────────────────────────────────────────────────────────────────────
string
Round# DOLLARS Rounding at specified decimal place
Rpad$ STRINGS Returns right-justified input string
RtrimSet$ STRINGS Deletes specified characters from right
Second2Date$ CALENDAR Seconds from last of 1979 to date given
Second2Time$ CALENDAR Time of day from seconds since last of
1979
Shade& COLORS Color value from given red, green, and
blue
Shuffle$ GAMES Randomizes character bytes in string
Sign# QCALMATH Sign of a number
Sine# QCALMATH Sine of a number
SquareRoot# QCALMATH Square root of a number
Subtract# QCALMATH Difference between two numbers
Tangent# QCALMATH Tangent of a number
Time2Hour% CALENDAR Hour number from time string
Time2Minute% CALENDAR Minute number from time string
Time2Second% CALENDAR Seconds number from time string
TimeDate2Second& CALENDAR Seconds from last of 1979 from date/time
Function Module Description
──────────────────────────────────────────────────────────────────────────
TimeDate2Second& CALENDAR Seconds from last of 1979 from date/time
Translate$ STRINGS Exchanges characters in string from
table
TranslateCountry$ DOSCALLS Translates string──current country
setting
TriangleArea# TRIANGLE Calculates area of triangle from three
sides
WordCount% WORDCOUN Returns number of words in a string
Xsquared# QCALMATH Square of a number
YRaisedToX# QCALMATH Number raised to the power of a second
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
Appendix C Subprograms-to-Modules Cross Reference
╓┌─┌────────────────────────┌───────────┌────────────────────────────────────╖
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
AssignKey STDOUT Reassigns a string to a key
Attrib ATTRIB Table of color attributes (text mode)
Attribute STDOUT Sets screen color (ANSI driver
definition)
BitGet BITS Value from any bit position in a
string
BitPut BITS translation tables at location in a
string
BuildAEStrings STRINGS Builds ASCII and EBCDIC character
ClearLine STDOUT Clears line from cursor to end of
line
ClearScreen STDOUT Clears screen
Complex2String COMPLEX String representation of a complex
number
ComplexAdd COMPLEX Adds two complex numbers
ComplexDiv COMPLEX Divides two complex numbers
ComplexExp COMPLEX Exponential function of a complex
number
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
number
ComplexLog COMPLEX numberl log of a complex number
ComplexMul COMPLEX Multiplies two complex numbers
ComplexPower COMPLEX Complex number raised to a complex
ComplexReciprocal COMPLEX Reciprocal of a complex number
ComplexRoot COMPLEX Complex root of a complex number
ComplexSqr COMPLEX Square root of a complex number
ComplexSub COMPLEX Subtracts two complex numbers
CrLf STDOUT Sends carriage return and line feed
Curschek MOUSSUBS Check-mark mouse cursor pattern mask
Cursdflt MOUSSUBS Arrow mouse cursor pointing up and
left
Curshand MOUSSUBS Pointing hand mouse cursor
Curshour MOUSSUBS Hourglass mouse cursor
Cursjet MOUSSUBS Jet-shaped mouse cursor
Cursleft MOUSSUBS Left arrow mouse cursor
CursorDown STDOUT Moves cursor down specified number of
lines
CursorHome STDOUT Moves cursor to upper left corner of
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
CursorHome STDOUT Moves cursor to upper left corner of
screen
CursorLeft STDOUT Moves cursor left specified number of
spaces
CursorPosition STDOUT Moves cursor to specified row and
column
CursorRight STDOUT Moves cursor right specified number
of spaces
CursorUp STDOUT Moves cursor up specified number of
lines
Cursplus MOUSSUBS Plus sign mouse cursor
Cursup MOUSSUBS Up arrow mouse cursor
Cursx MOUSSUBS X-mark mouse cursor
DisplayStack QCAL Displays final results of the program
DrawBox EDIT Creates a double-lined box on the
display
Dup QCALMATH Duplicates top entry on the stack
EditBox EDIT Allows editing in a boxed area of the
screen
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
screen
EditLine EDIT Allows editing of string at cursor
position
Equipment BIOSCALL Equipment/hardware information
ErrorMessage ERROR Error message display
FilePut FIGETPUT Writes contents of string into binary
file
FileRead LOOK Reads lines of ASCII files into an
array
FileTreeSearch QBTREE numbers defined by the boundsutine
FillArray GAMES Fills an integer array with a
FindFirstFile FILEINFO Finds first file that matches
parameter
FindNextFile FILEINFO Finds next file that matches
parameter
FormatTwo EDIT Splits string into two strings
FractionAdd FRACTION Adds two fractions and reduces
FractionDiv FRACTION Divides two fractions and reduces
FractionMul FRACTION Multiplies two fractions and reduces
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
FractionMul FRACTION Multiplies two fractions and reduces
FractionReduce FRACTION Reduces fraction to lowest terms
FractionSub FRACTION Subtracts two fractions and reduces
GetCountry DOSCALLS Current country setting
GetDiskFreeSpace DOSCALLS Disk space format and usage for input
drive
GetFileAttributes DOSCALLS Attribute bits for given file
GetFileData FILEINFO Extracts file directory information
GetMediaDescriptor DOSCALLS Drive information for system
GetShiftStates BIOSCALL Shift key states
Indent QBFMT Performs line indention
InsertCharacter EDIT Inserts a character
Justify JUSTIFY Adjusts strings to specified widths
MouseHide MOUSSUBS Turns off mouse visibility
MouseInches MOUSSUBS parameters-to-cursor motion ratio
MouseInstall MOUSSUBS Checks mouse availability; resets
MouseLightPen MOUSSUBS Mouse emulation of a lightpen
MouseMaskTranslate MOUSSUBS Translates pattern/hot spot to binary
MouseMickey MOUSSUBS Returns motion increments since last
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
MouseMickey MOUSSUBS Returns motion increments since last
call
MouseNow MOUSSUBS Current state/location of the mouse
MousePressLeft MOUSSUBS Location of mouse──left button press
MousePressRight MOUSSUBS Location of mouse──right button press
MousePut MOUSSUBS Moves cursor to the given position
MouseRange MOUSSUBS Limits mouse cursor motion to
rectangle
MouseReleaseLeft MOUSSUBS Location of mouse──left button
release
MouseReleaseRight MOUSSUBS Location of mouse──right button
release
MouseSetGcursor MOUSSUBS Sets graphics-mode mouse cursor
MouseShow MOUSSUBS Activates and displays mouse cursor
MouseSoftCursor MOUSSUBS Sets text-mode attributes (mouse
cursor)
MouseWarp MOUSSUBS Sets mouse double-speed threshold
MovBytes CTOOLS1 Moves bytes from one location to
another
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
another
MovWords CTOOLS1 Moves blocks of words in memory
OneMonthCalendar CALENDAR One-month calendar for given date
PackWord CTOOLS2 Packs two bytes into an integer value
ParseLine PARSE Breaks a string into individual words
ParseWord PARSE Parses and removes first word from
string
Pol2Rec CARTESIA Polar to Cartesian conversion
PrintScreen BIOSCALL Screen dump
Process QCAL Controls action for command line
parameters
ProcesX CIPHER Enciphers string by XORing bytes
QcalHelp QCAL Provides a "Help" display for program
RandShuffle RANDOMS Initializes random number generator
ReBoot BIOSCALL System reboot
Rec2Pol CARTESIA Cartesian to polar conversion
SaveObject OBJECT Creates graphics "PUT" file source
code
Scroll BIOSCALL Moves text in designated area of
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
Scroll BIOSCALL Moves text in designated area of
screen
SetCode QBFMT Determines indention code by keyword
SetDirectory DOSCALLS Sets current directory
SetDrive DOSCALLS Sets current disk drive
SetFileAttributes DOSCALLS Sets the attribute bits for a given
file
SetVerifyState DOSCALLS Sets or clears verify state (writing
to file)
ShuffleArray GAMES Randomizes integers in an array
SplitFractions FRACTION Parses fraction problem string
SplitUp QBFMT Splits line into major components
StdOut STDOUT Sends a string to standard output
channel
String2Complex COMPLEX Converts string to complex variable
String2Fraction FRACTION Converts a string to Fraction
variable
SwapXY QCALMATH of screen two entries on the stack
TextGet CTOOLS2 Saves characters and attributes from
Subprogram Module Description
──────────────────────────────────────────────────────────────────────────
TextGet CTOOLS2 Saves characters and attributes from
TextPut CTOOLS2 Restores text from TextGet to screen
Triangle TRIANGLE Calculates sides and angles of
triangle
UnPackWord CTOOLS2 Unpacks values from high and low
state
VideoState BIOSCALL Mode, column, and page display of
Windows WINDOWS Creates a pop-up window
WindowsPop WINDOWS Removes last displayed window
WriteToDevice DOSCALLS Outputs a string to a device
──────────────────────────────────────────────────────────────────────────
────────────────────────────────────────────────────────────────────────────
Appendix D Hexadecimal Format (.obj) Files
Three assembly-language modules are discussed in this book. The suggested
method for creating object-code files is to use the Microsoft Macro
Assembler 5.0 on the source-code files. If necessary, however, you can
process these files using the HEX2BIN.BAS program to create the desired
object-code files. (See the HEX2BIN.BAS program for information about how
to make the conversions.)
MOUSE.HEX (MOUSE.OBJ)
80 0B 00 09 4D 4F 55 53 - 45 2E 41 53 4D D4 96 24
00 00 06 44 47 52 4F 55 - 50 04 44 41 54 41 04 43
4F 44 45 0A 4D 4F 55 53 - 45 5F 54 45 58 54 05 5F
44 41 54 41 7D 98 07 00 - 48 39 00 05 04 01 D6 98
07 00 48 00 00 06 03 01 - 0F 9A 04 00 02 FF 02 5F
90 0C 00 00 01 05 4D 4F - 55 53 45 00 00 00 D5 88
04 00 00 A2 01 D1 A0 3D - 00 01 00 00 55 8B EC 8B
5E 0C 8B 07 50 8B 5E 0A - 8B 07 50 8B 5E 08 8B 0F
8B 5E 06 8B 17 5B 58 1E - 07 CD 33 53 8B 5E 0C 89
07 58 8B 5E 0A 89 07 8B - 5E 08 89 0F 8B 5E 06 89
17 5D CA 08 00 BC 8A 02 - 00 00 74
INTRPT.HEX (INTRPT.OBJ)
80 0C 00 0A 49 4E 54 52 - 50 54 2E 41 53 4D 7A 88
03 00 80 9E 57 96 25 00 - 00 06 44 47 52 4F 55 50
04 44 41 54 41 04 43 4F - 44 45 05 5F 44 41 54 41
0B 49 4E 54 52 50 54 5F - 54 45 58 54 23 98 07 00
48 07 01 06 04 01 06 98 - 07 00 48 00 00 05 03 01
10 9A 04 00 02 FF 02 5F - 90 1E 00 00 01 09 49 4E
54 45 52 52 55 50 54 00 - 00 00 0A 49 4E 54 45 52
52 55 50 54 58 0D 00 00 - 3F 88 04 00 00 A2 01 D1
A0 0B 01 01 00 00 55 8B - EC 83 C4 E2 C7 46 FA 08
00 EB 0B 55 8B EC 83 C4 - E2 C7 46 FA 0A 00 89 76
E4 89 7E E2 8C 5E FC 9C - 8F 46 FE 8B 76 08 8D 7E
E6 8B 4E FA FC 16 07 F3 - A5 55 8B 76 0A 8B 1C 0A
FF 74 03 E9 00 00 80 FB - 25 74 05 80 FB 26 75 0E
B8 08 00 50 B8 02 CA 50 - B8 83 C4 50 EB 07 33 C0
50 B8 CA 06 50 8A E3 B0 - CD 50 0E B8 00 00 50 16
8B C4 05 06 00 50 8B 46 - F4 25 D5 0F 50 8B 46 E6
8B 5E E8 8B 4E EA 8B 56 - EC 8B 76 F0 8B 7E E2 83
7E FA 08 74 14 81 7E F6 - FF FF 74 03 8E 5E F6 81
7E F8 FF FF 74 03 8E 46 - F8 8B 6E EE 9D CB 55 8B
EC 8B 6E 02 9C 8F 46 F4 - FF 76 FE 9D 89 46 E6 89
5E E8 89 4E EA 89 56 EC - 8B 46 DE 89 46 EE 89 76
F0 89 7E E2 8C 5E F6 8C - 46 F8 8E 5E FC 8D 76 E6
1E 07 8B 7E 06 8B 4E FA - FC F3 A5 8B 76 E4 8B 7E
E2 8B E5 5D CA 06 00 8B - 76 0A C7 04 FF FF 8B 76
E4 8B 7E E2 8E 5E FC 8B - E5 5D CA 06 00 0B 9C 0F
00 84 3E 00 01 01 F1 00 - C4 66 00 01 01 A8 00 CC
8A 02 00 00 74
CASEMAP.HEX (CASEMAP.OBJ)
80 0D 00 0B 43 41 53 45 - 4D 41 50 2E 41 53 4D 5F
96 26 00 00 06 44 47 52 - 4F 55 50 0C 43 41 53 45
4D 41 50 5F 54 45 58 54 - 04 44 41 54 41 04 43 4F
44 45 05 5F 44 41 54 41 - 08 98 07 00 48 14 00 03
05 01 FC 98 07 00 48 00 - 00 06 04 01 0E 9A 04 00
02 FF 02 5F 90 0E 00 00 - 01 07 43 41 53 45 4D 41
50 00 00 00 60 88 04 00 - 00 A2 01 D1 A0 18 00 01
00 00 55 8B EC 8B 5E 0A - 8B 07 FF 5E 06 8B 5E 0A
89 07 5D CA 06 00 E3 8A - 02 00 00 74
────────────────────────────────────────────────────────────────────────────
Appendix E Line-Drawing Characters
You can enter line-drawing characters into QuickBASIC programs by pressing
and holding the Alt key while typing up to three decimal digits on the
numeric keypad. You can then use QuickBASIC strings to outline screen
areas or to draw boxes around text. This chart organizes the line-drawing
characters by type rather than by ASCII value.
201 203 187 218 194 191
╔ ╦ ╗ ┌ ┬ ┐
204 ╠ ╣ 185 195 ├ ┤ 180
╚ ╩ ╝ └ ┴ ┘
200 202 188 192 193 217
206 205 197
╬ ═ ┼
186 ║ │ 179
╪ ─ ╫
216 196 215
213 209 184 214 210 183
╒ ╤ ╕ ╓ ╥ ╖
198 ╞ ╡ 181 199 ╟ ╢ 182
╘ ╧ ╛ ╙ ╨ ╜
212 207 190 211 208 189
John Clark Craig has written several books on computer programming since
1980, including True BASIC Programs and Subroutines. He lives with his
family in Eagle River, Alaska, where he is a programmer for ARCO Alaska,
Inc.